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

···

--
PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
Mail: blambeau@gmail.com
Mobile: +32 477 24 58 61
Blog: http://revision-zero.org/
Code: http://github.com/blambeau/
Follow: http://twitter.com/blambeau/

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

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

···

On 06/20/2011 05:40 AM, Bernard Lambeau 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

I believe pry has this kind of thing: https://github.com/banister/pry

From its readme:

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

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

···

From: /home/john/ruby/projects/pry/lib/pry/pry_instance.rb @ line 143:

# 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

···

On Mon, Jun 20, 2011 at 7:40 AM, Bernard Lambeau <blambeau@gmail.com> 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

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:

rubygems/lib/rubygems/specification.rb at master · rubygems/rubygems · GitHub

Thanks for any suggestion,
Bernard

--
PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
Mail: blambeau@gmail.com
Mobile: +32 477 24 58 61
Blog: http://revision-zero.org/
Code: blambeau (Bernard Lambeau) · GitHub
Follow: http://twitter.com/blambeau/

---

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 Davis' 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 Klabnik <steve@steveklabnik.com>wrote:

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

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.

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:in `irb_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:37 PM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Mon, Jun 20, 2011 at 7:40 AM, Bernard Lambeau <blambeau@gmail.com> > 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
>
> 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:
>
>
rubygems/lib/rubygems/specification.rb at master · rubygems/rubygems · GitHub
>
> Thanks for any suggestion,
> Bernard
>
> --
> PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
> Mail: blambeau@gmail.com
> Mobile: +32 477 24 58 61
> 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 Davis' tools (last I
heard,
they don't work on 1.9, though):
https://rubygems.org/gems/ruby2ruby
ruby_parser | RubyGems.org | your community gem host
sexp_processor | RubyGems.org | your community gem host

Rubinius might also be able to do something like this.

On Mon, Jun 20, 2011 at 12:20 PM, Steve Klabnik <steve@steveklabnik.com > >wrote:

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

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.

--
PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
Mail: blambeau@gmail.com
Mobile: +32 477 24 58 61
Blog: http://revision-zero.org/
Code: blambeau (Bernard Lambeau) · GitHub
Follow: http://twitter.com/blambeau/

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 Edgar
adgar@carboni.ca
http://carboni.ca/

···

On Jun 20, 2011, at 2:02 PM, Bernard Lambeau 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:

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 Edgar
adgar@carboni.ca
http://carboni.ca/

···

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

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