How to use reusable controller methods in Rails? - ruby-on-rails

I'm relatively new to Rails and am working on creating a simple user authentication system to get to grips with how Rails works.
I'm currently at the point where I'd like to create some methods that I can use in my controllers like so:
is_logged? # => true
and
current_user_id # => 6
These would be used to interact with sessions, mainly so I'm not repeating myself in the controller.
Where would I define these functions and how would I include them in a controller?
Thanks a lot in advance for any help.

Method 1
You can define these method in helper files, inside app/helpers/my_module.rb. You can create a module there, put all the methods inside of it, and then include the modules in your control to use these method.
module MyMoule
def is_logged?
...
end
end
Then in you class include the module
class MyClassController < ApplicationController
include MyModule
def my_method
#Use it like this
logged_in = MyModule.is_logged?
end
end
Method 2
If you using session related stuff you can always put them inside application_controller.rb. And since all your controller will inherit ApplicationController the methods will be available to you.
class ApplicationController < ActionController::Base
def is_logged?
...
end
end
In your other controller you can use them directly.
class MyClassController < ApplicationController
def my_method
logged_in = is_logged?
end
end

Related

Create an "includable" Rails controller action within helper

Is it possible to make an includable controller action within a Rails Helper through an included block? I'm thinking something like this:
module XablauHelper
included do
def my_shared_action
true
end
end
end
Already tried doing it through class.eval block and through using like a class method i.e. self.my_shared_action but no success, I have already found a solution that is making a parent controller with the desired shared actions and inheriting from it, but for the sake of modular design I would like to make it a more "global" approach, so I could gemify my solution and reuse code, any suggestions that doesn't use inheritance?
Adding controller actions in a helper is probably the wrong choice, as these methods are intended for your views.
Consider using controller concerns instead, and including them where required. For example:
# in app/controllers/concerns/useful_functions_concern.rb
module UsefulFunctionsConcern
extend ActiveSupport::Concern
included do
rescue_from SomeException, with: :handle_access_denied
end
def useful_method
# ...
end
protected
def handle_access_denied
# ...
end
end
# in your controller
class XyzController < ApplicationController
include UsefulFunctionsConcern
def index
useful_method
end
end
Where common controller actions can be shared and the controllers have something in common e.g. they are all API controllers, also consider using inheritance to achieve this. For example:
# parent controller
class ApiController < ApplicationController
def my_shared_action
end
end
class SpecificApiController < ApiController
end

Accessing variables from multiple models

I have six distinct sections of my Rails application, all of which have their own models, views, and controllers.
I'm trying to create a "dashboard" page that accesses variables from each of the sections. For instance, in one of my controllers, I have this condition:
if #retirementsavingsdiff < 0
#retiregrade = "pass"
end
I can't seem to access this variable from a different view/controller though.
Do I put my dashboard logic in application_controller.rb?
A good option for making code reusable is separating it out into modules. Rails 4 includes something called Concerns that make this really easy. Here's a blog post with a good illustration of using Concerns for Controllers, and here's a sample of what your code might look like:
# /app/controllers/concerns/retirement_grade_checker.rb
module RetirementGradeChecker
extend ActiveSupport::Concern
def check_retire_grade
#retirementsavingsdiff = params[:retirementsavingsdiff]
if #retirementsavingsdiff < 0
#retiregrade = "pass"
end
end
end
# /app/controllers/retirement_controller.rb
class RetirementController < ApplicationController
include RetirementGradeChecker
def index
check_retire_grade
#... other stuff
end
end
# /app/controllers/dashboard_controller.rb
class DashboardController < ApplicationController
include RetirementGradeChecker
def index
check_retire_grade
#... other stuff
end
end
I would avoid using view helpers and instead create a new class or module with all of your logic inside. By doing that you can reuse that logic whenever you need it.
Why do this instead of helpers? You can easily test it.
methods defined inside helpers are automatically available across all views.
if you want to convert a method defined inside the controller to a helper method, you can do that too:
def my_method
# code
end
helper_method :my_method
UPDATE:
here is an example from API
class ApplicationController < ActionController::Base
helper_method :current_user, :logged_in?
def current_user
#current_user ||= User.find_by(id: session[:user])
end
def logged_in?
current_user != nil
end
end

where to put helper methods for controllers only?

I am looking to write certain methods for processing strings, and other tasks that take place in numerous of my controllers. I know its bad practice to include helpers in your controller, so I was just wondering, where is the best place to put application wide methods used in controllers?
I realize some of you will say to put them in models, but you have to realize that not all my controllers have an associated model. Any and all input would be appreciated.
I tend to put them into helpers. The fact that they are included in views
automatically haven't been a problem for me. You can also place them into
something like app/concerns/ or lib/
I don't like cluttering ApplicationController with private methods
because this often becomes a mess.
Example:
module AuthenticationHelper
def current_user
#current_user # ||= ...
end
def authenticate!
redirect_to new_session_url unless current_user.signed_in?
end
end
module MobileSubdomain
def self.included(controller)
controller.before_filter :set_mobile_format
end
def set_mobile_format
request.format = :mobile if request.subdomain == "m"
end
end
class ApplicationController
include AuthenticationHelper
include MobileSubdomain
end
If you need to use a method in the application scope then I would suggest that you keep those methods inside the application controller and in order to use them inside views.. declare those as helper methods.
For example,
class ApplicationController < ActionController::Base
helper_method :current_user, :some_method
def current_user
#user ||= User.find_by_id(session[:user_id])
end
def some_method
end
end
I would suggest to put them in lib folder. So for example I have:
lib/utils/string_utils
module StringUtils
def foo
...
end
end
class BarController < ActionController::Base
include StringUtils
end
This demonstrates good methodology called Fat model, Thin controller, in this case we are using Mixins instead of Models to separate logic but idea is same. You want your controllers as simple as possible.
It all depends on your needs. I will provide here 2 examples. Both of them are just a custom libraries, located under lib directory.
First example - "custom string processing"
# lib/filters.rb
module Filters
# Converts value to canonical view
def self.phone(value)
# remove all non-digits
clean_value = value.gsub(/\D/, '')
country_codes = configus.phone.country_codes
area_code = configus.phone.defaults.area_code
case clean_value.length
when 7
"#{area_code}#{clean_value}"
when 11
# remove country code only if phone starts with the allowed country code
if country_codes.include?(clean_value[0].to_i)
clean_value[1..-1]
else
clean_value
end
else clean_value
end
end
# usage
# app/api/phones_controller.rb
class Api::PhonesController < Api::ApplicationController
def exists
if params[:q]
clean_value = Filters.phone(params[:q])
...
end
end
end
Second example - helper for flash messages
# lib/flash_helper.rb
module FlashHelper
def flash_translate(key, options = {})
scope = [:flash, :controllers]
scope += params[:controller].split('/')
scope << params[:action]
t(key, {:scope => scope}.merge(options))
end
end
# app/application_controller.rb
class ApplicationController < ActionController::Base
include FlashHelper
end
# usage
# app/your_controller.rb
class YourController < ApplicationController
def create
#object = Object.new(params[:object])
if #object.save
flash[:success] = flash_translate(:success)
...
end
end
end
Note: do not forget to add lib dir to the autoload paths. In config/application.rb add/modify this line config.autoload_paths += %W(#{config.root}/lib).
So for me the answer is lib directory.
Starting from Rails 4 there is a dedicated folder for it app/controllers/concerns. So you can create a module there and then include it in a specific controller(s) or in ApplicationController if you need it available throughout all your controllers.
In case those methods are used in numerous controllers, I would define them in application_controller.rb. Every controller will inherits from it and will be capable to use any method defined there

Calling a method from another controller

If I've got a method in a different controller to the one I'm writing in, and I want to call that method, is it possible, or should I consider moving that method to a helper?
You could technically create an instance of the other controller and call methods on that, but it is tedious, error prone and highly not recommended.
If that function is common to both controllers, you should probably have it in ApplicationController or another superclass controller of your creation.
class ApplicationController < ActionController::Base
def common_to_all_controllers
# some code
end
end
class SuperController < ApplicationController
def common_to_some_controllers
# some other code
end
end
class MyController < SuperController
# has access to common_to_all_controllers and common_to_some_controllers
end
class MyOtherController < ApplicationController
# has access to common_to_all_controllers only
end
Yet another way to do it as jimworm suggested, is to use a module for the common functionality.
# lib/common_stuff.rb
module CommonStuff
def common_thing
# code
end
end
# app/controllers/my_controller.rb
require 'common_stuff'
class MyController < ApplicationController
include CommonStuff
# has access to common_thing
end
Try and progressively move you methods to your models, if they don't apply to a model then a helper and if it still needs to be accessed elsewhere put in the ApplicationController
If you requirement has to Do with some DB operations, then you can write a common function (class method) inside that Model. Functions defined inside model are accessible across to all the controllers. But this solution does to apply to all cases.
I don't know any details of your problem, but maybe paths could be solution in your case (especially if its RESTful action).
http://guides.rubyonrails.org/routing.html#path-and-url-helpers

how do you put before filters in modular controllers?

I have several controllers that are in a module:
class SoapTest::DashboardController < ApplicationController
class SoapTest::TestCasesController < ApplicationController
etc.
I want to be able to check if a user has certain permissions for a module, and since I don't have a "parent" controller where the above ones inherit, i thought to put the check in a before filter in applications. But I can't seem to get the module name:
in application controller, i have:
before_filter :check_company_features
def check_company_features
puts controller_name
end
but controller_name just returns "dashboard". I need to get the "SoapTest" clause
Be attention, what you currently call modules actually are namespaces.
The reason why controller_name returns only the class name (and not the fully qualified name) is because Rails explicitly strips the namespaces. You can get them by calling the Ruby #name method on the controller class.
class SoapTest::DashboardController < ApplicationController
before_filter :check_company_features
def check_company_features
puts controller_name
# => "dashboard_controller"
puts self.class.name
# => "SoapTest::DashboardController"
end
end
There are several String inflection methods you can call on the #name to get the formatted version.
However, I strongly encourage you to use a namespaced main controller.
Instead of using
class SoapTest::DashboardController < ApplicationController
you can extend a SoapTest::ApplicationController
class SoapTest::ApplicationController < ApplicationController
before_filter :check_company_features
def check_company_features
# ...
end
end
class SoapTest::DashboardController < SoapTest::ApplicationController
end

Resources