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.
- 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
|