360 likes | 486 Views
Dive into the fascinating world of Ruby metaprogramming and functional programming through key concepts such as blocks, yield, iterators, and mix-ins. This guide explores Ruby's dynamically typed nature, its object model, and how to enhance code readability. Learn about closures, duck-typing, and the implications of method calls within Ruby's architecture. With practical examples and clear explanations, this resource is perfect for software engineers looking to deepen their knowledge of Ruby and improve their coding practices in both object-oriented and functional styles.
E N D
Ruby and the tools740Tools07RubyMetaprogramming CSCE 740 Software Engineering • Topics • Blocks • Yield • Iterators • Mix-ins Spring 2014
Tools - • Last Time • Blocks • Iterators • Closures • New Ruby • Yield • Iterators • Duck-Typing • Mix-ins • Next Time: System Modelling
REMEMBER! • a.bmeans: call method b on object a • a is the receiver to which you sendthe method call, assuming a will respond tothat 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
3.2 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
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
long lines multiple lines http://pastebin.com/dFJjugTf • # downcase and split are defined in String class • words = IO.read("file"). • split(/\W+/). • select { |s| s =~ /^[aeiou]/i }. • map { |s| s.downcase }. • uniq. • sort
http://pastebin.com/K6ev3S7g Splat Args • # using 'keyword style' arguments • defmymethod(required_arg, args={}) • do_fancy_stuff if args[:fancy] • end • mymethod "foo",:fancy => true # => args={:fancy => true} • mymethod "foo" # => args={} • # using * (splat) arguments • defmymethod(required_arg, *args) • # args is an array of extra args, maybe empty • end • mymethod "foo","bar",:fancy => true # => args=["bar",{:fancy=>true}] • mymethod "foo" # => args=[]
3.5 All Programming is Metaprogramming • attr_accessor :year • creating code at run-time SaaS book 2012 Fox Patterson
# Note: Time.now returns current time as seconds since epoch • class Fixnum • def seconds ; self ; end • def minutes ; self * 60 ; end • def hours ; self * 60 * 60 ; end • def ago ; Time.now - self ; end • deffrom_now ; Time.now + self ; end • end • Time.now # => Mon Nov 07 10:18:10 -0800 2011 • 5.minutes.ago # => Mon Nov 07 10:13:15 -0800 2011 • 5.minutes - 4.minutes # => 60 • 3.hours.from_now # => Mon Nov 07 13:18:15 -0800 2011
method_missinghttp://pastebin.com/G0ztHTTP • class Fixnum • defmethod_missing(method_id, *args) • name = method_id.to_s • if name =~ /^(second|minute|hour)$/ • self.send(name + 's') • else • super # pass the buck to superclass • end • end • end
3.6 Blocks: Iterators, Functional Idioms, and Closures • Fox, Armando; Patterson, David (2014-01-31). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Location 2514). Strawberry Canyon LLC. Kindle Edition.
Loops—but don’t think of them that way ["apple", "banana", "cherry"].each do |string| puts string end for i in (1..10) do puts i end 1.upto 10 do |num| puts num end 3.times { print "Rah, " } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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| ... endhsh.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
“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 end.sort # => ['ananab','elppa','elppa','yrrehc'] x.collect { |f| f.include?("e") } x.any? { |f| f.length > 5 } • A real life example.... http://pastebin.com/Aqgs4mhE UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
Which string will not appear in the result of: ['banana','anana','naan'].map do |food| food.reverse end.select { |f| f.match /^a/ } naan ☐ ananab ☐ anana ☐ The above code won’t run due to syntax error(s) ☐ UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
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 (2014-01-31). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Locations 2594-2595). Strawberry Canyon LLC. Kindle Edition.
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
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
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 <=> • provides < <= => > == between?for free • 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
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
a = SavingsAccount.new(100) b = SavingsAccount.new(50) c = SavingsAccount.new(75) What’s result of [a,b,c].sort Works, because account balances (numbers) get compared ☐ 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 ☐ UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
Making accounts comparable • Just define<=> and then use the Comparable module to get the other methods • Now, an Account quacks like a numeric http://pastebin.com/itkpaqMh UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
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)) (filter '(lambda (x) (even? x)) mylist)) mylist.select {|x| x.even?}.map {|x| x+2 } UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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... http://pastebin.com/T3JhV7Bk UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
http://pastebin.com/0sTEMcdN • <!DOCTYPE html> • <html> • <head> • <title>Report</title> • </head> • <body> • <div id="main"> • ...user-generated content here... • </div> • </body> • </html> SaaS book 2012 Fox Patterson
1 defone_page • 2 page = ’’ • 3 page << make_header() • 4 page << "Hello" • 5 page << make_footer() • 6 end 7 defanother_page • 8 page = ’’ • 9 page << make_header() • 10 page << "World" • 11 page << make_footer() • 12 end SaaS book 2012 Fox Patterson
http://pastebin.com/TsvTN5ZT • defmake_page(contents) • page = '' • page << make_header() • page << contents • page << make_footer() • end • # • defone_page • make_page("Hello") • end • defanother_page • make_page("World") • end
http://pastebin.com/zQPh70NJ • defmake_page • page = '' • page << make_header() • page << yield • page << make_footer() • end • defone_page • make_page do • "Hello" • end • end • defanother_page • … SaaS book 2012 Fox Patterson
We can exploit Ruby’s idiom for single-line blocks to boil this down to: http://pastebin.com/Nqe8MwA5 • defmake_page • make_header << yield << make_footer • end • defone_page • make_page { "Hello" } • end • defanother_page • make_page { "World" } • end SaaSbook http://pastebin.com/Nqe8MwA5
http://pastebin.com/T3JhV7Bk • class RandomSequence • def initialize(limit,num) • @limit,@num = limit,num • end • def each • @num.times { yield (rand * @limit).floor } • end • end UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
Iterators are just one nifty use of yield # in some other library def before_stuff ...before code... end def after_stuff ...after code... end # in your code def do_everything before_stuff() my_custom_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 end With yield(): expose 1 call in other library UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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 http://pastebin.com/zQPh70NJ UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
3.9 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 (2014-01-31). Engineering Software as a Service: An Agile Approach Using Cloud Computing (Kindle Locations 2811-2812). Strawberry Canyon LLC. Kindle Edition. SaaS book 2012 Fox Patterson