The Action Mailer Basics guide states:
You could use a before_action to populate the mail object with defaults, delivery_method_options or insert default headers and attachments.
However, I don't see how it could be possible to set delivery_method_options by doing this, as this value is only fetched from the headers argument explicitly passed to the mail method. Am I missing something, or is the documentation wrong?
The Action Mailer Basics guide was, in fact, misleading. I submitted a PR to fix the guide.
In order to define "per-mailer" delivery options, you need to use an after_action callback:
class UserMailer < ApplicationMailer
after_action :set_delivery_options
private
def set_delivery_options
# You have access to the mail instance,
# #business and #user instance variables here
if #business && #business.has_smtp_settings?
mail.delivery_method.settings.merge!(#business.smtp_settings)
end
end
end
Related
I am trying to log additional custom fields using Logstasher gem.
I need to log some response parameters, but logstasher does not support itself logging response params using configuration, therefore I put the code into after_action method in ApplicationController.
ApplicationController
after_action :set_logstasher_params
def set_logstasher_params
if LogStasher.enabled?
res_params = JSON.parse(response.body.as_json)
LogStasher.add_custom_fields do |fields|
fields[:res_params] = res_params
end
end
end
This is logstasher initializer
initializer/logstasher.rb
if LogStasher.enabled?
LogStasher.add_custom_fields do |fields|
fields[:request_params] = request.filtered_parameters
LogStasher::CustomFields.add(:myapi_runtime)
end
LogStasher.add_custom_fields_to_request_context do |fields|
fields[:request_params] = request.filtered_parameters
end
end
The problem is next:
After starting rails server, first request I send, logs only parameters which are indicated in logstasher.rb except parameters added in ApplicationController.
But After that every request logs everything indicated logstasher.rb as well as ApplicationController method.
Does anyone have idea why first time does not log response parameters from ApplicationController method?
The callback is like below.
class User
include UserSettable
before_action :set_user
def show
[Something with #user]
end
end
class Group
include UserSettable
before_action :set_user
...
end
And the set_user is like below. And this method is defined in the controller concern.
module UserSettable
extend ActiveSupport::Concern
def set_user
#user = User.find(params[:id])
end
end
I have three questions.
How do I pass params to callback method?
Can the instance variable defined in concern be used in controller?
Does this implementation deviate from the usual usage of rails?
How do I pass params to callback method?
params of request will automatically be available to action. You don't need to pass it explicitly. Your code snippet is perfect use case of how it should be used.
Can the instance variable defined in concern be used in controller?
You mean controller's concern. I think the instance variable defined there should be available. Can you give an example of what you are trying to achieve?
Does this implementation deviate from the usual usage of rails?
No
Let's say I am using the Pundit gem for authorization. I have the following controller:
class BlogsController < ApplicationController
before_action :check_authorization
...
private
def check_authorization
authorize :blog
end
end
Pundit looks at the argument passed in, :blog and thus infers there is a BlogPolicy class. It then looks for the corresponding action name with a question mark, such as: BlogPolicy#index?.
If I want to look up the authorization on a specific resource I would just do this for that check_authorization method:
def check_authorization
authorize #blog
end
Again, no problem. Pundit looks at the class of the resource, Blog, and then looks for the BlogPolicy class.
Example: The app could call the BlogPolicy#show? method and pass in that #blog resource.
Here is the issue: what do I do when I want to authorize on a specific controller name AND authorize on a specific resource, but that resource does not sync up with the controller name?
Example:
class SomeOtherBlogsController < ApplicationController
before_action :check_authorization
...
private
def check_authorization
authorize :some_other_blog, #blog
end
end
The above code doesn't work, but hopefully it shows what I am trying to do. Lets pretend this is happening on SomeOtherBlogsController#show action.
I am trying to have pundit find the SomeOtherBlogPolicy#show? method,
Within that Policy method, I want to check the authorization access of the #blog resource as well.
Hopefully the issue is apparent. Since the resource class does not sync up with the controller name, It seems I am not able to do the above. So If I have the same resource used in various different controllers, I'm out of luck. I wouldn't be able to check authorization of the resource in those various controller contexts. Is this impossible to do with Pundit?
Update:
In the controller, I have also attempted to directly call the Policy method like so:
SomeOtherBlogPolicy.new(current_user, #blog).show?
However, calling that raised a Pundit::AuthorizationNotPerformedError. So it appears that more happens in that authorize method than just returning true or false.
You can manually specify the resource class for a model by:
class Blog
def self.policy_class
SomeOtherBlogPolicy
end
end
Unfortunately its not possible to specify the policy class from the controller when calling authorize...
This was true when I originally wrote the answer. v2.00 added a policy_class option to authorize:
authorize(#blog, policy_class: SomeOtherBlogPolicy)
So the workaround in my original answer is no longer needed.
This is now a built in feature in Pundit Version 2.0.0. The documentation is updated with the following:
You can pass an argument to override the policy class if necessary. For example:
def create
#publication = find_publication # assume this method returns any model that behaves like a publication
# #publication.class => Post
authorize #publication, policy_class: PublicationPolicy
#publication.publish!
redirect_to #publication
end
Is it possible to override the policy class in the view as well? I tried doing this but got an error unknown keyword: :policy_class
<% if policy(#publication, policy_class: PublicationPolicy).create? %>
Im working with a medium sized Rails application and I do this in every controller:
def create
#object = Model.new(params[:model].merge(editing_user: current_user))
...
end
def update
#object = Model.find(params[:id])
#object.editing_user = current_user
...
end
Setting the editing user over and over again is not DRY. I thought about cleaning this up with an observer but it would need access to the current user. Observers do not have access to the current user, neither should they (Law of Demeter).
Any suggestions how to DRY this up between controllers?
class ApplicationController < ActionController::Base
before_filter :init_request
def init_request
params[:editing_user] = current_user
end
end
I like using decent_exposure to dry up my controllers. It automatically finds or initializes a model instance, based on whether an :id was passed as a param, and it assigns the attributes from params[:model].
To finish drying up your code, you could use the new strategy support (see the end of the readme) to automatically set the editing_user attribute on your model.
You could try an after_filter for this. Perhaps something like so:
class ApplicationController < ActionController::Base
after_filter :set_editing_user
def set_editing_user
#object.update_attribute(:editing_user, current_user) if #object && current_user
end
The difficulty, of course, is that you'll be saving the object twice per call. Generally though creations and updates don't happen so frequently that two database commits is a serious problem, but if you expect to be the next Twitter -- with massive database insertion load -- it could be an issue.
You could also possibly set this in a before_filter, but then you'd have to find or set the object in a previous before_filter. Otherwise #object will always be nil and the before_filter will never fire. You can use the filter ordering methods prepend_before_filter and append_before_filter to ensure the correct sequencing of these filters.
I have an object in ruby on rails for #user which contains username, password, etc
How can I ensure that the values are kept throughout all views?
Thanks
If you set it up as follows:
class ApplicationController < ActionController::Base
before_filter :set_user
protected
def set_user
#user = User.find_by_id(session[:user_id])
end
end
Then in all of controller, since they all inherits from ApplicationController, will have the #user value set.
Note: this will set the #user to nil if the session[:user_id] as not been set for this session.
For more on filters and the :before_filter, check this link out: Module:ActionController::Filters::ClassMethods
I take it you want some sort of user sustem? logged in and tracking all over your system?
AuthenticatedSystem is something that can help you. there is a lot of documentation out their that will tell you exactly how to setup an environment that uses it. I personally use if for several systems I've made.
In your ApplicationController, add your object to the session and create a variable for it. Add a before_filter that calls the method that does that.