Paul Richter
Paul Richter

Reputation: 11072

Alter method visibility without redefining method

In the Spree framework, the Product model defines a private method called build_variants_from_option_values_hash.

This method is normally called internally by an after_create callback, and it is declared as private within the class. I would like to use of this method outside of the normal "create" lifecycle and call it directly to, however since it is declared privately, unfortunately it is not visible outside of the class.

Question: Is there a way to alter / modify the visibility of a method without redefining it?

Using class_eval, I can redefine the method in a product decorator outside of the private area, and this does do the trick. However, I feel like completely copying over an entire method simply to change its visibility is an unnecessary "monkey patching"-type approach. Is there a better way to accomplish this?

Upvotes: 0

Views: 302

Answers (2)

Малъ Скрылевъ
Малъ Скрылевъ

Reputation: 16507

Approach with redefining visibility of a method in ruby is not good. But however you are able to do this by getting and redefining the same method to public space as follows:

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

but the proper way to invoke the private method is to use #send public method as follows:

object.send :private_method, *args

Upvotes: 1

deefour
deefour

Reputation: 35350

Though @МалъСкрылевъ's approach is more sensible, IMO, you could alternatively create a public alias of the method:

Product.class_eval do

  alias_method :public_build_variants, :build_variants_from_option_values_hash
  public :public_build_variants

end

which could now be used as

p = Product.new
p.public_build_variants

Upvotes: 1

Related Questions