relaxo 0.3.1 → 0.4.0
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.md +11 -14
- data/bin/relaxo +73 -20
- data/lib/relaxo/attachments.rb +69 -0
- data/lib/relaxo/base64-1.8.rb +8 -0
- data/lib/relaxo/client.rb +31 -15
- data/lib/relaxo/connection.rb +5 -0
- data/lib/relaxo/database.rb +34 -83
- data/lib/relaxo/json.rb +33 -0
- data/lib/relaxo/{session.rb → transaction.rb} +12 -17
- data/lib/relaxo/version.rb +2 -2
- data/test/README.md +5 -0
- data/test/attachments_test.rb +32 -0
- data/test/connection_test.rb +29 -0
- data/test/database_test.rb +34 -0
- data/test/helper.rb +19 -0
- data/test/transaction_test.rb +63 -0
- metadata +68 -49
data/README.md
CHANGED
@@ -26,30 +26,28 @@ Connect to a local database and manipulate some documents.
|
|
26
26
|
|
27
27
|
require 'relaxo'
|
28
28
|
|
29
|
-
|
29
|
+
database = Relaxo.connect("http://localhost:5984/test")
|
30
30
|
|
31
31
|
doc1 = {:bob => 'dole'}
|
32
|
-
|
32
|
+
database.save(doc1)
|
33
33
|
|
34
|
-
doc2 =
|
34
|
+
doc2 = database.get(doc1['_id'])
|
35
35
|
doc2[:foo] = 'bar'
|
36
|
-
|
36
|
+
database.save(doc2)
|
37
37
|
|
38
|
-
Bulk Changes/Sessions
|
39
|
-
---------------------
|
38
|
+
### Bulk Changes/Sessions ###
|
40
39
|
|
41
40
|
Sessions support a very similar interface to the main database class and can for many cases be used interchangeably, but with added efficiency.
|
42
41
|
|
43
42
|
require 'relaxo'
|
44
43
|
require 'relaxo/session'
|
45
44
|
|
46
|
-
|
45
|
+
database = Relaxo.connect("http://localhost:5984/test")
|
46
|
+
animals = ['Neko-san', 'Wan-chan', 'Nezu-chan', 'Chicken-san']
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
$animals.each do |animal|
|
52
|
-
session.save({:name => animal})
|
48
|
+
database.transaction do |transaction|
|
49
|
+
animals.each do |animal|
|
50
|
+
transaction.save({:name => animal})
|
53
51
|
end
|
54
52
|
end
|
55
53
|
# => [
|
@@ -64,8 +62,7 @@ using CouchDB `_bulk_save`.
|
|
64
62
|
|
65
63
|
To abort the session, either raise an exception or call `session.abort!` which is equivalent to `throw :abort`.
|
66
64
|
|
67
|
-
Loading Data
|
68
|
-
------------
|
65
|
+
### Loading Data ###
|
69
66
|
|
70
67
|
Relaxo includes a command line script to import documents into a CouchDB database:
|
71
68
|
|
data/bin/relaxo
CHANGED
@@ -1,29 +1,76 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
3
|
require 'relaxo'
|
4
|
+
require 'relaxo/version'
|
5
|
+
require 'optparse'
|
4
6
|
require 'yaml'
|
7
|
+
require 'csv'
|
5
8
|
|
6
9
|
OPTIONS = {
|
7
|
-
:existing => :merge
|
10
|
+
:existing => :update, # :merge, :replace
|
11
|
+
:format => :yaml,
|
12
|
+
:transaction => true,
|
8
13
|
}
|
9
14
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
module Formats
|
16
|
+
def self.yaml(io, &block)
|
17
|
+
documents = YAML::load(io)
|
18
|
+
|
19
|
+
unless Array === documents
|
20
|
+
documents = [documents]
|
21
|
+
end
|
22
|
+
|
23
|
+
documents.each &block
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.csv(io, &block)
|
27
|
+
csv = CSV.new(io, :headers => :first_row)
|
28
|
+
|
29
|
+
csv.each do |row|
|
30
|
+
yield row.to_hash
|
31
|
+
end
|
32
|
+
end
|
16
33
|
end
|
17
34
|
|
35
|
+
ARGV.options do |o|
|
36
|
+
script_name = File.basename($0)
|
37
|
+
|
38
|
+
o.banner = "Usage: #{script_name} [options] [server-url] [files]"
|
39
|
+
o.define_head "This script can be used to import data to CouchDB."
|
40
|
+
|
41
|
+
o.separator ""
|
42
|
+
o.separator "Document creation:"
|
43
|
+
|
44
|
+
o.on("--existing [mode]", "Control whether to 'update (new document attributes takes priority), 'merge' (existing document attributes takes priority) or replace (old document attributes discarded) existing documents.") do |mode|
|
45
|
+
OPTIONS[:existing] = mode.to_sym
|
46
|
+
end
|
47
|
+
|
48
|
+
o.on("--format [type]", "Control the input format. 'yaml' files are imported as a single document or array of documents. 'csv' files are imported as records using the first row as attribute keys.") do |format|
|
49
|
+
OPTIONS[:format] = format.to_sym
|
50
|
+
end
|
51
|
+
|
52
|
+
o.on("--[no-]transaction", "Controls whether data is saved using the batch save operation. Not suitable for huge amounts of data.")
|
53
|
+
|
54
|
+
o.separator ""
|
55
|
+
o.separator "Help and Copyright information:"
|
56
|
+
|
57
|
+
o.on_tail("--copy", "Display copyright and warranty information") do
|
58
|
+
$stderr.puts "#{script_name} v#{Relaxo::VERSION::STRING}. Copyright (c) 2012 Samuel Williams."
|
59
|
+
$stderr.puts "This software is released under the MIT license and comes with ABSOLUTELY NO WARRANTY."
|
60
|
+
exit
|
61
|
+
end
|
62
|
+
|
63
|
+
o.on_tail("-h", "--help", "Show this help message.") do
|
64
|
+
$stderr.puts o
|
65
|
+
exit
|
66
|
+
end
|
67
|
+
end.parse!
|
68
|
+
|
18
69
|
$url = ARGV.shift
|
19
|
-
$database = Relaxo.connect($url)
|
20
70
|
$stderr.puts "Connecting to #{$url}..."
|
71
|
+
$database = Relaxo.connect($url)
|
21
72
|
|
22
|
-
$
|
23
|
-
|
24
|
-
unless Array === $documents
|
25
|
-
$documents = [$documents]
|
26
|
-
end
|
73
|
+
$format = Formats.method(OPTIONS[:format])
|
27
74
|
|
28
75
|
$stats = {
|
29
76
|
:saved => 0,
|
@@ -31,13 +78,13 @@ $stats = {
|
|
31
78
|
:total => 0
|
32
79
|
}
|
33
80
|
|
34
|
-
|
81
|
+
def process_document(database)
|
35
82
|
begin
|
36
|
-
$stderr.puts "Loading #{document[Relaxo::ID]}"
|
37
83
|
existing_document = nil
|
38
84
|
|
39
85
|
if document && document[Relaxo::ID]
|
40
|
-
|
86
|
+
$stderr.puts "Loading #{document[Relaxo::ID]}"
|
87
|
+
existing_document = database.get(document[Relaxo::ID]) rescue nil
|
41
88
|
end
|
42
89
|
|
43
90
|
if existing_document
|
@@ -59,7 +106,7 @@ $documents.each do |document|
|
|
59
106
|
end
|
60
107
|
|
61
108
|
$stderr.puts "Saving #{document.inspect}"
|
62
|
-
$result =
|
109
|
+
$result = database.save(document)
|
63
110
|
|
64
111
|
$stats[:saved] += 1
|
65
112
|
rescue RestClient::BadRequest => ex
|
@@ -71,7 +118,13 @@ $documents.each do |document|
|
|
71
118
|
$stats[:total] += 1
|
72
119
|
end
|
73
120
|
|
74
|
-
|
75
|
-
|
76
|
-
|
121
|
+
begin
|
122
|
+
$format.call(ARGF) do |document|
|
123
|
+
process_document(document)
|
124
|
+
end
|
125
|
+
ensure
|
126
|
+
$stderr.puts "#{$stats[:saved]} document(s) saved out of #{$stats[:total]}."
|
127
|
+
if $stats[:errors] > 0
|
128
|
+
$stderr.puts "#{$stats[:errors]} errors occurred!"
|
129
|
+
end
|
77
130
|
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
require 'relaxo/database'
|
22
|
+
|
23
|
+
if RUBY_VERSION < "1.9"
|
24
|
+
require 'relaxo/base64-1.8'
|
25
|
+
else
|
26
|
+
require 'base64'
|
27
|
+
end
|
28
|
+
|
29
|
+
module Relaxo
|
30
|
+
ATTACHMENTS = '_attachments'
|
31
|
+
|
32
|
+
def self.encode_attachments!(document)
|
33
|
+
return unless document[ATTACHMENTS]
|
34
|
+
|
35
|
+
document[ATTACHMENTS].each do |name,contents|
|
36
|
+
next if contents.include? 'stub'
|
37
|
+
|
38
|
+
contents['data'] = Base64.strict_encode64(contents['data'])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class Database
|
43
|
+
def attach(document, name, data, options = {})
|
44
|
+
parameters = {
|
45
|
+
:rev => document[REV]
|
46
|
+
}.merge(options[:parameters] || {})
|
47
|
+
|
48
|
+
headers = {}
|
49
|
+
|
50
|
+
if options[:headers]
|
51
|
+
headers = options[:headers].dup
|
52
|
+
else
|
53
|
+
headers = {}
|
54
|
+
end
|
55
|
+
|
56
|
+
if options[:content_type]
|
57
|
+
headers[:content_type] = options[:content_type]
|
58
|
+
end
|
59
|
+
|
60
|
+
Client.attach attachment_url(document[ID], name, parameters), data, headers
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def attachment_url(id, name, parameters = {})
|
66
|
+
Client.encode_url("#{@root}/#{Client.escape_id(id)}/#{Client.escape(name)}", parameters)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/lib/relaxo/client.rb
CHANGED
@@ -18,27 +18,30 @@
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
19
|
# THE SOFTWARE.
|
20
20
|
|
21
|
-
require 'json'
|
21
|
+
require 'relaxo/json'
|
22
22
|
require 'rest_client'
|
23
23
|
require 'cgi'
|
24
24
|
|
25
25
|
module Relaxo
|
26
|
+
# Typically returned from CouchDB when the request was successful:
|
27
|
+
SUCCESS = {'ok' => true}
|
28
|
+
|
26
29
|
module Client
|
27
|
-
@@debug = false
|
28
|
-
|
29
|
-
def self.debug= flag
|
30
|
-
@@debug = flag
|
31
|
-
end
|
32
|
-
|
33
30
|
DEFAULT_GET_HEADERS = {:accept => :json}
|
34
31
|
DEFAULT_PUT_HEADERS = {:accept => :json, :content_type => :json}
|
35
32
|
|
36
33
|
def self.execute(request)
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
34
|
+
if request[:method] == :head
|
35
|
+
begin
|
36
|
+
response = RestClient::Request.execute(request)
|
37
|
+
return response.code == 200
|
38
|
+
rescue RestClient::ResourceNotFound
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
else
|
42
|
+
response = RestClient::Request.execute(request)
|
43
|
+
return JSON.parse(response)
|
44
|
+
end
|
42
45
|
end
|
43
46
|
|
44
47
|
def self.get(url)
|
@@ -57,12 +60,25 @@ module Relaxo
|
|
57
60
|
})
|
58
61
|
end
|
59
62
|
|
60
|
-
def self.
|
63
|
+
def self.attach(url, data, headers = {})
|
64
|
+
headers = {
|
65
|
+
:accept => :json
|
66
|
+
}.merge(headers)
|
67
|
+
|
68
|
+
execute({
|
69
|
+
:method => :put,
|
70
|
+
:url => url,
|
71
|
+
:headers => headers,
|
72
|
+
:payload => data
|
73
|
+
})
|
74
|
+
end
|
75
|
+
|
76
|
+
def self.put(url, document = nil)
|
61
77
|
execute({
|
62
78
|
:method => :put,
|
63
79
|
:url => url,
|
64
80
|
:headers => DEFAULT_PUT_HEADERS,
|
65
|
-
:payload => JSON.generate(document)
|
81
|
+
:payload => document ? JSON.generate(document) : nil
|
66
82
|
})
|
67
83
|
end
|
68
84
|
|
@@ -115,4 +131,4 @@ module Relaxo
|
|
115
131
|
/^_design\/(.*)/ =~ id ? "_design/#{escape($1)}" : escape(id)
|
116
132
|
end
|
117
133
|
end
|
118
|
-
end
|
134
|
+
end
|
data/lib/relaxo/connection.rb
CHANGED
data/lib/relaxo/database.rb
CHANGED
@@ -21,102 +21,53 @@
|
|
21
21
|
require 'relaxo/client'
|
22
22
|
require 'relaxo/connection'
|
23
23
|
|
24
|
+
if RUBY_VERSION < "1.9"
|
25
|
+
require 'relaxo/base64-1.8'
|
26
|
+
else
|
27
|
+
require 'base64'
|
28
|
+
end
|
29
|
+
|
24
30
|
module Relaxo
|
25
|
-
|
26
31
|
ID = '_id'
|
27
32
|
REV = '_rev'
|
28
|
-
|
33
|
+
DELETED = '_deleted'
|
29
34
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
next if contents['stub']
|
35
|
-
|
36
|
-
contents['data'] = Base64.encode64(contents['data'])
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Session
|
41
|
-
def initialize(database)
|
42
|
-
@database = database
|
35
|
+
class Database
|
36
|
+
def initialize(connection, name)
|
37
|
+
@connection = connection
|
38
|
+
@name = name
|
43
39
|
|
44
|
-
@
|
45
|
-
@uuids = []
|
40
|
+
@root = connection.url + "/" + CGI.escape(name)
|
46
41
|
end
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
attr :connection
|
44
|
+
attr :name
|
45
|
+
attr :root
|
51
46
|
|
52
|
-
|
53
|
-
|
47
|
+
# Create the database, will potentially throw an exception if it already exists.
|
48
|
+
def create!
|
49
|
+
Client.put @root
|
54
50
|
end
|
55
51
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
@documents << document
|
60
|
-
|
61
|
-
# We assume the save operation will be successful:
|
62
|
-
unless document.key? ID
|
63
|
-
@uuids << (document[ID] = @database.connection.next_uuid)
|
64
|
-
end
|
52
|
+
# Return true if the database already exists.
|
53
|
+
def exist?
|
54
|
+
Client.head @root
|
65
55
|
end
|
66
56
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
klass.new(self, @documents.last)
|
57
|
+
# Delete the database and all data.
|
58
|
+
def delete!
|
59
|
+
Client.delete @root
|
71
60
|
end
|
72
61
|
|
73
|
-
|
74
|
-
|
62
|
+
# Compact the database, removing old document revisions and optimizing space use.
|
63
|
+
def compact!
|
64
|
+
Client.post "#{@root}/_compact"
|
75
65
|
end
|
76
66
|
|
77
|
-
def
|
78
|
-
|
79
|
-
#puts YAML::dump(@documents)
|
80
|
-
|
81
|
-
changed = []
|
82
|
-
|
83
|
-
unless @documents.empty?
|
84
|
-
results = @database.bulk_save @documents
|
85
|
-
|
86
|
-
# Update the documents with revision information:
|
87
|
-
@documents.each_with_index do |document, index|
|
88
|
-
status = results[index]
|
89
|
-
|
90
|
-
if status['ok']
|
91
|
-
document[ID] = status['id']
|
92
|
-
document[REV] = status['rev']
|
93
|
-
end
|
94
|
-
end
|
95
|
-
|
96
|
-
changed = @documents
|
97
|
-
@documents = []
|
98
|
-
end
|
99
|
-
|
100
|
-
return changed
|
67
|
+
def id?(id, parameters = {})
|
68
|
+
Client.head document_url(id, parameters)
|
101
69
|
end
|
102
70
|
|
103
|
-
#def rollback!
|
104
|
-
# Restore all documents based on @uuids
|
105
|
-
#end
|
106
|
-
end
|
107
|
-
|
108
|
-
class Database
|
109
|
-
def initialize(connection, name)
|
110
|
-
@connection = connection
|
111
|
-
@name = name
|
112
|
-
|
113
|
-
@root = connection.url + "/" + CGI.escape(name)
|
114
|
-
end
|
115
|
-
|
116
|
-
attr :connection
|
117
|
-
attr :name
|
118
|
-
attr :root
|
119
|
-
|
120
71
|
def get(id, parameters = {})
|
121
72
|
Client.get document_url(id, parameters)
|
122
73
|
end
|
@@ -130,8 +81,6 @@ module Relaxo
|
|
130
81
|
end
|
131
82
|
|
132
83
|
def save(document)
|
133
|
-
Relaxo::encode_attachments!(document)
|
134
|
-
|
135
84
|
status = put(document)
|
136
85
|
|
137
86
|
if status['ok']
|
@@ -142,11 +91,13 @@ module Relaxo
|
|
142
91
|
return status
|
143
92
|
end
|
144
93
|
|
145
|
-
def bulk_save(documents)
|
146
|
-
|
94
|
+
def bulk_save(documents, options = {})
|
95
|
+
options = {
|
147
96
|
:docs => documents,
|
148
97
|
:all_or_nothing => true
|
149
|
-
}
|
98
|
+
}.merge(options)
|
99
|
+
|
100
|
+
Client.post command_url("_bulk_docs"), options
|
150
101
|
end
|
151
102
|
|
152
103
|
# Accepts paramaters as described in http://wiki.apache.org/couchdb/HttpViewApi
|
data/lib/relaxo/json.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
|
2
|
+
#
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
8
|
+
# furnished to do so, subject to the following conditions:
|
9
|
+
#
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
11
|
+
# all copies or substantial portions of the Software.
|
12
|
+
#
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
# THE SOFTWARE.
|
20
|
+
|
21
|
+
# You can define `Relaxo::JSON` before loading Relaxo, and it will use this implementation without trying to load the default JSON implementation.
|
22
|
+
|
23
|
+
module Relaxo
|
24
|
+
end
|
25
|
+
|
26
|
+
unless defined? Relaxo::JSON
|
27
|
+
unless defined? JSON
|
28
|
+
# Try to load a JSON implementation if it doesn't already exist:
|
29
|
+
require 'json'
|
30
|
+
end
|
31
|
+
|
32
|
+
Relaxo::JSON = JSON
|
33
|
+
end
|
@@ -23,15 +23,15 @@ require 'relaxo/connection'
|
|
23
23
|
|
24
24
|
module Relaxo
|
25
25
|
|
26
|
-
# We monkey-patch this in as
|
26
|
+
# We monkey-patch this in as Transaction is basically an optional feature for doing bulk_saves.
|
27
27
|
class Database
|
28
|
-
def
|
29
|
-
|
28
|
+
def transaction(klass = nil)
|
29
|
+
transaction = Transaction.new(self)
|
30
30
|
|
31
31
|
catch(:abort) do
|
32
|
-
yield
|
32
|
+
yield transaction
|
33
33
|
|
34
|
-
changed =
|
34
|
+
changed = transaction.commit!
|
35
35
|
|
36
36
|
if klass
|
37
37
|
klass.new(self, changed.last)
|
@@ -42,7 +42,7 @@ module Relaxo
|
|
42
42
|
end
|
43
43
|
end
|
44
44
|
|
45
|
-
class
|
45
|
+
class Transaction
|
46
46
|
def initialize(database)
|
47
47
|
@database = database
|
48
48
|
|
@@ -55,12 +55,14 @@ module Relaxo
|
|
55
55
|
end
|
56
56
|
|
57
57
|
def delete(document)
|
58
|
-
@documents << {
|
58
|
+
@documents << {
|
59
|
+
ID => document[ID],
|
60
|
+
REV => document[REV],
|
61
|
+
DELETED => true
|
62
|
+
}
|
59
63
|
end
|
60
64
|
|
61
65
|
def save(document)
|
62
|
-
Relaxo::encode_attachments!(document)
|
63
|
-
|
64
66
|
@documents << document
|
65
67
|
|
66
68
|
# We assume the save operation will be successful:
|
@@ -69,7 +71,7 @@ module Relaxo
|
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
def
|
74
|
+
def transaction(klass)
|
73
75
|
yield self
|
74
76
|
|
75
77
|
klass.new(self, @documents.last)
|
@@ -80,9 +82,6 @@ module Relaxo
|
|
80
82
|
end
|
81
83
|
|
82
84
|
def commit!
|
83
|
-
#puts "Commiting session:"
|
84
|
-
#puts YAML::dump(@documents)
|
85
|
-
|
86
85
|
changed = []
|
87
86
|
|
88
87
|
unless @documents.empty?
|
@@ -108,9 +107,5 @@ module Relaxo
|
|
108
107
|
def abort!
|
109
108
|
throw :abort
|
110
109
|
end
|
111
|
-
|
112
|
-
#def rollback!
|
113
|
-
# Restore all documents based on @uuids
|
114
|
-
#end
|
115
110
|
end
|
116
111
|
end
|
data/lib/relaxo/version.rb
CHANGED
data/test/README.md
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
require 'relaxo'
|
6
|
+
require 'relaxo/attachments'
|
7
|
+
|
8
|
+
class DatabaseTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@connection = Relaxo::Connection.new(TEST_DATABASE_HOST)
|
11
|
+
@database = Relaxo::Database.new(@connection, TEST_DATABASE_NAME)
|
12
|
+
|
13
|
+
if @database.exist?
|
14
|
+
@database.delete!
|
15
|
+
end
|
16
|
+
|
17
|
+
@database.create!
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_attachments
|
21
|
+
document = {
|
22
|
+
Relaxo::ATTACHMENTS => {
|
23
|
+
"foo.txt" => {
|
24
|
+
"content_type" => "text\/plain",
|
25
|
+
"data" => "VGhpcyBpcyBhIGJhc2U2NCBlbmNvZGVkIHRleHQ="
|
26
|
+
}
|
27
|
+
}
|
28
|
+
}
|
29
|
+
|
30
|
+
puts @database.save(document).inspect
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
require 'relaxo'
|
6
|
+
|
7
|
+
class ConnectionTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@connection = Relaxo::Connection.new(TEST_DATABASE_HOST)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_info
|
13
|
+
assert_equal Hash, @connection.info.class
|
14
|
+
end
|
15
|
+
|
16
|
+
def test_create_database
|
17
|
+
database = Relaxo::Database.new(@connection, TEST_DATABASE_NAME)
|
18
|
+
|
19
|
+
if database.exist?
|
20
|
+
assert_equal Relaxo::SUCCESS, database.delete!
|
21
|
+
end
|
22
|
+
|
23
|
+
assert_equal Relaxo::SUCCESS, database.create!
|
24
|
+
|
25
|
+
assert_includes TEST_DATABASE_NAME, @connection.databases
|
26
|
+
|
27
|
+
assert_equal Relaxo::SUCCESS, database.delete!
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
require 'relaxo'
|
6
|
+
|
7
|
+
class DatabaseTest < Test::Unit::TestCase
|
8
|
+
def setup
|
9
|
+
@connection = Relaxo::Connection.new(TEST_DATABASE_HOST)
|
10
|
+
@database = Relaxo::Database.new(@connection, TEST_DATABASE_NAME)
|
11
|
+
|
12
|
+
if @database.exist?
|
13
|
+
@database.delete!
|
14
|
+
end
|
15
|
+
|
16
|
+
@database.create!
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_adding_document
|
20
|
+
assert_equal false, @database.id?('foobar')
|
21
|
+
|
22
|
+
document = {'animal' => 'Cat', 'name' => 'Seifa'}
|
23
|
+
|
24
|
+
@database.save(document)
|
25
|
+
|
26
|
+
id = document[Relaxo::ID]
|
27
|
+
assert_equal true, @database.id?(id)
|
28
|
+
|
29
|
+
copy = @database.get(id)
|
30
|
+
document.each do |key, value|
|
31
|
+
assert_equal value, copy[key]
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
|
2
|
+
$LOAD_PATH.unshift File.expand_path("../../lib/", __FILE__)
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'test/unit'
|
6
|
+
|
7
|
+
TEST_DATABASE_NAME = 'relaxo-test-database'
|
8
|
+
TEST_DATABASE_HOST = 'localhost:5984'
|
9
|
+
|
10
|
+
class Test::Unit::TestCase
|
11
|
+
# Why isn't this in rails?
|
12
|
+
def assert_includes(elem, array, message = nil)
|
13
|
+
message = build_message message, '<?> is not found in <?>.', elem, array
|
14
|
+
|
15
|
+
assert_block message do
|
16
|
+
array.include? elem
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'helper'
|
4
|
+
|
5
|
+
require 'relaxo'
|
6
|
+
require 'relaxo/transaction'
|
7
|
+
|
8
|
+
class TransactionTest < Test::Unit::TestCase
|
9
|
+
def setup
|
10
|
+
@connection = Relaxo::Connection.new(TEST_DATABASE_HOST)
|
11
|
+
@database = Relaxo::Database.new(@connection, TEST_DATABASE_NAME)
|
12
|
+
|
13
|
+
if @database.exist?
|
14
|
+
@database.delete!
|
15
|
+
end
|
16
|
+
|
17
|
+
@database.create!
|
18
|
+
end
|
19
|
+
|
20
|
+
def test_adding_documents
|
21
|
+
documents = @database.transaction do |txn|
|
22
|
+
10.times do |i|
|
23
|
+
txn.save({:i => i})
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# We got 10 document IDs back
|
28
|
+
assert_equal 10, documents.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_abortion
|
32
|
+
all_documents = @database.documents
|
33
|
+
|
34
|
+
documents = @database.transaction do |txn|
|
35
|
+
10.times do |i|
|
36
|
+
txn.save({:i => i})
|
37
|
+
|
38
|
+
txn.abort! if i == 5
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_equal nil, documents
|
43
|
+
assert_equal all_documents, @database.documents
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_deletion
|
47
|
+
documents = @database.transaction do |txn|
|
48
|
+
3.times do |i|
|
49
|
+
txn.save({:i => i})
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
@database.transaction do |txn|
|
54
|
+
documents.each do |document|
|
55
|
+
txn.delete(document)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
documents.each do |document|
|
60
|
+
assert_equal false, @database.id?(document[Relaxo::ID])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,86 +1,105 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: relaxo
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
5
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 4
|
8
|
+
- 0
|
9
|
+
version: 0.4.0
|
6
10
|
platform: ruby
|
7
|
-
authors:
|
11
|
+
authors:
|
8
12
|
- Samuel Williams
|
9
13
|
autorequire:
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
16
|
+
|
17
|
+
date: 2012-10-14 00:00:00 +13:00
|
18
|
+
default_executable:
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: json
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ~>
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: 1.7.3
|
22
|
-
type: :runtime
|
23
22
|
prerelease: false
|
24
|
-
|
25
|
-
|
26
|
-
requirements:
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
27
25
|
- - ~>
|
28
|
-
- !ruby/object:Gem::Version
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
segments:
|
28
|
+
- 1
|
29
|
+
- 7
|
30
|
+
- 3
|
29
31
|
version: 1.7.3
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: rest-client
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
|
-
requirements:
|
35
|
-
- - ~>
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
version: 1.6.7
|
38
32
|
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rest-client
|
39
36
|
prerelease: false
|
40
|
-
|
41
|
-
|
42
|
-
requirements:
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
43
39
|
- - ~>
|
44
|
-
- !ruby/object:Gem::Version
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 1
|
43
|
+
- 6
|
44
|
+
- 7
|
45
45
|
version: 1.6.7
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
46
48
|
description:
|
47
49
|
email: samuel.williams@oriontransfer.co.nz
|
48
|
-
executables:
|
50
|
+
executables:
|
49
51
|
- relaxo
|
50
52
|
extensions: []
|
53
|
+
|
51
54
|
extra_rdoc_files: []
|
52
|
-
|
55
|
+
|
56
|
+
files:
|
53
57
|
- bin/relaxo
|
58
|
+
- lib/relaxo/attachments.rb
|
59
|
+
- lib/relaxo/base64-1.8.rb
|
54
60
|
- lib/relaxo/client.rb
|
55
61
|
- lib/relaxo/connection.rb
|
56
62
|
- lib/relaxo/database.rb
|
57
|
-
- lib/relaxo/
|
63
|
+
- lib/relaxo/json.rb
|
64
|
+
- lib/relaxo/transaction.rb
|
58
65
|
- lib/relaxo/version.rb
|
59
66
|
- lib/relaxo.rb
|
67
|
+
- test/README.md
|
68
|
+
- test/attachments_test.rb
|
69
|
+
- test/connection_test.rb
|
70
|
+
- test/database_test.rb
|
71
|
+
- test/helper.rb
|
72
|
+
- test/transaction_test.rb
|
60
73
|
- README.md
|
74
|
+
has_rdoc: true
|
61
75
|
homepage: http://www.oriontransfer.co.nz/gems/relaxo
|
62
76
|
licenses: []
|
77
|
+
|
63
78
|
post_install_message:
|
64
79
|
rdoc_options: []
|
65
|
-
|
80
|
+
|
81
|
+
require_paths:
|
66
82
|
- lib
|
67
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
requirements:
|
76
|
-
- -
|
77
|
-
- !ruby/object:Gem::Version
|
78
|
-
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - ">="
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
segments:
|
88
|
+
- 0
|
89
|
+
version: "0"
|
90
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
91
|
+
requirements:
|
92
|
+
- - ">="
|
93
|
+
- !ruby/object:Gem::Version
|
94
|
+
segments:
|
95
|
+
- 0
|
96
|
+
version: "0"
|
79
97
|
requirements: []
|
98
|
+
|
80
99
|
rubyforge_project:
|
81
|
-
rubygems_version: 1.
|
100
|
+
rubygems_version: 1.3.6
|
82
101
|
signing_key:
|
83
102
|
specification_version: 3
|
84
103
|
summary: Relaxo is a helper for loading and working with CouchDB.
|
85
104
|
test_files: []
|
86
|
-
|
105
|
+
|