mongo_mapper 0.13.0 → 0.15.1

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