instance eval and expression substitution

4 views
Skip to first unread message

Sven Schott

unread,
Nov 15, 2009, 7:44:44 PM11/15/09
to rails-...@googlegroups.com
Hello all

I'm defining a new method using instance_eval and it's working ok except for the fact that I cannot write an expression substitution into the method because it's already happening at the instance_eval string parameter level. So, I'm writing the following method:

def #{method_name + '_xml'}
    '<#{method_name} #{@#{method_name}.to_xml_properties} />\n'
end

passed as a string to instance eval. The problem I have is that I need to evaluate the method name as a string which is then defined as the name of the param which is substituted in the method. But you can't do this (at least not like this) since you can't do a double substitution (even Textmate tells me to fly a kite). So how does everyone else pass a variable name to instance_eval that will then be used as a substitution in a string?

In case you're wondering why the crazy man is doing this, it's part of a larger method_missing implementation that looks like:

class Hash
  def to_xml_properties
    self.collect { |k,v| %{#{k.to_s}='#{v.to_s}'} }.join(" ")
  end
end

class Chart
def method_missing(method, *args, &block)
     method_name = method.to_s
     instance_eval %{@#{method_name} = #{args[0].inspect} 
                   def #{method_name}= (properties)\n@#{method_name} = properties\nend
                   def #{method_name}\n@#{method_name}\nend
                   def #{method_name + '_xml'}\n'<#{method_name} \#{@#{method_name}.to_xml_properties} />\n'\nend }
end
end

Julio Cesar Ody

unread,
Nov 15, 2009, 7:50:50 PM11/15/09
to rails-...@googlegroups.com
Quick skim, so pardon if it doesn't address exactly what you need.

About parsing XML/JSON into objects/hashes, check

http://github.com/jnunemaker/crack/

Parameters passing to instance_eval, check

http://blog.jayfields.com/2006/09/ruby-instanceexec-aka-instanceeval.html

Sven Schott

unread,
Nov 15, 2009, 10:10:43 PM11/15/09
to rails-...@googlegroups.com
I'm an idiot. Please ignore. I was using single quotes. My bad.

Max Muermann

unread,
Nov 15, 2009, 10:15:14 PM11/15/09
to rails-...@googlegroups.com
Have a look here as well - seems to be similar to what you are trying to do:

http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def

--max

Daniel Bush

unread,
Nov 15, 2009, 10:41:07 PM11/15/09
to rails-...@googlegroups.com


2009/11/16 Max Muermann <max.mu...@gmail.com>


Have a look here as well - seems to be similar to what you are trying to do:

http://stackoverflow.com/questions/185947/ruby-definemethod-vs-def

--max


Was going to suggest something similar.  I haven't tried to understand Sven's code but I thought the following things might help:

a) %(foo%s} % 'bar'  (=> 'foobar')
b) Module#define_method: define_method :meth { ... }
c) instance_variable_get/set

For instance (and without any recommendation(!!)):

class Hash
  def to_xml_properties
    self

  end
end
class Chart
  def method_missing(method,*args,&block)
    STDERR.puts 'method missing!'
    method_name=method.to_s
    instance_variable_set '@'+method_name , {method_name => 1}
    a=self.class.send(:define_method, method_name) do
      %{<#{method_name} %s />} % instance_variable_get('@'+method_name).to_xml_properties
    end
    a.call
  end
end

c=Chart.new
p c.baz_xml
p c.baz_xml
p c.foobar
p c.foobar

Regards,
Daniel Bush

Sven Schott

unread,
Nov 15, 2009, 11:30:22 PM11/15/09
to rails-...@googlegroups.com
I like it. But would there be any way to generate the setters and getters in the same way (as in, without instance_eval or any major tomfoolery)?

Just out of interest really because it's all working nicely now and looks much neater with everyone's suggestions.

Daniel Bush

unread,
Nov 15, 2009, 11:48:49 PM11/15/09
to rails-...@googlegroups.com

2009/11/16 Sven Schott <sven....@gmail.com>

I like it. But would there be any way to generate the setters and getters in the same way (as in, without instance_eval or any major tomfoolery)?


If you mean attr_writer and reader then extending the previous example I guess you could do something like this:


class Hash
  def to_xml_properties
    self
  end
end
class Chart
  def method_missing(method,*args,&block)
    STDERR.puts 'method missing!'
    method_name=method.to_s
    instance_variable_set '@'+method_name , {method_name => 1}
    self.class.send(:attr_reader, method_name)
    self.class.send(:attr_writer, method_name)
    a=self.class.send(:define_method, method_name+'_xml') do

      %{<#{method_name} %s />} % instance_variable_get('@'+method_name).to_xml_properties
    end
    self.send(method_name)
  end
end

c=Chart.new
p c.baz
p c.baz_xml
p c.baz
p c.baz_xml

 
Cheers,
--
Daniel Bush


Daniel Bush

unread,
Nov 16, 2009, 12:53:14 AM11/16/09
to rails-...@googlegroups.com
Actually, this might be a place where you want to use a virtual class (or eigen class or whatever the name du jour for it is).
So basically, you create an instance of Chart and give it instance methods that only it and no other instance has by modifying its virtual class rather than the Chart class.
One way to get at it might be something like below using 'vclass' and extending the previous example.


class Hash
  def to_xml_properties
    self
  end
end

class Chart
  def method_missing(method,*args,&block)
    STDERR.puts 'method missing!'
    method_name=method.to_s
    instance_variable_set '@'+method_name , {method_name => 1}
    self.class.send(:attr_reader, method_name)
    self.class.send(:attr_writer, method_name)
    vclass = class << self; self; end
    vclass.send(:define_method,'special_'+method_name) do
      "special! "+method_name
    end
    a=self.class.send(:define_method, method_name+'_xml') do
      %{<#{method_name} %s />} % instance_variable_get('@'+method_name).to_xml_properties
    end
    self.send(method_name)
  end
end

c=Chart.new
p c.baz
p c.baz_xml
p c.baz
p c.baz_xml
p c.foo
p c.foo_xml
p c.special_baz
p c.special_foo
.
Regards,
Daniel Bush
--
Daniel Bush


Reply all
Reply to author
Forward
0 new messages