mongo_mapper 0.7.0 → 0.7.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/README.rdoc +3 -1
  2. data/Rakefile +9 -12
  3. data/lib/mongo_mapper.rb +30 -10
  4. data/lib/mongo_mapper/document.rb +16 -74
  5. data/lib/mongo_mapper/embedded_document.rb +7 -1
  6. data/lib/mongo_mapper/plugins.rb +3 -0
  7. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +1 -12
  8. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +6 -1
  9. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +4 -1
  10. data/lib/mongo_mapper/plugins/callbacks.rb +183 -12
  11. data/lib/mongo_mapper/plugins/keys.rb +17 -5
  12. data/lib/mongo_mapper/plugins/modifiers.rb +87 -0
  13. data/lib/mongo_mapper/plugins/pagination/proxy.rb +7 -3
  14. data/lib/mongo_mapper/plugins/protected.rb +1 -1
  15. data/lib/mongo_mapper/plugins/rails.rb +16 -8
  16. data/lib/mongo_mapper/plugins/serialization.rb +51 -81
  17. data/lib/mongo_mapper/plugins/timestamps.rb +21 -0
  18. data/lib/mongo_mapper/plugins/userstamps.rb +14 -0
  19. data/lib/mongo_mapper/query.rb +1 -1
  20. data/lib/mongo_mapper/version.rb +3 -0
  21. data/mongo_mapper.gemspec +22 -11
  22. data/test/active_model_lint_test.rb +11 -0
  23. data/test/functional/associations/test_in_array_proxy.rb +16 -0
  24. data/test/functional/associations/test_many_documents_proxy.rb +22 -0
  25. data/test/functional/test_callbacks.rb +104 -34
  26. data/test/functional/test_document.rb +70 -149
  27. data/test/functional/test_embedded_document.rb +39 -34
  28. data/test/functional/test_indexing.rb +44 -0
  29. data/test/functional/test_modifiers.rb +297 -227
  30. data/test/functional/test_protected.rb +11 -5
  31. data/test/functional/test_timestamps.rb +64 -0
  32. data/test/functional/test_userstamps.rb +28 -0
  33. data/test/support/timing.rb +1 -1
  34. data/test/unit/serializers/test_json_serializer.rb +30 -17
  35. data/test/unit/test_embedded_document.rb +15 -15
  36. data/test/unit/test_keys.rb +15 -11
  37. data/test/unit/test_mongo_mapper.rb +31 -1
  38. data/test/unit/test_pagination.rb +33 -0
  39. data/test/unit/test_query.rb +6 -0
  40. data/test/unit/test_serialization.rb +3 -3
  41. data/test/unit/test_support.rb +9 -5
  42. metadata +17 -6
  43. data/VERSION +0 -1
@@ -170,11 +170,14 @@ module MongoMapper
170
170
 
171
171
  def attributes=(attrs)
172
172
  return if attrs.blank?
173
-
173
+
174
174
  attrs.each_pair do |name, value|
175
175
  writer_method = "#{name}="
176
176
 
177
177
  if respond_to?(writer_method)
178
+ if writer_method == '_root_document='
179
+ puts "_root_document= #{value.inspect}"
180
+ end
178
181
  self.send(writer_method, value)
179
182
  else
180
183
  self[name.to_s] = value
@@ -217,7 +220,7 @@ module MongoMapper
217
220
  def id
218
221
  _id
219
222
  end
220
-
223
+
221
224
  def id=(value)
222
225
  if self.class.using_object_id?
223
226
  value = MongoMapper.normalize_object_id(value)
@@ -259,7 +262,7 @@ module MongoMapper
259
262
  def assign_type_if_present
260
263
  self._type = self.class.name if respond_to?(:_type=)
261
264
  end
262
-
265
+
263
266
  def ensure_key_exists(name)
264
267
  self.class.key(name) unless respond_to?("#{name}=")
265
268
  end
@@ -280,11 +283,16 @@ module MongoMapper
280
283
 
281
284
  def write_key(name, value)
282
285
  key = keys[name]
286
+
287
+ if key.embeddable? && value.is_a?(key.type)
288
+ value._parent_document = self
289
+ end
290
+
283
291
  instance_variable_set "@#{name}_before_typecast", value
284
292
  instance_variable_set "@#{name}", key.set(value)
285
293
  end
286
294
  end
287
-
295
+
288
296
  class Key
289
297
  attr_accessor :name, :type, :options, :default_value
290
298
 
@@ -309,7 +317,11 @@ module MongoMapper
309
317
 
310
318
  def get(value)
311
319
  if value.nil? && !default_value.nil?
312
- return default_value
320
+ if default_value.respond_to?(:call)
321
+ return default_value.call
322
+ else
323
+ return default_value
324
+ end
313
325
  end
314
326
 
315
327
  type.from_mongo(value)
@@ -0,0 +1,87 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Modifiers
4
+ module ClassMethods
5
+ def increment(*args)
6
+ modifier_update('$inc', args)
7
+ end
8
+
9
+ def decrement(*args)
10
+ criteria, keys = criteria_and_keys_from_args(args)
11
+ values, to_decrement = keys.values, {}
12
+ keys.keys.each_with_index { |k, i| to_decrement[k] = -values[i].abs }
13
+ collection.update(criteria, {'$inc' => to_decrement}, :multi => true)
14
+ end
15
+
16
+ def set(*args)
17
+ modifier_update('$set', args)
18
+ end
19
+
20
+ def push(*args)
21
+ modifier_update('$push', args)
22
+ end
23
+
24
+ def push_all(*args)
25
+ modifier_update('$pushAll', args)
26
+ end
27
+
28
+ def push_uniq(*args)
29
+ criteria, keys = criteria_and_keys_from_args(args)
30
+ keys.each { |key, value | criteria[key] = {'$ne' => value} }
31
+ collection.update(criteria, {'$push' => keys}, :multi => true)
32
+ end
33
+
34
+ def pull(*args)
35
+ modifier_update('$pull', args)
36
+ end
37
+
38
+ def pull_all(*args)
39
+ modifier_update('$pullAll', args)
40
+ end
41
+
42
+ def pop(*args)
43
+ modifier_update('$pop', args)
44
+ end
45
+
46
+ private
47
+ def modifier_update(modifier, args)
48
+ criteria, keys = criteria_and_keys_from_args(args)
49
+ modifiers = {modifier => keys}
50
+ collection.update(criteria, modifiers, :multi => true)
51
+ end
52
+
53
+ def criteria_and_keys_from_args(args)
54
+ keys = args.pop
55
+ criteria = args[0].is_a?(Hash) ? args[0] : {:id => args}
56
+ [to_criteria(criteria), keys]
57
+ end
58
+ end
59
+
60
+ module InstanceMethods
61
+ def increment(hash)
62
+ self.class.increment({:_id => id}, hash)
63
+ end
64
+
65
+ def decrement(hash)
66
+ self.class.decrement({:_id => id}, hash)
67
+ end
68
+
69
+ def set(hash)
70
+ self.class.set({:_id => id}, hash)
71
+ end
72
+
73
+ def push(hash)
74
+ self.class.push({:_id => id}, hash)
75
+ end
76
+
77
+ def pull(hash)
78
+ self.class.pull({:_id => id}, hash)
79
+ end
80
+
81
+ def push_uniq(hash)
82
+ self.class.push_uniq({:_id => id}, hash)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -2,8 +2,8 @@ module MongoMapper
2
2
  module Plugins
3
3
  module Pagination
4
4
  class Proxy
5
- instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|proxy_|^object_id$)/ }
6
-
5
+ instance_methods.each { |m| undef_method m unless m =~ /(^__|^nil\?$|^send$|respond_to\?|proxy_|^object_id$)/ }
6
+
7
7
  attr_accessor :subject
8
8
  attr_reader :total_entries, :per_page, :current_page
9
9
  alias limit per_page
@@ -50,7 +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
+ def respond_to?(name, *args, &block)
55
+ super || @subject.respond_to?(name, *args, &block)
56
+ end
57
+
54
58
  private
55
59
  def per_page=(value)
56
60
  value = 25 if value.blank?
@@ -37,7 +37,7 @@ module MongoMapper
37
37
  protected
38
38
  def filter_protected_attrs(attrs)
39
39
  return attrs if protected_attributes.blank?
40
- attrs.dup.delete_if { |key, val| protected_attributes.include?(key) }
40
+ attrs.dup.delete_if { |key, val| protected_attributes.include?(key.to_sym) }
41
41
  end
42
42
  end
43
43
  end
@@ -1,41 +1,49 @@
1
1
  module MongoMapper
2
2
  module Plugins
3
3
  module Rails
4
+ def self.configure(model)
5
+ model.extend ActiveModel::Naming if defined?(ActiveModel)
6
+ end
7
+
4
8
  module InstanceMethods
5
9
  def to_param
6
10
  id.to_s
7
11
  end
8
-
12
+
13
+ def to_model
14
+ self
15
+ end
16
+
9
17
  def new_record?
10
18
  new?
11
19
  end
12
-
20
+
13
21
  def read_attribute(name)
14
22
  self[name]
15
23
  end
16
-
24
+
17
25
  def read_attribute_before_typecast(name)
18
26
  read_key_before_typecast(name)
19
27
  end
20
-
28
+
21
29
  def write_attribute(name, value)
22
30
  self[name] = value
23
31
  end
24
32
  end
25
-
33
+
26
34
  module ClassMethods
27
35
  def has_one(*args)
28
36
  one(*args)
29
37
  end
30
-
38
+
31
39
  def has_many(*args)
32
40
  many(*args)
33
41
  end
34
-
42
+
35
43
  def column_names
36
44
  keys.keys
37
45
  end
38
-
46
+
39
47
  def human_name
40
48
  self.name.demodulize.titleize
41
49
  end
@@ -4,102 +4,72 @@ module MongoMapper
4
4
  module Plugins
5
5
  module Serialization
6
6
  def self.configure(model)
7
- model.class_eval { include Json }
7
+ model.class_eval { cattr_accessor :include_root_in_json, :instance_writer => true }
8
8
  end
9
9
 
10
- class Serializer
11
- attr_reader :options
12
-
13
- def initialize(record, options={})
14
- @record, @options = record, options.dup
15
- end
16
-
17
- def serializable_key_names
18
- key_names = @record.attributes.keys
19
-
20
- if options[:only]
21
- options.delete(:except)
22
- key_names = key_names & Array(options[:only]).collect { |n| n.to_s }
23
- else
24
- options[:except] = Array(options[:except])
25
- key_names = key_names - options[:except].collect { |n| n.to_s }
26
- end
27
-
28
- key_names
29
- end
30
-
31
- def serializable_method_names
32
- Array(options[:methods]).inject([]) do |method_attributes, name|
33
- method_attributes << name if @record.respond_to?(name.to_s)
34
- method_attributes
10
+ module InstanceMethods
11
+ def as_json options={}
12
+ options ||= {}
13
+ unless options[:only]
14
+ methods = [options.delete(:methods)].flatten.compact
15
+ methods << :id
16
+ options[:methods] = methods.uniq
35
17
  end
36
- end
37
18
 
38
- def serializable_names
39
- serializable_key_names + serializable_method_names
40
- end
19
+ except = [options.delete(:except)].flatten.compact
20
+ except << :_id
21
+ options[:except] = except
41
22
 
42
- def serializable_record
43
- returning(serializable_record = {}) do
44
- serializable_names.each { |name| serializable_record[name] = @record.send(name) }
45
- end
46
- end
23
+ # Direct rip from Rails 3 ActiveModel Serialization (#serializable_hash)
24
+ hash = begin
25
+ options[:only] = Array.wrap(options[:only]).map { |n| n.to_s }
26
+ options[:except] = Array.wrap(options[:except]).map { |n| n.to_s }
47
27
 
48
- def serialize
49
- # overwrite to implement
50
- end
28
+ attribute_names = attributes.keys.sort
29
+ if options[:only].any?
30
+ attribute_names &= options[:only]
31
+ elsif options[:except].any?
32
+ attribute_names -= options[:except]
33
+ end
51
34
 
52
- def to_s(&block)
53
- serialize(&block)
54
- end
55
- end
56
-
57
- module Json
58
- def self.included(base)
59
- base.cattr_accessor :include_root_in_json, :instance_writer => false
60
- base.extend ClassMethods
61
- end
35
+ method_names = Array.wrap(options[:methods]).inject([]) do |methods, name|
36
+ methods << name if respond_to?(name.to_s)
37
+ methods
38
+ end
62
39
 
63
- module ClassMethods
64
- def json_class_name
65
- @json_class_name ||= name.demodulize.underscore.inspect
40
+ (attribute_names + method_names).inject({}) { |hash, name|
41
+ hash[name] = send(name)
42
+ hash
43
+ }
66
44
  end
67
- end
68
-
69
- def to_json(options={})
70
- apply_to_json_defaults(options)
71
-
72
- if include_root_in_json
73
- "{#{self.class.json_class_name}: #{JsonSerializer.new(self, options).to_s}}"
74
- else
75
- JsonSerializer.new(self, options).to_s
45
+ # End rip
46
+
47
+ options.delete(:only) if options[:only].nil? or options[:only].empty?
48
+
49
+ hash.each do |key, value|
50
+ if value.is_a?(Array)
51
+ hash[key] = value.map do |item|
52
+ item.respond_to?(:as_json) ? item.as_json(options) : item
53
+ end
54
+ elsif value.is_a? Mongo::ObjectID
55
+ hash[key] = value.to_s
56
+ elsif value.respond_to?(:as_json)
57
+ hash[key] = value.as_json(options)
58
+ end
76
59
  end
60
+
61
+ # Replicate Rails 3 naming - and also bin anytihng after : for use in our dynamic classes from unit tests
62
+ hash = { ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).gsub(/:.*/,'') => hash } if include_root_in_json
63
+ hash
77
64
  end
65
+ end
78
66
 
67
+ module ClassMethods
79
68
  def from_json(json)
80
- self.attributes = ActiveSupport::JSON.decode(json)
81
- self
69
+ self.new(ActiveSupport::JSON.decode(json))
82
70
  end
83
-
84
- class JsonSerializer < Serializer
85
- def serialize
86
- serializable_record.to_json
87
- end
88
- end
89
-
90
- private
91
- def apply_to_json_defaults(options)
92
- unless options[:only]
93
- methods = [options.delete(:methods)].flatten.compact
94
- methods << :id
95
- options[:methods] = methods.uniq
96
- end
97
-
98
- except = [options.delete(:except)].flatten.compact
99
- except << :_id
100
- options[:except] = except
101
- end
102
71
  end
72
+
103
73
  end
104
74
  end
105
75
  end
@@ -0,0 +1,21 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Timestamps
4
+ module ClassMethods
5
+ def timestamps!
6
+ key :created_at, Time
7
+ key :updated_at, Time
8
+ class_eval { before_save :update_timestamps }
9
+ end
10
+ end
11
+
12
+ module InstanceMethods
13
+ def update_timestamps
14
+ now = Time.now.utc
15
+ self[:created_at] = now if new? && !created_at?
16
+ self[:updated_at] = now
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ module Userstamps
4
+ module ClassMethods
5
+ def userstamps!
6
+ key :creator_id, ObjectId
7
+ key :updater_id, ObjectId
8
+ belongs_to :creator, :class_name => 'User'
9
+ belongs_to :updater, :class_name => 'User'
10
+ end
11
+ end
12
+ end
13
+ end
14
+ end
@@ -93,7 +93,7 @@ module MongoMapper
93
93
  def to_order(field, direction=nil)
94
94
  direction ||= 'ASC'
95
95
  direction = direction.upcase == 'ASC' ? 1 : -1
96
- [field.to_s, direction]
96
+ [normalized_key(field).to_s, direction]
97
97
  end
98
98
 
99
99
  def normalized_key(field)
@@ -0,0 +1,3 @@
1
+ module MongoMapper
2
+ Version = '0.7.1'
3
+ end