lookout-mongo_mapper 0.11.3
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.
- data/LICENSE +20 -0
- data/README.rdoc +33 -0
- data/UPGRADES +26 -0
- data/bin/mmconsole +59 -0
- data/examples/attr_accessible.rb +22 -0
- data/examples/attr_protected.rb +22 -0
- data/examples/cache_key.rb +24 -0
- data/examples/custom_types.rb +24 -0
- data/examples/identity_map.rb +33 -0
- data/examples/identity_map/automatic.rb +2 -0
- data/examples/keys.rb +40 -0
- data/examples/modifiers/set.rb +25 -0
- data/examples/plugins.rb +38 -0
- data/examples/querying.rb +35 -0
- data/examples/safe.rb +43 -0
- data/examples/scopes.rb +52 -0
- data/examples/validating/embedded_docs.rb +29 -0
- data/lib/mongo_mapper.rb +94 -0
- data/lib/mongo_mapper/connection.rb +96 -0
- data/lib/mongo_mapper/document.rb +42 -0
- data/lib/mongo_mapper/embedded_document.rb +32 -0
- data/lib/mongo_mapper/exceptions.rb +30 -0
- data/lib/mongo_mapper/extensions/array.rb +19 -0
- data/lib/mongo_mapper/extensions/binary.rb +22 -0
- data/lib/mongo_mapper/extensions/boolean.rb +44 -0
- data/lib/mongo_mapper/extensions/date.rb +25 -0
- data/lib/mongo_mapper/extensions/float.rb +14 -0
- data/lib/mongo_mapper/extensions/hash.rb +14 -0
- data/lib/mongo_mapper/extensions/integer.rb +19 -0
- data/lib/mongo_mapper/extensions/kernel.rb +9 -0
- data/lib/mongo_mapper/extensions/nil_class.rb +18 -0
- data/lib/mongo_mapper/extensions/object.rb +26 -0
- data/lib/mongo_mapper/extensions/object_id.rb +32 -0
- data/lib/mongo_mapper/extensions/set.rb +20 -0
- data/lib/mongo_mapper/extensions/string.rb +18 -0
- data/lib/mongo_mapper/extensions/time.rb +28 -0
- data/lib/mongo_mapper/locale/en.yml +5 -0
- data/lib/mongo_mapper/middleware/identity_map.rb +16 -0
- data/lib/mongo_mapper/plugins.rb +22 -0
- data/lib/mongo_mapper/plugins/accessible.rb +52 -0
- data/lib/mongo_mapper/plugins/active_model.rb +18 -0
- data/lib/mongo_mapper/plugins/associations.rb +90 -0
- data/lib/mongo_mapper/plugins/associations/base.rb +92 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_association.rb +54 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_polymorphic_proxy.rb +34 -0
- data/lib/mongo_mapper/plugins/associations/belongs_to_proxy.rb +52 -0
- data/lib/mongo_mapper/plugins/associations/collection.rb +27 -0
- data/lib/mongo_mapper/plugins/associations/embedded_collection.rb +44 -0
- data/lib/mongo_mapper/plugins/associations/in_array_proxy.rb +133 -0
- data/lib/mongo_mapper/plugins/associations/many_association.rb +63 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_as_proxy.rb +28 -0
- data/lib/mongo_mapper/plugins/associations/many_documents_proxy.rb +118 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_polymorphic_proxy.rb +32 -0
- data/lib/mongo_mapper/plugins/associations/many_embedded_proxy.rb +24 -0
- data/lib/mongo_mapper/plugins/associations/many_polymorphic_proxy.rb +14 -0
- data/lib/mongo_mapper/plugins/associations/one_as_proxy.rb +22 -0
- data/lib/mongo_mapper/plugins/associations/one_association.rb +48 -0
- data/lib/mongo_mapper/plugins/associations/one_embedded_polymorphic_proxy.rb +30 -0
- data/lib/mongo_mapper/plugins/associations/one_embedded_proxy.rb +44 -0
- data/lib/mongo_mapper/plugins/associations/one_proxy.rb +95 -0
- data/lib/mongo_mapper/plugins/associations/proxy.rb +134 -0
- data/lib/mongo_mapper/plugins/associations/single_association.rb +46 -0
- data/lib/mongo_mapper/plugins/caching.rb +21 -0
- data/lib/mongo_mapper/plugins/callbacks.rb +29 -0
- data/lib/mongo_mapper/plugins/clone.rb +22 -0
- data/lib/mongo_mapper/plugins/dirty.rb +60 -0
- data/lib/mongo_mapper/plugins/document.rb +41 -0
- data/lib/mongo_mapper/plugins/dynamic_querying.rb +45 -0
- data/lib/mongo_mapper/plugins/dynamic_querying/dynamic_finder.rb +44 -0
- data/lib/mongo_mapper/plugins/embedded_callbacks.rb +56 -0
- data/lib/mongo_mapper/plugins/embedded_document.rb +53 -0
- data/lib/mongo_mapper/plugins/equality.rb +23 -0
- data/lib/mongo_mapper/plugins/identity_map.rb +128 -0
- data/lib/mongo_mapper/plugins/indexes.rb +13 -0
- data/lib/mongo_mapper/plugins/inspect.rb +16 -0
- data/lib/mongo_mapper/plugins/keys.rb +313 -0
- data/lib/mongo_mapper/plugins/keys/key.rb +61 -0
- data/lib/mongo_mapper/plugins/logger.rb +18 -0
- data/lib/mongo_mapper/plugins/modifiers.rb +134 -0
- data/lib/mongo_mapper/plugins/pagination.rb +16 -0
- data/lib/mongo_mapper/plugins/persistence.rb +69 -0
- data/lib/mongo_mapper/plugins/protected.rb +45 -0
- data/lib/mongo_mapper/plugins/querying.rb +165 -0
- data/lib/mongo_mapper/plugins/querying/decorator.rb +36 -0
- data/lib/mongo_mapper/plugins/rails.rb +58 -0
- data/lib/mongo_mapper/plugins/rails/active_record_association_adapter.rb +33 -0
- data/lib/mongo_mapper/plugins/safe.rb +28 -0
- data/lib/mongo_mapper/plugins/sci.rb +36 -0
- data/lib/mongo_mapper/plugins/scopes.rb +27 -0
- data/lib/mongo_mapper/plugins/serialization.rb +109 -0
- data/lib/mongo_mapper/plugins/timestamps.rb +22 -0
- data/lib/mongo_mapper/plugins/touch.rb +18 -0
- data/lib/mongo_mapper/plugins/userstamps.rb +18 -0
- data/lib/mongo_mapper/plugins/validations.rb +86 -0
- data/lib/mongo_mapper/railtie.rb +48 -0
- data/lib/mongo_mapper/railtie/database.rake +65 -0
- data/lib/mongo_mapper/translation.rb +10 -0
- data/lib/mongo_mapper/version.rb +4 -0
- data/lib/rails/generators/mongo_mapper/config/config_generator.rb +24 -0
- data/lib/rails/generators/mongo_mapper/config/templates/mongo.yml +18 -0
- data/lib/rails/generators/mongo_mapper/model/model_generator.rb +23 -0
- data/lib/rails/generators/mongo_mapper/model/templates/model.rb +13 -0
- data/test/_NOTE_ON_TESTING +1 -0
- data/test/functional/associations/test_belongs_to_polymorphic_proxy.rb +64 -0
- data/test/functional/associations/test_belongs_to_proxy.rb +238 -0
- data/test/functional/associations/test_in_array_proxy.rb +349 -0
- data/test/functional/associations/test_many_documents_as_proxy.rb +231 -0
- data/test/functional/associations/test_many_documents_proxy.rb +866 -0
- data/test/functional/associations/test_many_embedded_polymorphic_proxy.rb +239 -0
- data/test/functional/associations/test_many_embedded_proxy.rb +289 -0
- data/test/functional/associations/test_many_polymorphic_proxy.rb +303 -0
- data/test/functional/associations/test_one_as_proxy.rb +491 -0
- data/test/functional/associations/test_one_embedded_polymorphic_proxy.rb +208 -0
- data/test/functional/associations/test_one_embedded_proxy.rb +100 -0
- data/test/functional/associations/test_one_proxy.rb +383 -0
- data/test/functional/test_accessible.rb +198 -0
- data/test/functional/test_associations.rb +46 -0
- data/test/functional/test_binary.rb +27 -0
- data/test/functional/test_caching.rb +77 -0
- data/test/functional/test_callbacks.rb +232 -0
- data/test/functional/test_dirty.rb +301 -0
- data/test/functional/test_document.rb +282 -0
- data/test/functional/test_dynamic_querying.rb +75 -0
- data/test/functional/test_embedded_document.rb +288 -0
- data/test/functional/test_equality.rb +20 -0
- data/test/functional/test_identity_map.rb +513 -0
- data/test/functional/test_indexes.rb +50 -0
- data/test/functional/test_logger.rb +20 -0
- data/test/functional/test_modifiers.rb +537 -0
- data/test/functional/test_pagination.rb +91 -0
- data/test/functional/test_protected.rb +201 -0
- data/test/functional/test_querying.rb +935 -0
- data/test/functional/test_safe.rb +76 -0
- data/test/functional/test_sci.rb +240 -0
- data/test/functional/test_scopes.rb +171 -0
- data/test/functional/test_timestamps.rb +62 -0
- data/test/functional/test_touch.rb +125 -0
- data/test/functional/test_userstamps.rb +44 -0
- data/test/functional/test_validations.rb +414 -0
- data/test/models.rb +261 -0
- data/test/support/railtie.rb +4 -0
- data/test/support/railtie/autoloaded.rb +2 -0
- data/test/support/railtie/not_autoloaded.rb +3 -0
- data/test/support/railtie/parent.rb +3 -0
- data/test/test_active_model_lint.rb +18 -0
- data/test/test_helper.rb +93 -0
- data/test/unit/associations/test_base.rb +146 -0
- data/test/unit/associations/test_belongs_to_association.rb +29 -0
- data/test/unit/associations/test_many_association.rb +63 -0
- data/test/unit/associations/test_one_association.rb +47 -0
- data/test/unit/associations/test_proxy.rb +100 -0
- data/test/unit/serializers/test_json_serializer.rb +216 -0
- data/test/unit/serializers/test_xml_serializer.rb +196 -0
- data/test/unit/test_clone.rb +69 -0
- data/test/unit/test_document.rb +249 -0
- data/test/unit/test_dynamic_finder.rb +125 -0
- data/test/unit/test_embedded_document.rb +682 -0
- data/test/unit/test_equality.rb +38 -0
- data/test/unit/test_exceptions.rb +12 -0
- data/test/unit/test_extensions.rb +380 -0
- data/test/unit/test_identity_map_middleware.rb +34 -0
- data/test/unit/test_inspect.rb +47 -0
- data/test/unit/test_key.rb +205 -0
- data/test/unit/test_keys.rb +65 -0
- data/test/unit/test_mongo_mapper.rb +143 -0
- data/test/unit/test_pagination.rb +11 -0
- data/test/unit/test_plugins.rb +89 -0
- data/test/unit/test_rails.rb +183 -0
- data/test/unit/test_rails_compatibility.rb +38 -0
- data/test/unit/test_rails_reflect_on_association.rb +118 -0
- data/test/unit/test_railtie.rb +66 -0
- data/test/unit/test_serialization.rb +166 -0
- data/test/unit/test_time_zones.rb +44 -0
- data/test/unit/test_translation.rb +27 -0
- data/test/unit/test_validations.rb +562 -0
- metadata +285 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module MongoMapper
|
|
3
|
+
module Plugins
|
|
4
|
+
module EmbeddedCallbacks
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
extend ::ActiveModel::Callbacks
|
|
9
|
+
|
|
10
|
+
define_model_callbacks :save, :create, :update, :destroy, :only => [:before, :after]
|
|
11
|
+
define_model_callbacks :touch, :only => [:after]
|
|
12
|
+
|
|
13
|
+
embedded_callbacks_on
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
module ClassMethods
|
|
17
|
+
def embedded_callbacks_on
|
|
18
|
+
@embedded_callbacks_status = true
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def embedded_callbacks_off
|
|
22
|
+
@embedded_callbacks_status = false
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def embedded_callbacks_on?
|
|
26
|
+
@embedded_callbacks_status == true
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def embedded_callbacks_off?
|
|
30
|
+
!embedded_callbacks_on?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def run_callbacks(callback, *args, &block)
|
|
35
|
+
|
|
36
|
+
if self.class.embedded_callbacks_on?
|
|
37
|
+
embedded_docs = []
|
|
38
|
+
|
|
39
|
+
embedded_associations.each do |association|
|
|
40
|
+
embedded_docs += Array(get_proxy(association).send(:load_target))
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
block = embedded_docs.inject(block) do |chain, doc|
|
|
44
|
+
if doc.class.respond_to?("_#{callback}_callbacks")
|
|
45
|
+
lambda { doc.run_callbacks(callback, *args, &chain) }
|
|
46
|
+
else
|
|
47
|
+
chain
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
super callback, *args, &block
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module MongoMapper
|
|
3
|
+
module Plugins
|
|
4
|
+
module EmbeddedDocument
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
included do
|
|
8
|
+
attr_accessor :_parent_document
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
module ClassMethods
|
|
12
|
+
def embeddable?
|
|
13
|
+
true
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def embedded_in(owner_name)
|
|
17
|
+
alias_method owner_name, :_parent_document
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def new?
|
|
22
|
+
_root_document.try(:new?) || @_new
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def destroyed?
|
|
26
|
+
!!_root_document.try(:destroyed?)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def save(options={})
|
|
30
|
+
_root_document.try(:save, options).tap do |result|
|
|
31
|
+
persist(options) if result
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def save!(options={})
|
|
36
|
+
valid? || raise(DocumentNotValid.new(self))
|
|
37
|
+
_root_document.try(:save!, options).tap do |result|
|
|
38
|
+
persist(options) if result
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def persist(options={})
|
|
43
|
+
@_new = false
|
|
44
|
+
clear_changes if respond_to?(:clear_changes)
|
|
45
|
+
save_to_collection(options)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def _root_document
|
|
49
|
+
@_root_document ||= _parent_document.try(:_root_document)
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module MongoMapper
|
|
3
|
+
module Plugins
|
|
4
|
+
module Equality
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
def ===(other)
|
|
9
|
+
other.is_a?(self)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def eql?(other)
|
|
14
|
+
other.is_a?(self.class) && _id == other._id
|
|
15
|
+
end
|
|
16
|
+
alias :== :eql?
|
|
17
|
+
|
|
18
|
+
def hash
|
|
19
|
+
_id.hash
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'set'
|
|
3
|
+
|
|
4
|
+
module MongoMapper
|
|
5
|
+
module Plugins
|
|
6
|
+
module IdentityMap
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
def self.models
|
|
10
|
+
@models ||= Set.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.clear
|
|
14
|
+
models.each { |m| m.identity_map.clear }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
included do
|
|
18
|
+
IdentityMap.models << self
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ClassMethods
|
|
22
|
+
def inherited(descendant)
|
|
23
|
+
descendant.identity_map = identity_map
|
|
24
|
+
super
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def identity_map
|
|
28
|
+
@identity_map ||= {}
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def identity_map=(v)
|
|
32
|
+
@identity_map = v
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
module IdentityMapQueryMethods
|
|
36
|
+
def all(opts={})
|
|
37
|
+
query = clone.amend(opts)
|
|
38
|
+
super.tap do |docs|
|
|
39
|
+
model.remove_documents_from_map(docs) if query.fields?
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def find_one(opts={})
|
|
44
|
+
query = clone.amend(opts)
|
|
45
|
+
|
|
46
|
+
if model.identity_map_on? && query.simple? && model.identity_map[query[:_id]]
|
|
47
|
+
model.identity_map[query[:_id]]
|
|
48
|
+
else
|
|
49
|
+
super.tap do |doc|
|
|
50
|
+
model.remove_documents_from_map(doc) if query.fields?
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def query(opts={})
|
|
57
|
+
super.extend(IdentityMapQueryMethods)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def remove_documents_from_map(*documents)
|
|
61
|
+
documents.flatten.compact.each do |document|
|
|
62
|
+
identity_map.delete(document['_id'])
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def load(attrs)
|
|
67
|
+
return nil if attrs.nil?
|
|
68
|
+
document = identity_map[attrs['_id']]
|
|
69
|
+
|
|
70
|
+
if document.nil? || identity_map_off?
|
|
71
|
+
document = super
|
|
72
|
+
identity_map[document._id] = document if identity_map_on?
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
document
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
def identity_map_status
|
|
79
|
+
defined?(@identity_map_status) ? @identity_map_status : true
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def identity_map_on
|
|
83
|
+
@identity_map_status = true
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def identity_map_off
|
|
87
|
+
@identity_map_status = false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def identity_map_on?
|
|
91
|
+
identity_map_status
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def identity_map_off?
|
|
95
|
+
!identity_map_on?
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def without_identity_map(&block)
|
|
99
|
+
identity_map_off
|
|
100
|
+
yield
|
|
101
|
+
ensure
|
|
102
|
+
identity_map_on
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
def selecting_fields?(options)
|
|
107
|
+
!options[:fields].nil?
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def identity_map
|
|
112
|
+
self.class.identity_map
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def save(*args)
|
|
116
|
+
if result = super
|
|
117
|
+
identity_map[_id] = self if self.class.identity_map_on?
|
|
118
|
+
end
|
|
119
|
+
result
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def delete
|
|
123
|
+
identity_map.delete(_id) if self.class.identity_map_on?
|
|
124
|
+
super
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module MongoMapper
|
|
3
|
+
module Plugins
|
|
4
|
+
module Indexes
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
extend Forwardable
|
|
9
|
+
def_delegators :collection, :ensure_index, :create_index, :drop_index, :drop_indexes
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
module MongoMapper
|
|
3
|
+
module Plugins
|
|
4
|
+
module Inspect
|
|
5
|
+
extend ActiveSupport::Concern
|
|
6
|
+
|
|
7
|
+
def inspect(include_nil = false)
|
|
8
|
+
keys = include_nil ? key_names : attributes.keys
|
|
9
|
+
attributes_as_nice_string = keys.sort.collect do |name|
|
|
10
|
+
"#{name}: #{self.send(:"#{name}").inspect}"
|
|
11
|
+
end.join(", ")
|
|
12
|
+
"#<#{self.class} #{attributes_as_nice_string}>"
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
require 'mongo_mapper/plugins/keys/key'
|
|
3
|
+
|
|
4
|
+
module MongoMapper
|
|
5
|
+
module Plugins
|
|
6
|
+
module Keys
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
included do
|
|
10
|
+
extend ActiveSupport::DescendantsTracker
|
|
11
|
+
key :_id, ObjectId, :default => lambda { BSON::ObjectId.new }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module ClassMethods
|
|
15
|
+
def inherited(descendant)
|
|
16
|
+
descendant.instance_variable_set(:@keys, keys.dup)
|
|
17
|
+
super
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def keys
|
|
21
|
+
@keys ||= {}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def key(*args)
|
|
25
|
+
Key.new(*args).tap do |key|
|
|
26
|
+
keys[key.name] = key
|
|
27
|
+
create_accessors_for(key)
|
|
28
|
+
create_key_in_descendants(*args)
|
|
29
|
+
create_indexes_for(key)
|
|
30
|
+
create_validations_for(key)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def key?(key)
|
|
35
|
+
keys.keys.include?(key.to_s)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def using_object_id?
|
|
39
|
+
object_id_key?(:_id)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def object_id_keys
|
|
43
|
+
keys.keys.select { |key| keys[key].type == ObjectId }.map { |k| k.to_sym }
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def object_id_key?(name)
|
|
47
|
+
object_id_keys.include?(name.to_sym)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def to_mongo(instance)
|
|
51
|
+
return nil if instance.nil?
|
|
52
|
+
instance.to_mongo
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def from_mongo(value)
|
|
56
|
+
return nil if value.nil?
|
|
57
|
+
value.is_a?(self) ? value : load(value)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# load is overridden in identity map to ensure same objects are loaded
|
|
61
|
+
def load(attrs)
|
|
62
|
+
return nil if attrs.nil?
|
|
63
|
+
begin
|
|
64
|
+
attrs['_type'].present? ? attrs['_type'].constantize : self
|
|
65
|
+
rescue NameError
|
|
66
|
+
self
|
|
67
|
+
end.allocate.initialize_from_database(attrs)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
def key_accessors_module_defined?
|
|
72
|
+
if method(:const_defined?).arity == 1 # Ruby 1.9 compat check
|
|
73
|
+
const_defined?('MongoMapperKeys')
|
|
74
|
+
else
|
|
75
|
+
const_defined?('MongoMapperKeys', false)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def accessors_module
|
|
80
|
+
if key_accessors_module_defined?
|
|
81
|
+
const_get 'MongoMapperKeys'
|
|
82
|
+
else
|
|
83
|
+
const_set 'MongoMapperKeys', Module.new
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def create_accessors_for(key)
|
|
88
|
+
accessors_module.module_eval <<-end_eval
|
|
89
|
+
def #{key.name}
|
|
90
|
+
read_key(:#{key.name})
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def #{key.name}_before_type_cast
|
|
94
|
+
read_key_before_type_cast(:#{key.name})
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def #{key.name}=(value)
|
|
98
|
+
write_key(:#{key.name}, value)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def #{key.name}?
|
|
102
|
+
read_key(:#{key.name}).present?
|
|
103
|
+
end
|
|
104
|
+
end_eval
|
|
105
|
+
|
|
106
|
+
include accessors_module
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def create_key_in_descendants(*args)
|
|
110
|
+
descendants.each { |descendant| descendant.key(*args) }
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def create_indexes_for(key)
|
|
114
|
+
if key.options[:index] && !key.embeddable?
|
|
115
|
+
warn "[DEPRECATION] :index option when defining key #{key.name.inspect} is deprecated. Put indexes in `db/indexes.rb`"
|
|
116
|
+
ensure_index key.name
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def create_validations_for(key)
|
|
121
|
+
attribute = key.name.to_sym
|
|
122
|
+
|
|
123
|
+
if key.options[:required]
|
|
124
|
+
if key.type == Boolean
|
|
125
|
+
validates_inclusion_of attribute, :in => [true, false]
|
|
126
|
+
else
|
|
127
|
+
validates_presence_of(attribute)
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
if key.options[:unique]
|
|
132
|
+
validates_uniqueness_of(attribute)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
if key.options[:numeric]
|
|
136
|
+
number_options = key.type == Integer ? {:only_integer => true} : {}
|
|
137
|
+
validates_numericality_of(attribute, number_options)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
if key.options[:format]
|
|
141
|
+
validates_format_of(attribute, :with => key.options[:format])
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
if key.options[:in]
|
|
145
|
+
validates_inclusion_of(attribute, :in => key.options[:in])
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
if key.options[:not_in]
|
|
149
|
+
validates_exclusion_of(attribute, :in => key.options[:not_in])
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
if key.options[:length]
|
|
153
|
+
length_options = case key.options[:length]
|
|
154
|
+
when Integer
|
|
155
|
+
{:minimum => 0, :maximum => key.options[:length]}
|
|
156
|
+
when Range
|
|
157
|
+
{:within => key.options[:length]}
|
|
158
|
+
when Hash
|
|
159
|
+
key.options[:length]
|
|
160
|
+
end
|
|
161
|
+
validates_length_of(attribute, length_options)
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
def initialize(attrs={})
|
|
167
|
+
@_new = true
|
|
168
|
+
self.attributes = attrs
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def initialize_from_database(attrs={})
|
|
172
|
+
@_new = false
|
|
173
|
+
load_from_database(attrs)
|
|
174
|
+
self
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def persisted?
|
|
178
|
+
!new? && !destroyed?
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def attributes=(attrs)
|
|
182
|
+
return if attrs.blank?
|
|
183
|
+
|
|
184
|
+
attrs.each_pair do |key, value|
|
|
185
|
+
if respond_to?(:"#{key}=")
|
|
186
|
+
self.send(:"#{key}=", value)
|
|
187
|
+
else
|
|
188
|
+
self[key] = value
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def attributes
|
|
194
|
+
HashWithIndifferentAccess.new.tap do |attrs|
|
|
195
|
+
keys.select { |name,key| !self[key.name].nil? || key.type == ObjectId }.each do |name, key|
|
|
196
|
+
value = key.set(self[key.name])
|
|
197
|
+
attrs[name] = value
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
embedded_associations.each do |association|
|
|
201
|
+
if documents = instance_variable_get(association.ivar)
|
|
202
|
+
if association.is_a?(Associations::OneAssociation)
|
|
203
|
+
attrs[association.name] = documents.to_mongo
|
|
204
|
+
else
|
|
205
|
+
attrs[association.name] = documents.map { |document| document.to_mongo }
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
alias :to_mongo :attributes
|
|
212
|
+
|
|
213
|
+
def assign(attrs={})
|
|
214
|
+
warn "[DEPRECATION] #assign is deprecated, use #attributes="
|
|
215
|
+
self.attributes = attrs
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
def update_attributes(attrs={})
|
|
219
|
+
self.attributes = attrs
|
|
220
|
+
save
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
def update_attributes!(attrs={})
|
|
224
|
+
self.attributes = attrs
|
|
225
|
+
save!
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def update_attribute(name, value)
|
|
229
|
+
self.send(:"#{name}=", value)
|
|
230
|
+
save(:validate => false)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
def id
|
|
234
|
+
_id
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def id=(value)
|
|
238
|
+
if self.class.using_object_id?
|
|
239
|
+
value = ObjectId.to_mongo(value)
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
self[:_id] = value
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
def [](name)
|
|
246
|
+
read_key(name)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def []=(name, value)
|
|
250
|
+
ensure_key_exists(name)
|
|
251
|
+
write_key(name, value)
|
|
252
|
+
end
|
|
253
|
+
|
|
254
|
+
def keys
|
|
255
|
+
self.class.keys
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
def key_names
|
|
259
|
+
keys.keys
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
def non_embedded_keys
|
|
263
|
+
keys.values.select { |key| !key.embeddable? }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
def embedded_keys
|
|
267
|
+
keys.values.select { |key| key.embeddable? }
|
|
268
|
+
end
|
|
269
|
+
|
|
270
|
+
private
|
|
271
|
+
def load_from_database(attrs)
|
|
272
|
+
return if attrs.blank?
|
|
273
|
+
attrs.each do |key, value|
|
|
274
|
+
if respond_to?(:"#{key}=") && !self.class.key?(key)
|
|
275
|
+
self.send(:"#{key}=", value)
|
|
276
|
+
else
|
|
277
|
+
self[key] = value
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def ensure_key_exists(name)
|
|
283
|
+
self.class.key(name) unless respond_to?("#{name}=")
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def set_parent_document(key, value)
|
|
287
|
+
if key.embeddable? && value.is_a?(key.type)
|
|
288
|
+
value._parent_document = self
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
def read_key(key_name)
|
|
293
|
+
if key = keys[key_name.to_s]
|
|
294
|
+
value = key.get(instance_variable_get(:"@#{key_name}"))
|
|
295
|
+
set_parent_document(key, value)
|
|
296
|
+
instance_variable_set(:"@#{key_name}", value)
|
|
297
|
+
end
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
def read_key_before_type_cast(name)
|
|
301
|
+
instance_variable_get(:"@#{name}_before_type_cast")
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def write_key(name, value)
|
|
305
|
+
key = keys[name.to_s]
|
|
306
|
+
set_parent_document(key, value)
|
|
307
|
+
instance_variable_set :"@#{name}_before_type_cast", value
|
|
308
|
+
instance_variable_set :"@#{name}", key.set(value)
|
|
309
|
+
end
|
|
310
|
+
end
|
|
311
|
+
end
|
|
312
|
+
end
|
|
313
|
+
|