不能在 RoR 夹具中使用哈希数组

Cannot use Array of Hashes in RoR fixtures

提问人:zaphodbln_ 提问时间:10/15/2023 最后编辑:zaphodbln_ 更新时间:10/15/2023 访问量:59

问:

我将 Ruby On Rails 与 PostgreSQL 数据库一起使用。出于性能原因,我使用带有哈希数组的 jsonb 字段,而不是将其提取到特定表中。在现实世界中,这个 blob 看起来像

[ { "row": 1, "price": 0, "column": 1, "status": 1, "index_row": 1, "contingency": 0, "index_column": 1 }, { "row": 1, "price": 0, "column": 2, "status": 1, "index_row": 1, "contingency": 0, "index_column": 2 }... ]

并且工作正常。

现在,我试图增加测试覆盖率,并注意到,我无法使用标准夹具方法创建哈希数组。假设以下情况:

CREATE TABLE IF NOT EXISTS public.circles
(
    id bigint NOT NULL DEFAULT nextval('circles_id_seq'::regclass),
    blob jsonb
)

以及包含以下内容的夹具文件circles.yml:

templatecircle_1:
  blob: <%= [1, 2] %>

这在运行 bin/rails 测试时按预期工作。

templatecircle_1:
  blob: {"abc": "def"}

这也按预期工作。

然而

templatecircle_1:
  blob: <%= [1, {"abc": "def"}] %>

产生 ActiveRecord::Fixture::FormatError:

请注意,YAML 必须始终使用空格缩进。不允许使用制表符。错误:():在第 2 行第 11 列解析流节点时未找到预期的节点内容

我什至尝试使用以下解决方法:

templatecircle_1:
  blob: <%= JSON.parse("[1, {\"abc\": \"def\"}]") %>

这给出了相同的错误。

有没有人知道,如何在 RoR 夹具中创建哈希数组?

Ruby-on-Rails PostgreSQL 夹具小 测试

评论

0赞 max 10/15/2023
这很可能是过早/优化不良的情况。随便一张桌子。维护者/未来你会感谢你。2ndquadrant.com/en/blog/......
1赞 mechnicov 10/15/2023
看起来您尝试在JSON字段中存储结构良好的数据。这不是引入此列类型的目的。JSON 仅适用于半结构化、非常不同的非关系数据。关于性能改进的论点也是有问题的,您可能需要使用特殊索引来索引此字段
0赞 max 10/15/2023
如果我们忽略这是否是一个好主意,您只需要对数组和哈希使用正确的 YAML 语法即可。一般来说,你几乎永远不需要手动将任何内容转换为JSON,这样做只会导致你存储一个字符串而不是实际的JSON对象。数据库驱动程序将您分配的任何内容转换为 JSON。我也不明白你为什么要在这里插值。夹具的全部意义在于 KISS,只需编写实际的文字值即可。
0赞 zaphodbln_ 10/15/2023
嗨,Max - 感谢您参考 YAML 手册。

答:

1赞 mechnicov 10/15/2023 #1

若要在 YAML 固定器文件中定义“哈希数组”字段,请对每个数组元素和每个哈希键/值对使用表示法。不需要 ERB 语法或一些 JSON 解析。就是这样的结构:-:

# test/fixtures/circles.yml

templatecircle_1:
  blob:
    - row: 1
      price: 0
      column: 1
      status: 1
      index_row: 1
      contingency: 0
      index_column: 1
    - row: 1
      price: 0
      column: 2
      status: 1
      index_row: 1
      contingency: 0
      index_column: 2

之后,您可以在测试中调用

Circle.first.blob
# => [{
#      "row"=>1,
#      "price"=>0,
#      "column"=>1,
#      "status"=>1,
#      "index_row"=>1,
#      "contingency"=>0,
#      "index_column"=>1
#    },
#      "row"=>1,
#      "price"=>0,
#      "column"=>2,
#      "status"=>1,
#      "index_row"=>1,
#      "contingency"=>0,
#      "index_column"=>2
#    }]

如您所见,它将被自动解析


因此,对于结构将是:[1, {"abc": "def"}]

# test/fixtures/circles.yml

templatecircle_1:
  blob:
    - 1
    - abc: def

在 Ruby 中:

Circle.first.blob
# => [1, {"abc"=>"def"}]

也可以使用这样的语法(只需从原始尝试中删除 ERB):

# test/fixtures/circles.yml

templatecircle_1:
  blob: [1, {"abc": "def"}]

在 Ruby 中:

Circle.first.blob
# => [1, {"abc"=>"def"}]

如果您由于某些原因仍然需要 ERB,您可以使用方法to_json

# test/fixtures/circles.yml

templatecircle_1:
  blob: <%= [1, { "abc": "def" }].to_json %>

在 Ruby 中:

Circle.first.blob
# => [1, {"abc"=>"def"}]

评论

0赞 zaphodbln_ 10/15/2023
这成功了。我不能完全省略 ERB,因为我需要 100 个条目并使用 ERB 以获得更好的可维护性,但我可以从那里获得它。再次感谢大家。
0赞 mechnicov 10/15/2023
@zaphodbln_,我已经用 ERB 更新了答案,在这种情况下您需要to_json
0赞 zaphodbln_ 10/15/2023
我现在有并且一切都按预期工作templatecircle_1: blob: <% 100.times do |i| %> - row: <%= (1 + i / 10) %> price: 0 column: <%= (1 + i % 10) %> status: <%= Circle::SeatOccupancyFree %> index_row: <%= (1 + i / 10).to_s %> contingency: 0 index_column: <%= (1 + i % 10).to_s %> <% end %>