rails 4 与 rails 3 的差异记录

修改rails为4.1.5，及依赖的gem包 升级rails关联组建的版本号到4

    -gem 'sass-rails', '3.2.6'
    -gem 'coffee-rails', '3.2.2'
    -gem 'uglifier', '2.1.2'
    -gem 'cells', '3.8.8'
    -gem 'rails', '3.2.15'
    +gem 'rails', '4.1.5'
    +gem 'sass-rails', '~> 4.0.3'
    +gem 'coffee-rails', '~> 4.0.0'
    +gem 'uglifier', '>= 1.3.0'
    +gem 'cells', '3.9.1'
添加如下gem包，处理rails4新增特性，比如rails4去掉了observers和参数检查的特性

    +gem 'protected_attributes'
    +gem 'rails-observers'
    +gem 'mongoid-observers'
    +gem 'actionpack-page_caching'
    +gem 'actionpack-action_caching'
执行bundle update更新到最新的版本，如果发生依赖冲突，则暂时去掉版本号

    -gem 'mongo', '1.9.1'
    -gem 'mongoid', '3.1.4'
    -gem 'bson_ext', '1.9.1'
    +gem 'mongo'
    +gem 'mongoid'
    +gem 'bson_ext'
例如：db-charmer发生了版本冲突，则去掉版本1.9.0, 更新到最新的版本

      In Gemfile:
        db-charmer (= 1.9.0) ruby depends on
          activesupport (< 4.0.0) ruby

        rails (= 4.1.5) ruby depends on
          activesupport (4.1.5) 
启动应用，根据log来检查所有gem包的加载是否报错，根据错误提示解决
有问题的几个gem

    #client_side_validations 没有支持rails4的版本，暂时去掉
    #gem "client_side_validations"

    #替换为rails4支持的版本，否则会报错
    -gem "galetahub-simple_captcha", '0.1.5', :require => "simple_captcha"
    +gem 'simple_captcha2', require: 'simple_captcha'

    -gem 'pry-debugger' 
    +gem 'pry-byebug' 
Gem Install Debugger Error

No such file or directory @ rb_file_s_stat - ./213/ruby_debug.h (Errno::ENOENT)

Ruby 2.1.2 isn't supported by debugger, unfortunately. Instead, 
use the byebug gem. See this discussion for more details.

https://github.com/cldwalker/debugger/issues/125#issuecomment-43353446

修改gem 'pry-debugger' 为 gem 'pry-byebug'

rails4.1 Rails 4.1 breaks Cells

uninitialized constant Cell::Base::Layouts

修改cells版本为3.9.1 gem 'cells', '3.9.1'

https://github.com/apotonick/cells/issues/190

rails4 强制加载路由文件

undefined method `concat' for nil:NilClass (NoMethodError)

在rails3中，config.paths['config/routes'].concat %w{admin schools}

在rails4中，使用Rails.application.reload_routes!替代

替换解析器

去掉ActiveSupport::XmlMini.backend = 'Nokogiri'， activeSupport4,默认使用nokogiri解析xml文件 application.rb

      # 替换解析器
      +config.gem 'yajl-ruby', :lib => 'yajl/json_gem'
      -ActiveSupport::XmlMini.backend = 'Nokogiri'
      -ActiveSupport::JSON.backend = 'yajl'
undefined method `backend=' for ActiveSupport::JSON:Module (NoMethodError)
Yail advertises as much faster than JSON and YAML
ActiveSupport::JSON.backend = 'yajl' #activeSupport4 去掉了backend方法

https://github.com/brianmario/yajl-ruby/
http://stackoverflow.com/questions/3073711/whats-the-best-way-to-use-yajl-ruby-with-my-rails-project

更新配置文件 config/environments/*.rb

注释掉: config.active_record.auto_explain_threshold_in_seconds = 0.5

新增： config.eager_load = false

mongoid配置文件

去掉配置文件mongoid.yml中的

allow_dynamic_fields: true
identity_map_enabled: true

合法的配置选项有：
:include_root_in_json, :include_type_for_serialization, :preload_models, :raise_not_found_error, 
:scope_overwrite_exception, :duplicate_fields_exception, :use_activesupport_time_zone, :use_utc

在升级Rails4的同时, mongoid 也从3.1.4 升到4.0.0， 运行rails server 命令， 结果报错 说：allow_dynamic_fields 是无效参数。
查看mongoid的change log， 发现 allow_dynamic_fields 已经从配置文件中 移除了， 
如果想继续使用动态属性， 只需要在model 中添加

include Mongoid::Attributes::Dynamic

http://www.ml-china.org/blog/42

修改模型关联关系，去掉:conditions, order等rails3中的方法，在rails4中使用 ->{} 代替

  -  has_many :ws, :class_name => "B", :conditions => "b_type = 'b", :order => "status desc"
  -  has_many :ss, :class_name => "S", :conditions => "s_type = 's'", :order => "status desc"
  +  has_many :ws, -> { where("b_type = 'b'").order("status desc")}, :class_name => "B" 
  +  has_many :ss, -> { where("s_type = 's'").order("status desc")}, :class_name => "S"
validates 正则表达式

check_options_validity': The provided regular expression is using multiline anchors (^ or $)
rails4中validates不支持 ^ 和 $ , 替换成\A 和 \z

  -  ACCOUNT_EMAIL_FORMAT = /^\s*#{FORMAT}\s*$/
  +  ACCOUNT_EMAIL_FORMAT = /\A\s*#{FORMAT}\s\z/
方式一，使用\A \z替代 ^ $
validates :mobile_number, :format => { :with => FORMAT }, :allow_blank => true
方式二，使用multiline: true说明
validates :content, format: { with: /^Meanwhile$/, multiline: true }

http://stackoverflow.com/questions/17759735/regular-expressions-with-validations-in-ror-4
http://guides.rubyonrails.org/security.html#regular-expressions

rails4 has_many uniq

uniq在rails4中的关联关系中需要放在一个block中，而且作为has_many的第二个参数
The uniq option needs to be moved into a scope block. 
Note that the scope block needs to be the second parameter to has_many 
(i.e. you can't leave it at the end of the line,
it needs to be moved before the :through => :donations part):

  rails3: has_many :ds,  :through => :ds, :uniq => true
  rails4: has_many :ds, -> { uniq }, :through => :ds

  rails3: has_many :ds, :through => :ds, :uniq => true, :order => "name", :conditions => "age < 30"
  rails4: has_many :ds, -> { where("age < 30").order("name").uniq }, :through => :ds
http://stackoverflow.com/questions/16569994/deprecation-warning-when-using-has-many-through-uniq-in-rails-4

mongoid扩展

mongoid 从3.1.4升级到4.0.0, 查看源代码，发现mongoid的结构有了比较大的变化，那么我们在
3.1.4上做的扩展在这里就不适用了，主要从以下几个方面来进行检查

1.文件结构

  3.1.4: Mongoid::Persistence
  4.0.0: Mongoid::Persistence::Updatable
updatable是mongoid4抽象出来的一个module，原来放在Persistence这个module里的方法，一部分放到了
updatable中，重写方法的时候，注意函数的位置
2.函数

    def update_attributes_with_format(attributes = {}, options = {})
      update_attributes_without_format(attrs， options)
      alias_method_chain :update_attributes, :format
    end
扩展的时候重写了update_attributes方法，但是mongoid4.0.0版本中，已经去掉了options参数，这里也需要去掉

change logs mongoid去掉了IdentityMap，如果重写了IdentityMap相关的方法，就需要重新根据mongoid4.0.0 change log:
Scopes and default scopes must now all be defined within lambdas or procs.
skip_version_check config option was removed.
IdentityMap removed. (Arthur Neves)
Eager load rework. Eager load now doesnt need the identity map to load
related documents. A set of preloaders can eager load the associations
passed to .includes method. (Arthur Neves)

https://github.com/mongoid/mongoid/blob/006063727efe08c7fc5f5c93ef60be23327af422/CHANGELOG.md
https://github.com/mongoid/mongoid/issues/3406
rails4 加载多个路由文件

rails3中，项目比较大，很多路由被单独存放在 config/routes/*.rb 中， 
加载的时候，在application.rb文件中使用下面的代码进行加载
-config.paths['config/routes'].concat %w{aa bb cc ...}

而在rails4中，Rails::Engine.paths 去掉了 ["config/routes"] 的key
解决办法，在config/routes.rb 的首行进行加载
+Dir[Rails.root.join("config/routes/*.rb")].each{|route| load route}

http://stackoverflow.com/questions/18902878/how-to-reload-routes-config-routes-in-rails-4

rails 路由

rails4中如果使用match，必须指定http方法 
match 'homejson', to: 'afterwork#home_json', via: [:get, :post]

否则 需要替换match方法为get
- match 'like'
+ get 'like'

  get 'file/:file_id', action: 'file', as: 'file'
  delete 'file/:file_id', action: 'delete_file', as: 'file'
上面的这种配置，在rails4中会报以下错误， 解决办法是去掉最后一个，rails4会根据上一个自动添加
You may have defined two routes with the same name using the :as option

delete 'file/:file_id', action: 'delete_file'
find_or_create_by find_or_initialize_by

  rails3:
    User.find_or_create_by_name('XXX')

  rails4:
    User.where(name: 'XXX').first_or_create
    User.where(name: 'XXX').first_or_create!
    User.find_or_create_by(name: 'XXX')

  rails3:
    User.find_or_initialize_by_name('XXX')

  rails4:
    User.where(name: 'XXX').first_or_initialize
http://snippets.aktagon.com/snippets/620-rails-find-or-initialize-and-find-or-create-methods-are-deprecated

client_side_validations

client_side_validations 3.2.6 不适用rails4,暂时先去掉

preloading : no such column问题

    class Corp <  ActiveRecord::Base 
      has_many :ins, ->{where("corp_enus.category=2")}, class_name: "CorpEnu"
    end
方式一： 使用includes, 添加references
rails3
includes(:ins).where("corp_enus.enum_value1 = ?",id).order("followers desc").limit(limit)
rails4, 添加.references(:industries)
includes(:ins).where("corp_enus.enum_value1 = ?", id).order("followers desc").limit(limit).references(:ins)

方式二：使用eager_load替换includes
includes(:ins).where("corp_enus.enum_value1 = ?",id).order("followers desc").limit(limit)
eager_load(:ins).where("corp_enus.enum_value1 = ?",id).order("followers desc").limit(limit)

http://api.rubyonrails.org/v4.0.1/classes/ActiveRecord/QueryMethods.html#method-i-eager_load
http://api.rubyonrails.org/v4.0.1/classes/ActiveRecord/QueryMethods.html#method-i-preload
http://api.rubyonrails.org/v4.0.1/classes/ActiveRecord/QueryMethods.html#method-i-includes
http://blog.arkency.com/2013/12/rails4-preloading/

Mongoid::Errors::InvalidField

错误信息
Mongoid::Errors::InvalidField - 
Problem:
translation missing: zh-CN.mongoid.errors.messages.invalid_field.message
Summary:
translation missing: zh-CN.mongoid.errors.messages.invalid_field.summary
Resolution:
translation missing: zh-CN.mongoid.errors.messages.invalid_field.resolution:

  class AuthorizationMongo
    include Mongoid::Document
    include Mongoid::Timestamps

    field :deleted, type: Boolean, default: false
  end
修改字段 deleted 为 is_deleted, 问题解决

SystemStackError - stack level too deep

错误信息：
actionpack (4.0.0) lib/action_dispatch/middleware/reloader.rb:70:in `'
可能是写法问题导致的，调试发现把user.name 改成 user[:name]就不会出现这个错误
但是不清楚为什么
代码：

  def link_to_user(user, opt={})
      return if user.blank?
      inner = opt.delete(:inner)
      opt[:title] ||= "#{user[:name]}, #{user[:headline]}"
  end
把user.name 改成 user[:name]

uninitialized constant Mongoid::Observer

mongoid4去掉了Mongoid::Observer, 所以找不到Mongoid::Observer
在Gemfile中添加gem 'mongoid-observers'问题解决，用法和原来一样

    # 资料完整度的观察者
    class Profile::ProfileCentObserver < Mongoid::Observer
      observe :user_mongo
      def after_save user 
      end
    end