Issue #6688 has been updated by trans (Thomas Sawyer).


=begin
Even if this idea of Proxy/Proxy#become isn't deemed suitable for core, is it still possible to do it as a 3rd party extension? I took a stab at based on some similar code by 

    #include <ruby.h>

    VALUE cProxy;

    /* call-seq:
     *   proxy.become(obj2)
     */
    static VALUE proxy_become(VALUE self, VALUE newobj) {
      VALUE name = rb_mod_name(rb_obj_class(newobj));
      char * c_name = StringValueCStr(name);
      struct RObject *robj1 = ROBJECT(self);
      struct RObject *robj2 = ROBJECT(newobj);

      rb_secure(1);

      if(OBJ_FROZEN(self) || OBJ_FROZEN(newobj)) {
        rb_error_frozen("object");
      }

      switch(TYPE(newobj)) {
        case T_NIL:
        case T_FALSE:
        case T_TRUE:
        case T_SYMBOL:
        case T_FIXNUM:
          /* rb_raise(rb_eTypeError, "cannot become %s", c_name); */
          break;
        default:
          *robj1  = *robj2;
      }

      return newobj;
    }

    void Init_proxy(void)
    {
      cProxy = rb_define_class("Proxy", rb_cObject);
      rb_define_method(cProxy, "become", proxy_become, 1);
    }

This ((*almost*)) works. But sometimes I get a core dump --something about (({glibc detected ruby: double free or corruption (fasttop)})).

Is it possible to fix this? Or is this just something outside the realm of possibility?
=end

----------------------------------------
Feature #6688: Object#replace
https://bugs.ruby-lang.org/issues/6688#change-36694

Author: prijutme4ty (Ilya Vorontsov)
Status: Open
Priority: Normal
Assignee: 
Category: core
Target version: Next Major


I suggest that #replace works not only on Enumerables but on any Object. It can make use the same object in different places more consistent. It makes it possible to write
class Egg; end
class Hen; end
class HenHouse; attr_accessor :species; end
class Incubator; def incubate(egg) Hen.new; end

# Here it is!
class IncubatorWithReplace; 
  def incubate(egg) 
    egg.replace(Hen.new)
  end
end

e1,e2,e3 = Egg.new, Egg.new, Egg.new
h1, h2 = HenHouse.new, HenHouse.new

# One egg is shared between hen houses
h1.species = [e1, e2]
h2.species = [e1, e3]
p h1 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]


 # First option. It's bad choise because it makes two "data structures" HenHouse inconsistent: 
 #   they have different object while must have the same
h1[0] = Incubator.new.incubate(h1[0])
p h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]

 # Second option is ok - now both shared objects're changed.
IncubatorWithReplace.new.incubate(h1[0])
h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
h2 # ==> <HenHouse @species = [#<Hen>,#<Egg 3>]  

 # Third option is bad - it wouldn't affect HenHouses at all
e1 = Incubator.new.incubate(e1)
p h1 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]

 # while Fourth option is ok and works as second do
IncubatorWithReplace.new.incubate(e1) ## would affect both HenHouses
p h1 # ==> <HenHouse @species = [#<Hen>,#<Egg 2>]
p h2 # ==> <HenHouse @species = [#<Egg 1>,#<Egg 3>]


I can't imagine how it'd be realized, it looks like some dark magic with ObjectSpace needed to replace one object at a reference with another at the same reference. But I didn't found a solution.

About ret-value. I think it should be two forms:
Object#replace(obj, retain = false)
If retain is false #replace should return a reference to a new object (in fact the same reference as to old object but with other content)
If retain is true, old object should be moved at another place and new reference to it is returned, so:
e1 # ==> <Egg id:1>
e1.replace( Hen.new, true ) # ==> <Egg id:2>
e1 # ==> <Hen id:1>


-- 
http://bugs.ruby-lang.org/