scope :trending, -> { |num = nil| where('started_trending > ?', 1.days.ago).
order('mentions desc').
limit(num) }
@tweets = current_user.tweets.unscoped.order(:status).limit(10)
before_filter :auth, :only => [:edit, :update, :destroy]
:except => [:index, :create]
skip_before_filter :require_login, :only => [:new, :create]
- use active_model to extract your non database model
class ContactForm
include ActiveModel::Validations
include ActiveModel::Conversion
attr_accessor :name, :email, :body
validates_presence_of :name, :email, :body
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
- use
accepts_nested_attributes_for :account_setting
in your model
- use
<%= fields_for :account_setting do |a| %>
in your view
- use
@user = User.new(:account_setting => AccountSetting.new)
in your controller
- use presenters to extract your view logic
#bad code
def index
@followers_tweets = current_user.followers_tweets.limit(20)
@recent_tweet = current_user.tweets.first
@following = current_user.following.limit(5)
@followers = current_user.followers.limit(5)
...
end
#good code
def index
@presenter = Tweets::IndexPresenter.new(current_user)
end
class Tweets::IndexPresenter
extend ActiveSupport::Memoizable
def initializer(user)
@user = user
end
def followers_tweets
@user.follower_tweets.limit(20)
end
def recent_tweet
@user.tweets.first
end
...
memoize :recent_tweet, :followers_tweets
end
#now in the view
<%= @presenter.recent_tweet.body %>
<%= @presenter.recent_tweet.created_at %>
#and because of memoize, this will only generate one tweet object
User.where "name = ?", params[:name]
User.where name: params[:name]
Tweet.where("created_at >= :start_date AND created_at <= :end_date",
{:start_date => params[:start_date], :end_date => params[:end_date]})
Tweet.where(created_at: (params[:start_date].to_date)..(params[:end_date].to_date))
class User < ActiveRecord::Base
attr_accessible :email, :password
end
class Topic < ActiveRecord::Base
TRENDING_PERIOD = 1.week
before_create :set_trend_ending
after_create :queue_new_topic_user, :if => Proc.new { |t| t.user.name.match /xiongbo/ }
private
def set_trend_ending
self.finish_trending = TRENDING_PERIOD.from_now
end
end
class Topic < ActiveRecord::Base
validates :name, :appropriate => true
end
#/lib/appropriate_validator.rb
class AppropriateValidator < ActiveRecord::EachValidator
def validate_each(record, attribute, value)
unless ContentModerator.is_suitable?(value)
record.errors.add(attribute, 'is inappropriate')
end
end
end
- use size with counter_cache
<%= pluralize(tweet.retweets.size, "ReTweet") %>
class Tweet < ActiveRecord::Base
belongs_to :original_tweet,
:class_name => 'Tweet',
:foreign_key => :tweet_id,
:counter_cache => :retweets_count
has_many :retweets,
:class_name => 'Tweet',
:foreign_key => :tweet_id
end
- use
find_each
for running query
desc 'Task involving all tweets'
task :tweet_task => :environment do
#default batch_size is 100
Tweet.find_each(:batch_size => 200) do |tweet|
p "task for #{tweet}"
end
end
- each unit should have limited knowledge about other units which we should use delegate
class Tweet < ActiveRecord::Base
def location_data
#bad: when user change location logic on other object
if self.user.account_setting.location_on_tweets
self.location
else
"unavailable"
end
#good
if self.user.location_on_tweets
...
end
end
class User < ActiveRecord::Base
has_one :account_setting, :dependent => :destroy
delegate :location_on_tweets, :public_email, :to => :account_setting, :allow_nil => true
end
- use to_s for model's default output and use to_param for fridndly urls
def to_s
"#{first_name} #{last_name}"
end
#use for <%= @user %>
def to_param
"#{id}-#{name.parameterize}"
end
#use for <%= link to topic.name, topic %> #=>/post/21-rails-best-practices
- use html helper to generate html content for helper_methods
def follow_box(title, count, recent)
content_tag :div, :class => title.downcase do
raw(
title +
content_tag(:span, count) +
recent.collect do |user|
link_to user do
image_tag(user.avatar.url(:thumb))
end
end.join
)
end
end
# then in the view
<%= follow_box("Followers", @followers_count, @recent_followers) %>
class Favorite < ActiveRecord::Base
belongs_to :tweet,
:counter_cache => :favorites_count
end
class AddCounterCacheColumnToTweets < ActiveRecord::Migration
def self.up
# add column here
add_column :tweets, :favorites_count, :integer
end
def self.down
# remove column here
remove_column :tweets, :favorites_count
end
end
# @tweet.favorites.create() will cause an insert and an update
# update "tweets" set "favorites_count" = "favorited_count" + 1 where ("tweets"."id" = 1)