couch-quilt 0.4.1 → 0.5.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/couch-quilt.gemspec +7 -2
- data/lib/couchquilt.rb +3 -0
- data/lib/couchquilt/core_ext/array.rb +37 -0
- data/lib/couchquilt/core_ext/hash.rb +43 -0
- data/lib/couchquilt/database.rb +68 -0
- data/lib/couchquilt/fs.rb +51 -83
- data/lib/couchquilt/mapper.rb +6 -42
- data/lib/couchquilt/version.rb +1 -1
- data/spec/couchquilt/core_ext_spec.rb +111 -0
- data/spec/couchquilt/fs_spec.rb +418 -207
- data/spec/couchquilt/mapper_spec.rb +0 -76
- data/spec/spec_helper.rb +2 -2
- metadata +9 -4
data/couch-quilt.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{couch-quilt}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.5.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Johannes J\303\266rg Schmidt"]
|
12
|
-
s.date = %q{2010-
|
12
|
+
s.date = %q{2010-04-02}
|
13
13
|
s.default_executable = %q{couchquilt}
|
14
14
|
s.description = %q{Access CouchDB JSON documents from filesystem.}
|
15
15
|
s.email = %q{schmidt@netzmerk.com}
|
@@ -26,11 +26,15 @@ Gem::Specification.new do |s|
|
|
26
26
|
"bin/couchquilt",
|
27
27
|
"couch-quilt.gemspec",
|
28
28
|
"lib/couchquilt.rb",
|
29
|
+
"lib/couchquilt/core_ext/array.rb",
|
30
|
+
"lib/couchquilt/core_ext/hash.rb",
|
29
31
|
"lib/couchquilt/couch_client.rb",
|
32
|
+
"lib/couchquilt/database.rb",
|
30
33
|
"lib/couchquilt/debugged_fs.rb",
|
31
34
|
"lib/couchquilt/fs.rb",
|
32
35
|
"lib/couchquilt/mapper.rb",
|
33
36
|
"lib/couchquilt/version.rb",
|
37
|
+
"spec/couchquilt/core_ext_spec.rb",
|
34
38
|
"spec/couchquilt/fs_spec.rb",
|
35
39
|
"spec/couchquilt/mapper_spec.rb",
|
36
40
|
"spec/spec_helper.rb"
|
@@ -44,6 +48,7 @@ Gem::Specification.new do |s|
|
|
44
48
|
s.test_files = [
|
45
49
|
"spec/spec_helper.rb",
|
46
50
|
"spec/couchquilt/fs_spec.rb",
|
51
|
+
"spec/couchquilt/core_ext_spec.rb",
|
47
52
|
"spec/couchquilt/mapper_spec.rb"
|
48
53
|
]
|
49
54
|
|
data/lib/couchquilt.rb
CHANGED
@@ -29,7 +29,10 @@ $:.unshift File.dirname(__FILE__) unless
|
|
29
29
|
$:.include?(File.expand_path(File.dirname(__FILE__)))
|
30
30
|
|
31
31
|
require 'couchquilt/mapper'
|
32
|
+
require 'couchquilt/core_ext/array'
|
33
|
+
require 'couchquilt/core_ext/hash'
|
32
34
|
require 'couchquilt/couch_client'
|
35
|
+
require 'couchquilt/database'
|
33
36
|
require 'couchquilt/fs'
|
34
37
|
|
35
38
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Array
|
2
|
+
include Couchquilt::Mapper
|
3
|
+
|
4
|
+
def at_path(path)
|
5
|
+
head, *parts = to_parts(path)
|
6
|
+
return self if head.nil?
|
7
|
+
head = head.to_i
|
8
|
+
part = self[head]
|
9
|
+
return part if parts.empty? || !part.respond_to?(:at_path)
|
10
|
+
part.at_path(parts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def update_at_path(path, value)
|
14
|
+
head, *parts = to_parts(path)
|
15
|
+
return if head.nil?
|
16
|
+
head = head.to_i
|
17
|
+
return self[head] = value if parts.empty?
|
18
|
+
self[head] ||= []
|
19
|
+
self[head].update_at_path(parts, value)
|
20
|
+
end
|
21
|
+
|
22
|
+
def delete_at_path(path)
|
23
|
+
head, *parts = to_parts(path)
|
24
|
+
return if head.nil?
|
25
|
+
head = head.to_i
|
26
|
+
return self.delete_at(head) if parts.empty?
|
27
|
+
return unless self[head]
|
28
|
+
self[head].delete_at_path(parts)
|
29
|
+
end
|
30
|
+
|
31
|
+
def to_fs(named_path = true)
|
32
|
+
# using zip for backwards compatibily:
|
33
|
+
# in Ruby 1.9 (and 1.8.7) we could simply use
|
34
|
+
# each_with_index.map { |k,i| ... }
|
35
|
+
named_path ? zip((0..size).to_a).map { |v,i| name_for(i, v) } : self
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Hash
|
2
|
+
include Couchquilt::Mapper
|
3
|
+
|
4
|
+
def at_path(path)
|
5
|
+
head, *parts = to_parts(path)
|
6
|
+
return self if head.nil?
|
7
|
+
part = self[head]
|
8
|
+
return part if parts.empty? || !part.respond_to?(:at_path)
|
9
|
+
part.at_path(parts)
|
10
|
+
end
|
11
|
+
|
12
|
+
def update_at_path(path, value)
|
13
|
+
head, *parts = to_parts(path)
|
14
|
+
return if head.nil?
|
15
|
+
return self[head] = value if parts.empty?
|
16
|
+
self[head] ||= {}
|
17
|
+
self[head].update_at_path(parts, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete_at_path(path)
|
21
|
+
head, *parts = to_parts(path)
|
22
|
+
return if head.nil?
|
23
|
+
return self.delete(head) if parts.empty?
|
24
|
+
return unless self[head]
|
25
|
+
self[head].delete_at_path(parts)
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_fs(named_path = true)
|
29
|
+
named_path ? keys.map { |k| name_for(k, self[k]) }.sort : keys.sort
|
30
|
+
end
|
31
|
+
|
32
|
+
def map_arrays!
|
33
|
+
each do |key, value|
|
34
|
+
next unless value.is_a?(Hash)
|
35
|
+
if value.keys.all? { |k| k =~ /^\d+$/ }
|
36
|
+
self[key] = value.keys.sort.map { |k| value[k] }
|
37
|
+
else
|
38
|
+
value.map_arrays!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Couchquilt
|
2
|
+
module Database
|
3
|
+
## database queries
|
4
|
+
|
5
|
+
# does a database, document or design_document exists?
|
6
|
+
def exists?(path)
|
7
|
+
@couch.head key_for(path)
|
8
|
+
end
|
9
|
+
|
10
|
+
# query for all dbs
|
11
|
+
def all_dbs
|
12
|
+
@couch.get("_all_dbs")
|
13
|
+
end
|
14
|
+
|
15
|
+
# query for database info
|
16
|
+
def database_info(database, parts = [])
|
17
|
+
@couch.get(database).at_path(parts)
|
18
|
+
end
|
19
|
+
|
20
|
+
# query for all docs ids
|
21
|
+
def all_doc_ids(database, query_string = nil)
|
22
|
+
path = "#{database}/_all_docs"
|
23
|
+
path << "?#{URI.encode query_string}" if query_string
|
24
|
+
@couch.get(path)["rows"].map { |r| r["id"] }
|
25
|
+
end
|
26
|
+
|
27
|
+
# query for document
|
28
|
+
def document(database, id, parts = [])
|
29
|
+
@couch.get("#{database}/#{id}").at_path(parts)
|
30
|
+
end
|
31
|
+
|
32
|
+
# query for view
|
33
|
+
def view(database, id, parts)
|
34
|
+
a, view_function_name, *rest = parts
|
35
|
+
query = [id.sub("_design/", ""), "_view", view_function_name].join("/")
|
36
|
+
doc = @couch.get("#{database}/_design/#{query}").at_path(rest)
|
37
|
+
end
|
38
|
+
|
39
|
+
# query a function
|
40
|
+
def function_result(database, id, parts)
|
41
|
+
@couch.get key_for(File.join(database, id, *parts))
|
42
|
+
end
|
43
|
+
|
44
|
+
## document manipulation
|
45
|
+
|
46
|
+
# updating documents
|
47
|
+
def update(database, id, doc)
|
48
|
+
doc.map_arrays!
|
49
|
+
@couch.put("#{database}/#{id}", doc)
|
50
|
+
end
|
51
|
+
|
52
|
+
# create database or document
|
53
|
+
def create(database, id = nil)
|
54
|
+
@couch.put("#{database}/#{id}")
|
55
|
+
end
|
56
|
+
|
57
|
+
# delete a database
|
58
|
+
def delete_database(database)
|
59
|
+
@couch.delete(database)
|
60
|
+
end
|
61
|
+
|
62
|
+
# delete a document
|
63
|
+
def delete_document(database, id)
|
64
|
+
doc = document(database, id)
|
65
|
+
@couch.delete("#{database}/#{id}?rev=#{doc["_rev"]}")
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
data/lib/couchquilt/fs.rb
CHANGED
@@ -4,6 +4,7 @@ require 'uri'
|
|
4
4
|
module Couchquilt
|
5
5
|
class FS
|
6
6
|
include Mapper
|
7
|
+
include Database
|
7
8
|
|
8
9
|
# initializes Quilt FS with the database server name
|
9
10
|
def initialize(server_name)
|
@@ -16,39 +17,35 @@ module Couchquilt
|
|
16
17
|
|
17
18
|
list case named_path(path)
|
18
19
|
when :root
|
19
|
-
["_uuids"] +
|
20
|
+
["_uuids"] + all_dbs
|
20
21
|
when :uuids
|
21
22
|
["0i.js"]
|
22
23
|
when :database
|
23
24
|
["_design"] +
|
24
25
|
# database meta data
|
25
|
-
|
26
|
+
database_info(database, id).to_fs +
|
26
27
|
# all documents but design documents
|
27
|
-
|
28
|
-
# because that would return no results for databases without design documents
|
29
|
-
@couch.get("#{database}/_all_docs")["rows"].map { |r| r["id"] }.select { |id| id !~ /^_design\// }
|
28
|
+
all_doc_ids(database).select { |id| id !~ /^_design\// }
|
30
29
|
when :_design
|
31
|
-
query = URI.encode('startkey="_design"&endkey="_design0"')
|
32
30
|
# all design documents
|
33
|
-
|
31
|
+
all_doc_ids(database, 'startkey="_design"&endkey="_design0"').map { |id| id.sub("_design/", "") }
|
34
32
|
when :_show
|
35
|
-
(
|
33
|
+
document(database, id, "shows").to_fs(false)
|
36
34
|
when :_list
|
37
|
-
(
|
35
|
+
document(database, id, "lists").to_fs(false)
|
38
36
|
when :_view
|
39
|
-
(
|
37
|
+
document(database, id, "views").to_fs(false)
|
40
38
|
when :list_function
|
41
|
-
(
|
39
|
+
document(database, id, "views").to_fs(false).map { |name| "#{name}.html" }
|
42
40
|
when :show_function
|
43
|
-
|
44
|
-
@couch.get("#{database}/_all_docs?#{query}")["rows"].map { |r| "#{r["id"]}.html" }
|
41
|
+
all_doc_ids(database, 'startkey="_design0"').map { |id| "#{id}.html" }
|
45
42
|
when :view_function, :view_function_result
|
46
|
-
|
43
|
+
view(database, id, parts).to_fs
|
47
44
|
when :design_document
|
48
45
|
["_list", "_show", "_view"] +
|
49
|
-
|
46
|
+
document(database, id).to_fs
|
50
47
|
else
|
51
|
-
|
48
|
+
document(database, id, parts).to_fs
|
52
49
|
end
|
53
50
|
end
|
54
51
|
|
@@ -58,21 +55,21 @@ module Couchquilt
|
|
58
55
|
|
59
56
|
case named_path(path)
|
60
57
|
when :database, :document, :design_document
|
61
|
-
|
58
|
+
exists?(path)
|
62
59
|
when :view_function_result
|
63
|
-
doc =
|
60
|
+
doc = view(database, id, parts)
|
64
61
|
# arrays and hashes are mapped into directories
|
65
62
|
doc.is_a?(Hash) || doc.is_a?(Array)
|
66
63
|
when :database_info, :show_function_result, :list_function_result, :uuid
|
67
64
|
false
|
68
|
-
when
|
65
|
+
when :document_part
|
69
66
|
# look into document
|
70
|
-
doc =
|
67
|
+
doc = document(database, id, parts)
|
71
68
|
# arrays and hashes are mapped into directories
|
72
69
|
doc.is_a?(Hash) || doc.is_a?(Array)
|
73
70
|
else
|
74
71
|
# all other special paths are directories by now
|
75
|
-
# TODO: thats not
|
72
|
+
# TODO: thats not very good.
|
76
73
|
true
|
77
74
|
end
|
78
75
|
end
|
@@ -85,11 +82,11 @@ module Couchquilt
|
|
85
82
|
when :database_info, :show_function_result, :list_function_result, :uuid
|
86
83
|
true
|
87
84
|
when :view_function_result
|
88
|
-
# Every javascript or HTML is file, based on extension
|
85
|
+
# Every javascript or HTML is a file, based on extension
|
89
86
|
[".js", ".html"].include?(File.extname(path))
|
90
|
-
when
|
87
|
+
when :document_part
|
91
88
|
# look into document
|
92
|
-
doc =
|
89
|
+
doc = document(database, id, parts)
|
93
90
|
# only arrays and hashes are mapped into directories
|
94
91
|
!doc.nil? && !(doc.is_a?(Hash) || doc.is_a?(Array))
|
95
92
|
else
|
@@ -103,19 +100,15 @@ module Couchquilt
|
|
103
100
|
|
104
101
|
content case named_path(path)
|
105
102
|
when :database_info
|
106
|
-
|
107
|
-
when :show_function_result
|
108
|
-
parts
|
109
|
-
@couch.get(key_for(File.join(database, id, "_show", *parts)))
|
110
|
-
when :list_function_result
|
111
|
-
parts.shift
|
112
|
-
@couch.get(key_for(File.join(database, id, "_list", *parts)))
|
103
|
+
database_info(database, id)
|
104
|
+
when :show_function_result, :list_function_result
|
105
|
+
function_result(database, id, parts)
|
113
106
|
when :view_function_result
|
114
|
-
|
107
|
+
view(database, id, parts)
|
115
108
|
when :uuid
|
116
|
-
|
109
|
+
document(database, id, "uuids")
|
117
110
|
else
|
118
|
-
|
111
|
+
document(database, id, parts)
|
119
112
|
end
|
120
113
|
end
|
121
114
|
|
@@ -139,11 +132,11 @@ module Couchquilt
|
|
139
132
|
str.strip!
|
140
133
|
database, id, *parts = extract_parts(path)
|
141
134
|
# fetch document
|
142
|
-
doc =
|
135
|
+
doc = document(database, id)
|
143
136
|
# update the value that the file at path holds
|
144
|
-
|
137
|
+
doc.update_at_path(parts, str)
|
145
138
|
# save document
|
146
|
-
|
139
|
+
update(database, id, doc)
|
147
140
|
end
|
148
141
|
|
149
142
|
# can I delete path?
|
@@ -164,11 +157,11 @@ module Couchquilt
|
|
164
157
|
database, id, *parts = extract_parts(path)
|
165
158
|
|
166
159
|
# fetch document
|
167
|
-
doc =
|
160
|
+
doc = document(database, id)
|
168
161
|
# remove object
|
169
|
-
|
162
|
+
doc.delete_at_path(parts)
|
170
163
|
# save document
|
171
|
-
|
164
|
+
update(database, id, doc)
|
172
165
|
end
|
173
166
|
|
174
167
|
# can I make a directory at path?
|
@@ -180,9 +173,9 @@ module Couchquilt
|
|
180
173
|
false
|
181
174
|
when :database, :document, :design_document
|
182
175
|
# can create database or document unless exists
|
183
|
-
|
176
|
+
!exists?(path)
|
184
177
|
else
|
185
|
-
!
|
178
|
+
!document(database, id, parts)
|
186
179
|
end
|
187
180
|
end
|
188
181
|
|
@@ -193,16 +186,16 @@ module Couchquilt
|
|
193
186
|
|
194
187
|
case named_path(path)
|
195
188
|
when :database
|
196
|
-
|
189
|
+
create(database)
|
197
190
|
when :document, :design_document
|
198
|
-
|
191
|
+
create(database, id)
|
199
192
|
else
|
200
193
|
# fetch document
|
201
|
-
doc =
|
194
|
+
doc = document(database, id)
|
202
195
|
# insert empty object
|
203
|
-
|
196
|
+
doc.update_at_path(parts, {})
|
204
197
|
# save document
|
205
|
-
|
198
|
+
update(database, id, doc)
|
206
199
|
end
|
207
200
|
end
|
208
201
|
|
@@ -214,7 +207,7 @@ module Couchquilt
|
|
214
207
|
when :root, :_design, :_list, :list_function, :_show, :show_function, :_view, :view_function, :view_function_result, :database, :document, :design_document
|
215
208
|
false
|
216
209
|
else
|
217
|
-
|
210
|
+
document(database, id, parts).empty? rescue false
|
218
211
|
end
|
219
212
|
end
|
220
213
|
|
@@ -224,11 +217,11 @@ module Couchquilt
|
|
224
217
|
database, id, *parts = extract_parts(path)
|
225
218
|
|
226
219
|
# fetch document
|
227
|
-
doc =
|
220
|
+
doc = document(database, id)
|
228
221
|
# remove object
|
229
|
-
|
222
|
+
doc.delete_at_path(parts)
|
230
223
|
# save document
|
231
|
-
|
224
|
+
update(database, id, doc)
|
232
225
|
end
|
233
226
|
|
234
227
|
# switch to delete a database or document
|
@@ -237,10 +230,9 @@ module Couchquilt
|
|
237
230
|
|
238
231
|
case named_path(path)
|
239
232
|
when :switch_delete_database
|
240
|
-
|
233
|
+
delete_database(database)
|
241
234
|
when :switch_delete_document
|
242
|
-
|
243
|
-
@couch.delete("#{database}/#{id}?rev=#{doc["_rev"]}")
|
235
|
+
delete_document(database, id)
|
244
236
|
end
|
245
237
|
end
|
246
238
|
|
@@ -315,37 +307,13 @@ module Couchquilt
|
|
315
307
|
# /database_id/document_id/object
|
316
308
|
# /database_id/_design/design_document_id
|
317
309
|
# /database_id/_design/design_document_id/object
|
318
|
-
|
310
|
+
:document_part
|
319
311
|
end
|
320
312
|
end
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
parts.map! { |p| key_for p }
|
326
|
-
get_part(doc, parts)
|
327
|
-
end
|
328
|
-
|
329
|
-
# get view result, or a part of that document.
|
330
|
-
def get_view_result_part(database, id, parts = [])
|
331
|
-
a, view_function_name, *rest = parts
|
332
|
-
view = [id.sub("_design/", ""), "_view", view_function_name].join("/")
|
333
|
-
doc = @couch.get("#{database}/_design/#{view}")
|
334
|
-
rest.map! { |p| key_for p }
|
335
|
-
get_part(doc, rest)
|
336
|
-
end
|
337
|
-
|
338
|
-
# get a part of the document
|
339
|
-
# eg: get_part({ :a => { :b => :c}}, [:a, :b]) #=> :c
|
340
|
-
def get_part(doc, keys = [])
|
341
|
-
return if doc.nil?
|
342
|
-
doc = doc.dup
|
343
|
-
keys.each do |key|
|
344
|
-
doc = doc[key]
|
345
|
-
end
|
346
|
-
doc
|
347
|
-
end
|
348
|
-
|
313
|
+
|
314
|
+
|
315
|
+
## list and content helper
|
316
|
+
|
349
317
|
# escapes the value for using as filename
|
350
318
|
def list(array)
|
351
319
|
return [] if array.nil?
|