Rails utilize a function in both ActionController and ActiveJob - ruby-on-rails

We have a generic logging function that is in application.rb under our controllers. This function is not found by active job though (I'm assuming as because our email jobs extend ActiveJob::Base vs our controllers that reference ActionController which then references ActionController::Base)
Where would the right place be to put the logging function so we can keep are code as DRY as possible?

So after some talking with others the best course was decided by using the lib folder and creating a module within it.
We created a folder called trackers and a file called tracker.rb in it. Below is a basic snippet of what it looks like
module Trackers
module_function
mattr_accessor :controller
def track_action(event_name, event_params)
event_params["time"] ||= Time.now.utc.to_i
# Controller scope only - this only gets executed if the function is called via a controller vs an ActiveJob
if controller
event_params["controller_name"] ||= controller_name
event_params["action_name"] ||= action_name
end
#Other stuff redacted
end
end
Within the application.rb file we modified the code to include the folder as such:
config.autoload_paths += %W(
#{config.root}/lib/
#{config.root}/lib/trackers/
)
Within a method in a Controller or ActiveJob it is called as such -
Trackers.track_action("eventName", {
"someVar" => "someValue",
})
Another alternative was using a model but I felt this is much more of a lib function - we may turn it into a gem later on.
Hope this helps others in the future.

Related

Devise sign_in resource works even without importing module

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

Rails: Extending existing code

I'm a PHP developer and have worked extensively with Laravel. However, I currently need to make small extension to Redmine (a Ruby Issue Tracker Tool) for work.
I'm brand new to Ruby and Rails, so I'm simultaneously trying to get up to speed on the language and the framework.
In general, I'll need to make some migrations which add a few columns to Redmines existing table. Then when various methods are trigged in Redmine (logging time entries, deleting entries, creating projects, etc), I'll need to make a couple API calls, and insert/update the returned data in said columns.
So not terribly complicated, however I'm wondering a few things as I get off the ground:
1) Because I'm extending an existing Rails app, should I be creating a Plugin? or a Gem? It seems Redmine has a 'plugin generator' that provides some boiler plate
2) I'll need to hook into existing Save and Update events in Redmine. From what I understand, you're not meant to override existing Controllers and Models. In that, what methods are used for implementing additional functionality to an existing application?
I found this helpful piece: http://www.redmine.org/projects/redmine/wiki/Plugin_Internals
However, it mentions:
As explained above: you rarely want to override a model/controller. Instead you should either:
1) add new methods to a model/controller or
2) wrap an existing method.
Presumably, you wouldn't be adding methods directly to the original source? I notice he uses Modules to implement this, but unsure of exactly how they work.
Yes, original source modification is not recomended because of:
Merge problems when You are updates Redmine
Problems with other plugins
For add new or modify existing methods You must create controller, model or helper patch:
require_dependency 'issues_controller'
module IssuesControllerPatch
def self.included(base) # :nodoc:
base.send(:include, InstanceMethods)
base.class_eval do
unloadable
alias_method_chain :some_method, :your_action # modify some_method method by adding your_action action
end
module InstanceMethods
# modified some_method
# You can call original method before or after
# even in the middle of your actions
# or not to call to all
def some_method_with_your_action # modified some_method
do_something_before # your actions before
some_method_with_your_action # call original some_method if needed
do_something_after # your actions after
end
# new method
def your_method
do_something
end
end
end
IssuesController.send :include, IssuesControllerPatch
And add
require 'path/to/your/issues_controller_patch'
to your_plugin/init.rb
Also, if You want call your code in the middle of original code, You must use hooks. Find nessecary hook in original code (controller, view, helper, model), they looks like this:
call_hook(:controller_account_success_authentication_after, {:user => user})
If not found suitable hook, You can add your own (still have modify original code) or add issue at Redmine page (will have to wait a long)
To use hooks, add hook listener like:
class IssuesControllerHookListener < Redmine::Hook::ViewListener
# use view hook - add path/to/your/view.html.erb redmine issues list
# position of your additions depends of used hook position
# view_issues_index_bottom is hook name
# :partial is parameter, value of that is your view
render_on :view_issues_index_bottom, :partial => 'path/to/your/view'
# use controller hook - call your code inside original
# controller_issues_ready_before_index is hook name, method must be named same
# context here is parameters come from hook calling method
# You can use it for your own purposes
def controller_issues_ready_before_index(context = {})
if context[:some_context_param] == some_value
do_something
end
end
end
And add
require 'path/to/your/hook'
to your_plugin/init.rb

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.

(ruby on rails) how to override module method placed in lib/

I'm developing a web application in Rails 2.3.5.
I defined the module in lib/ folder as follows.
module TestModule
class Basic < ApplicationController
def show
p "module method"
end
end
end
and the load_paths are described in the environment.rb for this library as follows
Rails::Initializer.run do |config|
config.load_paths += %W[ #{RAILS_ROOT}/lib/test_module ]
end
but temporally, I want to override the above method without editing the lib method.
So, I put the override method in the "config/initializers/override_show.rb" as follows.
require_dependency "lib/test_module/basic.rb"
module TestModule
class Basic
def show
p "new method"
end
end
end
without "require_dependency", I get an error, because the original method located in lib/ folder wasn't loaded, so I put the "require_dependency" before overriding the TestModule.
In the above code, the new method works fine only once just after activating the server.
However, the new method is never called again, and the old method is called.
When I restart the rails server, the new method will be called just once.
Please give me some advice on how to override the method in the lib folder.
Thank you very much in advance.
Is it possible that you simply append the code in config/initializers/override_show.rb to lib/test_module/basic.rb ?

Rails 3.0 Engine - Execute code in ActionController

I am upgrading my Rails plugin to be an engine that works with the latest 3.0RC1 release and I'm having a bit of trouble figuring out the best (and most correct) way to extend ActionController. I've seen this post by DHH and this question here on SO, but my question is more about how to properly call code within the ActionController.
For instance, I need to call the following within my engine's controller:
class ApplicationController < ActionController::Base
helper :all
before_filter :require_one_user
after_filter :store_location
private
def require_one_user
# Code goes here
end
def store_location
# Code goes here
end
end
I know how to properly include my two private functions, but I can't find a way to get it to properly call helper, before_filter and after_filter.
I would greatly appreciate some links or a way to make this work. I have tried naming my controller something other than ApplicationController and having the real ApplicationController extend it, but that doesn't seem to work either. I'm really up for any solution that makes the life of the engine user as easy as possible. Ideally, they wouldn't have to extend my class, but they'd have all of the functionality built into their own ApplicationController.
You may also want to look into the initializers inside your engine subclass, so you don't have to include view helpers inside your controller class. And this will give you control over the load order of these modules.
Here is what I have been using:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.helper' do |app|
ActionView::Base.send :include, MyEngineHelper
end
initializer 'my_engine.controller' do |app|
ActiveSupport.on_load(:action_controller) do
include MyEngineActionControllerExtension
end
end
end
end
Also, another option for the action controller extension is using a mixin module. This will let you use the before_filter, after_filter, etc..
module MyEngineActionControllerExtension
def self.included(base)
base.send(:include, InstanceMethods)
base.before_filter :my_method_1
base.after_filter :my_method_2
end
module InstanceMethods
#...........
end
end
One other thing... if you create the default rails directories at the top level of your gem, you don't have to worry about requiring the helpers or controllers. Your engine subclass has access to them. So I add my application controller and application helper extensions here:
/myengine/app/helpers/myengine_application_helper_extension.rb
/myengine/app/controllers/my_engine_action_controller_extension.rb
I like this setup because it looks similar to the application_controller and application_helper in your rails app. Again, this is just personal preference, but I try to keep anything that is directly rails related, such as controllers, helpers and models inside /my_engine/app and anything that is related to the plugin in general inside /my_engine/lib
Check out this tutorial by Jose Valim for more info on initializers:
https://gist.github.com/e139fa787aa882c0aa9c (engine_name is deprecated now, but most of this doc seems up-to-date)
So, I finally figured out the solution and I hope it helps someone else.
You need to create a file in your lib directory because you are actually going to extend the class. I did myplugin/lib/extensions/action_controller_base.rb.
Then, inside of your myplugin/lib/myplugin.rb file, do the following:
require 'extensions/action_controller_base.rb'
Inside of myplugin/lib/extensions/action_controller_base.rb put the following:
require 'action_controller' # Make sure ActionController::Base is defined
ActionController::Base.class_eval {
private
def my_method_1
# Code Goes Here
end
def my_method_2
# Code Goes Here
end
}
ActionController::Base.instance_eval {
helper_method :my_method_1, :my_method_2
before_filter :my_method_1
after_filter :my_method_2
}
If you need to have view helpers, create them in the myplugin/lib/helpers directory (or anything inside of lib, the name "helpers" doesn't matter) also and add the following to the bottom of myplugin/lib/extensions/action_controller_base.rb:
require 'helpers/helper_file_1'
require 'helpers/helper_file_2'
ActionView::Base.send :include, MyHelper1
ActionView::Base.send :include, MyHelper2

Resources