博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
rails最佳实践 --codeschool笔记
阅读量:6834 次
发布时间:2019-06-26

本文共 9768 字,大约阅读时间需要 32 分钟。

  hot3.png

rails最佳实践
--codeschool笔记
FAT MODEL, SKINNY CONTROLLER
  代码争取放在model中,因为model是功能模块,更易于测试,而且更底层,易于复用。
  controller是业务逻辑,对错后知后觉.而且想想把业务逻辑和功能都放在一起,那是多么杯具的事件。
Scope it out
  bad_code:
  XX_controller:
    @tweets = Tweet.find(
      :all,
      :conditions => {:user_id => current_user.id},
      :order => 'created_at desc',
      :limit => 10
      )
  好一点,但还是行:
    @tweets = current_user.tweets.order('created_at desc').limit(10)
  good_code:
    tweets_controller.rb
      @tweets = current_user.tweets.recent.limit(10)
    models/tweet.rb
      scope :recent, order('created_at desc' )
  这个重构的原则就是不要在controller里出现与实现功能相关的代码如created_at desc ,这个代码属于功能性代码,而recent这个scope则能很好的表达order('created_at desc' )这个功能组合 。
    使用:default_scope  个人认为这个不是很好,因为default这种有辐射性质的函数很难控制
    使用lambda{}
    scope :trending, lambda { where('started_trending > ?', 1.day.ago ).order( 'mentions desc' ) }
    上面会查询一次,后面再用是取值,把前面取到的词返回,加lambda解决这个问题
  lambda { |var = nil| xxxx } 使用默认值:
    scope :trending, lambda { |num = nil| where('started_trending > ?', 1.day.ago ).order( 'mentions desc' ).limit(num) }
    @trending = Topic.trending(5)
    @trending = Topic.trending
  unscoped:  
    default_scope order ('created_at desc' ) 
    @tweets = current_user.tweets.unscoped.order(:status).limit(10)
  tweets_controller.rb
    BAD:
      t = Tweet.new
      t.status = "RT #{@tweet.user.name}: #{@tweet.status}"
      t.original_tweet = @tweet
      t.user = current_user
      t.save
    GOOD:
      current_user.tweets.create(
        :status => "RT #{@tweet.user.name}: #{@tweet.status}",
        :original_tweet => @tweet
        )
fantastic filters
  BAD
    before_filter :get_tweet, :only => [:edit, :update, :destroy]
  GOOD:
    @tweet = get_tweet( params[:id])
    private
    def get_tweet (tweet_id)
      Tweet.find(tweet_id)
    end
  :except => [:index, :create]
Nested attributes
  @user = User.new(params[:user])
  @user.save
  has_one :account_setting, :dependent => :destroy
  accepts_nested_attributes_for :account_setting
  <%= form_for(@user) do |f| %>
    ...
    <%= f. fields_for :account_setting do |a| %>
  def new
    @user = User.new (:account_setting => AccountSetting.new)
  end
Models without the database
  class ContactForm
    include ActiveModel::Validations
    include ActiveModel::Conversion #<%= form_for @contact_form
    
    attr_accessor :name, :email, :body
    validates_presence_of :name, :email, :body
    
    def initialize(attributes = {})
      attributes.each do |name, value|
        send("#{name}=", value)       #ContactForm.new(params[:contact_form])
      end
    end
    
    def persisted?
      false
    end
  end
  <%= form_for @contact_form , :url => send_email_path do |f| %>
  @contact_form = ContactForm.new
  def new
    @contact_form = ContactForm.new
  end
  def send_email
    @contact_form = ContactForm.new(params[:contact_form])
    if @contact_form.valid?
      Notifications.contact_us( @contact_form ).deliver
      redirect_to root_path, :notice => "Email sent, we'll get back to you"
    else
      render :new
    end
  end
really Rest
  UsersController
    subscribe_mailing_list
    unsubscribe_mailing_list
  SubscriptionsController
    create
    destroy
Enter the Presenters
  @presenter = Tweets::IndexPresenter.new(current_user)
  /conditionsnfig/application.rb
    config.autoload_paths += [config.root.join("app/presenters")]
  /app/presenters/tweets/index_presenter.rb
    class Tweets::IndexPresenter
      extend ActiveSupport::Memoizable
      def initialize(user)
        @user = user
      end
      def followers_tweets
        @user.followers_tweets.limit(20)
      end
      def recent_tweet
        @recent_tweet ||= @user.tweets.first
      end
      def trends
        @user.trend_option == "worldwide"
        if trends
          Trend.worldwide.by_promoted.limit(10)
        else
          Trend.filter_by(@user.trend_option).limit(10)
        end
      end
      memoize :recent_tweet, :followers_tweet, ...
    end
Memoization
  extend ActiveSupport::Memoizable
  memoize :recent_tweet, :followers_tweet, 
  def expensive(num)
  # lots of processing
  end
  memoize :expensive
  expensive(2)
  expensive(2)
reject sql injection
  
  BAD:
    User.where("name = #{params[:name]}")
  GOOD:
    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))
Rails 3 responder syntax
  
  respond_to :html, :xml, :json
  def index
    @users = User.all
    respond_with(@users)
  end
  def show
    @user = User.find(params[:id])
    respond_with(@user)
  end
Loving your indices  #index索引的复数
  经常desc的属性,可以加index,index就是用插入时间换查询时间
protecting your attributes
  bad:
    attr_protected :is_admin
  good:
    attr_accessible :email, :password, :password_confirmation
default values
  
  change_column_default :account_settings, :time_zone, 'EST'
  change_column :account_settings, :time_zone, :string, nil
Proper use of callbacks
  
  RENDING_PERIOD = 1.week
  before_create :set_trend_ending
  private
  def set_trend_ending
    self.finish_trending = TRENDING_PERIOD .from_now
  end
Rails date helpers
  Date Helpers:
    1.minute
    2.hour
    3.days
    4.week
    5.months
    6.year
  Modifiers:
    beginning_of_day      #end
    beginning_of_week
    beginning_of_month
    beginning_of_quarter
    beginning_of_year
    2.weeks.ago
    3.weeks.from_now
    next_week
    next_month
    next_year
improved validation
  /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
  /app/models/topic.rb
  validates :name, :appropriate => true
Sowing the Seeds
  topics =[ {:name=> "Rails for Zombies", :mentions => 1023},
            { :name=> "Top Ruby Jobs", :mentions => 231},
            {:name=> "Ruby5", :mentions => 2312}]
  Topic.destroy_all
  Topic.create do |t|
    t.name = attributes[:name]
    t.mentions = attributes[:mentions]
  end
  不够好
  topics.each do |attributes|
    Topic.find_or_initialize_by_name( attributes[:name]).tap do |t|
      t.mentions = attributes[:mentions]
      t.save!
    end
  end
N+1 is not for fun
  self.followers.recent.collect{ |f| f.user.name }.to_sentence
  self.followers.recent.includes(:user).collect{ |f| f.user.name }.to_sentence
    Select followers where user_id=1
    Select users where user_id in (2,3,4,5)
Bullet gem
  https://github.com/flyerhzm/bullet
  To find all your n+1 queries
counter_cache Money
  pluralize( tweet.retweets.length , "ReTweet")   
  pluralize( tweet.retweets.count , "ReTweet")
  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
  add_column :tweets,:retweets_count,:integer,:default => 0
Batches of find_each
  Tweet .find_each(:batch_size => 200) do |tweet|
    p "task for #{tweet}"
  end
  数量大的时候很有用 pulls batches of 1,000 at a time default
Law of Demeter
  delegate :location_on_tweets, :public_email,
            :to => :account_setting,
            :allow_nil => true
Head to to_s
  def to_s
    "#{first_name} #{last_name}"
  end
to_param-alama ding dong
  /post/2133
  /post/rails-best-practices
  /post/2133-rails-best-practices
  class Topic < ActiveRecord::Base
    def to_param
      "#{id}-#{name.parameterize}"
    end
  end
  <%= link_to topic.name, topic %>
  /post/2133-rails-best-practices
  {:id => "2133-rails-best-practices"}
  Topic.find(params[:id])
  call to_i
  Topic.find(2133)
No queries in your view!
  
Helper Skelter
  index.html.erb
    <%= follow_box("Followers", @followers_count , @recent_followers) %>
    <%= follow_box("Following", @following_count , @recent_following) %>
    tweets_helper.rb
  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
Partial sanity
  <%= render 'trending', :area => @user.trending_area,:topics => @trending %>
  <% topics.each do |topic| %>
  <li>
    <%= link_to topic.name, topic %>
    <% if topic.promoted? %>
      <%= link_to image_tag('promoted.jpg'), topic %>
    <% end %>
  </li>
  <% end %>
  <% topics.each do |topic| %>
    <%= render topic %>
  <% end %>
  <li>
    <%= link_to topic.name, topic %>
    <% if topic.promoted? %>
      <%= link_to image_tag('promoted.jpg'), topic %>
    <% end %>
  </li>
empty string things
  @user.email.blank?  @user.email.present? =  @user.email?
  <%= @user.city || @user.state || "Unknown" %>
  <%= @user.city.presence || @user.state.presence || "Unknown" %>
  <%= @user.city ? @user.city.titleize : "Unknown" %>
  <%= @user.city.try(:titleize) || "Unknown" %>
rock your block helpers
  <% @presenter.tweets.each do |tweet| %>
    <div id="tweet_<%= tweet.id %>"
      class="<%= 'favorite' if tweet.is_a_favorite?(current_user) %>">
    <%= tweet.status %>
    </div>
  <% end %>
  /app/views/tweets/index.html.erb
  <% @presenter.tweets.each do |tweet| %>
    <%= tweet_div_for(tweet, current_user) do %>
      <%= tweet.status %>
    <% end %>
  <% end %>
  /app/helpers/tweets_helper.rb
  def tweet_div_for(tweet, user, &block)
    klass = 'favorite' if tweet.is_a_favorite?(user)
    content_tag tweet, :class => klass do
      yield
    end
  end
Yield to the content_for
  <% if flash[:notice] %>
    <span style="color: green"><%= flash[:notice] %></span>
  <% end %>
  <%= yield :sidebar %>
  <% content_for(:sidebar) do %>
    ... html here ...
  <% end %>
  /app/views/layouts/applica5on.html.erb
  <%= yield :sidebar %>
    <% if flash[:notice] %>
      <span style="color: green"><%= flash[:notice] %></span>
    <% end %>
  <%= yield %>
  /app/controllers/tweets_controller.rb
  class TweetsController < ApplicationController
    layout 'with_sidebar'
  end
  /app/views/layouts/with_sidebar.html.erb
  <% content_for(:sidebar) do %>
    ... html here ...
  <% end %>
  <%= render :file => 'layouts/application' %>
meta Yield
/app/views/layouts/applica5on.html.erb
  <title>Twitter <%= yield(:title) %></title>
  <meta name="description"
    content="<%= yield(:description) || "The best way ..." %>">
  <meta name ="keywords"
    content="<%= yield(:keywords) || "social,tweets ..." %>">
/app/views/tweets/show.html.erb
  <%
    content_for(:title, @tweet.user.name)
    content_for(:description, @tweet.status)
    content_for(:keywords, @tweet.hash_tags.join(","))
  %>
  

转载于:https://my.oschina.net/zhao/blog/52920

你可能感兴趣的文章
版本升级提示
查看>>
FlipViewPager 对item实现左右对折滑动翻页效果《IT蓝豹》
查看>>
直接方式,反射方式,dynamic方式性能比较
查看>>
Leetcode日记5
查看>>
时间:2014年4月11日22:15:47 session 概念
查看>>
我的友情链接
查看>>
教育“优先”,落实才是关键
查看>>
传统IT大佬们,路在何方?
查看>>
基础练习
查看>>
shell学习笔记 (9.3)
查看>>
用chrome在电脑上模拟微信内置浏览器
查看>>
PHP获取常用时间的总结
查看>>
设计模式6大原则:里氏置换原则
查看>>
hbase0.94.14伪分布式安装
查看>>
Debug记录:vCenter6.5突然不能访问并报错“503 Service Unavailable”
查看>>
自动导出oracle的数据
查看>>
顺序表实现的源码
查看>>
我的友情链接
查看>>
iOS调用系统摄像头和相册
查看>>
mysql文件导入办法(直接copy数据库文件)
查看>>