Default serializer render options in Rails controller - ruby-on-rails

I am using Active Model Serializers in a rails project and have a user object that needs to be passed in from the controller to the serializer like this:
# Note the 'user:' option that will be accessible inside
# the serializer with #options[:user]
def show
render json: #some_object, user: current_user
end
def index
render json: SomeObject.all, user: current_user
end
This is good enough for what I am trying to do, but it is not very DRY and results in render statements that are littered with options. When those options change, I need to go back and manually remove/modify them across all actions.
My question is: Is there a way to set a list of default options for a render call at the controller level, instead of manually putting the options in every single controller action?

This can be accomplished by adding this method to your controller:
def default_serializer_options
{user: current_user}
end
You can then access it from within the serializer via options[:user]

Related

Why Rails allows double rendering?

class SomeController < ApplicationController
before_action :api_limit
def new
if user.can_access_foo?
render 'foo'
end
if user.can_access_bar?
render 'bar'
end
if user.can_access_hello?
render 'hello'
end
end
def api_limit
render 'exceed_limit_error'
# code in action will not be executed.
end
end
When render or redirect_to exists in before_filter, action will not be executed. In short, there is no risk of double rendering.
See the Rails Guide on controllers :
If a "before" filter renders or redirects, the action will not run. If there are additional filters scheduled to run after that filter, they are also cancelled.
However, why Rails allow double rendering in an action?
Take following code as example. a Double Render Exception will raised by Rails when user can access foo, bar, or hello.
class SomeController < ApplicationController
before_action :callback
def new
if user.can_access_foo?
render 'foo'
# From my understanding, following render should
# be ignored if this step is successfully performed.
end
if user.can_access_bar?
render 'bar'
end
if user.can_access_hello?
render 'hello'
end
end
end
Why not respond immediately and halt the request cycle when render 'foo' completes? That sounds more reasonable.
The reason why a render statement in an action doesn't return the code execution is that it is a reasonable use case that there is further (non-rendering) code to be executed after the render in the action.
The reason that a render in the before_action callback doesn't allow code execution to go into the action is because this would assume that you have actions that have code paths that neither render nor redirect (otherwise you would get a double render error). This code path in the action is a much less reasonable use case because it would rely on the "before" filter to have triggered and performed a render already.
The intention of the structure of Rails' actions and filters in the controllers is that they are not so tightly coupled. Generally a filter will not be aware of what action will run after it, and an action is not aware of what filters triggered before it ran. So to make them coordinate as to which is doing the rendering would break that loose coupling. Each action must assume that rendering is an essential part of its role, that is why it doesn't make sense for an action to run if the filter already rendered.

How do I pass parameters to a MailerPreview class in Rails?

I am learning how to send emails with ActionMailer in rails, and I am having trouble passing parameters into my OrderMailerPreview class. I need to pass an order_id into the new_order method so I can look up the appropriate order. Here is the code:
# Preview all emails at http://localhost:3000/rails/mailers/order_mailer
class OrderMailerPreview < ActionMailer::Preview
# Preview this email at http://localhost:3000/rails/mailers/order_mailer/new_order
def new_order(order_id)
#order = Shoppe::Order.find(order_id)
OrderMailer.new_order(order)
end
end
How can I get that order_id into this method?
ActionMailer::Preview is simply made to render previews with fake or seeded data and does not support param passing as far as I know. Rather it simply uses the url to locate files based on class / method. This corresponds with its intended use as a development aid.
If you need to use dynamic data in the mailer previews or for example make the preview viewable to end users you should instead create a controller which renders the mailer view.
class OrderMailerPreviewController < ApplicationController
# GET /mailers_previews/orders/:id
def show
#order = Order.find(params[:id])
render "mailers/order", layout: nil
end
end

Render page more flexible

in my current project, have a assumption that a lot of different UI will be required.
ex> there will be two clients, boogle and yumhoo, these two client want totally different view.
I made a prototype like this,
class HomeController < ApplicationController
after_action :render_ui
def index
end
def render_ui
render "#{self.controller_name}/#{ENV['CLIENT_UI']}/#{self.action_name}"
end
end
my plan was to generate the path to the view file dynamically,
but there is a big problem here,
as you all know rails automatically runs,
render 'there own contoller and action name combination'
and throws an error,
Render and/or redirect were called multiple times in this action
Is there a way to skip the default automatic render feature in rails?
after_action is called when the view has already been rendered. If you want to render a specific file, simply call render in the action itself.
class HomeController < ApplicationController
def index
render "#{self.controller_name}/#{ENV['CLIENT_UI']}/#{self.action_name}"
end
end
You may also want to consider to use the Rails 4.1 template variants feature.

How to DRY up controller code which involves views code as well?

In my Rails 3 app, I have a number of models with a boolean field disabled. In controllers for these models, I use a custom action disable to toggle the disabled field using Ajax.
Example (For client),
# routes.rb
resources :clients do
member do
get :toggle_disable, to: 'clients#disable', as: :disable
end
end
# clients_controller.rb
def disable
#client = Client.find(params[:id])
#client.update_attribute :disabled, !#client.disabled
render 'clients/update_client', format: :js
end
# update_client.js.erb
$('#client-<%= #client.id %>-details').html("<%= escape_javascript(render 'clients/client', client: #client) %>");
I have this code for at least ten resources in my application.
QUESTION
How do I go about DRYing up this code and add actions for these boolean fields dynamically? I could have gone with creating a parent controller or module but I am not sure how will I take care of the views code.
I should be able to do something like this
#clients_controller.rb
class ClientsController < ApplicationController
add_toggle_action :disable
end
Two main ways to share methods:
inheritance: define your action in ApplicationController
mixins: add your method in a module and include the module in the appropriate controllers
Since you want only some controllers to get the method, I'll head towards mixin.
Your controller action must use a view with a full path, not relative, something like:
render '/shared/clien/update', format: :js
Lastly, you'll have to define all routes.

where does this param symbol come from in Rails session

I'm reading Rails 3 Way by Obie Fernandez. He's demonstrating the use of a plugin Authlogic, and created a User and UserSession model and a UsersController and UserSessionsController.
He hasn't created any views (but he might assume some exist)
In the UserSessionsController, he creates this code
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
if #user_session.save
redirect_to user_path(current_user)
else
render :action => :new
end
end
def destroy
current_user_session.destroy
redirect_to new_user_session_path
end
end
My question relates to the create method. When he writes
UserSession.new(params[:user_session])
where is :user_session coming from? I undersdtand that UserSession.new instantiates a new object, but where do the params come from? and what names would they have?
Does it depend on something in the imaginary view? or are these params automatically generated by Rails based on the name of the Models?
params is a special hash that is passed to all actions, regardless of the type. If a given action has no parameters, then it's simply empty. It's how you can pass parameters from a page/form/URL parameters into the action. One of the most common sources of parameters are data elements from a form.
In the case of authlogic, it contains user credentials for creating the user session (username, password).
Check out the parameters section for more information.

Resources