controller concerns in rails 4, sharing actions - ruby-on-rails

i am trying to share an action between controllers using a concern like for example:
module Backend
module Exportable
extend ActiveSupport::Concern
def show
respond_to do |format|
format.xls { set_excel_headers "#{controller_name.classify}_#{params[:id]}_#{Time.now.to_i.to_s}.xls" }
end
end
end
end
Do you see any problems with this?
Should I never shared default actions from rails through concerns?

you have to create a file in concerns directory (named same as your module : backend.rb)
and here is your code :
module Backend
def show
respond_to do |format|
format.xls { set_excel_headers "#{controller_name.classify}_#{params[:id]}_#{Time.now.to_i.to_s}.xls" }
end
end
end
So in your controller you add :
extend Backend

Related

Rails extend Module in controller

I am trying to write a module and I want to replace that module with my action in my controller. For that I have created a module called test inside my controller folder which is. Where I want to put my action my controller action code is:
def test
rain_fall_type = "test"
year = ""
compare = params[:compare]
respond_to do |format|
format.html { render json: rain_fall_type }
end
end
I want to put this code inside my module code I have added this code into my module whose code is:
module Test
def test
rain_fall_type = "params[:rain_fall_type]
views = params[:views]"
year = ""
compare = params[:compare]
respond_to do |format|
format.html { render json: rain_fall_type }
end
end
end
And I am trying to extend this into my controller so I am putting extend Test into my controller but I am getting this error:
The action 'test' could not be found for ProductionProductivity7sController
When I remove def test from my module and put this code in controller like this:
def test
extend Test
end
And I remove def test from module and changed it to:
module Test
rain_fall_type = "params[:rain_fall_type]
views = params[:views]"
year = ""
compare = params[:compare]
respond_to do |format|
format.html { render json: rain_fall_type }
end
end
When I am doing this I am getting this error:
undefined local variable or method `params' for Test:Module
What should I do to just replace my test action into my module.
This is a good task for concerns, here is an example:
your controller code:
class ProductionProductivity7sController < ApplicationController
include Concerns::MyAwesomeModule
end
your module placed in app/controllers/concerns/my_awesome_module.rb:
module Concerns
module MyAwesomeModule
extend ActiveSupport::Concern
included do
# here you can use controller level methods
# like `before_action`
end
def the_action_name
# your logic goes here
# you can use all variables from original controller
# for e.g. `params`, `render` and so on.
end
end
end

Extending Rails Engine controller method without duplicating it

How to extend a controller method from a Rails Engine without having to duplicate the whole thing?
Trying to extend https://github.com/radar/forem/blob/rails4/app/controllers/forem/forums_controller.rb -- app/decorators/controllers/forem/forums_controller_decorator.rb:
Ideal
Forem::ForumsController.class_eval do
def show
# A simple `include` here or something?
# New code goes here...
end
end
Current
Forem::ForumsController.class_eval do
def show
# Repeat ALL the code from:
# https://github.com/radar/forem/blob/rails4/app/controllers/forem/forums_controller.rb
authorize! :show, #forum
register_view
#topics = if forem_admin_or_moderator?(#forum)
#forum.topics
else
#forum.topics.visible.approved_or_pending_review_for(forem_user)
end
#topics = #topics.by_pinned_or_most_recent_post
# Kaminari allows to configure the method and param used
#topics = #topics.send(pagination_method, params[pagination_param]).per(Forem.per_page)
respond_to do |format|
format.html
format.atom { render :layout => false }
end
# New code goes here...
end
end
We use this gem for multiple applications and engines to do exactly what you want:
https://github.com/EPI-USE-Labs/activesupport-decorators
I could extend a controller method from a Rails Engine without having to duplicate code using alias_method
module ValueSets
SetsController.class_eval do
def new_with_authorize
new_without_authorize
authorize #value_set
end
alias_method :new_without_authorize, :new
alias_method :new, :new_with_authorize
end
end

How to disable ActionMailer in rails_admin?

I use ActionMailer to send different notifications to user. I use Model callbacks for this.
When I do changes as admin, I don't want any email to be sent to client.
How can I disable ActionMailer in RailsAdmin?
Actually, I'd like to provide an ability to admin to turn on/off emails.
Thanks
Triggering your mailers in the model lifecycle is not recommended IMHO. The recommended approach would be to trigger the mailers from the controller.
If you want to achieve separation of concerns in your controller and not pollute your controller code with mailer calls, you can use a combination of ActiveSupport::Notifications and controller after_filter to extract the mailer logic into its own module.
module MailerCallbacks
module ControllerExtensions
def self.included(base)
base.after_filter do |controller|
ActiveSupport::Notifications.instrument(
"mailer_callbacks.#{controller_path}##{action_name}", controller: controller
)
end
end
end
module Listener
def listen_to(action, &block)
ActiveSupport::Notifications.subscribe("mailer_callbacks.#{action}") do |*args|
event = ActiveSupport::Notifications::Event.new(*args)
controller = event.payload[:controller]
controller.instance_eval(&block)
end
end
end
end
Let's assume you want to refactor the following controller using our module created above:
class PostsController < ApplicationController
def create
#post = Post.new permitted_params
respond_to do |format|
if #post.save
PostMailer.notify(#post).deliver
format.html { redirect_to #post, notice: 'Successfully created Post' }
else
format.html { render action: 'new' }
end
end
end
end
Take the following steps:
Create an initializer to register the controller extension:
# config/initializers/mailer_callbacks.rb
ActiveSupport.on_load(:action_controller) do
include MailerCallbacks::ControllerExtensions
end
In the same or a separate initializer, create a class and extend the Listener module to register your callbacks:
# config/initializers/mailer_callbacks.rb
class MailerListeners
extend MailerCallbacks::Listener
# register as many listeners as you would like here
listen_to 'posts#create' do
PostMailer.notify(#post).deliver if #post.persisted?
end
end
Remove mailer code from your controller.
class PostsController < ApplicationController
def create
#post = Post.new permitted_params
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Successfully created Post' }
else
format.html { render action: 'new' }
end
end
end
end
Essentially we have created an observer on the controller actions and registered our mailer callbacks with the controller instead of tying it into the model lifecycle. I personally consider this approach cleaner and easier to manage.
You should handle it on callback method.
def call_back_name
if is_rails_admin?
#Do nothing
else
#send mail
end
end
Let's separate this question into 2 parts:
Disable email
Check this answer
Integration with Rails Admin
You can add it to Rails Admin Navigation part and put it into separate controller for admin mailer turn on/off. More info here.

Rails Authority gem, trouble with 'show' action

It's the first time I'm using this gem and it's driving me crazy with something as simple as authorize the showaction only for the resource owner.
I tried different ways, configuring the controller mapping and actions, but always get the unauthorized message for show, other actions work as they should.
It seems that showis not getting it's way to the ApplicationAuthorizer.
This is how it's configured:
class EnterpriseAuthorizer < ApplicationAuthorizer
# This works
def self.creatable_by?(user)
user.is_enterpriser?
end
# This doesn't
def readable_by?(user)
true # Just for testing
end
end
class EnterprisesController < ApplicationController
authorize_actions_for Enterprise
def show
#enterprise = Enterprise.find(params[:id])
respond_to do |format|
format.html
format.json { render json: #enterprise }
end
end
I have include Authority::UserAbilities in User and include Authority::Abilities in the Enterprise model. And User has_one :enterprise
Any idea? Thinking seriously about rolling back to cancan.
Thanks in advance.
Authority has different ways of checking permissions. For collection-based actions (e.g. new, create, index), you use authorize_actions_for Model.
For instance-based actions (e.g. edit, update, show, delete), you must call authorize_action_for #instance.
Change your code to this and it should work.
class EnterprisesController < ApplicationController
authorize_actions_for Enterprise
def show
#enterprise = Enterprise.find(params[:id])
authorize_action_for #enterprise
respond_to do |format|
format.html
format.json { render json: #enterprise }
end
end
end
If you want a less messy way to do this, put the
#enterprise = Enterprise.find(params[:id])
authorize_action_for #enterprise
into a before filter that's called by each instance action.

RoR: Super and Sub classing controllers

So.. i have a method in a super controller that is the same as one in the sub controller.. all except for the redirect_to if the item doesn't save..
subclass method:
def create
some logic
respond_to do |format|
if #template_object.save
format.html { redirect_to({:controller=>:template_objects,:action=>:build,:id=>#template_object}) }
..
end
super method:
def create
some logic
respond_to do |format|
if #template_object.save
format.html { redirect_to({:controller=>:objects,:action=>:build,:id=>#object}) }
..
end
what is the best way to go about this?
Do you have redirect_to in both super and sub class? In that case you might need to use a flag, like a session variable, to decide whether to use or skip redirect_to in the super class.
Devise uses a similar technique, for example, when we need to redirect_to a specific page after signing in using devise.
your super class
def method
...some logic...
if !session[:redirect_var].nil?
session.delete :redirect_var
redirect_to ....
end
end
your sub class
def method
session[:redirect_var] = 'skip_redirect' # or whatever, just create a session variable to use as a flag
super
...some method...
redirect_to ....
end
The usual "object-oriented" approach to this is to create a method that both can call, then define it differently in each of them:
def redirect_to_completed_template(template_object)
redirect_to(...)
end
The idea is to allow sub-classes to re-define only the portion of the functionality they require. This is why you will often see specific features broken out as functions even when what they do isn't especially complicated.

Resources