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.
- checksums.yaml +5 -5
- data/LICENSE +1 -1
- data/README.md +61 -0
- data/examples/keys.rb +1 -1
- data/examples/modifiers/set.rb +1 -1
- data/examples/querying.rb +1 -1
- data/examples/safe.rb +2 -2
- data/examples/scopes.rb +1 -1
- data/lib/mongo_mapper.rb +7 -0
- data/lib/mongo_mapper/connection.rb +16 -37
- data/lib/mongo_mapper/document.rb +4 -0
- data/lib/mongo_mapper/extensions/array.rb +14 -6
- data/lib/mongo_mapper/extensions/hash.rb +15 -3
- data/lib/mongo_mapper/extensions/object.rb +4 -0
- data/lib/mongo_mapper/extensions/object_id.rb +5 -1
- data/lib/mongo_mapper/extensions/string.rb +13 -5
- data/lib/mongo_mapper/extensions/symbol.rb +18 -0
- data/lib/mongo_mapper/plugins/accessible.rb +15 -5
- data/lib/mongo_mapper/plugins/associations.rb +7 -6
- data/lib/mongo_mapper/plugins/associations/base.rb +27 -14
- data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +10 -1
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +12 -11
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +4 -4
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +60 -29
- data/lib/mongo_mapper/plugins/associations/in_foreign_array_proxy.rb +136 -0
- data/lib/mongo_mapper/plugins/associations/many_association.rb +4 -2
- data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +18 -16
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +55 -48
- data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +14 -13
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +7 -6
- data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +7 -5
- data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +14 -11
- data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +14 -13
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +9 -9
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
- data/lib/mongo_mapper/plugins/associations/proxy.rb +36 -29
- data/lib/mongo_mapper/plugins/associations/single_association.rb +5 -4
- data/lib/mongo_mapper/plugins/callbacks.rb +13 -0
- data/lib/mongo_mapper/plugins/counter_cache.rb +97 -0
- data/lib/mongo_mapper/plugins/dirty.rb +29 -37
- data/lib/mongo_mapper/plugins/document.rb +1 -1
- data/lib/mongo_mapper/plugins/dynamic_querying.rb +10 -9
- data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +18 -17
- data/lib/mongo_mapper/plugins/embedded_callbacks.rb +2 -1
- data/lib/mongo_mapper/plugins/embedded_document.rb +1 -1
- data/lib/mongo_mapper/plugins/identity_map.rb +4 -2
- data/lib/mongo_mapper/plugins/indexes.rb +14 -7
- data/lib/mongo_mapper/plugins/keys.rb +170 -151
- data/lib/mongo_mapper/plugins/keys/key.rb +27 -16
- data/lib/mongo_mapper/plugins/keys/static.rb +45 -0
- data/lib/mongo_mapper/plugins/modifiers.rb +64 -38
- data/lib/mongo_mapper/plugins/partial_updates.rb +86 -0
- data/lib/mongo_mapper/plugins/persistence.rb +13 -8
- data/lib/mongo_mapper/plugins/protected.rb +6 -5
- data/lib/mongo_mapper/plugins/querying.rb +85 -42
- data/lib/mongo_mapper/plugins/querying/decorated_plucky_query.rb +20 -15
- data/lib/mongo_mapper/plugins/rails.rb +1 -0
- data/lib/mongo_mapper/plugins/safe.rb +10 -4
- data/lib/mongo_mapper/plugins/sci.rb +0 -0
- data/lib/mongo_mapper/plugins/scopes.rb +78 -7
- data/lib/mongo_mapper/plugins/stats.rb +17 -0
- data/lib/mongo_mapper/plugins/strong_parameters.rb +26 -0
- data/lib/mongo_mapper/plugins/timestamps.rb +1 -0
- data/lib/mongo_mapper/plugins/validations.rb +1 -1
- data/lib/mongo_mapper/railtie.rb +4 -3
- data/lib/mongo_mapper/utils.rb +2 -2
- data/lib/mongo_mapper/version.rb +1 -1
- data/lib/rails/generators/mongo_mapper/config/config_generator.rb +12 -13
- data/lib/rails/generators/mongo_mapper/model/model_generator.rb +9 -9
- data/spec/examples.txt +1717 -0
- data/spec/functional/accessible_spec.rb +19 -13
- data/spec/functional/associations/belongs_to_polymorphic_proxy_spec.rb +13 -13
- data/spec/functional/associations/belongs_to_proxy_spec.rb +36 -20
- data/spec/functional/associations/in_array_proxy_spec.rb +145 -10
- data/spec/functional/associations/in_foreign_array_proxy_spec.rb +321 -0
- data/spec/functional/associations/many_documents_as_proxy_spec.rb +6 -6
- data/spec/functional/associations/many_documents_proxy_spec.rb +85 -14
- data/spec/functional/associations/many_embedded_polymorphic_proxy_spec.rb +13 -13
- data/spec/functional/associations/many_embedded_proxy_spec.rb +1 -1
- data/spec/functional/associations/many_polymorphic_proxy_spec.rb +4 -4
- data/spec/functional/associations/one_as_proxy_spec.rb +10 -10
- data/spec/functional/associations/one_embedded_polymorphic_proxy_spec.rb +9 -9
- data/spec/functional/associations/one_embedded_proxy_spec.rb +3 -3
- data/spec/functional/associations/one_proxy_spec.rb +10 -10
- data/spec/functional/associations_spec.rb +3 -3
- data/spec/functional/binary_spec.rb +2 -2
- data/spec/functional/caching_spec.rb +8 -15
- data/spec/functional/callbacks_spec.rb +89 -2
- data/spec/functional/counter_cache_spec.rb +235 -0
- data/spec/functional/dirty_spec.rb +63 -46
- data/spec/functional/document_spec.rb +30 -5
- data/spec/functional/dumpable_spec.rb +1 -1
- data/spec/functional/embedded_document_spec.rb +17 -17
- data/spec/functional/identity_map_spec.rb +29 -16
- data/spec/functional/indexes_spec.rb +19 -18
- data/spec/functional/keys_spec.rb +86 -28
- data/spec/functional/logger_spec.rb +3 -3
- data/spec/functional/modifiers_spec.rb +81 -19
- data/spec/functional/partial_updates_spec.rb +577 -0
- data/spec/functional/protected_spec.rb +14 -14
- data/spec/functional/querying_spec.rb +77 -28
- data/spec/functional/safe_spec.rb +23 -27
- data/spec/functional/sci_spec.rb +9 -9
- data/spec/functional/scopes_spec.rb +235 -2
- data/spec/functional/static_keys_spec.rb +153 -0
- data/spec/functional/stats_spec.rb +86 -0
- data/spec/functional/strong_parameters_spec.rb +49 -0
- data/spec/functional/touch_spec.rb +1 -1
- data/spec/functional/validations_spec.rb +51 -57
- data/spec/quality_spec.rb +51 -0
- data/spec/spec_helper.rb +37 -9
- data/spec/support/matchers.rb +5 -14
- data/spec/unit/associations/base_spec.rb +12 -12
- data/spec/unit/associations/belongs_to_association_spec.rb +2 -2
- data/spec/unit/associations/many_association_spec.rb +2 -2
- data/spec/unit/associations/one_association_spec.rb +2 -2
- data/spec/unit/associations/proxy_spec.rb +19 -20
- data/spec/unit/clone_spec.rb +1 -1
- data/spec/unit/document_spec.rb +8 -8
- data/spec/unit/dynamic_finder_spec.rb +8 -8
- data/spec/unit/embedded_document_spec.rb +18 -19
- data/spec/unit/extensions_spec.rb +41 -17
- data/spec/unit/identity_map_middleware_spec.rb +65 -96
- data/spec/unit/key_spec.rb +28 -26
- data/spec/unit/keys_spec.rb +20 -11
- data/spec/unit/model_generator_spec.rb +0 -0
- data/spec/unit/mongo_mapper_spec.rb +38 -85
- data/spec/unit/rails_spec.rb +5 -0
- data/spec/unit/serialization_spec.rb +1 -1
- data/spec/unit/time_zones_spec.rb +2 -2
- data/spec/unit/validations_spec.rb +46 -33
- metadata +66 -37
- data/README.rdoc +0 -59
- data/lib/mongo_mapper/connections/10gen.rb +0 -0
- data/lib/mongo_mapper/connections/moped.rb +0 -0
- 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
|
-
|
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
|
-
|
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
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
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
|
21
|
-
|
22
|
-
|
16
|
+
def create_or_update(*)
|
17
|
+
super.tap do
|
18
|
+
changes_applied
|
19
|
+
end
|
23
20
|
end
|
24
21
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
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
|
-
|
28
|
+
private
|
36
29
|
|
37
|
-
|
38
|
-
|
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
|
-
|
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
|
-
|
50
|
-
|
51
|
-
|
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
|
-
|
57
|
-
|
48
|
+
super
|
49
|
+
end
|
58
50
|
end
|
59
51
|
end
|
60
52
|
end
|
@@ -29,16 +29,17 @@ module MongoMapper
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
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
|
-
|
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}
|
@@ -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
|
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
|
-
|
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.
|
14
|
+
collection.indexes.create_one dealias_options(spec), options
|
14
15
|
end
|
15
16
|
|
16
17
|
def drop_index(name)
|
17
|
-
collection.
|
18
|
+
collection.indexes.drop_one name
|
18
19
|
end
|
19
20
|
|
20
21
|
def drop_indexes
|
21
|
-
collection.
|
22
|
+
collection.indexes.drop_all
|
22
23
|
end
|
23
24
|
|
24
|
-
|
25
|
+
private
|
25
26
|
|
26
27
|
def dealias_options(options)
|
27
28
|
case options
|
28
29
|
when Symbol, String
|
29
|
-
|
30
|
+
{abbr(options) => 1}
|
30
31
|
when Hash
|
31
32
|
dealias_keys(options)
|
32
33
|
when Array
|
33
|
-
options.
|
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
|
-
|
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
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
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
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
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
|
-
|
175
|
-
|
176
|
-
|
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
|
-
|
181
|
-
|
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
|
-
|
185
|
-
|
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
|
-
|
189
|
-
|
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
|
-
|
193
|
-
|
194
|
-
|
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
|
-
|
200
|
-
|
182
|
+
accessors_module.module_eval accessors
|
183
|
+
include accessors_module
|
184
|
+
end
|
201
185
|
|
202
|
-
|
203
|
-
|
204
|
-
|
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
|
-
|
211
|
-
|
212
|
-
|
190
|
+
def remove_key_in_descendants(name)
|
191
|
+
descendants.each { |descendant| descendant.remove_key(name) }
|
192
|
+
end
|
213
193
|
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
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
|
-
|
220
|
-
|
221
|
-
end
|
201
|
+
def create_validations_for(key)
|
202
|
+
attribute = key.name.to_sym
|
222
203
|
|
223
|
-
|
224
|
-
|
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
|
-
|
228
|
-
|
229
|
-
|
212
|
+
if key.options[:unique]
|
213
|
+
validates_uniqueness_of(attribute)
|
214
|
+
end
|
230
215
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
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
|
-
|
245
|
-
|
246
|
-
|
246
|
+
def remove_validations_for(name)
|
247
|
+
name = name.to_sym
|
248
|
+
a_name = [name]
|
247
249
|
|
248
|
-
|
249
|
-
|
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
|
-
|
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.
|
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
|
-
|
364
|
-
|
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
|
-
|
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
|
-
|
414
|
+
private
|
394
415
|
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
|
399
|
-
|
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
|
-
|
402
|
-
|
422
|
+
def load_from_database(attrs, with_cast = false)
|
423
|
+
return if attrs == nil || attrs.blank?
|
403
424
|
|
404
|
-
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
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
|
-
|
414
|
-
|
415
|
-
|
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
|
-
|
420
|
-
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
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
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
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
|
-
|
440
|
-
|
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
|
-
|
444
|
-
|
445
|
-
|
463
|
+
def dynamic_key(name)
|
464
|
+
self.class.key(name, :__dynamic => true)
|
465
|
+
end
|
446
466
|
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
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
|
-
|
473
|
+
end
|
455
474
|
end
|
456
475
|
end
|
457
476
|
end
|