mongo_mapper 0.13.0.beta2 → 0.15.0

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 (132) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +64 -0
  4. data/examples/keys.rb +3 -3
  5. data/examples/modifiers/set.rb +2 -2
  6. data/examples/querying.rb +3 -3
  7. data/examples/safe.rb +2 -2
  8. data/examples/scopes.rb +1 -1
  9. data/lib/mongo_mapper.rb +5 -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 +14 -4
  19. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  20. data/lib/mongo_mapper/plugins/associations/base.rb +18 -13
  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 +24 -23
  26. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  27. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  28. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  29. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  30. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  31. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +17 -14
  32. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  33. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +9 -9
  34. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  35. data/lib/mongo_mapper/plugins/associations/proxy.rb +31 -28
  36. data/lib/mongo_mapper/plugins/callbacks.rb +14 -1
  37. data/lib/mongo_mapper/plugins/counter_cache.rb +97 -0
  38. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  39. data/lib/mongo_mapper/plugins/document.rb +1 -1
  40. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  41. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  42. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  43. data/lib/mongo_mapper/plugins/embedded_document.rb +1 -1
  44. data/lib/mongo_mapper/plugins/identity_map.rb +1 -1
  45. data/lib/mongo_mapper/plugins/indexes.rb +37 -2
  46. data/lib/mongo_mapper/plugins/keys.rb +202 -142
  47. data/lib/mongo_mapper/plugins/keys/key.rb +22 -13
  48. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  49. data/lib/mongo_mapper/plugins/modifiers.rb +59 -28
  50. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  51. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  52. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  53. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  54. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +32 -9
  55. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  56. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  57. data/lib/mongo_mapper/plugins/sci.rb +4 -1
  58. data/lib/mongo_mapper/plugins/scopes.rb +78 -7
  59. data/lib/mongo_mapper/plugins/stats.rb +17 -0
  60. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  61. data/lib/mongo_mapper/plugins/touch.rb +1 -1
  62. data/lib/mongo_mapper/plugins/validations.rb +7 -2
  63. data/lib/mongo_mapper/railtie.rb +20 -0
  64. data/lib/mongo_mapper/railtie/database.rake +1 -1
  65. data/lib/mongo_mapper/utils.rb +2 -2
  66. data/lib/mongo_mapper/version.rb +1 -1
  67. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  68. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  69. data/spec/examples.txt +1643 -0
  70. data/spec/functional/accessible_spec.rb +13 -13
  71. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  72. data/spec/functional/associations/belongs_to_proxy_spec.rb +18 -19
  73. data/spec/functional/associations/in_array_proxy_spec.rb +10 -10
  74. data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
  75. data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
  76. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
  77. data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
  78. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
  79. data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
  80. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
  81. data/spec/functional/associations/one_embedded_proxy_spec.rb +3 -3
  82. data/spec/functional/associations/one_proxy_spec.rb +10 -10
  83. data/spec/functional/associations_spec.rb +3 -3
  84. data/spec/functional/binary_spec.rb +2 -2
  85. data/spec/functional/caching_spec.rb +8 -15
  86. data/spec/functional/callbacks_spec.rb +89 -2
  87. data/spec/functional/counter_cache_spec.rb +235 -0
  88. data/spec/functional/dirty_spec.rb +63 -46
  89. data/spec/functional/document_spec.rb +30 -2
  90. data/spec/functional/dumpable_spec.rb +1 -1
  91. data/spec/functional/embedded_document_spec.rb +18 -18
  92. data/spec/functional/identity_map_spec.rb +27 -14
  93. data/spec/functional/indexes_spec.rb +44 -19
  94. data/spec/functional/keys_spec.rb +117 -15
  95. data/spec/functional/logger_spec.rb +3 -3
  96. data/spec/functional/modifiers_spec.rb +67 -19
  97. data/spec/functional/partial_updates_spec.rb +577 -0
  98. data/spec/functional/protected_spec.rb +14 -14
  99. data/spec/functional/querying_spec.rb +55 -28
  100. data/spec/functional/safe_spec.rb +23 -27
  101. data/spec/functional/sci_spec.rb +49 -14
  102. data/spec/functional/scopes_spec.rb +235 -2
  103. data/spec/functional/static_keys_spec.rb +153 -0
  104. data/spec/functional/stats_spec.rb +86 -0
  105. data/spec/functional/touch_spec.rb +6 -6
  106. data/spec/functional/validations_spec.rb +51 -57
  107. data/spec/quality_spec.rb +51 -0
  108. data/spec/spec_helper.rb +37 -9
  109. data/spec/support/matchers.rb +5 -14
  110. data/spec/unit/associations/base_spec.rb +12 -12
  111. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  112. data/spec/unit/associations/many_association_spec.rb +2 -2
  113. data/spec/unit/associations/one_association_spec.rb +2 -2
  114. data/spec/unit/associations/proxy_spec.rb +19 -16
  115. data/spec/unit/clone_spec.rb +1 -1
  116. data/spec/unit/document_spec.rb +8 -8
  117. data/spec/unit/dynamic_finder_spec.rb +8 -8
  118. data/spec/unit/embedded_document_spec.rb +18 -19
  119. data/spec/unit/extensions_spec.rb +41 -17
  120. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  121. data/spec/unit/inspect_spec.rb +4 -4
  122. data/spec/unit/key_spec.rb +28 -26
  123. data/spec/unit/keys_spec.rb +10 -10
  124. data/spec/unit/model_generator_spec.rb +2 -4
  125. data/spec/unit/mongo_mapper_spec.rb +38 -85
  126. data/spec/unit/rails_spec.rb +5 -0
  127. data/spec/unit/serialization_spec.rb +1 -1
  128. data/spec/unit/time_zones_spec.rb +2 -2
  129. data/spec/unit/validations_spec.rb +28 -15
  130. metadata +188 -161
  131. data/README.rdoc +0 -55
  132. data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -42,11 +42,21 @@ module MongoMapper
42
42
  self.class.accessible_attributes?
43
43
  end
44
44
 
45
- protected
46
- def filter_inaccessible_attrs(attrs)
47
- return attrs if !accessible_attributes? || attrs.blank?
48
- attrs.dup.delete_if { |key, val| !accessible_attributes.include?(key.to_sym) }
45
+ protected
46
+
47
+ def filter_inaccessible_attrs(attrs)
48
+ return attrs if !accessible_attributes? || attrs.blank?
49
+ attrs.dup.delete_if { |key, val| attribute_inaccessible?(key.to_sym) }
50
+ end
51
+
52
+ def attribute_inaccessible?(attribute)
53
+ unless accessible_attributes.include?(attribute)
54
+ message = "Can't mass-assign protected attribute: #{attribute}"
55
+ MongoMapper.logger ? MongoMapper.logger.warn(message) : puts(message)
56
+
57
+ return true
49
58
  end
59
+ end
50
60
  end
51
61
  end
52
62
  end
@@ -56,12 +56,13 @@ module MongoMapper
56
56
  end
57
57
  end
58
58
 
59
- private
60
- def create_association(association)
61
- @embedded_associations = nil
62
- associations[association.name] = association
63
- association.setup(self)
64
- end
59
+ private
60
+
61
+ def create_association(association)
62
+ @embedded_associations = nil
63
+ associations[association.name] = association
64
+ association.setup(self)
65
+ end
65
66
  end
66
67
 
67
68
  def associations
@@ -6,7 +6,7 @@ module MongoMapper
6
6
  attr_reader :name, :options, :query_options
7
7
 
8
8
  # Options that should not be considered MongoDB query options/criteria
9
- AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic, :autosave, :touch]
9
+ AssociationOptions = [:as, :class, :class_name, :dependent, :extend, :foreign_key, :in, :polymorphic, :autosave, :touch, :counter_cache]
10
10
 
11
11
  def initialize(name, options={}, &extension)
12
12
  @name, @options, @query_options, @original_options = name.to_sym, {}, {}, options
@@ -43,6 +43,10 @@ module MongoMapper
43
43
  !!@options[:touch]
44
44
  end
45
45
 
46
+ def counter_cache?
47
+ !!@options[:counter_cache]
48
+ end
49
+
46
50
  def type_key_name
47
51
  "_type"
48
52
  end
@@ -72,22 +76,23 @@ module MongoMapper
72
76
  def setup(model)
73
77
  end
74
78
 
75
- private
76
- def separate_options_and_conditions
77
- @original_options.each_pair do |key, value|
78
- if AssociationOptions.include?(key)
79
- @options[key] = value
80
- else
81
- @query_options[key] = value
82
- end
79
+ private
80
+
81
+ def separate_options_and_conditions
82
+ @original_options.each_pair do |key, value|
83
+ if AssociationOptions.include?(key)
84
+ @options[key] = value
85
+ else
86
+ @query_options[key] = value
83
87
  end
84
88
  end
89
+ end
85
90
 
86
- def modularized_extensions(*extensions)
87
- extensions.flatten.compact.map do |extension|
88
- Proc === extension ? Module.new(&extension) : extension
89
- end
91
+ def modularized_extensions(*extensions)
92
+ extensions.flatten.compact.map do |extension|
93
+ Proc === extension ? Module.new(&extension) : extension
90
94
  end
95
+ end
91
96
  end
92
97
  end
93
98
  end
@@ -20,6 +20,7 @@ module MongoMapper
20
20
  model.key type_key_name, String unless model.key?(type_key_name) if polymorphic?
21
21
  super
22
22
  add_touch_callbacks if touch?
23
+ add_counter_cache if counter_cache?
23
24
  end
24
25
 
25
26
  def autosave?
@@ -28,7 +29,7 @@ module MongoMapper
28
29
 
29
30
  def add_touch_callbacks
30
31
  name = self.name
31
- method_name = "belongs_to_touch_after_save_or_destroy_for_#{name}"
32
+ method_name = :"belongs_to_touch_after_save_or_destroy_for_#{name}"
32
33
  touch = options.fetch(:touch)
33
34
 
34
35
  @model.send(:define_method, method_name) do
@@ -46,7 +47,15 @@ module MongoMapper
46
47
  @model.after_save(method_name)
47
48
  @model.after_touch(method_name)
48
49
  @model.after_destroy(method_name)
50
+ end
51
+
52
+ def add_counter_cache
53
+ options = {}
54
+ if @options[:counter_cache] && @options[:counter_cache] != true
55
+ options[:field] = @options[:counter_cache]
56
+ end
49
57
 
58
+ @model.counter_cache name, options
50
59
  end
51
60
  end
52
61
  end
@@ -19,15 +19,16 @@ module MongoMapper
19
19
  @target
20
20
  end
21
21
 
22
- protected
23
- def find_target
24
- return nil if association_class.nil? || proxy_owner[association.foreign_key].nil?
25
- association_class.find_by_id(proxy_owner[association.foreign_key])
26
- end
22
+ protected
27
23
 
28
- def association_class
29
- proxy_owner[association.type_key_name] ? proxy_owner[association.type_key_name].constantize : nil
30
- end
24
+ def find_target
25
+ return nil if association_class.nil? || proxy_owner[association.foreign_key].nil?
26
+ association_class.find_by_id(proxy_owner[association.foreign_key])
27
+ end
28
+
29
+ def association_class
30
+ proxy_owner[association.type_key_name] ? proxy_owner[association.type_key_name].constantize : nil
31
+ end
31
32
  end
32
33
  end
33
34
  end
@@ -34,18 +34,19 @@ module MongoMapper
34
34
  @target.save(options) if @target
35
35
  end
36
36
 
37
- protected
38
- def find_target
39
- return nil if proxy_owner[association.foreign_key].nil?
40
- klass.find_by_id(proxy_owner[association.foreign_key])
41
- end
37
+ protected
42
38
 
43
- def instantiate_target(instantiator, attrs={}, &block)
44
- @target = klass.send(instantiator, attrs, &block)
45
- proxy_owner[association.foreign_key] = @target.id
46
- loaded
47
- @target
48
- end
39
+ def find_target
40
+ return nil if proxy_owner[association.foreign_key].nil?
41
+ klass.find_by_id(proxy_owner[association.foreign_key])
42
+ end
43
+
44
+ def instantiate_target(instantiator, attrs={}, &block)
45
+ @target = klass.send(instantiator, attrs, &block)
46
+ proxy_owner[association.foreign_key] = @target.id
47
+ loaded
48
+ @target
49
+ end
49
50
  end
50
51
  end
51
52
  end
@@ -34,10 +34,10 @@ module MongoMapper
34
34
  @target.each { |doc| doc.persist(options) } if @target
35
35
  end
36
36
 
37
- private
38
- def assign_references(*docs)
39
- docs.each { |doc| doc._parent_document = proxy_owner }
40
- end
37
+ private
38
+ def assign_references(*docs)
39
+ docs.each { |doc| doc._parent_document = proxy_owner }
40
+ end
41
41
  end
42
42
  end
43
43
  end
@@ -99,34 +99,35 @@ module MongoMapper
99
99
  reset
100
100
  end
101
101
 
102
- private
103
- def query(options={})
104
- klass.
105
- query(association.query_options).
106
- amend(options).
107
- amend(criteria)
108
- end
102
+ private
109
103
 
110
- def criteria
111
- {:_id => ids}
112
- end
104
+ def query(options={})
105
+ klass.
106
+ query(association.query_options).
107
+ amend(options).
108
+ amend(criteria)
109
+ end
113
110
 
114
- def scoped_ids(args)
115
- valid = args.flatten.select do |id|
116
- id = ObjectId.to_mongo(id) if klass.using_object_id?
117
- ids.include?(id)
118
- end
119
- valid.empty? ? nil : valid
120
- end
111
+ def criteria
112
+ {:_id => ids}
113
+ end
121
114
 
122
- def find_target
123
- return [] if ids.blank?
124
- all
115
+ def scoped_ids(args)
116
+ valid = args.flatten.select do |id|
117
+ id = ObjectId.to_mongo(id) if klass.using_object_id?
118
+ ids.include?(id)
125
119
  end
120
+ valid.empty? ? nil : valid
121
+ end
126
122
 
127
- def ids
128
- proxy_owner[options[:in]]
129
- end
123
+ def find_target
124
+ return [] if ids.blank?
125
+ all
126
+ end
127
+
128
+ def ids
129
+ proxy_owner[options[:in]]
130
+ end
130
131
  end
131
132
  end
132
133
  end
@@ -3,25 +3,27 @@ module MongoMapper
3
3
  module Plugins
4
4
  module Associations
5
5
  class ManyDocumentsAsProxy < ManyDocumentsProxy
6
- protected
7
- def criteria
8
- {type_key_name => proxy_owner.class.name, id_key_name => proxy_owner.id}
9
- end
6
+ protected
10
7
 
11
- def apply_scope(doc)
12
- ensure_owner_saved
13
- criteria.each { |key, value| doc[key] = value }
14
- doc
15
- end
8
+ def criteria
9
+ {type_key_name => proxy_owner.class.name, id_key_name => proxy_owner.id}
10
+ end
16
11
 
17
- private
18
- def type_key_name
19
- "#{options[:as]}_type"
20
- end
12
+ def apply_scope(doc)
13
+ ensure_owner_saved
14
+ criteria.each { |key, value| doc[key] = value }
15
+ doc
16
+ end
21
17
 
22
- def id_key_name
23
- "#{options[:as]}_id"
24
- end
18
+ private
19
+
20
+ def type_key_name
21
+ "#{options[:as]}_type"
22
+ end
23
+
24
+ def id_key_name
25
+ "#{options[:as]}_id"
26
+ end
25
27
  end
26
28
  end
27
29
  end
@@ -80,63 +80,70 @@ module MongoMapper
80
80
  load_target.each(&block)
81
81
  end
82
82
 
83
- protected
84
- def query(options={})
85
- klass.
86
- query(association.query_options).
87
- amend(options).amend(criteria)
88
- end
83
+ protected
89
84
 
90
- def method_missing(method, *args, &block)
91
- if klass.respond_to?(method)
92
- result = klass.send(method, *args, &block)
93
- case result
94
- when Plucky::Query
95
- query.merge result
96
-
97
- # If we got a single record of this classas a result, return it
98
- when klass
99
- result
100
-
101
- # If we got an array of this class as a result, return it
102
- when Array
103
- if result[0].is_a? klass
104
- result
105
- else
106
- super
107
- end
108
- else
109
- super
110
- end
111
- else
112
- super
113
- end
114
- end
85
+ def query(options={})
86
+ klass.
87
+ query(association.query_options).
88
+ amend(options).
89
+ amend(criteria)
90
+ end
115
91
 
116
- def criteria
117
- {self.foreign_key => proxy_owner.id}
118
- end
92
+ def criteria
93
+ {self.foreign_key => proxy_owner.id}
94
+ end
119
95
 
120
- def find_target
121
- all
122
- end
96
+ def find_target
97
+ all
98
+ end
123
99
 
124
- def ensure_owner_saved
125
- proxy_owner.save unless proxy_owner.persisted?
126
- end
100
+ def ensure_owner_saved
101
+ proxy_owner.save unless proxy_owner.persisted?
102
+ end
127
103
 
128
- def prepare(doc)
129
- klass === doc ? apply_scope(doc) : build(doc)
130
- end
104
+ def prepare(doc)
105
+ klass === doc ? apply_scope(doc) : build(doc)
106
+ end
131
107
 
132
- def apply_scope(doc)
133
- criteria.each { |key, value| doc[key] = value }
134
- doc
108
+ def apply_scope(doc)
109
+ criteria.each { |key, value| doc[key] = value }
110
+ doc
111
+ end
112
+
113
+ def foreign_key
114
+ options[:foreign_key] || proxy_owner.class.name.foreign_key
115
+ end
116
+
117
+ private
118
+
119
+ def method_missing(method, *args, &block)
120
+ return super unless klass.respond_to?(method)
121
+
122
+ result = nil
123
+
124
+ query.with_scope(query.criteria_hash) do
125
+ result = klass.send(method, *args, &block)
135
126
  end
136
127
 
137
- def foreign_key
138
- options[:foreign_key] || proxy_owner.class.name.foreign_key
128
+ case result
129
+ when Plucky::Query
130
+ query.merge result
131
+
132
+ # If we got a single record of this class as a result, return it
133
+ when klass
134
+ result
135
+
136
+ # If we got an array of this class as a result, return it
137
+ when Array
138
+ if result[0].is_a? klass
139
+ result
140
+ else
141
+ super
142
+ end
143
+ else
144
+ super
139
145
  end
146
+ end
140
147
  end
141
148
  end
142
149
  end
@@ -10,22 +10,23 @@ module MongoMapper
10
10
  reset
11
11
  end
12
12
 
13
- protected
14
- def find_target
15
- (@_values || []).map do |hash|
16
- child = polymorphic_class(hash).load(hash, true)
17
- assign_references(child)
18
- child
19
- end
13
+ protected
14
+
15
+ def find_target
16
+ (@_values || []).map do |hash|
17
+ child = polymorphic_class(hash).load(hash, true)
18
+ assign_references(child)
19
+ child
20
20
  end
21
+ end
21
22
 
22
- def polymorphic_class(doc)
23
- if class_name = doc[association.type_key_name]
24
- class_name.constantize
25
- else
26
- klass
27
- end
23
+ def polymorphic_class(doc)
24
+ if class_name = doc[association.type_key_name]
25
+ class_name.constantize
26
+ else
27
+ klass
28
28
  end
29
+ end
29
30
  end
30
31
  end
31
32
  end