mongodb 0.0.1 → 0.0.2
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/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
|