ruote-mongodb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Nathan Stults
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,15 @@
1
+ ruote-mongodb is a storage provider for the Ruote workflow engine: http://ruote.rubyforge.org/ It enables Ruote to store its data in MongoDB.
2
+
3
+ INSTALLATION:
4
+ If you're using bundler, you can include this in your Gemfile as follows:
5
+ gem 'ruote-mongodb', :git=>"git://github.com/PlasticLizard/ruote-mongodb.git"
6
+
7
+ USAGE:
8
+ You can initialize Ruote::MongoDbStorage and pass it directly into the constructor for a Ruote::Worker just like any other Ruote storage provider.
9
+ You can pass in connection information on the constructor as follows:
10
+ Ruote::MongoDbStorage.new(:connection=>{"host"=>"localhost", "port"=>27017, "database"=>"Ruote", "username"=>"pat", "password"=>"s3cret"})
11
+ You can pass also pass in a :config option in the constructor parameters with a path to a YAML file containing your configuration settings. If you do that, also specify an :environment option representing which set of config settings to use (eg. "development" or Rails.env).
12
+ Note that any specific connection settings you pass to the constructor will over-ride the settings from the YAML file.
13
+ By default (if you don't pass anything in on the constructor), the provider will attempt to connect to host=localhost, port=27017, database=Ruote (unauthenticated)
14
+
15
+ *** USE AT YOUR OWN RISK! There is no warrany of any kind for this software. The author accepts no responsibility for data loss or any ohter harm that may come from using this software. In particular, you should be aware that Ruote will call this storage provider's purge! method, which is designed to remove any collections from the database it's using which begin with the string stored in the @@collection_prefix class variable ("ruote_" by default). If that sounds like it could be harmful, consider changing the prefix and/or configuring this provider to use its own database.
@@ -0,0 +1,6 @@
1
+ root = ::File.expand_path(File.join(File.dirname(__FILE__),'..'))
2
+
3
+ require 'rubygems'
4
+ require 'mongo'
5
+ require 'yaml'
6
+ require File.join(root,'lib','ruote-mongodb','mongodb_storage')
@@ -0,0 +1,203 @@
1
+ require 'ruote'
2
+ require 'date'
3
+
4
+ module Ruote
5
+ class MongoDbStorage
6
+ include StorageBase
7
+ include MonitorMixin
8
+
9
+ @@collection_prefix = "ruote_"
10
+ @@encoded_dollar_sign = "~#~"
11
+
12
+ def initialize(options={})
13
+ super()
14
+ db_config = {"host"=>"localhost", "port"=>27017, "database"=>"Ruote"}
15
+ options = options.dup
16
+ if environment = options.delete(:environment)
17
+ all_db_config=
18
+ File.open(options.delete(:config) || 'config/database.yml','r') do |f|
19
+ YAML.load(f)
20
+ end
21
+
22
+ raise "no configuration for environment: #{environment}" unless env_config = all_db_config[environment]
23
+ db_config.merge!(env_config)
24
+ end
25
+ #args take precedent over config
26
+ db_config.merge! options.delete(:connection) if options[:connection]
27
+
28
+ @db = Mongo::Connection.new(db_config['host'], db_config['port']).
29
+ db(db_config['database'])
30
+ if db_config['username'] && db_config['password']
31
+ @db.authenticate(db_config['username'], db_config['password'])
32
+ end
33
+
34
+ unless get('configurations','engine')
35
+ put(options.merge('type'=>'configurations', '_id'=>'engine'))
36
+ end
37
+ end
38
+
39
+ def close_connection()
40
+ @db.connection.close()
41
+ end
42
+
43
+ def put(doc, opts={})
44
+ synchronize do
45
+ raise "doc must have a type" unless doc['type']
46
+ raise "doc must have an ID" unless doc['_id']
47
+ pre = get(doc['type'], doc['_id'])
48
+
49
+ if pre && pre['_rev'] != doc['_rev']
50
+ return pre
51
+ end
52
+
53
+ if pre.nil? && doc['_rev']
54
+ return true
55
+ end
56
+
57
+ doc = if opts[:update_rev]
58
+ doc['_rev'] = pre ? pre['_rev'] : -1
59
+ doc
60
+ else
61
+ doc.merge('_rev' => doc['_rev'] || -1)
62
+ end
63
+
64
+ doc['put_at'] = Ruote.now_to_utc_s
65
+ doc['_rev'] = doc['_rev'] + 1
66
+
67
+ encoded_doc = Rufus::Json.dup(doc)
68
+ to_mongo encoded_doc
69
+ get_collection(doc['type']).save(encoded_doc)
70
+
71
+ nil
72
+ end
73
+ end
74
+
75
+ def get(type, key)
76
+ synchronize do
77
+ doc = get_collection(type).find_one("_id" => key)
78
+ from_mongo doc if doc
79
+ doc
80
+ end
81
+ end
82
+
83
+ def delete(doc)
84
+ drev = doc['_rev']
85
+ raise ArgumentError.new("can't delete doc without _rev") unless drev
86
+ synchronize do
87
+ raise "doc must have a type" unless doc['type']
88
+ prev = get(doc['type'], doc['_id'])
89
+ return true if prev.nil?
90
+ doc['_rev'] ||= 0
91
+ if prev['_rev'] == drev
92
+ get_collection(doc['type']).remove("_id" => doc["_id"])
93
+ nil
94
+ else
95
+ prev
96
+ end
97
+ end
98
+ end
99
+
100
+ def get_many(type, key=nil, opts={})
101
+ synchronize do
102
+ return get_collection(type).count if opts[:count]
103
+ criteria = {}
104
+ find_opts = {}
105
+ find_opts[:limit] = opts[:limit] if opts[:limit]
106
+ find_opts[:skip] = opts[:skip] if opts[:skip]
107
+ find_opts[:sort] = ["_id", opts[:descending] ? :descending : :ascending]
108
+ if key
109
+ id_criteria = Array(key).map do |k|
110
+ case k.class.to_s
111
+ when "String" then "!#{k}$"
112
+ when "Regexp" then k.source
113
+ else k.to_s
114
+ end
115
+ end
116
+ criteria = {"_id" => /#{id_criteria.join "|"}/}
117
+ end
118
+ docs = get_collection(type).find(criteria, find_opts).to_a
119
+ docs.each do |doc|
120
+ from_mongo doc
121
+ end
122
+ docs
123
+ end
124
+ end
125
+
126
+ def ids(type)
127
+ synchronize do
128
+ result = get_collection(type).find({}, {:fields=>["_id"]}).map do |row|
129
+ row["_id"].to_s
130
+ end
131
+ result.sort
132
+ end
133
+ end
134
+
135
+ def purge!
136
+ synchronize do
137
+ @db.collection_names.each do |name|
138
+ @db.drop_collection(name) if name =~ /^#{@@collection_prefix}/
139
+ end
140
+ end
141
+ end
142
+
143
+ def add_type(type)
144
+ synchronize do
145
+ get_collection(type).create_index("_id")
146
+ end
147
+ end
148
+
149
+ def purge_type!(type)
150
+ synchronize do
151
+ @db.drop_collection(@@collection_prefix + type)
152
+ end
153
+ end
154
+
155
+ private
156
+
157
+ def get_collection(type)
158
+ @db[@@collection_prefix + type]
159
+ end
160
+
161
+ # encodes unsupported data ($, Date) for storage in MongoDB
162
+ def from_mongo(doc)
163
+ mongo_encode(doc, /^#{@@encoded_dollar_sign}/, "$", :backward)
164
+ end
165
+
166
+ # unencodes unsupported values ($, Date) from storage in MongoDB
167
+ def to_mongo(doc)
168
+ mongo_encode(doc, /^\$/, @@encoded_dollar_sign, :forward)
169
+ end
170
+
171
+ # called by from_mongo and to_mongo
172
+ def mongo_encode(doc, pattern, replacement, date_conv)
173
+ if doc.is_a? Hash
174
+ doc.each_pair do |key, value|
175
+ new_key = key
176
+ if key.is_a?(String) && key =~ pattern
177
+ new_key = key.sub(pattern, replacement)
178
+ doc[new_key] = value
179
+ doc.delete key
180
+ end
181
+ mongo_encode(value, pattern, replacement, date_conv)
182
+ ensure_date_encoding(value, doc, new_key, date_conv)
183
+ doc[new_key] = value.to_s if value.is_a? Symbol
184
+ end
185
+ elsif doc.is_a? Array
186
+ doc.each_with_index do |entry, i|
187
+ mongo_encode(entry, pattern, replacement, date_conv)
188
+ ensure_date_encoding(entry, doc, i, date_conv)
189
+ doc[i] = entry.to_s if entry.is_a? Symbol
190
+ end
191
+ end
192
+ end
193
+
194
+ def ensure_date_encoding(value, doc, key, date_conv)
195
+ if value.is_a?(Date) && date_conv == :forward
196
+ doc[key] = "DT_" + value.to_s
197
+ end
198
+ if value.is_a?(String) && value[0,3] == "DT_" && date_conv == :backward
199
+ doc[key] = Date.parse(value[3..-1])
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,225 @@
1
+ require 'ruote-mongodb'
2
+ require 'date'
3
+
4
+ describe Ruote::MongoDbStorage do
5
+ describe "options" do
6
+ after :each do
7
+ File.delete("config/database.yml") if File.exist?("config/database.yml")
8
+ end
9
+
10
+ it "uses sensible defaults if no DB configuration specified" do
11
+ lambda {
12
+ Ruote::MongoDbStorage.new
13
+ }.should_not raise_error
14
+ end
15
+
16
+ it "fails if invalid server passed in on constructor" do
17
+ lambda {
18
+ Ruote::MongoDbStorage.new :connection=>{"host"=>"doesntexist"}
19
+ }.should raise_error Mongo::ConnectionFailure
20
+ end
21
+
22
+ it "fails if an environment is passed in but database.yml doesn't exist" do
23
+ lambda {
24
+ Ruote::MongoDbStorage.new :environment=>"test"
25
+ }.should raise_error "No such file or directory - config/database.yml"
26
+ #TODO: consider a better error message for this case
27
+ end
28
+
29
+ it "works when database.yml specifies valid settings for the environment" do
30
+ File.open("config/database.yml", "w") do |f|
31
+ f.puts "test:"
32
+ f.puts " host: localhost"
33
+ end
34
+ lambda {
35
+ Ruote::MongoDbStorage.new :environment=>"test"
36
+ }.should_not raise_error
37
+ end
38
+
39
+ it "fails when environment doesn't exist in database.yml" do
40
+ File.open("config/database.yml", "w") do |f|
41
+ f.puts "test:"
42
+ f.puts " host: localhost"
43
+ end
44
+ lambda {
45
+ Ruote::MongoDbStorage.new :environment=>"development"
46
+ }.should raise_error "no configuration for environment: development"
47
+ end
48
+
49
+ it "fails when invalid server in database.yml" do
50
+ File.open("config/database.yml", "w") do |f|
51
+ f.puts "test:"
52
+ f.puts " host: doesntexist"
53
+ end
54
+ lambda {
55
+ Ruote::MongoDbStorage.new :environment=>"test"
56
+ }.should raise_error Mongo::ConnectionFailure
57
+ end
58
+ end
59
+
60
+ describe "provider" do
61
+ before :each do
62
+ @repo = Ruote::MongoDbStorage.new
63
+ @repo.purge!
64
+ end
65
+
66
+ after :each do
67
+ @repo.close_connection
68
+ end
69
+
70
+ it "can store and retrieve a document by ID" do
71
+ key = BSON::ObjectId.new.to_s
72
+ doc = {"_id" => key, "name" => "ralph", "type" => "test"}
73
+ result = @repo.put doc
74
+ result.should be_nil
75
+ doc = @repo.get 'test', key
76
+ doc["name"].should == "ralph"
77
+ end
78
+
79
+ it "can update a document by ID" do
80
+ key = BSON::ObjectId.new.to_s
81
+ doc = {"_id" => key, "name" => "ralph", "type" => "test"}
82
+ @repo.put doc
83
+ doc = @repo.get 'test', key
84
+ doc["name"] = "bill"
85
+ @repo.put doc
86
+ doc = @repo.get 'test', key
87
+ doc["name"].should == "bill"
88
+ end
89
+
90
+ it "can store documents with keys starting with dollar sign" do
91
+ key = BSON::ObjectId.new.to_s
92
+ doc = {"_id" => key, "type" => "test", "a" => ["$b" => "c"]}
93
+ @repo.put doc
94
+ doc = @repo.get 'test', key
95
+ doc["a"].should == ["$b" => "c"]
96
+ end
97
+
98
+ it "can store documents with dates" do
99
+ key = BSON::ObjectId.new.to_s
100
+ doc = {"_id" => key, "type" => "test", "a" => ["b" => Date.parse("11/9/2010")]}
101
+ @repo.put doc
102
+ doc = @repo.get 'test', key
103
+ doc["a"][0]["b"].to_s.should == "2010-11-09"
104
+ end
105
+
106
+ it "can store large floating point numbers accurately" do
107
+ key = BSON::ObjectId.new.to_s
108
+ doc = {"_id" => key, "type" => "test", "raw" => 1289501850.34665} #1289443610.7243}
109
+ @repo.put doc
110
+ doc = @repo.get 'test', key
111
+ doc["raw"].should == 1289501850.34665 #1289443610.7243
112
+ end
113
+
114
+ it "can retrieve a document by a string ID" do
115
+ key = "hello"
116
+ doc = {"_id" => key, "name" => "ralph", "type" => "test"}
117
+ @repo.put doc
118
+ doc = @repo.get 'test', key
119
+ doc["name"].should == "ralph"
120
+ end
121
+
122
+ it "can delete a document" do
123
+ key = BSON::ObjectId.new.to_s
124
+ doc = {"_id" => key, "name" => "ralph", "type" => "test", "_rev" => 0}
125
+ @repo.put doc
126
+ @repo.delete doc
127
+ @repo.get('test', key).should be_nil
128
+ end
129
+
130
+ it "will provide a list of IDs" do
131
+ key1 = "hello" + BSON::ObjectId.new.to_s
132
+ key2 = "hello" + BSON::ObjectId.new.to_s
133
+ key3 = "hello" + BSON::ObjectId.new.to_s
134
+ @repo.put({"_id" => key1, "name" => "ralph", "type" => "test"})
135
+ @repo.put({"_id" => key2, "name" => "ralph", "type" => "test"})
136
+ @repo.put({"_id" => key3, "name" => "ralph", "type" => "test2"})
137
+ @repo.ids("test").should == [key1, key2]
138
+ @repo.ids("test2").should == [key3]
139
+ end
140
+
141
+ it "only purges collections starting with the ruote_ prefix" do
142
+ db = @repo.instance_eval "@db"
143
+ db.drop_collection("something_else")
144
+ @repo.put({"_id" => BSON::ObjectId.new.to_s, "name" => "ralph", "type" => "test"})
145
+ @repo.put({"_id" => BSON::ObjectId.new.to_s, "name" => "bill", "type" => "test2"})
146
+ db.collection_names.should == ["system.indexes", "ruote_test", "ruote_test2"]
147
+ db["something_else"].insert({"name" => "doug"})
148
+ @repo.purge!
149
+ db.collection_names.should == ["system.indexes", "something_else"]
150
+ end
151
+
152
+ it "can purge a particular type" do
153
+ key1 = BSON::ObjectId.new.to_s
154
+ key2 = BSON::ObjectId.new.to_s
155
+ @repo.put({"_id" => key1, "name" => "ralph", "type" => "test"})
156
+ @repo.put({"_id" => key2, "name" => "bill", "type" => "test2"})
157
+ @repo.purge_type! "test2"
158
+ @repo.get('test', key1)["name"].should == "ralph"
159
+ @repo.get('test2', key2).should be_nil
160
+ end
161
+
162
+ describe "can get multiple documents" do
163
+ before :each do
164
+ key1 = "TANGO!ALPHA!BRAVO"
165
+ key2 = "TANGO!ALPHA-BRAVO"
166
+ key3 = "FOXTROT!ALPHA-BRAVO"
167
+ @repo.put({"_id"=>key1, "fname"=>"ralph", "lname"=>"A", "type"=>"test"})
168
+ @repo.put({"_id"=>key2, "fname"=>"bill", "lname"=>"B", "type"=>"test"})
169
+ @repo.put({"_id"=>key3, "fname"=>"nancy", "lname"=>"A", "type"=>"test"})
170
+ end
171
+
172
+ it "with criteria" do
173
+ search_key = ["BRAVO", /FOXTROT/]
174
+ docs = @repo.get_many("test", search_key)
175
+ docs.count.should == 2
176
+ (docs.select {|doc| doc["fname"] == "ralph"}).count.should == 1
177
+ (docs.select {|doc| doc["fname"] == "nancy"}).count.should == 1
178
+ (docs.select {|doc| doc["fname"] == "bill"}).count.should == 0
179
+ end
180
+
181
+ it "without criteria" do
182
+ docs = @repo.get_many("test", nil)
183
+ docs.count.should == 3
184
+ end
185
+
186
+ it "up to a certain limit" do
187
+ docs = @repo.get_many("test", nil, {:limit => 2})
188
+ docs.count.should == 2
189
+ (docs.select {|doc| doc["fname"] == "nancy"}).count.should == 1
190
+ (docs.select {|doc| doc["fname"] == "ralph"}).count.should == 1
191
+ end
192
+
193
+ it "skipping a certain number" do
194
+ docs = @repo.get_many("test", nil, {:skip => 2})
195
+ docs.count.should == 1
196
+ (docs.select {|doc| doc["fname"] == "bill"}).count.should == 1
197
+ end
198
+
199
+ it "in descending order" do
200
+ docs = @repo.get_many("test", nil, {:descending => true})
201
+ docs[0]["fname"].should == "bill"
202
+ docs[1]["fname"].should == "ralph"
203
+ docs[2]["fname"].should == "nancy"
204
+ end
205
+
206
+ it "in ascending order" do
207
+ docs = @repo.get_many("test", nil, {:descending => false})
208
+ docs[0]["fname"].should == "nancy"
209
+ docs[1]["fname"].should == "ralph"
210
+ docs[2]["fname"].should == "bill"
211
+ end
212
+
213
+ it "in asceneding order, by default" do
214
+ docs = @repo.get_many("test", nil)
215
+ docs[0]["fname"].should == "nancy"
216
+ docs[1]["fname"].should == "ralph"
217
+ docs[2]["fname"].should == "bill"
218
+ end
219
+
220
+ it "count" do
221
+ @repo.get_many("test", nil, {:count => true}).should == 3
222
+ end
223
+ end
224
+ end
225
+ end
@@ -0,0 +1,264 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'mongo'
4
+ require 'lib/ruote-mongodb'
5
+
6
+ #
7
+ # note : using the 'errors' type, but this test is about generic storage, not
8
+ # about errors per se.
9
+ #
10
+
11
+ class UtStorage < Test::Unit::TestCase
12
+
13
+ def setup
14
+
15
+ # @s = determine_storage({})
16
+ @s = Ruote::MongoDbStorage.new
17
+
18
+ #@s.add_type('errors')
19
+
20
+ @s.put(
21
+ '_id' => 'toto',
22
+ 'type' => 'errors',
23
+ 'message' => 'testing')
24
+ end
25
+ def teardown
26
+
27
+ @s.get_many('errors').each do |d|
28
+ @s.delete(d)
29
+ end
30
+ end
31
+
32
+ def test_get_configuration
33
+
34
+ assert_not_nil @s.get_configuration('engine')
35
+ end
36
+
37
+ def test_get
38
+
39
+ h = @s.get('errors', 'toto')
40
+
41
+ assert_not_nil h['_rev']
42
+
43
+ h = @s.get('errors', 'nada')
44
+
45
+ assert_nil h
46
+ end
47
+
48
+ def test_put
49
+
50
+ doc = {
51
+ '_id' => 'test_put', 'type' => 'errors', 'message' => 'testing (2)' }
52
+
53
+ @s.put(doc)
54
+
55
+ assert_nil doc['_rev']
56
+
57
+ h = @s.get('errors', 'test_put')
58
+
59
+ assert_not_nil h['_rev']
60
+ assert_not_nil h['put_at']
61
+ end
62
+
63
+ def test_put_fail
64
+
65
+ r = @s.put('_id' => 'toto', 'type' => 'errors', 'message' => 'more')
66
+
67
+ assert_equal 'toto', r['_id']
68
+ assert_not_nil r['_rev']
69
+ end
70
+
71
+ def test_put_update_rev
72
+
73
+ doc = { '_id' => 'tpur', 'type' => 'errors', 'message' => 'more' }
74
+
75
+ r = @s.put(doc, :update_rev => true)
76
+
77
+ assert_not_nil doc['_rev']
78
+ end
79
+
80
+ def test_put_put_and_put
81
+
82
+ doc = { '_id' => 'whiskas', 'type' => 'errors', 'message' => 'miam' }
83
+
84
+ r = @s.put(doc)
85
+ doc = @s.get('errors', 'whiskas')
86
+
87
+ r = @s.put(doc)
88
+ assert_nil r
89
+
90
+ doc = @s.get('errors', 'whiskas')
91
+
92
+ assert_not_nil doc['put_at']
93
+
94
+ r = @s.put(doc)
95
+ assert_nil r
96
+ end
97
+
98
+ def test_put_update_rev_twice
99
+
100
+ doc = { '_id' => 'tpurt', 'type' => 'errors', 'message' => 'more' }
101
+
102
+ r = @s.put(doc, :update_rev => true)
103
+ assert_nil r
104
+
105
+ doc = { '_id' => 'tpurt', 'type' => 'errors', 'message' => 'more' }
106
+
107
+ r = @s.put(doc, :update_rev => true)
108
+ assert_not_nil r
109
+ end
110
+
111
+ def test_delete_fail
112
+
113
+ # missing _rev
114
+
115
+ assert_raise(ArgumentError) do
116
+ @s.delete('_id' => 'toto')
117
+ end
118
+ end
119
+
120
+ def test_delete
121
+
122
+ doc = @s.get('errors', 'toto')
123
+ puts "test_delete: #{doc}"
124
+ r = @s.delete(doc)
125
+
126
+ assert_nil r
127
+ end
128
+
129
+ def test_delete_missing
130
+
131
+ r = @s.delete('_id' => 'x', '_rev' => '12-13231123132', 'type' => 'errors')
132
+
133
+ assert_equal true, r
134
+ end
135
+
136
+ def test_keys_should_be_string
137
+
138
+ doc = { '_id' => 'h0', 'type' => 'errors', :m0 => :z, :m1 => [ :a, :b ] }
139
+
140
+ @s.put(doc)
141
+
142
+ doc = @s.get('errors', 'h0')
143
+
144
+ assert_equal 'z', doc['m0']
145
+ assert_equal %w[ a b ], doc['m1']
146
+ end
147
+
148
+ # Updating a gone document must result in a 'true' reply.
149
+ #
150
+ def test_put_gone
151
+
152
+ h = @s.get('errors', 'toto')
153
+
154
+ assert_nil @s.delete(h)
155
+
156
+ h['colour'] = 'blue'
157
+
158
+ assert_equal true, @s.put(h)
159
+ end
160
+
161
+ def test_purge_type
162
+
163
+ @s.purge_type!('errors')
164
+
165
+ assert_equal 0, @s.get_many('errors').size
166
+ end
167
+
168
+ def test_clear
169
+
170
+ @s.clear
171
+
172
+ assert_equal 0, @s.get_many('errors').size
173
+ end
174
+
175
+ #def test_purge
176
+ # @s.purge!
177
+ # assert_equal 0, @s.get_many('errors').size
178
+ #end
179
+
180
+ def test_ids
181
+
182
+ @s.put('_id' => 't_ids0', 'type' => 'errors', 'message' => 'testing')
183
+ @s.put('_id' => 't_ids1', 'type' => 'errors', 'message' => 'testing')
184
+ @s.put('_id' => 't_ids2', 'type' => 'errors', 'message' => 'testing')
185
+
186
+ assert_equal %w[ t_ids0 t_ids1 t_ids2 toto ], @s.ids('errors').sort
187
+ end
188
+
189
+ def test_get_many
190
+
191
+ 30.times do |i|
192
+ @s.put(
193
+ '_id' => "xx!#{i}",
194
+ 'type' => 'errors',
195
+ 'wfid' => i.to_s,
196
+ 'msg' => "whatever #{i}")
197
+ end
198
+
199
+ assert_equal 31, @s.get_many('errors').size
200
+ assert_equal 1, @s.get_many('errors', '7').size
201
+ assert_equal 1, @s.get_many('errors', /!7$/).size
202
+ assert_equal 30, @s.get_many('errors', /^xx!/).size
203
+ assert_equal 30, @s.get_many('errors', /x/).size
204
+ assert_equal 10, @s.get_many('errors', nil, :limit => 10).size
205
+ end
206
+
207
+ def load_30_errors
208
+ 30.times do |i|
209
+ @s.put(
210
+ '_id' => sprintf("yy!%0.2d", i),
211
+ 'type' => 'errors',
212
+ 'msg' => "whatever #{i}")
213
+ end
214
+ end
215
+
216
+ def test_get_many_options
217
+ load_30_errors
218
+
219
+ # limit
220
+
221
+ assert_equal 10, @s.get_many('errors', nil, :limit => 10).size
222
+
223
+ # count
224
+
225
+ assert_equal 31, @s.get_many('errors', nil, :count => true)
226
+
227
+ # skip and limit
228
+
229
+ assert_equal(
230
+ %w[ toto yy!00 yy!01 yy!02 ],
231
+ @s.get_many(
232
+ 'errors', nil, :skip => 0, :limit => 4
233
+ ).collect { |d| d['_id'] })
234
+ assert_equal(
235
+ %w[ yy!02 yy!03 yy!04 ],
236
+ @s.get_many(
237
+ 'errors', nil, :skip => 3, :limit => 3
238
+ ).collect { |d| d['_id'] })
239
+
240
+ # skip, limit and reverse
241
+
242
+ assert_equal(
243
+ %w[ yy!29 yy!28 yy!27 ],
244
+ @s.get_many(
245
+ 'errors', nil, :skip => 0, :limit => 3, :descending => true
246
+ ).collect { |d| d['_id'] })
247
+ assert_equal(
248
+ %w[ yy!29 yy!28 yy!27 ],
249
+ @s.get_many(
250
+ 'errors', nil, :skip => 0, :limit => 3, :descending => true
251
+ ).collect { |d| d['_id'] })
252
+ end
253
+
254
+ # def test_dump
255
+ # load_30_errors
256
+ # assert @s.dump('errors').length > 0
257
+ # end
258
+
259
+ def test_ids
260
+ load_30_errors
261
+ assert_equal 31, @s.ids('errors').length
262
+ end
263
+ end
264
+
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruote-mongodb
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Patrick Gannon
14
+ - Nathan Stults
15
+ autorequire:
16
+ bindir: bin
17
+ cert_chain: []
18
+
19
+ date: 2010-12-10 00:00:00 -08:00
20
+ default_executable:
21
+ dependencies:
22
+ - !ruby/object:Gem::Dependency
23
+ name: mongo
24
+ prerelease: false
25
+ requirement: &id001 !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ hash: 17
31
+ segments:
32
+ - 1
33
+ - 1
34
+ - 1
35
+ version: 1.1.1
36
+ type: :runtime
37
+ version_requirements: *id001
38
+ - !ruby/object:Gem::Dependency
39
+ name: bson
40
+ prerelease: false
41
+ requirement: &id002 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ~>
45
+ - !ruby/object:Gem::Version
46
+ hash: 17
47
+ segments:
48
+ - 1
49
+ - 1
50
+ - 1
51
+ version: 1.1.1
52
+ type: :runtime
53
+ version_requirements: *id002
54
+ - !ruby/object:Gem::Dependency
55
+ name: bson_ext
56
+ prerelease: false
57
+ requirement: &id003 !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ hash: 17
63
+ segments:
64
+ - 1
65
+ - 1
66
+ - 1
67
+ version: 1.1.1
68
+ type: :runtime
69
+ version_requirements: *id003
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ prerelease: false
73
+ requirement: &id004 !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ hash: 3
79
+ segments:
80
+ - 0
81
+ version: "0"
82
+ type: :development
83
+ version_requirements: *id004
84
+ - !ruby/object:Gem::Dependency
85
+ name: test-unit
86
+ prerelease: false
87
+ requirement: &id005 !ruby/object:Gem::Requirement
88
+ none: false
89
+ requirements:
90
+ - - ">="
91
+ - !ruby/object:Gem::Version
92
+ hash: 3
93
+ segments:
94
+ - 0
95
+ version: "0"
96
+ type: :development
97
+ version_requirements: *id005
98
+ description:
99
+ email:
100
+ - hereiam@sonic.net
101
+ executables: []
102
+
103
+ extensions: []
104
+
105
+ extra_rdoc_files: []
106
+
107
+ files:
108
+ - lib/ruote-mongodb.rb
109
+ - lib/ruote-mongodb/mongodb_storage.rb
110
+ - test/test_storage.rb
111
+ - spec/mongodb_storage_spec.rb
112
+ - LICENSE
113
+ - README
114
+ has_rdoc: true
115
+ homepage: http://github.com/PlasticLizard/ruote-mongodb
116
+ licenses: []
117
+
118
+ post_install_message:
119
+ rdoc_options: []
120
+
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ hash: 3
129
+ segments:
130
+ - 0
131
+ version: "0"
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ none: false
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ hash: 3
138
+ segments:
139
+ - 0
140
+ version: "0"
141
+ requirements: []
142
+
143
+ rubyforge_project:
144
+ rubygems_version: 1.3.7
145
+ signing_key:
146
+ specification_version: 3
147
+ summary: MongoDB persistence for Ruote
148
+ test_files: []
149
+