mongo_mapper 0.7.5 → 0.7.6

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 (76) hide show
  1. data/lib/mongo_mapper.rb +3 -5
  2. data/lib/mongo_mapper/document.rb +23 -53
  3. data/lib/mongo_mapper/plugins/associations.rb +1 -1
  4. data/lib/mongo_mapper/plugins/associations/base.rb +4 -4
  5. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +1 -1
  6. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +1 -1
  7. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +1 -1
  8. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +1 -1
  9. data/lib/mongo_mapper/plugins/equality.rb +3 -3
  10. data/lib/mongo_mapper/plugins/identity_map.rb +8 -7
  11. data/lib/mongo_mapper/plugins/keys.rb +49 -73
  12. data/lib/mongo_mapper/plugins/keys/key.rb +44 -0
  13. data/lib/mongo_mapper/plugins/modifiers.rb +9 -5
  14. data/lib/mongo_mapper/plugins/pagination/proxy.rb +3 -3
  15. data/lib/mongo_mapper/plugins/serialization.rb +3 -3
  16. data/lib/mongo_mapper/plugins/timestamps.rb +1 -1
  17. data/lib/mongo_mapper/plugins/validations.rb +2 -2
  18. data/lib/mongo_mapper/query.rb +9 -129
  19. data/lib/mongo_mapper/support.rb +17 -39
  20. data/lib/mongo_mapper/version.rb +1 -1
  21. metadata +54 -140
  22. data/.gitignore +0 -10
  23. data/Rakefile +0 -37
  24. data/mongo_mapper.gemspec +0 -214
  25. data/performance/read_write.rb +0 -52
  26. data/specs.watchr +0 -51
  27. data/test/NOTE_ON_TESTING +0 -1
  28. data/test/active_model_lint_test.rb +0 -13
  29. data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +0 -63
  30. data/test/functional/associations/test_belongs_to_proxy.rb +0 -101
  31. data/test/functional/associations/test_in_array_proxy.rb +0 -325
  32. data/test/functional/associations/test_many_documents_as_proxy.rb +0 -229
  33. data/test/functional/associations/test_many_documents_proxy.rb +0 -536
  34. data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +0 -176
  35. data/test/functional/associations/test_many_embedded_proxy.rb +0 -256
  36. data/test/functional/associations/test_many_polymorphic_proxy.rb +0 -302
  37. data/test/functional/associations/test_one_embedded_proxy.rb +0 -68
  38. data/test/functional/associations/test_one_proxy.rb +0 -196
  39. data/test/functional/test_associations.rb +0 -44
  40. data/test/functional/test_binary.rb +0 -27
  41. data/test/functional/test_callbacks.rb +0 -151
  42. data/test/functional/test_dirty.rb +0 -163
  43. data/test/functional/test_document.rb +0 -1219
  44. data/test/functional/test_embedded_document.rb +0 -210
  45. data/test/functional/test_identity_map.rb +0 -507
  46. data/test/functional/test_indexing.rb +0 -44
  47. data/test/functional/test_logger.rb +0 -20
  48. data/test/functional/test_modifiers.rb +0 -394
  49. data/test/functional/test_pagination.rb +0 -93
  50. data/test/functional/test_protected.rb +0 -163
  51. data/test/functional/test_string_id_compatibility.rb +0 -67
  52. data/test/functional/test_timestamps.rb +0 -64
  53. data/test/functional/test_userstamps.rb +0 -28
  54. data/test/functional/test_validations.rb +0 -342
  55. data/test/models.rb +0 -227
  56. data/test/support/custom_matchers.rb +0 -37
  57. data/test/support/timing.rb +0 -16
  58. data/test/test_helper.rb +0 -64
  59. data/test/unit/associations/test_base.rb +0 -212
  60. data/test/unit/associations/test_proxy.rb +0 -105
  61. data/test/unit/serializers/test_json_serializer.rb +0 -202
  62. data/test/unit/test_descendant_appends.rb +0 -71
  63. data/test/unit/test_document.rb +0 -225
  64. data/test/unit/test_dynamic_finder.rb +0 -123
  65. data/test/unit/test_embedded_document.rb +0 -657
  66. data/test/unit/test_keys.rb +0 -185
  67. data/test/unit/test_mongo_mapper.rb +0 -118
  68. data/test/unit/test_pagination.rb +0 -160
  69. data/test/unit/test_plugins.rb +0 -50
  70. data/test/unit/test_query.rb +0 -374
  71. data/test/unit/test_rails.rb +0 -181
  72. data/test/unit/test_rails_compatibility.rb +0 -52
  73. data/test/unit/test_serialization.rb +0 -51
  74. data/test/unit/test_support.rb +0 -382
  75. data/test/unit/test_time_zones.rb +0 -39
  76. data/test/unit/test_validations.rb +0 -544
@@ -0,0 +1,44 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Keys
4
+ class Key
5
+ attr_accessor :name, :type, :options, :default_value
6
+
7
+ def initialize(*args)
8
+ options = args.extract_options!
9
+ @name, @type = args.shift.to_s, args.shift
10
+ self.options = (options || {}).symbolize_keys
11
+ self.default_value = self.options.delete(:default)
12
+ end
13
+
14
+ def ==(other)
15
+ @name == other.name && @type == other.type
16
+ end
17
+
18
+ def embeddable?
19
+ type.respond_to?(:embeddable?) && type.embeddable? ? true : false
20
+ end
21
+
22
+ def number?
23
+ [Integer, Float].include?(type)
24
+ end
25
+
26
+ def get(value)
27
+ if value.nil? && !default_value.nil?
28
+ if default_value.respond_to?(:call)
29
+ return default_value.call
30
+ else
31
+ return default_value
32
+ end
33
+ end
34
+
35
+ type.from_mongo(value)
36
+ end
37
+
38
+ def set(value)
39
+ type.to_mongo(value)
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -14,7 +14,11 @@ module MongoMapper
14
14
  end
15
15
 
16
16
  def set(*args)
17
- modifier_update('$set', args)
17
+ criteria, updates = criteria_and_keys_from_args(args)
18
+ updates.each do |key, value|
19
+ updates[key] = keys[key].set(value) if key?(key)
20
+ end
21
+ collection.update(criteria, {'$set' => updates}, :multi => true)
18
22
  end
19
23
 
20
24
  def unset(*args)
@@ -25,7 +29,7 @@ module MongoMapper
25
29
  criteria = {:id => ids}
26
30
  end
27
31
 
28
- criteria = to_criteria(criteria)
32
+ criteria = query(criteria).criteria.to_hash
29
33
  modifiers = keys.inject({}) { |hash, key| hash[key] = 1; hash }
30
34
  collection.update(criteria, {'$unset' => modifiers}, :multi => true)
31
35
  end
@@ -57,14 +61,14 @@ module MongoMapper
57
61
 
58
62
  private
59
63
  def modifier_update(modifier, args)
60
- criteria, keys = criteria_and_keys_from_args(args)
61
- collection.update(criteria, {modifier => keys}, :multi => true)
64
+ criteria, updates = criteria_and_keys_from_args(args)
65
+ collection.update(criteria, {modifier => updates}, :multi => true)
62
66
  end
63
67
 
64
68
  def criteria_and_keys_from_args(args)
65
69
  keys = args.pop
66
70
  criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
67
- [to_criteria(criteria), keys]
71
+ [query(criteria).criteria.to_hash, keys]
68
72
  end
69
73
  end
70
74
 
@@ -3,7 +3,7 @@ module MongoMapper
3
3
  module Pagination
4
4
  class Proxy
5
5
  instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|respond_to\?|proxy_|^object_id$)/ }
6
-
6
+
7
7
  attr_accessor :subject
8
8
  attr_reader :total_entries, :per_page, :current_page
9
9
  alias limit per_page
@@ -50,11 +50,11 @@ module MongoMapper
50
50
  def method_missing(name, *args, &block)
51
51
  @subject.send(name, *args, &block)
52
52
  end
53
-
53
+
54
54
  def respond_to?(name, *args, &block)
55
55
  super || @subject.respond_to?(name, *args, &block)
56
56
  end
57
-
57
+
58
58
  private
59
59
  def per_page=(value)
60
60
  value = 25 if value.blank?
@@ -6,7 +6,7 @@ module MongoMapper
6
6
  def self.configure(model)
7
7
  model.class_eval { cattr_accessor :include_root_in_json, :instance_writer => true }
8
8
  end
9
-
9
+
10
10
  module InstanceMethods
11
11
  def as_json options={}
12
12
  options ||= {}
@@ -43,7 +43,7 @@ module MongoMapper
43
43
  }
44
44
  end
45
45
  # End rip
46
-
46
+
47
47
  options.delete(:only) if options[:only].nil? or options[:only].empty?
48
48
 
49
49
  hash.each do |key, value|
@@ -57,7 +57,7 @@ module MongoMapper
57
57
  hash[key] = value.as_json(options)
58
58
  end
59
59
  end
60
-
60
+
61
61
  # Replicate Rails 3 naming - and also bin anytihng after : for use in our dynamic classes from unit tests
62
62
  hash = { ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).gsub(/:.*/,'') => hash } if include_root_in_json
63
63
  hash
@@ -8,7 +8,7 @@ module MongoMapper
8
8
  class_eval { before_save :update_timestamps }
9
9
  end
10
10
  end
11
-
11
+
12
12
  module InstanceMethods
13
13
  def update_timestamps
14
14
  now = Time.now.utc
@@ -4,13 +4,13 @@ module MongoMapper
4
4
  def self.configure(model)
5
5
  model.class_eval { include Validatable }
6
6
  end
7
-
7
+
8
8
  module DocumentMacros
9
9
  def validates_uniqueness_of(*args)
10
10
  add_validations(args, MongoMapper::Plugins::Validations::ValidatesUniquenessOf)
11
11
  end
12
12
  end
13
-
13
+
14
14
  class ValidatesUniquenessOf < Validatable::ValidationBase
15
15
  option :scope, :case_sensitive
16
16
  default :case_sensitive => true
@@ -1,143 +1,23 @@
1
1
  module MongoMapper
2
- # IMPORTANT
3
- # This class is private to MongoMapper and should not be considered part of MongoMapper's public API.
4
- #
5
2
  class Query
6
- OptionKeys = [:fields, :select, :skip, :offset, :limit, :sort, :order]
7
-
8
- attr_reader :model
9
-
10
- def initialize(model, options)
3
+ def initialize(model, options={})
11
4
  raise ArgumentError, "Options must be a hash" unless options.is_a?(Hash)
12
- @model, @options, @conditions, @original_options = model, {}, {}, options
13
- separate_options_and_conditions
5
+ @model, @options, @conditions = model, {}, {}
6
+ query.update(options)
14
7
  add_sci_condition
15
8
  end
16
9
 
17
- def criteria
18
- to_criteria(@conditions)
19
- end
20
-
21
- def options
22
- fields = @options[:fields] || @options[:select]
23
- skip = @options[:skip] || @options[:offset] || 0
24
- limit = @options[:limit] || 0
25
- sort = @options[:sort] || normalized_sort(@options[:order])
26
-
27
- {:fields => to_fields(fields), :skip => skip.to_i, :limit => limit.to_i, :sort => sort}
28
- end
29
-
30
- def to_a
31
- [criteria, options]
32
- end
33
-
34
10
  private
35
- def separate_options_and_conditions
36
- @original_options.each_pair do |key, value|
37
- key = key.respond_to?(:to_sym) ? key.to_sym : key
38
-
39
- if OptionKeys.include?(key)
40
- @options[key] = value
41
- elsif key == :conditions
42
- @conditions.update(value)
43
- else
44
- @conditions[key] = value
45
- end
46
- end
11
+ def method_missing(method, *args, &block)
12
+ query.send(method, *args, &block)
47
13
  end
48
14
 
49
- # adds _type single collection inheritance scope for models that need it
50
- def add_sci_condition
51
- @conditions[:_type] = model.to_s if model.single_collection_inherited?
15
+ def query
16
+ @query ||= Plucky::Query.new(@model.collection).object_ids(@model.object_id_keys)
52
17
  end
53
18
 
54
- def modifier?(field)
55
- field.to_s =~ /^\$/
56
- end
57
-
58
- def symbol_operator?(object)
59
- object.respond_to?(:field, :operator)
60
- end
61
-
62
- def to_criteria(conditions, parent_key=nil)
63
- criteria = {}
64
-
65
- conditions.each_pair do |key, value|
66
- key = normalized_key(key)
67
-
68
- if model.object_id_key?(key)
69
- case value
70
- when String
71
- value = ObjectId.to_mongo(value)
72
- when Array
73
- value.map! { |id| ObjectId.to_mongo(id) }
74
- end
75
- end
76
-
77
- if symbol_operator?(key)
78
- key, value = normalized_key(key.field), {"$#{key.operator}" => value}
79
- end
80
-
81
- criteria[key] = normalized_value(criteria, key, value)
82
- end
83
-
84
- criteria
85
- end
86
-
87
- def to_fields(keys)
88
- return keys if keys.is_a?(Hash)
89
- return nil if keys.blank?
90
-
91
- if keys.respond_to?(:flatten, :compact)
92
- keys.flatten.compact
93
- else
94
- keys.split(',').map { |key| key.strip }
95
- end
96
- end
97
-
98
- def to_order(key, direction=nil)
99
- [normalized_key(key).to_s, normalized_direction(direction)]
100
- end
101
-
102
- def normalized_key(key)
103
- key.to_s == 'id' ? :_id : key
104
- end
105
-
106
- # TODO: this is getting heavy enough to move to a class
107
- def normalized_value(criteria, key, value)
108
- case value
109
- when Array, Set
110
- modifier?(key) ? value.to_a : {'$in' => value.to_a}
111
- when Hash
112
- if criteria[key].kind_of?(Hash)
113
- criteria[key].dup.merge(to_criteria(value, key))
114
- else
115
- to_criteria(value, key)
116
- end
117
- when Time
118
- value.utc
119
- else
120
- value
121
- end
122
- end
123
-
124
- def normalized_direction(direction)
125
- direction ||= 'asc'
126
- direction.downcase == 'asc' ? Mongo::ASCENDING : Mongo::DESCENDING
127
- end
128
-
129
- def normalized_sort(sort)
130
- return if sort.blank?
131
-
132
- if sort.respond_to?(:all?) && sort.all? { |s| symbol_operator?(s) }
133
- sort.map { |s| to_order(s.field, s.operator) }
134
- elsif symbol_operator?(sort)
135
- [to_order(sort.field, sort.operator)]
136
- else
137
- sort.split(',').map do |str|
138
- to_order(*str.strip.split(' '))
139
- end
140
- end
19
+ def add_sci_condition
20
+ query[:_type] = @model.to_s if @model.single_collection_inherited?
141
21
  end
142
22
  end
143
23
  end
@@ -3,7 +3,7 @@ class Array
3
3
  value = value.respond_to?(:lines) ? value.lines : value
4
4
  value.to_a
5
5
  end
6
-
6
+
7
7
  def self.from_mongo(value)
8
8
  value || []
9
9
  end
@@ -28,7 +28,7 @@ class Boolean
28
28
  true => true, 'true' => true, 'TRUE' => true, 'True' => true, 't' => true, 'T' => true, '1' => true, 1 => true, 1.0 => true,
29
29
  false => false, 'false' => false, 'FALSE' => false, 'False' => false, 'f' => false, 'F' => false, '0' => false, 0 => false, 0.0 => false, nil => nil
30
30
  }
31
-
31
+
32
32
  def self.to_mongo(value)
33
33
  if value.is_a?(Boolean)
34
34
  value
@@ -53,7 +53,7 @@ class Date
53
53
  rescue
54
54
  nil
55
55
  end
56
-
56
+
57
57
  def self.from_mongo(value)
58
58
  value.to_date if value.present?
59
59
  end
@@ -61,7 +61,7 @@ end
61
61
 
62
62
  class Float
63
63
  def self.to_mongo(value)
64
- value.to_f
64
+ value.nil? ? nil : value.to_f
65
65
  end
66
66
  end
67
67
 
@@ -69,7 +69,7 @@ class Hash
69
69
  def self.from_mongo(value)
70
70
  HashWithIndifferentAccess.new(value || {})
71
71
  end
72
-
72
+
73
73
  def to_mongo
74
74
  self
75
75
  end
@@ -90,7 +90,7 @@ class NilClass
90
90
  def to_mongo(value)
91
91
  value
92
92
  end
93
-
93
+
94
94
  def from_mongo(value)
95
95
  value
96
96
  end
@@ -115,11 +115,11 @@ class Object
115
115
  def class_def(name, &blk)
116
116
  class_eval { define_method(name, &blk) }
117
117
  end
118
-
118
+
119
119
  def self.to_mongo(value)
120
120
  value
121
121
  end
122
-
122
+
123
123
  def self.from_mongo(value)
124
124
  value
125
125
  end
@@ -127,15 +127,9 @@ end
127
127
 
128
128
  class ObjectId
129
129
  def self.to_mongo(value)
130
- if value.blank?
131
- nil
132
- elsif value.is_a?(BSON::ObjectID)
133
- value
134
- else
135
- BSON::ObjectID.from_string(value.to_s)
136
- end
130
+ Plucky.to_object_id(value)
137
131
  end
138
-
132
+
139
133
  def self.from_mongo(value)
140
134
  value
141
135
  end
@@ -145,7 +139,7 @@ class Set
145
139
  def self.to_mongo(value)
146
140
  value.to_a
147
141
  end
148
-
142
+
149
143
  def self.from_mongo(value)
150
144
  Set.new(value || [])
151
145
  end
@@ -155,28 +149,12 @@ class String
155
149
  def self.to_mongo(value)
156
150
  value.nil? ? nil : value.to_s
157
151
  end
158
-
152
+
159
153
  def self.from_mongo(value)
160
154
  value.nil? ? nil : value.to_s
161
155
  end
162
156
  end
163
157
 
164
- class SymbolOperator
165
- attr_reader :field, :operator
166
-
167
- def initialize(field, operator, options={})
168
- @field, @operator = field, operator
169
- end unless method_defined?(:initialize)
170
- end
171
-
172
- class Symbol
173
- %w(gt lt gte lte ne in nin mod all size where exists asc desc).each do |operator|
174
- define_method(operator) do
175
- SymbolOperator.new(self, operator)
176
- end unless method_defined?(operator)
177
- end
178
- end
179
-
180
158
  class Time
181
159
  def self.to_mongo(value)
182
160
  if value.nil? || value == ''
@@ -184,11 +162,11 @@ class Time
184
162
  else
185
163
  time_class = Time.try(:zone).present? ? Time.zone : Time
186
164
  time = value.is_a?(Time) ? value : time_class.parse(value.to_s)
187
- # Convert time to milliseconds since BSON stores dates with that accurracy, but Ruby uses microseconds
188
- Time.at((time.to_f * 1000).round / 1000.0).utc if time
165
+ # strip milliseconds as Ruby does micro and bson does milli and rounding rounded wrong
166
+ at(time.to_i).utc if time
189
167
  end
190
168
  end
191
-
169
+
192
170
  def self.from_mongo(value)
193
171
  if Time.try(:zone).present? && value.present?
194
172
  value.in_time_zone(Time.zone)
@@ -200,11 +178,11 @@ end
200
178
 
201
179
  class BSON::ObjectID
202
180
  alias_method :original_to_json, :to_json
203
-
181
+
204
182
  def as_json(options=nil)
205
183
  to_s
206
184
  end
207
-
185
+
208
186
  def to_json(options = nil)
209
187
  as_json.to_json
210
188
  end