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
data/Rakefile
CHANGED
@@ -2,12 +2,10 @@ require 'rake_ext'
|
|
2
2
|
|
3
3
|
project(
|
4
4
|
name: "mongodb",
|
5
|
-
#
|
6
|
-
version: '0.0.1',
|
5
|
+
# version: '0.1.0',
|
7
6
|
gem: true,
|
8
|
-
summary: "
|
9
|
-
# Very small, schema-less, config-less Ruby ODM for MongoDB",
|
7
|
+
summary: "Object Model & Ruby driver enhancements for MongoDB",
|
10
8
|
|
11
9
|
author: "Alexey Petrushin",
|
12
|
-
homepage: "http://github.com/alexeypetrushin/
|
10
|
+
homepage: "http://github.com/alexeypetrushin/mongodb"
|
13
11
|
)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'mongodb/gems'
|
2
|
+
|
3
|
+
require 'mongo'
|
4
|
+
|
5
|
+
class Mongo::Error < StandardError; end
|
6
|
+
class Mongo::NotFound < Mongo::Error; end
|
7
|
+
|
8
|
+
%w(
|
9
|
+
database
|
10
|
+
collection
|
11
|
+
dynamic_finders
|
12
|
+
).each{|f| require "mongodb/driver/#{f}"}
|
13
|
+
|
14
|
+
# defaults
|
15
|
+
Mongo.class_eval do
|
16
|
+
class << self
|
17
|
+
def defaults; @defaults ||= {} end
|
18
|
+
attr_writer :defaults
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# database
|
23
|
+
Mongo::DB.send :include, Mongo::DBExt
|
24
|
+
|
25
|
+
# collection
|
26
|
+
Mongo::Collection.class_eval do
|
27
|
+
include Mongo::CollectionExt, Mongo::DynamicFinders
|
28
|
+
|
29
|
+
%w(insert update remove save count).each do |method|
|
30
|
+
alias_method "#{method}_without_ext", method
|
31
|
+
alias_method method, "#{method}_with_ext"
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'set'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
module Mongo::CollectionExt
|
5
|
+
#
|
6
|
+
# CRUD
|
7
|
+
#
|
8
|
+
def save_with_ext doc, opts = {}
|
9
|
+
save_without_ext doc, reverse_merge_defaults(opts, :safe)
|
10
|
+
end
|
11
|
+
|
12
|
+
def insert_with_ext args, opts = {}
|
13
|
+
result = insert_without_ext args, reverse_merge_defaults(opts, :safe)
|
14
|
+
|
15
|
+
# fix for mongodriver, it will return single result if we supply [doc] as args
|
16
|
+
(args.is_a?(Array) and !result.is_a?(Array)) ? [result] : result
|
17
|
+
end
|
18
|
+
|
19
|
+
def update_with_ext selector, doc, opts = {}
|
20
|
+
selector = convert_underscore_to_dollar_in_selector selector
|
21
|
+
doc = convert_underscore_to_dollar_in_update doc
|
22
|
+
|
23
|
+
# because :multi works only with $ operators, we need to check if it's applicable
|
24
|
+
opts = if doc.keys.any?{|k| k =~ /^\$/}
|
25
|
+
reverse_merge_defaults(opts, :safe, :multi)
|
26
|
+
else
|
27
|
+
reverse_merge_defaults(opts, :safe)
|
28
|
+
end
|
29
|
+
|
30
|
+
update_without_ext selector, doc, opts
|
31
|
+
end
|
32
|
+
|
33
|
+
def remove_with_ext selector = {}, opts = {}
|
34
|
+
selector = convert_underscore_to_dollar_in_selector selector
|
35
|
+
remove_without_ext selector, reverse_merge_defaults(opts, :safe, :multi)
|
36
|
+
end
|
37
|
+
|
38
|
+
def destroy *args
|
39
|
+
remove *args
|
40
|
+
end
|
41
|
+
|
42
|
+
def create *args
|
43
|
+
insert *args
|
44
|
+
end
|
45
|
+
|
46
|
+
#
|
47
|
+
# Querying
|
48
|
+
#
|
49
|
+
def first selector = {}, opts = {}
|
50
|
+
selector = convert_underscore_to_dollar_in_selector selector if selector.is_a? Hash
|
51
|
+
|
52
|
+
h = find_one selector, opts
|
53
|
+
symbolize_doc h
|
54
|
+
end
|
55
|
+
|
56
|
+
def first! selector = {}, opts = {}
|
57
|
+
first(selector, opts) || raise(Mongo::NotFound, "document with selector #{selector} not found!")
|
58
|
+
end
|
59
|
+
|
60
|
+
def all selector = {}, opts = {}, &block
|
61
|
+
if block
|
62
|
+
each selector, opts, &block
|
63
|
+
else
|
64
|
+
list = []
|
65
|
+
each(selector, opts){|doc| list << doc}
|
66
|
+
list
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def each selector = {}, opts = {}, &block
|
71
|
+
selector = convert_underscore_to_dollar_in_selector selector
|
72
|
+
|
73
|
+
cursor = nil
|
74
|
+
begin
|
75
|
+
cursor = find selector, reverse_merge_defaults(opts, :batch_size)
|
76
|
+
cursor.each do |doc|
|
77
|
+
doc = symbolize_doc doc
|
78
|
+
block.call doc
|
79
|
+
end
|
80
|
+
nil
|
81
|
+
ensure
|
82
|
+
cursor.close if cursor
|
83
|
+
end
|
84
|
+
nil
|
85
|
+
end
|
86
|
+
|
87
|
+
def count_with_ext selector = {}, opts = {}
|
88
|
+
find(selector, opts).count()
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
QUERY_KEYWORDS = [
|
93
|
+
:_lt, :_lte, :_gt, :_gte,
|
94
|
+
:_all, :_exists, :_mod, :_ne, :_in, :_nin,
|
95
|
+
:_nor, :_or, :_and,
|
96
|
+
:_size, :_type
|
97
|
+
].to_set
|
98
|
+
|
99
|
+
UPDATE_KEYWORDS = [
|
100
|
+
:_inc, :_set, :_unset, :_push, :_pushAll, :_addToSet, :_pop, :_pull, :_pullAll, :_rename, :_bit
|
101
|
+
].to_set
|
102
|
+
|
103
|
+
def reverse_merge_defaults opts, *keys
|
104
|
+
h = opts.clone
|
105
|
+
keys.each do |k|
|
106
|
+
h[k] = Mongo.defaults[k] if Mongo.defaults.include?(k) and !h.include?(k)
|
107
|
+
end
|
108
|
+
h
|
109
|
+
end
|
110
|
+
|
111
|
+
# symbolizing hashes
|
112
|
+
def symbolize_doc doc
|
113
|
+
return doc unless Mongo.defaults[:symbolize]
|
114
|
+
|
115
|
+
Mongo::CollectionExt.convert_doc doc do |k, v, result|
|
116
|
+
k = k.to_sym if k.is_a? String
|
117
|
+
result[k] = v
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# replaces :_lt to :$lt in query
|
122
|
+
def convert_underscore_to_dollar_in_selector selector
|
123
|
+
return selector unless Mongo.defaults[:convert_underscore_to_dollar]
|
124
|
+
|
125
|
+
Mongo::CollectionExt.convert_doc selector do |k, v, result|
|
126
|
+
k = "$#{k.to_s[1..-1]}".to_sym if QUERY_KEYWORDS.include?(k)
|
127
|
+
result[k] = v
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# replaces :_set to :$set in query
|
132
|
+
def convert_underscore_to_dollar_in_update update
|
133
|
+
return update unless Mongo.defaults[:convert_underscore_to_dollar]
|
134
|
+
|
135
|
+
Mongo::CollectionExt.convert_doc update do |k, v, result|
|
136
|
+
k = "$#{k.to_s[1..-1]}".to_sym if UPDATE_KEYWORDS.include?(k)
|
137
|
+
result[k] = v
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
# walks on hash and creates another (also works with nested & arrays)
|
142
|
+
def self.convert_doc doc, &block
|
143
|
+
if doc.is_a? Hash
|
144
|
+
result = {}
|
145
|
+
doc.each do |k, v|
|
146
|
+
v = convert_doc v, &block
|
147
|
+
block.call k, v, result
|
148
|
+
end
|
149
|
+
result
|
150
|
+
elsif doc.is_a? Array
|
151
|
+
doc.collect{|v| convert_doc v, &block}
|
152
|
+
else
|
153
|
+
doc
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Mongo::DynamicFinders
|
2
|
+
protected
|
3
|
+
#
|
4
|
+
# first_by_field, all_by_field, each_by_field, first_by_field
|
5
|
+
#
|
6
|
+
def method_missing clause, *args, &block
|
7
|
+
if clause =~ /^([a-z]_by_[a-z_])|(by_[a-z_])/
|
8
|
+
clause = clause.to_s
|
9
|
+
|
10
|
+
bang = clause =~ /!$/
|
11
|
+
clause = clause[0..-2] if bang
|
12
|
+
|
13
|
+
finder, field = if clause =~ /^by_/
|
14
|
+
['first', clause.sub(/by_/, '')]
|
15
|
+
else
|
16
|
+
clause.split(/_by_/, 2)
|
17
|
+
end
|
18
|
+
|
19
|
+
finder = 'first' if finder == 'find'
|
20
|
+
field = '_id' if field == 'id'
|
21
|
+
|
22
|
+
if bang
|
23
|
+
raise "You can't use bang version with :#{finder}!" unless finder == 'first'
|
24
|
+
finder = "#{finder}!"
|
25
|
+
end
|
26
|
+
|
27
|
+
raise "invalid arguments for finder (#{args})!" unless args.size == 1
|
28
|
+
field_value = args.first
|
29
|
+
|
30
|
+
finder, field = finder.to_sym, field.to_sym
|
31
|
+
|
32
|
+
if respond_to? finder
|
33
|
+
send finder, {field => field_value}, &block
|
34
|
+
else
|
35
|
+
super
|
36
|
+
end
|
37
|
+
else
|
38
|
+
super
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,31 +1,31 @@
|
|
1
|
-
|
1
|
+
MONGO_TEST_DATABASE_NAME = 'default_test'
|
2
2
|
|
3
|
-
rspec do
|
3
|
+
rspec do
|
4
4
|
def mongo
|
5
5
|
$mongo || raise('Mongo not defined (use :with_mongo helper)!')
|
6
6
|
end
|
7
|
-
|
8
|
-
def clear_mongo name =
|
7
|
+
|
8
|
+
def clear_mongo name = MONGO_TEST_DATABASE_NAME
|
9
9
|
mongo.db.collection_names.each do |name|
|
10
10
|
next if name =~ /^system\./
|
11
11
|
mongo.db.collection(name).drop
|
12
12
|
end
|
13
13
|
end
|
14
|
-
|
14
|
+
|
15
15
|
class << self
|
16
16
|
def with_mongo
|
17
|
-
require 'ostruct'
|
18
|
-
|
19
17
|
before :all do
|
18
|
+
require 'ostruct'
|
19
|
+
|
20
20
|
$mongo = OpenStruct.new.tap do |m|
|
21
21
|
m.connection = Mongo::Connection.new
|
22
|
-
m.db = m.connection.db
|
22
|
+
m.db = m.connection.db MONGO_TEST_DATABASE_NAME
|
23
23
|
end
|
24
|
-
end
|
24
|
+
end
|
25
25
|
after(:all){$mongo = nil}
|
26
|
-
|
27
|
-
before do
|
28
|
-
clear_mongo
|
26
|
+
|
27
|
+
before do
|
28
|
+
clear_mongo
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
data/lib/mongodb/gems.rb
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
ru:
|
2
|
+
errors:
|
3
|
+
# The default format to use in full error messages.
|
4
|
+
format: "%{attribute} %{message}"
|
5
|
+
|
6
|
+
# The values :model, :attribute and :value are always available for interpolation
|
7
|
+
# The value :count is available when applicable. Can be used for pluralization.
|
8
|
+
messages:
|
9
|
+
inclusion: "недопустимое значение"
|
10
|
+
exclusion: "недопустимо"
|
11
|
+
invalid: "неверно"
|
12
|
+
confirmation: "не совпадает с подтверждением"
|
13
|
+
accepted: "долно быть принято"
|
14
|
+
empty: "не может быть пустым"
|
15
|
+
blank: "не может быть пустым"
|
16
|
+
too_long: "слишком длинно (максимально допустимо %{count})"
|
17
|
+
too_short: "слищком коротко (минимально допустимо %{count})"
|
18
|
+
wrong_length: "неверная длинна (должно быть %{count})"
|
19
|
+
not_a_number: "не номер"
|
20
|
+
not_an_integer: "долно быть целочисленно"
|
21
|
+
greater_than: "должно быть больше чем %{count}"
|
22
|
+
greater_than_or_equal_to: "должно быть больше чем или равно %{count}"
|
23
|
+
equal_to: "должно быть равно %{count}"
|
24
|
+
less_than: "должно быть меньше чем %{count}"
|
25
|
+
less_than_or_equal_to: "должно быть меньше чем или равно %{count}"
|
26
|
+
odd: "должно быть не четно"
|
27
|
+
even: "должно быть четно"
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Mongo::Migration::Definition
|
2
|
+
def upgrade &block
|
3
|
+
if block
|
4
|
+
@upgrade = block
|
5
|
+
else
|
6
|
+
@upgrade
|
7
|
+
end
|
8
|
+
end
|
9
|
+
alias_method :up, :upgrade
|
10
|
+
|
11
|
+
def downgrade &block
|
12
|
+
if block
|
13
|
+
@downgrade = block
|
14
|
+
else
|
15
|
+
@downgrade
|
16
|
+
end
|
17
|
+
end
|
18
|
+
alias_method :down, :downgrade
|
19
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
class Mongo::Migration
|
2
|
+
def initialize db
|
3
|
+
@db, @definitions = db, {}
|
4
|
+
end
|
5
|
+
|
6
|
+
def add version, &block
|
7
|
+
raise "version should be an Integer! (but you provided '#{version}' instad)!" unless version.is_a? Integer
|
8
|
+
definition = Definition.new
|
9
|
+
block.call definition
|
10
|
+
definitions[version] = definition
|
11
|
+
end
|
12
|
+
|
13
|
+
def update version = nil
|
14
|
+
version ||= definitions.keys.max
|
15
|
+
|
16
|
+
if current_version == version
|
17
|
+
info "database '#{db.name}' already is of #{version} version, no migration needed"
|
18
|
+
return false
|
19
|
+
else
|
20
|
+
info "updating '#{db.name}' to #{version}"
|
21
|
+
end
|
22
|
+
|
23
|
+
increase_db_version while current_version < version
|
24
|
+
decrease_db_version while current_version > version
|
25
|
+
true
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_version
|
29
|
+
if doc = db.db_metadata.first(name: 'migration')
|
30
|
+
doc[:version] || doc['version']
|
31
|
+
else
|
32
|
+
0
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
attr_accessor :db, :definitions
|
38
|
+
|
39
|
+
def info msg
|
40
|
+
db.connection.logger and db.connection.logger.info(msg)
|
41
|
+
end
|
42
|
+
|
43
|
+
def update_version new_version
|
44
|
+
db.db_metadata.update({name: 'migration'}, {name: 'migration', version: new_version}, {upsert: true, safe: true})
|
45
|
+
end
|
46
|
+
|
47
|
+
def increase_db_version
|
48
|
+
new_version = current_version + 1
|
49
|
+
migration = definitions[new_version]
|
50
|
+
raise "no upgrade of #{db.name} database to #{new_version} version!" unless migration and migration.up
|
51
|
+
|
52
|
+
migration.up.call db
|
53
|
+
update_version new_version
|
54
|
+
|
55
|
+
info "database '#{db.name}' upgraded to #{new_version} version."
|
56
|
+
end
|
57
|
+
|
58
|
+
def decrease_db_version
|
59
|
+
new_version = current_version - 1
|
60
|
+
migration = definitions[new_version + 1]
|
61
|
+
raise "no downgrade of #{db.name} database to #{new_version} version!" unless migration and migration.down
|
62
|
+
|
63
|
+
migration.down.call db
|
64
|
+
update_version new_version
|
65
|
+
|
66
|
+
info "database '#{db.name}' downgraded to #{new_version} version."
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# namespace :db do
|
2
|
+
# desc "Migrate Database"
|
3
|
+
# task migrate: :migration_evnironment do
|
4
|
+
# require 'mongo_migration'
|
5
|
+
#
|
6
|
+
# database_name = (ENV['d'] || ENV['database'] || :default).to_sym
|
7
|
+
# version = ENV['v'] || ENV['version']
|
8
|
+
#
|
9
|
+
# if version.blank?
|
10
|
+
# size = Mongo.migration.definitions[database_name].size
|
11
|
+
# highest_defined_version = size == 0 ? 0 : size - 1
|
12
|
+
# version = highest_defined_version
|
13
|
+
# else
|
14
|
+
# version = version.to_i
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# Mongo.migration.update version, database_name
|
18
|
+
# end
|
19
|
+
# end
|