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.
- 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 +4 -0
- data/lib/mongo_mapper/connection.rb +16 -38
- data/lib/mongo_mapper/document.rb +2 -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/plugins/accessible.rb +13 -12
- data/lib/mongo_mapper/plugins/associations.rb +7 -6
- data/lib/mongo_mapper/plugins/associations/base.rb +23 -14
- data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +1 -1
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +9 -8
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +18 -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 +12 -10
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +27 -26
- data/lib/mongo_mapper/plugins/associations/proxy.rb +38 -27
- 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 +23 -4
- 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 +2 -2
- 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 +164 -159
- 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/safe.rb +10 -4
- data/lib/mongo_mapper/plugins/scopes.rb +94 -7
- data/lib/mongo_mapper/plugins/stats.rb +1 -3
- 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 +0 -0
- data/lib/mongo_mapper/railtie.rb +1 -0
- 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 +1728 -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 +54 -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 +31 -3
- data/spec/functional/associations/one_proxy_spec.rb +21 -11
- 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 +89 -0
- data/spec/functional/dirty_spec.rb +84 -46
- data/spec/functional/dirty_with_callbacks_spec.rb +59 -0
- data/spec/functional/document_spec.rb +2 -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 +55 -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 +323 -2
- data/spec/functional/static_keys_spec.rb +153 -0
- data/spec/functional/stats_spec.rb +28 -16
- 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 +2 -2
- 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 +26 -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 +17 -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/mongo_mapper_spec.rb +38 -85
- 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 +56 -32
- 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
|
|
@@ -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
|
-
|
|
53
|
-
|
|
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
|
-
|
|
56
|
-
|
|
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
|
-
|
|
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 save_to_collection(*)
|
|
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.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
|
-
|
|
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}
|
|
@@ -41,12 +41,12 @@ module MongoMapper
|
|
|
41
41
|
|
|
42
42
|
def persist(options={})
|
|
43
43
|
@_new = false
|
|
44
|
-
|
|
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
|
-
|
|
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
|
|
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,144 +123,142 @@ 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
|
+
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
|
-
|
|
181
|
-
|
|
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
|
-
|
|
185
|
-
|
|
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
|
-
|
|
189
|
-
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
|
|
200
|
-
|
|
181
|
+
include accessors_module
|
|
182
|
+
end
|
|
201
183
|
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
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
|
-
|
|
211
|
-
|
|
212
|
-
|
|
188
|
+
def remove_key_in_descendants(name)
|
|
189
|
+
descendants.each { |descendant| descendant.remove_key(name) }
|
|
190
|
+
end
|
|
213
191
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
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
|
-
|
|
220
|
-
|
|
221
|
-
end
|
|
199
|
+
def create_validations_for(key)
|
|
200
|
+
attribute = key.name.to_sym
|
|
222
201
|
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
|
|
228
|
-
|
|
229
|
-
|
|
210
|
+
if key.options[:unique]
|
|
211
|
+
validates_uniqueness_of(attribute)
|
|
212
|
+
end
|
|
230
213
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
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
|
-
|
|
245
|
-
|
|
246
|
-
|
|
219
|
+
if key.options[:format]
|
|
220
|
+
validates_format_of(attribute, :with => key.options[:format])
|
|
221
|
+
end
|
|
247
222
|
|
|
248
|
-
|
|
249
|
-
|
|
223
|
+
if key.options[:in]
|
|
224
|
+
validates_inclusion_of(attribute, :in => key.options[:in])
|
|
250
225
|
end
|
|
251
226
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
-
|
|
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
|
-
|
|
376
|
-
|
|
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
|
-
|
|
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
|
-
|
|
412
|
+
private
|
|
406
413
|
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
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
|
-
|
|
414
|
-
|
|
420
|
+
def load_from_database(attrs, with_cast = false)
|
|
421
|
+
return if attrs == nil || attrs.blank?
|
|
415
422
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
|
|
426
|
-
|
|
427
|
-
|
|
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
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
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
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
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
|
-
|
|
452
|
-
|
|
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
|
-
|
|
456
|
-
|
|
457
|
-
|
|
461
|
+
def dynamic_key(name)
|
|
462
|
+
self.class.key(name, :__dynamic => true)
|
|
463
|
+
end
|
|
458
464
|
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
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
|
-
|
|
471
|
+
end
|
|
467
472
|
end
|
|
468
473
|
end
|
|
469
474
|
end
|