Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change const_get to constantize to fix undefined method `default_scoped' #967

Merged
merged 1 commit into from
Sep 22, 2017

Conversation

joshua5201
Copy link
Contributor

@joshua5201 joshua5201 commented Sep 1, 2017

Found a strange problem when my namespace and model name are the same
(e.g. Order::Order)
config/routes

resources :order do
  resources :orders
end

When restart / modify source and the rails got reloaded, accessing /admin/order/orders and I get:

undefined method `default_scoped' for Order:Module

No problem if I get back to /admin and click the navigator link in the left.

And I found that in rails console

Object.const_get('Order::Order') 
=> Order
"Order::Order".constantize 
=> Order::Order

using constantize, I can get the correct model class name.

p.s. my model sources

app/models/order.rb

module Order
  def self.table_name_prefix
    'order_'
  end
end

app/models/order/order.rb

class Order::Order < ApplicationRecord
end

@joshua5201
Copy link
Contributor Author

Don't know if the problem is related to rails' module and class loading. I tried in plain ruby with same class and module relationships, and there was no problem.

@pablobm
Copy link
Collaborator

pablobm commented Sep 21, 2017

So this is what I think is going on. I got to this conclusion after reading https://stackoverflow.com/questions/38086328/how-does-rubys-objectconst-get-actually-work

  1. When you load the console, models are not loaded yet. Rails hasn't required them because it instead relies on autoloading.

  2. Due to the way Object.const_get is implemented by Ruby, the call Object.const_get('Order::Order') is equivalent to Object.const_get('Order').const_get('Order').

  3. When the first of these two const_get runs, Ruby gets to the point where a name is not defined, and Rails takes over, autoloading Order.

  4. When the second const_get runs, it doesn't find an Order inside Order (it's not autoloaded yet). Before complaining about an undefined name, it goes up to the parent, Object, and asks again. There it finds an Order, which is the parent order. Autoloading of Order::Order never takes place.

ActiveSupport::Inflector#constantize uses a different algorithm. Internally it uses const_get, but it makes additional checks to avoid its weird behaviour. The documentation says:

The name is assumed to be the one of a top-level constant, no matter whether it starts with “::” or not.

This can sound confusing until you realise how const_get works internally.

@joshua5201's fix looks good to me, although my personal preference would be for using ActiveSupport::Inflector.constantize directly rather than an extension method. Eg:

ActiveSupport::Inflector.constantize(resource_class_name)

On the other hand, it's worth noting that Administrate doesn't support namespaced models at the moment. Other dangers may lay ahead.

@joshua5201
Copy link
Contributor Author

@pablobm Thanks for your reply. I've changed to the implementation as you suggested
I think the current administrate support namespaced model in this pull request ?

@pablobm
Copy link
Collaborator

pablobm commented Sep 22, 2017

@joshua5201 I stand corrected, namespaces are supported now. Sorry!

@pablobm pablobm merged commit 446fa9e into thoughtbot:master Sep 22, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants