Best way to 'hide' a method when method_missing is in town

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?

Cheers,
Kevin

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.

Paul

On Sep 24, 9:33 am, Paul B. [email protected] wrote:

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

On Sep 24, 9:53 am, vinbarnes [email protected] wrote:

On Sep 24, 9:33 am, Paul B. [email protected] wrote:

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).


-yossef

On Sep 24, 10:42 am, Yossef M. [email protected] wrote:

end

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.

On Sep 24, 12:34 pm, “Gaspard B.” [email protected] wrote:

Shouldn’t you just use attr_protected ?
class Node < ActiveRecord::Base
attr_protected :no_see
end

It does not prevent the attribute from being used from an instance of
the object.

Kevin

Shouldn’t you just use attr_protected ?
class Node < ActiveRecord::Base
attr_protected :no_see
end

2007/9/24, vinbarnes [email protected]:

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

def no_see
puts ‘hidden’
end
hide :no_see
end

s = Sub.new
s.see_me
s.no_see

On Sep 24, 7:40 pm, Tom M [email protected] wrote:

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.

class Penguin
def yes
“YES!!1”
end
end
=> nil

pokey = Penguin.new
=> #Penguin:0x109562c

pokey.yes
=> “YES!!1”

Penguin.class_eval {remove_method :yes}
=> Penguin

pokey.yes
NoMethodError: undefined method `yes’ for #Penguin:0x109562c
from (irb):30

However, I would seriously advocate caution here. Stripping methods
from ActiveRecord models manually is kind of running up the down
staircase.


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

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.

On Sep 26, 2:21 am, “Giles B.” [email protected] wrote:

class Penguin

NoMethodError: undefined method `yes’ for #Penguin:0x109562c
Portfolio:http://www.gilesgoatboy.org
Tumblelog:http://giles.tumblr.com/

But removing the method doesn’t work when method_missing is in play:

Cassady:~ yossef$ irb
irb(main):001:0> class Penguin
irb(main):002:1> def yes
irb(main):003:2> “YES!!1”
irb(main):004:2> end
irb(main):005:1>
irb(main):006:1* def method_missing(meth, args, &block)
irb(main):007:2> “caught attempt at #{meth}”
irb(main):008:2> end
irb(main):009:1> end
=> nil
irb(main):010:0>
irb(main):011:0
pokey = Penguin.new
=> #Penguin:0x732a8
irb(main):012:0> pokey.yes
=> “YES!!1”
irb(main):013:0> Penguin.class_eval {remove_method :yes}
=> Penguin
irb(main):014:0> pokey.yes
=> “caught attempt at yes”

Penguin.class_eval {remove_method :yes}

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.


Giles B.

Blog: http://gilesbowkett.blogspot.com
Portfolio: http://www.gilesgoatboy.org
Tumblelog: http://giles.tumblr.com/

end

class B < A
end

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