Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Unfortunately try illustrates the exact problem with open classes so many non-Rubyists are afraid of: collisions.

try was originally defined as:

  class Object
    def try(method)
      send method if respond_to? method
    end
  end
  
In Rails it is defined as:

  class Object
    alias_method :try, :__send__
  end

  class NilClass 
    def try(*args)
      nil
    end
  end
The following code will exhibit different behavior depending on which version of try you're using:

  [].try :upcase
For example:

  # original try
  >> [].try :upcase
  => nil  

  # rails try
  >> [].try :upcase
  NoMethodError: undefined method `upcase' for []:Array
If Rails uses try internally, and a Rails plugin you've loaded depends on the original version of try, what now? They both behave differently and, presumably, code using them depends on their specific behavior.

Also don't forget the dozen or so other versions of try people have added to their own plugins and apps...



Articulated perfectly! Any time I'm working on a Rails project, and run into odd behaviours, the first thing I'll do is check to see if I'm actually using a Ruby or a Rails method. Most often than not, I'm expecting a "Ruby" way of doing things, but find out I'm actually using the "Rails" version, which does something completely different. If I run into any in the next few hours, I'll post them here :)


It's not really a Ruby vs Rails way of doing things. The Rails implementation is arguably more in line with the standard Ruby behavior since it throws a NoMethodError for everything other than Nil objects.


I would argue that, no, there isn't "standard Ruby behavior" in this instance. It's just a method.


True, they are all just methods, but it's basically just an altered version of Object#send, and the current Rails implementation only significantly differs from it in the specific situation it's intended for.

I realize there are a range of preferences regarding ways to tackle the problem this is intended to solve, and I'm relatively agnostic about the overall issue, but if the implementation is basically a #send that guards against nil, then it would be unexpected for it to deviate remarkably from #send when dealing with non-nil objects.


Another difference: the original version doesn't take multiple arguments, but the new one does.


This was supposed to be a really simple example. One could always write the code such that try only gets defined if there's no existing definition.


Again: you are using two libraries (Rails and plugin X) that depend on their own versions of try, which they include.

One of the libraries is going to break.


Indeed. It's probably wise to use namespaced internal methods inside of libraries or frameworks. However, Rails has historically pioneered new language features that eventually make their way into the language itself (Object#tap, Enumerable#group_by, etc. etc.).

In Rails 3, we've made it reasonable to pull in just small pieces of the ActiveSupport library, making it more likely that users will reuse functionality from there rather than reinvent the wheel.

Of course, it's not perfect.


Am I right in thinking that once a piece of ActiveSupport is pulled in anywhere in the codebase it will affect ALL Ruby objects, rather than having its effect scoped to just the module that included it?


Yep. You'd be modifying the String class itself, not just Strings that find their way into some file.


Well... yes. You're right. If you're depending on one behavior rather than the other, then totally. I guess I was thinking about it more in terms of two different implementations of the same thing. "If we don't have a quicksort defined yet, make one."


This is basically how ActiveSupport's "activesupport/ruby/shim" functionality works. It adds pure-ruby versions of features added in Ruby 1.9 only if they aren't already defined.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: