simonmorley
simonmorley

Reputation: 2804

How can I dry this Rails Controller Action further

Our application uses a number of environments so we can experiment with settings without breaking things. In a typical controller action, I have something like this:

def some_action    
  ...
  if @foo.development_mode == 'Production'           
    @settings = SomeHelper::Production.lan(bar)
  elsif @foo.development_mode == 'Beta'
    @settings = SomeHelper::Beta.lan(nas)
  elsif @foo.development_mode == 'Experimental'
    @settings = SomeHelper::Experimental.lan(nas)
  end
  ...
 end

Since we have dozens of these, I figured I could try and dry things up with something like this:

 @settings = "SomeHelper::#{@foo.development_mode}.lan(bar)"

Which obviously doesn't work - I just get:

 "NasHelper::Production.lan(bar)"

How can I reduce this down or do I have to stick with what I've got??

Upvotes: 1

Views: 91

Answers (2)

DRobinson
DRobinson

Reputation: 4471

If your concern is that you're ending up with a String rather than the object, you can use String.constantize (Rails only, with standard Ruby you'd have to implement this; it uses Object.const_get(String))

Another option would be .const_get (e.g. Object.const_get(x) where x is your string), you it doesn't, on its own, nest correctly, so you would have to split at "::", etc.

Also, there's the option of using eval to evaluate the String. But note: eval should be used with great care (it's powerful), or not at all.

Edit:

This means that instead of:

 @settings = "SomeHelper::#{@foo.development_mode}.lan(bar)"

You could run:

 @settings = "SomeHelper::#{@foo.development_mode}".constantize.lan(bar)

Useful Sources:

http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-constantize

http://www.ruby-forum.com/topic/183112

http://blog.grayproductions.net/articles/eval_isnt_quite_pure_evil

Upvotes: 1

Tom Harrison
Tom Harrison

Reputation: 14018

In the first case, @settings receives the result of the method SomeHelper::Production.lan(bar); in the second, @settings just gets a string. You could use the send method of Object linked here to fire the method, or eval, but this wouldn't be my first choice.

It looks like you might be reinventing a wheel -- Rails already has the concept of "environments" pretty well wired into everything -- they are defined in app/config/environments. You set the environment when you launch the server, and can test like Rails.env.production?. To create new environments, just copy the existing environment file of the one closest to the new one, e.g. copy production.rb to beta.rb and edit as necessary, then test Rails.env.beta?, for example.

But this still leaves you testing which one all over the place. You can add to the config hash (e.g. config.some_helper.lan = 'bar'), which value you can assign to @settings directly. You have to make sure there's either a default or it's defined in all environments, but I think this is probably the right approach ... not knowing exactly what you aim to accomplish.

Upvotes: 0

Related Questions