Automagic class prototyping

Discussion in 'Ruby' started by Aleks Kissinger, Jul 25, 2006.

  1. Anybody know any slick techniques for automatically prototyping
    classes so they can interact with each other in top-level code? I've
    run through two methods, but I'm not real wild about either of them.

    Say for example, classes represent some security label and I want to
    allow things with one label to access those of another label. So,
    these hypothetical classes all descent from Sec....

    class Sec
    class <<self
    def allow(c)
    (@allowed_classes||=[]) <<c
    end
    end
    end


    And I want to ultimately just indescriminately run stuff like this:

    class Documents < Sec
    allow Downloads
    end

    class Downloads < Sec
    allow Documents
    end

    But this is a run-time error unless the classes are prototyped somehow. Like:

    class Downloads < Sec; end

    class Documents < Sec
    allow Downloads
    end

    class Downloads < Sec
    allow Documents
    end

    As a framework, this would be so much nicer for the end-user if it
    does that automatically, so she doesnt have to thing about declaration
    order. So thats the problem. Possible solutions I've tried:

    1. Grep-prototyping:
    File.grep out all class defs and eval them before I actually require
    the code. I don't like this because it chokes on classes defined
    inside of modules or other classes. Grep has no idea about the
    structure of the ruby code.

    2. Delayed execution of the 'allow' statements.
    I dump allow statements off into proc's then after all the definitions
    are loaded, I run them. This works great, but syntactically its
    clunky.

    class Documents < Sec
    later do
    allow Downloads
    end
    end

    class Downloads < Sec
    later do
    allow Documents
    end
    end

    with:
    class Sec
    class << self
    def later(&code)
    (@code_for_later||=[]) << code
    end
    end

    And I go back and exec all the @code_for_later blocks when all the
    files are require'd. So, if any of you have questions about this code
    or have any ideas for a prettier way to do it, I'd love to hear it.
     
    Aleks Kissinger, Jul 25, 2006
    #1
    1. Advertising

  2. Aleks Kissinger

    Alex Young Guest

    Aleks Kissinger wrote:
    > Anybody know any slick techniques for automatically prototyping
    > classes so they can interact with each other in top-level code? I've
    > run through two methods, but I'm not real wild about either of them.
    >
    > Say for example, classes represent some security label and I want to
    > allow things with one label to access those of another label. So,
    > these hypothetical classes all descent from Sec....
    >
    > class Sec
    > class <<self
    > def allow(c)
    > (@allowed_classes||=[]) <<c
    > end
    > end
    > end
    >
    >
    > And I want to ultimately just indescriminately run stuff like this:
    >
    > class Documents < Sec
    > allow Downloads
    > end
    >
    > class Downloads < Sec
    > allow Documents
    > end
    >
    > But this is a run-time error unless the classes are prototyped somehow.
    > Like:
    >
    > class Downloads < Sec; end
    >
    > class Documents < Sec
    > allow Downloads
    > end
    >
    > class Downloads < Sec
    > allow Documents
    > end
    >
    > As a framework, this would be so much nicer for the end-user if it
    > does that automatically, so she doesnt have to thing about declaration
    > order. So thats the problem. Possible solutions I've tried:
    >
    > 1. Grep-prototyping:
    > File.grep out all class defs and eval them before I actually require
    > the code. I don't like this because it chokes on classes defined
    > inside of modules or other classes. Grep has no idea about the
    > structure of the ruby code.
    >
    > 2. Delayed execution of the 'allow' statements.
    > I dump allow statements off into proc's then after all the definitions
    > are loaded, I run them. This works great, but syntactically its
    > clunky.


    3:
    class Sec
    class <<self
    def allow(c)
    begin
    Kernel.const_get(c)
    rescue NameError #is there a better way to do this?
    class c < Sec; end
    end
    (@allowed_classes||=[]) <<c
    end
    end
    end

    There's probably stuff in there to trip you up, but you get the idea.
    I'm not too keen on using an exception like that, but I can't seem to
    find a better or more succinct way to test for the existence of a class.

    --
    Alex
     
    Alex Young, Jul 26, 2006
    #2
    1. Advertising

  3. If you merge the functions of your #later and #allow, you could write
    something like this:

    class Documents < Sec
    allow{Downloads}
    end

    class Downloads <Sec
    allow{Documents}
    end
     
    Caleb Clausen, Jul 26, 2006
    #3
  4. Thanks for the suggestions. The problem with the rescue on NameError
    technique is the exception gets thrown before we're actually in the
    method body of allow, since the offender is a parameter. I dig the
    idea of combining allow and later, but it gets a little uglier since I
    really need allow to take multiple args...

    allow {[ClassA, ClassB, OpC]}

    However, thats nicer looking than some of the other alternatives. If
    there was some sort of hook or metaprogramming magic that would delay
    executing class bodies until everything was defined, that would be
    ideal, but that might be stretching a little too far.
     
    Aleks Kissinger, Jul 26, 2006
    #4
  5. Aleks Kissinger

    Guest

    On Wed, 26 Jul 2006, Aleks Kissinger wrote:

    > Anybody know any slick techniques for automatically prototyping
    > classes so they can interact with each other in top-level code? I've
    > run through two methods, but I'm not real wild about either of them.
    >
    > Say for example, classes represent some security label and I want to
    > allow things with one label to access those of another label. So,
    > these hypothetical classes all descent from Sec....
    >
    > class Sec
    > class <<self
    > def allow(c)
    > (@allowed_classes||=[]) <<c
    > end
    > end
    > end
    >
    >
    > And I want to ultimately just indescriminately run stuff like this:
    >
    > class Documents < Sec
    > allow Downloads
    > end
    >
    > class Downloads < Sec
    > allow Documents
    > end
    >
    > But this is a run-time error unless the classes are prototyped somehow.
    > Like:
    >
    > class Downloads < Sec; end
    >
    > class Documents < Sec
    > allow Downloads
    > end
    >
    > class Downloads < Sec
    > allow Documents
    > end
    >
    > As a framework, this would be so much nicer for the end-user if it
    > does that automatically, so she doesnt have to thing about declaration
    > order. So thats the problem. Possible solutions I've tried:



    harp:~ > cat a.rb
    class Sec
    ALLOWED_CLASSES = []

    def self.allowed_classes
    ALLOWED_CLASSES
    end

    def self.allow *list
    allowed_classes.push *list
    allowed_classes.uniq!
    allowed_classes
    end

    def self.const_missing c
    c
    end

    def self.allowed
    allowed_classes.map{|c| eval c.to_s }
    end
    end

    class Documents < Sec
    allow Downloads
    end

    class Foobar < Sec
    allow Downloads
    end

    class Downloads < Sec
    allow Documents, Foobar
    end

    p Sec.allowed



    harp:~ > ruby a.rb
    [Downloads, Documents, Foobar]


    i'd push/pop that 'const_missing' handler during the 'allow' method to avoid
    any nasty recursion. but there ya go.

    -a
    --
    suffering increases your inner strength. also, the wishing for suffering
    makes the suffering disappear.
    - h.h. the 14th dali lama
     
    , Jul 26, 2006
    #5
  6. I can see some pitfalls here, but this is the most promising technique
    I've seen so far.

    On 7/26/06, <> wrote:
    > On Wed, 26 Jul 2006, Aleks Kissinger wrote:
    >
    > > Anybody know any slick techniques for automatically prototyping
    > > classes so they can interact with each other in top-level code? I've
    > > run through two methods, but I'm not real wild about either of them.
    > >
    > > Say for example, classes represent some security label and I want to
    > > allow things with one label to access those of another label. So,
    > > these hypothetical classes all descent from Sec....
    > >
    > > class Sec
    > > class <<self
    > > def allow(c)
    > > (@allowed_classes||=[]) <<c
    > > end
    > > end
    > > end
    > >
    > >
    > > And I want to ultimately just indescriminately run stuff like this:
    > >
    > > class Documents < Sec
    > > allow Downloads
    > > end
    > >
    > > class Downloads < Sec
    > > allow Documents
    > > end
    > >
    > > But this is a run-time error unless the classes are prototyped somehow.
    > > Like:
    > >
    > > class Downloads < Sec; end
    > >
    > > class Documents < Sec
    > > allow Downloads
    > > end
    > >
    > > class Downloads < Sec
    > > allow Documents
    > > end
    > >
    > > As a framework, this would be so much nicer for the end-user if it
    > > does that automatically, so she doesnt have to thing about declaration
    > > order. So thats the problem. Possible solutions I've tried:

    >
    >
    > harp:~ > cat a.rb
    > class Sec
    > ALLOWED_CLASSES = []
    >
    > def self.allowed_classes
    > ALLOWED_CLASSES
    > end
    >
    > def self.allow *list
    > allowed_classes.push *list
    > allowed_classes.uniq!
    > allowed_classes
    > end
    >
    > def self.const_missing c
    > c
    > end
    >
    > def self.allowed
    > allowed_classes.map{|c| eval c.to_s }
    > end
    > end
    >
    > class Documents < Sec
    > allow Downloads
    > end
    >
    > class Foobar < Sec
    > allow Downloads
    > end
    >
    > class Downloads < Sec
    > allow Documents, Foobar
    > end
    >
    > p Sec.allowed
    >
    >
    >
    > harp:~ > ruby a.rb
    > [Downloads, Documents, Foobar]
    >
    >
    > i'd push/pop that 'const_missing' handler during the 'allow' method to avoid
    > any nasty recursion. but there ya go.
    >
    > -a
    > --
    > suffering increases your inner strength. also, the wishing for suffering
    > makes the suffering disappear.
    > - h.h. the 14th dali lama
    >
    >
     
    Aleks Kissinger, Jul 26, 2006
    #6
  7. On Jul 26, 2006, at 1:40 AM, Aleks Kissinger wrote:

    > Thanks for the suggestions. The problem with the rescue on NameError
    > technique is the exception gets thrown before we're actually in the
    > method body of allow, since the offender is a parameter. I dig the
    > idea of combining allow and later, but it gets a little uglier since I
    > really need allow to take multiple args...
    >
    > allow {[ClassA, ClassB, OpC]}
    >
    > However, thats nicer looking than some of the other alternatives. If
    > there was some sort of hook or metaprogramming magic that would delay
    > executing class bodies until everything was defined, that would be
    > ideal, but that might be stretching a little too far.
    >


    how about:

    class Sec
    def self.const_missing(constant_name)
    Object.const_set(constant_name, Class.new(Sec))
    end
    end
     
    Logan Capaldo, Jul 26, 2006
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Jon Slaughter
    Replies:
    4
    Views:
    451
    Jon Slaughter
    Oct 26, 2005
  2. Mauricio Fernandez
    Replies:
    1
    Views:
    155
    Peña, Botp
    Oct 16, 2006
  3. Mauricio Fernandez
    Replies:
    4
    Views:
    123
    Mauricio Fernandez
    Dec 30, 2006
  4. Mauricio Fernandez
    Replies:
    2
    Views:
    98
    Benjamin Ritcey
    Jan 14, 2007
  5. Mauricio Fernandez
    Replies:
    4
    Views:
    144
    Mauricio Fernandez
    Feb 8, 2007
Loading...

Share This Page