Ruby on rails 如何在Rails 4.2中将nil保存到序列化属性中
我正在将一个应用程序升级到Rails 4.2,遇到了一个问题,即序列化为数组的字段中的nil值被解释为空数组。有没有办法让Rails 4.2区分序列化为数组属性的nil和空数组 顶级问题演示:Ruby on rails 如何在Rails 4.2中将nil保存到序列化属性中,ruby-on-rails,arrays,serialization,Ruby On Rails,Arrays,Serialization,我正在将一个应用程序升级到Rails 4.2,遇到了一个问题,即序列化为数组的字段中的nil值被解释为空数组。有没有办法让Rails 4.2区分序列化为数组属性的nil和空数组 顶级问题演示: #[old_app] > Rails.version => "3.0.3" > a = AsrProperty.new; a.save; a.keeps => nil #[new_app] > Rails.version => "4.2.3" >
#[old_app]
> Rails.version
=> "3.0.3"
> a = AsrProperty.new; a.save; a.keeps
=> nil
#[new_app]
> Rails.version
=> "4.2.3"
> a = AsrProperty.new; a.save; a.keeps
=> []
但是对于我的代码来说,区分nil和[]很重要,所以这是一个问题
模型:
class AsrProperty < ActiveRecord::Base
serialize :keeps, Array
#[...]
end
将保存[]时生成的语句更改为:
将值('--[]\n',0)插入到asr\U属性(保留,锁定版本
)中
允许:
> a = AsrProperty.new; a.save; a.keeps
=> nil
我现在将使用此解决方案,但是:
(1) 我觉得声明一个类型可以提高效率,还可以通过明确禁止存储错误的数据类型来防止bug
(2) 如果Rails允许的话,我真的很想找到一种“正确”的方法
那么:Rails 4.2是否可以被告知将[]作为自己的东西存储在序列化为数组属性中?发生了什么?
您所经历的是Rails 4如何处理序列化调用的第二个参数。它根据参数可以具有的三个不同值更改其行为(在解决方案中有更多关于这一点的内容)。最后一个分支是我们感兴趣的分支,因为当您传递Array
类时,它会传递给创建的ActiveRecord::Coders::YAMLColumn
实例。load
方法从数据库接收YAML并尝试将其转换回Ruby对象。如果编码器没有被赋予Object
的默认类,并且yaml
参数在null
列的情况下为nil
,那么它将返回该类的新实例,从而返回空数组
解决方案
似乎没有简单的Rails-y方式来表示,“嘿,如果数据库中的null
,请给我nil
”,但是查看第二个分支,我们可以传递实现load
和dump
方法或我称之为基本编码器协议的任何对象
示例代码
我团队的一名成员构建了这个简单的类来处理这个案例
class NullableSerializer < ActiveRecord::Coders::YAMLColumn
def load(yaml)
return nil if yaml.nil?
super
end
end
“正确”的方法
完成工作并发布代码是至关重要的,我希望这对您有所帮助。毕竟,如果代码没有被使用并且做得很好,谁会在乎它有多理想呢
我认为Rails的方法在这种情况下是正确的,特别是当您考虑Ruby的最小惊喜原则时。当一个属性可能是一个数组时,它应该始终返回该类型,即使是空的,以避免不断地使用特殊情况nil
。对于任何可以设置合理默认值的数据库列,我也会提出同样的观点(即t.integer:anything\u除了外键之外,默认值为0
)。我一直很感激过去的Aaron,因为每当我为nil:NilClass
得到一个意外的NoMethodError:undefined方法'whatever'时,他大部分时间都记得这一点。对于这个nil
我的特例几乎总是提供一个合理的默认值
这在你、你的团队、你的应用程序、你的应用程序和它的需求上都有很大的不同,所以它从来都不是很难做到的。当我在做某件事,想知道amount
是否可以默认为0
,或者代码中或者你的队友的头脑中是否有某种原因,为什么它需要能够nil
作为代码的用户,我的期望,那就是a.keeps
应该始终返回一个数组,因此您应该相应地调整代码,使其始终为数组,而不是有时为数组,有时为零。但这与数据库中存储的其他类型不一致。例如,可以使用返回nil的整数类型。NULL可以在数据库中的任何类型列中,因此它将与现有行为(与其他类型)和以前的行为(可以存储nil)更加一致,以允许存储和检索NULL。在ruby世界中,而不是数据库中,:keeps
不是nil
它是空的antar,你是什么意思?[]为空但不为零,nil为零且不响应:empty?。Rails正在数据库中将nil和[]保存为NULL。旧版本的Rails会将[]另存为[]。保存到_yaml,这允许您区分nil和[](空)。如果序列化时没有声明类型,则当前Rails还会将[]另存为[]。对于任何受SQL支持的类型,SQL NULL将作为Ruby nil检索。仅不适用于指定类型的序列化字段。因此,对我来说,当前Rails中的行为是令人困惑的。Andrew你是对的,其他字段(例如整数和字符串)可以有值或为零。但对我来说,对于任何返回数组的方法,约定是如果没有信息,那么它应该返回一个空数组,而不是nil。数组通常用于数据结构操作,例如与其他数组相加、相减、相交等,或将值推入其中。如果数组中的变量有时可能是nil,这将迫使编码人员编写各种额外的异常处理,这是一个PITA。感谢详细的解释和简洁的解决方案!我想我不是100%肯定我同意不允许nil
在这里是最不令人惊讶的,尽管我已经同意,如果所有其他列都可以做出类似的行为(就在这里,你说的“金额为零有意义吗?”)。但事实并非如此:所有SQL本机列都可以是其声明类型的值或nil
。也就是说,您要么想要nil
s,要么必须将返回Integer
s的安全列设为nil,因此对我来说,返回Array
s的列更令人惊讶
class NullableSerializer < ActiveRecord::Coders::YAMLColumn
def load(yaml)
return nil if yaml.nil?
super
end
end
class AsrProperty < ActiveRecord::Base
serialize :keeps, NullableSerializer.new(Array)
# …
end