Как я могу следить за методом `[] =` в хэше ruby?

У меня есть хэш. Один из этих ключей - daily_budget. После некоторого процесса daily_budget был изменен, а значения всех остальных ключей - нет. Либо значение хэша для этого ключа изменилось, либо я клонирую хеш и устанавливаю это значение в клонированном хэше.

Я хочу шпионить за методом Hash#[]=, чтобы выяснить, где это происходит. Я бы обезьяна исправил его, следил за ключом с именем daily_budget и сбрасывал трассировку стека всякий раз, когда он установлен.

Я пытался использовать такой код:

module HashPatches
  def []=(key, value)
    puts ">>>> hey! I got here!"
    super(key, value)
  end
end

Hash.send(:include, HashPatches)

Похоже, это изменение игнорируется, в то время как другие исправления на Hash работают. Я сделал что-то не так?

Я также пробовал использовать set_trace_func для отслеживания вызовов хеша с помощью этого кода,

set_trace_func proc { |event, file, line, id, binding, classname|
  if file =~ /\/my_project_name\//
    puts ">>>> #{id}"
    puts ">>>> #{classname}"
    puts ">>>> #{event}"
    puts ">>>> #{file}"
    puts ">>>> #{line}"
  end
}

но :[]= не отслеживается. Я удалил пластырь обезьяны. Я не смог заставить использовать :[]=, чтобы появиться в этом выводе.

Есть ли способ отслеживать изменения хэшей, чтобы я мог отследить, где изменяется значение этого ключа?

Вероятно, было бы лучше обернуть оскорбительный Hash в прокси-класс и отслеживать все вызовы, чем было бы обезьяны исправлять класс Hash. Также помните, что переназначение ключа не требуется для изменения значения, например. h = {n: "hello"}; h[:n].concat(" world"); h[:n] #=> "hello world" или h = {n: 12}; h[:n] += 12; h[:n] #=> 24 и, более того, Hash#[]= - не единственный метод, который изменяет назначения ключей => значений, которые могут сделать другие методы, такие как store, transform_values, update, merge и т. д.

engineersmnky 31.10.2018 15:01

Я согласен с вами, но я не уверен, есть ли другой экземпляр хэша, созданный dup с этим хешем или нет. Это всего лишь один способ увидеть, что происходит.

Doug Hughes 01.11.2018 18:44

Если вы оберните его в прокси-объект, вы можете отслеживать все сделанные вызовы, включая дублирование, и переносить дублирование в другой прокси-объект, поэтому я предложил это

engineersmnky 01.11.2018 19:00
2
3
102
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вы можете как обезьяна пропатчить сам класс Hash. Поместите это в определение модуля / класса,

class Hash
  def []=(arg)
    # do your magic here with a debugger or pry
  end 
end

Обновлено: этот способ устарел, но оставлен для справки.

Действительно, это был старый способ. Он устарел уже 5 лет. Современный способ - это Module#prepend

Sergio Tulentsev 01.11.2018 10:42
Ответ принят как подходящий

Hash.send(:include, HashPatches) заставляет ваш HashPatches#[]= вызываться только тогда, когда (оригинальный) Hash#[]= недоступен, что не так. Кроме того, super в вашем определении HashPatches#[]= не будет работать, поскольку суперкласс Object вашего HashPatches не имеет []=.

Чтобы дать вашему HashPatches#[]= приоритет над оригинальным Hash#[]=, вам необходимо сделать:

Hash.prepend(HashPatches)

Другие вопросы по теме