Rails Web Service: Convert Parameter to Class Method Call?


#1

I’m trying to write a method for a web service that will find objects
from a user specified table, i.e. find all Widgets added in last 7
days, or find all Whatsits whose name starts with Q.

I could right a separate method for each table calling i.e.
Widget.find() or Whatsit.find(), but what I’d like to do is have a
single interface that lets me call something like
user_specified_table.find() where user_specified_table is a string
equal to either Widget or Whatsit.

How can I do this? Is there an entirely different way of doing this
that I should be looking at?


#2

Craig wrote:

How can I do this? Is there an entirely different way of doing this
that I should be looking at?

method_missing is your best friend.
Oh, and ‘send’, too.

Fast attempt at an example:
#!/usr/local/bin/ruby
class Finder

def method_missing( sym, *args )
if sym.to_s =~ /(\w+)find$/
klass = $1.capitalize
“#{klass}.find”
elsif sym.to_s =~ /(\w+)find_by([a-z
]+)/
klass = $1.capitalize
params = $2
“#{klass}.find_by_#{params}”
else
super
end
end
end

if FILE == $0
require ‘test/unit’
class TC_FOO < Test::Unit::TestCase

 def setup
  @f = Finder.new
end

def test_00
  res = @f.whatsit_find
  assert_equal( "Whatsit.find" , res )
end

def test_01
  res = @f.widget_find_by_name
  assert_equal( "Widget.find_by_name" , res )
end


def test_02
  assert_raise( NoMethodError ){
  @f.widget_fetch_me_coffee
  }
end

end
end

James

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools


#3

Thanks for the quick reply. I’m just starting with both Ruby and Rails,
so this was very helpful. How would you execute the result of the
method_missing call? What should I do to make sure this is secure?


#4

This is very helpful. Thank you very much. Not only have you helped me
with this particular problem, you’ve sent me scurrying after several
new concepts that have me thinking about other problems in new ways.


#5

Craig wrote:

Thanks for the quick reply. I’m just starting with both Ruby and Rails,
so this was very helpful. How would you execute the result of the
method_missing call? What should I do to make sure this is secure?

Once method_missing has teased apart the request, you have few options.

The example just created a string of code, so you could just pass that
to eval(). Not recommend expect perhaps to test some code to see that
things are perhaps working up to that point.

Since you decompose the name of the missing method into a class name and
a request, you can get a reference to the class then try to invoke
request as a class method.

class Finder

def method_missing( sym, *args )
if sym.to_s =~ /(\w+)find$/
klass = $1.capitalize
cls = Object.const_get( klass )
return cls.find
elsif sym.to_s =~ /(\w+)find_by([a-z
]+)/
klass = $1.capitalize
cls = Object.const_get( klass )
params = $2
cls.send( “find_by_#{params}”, args )
else
super
end
end
end

If you prefer to create object instances, then first call ‘new’ on the
class reference:

def method_missing( sym, *args )
if sym.to_s =~ /(\w+)find$/
klass = $1.capitalize
cls = Object.const_get( klass )
obj = cls.new
return obj.find
elsif sym.to_s =~ /(\w+)find_by([a-z
]+)/
klass = $1.capitalize
cls = Object.const_get( klass )
params = $2
obj = cls.new
obj.send( “find_by_#{params}”, args )
else
super
end
end

Note that all sorts of error handling has been omitted here.

You may want to take more precautions in what classes are created and
what methods get invoked. For example you could first check that the
extracted class name is contained in an ‘allowed objects’ list before
instantiation anything.

Hope this helps,

James B.

http://www.ruby-doc.org - Ruby Help & Documentation
http://www.artima.com/rubycs/ - Ruby Code & Style: Writers wanted
http://www.rubystuff.com - The Ruby Store for Ruby Stuff
http://www.jamesbritt.com - Playing with Better Toys
http://www.30secondrule.com - Building Better Tools