mongomodel 0.2.20 → 0.3.0
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/Gemfile +1 -1
- data/bin/console +2 -0
- data/lib/mongomodel.rb +10 -7
- data/lib/mongomodel/attributes/mongo.rb +3 -2
- data/lib/mongomodel/attributes/typecasting.rb +6 -1
- data/lib/mongomodel/concerns/associations.rb +0 -61
- data/lib/mongomodel/concerns/associations/base/association.rb +1 -2
- data/lib/mongomodel/concerns/associations/belongs_to.rb +8 -3
- data/lib/mongomodel/concerns/associations/has_many_by_foreign_key.rb +7 -3
- data/lib/mongomodel/concerns/associations/has_many_by_ids.rb +5 -1
- data/lib/mongomodel/document.rb +1 -0
- data/lib/mongomodel/document/collection_modifiers.rb +83 -0
- data/lib/mongomodel/document/optimistic_locking.rb +1 -1
- data/lib/mongomodel/document/persistence.rb +4 -5
- data/lib/mongomodel/log_subscriber.rb +48 -0
- data/lib/mongomodel/railtie.rb +8 -0
- data/lib/mongomodel/railties/controller_runtime.rb +33 -0
- data/lib/mongomodel/support/collection.rb +1 -1
- data/lib/mongomodel/support/configuration.rb +1 -2
- data/lib/mongomodel/support/instrumented_collection.rb +122 -0
- data/lib/mongomodel/support/mongo_options.rb +18 -4
- data/lib/mongomodel/support/reference.rb +48 -6
- data/lib/mongomodel/support/scope.rb +8 -3
- data/lib/mongomodel/support/scope/finder_methods.rb +3 -2
- data/lib/mongomodel/support/scope/load_methods.rb +13 -0
- data/lib/mongomodel/support/scope/query_methods.rb +1 -1
- data/lib/mongomodel/support/types.rb +13 -10
- data/lib/mongomodel/support/types/array.rb +1 -1
- data/lib/mongomodel/support/types/hash.rb +1 -1
- data/lib/mongomodel/support/types/rational.rb +42 -0
- data/lib/mongomodel/tasks/database.rake +54 -2
- data/lib/mongomodel/version.rb +1 -1
- data/spec/mongomodel/attributes/store_spec.rb +15 -2
- data/spec/mongomodel/concerns/logging_spec.rb +1 -1
- data/spec/mongomodel/document/collection_modifiers_spec.rb +103 -0
- data/spec/mongomodel/document/persistence_spec.rb +3 -3
- data/spec/mongomodel/mongomodel_spec.rb +1 -1
- data/spec/mongomodel/support/scope_spec.rb +5 -1
- data/spec/support/helpers/document_finder_stubs.rb +4 -4
- metadata +13 -6
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'active_support/core_ext/module/attr_internal'
|
2
|
+
|
3
|
+
module MongoModel
|
4
|
+
module Railties
|
5
|
+
module ControllerRuntime
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
|
8
|
+
protected
|
9
|
+
attr_internal :db_runtime
|
10
|
+
|
11
|
+
def cleanup_view_runtime
|
12
|
+
db_rt_before_render = MongoModel::LogSubscriber.reset_runtime
|
13
|
+
runtime = super
|
14
|
+
db_rt_after_render = MongoModel::LogSubscriber.reset_runtime
|
15
|
+
self.db_runtime = db_rt_before_render + db_rt_after_render
|
16
|
+
runtime - db_rt_after_render
|
17
|
+
end
|
18
|
+
|
19
|
+
def append_info_to_payload(payload)
|
20
|
+
super
|
21
|
+
payload[:db_runtime] = db_runtime
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def log_process_action(payload)
|
26
|
+
messages, db_runtime = super, payload[:db_runtime]
|
27
|
+
messages << ("MongoModel: %.1fms" % db_runtime.to_f) if db_runtime
|
28
|
+
messages
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
# MongoModel::InstrumentedCursor & MongoModel::InstrumentedCollection are wrappers
|
2
|
+
# around Mongo::Cursor & Mongo::Collection respectively to add in support for
|
3
|
+
# ActiveSupport notifications.
|
4
|
+
#
|
5
|
+
# They are primarily used in MongoModel to implement logging.
|
6
|
+
module MongoModel
|
7
|
+
class InstrumentedCursor
|
8
|
+
attr_reader :cursor
|
9
|
+
|
10
|
+
def initialize(cursor)
|
11
|
+
@cursor = cursor
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_a
|
15
|
+
instrument(query_description) do
|
16
|
+
cursor.to_a
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def count
|
21
|
+
instrument("count(#{cursor.selector.inspect})") do
|
22
|
+
cursor.count
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def method_missing(method, *args, &block)
|
28
|
+
cursor.send(method, *args, &block)
|
29
|
+
end
|
30
|
+
|
31
|
+
def query_description
|
32
|
+
"find(#{cursor.selector.inspect}, #{cursor.fields ? cursor.fields.inspect : '{}'})" +
|
33
|
+
"#{cursor.skip != 0 ? ('.skip(' + cursor.skip.to_s + ')') : ''}#{cursor.limit != 0 ? ('.limit(' + cursor.limit.to_s + ')') : ''}" +
|
34
|
+
"#{cursor.order ? ('.sort(' + cursor.order.inspect + ')') : ''}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def instrument(query, &block)
|
38
|
+
ActiveSupport::Notifications.instrument("query.mongomodel", :collection => cursor.collection.name, :query => query, &block)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class InstrumentedCollection
|
43
|
+
attr_reader :collection
|
44
|
+
|
45
|
+
def initialize(collection)
|
46
|
+
@collection = collection
|
47
|
+
end
|
48
|
+
|
49
|
+
def ==(other)
|
50
|
+
case other
|
51
|
+
when self.class
|
52
|
+
collection == other.collection
|
53
|
+
else
|
54
|
+
collection == other
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def find(selector={}, options={})
|
59
|
+
cursor = InstrumentedCursor.new(collection.find(selector, options))
|
60
|
+
|
61
|
+
if block_given?
|
62
|
+
yield cursor
|
63
|
+
cursor.close
|
64
|
+
nil
|
65
|
+
else
|
66
|
+
cursor
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def save(doc, options={})
|
71
|
+
if doc.has_key?(:_id) || doc.has_key?('_id')
|
72
|
+
selector = { '_id' => doc[:_id] || doc['_id'] }
|
73
|
+
instrument("update(#{selector.inspect}, #{doc.inspect})") do
|
74
|
+
collection.save(doc, options)
|
75
|
+
end
|
76
|
+
else
|
77
|
+
instrument("insert(#{doc})") do
|
78
|
+
collection.insert(doc, options)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def remove(selector={}, options={})
|
84
|
+
instrument("remove(#{selector.inspect})") do
|
85
|
+
collection.remove(selector, options)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def update(selector, document, options={})
|
90
|
+
instrument("update(#{selector.inspect}, #{document.inspect})") do
|
91
|
+
collection.update(selector, document, options)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def create_index(spec, options={})
|
96
|
+
instrument("create_index(#{spec.inspect})") do
|
97
|
+
collection.create_index(spec, options)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def group(key, condition, initial, reduce, finalize=nil)
|
102
|
+
instrument("group(#{key.inspect}, #{condition.inspect})") do
|
103
|
+
collection.group(key, condition, initial, reduce, finalize)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def distinct(key, query=nil)
|
108
|
+
instrument("distinct(#{key.inspect}#{query.present? ? ', ' + query.inspect : ''})") do
|
109
|
+
collection.distinct(key, query)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def method_missing(method, *args, &block)
|
115
|
+
collection.send(method, *args, &block)
|
116
|
+
end
|
117
|
+
|
118
|
+
def instrument(query, &block)
|
119
|
+
ActiveSupport::Notifications.instrument("query.mongomodel", :collection => collection.name, :query => query, &block)
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
@@ -28,15 +28,29 @@ module MongoModel
|
|
28
28
|
(options[:conditions] || {}).each do |k, v|
|
29
29
|
if k.is_a?(MongoOperator)
|
30
30
|
key = k.field
|
31
|
-
value = k.to_mongo_selector(v)
|
32
31
|
else
|
33
32
|
key = k
|
34
|
-
value = v
|
35
33
|
end
|
36
34
|
|
37
|
-
property = @model.properties[key]
|
35
|
+
if property = @model.properties[key]
|
36
|
+
key = property.as
|
37
|
+
|
38
|
+
if k.is_a?(MongoOperator)
|
39
|
+
value = k.to_mongo_selector(v.is_a?(Array) ? v.map { |i| property.to_mongo(property.cast(i)) } : property.to_mongo(property.cast(v)))
|
40
|
+
else
|
41
|
+
value = property.to_mongo(property.cast(v))
|
42
|
+
end
|
43
|
+
else
|
44
|
+
converter = Types.converter_for(value.class)
|
45
|
+
|
46
|
+
if k.is_a?(MongoOperator)
|
47
|
+
value = k.to_mongo_selector(converter.to_mongo(v))
|
48
|
+
else
|
49
|
+
value = converter.to_mongo(v)
|
50
|
+
end
|
51
|
+
end
|
38
52
|
|
39
|
-
result[
|
53
|
+
result[key] = value
|
40
54
|
end
|
41
55
|
|
42
56
|
result
|
@@ -1,14 +1,56 @@
|
|
1
1
|
module MongoModel
|
2
|
-
class Reference
|
2
|
+
class Reference
|
3
|
+
attr_reader :id
|
4
|
+
|
5
|
+
def initialize(id)
|
6
|
+
@id = id
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_s
|
10
|
+
id.to_s
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash
|
14
|
+
id.hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def as_json(*)
|
18
|
+
to_s
|
19
|
+
end
|
20
|
+
|
21
|
+
def blank?
|
22
|
+
id.blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
def eql?(other)
|
26
|
+
case other
|
27
|
+
when Reference
|
28
|
+
id.to_s == other.id.to_s
|
29
|
+
else
|
30
|
+
id.to_s == other.to_s
|
31
|
+
end
|
32
|
+
end
|
33
|
+
alias_method :==, :eql?
|
34
|
+
|
35
|
+
def to_mongo
|
36
|
+
id.blank? ? nil : id
|
37
|
+
end
|
38
|
+
|
3
39
|
def self.cast(value)
|
4
|
-
value = value.to_s if value.respond_to?(:to_s)
|
5
|
-
|
6
40
|
case value
|
7
|
-
when
|
8
|
-
nil
|
9
|
-
else
|
41
|
+
when BSON::ObjectId
|
10
42
|
new(value)
|
43
|
+
else
|
44
|
+
if BSON::ObjectId.legal?(value.to_s)
|
45
|
+
new(BSON::ObjectId(value.to_s))
|
46
|
+
else
|
47
|
+
new(value.to_s)
|
48
|
+
end
|
11
49
|
end
|
12
50
|
end
|
51
|
+
|
52
|
+
def self.from_mongo(value)
|
53
|
+
cast(value)
|
54
|
+
end
|
13
55
|
end
|
14
56
|
end
|
@@ -8,11 +8,12 @@ module MongoModel
|
|
8
8
|
autoload :SpawnMethods, 'mongomodel/support/scope/spawn_methods'
|
9
9
|
autoload :QueryMethods, 'mongomodel/support/scope/query_methods'
|
10
10
|
autoload :FinderMethods, 'mongomodel/support/scope/finder_methods'
|
11
|
+
autoload :LoadMethods, 'mongomodel/support/scope/load_methods'
|
11
12
|
autoload :DynamicFinders, 'mongomodel/support/scope/dynamic_finders'
|
12
13
|
autoload :Pagination, 'mongomodel/support/scope/pagination'
|
13
14
|
autoload :Batches, 'mongomodel/support/scope/batches'
|
14
15
|
|
15
|
-
include Batches, Pagination, DynamicFinders, FinderMethods, QueryMethods, SpawnMethods
|
16
|
+
include Batches, Pagination, DynamicFinders, LoadMethods, FinderMethods, QueryMethods, SpawnMethods
|
16
17
|
|
17
18
|
delegate :inspect, :as_json, :to => :to_a
|
18
19
|
|
@@ -76,7 +77,7 @@ module MongoModel
|
|
76
77
|
|
77
78
|
def delete_all
|
78
79
|
selector = MongoOptions.new(klass, :conditions => finder_conditions).selector
|
79
|
-
collection.remove(selector)
|
80
|
+
collection.remove(selector, {})
|
80
81
|
reset
|
81
82
|
end
|
82
83
|
|
@@ -179,7 +180,11 @@ module MongoModel
|
|
179
180
|
end
|
180
181
|
|
181
182
|
def _find_and_instantiate
|
182
|
-
_find.to_a.map { |doc|
|
183
|
+
_find.to_a.map { |doc|
|
184
|
+
klass.from_mongo(doc).tap { |instance|
|
185
|
+
on_load_proc.call(instance) if on_load_proc
|
186
|
+
}
|
187
|
+
}
|
183
188
|
end
|
184
189
|
|
185
190
|
def finder_conditions
|
@@ -11,10 +11,11 @@ module MongoModel
|
|
11
11
|
when 0
|
12
12
|
raise ArgumentError, "At least one id must be specified"
|
13
13
|
when 1
|
14
|
-
id = ids.first
|
14
|
+
id = ids.first
|
15
15
|
where(:id => id).first || raise(DocumentNotFound, "Couldn't find document with id: #{id}")
|
16
16
|
else
|
17
|
-
|
17
|
+
ids = ids.map { |id| Reference.cast(id) }
|
18
|
+
docs = where(:id.in => ids).to_a
|
18
19
|
raise DocumentNotFound if docs.size != ids.size
|
19
20
|
docs.sort_by { |doc| ids.index(doc.id) }
|
20
21
|
end
|
@@ -33,7 +33,7 @@ module MongoModel
|
|
33
33
|
|
34
34
|
def from(value, &block)
|
35
35
|
new_scope = clone
|
36
|
-
new_scope.from_value = value.is_a?(String) ? klass.database.collection(value) : value
|
36
|
+
new_scope.from_value = InstrumentedCollection.new(value.is_a?(String) ? klass.database.collection(value) : value)
|
37
37
|
new_scope
|
38
38
|
end
|
39
39
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'rational' unless RUBY_VERSION >= '1.9.2'
|
1
2
|
require 'set'
|
2
3
|
|
3
4
|
require 'mongomodel/support/types/object'
|
@@ -12,20 +13,22 @@ require 'mongomodel/support/types/custom'
|
|
12
13
|
require 'mongomodel/support/types/array'
|
13
14
|
require 'mongomodel/support/types/set'
|
14
15
|
require 'mongomodel/support/types/hash'
|
16
|
+
require 'mongomodel/support/types/rational'
|
15
17
|
|
16
18
|
module MongoModel
|
17
19
|
module Types
|
18
20
|
CONVERTERS = {
|
19
|
-
::String
|
20
|
-
::Integer
|
21
|
-
::Float
|
22
|
-
::Boolean
|
23
|
-
::Symbol
|
24
|
-
::Date
|
25
|
-
::Time
|
26
|
-
::Array
|
27
|
-
::Set
|
28
|
-
::Hash
|
21
|
+
::String => Types::String.new,
|
22
|
+
::Integer => Types::Integer.new,
|
23
|
+
::Float => Types::Float.new,
|
24
|
+
::Boolean => Types::Boolean.new,
|
25
|
+
::Symbol => Types::Symbol.new,
|
26
|
+
::Date => Types::Date.new,
|
27
|
+
::Time => Types::Time.new,
|
28
|
+
::Array => Types::Array.new,
|
29
|
+
::Set => Types::Set.new,
|
30
|
+
::Hash => Types::Hash.new,
|
31
|
+
::Rational => Types::Rational.new
|
29
32
|
}
|
30
33
|
|
31
34
|
def self.converter_for(type)
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module MongoModel
|
2
|
+
module Types
|
3
|
+
class Rational < Object
|
4
|
+
if RUBY_VERSION >= '1.9.2'
|
5
|
+
def cast(value)
|
6
|
+
Rational(value)
|
7
|
+
end
|
8
|
+
else
|
9
|
+
def cast(value)
|
10
|
+
case value
|
11
|
+
when ::Rational
|
12
|
+
value
|
13
|
+
when ::String
|
14
|
+
rational_from_string(value)
|
15
|
+
else
|
16
|
+
Rational(value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def from_mongo(value)
|
22
|
+
rational_from_string(value)
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_mongo(value)
|
26
|
+
value.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
if RUBY_VERSION >= '1.9.2'
|
31
|
+
def rational_from_string(str)
|
32
|
+
Rational(str)
|
33
|
+
end
|
34
|
+
else
|
35
|
+
def rational_from_string(str)
|
36
|
+
numerator, denominator = str.split("/", 2)
|
37
|
+
Rational(numerator.to_i, (denominator || 1).to_i)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|