Injectionless DCI in Ruby

Posted: January 24, 2013 in DCI
Tags: , ,

The last few years I’ve been rather active in the DCI community but never really blogged much about it. This post is not going to be my first post on the main DCI concepts either. I’ve been fairly active on the mailing list object-composition, on stackoverflow.com and not least developing the Marvin compiler. One should think with all that activity I’d have enough information to a series of blog post and potentially an entire book and one would probably be right. However this time around I’ll will stick to describing the use of a Ruby tool I’ve recently created.

The first step to creating this tools was learning Ruby altogether. Until recently I’d only read Ruby but never actually coded anything in Ruby nor read a text on the subject and I couldn’t find a better “hello world” that trying to replicate functionality I’d already coded in a different language.

Inspired by a discussion on SO, where you could be led to believe that DCI in Ruby requires using #extend and that it will always be slow I decided to see how many of the tricks used in the Marvin compiler to change C# into Marvin I could replicate in Ruby. From a functional point of view I’m satisfied with the result

The canonical DCI example (MoneyTransfer) looks like this

class MoneyTransfer < Context
#Create a role called source with two role methods 'withdraw' and 'log'
role :source do
    role_method :withdraw do |amount|
           source.movement(amount)
           source.log "withdrawal #{amount}"
    end
    role_method :log do |message|
      p "role #{message}"
    end
  end


#Create a role called source with two role methods 'deposit'

role :destination do
 role_method :deposit do |amount|
 destination.movement(amount)
 destination.log "deposit #{amount}"
 end
end

interaction :transfer do |amount|
 source.withdraw -amount
 destination.deposit amount
end

def initialize(s,d)
 self.source = s
 self.destination = d
end
end

There's three methods in play here to actually generate the code
  • role -> defines a role, the accessor to the current RolePlayer is defined as private
  • role_method -> defines a role method, though it’s defined as a block, that block is never really executed. Instead it’s transformed into an instance method of the context class
  • interaction -> defines a public instance method. The only “magic” here has to do with bookkeeping in relation to the context stack

There’s absolutely no use of #extend. However there’s is some dynamic changes to classes but these are only performed the first time an object of a given type is playing any role and in the odd case where a role method and an instance method clashes then this is handled on a class level as well.


Being a Ruby novice there might be some subtleties I’ve missed or even big no-nos 🙂 however for the MoneyTransfer I’ve verified that


  • An instance method is chosen even if the object is playing two roles in the same context and the ‘other’ role defines a method with the same name (in the example above log is both a role method for source and on the account object I used for testing)
  • A role method is chosen over an instance method if the current role defines a method that clashes with an instance method
  • The role behavior doesn’t leak. I.e. the object looses the capabilities as soon as it stops playing the role (or when accessed outside the role context (pun not intended)
At present the code can be found as a gist

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s