I have a class in my Rails app's lib directory and I want to be able to use the helpers I created in ApplicationHelper from inside that class. I have the following:
module ApplicationHelper
def works
"Yay!"
end
def breaks
session[:username]
end
end
class MyClass
include ApplicationHelper
end
The following works
MyClass.new.works
This breaks
MyClass.new.breaks
I get a message about Rails trying to delegate session to controller.session, but it can't do it because controller is nil.
What's the right way to include ApplicationHelper?
The issue is not with including the helper, but with accessing the session.
In the normal scenario, the ApplicationHelper is automatically included in all controllers, and some of the ActionView methods - including session - are delegated to the controller which is present in this case.
When included in a regular ruby class, there is no controller; so any call to these methods will throw an error saying the controller is nil.
Not sure why you would need session information in a regular ruby class.
Related
So, I was customizing devise in a custom sign up page which required me to sign_in a user after creating the account along with some other operations. After creating the resource I did
sign_in resource if resource.active_for_authentication?
and it signs in the user. My controller inherits the
ApplicationController
and I haven't included any modules like this
include Devise::Controllers::SignInOut
How did rails know about the
sign_in
method
Basic answer - the module Devise::Controllers::Helpers (which includes Devise::Controllers::SignInOut that you discovered) is automatically included in ApplicationController by one of the Devise initializer named "devise.url_helpers". Initializer is included by adding Devise gem, and its content is run during rails Application startup.
Going deeper
Devise is a Rails Engine - you can check this article for brief review.
Engines can be considered miniature applications that provide
functionality to their host applications. A Rails application is
actually just a "supercharged" engine, with the Rails::Application
class inheriting a lot of its behavior from Rails::Engine.
...
Engines are also closely related to plugins.
Then, you will find the following call at rails.rb of Devise (this rails.rb is required by gem root devise.rb file - see below) here:
initializer "devise.url_helpers" do
Devise.include_helpers(Devise::Controllers)
end
To state, initializer here is not a method definition, but actual calling a class method with string name parameter and block parameter. It's executed on class load (i.e. as a result of loading a class by require). Same time, passed block serves as parameter to this call, and in this particular case is saved to be executed later - see below explanation of initializers.
Side note on engines (in fact railtie) initializer concept
Initializer is a concept by one of the Rails basic class Railtie. Concept is described here:
Initializers - To add an initialization step from your Railtie to Rails boot process, you just need to create an initializer block:
# class MyRailtie < Rails::Railtie
# initializer "my_railtie.configure_rails_initialization" do
# # some initialization behavior
# end
# end
The implementation of initializers logic is part of Initilizable module which is included into Railtie class. Specific initializer class method basically adds passed block to class initializers array source:
def initializer(name, opts = {}, &blk)
...
initializers << Initializer.new(name, nil, opts, &blk)
end
It's not executed immediately. It's run by executing run method on initializers in specific order by run_initializers call, which is also part of Initializable module. This method is available for rails Application with inherits from Engine (which includes Initializable module).
def run_initializers(group=:default, *args)
return if instance_variable_defined?(:#ran)
initializers.tsort_each do |initializer|
initializer.run(*args) if initializer.belongs_to?(group)
end
#ran = true
end
This run_initializers method is triggered by initialize! call (see below a bit later) of the Application.
Side note on collecting all initializers by Rails Application.
Meanwhile, initializers here is an overloaded method in Application class:
def initializers #:nodoc:
Bootstrap.initializers_for(self) +
railties_initializers(super) +
Finisher.initializers_for(self)
end
This method will load all initializers of the application for further ordering and running.
Inside, railties_initializers will call ordered_railties, which will use railties getter of Engine class (which Application is inherited from). This getter is the following
def railties
#railties ||= Railties.new
end
Railties (plural) service class is different from Railtie. It actually just collects all railties by looking at all subclasses of both Engine and Railtie classes.
def initialize
#_all ||= ::Rails::Railtie.subclasses.map(&:instance) +
::Rails::Engine.subclasses.map(&:instance)
end
Finally, subclasses is a method from extension of Ruby base Class, which Rails extend for its convenience
def subclasses
subclasses, chain = [], descendants
chain.each do |k|
subclasses << k unless chain.any? { |c| c > k }
end
subclasses
end
end
Back to running initialiers by Application. As mentioned above, run_initializers is called by initialize! call of the Application class:
def initialize!(group=:default) #:nodoc:
raise "Application has been already initialized." if #initialized
run_initializers(group, self)
#initialized = true
self
end
Which for the Rails app is triggered by Rails.application.initialize! call in environment.rb file - see generator source
How those initializers got added to the running queue? This happens by adding Devise gem (e.g. by Bundle.require), which loads lib/devise.rb gem root file, and which has following require at the very bottom:
require 'devise/rails'
As this loads Devise class, it will be discovered by Railties class by looking at subclasses for Engine.
Back to Devise devise.url_helpers initializer
If you look at include_helpers call, this is what it does:
def self.include_helpers(scope)
ActiveSupport.on_load(:action_controller) do
include scope::Helpers if defined?(scope::Helpers)
include scope::UrlHelpers
end
ActiveSupport.on_load(:action_view) do
include scope::UrlHelpers
end
end
ActiveSupport on_load call is a Rails feature to lazy_load components. source:
# lazy_load_hooks allows Rails to lazily load a lot of components
and thus # making the app boot faster.
In this case, those include commands for controller will be executed when controller is loaded, but not on server start. Check this or any other articles on the concept.
And this is the place where that lazy block is run - source:
module ActionController
# Action Controllers are the core of a web request in \Rails. They are made up of one or more actions that are executed
# on request and then either it renders a template or redirects to another action. An action is defined as a public method
# on the controller, which will automatically be made accessible to the web-server through \Rails Routes.
#
# By default, only the ApplicationController in a \Rails application inherits from <tt>ActionController::Base</tt>. All other
# controllers in turn inherit from ApplicationController. This gives you one class to configure things such as
# request forgery protection and filtering of sensitive request parameters.
...
class Base < Metal
...
ActiveSupport.run_load_hooks(:action_controller, self)
end
end
BTW, your ApplicationController generated by Rails is inherited from ActionController::Base
In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.
i have a method that uses devises user_signed_in? and current_user helpers. given that this is needed for almost all controllers in the project... it seemed like a great candidate for becoming a rails concern. however, when I move the relevant code into a concern i get:
undefined method 'user_signed_in'?
I thought maybe adding include Devise::Controllers::Helpers would help... but that was to no avail.
I trimmed this down to an example that has the mentioned issue and in a controller you would need this:
include CheckIt
and in the controller concerns directory a check_it.rb that looks like this:
module CheckIt
extend ActiveSupport::Concern
include Devise::Controllers::Helpers
included do
before_action self.checkit
end
module ClassMethods
def checkit
puts "user_signed_in? #{user_signed_in?}"
end
end
end
and then, as soon as you access some aspect of that controller it will say undefined method 'user_signed_in?'
My model, Widget.rb, has include ApplicationHelper and my instance methods have no trouble using any method defined in application_helper.rb
However, when I try to use one of the helper methods in any of my class methods such as
def self.send_broadcast(guid)
track_guids(guid) # defined in application_helper.rb
end
I get No Method error.
Is there some secret handshake to permit use of a ApplicationHelper method inside a class method?
ApplicationHelper is just a module:
module ApplicationHelper
def track_guids(something)
end
end
class Widget
extend ApplicationHelper
def self.send_broadcast(guid)
track_guids(guid)
end
end
Now you should have access to the module methods from a class method. I'm not sure if you can both extend and include the same module though... not really sure what that'd do.
Edit to add:
I'm not sure what will happen if you try both extending and including the same module into the class. With extend you get the module included at the class-level, with include it is included at the instance-level. It might give you the methods at both class and instance if you do both... or it might die horribly. Give it a try?
I don't think you can access instance methods unless self is an instance. You could make an instance of Widget and call a class method from that, or you could try to call the methods from the module directly.
i am using refinery cms at the moment. I created an engine and with it some helpers in app/helpers/admin/.
now i would like to use those helpers in my frontend view (ie. app/views/myapp/index) as well. but i can not...undefined methode error.
what do i have to do short of copying the whole thing to app/helpers/?
the helper looks like this
module Admin
module myHelper
def somefunc
end
end
end
so is it possible to use somefunc outside of the Admin module?
The "Rails way" to include a helper from a non-standard path in a view is to use the .helper method within your controller.
class MyController < ApplicationController
helper Admin::MyHelper
...
end
http://apidock.com/rails/AbstractController/Helpers/ClassMethods/helper
In your application_helper.rb:
module ApplicationHelper
include Admin::MyHelper
end
This will import those helper methods into the ApplicationHelper, thus making them available in your views. You could do this in any of your helpers really.
You might try to use the full object reference like Admin::myHelper::somefunc to call somefunc from outside the Admin module.