This repository has been archived by the owner on Jul 28, 2022. It is now read-only.
/
cloudlib
executable file
·265 lines (251 loc) · 8.46 KB
/
cloudlib
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
#!/usr/bin/env ruby
require 'yaml'
require 'rubygems'
require 'digest/sha1'
require 'cloudlib' # cloudlib gem
require 'optparse'
require 'highline/import' # highline gem
PROGNAME = "cloudlib"
NUMITEMS = 10
options = {}
opts = OptionParser.new do |opts|
opts.program_name = "#{PROGNAME} (c) 2008 John MacFarlane"
opts.banner = "cloudlib -- a library of books and articles in the AWS `cloud'\n" +
"Usage: #{PROGNAME} - start interactive menu\n" +
" #{PROGNAME} add FILE - upload FILE to library, prompting for metadata\n" +
" #{PROGNAME} new-library - initialize new library\n" +
" #{PROGNAME} delete-library - delete library and all its entries\n" +
" #{PROGNAME} dump [PATH] - create a local backup of library in PATH\n" +
" #{PROGNAME} restore [PATH] - restore library from local backup in PATH\n" +
" #{PROGNAME} list - list entries in library and print statistics\n" +
"Options:"
end
opts.on("-h", "--help", "Show usage message") do
puts opts.help
exit 0
end
begin
opts.parse!
rescue OptionParser::ParseError => e
puts e.message
puts opts.help
exit 1
end
# check that required environment variables are set, and prompt if they aren't
envvars = ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "CLOUDLIB_LIBRARY_NAME"]
envvars.each do |var|
unless ENV[var]
ENV[var] = ask("#{var}: ", String) { |q| q.echo = if var == "AWS_SECRET_ACCESS_KEY" then "*" else true end }
end
end
Cloudlib::Entry.connect(ENV['CLOUDLIB_LIBRARY_NAME'], ENV['AWS_ACCESS_KEY_ID'], ENV['AWS_SECRET_ACCESS_KEY'])
class Cloudlib::Entry
# Prompts for metadata for the entry, using existing metadata as defaults.
def ask_attributes
default_type = if self.show_attribute('entry_type').empty?
"article"
else
self.show_attribute('entry_type')
end
entry_types = ['article','book','chapter','incollection','unpublished']
type = ask("Type (#{entry_types.join(', ')})? ", entry_types) {|q| q.default = default_type; q.case = :downcase; q.readline = true }
self.attributes['entry_type'] = [type]
self.fields.each { |field| self.ask_attribute(field.to_s) }
return self
end
# Prompts for metadata for a particular field.
def ask_attribute(attribute)
default = self.show_attribute(attribute)
ans = ask(attribute.capitalize +
if attribute == 'editors' || attribute == 'authors' then " (name [and name...]) " else " " end, String) { |q| q.readline = true; q.default = default }
set_attribute(attribute, ans)
end
end
def show_items(items, more=false)
items.each_index do |i|
say "<%= color('[#{i}]', BOLD) %> #{items[i].to_s}\n"
end
if more
say "<%= color('Enter', BOLD)%> for more...\n"
end
end
def html_version(entries)
"<html>\n" +
"<meta http-equiv=\"Content-Type\" content=\"text/html;charset=utf-8\" />\n" +
"<body>\n" +
"<ol>\n" +
entries.map do |entry|
"<li><a href=\"" + CGI.escape(entry.name) + "\">" +
CGI.escapeHTML(entry.to_s) + "</a></li>"
end.join("\n") +
"</ol>\n" +
"</body>\n" +
"</html>"
end
def menu(items)
token = query = ""
while true
commands = ["find KEYWORDS", "quit"]
if items.length > 0
then commands = ["bib NUM", "get NUM", "del NUM", "mod NUM", "url NUM"] + commands
end
choices = commands.join(' | ')
ans = ask("#{choices} ? ", String) { |q| q.readline = true; q.case = :downcase }
if ans == ""
if token.empty?
items = []
else
token, items = Cloudlib::Entry.query(query, NUMITEMS, token)
end
show_items(items, more=(not token.empty?))
elsif ans =~ /^quit|q$/
exit 0
elsif ans =~ /^find *(.+)$/
query = $1
token, items = Cloudlib::Entry.query(query, NUMITEMS)
show_items(items, more=(not token.empty?))
elsif ans =~ /^(get|del|mod|bib|url) *(\d+)$/
num = $2.to_i
if (num < 0) || (num >= items.length)
next
else
item = items[num]
case $1
when "get":
destpath = ask("Save as: ", String) { |q| q.default = item.friendly_filename; q.readline = true }
item.download(destpath)
puts "Downloaded #{destpath}"
when "mod":
item.ask_attributes
item.save
when "del":
item.delete
when "bib":
puts item.to_bibtex
when "url":
validminutes = ask("How long (in minutes) should the link be valid? ", Integer) { |q| q.default = 10; q.readline = true; }
puts item.url(validminutes)
else
raise "Unknown command."
end
end
else
puts "Unknown command"
end
end
end
if ARGV.length >= 1
if ARGV[0] == "new-library"
print "Create new library `#{ENV['CLOUDLIB_LIBRARY_NAME']}' (y/n)? "
ans = STDIN.gets
if ans =~ /^[Yy]/
begin
Cloudlib::Entry.create_library
rescue AWS::S3::BucketAlreadyExists
STDERR.puts "The library name `#{ENV['CLOUDLIB_LIBRARY_NAME']}' is already taken by another user."
STDERR.puts "Please set CLOUDLIB_LIBRARY_NAME to something else and try again."
exit 1
end
end
exit 0
end
if ARGV[0] == "delete-library"
print "Delete `#{ENV['CLOUDLIB_LIBRARY_NAME']}' and ALL OF ITS CONTENTS (y/n)? "
ans = STDIN.gets
if ans =~ /^[Yy]/
Cloudlib::Entry.delete_library
end
exit 0
end
if ARGV[0] == "backup"
print "Delete `#{ENV['CLOUDLIB_LIBRARY_NAME']}' and ALL OF ITS CONTENTS (y/n)? "
ans = STDIN.gets
if ans =~ /^[Yy]/
Cloudlib::Entry.delete_library
end
exit 0
end
if ARGV[0] == "add"
ARGV[1..(ARGV.length - 1)].each do |target|
unless File.exists?(target)
puts "File not found: #{target}"
exit 1
end
item = Cloudlib::Entry.from_file(target)
puts "Please enter metadata for `#{target}':"
item.ask_attributes
item.save
puts "Uploaded #{target}"
end
exit 0
end
if ARGV[0] == "list"
STDERR.puts "Generating list of entries (this may take a while)..."
token, entries = Cloudlib::Entry.query("",100)
while not (token.empty?)
token, next_entries = Cloudlib::Entry.query("", 100, token)
entries += next_entries
end
total_size = entries.inject(0) do |accum, e|
size = e.attributes['size'][0].to_i / (1024.0 * 1024.0)
printf "%7.2f MB %s\n", size, e.to_s
accum + size
end
rate_gb_month = 0.15
printf("\n%7.2f MB TOTAL (%d entries). ", total_size, entries.length)
printf("This will cost $%.2f to store for a year at $%.2f/GB/month.\n", (total_size / 1024.0) * rate_gb_month * 12, rate_gb_month)
exit 0
end
if ARGV[0] == "dump"
path = ARGV[1] || ask("Dump to which directory? ", String) { |q| q.default = "."; q.echo = true }
new_entries = 0
token, entries = Cloudlib::Entry.query("",100)
while not (token.empty?)
token, next_entries = Cloudlib::Entry.query("", 100, token)
entries += next_entries
end
total_size = entries.inject(0) {|accum, e| accum + e.attributes['size'][0].to_i}
printf("Total size is %.2f megabytes.\n", (total_size / (1024.0 * 1024.0)))
open("#{path}/#{ENV['CLOUDLIB_LIBRARY_NAME']}.db", 'w') do |file|
STDERR.puts "Backing up metadata..."
file.write(YAML.dump(entries))
end
open("#{path}/index.html", 'w') do |file|
STDERR.puts "Writing HTML index..."
file.write(html_version(entries))
end
STDERR.puts "Backing up files:"
entries.each do |entry|
if not File.exists?("#{path}/#{entry.name}")
entry.download("#{path}/#{entry.name}")
new_entries += 1
STDERR.puts entry.to_s
end
end
STDERR.puts "Backed up metadata and #{new_entries} new files."
exit 0
end
if ARGV[0] == "restore"
path = ARGV[1] || "."
entries = open("#{path}/#{ENV['CLOUDLIB_LIBRARY_NAME']}.db", 'r') { |file| YAML.load(file) }
entries.each do |entry|
filename = "#{path}/#{entry.name}"
Cloudlib::Entry.from_file(filename)
entry.save
STDERR.puts entry.to_s
end
STDERR.puts "Restored #{entries.length} entries."
exit 0
end
STDERR.puts "Unknown command #{ARGV[0]}."
puts opts.help
exit 1
else
begin
menu([])
rescue AwsSdb::NoSuchDomainError
STDERR.puts "The library `#{ENV['CLOUDLIB_LIBRARY_NAME']}' does not exist."
STDERR.puts "Use `#{PROGNAME} new-library' to create it."
exit 1
end
end