Subclass models in subfolders are not autoloaded - ruby-on-rails

I have two levels of inheritance with some of my models: (this is all in /app/models)
A.rb
/A/B1.rb
/A/B1/C1.rb
/A/B1/C2.rb
/A/B1/C3.rb
/A/B2.rb
/A/B2/C1.rb
/A/B2/C2.rb
/A/B2/C3.rb
...
A.rb is a simple:
class A
def self.inherited(subclass)
puts "New subclass: #{subclass}"
end
# some methods
end
B1.rb is:
class A::B1 < A
# some methods
end
C1.rb is:
class A::B1::C1 < A::B1
# some methods
end
You can interpolate what the rest of the models look like.
When I load rails console, I see this:
New subclass: A::B1
New subclass: A::B1::C1
New subclass: A::B2
Notice that A::B1::C2, A::B1::C3, and none of the A::B2 subclasses are inherited! Why is that?
It gets even weirder. In rails console, I can then do this:
irb(main)> A::B1::C2
New subclass: A::B1::C2
A::B1::C2 < A::B1
irb(main)> A::B2::C1
New subclass: A::B2::C1
A::B2::C1 < A::B2
So I can type all the subclasses that weren't inherited, and then suddenly it fires the puts message. I think this proves the issue is with the autoloader, rather than my code.
This is happening in my development environment with Ruby 1.9. and Rails 3.2.
Here are my config.autoload_paths statements:
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
Thanks in advance!

You'll need to include the various models in your config.autoload_paths option:
#config/application.rb
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
This makes use of globbing - basically adding any files within a particular directory to your path. We use the above code for single-level directories (app/models/directory/1.rb); if you wanted to use multiple levels, you'll have to indicate them like this:
Dir[Rails.root.join('app', 'models', '{*}', '{**}')]

I wish I had a better answer but to Sergio's point, my subclasses are never directly mentioned so they aren't autoloaded.
I literally had to just write them out at the top of my class file:
B1.rb:
class A1::B1
A1::B1::C1 # simply mentioning the class autoloads it
A1::B2::C2
...
def foo
...
end
end

This may be too late to help you, but I ran into the same issue, so hopefully this answer will help others.
If you are using the Spring gem, your application is actually running in the background. So simply restarting won't re-run the initializers, and won't add those directories to your autoload path.
You want to run bin/spring stop so that when you restart your app the initializers are run.

Related

How to Service Objects in Subfolders

I have many service objects in my service folder. I'm using Rails 4:
->services
a_gen.rb
b_gen.rb
...
a_pro.rb
b_pro.rb
...
I would like my folder structure to be something like
->services
->gen
a_gen.rb
b_gen.rb
...
->pro
a_pro.rb
b_pro.rb
...
I tried just making the folders and putting the objects there, but Rails complains about not able to find it. Do I have to tell rails to look there?
Update:
I am getting this error:
NameError - uninitialized constant ExampleModelName::APro
Even with the path loaded. Once it is in the subfolder.. Rails is looking into the wrong place.
You can do that in two ways.
If you want rails to autoload them without you making any modifications to the config.autoload_paths, define your classes per the folder structure as follows:
module Gen
class ServiceA
end
end
If you don't want to make any changes to your class definition, then you could add those sub folders to the config.autoload_paths as follows in your config/application.rb
module YourApplication
class Application < Rails::Application
config.autoload_paths += [
"#{Rails.root}/app/services/gen",
"#{Rails.root}/app/services/prod"
]
end
end
for a recursive solution
# application.rb
config.autoload_paths += Dir["#{config.root}/app/services/**/"]
Yes, you do. In config/application.rb, you can add the subfolders to the config.autoload_paths collection.

Rails 4.1.1: uninitialized constant during initial Rails configuration

I'd like to add a microapp for errors to be processed (file I placed inside the lib folder).
module MyApp
class Application < Rails::Application
config.autoload_paths += %W( #{config.root}/lib )
config.exceptions_app = FooApp.new(Rails.public_path)
end
end
But Rails raises an unitialized constant (NameError) during it's initialization. Recently I found a similar post and tried every solution from there, but got no result. What actually I've tried:
Name a class and a filename according to convention (even tried to simplify to a single word - class Foo, filename lib/foo.rb).
Use config.eager_load_paths += %W( #{config.root}/lib ) rather than config.autoload_paths, same effect. :(
Create an initializer file and load a class with require: require "#{Rails.root}/lib/foo"
It doesn't work - seems initializers are performed after the initial Rails configuration.
Move the file into app/misc but it doesn't help.
Put a class inside a module with the same name, rename a class while it's still in the module - no effect.
The only working solution I found - is to require a file right inside the Rails configuration block, but... it's a freaky solution. Probably there still exists an idiomatic one?

How can I add autoload_paths in my Rails4 Engine?

Usually I add the following in config/application.rb to add autload_paths:
config.autoload_paths += Dir[Rails.root.join('app', 'poros', '{**}')]
How can I achieve the same in an engine?
It seems to work when I just use the same code in application.rb in the host app, however I think it's ugly that the code is not in the engine and needs to be added to the host app to make things work.
The only solution I found to add the load path through the engine is by adding this to lib/engine/engine.rb:
config.to_prepare do
Dir.glob(Rails.root + "../../app/poros/**/*.rb").each do |c|
require_dependency(c)
end
end
However there seems to be something fundamentally wrong with this as this leads to problems when I'm doing console reloads (e.g. it tells me that constants are already defined or that concerns can't execute the include block twice)
What is the right way to do this in the engine itself? (can't believe this is so hard/uncommon, I have really googled a lot but I can't find a solution)
According to the Rails::Engine documentation, you can add autoload paths in your Railtie like this:
class MyEngine < Rails::Engine
# Add a load path for this specific Engine
config.autoload_paths << File.expand_path("../lib/some/path", __FILE__)
initializer "my_engine.add_middleware" do |app|
app.middleware.use MyEngine::Middleware
end
end
If poros is a subdirectory of app, you do not need add it again.
All subdirectories of app in the application and engines present at boot time. For example, app/controllers. They do not need to be the default ones, any custom directories like app/workers belong automatically to autoload_paths.

Auto-loading lib files in Rails 4

I use the following line in an initializer to autoload code in my /lib directory during development:
config/initializers/custom.rb:
RELOAD_LIBS = Dir[Rails.root + 'lib/**/*.rb'] if Rails.env.development?
(from Rails 3 Quicktip: Auto reload lib folders in development mode)
It works great, but it's too inefficient to use in production- Instead of loading libs on each request, I just want to load them on start up. The same blog has another article describing how to do this:
config/application.rb:
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, when I switch to that, even in development, I get NoMethodErrors when trying to use the lib functions.
Example of one of my lib files:
lib/extensions.rb:
Time.class_eval do
def self.milli_stamp
Time.now.strftime('%Y%m%d%H%M%S%L').to_i
end
end
Calling Time.milli_stamp will throw NoMethodError
I realize others have answered similar questions on SO but they all seem to deal with naming conventions and other issues that I didn't to have to worry about before- My lib classes already worked for per-request loading, I just want to change it to per-startup loading. What's the right way to do this?
I think this may solve your problem:
in config/application.rb:
config.autoload_paths << Rails.root.join('lib')
and keep the right naming convention in lib.
in lib/foo.rb:
class Foo
end
in lib/foo/bar.rb:
class Foo::Bar
end
if you really wanna do some monkey patches in file like lib/extensions.rb, you may manually require it:
in config/initializers/require.rb:
require "#{Rails.root}/lib/extensions"
P.S.
Rails 3 Autoload Modules/Classes by Bill Harding.
And to understand what does Rails exactly do about auto-loading?
read Rails autoloading — how it works, and when it doesn't by Simon Coffey.
Though this does not directly answer the question, but I think it is a good alternative to avoid the question altogether.
To avoid all the autoload_paths or eager_load_paths hassle, create a "lib" or a "misc" directory under "app" directory. Place codes as you would normally do in there, and Rails will load files just like how it will load (and reload) model files.
This might help someone like me that finds this answer when searching for solutions to how Rails handles the class loading ... I found that I had to define a module whose name matched my filename appropriately, rather than just defining a class:
In file lib/development_mail_interceptor.rb (Yes, I'm using code from a Railscast :))
module DevelopmentMailInterceptor
class DevelopmentMailInterceptor
def self.delivering_email(message)
message.subject = "intercepted for: #{message.to} #{message.subject}"
message.to = "myemail#mydomain.org"
end
end
end
works, but it doesn't load if I hadn't put the class inside a module.
Use config.to_prepare to load you monkey patches/extensions for every request in development mode.
config.to_prepare do |action_dispatcher|
# More importantly, will run upon every request in development, but only once (during boot-up) in production and test.
Rails.logger.info "\n--- Loading extensions for #{self.class} "
Dir.glob("#{Rails.root}/lib/extensions/**/*.rb").sort.each do |entry|
Rails.logger.info "Loading extension(s): #{entry}"
require_dependency "#{entry}"
end
Rails.logger.info "--- Loaded extensions for #{self.class}\n"
end

What is the best way to load files from the lib folder that add methods to ~existing~ classes in Rails 3?

I am using config.autoload_paths in a way very similar to this related question to load classes from the lib directory in a Rails 3 project.
Specifically, I've added these lines to the config/application.rb file:
config.autoload_paths += %W(#{config.root}/lib)
config.autoload_paths += Dir["#{config.root}/lib/**/"]
However, this method is not working for me for existing classes. When I add a file like lib/extensions/string.rb:
class String
def foo
puts "foo"
end
end
I get an undefined method 'foo' for "":Stringerror. Through various searches I've got the sense that this problem has to do with the lazy loading of these files. I tried using config.eager_load_paths but was not able to get that to work.
I'm doing exactly what you are describing in my application, and the only difference is that I also have an initializer called extensions.rb with the following code:
Dir.glob('lib/extensions/*').each { |f| require f }

Resources