I have a file containing a helper class something like this:
app/classes/myfile.rb
Module mymodule
class myclass
# blah blah
end
end
I want to use this class in a controller, so I wrote something like this:
require 'myfile'
class MyController < ApplicationController
include mymodule
def index
mymodule::myclass.new
end
end
The route for the controller is defined like this:
match 'mycontroller', :to => 'mycontroller#index'
Now for the strange behaviour I'm facing. It works perfectly fine on the first run after the server starts. But when I refresh the page or hit the URL again, I get the following error.
Routing Error
uninitialized constant MyController::mymodule
I cannot make out anything out of the error, nor can I understand why it does not work from the second hit onward only. What's happening?
Generally speaking, Rails likes to see files containing:
module MyModule
named my_module.rb
Modules are generally capitalized
Also, it thinks that MyModule is scoped under the MyController class, which it is not. You could try
include ::MyModule
to access it from the top-level scope.
I also don't know if your load paths include your classes directory, so it is probably not autoloading the myfile.rb file in the first place.
I changed require 'myfile' to load 'myfile.rb' and it now works fine. I don't know if I solved the problem though. I don't know what is happening. Can someone enlighten me?
Related
I have question about naming conventions and autoloading.
I want to have a presenter ItemPresenter in app/presenters/items/item_presenter.rb
My understanding was that I can just create that file like this:
module Items
class ItemPresenter
end
end
But when I do this and try to call the presenter as Items::ItemPresenter I get uninitialized constant error:
uninitialized constant Items::ItemPresenter
def show
#presenter = Items::ItemPresenter.new # this is the highlighted line of my Controller
EDIT: Rails, Custom Folders and Namespaces is not duplicate because it's about different dir structure jobs/smth.rb while I am trying to implement presenters/items/item_presenter.rb (1 more level)
EDIT2: neither it works from rails console: NameError: uninitialized constant Items::ItemPresenter
EDIT2: I tried doing this as suggested:
module Presenters
module Items
class ItemPresenter
def test
"hello"
end
end
end
end
And #presenter = Presenters::Items::ItemPresenter.new in my controller:
uninitialized constant TrialsController::Presenters
It seems like Rails do not see that directory at all.
EDIT3: Created a sample app https://github.com/dontlookforme/test_app
EDIT4: Figured it out. I screwed up the file name (see the answer I've posted)
I found the answer but it's necessary to see #user1556912's sample app (link in the original question) to see what happened.
The problem is that the filename is items_presenter.rb (plural) but the class name is ItemPresenter (singular).
As I pointed out in a comment on #Anthony E's answer, Rails will autoload everything in the /app dir, so it's not necessary explicitly to tell Rails about these files. However, along with namespaces matching dir hierarchies, the names of the classes must also match the names of the files exactly. In this case, I was able to get the class to load in the rails console by renaming items_presenter.rb to item_presenter.rb.
Going back to #Anthony E's answer, though, I do agree that the Items:: namespace seems superfluous here. I would just do app/presenters/item_presenter.rb.
app/presenters/ is the conventional path to store presenters. In fact, you can probably go without folder nesting for items:
app/presenters/item_presenter.rb
You'll need to update the module path accordingly:
module Presenters
class ItemPresenter
def test
"hello"
end
end
end
Then, you can tell Rails to automatically load this file in your application.rb:
config.autoload_paths << '#{config.root}/app/presenters'
Ugh. The thing I was doing wrong is the file name.
I named the preseter file items_presenter.rb but the class had singular Item in the name ItemPresenter.
Fixed that and everything started working.
Thanks for the help guys!
I have a very odd error I cannot wrap my head around.
Basically, I have this class in my lib folder:
# lib/api/amazon.rb
module API
class Amazon
...
end
end
When I want to use it somewhere, I require it:
require 'api/amazon'
API::Amazon.do_stuff
This works initially but after a while it breaks and raises NameError: uninitialized constant API::Amazon. When I debug this and try to require the file again when the error is raised, it returns false, indicating that the file was already loaded. I can also see it in $" (this list of loaded files). Why can I then not access API::Amazon?
Note: I added "API" as an acronym to ActiveSupport::Inflector which is why I don't have to use "Api":
# config/initializers/inflections.rb
ActiveSupport::Inflector.inflections do |inflect|
inflect.acronym 'API'
end
EDIT:
I tried ::API::Amazon.do_stuff as well, same result.
I write some code aimed to get same result as yours, maybe it can give some clue.
trytemp.rb:
module API
class Amazon
def hello
puts "API::Amazon initially works well"
$stdout.flush
end
end
end
s = API::Amazon.new
s.hello
p API.constants
API = Module.new
p API.constants # Here you can see constant Amazon disappers from module API
s = API::Amazon.new
s.hello
It initially works well, then get same error,"uninitialized constant API::Amazon (NameError)":
$ ruby trytemp.rb
API::Amazon initially works well
[:Amazon]
trytemp.rb:15: warning: already initialized constant API
[]
trytemp.rb:19:in `<main>': uninitialized constant API::Amazon (NameError)
EDIT:
I though I had found the answer but the same error just occurred again... :(
END EDIT
It seems that I found the answer, with the help of #uncutstone.
Turns out I had not only used the API namespace for API::Amazon but also for some controllers, like so:
# app/controllers/api/v1/accounts_controller.rb
class API::V1::AccountsController < APIController
...
end
My theory is that one of these controllers was reloaded automatically at some point in time and re-initialized (and therefore cleared out) the API module/namespace.
Thus API::Amazon wasn't available after that, but rerequireing lib/api/amazon.rb didn't help because it had already been required and therefore wasn't loaded again.
I changed the controllers to look like this:
# app/controllers/api/v1/accounts_controller.rb
module API
class V1::AccountsController < APIController
...
end
end
and now it seems to work fine.
My application code is structured as shown below in Rails 3.2. If I go into the Rails console and enter Foo::Bar::FooBar it will return this warning:
warning: toplevel constant FooBar referenced by Foo::Bar::FooBar
Application code and files they are located in:
# app/models/foo/bar/foo_bar.rb
module Foo
class Bar
class FooBar
end
end
end
# app/models/foo/bar.rb
module Foo
class Bar
end
end
# app/models/foo_bar.rb
class FooBar
end
My autoload paths have not been changed from the Rails defaults.
One way I've been able to fix the issue has been to add the following code to Foo::Bar::FooBar. However, it feels dirty and I was wondering if there was a configuration option or some other thing I'm doing wrong that would fix the issue.
# app/models/foo/bar/foo_bar.rb
module Foo
# This line of code removes the warning and makes class methods execute
# on the Foo::Bar::FooBar class instead of the FooBar class.
class Bar; end
class Bar
class FooBar
end
end
end
The basic problem is that you're reusing the same name for different classes, in an overlapping scope. You might be able to do something clever to make Ruby resolve the constant name in the way you want, but this kind of "solution" is fundamentally brittle. (What if a new version of Ruby makes a tiny change to how constants are looked up, or you move to another Ruby implementation?)
Why don't you just namespace the top-level FooBar model class within a Module? (You'll have to move the file into a subdirectory with the same name as the Module.)
I am using a plugin in Rails, and I call its methods without problems:
plugin_module::class_inside_module.method_a(...)
I want to re-open the class_inside_module and add a new method, I tried in many different ways. I can't figure out why in this way doesn't work:
class plugin_module::class_inside_module
def new_method
puts 'new method'
end
end
I get the error: uninitialized constant plugin_module, but how is possible if I can call without problem plugin_module::class_inside_module.any_methods ?
Do you know why I get that error ? why "uninitialized constant" ? (it is a class declaration :-O )
Do you have any ideas how I can add a new methods in a class inside a module (that is part of a plugin) ?
Thank you,
Alessandro
If you have written your class and module-names like you did, so plugin_module instead of PluginModule this is against ruby/rails standards, and rails will not be able to automatically find the class and module.
If you write something like
module MyModule
class MyClass
end
end
Rails will expect this file to be located in lib\my_module\my_class.
But this can always easily be overwritten by explicitly doing a require.
So in your case, when you write
module plugin_module::class_inside_module
Rails will not know where to find the module plugin_module.
This way of writing only works if module plugin_module is previously defined (and loaded).
So either add the correct require, or rename your modules to standard rails naming, or write it as follows:
module plugin_module
class class_inside_module
This way will also work, because now the order no longer matters.
If the module is not known yet, this will define the module as well.
Either you are re-opening the class, or you define it first (and the actual definition will actually reopen it).
Hope this helps.
Have you tried reopening the module that's wrapping the class, rather than relying on ::?
module plugin_module
class class_inside_module
def new_method
puts 'new_method'
end
end
end
By the way, you know that the proper name for modules and classes is use CamelCase with a capital first letter?
module PluginModule
class ClassInsideModule
def new_method
puts 'new_method'
end
end
end
I am creating a rails3 application and I want to create a class that handles string formatting, so I made a class called FormatUtilites.rb in the lib directory but whenever I try calling it from somewhere else in my app I get this error:
ActionView::Template::Error (uninitialized constant ActionView::CompiledTemplates::FormatUtilities)
So it thinks its a constant and not a class method, which is how it is defined. Any ideas?
class FormatUtilities
def self.slugify(name)
name.downcase.gsub(/\s|\W|\D/, "")
end
end
Thanks!
Turns out rails3 stop autoloading the lib directory. I have no idea why they did it, but they did. Just needed to add it to the autoload in the application.rb
thanks anyways!
Classes are constants in Ruby, besides also being classes. Probably you just need to do "require format_utilities"
You need to add:
# in config/application.rb
config.autoload_paths = %W(#{config.root}/lib
The name of your file should be format_utilities.rb for autoload to work.
In your particular case i would use a different aproach. Instead of creating a class with static functions i would create a module named FormattingHelper in app/helpers/formatting_helper.rb like this.
class FormattingHelper
def slugify(name)
name.downcase.gsub(/\s|\W|\D/, "")
end
end
Then in ApplcationController or in a specific controller i would add:
class ApplicationController < ActionController::Base
helper :formatting
end
If you want rails to automatically load this file when it boots, you will need to name your file format_utilities.rb. The next time you restart your server or console, you should be able to do FormatUtilities.slugify("name")