Automagic class prototyping

A

Aleks Kissinger

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

Alex Young

Aleks said:
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.
 
C

Caleb Clausen

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
 
A

Aleks Kissinger

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

ara.t.howard

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
 
A

Aleks Kissinger

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

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
 
L

Logan Capaldo

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
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,483
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top