-
Notifications
You must be signed in to change notification settings - Fork 21.6k
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
autoload_paths not considering namespace? #8726
Comments
I think it's a conflict because builder it's in some way a reserved word. Rails uses it in the background. Can a team member or a good rails developer confirm this? (Or confirm that I'm saying bullshit) |
Note that the behavior is different on the current master: https://gist.github.com/4445870 |
This problem exists because you are using an ActiveRecord model as a namespace. I created a gist with some experimentation until I saw the root cause.
A possible solution is to use a module as a namespace. For example |
This gist illustrates the situation https://gist.github.com/4446037 |
cc @fxn |
Yeah @senny, I've realized that was the problem :) I submitted this issue because I don't know if this is the intended behavior for this situation. There are a lot of ancestors in this and other cases, and very often they have names that can be meaningful in the app context, and couldn't be used unless you require the classes before, as in If there is a way to get the "right" Let's see what you guys think ;) |
Some of our applications grew pretty big and we used a lot of namespaces. As stated before, I would not suggest to use an AR model as a namespace though. I'd create a module I don't see this as a bug but let's see what @fxn thinks. |
Yep, that is just plain Ruby. Even if you use a qualified constant name, a constant path, the ancestors of the parent class/module are scanned looking for the constant. Since Ruby finds the constant Active Support is not even called. In general it is safer to use bare modules as namespaces, because you inherit less things. But your example can still work if you want by loading notifications/builder with Note that there is no need to put app/services in |
I'd like to reopen this because it's not a Ruby thing. It is a Rails autoload problem. In Ruby, a qualified constant name will always return that constant and not return a different one. module Bar
module Baz
end
end
class Foo
include Bar::Baz
end
class Foo
class Baz
end
end
Foo::Baz # => Foo::Baz (it will never return Bar::Baz) Or if you actually have clashing classes from within the same namespace, ruby will let you know. module Bar
end
class Foo
include Bar
end
class Foo
class Bar # TypeError: wrong argument type Class (expected Module)
end
end I'm unfamiliar with the internals of autoloading/reloading so I can't tell what and how it does it but its very clear it's not a ruby issue nor expected. We've been bitten by this as well:
|
The problem of this issue is the following: let's suppose you have loaded in memory this: module M
X = 1
end
class C
include M
end
class User < C
p X
end If you have a file called x.rb in |
Ok, I see what you mean. Adjusting my own example to actually reproduce the issue: module Bar
module Baz
end
end
class Foo
include Bar
end
Foo::Baz # => Bar::Baz I guess its just another inherent issue of using inheritance/modules. Can this be documented somewhere though? It's time consuming and confusing to deal with problems like these, would be nice to educate people about this. |
Exactly, that's the point. Don't know if there's anything to document here. On one hand a Ruby programmer should know how constants work, and in the case of Active Record you have ActiveRecord::Base.constants.sort
=> [:ACTIONS, :ATTRIBUTE_TYPES_CACHED_BY_DEFAULT, :AbsenceValidator, :AcceptanceValidator,
:AggregateReflection, :AliasTracker, :AssociatedValidator, :Association, :AssociationBuilderExtension,
:AssociationReflection, :AssociationScope, :Attribute, :BeforeTypeCast, :Behavior, :BelongsToAssociation,
:BelongsToPolymorphicAssociation, :Builder, :CALLBACKS, :CALLBACK_FILTER_TYPES,
:CALL_COMPILABLE_REGEXP, :Callback, :CallbackChain, :Callbacks, :ClassMethods, :Clusivity,
:CollectionAssociation, :CollectionProxy, :ConfirmationValidator, :Default, :Dirty, :ExclusionValidator, :FormatValidator,
:HasAndBelongsToManyAssociation, :HasManyAssociation, :HasManyThroughAssociation, :HasOneAssociation,
:HasOneThroughAssociation, :HelperMethods, :InclusionValidator, :IndifferentCoder, :InstanceMethodsOnActivation,
:JoinDependency, :JoinHelper, :LengthValidator, :MacroReflection, :MultiparameterAttribute,
:NAME_COMPILABLE_REGEXP, :Named, :NumericalityValidator, :OrmAdapter, :Preloader, :PresenceValidator,
:PrimaryKey, :Query, :Read, :ScopeRegistry, :Serialization, :Serializer, :SingularAssociation, :ThroughAssociation,
:ThroughReflection, :TimeZoneConversion, :TooManyRecords, :TransactionError, :Type, :UNASSIGNABLE_KEYS,
:UniquenessValidator, :WithValidator, :Write] So you know that It could be argued, though, that precisely because of this the constants you inject in the ancestor chain belong in some sense to the public interface no matter whether their modules implement something internal. Meaning, a new constant may break existing applications. |
Right, but what I'm saying is just to add a notice somewhere in the docs so people can prevent those kind of problems and explaining the issue. If you don't know Rails' internals, it's hard to see what is going on, specially because you don't really know everything that is being included in your models through inheritance, unless you go and check. Anyway, just a thought to help other people to prevent from this kind of surprises. |
👍 this bug has bitten me more than once |
@joaohornburg s/bug/gotcha/ it is not really a bug. |
In
app/services
, I have some classes, asNotification::Finder
andNotification::Builder
.They are placed as
app/services/notification/builder.rb
andapp/services/notification/finder.rb
.There is also the
Notification
class as a model, atapp/models/notification.rb
The autoload_path is configurated as in
config.autoload_paths += %W(#{config.root}/app/services)
When I try to load
Finder
, it works:But when I try the
Builder
, I get a problem with the rails autoloading:It just ignores the namespace I’ve used when the constant name (Builder) has already been defined by other namespace, and gets the
ActiveRecord::Associations::Builder
instead.Is this the expected behavior, or a rails bug?
Going more detailed, the
const_missing
method atactivesupport/dependencies.rb
receives aconst_name ‘Builder’
, andnesting.inspect => ‘nil’
.Curious that when I use constantize, it resolves as expected:
I've created a project just with these classes for testing: https://github.com/rodrigues/autoload_zomg
The question was also opened at SO: http://stackoverflow.com/questions/14143318/autoload-paths-not-aware-of-namespace
The text was updated successfully, but these errors were encountered: