paulcarey-relaxdb 0.3.1 → 0.3.2

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require 'spec/rake/spectask'
4
4
 
5
5
  PLUGIN = "relaxdb"
6
6
  NAME = "relaxdb"
7
- GEM_VERSION = "0.3.1"
7
+ GEM_VERSION = "0.3.2"
8
8
  AUTHOR = "Paul Carey"
9
9
  EMAIL = "paul.p.carey@gmail.com"
10
10
  HOMEPAGE = "http://github.com/paulcarey/relaxdb/"
@@ -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
- on_update_conflict
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}") }
@@ -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
@@ -51,7 +51,7 @@ module RelaxDB
51
51
  end
52
52
 
53
53
  def view_path
54
- uri = "_design/#{RelaxDB.dd}/_view/#{@view_name}"
54
+ uri = (@view_name =~ /^_/) ? @view_name : "_design/#{RelaxDB.dd}/_view/#{@view_name}"
55
55
 
56
56
  query = ""
57
57
  @@params.each do |param|
@@ -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
- # TODO: Should this be bulk_save! ? Probably.
29
- RelaxDB.bulk_save(@client, obj)
28
+ RelaxDB.bulk_save! @client, obj
30
29
  end
31
30
 
32
31
  self
@@ -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
- begin
72
- resp = db.post("_bulk_docs", { "docs" => objs }.to_json )
73
- data = JSON.parse(resp.body)
74
-
75
- data.each do |new_rev|
76
- obj = docs[ new_rev["id"] ]
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
- def load(ids)
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
- resp = db.get(ids)
109
- data = JSON.parse(resp.body)
110
- create_object(data)
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
@@ -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('couchdb.log'))
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 "will not raise an exception if a document update conflict occurs on save" do
85
- Atom.new(:_id => "a1").save!
86
- RelaxDB.bulk_save! Atom.new(:_id => "a1")
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
@@ -180,7 +180,7 @@ class RichDescendant < Descendant
180
180
 
181
181
  references :ukulele
182
182
  property :ukulele_name,
183
- :derived => [:ukulele, lambda { |p, o| o.user.name } ]
183
+ :derived => [:ukulele, lambda { |p, o| o.ukulele.name } ]
184
184
  end
185
185
 
186
186
  module Inh
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.1
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-03-31 00:00:00 -07:00
12
+ date: 2009-05-22 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency