This project is archived and is in readonly mode.
Associations are picking the wrong class
-
Hugo Barauna
+1
-
Andrew White
- Title changed from Associations are picking the wrong class to Associations are picking the wrong class
I can't replicate this in a new 2.3.2 application even if I create the
Admin::Usermodel in a plugin. I can nearly replicate the opposite withAdmin::Account, but what happens is also depends on thestore_full_sti_classsetting along with the loaded states of theUserandAdmin::Usermodels.Historically STI models were stored with a demodulized class name in the type column and the
compute_typemethod was used to restore the namespace when loading the constant. This had the side-effect of making namespaced associated models working as well. However now that the STI class is stored in full thetype_name_with_modulemethod just returns the default class name unmodified and reverts to the standard Rails constant loading mechanism.So what happens here is that when
store_full_sti_classis false thetype_name_with_modulemethod prefixes the name withAdminandAdmin::Useris found successfully. Ifstore_full_sti_classis true and neither model is loaded then anAdmin::Userinstance is returned. If theAdmin::Usermodel is loaded beforehand then either an exception is raised if theUsermodel is not loaded or an instance ofUseris returned if it is loaded.The patch does fix this specific problem but I think the real bug is actually in
AS::Dependencies.load_missing_constant. Rather than raising the exception it should just return the already defined constant as the test name for it seems to indicate that it's there to prevent double loading of constants. Changing that only breaks one test and that's checking to see if the exception is raised.Attached is a patch that makes this change.
-
José Valim
Andrew, I understand your problem but you're solving the other way: why Admin::Account is loading "Admin::User" instead of "User".
My problem is Account loading "Admin::User" instead of "User". I've just tried your patch here and it hasn't fixed it.
Who is returning the wrong class is class_eval. The backtrace would be: reflections call compute_type with "User", compute_type calls type_name_with_module with "User" and returns "User", and then class_eval is called and picks up "Admin::User" instead of "User".
I think this problem is strictly related with namespaces because both "Admin::User" and "User" are loaded before the error occurs (so can I discard this an AS::Dependencies bug?).
"I can't replicate this in a new 2.3.2 application even if I create the Admin::User model in a plugin."
I don't think this is an easy bug to reproduce, as you said depends who is loaded and who is not, but it's an easy one to understand why. If we say to ruby that I want ::User, it will load the right one.
However, your discussion just pointed me that we could solve the problem making type_name_with_module always returns "::User" or "::Admin::User".
But it doesn't change the fact that associations should default to a namespaced class, and thus, I think the patch I propose is still needed.
-
Andrew White
José, I just don't why it should be getting Admin::User instead of User as the constant lookup should Account::User and then Object::User - why would it go looking in the Admin namespace?
-
Andrew White
There is one way I can reproduce it and that's by defining the User constant within Account, e.g:
class Account < ActiveRecord::Base User = Admin::User belongs_to :user endIs it possible that something similar is happening in your code?
-
José Valim
I don't have a clue, debuggers don't go inside class_eval. :) But I agree that it's totally strange.
-
José Valim
Nops.
User == Admin::User #=> falseHave you tried with Admin::User being a module?
-
Andrew White
I just cannot reproduce - I've tried Admin being both a AR model and just a module. I've tried both methods in a plugin and in the main application. I've also tried Admin::User as a module but that didn't make any difference either.
What happens if you tweak the class_eval to be parent.class_eval in compute_type? Also what's the value of
store_full_sti_class -
José Valim
I'm afraid this can't be easily reproduced. The plugin I'm using that showed me this conflict is Typus (http://github.com/fesplugas/typu...>
In this case, Typus::User is a module and it's included in ActiveRecord::Base:
module Typus module User end end ActiveRecord::Base.send :include, Typus::UserHowever I've cut down their code in order to have just the lines above, and the problem couldn't be reproduced.
Anyway, with this bug or not, association are still wrong. It should have a default class name of ::Admin::User if I'm declaring it inside ::Admin::Account, and default class name of ::User if I'm declaring it on ::Account.
-
José Valim
store_full_sti_class is true. Calling parent.class_eval in compute_type works, but in my case the parent is Object.
However, I cannot supply a patch with parent.class_eval because this would make namespaces break, since we would be calling Admin.class_eval(Admin::User).
On the other hand, a possible solution could be calling Object.class_eval(User), which is the same as class_eval(::User). This is likely to don't break anything because type_name_with_module is already responsible to return a namespaced class.
So I propose: Object.class_eval in compute_type AND make associations namespaced by default, but without '::' at the beginning, which looks strange.
Andrew, your patch is still needed?
-
Andrew White
José, I've tracked down your problem with Typus. Typus includes Typus::User in AR::Base so all AR models have a constant User, e.g: Account::User and even User::User is defined. This is what compute_type is finding.
Your patch works around the problem by starting the search one level up, avoiding the match with Account::User. Unfortunately your patch would break one of my apps where I have the following:
class DeliveryMethod < ActiveRecord::Base class PriceBand < ActiveRecord::Base; end has_many :price_bands endIf you modify your patch so that the search starts at the same level then you'll still get the same problem with Account::User matching. The solution lies with Typus having a less common name for its User module - should be a simple fix.
As for my patch - I think it probably does still need to be applied but I'll open a new ticket.
-
José Valim
Andrew, I see you case.
However, I still think that a simple patch should be applied.
At least one that makes Admin::Account when belongs_to User have a default class name of Admin::User (without the '::'). This wouldn't break your application, it wouldn't fix my problem, but I think it's needed.
-
Andrew White
José, that's essentially what happens at the moment - the string "User" is evaluated within the Admin class scope so that if Admin::User is defined it will find it first before looking elsewhere.
-
Andrew White
Sorry, slightly wrong on the last comment. What actually happens is the following lookup:
- Admin::Account::User
- Admin::User
- User
What causes this to fail at the moment is the fact that load_missing_constant is called after not finding Admin::Account::User and the exception is raised if the Admin::User has been previously loaded. This is what my patch fixes.
-
José Valim
I just updated the ticket. Someone from core might close this one soon.
-
Frederick Cheung
- State changed from new to invalid
