其中部分示例稍老,但其思想仍然适用
gem install rails_best_practices
rails_best_practices .
class Post
  default_scope where(published: true).order("created_at desc")
end
you can't override default scope
Post.order("updated_at desc").limit(10) # =>
# Post Load (17.3ms)  SELECT `posts`.* FROM `posts` WHERE `posts`.`published` = 1 ORDER BY created_at desc, updated_at desc LIMIT 10
Post.unscoped.order("updated_at desc").limit(10) # =>
# Post Load (1.9ms)  SELECT `posts`.* FROM `posts` ORDER BY updated_at desc LIMIT 10
default_scope will affect your model initialization
Post.new # =>
<Post id: nil, published: true, title: nil, created_at: nil, updated_at: nil, user_id: nil>
loop do
  begin
    sleep 1
    eval "djsakru3924r9eiuorwju3498 += 5u84fior8u8t4ruyf8ihiure"
  rescue Exception => e
    puts "I refuse to fail or be stopped!"
  end
end
Exception
  NoMemoryError
  ScriptError
    LoadError
    NotImplementedError
    SyntaxError
  SignalException
    Interrupt
      Timeout::Error    # < ruby 1.9.2
  StandardError         # caught by rescue (default if no type was specified)
    ArgumentError
    IOError
      EOFError
    IndexError
    LocalJumpError
    NameError
      NoMethodError
    RangeError
      FloatDomainError
    RegexpError
    RuntimeError
      Timeout::Error    # >= ruby 1.9.2
    SecurityError
    SocketError
    SystemCallError
    SystemStackError
    ThreadError
    TypeError
    ZeroDivisionError
  SystemExit
  fatal
begin
  # iceberg!
rescue => e
  # lifeboats
end
# which is equivalent to:
begin
  # iceberg!
rescue StandardError => e
  # lifeboats
end
begin
  # iceberg?
rescue Exception => e
  # do some logging
  raise e  # not enough lifeboats ;)
end
# config/routes.rb
resources :auctions do
  resources :bids do
    resources :comments 
  end
end
edit_auction_bid_comment_path(@auction, @bid, @comment)
#show, #edit, #update and #destroy #index, #new and #create under the resourceresources :auctions do
  resources :bids, only: [:index, :new, :create]
end
resources :bids, only: [:show, :edit, :update, :destroy]
Or
resources :auctions, shallow: true do 
  resources :bids do
    resources :comments 
  end
end
Or
resources :auctions do 
   resources :bids
end
resources :bids do 
  resources :comments
end
resources :comments
bid_comments GET    /bids/:bid_id/comments(.:format)
             POST   /bids/:bid_id/comments(.:format)
new_bid_comment GET /bids/:bid_id/comments/new(.:format)
   edit_comment GET /comments/:id/edit(.:format)
        comment GET /comments/:id(.:format)
             PATCH  /comments/:id(.:format)
             PUT    /comments/:id(.:format)
             DELETE /comments/:id(.:format)
auction_bids GET    /auctions/:auction_id/bids(.:format)
             POST   /auctions/:auction_id/bids(.:format)
new_auction_bid GET /auctions/:auction_id/bids/new(.:format)
       edit_bid GET /bids/:id/edit(.:format)
            bid GET /bids/:id(.:format)
             PATCH  /bids/:id(.:format)
             PUT    /bids/:id(.:format)
             DELETE /bids/:id(.:format)
    auctions GET    /auctions(.:format)
             POST   /auctions(.:format)
    new_auction GET /auctions/new(.:format)
   edit_auction GET /auctions/:id/edit(.:format)
        auction GET /auctions/:id(.:format)
     ...
class PostsController < ApplicationController
  def show
    @item = ...
  end
end
# sidebar partial use @item directly
<%= render partial: "sidebar" %>
The downside of using instance variables in partials is that you create a dependency in the partial to something outside the partial's scope (coupling). This makes the partial harder to reuse, and can force changes in several parts of the application when you want to make a change in one part.
Partials that use instance variables in partial:
###Instead, pass locals to the partials
<%= render 'reusable_partial', item: @item %>
the partial only references item and not @item.
<%= render 'reusable_partial', :item => @other_object.item %>
Also, this can be reused in contexts where there is no @item:
<%= render 'reusable_partial', :item => @duck %>
Now, because the partial only references item and not @item, the action that renders the view that renders the reusable_partial is free to change without affecting the reusable_partial and the other actions/views that render it:
If my @duck changes in the future and no longer quacks like reusable_partial expects it to (the object's interface changes), I can also use an adapter to pass in the kind of item that reusable_partial expects:
@duck changes in the future, I can also use an adapter.
<%= render 'reusable_partial', :item => itemlike_duck(@duck) %>
Check the permission by comparing the owner of object with current_user
class PostsController < ApplicationController
  def edit
    @post = Post.find(params[:id])
    if @post.user != current_user
      flash[:warning] = 'Access denied'
      redirect_to posts_url
    end
  end
end
Just use scope access to make permission check simpler.
class PostsController < ApplicationController
  def edit
    # raise RecordNotFound exception (404 error) if not found
    @post = current_user.posts.find(params[:id])
  end
end
Code Smell
class Invoice < ActiveRecord::Base
  belongs_to :user
end
<%= @invoice.user.name %>
<%= @invoice.user.address %>
<%= @invoice.user.cellphone %>
Refactor
class Invoice < ActiveRecord::Base
  belongs_to :user
  delegate :name, :address, :cellphone, :to => :user, :prefix => true
end
<%= @invoice.user_name %>
<%= @invoice.user_address %>
<%= @invoice.user_cellphone %>
Bad Smell
class Post < ActiveRecord::Base
  has_many :comments
  def find_valid_comments
    self.comment.find(:all, :conditions => {:is_spam => false},
                            :limit => 10)
  end
end
class Comment < ActiveRecord::Base
  belongs_to :post
end
class CommentsController < ApplicationController
  def index
    @comments = @post.find_valid_comments
  end
end
class Post < ActiveRecord::Base
  has_many :comments
end
class Comment < ActiveRecord::Base
  belongs_to :post
  scope :only_valid, -> { is_spam: false }
  scope :limit, lambda { |size| { :limit => size } }
end
class CommentsController < ApplicationController
  def index
    @comments = @post.comments.only_valid.limit(10)
  end
end
Code Smell
<% if @user.login.blank? %>
  <%= link_to 'login', new_session_path %>
<% end %>
<% if @user.login.present? %>
  <%= @user.login %>
<% end %>
Refactor
<% unless @user.login? %>
  <%= link_to 'login', new_session_path %>
<% end %>
<% if @user.login? %>
  <%= @user.login %>
<% end %>
Before
<%= render :partial => 'sidebar' %>
<%= render :partial => 'shared/sidebar', :locals => { :parent => post } %>
After
<%= render 'sidebar' %>
<%= render 'shared/sidebar', :parent => post %>
if account && account.owner && account.owner.address
...
end
Isn't identical to
if account.try(:owner).try(:address)
...
end
Isn't identical to
if account&.owner&.address
...
end
account = Account.new(owner: nil) # account without an owner
account.owner.address
# => NoMethodError: undefined method `address' for nil:NilClass
account && account.owner && account.owner.address
# => nil
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => nil
account = Account.new(owner: false)
account.owner.address
# => NoMethodError: undefined method `address' for false:FalseClass `
account && account.owner && account.owner.address
# => false
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => undefined method `address' for false:FalseClass`
try, whatch out misspelling!account = Account.new(owner: Object.new)
account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>
account && account.owner && account.owner.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
account.try(:owner).try(:address)
# => nil
account&.owner&.address
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`
There's a stricter version of try - try! for you:
account.try!(:owner).try!(:address)
# => NoMethodError: undefined method `address' for #<Object:0x00559996b5bde8>`