Toggle "ActionController::Parameters.action_on_unpermitted_parameters = :raise" on specific controller methods? - ruby-on-rails

I want to use
ActionController::Parameters.action_on_unpermitted_parameters = :raise
To raise an exception unpermitted parameters are passed in, but I only want to do this on specific controller methods, instead of setting it in /config/ and having it apply to the whole environment. Is there any way to do so?

I'm not sure Wasif's answer will work properly because it never sets it back. At a minimum I think you want this:
class SomeController < ApplicationController
around_action :raise_action_on_unpermitted_parameters, only: %i[create update]
def index
# ...
end
def create
# ...
end
def update
# ...
end
private
def raise_action_on_unpermitted_parameters
begin
ActionController::Parameters.action_on_unpermitted_parameters = :raise
yield
ensure
ActionController::Parameters.action_on_unpermitted_parameters = :log
end
end
end
And even then I'm not sure you won't encounter a race condition where it's set to raise on different controller actions accidentally if you're using a multithreaded server like Puma.

You can create your own Parameters class:
class Parameters < ActionController::Parameters
self.action_on_unpermitted_parameters = :raise
end
def params
Parameters.new(super.to_unsafe_hash)
end
This relies on Rails' current implementation to check self.class.action_on_unpermitted_parameters to determine what to do.
I prefer this solution over toggling ActionController::Parameters.action_on_unpermitted_parameters during the action because you are in control where you want your custom behaviour. Other code is not affected. But it depends on your needs. Do you want the raising behaviour during some controller actions or within the controller action code?

Update: (taking inspiration from Mike's answer)
To restrict the change only for a set of controller actions and to revert it back to the original value that the class attribute was holding before the action being invoked, we can use around_action
class SomeController < ApplicationController
around_action :raise_action_on_unpermitted_parameters, only: %i[create update]
def index
# ...
end
def create
# ...
end
def update
# ...
end
private
def raise_action_on_unpermitted_parameters
original = ActionController::Parameters.action_on_unpermitted_parameters
ActionController::Parameters.action_on_unpermitted_parameters = :raise
yield
ensure
ActionController::Parameters.action_on_unpermitted_parameters = original
end
end

Related

Detect before_filter of a nonexistent action in Rails

How can I detect that I've got a before_filter of a non-existent action in Rails?
class PostsController < ApplicationController
before_action :authorize_user, only: [:kreate]
def create
end
end
This was a straightup hack, but the only way I could think of checking a before filter would be on initialize. The following checks if a controller has a before/after filter that uses a non-existant action and raises an exception message if it does
# config/initializers/before_filters.rb
# require all controllers
Dir['app/controllers/*'].each do |path|
require path.split('/').last if path.include? '.rb'
end
# get array of all controllers
controllers = ApplicationController.descendants
controllers.each do |controller|
# get all filters for this controller
filters = controller._process_action_callbacks
# get all actions under this controller
actions = controller.action_methods.to_a
filters.each do |filter|
# get all action_conditions for this filter
action_conditions = filter.instance_variable_get(:#if)
# raise error message if action used in filter not in available controller actions
action_conditions.each do |action_condition|
if actions.none? {|action| action_condition.include? action }
message = "#{controller.name} has a #{filter.kind.to_s} filter with a non-existant action (#{action_condition.scan(/'([^']*)'/)[0][0]})"
raise message
end
end
end
end
You cannot.
Thats why you need to have test cases. In this case, they would have caught it as authorize_user wouldn't have triggered as you applied it to a non-existent action due to a typo.

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

Filter to execute before render but after controller?

Suppose I have some logic in a base controller to pass information to the view to build something like a breadcrumb:
class ContextAwareController < ApplicationController
after_filter :build_breadcrumb
def build_breadcumb
#...
end
end
I want this build_breadcrumb method to run after the main controller logic, but before the view is rendered.
The above code runs too late, but a before_filter would be too early.
Can anybody suggest a way to accomplish this without explicitly calling build_breadcumb at the end of each of the actions in the child controllers?
Thanks
I had the same problem and solved it like this:
class ApplicationController < ActionController::Base
def render *args
add_breadcrumbs
super
end
end
There are also some gems to achieve this. One of them is rails3_before_render.
It works similarly to filters, for example:
class PostsController < ApplicationController
before_render :ping, :except => [:destroy]
def index; end
def new; end
def show; end
def destroy; end
private
def ping
Rails.logger.info "Ping-Pong actions"
end
end
(code snipped copied from gem documentation)
I believe rendering starts when render is called, and there's no default way to defer it. Here's one thing you could do:
filters are applied in the same order declared. So make a second after-filter that calls render with an array args stored in a class variable. Then anywhere you would normally call render, set the variable.
If we're overriding render, we're not really using the filter chain at all, so it might be simpler to determine which action we're in using the #_action_name.
StuffController < ApplicationController
def my_filter
# Do the stuff
end
def render(*args)
my_filter if #_action_name == "show"
super
end
end
You can use alias_method_chain like this
alias_method_chain :render, :before_render_action
this will create 2 methods :render_with_before_render_action and :render_without_before_render_action. If you call render, then :render_with_before_render_action will be called. You can override this method
def render_with_before_render_action(*options, &block)
<your code>
render_without_before_render_action(*options, &block)
end
If you don't want your code to be executed and you want to have default render then you should directly call the :render_without_before_render_action
You can do like this to fake a before_render:
class StuffController < ApplicationController
before_filter :my_filter, only: [:index, :show]
def my_filter
#my_filter = true
end
def _my_filter
# Do the actual stuff here
end
def render(*args)
_my_filter if #my_filter
super
end
end
Thanks to #joshua-muheim for the tip about using render

attributes and constructors in rails

I'm new to rails and don't even know if this is the correct way of solving my situation.
I have a "Club" ActiveRecords model which has a "has_many" association to a "Member" model. I want the logged in "Club" to only be able to administrate it's own "Member" so in the beginning of each action in the "Member" model I did something similar to the following:
def index
#members = Club.find(session[:club_id]).members
to access the right members. This did not however turn out very DRY as I did the same in every action. So I thought of using something equivalent to what would be called a constructor in other languages. The initialize method as I've understood it. This was however not working, this told me why, and proposed an alternative. The after_initialize.
def after_initialize
#club = Club.find(session[:club_id])
end
def index
#members = #club.members
....
does not seem to work anyway. Any pointers to why?
You have a nil object when you didn't expect it!
The error occurred while evaluating nil.members
Makes me think that the #club var isn't set at all.
Also, is this solution really a good one? This makes it hard to implement any kind of "super admin" who can manage the members in all of the clubs. Any ideas on where I am missing something?
You can use a before_filter.
Define the filter in your ApplicationController (so that you can access it from any controller).
class ApplicationController < ActionController::Base
# ..
protected
def load_members
#members = if session[:club_id]
Club.find(session[:club_id]).members
else
[]
end
end
end
Then, load the filter before any action where you need it.
For example
class ClubController < ApplicationController
before_filter :load_members, :only => %w( index )
def index
# here #members is set
end
end
Otherwise, use lazy loading. You can use the same load_members and call it whenever you need it.
class ClubController < ApplicationController
def index
# do something with members
load_members.each { ... }
end
end
Of course, you can customize load_member to raise an exception, redirect the client if #members.empty? or do whatever you want.
You want to use a before_filter for this.
class MembersController < ApplicationController
before_filter :find_club
def index
#members = #club.members
end
private
def find_club
#club = Club.find(session[:club_id])
end
end
I'm a fan of a plugin called Rolerequirement. It allows you to make custom roles and apply them by controller: http://code.google.com/p/rolerequirement/

Can we use a custom action with inherited_resources in a DRY manner?

We have a custom action (:register) for our Location model. The supporting code is very similar to a standard :update. Since inherited_resources provided a "template" for us, we copied the update code from actions.rb, changing 'update_attributes' to 'register' and the flash message reflects the different action.
This doesn't feel very DRY. We would like to utilize :update instead. Any ideas?
class LocationsController < InheritedResources::Base
def register(options={}, &block)
#TODO: copied update from actions.rb. I expect there is a better way.
# All I changed was the flash message (to reflect the action)
# and the method call on the object (update_attributes -> register)
object = resource
if object.register
set_flash_message!(:notice, '{{resource_name}} was successfully registered.')
options[:location] ||= resource_url rescue nil
respond_with_dual_blocks(object, options, true, block)
else
set_flash_message!(:error)
respond_with_dual_blocks(object, options, false, block)
end
end
Inherited resources provides helper methods for CRUD actions which you can override on your controller. The one you look for is
# Responsible for updating the resource in :update method. This allow you
# to handle how the resource is gona be updated, let's say in a different
# way then the usual :update_attributes:
#
# def update_resource(object, attributes)
# object.reset_password!(attributes)
# end
#
def update_resource(object, attributes)
object.update_attributes(attributes)
end
You override it like this:
class LocationController < ApplicationController
inherit_resources
protected
def update_resource(object, attributes)
object.register(attributes)
end

Resources