1

I have the following code to pull out and instantiate Rails controllers:

def get_controller(route)
  name = route.requirements[:controller]
  return if name.nil?


  if name.match(/\//)
    controller_name = name.split('/').map(&:camelize).join('::')
  else
    controller_name = name.camelize
  end

  p controller_name: controller_name

  controller = Object.const_get("#{controller_name}Controller")

  p controller: controller
  controller.new
end

some routes are single names - "users", "friends", "neighbors", "politicians", etc...

other routes are nested, such as "admin/pets", "admin/shopping_lists", "admin/users", etc...

The above code works (in that it properly builds and instantiates the controller) in most of the cases mentioned, except for one - in this example, "admin/users"

from the puts statements, I'm getting the following:

{:controller_name=>"Admin::Users"}
{:controller => UsersController}

You'll notice that the namespace Admin is getting cut off. My guess is that since this is only the case for controllers which share a name in multiple namespaces (users and admin/users), it has something do to with Rails autoloading (?). Any idea about what is causing this?

As per the comment from lx00st, I should also point out that I've tried various forms of getting these constants, another attempt was as follows:

sections = name.split('/')
sections.map(&:camelize).inject(Object) do |obj, const|
  const += "Controller" if const == sections.last
  obj.const_get(const)
end

The same problem was encountered with this approach.

dax
  • 10,779
  • 8
  • 51
  • 86
  • 1
    In this case `const_get` looks for a constant in `Object` module. You should end with something that call `Admin.const_get("UsersController")` – lx00st Apr 24 '15 at 14:19
  • I tried that as well - I'll add that to the OP – dax Apr 24 '15 at 14:28
  • And if this called directly from rails console? `Admin.const_get("UsersController")` – lx00st Apr 24 '15 at 14:35
  • and tell your ruby version – lx00st Apr 24 '15 at 15:17
  • No offence to the design approach you have selected... I just wonder how efficient it is (it might be expensive for go through these lookups for every request), as well if the approach itself could handle name-spacing. – Myst Apr 24 '15 at 15:19
  • @Myst, i agree, it's only for testing purposes, so efficiency doesn't matter so much – dax Apr 24 '15 at 15:39

2 Answers2

3

This was solved by an answer from user apneadiving which can be found here

Be aware that there are vicious cases in Rails development mode. In order to gain speed, the strict minimum is loaded. Then Rails looks for classes definitions when needed.

But this sometimes fails big time example, when you have say ::User already loaded, and then look for ::Admin::User. Rails would not look for it, it will think ::User does the trick.

This can be solved using require_dependency statements in your code.

Personally I think this is a bug, not a feature, but...so it goes. It solves the problem.

Community
  • 1
  • 1
dax
  • 10,779
  • 8
  • 51
  • 86
1

First of all, this code is superfluous:

if name.match(/\//)
  controller_name = name.split('/').map(&:camelize).join('::')
else
  controller_name = name.camelize
end

The only string would perfectly handle both cases:

controller_name = name.split('/').map(&:camelize).join('::')

Then, you probably want to handle namespaces properly:

n_k = controller_name.split('::')
klazz = n_k.last

namespace_object = if n_k.length == 1
                     Object
                   else
                     Kernel.const_get(n_k[0..-2].join('::'))
                   end

controller = namespace_object.const_get("#{klazz}Controller")

Hope that helps.

Aleksei Matiushkin
  • 119,336
  • 10
  • 100
  • 160
  • you're right about the top code being superfluous, thank you for that. At the bottom, calling `get_const` on `Kernel` gives me an undefined method error. What do you mean by handling the namespaces properly? – dax Apr 24 '15 at 15:44
  • That did not even what? The code above works for me pretty fine. – Aleksei Matiushkin Apr 25 '15 at 07:21
  • Sorry, I meant that I should have realized that `get_const` is not a method, but since it was the end of the week my brain had stopped working :p – dax Apr 25 '15 at 09:37
  • That code does definitely work, but it doesn't solve my problem - the interpreter is still just ignoring the "Admin" namespace. If I change the name of the namespaced controller - for example, 'AdminUsers' - then it is recognized - but that's going too far in my opinion. What's the point of namespaces if Rails ignores them? – dax Apr 27 '15 at 08:57
  • in fact, this is solved by an answer apneadiving gives [here](http://stackoverflow.com/a/29920332/2128691) – dax Apr 28 '15 at 14:35
  • `require_dependency`, yes. sigh. rails. sigh. – Aleksei Matiushkin Apr 28 '15 at 14:38