mongo_mapper 0.13.0.beta2 → 0.15.0

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