diff --git a/lab19/block.rb b/lab19/block.rb new file mode 100644 index 0000000..f3719e3 --- /dev/null +++ b/lab19/block.rb @@ -0,0 +1,52 @@ +def do_noisy + puts "About to call block" + yield + puts "Just called block" +end + +do_noisy do + puts 3 + 4 +end + + +class Array + def each_downcase + self.each do |word| + yield word.downcase + end + end +end + +["Alpha", "Beta", "AndSoOn"].each_downcase do |word| + puts word +end + +def iff b + return if b == false + yield +end + +def test_block + iff true do + puts "hello block" + return + end + puts "goodbye block" +end + +def iff2 b, lam + return if b == false + lam.call +end + +def test_lambda + iff2 true, (lambda do + puts "hello lambda" + return + end) + puts "goodbye lambda" +end + +test_block +test_lambda + diff --git a/lab19/conversion.rb b/lab19/conversion.rb new file mode 100644 index 0000000..06a56c4 --- /dev/null +++ b/lab19/conversion.rb @@ -0,0 +1,25 @@ +def conversion_chart(from_units, to_units, values) + puts "#{from_units}\t#{to_units}" + left_line = right_line = "" + from_units.length.times { left_line += '-' } + to_units.length.times { right_line += '-' } + puts "#{left_line}\t#{right_line}" + for val in values + converted = yield val + puts "#{val}\t#{converted}" + end + puts +end + +celsius_temps = [0,10,20,30,40,50,60,70,80,90,100] +conversion_chart("C", "F", celsius_temps) {|cel| cel * 9 / 5 + 32} + +fahrenheit_temps = [0,10,20,30,40,50,60,70,80,90,100,110,120,130,140,150,160,170,180,190,200 ] +conversion_chart("Fahr.", "Celsius", fahrenheit_temps) {|fahr| (fahr-32) * 5 / 9 } + + +conversion_chart("Km", "Miles", (1..10)) do |km| + mile = 0.621371 * km +end + + diff --git a/lab19/emp.rb b/lab19/emp.rb new file mode 100644 index 0000000..bdd1ee1 --- /dev/null +++ b/lab19/emp.rb @@ -0,0 +1,42 @@ +class Employee + # Adding methods to the singleton class of the object + # Employee (which has the class Class). + class << self + def add(emp) + puts "Adding #{emp}" + @employees = Hash.new unless @employees + @employees[emp.name] = emp + end + def get_emp_by_name name + @employees[name] + end + end + ##################################################### + attr_accessor :name, :ssid, :salary + def initialize(name, ssid, salary) + @name = name + @ssid = ssid + @salary = salary + Employee.add self + end + def to_s + @name + end +end + +alice = Employee.new("Alice Alley", 1234, 75000); +bob = Employee.new("Robert Tables", 5678, 50000); + +class << bob + def signing_bonus + 2000 + end +end + +puts(bob.signing_bonus); +#puts(alice.signing_bonus); + +b = Employee.get_emp_by_name "Robert Tables" +puts b.signing_bonus + + diff --git a/lab19/eval/class-eval-example.rb b/lab19/eval/class-eval-example.rb new file mode 100644 index 0000000..76d839e --- /dev/null +++ b/lab19/eval/class-eval-example.rb @@ -0,0 +1,19 @@ +class Class + def my_attr_accessor(*args) + args.each do |prop| + # Creating the getter + self.class_eval("def #{prop}; @#{prop}; end") + # Creating the setter + self.class_eval("def #{prop}=(v); @#{prop}=v; end") + end + end +end + +class Musician + my_attr_accessor :name, :genre, :instrument +end + +m = Musician.new +m.name = "Norah Jones" +puts m.name + diff --git a/lab19/eval/eval.js b/lab19/eval/eval.js new file mode 100644 index 0000000..0d438ea --- /dev/null +++ b/lab19/eval/eval.js @@ -0,0 +1,21 @@ +// Assume that this string comes from across the network +// representing employee records. +var jsonStr = + "[{name: 'Philip J. Fry', age: 1000, job: 'delivery boy'}," + + " {name: (function(){console.log('***All glory to the Hypnotoad!***')})() }," + + " {name: 'Bender Rodriguez', age: 42, job: 'bending unit'}]"; + +var employeeRecords = eval(jsonStr); +for (var i in employeeRecords) { + var emp = employeeRecords[i]; + console.log(emp.name); +} + +/* +$ node eval.js +***All glory to the Hypnotoad!*** +Philip J. Fry +undefined +Bender Rodriguez +*/ + diff --git a/lab19/eval/eval.rb b/lab19/eval/eval.rb new file mode 100644 index 0000000..264b3dd --- /dev/null +++ b/lab19/eval/eval.rb @@ -0,0 +1,67 @@ +# Ruby has rich metaprogramming tools, often bowered from Smalltalk. +# Like JavaScript, it has an eval feature. +prog = "puts 3 + 4" +eval prog + +# Eval is one of the most powerful metaprogramming features, +# but it is also one of the most dangerous. +print "Please enter your method name: " +m = gets.chomp +eval "def #{m}; puts 'Hi!'; end" +eval m + +=begin +Thomass-MacBook-Pro-3:lab15 taustin$ ruby eval.rb +Please enter your method name: abc; end; puts "Mwah, hah, hah!"; # +Mwah, hah, hah! +eval.rb:11: (eval):1: compile error (SyntaxError) +(eval):1: syntax error, unexpected kEND, expecting $end +abc; end; puts "Mwah, hah, hah!"; # +----------------------- +Eval is horribly abused (Richards et al. 2011, See the Eval that men do,), but it is useful at times. +For instance, in JavaScript, it served as an early (but unsafe) version of JSON.parse. +Similar to goto, it is a powerful but dangerous construct, and is often used in places +where the language is missing a key feature. +"A design pattern is the sincerest form of feature request". + +However, it does not tend to show up as often in Ruby. +In part, Ruby has some safer alternatives that are nearly as powerful. +They take blocks rather than expressions. +=end + +# instance_eval -- used for prying open objects to get at their private data. +# This can be handy for things like writing a debugger. +class Person + attr_reader :name + def initialize name + @name = name + end +end + +bob = Person.new "Robert" +puts bob.name +#bob.name = "Bobby" # Error + +bob.instance_eval do + @name = "Bobby" +end +puts bob.name + +# And finally class_eval/module_eval, which serve as an alternate way of opening up a class. +favorite_song = "Streets of Laredo" +class Person + #puts favorite_song # error +end + +Person.class_eval do + puts favorite_song + #def sing + # puts "When #{@name} went out in the #{favorite_song}..." # Will not see favorite_song + #end + define_method "sing" do + puts "When #{@name} went out in the #{favorite_song}..." + end +end + +bob.sing + diff --git a/lab19/eval/taint.rb b/lab19/eval/taint.rb new file mode 100644 index 0000000..8470cfa --- /dev/null +++ b/lab19/eval/taint.rb @@ -0,0 +1,30 @@ +=begin +Update the Record class so that updates with either +a tainted name or a tainted value are ignored. +Do this first by explicitly checking the taint on a field. + +Would this be sufficient if an attacker could control part of the code? +If not, how could the different taint modes be useful? +=end + +class Record + def initialize fields + @fields = fields + end + + def set_property name, value + @fields[name] = value + end + def get_property name + @fields[name] + end +end + +r = Record.new 'fname' => 'Rick', 'lname' => 'Grimes', 'profession' => 'Police Officer' +r.set_property 'profession'.taint, 'Zombie Hunter' +r.set_property 'lname', 'Smith'.taint + +p r.get_property 'profession' +p r.get_property 'lname' + + diff --git a/lab19/method-missing.rb b/lab19/method-missing.rb new file mode 100644 index 0000000..6a0c0ce --- /dev/null +++ b/lab19/method-missing.rb @@ -0,0 +1,21 @@ +class Person + attr_accessor :name + def initialize(name) + @name = name + end + + def make_introduction + puts "Hi, my name is #{@name}. Nice to meet you." + end + + def method_missing(m) + puts "Didn't understand #{m}" + end +end + +alice = Person.new('Alice') + +alice.make_introduction +alice.foo + + diff --git a/lab19/record.rb b/lab19/record.rb new file mode 100644 index 0000000..93c0623 --- /dev/null +++ b/lab19/record.rb @@ -0,0 +1,37 @@ +=begin +Ruby provides a number of hooks that allow a developer +to change the behavior of objects. +The design is similar to JavaScript's Proxies (both are +metaobject protocols), but unlike proxies, +these hooks apply for all objects. + +The most famous of these is method_missing, +based on Smalltalk's doesNotUnderstand:. +=end + +# Consider Ruby on Rails. +# With Rails you refer to a record's fields by their names. +# We will (crudely) simulate that. +class Record + def initialize fields + @fields = fields + end + + def method_missing m, *args + meth_name = m.to_s + if (meth_name.end_with?("=")) then + @fields[meth_name.chop] = args[0] + else + @fields[meth_name] + end + end +end + +r = Record.new ({ 'fname' => 'Rick', 'lname' => 'Grimes', 'profession' => 'Police Officer' }) +puts r.profession +r.profession = 'Zombie hunter' +puts r.profession + +# Ruby has const_missing as well, which works in a similar manner, except that it applies +# to missing class constants + diff --git a/lab19/tree.rb b/lab19/tree.rb new file mode 100644 index 0000000..36566b0 --- /dev/null +++ b/lab19/tree.rb @@ -0,0 +1,35 @@ +class Tree + attr_accessor :value, :left, :right + def initialize(value, left=nil, right=nil) + @value = value + @left = left + @right = right + end +end + +my_tree = Tree.new(42, + Tree.new(3, + Tree.new(1, + Tree.new(7, + Tree.new(22), + Tree.new(123)), + Tree.new(32))), + Tree.new(99, + Tree.new(81))) + +my_tree.each_node do |v| + puts v +end + +arr = [] +my_tree.each_node do |v| + arr.push v +end +p arr + +p "Getting nodes from tree" +p my_tree.left_left +p my_tree.right_left +p my_tree.left_left_right +p my_tree.left_left_left_right + diff --git a/lab19/with-prob.rb b/lab19/with-prob.rb new file mode 100644 index 0000000..fb23f83 --- /dev/null +++ b/lab19/with-prob.rb @@ -0,0 +1,20 @@ +def with_prob (prob) + yield if (Random.rand < prob) +end + +with_prob 0.42 do + puts "There is a 42% chance that this code will print" +end + + + +def foo x + with_prob 0.5 do + puts "Executing with_prob block" + return 0 + end + return x +end + +puts "Foo is #{foo 1}" + diff --git a/lab19/withProb.js b/lab19/withProb.js new file mode 100644 index 0000000..56477ac --- /dev/null +++ b/lab19/withProb.js @@ -0,0 +1,17 @@ +function withProb(prob, f) { + if (Math.random() < prob) { + return f(); + } +} + + +function foo(x) { + withProb(0.5, function() { + console.log("Execution withProb callback"); + return 0; + }); + return x; +} + +console.log("Foo is " + foo(1)); +