(Ruby) Corrupted by JavaScript
I have been corrupted by JavaScript. Ruby really annoyed me when I could not just add properties and functions totally willy-nilly to instances. Ruby makes it easy to reopen classes, or even instances, but it is not so obvious how to do so without creating a new scope (and hence losing your closure).
The ~standared mechanism is to do something vicious like this:
foo = "hello"
msg = "woot!"
class<<foo; self; end.send(:attr_accessor, :bar)
class<<foo; self; end.send(:define_method, :woot) { puts msg }
foo.bar = 7
foo.woot # => "woot!"
 Aside from the fact that this is a vicious hack, it relies on the
 fact that send lets you invoke private methods
 (define_method) on the singleton class (more recently
 going by the alias of eigenclass) of foo. This is pretty
 much a bug and a violation of the object. It's like object rape, or
 something.
  A nicer way to do it, to my mind, is to use anonymous modules. The
  really nice part of using anonymous modules is that the
  Module constructor takes a block which is evaluated in
  the context of the new module, letting you legally call
  private methods, like define_method.
moo = "TOOW!"
foo.extend(Module.new { attr_accessor :baz })
foo.extend(Module.new { define_method(:toow) { puts moo }} )
foo.baz = 7
foo.toow # => "TOOW!"
moo = "MOOO!!"
foo.toow # => "MOOO!!"
This has the same effect and no nasty exploitation of the send bug!
Of course, with javascript, it is just...
foo = "woot"
foo.bar = 7
foo.stuff = function(a, b, c) { a * b * c }
I still like ruby more, though ;-)
1 writebacks [/src/ruby] permanent link
Ruby Orchestration Language Fun
After the Silicon Valley Ruby Conference I wanted to hack some at a declarative language for web service orchestration. Tests pass, but the web service bindings don't exist yet =( Right now it can be used as a nice declarative process for... er, ruby?
class TestProcess < Test::Unit::TestCase
  
  def test_example
    p = Processor::process :wombats do
      receive :message
      forward :message, :to => :provisioning, 
                        :save_reply => :provision_response
            
      forward :message, :to => :payroll
      
      if_test( proc { provision_response =~ /example/ } ) do
        transform :message, :with => :some_transformer,
                            :save_in => :transformed_message
        forward :transformed_message, :to => :server_group    
      end
                            
      forward :provision_response, :to => :helpdesk
    end    
The rest of the test case just mocks out the services needed and tests that the things works. Not as exciting but here it is for completeness =)
  
    p.services[:provisioning] = lambda do |process, msg| 
      @provision_msg = msg 
      assert process.expecting_response?
      "address@example.com"
    end
    
    p.services[:payroll] = lambda do |process, msg|
      @payroll_msg = msg
    end
    
    p.services[:server_group] = lambda do |process, msg| 
      @server_group_msg = msg
    end
    
    p.services[:helpdesk] = lambda do |process, msg|
      process.suspend
    end
    
    msg = OpenStruct.new
    
    p.start msg
    
    assert_equal @provision_msg, msg    
    assert_equal @payroll_msg, msg
    assert_equal @server_group_msg, "address@example.com"
    assert_equal p.transformed_msg, "address@example.com"
    assert p.suspended?
    
    p.resume "back from helpdesk"
    
    assert p.completed?
  end
end
For the most part I am kind of happy with it. The nasty wart of
if_test( proc { provision_response =~ /example/ } ) do
  transform :message, :with => :some_transformer,
                      :save_in => :transformed_message
  forward :transformed_message, :to => :server_group    
end
  annoys me. It's needed to lazily evaluate the construct, using
  a real if there evaluates it at the wrong time. Astute
  code readers will also notice I am not testing the transform. Haven't
  done that yet -- will probably remove it as a first class concept
  if I do anything more with the code, a transform is just a local
  service, and services are all wrapped in ruby methods, so instead
  of importing the service, just provide the transform and voila, your
  toast is burnt.
Wish ruby continuations were migratable/serializable.