0

I'm trying to write a method that will both initialize (.new(...) and call a .run method on a class found from a string that will replace lots of duplicate code. This is the old, working method (there are lots of them similar):

def facebook
  @share = params[:share_document]
  ::Documents::SendToFacebook.new(@document, @share).run
end

The new "meta-" method looks like this:

def send_to_provider
  provider = params[:provider]
  @share = params[:share_document] || nil
  klass = "send_to_#{provider}".classify
  p = class_send(klass, :new, @document, @share)
  return true if p.run
end

And the class_send (inspired by this) looks like this:

def class_send(class_name, method, *args)
  return nil unless Object.const_defined?(class_name)
  c = Object.const_get(class_name)
  c.send(method, *args)
  c
end

I can't seem to be able to access the c object from the send_to_provider method, i.e., either p.run can't be found of the class isn't returned properly with c. I have to call :new instead of :run in the send_to_provider method to initialize the object.

How can I both initialize and call :run on the object from the send_to_provider method?

EDIT

The current code gives me this error:

Completed 500 Internal Server Error in 574.9ms

NoMethodError (undefined method `run' for nil:NilClass):
  app/controllers/documents_controller.rb:98:in `send_to_provider'
Community
  • 1
  • 1
t56k
  • 6,769
  • 9
  • 52
  • 115

2 Answers2

3

You're sending :new to the class and then ignoring the result, which seems wrong. And then you're returning the class and trying to call :run on it, which also seems wrong.

Do you mean to write instead:

def class_send(class_name, method, *args)
  return nil unless Object.const_defined?(class_name)
  c = Object.const_get(class_name)
  c.send(method, *args)
end

That doesn't explain the error message you're reporting, although I'm not sure anything does: if the value returned by the original method is nil (as implied by the error message), then we should have seen the send fail (because nil doesn't respond to :new).

Todd Agulnick
  • 1,945
  • 11
  • 10
  • I get what you're saying. I removed the `c` returning line, same result though. I guess why I'm getting `nil` is a different question. – t56k Nov 25 '14 at 22:33
  • 1
    You should try printing out the return value to see what's going on: `c.send(method, *args).tap {|object| puts object }`. Maybe your class' initializer is failing for some reason. – Todd Agulnick Nov 25 '14 at 22:36
  • Hmm. No output. I guess it's not being initialised correctly, right? Can I even `.send(:new)` to initialize a class? – t56k Nov 25 '14 at 23:18
  • Eugh, **namespacing** error, ha. Works with `c = ::Documents.const_get(klass).new(@document, @share)` – t56k Nov 26 '14 at 00:44
  • 1
    Ha! The thought about namepspace passed through my head when I first looked at your question, but then disappeared when I noticed the other problem. Good catch. – Todd Agulnick Nov 26 '14 at 07:17
1

@Todd Agulnick's answer was helpful, but it was a namespacing issue that caused my particular problem.

This fixed the problem for me:

c = ::Documents.const_get(klass).new(@document, @share)

Pretty simple.

t56k
  • 6,769
  • 9
  • 52
  • 115