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 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