01 Jan 2011, 23:48
Generic-user-small

Filippo Diotalevi (4 posts)

Ciao Paolo, first of all congrats for the book. I’m really enjoying studying it.

I have some problems with the ‘A Better DSL’ code though. Either I’m losing my mind (not impossible, since the book is so dense at times :) or the code of blocks/monitor_framework/redflag.rb and the code of blocks/monitor_final/redflag.rb are really doing different things as it seems from my experiments.

My test is simple: I just create a new file called ‘test2_event.rb’ containing, f.i., the following code

#   code starts here
event "the lake is dry" do
  @lake_level < 10
end

setup do
  puts "Setting up lake"
  @lake_level = 5
end
#  code ends here

and I copy the file in both blocks/monitor_framework and blocks/monitor_final

After that, I execute the redflag.rb file in both the cases.

The system out in the monitor_framework example is the following

$ ruby redflag.rb 
Setting up lake
ALERT: the lake is dry
Setting up sky
Setting up mountains
ALERT: the sky is falling
Setting up sky
Setting up mountains
ALERT: it's getting closer

The system out in the monitor_final is the following

$ ruby redflag.rb 
Setting up lake
ALERT: the lake is dry
Setting up lake
Setting up sky
Setting up mountains
ALERT: the sky is falling
Setting up lake
Setting up sky
Setting up mountains
ALERT: the lake is dry
Setting up lake
Setting up sky
Setting up mountains
ALERT: it's getting closer

The behavior is different, and that’s in fact what I was expecting reading the code.

The monitor_framework resets @events and @setups for each file execution.

The monitor_final has unique copies of setups and events that are shared among all event file execution. As a result, if I have many event files, the last one will run all the previously defined events and setups.

Am I correct? Or am I missing something important?

Thanks!

Filippo

01 Jan 2011, 23:57
Generic-user-small

Filippo Diotalevi (4 posts)

Attached a revised monitor_final/redflag.rb file that works for me. I added a reset_events method and invoked before loading a file.

#---
# Excerpted from "Metaprogramming Ruby",
# published by The Pragmatic Bookshelf.
# Copyrights apply to this code. It may not be used to create training material, 
# courses, books, articles, and the like. Contact us if you are in doubt.
# We make no guarantees that this code is fit for any purpose. 
# Visit http://www.pragmaticprogrammer.com/titles/ppmetr for more book information.
#---
lambda {
  setups = []
  events = {}
  
  Kernel.send :define_method, :reset_events do 
    setups = []
    events = {}
  end

  Kernel.send :define_method, :event do |name, &block|
    events[name] = block
  end

  Kernel.send :define_method, :setup do |&block|
    setups << block
  end

  Kernel.send :define_method, :each_event do |&block|
    events.each_pair do |name, event|
      block.call name, event
    end
  end

  Kernel.send :define_method, :each_setup do |&block|
    setups.each do |setup|
      block.call setup
    end
  end
}.call

Dir.glob('*events.rb').each do |file|
  reset_events  
  load file
  each_event do |name, event|
    env = Object.new
    each_setup do |setup|
      env.instance_eval &setup
    end
    puts "ALERT: #{name}" if env.instance_eval &event
  end
end
09 Jan 2011, 11:22
Photo_17_pragsmall

Paolo Perrotta (49 posts)

Ciao, Filippo! Bonus points for noticing this problem (nobody did for one year, apparently). Also, extra kudos for making it easy to reproduce. If we meet in person, I owe you a beer. :) I added a report for this on the errata page (http://www.pragprog.com/titles/ppmetr/errata). If we ever get to a second edition of the book (or even a third printing), then I’ll fix it.

  You must be logged in to comment