Ruby on Rails 似乎破坏了方法中的常量

Ruby on Rails seems to corrupt a constant in a method

提问人:Qd1y 提问时间:10/23/2023 更新时间:10/23/2023 访问量:40

问:

我的常量似乎被修改了,但它怎么可能。

环境是 Rails 7.0.7.2 Ruby 3.1.2p20 通过 rbenv

考虑

module ArrayTestMethods
  PROFILE_PARAMS = [["id","id"],["customer_id","contactId"],["channelId","channelId"],["identifierId","identifierId"],["platform","platform"],["identifier","identifier"],["created","createdAt"],["updated","updatedAt"]].freeze

  def showme
    params = {"id"=>"d1", "contactId"=>"contact1", "channelId"=>"bbc2", "identifierId"=>"ident2", "platform"=>"events", "inheritedFrom"=>"", "createdAt"=>"2023-10-19T17:03:58Z", "updatedAt"=>"2023-10-19T17:03:58Z", "identifier"=>"353860872561"}
    x = model_from_hash(params, PROFILE_PARAMS)
    pp x

  end
  def json_walker(params, *args)
    args.each do | arg|
      params = params[arg]
      return nil if params.nil?
    end
    return params
  end
   def model_from_hash(params, array_map, debug = false)
    #im doing the below as a test i've tried
    # array = array_map.clone same result
    # i've also tried using array_map on its own
    array =  Array.new
    array << array_map
    attributes_array = array[0]

    puts "array map before"
    pp attributes_array
    attr_hash = Hash.new

    attributes_array.each do | arr|
      model_attribute = arr[0]
      arr.shift
      data = json_walker(params, *arr)
      puts "arr0 - #{model_attribute} data - #{data}" if debug
    end
    puts "array map after "
    pp array_map
    return array_map
  end
  module_function :json_walker, :model_from_hash, :showme

如果你运行一次 ArrayTestMethods::showme,你会得到Run 1

如果你第二次运行,你会得到Run 2

当然,结果应该是Array should be unchanged

阵列 Ruby-on-Rails Ruby

评论

2赞 Holger Just 10/23/2023
不要将代码或文本数据的屏幕截图放入您的问题中。将数据添加为实际的代码格式文本。这样,我们就可以尝试、复制、编辑和改进您的代码,并实际回答您的问题。对此,请编辑您的问题并将屏幕截图替换为文本。
1赞 Qd1y 10/23/2023
我最初确实尝试这样做,但 Stack 不让我发布这个问题。我得到了太多的代码,而没有足够的文本或关于那里。如果运行代码,将获得输出。重现 ''' rails new testconstant''' 的步骤 创建 app/lib,将上面的代码放在 array_test_methods.rb 中,然后运行 ArrayTestMethods::showme
2赞 max 10/23/2023
在 Ruby 中,不会深度冻结对象。因此,数组中的对象仍然是可变的。这段代码也非常复杂和被误导 - 创建对象和方法,甚至使用哈希,而不是用数组做所有这些古怪的事情。.freeze

答:

2赞 spickermann 10/23/2023 #1

arr.shift修改数组。

因此,您可以在不修改原始数组的情况下将值从一个变量传递到另一个变量,而不是修改块中的数组。each

我会尝试改变

attributes_array.each do | arr|
  model_attribute = arr[0]
  arr.shift
  data = json_walker(params, *arr)
  puts "arr0 - #{model_attribute} data - #{data}" if debug
end

attributes_array.each do | arr|
  model_attribute = arr[0]                # first element
  data = json_walker(params, arr[1..-1])  # elements from second position to the end
  puts "arr0 - #{model_attribute} data - #{data}" if debug
end
1赞 max 10/23/2023 #2

首先,你似乎对 Ruby 抱有不切实际的期望。它不会深度冻结包含在另一个对象中的对象。例如:.freeze

[1, 2, 3].freeze.push(0) # can't modify frozen Array: [1, 2, 3] (FrozenError)
[[1, 2, 3]].freeze.tap {|x| x[0].push(4)} == [[1, 2, 3, 4]] # true

这不是数据被“损坏”。这就是语言的工作方式,Rails 并没有改变 Ruby 的基本原理。

如果您要做的是规范化一组输入参数或生成序列化输出,那么有更好的方法可以做到这一点。

您可以简单地创建一个 Hash,其中包含输入键和规范化形式的映射:

MAPPING = {
  "contactId"   => "customer_id"
  "createdAt"   => "created"
  "updatedAt"   => "updated"
}.freeze

然后,您只需转换哈希的键

params.transform_keys(MAPPING)
{"id"=>"d1",
 "customer_id"=>"contact1",
 "channelId"=>"bbc2",
 "identifierId"=>"ident2",
 "platform"=>"events",
 "inheritedFrom"=>"",
 "created"=>"2023-10-19T17:03:58Z",
 "updated"=>"2023-10-19T17:03:58Z",
 "identifier"=>"353860872561"}

如果作业更复杂,请创建一个具有属性的对象(类),并在 setter、initializer 或工厂方法中处理转换。

class ThingAdapter
  attribute_accessor :customer_id
  alias_method :contactId=, :customer_id=

  def initialize(**params)
    params.each do |k,v|
      self.send("#{k}=", v)
    end
  end
end

一般来说,在 Rails 中,每当你进行任何繁重的数组或哈希操作时,你几乎肯定做错了。

评论

0赞 max 10/24/2023
是的,Ruby 中的常量并不意味着不可变。这只是意味着,如果你尝试重新分配 Ruby,它会抱怨一点。
0赞 Qd1y 10/24/2023
谢谢大家。对不起,我把问题复杂化了。在控制台中,定义一个常量,然后PARAMS.object_id给出 497700。然后如果我说y.object_id给出 528900。如果我现在执行'y[0].shift,我得到[[1],[1,1]]。然后,如果我将 PARAMS 放在控制台中,即使它们是两个不同的对象,我也会得到与 y 相同的结果。- 我错过了什么PARAMS=[[0,0],[1,1]]y = PARAMS.clone
0赞 max 10/24/2023
这是因为它不是深度克隆。数组内部的数组是对原始对象的引用。.请参阅 makandracards.com/makandra/...x = ARY.clone; x[0].object_id == ARY[0].object_id;
0赞 max 10/24/2023
同样,对数组实际上并不是您通常应该使用的数据类型。我会放弃这个尝试,花一些时间实际学习如何在 Ruby 中使用哈希值以及 Enumerable 提供的方法。你基本上用一些非常糟糕的代码把自己画到了一个角落里,你并没有真正去尝试修复它。