This question already has answers here:
Ruby's double colon (::) operator usage differences
(2 answers)
Closed 7 years ago.
I am looking at the Rails Engine tutorial and one of the code blocks looks like the following:
module Blorgh
class Engine < ::Rails::Engine
isolate_namespace Blorgh
end
end
What does the ::Rails::Engine mean? I know this is probably a trivial Ruby question, however, I don't seem to be able to find anything anywhere.
Thanks.
So, Ray has this exactly right. I would just like to add a further example.
Let's say we have an engine called Foo (original, right?) that is mounted in a host application called Bar with something like:
#bar/config/routes.rb
Rails.application.routes.draw do
...
mount Foo::Engine, at: '/'
...
end
Foo has an application_controller:
#foo/app/controllers/foo/application_controller.rb
module Foo
class ApplicationController < ActionController::Base
...
def foo_action
...
end
...
end
end
And Bar has an application_controller:
#bar/app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
def bar_action
...
end
...
end
Now, let's say we have two controllers within the Foo engine, like:
#foo/app/controllers/foo/baz_controller.rb
module Foo
def BazController < ApplicationController
...
end
end
and
#foo/app/controllers/foo/bif_controller.rb
module Foo
def BifController < ::ApplicationController
...
end
end
The BazController inherits from ApplicationController (no :: in front). This means that it looks up ApplicationController in the current namespace (Foo). And so it will have the foo_action defined in Foo::ApplicationController.
The BifController inherits from ::ApplicationController (:: in front). This means that it looks up ApplicationController in the global namespace which, in this case, is the host application Bar. And so it will have the bar_action defined in Bar's ApplicationController.
The :: at the front of ::Rails::Engine means to lookup Rails::Engine at the top of the global namespace, not inside the Blorgh namespace.
Without contrast, without the :: as in this code,
module Blorgh
class Engine < Rails::Engine
isolate_namespace Blorgh
end
end
the second line would be looking for Blorgh::Rails and you would get an error, NameError: uninitialized constant Blorgh::Rails.
Think of all the Modules and Classes hierarchically: it is looking for "Rails" at the top-level with "Engine" at the next level.
e.g.:
module Rails
class Engine
end
end
module SomethingElse
class Rails
end
end
::Rails selects the Module in the first code snippet, which is on the top-level.
::Rails::Engine selects the Class Engine which is in the top-level Rails Module.
Related
I would like to know what is the right way to use a class with namespace in rails 6.
I have the follow, but it isn't working and I'm receiving the error:
"Uninitialized constant ProductsController::Operations Did you mean? ProductsController::Options"
#app/operations/create.rb
module Operations
class Create
def self.foo
...
end
end
end
#app/controllers/products_controller.rb
class ProductsController < ApplicationController
def create
Operations::Create.foo
end
end
Can you help me please?
Your module should be inside of any folder. For example app/services/operations/create.rb (any name will work) with the same content you have:
module Operations
class Create
def self.foo
...
end
end
end
and call it Operations::Create.foo.
Also make sure you restart spring with spring stop.
Given the following controller structure:
# application_controller.rb
class ApplicationController < ActiveController::Base; end
# pages_controller.rb
class PagesController < ApplicationController; end
# admin/application_controller.rb
module Admin
class ApplicationController < ::ApplicationController; end
end
# admin/pages_controller.rb
module Admin
class PagesController < ApplicationController; end
end
One would expect Admin::PagesController to inherit from Admin::ApplicationController and it does. But I have noticed that sometimes it inherits from ::ApplicationController.
So I decided not to risk it and changed declaration of all controllers in /admin to specifically target Admin::ApplicationController
# admin/pages_controller.rb
module Admin
class PagesController < Admin::ApplicationController; end
end
Okay that works, but from what I know it was correct in the first place. Why Rails inherits from a wrong controller sometimes?
Admin::PagesController sometimes inherits from ApplicationController instead of Admin::ApplicationController despite both being in the same module Admin
The problem here is rails' development mode code loading: in general code is loaded when you try to do something with a constant (eg subclass from it) and that constant doesn't exist. This results in const_missing being called and rails uses it this to try to load the class (for a detailed description see the guide).
If neither ApplicationController nor Admin::ApplicationController exist then when you access your admin pages controller ruby will hit that const_missing and try to load admin/application_controller.rb
However if ApplicationController is already loaded then ruby won't fire const_missing since it perfectly legal for a class in the admin module to inherit from something at the toplevel.
The solution as you say is to make explicit what you are inheriting from. Personally in my own apps I use Admin::BaseController as the base class.
Another option is to use require_dependency to point Rails to the correct file:
# admin/application_controller.rb
require_dependency 'admin/application_controller'
module Admin
class PagesController < ApplicationController
end
end
I have alredy 3 grey hair from this. Rails4.0/ruby 1.9.3. I have file test.rb in directory /lib/moduletest/test. test.rb looks like this:
module Moduletest
class test
end
end
How can I instantiate this class in my controller? How should I use the require command? Moduletest::test.new() ?
At first may I suggest you to use "foobar" instead of "test". "test" looks really like, test.
Back to question, there are two ways to use it in controller, given you have already loaded the module correctly as per comments.
The first is to explicitly include it. Preferred
class ApplicationController < ActionController::Base
include ModuleFoo
def index
bar # Use ModuleFoo's method directly
#...
end
end
The second is to hook the extension in Rails loading
# ModuleFoo
module ModuleFoo
def bar
end
end
if defined? ActionController::Base
ActionController::Base.class_eval do
include ModuleFoo
end
end
# Controller
class SomethingController < ApplicationController
def some_method
bar # use this directly
end
end
You have to put the lib directory into your autoload path. So Rails load your file on startup:
config/application.rb and add:
config.autoload_paths += %W(#{config.root}/lib)
I thought I'd come up with a slick way to extend ApplicationController in a Rails 3.x gem.
In my gem's lib/my_namespace/my_controller.rb, I had:
class MyNamespace::MyController < ApplicationController
before_filter :some_method
after_filter :another_method
def initialize
# getting classname of the subclass to use for lookup of the associated model, etc.
# and storing the model_class in an instance variable
# ...
end
# define :some_method, :another_method, etc.
# ...
private
attr_accessor :subclass_defined_during_initialize # etc.
# etc.
end
but when the Gem is loaded, app/controllers/application_controller.rb is not yet loaded, so it fails:
/path/to/rvm/gemset/gems/activesupport-3.2.6/lib/active_support/dependencies.rb:251:
in `require': cannot load such file -- my_gem_name/application_controller (LoadError)
As a workaround, I had defined ApplicationController in my gem's lib/gem_namespace/application_controller.rb as:
class ApplicationController < ActionController::Base
end
I assumed that even though I had defined it there, it would be redefined in my Rails 3 application's app/controllers/application_controller.rb, such that both controllers in the application that extended ApplicationController and controllers that extended MyNamespace::MyController would directly or indirectly extend the ApplicationController defined in app/controllers/application_controller.rb.
However, we noticed that after loading the gem, controllers that extend ApplicationController were unable to access methods defined in app/controllers/application_controller.rb. Also, the ApplicationHelper (app/helpers/application_helper.rb) module was no longer being loaded by other helper modules.
How can I extend ApplicationController within the controller in my gem for the purpose of defining a before_filter and after_filter to and use initialize to access the class's name to determine the associated model's class that it could then store and use within its methods?
Update 2012/10/22:
Here's what I came up with:
In lib/your_gem_name/railtie.rb:
module YourGemsModuleName
class Railtie < Rails::Railtie
initializer "your_gem_name.action_controller" do
ActiveSupport.on_load(:action_controller) do
puts "Extending #{self} with YourGemsModuleName::Controller"
# ActionController::Base gets a method that allows controllers to include the new behavior
include YourGemsModuleName::Controller # ActiveSupport::Concern
end
end
end
and in lib/your_gem_name/controller.rb:
module YourGemsModuleName
module Controller
extend ActiveSupport::Concern
# note: don't specify included or ClassMethods if unused
included do
# anything you would want to do in every controller, for example: add a class attribute
class_attribute :class_attribute_available_on_every_controller, instance_writer: false
end
module ClassMethods
# notice: no self.method_name here, because this is being extended because ActiveSupport::Concern was extended
def make_this_controller_fantastic
before_filter :some_instance_method_available_on_every_controller # to be available on every controller
after_filter :another_instance_method_available_on_every_controller # to be available on every controller
include FantasticStuff
end
end
# instance methods to go on every controller go here
def some_instance_method_available_on_every_controller
puts "a method available on every controller!"
end
def another_instance_method_available_on_every_controller
puts "another method available on every controller!"
end
module FantasticStuff
extend ActiveSupport::Concern
# note: don't specify included or ClassMethods if unused
included do
class_attribute :class_attribute_only_available_on_fantastic_controllers, instance_writer: false
end
module ClassMethods
# class methods available only if make_this_controller_fantastic is specified in the controller
def some_fanastic_class_method
put "a fantastic class method!"
end
end
# instance methods available only if make_this_controller_fantastic is specified in the controller
def some_fantastic_instance_method
puts "a fantastic instance method!"
end
def another_fantastic_instance_method
puts "another fantastic instance method!"
end
end
end
end
For this specific kind of functionality I would recommend creating a module in your gem and include that module in your Application Controller
class ApplicationController < ActionController::Base
include MyCoolModule
end
To add before filters, etc (add this to your module)
def self.included(base)
base.send(:before_filter, my_method)
end
Update: you may be able to just do base.before_filter :my_method which is cleaner.
Here is a Gist
that shows how to access the class of the subclass and store it in an instance variable and access it in the before and after filters. It uses the include method.
Truth is much much simpler and flexible.
Add to lib/engine.rb this: class Engine < Rails::Engine; end
And then simply use:
ActionController::Base.class_eval do
include SomethingFromMineGemModule
# or:
def hello_from_gem
'Hey people!'
end
end
I was able to reference ApplicationController with an initializer callback.
gem code that subclasses/references ApplicationController:
class GemApplicationController < ApplicationController
before_filter :method_to_call
def method_to_call
#your code here
end
end
gem code callback to create subclassed controller:
module GemName
def self.load_gem_application_controller
require "path/to/gem_application_controller"
end
end
rails_app/config/initializers/gem_name.rb
GemName.load_gem_application_controller
Then have controllers that use this functionality subclass GemApplicationController
class SpecialCaseController < GemApplicationController
# this will inherit from the gem's controller,
# which inherits from the rails_app ApplicationController
end
I have a controller having more than 1000 lines of code.
Right not I am doing Code review for this controller.
I arrange my methods according to the module.
Now I realise that my controller is not easy to maintain and so I want to something like following
class UsersController < ApplicationController
#Code to require files here
#before filter code will goes here
#############Here i want to call that partial like things. following is just pseudo #########
history module
account module
calendar module
shipment module
payment module
####################################################################
end #end of class
this help me so much to maintained the code as when i change history module i am sure that my account module is unchanged.I know CVS but i prefer 50 copies of each module instead 200 copies of my users_controller.rb itself.
P.S. :- I would like Affirmative answer.please don't answer like, you should use different controller for different module.....bla...bla...bla... as it's not possible for me to do so.
EDIT:- My Versions Are
rails -v
Rails 2.3.4
ruby -v
ruby 1.8.6 (2008-08-11 patchlevel 287) [i386-linux]
This should work for you:
app/controllers/some_controller.rb
class SomeController < ApplicationController
include MyCustomMethods
end
lib/my_custom_methods.rb
module MyCustomMethods
def custom
render :text => "Rendered from a method included from a module!"
end
end
config/routes.rb
# For rails 3:
match '/cool' => "some#custom"
# For rails 2:
map.cool 'cool', :controller => "some", :action => "custom"
Fire up your app and hit http://localhost:3000/cool, and you'll get your custom method included from a module.
Assuming from you pseudocode that you are referring to Ruby modules, and not something else, just put all your requires/modules in a separate module and include that or have your UsersController inherit from a base class if you are reusing those files. In the first case you can think of a module as a mix-in and it is designed for exactly the modularity you want.
module AllMyStuff
include History
include Account
...
end
class UsersController < ApplicationController
include AllMyStuff
def new
end
...
end
or you can inherit from a base controller,in this case its probably a reasonable solution.
def BaseController < ActionController
include history
include account
end
def UsersController < BaseController
# modules available to this controller by inheritance
def new
end
...
end
I tried the following method and I have it running, maybe it suits for you:
app/user_controller.rb
require 'index.rb'
class UsersController < ApplicationController
# some other code
end
app/index.rb
class UsersController < ApplicationController
def index
#users = User.all
end
end
MY ENV: rails 3 beta4