mongo_mapper 0.13.1 → 0.15.2

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 (133) 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 +4 -0
  10. data/lib/mongo_mapper/connection.rb +16 -38
  11. data/lib/mongo_mapper/document.rb +2 -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/plugins/accessible.rb +13 -12
  18. data/lib/mongo_mapper/plugins/associations.rb +7 -6
  19. data/lib/mongo_mapper/plugins/associations/base.rb +23 -14
  20. data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +1 -1
  21. data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
  22. data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +18 -11
  23. data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -4
  24. data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +60 -29
  25. data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
  26. data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
  27. data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
  28. data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
  29. data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
  30. data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
  31. data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
  32. data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
  33. data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
  34. data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +12 -10
  35. data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
  36. data/lib/mongo_mapper/plugins/associations/proxy.rb +38 -27
  37. data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
  38. data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
  39. data/lib/mongo_mapper/plugins/counter_cache.rb +23 -4
  40. data/lib/mongo_mapper/plugins/dirty.rb +29 -37
  41. data/lib/mongo_mapper/plugins/document.rb +1 -1
  42. data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
  43. data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
  44. data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
  45. data/lib/mongo_mapper/plugins/embedded_document.rb +2 -2
  46. data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
  47. data/lib/mongo_mapper/plugins/indexes.rb +14 -7
  48. data/lib/mongo_mapper/plugins/keys.rb +164 -159
  49. data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
  50. data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
  51. data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
  52. data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
  53. data/lib/mongo_mapper/plugins/persistence.rb +13 -8
  54. data/lib/mongo_mapper/plugins/protected.rb +6 -5
  55. data/lib/mongo_mapper/plugins/querying.rb +85 -42
  56. data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
  57. data/lib/mongo_mapper/plugins/safe.rb +10 -4
  58. data/lib/mongo_mapper/plugins/scopes.rb +94 -7
  59. data/lib/mongo_mapper/plugins/stats.rb +1 -3
  60. data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
  61. data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
  62. data/lib/mongo_mapper/plugins/validations.rb +0 -0
  63. data/lib/mongo_mapper/railtie.rb +1 -0
  64. data/lib/mongo_mapper/utils.rb +2 -2
  65. data/lib/mongo_mapper/version.rb +1 -1
  66. data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
  67. data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
  68. data/spec/examples.txt +1728 -0
  69. data/spec/functional/accessible_spec.rb +19 -13
  70. data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
  71. data/spec/functional/associations/belongs_to_proxy_spec.rb +54 -20
  72. data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
  73. data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
  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 +31 -3
  82. data/spec/functional/associations/one_proxy_spec.rb +21 -11
  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 +89 -0
  88. data/spec/functional/dirty_spec.rb +84 -46
  89. data/spec/functional/dirty_with_callbacks_spec.rb +59 -0
  90. data/spec/functional/document_spec.rb +2 -5
  91. data/spec/functional/dumpable_spec.rb +1 -1
  92. data/spec/functional/embedded_document_spec.rb +17 -17
  93. data/spec/functional/identity_map_spec.rb +29 -16
  94. data/spec/functional/indexes_spec.rb +19 -18
  95. data/spec/functional/keys_spec.rb +55 -28
  96. data/spec/functional/logger_spec.rb +3 -3
  97. data/spec/functional/modifiers_spec.rb +81 -19
  98. data/spec/functional/partial_updates_spec.rb +577 -0
  99. data/spec/functional/protected_spec.rb +14 -14
  100. data/spec/functional/querying_spec.rb +77 -28
  101. data/spec/functional/safe_spec.rb +23 -27
  102. data/spec/functional/sci_spec.rb +9 -9
  103. data/spec/functional/scopes_spec.rb +323 -2
  104. data/spec/functional/static_keys_spec.rb +153 -0
  105. data/spec/functional/stats_spec.rb +28 -16
  106. data/spec/functional/strong_parameters_spec.rb +49 -0
  107. data/spec/functional/touch_spec.rb +1 -1
  108. data/spec/functional/validations_spec.rb +51 -57
  109. data/spec/quality_spec.rb +2 -2
  110. data/spec/spec_helper.rb +37 -9
  111. data/spec/support/matchers.rb +5 -14
  112. data/spec/unit/associations/base_spec.rb +12 -12
  113. data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
  114. data/spec/unit/associations/many_association_spec.rb +2 -2
  115. data/spec/unit/associations/one_association_spec.rb +2 -2
  116. data/spec/unit/associations/proxy_spec.rb +26 -20
  117. data/spec/unit/clone_spec.rb +1 -1
  118. data/spec/unit/document_spec.rb +8 -8
  119. data/spec/unit/dynamic_finder_spec.rb +8 -8
  120. data/spec/unit/embedded_document_spec.rb +18 -19
  121. data/spec/unit/extensions_spec.rb +17 -17
  122. data/spec/unit/identity_map_middleware_spec.rb +65 -96
  123. data/spec/unit/key_spec.rb +28 -26
  124. data/spec/unit/keys_spec.rb +20 -11
  125. data/spec/unit/mongo_mapper_spec.rb +38 -85
  126. data/spec/unit/serialization_spec.rb +1 -1
  127. data/spec/unit/time_zones_spec.rb +2 -2
  128. data/spec/unit/validations_spec.rb +46 -33
  129. metadata +56 -32
  130. data/README.rdoc +0 -59
  131. data/lib/mongo_mapper/connections/10gen.rb +0 -0
  132. data/lib/mongo_mapper/connections/moped.rb +0 -0
  133. 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
@@ -49,29 +49,48 @@ module MongoMapper
49
49
  raise InvalidCounterCacheError, "You must define an association with name `#{association_name}' on model #{self}"
50
50
  end
51
51
 
52
- association_class = association.klass
53
- key_names = association_class.keys.keys
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
54
58
 
55
- if !key_names.include?(field.to_s)
56
- raise InvalidCounterCacheError, "Missing `key #{field.to_sym.inspect}, Integer, :default => 0' on model #{association_class}"
59
+ if !key_names.include?(field.to_s)
60
+ _raise_when_missing_counter_cache_key(association_class, field)
61
+ end
57
62
  end
58
63
 
59
64
  after_create do
60
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
+
61
70
  obj.increment(field => 1)
62
71
  obj.write_attribute(field, obj.read_attribute(field) + 1)
63
72
  end
73
+
64
74
  true
65
75
  end
66
76
 
67
77
  after_destroy do
68
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
+
69
83
  obj.decrement(field => 1)
70
84
  obj.write_attribute(field, obj.read_attribute(field) - 1)
71
85
  end
86
+
72
87
  true
73
88
  end
74
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
75
94
  end
76
95
  end
77
96
  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 save_to_collection(*)
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.get(key.set(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,12 +41,12 @@ 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
 
48
48
  def _root_document
49
- @_root_document ||= _parent_document.try(:_root_document)
49
+ _parent_document.try(:_root_document)
50
50
  end
51
51
  end
52
52
  end
@@ -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,144 +123,142 @@ 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
+ if key.read_accessor?
148
+ accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
149
+ def #{key.name}
150
+ read_key(:#{key.name})
177
151
  end
178
- end
179
152
 
180
- accessors_module.module_eval accessors
181
- include accessors_module
153
+ def #{key.name}_before_type_cast
154
+ read_key_before_type_cast(:#{key.name})
155
+ end
156
+ end_eval
182
157
  end
183
158
 
184
- def create_key_in_descendants(*args)
185
- descendants.each { |descendant| descendant.key(*args) }
159
+ if key.write_accessor?
160
+ accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
161
+ def #{key.name}=(value)
162
+ write_key(:#{key.name}, value)
163
+ end
164
+ end_eval
186
165
  end
187
166
 
188
- def remove_key_in_descendants(name)
189
- descendants.each { |descendant| descendant.remove_key(name) }
167
+ if key.predicate_accessor?
168
+ accessors_module.module_eval(<<-end_eval, __FILE__, __LINE__+1)
169
+ def #{key.name}?
170
+ read_key(:#{key.name}).present?
171
+ end
172
+ end_eval
190
173
  end
191
174
 
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
175
+ if block_given?
176
+ accessors_module.module_eval do
177
+ yield
196
178
  end
197
179
  end
198
180
 
199
- def create_validations_for(key)
200
- attribute = key.name.to_sym
181
+ include accessors_module
182
+ end
201
183
 
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
184
+ def create_key_in_descendants(*args)
185
+ descendants.each { |descendant| descendant.key(*args) }
186
+ end
209
187
 
210
- if key.options[:unique]
211
- validates_uniqueness_of(attribute)
212
- end
188
+ def remove_key_in_descendants(name)
189
+ descendants.each { |descendant| descendant.remove_key(name) }
190
+ end
213
191
 
214
- if key.options[:numeric]
215
- number_options = key.type == Integer ? {:only_integer => true} : {}
216
- validates_numericality_of(attribute, number_options)
217
- end
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
196
+ end
197
+ end
218
198
 
219
- if key.options[:format]
220
- validates_format_of(attribute, :with => key.options[:format])
221
- end
199
+ def create_validations_for(key)
200
+ attribute = key.name.to_sym
222
201
 
223
- if key.options[:in]
224
- validates_inclusion_of(attribute, :in => key.options[:in])
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)
225
207
  end
208
+ end
226
209
 
227
- if key.options[:not_in]
228
- validates_exclusion_of(attribute, :in => key.options[:not_in])
229
- end
210
+ if key.options[:unique]
211
+ validates_uniqueness_of(attribute)
212
+ end
230
213
 
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)
241
- end
214
+ if key.options[:numeric]
215
+ number_options = key.type == Integer ? {:only_integer => true} : {}
216
+ validates_numericality_of(attribute, number_options)
242
217
  end
243
218
 
244
- def remove_validations_for(name)
245
- name = name.to_sym
246
- a_name = [name]
219
+ if key.options[:format]
220
+ validates_format_of(attribute, :with => key.options[:format])
221
+ end
247
222
 
248
- _validators.reject!{ |key, _| key == name }
249
- remove_validate_callbacks a_name
223
+ if key.options[:in]
224
+ validates_inclusion_of(attribute, :in => key.options[:in])
250
225
  end
251
226
 
252
- def remove_validate_callbacks(a_name)
253
- chain = _validate_callbacks.dup.reject do |callback|
254
- f = callback.raw_filter
255
- f.respond_to?(:attributes) && f.attributes == a_name
256
- end
257
- reset_callbacks(:validate)
258
- chain.each do |callback|
259
- set_callback 'validate', callback.raw_filter
227
+ if key.options[:not_in]
228
+ validates_exclusion_of(attribute, :in => key.options[:not_in])
229
+ end
230
+
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]
260
239
  end
240
+ validates_length_of(attribute, length_options)
261
241
  end
242
+ end
243
+
244
+ def remove_validations_for(name)
245
+ name = name.to_sym
246
+ a_name = [name]
247
+
248
+ _validators.reject!{ |key, _| key == name }
249
+ remove_validate_callbacks a_name
250
+ end
262
251
 
252
+ def remove_validate_callbacks(a_name)
253
+ chain = _validate_callbacks.dup.reject do |callback|
254
+ f = callback.raw_filter
255
+ f.respond_to?(:attributes) && f.attributes == a_name
256
+ end
257
+ reset_callbacks(:validate)
258
+ chain.each do |callback|
259
+ set_callback 'validate', callback.raw_filter
260
+ end
261
+ end
263
262
  end
264
263
 
265
264
  def initialize(attrs={})
@@ -294,8 +293,16 @@ module MongoMapper
294
293
  end
295
294
  end
296
295
 
296
+ # NOTE: We can't use alias_method here as we need the #attributes=
297
+ # superclass method to get called (for example:
298
+ # MongoMapper::Plugins::Accessible filters non-permitted parameters
299
+ # through `attributes=`
300
+ def assign_attributes(new_attributes)
301
+ self.attributes = new_attributes
302
+ end
303
+
297
304
  def to_mongo(include_abbreviatons = true)
298
- BSON::OrderedHash.new.tap do |attrs|
305
+ Hash.new.tap do |attrs|
299
306
  self.class.unaliased_keys.each do |name, key|
300
307
  value = self.read_key(key.name)
301
308
  if key.type == ObjectId || !value.nil?
@@ -372,8 +379,8 @@ module MongoMapper
372
379
  end
373
380
  end
374
381
 
375
- alias_method :[], :read_key
376
- alias_method :attribute, :read_key
382
+ def [](key_name); read_key(key_name); end
383
+ def attribute(key_name); read_key(key_name); end
377
384
 
378
385
  def []=(name, value)
379
386
  write_key(name, value)
@@ -391,7 +398,7 @@ module MongoMapper
391
398
  @embedded_keys ||= keys.values.select(&:embeddable?)
392
399
  end
393
400
 
394
- protected
401
+ protected
395
402
 
396
403
  def unalias_key(name)
397
404
  name = name.to_s
@@ -402,68 +409,66 @@ module MongoMapper
402
409
  end
403
410
  end
404
411
 
405
- private
412
+ private
406
413
 
407
- def init_ivars
408
- @__mm_keys = self.class.keys # Not dumpable
409
- @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
410
- @_dynamic_attributes = {} # Dumpable
411
- end
414
+ def init_ivars
415
+ @__mm_keys = self.class.keys # Not dumpable
416
+ @__mm_default_keys = @__mm_keys.values.select(&:default?) # Not dumpable
417
+ @_dynamic_attributes = {} # Dumpable
418
+ end
412
419
 
413
- def load_from_database(attrs, with_cast = false)
414
- return if attrs == nil || attrs.blank?
420
+ def load_from_database(attrs, with_cast = false)
421
+ return if attrs == nil || attrs.blank?
415
422
 
416
- attrs.each do |key, value|
417
- if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
418
- self.send(:"#{key}=", value)
419
- else
420
- internal_write_key key, value, with_cast
421
- end
423
+ attrs.each do |key, value|
424
+ if !@__mm_keys.key?(key) && respond_to?(:"#{key}=")
425
+ self.send(:"#{key}=", value)
426
+ else
427
+ internal_write_key key, value, with_cast
422
428
  end
423
429
  end
430
+ end
424
431
 
425
- def set_parent_document(key, value)
426
- if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
427
- value._parent_document = self
428
- end
432
+ def set_parent_document(key, value)
433
+ if key.type and value.instance_of?(key.type) && key.embeddable? && value.respond_to?(:_parent_document)
434
+ value._parent_document = self
429
435
  end
436
+ end
430
437
 
431
- # This exists to be patched over by plugins, while letting us still get to the undecorated
432
- # version of the method.
433
- def write_key(name, value)
434
- init_ivars unless @__mm_keys
435
- internal_write_key(name.to_s, value)
436
- end
438
+ # This exists to be patched over by plugins, while letting us still get to the undecorated
439
+ # version of the method.
440
+ def write_key(name, value)
441
+ init_ivars unless @__mm_keys
442
+ internal_write_key(name.to_s, value)
443
+ end
437
444
 
438
- def internal_write_key(name, value, cast = true)
439
- key = @__mm_keys[name] || dynamic_key(name)
440
- as_mongo = cast ? key.set(value) : value
441
- as_typecast = key.get(as_mongo)
442
- if key.ivar
443
- if key.embeddable?
444
- set_parent_document(key, value)
445
- set_parent_document(key, as_typecast)
446
- end
447
- instance_variable_set key.ivar, as_typecast
448
- else
449
- @_dynamic_attributes[key.name.to_sym] = as_typecast
445
+ def internal_write_key(name, value, cast = true)
446
+ key = @__mm_keys[name] || dynamic_key(name)
447
+ as_mongo = cast ? key.set(value) : value
448
+ as_typecast = key.get(as_mongo)
449
+ if key.ivar
450
+ if key.embeddable?
451
+ set_parent_document(key, value)
452
+ set_parent_document(key, as_typecast)
450
453
  end
451
- @attributes = nil
452
- value
454
+ instance_variable_set key.ivar, as_typecast
455
+ else
456
+ @_dynamic_attributes[key.name.to_sym] = as_typecast
453
457
  end
458
+ value
459
+ end
454
460
 
455
- def dynamic_key(name)
456
- self.class.key(name, :__dynamic => true)
457
- end
461
+ def dynamic_key(name)
462
+ self.class.key(name, :__dynamic => true)
463
+ end
458
464
 
459
- def initialize_default_values(except = {})
460
- @__mm_default_keys.each do |key|
461
- if !(except && except.key?(key.name))
462
- internal_write_key key.name, key.default_value, false
463
- end
465
+ def initialize_default_values(except = {})
466
+ @__mm_default_keys.each do |key|
467
+ if !(except && except.key?(key.name))
468
+ internal_write_key key.name, key.default_value, false
464
469
  end
465
470
  end
466
- #end private
471
+ end
467
472
  end
468
473
  end
469
474
  end