What about Object#to_ruby ?

Discussion in 'Ruby' started by Bernard Lambeau, Jun 20, 2011.

  1. [Note: parts of this message were removed to make it a legal post.]

    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:
    https://github.com/rubygems/rubygems/blob/master/lib/rubygems/specification.rb#L1899-1915

    Thanks for any suggestion,
    Bernard

    --
    PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
    Mail:
    Mobile: +32 477 24 58 61
    Blog: http://revision-zero.org/
    Code: http://github.com/blambeau/
    Follow: http://twitter.com/blambeau/
     
    Bernard Lambeau, Jun 20, 2011
    #1
    1. Advertising

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


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

    http://www.a-k-r.org/amarshal
     
    Joel VanderWerf, Jun 20, 2011
    #2
    1. Advertising

  3. [Note: parts of this message were removed to make it a legal post.]

    I believe pry has this kind of thing: https://github.com/banister/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
     
    Steve Klabnik, Jun 20, 2011
    #3
  4. Bernard Lambeau

    Josh Cheek Guest

    [Note: parts of this message were removed to make it a legal post.]

    On Mon, Jun 20, 2011 at 7: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
    >
    > 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:
    >
    > https://github.com/rubygems/rubygems/blob/master/lib/rubygems/specification.rb#L1899-1915
    >
    > Thanks for any suggestion,
    > Bernard
    >
    > --
    > PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
    > Mail:
    > Mobile: +32 477 24 58 61
    > Blog: http://revision-zero.org/
    > Code: http://github.com/blambeau/
    > 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
    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 <>wrote:

    > I believe pry has this kind of thing: https://github.com/banister/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.
     
    Josh Cheek, Jun 20, 2011
    #4
  5. [Note: parts of this message were removed to make it a legal post.]

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

    > On Mon, Jun 20, 2011 at 7: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
    > >
    > > 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:
    > >
    > >

    > https://github.com/rubygems/rubygems/blob/master/lib/rubygems/specification.rb#L1899-1915
    > >
    > > Thanks for any suggestion,
    > > Bernard
    > >
    > > --
    > > PhD Student, Computer Science Department, EPL/INGI, UCLouvain, Belgium
    > > Mail:
    > > Mobile: +32 477 24 58 61
    > > Blog: http://revision-zero.org/
    > > Code: http://github.com/blambeau/
    > > 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
    > 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 <
    > >wrote:

    >
    > > I believe pry has this kind of thing: https://github.com/banister/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:
    Mobile: +32 477 24 58 61
    Blog: http://revision-zero.org/
    Code: http://github.com/blambeau/
    Follow: http://twitter.com/blambeau/
     
    Bernard Lambeau, Jun 20, 2011
    #5
  6. 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.
    >=20
    > 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 =3D "#{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 =3D 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

    http://carboni.ca/=
     
    Michael Edgar, Jun 20, 2011
    #6
  7. 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"

    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

    http://carboni.ca/=
     
    Michael Edgar, Jun 20, 2011
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Parthiv Joshi
    Replies:
    2
    Views:
    702
    Kalyan
    Jul 2, 2004
  2. Suresh Kojhani
    Replies:
    1
    Views:
    2,398
    Anushi
    Jul 29, 2004
  3. Chris Fink
    Replies:
    2
    Views:
    4,111
    David Waz...
    Jul 3, 2003
  4. jon wayne
    Replies:
    9
    Views:
    722
    Jim Langston
    Sep 22, 2005
  5. Ben Giddings

    Object#to_ruby and Object#to_c

    Ben Giddings, Feb 8, 2005, in forum: Ruby
    Replies:
    2
    Views:
    90
    Ilmari Heikkinen
    Feb 13, 2005
Loading...

Share This Page