mongo_mapper 0.13.0 → 0.15.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (137) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +61 -0
  4. data/examples/keys.rb +1 -1
  5. data/examples/modifiers/set.rb +1 -1
  6. data/examples/querying.rb +1 -1
  7. data/examples/safe.rb +2 -2
  8. data/examples/scopes.rb +1 -1
  9. data/lib/mongo_mapper.rb +7 -0
  10. data/lib/mongo_mapper/connection.rb +16 -37
  11. data/lib/mongo_mapper/document.rb +4 -0
  12. data/lib/mongo_mapper/extensions/array.rb +14 -6
  13. data/lib/mongo_mapper/extensions/hash.rb +15 -3
  14. data/lib/mongo_mapper/extensions/object.rb +4 -0
  15. data/lib/mongo_mapper/extensions/object_id.rb +5 -1
  16. data/lib/mongo_mapper/extensions/string.rb +13 -5
  17. data/lib/mongo_mapper/extensions/symbol.rb +18 -0
  18. data/lib/mongo_mapper/plugins/accessible.rb +15 -5
  19. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  20. data/lib/mongo_mapper/plugins/associations/base.rb +27 -14
  21. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +10 -1
  22. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
  23. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +12 -11
  24. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -4
  25. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +60 -29
  26. data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
  27. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
  28. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  29. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  30. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  31. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  32. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  33. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
  34. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  35. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +9 -9
  36. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  37. data/lib/mongo_mapper/plugins/associations/proxy.rb +36 -29
  38. data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
  39. data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
  40. data/lib/mongo_mapper/plugins/counter_cache.rb +97 -0
  41. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  42. data/lib/mongo_mapper/plugins/document.rb +1 -1
  43. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  44. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  45. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  46. data/lib/mongo_mapper/plugins/embedded_document.rb +1 -1
  47. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  48. data/lib/mongo_mapper/plugins/indexes.rb +14 -7
  49. data/lib/mongo_mapper/plugins/keys.rb +170 -151
  50. data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
  51. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  52. data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
  53. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  54. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  55. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  56. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  57. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
  58. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  59. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  60. data/lib/mongo_mapper/plugins/sci.rb +0 -0
  61. data/lib/mongo_mapper/plugins/scopes.rb +78 -7
  62. data/lib/mongo_mapper/plugins/stats.rb +17 -0
  63. data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
  64. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  65. data/lib/mongo_mapper/plugins/validations.rb +1 -1
  66. data/lib/mongo_mapper/railtie.rb +4 -3
  67. data/lib/mongo_mapper/utils.rb +2 -2
  68. data/lib/mongo_mapper/version.rb +1 -1
  69. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  70. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  71. data/spec/examples.txt +1717 -0
  72. data/spec/functional/accessible_spec.rb +19 -13
  73. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  74. data/spec/functional/associations/belongs_to_proxy_spec.rb +36 -20
  75. data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
  76. data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
  77. data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
  78. data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
  79. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
  80. data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
  81. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
  82. data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
  83. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
  84. data/spec/functional/associations/one_embedded_proxy_spec.rb +3 -3
  85. data/spec/functional/associations/one_proxy_spec.rb +10 -10
  86. data/spec/functional/associations_spec.rb +3 -3
  87. data/spec/functional/binary_spec.rb +2 -2
  88. data/spec/functional/caching_spec.rb +8 -15
  89. data/spec/functional/callbacks_spec.rb +89 -2
  90. data/spec/functional/counter_cache_spec.rb +235 -0
  91. data/spec/functional/dirty_spec.rb +63 -46
  92. data/spec/functional/document_spec.rb +30 -5
  93. data/spec/functional/dumpable_spec.rb +1 -1
  94. data/spec/functional/embedded_document_spec.rb +17 -17
  95. data/spec/functional/identity_map_spec.rb +29 -16
  96. data/spec/functional/indexes_spec.rb +19 -18
  97. data/spec/functional/keys_spec.rb +86 -28
  98. data/spec/functional/logger_spec.rb +3 -3
  99. data/spec/functional/modifiers_spec.rb +81 -19
  100. data/spec/functional/partial_updates_spec.rb +577 -0
  101. data/spec/functional/protected_spec.rb +14 -14
  102. data/spec/functional/querying_spec.rb +77 -28
  103. data/spec/functional/safe_spec.rb +23 -27
  104. data/spec/functional/sci_spec.rb +9 -9
  105. data/spec/functional/scopes_spec.rb +235 -2
  106. data/spec/functional/static_keys_spec.rb +153 -0
  107. data/spec/functional/stats_spec.rb +86 -0
  108. data/spec/functional/strong_parameters_spec.rb +49 -0
  109. data/spec/functional/touch_spec.rb +1 -1
  110. data/spec/functional/validations_spec.rb +51 -57
  111. data/spec/quality_spec.rb +51 -0
  112. data/spec/spec_helper.rb +37 -9
  113. data/spec/support/matchers.rb +5 -14
  114. data/spec/unit/associations/base_spec.rb +12 -12
  115. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  116. data/spec/unit/associations/many_association_spec.rb +2 -2
  117. data/spec/unit/associations/one_association_spec.rb +2 -2
  118. data/spec/unit/associations/proxy_spec.rb +19 -20
  119. data/spec/unit/clone_spec.rb +1 -1
  120. data/spec/unit/document_spec.rb +8 -8
  121. data/spec/unit/dynamic_finder_spec.rb +8 -8
  122. data/spec/unit/embedded_document_spec.rb +18 -19
  123. data/spec/unit/extensions_spec.rb +41 -17
  124. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  125. data/spec/unit/key_spec.rb +28 -26
  126. data/spec/unit/keys_spec.rb +20 -11
  127. data/spec/unit/model_generator_spec.rb +0 -0
  128. data/spec/unit/mongo_mapper_spec.rb +38 -85
  129. data/spec/unit/rails_spec.rb +5 -0
  130. data/spec/unit/serialization_spec.rb +1 -1
  131. data/spec/unit/time_zones_spec.rb +2 -2
  132. data/spec/unit/validations_spec.rb +46 -33
  133. metadata +66 -37
  134. data/README.rdoc +0 -59
  135. data/lib/mongo_mapper/connections/10gen.rb +0 -0
  136. data/lib/mongo_mapper/connections/moped.rb +0 -0
  137. data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -3,10 +3,11 @@ module MongoMapper
3
3
  module Plugins
4
4
  module Keys
5
5
  class Key
6
- attr_accessor :name, :type, :options, :default, :ivar, :abbr, :accessors
7
-
6
+ RESERVED_KEYS = %w( id class object_id attributes )
8
7
  ID_STR = '_id'
9
8
 
9
+ attr_accessor :name, :type, :options, :default, :ivar, :abbr, :accessors
10
+
10
11
  def initialize(*args)
11
12
  options_from_args = args.extract_options!
12
13
  @name, @type = args.shift.to_s, args.shift
@@ -60,12 +61,17 @@ module MongoMapper
60
61
  # Special Case: Generate default _id on access
61
62
  value = default_value if @is_id and !value
62
63
 
64
+ value = type.from_mongo(value)
65
+
63
66
  if @typecast
64
- klass = typecast_class # Don't make this lookup on every call
65
- type.from_mongo(value).map { |v| klass.from_mongo(v) }
66
- else
67
- type.from_mongo(value)
67
+ klass = typecast_class # Don't make this lookup on every call
68
+ # typecast assumes array-ish object.
69
+ value = value.map { |v| klass.from_mongo(v) }
70
+ # recast it in the original type
71
+ value = type.from_mongo(value)
68
72
  end
73
+
74
+ value
69
75
  end
70
76
 
71
77
  def set(value)
@@ -93,6 +99,10 @@ module MongoMapper
93
99
  !!@name.match(/\A[a-z_][a-z0-9_]*\z/i)
94
100
  end
95
101
 
102
+ def reserved_name?
103
+ RESERVED_KEYS.include?(@name)
104
+ end
105
+
96
106
  def read_accessor?
97
107
  any_accessor? ["read"]
98
108
  end
@@ -111,18 +121,19 @@ module MongoMapper
111
121
  return !(@accessors & arr_opt).empty?
112
122
  end
113
123
 
114
- private
115
- def typecast_class
116
- @typecast_class ||= options[:typecast].constantize
117
- end
124
+ private
118
125
 
119
- def validate_key_name!
120
- if %w( id ).include? @name
121
- raise MongoMapper::InvalidKey.new("`#{@name}` is a reserved key name (did you mean to use _id?)")
122
- elsif !valid_ruby_name?
123
- raise MongoMapper::InvalidKey.new("`#{@name}` is not a valid key name. Keys must match [a-z][a-z0-9_]*")
124
- end
126
+ def typecast_class
127
+ @typecast_class ||= options[:typecast].constantize
128
+ end
129
+
130
+ def validate_key_name!
131
+ if reserved_name?
132
+ raise MongoMapper::InvalidKey.new("`#{@name}` is a reserved key name")
133
+ elsif !valid_ruby_name?
134
+ raise MongoMapper::InvalidKey.new("`#{@name}` is not a valid key name. Keys must match [a-z][a-z0-9_]*")
125
135
  end
136
+ end
126
137
  end
127
138
  end
128
139
  end
@@ -0,0 +1,45 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Keys
4
+ module Static
5
+ class MissingKeyError < StandardError; end
6
+
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ attr_writer :static_keys
11
+
12
+ def static_keys
13
+ @static_keys || false
14
+ end
15
+ end
16
+
17
+ def read_key(name)
18
+ if !self.class.static_keys || self.class.key?(name)
19
+ super
20
+ else
21
+ raise MissingKeyError, "Tried to read the key #{name.inspect}, but no key was defined. Either define key :#{name} or set self.static_keys = false"
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def write_key(name, value)
28
+ if !self.class.static_keys || self.class.key?(name)
29
+ super
30
+ else
31
+ raise MissingKeyError, "Tried to write the key #{name.inspect}, but no key was defined. Either define key :#{name} or set self.static_keys = false"
32
+ end
33
+ end
34
+
35
+ def load_from_database(attrs, with_cast = false)
36
+ return super if !self.class.static_keys || !attrs.respond_to?(:each)
37
+
38
+ attrs = attrs.select { |key, _| self.class.key?(key) }
39
+
40
+ super(attrs, with_cast)
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -13,7 +13,7 @@ module MongoMapper
13
13
  criteria, keys, options = criteria_and_keys_from_args(args)
14
14
  values, to_decrement = keys.values, {}
15
15
  keys.keys.each_with_index { |k, i| to_decrement[k] = -values[i].abs }
16
- collection.update(criteria, {'$inc' => to_decrement}, :multi => true)
16
+ collection.update_many(criteria, {'$inc' => to_decrement}, options || {})
17
17
  end
18
18
 
19
19
  def set(*args)
@@ -44,7 +44,16 @@ module MongoMapper
44
44
  end
45
45
 
46
46
  def push_all(*args)
47
- modifier_update('$pushAll', args)
47
+ Kernel.warn "push_all no longer supported. use $push with $each"
48
+
49
+ hash = args.pop
50
+ ids = args
51
+
52
+ push_values = hash.inject({}) do |hsh, (key, values)|
53
+ { key => { '$each' => values } }
54
+ end
55
+
56
+ modifier_update('$addToSet', [ids, push_values].flatten)
48
57
  end
49
58
 
50
59
  def add_to_set(*args)
@@ -64,61 +73,64 @@ module MongoMapper
64
73
  modifier_update('$pop', args)
65
74
  end
66
75
 
67
- def find_and_modify(args)
68
- args[:query] = dealias_keys(args[:query]) if args.key? :query
69
- args[:update] = dealias_keys(args[:update]) if args.key? :update
70
- collection.find_and_modify(args)
76
+ def find_one_and_update(args)
77
+ args = args.dup
78
+ args[:query] = dealias_keys(args.delete :query) if args.key? :query
79
+ args[:update] = dealias_keys(args.delete :update) if args.key? :update
80
+ collection.find_one_and_update(args[:query], args[:update], args)
71
81
  end
82
+ alias_method :find_and_modify, :find_one_and_update
72
83
 
73
84
  def upsert(selector, updates, args = {})
74
85
  criteria = dealias_keys(selector)
75
86
  updates = dealias_keys(updates)
76
- collection.update(criteria, updates, args)
87
+ collection.update_one(criteria, updates, args.merge(upsert: true))
77
88
  end
78
89
 
79
- private
80
- def modifier_update(modifier, args)
81
- criteria, updates, options = criteria_and_keys_from_args(args)
82
- if options
83
- collection.update(criteria, {modifier => updates}, options.merge(:multi => true))
84
- else
85
- collection.update(criteria, {modifier => updates}, :multi => true)
86
- end
90
+ private
91
+
92
+ def modifier_update(modifier, args)
93
+ criteria, updates, options = criteria_and_keys_from_args(args)
94
+ if options
95
+ collection.update_many(criteria, {modifier => updates}, options)
96
+ else
97
+ collection.update_many(criteria, {modifier => updates})
87
98
  end
99
+ end
88
100
 
89
- def criteria_and_keys_from_args(args)
90
- if args[0].is_a?(Hash)
91
- criteria = args[0]
92
- updates = args[1]
93
- options = args[2]
94
- else
95
- criteria, (updates, options) = args.partition { |a| !a.is_a?(Hash) }
96
- criteria = { :id => criteria }
97
- end
98
- upgrade_legacy_safe_usage!(options)
99
- updates = dealias_keys updates
100
-
101
- [criteria_hash(criteria).to_hash, updates, options]
101
+ def criteria_and_keys_from_args(args)
102
+ if args[0].is_a?(Hash)
103
+ criteria = args[0]
104
+ updates = args[1]
105
+ options = args[2]
106
+ else
107
+ criteria, (updates, options) = args.partition { |a| !a.is_a?(Hash) }
108
+ criteria = { :id => criteria }
102
109
  end
110
+ upgrade_legacy_safe_usage!(options)
111
+ updates = dealias_keys updates
103
112
 
104
- def upgrade_legacy_safe_usage!(options)
105
- if options and options.key?(:safe)
106
- options.merge! Utils.get_safe_options(options)
107
- options.delete :safe
108
- end
113
+ [criteria_hash(criteria).to_hash, updates, options]
114
+ end
115
+
116
+ def upgrade_legacy_safe_usage!(options)
117
+ if options and options.key?(:safe)
118
+ options.merge! Utils.get_safe_options(options)
119
+ options.delete :safe
109
120
  end
121
+ end
110
122
  end
111
123
 
112
124
  def unset(*args)
113
125
  self.class.unset({:_id => id}, *args)
114
126
  end
115
127
 
116
- def increment(hash, options=nil)
117
- self.class.increment({:_id => id}, hash, options)
128
+ def increment(args, options=nil)
129
+ self.class.increment({:_id => id}, _args_for_counter(args), options)
118
130
  end
119
131
 
120
- def decrement(hash, options=nil)
121
- self.class.decrement({:_id => id}, hash, options)
132
+ def decrement(args, options=nil)
133
+ self.class.decrement({:_id => id}, _args_for_counter(args), options)
122
134
  end
123
135
 
124
136
  def set(hash, options=nil)
@@ -130,7 +142,13 @@ module MongoMapper
130
142
  end
131
143
 
132
144
  def push_all(hash, options=nil)
133
- self.class.push_all({:_id => id}, hash, options)
145
+ Kernel.warn "push_all no longer supported. use $push with $each"
146
+
147
+ push_values = hash.inject({}) do |hsh, (key, values)|
148
+ { key => { '$each' => values } }
149
+ end
150
+
151
+ self.class.push({:_id => id}, push_values, options)
134
152
  end
135
153
 
136
154
  def pull(hash, options=nil)
@@ -149,6 +167,14 @@ module MongoMapper
149
167
  def pop(hash, options=nil)
150
168
  self.class.pop({:_id => id}, hash, options)
151
169
  end
170
+
171
+ private
172
+
173
+ def _args_for_counter(args)
174
+ args.kind_of?(String) || args.kind_of?(Symbol) ?
175
+ { args => 1 } :
176
+ args
177
+ end
152
178
  end
153
179
  end
154
180
  end
@@ -0,0 +1,86 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module PartialUpdates
4
+ extend ActiveSupport::Concern
5
+
6
+ class PartialUpdatesDisabledError < StandardError; end
7
+
8
+ included do
9
+ class_attribute :partial_updates
10
+ self.partial_updates = false
11
+
12
+ self.after_find :_reset_partial_updates_callback
13
+ self.after_save :_reset_partial_updates_callback
14
+ end
15
+
16
+ def initialize(*)
17
+ _reset_partial_updates_callback
18
+ super
19
+ end
20
+
21
+ def fields_for_partial_update
22
+ raise PartialUpdatesDisabledError if !partial_updates
23
+
24
+ Hash.new.tap do |hash|
25
+ attrs = _dealiased_attributes
26
+
27
+ hash[:set_fields] = Array.new.tap do |array|
28
+ attrs.each do |key, value|
29
+ if !@_last_saved_attributes.include?(key) ||
30
+ @_last_saved_attributes[key] != value
31
+ array << key
32
+ end
33
+ end
34
+ end
35
+
36
+ hash[:unset_fields] = @_last_saved_attributes.keys - attrs.keys
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def _reset_partial_updates_callback
43
+ _reset_attributes_for_partial_update if partial_updates
44
+ true
45
+ end
46
+
47
+ def update(options={})
48
+ if partial_updates
49
+ super(options.merge(:persistence_method => :update))
50
+ else
51
+ super
52
+ end
53
+ end
54
+
55
+ def save_to_collection(options={})
56
+ if partial_updates && options[:persistence_method] == :update
57
+ updates = fields_for_partial_update
58
+
59
+ set_fields = updates[:set_fields]
60
+ unset_fields = updates[:unset_fields]
61
+
62
+ if set_fields.any? || unset_fields.any?
63
+ set_fields.push("_id") if !set_fields.include?("_id")
64
+ end
65
+
66
+ options = options.merge({
67
+ :set_fields => set_fields,
68
+ :unset_fields => unset_fields
69
+ })
70
+
71
+ super(options)
72
+ else
73
+ super
74
+ end
75
+ end
76
+
77
+ def _dealiased_attributes
78
+ self.class.dealias_keys(attributes)
79
+ end
80
+
81
+ def _reset_attributes_for_partial_update
82
+ @_last_saved_attributes = _dealiased_attributes._mongo_mapper_deep_copy_
83
+ end
84
+ end
85
+ end
86
+ end
@@ -39,7 +39,7 @@ module MongoMapper
39
39
  if database_name.nil?
40
40
  MongoMapper.database
41
41
  else
42
- connection.db(database_name)
42
+ connection.use(database_name).database
43
43
  end
44
44
  end
45
45
 
@@ -55,16 +55,21 @@ module MongoMapper
55
55
 
56
56
  def collection
57
57
  assert_supported
58
- database.collection(collection_name)
58
+ database.collection(collection_name, collection_options)
59
59
  end
60
60
 
61
- private
62
- def assert_supported
63
- @embeddable ||= embeddable?
64
- if @embeddable
65
- raise MongoMapper::NotSupported.new('This is not supported for embeddable documents at this time.')
66
- end
61
+ def collection_options
62
+ {}
63
+ end
64
+
65
+ private
66
+
67
+ def assert_supported
68
+ @embeddable ||= embeddable?
69
+ if @embeddable
70
+ raise MongoMapper::NotSupported.new('This is not supported for embeddable documents at this time.')
67
71
  end
72
+ end
68
73
  end
69
74
 
70
75
  def collection
@@ -35,11 +35,12 @@ module MongoMapper
35
35
  super(filter_protected_attrs(attrs))
36
36
  end
37
37
 
38
- protected
39
- def filter_protected_attrs(attrs)
40
- return attrs if protected_attributes.blank? || attrs.blank?
41
- attrs.dup.delete_if { |key, val| protected_attributes.include?(key.to_sym) }
42
- end
38
+ protected
39
+
40
+ def filter_protected_attrs(attrs)
41
+ return attrs if protected_attributes.blank? || attrs.blank?
42
+ attrs.dup.delete_if { |key, val| protected_attributes.include?(key.to_sym) }
43
+ end
43
44
  end
44
45
  end
45
46
  end
@@ -61,41 +61,42 @@ module MongoMapper
61
61
  Plucky::CriteriaHash.new(criteria, :object_ids => object_id_keys)
62
62
  end
63
63
 
64
- private
65
- def transformer
66
- @transformer ||= lambda { |doc| load(doc) }
67
- end
64
+ private
68
65
 
69
- def initialize_each(*docs)
70
- instances = []
71
- docs = [{}] if docs.blank?
72
- docs.flatten.each do |attrs|
73
- doc = new(attrs)
74
- yield(doc)
75
- instances << doc
76
- end
77
- instances.size == 1 ? instances[0] : instances
78
- end
66
+ def transformer
67
+ @transformer ||= lambda { |doc| load(doc) }
68
+ end
79
69
 
80
- def update_single(id, attrs)
81
- if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
82
- raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
83
- end
70
+ def initialize_each(*docs)
71
+ instances = []
72
+ docs = [{}] if docs.blank?
73
+ docs.flatten.each do |attrs|
74
+ doc = new(attrs)
75
+ yield(doc)
76
+ instances << doc
77
+ end
78
+ instances.size == 1 ? instances[0] : instances
79
+ end
84
80
 
85
- find(id).tap do |doc|
86
- doc.update_attributes(attrs)
87
- end
81
+ def update_single(id, attrs)
82
+ if id.blank? || attrs.blank? || !attrs.is_a?(Hash)
83
+ raise ArgumentError, "Updating a single document requires an id and a hash of attributes"
88
84
  end
89
85
 
90
- def update_multiple(docs)
91
- unless docs.is_a?(Hash)
92
- raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
93
- end
86
+ find(id).tap do |doc|
87
+ doc.update_attributes(attrs)
88
+ end
89
+ end
94
90
 
95
- instances = []
96
- docs.each_pair { |id, attrs| instances << update(id, attrs) }
97
- instances
91
+ def update_multiple(docs)
92
+ unless docs.is_a?(Hash)
93
+ raise ArgumentError, "Updating multiple documents takes 1 argument and it must be hash"
98
94
  end
95
+
96
+ instances = []
97
+ docs.each_pair { |id, attrs| instances << update(id, attrs) }
98
+ instances
99
+ end
99
100
  end
100
101
 
101
102
  def save(options={})
@@ -116,25 +117,67 @@ module MongoMapper
116
117
  self.class.delete(id).tap { @_destroyed = true } if persisted?
117
118
  end
118
119
 
119
- private
120
- def create_or_update(options={})
121
- result = persisted? ? update(options) : create(options)
122
- result != false
123
- end
120
+ private
124
121
 
125
- def create(options={})
126
- save_to_collection(options.merge(:persistence_method => :insert))
127
- end
122
+ def create_or_update(options={})
123
+ result = persisted? ? update(options) : create(options)
124
+ result != false
125
+ end
128
126
 
129
- def update(options={})
130
- save_to_collection(options.merge(:persistence_method => :save))
127
+ def create(options={})
128
+ save_to_collection(options.merge(:persistence_method => :insert))
129
+ end
130
+
131
+ def update(options={})
132
+ save_to_collection(options.reverse_merge(:persistence_method => :save))
133
+ end
134
+
135
+ def save_to_collection(options={})
136
+ @_new = false
137
+ method = options.delete(:persistence_method) || :save
138
+ update = to_mongo
139
+ query_options = Utils.get_safe_options(options)
140
+
141
+ if query_options.any?
142
+ collection = self.collection.with(write: query_options)
143
+ else
144
+ collection = self.collection
131
145
  end
132
146
 
133
- def save_to_collection(options={})
134
- @_new = false
135
- method = options.delete(:persistence_method) || :save
136
- collection.send(method, to_mongo, Utils.get_safe_options(options))
147
+ case method
148
+ when :insert
149
+ collection.insert_one(update, query_options)
150
+ when :save
151
+ collection.update_one({:_id => _id}, update, query_options.merge(upsert: true))
152
+ when :update
153
+ update.stringify_keys!
154
+
155
+ id = update.delete("_id")
156
+
157
+ set_values = update
158
+ unset_values = {}
159
+
160
+ if fields_for_set = options.delete(:set_fields)
161
+ set_values = set_values.slice(*fields_for_set)
162
+ end
163
+
164
+ if fields_for_unset = options.delete(:unset_fields)
165
+ fields_for_unset.each do |field|
166
+ unset_values[field] = true
167
+ end
168
+ end
169
+
170
+ find_query = { :_id => id }
171
+
172
+ update_query = {}
173
+ update_query["$set"] = set_values if set_values.any?
174
+ update_query["$unset"] = unset_values if unset_values.any?
175
+
176
+ if update_query.any?
177
+ collection.update_one(find_query, update_query, query_options)
178
+ end
137
179
  end
180
+ end
138
181
  end
139
182
  end
140
183
  end