Recently I was pleasantly surprised that my pre_render methodology still works with Rails 4.1.4, which led me to re-visit and re-answer for myself the question of why it is helpful.
Consider the following Account model. To properly create an account, we need to associate it with a user record:
class Account < ActiveRecord::Base belongs_to :user validates :user, :presence => true end
To do so, the following AccountsController#new is necessary:
class AccountsController < AuthenticatedController before_action :set_account, only: [:show, :edit, :update, :destroy] # GET /accounts/new def new @account = Account.new # Will be used by view to populate a # user drop down @users = User.all.order(:last_name) end # GET /accounts/1/edit def edit # Will be used by view to populate a # user drop down @users = User.all.order(:last_name) end # POST /accounts # POST /accounts.json def create @account = Account.new(account_params) respond_to do |format| if @account.save format.html { ... } format.json { ... } else format.html do # Needed here again to populate the user # drop down in error case @users = User.all.order(:last_name) render action: 'new' end format.json { ... } end end end # PATCH/PUT /accounts/1 # PATCH/PUT /accounts/1.json def update respond_to do |format| if @account.update(account_params) format.html { ... } format.json { ... } else format.html do # Needed here again to populate the user # drop down in error case @users = User.all.order(:last_name) render action: 'edit' end format.json { ... } end end end end
The same pattern needs to be repeated for the edit/update actions as well. Which means there are 4 different code paths in which @users needs to be initialized. This problem will grow for models with more complex relationships as well. So wouldn't it be nice if we can have a central place where @user is initialized? We can accomplish this using the #pre_render approach to be DRY-ier:
class AccountsController < AuthenticatedController before_action :set_account, only: [:show, :edit, :update, :destroy] # GET /accounts/new def new @account = Account.new end # GET /accounts/1/edit def edit end # POST /accounts # POST /accounts.json def create @account = Account.new(account_params) respond_to do |format| if @account.save format.html { ... } format.json { ... } else format.html { render action: 'new' } format.json { ... } end end end # PATCH/PUT /accounts/1 # PATCH/PUT /accounts/1.json def update respond_to do |format| if @account.update(account_params) format.html { ... } format.json { ... } else format.html { render action: 'edit' } format.json { ... } end end end protected def pre_render(action) case action when :edit, :new # Will be used by view to populate a # user drop down @users = User.all.order(:last_name) end end end
Pretty nifty!
No comments:
Post a Comment