Rails Rails 3.2 升级到 Rails 4 中遇到的问题

warmwind · September 27, 2014 · Last by rdongxie replied at October 29, 2014 · 7406 hits
Topic has been selected as the excellent topic by the admin.

很久一段时间以来,我们使用的都是 Rails3.2 + Mongoid3,虽然 Rails4 发布已经快一年的时间了,但由于 mongoid3 不能支持 Rails4,所以升级就一推再推,不过终于在近期 Mongoid 发布 4.0 以后完成了这次期盼已经的升级。心情是兴奋地,不过过程还是曲折的,不少细节,只看升级文档,或者 google,不看源码还是真心不好解决。本文不是升级指导,因已经有很多文章,本文将对这次升级遇到的问题做个简单的介绍,包括了 Rails,Mongoid,Capistrano。

每次升级有两个前提必须保证才可以稍微顺利一些:

  • 完备的测试
  • 通读官方升级文档。当然升级基本完成后才发现因为已经有 Rails4 已经有一年的时间,网上其实有不少可以参考的其他人的文章,中英文都可以,还有好心人翻译了国外的博文。

1. Strong Parameters 它主要用来增强 mass assignment 的安全性,Rails3 中通过使用 attr_accessible 在 model 层面进行控制,没有声明为 attr_accessible 的属性不能用 mass assignment 来赋值。但通常来说这个赋值的行为发生在 controller 级别,所以 Strong Parameter 将这样行为的限制上升在 controller,并通过下面的格式来进行限制。

params.permit(:name, {:emails => []}, :friends => [ :name, { :family => [ :name ], :hobbies => [] }])

这其中定义了三种格式的参数类型,其中期望 emails 的值为 Array 的类型,而 friends 是一组 Array 的资源,有 name 属性,family 的值为 Array 并含有 name 属性,hobbies 的值则是 Array 类型。

2. controller 测试异常缓慢 我们使用的 MiniTest,升级完成后运行 controller 测试时,非常非常的缓慢。后来发现当测试中 request 请求成功后,停在了在 render layout 那这一步,需要将近 5 分钟才可以完成。测试本身是成功的,而这 5 分钟也与 asset precompile 的时间类似。

Rails 4 no longer sets default config values for Sprockets in test.rb, so test.rb now requires Sprockets configuration. The old defaults in the test environment are: config.assets.compile = true, config.assets.compress = false, config.assets.debug = false and config.assets.digest = false.

其中最主要的设置为

# Don't fallback to assets pipeline if a precompiled asset is missed
config.assets.compile = true

此项设置的主要目的是当找不到 precompiled 的 asset 时是不是需要时时编译。当然,我们是不需要这样的设置的,所以当设置为false时就解决了这个问题。 不过有个问题还是不明白,之前的默认值为true是如何正确工作的呢?

3. 测试单独通过,rake test 失败 使用 rake 运行所有测试时,抛出

TypeError: compared with non class/module

无法定位是什么问题,好在有人遇到了一样的问题 https://github.com/freerange/mocha/issues/199,不要使用 ruby2.0.0-p0,改为 2.0.0-p353 就好了。

4. Capistrano 原先使用的是 Capistrano2,由于 Capistrano3 做了很大的改动,所以为了平稳尽快完成 Rails 的升级,对 Capistrano 尽量做到最小的改动,这篇文章一定要看。其中两点最重要:

  • 升级到 2.15.4
  • 将 manifest.yml 从 shared/assets 目录移到 releases,并重命名为 assets_manifest.yml,否则部署时会报错说有重复的 manifest 文件

需要注意的是,升级之后在部署过中可能会看到一些 err 输出,实际上是 Capistrano 将 info 的输出信息作为 err 打印到 console 了。参见这里INFO messages while asset precompiling treated as errors

5. 嵌入的支持 Rails4 会在 response 的 header 里增加一下的默认值,其中SAMEORIGIN限定了 iframe 在同一个 domain 中可以使用。如果取消这一限制有两种做法,一个是在下面的全局配置中将X-Frame-Options改为ALLOWALL。当然,如果只想针对单个请求,可以将这个设置在该请求的 response 中去除response.headers.except! 'X-Frame-Options'

config.action_dispatch.default_headers = {
'X-Frame-Options' => 'SAMEORIGIN',
'X-XSS-Protection' => '1; mode=block',
'X-Content-Type-Options' => 'nosniff'
}

6. Mongid 中使用 Only 后的限制 升级 Mongoid4 后,使用 Only 后的 model 对象将为只读,不可以再修改,否则会抛出下面的异常。检测 document 是否为只读可以直接在 model 上调用readonly?。在 Mongoid3 中没有这样的限制

Mongoid::Errors::ReadonlyDocument:
Problem:  Attempted to persist the readonly document 'Entry'.
Summary:
  Documents loaded from the database using #only cannot be persisted.
Resolution:
  Donot attempt to persist documents that are flagged as readonly.

另外使用 only 后,如果直接读取没有加载的属性,将抛出异常ActiveModel::MissingAttributeError: Missing attribute: 'not_load_attr’。在 Mongoid3 中返回 nil。

下面是一些升级指导的链接

原文来自我的博客,不正确的地方欢迎大家交流。

升级 rails4 还好啦,把 bootstrap 2 升级 Bootstrap 3 折腾了我好久。

升级 Rails4 给我造成的一个 Bug 是我有一个嵌套的 parameter 没有 Permit, Rails 仅仅在日志中给出了 Warning, Unpermitted XXX, 过了好久才发现。这当然部分原因是我的测试写的不够充分。不过大家也要留点心。

config.assets.compile = true 对于这个问题,我也有疑惑,是不是大家针对静态文件都会选择预编译?

#1 楼 @48hour 我们也快要做 Bootstrap 的升级了。。。

非常好,我准备找个 Rails3 的项目升级一下练练手。哈哈

Unknow user #6 October 01, 2014

:plus1:

You need to Sign in before reply, if you don't have an account, please Sign up first.