mongomodel 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +22 -0
- data/README.md +34 -0
- data/Rakefile +47 -0
- data/bin/console +45 -0
- data/lib/mongomodel.rb +92 -0
- data/lib/mongomodel/attributes/mongo.rb +40 -0
- data/lib/mongomodel/attributes/store.rb +30 -0
- data/lib/mongomodel/attributes/typecasting.rb +51 -0
- data/lib/mongomodel/concerns/abstract_class.rb +17 -0
- data/lib/mongomodel/concerns/activemodel.rb +11 -0
- data/lib/mongomodel/concerns/associations.rb +103 -0
- data/lib/mongomodel/concerns/associations/base/association.rb +33 -0
- data/lib/mongomodel/concerns/associations/base/definition.rb +56 -0
- data/lib/mongomodel/concerns/associations/base/proxy.rb +58 -0
- data/lib/mongomodel/concerns/associations/belongs_to.rb +68 -0
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +159 -0
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +175 -0
- data/lib/mongomodel/concerns/attribute_methods.rb +55 -0
- data/lib/mongomodel/concerns/attribute_methods/before_type_cast.rb +29 -0
- data/lib/mongomodel/concerns/attribute_methods/dirty.rb +35 -0
- data/lib/mongomodel/concerns/attribute_methods/protected.rb +127 -0
- data/lib/mongomodel/concerns/attribute_methods/query.rb +22 -0
- data/lib/mongomodel/concerns/attribute_methods/read.rb +29 -0
- data/lib/mongomodel/concerns/attribute_methods/write.rb +29 -0
- data/lib/mongomodel/concerns/attributes.rb +85 -0
- data/lib/mongomodel/concerns/callbacks.rb +294 -0
- data/lib/mongomodel/concerns/logging.rb +15 -0
- data/lib/mongomodel/concerns/pretty_inspect.rb +29 -0
- data/lib/mongomodel/concerns/properties.rb +69 -0
- data/lib/mongomodel/concerns/record_status.rb +42 -0
- data/lib/mongomodel/concerns/timestamps.rb +32 -0
- data/lib/mongomodel/concerns/validations.rb +38 -0
- data/lib/mongomodel/concerns/validations/associated.rb +46 -0
- data/lib/mongomodel/document.rb +20 -0
- data/lib/mongomodel/document/callbacks.rb +46 -0
- data/lib/mongomodel/document/dynamic_finders.rb +88 -0
- data/lib/mongomodel/document/finders.rb +82 -0
- data/lib/mongomodel/document/indexes.rb +91 -0
- data/lib/mongomodel/document/optimistic_locking.rb +48 -0
- data/lib/mongomodel/document/persistence.rb +143 -0
- data/lib/mongomodel/document/scopes.rb +161 -0
- data/lib/mongomodel/document/validations.rb +68 -0
- data/lib/mongomodel/document/validations/uniqueness.rb +78 -0
- data/lib/mongomodel/embedded_document.rb +42 -0
- data/lib/mongomodel/locale/en.yml +55 -0
- data/lib/mongomodel/support/collection.rb +109 -0
- data/lib/mongomodel/support/configuration.rb +35 -0
- data/lib/mongomodel/support/core_extensions.rb +10 -0
- data/lib/mongomodel/support/exceptions.rb +25 -0
- data/lib/mongomodel/support/mongo_options.rb +177 -0
- data/lib/mongomodel/support/types.rb +35 -0
- data/lib/mongomodel/support/types/array.rb +11 -0
- data/lib/mongomodel/support/types/boolean.rb +25 -0
- data/lib/mongomodel/support/types/custom.rb +38 -0
- data/lib/mongomodel/support/types/date.rb +20 -0
- data/lib/mongomodel/support/types/float.rb +13 -0
- data/lib/mongomodel/support/types/hash.rb +18 -0
- data/lib/mongomodel/support/types/integer.rb +13 -0
- data/lib/mongomodel/support/types/object.rb +21 -0
- data/lib/mongomodel/support/types/string.rb +9 -0
- data/lib/mongomodel/support/types/symbol.rb +9 -0
- data/lib/mongomodel/support/types/time.rb +12 -0
- data/lib/mongomodel/version.rb +3 -0
- data/spec/mongomodel/attributes/store_spec.rb +273 -0
- data/spec/mongomodel/concerns/activemodel_spec.rb +61 -0
- data/spec/mongomodel/concerns/associations/belongs_to_spec.rb +153 -0
- data/spec/mongomodel/concerns/associations/has_many_by_foreign_key_spec.rb +165 -0
- data/spec/mongomodel/concerns/associations/has_many_by_ids_spec.rb +192 -0
- data/spec/mongomodel/concerns/attribute_methods/before_type_cast_spec.rb +46 -0
- data/spec/mongomodel/concerns/attribute_methods/dirty_spec.rb +131 -0
- data/spec/mongomodel/concerns/attribute_methods/protected_spec.rb +86 -0
- data/spec/mongomodel/concerns/attribute_methods/query_spec.rb +27 -0
- data/spec/mongomodel/concerns/attribute_methods/read_spec.rb +52 -0
- data/spec/mongomodel/concerns/attribute_methods/write_spec.rb +43 -0
- data/spec/mongomodel/concerns/attributes_spec.rb +152 -0
- data/spec/mongomodel/concerns/callbacks_spec.rb +90 -0
- data/spec/mongomodel/concerns/logging_spec.rb +20 -0
- data/spec/mongomodel/concerns/pretty_inspect_spec.rb +68 -0
- data/spec/mongomodel/concerns/properties_spec.rb +29 -0
- data/spec/mongomodel/concerns/timestamps_spec.rb +170 -0
- data/spec/mongomodel/concerns/validations_spec.rb +159 -0
- data/spec/mongomodel/document/callbacks_spec.rb +80 -0
- data/spec/mongomodel/document/dynamic_finders_spec.rb +183 -0
- data/spec/mongomodel/document/finders_spec.rb +231 -0
- data/spec/mongomodel/document/indexes_spec.rb +121 -0
- data/spec/mongomodel/document/optimistic_locking_spec.rb +57 -0
- data/spec/mongomodel/document/persistence_spec.rb +319 -0
- data/spec/mongomodel/document/scopes_spec.rb +204 -0
- data/spec/mongomodel/document/validations/uniqueness_spec.rb +217 -0
- data/spec/mongomodel/document/validations_spec.rb +132 -0
- data/spec/mongomodel/document_spec.rb +74 -0
- data/spec/mongomodel/embedded_document_spec.rb +66 -0
- data/spec/mongomodel/mongomodel_spec.rb +33 -0
- data/spec/mongomodel/support/collection_spec.rb +248 -0
- data/spec/mongomodel/support/mongo_options_spec.rb +295 -0
- data/spec/mongomodel/support/property_spec.rb +83 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/specdoc.opts +6 -0
- data/spec/support/callbacks.rb +44 -0
- data/spec/support/helpers/define_class.rb +24 -0
- data/spec/support/helpers/specs_for.rb +11 -0
- data/spec/support/matchers/be_a_subclass_of.rb +5 -0
- data/spec/support/matchers/respond_to_boolean.rb +17 -0
- data/spec/support/matchers/run_callbacks.rb +20 -0
- data/spec/support/models.rb +23 -0
- data/spec/support/time.rb +6 -0
- metadata +232 -0
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'active_support/core_ext/array/extract_options'
|
2
|
+
|
3
|
+
module MongoModel
|
4
|
+
module DocumentExtensions
|
5
|
+
module Indexes
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
included do
|
9
|
+
index :_type
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def property(name, *args, &block) #:nodoc:
|
14
|
+
property = super
|
15
|
+
index(name) if property.options[:index]
|
16
|
+
property
|
17
|
+
end
|
18
|
+
|
19
|
+
def index(*args)
|
20
|
+
index = Index.new(*args)
|
21
|
+
indexes << index
|
22
|
+
@_indexes_initialized = false
|
23
|
+
index
|
24
|
+
end
|
25
|
+
|
26
|
+
def indexes
|
27
|
+
read_inheritable_attribute(:indexes) || write_inheritable_attribute(:indexes, [])
|
28
|
+
end
|
29
|
+
|
30
|
+
def indexes_initialized?
|
31
|
+
@_indexes_initialized == true
|
32
|
+
end
|
33
|
+
|
34
|
+
def ensure_indexes!
|
35
|
+
indexes.each do |index|
|
36
|
+
collection.create_index(*index.to_args)
|
37
|
+
end
|
38
|
+
|
39
|
+
@_indexes_initialized = true
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
def _find(*)
|
44
|
+
ensure_indexes! unless indexes_initialized?
|
45
|
+
super
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class Index
|
52
|
+
def initialize(*keys)
|
53
|
+
options = keys.extract_options!
|
54
|
+
@unique = options.delete(:unique)
|
55
|
+
|
56
|
+
keys.each do |key|
|
57
|
+
self.keys[key.to_sym] = :ascending
|
58
|
+
end
|
59
|
+
|
60
|
+
options.each do |key, order|
|
61
|
+
self.keys[key.to_sym] = order
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def keys
|
66
|
+
@keys ||= OrderedHash.new
|
67
|
+
end
|
68
|
+
|
69
|
+
def unique?
|
70
|
+
@unique
|
71
|
+
end
|
72
|
+
|
73
|
+
def to_args
|
74
|
+
args = []
|
75
|
+
|
76
|
+
if keys.size == 1 && keys.all? { |k, o| o == :ascending }
|
77
|
+
args << keys.keys.first
|
78
|
+
else
|
79
|
+
args << keys.map { |k, o| [k, o == :ascending ? 1 : -1] }.sort_by { |k| k.first.to_s }
|
80
|
+
end
|
81
|
+
|
82
|
+
args << true if unique?
|
83
|
+
|
84
|
+
args
|
85
|
+
end
|
86
|
+
|
87
|
+
def ==(other)
|
88
|
+
other.is_a?(Index) && to_args == other.to_args
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module MongoModel
|
2
|
+
module DocumentExtensions
|
3
|
+
module OptimisticLocking
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
def locking_enabled?
|
8
|
+
properties.include?(:_lock_version)
|
9
|
+
end
|
10
|
+
|
11
|
+
def lock_optimistically=(value)
|
12
|
+
if value == true
|
13
|
+
property :_lock_version, Integer, :default => 0, :internal => true, :protected => true
|
14
|
+
before_save :increment_lock_version, :if => :locking_enabled?
|
15
|
+
else
|
16
|
+
properties.delete(:_lock_version)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def locking_enabled?
|
22
|
+
self.class.locking_enabled?
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def increment_lock_version
|
27
|
+
self._lock_version += 1
|
28
|
+
end
|
29
|
+
|
30
|
+
def save_to_collection
|
31
|
+
if locking_enabled? && _lock_version > 1
|
32
|
+
begin
|
33
|
+
collection.update({ '_id' => id, '_lock_version' => _lock_version-1 }, to_mongo)
|
34
|
+
success = database.last_status['updatedExisting']
|
35
|
+
|
36
|
+
self._lock_version -= 1 unless success
|
37
|
+
|
38
|
+
success
|
39
|
+
rescue Mongo::OperationFailure => e
|
40
|
+
false
|
41
|
+
end
|
42
|
+
else
|
43
|
+
super
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module MongoModel
|
2
|
+
module DocumentExtensions
|
3
|
+
module Persistence
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
undef_method :id if method_defined?(:id)
|
8
|
+
property :id, String, :as => '_id', :default => lambda { ::Mongo::ObjectID.new.to_s }
|
9
|
+
|
10
|
+
class_inheritable_writer :collection_name
|
11
|
+
end
|
12
|
+
|
13
|
+
def save
|
14
|
+
create_or_update
|
15
|
+
end
|
16
|
+
|
17
|
+
def save!
|
18
|
+
create_or_update || raise(DocumentNotSaved)
|
19
|
+
end
|
20
|
+
|
21
|
+
def delete
|
22
|
+
self.class.delete(id)
|
23
|
+
set_destroyed(true)
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
def destroy
|
28
|
+
delete
|
29
|
+
end
|
30
|
+
|
31
|
+
# Updates all the attributes from the passed-in Hash and saves the document.
|
32
|
+
# If the object is invalid, the saving will fail and false will be returned.
|
33
|
+
def update_attributes(attributes)
|
34
|
+
self.attributes = attributes
|
35
|
+
save
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates a single attribute and saves the document without going through the normal validation procedure.
|
39
|
+
# This is especially useful for boolean flags on existing documents.
|
40
|
+
def update_attribute(name, value)
|
41
|
+
send("#{name}=", value)
|
42
|
+
save(false)
|
43
|
+
end
|
44
|
+
|
45
|
+
def collection
|
46
|
+
self.class.collection
|
47
|
+
end
|
48
|
+
|
49
|
+
def database
|
50
|
+
self.class.database
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods
|
54
|
+
def create(attributes={}, &block)
|
55
|
+
if attributes.is_a?(Array)
|
56
|
+
attributes.map { |attrs| create(attrs, &block) }
|
57
|
+
else
|
58
|
+
instance = new(attributes, &block)
|
59
|
+
instance.save
|
60
|
+
instance
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def delete(id_or_conditions)
|
65
|
+
collection.remove(MongoOptions.new(self, :conditions => id_to_conditions(id_or_conditions)).selector)
|
66
|
+
end
|
67
|
+
|
68
|
+
def destroy(id_or_conditions)
|
69
|
+
find(:all, :conditions => id_to_conditions(id_or_conditions)).each { |instance| instance.destroy }
|
70
|
+
end
|
71
|
+
|
72
|
+
def from_mongo(document)
|
73
|
+
instance = super
|
74
|
+
instance.send(:instantiate, document)
|
75
|
+
instance
|
76
|
+
end
|
77
|
+
|
78
|
+
def collection_name
|
79
|
+
if superclass.abstract_class?
|
80
|
+
read_inheritable_attribute(:collection_name) || name.tableize.gsub(/\//, '.')
|
81
|
+
else
|
82
|
+
superclass.collection_name
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def collection
|
87
|
+
@_collection ||= database.collection(collection_name)
|
88
|
+
end
|
89
|
+
|
90
|
+
def database
|
91
|
+
MongoModel.database
|
92
|
+
end
|
93
|
+
|
94
|
+
def save_safely?
|
95
|
+
@save_safely
|
96
|
+
end
|
97
|
+
|
98
|
+
def save_safely=(val)
|
99
|
+
@save_safely = val
|
100
|
+
end
|
101
|
+
|
102
|
+
private
|
103
|
+
def id_to_conditions(id_or_conditions)
|
104
|
+
case id_or_conditions
|
105
|
+
when String
|
106
|
+
{ :id => id_or_conditions }
|
107
|
+
when Array
|
108
|
+
{ :id.in => id_or_conditions }
|
109
|
+
else
|
110
|
+
id_or_conditions
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
def create_or_update
|
117
|
+
result = new_record? ? create : update
|
118
|
+
result != false
|
119
|
+
end
|
120
|
+
|
121
|
+
def create
|
122
|
+
save_to_collection
|
123
|
+
end
|
124
|
+
|
125
|
+
def update
|
126
|
+
save_to_collection
|
127
|
+
end
|
128
|
+
|
129
|
+
def save_to_collection
|
130
|
+
collection.save(to_mongo, :safe => self.class.save_safely?)
|
131
|
+
set_new_record(false)
|
132
|
+
true
|
133
|
+
rescue Mongo::OperationFailure => e
|
134
|
+
false
|
135
|
+
end
|
136
|
+
|
137
|
+
def instantiate(document)
|
138
|
+
attributes.from_mongo!(document)
|
139
|
+
set_new_record(false)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'active_support/core_ext/module/aliasing'
|
2
|
+
require 'active_support/core_ext/module/delegation'
|
3
|
+
require 'active_support/core_ext/object/metaclass'
|
4
|
+
|
5
|
+
module MongoModel
|
6
|
+
module DocumentExtensions
|
7
|
+
module Scopes
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
|
10
|
+
included do
|
11
|
+
class << self
|
12
|
+
[:find, :count].each do |method|
|
13
|
+
alias_method_chain method, :scope
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
named_scope :scoped, lambda { |scope| scope }
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
protected
|
22
|
+
#
|
23
|
+
def named_scope(name, options={})
|
24
|
+
named_scopes[name] = options
|
25
|
+
|
26
|
+
metaclass.instance_eval do
|
27
|
+
define_method(name) do |*args|
|
28
|
+
scope(name).apply(*args)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
def default_scope(options={})
|
35
|
+
if options.empty?
|
36
|
+
read_inheritable_attribute(:default_scope) || write_inheritable_attribute(:default_scope, Scope.new(self))
|
37
|
+
else
|
38
|
+
write_inheritable_attribute(:default_scope, default_scope.merge(:find => options))
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
#
|
43
|
+
def with_scope(options={}, &block)
|
44
|
+
push_scope(current_scope.merge(options), &block)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def with_exclusive_scope(options={}, &block)
|
49
|
+
push_scope(Scope.new(self, options), &block)
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
def push_scope(scope, &block)
|
54
|
+
scopes << scope
|
55
|
+
yield
|
56
|
+
ensure
|
57
|
+
scopes.pop
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_with_scope(*args)
|
61
|
+
options = args.extract_options!
|
62
|
+
options = current_scope.options_for(:find).deep_merge(options)
|
63
|
+
|
64
|
+
find_without_scope(*(args << options))
|
65
|
+
end
|
66
|
+
|
67
|
+
def count_with_scope(conditions={})
|
68
|
+
scope_conditions = current_scope.options_for(:find)[:conditions] || {}
|
69
|
+
|
70
|
+
count_without_scope(scope_conditions.deep_merge(conditions))
|
71
|
+
end
|
72
|
+
|
73
|
+
def named_scopes
|
74
|
+
read_inheritable_attribute(:named_scopes) || write_inheritable_attribute(:named_scopes, {})
|
75
|
+
end
|
76
|
+
|
77
|
+
def scope(name)
|
78
|
+
Scope.new(self, named_scopes[name]) if named_scopes[name]
|
79
|
+
end
|
80
|
+
|
81
|
+
def scopes
|
82
|
+
Thread.current[:"#{self}_scopes"] ||= [ default_scope.dup ]
|
83
|
+
end
|
84
|
+
|
85
|
+
def current_scope
|
86
|
+
scopes.last
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class Scope
|
92
|
+
attr_reader :model, :options
|
93
|
+
|
94
|
+
delegate :with_scope, :with_exclusive_scope, :scope, :to => :model
|
95
|
+
delegate :inspect, :to => :proxy_found
|
96
|
+
|
97
|
+
def initialize(model, options={})
|
98
|
+
if options.is_a?(Proc) || options.has_key?(:find) || options.has_key?(:create)
|
99
|
+
@options = options
|
100
|
+
else
|
101
|
+
@exclusive = options.delete(:exclusive)
|
102
|
+
@options = { :find => options }
|
103
|
+
end
|
104
|
+
|
105
|
+
@model = model
|
106
|
+
end
|
107
|
+
|
108
|
+
def merge(scope)
|
109
|
+
raise ArgumentError, "Scope must be applied before it can be merged" unless options.is_a?(Hash)
|
110
|
+
options_to_merge = scope.is_a?(self.class) ? scope.options : scope
|
111
|
+
self.class.new(model, options.deep_merge(options_to_merge))
|
112
|
+
end
|
113
|
+
|
114
|
+
def merge!(scope)
|
115
|
+
raise ArgumentError, "Scope must be applied before it can be merged" unless options.is_a?(Hash)
|
116
|
+
options_to_merge = scope.is_a?(self.class) ? scope.options : scope
|
117
|
+
options.deep_merge!(options_to_merge)
|
118
|
+
self
|
119
|
+
end
|
120
|
+
|
121
|
+
def reload
|
122
|
+
@found = nil
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def apply(*args)
|
127
|
+
if options.is_a?(Hash)
|
128
|
+
reload
|
129
|
+
else
|
130
|
+
self.class.new(model, options.call(*args))
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def options_for(action=:find)
|
135
|
+
options[action] || {}
|
136
|
+
end
|
137
|
+
|
138
|
+
def exclusive?
|
139
|
+
@exclusive == true
|
140
|
+
end
|
141
|
+
|
142
|
+
protected
|
143
|
+
def proxy_found
|
144
|
+
@found ||= find(:all)
|
145
|
+
end
|
146
|
+
|
147
|
+
private
|
148
|
+
def method_missing(method, *args, &block)
|
149
|
+
if scope = scope(method)
|
150
|
+
scope.exclusive? ? scope : merge(scope.apply(*args))
|
151
|
+
else
|
152
|
+
send(scope_type, options) { model.send(method, *args, &block) }
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def scope_type
|
157
|
+
exclusive? ? :with_exclusive_scope : :with_scope
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module MongoModel
|
2
|
+
module DocumentExtensions
|
3
|
+
module Validations
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
alias_method_chain :save, :validation
|
8
|
+
alias_method_chain :save!, :validation
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def property(name, *args, &block) #:nodoc:
|
13
|
+
property = super
|
14
|
+
|
15
|
+
validates_uniqueness_of(name) if property.options[:unique]
|
16
|
+
|
17
|
+
property
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates an object just like Document.create but calls save! instead of save
|
21
|
+
# so an exception is raised if the document is invalid.
|
22
|
+
def create!(attributes={}, &block)
|
23
|
+
if attributes.is_a?(Array)
|
24
|
+
attributes.map { |attrs| create!(attrs, &block) }
|
25
|
+
else
|
26
|
+
object = new(attributes, &block)
|
27
|
+
object.save!
|
28
|
+
object
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# The validation process on save can be skipped by passing false. The regular Document#save method is
|
34
|
+
# replaced with this when the validations module is mixed in, which it is by default.
|
35
|
+
def save_with_validation(perform_validation = true)
|
36
|
+
if perform_validation && valid? || !perform_validation
|
37
|
+
begin
|
38
|
+
save_without_validation
|
39
|
+
rescue DocumentNotSaved
|
40
|
+
valid?
|
41
|
+
false
|
42
|
+
end
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Attempts to save the document just like Document#save but will raise a DocumentInvalid exception
|
49
|
+
# instead of returning false if the document is not valid.
|
50
|
+
def save_with_validation!
|
51
|
+
if valid?
|
52
|
+
begin
|
53
|
+
save_without_validation!
|
54
|
+
rescue DocumentNotSaved => e
|
55
|
+
raise valid? ? e : DocumentInvalid.new(self)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
raise DocumentInvalid.new(self)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
Dir[File.dirname(__FILE__) + "/validations/*.rb"].sort.each do |path|
|
66
|
+
filename = File.basename(path)
|
67
|
+
require "mongomodel/document/validations/#{filename}"
|
68
|
+
end
|