uninitialized constant while calling worker method in sidekiq - ruby-on-rails

I am using sidekiq gem in rails 3 and I have define a foo_workers.rb in app/workers folder, when I try to call FooWorker.perform_async(#article) in a rails controller named articles I get this error,
uninitialized constant ArticlesController::FooWorker
Can anyone please help me.

Check your pluralization. The file is called foo_workers.rb, which means you're probably defining FooWorkers, not FooWorker. It should be singular. But this is all guessing because you didn't actually post any code.

The autoload mechanism won't pluralize your class names. So the class FooWorker is expected to be defined in a foo_worker.rb file in some of the autoload paths.
If the file name is different (like foo_workers.rb), Rails won't try to load the class from it.

Related

Rails unable to autoload constant from file despite being defined in that file

This is a tricky one to explain. I have a module in another module namespace like so:
# app/models/points/calculator.rb
module Points
module Calculator
def self.included(base)
base.send(:include, CommonMethods)
base.send(:include, "Points::Calculator::#{base}Methods".constantize)
end
end
end
So then in other classes all I need to do is:
class User
include Points::Calculator
end
I've specified this directory in application.rb to be autoloadable...(even though i think rails recurses through models...)
config.autoload_paths += Dir[ Rails.root.join('app', 'models', "points") ]
In development env, everything works fine. When running tests(and production env), I get the following error:
Unable to autoload constant Points::Calculator, expected /Users/pete/work/recognize/app/models/points/calculator.rb to define it (LoadError)
I actually followed the advice here to fix the problem: Stop Rails from unloading a module in development mode by explicitly requiring calculator.rb in application.rb.
However, why is this happening??
I stuck some debug output in ActiveSupport's dependencies.rb file and noticed that this file is being required twice. The first time its required I can see that the constant is indeed loaded.
But the 2nd time its required the constant has been unloaded as far as Rails can tell, but when the actual require is called, ruby returns false because ruby knows its already required it. Then Rails throws the "unable to autoload constant" error because the constant still isn't present and ruby didn't "re-require" the file.
Can anyone shed light on why this might be happening?
Rails augments the constant lookup mechanism of ruby.
Constant lookup in Ruby:
Similar to method missing, a Module#constant-missing is invoked when a reference to a constant fails to be resolved. When we refer to a constant in a given lexical scope, that constant is searched for in:
Each entry in Module.nesting
Each entry in Module.nesting.first.ancestors
Each entry in Object.ancestors if Module.nesting.first is nil or a module.
When we refer to a constant, Ruby first attempts to find it according to this built-in lookup rules.
When ruby fails to find... rails kicks in, and using its own lookup convention and its knowledge about which constants have already been loaded (by ruby), Rails overrides Module#const_missing to load missing constants without the need for explicit require calls by the programmer.
Its own lookup convention?
Contrasting Ruby’s autoload (which requires the location of each autoloaded constant to be specified in advance) rails following a convention that maps constants to file names.
Points::Calculator # =>points/calculator.rb
Now for the constant Points::Calculator, rails searches this file path (ie 'points/calculator.rb') within the autoload paths, defined by the autoload_paths configuration.
In this case, rails searched for file path points/calculator in its autoloaded paths, but fails to find file and hence this error/warning is shown.
This answer is an abstract from this Urbanautomation blog.
Edit:
I wrote a blog about Zeitwerk, the new code reloader in Rails. Check it out at -> https://blog.bigbinary.com/2019/10/08/rails-6-introduces-new-code-loader-called-zeitwerk.html
If someone is having this issue in rails 6 which has zeitwerk autoloader,
Change ruby constant lookup back to classic in your application.rb
# config/application.rb
#...
config.autoloader = :classic
#...
Read more details here Rails Official Guides
Calculator should be a class to be autoloaded correctly
module Points
class Calculator
...
end
end

Rails Module/Folder Naming Convention

I'm having a problem with a module name and the folder structure.
I have a model defined as
module API
module RESTv2
class User
end
end
end
The folder structure looks like
models/api/restv2/user.rb
When trying to access the class, I get an uninitialized constant error. However, if I change the module name to REST and the folder to /rest, I don't get the error.
I assume the problem has to do with the naming of the folder, and I've tried all different combos of /rest_v_2, /rest_v2, /restv_2, etc.
Any suggestions?
Rails uses the 'underscore' method on a module or class name to try and figure out what file to load when it comes across a constant it doesn't know yet. When you run your module through this method, it doesn't seem to give the most intuitive result:
"RESTv2".underscore
# => "res_tv2"
I'm not sure why underscore makes this choice, but I bet renaming your module dir to the above would fix your issue (though I think I'd prefer just renaming it to "RestV2 or RESTV2 so the directory name is sane).
You'll need to configure Rails to autoload in the subdirectories of the app/model directory. Put this in your config/application.rb:
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
Then you should be able to autoload those files.
Also, your likely filename will have to be app/model/api/res_tv2/user.rb, as Rails uses String.underscore to determine the filename. I'd just call it API::V2::User to avoid headaches, unless you have more than one type of API.

Rails 3 trouble with namespaces & custom classes (uninitialized constant)

I have a file in my Rails 3.2.11 project called app/queries/visible_discussions.rb which looks like the following:
class VisibleDiscussions
...
end
I'd like to namespace the query so that I can call it using something like Queries::VisibleDiscussions so I tried to do the following:
module Queries
class VisibleDiscussions
...
end
end
However, I'm getting a uninitialized constant Queries (NameError) when I try to call Queries::VisibleDiscussions from the rails console.
Any ideas?
if you add lib to your autoload_paths then it will respect the namespacing under lib - lib/query/visible_discussions.rb
or create a new dir under app - say src and then nest your code there - app/src/query/visible_discussions.rb
i would use the 3rd style in your post for either of these, i.e.
module Query
class VisibleDiscussions
...
end
end
both of these solutions are annoying to me, there might be a way to tell rails to namespace directories under app, but i have no clue how it would be done
Rails needs to know what directories to load (a part from the defaults). Try:
#config.application.rb
config.autoload_paths += %W(#{config.root}/queries)

Preventing "warning: toplevel constant B referenced by A::B" with namespaced classes in Rails

When storing files in a custom directory (Eg: app/presenters/), how do you ensure that namespaced classes are loaded?
For example, if you have:
app/models/mega_menu.rb
app/presenters/catalog_presenter.rb
app/presenters/mega_menu/catalog_presenter.rb
Rails fails to load MegaMenu::CatalogPresenter:
CatalogPresenter.new
=> #<CatalogPresenter:0x85bca68 #_routes=nil>
MegaMenu::CatalogPresenter.new
(irb):3: warning: toplevel constant CatalogPresenter referenced by MegaMenu::CatalogPresenter
=> #<CatalogPresenter:0x85750a0 #_routes=nil>
I've created a sample Rails 3.2 app that reproduces this problem.
In config/application.rb, the app's configured to load files in app/presenters/.
I solved this issue by using a require statement in an initializer. I don't like it much but I liked the structure and class names of my application, they made sense so an initializer was my best solution. In the initializer try:
require File.join(Rails.root, "app", "presenters", "mega_menu", "catalog_presenter")
require File.join(Rails.root, "app", "presenters", "catalog_presenter")
This problem occurs because autoload relies on const_missing being called which won't happen in your case.
When ruby first encounters a reference to MegaMenu::CatalogPresenter, the mega_menu/catalog_presenter.rb file has not been included. Standard ruby behaviour causes it walks up the namespace tree (figure of speech) and it instead finds the top level reference CatalogPresenter as this HAS been included at this point.
Creating new toplevel constants inside classes raises this error. You want something more like this in catalog_presenter.rb:
class MegaMenu
class MegaMenu::CatalogPresenter
end
end

My first RoR Gem: NameError in MicropostsController#up_vote uninitialized constant VoterLove::Voter::Vote

I'm working on my first RubyGem voter_love. When I install the Gem and use the up_vote method I get this error:
NameError in MicropostsController#up_vote
uninitialized constant VoterLove::Voter::Vote
Do I need to generate an initializer or require the Gem somewhere in my code to initialize the Votes model?
From here, your model is VoterLove::Votes not VoterLove::Voter::Vote.
And a simple advice: simply adopt a normal Rails app architecture and use the Engine power to have everything painlessly included (models, controllers, views...).
You've most likely referred to a class or module that doesn't exist. Most likely, you've forgotten to require a gem or library needed for the code to work, or you've made a typo. Another possibility is that the class you'd like to refer to is in another module. If that's the case, you'll have to refer to it with its full name as in the following code.
#!/usr/bin/env ruby
module MyModule
class MyClass; end
end
c = MyModule::MyClass.new

Resources