Rails 5 динамически добавляет непостоянные атрибуты ActiveRecord

#ruby-on-rails #ruby-on-rails-5 #rails-activerecord

#ruby-on-rails #ruby-on-rails-5 #rails-activerecord

Вопрос:

У меня есть приложение Rails 2.3, которое я хочу обновить до Rails 5.

В некоторых моих моделях есть непостоянные атрибуты, я мог бы таким образом читать и записывать в свой контроллер:

   r_v = Model.new
  r_v[:count_all_bqu] = 0
  r_v[:count_all_bsk] = 0
  r_v[:count_all_bmd] = 0
  

Я изменил структуру, потому что по какой-то причине Rails 5 больше не обрабатывает ее.

Итак, я создал attr_accessors в своей модели:

   attr_accessor :count_all_bqu
  attr_accessor :count_all_bsk
  attr_accessor :count_all_bmd
  

и заменил приведенные выше строки на эти:

   r_v = Model.new
  r_v.count_all_bqu = 0
  r_v.count_all_bsk = 0
  r_v.count_all_bmd = 0  
  

Пока никаких проблем.

Но есть и такие вещи, как:

   for r in Model.where(condition)
    r_v["count_#{r.b_id}_erg_bst"] = 0
    ...
  

Как мне преобразовать «count_#{r.b_id}_erg_bst» в непостоянный атрибут?

Ответ №1:

Хотя я уверен, что есть другой ActiveModel (RoR Guides, API — но это может быть не ActiveModel просто аналогичный модуль) способ сделать это, с простым Ruby вы бы сделали это так:

 r_v.send("count_#{r.b_id}_erg_bst=", 0)
  

По сути, вы «вызываете» метод count_..._erg...= (метод, присваивающий локальную переменную, определенную с помощью attr_accessor ) с аргументом 0 .

Ибо r.bid == 'my' это было бы то же самое, что и вызов r_v.count_my_erg_bst= 0 .

Обратите внимание, что это будет работать только в том случае, если что-то подобное attr_accessor :count_my_erg_bst является частью вашего определения класса.

В противном случае вы можете сделать это более метапрограммным с помощью чего-то вроде rv.instance_eval { @count_my_erg_bst = 0 } или, потому что вам нужна интерполяция строк rv.instance_eval " @count_#{r.bid}_erg_bst = 0 "

Обратите внимание на последствия для безопасности!Если r.bid предоставлено пользователем, это может быть что-то вроде "a = 1; system("rm -rf /");" или другой вредоносный код!

Комментарии:

1. Спасибо, Феликс! Хорошая идея, к сожалению, у меня это не совсем сработало, потому что я также хотел бы сделать сравнения и дополнения. Тем не менее, это привело меня в правильном направлении. Теперь я решил это так: я создал attr_accessor :z_hash в своей модели, которую я заполняю в контроллере парами ключ-значение: r_v.z_hash["count_#{r.b_id}_erg_bst"] = 0