lookout-mongo_mapper 0.11.3
Sign up to get free protection for your applications and to get access to all the features.
- 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,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Querying
|
5
|
+
Methods = Plucky::Methods + [:find!]
|
6
|
+
|
7
|
+
module Decorator
|
8
|
+
include DynamicQuerying::ClassMethods
|
9
|
+
|
10
|
+
def model(model=nil)
|
11
|
+
return @model if model.nil?
|
12
|
+
@model = model
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def find!(*ids)
|
17
|
+
raise DocumentNotFound, "Couldn't find without an ID" if ids.size == 0
|
18
|
+
|
19
|
+
find(*ids).tap do |result|
|
20
|
+
if result.nil? || ids.size != Array(result).size
|
21
|
+
raise DocumentNotFound, "Couldn't find all of the ids (#{ids.join(',')}). Found #{Array(result).size}, but was expecting #{ids.size}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def method_missing(method, *args, &block)
|
28
|
+
return super unless model.respond_to?(method)
|
29
|
+
result = model.send(method, *args, &block)
|
30
|
+
return super unless result.is_a?(Plucky::Query)
|
31
|
+
merge(result)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Rails
|
5
|
+
autoload :ActiveRecordAssociationAdapter, "mongo_mapper/plugins/rails/active_record_association_adapter"
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
def to_param
|
9
|
+
id.to_s if persisted?
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_model
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_key
|
17
|
+
[id] if persisted?
|
18
|
+
end
|
19
|
+
|
20
|
+
def new_record?
|
21
|
+
new?
|
22
|
+
end
|
23
|
+
|
24
|
+
def read_attribute(name)
|
25
|
+
self[name]
|
26
|
+
end
|
27
|
+
|
28
|
+
def read_attribute_before_type_cast(name)
|
29
|
+
read_key_before_type_cast(name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def write_attribute(name, value)
|
33
|
+
self[name] = value
|
34
|
+
end
|
35
|
+
|
36
|
+
module ClassMethods
|
37
|
+
def has_one(*args)
|
38
|
+
one(*args)
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_many(*args, &extension)
|
42
|
+
many(*args, &extension)
|
43
|
+
end
|
44
|
+
|
45
|
+
def column_names
|
46
|
+
keys.keys
|
47
|
+
end
|
48
|
+
|
49
|
+
# Returns returns an ActiveRecordAssociationAdapter for an association. This adapter has an API that is a
|
50
|
+
# subset of ActiveRecord::Reflection::AssociationReflection. This allows MongoMapper to be used with the
|
51
|
+
# association helpers in gems like simple_form and formtastic.
|
52
|
+
def reflect_on_association(name)
|
53
|
+
ActiveRecordAssociationAdapter.for_association(associations[name]) if associations[name]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Rails
|
5
|
+
class ActiveRecordAssociationAdapter
|
6
|
+
attr_reader :klass, :macro, :name, :options
|
7
|
+
|
8
|
+
def self.for_association(association)
|
9
|
+
macro = case association
|
10
|
+
when MongoMapper::Plugins::Associations::BelongsToAssociation
|
11
|
+
:belongs_to
|
12
|
+
when MongoMapper::Plugins::Associations::ManyAssociation
|
13
|
+
:has_many
|
14
|
+
when MongoMapper::Plugins::Associations::OneAssociation
|
15
|
+
:has_one
|
16
|
+
else
|
17
|
+
raise "no #{name} for association of type #{association.class}"
|
18
|
+
end
|
19
|
+
|
20
|
+
new(association, macro)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(association, macro)
|
24
|
+
@klass, @name = association.klass, association.name
|
25
|
+
# only include compatible options
|
26
|
+
@options = association.options.slice(:conditions, :order)
|
27
|
+
|
28
|
+
@macro = macro
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Safe
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def inherited(subclass)
|
9
|
+
super
|
10
|
+
subclass.safe if safe?
|
11
|
+
end
|
12
|
+
|
13
|
+
def safe
|
14
|
+
@safe = true
|
15
|
+
end
|
16
|
+
|
17
|
+
def safe?
|
18
|
+
@safe == true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def save_to_collection(options={})
|
23
|
+
options[:safe] = self.class.safe? unless options.key?(:safe)
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Sci
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
extend ActiveSupport::DescendantsTracker
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def inherited(subclass)
|
13
|
+
key :_type, String unless key?(:_type)
|
14
|
+
subclass.instance_variable_set("@single_collection_inherited", true)
|
15
|
+
subclass.set_collection_name(collection_name) unless subclass.embeddable?
|
16
|
+
super
|
17
|
+
end
|
18
|
+
|
19
|
+
def single_collection_inherited?
|
20
|
+
@single_collection_inherited == true
|
21
|
+
end
|
22
|
+
|
23
|
+
def query(options={})
|
24
|
+
super.tap do |query|
|
25
|
+
query[:_type] = {'$in' => [name] + descendants.map(&:name)} if single_collection_inherited?
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(*args)
|
31
|
+
super
|
32
|
+
write_key :_type, self.class.name if self.class.key?(:_type)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Scopes
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
class_attribute :_scopes
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def scope(name, scope_options={})
|
13
|
+
scopes[name] = lambda do |*args|
|
14
|
+
result = scope_options.is_a?(Proc) ? scope_options.call(*args) : scope_options
|
15
|
+
result = self.query(result) if result.is_a?(Hash)
|
16
|
+
self.query.merge(result)
|
17
|
+
end
|
18
|
+
singleton_class.send :define_method, name, &scopes[name]
|
19
|
+
end
|
20
|
+
|
21
|
+
def scopes
|
22
|
+
self._scopes || self._scopes = {}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,109 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'active_model/serializers/json'
|
3
|
+
require 'active_model/serializers/xml'
|
4
|
+
|
5
|
+
module MongoMapper
|
6
|
+
module Plugins
|
7
|
+
module Serialization
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
include ::ActiveModel::Serializers::JSON
|
12
|
+
include ::ActiveModel::Serializers::Xml
|
13
|
+
self.include_root_in_json = false
|
14
|
+
end
|
15
|
+
|
16
|
+
def serializable_attributes
|
17
|
+
attributes.keys.map { |k| k.to_s } + ['id'] - ['_id']
|
18
|
+
end
|
19
|
+
|
20
|
+
def serializable_hash(options = nil)
|
21
|
+
options ||= {}
|
22
|
+
|
23
|
+
options[:only] = Array.wrap(options[:only]).map { |k| k.to_s }
|
24
|
+
options[:except] = Array.wrap(options[:except]).map { |k| k.to_s }
|
25
|
+
|
26
|
+
attribute_names = serializable_attributes
|
27
|
+
|
28
|
+
if options[:only].any?
|
29
|
+
attribute_names &= options[:only]
|
30
|
+
elsif options[:except].any?
|
31
|
+
attribute_names -= options[:except]
|
32
|
+
end
|
33
|
+
|
34
|
+
attribute_names += Array.wrap(options[:methods]).map { |m| m.to_s }.select do |method|
|
35
|
+
respond_to?(method)
|
36
|
+
end
|
37
|
+
|
38
|
+
hash = attribute_names.sort.inject({}) do |hash, name|
|
39
|
+
value = send(name)
|
40
|
+
hash[name] = if value.is_a?(Array)
|
41
|
+
value.map {|v| v.respond_to?(:serializable_hash) ? v.serializable_hash : v }
|
42
|
+
elsif value.respond_to?(:serializable_hash)
|
43
|
+
value.serializable_hash
|
44
|
+
else
|
45
|
+
value
|
46
|
+
end
|
47
|
+
hash
|
48
|
+
end
|
49
|
+
|
50
|
+
serializable_add_includes(options) do |association, records, opts|
|
51
|
+
hash[association.to_s] = records.is_a?(Array) ?
|
52
|
+
records.map { |r| r.serializable_hash(opts) } :
|
53
|
+
records.serializable_hash(opts)
|
54
|
+
end
|
55
|
+
|
56
|
+
hash
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_xml(options = {}, &block)
|
60
|
+
XmlSerializer.new(self, options).serialize(&block)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def serializable_add_includes(options = {})
|
66
|
+
return unless include_associations = options.delete(:include)
|
67
|
+
|
68
|
+
base_only_or_except = { :except => options[:except],
|
69
|
+
:only => options[:only] }
|
70
|
+
|
71
|
+
include_has_options = include_associations.is_a?(Hash)
|
72
|
+
associations = include_has_options ? include_associations.keys : Array.wrap(include_associations)
|
73
|
+
|
74
|
+
associations.each do |association|
|
75
|
+
records = get_proxy(self.class.associations[association])
|
76
|
+
unless records.nil?
|
77
|
+
association_options = include_has_options ? include_associations[association] : base_only_or_except
|
78
|
+
opts = options.merge(association_options)
|
79
|
+
yield(association, records, opts)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
options[:include] = include_associations
|
84
|
+
end
|
85
|
+
|
86
|
+
module ClassMethods
|
87
|
+
def from_json(json)
|
88
|
+
self.new.from_json(json)
|
89
|
+
end
|
90
|
+
|
91
|
+
def from_xml(xml)
|
92
|
+
self.new.from_xml(xml)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Override default Serializer to use #serializable_hash
|
98
|
+
class XmlSerializer < ::ActiveModel::Serializers::Xml::Serializer
|
99
|
+
def attributes_hash
|
100
|
+
@serializable.serializable_hash(options)
|
101
|
+
end
|
102
|
+
|
103
|
+
def serializable_methods
|
104
|
+
# Methods are already included in #serializable_hash
|
105
|
+
[]
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Timestamps
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def timestamps!
|
9
|
+
key :created_at, Time
|
10
|
+
key :updated_at, Time
|
11
|
+
class_eval { before_save :update_timestamps }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def update_timestamps
|
16
|
+
now = Time.now.utc
|
17
|
+
self[:created_at] = now if !persisted? && !created_at?
|
18
|
+
self[:updated_at] = now
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MongoMapper
|
2
|
+
module Plugins
|
3
|
+
module Touch
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
def touch(key = :updated_at)
|
7
|
+
raise ArgumentError, "Invalid key named #{key}" unless self.key_names.include?(key.to_s)
|
8
|
+
if self.class.embeddable?
|
9
|
+
self.write_attribute(key, Time.now.utc)
|
10
|
+
self._parent_document.touch
|
11
|
+
else
|
12
|
+
self.set(key => Time.now.utc)
|
13
|
+
end
|
14
|
+
true
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Userstamps
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def userstamps!(options = {})
|
9
|
+
class_name = (options[:class_name] || options[:class] || 'User' ).to_s
|
10
|
+
key :creator_id, ObjectId
|
11
|
+
key :updater_id, ObjectId
|
12
|
+
belongs_to :creator, :class_name => class_name
|
13
|
+
belongs_to :updater, :class_name => class_name
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
module MongoMapper
|
3
|
+
module Plugins
|
4
|
+
module Validations
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
include ::ActiveModel::Validations
|
8
|
+
include ::ActiveModel::Validations::Callbacks
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def validates_uniqueness_of(*attr_names)
|
12
|
+
validates_with UniquenessValidator, _merge_attributes(attr_names)
|
13
|
+
end
|
14
|
+
|
15
|
+
def validates_associated(*attr_names)
|
16
|
+
validates_with AssociatedValidator, _merge_attributes(attr_names)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def save(options = {})
|
21
|
+
options.reverse_merge!(:validate => true)
|
22
|
+
!options[:validate] || valid? ? super : false
|
23
|
+
end
|
24
|
+
|
25
|
+
def valid?(context = nil)
|
26
|
+
context ||= (new_record? ? :create : :update)
|
27
|
+
super(context)
|
28
|
+
end
|
29
|
+
|
30
|
+
class UniquenessValidator < ::ActiveModel::EachValidator
|
31
|
+
def initialize(options)
|
32
|
+
super(options.reverse_merge(:case_sensitive => true))
|
33
|
+
end
|
34
|
+
|
35
|
+
def setup(klass)
|
36
|
+
@klass = klass
|
37
|
+
end
|
38
|
+
|
39
|
+
def validate_each(record, attribute, value)
|
40
|
+
conditions = scope_conditions(record)
|
41
|
+
|
42
|
+
if options[:case_sensitive]
|
43
|
+
conditions[attribute] = value
|
44
|
+
else
|
45
|
+
conditions[attribute] = /^#{Regexp.escape(value.to_s)}$/i
|
46
|
+
end
|
47
|
+
|
48
|
+
# Make sure we're not including the current document in the query
|
49
|
+
conditions[:_id.ne] = record._id if record._id
|
50
|
+
|
51
|
+
if @klass.exists?(conditions)
|
52
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def message(instance)
|
57
|
+
super || "has already been taken"
|
58
|
+
end
|
59
|
+
|
60
|
+
def scope_conditions(instance)
|
61
|
+
Array(options[:scope] || []).inject({}) do |conditions, key|
|
62
|
+
conditions.merge(key => instance[key])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class AssociatedValidator < ::ActiveModel::EachValidator
|
68
|
+
def validate_each(record, attribute, value)
|
69
|
+
if !Array.wrap(value).all? { |c| c.nil? || c.valid?(options[:context]) }
|
70
|
+
record.errors.add(attribute, :invalid, :message => options[:message], :value => value)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Need to monkey patch ActiveModel for now since it uses the internal
|
80
|
+
# _run_validation_callbacks, which is impossible to override due to the
|
81
|
+
# way ActiveSupport::Callbacks is implemented.
|
82
|
+
ActiveModel::Validations::Callbacks.class_eval do
|
83
|
+
def run_validations!
|
84
|
+
run_callbacks(:validation) { super }
|
85
|
+
end
|
86
|
+
end
|