avdi / naught

A toolkit for building Null Object classes in Ruby
MIT License
1.05k stars 53 forks source link

Cannot send messages with `#send` to a null object #45

Closed sferik closed 10 years ago

sferik commented 10 years ago
NullObject = Naught.build do |config|
  config.define_explicit_conversions
  def to_path
    "/dev/null"
  end
end

null = NullObject.new
null.to_s                  # => ""
null.send(:to_s)           # => nil
null.__send__(:to_s)       # => ""

null.to_path               # => "/dev/null"
null.send(:to_path)        # => nil
null.__send__(:to_path)    # => "/dev/null"

This may or may not be considered a bug by you, but it caught me by surprise. Null objects inherit from BasicObject, not Object, so you can’t send them a message with #send, only with #__send__.

This does not apply to mimic or impersonate null objects, as they have Object in their ancestry .

This could be fixed in a variety of ways, including:

  1. make null objects inherit from Object
  2. make null object inherit from a custom Naught::BasicObject that defines #send
  3. add a special case for #send to the definition of NullClassBuilder#method_missing that delegates to #__send__

Or you may not consider this to be a bug at all. So, I figured I’d opened it as an issue for discussion rather than start coding one of the above solutions.

avdi commented 10 years ago

I sort of consider this a feature, although not with great strength of conviction. The reasoning is this: #send has always been a problematic name. Lots of objects implement domain-specific #send methods--e.g. Email#send--and someone may want to replace one of those objects with a Null Object and have it Just Work.

To my mind the lack of #public_send is a bigger deal - it's far less likely to conflict with a domain method name, and 90% of the time it's the method the programmer SHOULD have used instead of #send, because it respects privacy.

On Mon, Jan 6, 2014 at 4:27 PM, Erik Michaels-Ober <notifications@github.com

wrote:

NullObject = Naught.build do |config| config.define_explicit_conversions def to_path "/dev/null" endend null = NullObject.newnull.to_s # => ""null.send(:to_s) # => nilnull.send(:to_s) # => "" null.to_path # => "/dev/null"null.send(:to_path) # => nilnull.send(:to_path) # => "/dev/null"

This may or may not be considered a bug by you, but it caught me by surprise. Null objects inherit from BasicObject, not Object, so you can’t send them a message with #send, only with #send.

This does not apply to mimic or impersonate null objects since their ancestors include Object.

This could be fixed in a variety of ways, including:

  1. make null objects inherit from Object
  2. make null object inherit from a custom Naught::BasicObject that defines #send
  3. add a special case for #send to the definition of NullClassBuilder#method_missing that delegates to #send

Or you may not consider this to be a bug at all. So, I figured I’d opened it as an issue for discussion rather than start coding one of the above solutions.

— Reply to this email directly or view it on GitHubhttps://github.com/avdi/naught/issues/45 .

Avdi Grimm http://avdi.org

I only check email twice a day. to reach me sooner, go to http://awayfind.com/avdi

sferik commented 10 years ago

Fair enough.