Lec07RubyMetaprogramming - Computer Science & Engineering
Download
Report
Transcript Lec07RubyMetaprogramming - Computer Science & Engineering
CSCE 740 Software Engineering
Ruby and the tools
740Tools07RubyMetaprogramming
Topics
Spring 2014
Blocks
Yield
Iterators
Mix-ins
Tools Last Time
Blocks
Iterators
Closures
New Ruby
Yield
Iterators
Duck-Typing
Mix-ins
Next Time: System Modelling
–2–
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
–3–
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
CSCE 740 Spring 2014
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 …
–4–
SaaS book 2012 Fox Patterson
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
–5–
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014
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
–6–
CSCE 740 Spring 2014
http://pastebin.com/K6ev3S7g Splat Args
# using 'keyword style' arguments
def mymethod(required_arg, args={})
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
end
mymethod "foo","bar",:fancy => true # =>
args=["bar",{:fancy=>true}]
"foo"
# => args=[]
–mymethod
7–
CSCE 740 Spring 2014
3.5 All Programming is Metaprogramming
attr_accessor :year
creating code at run-time
–8–
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014
# 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
def from_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
–9–
CSCE 740 Spring 2014
method_missing
http://pastebin.com/G0ztHTTP
class Fixnum
def method_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
– 10 –
CSCE 740 Spring 2014
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.
– 11 –
CSCE 740 Spring 2014
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 {
– 12 –
print "Rah, " }
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
– 13 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
end.sort
# => ['ananab','elppa','elppa','yrrehc']
x.collect { |f| f.include?("e") }
x.any? { |f| f.length > 5 }
A real life example....
– 14 –
http://pastebin.com/Aqgs4mhE
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
CSCE 740 Spring 2014
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
15
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
– 16 –
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 (2014-01-31). Engineering
Software as a Service: An Agile Approach Using Cloud
Computing (Kindle Locations 2594-2595). Strawberry
Canyon LLC. Kindle Edition.
– 17 –
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")
– 18 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
– 19 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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 <=>
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
– 20 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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 }
– 21 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
CSCE 740 Spring 2014
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
22
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
– 23 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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
– 24 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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))
(filter '(lambda (x) (even? x)) mylist))
mylist.select {|x| x.even?}.map {|x| x+2 }
– 25 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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...
http://pastebin.com/T3JhV7Bk
– 26 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
CSCE 740 Spring 2014
http://pastebin.com/0sTEMcdN
<!DOCTYPE html>
<html>
<head>
<title>Report</title>
</head>
<body>
<div id="main">
...user-generated content here...
</div>
</body>
</html>
– 27 –
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014
1 def one_page
2
page = ’’
3
page << make_header()
4
page << "Hello"
5
page << make_footer()
6 end 7 def another_page
8
page = ’’
9
page << make_header()
10
page << "World"
11
page << make_footer()
12 end
– 28 –
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014
http://pastebin.com/TsvTN5ZT
def make_page(contents)
page = ''
page << make_header()
page << contents
page << make_footer()
end
#
def one_page
make_page("Hello")
end
def another_page
make_page("World")
– end
29 –
CSCE 740 Spring 2014
http://pastebin.com/zQPh70NJ
def make_page
page = ''
page << make_header()
page << yield
page << make_footer()
end
def one_page
make_page do
"Hello"
end
end
def another_page
– 30 – …
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014
We can exploit Ruby’s idiom for single-line blocks to
boil this down to: http://pastebin.com/Nqe8MwA5
def make_page
make_header << yield << make_footer
end
def one_page
make_page { "Hello" }
end
def another_page
make_page { "World" }
end
– 31 –
SaaSbook http://pastebin.com/Nqe8MwA5
CSCE 740 Spring 2014
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
– 32 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
CSCE 740 Spring 2014
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
– 33 –
# 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
otherCSCE
library
UCB CS169 Sp 2012 Slides Fox, Patterson and in
Sen
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
http://pastebin.com/zQPh70NJ
– 34 –
UCB CS169 Sp 2012 Slides Fox, Patterson and Sen
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:
– 35 –
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
CSCE 740 Spring 2014
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.
– 36 –
SaaS book 2012 Fox Patterson
CSCE 740 Spring 2014