I want to create a method like current_user for devise's current resources.
Suppose I have two resources like User and Admin and devise is associated with both. So as usual it dynamically creates it's default methods like current_user and current_admin.
It creates it by defining like this in file lib/devise/controllers/helpers.rb:
def current_#{mapping}
How can I add a new method like this to it's dynamic methods.
I want to implement it with devise methods, so that when devise initializes then my method is also initialize with same mapping name.
Copy this code into your application controller. and your problem will solved.
Devise.mappings.each do |mapping, obj|
define_method "current_#{mapping}_email" do
eval("current_#{mapping}.email if current_#{mapping}")
end
helper_method "current_#{mapping}_email"
On view page/controller used according to your resource name
like if you have resource_name as user then its 'current_user_email'
or if have admin then 'current_admin_email'
This works!
I added a custom method current_user_email as follows!
Add a file devise_ext.rb in config/initializers:
And in that file, add your custom methods in this way:
module Devise
module Controllers
# Those helpers are convenience methods added to ApplicationController.
module Helpers
def self.define_helpers(mapping) #:nodoc:
mapping = mapping.name
class_eval <<-METHODS, __FILE__, __LINE__ + 1
def authenticate_#{mapping}!(opts={})
opts[:scope] = :#{mapping}
warden.authenticate!(opts) if !devise_controller? || opts.delete(:force)
end
def #{mapping}_signed_in?
!!current_#{mapping}
end
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
def #{mapping}_session
current_#{mapping} && warden.session(:#{mapping})
end
def current_#{mapping}_email
#current_#{mapping}.email if #current_#{mapping}
end
METHODS
ActiveSupport.on_load(:action_controller) do
helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session", "current_#{mapping}_email"
end
end
end
end
end
P.S. I don't think this is the best way to do this. But it works. I tried this in one of my applications! You can use this code till I find a better way to do this :)
Related
I have a helper module ModelHelper. I want to use user_signed_in? method inside that helper module. But it shows error. How can I call this method inside helper file.
Method user_signed_in? defined in the Devise::Controllers::Helpers::ClassModule module. Long story short it just checks if scope authenticated in warden. So you can try to check it without Devise helpers
def #{mapping}_signed_in?
!!current_#{mapping}
end
def current_#{mapping}
#current_#{mapping} ||= warden.authenticate(scope: :#{mapping})
end
I think that you can use current_user.present?
Anyway, rails helpers are very ugly and I advise to don't use it at all
I have 2 controllers in rails with different authentications schemes,
but they do almost the same.
What is the best way in rails to encapsulate
the logic of a controller in another class or helper?
Sample:
def ControllerA < BasicAuthController
def create
blablacode
end
end
def ControllerB < TokenAuthController
def create
blablacode
end
end
Whats the proper way to do this? create a model with the code?
Create a helper? other?
The simplest thing is to make a module and then include it into the other controllers:
module ControllerMixin
def create
blablacode
end
end
The remaining question, though, is where to put this code such that it is works with Rails autoloader, since it needs to be loaded before the controllers. One way to do it would be to write the module to a file in the lib/ directory, then add that to the autoload paths (see auto-loading-lib-files-in-rails-4
Why don't you enable both schemes for a single controller? Especially if the only difference is Authentication. You could have two app/controllers/concerns to encapsulate both authentication methods and include Auth1 and include Auth2 for a single controller who is only responsible for whatever resource it manages.
Otherwise, services are the best approach to encapsulate controller logic.
Create a folder called services in your app folder and write PORO classes here. Say you have a few places in your app where you want to pay for stuff via make Stripe.
# app/services/stripe_service.rb
module StripeService
def customer(args)
...
end
def pay(amount, customer)
...
end
def reverse(stripe_txn_id)
...
end
end
# controller
StripeService.customer(data)
=> <#Stripe::Customer>
Or if you only need to do one thing.
# app/services/some_thing.rb
module SomeThing
def call
# do stuff
end
end
# controller
SomeThing.call
=> # w/e
If you need an object with multiple reponsibilities you could create a class instead.
class ReportingService
def initialize(args)
...
end
def query
...
end
def data
...
end
def to_json
...
end
end
https://blog.engineyard.com/2014/keeping-your-rails-controllers-dry-with-services
I do it something like this:
#app/services/my_app/services/authentication.rb
class MyApp::Services::Authentication
class < self
def call(params={})
new(params).call
end
end # Class Methods
#==============================================================================================
# Instance Methods
#==============================================================================================
def initialize(params)
#params = params
end
def call
... do a lot of clever stuff
... end by returning true or false
end
private
def params() #params end
end
Then:
class FooController < ApplicationController
before_action :authenticate
def authenticate
redirect_to 'some_path' unless MyApp::Services::Authenticate.call(with: 'some_params')
end
end
Short answer, i choose to create a Helper.
From all the suggestions in the answers
Create a Module:
Seems correct but it didnt feel right to have logic outside
the app directory. This wasnt an external module or library but
something very related to the logic of my app.
Integrate diferents authentications in one controller:
Was a good suggestion but i have to change all the logic of my app.
Create a Helpers:
It seems to me the better solution, i had the code on a helper, and
is inside the app directory, very near from the other logic.
I'm working through RailTutorial.org, and in section 8, it has you create helper methods and use them in your controller. I am not able to access any of them
Controller line: if current_student.admin?
Helper method:
module SessionsHelper
[...]
def current_student
if session[:student_id]
#current_student ||= Student.find_by(id: session[:student_id])
elsif cookies.signed[:student_id]
student = Student.find_by(id: cookies.signed[:student_id])
if student && student.authenticated?(cookies[:remember_token])
log_in student
#current_student = student
end
end
end
[...]
end
Thanks!
Helper methods are only available within views. If you need it within views and controllers, you must define it within a controller, then declare it a helper method as well:
class ApplicationController
def current_student
# ...
end
helper_method :current_student
end
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
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