Rails 4.1.1: uninitialized constant during initial Rails configuration - ruby-on-rails

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?

Related

Rails 5 autoloading issue in production

I have a hard time understanding an issue with Rails autoloading.
So I have a controller which is located in a folder named api. The constroller is declared as class Api::TestController.
In another folder, I have a file called app/my_folder/service_name/api.rb
In production, I get this error : load_missing_constant': Unable to autoload constant Api, expected /app/my_folder/service_name/api.rb to define it (LoadError)
application.rb has this line :
config.autoload_paths += Dir[Rails.root.join('app', 'my_folder', '{**/}')]
I have no issues in development.
Why is this happening ?
Its happening since the auto load paths are only used for auto loading. In order for your class to be loaded in production you need to augment the eager_load_paths as well:
config.eager_load_paths += Dir[Rails.root.join('app', 'my_folder', '{**/}')]
However this isn't exactly a great practice - any directory in app is automatically used as a root directory. What you're doing is adding every subdirectory of a given directory as root directories.
Using the scope resultion operator (class Api::TestController) for namespace definition should also be avoided as it leads to suprising constant lookup behavior and is the source of autoloading bugs. Instead you should nest the class explicitly and setup an inflection for the acronym:
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections(:en) do |inflect|
inflect.acronym 'API'
end
# every time you use Api as a constant name a kitten dies.
module API
class TestController
puts Module.nesting.inspect # [TestController::API, API]
end
end

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.

How can I add non-existing class to a exiting module in gem in rails

I understand that I can overwrite or to add on to a class that's already existed but I want to add additional class to an existing module(in a gem) so I can keep in constant when I call it. Cuz I though that when rails load up the lib. They should recognize it.
See following example. I would like to add the NotAcceptableHttpResponseError to the same HttpService module
Example
module HttpService (in a gem) only have a decent amounts of exceptions class and I would like to add some custom one for others
config/application.rb
config.autoload_paths += %W(#{config.root}/lib)
In the gem/exceptions.rb
module HttpService
class BadHttpResponseError
xxx
xxx
end
In lib/http_service/exceptions.rb
module HttpService
class NotAcceptableHttpResponseError
xxx
xxx
end
end
The Error
NameError(uninitialized constant HttpServices::NotAcceptableHttpResponseError)
Rails 3 does not autoload lib file
1) add this in config/application.rb:
config.autoload_paths += Dir["#{config.root}/lib/**/"]
2) Name of the folder should be the same as the module name and the name of the file should be same as the name of the class(rails naming convention should be followed)
Rename exceptions.rb to name of the class i.e., not_acceptable_http_response_error.rb
or
add in config/initializers/require.rb
require "#{Rails.root}/lib/http_service/exceptions"
Figured out the Answer. It's actually not ideal to modify module that's in the gem. since you will have to fork the project in the gem file and maintain the version if they are updated

how do I unload & load a class?

I've got a class in /lib that I'm mucking with and testing via the console. I'm making changes to the class (adding a debugger line for instance) and using reload! but the new or removed line is not reflected in the version of the code that the console is running.
Tried these:
config.cache_classes = false
reload!
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/lib/service_processors"]
config.autoload_paths << 'lib'
I'm running a saved script that creates an instance of the class. I tried just loading the class at the top of my script, but that seemed to cause some unintended consequences.
So, how do I completely unload a class and then reload it? I'm thinking the script will force an unload of the class, then load the class via the file name.
Not sure it that is the problem, but in your application.rb you need to add the following line:
config.autoload_paths += %W( #{config.root}/lib )
And secondly, to make sure that classes/modules are found correctly on reload, the naming has to follow Rails conventions. This means that snake-casing a module or class name should give the filename, and different namespaces (or nesting) should be in different folders.
Some examples to make this more clear :)
class SomeClass --> /lib/some_class.rb
class SomeHTTPStuff --> /lib/some_http_stuff.rb
class API::Stuff --> /lib/api/stuff.rb
HTH.
Invalid after Rails tag added
Well, to load a class:
load "lib/class.rb"
To reload:
load "lib/class.rb"
Those settings are irrelevant.

Adding lib to 'config.autoload_paths' in Rails 3 does not autoload my module

I place a file name g.rb in side Rails.root/lib folder
The file content is like this:
module Google
end
Then I add
config.autoload_paths += %W(#{config.root}/lib #{Rails.root}/app/delayed_jobs)
to my Rails.root/config/application.rb
However, when I try to invoke Google from rails console, an exception is thrown. The exception goes away only if I execute require 'google'.
Why? Shouldn't my file is autoloaded and shouldn't I access the module without any extra require statement?
Hmm, I discovered an interesting thing. In order for Rails to auto load my class, the class name should be compliant to the file name and the folder structure.
For example, if I want to have Google module autoloaded, I must placed it inside google.rb, directly under /lib (incase I specify autoload from /lib).
If I want to auto load Google::Docs, then I either place it inside google.rb or google/docs.rb
I had a similar problem with getting my module to run on Heroku. In addition to the autoload naming convention stated by Stephen C, I found out that the module code must be require'd due to a threadsafe assumption made by the Rails' production environment on Heroku (even though threadsafe was commented out in my production.rb configuration file.) As soon as I require'd the module file before calling include on the module, everything started to work.
require 'mymodule'
include Mymodule
Please take a look at this excellent article on the subject of getting Modules to load correctly in Heroku (production).
That's because the point of autoload is not to 'require' everything up front (startup penalty). Classes are loaded as they are needed/referenced. In order to do this, you need some way to know where to look for the class. Otherwise, you would have to load every file in the autoload directory in advance to see what classes are declared. It's a tradeoff, but requiring everything in advance (as marbaq suggests) is not autoloading.
You can use the autoload command as provided by Ruby, which takes two arguments, the module to load (symbolized, i.e. :Google in your case), and the second argument is the filename, which would be g.rb if lib is in your load path ($:). See the Ruby docs for autoload.
Change config.autoload_paths to config.eager_load_paths
(based on Rails issue #6850 and Force reload! from lib directory in rails 3.2 console)
I faced the same problem just now, and my "solution" (or rather workaround) was to manually require every needed file from Rails.root/lib in my application.rb.
require 'lib/message'
require 'lib/store'
require 'lib/vault/vault.rb'
require 'lib/custom_loggers'
module MyApplication
class Application < Rails::Application
My next step would be to categorize the files in module folders as you mention.
i found this solution recently
config/application.rb
module AppName
class Application < Rails::Application
# Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += Dir[Rails.root.join('app', 'models', '{**}')]
config.autoload_paths += Dir[Rails.root.join('app', 'lib', 'extensions')]
end
end
the first config call induces rails to auto-load all sub-directories of the app/models directory
so now i can have /app/models/sub_directory/model.rb auto-loaded
(handy for organising an app with a large code base)
the second config call induces rails to autoload the lib/extensions directory
hope this helps
note: i believe this is rails 3 specific

Resources