Rails4 与 3的一些区别

  • Match routes:
  Rails 3:
    match '/item/:id/purchase', to: 'items#purchase'
  Rails 4(Ruby>=1.9.3):
    Error:
      You should not use the 'match' method in your router without specifying an HTTP method.(RuntimeError)
    post '/item/:id/purchase', to: 'items#purchase'
    match '/item/:id/purchase', to: 'items#purchase', via: [:get, :post]
    match '/item/:id/purchase', to: 'items#purchase', via: [:all]
  • Patch verb(用于局部更新):
  PUT:
    "The PUT method requests that the enclosed entity be stored under the supplied Request-URI."-RFC 2616/HTTP 1.1
  PATCH:
    "(...)the entity contains a list of differences between the original version of the resouces and the desired content of the resource after the PATCH action has been applied."-RFC 2068/HTTP 1.1
  config/routes.rb
    resources :items
  Rails 3:
    rake routes显示的资源路径为7条。
  Rails 4:
    rake routes显示的资源路径为8条。
    会多出一条路径:
      item PATCH /items/:id(.:format)   items#update
    Controller Tests:
      test "updates item with PATCH" do
        patch :update, id: @item, item: { description: @item.description }
        assert_redirected_to item_url(@item)
      end
  • Routing concerns:
  Rails 3:
    resources :messages do
      resources :comments
      resources :categories
      resources :tags
    end
    resources :posts do
      resources :comments
      resources :categories
      resources :tags
    end
    resources :items do
      resources :comments
      resources :categories
      resources :tags
    end
  Rails 4:
    e.g.
      concern :sociable do
        resources :comments
        resources :categories
        resources :tags
      end
      resources :messages, concern: :sociable
      resources :posts, concern: :sociable
      resources :items, concern: :sociable
    e.g.
      concern :sociable do |options|
        resources :comments, options
        resources :categories, options
        resources :tags, options
      end
      resources :messages, concerns: :sociable
      resources :posts, concerns: :sociable
      resources :items do
        concern: :sociable, only: :create
      end
    e.g.
      concern :sociable, Sociable
      resources :messages, concerns: :sociable
      resources :posts, concerns: :sociable
      resources :items do
        concerns :sociable, only: :create
      end
      app/concerns/sociable.rb
        class Sociable
          def self.call(mapper, options)
            mapper.resources :comments, options
            mapper.resources :categories, options
            mapper.resources :tags, options
          end
        end
  • 去除了烦人的nil:
  e.g.
    @post = Post.where(title: title).first
    @post.id
    Rails 3:
      RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
    Rails 4:
      NoMethodError: undefined method 'id' for nil:NilClass
Thread-safety:
  Rails 3:
    config/environments/production.rb
      MyApp::Application.configure do
        # Enable threaded mode
        # config.threadsafe! (此方法已被遗弃)
      end
  Rails 4(安全线程默认打开):
    config/environments/production.rb
      MyApp::Application.configure do
        config.cache_classes = true   # 确保在请求之间加载时,Rack::Lock不被包含在Rack::Lock不被包含在中间件栈内。
        config.eager_load = true    # 在新线程创建前加载所有代码
      end
  • Finders:
  Rails 3:
    Post.find(:all, conditions: { author: 'admin' })
    Post.find_all_by_title('Rails')
    Post.find_last_by_author('Devin')
  Rails 4:(上述特性一杯包含入activerecord-deprecated_finders gem)
    Post.where(author: 'admin')
    Post.where(title: 'Rails 4')
    Post.where(author: 'admin').last
  • Find_by:
  Rails 3:(activerecord-deprecated_finders gem)
    Post.find_by_title('Rails 4')
    Post.find_by_title('Rails 4', conditions: { author: 'admin' })
  Rails 4:
    Post.find_by_title('Rails 4')  --->better: Post.find_by(title: 'Rails 4')
    Post.find_by(title: 'Rails 4', author: 'admin')
    
    post_parms = { title: 'Rails 4', author: 'admin' }
    Post.find_by(post_parms)
    Post.find_by("published_on < ?", 2.weeks.ago)  # 可以上where一样使用
  • Find_or_*:
  Rails 3:(activerecord-deprecated_finders gem)
    Post.find_or_initialize_by_title('Rails 4')
    Post.find_or_create_by_title('Rails 4')
  Rails 4:
    Post.find_or_initialize_by(title: 'Rails 4')
    Post.find_or_create_by(title: 'Rails 4')
  • Update & Update_column:
  Rails 3:
    @post.update_attributes(post_parms)
    @post.update_attribute(:title, 'Rails 4')
    @post.update_column(:title, 'Rails 4')
  Rails 4:
    @post.update(post_parms)
    # 以下这些方法将会跳过验证逻辑
    @post.update_attribute(:title, 'Rails 4')
    @post.update_column(:title, 'Rails 4')
    @post.Update_columns(post_parms)
  • Model.all:
  Rails 3:
    @tweets = Tweet.scoped
  Rails 4:
    @tweets = Tweet.all    # return ActiveRecord::Relation
  • Scopes:
  Rails 3:
    scope :sold, where(state: 'sold')
    default_scope where(state: 'available')
    scope :recent, where(published_at: 2.weeks.ago)
    scope :recent_red, recent.where(color: 'red')
  Rails 4:
    scope :sold, ->{ where(state: 'sold') }
    default_scope {where(state: 'available')} or default_scope ->{where(state: 'available')}
    scope :recent,->{ where(published_at: 2.weeks.ago) }
    scope :recent_red, ->{ recent.where(color: 'red') }
  • Relation#none
  Rails 3:
    class User < ActiveRecord::Base
      def visible_posts
        case role
        when 'Country Manager'
          Post.where(country: country)
        when 'Reviewer'
          Post.published
        when 'Bad User'
          []
        end
      end
    end
    @posts = current_user.visible_posts
    if @posts.any?
      @posts.recent
    else
      []
    end
  Rails 4:
    class User < ActiveRecord::Base
      def visible_posts
        case role
        when 'Country Manager'
          Post.where(country: country)
        when 'Reviewer'
          Post.published
        when 'Bad User'
          Post.none   # return ActiveRecord::Relation不操作数据库
        end
      end
    end
    @posts = current_user.visible_posts
    @posts.recent
  • Relation#not:
  Rails 3:
    if author
      Post.where('author != ?', author)
    else
      Post.where('author is not null')
    end
  Rails 4:
    Post.where.not(author: author)
  • Relation#order
  Rails 3:
    class User < ActiveRecord::Base
      default_scope { order(:name) }
    end
    User.order("created_at DESC")
    sql: select * from users order by name asc, created_at desc

    User.order(:name, 'created_at DESC')
  Rails 4:
    User.order(created_at: :desc)
    sql: select * from users order by created_at desc, name asc

    User.order(:name, created_at: :desc)
  • Relation#references
  Rails 4:
    Post.includes(:comments).where("comments.name = 'foo'").references(:comments) #此种情况需要使用references声明引用表
    Post.includes(:comments).where(comments: { name: 'foo' })
    Post.includes(:comments).where('comments.name' => 'foo')
    Post.includes(:comments).order('comments.name')
  • ActiveModel::Model:
  Rails 3:
    class SupportTicket
      include ActiveModel::Conversion
      include ActiveModel::Validation
      extend ActiveModel::Naming

      attr_reader :title, :description
      validates_presence_of :title
      validates_presence_of :description
    end
  Rails 4:
    activemodel/lib/active_model/model.rb
      def self.included(base)
        base.class_eval do
          extend ActiveModel::Naming
          extend ActiveModel::Translation
          extend ActiveModel::Conversion
          extend ActiveModel::Validation
        end
      end
    class SupportTicket
      include ActiveModel::Model

      attr_reader :title, :description
      validates_presence_of :title
      validates_presence_of :description
    end
  • Strong parameters:
  Rails 3:
    models/user.rb
      class User < ActiveRecord::Base
        attr_accessible :name
      end
    controllers/users_controller.rb
      def update
        if @user.update_attributes(params[:user])
          redirect_to @user, notice: 'Updated'
        else
          render action: 'edit'
        end
      end
    Error:
      parameters: {"user"=>{"name"=>"Cowzombie", "admin"=>"1"}}
      ActiveModel::MassAssignmentSecurity::Error Cannot mass-assign protected attributes: admin
  Rails 4:(attr_reader等在protected_attributes gem中)
    models/user.rb
      class User < ActiveModel::Base
      end
    controllers/users_controller.rb
      def update
        user_params = params.require(:user).permit(:name)
        if @user.update(user_params)
          redirect_to @user, notice: 'Updated'
        else
          render action: 'edit'
        end
      end
    config/applcation.rb(默认开启日志)
      config.action_controller.action_on_unpermitted_parameters = :raise
    Error:
      ActionController::ParameterMissing: param not found:user
      返回400状态码
    Permit types:
      String,Symbol, NilClass, Numeric, TrueClass, FalseClass, Date, Time, DateTime, StringIO, IO, ActionDispatch::Http::UploadedFile and Rack::Test::UploadedFile
  • Protect from forgery:
  protect_from_forgery with: :exception       # ActionController::InvalidAuthenticityToken
  pretect_from_forgery with: :null_session    # an empty session(until the next valid request)
  pretect_from_forgery with: :reset_session   # a new session(destroying the old one)
  • Action Controller Filters/Actions
  Rails 3:
    before_filter :set_person, except: [:index, :new, :create]
    before_filter :ensure_permission, only: [:edit, :update]
  Rails 4:(filter 方法仍然可以用)
    before_action :set_person, except: [:index, :new, :create]
    before_action :ensure_permission, only: [:edit, :update]
  • Session:
  Rails 3:
    cookie-based session store
    require 'rack'
    cookie = '...'
    Rack::Session::Cookie::Base64::Marshal.new.decode(cookie)   # 可获取信息
  Rails 4:
    采用Encrypted cookie session store存储,无法通过解码获取cookie数据
    require 'rack'
    cookie = '...'
    Rack::Session::Cookie::Base64::Marshal.new.decode(cookie)   # nil
Securing secret key base:
  config/initializers/secret_token.rb
    MyApp::Application.config.secret_key_base = ENV['SECURE_KEY_BASE']
  • Flash:
  Rails 3 & 4:
    <p id="notice"><%= flash[:notice] %></p>
    <p id="notice"><%= notice %></p>
    <p id="notice"><%= flash[:alert] %></p>
    <p id="notice"><%= alert %></p>
  Rails 4:
    controllers/application_controller.rb
      class ApplicationController < ActionController::Base
        add_flash_types :grunt, :snarl
      end
    controllers/users_controller.rb
      flash[:grunt] = "Successfully created..."
    views/users/show.html.erb
      <p id="grunt"><%= grunt %></p>
  • Collection form helpers:
  class Owner < ActiveRecord::Base
    has_many :items
  end
  class Item < ActiveRecord::Base
    belongs_to :owner
  end
  Rails 3 & 4:
    collection_select(:item, :owner_id, Owner.all, :id, :name)
  Rails 4:
    collection_radio_buttons(:item, :owner_id, Owner.all, :id, :name)
    collection_check_boxes(:item, :owner_id, Owner.all, :id, :name)
  • Date field:
  Rails 3 & 4:
    <%= f.date_select :return_date %>
  Rails 4:(显示为一个控件,根据浏览器不同而不同。)
    <input id="item_return_date" name="item[return_date]" type="date"></input>
  • Response type from controller:
  Rails 4:
    views/owners/index.json.ruby
      owners_hashes = @owners.map { |owner| 
          { name: owner.name, url: owner_url(owner) }
      }
      owners_hashes.to_json
  • Memcache store uses dalli:
  config/environments/production.rb
    config.cache_store = :mem_cache_store
  Rails 3:
    Uses memcache-client
  Rails 4:
    Uses the Dalli client to connect to memcached
    gem 'dalli'
  Cache Store API:(Rails 3 & 4)
    Rails.cache.read('key')
    Rails.cache.write('key', value)
    Rails.cache.fetch('key'){ value }
    config/environments/production.rb
      config.action_controller.perform_caching = true
  Rails 3:
    app/views/comments/_comment.html.erb
      <% cache ['v1', comment] do %>
        <li><%= comment %></li>
      <% end %>
    app/views/comments/_comment.html.erb
      <% cache ['v2', comment] do %>
        <li><%= comment %> - <% comment.author %></li>
      <% end %>
  Rails 4:
    app/views/comments/_comment.html.erb
      <% cache comment do %>
        <li><%= comment %></li>
      <% end %>
    app/views/comments/_comment.html.erb
      <% cache comment do %>
        <li><%= comment %> - <% comment.author %></li>
      <% end %>
  Rails 3 & 4:
    app/views/projects/show.html.erb
      <%= render @project.documents %>
    app/views/documents/_document.html.erb
      <% cache document do %>
        <article>
          <h3><%= document.title %></h3>
          <ul>
            <%= render document.comments %>
          </ul>
          <%= link_to 'View details', document %>
        </article>
      <% end %>
    app/views/comments/_comment.html.erb
      <% cache comment do %>
        <li><%= comment %></li>
      <% end %>
    app/models/comment.rb
      class Comment < ActiveRecord::Base
        belongs_to :document, touch: true
      end
  Rails 4:
    app/views/projects/show.html.erb
      <%= render @project.documents %>
    app/views/documents/_document.html.erb
      <% cache document do %>
        <article>
          <h3><%= document.title %></h3>
          <ul>
            <%= render partial: "comments/comment", collection: document.recent_comments %>   # recent_comments是一个帮助方法
            <%# Template Dependency: comments/comment %>    # 1
            <%= render document.recent_comments %>          # 2

            <%= render document.comments %>
          </ul>
          <%= link_to 'View details', document %>
        </article>
      <% end %>
    app/views/comments/_comment.html.erb
      <% cache comment do %>
        <li><%= comment %></li>
      <% end %>
  • ActionController::Live:(Rails 4)
  controller/items_controller.rb:
    class ItemsController < ApplicationController
      include ActionController::Live

      def show
        response.headers["Content-Type"] = "text/event-stream"
        3.times {
          response.stream.write "Hello, browser!\n"
          sleep 1
        }
        response.stream.close
      end

      def events
        response.headers["Content-Type"] = "text/event-stream"
        redis = Redis.new
        redis.subscribe('item.create') do |on|
          on.message do |event, data|
            response.stream.write("data: #{data}\n\n")
          end
        end
        response.stream.close
      end
    end
  Gemfile:
    gem 'puma'
    gem 'jquery-turbolinks'
  assets/javascripts/application.js
    //= require jquery.turbolinks
  views/owners/show.html.erb
    <ul id="items"></ul>
  assets/javascripts/owners.js(设置监听事件)
    $(document).ready(initialize);
    $(document).on('page:load', initialize);
    function initialize(){
      var source = new EventSource('/items/events');
      source.addEventListener('message', update);

      $('#owner_active').click(function(){
        alert(this.checked);
      });
    };
    function update(event) {
      var item = $("<li>").text(event.data);
      $("#item").append(item);
    }
  views/layouts/application.html.erb
    <div id="loading">Loading...</div>
    <script>
      $(document).on('page:fetch', function(){
        $('#loading').show();
      });
      $(document).on('page:change', function(){
        $('#loading').show();
      });
    </script>
  assets/stylesheets/application.css
    #loading {
      font-size: 24px;
      color: #9900FF;
      display: nont;
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值