I am trying to hide a method in a subclass whose base class has
method_missing defined.
class Base
def method_missing(method, *args)
puts “#{method} caught in base”
end
end
class Sub < Base
def see_me
puts ‘hi’
end
def no_see
puts ‘hidden’
end
private :no_see
end
s = Sub.new
s.see_me
s.no_see
Of course, the method isn’t really hidden/private anymore. I know one
way to solve this with method_missing in Sub and querying
private_methods.include? Is there a better way?
On Mon, Sep 24, 2007 at 11:04:55PM +0900, Kevin B. wrote:
Of course, the method isn’t really hidden/private anymore. I know one
way to solve this with method_missing in Sub and querying
private_methods.include? Is there a better way?
The method is still private, and can’t be called via Sub’s public
interface.
There probably is a better solution to your problem, but it depends on why you desire this behavior.
There probably is a better solution to your problem, but it depends on why you desire this behavior.
I’m trying to hide attributes in a Rails model. ActiveRecord::Base has
method_missing defined to build getter/setter methods for attributes
on the fly. But the problem is more general in nature as I described.
There probably is a better solution to your problem, but it depends on why you desire this behavior.
I’m trying to hide attributes in a Rails model. ActiveRecord::Base has
method_missing defined to build getter/setter methods for attributes
on the fly. But the problem is more general in nature as I described.
Kevin
As you said, this problem can be solved with method_missing in Sub.
class Sub
def method_missing(method, *args)
if private_methods.include?(method.to_s)
send(method, *args)
else
puts “#{method} caught in sub”
end
end
end
This isn’t a solution for your problem, but a comment on the
(possible?) root of it: IMO, method_missing shouldn’t fire on private/
protected method calls. My opinion goes further, that calling a
private or protected method shouldn’t raise the same exception as
calling a method that doesn’t exist. If for some reason one wants to
track attempted access to private methods, rescuing NoMethodError and
checking text is somewhat unsavory. It would be nicer to rescue
MethodAccessLevelError (or something with an even better name), which
could be related to NoMethodError based on use cases (right now, I see
them as children of the same parent).
MethodAccessLevelError (or something with an even better name), which
could be related to NoMethodError based on use cases (right now, I see
them as children of the same parent).
–
-yossef
–
-yossef
Wow, excellent double-sig there. To balance it all out, I won’t put a
sig on this.
What about this solution ?
class HidenMethod < Exception; end
module Hide
def self.included(klass)
klass.class_eval <<-END_TXT
@@hidden_methods = []
def self.hide(method)
private method
@@hidden_methods << method.to_sym
end
def method_missing(method, *args)
raise Hide::HidenMethod.new("Hidden \#{method} called.") if
@@hidden_methods.include?(method)
super
end
END_TXT
end
end
class Base
def method_missing(method, *args)
puts “#{method} caught in base”
end
end
class Sub < Base
include Hide
def see_me
puts ‘hi’
end
Your suggestion seems to violate the notion of encapsulation. If an
change were to be made, I’d think it would be better to make the error
the same for calling a parent’s private method as it is for calling a
non-existant method.
A parent’s private method? I’m not sure I follow. Do you mean
something like the following?
class A
private
def priv_method
‘private’
end
end
class B < A
end
b = B.new
b.priv_method
Because that’s not calling a parent private method. The private method
is inherited from A and part of B now.
My suggestion might violate encapsulation. It’s not necessary to
change which exceptions are raised, but I am convinced that
method_missing shouldn’t come into the picture when attempting to call
a private or protected method. I took a look at the method_missing
code and it specifically checks for different reasons why the desired
method couldn’t be called, including access level. And it uses the
NoMethodError exception.
It seems to me that there are two viewpoints to consider. From outside
the class, there’s no difference between calling a private or
protected method and a method that doesn’t exist: it simply doesn’t
work. But a private method actually exists, so it is not exactly the
same as an undefined method. With that in mind, I go with my feeling
that they should be treated differently.
There probably is a better solution to your problem, but it depends on why you desire this behavior.
I’m trying to hide attributes in a Rails model. ActiveRecord::Base has
method_missing defined to build getter/setter methods for attributes
on the fly. But the problem is more general in nature as I described.
In keeping with the idea that you should do the simplest thing that
can possibly work, I would just remove_method the getters and setters.
them as children of the same parent).
Your suggestion seems to violate the notion of encapsulation. If an
change were to be made, I’d think it would be better to make the error
the same for calling a parent’s private method as it is for calling a
non-existant method.
But removing the method doesn’t work when method_missing is in play:
Doh. Good point. I think the masking method_missing in the subclass is
the best way to do it. You can probably also just define empty
methods, in which case the method won’t be missing, it’ll be empty.
This really seems like a bad idea, though. I ran into a situation on a
Rails app where I thought I needed something like this and then I
realized just changing the name of a particular table would involve
infinitely fewer headaches.
b = B.new
b.priv_method
Yes… that is the scenario I was referring to.
Because that’s not calling a parent private method. The private method
is inherited from A and part of B now.
That’s how I understand it to work.
Your analysis seems sound enough. When using a class or module, I
consider it’s interface to be its public methods. Unless you override
the default behavior of Object#respond_to?, it responds false for
private methods. Also, whether you are inside our outside a class,
you can’t call a private method w/ an explicit receiver, so
foo.priv_method is never valid; regardless of whether or not you are
in the class. Thus, to me, priave methods are “fair game” to be
appropriated by method_missing. (Note: I am not weighing in here on
what I think good usage of method_missing is!)
At the end of the day though, I think this point can be argued both
ways ad infinitum.
Cheers,
Tom
This forum is not affiliated to the Ruby language, Ruby on Rails framework, nor any Ruby applications discussed here.