Rails Rails 4 升级第一弹

michael_roshen · 2014年10月27日 · 最后由 hooopo 回复于 2016年06月16日 · 11584 次阅读
本帖已被管理员设置为精华贴

最近在做 rails4 升级的工作,过程中遇到了很多问题,这里记录下来供大家参考,个人建议如果还没有 用过 rails4 的,可以自己建一个 blog 工程,看一下整体结构跟 rails3 有什么不同的地方,哪里需要修改, 另外去官方网站把 rails4 升级的 change log 简单的过一遍,google 以下其他人的升级步骤和遇到的问题及 解决方案

下面是为在升级过程中遇到的问题及解决方案,链接为参考的资料,另外部分代码做了简单的处理,只为 说明问题,未完待续,关于发布和测试的正在整理中。。

不足或有错误的地方请指教^^

修改 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 参数,这里也需要去掉

  1. change logs mongoid 去掉了 IdentityMap,如果重写了 IdentityMap 相关的方法,就需要重新根据 mongoid4.0.0 change log:
  2. Scopes and default scopes must now all be defined within lambdas or procs.
  3. skip_version_check config option was removed.
  4. IdentityMap removed. (Arthur Neves)
  5. 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

https://github.com/chamnap/mongoid-observers

博文:http://michael-roshen.iteye.com/blog/2148179 微信:ruby 程序员

👍 总结好,收藏!

上回升的时候碰到! 在 rails3 用到了 strong_parameters,在升到 rails4 中就不用 protected_attributes 这个!

2 楼 已删除

好赞!坐等加精~

最近也在升级,发现 rails 4 precompile 所以的静态资源都 digest 过了,去掉了 non-digest

#4 楼 @lihaidong 有没有 procompile 升级得文档,借鉴一下

3.2 应该先升级到 4.0 版本,然后再升 4.1 这样比较好吧?

#6 楼 @ericguo 一步到位吧,rails 问题倒是不大,依赖包问题比较多,尤其是 mongoid

#5 楼 @michael_roshen 我比较简单粗暴,为了兼容 rails 3 的非 tag 的静态资源引用,写了个 non-digest rake 将 assets 下所有静态资源反 digest 了一次,此方案感觉不好

#9 楼 @michael_roshen find_or_create_by方法在 rails 4 下还是能用的吧,只是调用方式变了下:find_or_create_by(name: 'XXX')

#10 楼 @xuncheng 嗯呐,可以用,两种方式都可以,已添加

赞,收藏

michael_roshen Rails 4 升级第二弹 提及了此话题。 04月03日 10:57
需要 登录 后方可回复, 如果你还没有账号请 注册新账号