[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.ruby

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

Kevin Barnes

9/24/2007 2:05:00 PM

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

14 Answers

Paul Brannan

9/24/2007 2:34:00 PM

0

On Mon, Sep 24, 2007 at 11:04:55PM +0900, Kevin Barnes 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


Kevin Barnes

9/24/2007 2:53:00 PM

0



On Sep 24, 9:33 am, Paul Brannan <pbran...@atdesk.com> 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


Yossef Mendelssohn

9/24/2007 3:42:00 PM

0

On Sep 24, 9:53 am, vinbarnes <vinbar...@gmail.com> wrote:
> On Sep 24, 9:33 am, Paul Brannan <pbran...@atdesk.com> 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

--
-yossef


Yossef Mendelssohn

9/24/2007 4:54:00 PM

0

On Sep 24, 10:42 am, Yossef Mendelssohn <ymen...@pobox.com> wrote:
> On Sep 24, 9:53 am, vinbarnes <vinbar...@gmail.com> wrote:
>
> > On Sep 24, 9:33 am, Paul Brannan <pbran...@atdesk.com> 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
>
> --
> -yossef

Wow, excellent double-sig there. To balance it all out, I won't put a
sig on this.


Gaspard Bucher

9/24/2007 5:34:00 PM

0

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

2007/9/24, vinbarnes <vinbarnes@gmail.com>:
>
>
> On Sep 24, 9:33 am, Paul Brannan <pbran...@atdesk.com> 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
>

Kevin Barnes

9/24/2007 7:12:00 PM

0


On Sep 24, 12:34 pm, "Gaspard Bucher" <gasp...@teti.ch> 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


Gaspard Bucher

9/24/2007 7:40:00 PM

0

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, 12:34 pm, "Gaspard Bucher" <gasp...@teti.ch> 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
>

Tom M

9/25/2007 12:37:00 AM

0


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


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.


Yossef Mendelssohn

9/25/2007 4:09:00 PM

0

On Sep 24, 7:40 pm, Tom M <thomas.mack...@gmail.com> wrote:
> > 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).
>
> 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.

--
-yossef


Giles Bowkett

9/26/2007 7:21:00 AM

0

> > 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 Bowkett

Blog: http://gilesbowkett.bl...
Portfolio: http://www.gilesg...
Tumblelog: http://giles.t...