WTF? [resolvido, mas ainda assim...WTF!?]

Tou tentando criar umas classes programaticamente pra embonitar uma DSL que eu quero fazer:

[code]module Products
end

def product(name)
Products.const_set name, Class.new do |c|
def to_s
"I am a #{name}"
end
end
end

product :Banana

puts Products::Banana.to_s, Products::Banana.new.to_s
[/code]

Isso imprime:

Products::Banana #<Products::Banana:0x2815f4>

Quando na verdade eu esperava:

Products::Banana I am a Banana

Onde eu vacilei?

Exatamente do jeito que você quer não vai aparecer não, tem que ter o Products:: na frente. Esse chega perto:

[code=ruby]module Products
end

def product(name)
Products.const_set name, Class.new
end

product :Banana

module DynamicModule
def to_s
"I am a #{self.class.name}"
end
end

Products::Banana.send( :include, DynamicModule )

puts Products::Banana.to_s, Products::Banana.new.to_s[/code]

Saída:

Products::Banana I am a Products::Banana

Se você cortar os :: vai chegar no que você quer.

O Jay Fields tem mais a falar sobre definir métodos em instâncias -> http://blog.jayfields.com/2008/02/ruby-dynamically-define-method.html

Outra opção:

[code=ruby]class String

def constantize
unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
raise NameError, "#{camel_cased_word.inspect} is not a valid constant name!"
end

Object.module_eval("::#{self}", __FILE__, __LINE__)

end

end

module Products
end

def product(name)
eval( “class #{name} ; end” )
new_class = name.to_s.constantize
Products.const_set name, new_class
end

product :Banana

module DynamicModule
def to_s
"I am a #{self.class.name}"
end
end

Products::Banana.send( :include, DynamicModule )

puts Products::Banana.to_s, Products::Banana.new.to_s[/code]

Imprime:

Banana I am a Banana

Realmente ou é um ou é outro, não tem como ser os dois :stuck_out_tongue:

Valeu, Mauricio!

Achei outro jeito, tambem:

[code]def product(name, pricing_proc)
c = Class.new do
attr_reader :quantity

def initialize(quantity = 1)
  @quantity = quantity
end

end

c.class_eval do
define_method :price, pricing_proc
define_method :to_s do
"I am a #{name}"
end
end

Products.const_set name, c
end

include Products

product :Banana

puts Banana.to_s, Banana.new.to_s[/code]

Ainda sobre o WTF, o problema não é que naquela primeira definição o método está sendo definido na instância de Class não?

Nao… na verdade, a versao inicial tem dois problemas:

  1. o bloco tava sendo passado pro const_set ao inves do Class.new (o que eh um problema bem sutil, foi foda diagnosticar)

  2. codigo dentro de def, class e module nao pode usar variaveis que estao fora dele, caso a coisa esteja acontecendo num bloco. O Ola me explicou, e eu juro que entendi na hora, mas nao consigo explicar direito (e tambem nao sei nem se eu entendi tao bem assim). Se alguem souber de um be-a-bá sobre isso, ia ser bem-vindo :slight_smile:

Cara, quanto ao item “1)” do que você falou, juro que tinha pensado nisso assim que li a thread mas como tava sem tempo não fuxiquei pra ter certeza. Realmente é ruim de diagnosticar pois a tendência é achar que está sendo passado para o Class.new mesmo.

Quanto ao item “2)” eu não tenho a mínima ideia :smiley: