Err the Blog Atom Feed Icon
Err the Blog
Rubyisms and Railities
  • “Quickly: returning”
    – Chris on September 19, 2006

    This is a little tune I’ve been humming a lot lately. It’s as handy as array.map(&:property). Really. You ever find yourself doing something like this?

    def change_state(object_id, new_state)
      object = find(object_id)
      object.state = new_state
      object.save
      object
    end
    

    Maybe not, but the pattern’s there: make an object, do something to the object, return the object. Rails’ returning method bundles this all up into joyful syntax:

    def change_state(object_id, new_state)
      returning find(object_id) do |object|
        object.state = new_state
        object.save
      end
    end
    

    You get the idea.

    Try this one on for size (from ActionController):

    def self.view_class
      @view_class ||=
        # create a new class based on the default template class and include helper methods
        returning Class.new(ActionView::Base) do |view_class|
          view_class.send(:include, master_helper_module)
        end
    end
    

    Definitely.

  • Manfred Stienstra, about 4 hours later:

    update_attribute will save you another line.

  • lopex, about 3 hours later:

    This resembles let statement a bit – even javascript is going to support it: http://developer.mozilla.org/en/docs/New_in_JavaScript_1.7#Block_scope_with_let I also tend to write things like: let(v+4,x*5){|a,b| ...whatever} or quite useful Array#let ? ;) |fruit,count| do ..whetaver end

  • Robin, about 1 hour later:

    I’m fairly new to Ruby/Rails, but I’m not sure I follow your first example. How exactly is the returning syntax better than the original? It’s the same amount of lines and seems to be equivalent.

  • josh susser, 28 minutes later:

    returning is cool. It’s the Ruby version of the K combinator!

  • Brian Mitchell, about 3 hours later:

    I’ve used something like this for a long time (in my .irbrc file):

    # I actually use a much different way to patch it into Object that I call mix_eval (safer pollution ;-) )
    # but this is simpler than pasting my whole .irbrc file ;-).
    class Object
      def tap
        yield self if block_given?
        self
      end
    end
    

    The original inspiration for the name “tap” (rather than use k) was from MenTaLguYs blog

  • Chris, about 4 hours later:

    Robin: I would say this is more of a conceptual win than anything. You’re able to wrap up the code which needs to modify the same temporary variable into a block. I think of it as “I want to return this object, but first…”

  • Robin, about 21 hours later:

    Ah, that does make more sense, thank you.

  • Marshall, about 22 hours later:

    It’s the Ruby version of Lisp’s prog1! Which is the same thing as the K combinator! Learning new things is almost as much fun as exclamation points!

  • aadric, 1 day later:

    I must have missed array.map(&:property), can someone give me a link?

  • SusanJ, 1 day later:

    Can you explain this to a poor old Java programmer? Why do you have to save or return the object?

  • Stefan Kaes, 3 days later:

    The K combinator pattern is nice, but you don’t want to use it on a performance critical path. Using local variables is much faster than returning.

    BTW, I wrote the code that you cite from ActionController. It only gets executed once per view class ;-)

  • Simen, 4 days later:

    aadric: when Ruby sees &, it will try to call the to_proc method on something and then turn it into a block. The Symbol#to_proc method uses in Rails is a neat little trick that calls the Kernel method named ‘method’. The Ruby documentation says about ‘method’:

    
    Looks up the named method as a receiver in obj, returning a Method object (or raising NameError). The Method object acts as a closure in obj’s object instance, so instance variables and the value of self remain available.
    
       class Demo
         def initialize(n)
           @iv = n
         end
         def hello()
           "Hello, @iv = #{@iv}"
         end
       end
    
       k = Demo.new(99)
       m = k.method(:hello)
       m.call   #=> "Hello, @iv = 99" 
    
       l = Demo.new('Fred')
       m = l.method("hello")
       m.call   #=> "Hello, @iv = Fred" 
    
    
  • Jonathan Rochkind, about 1 year later:

    I hate to say it, but tricks like “&:property” start to remind me uncharitably of Perl.

  • Thirteen people have commented.
    Chime in.
    Sorry, no more comments :(
This is Err, the weblog of PJ Hyett and Chris Wanstrath.
All original content copyright ©2006-2008 the aforementioned.