paulcarey-relaxdb 0.3.2 → 0.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/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:
|