Automagic class prototyping

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

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

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

···

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

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:

--
suffering increases your inner strength. also, the wishing for suffering
makes the suffering disappear.
- h.h. the 14th dali lama

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.

I can see some pitfalls here, but this is the most promising technique
I've seen so far.

···

On 7/26/06, ara.t.howard@noaa.gov <ara.t.howard@noaa.gov> 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
>
> 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

how about:

class Sec
   def self.const_missing(constant_name)
     Object.const_set(constant_name, Class.new(Sec))
   end
end

···

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.