mongodb 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. data/Rakefile +3 -5
  2. data/lib/mongodb/driver.rb +33 -0
  3. data/lib/mongodb/driver/collection.rb +156 -0
  4. data/lib/mongodb/driver/database.rb +6 -0
  5. data/lib/mongodb/driver/dynamic_finders.rb +41 -0
  6. data/lib/{mongo_db → mongodb}/driver/spec.rb +12 -12
  7. data/lib/mongodb/gems.rb +6 -0
  8. data/lib/mongodb/integration/locales.rb +4 -0
  9. data/lib/mongodb/integration/locales/activemodel/ru.yml +27 -0
  10. data/lib/mongodb/migration.rb +8 -0
  11. data/lib/mongodb/migration/definition.rb +19 -0
  12. data/lib/mongodb/migration/migration.rb +68 -0
  13. data/lib/mongodb/migration/tasks.rb +19 -0
  14. data/lib/mongodb/model.rb +26 -0
  15. data/lib/mongodb/model/assignment.rb +65 -0
  16. data/lib/mongodb/model/attribute_convertors.rb +54 -0
  17. data/lib/mongodb/model/callbacks.rb +36 -0
  18. data/lib/mongodb/model/crud.rb +57 -0
  19. data/lib/mongodb/model/db.rb +53 -0
  20. data/lib/mongodb/model/misc.rb +33 -0
  21. data/lib/mongodb/model/model.rb +11 -0
  22. data/lib/mongodb/model/query.rb +36 -0
  23. data/lib/mongodb/model/scope.rb +99 -0
  24. data/lib/mongodb/model/spec.rb +12 -0
  25. data/lib/mongodb/model/support/types.rb +110 -0
  26. data/lib/mongodb/model/validation.rb +5 -0
  27. data/lib/mongodb/object.rb +18 -0
  28. data/lib/mongodb/object/object_helper.rb +62 -0
  29. data/lib/mongodb/object/object_serializer.rb +273 -0
  30. data/readme.md +261 -6
  31. data/spec/driver/collection_spec.rb +83 -0
  32. data/spec/{mongo_model/hash → driver}/crud_spec.rb +30 -29
  33. data/spec/driver/database_spec.rb +9 -0
  34. data/spec/driver/dynamic_finders_spec.rb +50 -0
  35. data/spec/driver/fixes_spec.rb +12 -0
  36. data/spec/driver/hash_helper_spec.rb +24 -0
  37. data/spec/driver/spec_helper.rb +28 -0
  38. data/spec/integration/am_conversion_spec.rb +1 -0
  39. data/spec/integration/am_validation_spec.rb +34 -0
  40. data/spec/integration/validatable2_spec.rb +40 -0
  41. data/spec/migration/migration_spec.rb +60 -0
  42. data/spec/model/assignment_spec.rb +80 -0
  43. data/spec/model/attribute_convertors_spec.rb +73 -0
  44. data/spec/model/callbacks_spec.rb +47 -0
  45. data/spec/model/crud_spec.rb +151 -0
  46. data/spec/model/db_spec.rb +63 -0
  47. data/spec/model/misc_spec.rb +58 -0
  48. data/spec/model/query_spec.rb +47 -0
  49. data/spec/model/scope_spec.rb +149 -0
  50. data/spec/model/spec_helper.rb +4 -0
  51. data/spec/model/validation_spec.rb +37 -0
  52. data/spec/object/callbacks_spec.rb +97 -0
  53. data/spec/object/crud_shared.rb +53 -0
  54. data/spec/object/crud_spec.rb +55 -0
  55. data/spec/object/spec_helper.rb +14 -0
  56. data/spec/{mongo_model/object → object}/validation_spec.rb +38 -36
  57. metadata +92 -25
  58. data/lib/mongo_db.rb +0 -3
  59. data/lib/mongo_db/driver.rb +0 -5
  60. data/lib/mongo_db/driver/connection.rb +0 -0
  61. data/lib/mongo_db/driver/database.rb +0 -5
  62. data/lib/mongo_db/gems.rb +0 -2
  63. data/lib/mongo_db/model.rb +0 -0
  64. data/spec/mongo_ext/migration_spec.rb +0 -0
  65. data/spec/mongo_ext/misc_spec.rb +0 -10
  66. data/spec/mongo_ext/spec_helper.rb +0 -4
  67. data/spec/mongo_model/model/crud_spec.rb +0 -123
  68. data/spec/mongo_model/model/query_spec.rb +0 -0
  69. data/spec/mongo_model/object/callbacks_spec.rb +0 -100
  70. data/spec/mongo_model/object/crud_shared.rb +0 -53
  71. data/spec/mongo_model/object/crud_spec.rb +0 -45
  72. data/spec/mongo_model/spec_helper.rb +0 -1
  73. data/spec/query_spec.rb +0 -0
  74. 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
- # official_name: "mongo_db",
6
- version: '0.0.1',
5
+ # version: '0.1.0',
7
6
  gem: true,
8
- summary: "MongDB ODM. In development, don't use it, come back at 11/8/25",
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/mongo_db"
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,6 @@
1
+ module Mongo::DBExt
2
+ protected
3
+ def method_missing collection_name
4
+ self.collection collection_name
5
+ end
6
+ 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
- MONGO_TEST_DATABASE = 'default_test'
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 = MONGO_TEST_DATABASE
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 MONGO_TEST_DATABASE
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
@@ -0,0 +1,6 @@
1
+ gem 'mongo', '~> 1.3'
2
+ gem 'i18n', '>= 0.5'
3
+
4
+ if respond_to? :fake_gem
5
+ fake_gem 'ruby_ext'
6
+ end
@@ -0,0 +1,4 @@
1
+ require 'i18n'
2
+
3
+ dir = File.dirname __FILE__
4
+ I18n.load_path += Dir["#{dir}/locales/**/*.{rb,yml}"]
@@ -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,8 @@
1
+ require 'mongodb/driver'
2
+
3
+ class Mongo::Migration; end
4
+
5
+ %w(
6
+ definition
7
+ migration
8
+ ).each{|f| require "mongodb/migration/#{f}"}
@@ -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