Rails Rails 4 升级第一弹

michael_roshen · October 27, 2014 · Last by hooopo replied at June 16, 2016 · 11505 hits
Topic has been selected as the excellent topic by the admin.

最近在做 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 Floor has deleted

好赞!坐等加精~

最近也在升级,发现 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 in Rails 4 升级第二弹 mention this topic. 03 Apr 10:57
You need to Sign in before reply, if you don't have an account, please Sign up first.