Ruby and the tools 740Tools07RubyMetaprogramming Topics Blocks Yield Iterators Mix-ins Spring 2014 CSCE 740 Software Engineering
– 2 – CSCE 740 Spring 2014 Tools - Last Time Blocks Iterators Closures New Ruby Yield Iterators Duck-Typing Mix-ins Next Time: System Modelling
– 3 – CSCE 740 Spring 2014 REMEMBER! a.b means: call method b on object a a is the receiver to which you send the method call, assuming a will respond to that method does not mean: b is an instance variable of a does not mean: a is some kind of data structure that has b as a member 5.class.superclass Understanding this distinction will save you from much grief and confusion UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 4 – CSCE 740 Spring Everything is an Object - revisited “Ruby’s object model descends from Smalltalk, whose design was inspired by ideas in Simula.” Ruby: is dynamically typed is lexically scoped does not support multiple inheritance supports reflection (asking about objects) “Even a class in Ruby is itself an object—it’s an instance of Class, which is a class whose instances are classes (a metaclass).” def class xxx … SaaS book 2012 Fox Patterson
– 5 – CSCE 740 Spring 2014 Poetry Mode revisited To improve readability by removing clutter: omit parentheses, braces as long as parsing remains unambiguous spreading long lines over multiple lines using “ ; ” instead of newline as separator surround “;” with spaces – making meaner clearer attr_accessor :year SaaS book 2012 Fox Patterson
– 6 – CSCE 740 Spring 2014 long lines multiple lines # downcase and split are defined in String class words = IO.read("file"). split(/\W+/). split(/\W+/). select { |s| s =~ /^[aeiou]/i }. select { |s| s =~ /^[aeiou]/i }. map { |s| s.downcase }. map { |s| s.downcase }. uniq. uniq. sort sort
– 7 – CSCE 740 Spring Splat Args Splat Args # using 'keyword style' arguments def mymethod(required_arg, args={}) do_fancy_stuff if args[:fancy] do_fancy_stuff if args[:fancy]end mymethod "foo",:fancy => true # => args={:fancy => true} mymethod "foo" # => args={} # using * (splat) arguments def mymethod(required_arg, *args) # args is an array of extra args, maybe empty # args is an array of extra args, maybe emptyend mymethod "foo","bar",:fancy => true # => args=["bar",{:fancy=>true}] mymethod "foo" # => args=[]
– 8 – CSCE 740 Spring All Programming is Metaprogramming attr_accessor :year creating code at run-time SaaS book 2012 Fox Patterson
– 9 – CSCE 740 Spring 2014 # Note: Time.now returns current time as seconds since epoch class Fixnum def seconds ; self ; end def seconds ; self ; end def minutes ; self * 60 ; end def minutes ; self * 60 ; end def hours ; self * 60 * 60 ; end def hours ; self * 60 * 60 ; end def ago ; Time.now - self ; end def ago ; Time.now - self ; end def from_now ; Time.now + self ; end def from_now ; Time.now + self ; endend Time.now # => Mon Nov 07 10:18: minutes.ago# => Mon Nov 07 10:13: minutes - 4.minutes# => 60 3.hours.from_now# => Mon Nov 07 13:18:
– 10 – CSCE 740 Spring 2014 method_missing class Fixnum def method_missing(method_id, *args) def method_missing(method_id, *args) name = method_id.to_s name = method_id.to_s if name =~ /^(second|minute|hour)$/ if name =~ /^(second|minute|hour)$/ self.send(name + 's') self.send(name + 's') else else super # pass the buck to superclass super # pass the buck to superclass end end end
– 11 – CSCE 740 Spring Blocks: Iterators, Functional Idioms, and Closures Fox, Armando; Patterson, David ( ). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Location 2514). Strawberry Canyon LLC. Kindle Edition.
– 12 – CSCE 740 Spring 2014 Loops—but don’t think of them that way ["apple", "banana", "cherry"].each do |string| puts string puts stringend for i in (1..10) do puts i puts iend 1.upto 10 do |num| puts num puts numend 3.times { print "Rah, " } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 13 – CSCE 740 Spring 2014 If you’re iterating with an index, you’re probably doing it wrong Iterators let objects manage their own traversal (1..10).each do |x|... end (1..10).each { |x|... } 1.upto(10) do |x|... end => range traversal my_array.each do |elt|... end => array traversal hsh.each_key do |key|... end hsh.each_pair do |key,val|... end => hash traversal 10.times {...} # => iterator of arity zero 10.times do... end UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 14 – CSCE 740 Spring 2014 “Expression orientation” x = ['apple','cherry','apple','banana'] x.sort # => ['apple','apple','banana','cherry'] x.uniq.reverse # => ['banana','cherry','apple'] x.reverse! # => modifies x x.map do |fruit| fruit.reverse fruit.reverse end.sort # => ['ananab','elppa','elppa','yrrehc'] # => ['ananab','elppa','elppa','yrrehc'] x.collect { |f| f.include?("e") } x.any? { |f| f.length > 5 } A real life example UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
ananab anana The above code won’t run due to syntax error(s) naan ☐ ☐ ☐ ☐ 15 Which string will not appear in the result of: ['banana','anana','naan'].map do |food| food.reverse end.select { |f| f.match /^a/ } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 16 – CSCE 740 Spring 2014 Collection Operators Method - #Args - Returns a new collection containing... c.map 1 - elements obtained by applying block to each element of c c.select 1 Subset of c for which block evaluates to true c.reject 1 Subset of c obtained by removing elements for which block evaluates to true c.uniq all elements of c with duplicates removed c.reverse elements of c in reverse order c.compact all non-nil elements of c c.flatten elements of c and any of its sub-arrays, recursively flattened to contain only non-array elements
– 17 – CSCE 740 Spring 2014 More Collection Operators c.sort -If sort is called without a block, the elements are sorted according to how they respond to. c.partition c.sort_by c.max c.min Fox, Armando; Patterson, David ( ). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Locations ). Strawberry Canyon LLC. Kindle Edition.
– 18 – CSCE 740 Spring 2014 What is “duck typing”? If it responds to the same methods as a duck...it might as well be a duck More than just overloading; similar to Java Interfaces Example: my_list.sort [5, 4, 3].sort ["dog", "cat", "rat"].sort [:a, :b, :c].sort IO.readlines("my_file") UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 19 – CSCE 740 Spring 2014 Modules A module is a collection of class & instance methods that are not actually a class you can’t instantiate it Some modules are namespaces, similar to Python: Math::sin(Math::PI / 2.0) The more interesting ones let you mix the methods into a class: class A < B ; include MyModule ; end A.foo will search A, then MyModule, then B sort is actually defined in module Enumerable, which is mixed into Array by default UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 20 – CSCE 740 Spring 2014 A Mix-in Is A Contract Example: Enumerable assumes objects of target class respond to each...provides all?, any?, collect, find, include?, inject, map, partition,.... Example: Comparable assumes that objects of target class respond to Example: Comparable assumes that objects of target class respond to provides > == between? for free Enumerable also provides sort, which requires elements of target class (things returned by each ) to respond to Enumerable also provides sort, which requires elements of target class (things returned by each ) to respond to Class of objects doesn’t matter: only methods to which they respond UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 21 – CSCE 740 Spring 2014 Example: sorting a file Sorting a file File.open returns an IO object IO objects respond to each by returning each line as a String So we can say File.open('filename.txt').sort relies on IO#each and String# Which lines of file begin with vowel? File.open('file'). select { |s| s =~ /^[aeiou]/i } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
Doesn’t work, but would work if we passed a comparison method to sort Doesn’t work, but would work if we defined on SavingsAccount Doesn’t work: SavingsAccount isn’t a basic Ruby type so can’t compare them Works, because account balances (numbers) get compared ☐ ☐ ☐ ☐ 22 a = SavingsAccount.new(100) b = SavingsAccount.new(50) c = SavingsAccount.new(75) What’s result of [a,b,c].sort UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 23 – CSCE 740 Spring 2014 Making accounts comparable Just define and then use the Comparable module to get the other methods Now, an Account quacks like a numeric Now, an Account quacks like a numeric UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 24 – CSCE 740 Spring 2014 When Module? When Class? Modules reuse behaviors high-level behaviors that could conceptually apply to many classes Example: Enumerable, Comparable Mechanism: mixin (include Enumerable) Classes reuse implementation subclass reuses/overrides superclass methods Mechanism: inheritance (class A < B) Remarkably often, we will prefer composition over inheritance UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 25 – CSCE 740 Spring 2014 Blocks (anonymous λ) (map '(lambda (x) (+ x 2)) mylist ) mylist.map { |x| x+2 } (filter '(lambda (x) (even? x)) mylist) mylist.select do |x| ; x.even? ; end (map '(lambda (x) (+ x 2)) '(lambda (x) (+ x 2)) (filter '(lambda (x) (even? x)) mylist)) (filter '(lambda (x) (even? x)) mylist)) mylist.select {|x| x.even?}.map {|x| x+2 } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 26 – CSCE 740 Spring 2014 Turning iterators inside-out Java: You hand me each element of that collection in turn. I’ll do some stuff. Then I’ll ask you if there’s any more left.Ruby: Here is some code to apply to every element of the collection. You manage the iteration or data structure traversal. Let’s do an example... UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 27 – CSCE 740 Spring <html> Report Report...user-generated content here......user-generated content here... </html> SaaS book 2012 Fox Patterson
– 28 – CSCE 740 Spring def one_page 1 def one_page 2 page = ’’ 2 page = ’’ 3 page << make_header() 3 page << make_header() 4 page << "Hello" 4 page << "Hello" 5 page << make_footer() 5 page << make_footer() 6 end 7 def another_page 6 end 7 def another_page 8 page = ’’ 8 page = ’’ 9 page << make_header() 10 page << "World" 11 page << make_footer() 12 end SaaS book 2012 Fox Patterson
– 29 – CSCE 740 Spring def make_page(contents) page = '' page = '' page << make_header() page << make_header() page << contents page << contents page << make_footer() page << make_footer()end# def one_page make_page("Hello") make_page("Hello")end def another_page make_page("World") make_page("World")end
– 30 – CSCE 740 Spring def make_page page = '' page = '' page << make_header() page << make_header() page << yield page << yield page << make_footer() page << make_footer()end def one_page make_page do make_page do "Hello" "Hello" end endend def another_page … SaaS book 2012 Fox Patterson
– 31 – CSCE 740 Spring 2014 We can exploit Ruby’s idiom for single-line blocks to boil this down to: def make_page make_header << yield << make_footer make_header << yield << make_footerend def one_page make_page { "Hello" } make_page { "Hello" }end def another_page make_page { "World" } make_page { "World" }end SaaSbook
– 32 – CSCE 740 Spring class RandomSequence def initialize(limit,num) def initialize(limit,num) = limit,num = limit,num end end def each def { yield (rand { yield (rand } end endend UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 33 – CSCE 740 Spring 2014 Iterators are just one nifty use of yield # in some other library def before_stuff...before code......before code...end def after_stuff...after code......after code...end # in your code def do_everything before_stuff() before_stuff() my_custom_stuff() my_custom_stuff() after_stuff() after_stuff()end Without yield(): expose 2 calls in other library # in some other library def around_stuff...before code... yield...after code... end # in your code def do_everything around_stuff do my_custom_stuff() end With yield(): expose 1 call in other library UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 34 – CSCE 740 Spring 2014 Blocks are Closures A closure is the set of all variable bindings you can “see” at a given point in time In Scheme, it’s called an environment Blocks are closures: they carry their environment around with them Result: blocks can help reuse by separating what to do from where & when to do it We’ll see various examples in Rails UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
– 35 – CSCE 740 Spring 2014 Summary of Yield In the body of a method that takes a block as a parameter, yield transfers control to the block and optionally passes it an argument. A block is a closure, its scope is the one that was in effect when the block was defined, Yielding is the general mechanism behind iterators: an iterator is simply a method that traverses some data structure and uses yield to pass one element at a time to the iterator’s receiver. SaaS book 2012 Fox Patterson
– 36 – CSCE 740 Spring Fallacies and Pitfalls Pitfall: Writing Java in Ruby Pitfall: Thinking of symbols and strings as interchangeable. Pitfall: Naming a local variable when you meant a local method. Pitfall: Confusing require with include. Fox, Armando; Patterson, David ( ). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Locations ). Strawberry Canyon LLC. Kindle Edition. SaaS book 2012 Fox Patterson