mongodb 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/Rakefile +3 -5
- data/lib/mongodb/driver.rb +33 -0
- data/lib/mongodb/driver/collection.rb +156 -0
- data/lib/mongodb/driver/database.rb +6 -0
- data/lib/mongodb/driver/dynamic_finders.rb +41 -0
- data/lib/{mongo_db → mongodb}/driver/spec.rb +12 -12
- data/lib/mongodb/gems.rb +6 -0
- data/lib/mongodb/integration/locales.rb +4 -0
- data/lib/mongodb/integration/locales/activemodel/ru.yml +27 -0
- data/lib/mongodb/migration.rb +8 -0
- data/lib/mongodb/migration/definition.rb +19 -0
- data/lib/mongodb/migration/migration.rb +68 -0
- data/lib/mongodb/migration/tasks.rb +19 -0
- data/lib/mongodb/model.rb +26 -0
- data/lib/mongodb/model/assignment.rb +65 -0
- data/lib/mongodb/model/attribute_convertors.rb +54 -0
- data/lib/mongodb/model/callbacks.rb +36 -0
- data/lib/mongodb/model/crud.rb +57 -0
- data/lib/mongodb/model/db.rb +53 -0
- data/lib/mongodb/model/misc.rb +33 -0
- data/lib/mongodb/model/model.rb +11 -0
- data/lib/mongodb/model/query.rb +36 -0
- data/lib/mongodb/model/scope.rb +99 -0
- data/lib/mongodb/model/spec.rb +12 -0
- data/lib/mongodb/model/support/types.rb +110 -0
- data/lib/mongodb/model/validation.rb +5 -0
- data/lib/mongodb/object.rb +18 -0
- data/lib/mongodb/object/object_helper.rb +62 -0
- data/lib/mongodb/object/object_serializer.rb +273 -0
- data/readme.md +261 -6
- data/spec/driver/collection_spec.rb +83 -0
- data/spec/{mongo_model/hash → driver}/crud_spec.rb +30 -29
- data/spec/driver/database_spec.rb +9 -0
- data/spec/driver/dynamic_finders_spec.rb +50 -0
- data/spec/driver/fixes_spec.rb +12 -0
- data/spec/driver/hash_helper_spec.rb +24 -0
- data/spec/driver/spec_helper.rb +28 -0
- data/spec/integration/am_conversion_spec.rb +1 -0
- data/spec/integration/am_validation_spec.rb +34 -0
- data/spec/integration/validatable2_spec.rb +40 -0
- data/spec/migration/migration_spec.rb +60 -0
- data/spec/model/assignment_spec.rb +80 -0
- data/spec/model/attribute_convertors_spec.rb +73 -0
- data/spec/model/callbacks_spec.rb +47 -0
- data/spec/model/crud_spec.rb +151 -0
- data/spec/model/db_spec.rb +63 -0
- data/spec/model/misc_spec.rb +58 -0
- data/spec/model/query_spec.rb +47 -0
- data/spec/model/scope_spec.rb +149 -0
- data/spec/model/spec_helper.rb +4 -0
- data/spec/model/validation_spec.rb +37 -0
- data/spec/object/callbacks_spec.rb +97 -0
- data/spec/object/crud_shared.rb +53 -0
- data/spec/object/crud_spec.rb +55 -0
- data/spec/object/spec_helper.rb +14 -0
- data/spec/{mongo_model/object → object}/validation_spec.rb +38 -36
- metadata +92 -25
- data/lib/mongo_db.rb +0 -3
- data/lib/mongo_db/driver.rb +0 -5
- data/lib/mongo_db/driver/connection.rb +0 -0
- data/lib/mongo_db/driver/database.rb +0 -5
- data/lib/mongo_db/gems.rb +0 -2
- data/lib/mongo_db/model.rb +0 -0
- data/spec/mongo_ext/migration_spec.rb +0 -0
- data/spec/mongo_ext/misc_spec.rb +0 -10
- data/spec/mongo_ext/spec_helper.rb +0 -4
- data/spec/mongo_model/model/crud_spec.rb +0 -123
- data/spec/mongo_model/model/query_spec.rb +0 -0
- data/spec/mongo_model/object/callbacks_spec.rb +0 -100
- data/spec/mongo_model/object/crud_shared.rb +0 -53
- data/spec/mongo_model/object/crud_spec.rb +0 -45
- data/spec/mongo_model/spec_helper.rb +0 -1
- data/spec/query_spec.rb +0 -0
- data/spec/test_spec.rb +0 -5
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'mongodb/object'
|
2
|
+
require 'ruby_ext'
|
3
|
+
require 'i18n'
|
4
|
+
|
5
|
+
module Mongo::Model; end
|
6
|
+
|
7
|
+
%w(
|
8
|
+
support/types
|
9
|
+
|
10
|
+
db
|
11
|
+
assignment
|
12
|
+
callbacks
|
13
|
+
validation
|
14
|
+
crud
|
15
|
+
query
|
16
|
+
scope
|
17
|
+
attribute_convertors
|
18
|
+
misc
|
19
|
+
model
|
20
|
+
).each{|f| require "mongodb/model/#{f}"}
|
21
|
+
|
22
|
+
module Mongo
|
23
|
+
module Model
|
24
|
+
inherit Db, Assignment, Callbacks, Validation, Crud, Query, Scope, AttributeConvertors, Misc
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Mongo::Model::Assignment
|
2
|
+
class Dsl < BasicObject
|
3
|
+
def initialize
|
4
|
+
@attributes = {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.const_missing name
|
8
|
+
# BasicObject doesn't have access to any constants like String, Symbol, ...
|
9
|
+
::Object.const_get name
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_h; attributes end
|
13
|
+
|
14
|
+
protected
|
15
|
+
attr_reader :attributes
|
16
|
+
|
17
|
+
def method_missing attribute_name, *args
|
18
|
+
attribute_name.must_be.a Symbol
|
19
|
+
|
20
|
+
args.size.must_be.in 1..2
|
21
|
+
if args.first.is_a? Class
|
22
|
+
type, mass_assignment = args
|
23
|
+
mass_assignment ||= false
|
24
|
+
type.must.respond_to :cast
|
25
|
+
else
|
26
|
+
type, mass_assignment = nil, args.first
|
27
|
+
end
|
28
|
+
|
29
|
+
attributes[attribute_name] = [type, mass_assignment]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def set attributes, options = {}
|
34
|
+
if rules = self.class._assign
|
35
|
+
force = options[:force]
|
36
|
+
attributes.each do |n, v|
|
37
|
+
n = n.to_sym
|
38
|
+
if rule = rules[n]
|
39
|
+
type, mass_assignment = rule
|
40
|
+
if mass_assignment or force
|
41
|
+
v = type.cast(v) if type
|
42
|
+
send "#{n}=", v
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
else
|
47
|
+
attributes.each{|n, v| send "#{n}=", v}
|
48
|
+
end
|
49
|
+
self
|
50
|
+
end
|
51
|
+
|
52
|
+
def set! attributes, options = {}
|
53
|
+
set attributes, options.merge(force: true)
|
54
|
+
end
|
55
|
+
|
56
|
+
module ClassMethods
|
57
|
+
inheritable_accessor :_assign, nil
|
58
|
+
|
59
|
+
def assign &block
|
60
|
+
dsl = ::Mongo::Model::Assignment::Dsl.new
|
61
|
+
dsl.instance_eval &block
|
62
|
+
self._assign = (_assign || {}).merge dsl.to_h
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Mongo::Model::AttributeConvertors
|
5
|
+
CONVERTORS = {
|
6
|
+
line: {
|
7
|
+
from_string: -> s {(s || "").split(',').collect{|s| s.strip}},
|
8
|
+
to_string: -> v {v.join(', ')}
|
9
|
+
},
|
10
|
+
column: {
|
11
|
+
from_string: -> s {(s || "").split("\n").collect{|s| s.strip}},
|
12
|
+
to_string: -> v {v.join("\n")}
|
13
|
+
},
|
14
|
+
yaml: {
|
15
|
+
from_string: -> s {YAML.load s rescue {}},
|
16
|
+
to_string: -> v {v.to_yaml.strip}
|
17
|
+
},
|
18
|
+
json: {
|
19
|
+
from_string: -> s {JSON.parse s rescue {}},
|
20
|
+
to_string: -> v {v.to_json.strip}
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
def available_as_string name, converter_name
|
26
|
+
converter = CONVERTORS[converter_name]
|
27
|
+
raise "unknown converter name :#{converter_name} for :#{name} field!" unless converter
|
28
|
+
|
29
|
+
from_string, to_string = converter[:from_string], converter[:to_string]
|
30
|
+
name_as_string = "#{name}_as_string".to_sym
|
31
|
+
define_method name_as_string do
|
32
|
+
_cache[name_as_string] ||= to_string.call(send(name))
|
33
|
+
end
|
34
|
+
|
35
|
+
define_method "#{name_as_string}=" do |value|
|
36
|
+
_cache.delete name_as_string
|
37
|
+
self.send "#{name}=", from_string.call(value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def available_as_yaml name
|
42
|
+
raise "delimiter not specified for :#{name} field!" unless delimiter
|
43
|
+
method = "#{name}_as_string"
|
44
|
+
define_method method do
|
45
|
+
self.send(name).join(delimiter)
|
46
|
+
end
|
47
|
+
define_method "#{method}=" do |value|
|
48
|
+
value = (value || "").split(delimiter.strip).collect{|s| s.strip}
|
49
|
+
self.send "#{name}=", value
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mongo::Model::Callbacks
|
2
|
+
inherit RubyExt::Callbacks
|
3
|
+
|
4
|
+
def _run_callbacks type, method_name
|
5
|
+
if type == :before
|
6
|
+
run_before_callbacks method_name, method: method_name
|
7
|
+
elsif type == :after
|
8
|
+
run_after_callbacks method_name, method: method_name
|
9
|
+
else
|
10
|
+
raise "invalid callback type (#{type})!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
[:validate, :update, :save, :destroy].each do |method_name|
|
16
|
+
define_method "before_#{method_name}" do |*args, &block|
|
17
|
+
opt = args.extract_options!
|
18
|
+
if block
|
19
|
+
set_callback method_name, :before, opt, &block
|
20
|
+
else
|
21
|
+
opt[:terminator] = false unless opt.include? :terminator
|
22
|
+
args.each{|executor| set_callback method_name, :before, executor, opt}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
define_method "after_#{method_name}" do |*args, &block|
|
27
|
+
opt = args.extract_options!
|
28
|
+
if block
|
29
|
+
set_callback method_name, :after, opt, &block
|
30
|
+
else
|
31
|
+
args.each{|executor| set_callback method_name, :after, executor, opt}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Mongo::Model::Crud
|
2
|
+
def save opts = {}
|
3
|
+
with_collection opts do |collection, opts|
|
4
|
+
collection.save self, opts
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
def save! *args
|
9
|
+
save(*args) || raise(Mongo::Error, "can't save #{self.inspect}!")
|
10
|
+
end
|
11
|
+
|
12
|
+
def destroy opts = {}
|
13
|
+
with_collection opts do |collection, opts|
|
14
|
+
collection.destroy self, opts
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy! *args
|
19
|
+
destroy(*args) || raise(Mongo::Error, "can't destroy #{self.inspect}!")
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
def build attributes, opts = {}
|
24
|
+
self.new.set attributes, opts
|
25
|
+
end
|
26
|
+
|
27
|
+
def create attributes, opts = {}
|
28
|
+
o = build attributes, opts
|
29
|
+
o.save
|
30
|
+
o
|
31
|
+
end
|
32
|
+
|
33
|
+
def create! attributes, opts = {}
|
34
|
+
o = create attributes
|
35
|
+
raise(Mongo::Error, "can't create #{attributes.inspect}!") if o.new_record?
|
36
|
+
o
|
37
|
+
end
|
38
|
+
|
39
|
+
def destroy_all selector = {}, opts = {}
|
40
|
+
success = true
|
41
|
+
collection = opts[:collection] || self.collection
|
42
|
+
each(selector){|o| success = false unless o.destroy}
|
43
|
+
success
|
44
|
+
end
|
45
|
+
|
46
|
+
def destroy_all! selector = {}, opts = {}
|
47
|
+
destroy_all(selector, opts) || raise(Mongo::Error, "can't destroy #{selector.inspect}!")
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
protected
|
52
|
+
def with_collection opts, &block
|
53
|
+
opts = opts.clone
|
54
|
+
collection = opts.delete(:collection) || self.class.collection
|
55
|
+
block.call collection, opts
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Mongo::Model::Db
|
2
|
+
module ClassMethods
|
3
|
+
inheritable_accessor :_db, nil
|
4
|
+
def db= v
|
5
|
+
self._db = if v.is_a? ::Proc
|
6
|
+
v
|
7
|
+
elsif v.is_a? ::Symbol
|
8
|
+
-> {::Mongo::Model.connection.db v.to_s}
|
9
|
+
else
|
10
|
+
-> {v}
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def db *args, &block
|
15
|
+
if block
|
16
|
+
self.db = block
|
17
|
+
elsif !args.empty?
|
18
|
+
args.size.must == 1
|
19
|
+
self.db = args.first
|
20
|
+
else
|
21
|
+
(_db && _db.call) || ::Mongo::Model.db
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
inheritable_accessor :_collection, nil
|
26
|
+
def collection= v
|
27
|
+
self._collection = if v.is_a? ::Proc
|
28
|
+
v
|
29
|
+
elsif v.is_a? ::Symbol
|
30
|
+
-> {db.collection v}
|
31
|
+
else
|
32
|
+
-> {v}
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def collection *args, &block
|
37
|
+
if block
|
38
|
+
self.collection = block
|
39
|
+
elsif !args.empty?
|
40
|
+
args.size.must == 1
|
41
|
+
self.collection = args.first
|
42
|
+
else
|
43
|
+
(_collection && _collection.call) || db.collection(default_collection_name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def default_collection_name
|
48
|
+
first_ancestor_class = ancestors.find{|a| a.is_a? Class} ||
|
49
|
+
raise("can't evaluate default collection name for #{self}!")
|
50
|
+
first_ancestor_class.alias.pluralize.underscore.to_sym
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Mongo::Model::Misc
|
2
|
+
def update_timestamps
|
3
|
+
now = Time.now.utc
|
4
|
+
self.created_at ||= now
|
5
|
+
self.updated_at = now
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def _cache
|
10
|
+
@_cache ||= {}
|
11
|
+
end
|
12
|
+
def _clear_cache
|
13
|
+
@_cache = {}
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def dom_id
|
18
|
+
# new_record? ? "new_#{self.class.name.underscore}" : to_param
|
19
|
+
to_param
|
20
|
+
end
|
21
|
+
|
22
|
+
def to_param
|
23
|
+
(_id || '').to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
def timestamps!
|
29
|
+
attr_accessor :created_at, :updated_at
|
30
|
+
before_save :update_timestamps
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Mongo::Model::Query
|
2
|
+
module ClassMethods
|
3
|
+
include Mongo::DynamicFinders
|
4
|
+
|
5
|
+
def count selector = {}, opts = {}
|
6
|
+
collection.count selector, opts
|
7
|
+
end
|
8
|
+
|
9
|
+
def first selector = {}, opts = {}
|
10
|
+
collection.first selector, opts
|
11
|
+
end
|
12
|
+
|
13
|
+
def each selector = {}, opts = {}, &block
|
14
|
+
collection.each selector, opts, &block
|
15
|
+
end
|
16
|
+
|
17
|
+
def all selector = {}, opts = {}, &block
|
18
|
+
if block
|
19
|
+
each selector, opts, &block
|
20
|
+
else
|
21
|
+
list = []
|
22
|
+
each(selector, opts){|doc| list << doc}
|
23
|
+
list
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def first! selector = {}, opts = {}
|
28
|
+
first(selector, opts) || raise(Mongo::NotFound, "document with selector #{selector} not found!")
|
29
|
+
end
|
30
|
+
|
31
|
+
def exists? selector = {}, opts = {}
|
32
|
+
count(selector, opts) > 0
|
33
|
+
end
|
34
|
+
alias :exist? :exists?
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
module Mongo::Model::Scope
|
2
|
+
class ScopeProxy < BasicObject
|
3
|
+
def initialize model, scope
|
4
|
+
@model, @scope = model, scope
|
5
|
+
end
|
6
|
+
|
7
|
+
def class
|
8
|
+
::Mongo::Model::Scope::ScopeProxy
|
9
|
+
end
|
10
|
+
|
11
|
+
def reverse_merge! scope
|
12
|
+
@scope = scope.merge @scope
|
13
|
+
end
|
14
|
+
|
15
|
+
def inspect
|
16
|
+
"#<ScopeProxy:{#{scope.inspect}}>"
|
17
|
+
end
|
18
|
+
alias_method :to_s, :inspect
|
19
|
+
|
20
|
+
protected
|
21
|
+
attr_reader :model, :scope
|
22
|
+
|
23
|
+
def method_missing method, *args, &block
|
24
|
+
model.with_scope scope do
|
25
|
+
result = model.send method, *args, &block
|
26
|
+
result.reverse_merge! scope if result.class == ::Mongo::Model::Scope::ScopeProxy
|
27
|
+
result
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module ClassMethods
|
33
|
+
def current_scope
|
34
|
+
scope, exclusive = Thread.current[:mongo_model_scope]
|
35
|
+
if exclusive
|
36
|
+
scope
|
37
|
+
elsif scope
|
38
|
+
default_scope.merge scope
|
39
|
+
else
|
40
|
+
default_scope
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def with_exclusive_scope options = {}, &block
|
45
|
+
with_scope options, true, &block
|
46
|
+
end
|
47
|
+
|
48
|
+
def with_scope options = {}, exclusive = false, &block
|
49
|
+
previous_options, previous_exclusive = Thread.current[:mongo_model_scope]
|
50
|
+
raise "exclusive scope already applied!" if previous_exclusive
|
51
|
+
|
52
|
+
begin
|
53
|
+
options = previous_options.merge options if previous_options and !exclusive
|
54
|
+
Thread.current[:mongo_model_scope] = [options, exclusive]
|
55
|
+
return block.call
|
56
|
+
ensure
|
57
|
+
Thread.current[:mongo_model_scope] = [previous_options, false]
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
inheritable_accessor :_default_scope, -> {{}}
|
62
|
+
def default_scope *args, &block
|
63
|
+
if block
|
64
|
+
self._default_scope = block
|
65
|
+
elsif !args.empty?
|
66
|
+
args.size.must == 1
|
67
|
+
args.first.must_be.a Hash
|
68
|
+
scope = args.first
|
69
|
+
self._default_scope = -> {args.first}
|
70
|
+
else
|
71
|
+
_default_scope.call
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def scope name, options = nil, &block
|
76
|
+
model = self
|
77
|
+
metaclass.define_method name do
|
78
|
+
scope = (block && block.call) || options
|
79
|
+
ScopeProxy.new model, scope
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
#
|
85
|
+
# finders
|
86
|
+
#
|
87
|
+
def count selector = {}, opts = {}
|
88
|
+
super current_scope.merge(selector), opts
|
89
|
+
end
|
90
|
+
|
91
|
+
def first selector = {}, opts = {}
|
92
|
+
super current_scope.merge(selector), opts
|
93
|
+
end
|
94
|
+
|
95
|
+
def each selector = {}, opts = {}, &block
|
96
|
+
super current_scope.merge(selector), opts, &block
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|