Rails Rails 4 升级第一弹

michael_roshen · 2014年10月27日 · 最后由 hooopo 回复于 2016年06月16日 · 10702 次阅读
本帖已被设为精华帖!

最近在做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程序员

共收到 12 条回复

👍 总结好,收藏!

上回升的时候碰到! 在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 嗯呐,可以用,两种方式都可以,已添加

赞,收藏

需要 登录 后方可回复, 如果你还没有账号请点击这里 注册