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.metadata
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
|