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.
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/readme.md CHANGED
@@ -1,9 +1,264 @@
1
- # Ruby ODM for MongoDB
1
+ Object Model & Ruby driver enhancements for MongoDB.
2
2
 
3
- Not finished, in development.
3
+ 1. Driver enchancements & Migrations.
4
+ 2. Persistence for any Ruby object.
5
+ 3. Object Model (callbacks, validations, mass-assignment, finders, ...).
4
6
 
5
- - Very small.
6
- - Schema-less, dynamic.
7
+ Lower layers are independent from upper, use only what You need.
8
+
9
+ # MongoDB driver enhancements
10
+
11
+ MongoDB itself is very powerful, flexible and simple tool, but the API of the Ruby driver is a little complicated.
12
+ These enhancements alter the driver's API and made it more simple and intuitive.
13
+
14
+ - Makes API of mongo-ruby-driver friendly & handy.
15
+ - No extra abstraction or complexities introduced, all things are exactly the same as in MongoDB.
16
+ - 100% backward compatibility with original driver API (if not - it's a bug, report it please)
17
+
18
+ ``` ruby
19
+ require 'mongodb/driver'
20
+
21
+ # Changing some defaults.
22
+ Mongo.defaults.merge! symbolize: true, multi: true, safe: true
23
+
24
+ # Connection & db.
25
+ connection = Mongo::Connection.new
26
+ db = connection.db 'default_test'
27
+ db.units.drop
28
+
29
+ # Collection shortcuts.
30
+ db.some_collection
31
+
32
+ # Create.
33
+ zeratul = {name: 'Zeratul', stats: {attack: 85, life: 300, shield: 100}}
34
+ tassadar = {name: 'Tassadar', stats: {attack: 0, life: 80, shield: 300}}
35
+
36
+ db.units.save zeratul
37
+ db.units.save tassadar
38
+
39
+ # Udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it).
40
+ tassadar[:stats][:attack] = 20
41
+ db.units.save tassadar
42
+
43
+ # Querying first & all, there's also :each, the same as :all.
44
+ db.units.first name: 'Zeratul' # => zeratul
45
+ db.units.all name: 'Zeratul' # => [zeratul]
46
+ db.units.all name: 'Zeratul' do |unit|
47
+ unit # => zeratul
48
+ end
49
+
50
+ # Dynamic Finders (bang versions also availiable).
51
+ db.units.by_name 'Zeratul' # => zeratul
52
+ db.units.first_by_name 'Zeratul' # => zeratul
53
+ db.units.all_by_name 'Zeratul' # => [zeratul]
54
+
55
+ # Query sugar, use {name: {_gt: 'Z'}} instead of {name: {:$gt => 'Z'}}.
56
+ Mongo.defaults.merge! convert_underscore_to_dollar: true
57
+ db.units.all name: {_gt: 'Z'} # => [zeratul]
58
+ ```
59
+
60
+ Source: examples/driver.rb
61
+
62
+ More docs - there's no need for more docs, the whole point of this extension is to be small, intuitive, 100% compatible with the official driver, and require no extra knowledge.
63
+ So, please use standard Ruby driver documentation.
64
+
65
+ # Persistence for any Ruby object
66
+
67
+ Save any Ruby object to MongoDB, as if it's a document. Objects can be any type, simple or composite with other objects / arrays / hashes inside.
68
+
69
+ Note: the :initialize method should allow to create object without arguments.
70
+
71
+ ``` ruby
72
+ # Let's define the game unit.
73
+ class Unit
74
+ attr_reader :name, :stats
75
+
76
+ # don't forget to allow creating object with no arguments
77
+ def initialize name = nil, stats = nil
78
+ @name, @stats = name, stats
79
+ end
80
+
81
+ class Stats
82
+ attr_accessor :attack, :life, :shield
83
+
84
+ def initialize attack = nil, life = nil, shield = nil
85
+ @attack, @life, @shield = attack, life, shield
86
+ end
87
+ end
88
+ end
89
+
90
+ # Connecting to MongoDB.
91
+ require 'mongodb/object'
92
+ Mongo.defaults.merge! symbolize: true, multi: true, safe: true
93
+ connection = Mongo::Connection.new
94
+ db = connection.db 'default_test'
95
+ db.units.drop
96
+
97
+ # Create.
98
+ zeratul = Unit.new('Zeratul', Unit::Stats.new(85, 300, 100))
99
+ tassadar = Unit.new('Tassadar', Unit::Stats.new(0, 80, 300))
100
+
101
+ db.units.save zeratul
102
+ db.units.save tassadar
103
+
104
+ # Udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it).
105
+ tassadar.stats.attack = 20
106
+ db.units.save tassadar
107
+
108
+ # Querying first & all, there's also :each, the same as :all.
109
+ db.units.first name: 'Zeratul' # => zeratul
110
+ db.units.all name: 'Zeratul' # => [zeratul]
111
+ db.units.all name: 'Zeratul' do |unit|
112
+ unit # => zeratul
113
+ end
114
+
115
+ # Simple finders (bang versions also availiable).
116
+ db.units.by_name 'Zeratul' # => zeratul
117
+ db.units.first_by_name 'Zeratul' # => zeratul
118
+ db.units.all_by_name 'Zeratul' # => [zeratul]
119
+
120
+ # Query sugar, use {name: {_gt: 'Z'}} instead of {name: {:$gt => 'Z'}}.
121
+ Mongo.defaults.merge! convert_underscore_to_dollar: true
122
+ db.units.all name: {_gt: 'Z'} # => [zeratul]
123
+ ```
124
+
125
+ Source: examples/object.rb
126
+
127
+ # Object Model
128
+
129
+ - The same API for pure driver and Models.
130
+ - Minimum extra abstractions, trying to keep things as close to the MongoDB semantic as possible.
131
+ - Schema-less, dynamic (with ability to specify types for mass-assignment).
7
132
  - Models can be saved to any collection.
8
- - Full support for embedded objects (and MDD composite pattern).
9
- - Doesn't try to mimic ActiveRecord, it's differrent and designed to get most of MongoDB.
133
+ - Full support for embedded objects (validations, callbacks, ...).
134
+ - Scope, default_scope
135
+ - Doesn't try to mimic ActiveRecord, MongoDB is differrent and this tool designed to get most of it.
136
+ - Very small, see [code stats][code_stats].
137
+
138
+ Other ODM usually try to cover simple but non-standard API of MongoDB behind complex ORM-like abstractions. This tool **exposes simplicity and power of MongoDB and leverages it's differences**.
139
+
140
+ ``` ruby
141
+ # Connecting to MongoDB.
142
+ require 'mongodb/model'
143
+ Mongo.defaults.merge! symbolize: true, multi: true, safe: true
144
+ connection = Mongo::Connection.new
145
+ db = connection.db 'default_test'
146
+ db.units.drop
147
+ Mongo::Model.db = db
148
+
149
+ # Let's define the game unit.
150
+ class Unit
151
+ inherit Mongo::Model
152
+ collection :units
153
+
154
+ attr_accessor :name, :status, :stats
155
+
156
+ scope :alive, status: 'alive'
157
+
158
+ class Stats
159
+ inherit Mongo::Model
160
+ attr_accessor :attack, :life, :shield
161
+ end
162
+ end
163
+
164
+ # Create.
165
+ zeratul = Unit.build(name: 'Zeratul', status: 'alive', stats: Unit::Stats.build(attack: 85, life: 300, shield: 100))
166
+ tassadar = Unit.build(name: 'Tassadar', status: 'dead', stats: Unit::Stats.build(attack: 0, life: 80, shield: 300))
167
+
168
+ zeratul.save
169
+ tassadar.save
170
+
171
+ # Udate (we made error - mistakenly set Tassadar's attack as zero, let's fix it).
172
+ tassadar.stats.attack = 20
173
+ tassadar.save
174
+
175
+ # Querying first & all, there's also :each, the same as :all.
176
+ Unit.first name: 'Zeratul' # => zeratul
177
+ Unit.all name: 'Zeratul' # => [zeratul]
178
+ Unit.all name: 'Zeratul' do |unit|
179
+ unit # => zeratul
180
+ end
181
+
182
+ # Simple finders (bang versions also availiable).
183
+ Unit.by_name 'Zeratul' # => zeratul
184
+ Unit.first_by_name 'Zeratul' # => zeratul
185
+ Unit.all_by_name 'Zeratul' # => [zeratul]
186
+
187
+ # Scopes.
188
+ Unit.alive.count # => 1
189
+ Unit.alive.first # => zeratul
190
+
191
+ # Callbacks & callbacks on embedded models.
192
+
193
+ # Validations.
194
+
195
+ # Save model to any collection.
196
+ ```
197
+
198
+ Source: examples/model.rb
199
+
200
+ # Migrations
201
+
202
+ Define migration steps, specify desired version and apply it (usually all this should be done via Rake task).
203
+
204
+ ``` ruby
205
+ require 'mongodb/migration'
206
+
207
+ # Connection & db.
208
+ connection = Mongo::Connection.new
209
+ db = connection.db 'default_test'
210
+ db.units.drop
211
+
212
+ # Initialize migration (usually all this should be done inside of :migrate
213
+ # rake task).
214
+ migration = Mongo::Migration.new db
215
+
216
+ # Define migrations.
217
+ # Usually they are defined as files in some folder and You loading it by
218
+ # using something like this:
219
+ # Dir['<runtime_dir>/db/migrations/*.rb'].each{|fname| load fname}
220
+ migration.add 1 do |m|
221
+ m.up{|db| db.units.save name: 'Zeratul'}
222
+ m.down{|db| db.units.remove name: 'Zeratul'}
223
+ end
224
+
225
+ # Let's add another one.
226
+ migration.add 2 do |m|
227
+ m.up{|db| db.units.save name: 'Tassadar'}
228
+ m.down{|db| db.units.remove name: 'Tassadar'}
229
+ end
230
+
231
+ # Specify what version of database You need and apply migration.
232
+ migration.update 2
233
+
234
+ migration.current_version # => 2
235
+ db.units.count # => 2
236
+
237
+ # You can rollback it the same way.
238
+ migration.update 0
239
+
240
+ migration.current_version # => 0
241
+ db.units.count # => 0
242
+
243
+ # To update to the highest version just call it without the version specified
244
+ migration.update
245
+
246
+ migration.current_version # => 2
247
+ db.units.count # => 2
248
+ ```
249
+
250
+ Source: examples/migration.rb
251
+
252
+ # Installation
253
+
254
+ ``` bash
255
+ gem install mongodb
256
+ ```
257
+
258
+ # License
259
+
260
+ Copyright (c) Alexey Petrushin, http://petrush.in, released under the MIT license.
261
+
262
+ [mongo_mapper_ext]: https://github.com/alexeypetrushin/mongo_mapper_ext
263
+ [mongoid_misc]: https://github.com/alexeypetrushin/mongoid_misc
264
+ [code_stats]: https://github.com/alexeypetrushin/mongodb/raw/master/docs/code_stats.png
@@ -0,0 +1,83 @@
1
+ require 'driver/spec_helper'
2
+
3
+ describe "Collection" do
4
+ with_mongo
5
+
6
+ it 'by default save must update all matched by criteria (not first as defautl in mongo)' do
7
+ db.units.save name: 'Probe', race: 'Protoss', status: 'alive'
8
+ db.units.save name: 'Zealot', race: 'Protoss', status: 'alive'
9
+
10
+ # update
11
+ db.units.update({race: 'Protoss'}, :$set => {status: 'dead'})
12
+ db.units.all.collect{|u| u[:status]}.should == %w(dead dead)
13
+
14
+ # destroy
15
+ db.units.destroy race: 'Protoss'
16
+ db.units.count.should == 0
17
+ end
18
+
19
+ describe "symbolize" do
20
+ it 'should always return symbolized hashes' do
21
+ zeratul = {name: 'Zeratul'}
22
+ db.units.save(zeratul).should be_mongo_id
23
+ r = db.units.first(name: 'Zeratul')
24
+ r[:_id].should be_mongo_id
25
+ r['_id'].should be_nil
26
+ r[:name].should == 'Zeratul'
27
+ r['name'].should be_nil
28
+ end
29
+
30
+ it "should be able to disable symbolization" do
31
+ old = Mongo.defaults[:symbolize]
32
+ begin
33
+ Mongo.defaults[:symbolize] = false
34
+
35
+ zeratul = {name: 'Zeratul'}
36
+ db.units.save(zeratul).should be_mongo_id
37
+ r = db.units.first(name: 'Zeratul')
38
+ r[:_id].should be_nil
39
+ r['_id'].should be_mongo_id
40
+ r[:name].should be_nil
41
+ r['name'].should == 'Zeratul'
42
+ ensure
43
+ Mongo.defaults[:symbolize] = old
44
+ end
45
+ end
46
+ end
47
+
48
+ it "first" do
49
+ db.units.first.should be_nil
50
+ zeratul = {name: 'Zeratul'}
51
+ db.units.save(zeratul).should be_mongo_id
52
+ db.units.first(name: 'Zeratul')[:name].should == 'Zeratul'
53
+ end
54
+
55
+ it 'all' do
56
+ db.units.all.should == []
57
+
58
+ zeratul = {name: 'Zeratul'}
59
+ db.units.save(zeratul).should be_mongo_id
60
+
61
+ list = db.units.all(name: 'Zeratul')
62
+ list.size.should == 1
63
+ list.first[:name].should == 'Zeratul'
64
+
65
+ # with block
66
+ list = []; db.units.all{|o| list << o}
67
+ list.size.should == 1
68
+ list.first[:name].should == 'Zeratul'
69
+ end
70
+
71
+ it 'count' do
72
+ db.units.count(name: 'Zeratul').should == 0
73
+ db.units.save name: 'Zeratul'
74
+ db.units.save name: 'Tassadar'
75
+ db.units.count(name: 'Zeratul').should == 1
76
+ end
77
+
78
+ it "underscore to dollar" do
79
+ db.units.save name: 'Jim', age: 34
80
+ db.units.save name: 'Zeratul', age: 600
81
+ db.units.all(age: {_lt: 100}).count.should == 1
82
+ end
83
+ end
@@ -1,53 +1,54 @@
1
- require 'spec_helper'
1
+ require 'driver/spec_helper'
2
2
 
3
3
  describe "Hash CRUD" do
4
- with_mongo_model
5
-
4
+ with_mongo
5
+
6
6
  describe 'simple' do
7
7
  before do
8
8
  @zeratul = {name: 'Zeratul', info: 'Dark Templar'}
9
9
  end
10
-
10
+
11
11
  it 'crud' do
12
12
  # read
13
- db.heroes.count.should == 0
14
- db.heroes.all.should == []
15
- db.heroes.first.should == nil
16
-
13
+ db.units.count.should == 0
14
+ db.units.all.should == []
15
+ db.units.first.should == nil
16
+
17
17
  # create
18
- db.heroes.save(@zeratul)
19
-
18
+ db.units.save(@zeratul).should be_mongo_id
19
+ @zeratul[:_id].should be_mongo_id
20
+
20
21
  # read
21
- db.heroes.all.should == [@zeratul]
22
- db.heroes.count.should == 1
23
- db.heroes.first.should == @zeratul
24
-
22
+ db.units.all.should == [@zeratul]
23
+ db.units.count.should == 1
24
+ db.units.first.should == @zeratul
25
+
25
26
  # update
26
27
  @zeratul[:info] = 'Killer of Cerebrates'
27
- db.heroes.save({name: 'Zeratul'}, @zeratul)
28
- db.heroes.count.should == 1
29
- db.heroes.first(name: 'Zeratul').info.should == 'Killer of Cerebrates'
30
-
28
+ db.units.save @zeratul
29
+ db.units.count.should == 1
30
+ db.units.first(name: 'Zeratul')[:info].should == 'Killer of Cerebrates'
31
+
31
32
  # destroy
32
- db.heroes.destroy name: 'Zeratul'
33
- db.heroes.count.should == 0
33
+ db.units.destroy @zeratul
34
+ db.units.count.should == 0
34
35
  end
35
36
  end
36
-
37
+
37
38
  describe 'embedded' do
38
- before do
39
+ before do
39
40
  @player = {
40
41
  name: 'Alex',
41
42
  missions: [
42
43
  {name: 'Wasteland', stats: {buildings: 5, units: 10}},
43
44
  {name: 'Backwater Station', stats: {buildings: 8, units: 25}}
44
45
  ]
45
- }
46
+ }
46
47
  end
47
-
48
+
48
49
  it 'crud' do
49
50
  # create
50
- db.players.save(@player)
51
+ db.players.save(@player).should be_mongo_id
51
52
 
52
53
  # read
53
54
  db.players.count.should == 1
@@ -55,15 +56,15 @@ describe "Hash CRUD" do
55
56
 
56
57
  # update
57
58
  @player[:missions].first[:stats][:units] = 9
58
- @player.missions << {name: 'Desperate Alliance', stats: {buildings: 11, units: 40}},
59
- db.players.save({name: 'Alex'}, @player)
59
+ @player[:missions].push name: 'Desperate Alliance', stats: {buildings: 11, units: 40}
60
+ db.players.save(@player).should_not be_nil
60
61
  db.players.count.should == 1
61
62
  db.players.first.should == @player
62
63
  db.players.first.object_id.should_not == @player.object_id
63
64
 
64
65
  # destroy
65
- db.players.destroy name: 'Alex'
66
+ db.players.destroy @player
66
67
  db.players.count.should == 0
67
68
  end
68
- end
69
+ end
69
70
  end