Forum: Ruby Proc metaprogramming tricks?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Adam S. (Guest)
on 2008-10-19 21:01
(Received via mailing list)
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?
Thomas S. (Guest)
on 2008-10-20 00:51
(Received via mailing list)
On Oct 19, 1:00 pm, Adam S. <removed_email_address@domain.invalid> wrote:
> Hi,
>
> I'm building a DSL and trying some weird syntax structures. My DSL
> should look like this.

    def dsl(&block)
      C.module_eval(&block)
    end

T.
Adam S. (Guest)
on 2008-10-20 01:24
(Received via mailing list)
>> I'm building a DSL and trying some weird syntax structures. My DSL
>> should look like this.

>     def dsl(&block)
>       C.module_eval(&block)
>     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 :-)
Mikael Høilund (Guest)
on 2008-10-20 01:33
(Received via mailing list)
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.
Adam S. (Guest)
on 2008-10-20 01:42
(Received via mailing list)
> 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.)?
Mikael Høilund (Guest)
on 2008-10-20 01:50
(Received via mailing list)
On Oct 19, 2008, at 23:40, Adam S. wrote:

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

> --
> Adam S.
>
>

--
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[i]-email[i]+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
Thomas S. (Guest)
on 2008-10-20 06:42
(Received via mailing list)
On Oct 19, 5:18 pm, Adam S. <removed_email_address@domain.invalid> wrote:
>   def dsl(&block)
> end
>   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 = Root:C1
  D::D1 = Root:D1
  ...

(using const_get and const_set)

T.
Robert K. (Guest)
on 2008-10-20 12:34
(Received via mailing list)
2008/10/19 Adam S. <removed_email_address@domain.invalid>:
>
>> 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
This topic is locked and can not be replied to.