web-dev-qa-db-fra.com

Comment créer une copie complète d'un objet dans Ruby?

J'ai fait quelques recherches pour trouver différentes méthodes et publications sur la création d'un opérateur de copie en profondeur.

Existe-t-il un moyen rapide et facile (intégré) de copier en profondeur des objets dans Ruby? Les champs ne sont pas des tableaux ou des hachages.

Travailler dans Ruby 1.9.2.

50
B Seven

La copie profonde n'est pas intégrée à Vanilla Ruby, mais vous pouvez la pirater en organisant et en désarchivant l'objet:

Marshal.load(Marshal.dump(@object))

Ce n'est pas parfait cependant, et ne fonctionnera pas pour tous les objets. Une méthode plus robuste:

class Object
  def deep_clone
    return @deep_cloning_obj if @deep_cloning
    @deep_cloning_obj = clone
    @deep_cloning_obj.instance_variables.each do |var|
      val = @deep_cloning_obj.instance_variable_get(var)
      begin
        @deep_cloning = true
        val = val.deep_clone
      rescue TypeError
        next
      ensure
        @deep_cloning = false
      end
      @deep_cloning_obj.instance_variable_set(var, val)
    end
    deep_cloning_obj = @deep_cloning_obj
    @deep_cloning_obj = nil
    deep_cloning_obj
  end
end

La source:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/Ruby/ruby-list/43424

65
Alex Peattie

J'ai créé une implémentation native pour effectuer des clones profonds d'objets Ruby.

C'est environ 6 à 7 fois plus rapide que l'approche du maréchal.

https://github.com/balmma/Ruby-deepclone

Notez que ce projet n'est plus maintenu (dernière validation en 2017 , il y a un rapport problèmes )

16
user2256822

Rails a une méthode récursive nommée deep_dup qui renverra une copie complète d'un objet et, contrairement à dup et clone, fonctionne même sur des objets composites (tableau/hachage de tableaux/hachages). C'est aussi simple que:

def deep_dup
  map { |it| it.deep_dup }
end
7
Claudio Floreani

Il existe une implémentation native pour effectuer des clones profonds d'objets Ruby: Ruby_deep_clone

Installez-le avec gem:

gem install Ruby_deep_clone

Exemple d'utilisation:

require "deep_clone"
object = SomeComplexClass.new()
cloned_object = DeepClone.clone(object)

C'est environ 6 à 7 fois plus rapide que l'approche du maréchal et l'événement fonctionne avec des objets gelés.

Notez que ce projet n'est plus maintenu (dernière validation en 2017 , il y a un rapport problèmes )

6
Mat

Vous pouvez utiliser un bijou en double pour cela.

C'est un petit Ruby gem qui peut dupliquer récursivement un objet. Il dupliquera aussi ses références d'objet à la nouvelle duplication.

require 'duplicate'
duplicate('target object')

https://rubygems.org/gems/duplicate

https://github.com/adamluzsi/duplicate.rb

3
Adam Luzsi

Je vous suggère d'utiliser la gemme ActiveSupport qui ajoute beaucoup de sucre à votre noyau Ruby core, pas seulement une méthode clone profond ).

Vous pouvez consulter la documentation pour plus d'informations sur les méthodes qui ont été ajoutées.

2
Noman Ur Rehman

Le clone profond automatique n'est pas toujours ce que vous voulez. Souvent, vous devez définir quelques attributs sélectionnés à cloner en profondeur. Une manière flexible de le faire consiste à implémenter le initialize_copy, initialize_dup et initialize_clone méthodes.

Si vous avez un cours:

class Foo
  attr_accessor :a, :b
end

et vous ne voulez que cloner en profondeur :b, vous remplacez le initialize_* méthode:

class Foo
  attr_accessor :a, :b

  def initialize_dup(source)
    @b = @b.dup
    super
  end
end

Bien sûr, si vous voulez @b pour cloner en profondeur certains de ses propres attributs, vous faites de même dans la classe b.

Rails fait cela (voir https://github.com/Rails/rails/blob/0951306ca5edbaec10edf3440d5ba11062a4f2e5/activemodel/lib/active_model/errors.rb#L78 )

Pour une explication plus complète, je l'ai appris ici de ce post https://aaronlasseigne.com/2014/07/20/know-Ruby-clone-and-dup/

2
lulalala

Consultez également deep_dive. Cela vous permet de faire des copies approfondies contrôlées de vos graphiques d'objets.

https://rubygems.org/gems/deep_dive

1
flajann

Vous n'avez vraiment pas besoin d'un Gem pour cela. Cela ne pourrait pas être beaucoup plus simple que cela, ce qui ne vaut pas les frais généraux d'un Gem!

def deep_clone(obj)
  obj.clone.tap do |new_obj|
    new_obj.each do |key, val|
      new_obj[key] = deep_clone(val) if val.is_a?(Hash)
    end
  end
end
0
Matthew O'Riordan