onrails.org home

Advanced Ruby - Day 3

20090311_avancedruby.jpg

The first two days of the training were really excellent. Great pace, clear explanations and samples, very technical, Chad and Dave really rock at Ruby.

Making Domain-Specific Languages

We start with a discussion on DSL versus an API.

Meat-and-Potato DSLs

Example is Rake. Rake is a dependency oriented programming language.

Eexample from Dhaka (a paraser generator)

for_pattern(“\n”) do
create_token(“newline”)
end

method_missing Trick

require ‘pp’

class CommandListener
def initialize
@commands = []
end

def method_missing(*args)
@commands << args
self
end
end

listener = CommandListener.new

listener.instance_eval do
this object accepts whatever you type and stores it
end
pp listener

outputs:

#<CommandListener:0x80264
 @commands=
  [[:you, CommandListener],
   [:whatever, #<CommandListener:0x80264 ...>],
   [:accepts, #<CommandListener:0x80264 ...>],
   [:object, #<CommandListener:0x80264 ...>],
   [:this, #<CommandListener:0x80264 ...>],
   [:it],
   [:stores, #<CommandListener:0x80264 ...>]]>
listener = CommandListener.new
listener.instance_eval do
this.object.accepts.whatever.commands.and.stores.it
end

pp listener

outputs:

#<CommandListener:0x712b4
 @commands=
  [[:this],
   [:object],
   [:accepts],
   [:whatever],
   [:commands],
   [:and],
   [:stores],
   [:it]]>

Note any keyword can be used with method_missing…E.g. freeze is not a missing method.

Now by using a BlankSlate object we can avoid this issue:

class BlankSlate
instance_methods.each do |method|
undef_method(method) unless method =~ /^__/ || method == ‘instance_eval’
end
end

We can now create a command listener that can use any commands:

require ‘pp’

class CommandListener < BlankSlate
def initialize
@commands = []
end

def method_missing(*args)
@commands << args unless [:inspect, :to_s].include? args.first
self
end

def pretty_print(pp)
pp.output.write @commands.inspect
end

end

listener = CommandListener.new

listener.instance_eval do
You can even type freeze!
end
pp listener

And we get the following output:

[[:freeze!], [:type, #<CommandListener:0x7ee28>], [:even, #<CommandListener:0x7ee28>], [:can, #<CommandListener:0x7ee28>], [:You, #<CommandListener:0x7ee28>]]

On the same principals we just create a morse encoder, and of course Dave had to show off and interface with the speech function of OSX and another version that interfaces with the MIDI controller.

Exotic Control Flow

Creating a loop with continuations

def start_loop
callcc{|c|c }
end

def end_loop©
c.call©
end


i = 0
again = start_loop

puts i
i += 1

end_loop(again) unless i > 5

Moving on…

We voted on what to cover next as we won’t have time to cover every thing…and the winners are:

* Concurrency                  ..................
* Debugging/Profiling          ................
* JRuby                        ......
* Ruby Extras                  ...........
* Distributed Programming      ......................

Distributed Programming

DRb

Marshaling

>> h = {:x => 1}
>> Marshal.dump(h)
=> “\004\b{\006:\006xi\006”
>> Marshal.load(_)
=> {:x=>1}

DRB Server

require ‘drb/drb’
require ‘ostruct’

DRb.start_service(“druby://localhost:4321”, OpenStruct.new)
DRb.thread.join

DRb Client

require ‘drb’
DRB.start_service
o = DRbOject.new_with_url(“druby:’’localhost:4321”)
o.last_accessed = Time.now
o.some_other_arbitrary_method = “Set this on the open struct”

By default Pass-by-value, but can be Pass-by-reference can be enabled by including DRbUndumped.

That’s all Folk! …my hands/brain where running tired during the last hour :-)

That was an incredible 3 days thanks to Chad and Dave. So if you want to dive deeper in Ruby, the Advanced Ruby is the best way to get there!

Enjoy!
Daniel

Fork me on GitHub