# Question about BlankSlate.reveal

**URL:** <https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579>\
**Category:** Ruby\
**Created:** [January 25, 2009, 11:33am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579 "2009-01-25T11:33:27Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 25, 2009, 11:33am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/1 "2009-01-25T11:33:27Z")

</div>

Hi folks,

I’ve been working on a chapter for “Ruby Best Practices”[0] about the  
dynamic nature of Ruby, and I initially thought Jim W.'s  
BlankSlate would be an excellent example (even though 1.9 has  
BasicObject).  
However, when I look at the reveal class method, I found what seems to  
be a fairly major limitation that I am having a hard time glossing  
over in my prose.

The issue is that when you reveal a previously hidden method, the  
UnboundMethod that was stored gets bound to whatever the first  
instance is that calls the revealed method, because Jim’s code caches  
the bound\_method via a closure. The best ‘fix’ I got was to drop the  
caching and rebind the method to self on every single method call, but  
I wasn’t able to get away with that without saying “This is probably a  
performance nightmare”. Does anyone have a better suggested fix? The  
original code for reveal, pulled from the builder gem, is below.

-greg

[0] [http://rubybestpractices.com](http://rubybestpractices.com)

### Code pulled from blankslate.rb in the builder gem.

```
# Redefine a previously hidden method so that it may be called on a 

```

blank  
# slate object.  
def reveal(name)  
bound\_method = nil  
unbound\_method = find\_hidden\_method(name)  
fail “Don’t know how to reveal method ‘#{name}’” unless  
unbound\_method  
define\_method(name) do |\*args|  
bound\_method ||= unbound\_method.bind(self)  
bound\_method.call(\*args)  
end  
end  
end

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 26, 2009, 2:58pm UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/2 "2009-01-26T14:58:08Z")

</div>

2009/1/25 Gregory B. [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid):

> The issue is that when you reveal a previously hidden method, the  
> UnboundMethod that was stored gets bound to whatever the first  
> instance is that calls the revealed method, because Jim’s code caches  
> the bound\_method via a closure. The best ‘fix’ I got was to drop the  
> caching and rebind the method to self on every single method call, but  
> I wasn’t able to get away with that without saying “This is probably a  
> performance nightmare”. Does anyone have a better suggested fix? The  
> original code for reveal, pulled from the builder gem, is below.

From what you write caching a bound method seems a bad idea because it  
is instance specific. Caching of an unbound method would seem much  
more reasonable (unless, that is, I am missing something).

On the other hand:

> ```
> define_method(name) do |*args|
> bound_method ||= unbound_method.bind(self)
> bound_method.call(*args)
> end
> 
> ```
> 
> end  
> end

I do not really see caching that extends the instance on which #reveal  
was invoked. There is actually one closure per instance (more  
correctly: one closure per call of reveal) and so no global cache.

Where exactly is your problem?

Cheers

robert

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 26, 2009, 5:21pm UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/3 "2009-01-26T17:21:14Z")

</div>

On Mon, Jan 26, 2009 at 8:55 AM, Robert K.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> I do not really see caching that extends the instance on which #reveal  
> was invoked. There is actually one closure per instance (more  
> correctly: one closure per call of reveal) and so no global cache.
> 
> Where exactly is your problem?

I think you missed that the local variable was at the class level.

> > class A  
> > a = nil  
> > define\_method :foo do  
> > ?\> a ||= self  
> > end  
> > end  
> > =\> #Proc:0x000585d4@:11(irb)  
> > A.new.foo  
> > =\> #\<A:0x57030\>  
> > A.new.foo  
> > =\> #\<A:0x57030\>  
> > A.new.foo  
> > =\> #\<A:0x57030\>  
> > A.new.foo  
> > =\> #\<A:0x57030\>

But I think I have a suitable fix:  
[http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826)

What do you think?

-greg

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 26, 2009, 10:10pm UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/4 "2009-01-26T22:10:28Z")

</div>

On 26.01.2009 17:18, Gregory B. wrote:

> On Mon, Jan 26, 2009 at 8:55 AM, Robert K.  
> [removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:
> 
> > I do not really see caching that extends the instance on which #reveal  
> > was invoked. There is actually one closure per instance (more  
> > correctly: one closure per call of reveal) and so no global cache.
> > 
> > Where exactly is your problem?
> 
> I think you missed that the local variable was at the class level.

Right, you stated so in the text and then I got confused by  
“unbound\_method.bind(self)”. 🙂

But in that case my remark about binding at class level remains true:  
there is no point in binding to a particular instance at class level and  
caching that. And your code seems to indicate that you agree with me  
there.

> But I think I have a suitable fix:  
> [http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826)
> 
> What do you think?

Looks good to me. I didn’t test it though. 😉

Kind regards

robert

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 27, 2009, 12:35am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/5 "2009-01-27T00:35:58Z")

</div>

On Tue, 27 Jan 2009, Robert K. wrote:

> > I think you missed that the local variable was at the class level.
> 
> Right, you stated so in the text and then I got confused by  
> “unbound\_method.bind(self)”. 🙂

Sorry – I posted my reply to your earlier post without having noticed  
this post.

David

–  
David A. Black / Ruby Power and Light, LLC  
Ruby/Rails consulting & training: [http://www.rubypal.com](http://www.rubypal.com)  
Coming in 2009: The Well-Grounded Rubyist ([The Well-Grounded Rubyist](http://manning.com/black2))

[http://www.wishsight.com](http://www.wishsight.com) =\> Independent, social wishlist management!

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 27, 2009, 12:34am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/6 "2009-01-27T00:34:47Z")

</div>

Hi –

On Mon, 26 Jan 2009, Robert K. wrote:

> > 
> 
> is instance specific. Caching of an unbound method would seem much
> 
> > def reveal(name)  
> > I do not really see caching that extends the instance on which #reveal  
> > was invoked. There is actually one closure per instance (more  
> > correctly: one closure per call of reveal) and so no global cache.
> 
> Where exactly is your problem?

Let’s say you do BasicSlate.reveal(:meth). Then you do slate.meth. At  
that point, bound\_method in the closure is the meth method, bound to  
the object slate. When you then call other\_slate.meth, the ||=  
short-circuits, and bound\_method is _still_ the unbound method bound  
to slate (not to other\_slat).

So every time you call meth, you’re calling it on the same instance.

David

–  
David A. Black / Ruby Power and Light, LLC  
Ruby/Rails consulting & training: [http://www.rubypal.com](http://www.rubypal.com)  
Coming in 2009: The Well-Grounded Rubyist ([The Well-Grounded Rubyist](http://manning.com/black2))

[http://www.wishsight.com](http://www.wishsight.com) =\> Independent, social wishlist management!

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 27, 2009, 12:38am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/7 "2009-01-27T00:38:19Z")

</div>

On Mon, Jan 26, 2009 at 4:07 PM, Robert K.

---

<div class="post-metadata">

**Author:** ![naething](https://www.ruby-forum.com/letter_avatar_proxy/v4/letter/n/e47774/32.png) [@naething](https://www.ruby-forum.com/u/naething)\
**Post date:** [January 27, 2009, 12:40am UTC](https://www.ruby-forum.com/t/question-about-blankslate-reveal/158579/8 "2009-01-27T00:40:04Z")

</div>

On Mon, Jan 26, 2009 at 4:07 PM, Robert K.  
[removed\_email\_address@domain.invalid](mailto:removed_email_address@domain.invalid) wrote:

> > [http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826](http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/325826)
> > 
> > What do you think?
> 
> Looks good to me. I didn’t test it though. 😉

Cool. This is what I’m going to go with in my chapter. I emailed Jim  
to see if I can get a definitive ‘yes, this is a) a bug or b) some  
special casing for builder’s needs or c) some special sauce that you  
missed’. My guess is it’s a), but stranger things have happened 🙂

-greg
