Ruby on rails 如何在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" >

我正在将一个应用程序升级到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"
 > 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