Module: Mongoid::EmbeddedCopy

Extended by:
ActiveSupport::Concern
Defined in:
lib/mongoid/embedded_copy.rb,
lib/mongoid/embedded_copy/version.rb

Constant Summary collapse

VERSION =
'1.0.2'

Class Method Summary collapse

Class Method Details

.embedded_class(klass, opts, prefix = nil) ⇒ Object



22
23
24
25
# File 'lib/mongoid/embedded_copy.rb', line 22

def self.embedded_class(klass, opts, prefix = nil)
  embedded_class = opts[:embedded_class] || "CopyFor#{klass}".gsub('::', '')
  [prefix, embedded_class].compact.join('::')
end

.equality_module_for(klass) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/mongoid/embedded_copy.rb', line 113

def self.equality_module_for(klass)
  Module.new do
    extend ActiveSupport::Concern

    included do
      class_attribute :acts_as_method, instance_writer: false
      self.acts_as_method = "acts_as_#{klass.to_s.underscore}"

      define_method(acts_as_method) { true }
    end

    def ==(rhs)
      if rhs.respond_to?(acts_as_method) && rhs.public_send(acts_as_method)
        id == rhs.id
      else
        super
      end
    end
    alias_method :eql?, :==
  end
end

.for(klass, opts = {}) ⇒ Object

Raises:

  • (ArgumentError)


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
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/mongoid/embedded_copy.rb', line 27

def self.for(klass, opts = {})
  klass = klass.constantize if klass.is_a?(String)
  raise ArgumentError.new('Requires :in options to specify the document in which this one is embedded') if !opts.has_key?(:in)
  raise ArgumentError.new("Can't create an embedded copy of non-mongoid document class #{klass}") if !(klass < Mongoid::Document)
  skipped = Array(opts[:skip]).map{|f| f.to_s}
  skipped.push('deleted_at')

  embed_opts = opts[:in]
  embed_opts = {class_name: embed_opts.to_s, as: embed_opts.to_s.underscore} if embed_opts.is_a?(Class)
  embed_opts[:inverse_of] = opts[:inverse_of]
  embedded_class = self.embedded_class(embed_opts[:class_name], opts)
  embed_name = embed_opts.delete(:as)
  skipped.push(embed_name).push("#{embed_name}_id")

  return if klass.const_defined?(embedded_class) && [klass, embedded_class].join('::').constantize.respond_to?(:original_class)
  klass.send(:include, equality_module_for(klass))

  document_module = Module.new do
    extend ActiveSupport::Concern

    included do
      include Mongoid::Document
      include Mongoid::EmbeddedCopy.equality_module_for(klass)

      class_attribute :original_class, instance_writer: false
      class_attribute :skipped_attributes, instance_writer: false
      class_attribute :embed_name

      self.original_class = klass
      self.embed_name = embed_name
      self.skipped_attributes = skipped

      def initialize(*attrs)
        if attrs.first.is_a?(original_class)
          attrs = attrs.first.attributes.to_h.dup
          skipped_attributes.each {|n| attrs.delete(n) }
        end
        super(attrs)
      end

      def update_from_original
        update_from(load_original)
      end

      def update_from(document)
        attrs = document.attributes.to_h.dup
        skipped_attributes.each {|n| attrs.delete(n) }
        update_attributes(attrs)
      end

      embedded_in embed_name, embed_opts

      klass.fields.each do |name, f|
        next if skipped.include?(name) || name == '_id' || f.
        options = f.options.dup
        options.delete(:klass)
        field name, options

        if opts[:update_original]
          define_method("#{name}_with_update_original=") do |value|
            load_original.set(name => value)
            public_send("#{name}_without_update_original=", value)
          end
          alias_method_chain "#{name}=", :update_original
        end
      end

      klass.relations.each do |name, rel|
        next if skipped.include?(name)
        options = rel.relation.valid_options +
          Mongoid::Relations::Options::COMMON
        public_send(rel.macro, name, rel.to_h.slice(options))
      end
    end

    def load_original
      @original ||= original_class.find(id)
    end
  end

  if !klass.const_defined?(embedded_class)
    klass.const_set(embedded_class, Class.new)
  end
  klass.const_get(embedded_class).send(:include, document_module)
end