Pivotal Labs

The Law of Demeter is a Piffle

edit Posted by Nick Kallen on Wednesday May 07, 2008 at 09:15PM

One of The Blabs' most controversial articles was Lovely Demeter, Meter Maid, in which Pivotal and Thoughtworks battle over which Agile consultancy has the better understanding of the Law of Demeter, and which has better hair and music taste (seriously).

I have never found this "law" very persuasive.

  • The bizarre, culturally loaded analogy about a paperboy and a wallet says nothing insightful about encapsulation boundaries as they arise in real software systems.
  • The blogosphere's endless scholastic hermeneutics of the law's 4 allowances for message sending is a masturbatory philosophical enterprise with nothing relevant to real-world software.
  • The Mockist's insistence on easy mockability is of dubious merit--build better mocking frameworks!
  • And the few practical, real merits that arise in from following the Law of Demeter are better arrived at using other techniques, such as "Tell, Don't Ask".

Here Are Two Examples, one where I violate the Law of Demeter, and another where I don't.

I wrote this code recently, in flagrant violation of the Law:

cookies[:store_id] = @login.store.id

Suppose @login is not an ActiveRecord object, it does not automatically have a #store_id method. Should I create a delegator for this?

class Login
  def store_id
    store.id
  end
end

This is pretty silly. The store_id is not an attribute of the login; rather it's an attribute of the store, and the store is an attribute of the login. The delegator is needless code cruft to replace a dot with an underscore, it smells of the endless boilerplate Java code of my youth. Demeter be damned.

On the other hand, here is a refactoring I did, incidentally complying with the law of Demeter:

Here is the original, Demeter-violating code:

def find_attribute_given_name(name)
  attributes.detect { |a| a.name_or_alias == name }
end

The call to == here is the violation of Demeter. I later replaced this with:

attributes.detect { |a| a.named?(name) }

The latter complies with the "law". And it's much better code. But was I lead to the improvement to this by Demeter? No, I was lead to it by a better understanding of the encapsulation boundaries of the object (#name_or_alias became private) and by a desire to have my code be more terse and clear. a.named?(name) is the most terse explanation of the intended computation that I can think of.

Demeter be damned.

Ruby Pearls vol. 1 - The Splat

edit Posted by Nick Kallen on Wednesday April 23, 2008 at 05:03AM

Over the next week or so I'll be sharing Ruby idioms and flourishes that I quite like. Today I'd I'll show a few tiny uses of splat! that make me tremble with delight.

Splat! - For Beginners

Splat! is the star (*) operator, typically used in Ruby for defining methods that take an unlimited number of arguments:

def sprintf(string, *args)
end

It can also be used to convert an array to the multiple-argument form when invoking a function:

some_ints = [1,2,3]
sprintf("%i %i %i", *some_ints)

Splat! - For Wizards

Array to Hash Conversion

The best use of splat! for invoking a infinite-arity functions I've ever seen is the recipe for converting an array to a hash. Suppose you have an array of pairs:

array = [[key_1, value_1], [key_2, value_2], ... [key_n, value_n]]

You would like to produce from it the hash: {key1 => value1 ... } You could inject down the array, everybody loves inject, but there is a better way:

Hash[*array.flatten]

Amazing right? This relies on the the fact that the Hash class implements the [] (brackets) operator and behaves thusly:

Hash[key1, value1, ...] = { key1 => value1, ... }

Heads or tails?

Splat! can be used for more than just method definition and invocation. My personal favorite use is destructuring assignment. I read this in Active Record's source code recently:

  def sanitize_sql_array(ary)
    statement, *values = ary
    ...
  end

This is invoked when you do something like User.find(:all, :conditions => ['first_name = ? and last_name = ?', 'nick', 'kallen']). Splat! is used here is to get the head and tail of the conditions array. Of course, you could use always use shift, but the functional style used here is quite beautiful. Consider another example:

first, second, *rest = ary

One final trivium (#to_splat aka #to_ary)

You can actually customize the behavior of the splat operator. In Ruby 1.8, implement #to_ary and in 1.9 it's #to_splat. For example

class Foo
  def to_ary
    [1,2,3]
  end
end

a, *b = Foo.new
a # => 1
b # => [2,3]

This also works for method invocation:

some_method(*Foo.new) == some_method(1,2,3)

When I first learned this at RubyConf I thought this was mind-blowing. I have since never used it.

Ninja Patching jQuery

edit Posted by Nick Kallen on Tuesday April 01, 2008 at 11:23PM

Jonathan and I love jQuery's extended psuedo-selectors:

  • :input - Matches all input, textarea, select and button elements.
  • :text - Matches all input elements of type text.
  • :password - Matches all input elements of type password.
  • :hidden - Matches all elements that are hidden, or input elements of type * "hidden".
  • :visible - Matches all elements that are visible.
  • and so on

These aren't actually part of the CSS spec, but they're incredibly useful and can be chained:

$(':input:visible') // => finds all visible inputs

We wanted to customize the behaviors of :text and :visible:

  • We wanted :text to return both <input type="text"> AND <textarea>
  • We wanted :visible to return elements that aren't directly display:none or visibility:hidden, nor are their parents display:none or visibility:hidden

So, we decided to customize this behavior:

jQuery.extend(jQuery.expr[":"], {
  text    : "(a.tagName=='INPUT' && a.type=='text') || (a.tagName=='TEXTAREA')",
  visible : '"hidden"!=a.type && jQuery.css(a,"display")!="none" && jQuery.css(a,"visibility")!="hidden" && (jQuery(a).parent(":hidden").size() == 0)',
  hidden  : 'document != a && ("hidden"==a.type || jQuery.css(a,"display")=="none" || jQuery.css(a,"visibility")=="hidden" || (jQuery(a).parent(":hidden").size() > 0))'
});

So how would you like to ninja-patch jQuery's custom pseudo-selectors?

Making Ruby Look Like Smalltalk Haskell Erlang Ruby

edit Posted by Nick Kallen on Sunday December 09, 2007 at 04:49AM

As Seen on TV

Inspired by Haskell

Did you ever want to write Ruby Code like:

x = 1
increment(x).by(6)

Now you can:

def increment(variable)
  chain do
    by do |delta|
      variable + delta
    end
  end
end

This is an OO version of a technique called Currying:

g = 'hello world'.index_of('o')
h = g.starting_at(6)

Inspired by Smalltalk:

'hello world' indexOf: $o startingAt: 6

Let's do this in Ruby:

class String
  def index_of(substring)
    chain do
      starting_at do |starting_at|
        ...
      end
    end
  end
end

Now, in Ruby:

'hello world'.index_of('o').starting_at(6)

Inspired by Erlang

Here is pseudo-code for an interesting iteration pattern. If the Actor receives 'lock' it will not respond to any messages until it receives 'unlock':

loop(X) ->
  receive
    'incr' -> loop(X+1)
    'lock' ->
      receive
        'unlock' ->
          loop(X);
      end
  end.

The Ruby equivalent:

def loop(x)
  puts x # added puts just to see what's going on
  chain do
    incr do
      loop(x+1)
    end
    lock do
      unlock do
        loop(x)
      end
    end
  end
end

Try this:

loop(1).incr.incr.incr => prints 1, 2, 3, then 4

Now, the finale: We can respond to incr any number of times till we're locked; then, we respond to no messages other than unlock; once we've received unlock we proceed as before.

loop(1).incr.lock.incr => prints 1, 2, then raises an exception.
loop(1).incr.lock.unlock.incr => prints 1, 2, then 3

How does this work?

The call to chain do ... end creates a new Chain object with the block passed in to the constructor. Chain is kind of "blank slate": all methods inherited from Object are undefined so that any messages it receives go through method missing. The block the Chain is instantiated with is instance-eval'd in the chain's context, and all method invocations go through method missing (because of the blank slate). Method missing has two cases. It either dynamically defines a method returning a new link in the Chain (in the case of nested chaining), or it delegates the method back to the object that constructed the chain in the first place. Let's consider examples of these two cases.

Case 1, dynamically defining a new method:

def foo
  chain do
    a do # define a method named :a on the Chain.
      1
    end
  end
end

foo.a => 1

Case 2, delegating the method back the the creator of the Chain:

def bar
  1
end

def foo
  chain do
    a do
      bar # invokes the bar defined above
    end
  end
end

foo.a => 1

Nested chaining is just a variation on Case 1:

def foo
  chain do
    a do
      b do # create a nested Chain (i.e., a Link)
        1
      end
    end
  end
end

foo.a.b => 1

The only gotcha is knowing whether a method invoked with a block belongs to the object that created the chain or is a nested chain:

def b(&block)
end

def foo
  chain do
    a do
      b do # is this the above b, or a nested Chain?
        ... 
      end
    end
  end
end

We prioritize the #b defined on the parent object, rather than created a nested chain (I feel this is more intuitive).

Here is the source code:

require 'rubygems'
require 'active_support'

class Chain
  instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|^instance_exec$)/ }
  delegate :define_method, :respond_to, :to => :__caller
  attr_accessor :__caller

  def __has_links?
    @__has_links
  end

  def initialize(*args, &block)
    if block_given?
      self.__caller = eval("self", block.binding)
      instance_exec *args, &block
    end
  end

  def method_missing(method, *args, &block)
    if block_given? && !__caller.respond_to?(method)
      @__has_links = true
      metaclass.module_eval do
        define_method method do |*args|
          __link(*args, &block)
        end
      end
    else
      __caller.send(method, *args, &block)
    end
  end

  private
  def __link(*args, &block)
    link = Chain.new
    link.__caller = __caller
    result = link.instance_exec(*args, &block)
    link.__has_links?? link : result
  end

  def metaclass
    class << self
      self
    end
  end
end

def chain(&block)
  Chain.new &block
end

Bush Violates Standup Rules

edit Posted by Alex Chaffee on Sunday November 04, 2007 at 08:29PM

Courtesy of Steve C and Alex Tabarrok

There is no &quot;W&quot; in &quot;team&quot;

There is no "W" in "team"

Ruby quiz du jour answer

edit Posted by Felix Morio on Thursday September 06, 2007 at 10:15PM

Josh got it right.

1.0/0

indeed resolves to "Infinity". Pretty cool!

-Felix

Ruby quiz du jour

edit Posted by Felix Morio on Wednesday September 05, 2007 at 07:24PM

Question:

In Ruby, what does the following represent?

-1.0/0...0

Post your guesses as comments. Answer tomorrow. (No fair using irb.)

  • Felix and Alex and Koung

Rails Conference Links

edit Posted by Alex Chaffee on Monday May 21, 2007 at 08:30PM

(Blabbers who were at the conference, feel free to add your links to this post.)

Beer Night PDX

edit Posted by Alex Chaffee on Saturday May 19, 2007 at 10:59PM

front back

Extra Action

edit Posted by Alex Chaffee on Saturday May 19, 2007 at 10:32PM

It was fate. A crew of Pivots in Portland on the same weekend as the Extra Action Marching Band We just had to seize the opportunity...

Will they be making an appearance at our Beer Night tonight? You never know...

Other articles: