mongo_mapper 0.13.0 → 0.15.1

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 (137) hide show
  1. checksums.yaml +5 -5
  2. data/LICENSE +1 -1
  3. data/README.md +61 -0
  4. data/examples/keys.rb +1 -1
  5. data/examples/modifiers/set.rb +1 -1
  6. data/examples/querying.rb +1 -1
  7. data/examples/safe.rb +2 -2
  8. data/examples/scopes.rb +1 -1
  9. data/lib/mongo_mapper.rb +7 -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 +15 -5
  19. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  20. data/lib/mongo_mapper/plugins/associations/base.rb +27 -14
  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 +60 -29
  26. data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
  27. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
  28. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  29. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  30. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  31. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  32. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  33. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
  34. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  35. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +9 -9
  36. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  37. data/lib/mongo_mapper/plugins/associations/proxy.rb +36 -29
  38. data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
  39. data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
  40. data/lib/mongo_mapper/plugins/counter_cache.rb +97 -0
  41. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  42. data/lib/mongo_mapper/plugins/document.rb +1 -1
  43. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  44. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  45. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  46. data/lib/mongo_mapper/plugins/embedded_document.rb +1 -1
  47. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  48. data/lib/mongo_mapper/plugins/indexes.rb +14 -7
  49. data/lib/mongo_mapper/plugins/keys.rb +170 -151
  50. data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
  51. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  52. data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
  53. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  54. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  55. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  56. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  57. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
  58. data/lib/mongo_mapper/plugins/rails.rb +1 -0
  59. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  60. data/lib/mongo_mapper/plugins/sci.rb +0 -0
  61. data/lib/mongo_mapper/plugins/scopes.rb +78 -7
  62. data/lib/mongo_mapper/plugins/stats.rb +17 -0
  63. data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
  64. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  65. data/lib/mongo_mapper/plugins/validations.rb +1 -1
  66. data/lib/mongo_mapper/railtie.rb +4 -3
  67. data/lib/mongo_mapper/utils.rb +2 -2
  68. data/lib/mongo_mapper/version.rb +1 -1
  69. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  70. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  71. data/spec/examples.txt +1717 -0
  72. data/spec/functional/accessible_spec.rb +19 -13
  73. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  74. data/spec/functional/associations/belongs_to_proxy_spec.rb +36 -20
  75. data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
  76. data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
  77. data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
  78. data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
  79. data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
  80. data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
  81. data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
  82. data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
  83. data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
  84. data/spec/functional/associations/one_embedded_proxy_spec.rb +3 -3
  85. data/spec/functional/associations/one_proxy_spec.rb +10 -10
  86. data/spec/functional/associations_spec.rb +3 -3
  87. data/spec/functional/binary_spec.rb +2 -2
  88. data/spec/functional/caching_spec.rb +8 -15
  89. data/spec/functional/callbacks_spec.rb +89 -2
  90. data/spec/functional/counter_cache_spec.rb +235 -0
  91. data/spec/functional/dirty_spec.rb +63 -46
  92. data/spec/functional/document_spec.rb +30 -5
  93. data/spec/functional/dumpable_spec.rb +1 -1
  94. data/spec/functional/embedded_document_spec.rb +17 -17
  95. data/spec/functional/identity_map_spec.rb +29 -16
  96. data/spec/functional/indexes_spec.rb +19 -18
  97. data/spec/functional/keys_spec.rb +86 -28
  98. data/spec/functional/logger_spec.rb +3 -3
  99. data/spec/functional/modifiers_spec.rb +81 -19
  100. data/spec/functional/partial_updates_spec.rb +577 -0
  101. data/spec/functional/protected_spec.rb +14 -14
  102. data/spec/functional/querying_spec.rb +77 -28
  103. data/spec/functional/safe_spec.rb +23 -27
  104. data/spec/functional/sci_spec.rb +9 -9
  105. data/spec/functional/scopes_spec.rb +235 -2
  106. data/spec/functional/static_keys_spec.rb +153 -0
  107. data/spec/functional/stats_spec.rb +86 -0
  108. data/spec/functional/strong_parameters_spec.rb +49 -0
  109. data/spec/functional/touch_spec.rb +1 -1
  110. data/spec/functional/validations_spec.rb +51 -57
  111. data/spec/quality_spec.rb +51 -0
  112. data/spec/spec_helper.rb +37 -9
  113. data/spec/support/matchers.rb +5 -14
  114. data/spec/unit/associations/base_spec.rb +12 -12
  115. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  116. data/spec/unit/associations/many_association_spec.rb +2 -2
  117. data/spec/unit/associations/one_association_spec.rb +2 -2
  118. data/spec/unit/associations/proxy_spec.rb +19 -20
  119. data/spec/unit/clone_spec.rb +1 -1
  120. data/spec/unit/document_spec.rb +8 -8
  121. data/spec/unit/dynamic_finder_spec.rb +8 -8
  122. data/spec/unit/embedded_document_spec.rb +18 -19
  123. data/spec/unit/extensions_spec.rb +41 -17
  124. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  125. data/spec/unit/key_spec.rb +28 -26
  126. data/spec/unit/keys_spec.rb +20 -11
  127. data/spec/unit/model_generator_spec.rb +0 -0
  128. data/spec/unit/mongo_mapper_spec.rb +38 -85
  129. data/spec/unit/rails_spec.rb +5 -0
  130. data/spec/unit/serialization_spec.rb +1 -1
  131. data/spec/unit/time_zones_spec.rb +2 -2
  132. data/spec/unit/validations_spec.rb +46 -33
  133. metadata +66 -37
  134. data/README.rdoc +0 -59
  135. data/lib/mongo_mapper/connections/10gen.rb +0 -0
  136. data/lib/mongo_mapper/connections/moped.rb +0 -0
  137. data/lib/mongo_mapper/extensions/ordered_hash.rb +0 -23
@@ -5,10 +5,11 @@ module MongoMapper
5
5
  class SingleAssociation < Base
6
6
  def setup(model)
7
7
  @model = model
8
- model.associations_module.module_eval <<-end_eval
8
+
9
+ model.associations_module.module_eval(<<-end_eval, __FILE__, __LINE__ + 1)
9
10
  def #{name}
10
11
  proxy = get_proxy(associations[#{name.inspect}])
11
- proxy.nil? ? nil : proxy
12
+ proxy.nil? ? nil : proxy.read
12
13
  end
13
14
 
14
15
  def #{name}=(value)
@@ -20,7 +21,7 @@ module MongoMapper
20
21
  end
21
22
 
22
23
  proxy.replace(value)
23
- value
24
+ proxy.read
24
25
  end
25
26
 
26
27
  def #{name}?
@@ -43,4 +44,4 @@ module MongoMapper
43
44
  end
44
45
  end
45
46
  end
46
- end
47
+ end
@@ -4,6 +4,18 @@ module MongoMapper
4
4
  module Callbacks
5
5
  extend ActiveSupport::Concern
6
6
 
7
+ def initialize(*)
8
+ run_callbacks(:initialize) { super }
9
+ end
10
+
11
+ def initialize_from_database(*)
12
+ run_callbacks(:initialize) do
13
+ run_callbacks(:find) do
14
+ super
15
+ end
16
+ end
17
+ end
18
+
7
19
  def destroy
8
20
  run_callbacks(:destroy) { super }
9
21
  end
@@ -13,6 +25,7 @@ module MongoMapper
13
25
  end
14
26
 
15
27
  private
28
+
16
29
  def create_or_update(*)
17
30
  run_callbacks(:save) { super }
18
31
  end
@@ -0,0 +1,97 @@
1
+ module MongoMapper
2
+ module Plugins
3
+ # Counter Caching for MongoMapper::Document
4
+ #
5
+ # Examples:
6
+ #
7
+ # class Post
8
+ # belongs_to :user
9
+ # counter_cache :user
10
+ # end
11
+ #
12
+ # or:
13
+ #
14
+ # class Post
15
+ # belongs_to :user
16
+ # counter_cache :user, :custom_posts_count
17
+ # end
18
+ #
19
+ # Field names follow rails conventions, so counter_cache :user will increment the Integer field `posts_count' on User
20
+ #
21
+ # Alternatively, you can also use the more common ActiveRecord syntax:
22
+ #
23
+ # class Post
24
+ # belongs_to :user, :counter_cache => true
25
+ # end
26
+ #
27
+ # Or with an alternative field name:
28
+ #
29
+ # class Post
30
+ # belongs_to :user, :counter_cache => :custom_posts_count
31
+ # end
32
+ #
33
+ module CounterCache
34
+ class InvalidCounterCacheError < StandardError; end
35
+
36
+ extend ActiveSupport::Concern
37
+
38
+ module ClassMethods
39
+ def counter_cache(association_name, options = {})
40
+ options.symbolize_keys!
41
+
42
+ field = options[:field] ?
43
+ options[:field] :
44
+ "#{self.collection_name.gsub(/.*\./, '')}_count"
45
+
46
+ association = associations[association_name]
47
+
48
+ if !association
49
+ raise InvalidCounterCacheError, "You must define an association with name `#{association_name}' on model #{self}"
50
+ end
51
+
52
+ # make a define-time check to make sure the counter cache field is defined.
53
+ # note: this can only be done in non-polymorphic classes
54
+ # (since we may not know the class on the other side of the association)
55
+ if !association.polymorphic?
56
+ association_class = association.klass
57
+ key_names = association_class.keys.keys
58
+
59
+ if !key_names.include?(field.to_s)
60
+ _raise_when_missing_counter_cache_key(association_class, field)
61
+ end
62
+ end
63
+
64
+ after_create do
65
+ if obj = self.send(association_name)
66
+ if !obj.respond_to?(field)
67
+ self.class._raise_when_missing_counter_cache_key(obj.class, field)
68
+ end
69
+
70
+ obj.increment(field => 1)
71
+ obj.write_attribute(field, obj.read_attribute(field) + 1)
72
+ end
73
+
74
+ true
75
+ end
76
+
77
+ after_destroy do
78
+ if obj = self.send(association_name)
79
+ if !obj.respond_to?(field)
80
+ self.class._raise_when_missing_counter_cache_key(obj.class, field)
81
+ end
82
+
83
+ obj.decrement(field => 1)
84
+ obj.write_attribute(field, obj.read_attribute(field) - 1)
85
+ end
86
+
87
+ true
88
+ end
89
+ end
90
+
91
+ def _raise_when_missing_counter_cache_key(klass, field)
92
+ raise InvalidCounterCacheError, "Missing `key #{field.to_sym.inspect}, Integer, :default => 0' on model #{klass}"
93
+ end
94
+ end
95
+ end
96
+ end
97
+ end
@@ -3,58 +3,50 @@ module MongoMapper
3
3
  module Plugins
4
4
  module Dirty
5
5
  extend ActiveSupport::Concern
6
-
7
6
  include ::ActiveModel::Dirty
8
7
 
9
- def initialize(*)
10
- # never register initial id assignment as a change
11
- # Chaining super into tap breaks implicit block passing in Ruby 1.8
12
- doc = super
13
- doc.tap { changed_attributes.delete('_id') }
14
- end
15
-
16
- def save(*)
17
- clear_changes { super }
8
+ module ClassMethods
9
+ def create_accessors_for(key)
10
+ super.tap do
11
+ define_attribute_methods([key.name])
12
+ end
13
+ end
18
14
  end
19
15
 
20
- def reload(*)
21
- doc = super
22
- doc.tap { clear_changes }
16
+ def create_or_update(*)
17
+ super.tap do
18
+ changes_applied
19
+ end
23
20
  end
24
21
 
25
- def clear_changes
26
- previous = changes
27
- (block_given? ? yield : true).tap do |result|
28
- unless result == false #failed validation; nil is OK.
29
- @previously_changed = previous
30
- changed_attributes.clear
31
- end
22
+ def reload!
23
+ super.tap do
24
+ clear_changes_information
32
25
  end
33
26
  end
34
27
 
35
- protected
28
+ private
36
29
 
37
- # We don't call super here to avoid invoking #attributes, which builds a whole new hash per call.
38
- def attribute_method?(attr_name)
39
- keys.key?(attr_name) || !embedded_associations.detect {|a| a.name == attr_name }.nil?
40
- end
30
+ def write_key(key_name, value)
31
+ key_name = unalias_key(key_name)
41
32
 
42
- private
43
-
44
- def write_key(key, value)
45
- key = unalias_key(key)
46
- if !keys.key?(key)
33
+ if !keys.key?(key_name)
47
34
  super
48
35
  else
49
- attribute_will_change!(key) unless attribute_changed?(key)
50
- super.tap do
51
- changed_attributes.delete(key) unless attribute_value_changed?(key)
36
+ # find the MongoMapper::Plugins::Keys::Key
37
+ _, key = keys.detect { |n, v| n == key_name }
38
+
39
+ # typecast to the new value
40
+ old_value = read_key(key_name)
41
+ new_value = key.type.to_mongo(value)
42
+
43
+ # only mark changed if really changed value (after typecasting)
44
+ unless old_value == new_value
45
+ attribute_will_change!(key_name)
52
46
  end
53
- end
54
- end
55
47
 
56
- def attribute_value_changed?(key_name)
57
- changed_attributes[key_name] != read_key(key_name)
48
+ super
49
+ end
58
50
  end
59
51
  end
60
52
  end
@@ -19,7 +19,7 @@ module MongoMapper
19
19
  end
20
20
 
21
21
  def reload
22
- if doc = collection.find_one(:_id => id)
22
+ if doc = collection.find({:_id => id},{limit: -1}).first
23
23
  self.class.associations.each_value do |association|
24
24
  get_proxy(association).reset
25
25
  end
@@ -29,16 +29,17 @@ module MongoMapper
29
29
  end
30
30
  end
31
31
 
32
- protected
33
- def method_missing(method, *args, &block)
34
- finder = DynamicFinder.new(method)
35
-
36
- if finder.found?
37
- dynamic_find(finder, args)
38
- else
39
- super
40
- end
32
+ private
33
+
34
+ def method_missing(method, *args, &block)
35
+ finder = DynamicFinder.new(method)
36
+
37
+ if finder.found?
38
+ dynamic_find(finder, args)
39
+ else
40
+ super
41
41
  end
42
+ end
42
43
  end
43
44
  end
44
45
  end
@@ -20,25 +20,26 @@ module MongoMapper
20
20
  bang == true
21
21
  end
22
22
 
23
- protected
24
- def match
25
- case method.to_s
26
- when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
27
- @finder = :all if $1 == 'all_by'
28
- names = $2
29
- when /^find_by_([_a-zA-Z]\w*)\!$/
30
- @bang = true
31
- names = $1
32
- when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
33
- @instantiator = $1 == 'initialize' ? :new : :create
34
- names = $2
35
- else
36
- @finder = nil
37
- end
23
+ protected
38
24
 
39
- @attributes = names && names.split('_and_')
25
+ def match
26
+ case method.to_s
27
+ when /^find_(all_by|by)_([_a-zA-Z]\w*)$/
28
+ @finder = :all if $1 == 'all_by'
29
+ names = $2
30
+ when /^find_by_([_a-zA-Z]\w*)\!$/
31
+ @bang = true
32
+ names = $1
33
+ when /^find_or_(initialize|create)_by_([_a-zA-Z]\w*)$/
34
+ @instantiator = $1 == 'initialize' ? :new : :create
35
+ names = $2
36
+ else
37
+ @finder = nil
40
38
  end
39
+
40
+ @attributes = names && names.split('_and_')
41
+ end
41
42
  end
42
43
  end
43
44
  end
44
- end
45
+ end
@@ -8,7 +8,7 @@ module MongoMapper
8
8
  extend ::ActiveModel::Callbacks
9
9
 
10
10
  define_model_callbacks :save, :create, :update, :destroy, :only => [:before, :after]
11
- define_model_callbacks :touch, :only => [:after]
11
+ define_model_callbacks :initialize, :find, :touch, :only => [:after]
12
12
 
13
13
  proxy_callbacks(
14
14
  :before => [:save, :create, :update, :destroy],
@@ -44,6 +44,7 @@ module MongoMapper
44
44
  definition.each do |prefix, suffixes|
45
45
  suffixes.each do |suffix|
46
46
  callback = "%s_%s" % [prefix, suffix]
47
+
47
48
  class_eval <<-CALLBACK, __FILE__, __LINE__ + 1
48
49
  class << self
49
50
  alias_method :__original_#{callback}, :#{callback}
@@ -41,7 +41,7 @@ module MongoMapper
41
41
 
42
42
  def persist(options={})
43
43
  @_new = false
44
- clear_changes if respond_to?(:clear_changes)
44
+ changes_applied if respond_to?(:changes_applied)
45
45
  save_to_collection(options)
46
46
  end
47
47
 
@@ -69,7 +69,7 @@ module MongoMapper
69
69
  end
70
70
  end
71
71
 
72
- def load(attrs)
72
+ def load(attrs, with_cast = false)
73
73
  return super unless Thread.current[:mongo_mapper_identity_map_enabled]
74
74
  return nil unless attrs
75
75
  document = get_from_identity_map(attrs['_id'])
@@ -133,10 +133,12 @@ module PluckyMethods
133
133
  end
134
134
 
135
135
  def find_each(opts={})
136
+ return super if !block_given?
137
+
136
138
  query = clone.amend(opts)
137
139
  super(opts) do |doc|
138
140
  doc.remove_from_identity_map if doc && query.fields?
139
- yield doc if block_given?
141
+ yield doc
140
142
  end
141
143
  end
142
144
  end
@@ -6,31 +6,38 @@ module MongoMapper
6
6
 
7
7
  module ClassMethods
8
8
  def ensure_index(spec, options = {})
9
- collection.ensure_index dealias_options(spec), options
9
+ #TODO: should we emulate the mongo 1.x behaviour of caching attempts to create indexes?
10
+ collection.indexes.create_one dealias_options(spec), options
10
11
  end
11
12
 
12
13
  def create_index(spec, options = {})
13
- collection.create_index dealias_options(spec), options
14
+ collection.indexes.create_one dealias_options(spec), options
14
15
  end
15
16
 
16
17
  def drop_index(name)
17
- collection.drop_index name
18
+ collection.indexes.drop_one name
18
19
  end
19
20
 
20
21
  def drop_indexes
21
- collection.drop_indexes
22
+ collection.indexes.drop_all
22
23
  end
23
24
 
24
- private
25
+ private
25
26
 
26
27
  def dealias_options(options)
27
28
  case options
28
29
  when Symbol, String
29
- abbr(options)
30
+ {abbr(options) => 1}
30
31
  when Hash
31
32
  dealias_keys(options)
32
33
  when Array
33
- options.map {|o| dealias_options(o) }
34
+ if options.first.is_a?(Hash)
35
+ options.map {|o| dealias_options(o) }
36
+ elsif options.first.is_a?(Array) # [[:foo, 1], [:bar, 1]]
37
+ options.inject({}) {|acc, tuple| acc.merge(dealias_options(tuple))}
38
+ else
39
+ dealias_keys(Hash[*options])
40
+ end
34
41
  else
35
42
  options
36
43
  end
@@ -1,5 +1,6 @@
1
1
  # encoding: UTF-8
2
2
  require 'mongo_mapper/plugins/keys/key'
3
+ require 'mongo_mapper/plugins/keys/static'
3
4
 
4
5
  module MongoMapper
5
6
  module Plugins
@@ -57,7 +58,7 @@ module MongoMapper
57
58
  Key.new(*args).tap do |key|
58
59
  keys[key.name] = key
59
60
  keys[key.abbr] = key if key.abbr
60
- create_accessors_for(key) if key.valid_ruby_name?
61
+ create_accessors_for(key) if key.valid_ruby_name? && !key.reserved_name?
61
62
  create_key_in_descendants(*args)
62
63
  create_indexes_for(key)
63
64
  create_validations_for(key)
@@ -122,132 +123,144 @@ module MongoMapper
122
123
  end.allocate.initialize_from_database(attrs, with_cast)
123
124
  end
124
125
 
125
- private
126
- def key_accessors_module_defined?
127
- # :nocov:
128
- if IS_RUBY_1_9
129
- const_defined?('MongoMapperKeys')
130
- else
131
- const_defined?('MongoMapperKeys', false)
132
- end
133
- # :nocov:
134
- end
126
+ private
135
127
 
136
- def accessors_module
137
- if key_accessors_module_defined?
138
- const_get 'MongoMapperKeys'
139
- else
140
- const_set 'MongoMapperKeys', Module.new
141
- end
128
+ def key_accessors_module_defined?
129
+ # :nocov:
130
+ if IS_RUBY_1_9
131
+ const_defined?('MongoMapperKeys')
132
+ else
133
+ const_defined?('MongoMapperKeys', false)
142
134
  end
135
+ # :nocov:
136
+ end
143
137
 
144
- def create_accessors_for(key)
145
- accessors = ""
146
- if key.read_accessor?
147
- accessors << <<-end_eval
148
- def #{key.name}
149
- read_key(:#{key.name})
150
- end
151
-
152
- def #{key.name}_before_type_cast
153
- read_key_before_type_cast(:#{key.name})
154
- end
155
- end_eval
156
- end
157
-
158
- if key.write_accessor?
159
- accessors << <<-end_eval
160
- def #{key.name}=(value)
161
- write_key(:#{key.name}, value)
162
- end
163
- end_eval
164
- end
165
-
166
- if key.predicate_accessor?
167
- accessors << <<-end_eval
168
- def #{key.name}?
169
- read_key(:#{key.name}).present?
170
- end
171
- end_eval
172
- end
138
+ def accessors_module
139
+ if key_accessors_module_defined?
140
+ const_get 'MongoMapperKeys'
141
+ else
142
+ const_set 'MongoMapperKeys', Module.new
143
+ end
144
+ end
173
145
 
174
- if block_given?
175
- accessors_module.module_eval do
176
- yield
146
+ def create_accessors_for(key)
147
+ accessors = ""
148
+ if key.read_accessor?
149
+ accessors << <<-end_eval
150
+ def #{key.name}
151
+ read_key(:#{key.name})
177
152
  end
178
- end
179
153
 
180
- accessors_module.module_eval accessors
181
- include accessors_module
154
+ def #{key.name}_before_type_cast
155
+ read_key_before_type_cast(:#{key.name})
156
+ end
157
+ end_eval
182
158
  end
183
159
 
184
- def create_key_in_descendants(*args)
185
- descendants.each { |descendant| descendant.key(*args) }
160
+ if key.write_accessor?
161
+ accessors << <<-end_eval
162
+ def #{key.name}=(value)
163
+ write_key(:#{key.name}, value)
164
+ end
165
+ end_eval
186
166
  end
187
167
 
188
- def remove_key_in_descendants(name)
189
- descendants.each { |descendant| descendant.remove_key(name) }
168
+ if key.predicate_accessor?
169
+ accessors << <<-end_eval
170
+ def #{key.name}?
171
+ read_key(:#{key.name}).present?
172
+ end
173
+ end_eval
190
174
  end
191
175
 
192
- def create_indexes_for(key)
193
- if key.options[:index] && !key.embeddable?
194
- warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
195
- ensure_index key.name
176
+ if block_given?
177
+ accessors_module.module_eval do
178
+ yield
196
179
  end
197
180
  end
198
181
 
199
- def create_validations_for(key)
200
- attribute = key.name.to_sym
182
+ accessors_module.module_eval accessors
183
+ include accessors_module
184
+ end
201
185
 
202
- if key.options[:required]
203
- if key.type == Boolean
204
- validates_inclusion_of attribute, :in => [true, false]
205
- else
206
- validates_presence_of(attribute)
207
- end
208
- end
186
+ def create_key_in_descendants(*args)
187
+ descendants.each { |descendant| descendant.key(*args) }
188
+ end
209
189
 
210
- if key.options[:unique]
211
- validates_uniqueness_of(attribute)
212
- end
190
+ def remove_key_in_descendants(name)
191
+ descendants.each { |descendant| descendant.remove_key(name) }
192
+ end
213
193
 
214
- if key.options[:numeric]
215
- number_options = key.type == Integer ? {:only_integer => true} : {}
216
- validates_numericality_of(attribute, number_options)
217
- end
194
+ def create_indexes_for(key)
195
+ if key.options[:index] && !key.embeddable?
196
+ warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
197
+ ensure_index key.name
198
+ end
199
+ end
218
200
 
219
- if key.options[:format]
220
- validates_format_of(attribute, :with => key.options[:format])
221
- end
201
+ def create_validations_for(key)
202
+ attribute = key.name.to_sym
222
203
 
223
- if key.options[:in]
224
- validates_inclusion_of(attribute, :in => key.options[:in])
204
+ if key.options[:required]
205
+ if key.type == Boolean
206
+ validates_inclusion_of attribute, :in => [true, false]
207
+ else
208
+ validates_presence_of(attribute)
225
209
  end
210
+ end
226
211
 
227
- if key.options[:not_in]
228
- validates_exclusion_of(attribute, :in => key.options[:not_in])
229
- end
212
+ if key.options[:unique]
213
+ validates_uniqueness_of(attribute)
214
+ end
230
215
 
231
- if key.options[:length]
232
- length_options = case key.options[:length]
233
- when Integer
234
- {:minimum => 0, :maximum => key.options[:length]}
235
- when Range
236
- {:within => key.options[:length]}
237
- when Hash
238
- key.options[:length]
239
- end
240
- validates_length_of(attribute, length_options)
216
+ if key.options[:numeric]
217
+ number_options = key.type == Integer ? {:only_integer => true} : {}
218
+ validates_numericality_of(attribute, number_options)
219
+ end
220
+
221
+ if key.options[:format]
222
+ validates_format_of(attribute, :with => key.options[:format])
223
+ end
224
+
225
+ if key.options[:in]
226
+ validates_inclusion_of(attribute, :in => key.options[:in])
227
+ end
228
+
229
+ if key.options[:not_in]
230
+ validates_exclusion_of(attribute, :in => key.options[:not_in])
231
+ end
232
+
233
+ if key.options[:length]
234
+ length_options = case key.options[:length]
235
+ when Integer
236
+ {:minimum => 0, :maximum => key.options[:length]}
237
+ when Range
238
+ {:within => key.options[:length]}
239
+ when Hash
240
+ key.options[:length]
241
241
  end
242
+ validates_length_of(attribute, length_options)
242
243
  end
244
+ end
243
245
 
244
- def remove_validations_for(name)
245
- name = name.to_sym
246
- a_name = [name]
246
+ def remove_validations_for(name)
247
+ name = name.to_sym
248
+ a_name = [name]
247
249
 
248
- _validators.reject!{ |key, _| key == name }
249
- _validate_callbacks.reject! {|callback| callback.raw_filter.attributes == a_name }
250
+ _validators.reject!{ |key, _| key == name }
251
+ remove_validate_callbacks a_name
252
+ end
253
+
254
+ def remove_validate_callbacks(a_name)
255
+ chain = _validate_callbacks.dup.reject do |callback|
256
+ f = callback.raw_filter
257
+ f.respond_to?(:attributes) && f.attributes == a_name
258
+ end
259
+ reset_callbacks(:validate)
260
+ chain.each do |callback|
261
+ set_callback 'validate', callback.raw_filter
250
262
  end
263
+ end
251
264
  end
252
265
 
253
266
  def initialize(attrs={})
@@ -282,8 +295,16 @@ module MongoMapper
282
295
  end
283
296
  end
284
297
 
298
+ # NOTE: We can't use alias_method here as we need the #attributes=
299
+ # superclass method to get called (for example:
300
+ # MongoMapper::Plugins::Accessible filters non-permitted parameters
301
+ # through `attributes=`
302
+ def assign_attributes(new_attributes)
303
+ self.attributes = new_attributes
304
+ end
305
+
285
306
  def to_mongo(include_abbreviatons = true)
286
- BSON::OrderedHash.new.tap do |attrs|
307
+ Hash.new.tap do |attrs|
287
308
  self.class.unaliased_keys.each do |name, key|
288
309
  value = self.read_key(key.name)
289
310
  if key.type == ObjectId || !value.nil?
@@ -293,7 +314,7 @@ module MongoMapper
293
314
 
294
315
  embedded_associations.each do |association|
295
316
  if documents = instance_variable_get(association.ivar)
296
- if association.instance_of?(Associations::OneAssociation)
317
+ if association.is_a?(Associations::OneAssociation)
297
318
  attrs[association.name] = documents.to_mongo
298
319
  else
299
320
  attrs[association.name] = documents.map(&:to_mongo)
@@ -360,8 +381,8 @@ module MongoMapper
360
381
  end
361
382
  end
362
383
 
363
- alias_method :[], :read_key
364
- alias_method :attribute, :read_key
384
+ def [](key_name); read_key(key_name); end
385
+ def attribute(key_name); read_key(key_name); end
365
386
 
366
387
  def []=(name, value)
367
388
  write_key(name, value)
@@ -379,7 +400,7 @@ module MongoMapper
379
400
  @embedded_keys ||= keys.values.select(&:embeddable?)
380
401
  end
381
402
 
382
- protected
403
+ protected
383
404
 
384
405
  def unalias_key(name)
385
406
  name = name.to_s
@@ -390,68 +411,66 @@ module MongoMapper
390
411
  end
391
412
  end
392
413
 
393
- private
414
+ private
394
415
 
395
- def init_ivars
396
- @__mm_keys = self.class.keys # Not dumpable
397
- @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
398
- @_dynamic_attributes = {} # Dumpable
399
- end
416
+ def init_ivars
417
+ @__mm_keys = self.class.keys # Not dumpable
418
+ @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
419
+ @_dynamic_attributes = {} # Dumpable
420
+ end
400
421
 
401
- def load_from_database(attrs, with_cast = false)
402
- return if attrs == nil || attrs.blank?
422
+ def load_from_database(attrs, with_cast = false)
423
+ return if attrs == nil || attrs.blank?
403
424
 
404
- attrs.each do |key, value|
405
- if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
406
- self.send(:"#{key}=", value)
407
- else
408
- internal_write_key key, value, with_cast
409
- end
425
+ attrs.each do |key, value|
426
+ if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
427
+ self.send(:"#{key}=", value)
428
+ else
429
+ internal_write_key key, value, with_cast
410
430
  end
411
431
  end
432
+ end
412
433
 
413
- def set_parent_document(key, value)
414
- if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
415
- value._parent_document = self
416
- end
434
+ def set_parent_document(key, value)
435
+ if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
436
+ value._parent_document = self
417
437
  end
438
+ end
418
439
 
419
- # This exists to be patched over by plugins, while letting us still get to the undecorated
420
- # version of the method.
421
- def write_key(name, value)
422
- init_ivars unless @__mm_keys
423
- internal_write_key(name.to_s, value)
424
- end
440
+ # This exists to be patched over by plugins, while letting us still get to the undecorated
441
+ # version of the method.
442
+ def write_key(name, value)
443
+ init_ivars unless @__mm_keys
444
+ internal_write_key(name.to_s, value)
445
+ end
425
446
 
426
- def internal_write_key(name, value, cast = true)
427
- key = @__mm_keys[name] || dynamic_key(name)
428
- as_mongo = cast ? key.set(value) : value
429
- as_typecast = key.get(as_mongo)
430
- if key.ivar
431
- if key.embeddable?
432
- set_parent_document(key, value)
433
- set_parent_document(key, as_typecast)
434
- end
435
- instance_variable_set key.ivar, as_typecast
436
- else
437
- @_dynamic_attributes[key.name.to_sym] = as_typecast
447
+ def internal_write_key(name, value, cast = true)
448
+ key = @__mm_keys[name] || dynamic_key(name)
449
+ as_mongo = cast ? key.set(value) : value
450
+ as_typecast = key.get(as_mongo)
451
+ if key.ivar
452
+ if key.embeddable?
453
+ set_parent_document(key, value)
454
+ set_parent_document(key, as_typecast)
438
455
  end
439
- @attributes = nil
440
- value
456
+ instance_variable_set key.ivar, as_typecast
457
+ else
458
+ @_dynamic_attributes[key.name.to_sym] = as_typecast
441
459
  end
460
+ value
461
+ end
442
462
 
443
- def dynamic_key(name)
444
- self.class.key(name, :__dynamic => true)
445
- end
463
+ def dynamic_key(name)
464
+ self.class.key(name, :__dynamic => true)
465
+ end
446
466
 
447
- def initialize_default_values(except = {})
448
- @__mm_default_keys.each do |key|
449
- if !(except && except.key?(key.name))
450
- internal_write_key key.name, key.default_value, false
451
- end
467
+ def initialize_default_values(except = {})
468
+ @__mm_default_keys.each do |key|
469
+ if !(except && except.key?(key.name))
470
+ internal_write_key key.name, key.default_value, false
452
471
  end
453
472
  end
454
- #end private
473
+ end
455
474
  end
456
475
  end
457
476
  end