【问题标题】:Rails serialization of Range of integers is broken整数范围的 Rails 序列化被破坏
【发布时间】:2012-07-09 03:19:39
【问题描述】:

我需要在 Rails 上下文中使用 YAML 序列化 ruby​​ Ranges。 我想检查整数范围和字符串范围是否正确序列化。

这是我的测试:

# classic irb
require 'yaml'         # => true
YAML::VERSION          # => "0.60"
YAML.dump(1..2)        # => "--- !ruby/range \nbegin: 1\nend: 2\nexcl: false\n"
YAML.dump("1".."2")    # => "--- !ruby/range \nbegin: \"1\"\nend: \"2\"\nexcl: false\n"

这两个输出完全不同,所以我向前并在我的 rails 应用程序中对其进行了编码。

然而,在 Rails 上下文中,ruby 似乎忘记了如何正确序列化整数范围!

# ./script/rails console
Rails::VERSION::STRING # => "3.0.15"
RUBY_VERSION           # => "1.8.7"
YAML::VERSION          # => "0.60"
YAML.dump(1..2)        # => "--- !ruby/range\n  begin: 1\n  end: 2\n  excl: false"
YAML.dump("1".."2")    # => "--- !ruby/range\n  begin: 1\n  end: 2\n  excl: false"
# The two outputs are identical, the distinction between integers and strings is lost!

ruby 和 ruby​​ on rails 似乎都使用相同版本的 YAML 库。 如果我没记错的话,我的 ruby​​ 版本不支持在多个编码器引擎之间切换。

我有几个问题:

  1. 造成这种差异的原因是什么?
  2. 较新版本的 ruby / rails 是否会出现此问题?
  3. 如何以兼容的方式正确解决该问题?

感谢您的帮助。

【问题讨论】:

  • 这2种情况下YAML::ENGINE.yamler的值是多少?
  • NameError: uninitialized constant YAML::ENGINE 在这两种情况下,我猜这是由于

标签: ruby-on-rails ruby serialization range yaml


【解决方案1】:

范围是 Ruby 内部的,而不是 YAML 基本类型,如整数或字符串。与其按原样对范围进行编码,不如使用它的起点和终点并在接收端重建范围。

我使用类似的东西:

[1] (pry) main: 0> range = 0..1
=> 0..1
[2] (pry) main: 0> require 'yaml'
=> true
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range\nbegin: 0\nend: 1\nexcl: false\n"
[4] (pry) main: 0> YAML.dump({'min' => range.min, 'max' => range.max})
=> "---\nmin: 0\nmax: 1\n"

然后我可以使用类似的东西在接收端重新创建范围:

Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values)
=> 0..1

或者如果您不确定'min''max' 的顺序是否正确,请使用这个:

[19] (pry) main: 0> Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values_at('min', 'max'))
=> 0..1

添加一些关于 Ruby 1.9.3+ 序列化字符范围的信息:

[2] (pry) main: 0> range = '0'..'1'
=> "0".."1"
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range\nbegin: '0'\nend: '1'\nexcl: false\n"

[5] (pry) main: 0> RUBY_VERSION
=> "1.9.3"

再次使用 1.9.2+:

[2] (pry) main: 0> range = '0'..'1'
=> "0".."1"
[3] (pry) main: 0> YAML.dump(range)
=> "--- !ruby/range \nbegin: \"0\"\nend: \"1\"\nexcl: false\n"
[4] (pry) main: 0> RUBY_VERSION
=> "1.9.2"

而且,解决方法保持范围开始/结束类型:

[6] (pry) main: 0> Range.new(*YAML.load(YAML.dump({'min' => range.min, 'max' => range.max})).values_at('min', 'max'))
=> "0".."1"

在这两种情况下,YAML_VERSION 都是0.60

【讨论】:

  • 我正在序列化许多不同类型的对象,但是当 Ranges 存在时,它们是最顶层的对象。所以从这个意义上说,我唯一能做的就是检测一个 Range 并使用模拟 YAML 的自定义编码,并且仍然对其他类型使用纯 YAML ......我对此不是 100% 满意(因为它只是hack),但它可以解决问题。
  • 顺便说一句,我看到你有同样的问题。你在 Rails 控制台中使用过 pry 吗?什么版本的红宝石,轨道?你在用 yaml 吗?
  • 范围在 1.9+ 中序列化时保持其“类型”。查看添加的信息。
  • 感谢您提供更多信息。序列化发生在 ActiveRecord 3.0.15 中,使用 serialize 函数。这个版本甚至不允许我在一个类上提供dumpload 函数。我能想到的唯一解决方案是覆盖设置器并将值替换为专门创建的FixedYamlRange
猜你喜欢
  • 2016-01-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-26
相关资源
最近更新 更多