jchris-couchrest 0.9.2 → 0.9.3
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/couchview +40 -101
- data/lib/couch_rest.rb +1 -1
- data/lib/couch_rest/commands.rb +5 -0
- data/lib/couch_rest/commands/generate.rb +71 -0
- data/lib/couch_rest/commands/push.rb +99 -0
- data/lib/couchrest.rb +18 -0
- data/lib/file_manager.rb +35 -208
- metadata +5 -2
data/bin/couchview
CHANGED
@@ -1,111 +1,50 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
3
|
+
require 'optparse'
|
4
|
+
require File.dirname(__FILE__) + "/../lib/couch_rest/commands"
|
5
|
+
|
6
|
+
# Set defaults
|
7
|
+
options = {
|
8
|
+
:loud => true,
|
9
|
+
}
|
10
|
+
|
11
|
+
opts = OptionParser.new do |opts|
|
12
|
+
opts.banner = "Usage: #$0 [options] (push|generate) directory database"
|
13
|
+
opts.on('-q', '--quiet', "Omit extra debug info") do
|
14
|
+
options[:loud] = false
|
15
|
+
end
|
16
|
+
opts.on_tail('-h', '--help [push|generate]', "Display detailed help and exit") do |help_command|
|
17
|
+
puts opts
|
18
|
+
case help_command
|
19
|
+
when "push"
|
20
|
+
puts CouchRest::Commands::Push.help
|
21
|
+
when "generate"
|
22
|
+
puts CouchRest::Commands::Generate.help
|
23
|
+
end
|
24
|
+
exit
|
25
|
+
end
|
12
26
|
end
|
27
|
+
opts.parse!(ARGV)
|
13
28
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
puts <<-GEN
|
18
|
-
Usage: couchview generate directory design1 design2 design3 ...
|
19
|
-
|
20
|
-
Couchview will create directories and example views for the design documents you specify.
|
21
|
-
|
22
|
-
GEN
|
23
|
-
when "push"
|
24
|
-
puts <<-PUSH
|
25
|
-
== Pushing views with Couchview ==
|
26
|
-
|
27
|
-
Usage: couchview push directory dbname
|
28
|
-
|
29
|
-
Couchview expects a specific filesystem layout for your CouchDB views (see
|
30
|
-
example below). It also supports advanced features like inlining of library
|
31
|
-
code (so you can keep DRY) as well as avoiding unnecessary document
|
32
|
-
modification.
|
33
|
-
|
34
|
-
Couchview also solves a problem with CouchDB's view API, which only provides
|
35
|
-
access to the final reduce side of any views which have both a map and a
|
36
|
-
reduce function defined. The intermediate map results are often useful for
|
37
|
-
development and production. CouchDB is smart enough to reuse map indexes for
|
38
|
-
functions duplicated across views within the same design document.
|
39
|
-
|
40
|
-
For views with a reduce function defined, Couchview creates both a reduce view
|
41
|
-
and a map-only view, so that you can browse and query the map side as well as
|
42
|
-
the reduction, with no performance penalty.
|
43
|
-
|
44
|
-
== Example ==
|
45
|
-
|
46
|
-
couchview push foo-project/bar-views baz-database
|
29
|
+
options[:command] = ARGV.shift
|
30
|
+
options[:directory] = ARGV.shift
|
31
|
+
options[:trailing_args] = ARGV
|
47
32
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
foo-project/bar-views/my-design/viewname-map.js
|
53
|
-
foo-project/bar-views/my-design/viewname-reduce.js
|
54
|
-
foo-project/bar-views/my-design/noreduce-map.js
|
55
|
-
|
56
|
-
Pushed to => http://localhost:5984/baz-database/_design/my-design
|
57
|
-
|
58
|
-
And the design document:
|
59
|
-
{
|
60
|
-
"views" : {
|
61
|
-
"viewname-map" : {
|
62
|
-
"map" : "### contents of view-name-map.js ###"
|
63
|
-
},
|
64
|
-
"viewname-reduce" : {
|
65
|
-
"map" : "### contents of view-name-map.js ###",
|
66
|
-
"reduce" : "### contents of view-name-reduce.js ###"
|
67
|
-
},
|
68
|
-
"noreduce-map" : {
|
69
|
-
"map" : "### contents of noreduce-map.js ###"
|
70
|
-
}
|
71
|
-
}
|
72
|
-
}
|
73
|
-
|
74
|
-
Couchview will create a design document for each subdirectory of the views
|
75
|
-
directory specified on the command line.
|
76
|
-
|
77
|
-
== Library Inlining ==
|
78
|
-
|
79
|
-
Couchview can optionally inline library code into your views so you only have
|
80
|
-
to maintain it in one place. It looks for any files named lib.* in your
|
81
|
-
design-doc directory (for doc specific libs) and in the parent views directory
|
82
|
-
(for project global libs). These libraries are only inserted into views which
|
83
|
-
include the text
|
84
|
-
|
85
|
-
//include-lib
|
86
|
-
|
87
|
-
or
|
88
|
-
|
89
|
-
#include-lib
|
90
|
-
|
91
|
-
Couchview is a result of scratching my own itch. I'd be happy to make it more
|
92
|
-
general, so please contact me at jchris@grabb.it if you'd like to see anything
|
93
|
-
added or changed.
|
94
|
-
PUSH
|
95
|
-
end
|
33
|
+
# There must be a better way to check for extra required args
|
34
|
+
unless (["push", "generate"].include?(options[:command]) && options[:directory] && options[:trailing_args])
|
35
|
+
puts(opts)
|
96
36
|
exit
|
97
37
|
end
|
98
38
|
|
99
|
-
|
100
|
-
|
39
|
+
# The options hash now contains the resolved defaults
|
40
|
+
# and the overrides from the command line.
|
41
|
+
|
42
|
+
# Call your class and send it the options here
|
43
|
+
# cr = CouchRest::FileManager.new(options[:database_name])
|
101
44
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
puts "Pushing views from directory #{dirname} to database #{fm.db}"
|
108
|
-
fm.push_views(dirname)
|
109
|
-
elsif command == 'generate'
|
110
|
-
puts "Under construction ;)"
|
45
|
+
case options[:command]
|
46
|
+
when "push"
|
47
|
+
CouchRest::Commands::Push.run(options)
|
48
|
+
when "generate"
|
49
|
+
CouchRest::Commands::Generate.run(options)
|
111
50
|
end
|
data/lib/couch_rest.rb
CHANGED
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
class CouchRest
|
4
|
+
module Commands
|
5
|
+
module Generate
|
6
|
+
|
7
|
+
def self.run(options)
|
8
|
+
directory = options[:directory]
|
9
|
+
design_names = options[:trailing_args]
|
10
|
+
|
11
|
+
FileUtils.mkdir_p(directory)
|
12
|
+
filename = File.join(directory, "lib.js")
|
13
|
+
self.write(filename, <<-FUNC)
|
14
|
+
// Put global functions here.
|
15
|
+
// Include in your views with
|
16
|
+
//
|
17
|
+
// //include-lib
|
18
|
+
FUNC
|
19
|
+
|
20
|
+
design_names.each do |design_name|
|
21
|
+
subdirectory = File.join(directory, design_name)
|
22
|
+
FileUtils.mkdir_p(subdirectory)
|
23
|
+
filename = File.join(subdirectory, "sample-map.js")
|
24
|
+
self.write(filename, <<-FUNC)
|
25
|
+
function(doc) {
|
26
|
+
// Keys is first letter of _id
|
27
|
+
emit(doc._id[0], doc);
|
28
|
+
}
|
29
|
+
FUNC
|
30
|
+
|
31
|
+
filename = File.join(subdirectory, "sample-reduce.js")
|
32
|
+
self.write(filename, <<-FUNC)
|
33
|
+
function(keys, values) {
|
34
|
+
// Count the number of keys starting with this letter
|
35
|
+
return values.length;
|
36
|
+
}
|
37
|
+
FUNC
|
38
|
+
|
39
|
+
filename = File.join(subdirectory, "lib.js")
|
40
|
+
self.write(filename, <<-FUNC)
|
41
|
+
// Put functions specific to '#{design_name}' here.
|
42
|
+
// Include in your views with
|
43
|
+
//
|
44
|
+
// //include-lib
|
45
|
+
FUNC
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.help
|
50
|
+
helpstring = <<-GEN
|
51
|
+
|
52
|
+
Usage: couchview generate directory design1 design2 design3 ...
|
53
|
+
|
54
|
+
Couchview will create directories and example views for the design documents you specify.
|
55
|
+
|
56
|
+
GEN
|
57
|
+
helpstring.gsub(/^ /, '')
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.write(filename, contents)
|
61
|
+
puts "Writing #{filename}"
|
62
|
+
File.open(filename, "w") do |f|
|
63
|
+
# Remove leading spaces
|
64
|
+
contents.gsub!(/^ ( )?/, '')
|
65
|
+
f.write contents
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
class CouchRest
|
2
|
+
|
3
|
+
module Commands
|
4
|
+
|
5
|
+
module Push
|
6
|
+
|
7
|
+
def self.run(options)
|
8
|
+
directory = options[:directory]
|
9
|
+
database = options[:trailing_args].first
|
10
|
+
|
11
|
+
fm = CouchRest::FileManager.new(database)
|
12
|
+
fm.loud = options[:loud]
|
13
|
+
puts "Pushing views from directory #{directory} to database #{fm.db}"
|
14
|
+
fm.push_views(directory)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.help
|
18
|
+
helpstring = <<-GEN
|
19
|
+
|
20
|
+
== Pushing views with Couchview ==
|
21
|
+
|
22
|
+
Usage: couchview push directory dbname
|
23
|
+
|
24
|
+
Couchview expects a specific filesystem layout for your CouchDB views (see
|
25
|
+
example below). It also supports advanced features like inlining of library
|
26
|
+
code (so you can keep DRY) as well as avoiding unnecessary document
|
27
|
+
modification.
|
28
|
+
|
29
|
+
Couchview also solves a problem with CouchDB's view API, which only provides
|
30
|
+
access to the final reduce side of any views which have both a map and a
|
31
|
+
reduce function defined. The intermediate map results are often useful for
|
32
|
+
development and production. CouchDB is smart enough to reuse map indexes for
|
33
|
+
functions duplicated across views within the same design document.
|
34
|
+
|
35
|
+
For views with a reduce function defined, Couchview creates both a reduce view
|
36
|
+
and a map-only view, so that you can browse and query the map side as well as
|
37
|
+
the reduction, with no performance penalty.
|
38
|
+
|
39
|
+
== Example ==
|
40
|
+
|
41
|
+
couchview push foo-project/bar-views baz-database
|
42
|
+
|
43
|
+
This will push the views defined in foo-project/bar-views into a database
|
44
|
+
called baz-database. Couchview expects the views to be defined in files with
|
45
|
+
names like:
|
46
|
+
|
47
|
+
foo-project/bar-views/my-design/viewname-map.js
|
48
|
+
foo-project/bar-views/my-design/viewname-reduce.js
|
49
|
+
foo-project/bar-views/my-design/noreduce-map.js
|
50
|
+
|
51
|
+
Pushed to => http://localhost:5984/baz-database/_design/my-design
|
52
|
+
|
53
|
+
And the design document:
|
54
|
+
{
|
55
|
+
"views" : {
|
56
|
+
"viewname-map" : {
|
57
|
+
"map" : "### contents of view-name-map.js ###"
|
58
|
+
},
|
59
|
+
"viewname-reduce" : {
|
60
|
+
"map" : "### contents of view-name-map.js ###",
|
61
|
+
"reduce" : "### contents of view-name-reduce.js ###"
|
62
|
+
},
|
63
|
+
"noreduce-map" : {
|
64
|
+
"map" : "### contents of noreduce-map.js ###"
|
65
|
+
}
|
66
|
+
}
|
67
|
+
}
|
68
|
+
|
69
|
+
Couchview will create a design document for each subdirectory of the views
|
70
|
+
directory specified on the command line.
|
71
|
+
|
72
|
+
== Library Inlining ==
|
73
|
+
|
74
|
+
Couchview can optionally inline library code into your views so you only have
|
75
|
+
to maintain it in one place. It looks for any files named lib.* in your
|
76
|
+
design-doc directory (for doc specific libs) and in the parent views directory
|
77
|
+
(for project global libs). These libraries are only inserted into views which
|
78
|
+
include the text
|
79
|
+
|
80
|
+
//include-lib
|
81
|
+
|
82
|
+
or
|
83
|
+
|
84
|
+
#include-lib
|
85
|
+
|
86
|
+
Couchview is a result of scratching my own itch. I'd be happy to make it more
|
87
|
+
general, so please contact me at jchris@grabb.it if you'd like to see anything
|
88
|
+
added or changed.
|
89
|
+
|
90
|
+
GEN
|
91
|
+
helpstring.gsub(/^ /, '')
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
data/lib/couchrest.rb
CHANGED
@@ -8,4 +8,22 @@ require File.dirname(__FILE__) + '/pager'
|
|
8
8
|
require File.dirname(__FILE__) + '/file_manager'
|
9
9
|
require File.dirname(__FILE__) + '/streamer'
|
10
10
|
|
11
|
+
# this has to come after the JSON gem
|
11
12
|
|
13
|
+
# this date format sorts lexicographically
|
14
|
+
# and is compatible with Javascript's new Date(time_string) constructor
|
15
|
+
# note that sorting will break if you store times from multiple timezones
|
16
|
+
# I like to add a ENV['TZ'] = 'UTC' to my apps
|
17
|
+
class Time
|
18
|
+
def to_json(options = nil)
|
19
|
+
%("#{strftime("%Y/%m/%d %H:%M:%S %z")}")
|
20
|
+
end
|
21
|
+
# this works to decode the outputted time format
|
22
|
+
# from ActiveSupport
|
23
|
+
# def self.parse string, fallback=nil
|
24
|
+
# d = DateTime.parse(string).new_offset
|
25
|
+
# self.utc(d.year, d.month, d.day, d.hour, d.min, d.sec)
|
26
|
+
# rescue
|
27
|
+
# fallback
|
28
|
+
# end
|
29
|
+
end
|
data/lib/file_manager.rb
CHANGED
@@ -84,7 +84,7 @@ class CouchRest
|
|
84
84
|
|
85
85
|
doc["_attachments"][path] = {
|
86
86
|
"data" => content,
|
87
|
-
"content_type" =>
|
87
|
+
"content_type" => MIMES[path.split('.').last]
|
88
88
|
}
|
89
89
|
end
|
90
90
|
|
@@ -151,6 +151,40 @@ class CouchRest
|
|
151
151
|
designs
|
152
152
|
end
|
153
153
|
|
154
|
+
def pull_views(view_dir)
|
155
|
+
prefix = "_design"
|
156
|
+
ds = db.documents(:startkey => '#{prefix}/', :endkey => '#{prefix}/ZZZZZZZZZ')
|
157
|
+
ds['rows'].collect{|r|r['id']}.each do |id|
|
158
|
+
puts directory = id.split('/').last
|
159
|
+
FileUtils.mkdir_p(File.join(view_dir,directory))
|
160
|
+
views = db.get(id)['views']
|
161
|
+
|
162
|
+
vgroups = views.keys.group_by{|k|k.sub(/\-(map|reduce)$/,'')}
|
163
|
+
vgroups.each do|g,vs|
|
164
|
+
mapname = vs.find {|v|views[v]["map"]}
|
165
|
+
if mapname
|
166
|
+
# save map
|
167
|
+
mapfunc = views[mapname]["map"]
|
168
|
+
mapfile = File.join(view_dir, directory, "#{g}-map.js") # todo support non-js views
|
169
|
+
File.open(mapfile,'w') do |f|
|
170
|
+
f.write mapfunc
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
reducename = vs.find {|v|views[v]["reduce"]}
|
175
|
+
if reducename
|
176
|
+
# save reduce
|
177
|
+
reducefunc = views[reducename]["reduce"]
|
178
|
+
reducefile = File.join(view_dir, directory, "#{g}-reduce.js") # todo support non-js views
|
179
|
+
File.open(reducefile,'w') do |f|
|
180
|
+
f.write reducefunc
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
154
188
|
|
155
189
|
private
|
156
190
|
|
@@ -187,210 +221,3 @@ class CouchRest
|
|
187
221
|
end
|
188
222
|
end
|
189
223
|
end
|
190
|
-
|
191
|
-
__END__
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
# parse the file structure to load the public files, controllers, and views into a hash with the right shape for coucdb
|
197
|
-
couch = {}
|
198
|
-
|
199
|
-
couch["public"] = Dir["#{File.expand_path(File.dirname("."))}/public/**/*.*"].collect do |f|
|
200
|
-
{f.split("public/").last => open(f).read}
|
201
|
-
end
|
202
|
-
|
203
|
-
couch["controllers"] = {}
|
204
|
-
Dir["#{File.expand_path(File.dirname("."))}/app/controllers/**/*.*"].collect do |c|
|
205
|
-
path_parts = c.split("/")
|
206
|
-
|
207
|
-
controller_name = path_parts[path_parts.length - 2]
|
208
|
-
action_name = path_parts[path_parts.length - 1].split(".").first
|
209
|
-
|
210
|
-
couch["controllers"][controller_name] ||= {"actions" => {}}
|
211
|
-
couch["controllers"][controller_name]["actions"][action_name] = open(c).read
|
212
|
-
|
213
|
-
end
|
214
|
-
|
215
|
-
couch["designs"] = {}
|
216
|
-
Dir["#{File.expand_path(File.dirname("."))}/app/views/**/*.*"].collect do |design_doc|
|
217
|
-
design_doc_parts = design_doc.split('/')
|
218
|
-
pre_normalized_view_name = design_doc_parts.last.split("-")
|
219
|
-
view_name = pre_normalized_view_name[0..pre_normalized_view_name.length-2].join("-")
|
220
|
-
|
221
|
-
folder = design_doc.split("app/views").last.split("/")[1]
|
222
|
-
|
223
|
-
couch["designs"][folder] ||= {}
|
224
|
-
couch["designs"][folder]["views"] ||= {}
|
225
|
-
couch["designs"][folder]["language"] ||= LANGS[design_doc_parts.last.split(".").last]
|
226
|
-
|
227
|
-
if design_doc_parts.last =~ /-map/
|
228
|
-
couch["designs"][folder]["views"]["#{view_name}-map"] ||= {}
|
229
|
-
|
230
|
-
couch["designs"][folder]["views"]["#{view_name}-map"]["map"] = open(design_doc).read
|
231
|
-
|
232
|
-
couch["designs"][folder]["views"]["#{view_name}-reduce"] ||= {}
|
233
|
-
couch["designs"][folder]["views"]["#{view_name}-reduce"]["map"] = open(design_doc).read
|
234
|
-
end
|
235
|
-
|
236
|
-
if design_doc_parts.last =~ /-reduce/
|
237
|
-
couch["designs"][folder]["views"]["#{view_name}-reduce"] ||= {}
|
238
|
-
|
239
|
-
couch["designs"][folder]["views"]["#{view_name}-reduce"]["reduce"] = open(design_doc).read
|
240
|
-
end
|
241
|
-
end
|
242
|
-
|
243
|
-
# cleanup empty maps and reduces
|
244
|
-
couch["designs"].each do |name, props|
|
245
|
-
props["views"].delete("#{name}-reduce") unless props["views"]["#{name}-reduce"].keys.include?("reduce")
|
246
|
-
end
|
247
|
-
|
248
|
-
# parsing done, begin posting
|
249
|
-
|
250
|
-
# connect to couchdb
|
251
|
-
cr = CouchRest.new("http://localhost:5984")
|
252
|
-
@db = cr.database(DBNAME)
|
253
|
-
|
254
|
-
def create_or_update(id, fields)
|
255
|
-
existing = get(id)
|
256
|
-
|
257
|
-
if existing
|
258
|
-
updated = fields.merge({"_id" => id, "_rev" => existing["_rev"]})
|
259
|
-
else
|
260
|
-
puts "saving #{id}"
|
261
|
-
save(fields.merge({"_id" => id}))
|
262
|
-
end
|
263
|
-
|
264
|
-
if existing == updated
|
265
|
-
puts "no change to #{id}. skipping..."
|
266
|
-
else
|
267
|
-
puts "replacing #{id}"
|
268
|
-
save(updated)
|
269
|
-
end
|
270
|
-
|
271
|
-
end
|
272
|
-
|
273
|
-
def get(id)
|
274
|
-
doc = handle_errors do
|
275
|
-
@db.get(id)
|
276
|
-
end
|
277
|
-
end
|
278
|
-
|
279
|
-
def save(doc)
|
280
|
-
handle_errors do
|
281
|
-
@db.save(doc)
|
282
|
-
end
|
283
|
-
end
|
284
|
-
|
285
|
-
def handle_errors(&block)
|
286
|
-
begin
|
287
|
-
yield
|
288
|
-
rescue Exception => e
|
289
|
-
# puts e.message
|
290
|
-
nil
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
|
295
|
-
if todo.include? "views"
|
296
|
-
puts "posting views into CouchDB"
|
297
|
-
couch["designs"].each do |k,v|
|
298
|
-
create_or_update("_design/#{k}", v)
|
299
|
-
end
|
300
|
-
puts
|
301
|
-
end
|
302
|
-
|
303
|
-
if todo.include? "controllers"
|
304
|
-
puts "posting controllers into CouchDB"
|
305
|
-
couch["controllers"].each do |k,v|
|
306
|
-
create_or_update("controller/#{k}", v)
|
307
|
-
end
|
308
|
-
puts
|
309
|
-
end
|
310
|
-
|
311
|
-
|
312
|
-
if todo.include? "public"
|
313
|
-
puts "posting public docs into CouchDB"
|
314
|
-
|
315
|
-
if couch["public"].empty?
|
316
|
-
puts "no docs in public"; exit
|
317
|
-
end
|
318
|
-
|
319
|
-
@content_types = {
|
320
|
-
"html" => "text/html",
|
321
|
-
"htm" => "text/html",
|
322
|
-
"png" => "image/png",
|
323
|
-
"css" => "text/css",
|
324
|
-
"js" => "test/javascript"
|
325
|
-
}
|
326
|
-
|
327
|
-
def md5 string
|
328
|
-
Digest::MD5.hexdigest(string)
|
329
|
-
end
|
330
|
-
|
331
|
-
@attachments = {}
|
332
|
-
@signatures = {}
|
333
|
-
couch["public"].each do |doc|
|
334
|
-
@signatures[doc.keys.first] = md5(doc.values.first)
|
335
|
-
|
336
|
-
@attachments[doc.keys.first] = {
|
337
|
-
"data" => doc.values.first,
|
338
|
-
"content_type" => @content_types[doc.keys.first.split('.').last]
|
339
|
-
}
|
340
|
-
end
|
341
|
-
|
342
|
-
doc = get("public")
|
343
|
-
|
344
|
-
unless doc
|
345
|
-
puts "creating public"
|
346
|
-
@db.save({"_id" => "public", "_attachments" => @attachments, "signatures" => @signatures})
|
347
|
-
exit
|
348
|
-
end
|
349
|
-
|
350
|
-
# remove deleted docs
|
351
|
-
to_be_removed = doc["signatures"].keys.select{|d| !couch["public"].collect{|p| p.keys.first}.include?(d) }
|
352
|
-
|
353
|
-
to_be_removed.each do |p|
|
354
|
-
puts "deleting #{p}"
|
355
|
-
doc["signatures"].delete(p)
|
356
|
-
doc["_attachments"].delete(p)
|
357
|
-
end
|
358
|
-
|
359
|
-
# update existing docs:
|
360
|
-
doc["signatures"].each do |path, sig|
|
361
|
-
if (@signatures[path] == sig)
|
362
|
-
puts "no change to #{path}. skipping..."
|
363
|
-
else
|
364
|
-
puts "replacing #{path}"
|
365
|
-
doc["signatures"][path] = md5(@attachments[path]["data"])
|
366
|
-
doc["_attachments"][path].delete("stub")
|
367
|
-
doc["_attachments"][path].delete("length")
|
368
|
-
doc["_attachments"][path]["data"] = @attachments[path]["data"]
|
369
|
-
doc["_attachments"][path].merge!({"data" => @attachments[path]["data"]} )
|
370
|
-
|
371
|
-
end
|
372
|
-
end
|
373
|
-
|
374
|
-
# add in new files
|
375
|
-
new_files = couch["public"].select{|d| !doc["signatures"].keys.include?( d.keys.first) }
|
376
|
-
|
377
|
-
new_files.each do |f|
|
378
|
-
puts "creating #{f}"
|
379
|
-
path = f.keys.first
|
380
|
-
content = f.values.first
|
381
|
-
doc["signatures"][path] = md5(content)
|
382
|
-
|
383
|
-
doc["_attachments"][path] = {
|
384
|
-
"data" => content,
|
385
|
-
"content_type" => @content_types[path.split('.').last]
|
386
|
-
}
|
387
|
-
end
|
388
|
-
|
389
|
-
begin
|
390
|
-
@db.save(doc)
|
391
|
-
rescue Exception => e
|
392
|
-
puts e.message
|
393
|
-
end
|
394
|
-
|
395
|
-
puts
|
396
|
-
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: jchris-couchrest
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.9.
|
4
|
+
version: 0.9.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- J. Chris Anderson
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2008-
|
13
|
+
date: 2008-09-10 00:00:00 -07:00
|
14
14
|
default_executable:
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
@@ -47,6 +47,9 @@ files:
|
|
47
47
|
- lib/pager.rb
|
48
48
|
- lib/file_manager.rb
|
49
49
|
- lib/streamer.rb
|
50
|
+
- lib/couch_rest/commands.rb
|
51
|
+
- lib/couch_rest/commands/generate.rb
|
52
|
+
- lib/couch_rest/commands/push.rb
|
50
53
|
- Rakefile
|
51
54
|
- README.markdown
|
52
55
|
- bin/couchdir
|