Feature #5582

Allow clone of singleton methods on a BasicObject

Added by Simon Chiang over 2 years ago. Updated over 1 year ago.

[ruby-core:40805]
Status:Assigned
Priority:Normal
Assignee:Yukihiro Matsumoto
Category:-
Target version:next minor

Description

Currently I do not know of a way to implement something like 'clone' on a BasicObject subclass. This is as close as I've gotten but as you can see the singleton methods are not propagated to the clone.

require 'test/unit'

class Context < BasicObject
  def _singleton_class_
    class << self
      SINGLETON_CLASS = self
      def _singleton_class_
        SINGLETON_CLASS
      end
    end
    _singleton_class_
  end

  def _class_
    _singleton_class_.superclass
  end

  def _extend_(mod)
    mod.__send__(:extend_object, self)
  end

  def _initialize_clone_(orig)
    # set variables as needed
  end

  def _clone_
    clone = _class_.allocate
    clone._initialize_clone_(self)
    _singleton_class_.included_modules.each {|mod| clone._extend_ mod }
    clone
  end
end

class ContextTest < Test::Unit::TestCase
  module A
    def a
      :a
    end
  end

  def test__clone__inherits_modules
    context = Context.new
    context._extend_ A
    clone = context._clone_
    assert_equal :a, clone.a
  end

  def test__clone__inherits_singleton_methods
    context = Context.new

    def context.a
      :a
    end

    clone = context._clone_
    assert_equal :a, clone.a  # fails
  end
end

Is there a way to do this that I don't see? If not, then I request that a way be added - perhaps by allowing the singleton_class to be set somehow.

In my case I am using Context as the context for a dsl where methods write to a target (an instance variable). I want to be able to clone a context such that I can have multiple contexts with the same methods, including extensions and singletons, that write to different targets.

Thank you.

History

#1 Updated by George Koehler over 2 years ago

=begin
My first attempt:

module Clone
include Kernel
(instancemethods - [:clone, :initializeclone]).each {|m| undef_method m}
end

b = BasicObject.new
class << b
include ::Clone
def single; "Quack!"; end
end

c = b.clone
puts c.single

Output:

scratch.rb:3: warning: undefining `object_id' may cause serious problems
Quack!

Clone inherits from Kernel, but undefines all its instance methods except Clone#clone and Clone#initialize_clone. This technique has some awful side effects: Kernel === b and Kernel === c become true. Clone might inherit metamethods from Kernel (because I only undefined instance methods, not metamethods).
=end

#2 Updated by Yusuke Endoh about 2 years ago

  • Status changed from Open to Assigned
  • Assignee set to Yukihiro Matsumoto

#3 Updated by Yusuke Endoh over 1 year ago

  • Target version changed from 1.9.2 to next minor

#4 Updated by Nobuyoshi Nakada over 1 year ago

=begin
2.0 allows `method transplanting'.

module Clone
%i[clone initializecopy initializedup initializeclone].each do |m|
define
method(m, Kernel.instance_method(m))
end
end
=end

Also available in: Atom PDF