Upload
alkeshv
View
988
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Ruby - beyond the basics. Covers Blocks, Procs, lambda, Higher Order Functions, Closures, Metaprogramming, Continuations, Symbols.
Citation preview
Advanced Ruby
Beyond the Basics
Blocks{ puts 'Hello' }
do puts 'Hello'end
Blocksdef call_block puts 'Start of method' # you can call the block using the yield keyword yield yield puts 'End of method' end # invokecall_block {puts 'In the block'}
>> Start of methodIn the blockIn the blockEnd of method
Blocks
def call_block_with_params puts 'Start of method' yield 'foo', 'bar' puts 'End of method' end# invokecall_block_with_params{|a,b| puts "#{a} #{b}"}
>> Start of methodfoo barEnd of method
Exercise 1
• For the class ‘Exercise1’– Implement method ‘greet’ to:• Accept an array of names and a block• That joins all the names together with ‘and’
– Implement method ‘invoke’ to:• Call the ‘greet’ method, passing it the names ‘joe’ and
‘fred’• And pass greet a block that adds “Hello” in front of the
names returned from ‘greet’• Should return “Hello joe and fred”
Proc objects
p = Proc.new { puts "Hello" }p.call
l = lambda { puts "Hello" }l.call
Proc vs lambdadef return_test l = lambda { return } l.call puts "Still here!" p = Proc.new { return } p.call puts "You won't see this."end
return_test
>> Still here!
Convert Blocks to Proc objects
def grab_block(&block) block.callend
grab_block {puts "Hello"}
Higher Order Functionsproc = lambda{puts 'Hello'}proc.call
def hello(proc) puts "start of method" proc.call puts "end of method"end# invokehello proc
>> Hello
>> start of methodHelloend of method
Closures
>> a = 1@a = 2original
class Holder def call_block(pr) a = 101 @a = 102 pr.call endendclass Creator def create_block a = 1 @a = 2 lambda do puts "a = #{a}" puts "@a = #@a" puts yield end endendblock = Creator.new.create_block { "original" }Holder.new.call_block block
Metaprogramming
• Add/remove classes dynamically• Add/remove methods dynamically• Change existing methods• Intercept messages to objects
• Useful for DSLs
Adding methods to a classclass Object def greet puts "Hello" endend
obj = Object.newobj.greet
>> Hello
obj2 = Object.newobj2.greet
>> Hello
Adding methods to a class - example
3.hours.from_now
class Fixnum def hours self * 60 * 60 end def from_now Time.now + self endend
Adding a method to an objectobj = Object.new
def obj.greet puts "Hello" end
obj.greet
>> Hello
obj2 = Object.newobj2.greet
>> undefined method `greet' for #<Object:0x2bae594>
Singleton Class
object
Singleton class
class
module
Adding methods to singleton classs = "Hello"
class << s def twice self + " " + self endend
puts s.twice
>> Hello Hello
def s.twice self + " " + selfend
Using modulesmodule M def greet puts "Hello" endend
obj = C.newclass << obj include Mend
obj.greet
obj = C.newobj.extend(M)obj.greet
Class method definitionsclass String class << self def hello "hello" end endend
class << String def hello "hello" endend
def String def self.hello "hello" endend
Class methods and inheritance
class C singleton class of C
class D singleton class of D
extends
lookup pathobject
Exercise 2
• Add a method to the already existing class ‘Account’
• Create file ‘account_transfer_to.rb’ and “re-open” the Account class.
• To run the specs: spec .
Exercise 3
• Add a method to an instance of a class• Edit the file
‘account_instance_transfer_to_spec.rb’• Implement the ‘transfer_to’ method here, for
account1 only.
Method Aliasingclass C def hi puts "hello" endend
class C alias_method :original_hi, :hi def hi puts "greetings“ original_hi endend
obj = C.newobj.hi
>> greetingshello
Exercise 4
• Alias an existing method• implement the ‘number_of_withdrawals’ and
‘number_of_deposits’ methods in a new file: ‘account_auditing.rb’
• Reopen Account class and define @number_of_withdrawals and @number_of_deposits
• Alias original withdraw, deposit and initialize methods, and implement new versions.
Method_missing
class Echo def method_missing method_sym, *args puts "#{method_sym}: #{args.inspect}" endend
Echo.new.yell "Hello", "world!"Echo.new.say "Good", "bye!"
>> yell: ["Hello", "world!"]say: ["Good", "bye!"]
Exercise 5
• Using method_missing• Implement Remember class from scratch• Remember class should accept any method,
and store the method and any parameters in a history array
Dynamically add methods
>> "defining method yell“"yell: [\"Hello\", \"world!\"]“"yell: [\"good\", \"bye\"]"
Dynamically add methodsclass Echo
def method_missing method_sym, *args p "defining method #{method_sym}" self.class.class_eval <<-EOF def #{method_sym.to_s} *args p "#{method_sym}: " + args.inspect end EOF send(method_sym, *args) endend
Echo.new.yell "Hello", "world!"Echo.new.yell "good", "bye">> "defining method yell“
"yell: [\"Hello\", \"world!\"]“"yell: [\"good\", \"bye\"]"
Dynamically add methodsclass Echo
def method_missing method_sym, *args p "defining method #{method_sym}" Echo.class_eval <<-EOF def #{method_sym.to_s} *args p "#{method_sym}: " + args.inspect end EOF send(method_sym, *args) endend
Echo.new.yell "Hello", "world!"Echo.new.yell "good", "bye">> "defining method yell“
"yell: [\"Hello\", \"world!\"]“"yell: [\"good\", \"bye\"]"
Dynamically add instance methods
>> "new_method: [\"blah\"]"
Dynamically add instance methods
>> "new_method: [\"blah\"]"
Exercise 6
• Dynamically add methods to a class at runtime• Use method_missing the first time a method
is called, but define the missing method on the class
• Subsequent calls to that method then won’t have to use method_missing.
Further Reading
• Ruby for Rails book (ch13) – David A. Black• Seeing Metaclasses Clearly• Dwemthy’s Array• Ruby metaprogramming techniques
The End
Extra Stuff
• Continuations• Symbols
Continuationsdef strange callcc {|continuation| return continuation} print "Back in method. "end
print "Before method. "continuation = strange()print "After method. "continuation.call if continuation
Before method. After method. Back in method. After method. RETURN THIS
Symbols
• :this_is_a_symbol• :’This is also a symbol’• Has both integer and string representations• Is immutable• Quicker to lookup than strings• Quicker to compare• No need to construct a string literal when
doing lookups