[lnkForumImage]
TotalShareware - Download Free Software

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


 

Forums >

comp.lang.ruby

Automagic class prototyping

Aleks Kissinger

7/25/2006 9:28:00 PM

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.

6 Answers

Alex Young

7/25/2006 11:51:00 PM

0

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


Caleb Clausen

7/26/2006 1:16:00 AM

0

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

Aleks Kissinger

7/26/2006 5:40:00 AM

0

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.

Ara.T.Howard

7/26/2006 6:33:00 AM

0

Aleks Kissinger

7/26/2006 1:30:00 PM

0

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

Logan Capaldo

7/26/2006 9:53:00 PM

0


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