Devise sign_in resource works even without importing module - ruby-on-rails

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

Related

The reason why it's possible to use helper method like current_user, authenticate_user!, and so on without including any module

The title is my question.
devise provide us many useful methods like current_user, authenticate_user!, and so on. I want to know why is it possible to use them without including any module like below.
class ApplicationController < ActionController::Base
before_action :authenticate_user!
end
Those method's definition is here
Somebody please help me!
The answer is devise included its helper methods into ActionController on behalf of you when Rails on_load
# devise/rails.rb
module Devise
class Engine < ::Rails::Engine
# ...
initializer "devise.url_helpers" do
Devise.include_helpers(Devise::Controllers)
end
# ...
end
# devise.rb
# ...
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
# ...
I saw many 3rd gems using on_load to include their methods (or themselves) into Rails core, maybe it's a typical way to do that (allows Rails to lazily load a lot of components and thus making the app boot faster). If you install some gems and you could use their methods on your model/controller/view then those gems did the same thing devise did above.
About those methods current_user, authenticate_user! ... they are dynamic methods devise will generate when it did include scope::Helpers into Rails (see code above and the link).
So one very good thing about rails is the fact that you get a lot of things for free out of the box. One of these things at the top level is autoloading.
So in the case of Devise. When you install Devise and run the generate command, you get a devise.rb file inside of your config/initializers/ folder. This file is always autoloaded by rails on server startup and reload. That's how all these methods are able to be use these devise methods without importing anything.
Read more at rails docs

How to include rails ApplicationHelper module in a regular Ruby Class

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.

Calling a module's methods on a Gem's object?

I've generated an object via a ruby gem (Koala) and I've written a few modules with helper methods. What do I need to do in order to be able to use the methods within the modules on the object?
If I, model_object = Model.new, model_object will have access to all the instance variables but object does not (see below).
Ruby 2.1, Rails 4.1
config/application.rb - Autoloading modules in folder
config.autoload_paths << Rails.root.join('lib/module_folder')
Model
class Model < ActiveRecord::Base
include Module
include Module::Module2
include Module::Module3
def self.create_account(token)
object = Module.module_class_method(token) #this works and generates the Koala object
ERROR: object.module2_instance_method # Error: NoMethodError Exception: undefined method
end
end
Module
module Module
extend ActiveSupport::Concern
end
Module2
module Module
module Module2
def module2_instance_method
end
end
end
SOLVED MYSELF
- the issue was the include statements being within the class, if I moved them outside it worked.
I believe if you include your modules somewhere under the app/ directory - they will be included automatically. Otherwise, you actually have to require them in your rails code explicitly with a require statement
Without seeing the actual code, I think the problem with Module2 in your code snippet is the self. method.
Because you are calling module2_instance_method on an instance of your object, the method in the module cannot have the self. because that designates a class method and, as such, would have to be called as Module::Module2.module2_instance_but_not_really_because_I_am_a_class_method.
I believe if you change def self.module2_instance_method ... end to def module2_instance_method ... end, you should no longer receive the NoMethodError exception.
Apologies if I've misread or misunderstand the OP.
Moved the include statements from inside to above the class declaration and all methods began to work. My assumption is that when they are within the statement they are only available to objects of that class.

Rails undefined method for Module

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.

How to get a rails engine to interact with main App

I am creating a rails engine used as an interface to a payment service. I have to handle the use cases where the payment service is sending me message outside of any transaction.
I will take the following use case :
The engine receive a message that subscription FooBar42 has been correctly billed(through it's own routes). What does my engine do next?
If i start to call models specific to my app, my engine is only good for this app.
Only example i could find is Devise, but in devise, it just add methods to your user model and the engine handle how the user is stored and all the code.
How do i create a reusable system where my engine can call/trigger code in the main app ?
Do i override engine controller ? Do i generate a service object with empty methods that will be used as an engine-app communication system?
You need to have a way to configure your engine either in an initializer or through calling class methods in your model, much like how you configure Devise to use your User model when you call devise in the body of the User class:
class User < ActiveRecord::Base
devise :database_authenticatable
end
For one of my engines I use an initializer that stores the classes the engine interacts with using a config object.
Given a config object:
class Configuration
attr_reader :models_to_interact_with
def initialize
#models_to_interact_with = []
end
end
You can then provide a config hook as a module method in your main engine file in lib:
require 'my_engine/configuration'
module MyEngine
mattr_reader :config
def self.configure(&block)
self.config ||= Configuration.new
block.call self.config
end
end
Now when you're in the main app, you can create an initializer in config/initializers/my_engine.rb and add the models to your Configuration:
MyEngine.configure do |config|
config.models_to_interact_with << User
config.models_to_interact_with << SomeOtherModel
end
Now you have access to this from you engine without having to hardcode the model in your engine:
# in some controller in your engine:
def payment_webhook
MyEngine.config.models_to_interact_with.each do |model|
model.send_notification_or_something!
end
end
Hope that answers your question.

Resources