Module: DeepCloneable::DeepClone

Defined in:
lib/deep_cloneable/deep_clone.rb

Instance Method Summary collapse

Instance Method Details

#deep_clone(*args) {|_self, kopy| ... } ⇒ Object

Deep dups an ActiveRecord model. See README.rdoc

Yields:

  • (_self, kopy)

Yield Parameters:



6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
# File 'lib/deep_cloneable/deep_clone.rb', line 6

def deep_clone(*args, &block)
  options = args[0] || {}

  dictionary = options[:dictionary]
  dictionary ||= {} if options[:use_dictionary]

  kopy = if dictionary
           find_in_dictionary_or_dup(dictionary)
         else
           dup
         end

  options[:preprocessor].call(self, kopy) if options.key?(:preprocessor)

  deep_exceptions = {}
  if options[:except]
    exceptions = Array.wrap(options[:except])
    exceptions.each do |attribute|
      dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
    end
    deep_exceptions = exceptions.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
  end

  deep_onlinesses = {}
  if options[:only]
    onlinesses = Array.wrap(options[:only])
    object_attrs = kopy.attributes.keys.collect(&:to_sym)
    exceptions = object_attrs - onlinesses
    exceptions.each do |attribute|
      dup_default_attribute_value_to(kopy, attribute, self) unless attribute.is_a?(Hash)
    end
    deep_onlinesses = onlinesses.select { |e| e.is_a?(Hash) }.inject({}) { |m, h| m.merge(h) }
  end

  kopy.instance_eval { extend ::DeepCloneable::SkipValidations } if options[:validate] == false

  if options[:include]
    normalized_includes_list(options[:include]).each do |association, conditions_or_deep_associations|
      conditions = {}

      if association.is_a? Hash
        conditions_or_deep_associations = association[association.keys.first]
        association = association.keys.first
      end

      case conditions_or_deep_associations
      when Hash
        conditions_or_deep_associations = conditions_or_deep_associations.dup
        conditions[:if]     = conditions_or_deep_associations.delete(:if)     if conditions_or_deep_associations[:if]
        conditions[:unless] = conditions_or_deep_associations.delete(:unless) if conditions_or_deep_associations[:unless]
      when Array
        conditions_or_deep_associations = conditions_or_deep_associations.map { |entry| entry.is_a?(Hash) ? entry.dup : entry }
        conditions_or_deep_associations.each_with_index do |entry, index|
          if entry.is_a?(Hash)
            conditions[:if] = entry.delete(:if) if entry[:if]
            conditions[:unless] = entry.delete(:unless) if entry[:unless]
          end

          conditions_or_deep_associations.delete_at(index) if entry.empty?
        end
      end

      dup_options = {}
      dup_options[:include] = conditions_or_deep_associations if conditions_or_deep_associations.present?
      dup_options[:except] = deep_exceptions[association] if deep_exceptions[association]
      dup_options[:only] = deep_onlinesses[association] if deep_onlinesses[association]
      dup_options[:dictionary] = dictionary if dictionary

      [:skip_missing_associations, :validate, :preprocessor, :postprocessor].each do |option|
        dup_options[option] = options[option] if options.key?(option)
      end

      if (association_reflection = self.class.reflect_on_association(association))
        association_type = association_reflection.macro
        association_type = "#{association_type}_through" if association_reflection.is_a?(ActiveRecord::Reflection::ThroughReflection)

        duped_object = send(
          "dup_#{association_type}_association",
          { :reflection => association_reflection, :association => association, :copy => kopy, :conditions => conditions, :dup_options => dup_options },
          &block
        )

        kopy.send("#{association}=", duped_object)
      elsif !options[:skip_missing_associations]
        raise ::DeepCloneable::AssociationNotFoundException, "#{self.class}##{association}"
      end
    end
  end

  yield(self, kopy) if block
  options[:postprocessor].call(self, kopy) if options.key?(:postprocessor)

  kopy
end