Skip to content

Instantly share code, notes, and snippets.

@iannono
Last active December 21, 2015 12:19
Show Gist options
  • Save iannono/6304863 to your computer and use it in GitHub Desktop.
Save iannono/6304863 to your computer and use it in GitHub Desktop.
notes of rails 4 patterns

models

  • controllers should be responsible for orchestrating the models. means that you should always tell objects what to do, and not ask them question about there state
# controller
if @item.publish
  flash[:notice] = "xxxx"
end
# modle
class Item < ActiveRecord::Base
  def publish
    if user == "username"
      return false
    end
    
    self.published_on = Time.now
    self.save
  end
end
  • callbacks should only be for modifying internal state and should avoid calling other domain objects
class User < ActiveRecord::Base
  before_create :set_name, :set_token
  
  protected
  # right
  def set_name
    self.name = self.login.capitalize if name.blank?    
  end
  
  # wrong
  def set_toke
    self.token = TokenGenerator.create(self)
  end
end
  • encapsulate unique business logic out of your AR models by using PORO
class UserSuspension
  def initialize(user)
    @user = user
  end
  
  def create
    @user.class.transaction do
      disapprove_user!
      disapprove_comments!
    end
  end
  
  private
    def disapprove_user!
    disapprove_comments!
end

class methods and scopes

  • extracting queries to class method class or scope
class Post < ActiveRecord::Base
  def self.recent
    where("published = ? AND published_on > ?", true, 2.days.ago)
  end
  
  # or
  scope :recent, -> { where("published = ? AND published_on > ?", true, 2.days.ago) }
end
  • use scope to always return a chainable object, this is better than class method
class Post < ActiveRecord::Base
  scope :by_author, ->(author) { where(author: author) if author.present? }
  scope :recent, -> { where('published_on > ?', 2.days.ago) }
end

# this is very useful when doing chain query even author is 'nil'
# scope will automatically ignore the nil scope
Post.by_author(nil).recent
  • rails 4 default use append(rails 3 use override) when chain scopes with same attributes, you should use merge to approve override logic
scope :active, -> { where(state: 'active') }
scope :inactive, -> { where(state: 'inactive') }

User.active.inactive # select * from users where state = 'active' and state = 'inactive'
User.active.merge(User.inactive) # select * from users where state = 'inactive'

concerns

help extract duplicate code into reusable modules that can be mixed into multiple controllers or models

  • move shared model code into model concerns
module Commentable
  extend ActiveSupport::Concern
  
  included do
    has_many :comments, as: :commentable
  end
  
  def comments_by_user(id)
    comments.where(user_id: id)
  end
end

# use
class Post < ActiveRecord::Base
  include Commentable
end

class Image < ActiveRecord::Base
  include Commentable
end
  • move shared controller code into controller
module Previewable
  def thumbnail(attachment)
    file_name = File.basename(attachment.path)
    "/thumbs/#{file_name}"
  end
end

class ImageController < ApplicationController
  include Previewable
  def show
    @image = Image.find(params[:id])
    @thumbnail = thumbnail(@image)
  end
end

class VideoController < ApplicationController
  include Previewable
  def show
    @video = Image.find(params[:id])
    @thumbnail = thumbnail(@video)
  end
end

decorators

  • wrapped the modle for handling the view logic, you should define method_missing and respond_to_missing? methods for delegating method to the object
class PostDecorator
  attr_reader :post
  
  def initialize(post)
    @post = post
  end
  
  def is_front_page?
    post.published_at > 2.days.ago
  end
  
  def publication_date
    post.created_at.strftime '%Y-%m-%d'
  end
  
  def method_missing(method_name, *args, &block)
    post.send(method_name, *args, &block)
  end
  
  def respond_to_missing?(method_name, include_private=false)
    post.respond_to?(method_name) || super
  end
end
# use
<span><%= @post_decorator.publication_date %></span>
@hlee
Copy link

hlee commented Aug 24, 2013

cool

@zlx
Copy link

zlx commented Aug 24, 2013

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment