What about Object#to_ruby?

Hi!

It’s not the first time that I need a to_ruby method which would
guarantee
the following invariant:

Kernel.eval(foo.to_ruby) == foo

It actually works when using inspect with most basic objects (Integer,
String,
True & FalseClass, etc.), without actually being the specification of
inspect.

I admit that such a feature probably only makes sense for classes that
actually
capture datatypes, whose instances are then true values.

Does anyone know a gem that provides such a feature? To your knowledge,
would to_ruby be a conflicting name with existing gems in the ecosystem
or future plans for ruby itself?

Just in case you ask yourself whether this feature is needed, have a
look
at rubygems itself:

Thanks for any suggestion,
Bernard

On 06/20/2011 05:40 AM, Bernard L. wrote:

Hi!

It’s not the first time that I need a to_ruby method which would guarantee
the following invariant:

Kernel.eval(foo.to_ruby) == foo

There was a project called amarshal, in the pre-gem days:

http://www.a-k-r.org/amarshal

I believe pry has this kind of thing: GitHub - banister/pry: MOVED TO http://github.com/pry/pry

From its readme:

pry(Pry):1> show-method rep -l

From: /home/john/ruby/projects/pry/lib/pry/pry_instance.rb @ line 143:
Number of lines: 6

143: def rep(target=TOPLEVEL_BINDING)
144: target = Pry.binding_for(target)
145: result = re(target)
146:
147: show_result(result) if should_print?
148: end

Thanks a lot for your responses. I suspect that my question was not that
clear, though.

I’m not really looking or Marshal or YAML or JSON dump format, but for a
method
to print a ruby literal (I should have called it to_ruby_literal) from
a
ruby value,
where it makes sense. The goal is to ease human-readable ruby code
generation,
I must add. Nowadays, using inspect works for all but a few basic
values:

blambeau@kali:~$ irb -r time to_ruby_literal.rb
ruby-1.8.7-p334 :001 > eval(true.inspect)
=> true
ruby-1.8.7-p334 :002 > eval(1.inspect)
=> 1
ruby-1.8.7-p334 :003 > eval(1.0.inspect)
=> 1.0
ruby-1.8.7-p334 :004 > eval(nil.inspect)
=> nil
ruby-1.8.7-p334 :005 > eval(“O’Neil”.inspect)
=> “O’Neil”
ruby-1.8.7-p334 :006 > eval(‘O " Dude’.inspect)
=> “O " Dude”
ruby-1.8.7-p334 :007 > eval([1, 2, 3].inspect)
=> [1, 2, 3]
ruby-1.8.7-p334 :008 > eval({:hello => “world”}.inspect)
=> {:hello=>“world”}
ruby-1.8.7-p334 :009 > eval(/[a-z]/.inspect)
=> /[a-z]
/
ruby-1.8.7-p334 :010 > eval((1.0/0).inspect)
NameError: (eval):1:in irb_binding': uninitialized constant Infinity from to_ruby_literal.rb:10 from to_ruby_literal.rb:10 ruby-1.8.7-p334 :011 > eval(Time.now.inspect) SyntaxError: (eval):1:inirb_binding’: compile error
(eval):1: syntax error, unexpected tINTEGER, expecting $end
Mon Jun 20 20:01:19 +0200 2011
^
from to_ruby_literal.rb:11
from to_ruby_literal.rb:11

Bernard

On Mon, Jun 20, 2011 at 7:40 AM, Bernard L. [email protected]
wrote:

inspect.
at rubygems itself:
Blog: http://revision-zero.org/
Code: blambeau (Bernard Lambeau) · GitHub
Follow: http://twitter.com/blambeau/

I think the following works for everything except data that contains

code
itself (closures / methods)

Datum = Struct.new :some_attribute

data = [
Datum.new(“string”),
Datum.new(1234),
Datum.new(/regex/),
]

serial = Marshal.dump data
serial # =>
“\x04\b[\bS:\nDatum\x06:\x13some_attributeI"\vstring\x06:\x06ETS;\x00\x06;\x06i\x02\xD2\x04S;\x00\x06;\x06I/\nregex\x00\x06;\aF”
Marshal.load(serial) == data # => true

require ‘yaml’
serial = YAML.dump data
serial # => “— \n- !ruby/struct:Datum \n
some_attribute: string\n- !ruby/struct:Datum \n some_attribute:
1234\n-
!ruby/struct:Datum \n some_attribute: !ruby/regexp /regex/\n”
YAML.load(serial) == data # => true


I think the closest things that exist right now that would be able to do
this for closures as well, would be some of Ryan D.’ tools (last I
heard,
they don’t work on 1.9, though):
https://rubygems.org/gems/ruby2ruby
https://rubygems.org/gems/ruby_parser
https://rubygems.org/gems/sexp_processor

Rubinius might also be able to do something like this.

On Mon, Jun 20, 2011 at 12:20 PM, Steve K.
[email protected]wrote:

144: target = Pry.binding_for(target)
145: result = re(target)
146:
147: show_result(result) if should_print?
148: end

I’m admittedly uninformed, but I think they do this by keeping track of
where methods get defined, then opening that file and reading that
definition (as opposed to introspecting on the object itself) in the
example
above, for instance, they also report the file and line numbers of the
rep
method.

On Jun 20, 2011, at 2:17 PM, Michael E. wrote:

str << “obj.instance_variable_set(#{ivar},
#{instance_variable_get(ivar).to_ruby_literal})\n”

Correction: this doesn’t quote the ivar name, it should be:

str << “obj.instance_variable_set("#{ivar}",
#{instance_variable_get(ivar).to_ruby_literal})\n”

Which would output, in the previous example:

A.allocate.tap do |obj|
obj.instance_variable_set(“@x”, 1.3)
obj.instance_variable_set(“@y”, “hello”)
obj.instance_variable_set(“@z”, false)
end

Michael E.
[email protected]
http://carboni.ca/

On Jun 20, 2011, at 2:02 PM, Bernard L. wrote:

Thanks a lot for your responses. I suspect that my question was not that
clear, though.

I’m not really looking or Marshal or YAML or JSON dump format, but for a
method
to print a ruby literal (I should have called it to_ruby_literal) from a
ruby value,
where it makes sense. The goal is to ease human-readable ruby code
generation,
I must add. Nowadays, using inspect works for all but a few basic values:

It sounds like you’re essentially talking about a marshal format that
is
Ruby code.

Something along the lines of this, without procs, singleton classes, no
cyclic references (ruby 1.9), etc:

class Object
def to_ruby_literal
str = “#{self.class.name}.allocate.tap do |obj|\n”
instance_variables.each do |ivar|
str << “obj.instance_variable_set(#{ivar},
#{instance_variable_get(ivar).to_ruby_literal})\n”
end
str << “end”
end
end

[TrueClass, FalseClass, NilClass, String, Integer, Float].each do
|klass|
klass.class_eval do
alias_method :to_ruby_literal, :inspect
end
end

Usage:

class A
def initialize(x, y, z)
@x, @y, @z = x, y, z
end
end
puts A.new(1.3, “hello”, false).to_ruby_literal

A.allocate.tap do |obj|
obj.instance_variable_set(@x, 1.3)
obj.instance_variable_set(@y, “hello”)
obj.instance_variable_set(@z, false)
end

Michael E.
[email protected]
http://carboni.ca/

Michael E. wrote in post #1006439:

On Jun 20, 2011, at 2:17 PM, Michael E. wrote:

str << “obj.instance_variable_set(#{ivar},
#{instance_variable_get(ivar).to_ruby_literal})\n”

Correction: this doesn’t quote the ivar name, it should be:

str << “obj.instance_variable_set(”#{ivar}",
#{instance_variable_get(ivar).to_ruby_literal})\n"

Except that in ruby 1.9, instance_variables returns an array of symbols
rather than strings. So to be portable to both:

str << "obj.instance_variable_set(#{ivar.to_sym}, …

Regards,

Brian.