paulcarey-relaxdb 0.3.1 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Rakefile +1 -1
- data/lib/relaxdb/document.rb +12 -7
- data/lib/relaxdb/paginator.rb +7 -2
- data/lib/relaxdb/query.rb +1 -1
- data/lib/relaxdb/references_many_proxy.rb +1 -2
- data/lib/relaxdb/relaxdb.rb +35 -13
- data/lib/relaxdb/server.rb +10 -1
- data/spec/query_spec.rb +6 -0
- data/spec/relaxdb_spec.rb +65 -3
- data/spec/spec_models.rb +1 -1
- metadata +2 -2
data/Rakefile
CHANGED
data/lib/relaxdb/document.rb
CHANGED
@@ -61,6 +61,7 @@ module RelaxDB
|
|
61
61
|
|
62
62
|
property :_id
|
63
63
|
property :_rev
|
64
|
+
property :_conflicts
|
64
65
|
|
65
66
|
def self.create_validator(att, v)
|
66
67
|
method_name = "validate_#{att}"
|
@@ -198,17 +199,25 @@ module RelaxDB
|
|
198
199
|
resp = RelaxDB.db.put(_id, to_json)
|
199
200
|
self._rev = JSON.parse(resp.body)["rev"]
|
200
201
|
rescue HTTP_409
|
201
|
-
|
202
|
-
@update_conflict = true
|
202
|
+
conflicted
|
203
203
|
return false
|
204
204
|
end
|
205
205
|
end
|
206
206
|
|
207
|
+
def conflicted
|
208
|
+
@update_conflict = true
|
209
|
+
on_update_conflict
|
210
|
+
end
|
211
|
+
|
207
212
|
def on_update_conflict
|
208
213
|
# override with any behaviour you want to happen when
|
209
214
|
# CouchDB returns DocumentConflict on an attempt to save
|
210
215
|
end
|
211
216
|
|
217
|
+
def update_conflict?
|
218
|
+
@update_conflict
|
219
|
+
end
|
220
|
+
|
212
221
|
def pre_save
|
213
222
|
set_timestamps
|
214
223
|
return false unless validates?
|
@@ -238,11 +247,7 @@ module RelaxDB
|
|
238
247
|
raise ValidationFailure, self.errors.to_json
|
239
248
|
end
|
240
249
|
end
|
241
|
-
|
242
|
-
def update_conflict?
|
243
|
-
@update_conflict
|
244
|
-
end
|
245
|
-
|
250
|
+
|
246
251
|
def validates?
|
247
252
|
props = properties - validation_skip_list
|
248
253
|
prop_vals = props.map { |prop| instance_variable_get("@#{prop}") }
|
data/lib/relaxdb/paginator.rb
CHANGED
@@ -18,6 +18,11 @@ module RelaxDB
|
|
18
18
|
:descending => @orig_paginate_params.descending, :reduce => true
|
19
19
|
end
|
20
20
|
|
21
|
+
#
|
22
|
+
# view_keys are used to determine the params for the prev and next links. If a view_key is a symbol
|
23
|
+
# the key value will be the result of invoking the method named by the symbol on the first / last
|
24
|
+
# element in the result set. If a view_key is not a symbol, its value will be used directly.
|
25
|
+
#
|
21
26
|
def add_next_and_prev(docs, view_name, view_keys)
|
22
27
|
unless docs.empty?
|
23
28
|
no_docs = docs.size
|
@@ -45,14 +50,14 @@ module RelaxDB
|
|
45
50
|
end
|
46
51
|
|
47
52
|
def create_next(doc, view_keys)
|
48
|
-
next_key = view_keys.map { |a| doc.send(a) }
|
53
|
+
next_key = view_keys.map { |a| a.is_a?(Symbol) ? doc.send(a) : a }
|
49
54
|
next_key = next_key.length == 1 ? next_key[0] : next_key
|
50
55
|
next_key_docid = doc._id
|
51
56
|
{ :startkey => next_key, :startkey_docid => next_key_docid, :descending => @orig_paginate_params.descending }
|
52
57
|
end
|
53
58
|
|
54
59
|
def create_prev(doc, view_keys)
|
55
|
-
prev_key = view_keys.map { |a| doc.send(a) }
|
60
|
+
prev_key = view_keys.map { |a| a.is_a?(Symbol) ? doc.send(a) : a }
|
56
61
|
prev_key = prev_key.length == 1 ? prev_key[0] : prev_key
|
57
62
|
prev_key_docid = doc._id
|
58
63
|
prev_params = { :startkey => prev_key, :startkey_docid => prev_key_docid, :descending => !@orig_paginate_params.descending }
|
data/lib/relaxdb/query.rb
CHANGED
@@ -25,8 +25,7 @@ module RelaxDB
|
|
25
25
|
obj.send(@relationship_as_viewed_by_target).send(:<<, @client, true)
|
26
26
|
|
27
27
|
# Bulk save to ensure relationship is persisted on both sides
|
28
|
-
|
29
|
-
RelaxDB.bulk_save(@client, obj)
|
28
|
+
RelaxDB.bulk_save! @client, obj
|
30
29
|
end
|
31
30
|
|
32
31
|
self
|
data/lib/relaxdb/relaxdb.rb
CHANGED
@@ -49,6 +49,11 @@ module RelaxDB
|
|
49
49
|
db.db_exists? name
|
50
50
|
end
|
51
51
|
|
52
|
+
def db_info
|
53
|
+
data = JSON.parse db.get.body
|
54
|
+
create_object data
|
55
|
+
end
|
56
|
+
|
52
57
|
def delete_db(name)
|
53
58
|
db.delete_db name
|
54
59
|
end
|
@@ -62,25 +67,35 @@ module RelaxDB
|
|
62
67
|
end
|
63
68
|
|
64
69
|
def bulk_save!(*objs)
|
70
|
+
if objs[0].equal? :all_or_nothing
|
71
|
+
objs.shift
|
72
|
+
all_or_nothing = true
|
73
|
+
end
|
74
|
+
|
65
75
|
pre_save_success = objs.inject(true) { |s, o| s &= o.pre_save }
|
66
76
|
raise ValidationFailure, objs unless pre_save_success
|
67
77
|
|
68
78
|
docs = {}
|
69
79
|
objs.each { |o| docs[o._id] = o }
|
70
80
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
81
|
+
data = { "docs" => objs }
|
82
|
+
data[:all_or_nothing] = true if all_or_nothing
|
83
|
+
resp = db.post("_bulk_docs", data.to_json )
|
84
|
+
data = JSON.parse(resp.body)
|
85
|
+
|
86
|
+
conflicted = []
|
87
|
+
data.each do |new_rev|
|
88
|
+
obj = docs[ new_rev["id"] ]
|
89
|
+
if new_rev["rev"]
|
77
90
|
obj._rev = new_rev["rev"]
|
78
91
|
obj.post_save
|
92
|
+
else
|
93
|
+
conflicted << obj._id
|
94
|
+
obj.conflicted
|
79
95
|
end
|
80
|
-
rescue HTTP_409
|
81
|
-
raise UpdateConflict, objs
|
82
96
|
end
|
83
|
-
|
97
|
+
|
98
|
+
raise UpdateConflict, conflicted.inspect unless conflicted.empty?
|
84
99
|
objs
|
85
100
|
end
|
86
101
|
|
@@ -96,7 +111,12 @@ module RelaxDB
|
|
96
111
|
load(obj._id)
|
97
112
|
end
|
98
113
|
|
99
|
-
|
114
|
+
#
|
115
|
+
# Examples:
|
116
|
+
# RelaxDB.load "foo", :conflicts => true
|
117
|
+
# RelaxDB.load ["foo", "bar"]
|
118
|
+
#
|
119
|
+
def load(ids, atts={})
|
100
120
|
# RelaxDB.logger.debug(caller.inject("#{db.name}/#{ids}\n") { |a, i| a += "#{i}\n" })
|
101
121
|
|
102
122
|
if ids.is_a? Array
|
@@ -105,9 +125,11 @@ module RelaxDB
|
|
105
125
|
data["rows"].map { |row| row["doc"] ? create_object(row["doc"]) : nil }
|
106
126
|
else
|
107
127
|
begin
|
108
|
-
|
109
|
-
|
110
|
-
|
128
|
+
qs = atts.map{ |k, v| "#{k}=#{v}" }.join("&")
|
129
|
+
qs = atts.empty? ? ids : "#{ids}?#{qs}"
|
130
|
+
resp = db.get qs
|
131
|
+
data = JSON.parse resp.body
|
132
|
+
create_object data
|
111
133
|
rescue HTTP_404
|
112
134
|
nil
|
113
135
|
end
|
data/lib/relaxdb/server.rb
CHANGED
@@ -15,7 +15,7 @@ module RelaxDB
|
|
15
15
|
def initialize(config)
|
16
16
|
@get_count, @post_count, @put_count = 0, 0, 0
|
17
17
|
@server = RelaxDB::Server.new(config[:host], config[:port])
|
18
|
-
@logger = config[:logger] ? config[:logger] : Logger.new(Tempfile.new('
|
18
|
+
@logger = config[:logger] ? config[:logger] : Logger.new(Tempfile.new('relaxdb.log'))
|
19
19
|
end
|
20
20
|
|
21
21
|
def use_db(name)
|
@@ -27,6 +27,7 @@ module RelaxDB
|
|
27
27
|
@server.get("/#{name}") rescue false
|
28
28
|
end
|
29
29
|
|
30
|
+
# URL encode slashes e.g. RelaxDB.delete_db "foo%2Fbar"
|
30
31
|
def delete_db(name)
|
31
32
|
@logger.info("Deleting database #{name}")
|
32
33
|
@server.delete("/#{name}")
|
@@ -83,6 +84,14 @@ module RelaxDB
|
|
83
84
|
def name=(name)
|
84
85
|
@db = name
|
85
86
|
end
|
87
|
+
|
88
|
+
def req_count
|
89
|
+
get_count + put_count + post_count
|
90
|
+
end
|
91
|
+
|
92
|
+
def reset_req_count
|
93
|
+
@get_count = @put_count = @post_count = 0
|
94
|
+
end
|
86
95
|
|
87
96
|
private
|
88
97
|
|
data/spec/query_spec.rb
CHANGED
@@ -69,6 +69,12 @@ describe RelaxDB::Query do
|
|
69
69
|
q.view_path.should == "_design//_view/?endkey_docid=foo&reduce=false"
|
70
70
|
end
|
71
71
|
|
72
|
+
it "should not include design or view if it starts with a _" do
|
73
|
+
q = RelaxDB::Query.new "_"
|
74
|
+
# q.view_path.should == "_"
|
75
|
+
q.view_path.should == "_?reduce=false"
|
76
|
+
end
|
77
|
+
|
72
78
|
end
|
73
79
|
|
74
80
|
describe "#keys" do
|
data/spec/relaxdb_spec.rb
CHANGED
@@ -70,6 +70,60 @@ describe RelaxDB do
|
|
70
70
|
RelaxDB.bulk_save(x).first.foo.should == :bar
|
71
71
|
end
|
72
72
|
|
73
|
+
it "should save non conflicting docs and mark conflicting docs" do
|
74
|
+
p1, p2 = Atom.new.save!, Atom.new.save!
|
75
|
+
p1.dup.save!
|
76
|
+
RelaxDB.bulk_save p1, p2
|
77
|
+
p1._rev.should =~ /1-/
|
78
|
+
p1.should be_update_conflict
|
79
|
+
p2._rev.should =~ /2-/
|
80
|
+
end
|
81
|
+
|
82
|
+
#
|
83
|
+
# This spec is as much a verification of my understanding of
|
84
|
+
# bulk_save semantics as it is a test of RelaxDB
|
85
|
+
#
|
86
|
+
# See http://mail-archives.apache.org/mod_mbox/couchdb-dev/200905.mbox/%3CF476A3D8-8F50-40A0-8668-C00D72196FBA@apache.org%3E
|
87
|
+
# for an explanation of the final section
|
88
|
+
#
|
89
|
+
describe "all-or-nothing" do
|
90
|
+
it "should save non conflicting and conflicting docs" do
|
91
|
+
p1, p2 = Primitives.new(:num => 1).save!, Primitives.new(:num => 2).save!
|
92
|
+
p1d = p1.dup
|
93
|
+
p1d.num = 11
|
94
|
+
p1d.save!
|
95
|
+
p1.num = 6
|
96
|
+
RelaxDB.bulk_save :all_or_nothing, p1, p2
|
97
|
+
p1._rev.should =~ /2-/
|
98
|
+
p2._rev.should =~ /2-/
|
99
|
+
|
100
|
+
p1 = RelaxDB.load p1._id, :conflicts => true
|
101
|
+
p1n1 = p1.num
|
102
|
+
p1 = RelaxDB.load p1._id, :rev => p1._conflicts[0]
|
103
|
+
p1n2 = p1.num
|
104
|
+
if p1n1 == 11
|
105
|
+
p1n2.should == 6
|
106
|
+
else
|
107
|
+
p1n1.should == 6 && p1n2.should == 11
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
#
|
112
|
+
# Test behind
|
113
|
+
# http://mail-archives.apache.org/mod_mbox/couchdb-dev/200905.mbox/%3CF476A3D8-8F50-40A0-8668-C00D72196FBA@apache.org%3E
|
114
|
+
# Effectively defunct
|
115
|
+
#
|
116
|
+
# it "non-deterministic winner" do
|
117
|
+
# p = Primitives.new(:num => 1).save!
|
118
|
+
# pd = p.dup
|
119
|
+
# p.num = 2
|
120
|
+
# p.save!
|
121
|
+
# pd.num = 3
|
122
|
+
# RelaxDB.bulk_save :all_or_nothing, pd
|
123
|
+
# RelaxDB.reload(p).num.should == 2
|
124
|
+
# end
|
125
|
+
end
|
126
|
+
|
73
127
|
end
|
74
128
|
|
75
129
|
describe ".bulk_save!" do
|
@@ -81,13 +135,21 @@ describe RelaxDB do
|
|
81
135
|
lambda { RelaxDB.bulk_save!(c.new) }.should raise_error(RelaxDB::ValidationFailure)
|
82
136
|
end
|
83
137
|
|
84
|
-
it "
|
85
|
-
Atom.new
|
86
|
-
|
138
|
+
it "should raise an exception on document conflict after all docs have been processed" do
|
139
|
+
p1, p2 = Atom.new.save!, Atom.new.save!
|
140
|
+
p1.dup.save!
|
141
|
+
lambda { RelaxDB.bulk_save!(p1, p2) }.should raise_error(RelaxDB::UpdateConflict)
|
142
|
+
p2._rev.should =~ /2-/
|
87
143
|
end
|
88
144
|
|
89
145
|
end
|
90
146
|
|
147
|
+
describe ".db_info" do
|
148
|
+
it "should return db info" do
|
149
|
+
RelaxDB.db_info.doc_count.should == 1
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
91
153
|
describe ".replicate_db" do
|
92
154
|
|
93
155
|
it "should replicate the named database" do
|
data/spec/spec_models.rb
CHANGED
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.2
|
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-
|
12
|
+
date: 2009-05-22 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|