Proc metaprogramming tricks?

A

Adam Skobi

Hi,

I'm building a DSL and trying some weird syntax structures. My DSL
should look like this.

class Root
def dsl(&block)
instance_eval(&block)
end
end

class C
#...
end


#DSL

dsl {
met1

class C1 < C
#...
end

met2
}

Is there a way that the class C1 won't be defined inside class Root
but inside C (or somewhere else)? Maybe there is a trick in Ruby (hook
method?) so that the class C1 won't be created in Root at all and than
I could pass the block somewhere else for creation?
 
A

Adam Skobi

=0D=0A>> I'm building a DSL and trying some weird syntax structures. My DSL
=A0 def dsl(&block)
=A0 =A0 C.module_eval(&block)
=A0 end

Well yeah, that would work. But I was thinking about something more
general.

class Root
def dsl(&block)
instance_eval(&block)
end
end

class C
#...
end

class D
#...
end

dsl {
met1

class C1 < C
#...
end

class D1 < D
#...
end

}

Can I run part of the Proc in the context of C and part in the
context of D i.e. can I somehow split the Proc or convert it to a
readable String?

The more I think about it, the more absurd the reasoning behind it
seems. But since I'm at it, I may as well continue the debate :)

--=20
Adam Skobi
 
M

Mikael Høilund

class C
def self.inherited(klass)
klass_name = klass.name[/[^:]+$/]
klass.to_s.split(/::/)[0...-1].inject(Object) { |const, name|
const.const_get name
}.send :remove_const, klass_name
C.const_set klass_name, klass
end
end

NB: Don't do this.
 
A

Adam Skobi

class C
def self.inherited(klass)
klass_name = klass.name[/[^:]+$/]
klass.to_s.split(/::/)[0...-1].inject(Object) { |const, name|
const.const_get name
}.send :remove_const, klass_name
C.const_set klass_name, klass
end
end
NB: Don't do this.

Yes! Why haven't I thought of this solution?!
Thanks for that one.

Are the any non obvious reasons as why not to do this type of trick
(code obfuscation, maintance hell etc.)?
 
M

Mikael Høilund

class C
def self.inherited(klass)
klass_name = klass.name[/[^:]+$/]
klass.to_s.split(/::/)[0...-1].inject(Object) { |const, name|
const.const_get name
}.send :remove_const, klass_name
C.const_set klass_name, klass
end
end
NB: Don't do this.

Yes! Why haven't I thought of this solution?!
Thanks for that one.

Are the any non obvious reasons as why not to do this type of trick
(code obfuscation, maintance hell etc.)?

That, and

--8<----
class Ddddd < C
end

Ddddd.some_method # !> NameError
--8<----

Also, my roommate laughed uncontrollably the whole time I wrote that
snippet.

--
a,b=%Q=Z,O^NPO\r4_PV\\PI\x15^-\x0\v=,email=%\%%%c\%115%%# Mikael
Hoilund, CTO
okay=%#;hmm=(0...a.size).map{|i|((a-email+2)%128).# of Meta.io
ApS from
chr}.join;!email.gsub!'o',"%c%c"%[3+?0.<<(2),?G.~@];aha=#############
Denmark
hmm.scan(/#{'(.)'*5}/);!puts(email[1..-12]+aha.shift.zip(*aha).join)#
Ruby <3
 
T

Trans

Well yeah, that would work. But I was thinking about something more
general.

class Root
=A0 def dsl(&block)
=A0 =A0 instance_eval(&block)
=A0 end
end

class C
=A0 #...
end

class D
=A0 #...
end

dsl {
=A0 met1

=A0 class C1 < C
=A0 =A0 #...
=A0 end

=A0 class D1 < D
=A0 =A0 #...
=A0 end

}

Can I run part of the Proc in the context of C and part in the
context of D i.e. can I somehow split the Proc or convert it to a
readable String?

The more I think about it, the more absurd the reasoning behind it
seems. But since I'm at it, I may as well continue the debate :)

You can just allow them to be defined in Root and then use #inherited
to set

C::C1 =3D Root:C1
D::D1 =3D Root:D1
...

(using const_get and const_set)

T.
 
R

Robert Klemme

2008/10/19 Adam Skobi said:
class C
def self.inherited(klass)
klass_name = klass.name[/[^:]+$/]
klass.to_s.split(/::/)[0...-1].inject(Object) { |const, name|
const.const_get name
}.send :remove_const, klass_name
C.const_set klass_name, klass
end
end
NB: Don't do this.

Yes! Why haven't I thought of this solution?!
Thanks for that one.

Are the any non obvious reasons as why not to do this type of trick
(code obfuscation, maintance hell etc.)?

Yes, there's a reason: it does not work:

10:17:43 Temp$ ruby mod.rb
initial
A::X
A::X
after remove
A::X
A::X
after set
A::X
A::X
10:17:51 Temp$ cat mod.rb
module A
class X
end
end
$cl = ::A::X
puts "initial", $cl, $cl.name
A.send :remove_const, 'X'
puts "after remove", $cl, $cl.name
module B
end
B.send :const_set, 'Y', $cl
puts "after set", $cl, $cl.name
10:18:08 Temp$

Here's one way to do it:

class Root
# features all objects of dsl share
class Object
def my_name
# silly example
self.class.name
end
end

class <<self
def class_def(name, parent = Object, &b)
parent = const_get(parent) unless Module === parent
cl = Class.new parent
const_set name, cl
cl.class_eval(&b)
cl
end
end
end

def Object.const_missing(sym)
Root.send :const_get, sym
end

def dsl(&b)
Root.class_eval(&b)
end

dsl {
class_def :Base do
def hello() puts "Hello, I am #{my_name}." end
end

class_def :Derived, Base do
# nothing here
end

Derived.new.hello

p Derived.ancestors
}


Cheers

robert
 

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

Forum statistics

Threads
473,968
Messages
2,570,149
Members
46,695
Latest member
StanleyDri

Latest Threads

Top