Rob Williams on Ruby, Jibberish and English

I was amused to read Rob William's take on a Ruby article in SD Times. Aside from his sarcasm, he scores home with most of the arguments though I beg to differ with some of them. I will highlight some of my opinions and respond for Ruby :)
  1. Our tools do that.!! The article was pointing to how Ruby follows the Unified Access Principle where you only have one interface to the class data members, whether it is a simple storage operation or a complex one. But, as Rob points out, having such support in the language is useless because current tools do that. I bet Bertrand Meyer wouldn't roll on his grave because Ruby is trying to offer UAP even when there are such tools around.
    # initial class
    class Plan
    attr_accessor: owner
    end

    plan = Plan.new
    plan.owner = rob
    plan.owner # => rob

    # we now need to upgrate to a full fledged setter
    # rather than the one dynamically generated for us above
    class Plan
    attr_accessor: owner, assigned
    def owner=(owner)
    @owner = owner
    @assigned = true
    end
    end

    plan = Plan.new
    plan.owner = rob
    plan.owner # => rob
    plan.assinged # => true

  2. Long code is like short code is like medium code! Rob is picking at the author for mentioning that Ruby produces less lines of code. He's arguing that the tool is producing those verbose Java lines for us too!. As if we should accept garbage only and only if it is spit at us by our favourite tool! Why don't we all ditch the use of annotations for Hibernate mapping when our eclipse XML editor does autocompletion for the .hbm files? it is not about too many lines of code, it is about clutter and organization. I long for the day when i used VisualAge for Java, It was really anti clutter! (written in SmallTalk, no less!)
    # neat example on short code
    session.time_out = 48.hours.from_now
  3. Rob rightfully accused the author to have poorly written the testing section and dynamism. The author was seemingly speaking about mock objects and how using them in a dynamically typed system is easier than a statically typed one. In a dynamic setting, identifying a mock object and using it is seamless. Also writing the mock object itself is seamless, no interface or contract of some sort, you only code the methods that you intend to handle and the others are handled by a common method. It's rather interesting to see that almost all the Java guys would praise AOP and tell stories about the wonders that they achieved using AOP (many are actually finding their way around the static nature of Java through AOP)
    class Person
    def can_run?
    # some tedious operation
    end
    def can_jump?
    # some other operation
    end
    end

    # person mockup
    # returns true whenever the method called has the character ?
    class PersonMockup
    def method_missing(method_id)
    method_id.to_s["?"]
    end
    end

    # or for some dynamic magic
    class Roman
    def roman_to_int(str)
    # do conversion here
    end
    def method_missing(method_id)
    roman_to_int(method_id.to_s)
    end
    end

    roman = Roman.new
    roman.V # => 5
    roman.IV # => 4
    roman.VII # => 7
  4. "ActiveRecord is simplistic". Here Rob doesn't justify why he thinks AR is rather simplistic? may be he is commenting on the simple example given by the article author? AR is rather a simple interface for a stunningly powerfull engine that provides you with a wide variety of DB constructs, the has_many and sisters, are actually defined in the AR module and when invoked they add methods and functionality to the invoking class at the class definition time. And due to Ruby's syntax flexibility they fit naturally in the class definition you don't even notice that they are function calls but rather some seamless annotation of some sort (which actually affects the class being declared and adds methods and attributes dynamically to it). Ruby provides you with the ability to add even more of those to AR. By excercising this feature you can build very complex relations between your domain models and still keep your code clean and clutter free.

    class Person < ActiveRecord::Base
    has_many :plans
    has_many :tasks, :through => :plans
    end

    class Plan < ActiveRecord::Base
    has_many :tasks
    belongs_to :owner, :class => "Person"
    end

    class Task < ActiveRecord::Base
    belongs_to :plan
    acts_as_tree #defines parent/child relation ship among tasks
    acts_as_taggable #for folksonomy aware objects (AR plugin)
    end

    #get all completed tasks for rob (involves one hit to the database)
    completed_tasks = rob.tasks.select{|task| task.completed?}

    #get all completed tasks that are parents for other tasks
    completed_prent_tasks = completed_tasks.select{|taks|!task.children.empty?}

  5. On DSLs. There are many ways one can solve a problem, but if you can shape your language around the domain you're attempting at that makes for much clearer code (wich is evident by looking at the above example) DSLs are abundant in Ruby code, mainly because the language has enough metaprogramming constructs and syntax flexibility that promotes such an approach to problems. Here are a few examples on how you can shape your code around your specific problem domain in a way that a domain expert will naturally understand the code.
    # using a dsl suited for representing a workflow
    workflow "default" do
    step "scan"
    step "ocr" do
    when_error "manual"
    end
    step "cleanup"
    end

    workflow "manual" do
    step "correction"
    step "distribute"
    end

    #or a dsl for meal recipes
    recipe "PBJ Sandwich"
    ingredients "two slices of bread",
    "one heaping tablespoon of peanut butter",
    "one teaspoon of jam"
    instructions "spread peanut butter...",
    "spread jam...",
    "place other slice..."
    servings 1
    prep_time "2 minutes"

    #another dsl from rspec
    target.should.equal 7
    target.should.not.equal 5
    target.should.be Fixnum
    target.should.contain 'a'
    target.should.be.empty
    target.should_respond_to :quak #for the love of the duck!

    #or from the poignant guide :D
    class Dragon < Creature
    life 1340 # tough scales
    strength 451 # bristling veins
    charisma 1020 # toothy smile
    weapon 939 # fire breath
    end

  6. camelCaseVariablesLookPrettyNeatAndAreSoSweet , on the other hand underbar_variables_look_terribly_ugly_and_dull . That's what Rob thinks I suppose. I wont comment on Rob's taste, to each his own. I used to think like him, not any more.
    testHasThreeClientsAndOneSupplierAndTwoStores()  //pretty java

    test_has_three_clients_and_one_supplier_and_two_stores #ugly ruby :)

I have to stop before this turns into a Ruby vs. Java thing (a typical my daddy is bigger than your daddy duel) or did it happen already? Anyways, Rob was complaining from the poor quality of the article, which is largely true (I bet he'd complain from the poor quality of my writeup too, but would that stop me?). My issue is that harm was done to Ruby the language in the exchange, that's why I tried to shed some more light on the issues mentioned. I dont hate Java, I just think I had too much coffee ;)

To each his tools, to each his rules

oldmoe

"My opinion is right, though it could possibly be wrong. Your opinion is wrong, though it could possibly be right", Imam Shafey

Comments (2)

I have to say that your Roman example is exactly the kind of Ruby stuff that makes me cringe. It strikes me as the kind of code that appeals to the younger crowd because it is exotic and esoteric. If ever there was an excellent example of Ruby being "quick and dirty" your Roman example is it, except that it's just simply dirty. I'm probably missing something there; perhaps you can provide a more realistic example?

I'm genuinely interested in knowing what's dirty about it?
And what would be a "clean" solution from your point of view?

As for another example, a handy way to query objects in AR is to do something like:

Car.find_by_year_and_brand(year,brand)

the method find_by_xxxxxxxx is not present in the AR module but rather it is handeled in the method_missing and the query is generated from the method name itself. My guess is that you won't swallow that one :) though I find it usefull at times

Another example isdeveloping proxy objects in that matter, which is pretty easy, you trap any call to the proxy object in method_missing and then create the real one and pass it to it, along with the original arguments, a very simple implementation that is highly effective.

Oh and I did mention using method_missing in object mocking!

I believe that you might find other usefull uses of this if you give it a thought, or may be you wont find any and let it pass, just don't cringe :)

salam