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!
Related
I'd like to create a general purpose string manipulation class that can be used across Models, Views, and Controllers in my Rails application.
Right now, I'm attempting to put a Module in my lib directory and I'm just trying to access the function in rails console to test it. I've tried a lot of the techniques from similar questions, but I can't get it to work.
In my lib/filenames.rb file:
module Filenames
def sanitize_filename(filename)
# Replace any non-letter or non-number character with a space
filename.gsub!(/[^A-Za-z0-9]+/, ' ')
#remove spaces from beginning and end
filename.strip!
#replaces spaces with hyphens
filename.gsub!(/\ +/, '-')
end
module_function :sanitize_filename
end
When I try to call sanitize_filename("some string"), I get a no method error. When I try to call Filenames.sanitize_filename("some string"), I get an uninitilized constant error. And when I try to include '/lib/filenames' I get a load error.
Is this the most conventional way to create a method that I can access anywhere? Should I create a class instead?
How can I get it working? :)
Thanks!
For a really great answer, look at Yehuda Katz' answer referenced in the comment to your question (and really, do look at that).
The short answer in this case is that you probably are not loading your file. See the link that RyanWilcox gave you. You can check this by putting a syntax error in your file - if the syntax error is not raised when starting your app (server or console), you know the file is not being loaded.
If you think you are loading it, please post the code you are using to load it. Again, see the link RyanWilcox gave you for details. It includes this code, which goes into one of your environment config files:
# Autoload lib/ folder including all subdirectories
config.autoload_paths += Dir["#{config.root}/lib/**/"]
But really, read Yehuda's answer.
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.
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)
I'm running Rails 3.2.7,
I have a folder '/app/jobs'
and the following in my 'config/application.rb' file
config.autoload_paths += %W(#{Rails.root}/app/jobs)
And everything is okay.
However if I want to namespace my classes eg
class Jobs::UpdateGameStatus
#methods etc
end
Rather than
class UpdateGameStatus
#methods etc
end
Then I get
uninitialized constant Jobs (NameError)
It's not the end of the world but I'd love to know why...
I fixed it in the end, wrapping all my classes with a Jobs module was what I needed to do.
my files were located in 'app/jobs'
and looked like this
module Jobs
class JobName
#methods etc
end
end
and are used like so
Jobs::JobName.method(args)
I know you have already sorted this out, and this is old, but in ruby, it is also possible to declare the namespaced class directly using class Jobs::JobName. It's a little less typing, and achieves the same result.
Edit: As #D-side pointed out, Jobs has to already be defined. My own code that uses this is based around STI, which presumes that the previous class/module I am extending already exists.
The first line of text is the LoadError
the second line of text in the image is the folder where the file, shared.rb resides in my rails project.
And the sytax colored stuff is the top part of shared.rb
as you can see, shared.rb defines NamedScope::Shared, so why is RoR saying that it isn't?
using rails 2.3.8
UPDATE:
tried this:
module NamedScope
module Shared
in the same file, shared.rb in {AppRoot}/lib/named_scope/
which also didn't work (same error)
UPDATE 2: This error was caused my a model class not having a Constant defined.
I just had
CONSTANT_NAME
instead of
CONSTANT_NAME = value
this is upsetting, as I feel lied to o.o
Did you define the NamedScope module before? You might need to do:
module NamedScope
module Shared
# do your thing here
end
end
I would need to see more of the code to follow.
I've had that issue when the code uses a Class that is defined somewhere else and the magic name resolution is not strong enough. Like if I have a XyzLoan class that extends the Loan class but I did not require it or use it before. It shouldn't fail but it does.
If that is the case you can do a binary search for the problem by removing halves of the code and try again until you find what is causing it.