paulcarey-relaxdb 0.3.2 → 0.3.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +16 -0
- data/Rakefile +1 -1
- data/lib/relaxdb/migration.rb +40 -0
- data/lib/relaxdb/migration_version.rb +21 -0
- data/lib/relaxdb/relaxdb.rb +1 -1
- data/lib/relaxdb/views.rb +1 -1
- data/lib/relaxdb.rb +3 -0
- data/spec/doc_inheritable_spec.rb +100 -0
- data/spec/migration_spec.rb +97 -0
- data/spec/migration_version_spec.rb +28 -0
- data/spec/paginate_params_spec.rb +15 -0
- data/spec/paginate_spec.rb +351 -0
- data/spec/relaxdb_spec.rb +21 -5
- data/spec/server_spec.rb +32 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/view_by_spec.rb +76 -0
- data/spec/view_spec.rb +23 -0
- metadata +13 -3
data/README.textile
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
h3. What's New?
|
2
2
|
|
3
|
+
* 2009-05-27
|
4
|
+
** Added minimal support for data migrations. Although CouchDB's nature removes the necessity for migrations, certain knowledge that all objects possess a particular property can simplify client logic. This desire for simplification is the rationale behind this change.
|
3
5
|
* 2009-04-19
|
4
6
|
** Defaults to taf2-curb, falling back to Net/HTTP if it taf2-curb can't be loaded. Thanks to "Fred Cheung":http://www.spacevatican.org/2009/4/13/fun-with-ruby-http-clients.
|
5
7
|
** For those interested in using RelaxDB with an ETag based cache, "look here":http://github.com/fcheung/relaxdb/commit/1d9acfd5f6b3c23da0d275252b6a6e064865440e
|
@@ -171,6 +173,20 @@ h3. Creating views by hand
|
|
171
173
|
</code>
|
172
174
|
</pre>
|
173
175
|
|
176
|
+
h3. Migrations
|
177
|
+
|
178
|
+
<pre>
|
179
|
+
<code>
|
180
|
+
$ cat 001_double.rb
|
181
|
+
RelaxDB::Migration.run Primitives do |p|
|
182
|
+
p.num *= 2
|
183
|
+
p
|
184
|
+
end
|
185
|
+
|
186
|
+
$ ruby -e 'RelaxDB::Migration.run_all Dir["./*.rb"]'
|
187
|
+
</code>
|
188
|
+
</pre>
|
189
|
+
|
174
190
|
h3. Visualise
|
175
191
|
|
176
192
|
"Fuschia":http://github.com/paulcarey/fuschia/tree/master offers a web front end for visualising inter-document relationships.
|
data/Rakefile
CHANGED
@@ -0,0 +1,40 @@
|
|
1
|
+
module RelaxDB
|
2
|
+
|
3
|
+
class Migration
|
4
|
+
|
5
|
+
def self.run klass, limit = 1000
|
6
|
+
query = lambda do |page_params|
|
7
|
+
RelaxDB.paginate_view "#{klass}_all", :startkey => nil, :endkey => {}, :attributes => [:_id],
|
8
|
+
:page_params => page_params, :limit => limit
|
9
|
+
end
|
10
|
+
|
11
|
+
objs = query.call({})
|
12
|
+
until objs.empty?
|
13
|
+
migrated = objs.map { |o| yield o }.flatten.reject { |o| o.nil? }
|
14
|
+
RelaxDB.bulk_save! *migrated
|
15
|
+
objs = objs.next_params ? query.call(objs.next_params) : []
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# Runs all outstanding migrations in a given directory
|
21
|
+
#
|
22
|
+
# ==== Example
|
23
|
+
# RelaxDB::Migration.run_all Dir["couchdb/migrations/**/*.rb"]
|
24
|
+
#
|
25
|
+
def self.run_all file_names, action = lambda { |fn| require fn }
|
26
|
+
v = RelaxDB::MigrationVersion.version
|
27
|
+
file_names.select { |fn| fv(fn) > v }.each do |fn|
|
28
|
+
RelaxDB.logger.info "Applying #{fn}"
|
29
|
+
action.call fn
|
30
|
+
RelaxDB::MigrationVersion.update fv(fn)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.fv file_name
|
35
|
+
File.basename(file_name).split("_")[0].to_i
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class RelaxDB::MigrationVersion < RelaxDB::Document
|
2
|
+
|
3
|
+
DOC_ID = "relaxdb_migration_version"
|
4
|
+
|
5
|
+
property :version, :default => 0
|
6
|
+
|
7
|
+
def self.version
|
8
|
+
retrieve.version
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.update v
|
12
|
+
mv = retrieve
|
13
|
+
mv.version = v
|
14
|
+
mv.save!
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.retrieve
|
18
|
+
(v = RelaxDB.load(DOC_ID)) ? v : new(:_id => DOC_ID).save!
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
data/lib/relaxdb/relaxdb.rb
CHANGED
@@ -73,7 +73,7 @@ module RelaxDB
|
|
73
73
|
end
|
74
74
|
|
75
75
|
pre_save_success = objs.inject(true) { |s, o| s &= o.pre_save }
|
76
|
-
raise ValidationFailure, objs unless pre_save_success
|
76
|
+
raise ValidationFailure, objs.inspect unless pre_save_success
|
77
77
|
|
78
78
|
docs = {}
|
79
79
|
objs.each { |o| docs[o._id] = o }
|
data/lib/relaxdb/views.rb
CHANGED
data/lib/relaxdb.rb
CHANGED
@@ -30,6 +30,7 @@ require 'relaxdb/document'
|
|
30
30
|
require 'relaxdb/extlib'
|
31
31
|
require 'relaxdb/has_many_proxy'
|
32
32
|
require 'relaxdb/has_one_proxy'
|
33
|
+
require 'relaxdb/migration'
|
33
34
|
require 'relaxdb/paginate_params'
|
34
35
|
require 'relaxdb/paginator'
|
35
36
|
require 'relaxdb/query'
|
@@ -43,5 +44,7 @@ require 'relaxdb/view_uploader'
|
|
43
44
|
require 'relaxdb/views'
|
44
45
|
require 'more/grapher.rb'
|
45
46
|
|
47
|
+
require 'relaxdb/migration_version'
|
48
|
+
|
46
49
|
module RelaxDB
|
47
50
|
end
|
@@ -0,0 +1,100 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe "Inheritance" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "properties" do
|
11
|
+
|
12
|
+
it "should by inherited from a parent document" do
|
13
|
+
d = SubDescendant.new(:x => 1).save!
|
14
|
+
RelaxDB.reload(d).x.should == 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should store its own properties" do
|
18
|
+
r = RichDescendant.new(:x => 1, :foo => :bar).save!
|
19
|
+
RelaxDB.reload(r).x.should == 1
|
20
|
+
RelaxDB.reload(r).foo.should == "bar"
|
21
|
+
end
|
22
|
+
|
23
|
+
it "validators should behave as normal" do
|
24
|
+
d = SubDescendant.new(:y => false)
|
25
|
+
d.save.should be_false
|
26
|
+
d.errors[:y].should == "Uh oh"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "_all views" do
|
32
|
+
|
33
|
+
it "should be rewritten" do
|
34
|
+
a = Ancestor.new(:x => 0).save!
|
35
|
+
d = Descendant.new(:x => 1).save!
|
36
|
+
|
37
|
+
Ancestor.all.should == [a, d]
|
38
|
+
Descendant.all.should == [d]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should function with inheritance trees" do
|
42
|
+
Inh::X.new.save!
|
43
|
+
|
44
|
+
Inh::Y.new.save!
|
45
|
+
Inh::Y1.new.save!
|
46
|
+
|
47
|
+
Inh::Z.new.save!
|
48
|
+
Inh::Z1.new.save!
|
49
|
+
Inh::Z2.new.save!
|
50
|
+
|
51
|
+
Inh::X.all.size.should == 6
|
52
|
+
Inh::Y.all.size.should == 2
|
53
|
+
Inh::Y1.all.size.should == 1
|
54
|
+
Inh::Z.all.size.should == 3
|
55
|
+
Inh::Z1.all.size.should == 1
|
56
|
+
Inh::Z2.all.size.should == 1
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
describe "_by views" do
|
62
|
+
|
63
|
+
it "should be rewritten for ancestors and generated for descendants" do
|
64
|
+
a = Ancestor.new(:x => 0).save!
|
65
|
+
d = Descendant.new(:x => 1).save!
|
66
|
+
|
67
|
+
Ancestor.by_x.should == [a, d]
|
68
|
+
Descendant.by_x.should == [d]
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
describe "derived properties" do
|
74
|
+
|
75
|
+
it "should be stored" do
|
76
|
+
u = User.new(:_id => "foo", :name => "u").save!
|
77
|
+
s = SubDescendant.new(:user => u).save!
|
78
|
+
r = RichDescendant.new(:user => u, :ukulele => u).save!
|
79
|
+
|
80
|
+
RelaxDB.reload(s).user_name.should == "u"
|
81
|
+
RelaxDB.reload(r).user_name.should == "u"
|
82
|
+
RelaxDB.reload(r).ukulele_name.should == "u"
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
|
87
|
+
describe "references" do
|
88
|
+
|
89
|
+
it "should function as normal" do
|
90
|
+
u = User.new(:name => "u").save!
|
91
|
+
s = SubDescendant.new(:user => u).save!
|
92
|
+
r = RichDescendant.new(:user => u).save!
|
93
|
+
|
94
|
+
RelaxDB.reload(s).user.name.should == "u"
|
95
|
+
RelaxDB.reload(r).user.name.should == "u"
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "spec_models")
|
3
|
+
|
4
|
+
describe RelaxDB::Migration do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
@mig = RelaxDB::Migration
|
8
|
+
setup_test_db
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should yield each obj to the block and save the result" do
|
12
|
+
Primitives.new(:num => 5).save!
|
13
|
+
r = @mig.run Primitives do |p|
|
14
|
+
pn = Primitives.new(:num => p.num * 2)
|
15
|
+
[p, pn]
|
16
|
+
end
|
17
|
+
Primitives.by_num.map { |p| p.num}.should == [5, 10]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should raise an exception if a save results in a conflict" do
|
21
|
+
op = Primitives.new.save!
|
22
|
+
lambda do
|
23
|
+
@mig.run Primitives do |p|
|
24
|
+
op.save!
|
25
|
+
p
|
26
|
+
end
|
27
|
+
end.should raise_error(RelaxDB::UpdateConflict)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should not save docs for blocks that return nil" do
|
31
|
+
Primitives.new.save!
|
32
|
+
@mig.run Primitives do |p|
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "multiple docs" do
|
38
|
+
|
39
|
+
before(:each) do
|
40
|
+
ps = (1..5).map { |i| Primitives.new :num => i }
|
41
|
+
RelaxDB.bulk_save *ps
|
42
|
+
RelaxDB.db.reset_req_count
|
43
|
+
end
|
44
|
+
|
45
|
+
# Note: two extra requests per paginate_view request are required
|
46
|
+
it "should operate on a doc set of the given size aka limit" do
|
47
|
+
@mig.run(Primitives, 1) { |p| p.num *= p.num; p }
|
48
|
+
RelaxDB.db.req_count.should == 10 + 5 * 2
|
49
|
+
Primitives.by_num.map { |p| p.num }.should == [1, 4, 9, 16, 25]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should operate on a doc set of default size" do
|
53
|
+
@mig.run(Primitives) { |p| p.num *= p.num; p }
|
54
|
+
RelaxDB.db.req_count.should == 2 + 1 * 2
|
55
|
+
Primitives.by_num.map { |p| p.num }.should == [1, 4, 9, 16, 25]
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#fv" do
|
61
|
+
it "should return valid numbers" do
|
62
|
+
@mig.fv("foo/bar/001_foo.rb").should == 1
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe ".run_all" do
|
67
|
+
|
68
|
+
it "should save the version after each successful migration" do
|
69
|
+
@mig.run_all "a/b/1_", lambda {}
|
70
|
+
RelaxDB::MigrationVersion.version.should == 1
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should not run those migrations whose version is less than the current version" do
|
74
|
+
v = RelaxDB::MigrationVersion.retrieve
|
75
|
+
v.version = 2
|
76
|
+
v.save!
|
77
|
+
@mig.run_all "3_", lambda {}
|
78
|
+
RelaxDB::MigrationVersion.version.should == 3
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should run those migrations whose version is greater than the current version" do
|
82
|
+
v = RelaxDB::MigrationVersion.retrieve
|
83
|
+
v.version = 2
|
84
|
+
v.save!
|
85
|
+
@mig.run_all "1_foo", lambda {}
|
86
|
+
RelaxDB::MigrationVersion.version.should == 2
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise an exception on failure" do
|
90
|
+
lambda do
|
91
|
+
@mig.run_all "1_foo", lambda { raise "Expected" }
|
92
|
+
end.should raise_error(RuntimeError, "Expected")
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), "spec_helper")
|
2
|
+
require File.join(File.dirname(__FILE__), "spec_models")
|
3
|
+
|
4
|
+
describe RelaxDB::MigrationVersion do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
setup_test_db
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should not exist in a clean db" do
|
11
|
+
RelaxDB.load(RelaxDB::MigrationVersion::DOC_ID).should be_nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should return zero when it doesnt exist" do
|
15
|
+
RelaxDB::MigrationVersion.version.should == 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it "should autosave on retrieval when when it doesnt exist" do
|
19
|
+
RelaxDB::MigrationVersion.version
|
20
|
+
RelaxDB.load(RelaxDB::MigrationVersion::DOC_ID).should be
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should return the saved version" do
|
24
|
+
RelaxDB::MigrationVersion.update 10
|
25
|
+
RelaxDB::MigrationVersion.version.should == 10
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB::PaginateParams do
|
5
|
+
|
6
|
+
it "should be invalid if hasn't been initialized with both a startkey and endkey" do
|
7
|
+
RelaxDB::PaginateParams.new({}).should be_invalid
|
8
|
+
end
|
9
|
+
|
10
|
+
it "should be valid if initialized with both a startkey and endkey" do
|
11
|
+
pp = RelaxDB::PaginateParams.new :startkey => nil, :endkey => nil
|
12
|
+
pp.should_not be_invalid
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,351 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe "RelaxDB Pagination" do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
setup_test_db
|
8
|
+
|
9
|
+
letters = [
|
10
|
+
["a", 1], ["a", 2], ["a", 3],
|
11
|
+
["b", 1], ["b", 2], ["b", 3], ["b", 4], ["b", 5],
|
12
|
+
["c", 1], ["c", 2]
|
13
|
+
].map { |o| Letter.new :letter => o[0], :number => o[1], :_id => "#{o[0]}#{o[1]}" }
|
14
|
+
|
15
|
+
RelaxDB.bulk_save *letters
|
16
|
+
end
|
17
|
+
|
18
|
+
# helper function
|
19
|
+
def s(letters)
|
20
|
+
letters.map { |l| "#{l.letter}#{l.number}"}.join(", ")
|
21
|
+
end
|
22
|
+
|
23
|
+
def n(letters)
|
24
|
+
letters.map { |l| "#{l.number}"}.join(", ")
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "functional tests" do
|
28
|
+
|
29
|
+
it "should navigate through a series" do
|
30
|
+
query = lambda do |page_params|
|
31
|
+
Letter.paginate_by_letter_and_number :page_params => page_params,
|
32
|
+
:startkey => ["a"], :endkey => ["a",{}], :limit => 2
|
33
|
+
end
|
34
|
+
|
35
|
+
letters = query.call({})
|
36
|
+
s(letters).should == "a1, a2"
|
37
|
+
letters.prev_params.should be_false
|
38
|
+
|
39
|
+
letters = query.call(letters.next_params)
|
40
|
+
s(letters).should == "a3"
|
41
|
+
letters.next_params.should be_false
|
42
|
+
|
43
|
+
letters = query.call(letters.prev_params)
|
44
|
+
s(letters).should == "a1, a2"
|
45
|
+
letters.prev_params.should be_false
|
46
|
+
letters.next_params.should be
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should navigate through b series with descending false" do
|
50
|
+
query = lambda do |page_params|
|
51
|
+
Letter.paginate_by_letter_and_number :page_params => page_params,
|
52
|
+
:startkey => ["b"], :endkey => ["b",{}], :limit => 2
|
53
|
+
end
|
54
|
+
|
55
|
+
letters = query.call({})
|
56
|
+
s(letters).should == "b1, b2"
|
57
|
+
letters.prev_params.should be_false
|
58
|
+
|
59
|
+
letters = query.call(letters.next_params)
|
60
|
+
s(letters).should == "b3, b4"
|
61
|
+
letters.next_params.should be
|
62
|
+
|
63
|
+
letters = query.call(letters.prev_params)
|
64
|
+
s(letters).should == "b1, b2"
|
65
|
+
letters.prev_params.should be_false
|
66
|
+
|
67
|
+
letters = query.call(letters.next_params)
|
68
|
+
s(letters).should == "b3, b4"
|
69
|
+
letters.prev_params.should be
|
70
|
+
|
71
|
+
letters = query.call(letters.next_params)
|
72
|
+
s(letters).should == "b5"
|
73
|
+
letters.next_params.should be_false
|
74
|
+
|
75
|
+
letters = query.call(letters.prev_params)
|
76
|
+
s(letters).should == "b3, b4"
|
77
|
+
letters.next_params.should be
|
78
|
+
|
79
|
+
letters = query.call(letters.prev_params)
|
80
|
+
s(letters).should == "b1, b2"
|
81
|
+
letters.prev_params.should be_false
|
82
|
+
letters.next_params.should be
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should navigate through b series with descending true" do
|
86
|
+
query = lambda do |page_params|
|
87
|
+
Letter.paginate_by_letter_and_number :page_params => page_params,
|
88
|
+
:startkey => ["b", {}], :endkey => ["b"], :descending => true, :limit => 2
|
89
|
+
end
|
90
|
+
|
91
|
+
letters = query.call({})
|
92
|
+
s(letters).should == "b5, b4"
|
93
|
+
letters.prev_params.should be_false
|
94
|
+
|
95
|
+
letters = query.call(letters.next_params)
|
96
|
+
s(letters).should == "b3, b2"
|
97
|
+
letters.prev_params.should be
|
98
|
+
|
99
|
+
letters = query.call(letters.next_params)
|
100
|
+
s(letters).should == "b1"
|
101
|
+
letters.next_params.should be_false
|
102
|
+
|
103
|
+
letters = query.call(letters.prev_params)
|
104
|
+
s(letters).should == "b3, b2"
|
105
|
+
letters.next_params.should be
|
106
|
+
|
107
|
+
letters = query.call(letters.prev_params)
|
108
|
+
s(letters).should == "b5, b4"
|
109
|
+
letters.prev_params.should be_false
|
110
|
+
letters.next_params.should be
|
111
|
+
end
|
112
|
+
|
113
|
+
it "should not display pagination options for c series" do
|
114
|
+
letters = Letter.paginate_by_letter_and_number :page_params => {},
|
115
|
+
:startkey => ["c"], :endkey => ["c", {}], :limit => 2
|
116
|
+
|
117
|
+
letters.next_params.should be_false
|
118
|
+
letters.prev_params.should be_false
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "next_query" do
|
124
|
+
|
125
|
+
it "should emit a url encoded and json encoded string with query name page_params" do
|
126
|
+
letters = Letter.paginate_by_letter_and_number :page_params => {:startkey => ["b", 2]},
|
127
|
+
:startkey => ["b"], :endkey => ["b",{}],:limit => 2
|
128
|
+
|
129
|
+
# unescape and parse required as param order is implementation dependent
|
130
|
+
hash = JSON.parse(CGI.unescape(letters.next_query.split("=")[1]))
|
131
|
+
|
132
|
+
hash["descending"].should be_false
|
133
|
+
hash["startkey"].should == ["b", 4]
|
134
|
+
end
|
135
|
+
|
136
|
+
it "should be treated as next_param by the paginator" do
|
137
|
+
page_params = {}
|
138
|
+
query = lambda do
|
139
|
+
Letter.paginate_by_letter_and_number :page_params => page_params,
|
140
|
+
:startkey => ["b"], :endkey => ["b",{}], :limit => 2
|
141
|
+
end
|
142
|
+
|
143
|
+
letters = query.call
|
144
|
+
page_params = CGI::unescape(letters.next_query.split("=")[1])
|
145
|
+
letters = query.call
|
146
|
+
s(letters).should == "b3, b4"
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
150
|
+
|
151
|
+
describe "prev_query" do
|
152
|
+
|
153
|
+
it "should be treated as prev_query by the paginator" do
|
154
|
+
page_params = {}
|
155
|
+
query = lambda do
|
156
|
+
Letter.paginate_by_letter_and_number :page_params => page_params,
|
157
|
+
:startkey => ["b", {}], :endkey => ["b"], :descending => true, :limit => 2
|
158
|
+
end
|
159
|
+
|
160
|
+
letters = query.call
|
161
|
+
page_params = CGI::unescape(letters.next_query.split("=")[1])
|
162
|
+
letters = query.call
|
163
|
+
s(letters).should == "b3, b2"
|
164
|
+
end
|
165
|
+
|
166
|
+
it "should emit a url encoded and json encoded string with query name page_params" do
|
167
|
+
letters = Letter.paginate_by_letter_and_number :page_params => {:startkey => ["b", 2]},
|
168
|
+
:startkey => ["b"], :endkey => ["b",{}],:limit => 2
|
169
|
+
|
170
|
+
hash = JSON.parse(CGI.unescape(letters.prev_query.split("=")[1]))
|
171
|
+
|
172
|
+
hash["descending"].should be_true
|
173
|
+
hash["startkey"].should == ["b", 3]
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "multiple keys per document, simple (non array) keys" do
|
179
|
+
|
180
|
+
it "should work when descending is false" do
|
181
|
+
query = lambda do |page_params|
|
182
|
+
Letter.paginate_by_number :page_params => page_params,
|
183
|
+
:startkey => 1, :endkey => {}, :limit => 4
|
184
|
+
end
|
185
|
+
|
186
|
+
numbers = query.call({})
|
187
|
+
n(numbers).should == "1, 1, 1, 2"
|
188
|
+
numbers.prev_params.should be_false
|
189
|
+
|
190
|
+
numbers = query.call(numbers.next_params)
|
191
|
+
n(numbers).should == "2, 2, 3, 3"
|
192
|
+
numbers.next_params.should be
|
193
|
+
|
194
|
+
numbers = query.call(numbers.prev_params)
|
195
|
+
n(numbers).should == "1, 1, 1, 2"
|
196
|
+
numbers.prev_params.should be_false
|
197
|
+
|
198
|
+
numbers = query.call(numbers.next_params)
|
199
|
+
n(numbers) == "2, 2, 3, 3"
|
200
|
+
numbers.prev_params.should be
|
201
|
+
|
202
|
+
numbers = query.call(numbers.next_params)
|
203
|
+
n(numbers) == "4, 5"
|
204
|
+
numbers.next_params.should be_false
|
205
|
+
|
206
|
+
numbers = query.call(numbers.prev_params)
|
207
|
+
n(numbers) == "2, 2, 3, 3"
|
208
|
+
numbers.next_params.should be
|
209
|
+
|
210
|
+
numbers = query.call(numbers.prev_params)
|
211
|
+
n(numbers) == "1, 1, 1, 2"
|
212
|
+
numbers.next_params.should be
|
213
|
+
numbers.prev_params.should be_false
|
214
|
+
end
|
215
|
+
|
216
|
+
it "should work when descending is true" do
|
217
|
+
query = lambda do |page_params|
|
218
|
+
Letter.paginate_by_number :page_params => page_params,
|
219
|
+
:startkey => 5, :endkey => nil, :descending => true, :limit => 4
|
220
|
+
end
|
221
|
+
|
222
|
+
numbers = query.call({})
|
223
|
+
n(numbers).should == "5, 4, 3, 3"
|
224
|
+
numbers.prev_params.should be_false
|
225
|
+
|
226
|
+
numbers = query.call(numbers.next_params)
|
227
|
+
n(numbers).should == "2, 2, 2, 1"
|
228
|
+
numbers.next_params.should be
|
229
|
+
|
230
|
+
numbers = query.call(numbers.prev_params)
|
231
|
+
n(numbers).should == "5, 4, 3, 3"
|
232
|
+
numbers.prev_params.should be_false
|
233
|
+
|
234
|
+
numbers = query.call(numbers.next_params)
|
235
|
+
n(numbers).should == "2, 2, 2, 1"
|
236
|
+
numbers.prev_params.should be
|
237
|
+
|
238
|
+
numbers = query.call(numbers.next_params)
|
239
|
+
n(numbers).should == "1, 1"
|
240
|
+
numbers.next_params.should be_false
|
241
|
+
|
242
|
+
numbers = query.call(numbers.prev_params)
|
243
|
+
n(numbers).should == "2, 2, 2, 1"
|
244
|
+
numbers.next_params.should be
|
245
|
+
|
246
|
+
numbers = query.call(numbers.prev_params)
|
247
|
+
n(numbers).should == "5, 4, 3, 3"
|
248
|
+
numbers.prev_params.should be_false
|
249
|
+
numbers.next_params.should be
|
250
|
+
end
|
251
|
+
|
252
|
+
it "should not get stuck when the number of keys exceeds the limit" do
|
253
|
+
query = lambda do |page_params|
|
254
|
+
Letter.paginate_by_number :page_params => page_params,
|
255
|
+
:startkey => 1, :endkey => {}, :limit => 2
|
256
|
+
end
|
257
|
+
|
258
|
+
numbers = query.call({})
|
259
|
+
n(numbers).should == "1, 1"
|
260
|
+
numbers = query.call(numbers.next_params)
|
261
|
+
n(numbers).should == "1, 2"
|
262
|
+
end
|
263
|
+
|
264
|
+
end
|
265
|
+
|
266
|
+
describe ".paginate_by" do
|
267
|
+
|
268
|
+
it "should throw an error unless both startkey and endkey are specified" do
|
269
|
+
lambda do
|
270
|
+
Letter.paginate_by_number :page_params => page_params, :startkey => 1, :limit => 2
|
271
|
+
end.should raise_error
|
272
|
+
end
|
273
|
+
|
274
|
+
it "should return an empty array when no documents exist" do
|
275
|
+
Letter.all.destroy!
|
276
|
+
letters = Letter.paginate_by_number :page_params => {}, :startkey => 1, :endkey => 3
|
277
|
+
letters.should be_empty
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should return an array that responds negatively to next_query and prev_query when no documents exist" do
|
281
|
+
Letter.all.destroy!
|
282
|
+
letters = Letter.paginate_by_number :page_params => {}, :startkey => 1, :endkey => 3
|
283
|
+
letters.prev_query.should be_false
|
284
|
+
letters.next_query.should be_false
|
285
|
+
end
|
286
|
+
|
287
|
+
end
|
288
|
+
|
289
|
+
describe ".paginate_view functional tests" do
|
290
|
+
|
291
|
+
def navigate_b_series(query)
|
292
|
+
letters = query.call({})
|
293
|
+
s(letters).should == "b1, b2"
|
294
|
+
letters.prev_params.should be_false
|
295
|
+
|
296
|
+
letters = query.call(letters.next_params)
|
297
|
+
s(letters).should == "b3, b4"
|
298
|
+
letters.prev_params.should be
|
299
|
+
|
300
|
+
letters = query.call(letters.next_params)
|
301
|
+
s(letters).should == "b5"
|
302
|
+
letters.next_params.should be_false
|
303
|
+
|
304
|
+
letters = query.call(letters.prev_params)
|
305
|
+
s(letters).should == "b3, b4"
|
306
|
+
letters.next_params.should be
|
307
|
+
|
308
|
+
letters = query.call(letters.prev_params)
|
309
|
+
s(letters).should == "b1, b2"
|
310
|
+
letters.next_params.should be
|
311
|
+
letters.prev_params.should be_false
|
312
|
+
end
|
313
|
+
|
314
|
+
before(:each) do
|
315
|
+
map = <<-FUNC
|
316
|
+
function (doc) {
|
317
|
+
if (doc.relaxdb_class === "Letter") {
|
318
|
+
emit([doc.letter, doc.number], doc);
|
319
|
+
}
|
320
|
+
}
|
321
|
+
FUNC
|
322
|
+
|
323
|
+
reduce = <<-FUNC
|
324
|
+
function (keys, values, combine) {
|
325
|
+
return values.length;
|
326
|
+
}
|
327
|
+
FUNC
|
328
|
+
|
329
|
+
view_name = "Letter_by_letter_and_number"
|
330
|
+
RelaxDB::DesignDocument.get(RelaxDB.dd).add_map_view(view_name, map).add_reduce_view(view_name, reduce).save
|
331
|
+
end
|
332
|
+
|
333
|
+
it "should pass using symbols as view_keys" do
|
334
|
+
query = lambda do |page_params|
|
335
|
+
RelaxDB.paginate_view "Letter_by_letter_and_number", :page_params => page_params,
|
336
|
+
:startkey => ["b"], :endkey => ["b", {}], :limit => 2, :attributes => [:letter, :number]
|
337
|
+
end
|
338
|
+
navigate_b_series query
|
339
|
+
end
|
340
|
+
|
341
|
+
it "should pass using symbols and values as view_keys" do
|
342
|
+
query = lambda do |page_params|
|
343
|
+
RelaxDB.paginate_view "Letter_by_letter_and_number", :page_params => page_params,
|
344
|
+
:startkey => ["b"], :endkey => ["b", {}], :limit => 2, :attributes => ["b", :number]
|
345
|
+
end
|
346
|
+
navigate_b_series query
|
347
|
+
end
|
348
|
+
|
349
|
+
end
|
350
|
+
|
351
|
+
end
|
data/spec/relaxdb_spec.rb
CHANGED
@@ -39,11 +39,7 @@ describe RelaxDB do
|
|
39
39
|
ta.should == t1
|
40
40
|
tb.should == t2
|
41
41
|
end
|
42
|
-
|
43
|
-
it "should succeed when passed no args" do
|
44
|
-
RelaxDB.bulk_save
|
45
|
-
end
|
46
|
-
|
42
|
+
|
47
43
|
it "should return false on failure" do
|
48
44
|
c = Class.new(RelaxDB::Document) do
|
49
45
|
property :foo, :validator => lambda { false }
|
@@ -128,6 +124,16 @@ describe RelaxDB do
|
|
128
124
|
|
129
125
|
describe ".bulk_save!" do
|
130
126
|
|
127
|
+
it "should succeed when passed no args" do
|
128
|
+
RelaxDB.bulk_save!
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should raise when passed a nil value" do
|
132
|
+
lambda do
|
133
|
+
RelaxDB.bulk_save! *[nil]
|
134
|
+
end.should raise_error
|
135
|
+
end
|
136
|
+
|
131
137
|
it "should raise an exception if a obj fails validation" do
|
132
138
|
c = Class.new(RelaxDB::Document) do
|
133
139
|
property :foo, :validator => lambda { false }
|
@@ -207,6 +213,16 @@ describe RelaxDB do
|
|
207
213
|
ar2.should == a2
|
208
214
|
end
|
209
215
|
|
216
|
+
it "should load multiple documents in order" do
|
217
|
+
ns = (0...100).map { rand(1_000_000_000).to_s }
|
218
|
+
objs = ns.map { |n| Primitives.new :_id => n }
|
219
|
+
RelaxDB.bulk_save! *objs
|
220
|
+
objs = RelaxDB.load! ns
|
221
|
+
(0...100).each do |i|
|
222
|
+
ns[i].should == objs[i]._id
|
223
|
+
end
|
224
|
+
end
|
225
|
+
|
210
226
|
it "should throw an exception if given a single id for a non-existant doc" do
|
211
227
|
lambda do
|
212
228
|
RelaxDB.load! "nothere"
|
data/spec/server_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "spec_doc"
|
8
|
+
@server = RelaxDB::Server.new("localhost", 5984)
|
9
|
+
end
|
10
|
+
|
11
|
+
before(:each) do
|
12
|
+
RelaxDB.delete_db "relaxdb_spec" rescue "ok"
|
13
|
+
RelaxDB.use_db "relaxdb_spec"
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "GET" do
|
17
|
+
|
18
|
+
it "should raise a HTTP_404 for a non existant doc" do
|
19
|
+
lambda do
|
20
|
+
@server.get "/relaxdb_spec/foo"
|
21
|
+
end.should raise_error(RelaxDB::HTTP_404)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise a RuntimeError for non specific errors" do
|
25
|
+
lambda do
|
26
|
+
@server.get "/relaxdb_spec/_design/spec_doc/_view?fail=true"
|
27
|
+
end.should raise_error(RuntimeError)
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -9,8 +9,12 @@ end
|
|
9
9
|
$:.unshift(File.dirname(__FILE__) + '/../lib')
|
10
10
|
require 'relaxdb'
|
11
11
|
|
12
|
+
class RdbFormatter; def call(sv, time, progname, msg); puts msg; end; end
|
13
|
+
|
12
14
|
def setup_test_db
|
13
15
|
# RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "spec_doc", :logger => Logger.new(STDOUT)
|
16
|
+
# RelaxDB.logger.formatter = RdbFormatter.new
|
17
|
+
|
14
18
|
RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "spec_doc"
|
15
19
|
|
16
20
|
RelaxDB.delete_db "relaxdb_spec" rescue "ok"
|
@@ -0,0 +1,76 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe "view_by" do
|
5
|
+
|
6
|
+
before(:all) do
|
7
|
+
RelaxDB.configure :host => "localhost", :port => 5984, :design_doc => "spec_doc"
|
8
|
+
end
|
9
|
+
|
10
|
+
describe "view_by" do
|
11
|
+
|
12
|
+
before(:each) do
|
13
|
+
RelaxDB.delete_db "relaxdb_spec" rescue "ok"
|
14
|
+
RelaxDB.use_db "relaxdb_spec"
|
15
|
+
RelaxDB.enable_view_creation
|
16
|
+
|
17
|
+
class ViewByFoo < RelaxDB::Document
|
18
|
+
property :foo
|
19
|
+
view_by :foo, :descending => true
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should create corresponding views" do
|
25
|
+
dd = RelaxDB::DesignDocument.get "spec_doc"
|
26
|
+
dd.data["views"]["ViewByFoo_by_foo"].should be
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should create a by_ att list method" do
|
30
|
+
ViewByFoo.new(:foo => :bar).save!
|
31
|
+
res = ViewByFoo.by_foo
|
32
|
+
res.first.foo.should == "bar"
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should create a paginate_by_ att list method" do
|
36
|
+
ViewByFoo.new(:foo => :bar).save!
|
37
|
+
res = ViewByFoo.paginate_by_foo :page_params => {}, :startkey => {}, :endkey => nil
|
38
|
+
res.first.foo.should == "bar"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should apply query defaults to by_" do
|
42
|
+
ViewByFoo.new(:foo => "a").save!
|
43
|
+
ViewByFoo.new(:foo => "b").save!
|
44
|
+
|
45
|
+
ViewByFoo.by_foo.map{ |o| o.foo }.should == ["b", "a"]
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should allow a single arg to be passed to by_" do
|
49
|
+
vbf = ViewByFoo.new(:foo => "a").save!
|
50
|
+
ViewByFoo.by_foo("a").should == vbf
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should apply query defaults to paginate_by_" do
|
54
|
+
ViewByFoo.new(:foo => "a").save!
|
55
|
+
ViewByFoo.new(:foo => "b").save!
|
56
|
+
|
57
|
+
res = ViewByFoo.paginate_by_foo :page_params => {}, :startkey => {}, :endkey => nil
|
58
|
+
res.map{ |o| o.foo }.should == ["b", "a"]
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should allow query defaults to be overridden for paginate_by_" do
|
62
|
+
ViewByFoo.new(:foo => :bar).save!
|
63
|
+
res = ViewByFoo.paginate_by_foo :page_params => {}, :startkey => nil, :endkey => {}, :descending => false
|
64
|
+
res.first.foo.should == "bar"
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should allow query defaults to be overridden for by_" do
|
68
|
+
ViewByFoo.new(:foo => "a").save!
|
69
|
+
ViewByFoo.new(:foo => "b").save!
|
70
|
+
|
71
|
+
ViewByFoo.by_foo(:descending => false).map{ |o| o.foo }.should == ["a", "b"]
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
data/spec/view_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper.rb'
|
2
|
+
require File.dirname(__FILE__) + '/spec_models.rb'
|
3
|
+
|
4
|
+
describe RelaxDB::View do
|
5
|
+
|
6
|
+
describe "exists" do
|
7
|
+
|
8
|
+
before(:each) do
|
9
|
+
create_test_db
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should return nil if a view doesnt exist" do
|
13
|
+
RelaxDB::ViewCreator.all([Atom]).should_not be_exists
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should return the view if it exits" do
|
17
|
+
RelaxDB::ViewCreator.all([Atom]).save
|
18
|
+
RelaxDB::ViewCreator.all([Atom]).should be_exists
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: paulcarey-relaxdb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.3.
|
4
|
+
version: 0.3.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Paul Carey
|
@@ -9,7 +9,7 @@ autorequire: relaxdb
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-05-
|
12
|
+
date: 2009-05-27 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -65,6 +65,8 @@ files:
|
|
65
65
|
- lib/relaxdb/has_many_proxy.rb
|
66
66
|
- lib/relaxdb/has_one_proxy.rb
|
67
67
|
- lib/relaxdb/net_http_server.rb
|
68
|
+
- lib/relaxdb/migration.rb
|
69
|
+
- lib/relaxdb/migration_version.rb
|
68
70
|
- lib/relaxdb/paginate_params.rb
|
69
71
|
- lib/relaxdb/paginator.rb
|
70
72
|
- lib/relaxdb/query.rb
|
@@ -83,18 +85,26 @@ files:
|
|
83
85
|
- lib/more/atomic_bulk_save_support.rb
|
84
86
|
- spec/belongs_to_spec.rb
|
85
87
|
- spec/callbacks_spec.rb
|
86
|
-
- spec/design_doc_spec.rb
|
87
88
|
- spec/derived_properties_spec.rb
|
89
|
+
- spec/design_doc_spec.rb
|
90
|
+
- spec/doc_inheritable_spec.rb
|
88
91
|
- spec/document_spec.rb
|
89
92
|
- spec/has_many_spec.rb
|
90
93
|
- spec/has_one_spec.rb
|
94
|
+
- spec/migration_spec.rb
|
95
|
+
- spec/migration_version_spec.rb
|
96
|
+
- spec/paginate_params_spec.rb
|
97
|
+
- spec/paginate_spec.rb
|
91
98
|
- spec/query_spec.rb
|
92
99
|
- spec/references_many_spec.rb
|
93
100
|
- spec/relaxdb_spec.rb
|
101
|
+
- spec/server_spec.rb
|
94
102
|
- spec/spec.opts
|
95
103
|
- spec/spec_helper.rb
|
96
104
|
- spec/spec_models.rb
|
105
|
+
- spec/view_by_spec.rb
|
97
106
|
- spec/view_object_spec.rb
|
107
|
+
- spec/view_spec.rb
|
98
108
|
has_rdoc: false
|
99
109
|
homepage: http://github.com/paulcarey/relaxdb/
|
100
110
|
post_install_message:
|