mongo_mapper 0.7.5 → 0.7.6

Sign up to get free protection for your applications and to get access to all the features.
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