32
Simplify Your Rails Controllers Vengeance With a

Simplify Your Rails Controllers With a Vengeance

Embed Size (px)

DESCRIPTION

A step-by-step process for simplifying controller code in Ruby on Rails applications, with code examples. First presented at Philly.rb September 2014.

Citation preview

Page 1: Simplify Your Rails Controllers With a Vengeance

SimplifyYour Rails Controllers

VengeanceWith a

Page 2: Simplify Your Rails Controllers With a Vengeance

Brian Auton

[email protected]/brianautongithub.com/brianauton

Page 3: Simplify Your Rails Controllers With a Vengeance

I know what's wrongwith your Rails app...

Page 4: Simplify Your Rails Controllers With a Vengeance

The controllers(they're too complex)

Page 5: Simplify Your Rails Controllers With a Vengeance

1. Less responsibilities

2. Shorter methods

3. Less duplication

Page 6: Simplify Your Rails Controllers With a Vengeance

1. Less responsibilities

2. Shorter methods

3. Less duplication

Controllers are just code.

Page 7: Simplify Your Rails Controllers With a Vengeance

1. Less responsibilities

User's Intent <=> Business Logic

Page 8: Simplify Your Rails Controllers With a Vengeance

1. Less responsibilities

User's Intent <=> Business Logic

How? REST

Page 9: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :clients do get :download_pdf, on: :memberend

clients_controller.rbdef download_pdf client = Client.find params[:id] send_data client.to_pdf, type: :pdfend

def show @client = Client.find params[:id]end

Page 10: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :clients

clients_controller.rbdef show @client = Client.find params[:id] respond_to do |format| format.html {} format.pdf do send_data @client.to_pdf, type: :pdf end endend

Page 11: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :orders do post :submit, on: :memberend

orders_controller.rbdef submit order = Order.find params[:id] PaymentGateway.process order flash[:notice] = “Payment successful” order.update_attribute :status, :completerescue PaymentGateway::Error => e order.update_attribute :status, :failed redirect_to order, alert: “Error: #{e}”end

Page 12: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :ordersresources :payment_attempts, only: :create

payment_attempts_controller.rbdef create attempt = PaymentAttempt.create payment_attempt_params PaymentGateway.process attempt.order flash[:notice] = “Payment successful” attempt.update_attribute :status, :completerescue PaymentGateway::Error => e attempt.update_attribute :error, e.message redirect_to attempt.order, alert: “Error: #{e.message}”end

Page 13: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :regions do put :sort, on: :collectionend

regions_controller.rbdef sort params[:region].each do |id, position| Region.find(id).update_attribute :position, position endend

Page 14: Simplify Your Rails Controllers With a Vengeance

routes.rbresources :regionsresources :region_collections, only: :update

region_collections_controller.rbdef update region_collection_params.each do |attributes| Region.find(attributes[:id)].update_attributes attributes endend

private

def region_collection_params params.require(:region_collection).permit [:id, :position]end

Page 15: Simplify Your Rails Controllers With a Vengeance

2. Shorter methods

Page 16: Simplify Your Rails Controllers With a Vengeance

2. Shorter methods

How? Delegate to models

Page 17: Simplify Your Rails Controllers With a Vengeance

payment_attempts_controller.rbdef create attempt = PaymentAttempt.create payment_attempt_params PaymentGateway.process attempt.order flash[:notice] = “Payment successful” attempt.update_attribute :status, :completerescue PaymentGateway::Error => e attempt.update_attribute :error, e.message redirect_to attempt.order, alert: “Error: #{e.message}”end

Page 18: Simplify Your Rails Controllers With a Vengeance

payment_attempt.rbclass PaymentAttempt < ActiveRecord::Base before_save do PaymentGateway.process order rescue PaymentGateway::Error => e update_attribute :error, e.message end

def successful? error.present? endend

Page 19: Simplify Your Rails Controllers With a Vengeance

payment_attempts_controller.rbdef create @attempt = PaymentAttempt.create payment_attempt_params if @attempt.successful? flash[:notice] = “Payment successful.” else flash[:alert] = “Error: #{@attempt.error}” redirect_to @attempt.order endend

Page 20: Simplify Your Rails Controllers With a Vengeance

users_controller.rbdef update @user = User.find params[:id] @user.update_attributes user_params @user.address.update_attributes address_params ...end

def user_params params.require(:user).permit :name, :emailend

def address_params params.require(:address).permit :city, :state, :zipend

Page 21: Simplify Your Rails Controllers With a Vengeance

users_controller.rbdef update @user = User.find params[:id] @user.update_attributes user_params ...end

def user_params params.require(:user).permit :name, :email, { address_attributes: [:city, :state, :zip] }end

user.rbhas_one :addressaccepts_nested_attributes_for :address

Page 22: Simplify Your Rails Controllers With a Vengeance

3. Reduce Duplication

Page 23: Simplify Your Rails Controllers With a Vengeance

3. Reduce Duplication

How? It's just code.

Page 24: Simplify Your Rails Controllers With a Vengeance

widgets_controller.rbdef new @widget = Widget.newend

def show @widget = Widget.find params[:id]end

def update @widget = Widget.find params[:id] ...end

Page 25: Simplify Your Rails Controllers With a Vengeance

widgets_controller.rbbefore_action :build_widget, only: [:new]before_action :find_widget, only: [:show, :update]

private

def build_widget @widget = Widget.newend

def find_widget @widget = Widget.find params[:id]end

Page 26: Simplify Your Rails Controllers With a Vengeance

application_controller.rbprotected

def build_member set_member_instance_variable collection.newend

def find_member set_member_instance_variable collection.find(params[:id])end

def set_member_instance_variable(value) variable_name = “@#{controller_name.singularize}” instance_variable_set variable_name, (@member = value)end

def collection controller_name.classify.constantizeend

Page 27: Simplify Your Rails Controllers With a Vengeance

assets_controller.rbdef update if @asset.update_attributes asset_params redirect_to @asset, notice: “Asset updated” else flash[:alert] = @asset.errors.full_messages.join(', ') render :edit endend

surveys_controller.rbdef update if @survey.update_attributes survey_params redirect_to @survey, notice: “Survey updated” else flash[:alert] = @survey.errors.full_messages.join(', ') render :edit endend

Page 28: Simplify Your Rails Controllers With a Vengeance

assets_controller.rbdef update @asset.update_attributes asset_params respond_to_update @assetend

surveys_controller.rbdef update @survey.update_attributes survey_params respond_to_update @surveyend

application_controller.rbdef respond_to_update(model) if model.valid? type = model.class.name.humanize redirect_to model, notice: “#{type} updated” else flash[:alert] = model.errors.full_messages.join(', ') render :edit endend

Page 29: Simplify Your Rails Controllers With a Vengeance

shared_rest_actions.rbmodule SharedRestActions def self.included(base) base.before_action :build_member, only: [:new, :create] base.before_action :find_collection, only: :index base.before_action :find_member, except: [:index, :new, :create] end

def index end

def update member_params = send “#{controller_name.singularize}_params” @member.update_attribute member_params respond_to_update @member end

...end

Page 30: Simplify Your Rails Controllers With a Vengeance

assets_controller.rbclass AssetsController < ApplicationController include SharedRestActions before_action :paginate, only: :index before_action :sort_by_date, only: :indexend

surveys_controller.rbclass SurveysController < ApplicationController include SharedRestActions before_action :build_member, only: :indexend

Page 31: Simplify Your Rails Controllers With a Vengeance

1. Less responsibilities (REST)

2. Shorter methods (Models)

3. Less duplication

Recap:

Page 32: Simplify Your Rails Controllers With a Vengeance

Go try it out!

[email protected]/brianautongithub.com/brianauton