В структуре Spree Модель Product определяет частный метод под названием build_variants_from_option_values_hash.

Этот метод обычно вызывается изнутри функцией обратного вызова after_create и объявляется внутри класса как private. Я хотел бы использовать этот метод вне обычного жизненного цикла create и вызывать его напрямую, однако, поскольку он объявлен частным образом, к сожалению, он не виден за пределами класса.

Вопрос: Есть ли способ изменить видимость метода без его переопределения?

Используя class_eval, я могу переопределить метод в декораторе продукта за пределами области private, и это действительно помогает. Однако мне кажется, что полное копирование всего метода просто для того, чтобы изменить его видимость, является ненужным подходом типа «обезьяньего исправления». Есть ли лучший способ добиться этого?

0
Paul Richter 1 Дек 2014 в 19:51
2
Почему бы просто не отправить ему аргументы с помощью send :private_method, *args?
 – 
Малъ Скрылевъ
1 Дек 2014 в 19:54
Ага, понятно. Я это почему-то не учел. Спасибо, похоже, это работает.
 – 
Paul Richter
1 Дек 2014 в 19:58

2 ответа

Лучший ответ

Хотя подход @ МалъСкрылевъ более разумен, IMO, в качестве альтернативы вы можете создать публичный псевдоним метода:

Product.class_eval do

  alias_method :public_build_variants, :build_variants_from_option_values_hash
  public :public_build_variants

end

Который теперь можно было использовать как

p = Product.new
p.public_build_variants
1
deefour 1 Дек 2014 в 20:00
А, понятно, очень мило, я тоже не подумал о псевдонимах. Что касается части public :public_build_variants, есть ли какая-то причина, по которой я мог бы избежать прямого изменения видимости метода, а не его псевдонима (другими словами: public :build_variants_from_option_values_hash)?
 – 
Paul Richter
1 Дек 2014 в 20:33
1
Обычно есть очень веская причина, по которой разработчик гема пометил метод как частный. Я ничего не знаю о драгоценном камне, о котором идет речь, поэтому я не могу говорить о точных мотивах в этом случае. Как правило, приватные методы не предназначены для использования разработчиками, что позволяет сопровождающему гема модифицировать/переименовывать/удалять такую ​​функциональность без прерывания BC.
 – 
deefour
1 Дек 2014 в 23:19

Подход с переопределением видимости метода в ruby не годится. Но, тем не менее, вы можете сделать это, получив и переопределив тот же метод для публичного пространства следующим образом:

class CC
   private
   def private_method
   end
end
CC.new.private_method # => NoMethodError: private method `private_method' called for #<CC:0x8166144>

method = CC.instance_method(:private_method)
CC.send(:remove_method, :private_method)
CC.send(:define_method, :private_method, method)
CC.new.private_method # => nil

Но правильный способ вызвать частный метод - использовать открытый метод #send следующим образом:

object.send :private_method, *args
1
Малъ Скрылевъ 1 Дек 2014 в 23:04
Спасибо, что опубликовали это, очень полезно.
 – 
Paul Richter
3 Дек 2014 в 00:25
Да, оба ответа были очень хороши, и мне было трудно решить, какой из них принять. Однако я решил пойти с другим из-за того, что Дифур поднял в своем комментарии; что, поскольку имя метода может измениться (поскольку он объявлен как закрытый, разработчики могут свободно переименовывать его по мере необходимости), если я использую метод Дифура, мне нужно будет изменить имя только один раз, чтобы обновить мой код, потому что я объявил это в модели, как он описал. Теперь я мог бы сделать что-то подобное с вашим методом, но мне показалось, что версия псевдонима была немного чище.
 – 
Paul Richter
3 Дек 2014 в 19:32
Хороший способ — использовать #send, битовый псевдоним или get/assign =)
 – 
Малъ Скрылевъ
3 Дек 2014 в 19:56
Извините, я не совсем понял, что вы сказали с помощью bit alias или get/assign. Однако, если вы считаете, что метод send является лучшим подходом, я полностью открыт на 100%, если вы хотите помочь мне понять вашу позицию. Как я уже сказал, оба метода казались мне более или менее одинаково хорошими, если не считать упомянутой мною вещи "чистоты". Как вы думаете, почему #send может быть лучшим подходом?
 – 
Paul Richter
3 Дек 2014 в 20:01
Тем лучше, что он не вводится в классовую структуру. это просто косвенный вызов частного метода, например, прямой вызов общедоступного.
 – 
Малъ Скрылевъ
3 Дек 2014 в 20:08