The Artima Developer Community
Sponsored Link

Ruby Buzz Forum
Block Your Privates!

0 replies on 1 page.

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 0 replies on 1 page
Rick DeNatale

Posts: 269
Nickname: rdenatale
Registered: Sep, 2007

Rick DeNatale is a consultant with over three decades of experience in OO technology.
Block Your Privates! Posted: Sep 6, 2007 8:27 AM
Reply to this message Reply

This post originated from an RSS feed registered with Ruby Buzz by Rick DeNatale.
Original Post: Block Your Privates!
Feed Title: Talk Like A Duck
Feed URL: http://talklikeaduck.denhaven2.com/articles.atom
Feed Description: Musings on Ruby, Rails, and other topics by an experienced object technologist.
Latest Ruby Buzz Posts
Latest Ruby Buzz Posts by Rick DeNatale
Latest Posts From Talk Like A Duck

Advertisement

I’ve noticed that some rubyists like to use indentation to delineate method visibility.

The first time I noticed this was when Marcel Molina Jr. used it during the charity testing workshop at the Ruby Hoedown.. I just encountered it again in at least one piece of sample code from Rob Orsini’s “Rails Cookbook.”

During the testing workshop, Chad Fowler expressed displeasure with this style, and I’ve got to agree.

Here’s an example of this style:

class Gadget
  def framilize
    razzlitize if options[:secret_ingredient_6X]
  end

  private
    def razzlitize
    end
end

The problem is that the indentation is “artificial”, there really is no nesting of evaluation scope here. The way that Module#private and it’s kin work when they are called without arguments is to set a marker onto Ruby’s eval stack which gets popped when the current evaluation context is exitted. So the effects of the private method remain in effect until either the end of the current context, or one of the other methods like public or protected, whichever comes first. This means that if we extend the above example:

class Gadget
  #...
  private
    def razzlitize
    end

  def dazzlitize
  end
end

The indentation can trick us into thinking that dazzlitize is public when it’s really private.

I’d always found the nature of methods like private without arguments to be a minor annoyance with Ruby syntax. In my humble opinion they should have been able to take a block which delineated their effect. What if we could write:

class Gadget
  #...
  private do
    def razzlitize
    end
  end

  def dazzlitize
  end
end

Well, actually we can write that, but it doesn’t work as expected, since the block is silently ignored, and the method doesn’t even get defined.

So I started doing a little metaprogramming, to fix this. It seemed obvious how to do this. You alias_method Module#private so you can extend it, redefine the method to check for a block and if none is given, call the original method, otherwise evaluate the block in a way which makes any methods defined private. And of course you use test driven design to do this.

I made some progress but ran into some problems:

  1. The obvious way to evaluate the block:
    class module
      alias :old_private, :private
      def private(*args, &blk)
        if blk_given?
           module_eval do
             old_private
             blk.call
           end
        else
          old_private(*args)
        end
    end

    Doesn’t seem to work. The method isn’t made private. I had to resort to getting a list of the methods before the block, then calling old_private with the difference between the new methods after the block is evaluated, and those before.

    The module_eval now looked something like this:

    module_eval do
      existing = instance_methods(false)
      blk.call
      new_methods = instance_methods(false)-existing
      old_private(new_methods.map {|m| m.to_sym}

    There are some obvious shortcomings in this code, but at this point, the goal was to do the simplest thing which could work for the test cases at hands, the complications would be dealt with later.

  2. To add insult to injury, calling old_private from within the new definition doesn’t work either, the evaluation context gets marked as described above, but it gets popped off when we exit the context of the new definition. At this point I decided to punt, temporarily at least, and just define a new method Module#with_private which took a block, and leave the existing Module#private alone.
  3. Then I decided that I should test changing the visibility of an existing method, in other words a test case like this:
    class Test
      protected
      def meth;end
      with_private do
         def meth;end
      end
    end

    Test#meth should end up private, but my simple diff failed to notice the definition, things were starting to get more complicated. It looked like I’d need to hook Module#method_added and probably others. I began to wonder whether it was worth it.

  4. Then I started to ponder the fact that the effect of private/protected/public gets popped when the evaluation context exits. So I said to myself, “Is there a way to take advantage of that?” Well, Virginia, yes there is.

    class Gadget
      #...
      class_eval do
        private
    
        def razzlitize
        end
    
      end
    
      def dazzlitize
      end
    end

    This seems to work as expected, here’s a link to a test case file.

    Now I still prefer being able to give a block to Module#private and it’s ilk, and I might still work on that, but in the meantime, class_eval and module_eval seem to provide a way to actually do what I want.

Read: Block Your Privates!

Topic: Authenticated rss proxy Previous Topic   Next Topic Topic: Disabling nfsv4 on Ubuntu

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use