Proc To Send

31 Mar 2008

Ruby provides a handy shortcut for simple procs by letting you use a symbol:

    [1, 2, 3].map {|number| number.odd? }
    # => [true, false, true]
    [1, 2, 3].map(&:odd?)
    # => [true, false, true]

Despite a performance hit that occurs from converting the symbol to a proc it’s really useful and I use it in lots of production code when I only have to iterate through a loop a few times.

Today I was iterating through a few properties of an object and unconsciously used the inverse of the Symbol-to-Proc trick:

    [ :name,
      :type,
      :amount_in_cents, 
      :created_at,
      lambda {|record| record.class.superclass }
    ].map {|attribute| @item.send(attribute) }

Rather than specifying a symbol to perform a proc operation I felt like specifying a proc itself and just passing that into send. Surely that would get executed, right? Err… no. I know passing an eval-able string (e.g. @item.send(‘class.superclass’) ) doesn’t work, so I figured I’d throw that lambda in there to calculate the name of @item’s superclass. No dice. But there’s a pretty simple workaround:

    module ProcToSend
      def self.included(target)
        target.class_eval do
          def send_with_proc_to_symbol(*args)
            if args.first.is_a?(Proc)
              args.shift.call(self, *args)
            else
              send_without_proc_to_symbol *args
            end
          end
          alias_method_chain :send, :proc_to_symbol
        end
      end
    end

    Module.send :include, ProcToSend
    Object.send :include, ProcToSend

Now this works:

    Item.send Proc.new {|x| x.name }
    # => 'Item'

But, like all hacks, I need to go refactor away the need for this.

  • Ted said: It took me about ten minutes and some doodling in irb to figure out WTF you are doing here, but I finally got it, and it's pretty slick :-)
  • Daniel Fischer said: All I have to say is that you're a badass. Except one more thing... you going to Railsconf this year? I'll come say hi again.
  • Jack Danger said: @dfish: Yeah, I'll be there. Though I'm thinking of selling my conference ticket and using it all for beer money at CabooseConf.

Please if you found this post helpful or have questions.