mongo_mapper 0.13.0 → 0.15.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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