I have Rails Engine model that looks something like this:
module Adhocracy
class Membership < ActiveRecord::Base
. . .
end
end
So I would expect to be able to access it with Adhocracy::Membership. However, I'm getting an error in this namespaced controller:
module Api
module V1
class Adhocracy::MembershipsController < ApplicationController
def index
#memberships = Adhocracy::Membership.where(params)
end
end
end
end
The error is:
uninitialized constant Api::V1::Adhocracy::Membership
If I go into this controller with debugger and type in Adhocracy, it returns Api::V1::Adhocracy, while Adhocracy::Membership returns the above error. However, if I go into another controller with debugger (such as Api::V1::SessionsController), Adhocracy::Membership returns the expected model. Any idea what's going on?
Its due to how Ruby works: it first searches in your current classes, then in its ancestors.
So Adhocracy matches Api::V1::Adhocracy in your MembershipsController and it searches Membership there.
Whereas in another controller with no match, the search goes down the ancestor tree until it reaches Object where Adhocracy is defined.
To be sure to get top level constants append :: which leads you to: ::Adhocracy::Membership
Related
How does this random controller class
class RandomController < ApplicationController
def index
#user = User.all
end
end
access the User class? I've been searching for the connection in the source files, but I can't seem to find a logical explanation.
Rails has 'constant autoloading', so you don't need to add require 'user' to the top of your file.
http://guides.rubyonrails.org/autoloading_and_reloading_constants.html
When Rails encounters a missing constant, it tries to load a file with a filename based on the constant's name. This nearly always works smoothly... C-;
If I do:
rails generate scaffold account/user username
I get a controller that looks like this:
class Account::UsersController < ApplicationController
def index
#account_users = Account::User.all
end
...
end
If I include the Account Module, then it looks like all the database calls don't need to be prefixed with "Account::". I.e.
class Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this works because I included the Account Module above
end
...
end
Now if I were to move my
controllers/account/users_controller.rb
file to:
controllers/admin/account/users_controller.rb
The file looks like this (note: I also corrected my routes file after this move):
class Admin::Account::UsersController < ApplicationController
include Account
def index
#account_users = User.all #this call does not work now
end
...
end
But I get an error saying "uninitialized constant Admin::Account::UsersController::User"
It looks like rails is trying to make a database call on the "User" model without the "Account::" module in front of it.
So how does including modules in controllers work? Why does this not work when I move my controller into a different file (and leave the model in the same location from the generated scaffold) but it works with the scaffold generated files? How can I fix this issue?
Resolving the name of a module is done relative to the current module. Try and change it to:
include ::Account
or
include ::Admin::Account
(depending on the module in which your User model is defined)
This will tell ruby to look in the global namespace for the module Account
I guess I didn't realize you can just explicitly require the path to the module you would like to include. I learned this after reading up on modules some more...
So adding an explicit call to "require 'account/user'" just outside the controller class makes it so including the module in the controller works.
Can I have controllers in Rails that are 3 levels deep inheritance? One would think such a trivial thing is possible, but the concrete controller at the "third" level gives the generic/useless error of "uninitialized constant Ns2::SecondController"
This is basically with this code (I haven't tried this exact code)
module Ns3
class ThirdController < Ns2::SecondController
end
end
module Ns2
class SecondController< Ns1::FirstController
end
end
module Ns1
class FirstController< ApplicationController
end
end
NOTE: The use of namespaces, within the routes and all such directories should be set up properly.
I'm sure I could rearrange the logic and get something working with mixins or helpers. However, I'd like the immediate question answered for my own benefit. Either Y/N or a way passed the error. Not interested in a refactoring work-around solution ATM. Though I guess it couldn't hurt.
Thanks
This can be done.
However it appears RoR is weird, and that you have to implicitly specify the namespace for base classes. If you let it default to the current namespace it acts weird.
Its most likely a typo in either the class name or filename.
You need to put the classes in the correct file/directory structure for Rails autoloading to work, e.g:
#/controllers/ns3/third_controller.rb
module Ns3
class ThirdController < Ns2::SecondController
end
end
#/controllers/ns2/second_controller.rb
module Ns2
class SecondController < Ns1::FirstController
end
end
#/controllers/ns1/first_controller.rb
module Ns1
class FirstController < ApplicationController
end
end
Another thing to try is scoping from the root namespace so with a :: prefix, like so:
module Ns1
class SecondController < ::Ns1::FirstController
end
end
You could also try this:
#/controllers/ns3/third_controller.rb
class Ns3::ThirdController < ::Ns2::SecondController
end
Hey I am creating a rails 3 engine and trying to access a model in the application that is mounting the engine.
module MyEngine
class UsersController < ApplicationController
def index
#users = User.all
end
If I call the following then it gives me the error:
Could not find table 'my_engine_users' - Its automatically looking for the namespaced version that would exist if the model was inside the engine, but in this case its defined in the app that uses the engine.
If I call ::User.all instead of User.all then everything works, it looks a bit strange though. Is this valid ruby or is there a better way to get ahold of the Object?
So if I get your question right, you have a model in your engine looking something like this:
module MyEngine
class User
end
end
If the assumption above is right, you can just set the table name via table_name= in the model
module MyEngine
class User
self.table_name = 'users'
end
end
I have some inherited code that I am modifying. However, I am seeing something strange(to me).
I see some code like this:
::User.find_by_email(params[:user][:email]).update_attributes(:mag => 1)
I have never seen something like this(I am new to Ruby on Rails). What does this do and why doesn't my User.find_by_email(params[:user][:email]).update_attributes(:mag => 1) work? The error says something about the User constant.
I am using Rails 2.3.5 if that helps.
:: is a scope resolution operator, it effectively means "in the namespace", so ActiveRecord::Base means "Base, in the namespace of ActiveRecord"
A constant being resolved outside of any namespace means exactly what it sounds like - a constant not in any namespace at all.
It's used in places where code may be ambiguous without it:
module Document
class Table # Represents a data table
def setup
Table # Refers to the Document::Table class
::Table # Refers to the furniture class
end
end
end
class Table # Represents furniture
end
It makes sure to load the User model in the global namespace.
Imagine you have a global User model and another User model in your current module (Foo::User). By Calling ::User you make sure to get the global one.
Ruby uses (among other things) lexical scoping to find constant names. For example, if you have this code:
module Foo
class Bar
end
def self.get_bar
Bar.new
end
end
class Bar
end
The Foo.get_bar returns an instance of Foo::Bar. But if we put :: in front of a constant name, it forces Ruby to only look in the top level for the constant. So ::Bar always refers the top-level Bar class.
You will run into situations in Ruby where the way your code is being run will force you to use these 'absolute' constant references to get to the class you want.
You might find a lead here: What is Ruby's double-colon `::`?
The "::" operator is used to access Classes inside modules. That way you can also indirectly access methods. Example:
module Mathematics
class Adder
def Adder.add(operand_one, operand_two)
return operand_one + operand_two
end
end
end
You access this way:
puts “2 + 3 = “ + Mathematics::Adder.add(2, 3).to_s