smc-get 0.1.0 → 0.2.0.beta1
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/COPYING +674 -674
- data/HISTORY.rdoc +31 -0
- data/README.rdoc +163 -148
- data/VERSION.txt +1 -1
- data/bin/smc-checksum +44 -0
- data/bin/smc-get +31 -31
- data/config/smc-get.yml +32 -32
- data/lib/smc_get.rb +23 -21
- data/lib/smc_get/cui.rb +325 -289
- data/lib/smc_get/cui_commands/build.rb +329 -0
- data/lib/smc_get/cui_commands/command.rb +117 -81
- data/lib/smc_get/cui_commands/getinfo.rb +92 -91
- data/lib/smc_get/cui_commands/help.rb +65 -64
- data/lib/smc_get/cui_commands/install.rb +101 -91
- data/lib/smc_get/cui_commands/list.rb +101 -80
- data/lib/smc_get/cui_commands/search.rb +106 -109
- data/lib/smc_get/cui_commands/uninstall.rb +80 -78
- data/lib/smc_get/cui_commands/update.rb +119 -0
- data/lib/smc_get/cui_commands/version.rb +58 -57
- data/lib/smc_get/errors.rb +140 -113
- data/lib/smc_get/gui.rb +20 -19
- data/lib/smc_get/local_repository.rb +277 -0
- data/lib/smc_get/package.rb +260 -279
- data/lib/smc_get/package_archive.rb +80 -0
- data/lib/smc_get/package_specification.rb +253 -0
- data/lib/smc_get/remote_repository.rb +272 -0
- data/lib/smc_get/repository.rb +10 -0
- data/lib/smc_get/smc_get.rb +117 -139
- data/smcpak.rdoc +332 -0
- data/test/test_smc-get-cui.rb +123 -121
- data/test/test_smc-get-lib.rb +172 -170
- metadata +71 -36
@@ -0,0 +1,329 @@
|
|
1
|
+
#Encoding: UTF-8
|
2
|
+
################################################################################
|
3
|
+
# This file is part of smc-get.
|
4
|
+
# Copyright (C) 2010-2011 Entertaining Software, Inc.
|
5
|
+
# Copyright (C) 2011 Marvin Gülker
|
6
|
+
#
|
7
|
+
# This program is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
################################################################################
|
20
|
+
|
21
|
+
module SmcGet
|
22
|
+
|
23
|
+
module CUICommands
|
24
|
+
|
25
|
+
class BuildCommand < Command
|
26
|
+
|
27
|
+
def self.help
|
28
|
+
<<-EOF
|
29
|
+
USAGE: #{File.basename($0)} build [DIRECTORY]
|
30
|
+
|
31
|
+
If DIRECTORY is given, validates it's structure against the SMC packaging
|
32
|
+
guidelines and then packages it into a .smcpak file. If DIRECTORY is not
|
33
|
+
given, enters an interactive process that queries you for the files you
|
34
|
+
want to include in your SMC package. In both cases you’ll end up with
|
35
|
+
a SMC package in your current directory.
|
36
|
+
|
37
|
+
Files you enter during the interrogative process are looked for in your
|
38
|
+
user-level SMC directory, i.e. ~/.smc.
|
39
|
+
EOF
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.summary
|
43
|
+
"build\t\tBuild SMC packages."
|
44
|
+
end
|
45
|
+
|
46
|
+
def parse(args)
|
47
|
+
CUI.debug("Parsing #{args.count} args for build.")
|
48
|
+
raise(InvalidCommandline, "Too many arguments.") if args.count > 1
|
49
|
+
@directory = args.shift #nil if not given
|
50
|
+
end
|
51
|
+
|
52
|
+
def execute(config)
|
53
|
+
CUI.debug("Executing build.")
|
54
|
+
begin
|
55
|
+
if @directory
|
56
|
+
Package.create(@directory)
|
57
|
+
else #No directory given
|
58
|
+
puts(<<-MSG)
|
59
|
+
Welcome to the smc-get build process!
|
60
|
+
Answer the following questions properly and you'll end up with a ready-to-
|
61
|
+
install package you can either install locally or contribute to the repository
|
62
|
+
(which would make it installable via 'smc-get install' directly). When a question
|
63
|
+
asks you to input multiple files, you can end the query with an empty line.
|
64
|
+
If you like, you can specify multiple files at once by separating them
|
65
|
+
with a comma. Wildcards in filenames are allowed.
|
66
|
+
|
67
|
+
Files you don't specify via an absolute path (i.e. a path beginning with
|
68
|
+
either / on *nix or <letter>:\\ on Windows) are searched for in your
|
69
|
+
home directory's .smc directory and your SMC installation.
|
70
|
+
MSG
|
71
|
+
#All the information will be collected in this hash
|
72
|
+
spec = Hash.new{|hsh, key| hsh[key] = []}
|
73
|
+
|
74
|
+
#Start the questionaire
|
75
|
+
[:levels, :graphics, :music, :sounds, :worlds].each do |sym|
|
76
|
+
spec[sym].concat(input_files(sym))
|
77
|
+
end
|
78
|
+
|
79
|
+
puts
|
80
|
+
puts("Who participated in creating this package?")
|
81
|
+
loop do
|
82
|
+
print "> "
|
83
|
+
str = $stdin.gets.chomp
|
84
|
+
|
85
|
+
if str.empty?
|
86
|
+
if spec[:authors].empty?
|
87
|
+
$stderr.puts("You have to input at least one author.")
|
88
|
+
else
|
89
|
+
break
|
90
|
+
end
|
91
|
+
else #Something was entered
|
92
|
+
spec[:authors].concat(str.split(",").map(&:strip))
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
puts
|
97
|
+
puts "Enter this package's dependecy packages:"
|
98
|
+
loop do
|
99
|
+
print "> "
|
100
|
+
str = $stdin.gets.chomp
|
101
|
+
if str.empty?
|
102
|
+
break
|
103
|
+
else
|
104
|
+
spec[:dependencies].concat(str.split(",").map(&:strip))
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
loop do
|
109
|
+
puts
|
110
|
+
print("Enter the difficulty: ")
|
111
|
+
break unless (spec[:difficulty] = $stdin.gets.chomp).empty?
|
112
|
+
end
|
113
|
+
|
114
|
+
puts
|
115
|
+
puts("Enter the package's description. A single line containing containg")
|
116
|
+
puts("END")
|
117
|
+
puts("terminates the query.")
|
118
|
+
spec[:description] = ""
|
119
|
+
loop do
|
120
|
+
print "> "
|
121
|
+
str = $stdin.gets #No chomp here, the user may set spaces at the line end intentionally
|
122
|
+
if str == "END\n"
|
123
|
+
if spec[:description].strip.empty?
|
124
|
+
$stderr.puts("You *have* to input a description!")
|
125
|
+
$stderr.puts("And it must consist of something else than only whitespace!")
|
126
|
+
else
|
127
|
+
break
|
128
|
+
end
|
129
|
+
else
|
130
|
+
spec[:description] << str
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
[:install_message, :remove_message].each{|sym| spec[sym] = input_desc(sym)}
|
135
|
+
|
136
|
+
loop do
|
137
|
+
puts
|
138
|
+
print("Enter the package's full title (it can contain whitespace):")
|
139
|
+
spec[:title] = $stdin.gets.chomp
|
140
|
+
if spec[:title].strip.empty?
|
141
|
+
$stderr.puts "You *have* to specify a title that doesn't consist solely of whitespace!"
|
142
|
+
else
|
143
|
+
break
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
pkgname = nil
|
148
|
+
loop do
|
149
|
+
puts
|
150
|
+
print "Enter the package's name (this mustn't contain whitespace):"
|
151
|
+
pkgname = $stdin.gets.chomp
|
152
|
+
if pkgname.strip.empty?
|
153
|
+
$stderr.puts "You *have* to specify a name!"
|
154
|
+
elsif pkgname =~ /\s/
|
155
|
+
$stderr.puts "The package name mustn't contain whitespace!"
|
156
|
+
else
|
157
|
+
break
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
#Set the last_update spec field to now
|
162
|
+
spec[:last_update] = Time.now.utc
|
163
|
+
|
164
|
+
puts
|
165
|
+
#This is all. Start building the package.
|
166
|
+
Dir.mktmpdir("smc-get-build-package") do |tmpdir|
|
167
|
+
puts "Creating package..."
|
168
|
+
tmpdir = Pathname.new(tmpdir)
|
169
|
+
pkgdir = tmpdir + pkgname
|
170
|
+
pkgdir.mkdir
|
171
|
+
#Copy all the level, music, etc. files
|
172
|
+
[:levels, :music, :sounds, :worlds].each do |sym|
|
173
|
+
subdir = pkgdir + sym.to_s
|
174
|
+
subdir.mkdir
|
175
|
+
spec[sym].each{|file| FileUtils.cp(file, subdir)}
|
176
|
+
end
|
177
|
+
#The graphics for whatever reason have an own name...
|
178
|
+
subdir = pkgdir + "pixmaps"
|
179
|
+
subdir.mkdir
|
180
|
+
spec[:graphics].each{|file| FileUtils.cp(file, subdir)}
|
181
|
+
|
182
|
+
#Turn the file paths in the spec to relative ones and the
|
183
|
+
#keys to strings. Compute the checksums.
|
184
|
+
puts "Creating spec and computing SHA1 checksums..."
|
185
|
+
real_spec = {"checksums" => {}}
|
186
|
+
spec.each_pair do |key, value|
|
187
|
+
real_spec[key.to_s] = case key
|
188
|
+
when :levels, :graphics, :music, :sounds
|
189
|
+
real_spec["checksums"][key.to_s] = {}
|
190
|
+
value.map do |abs_path|
|
191
|
+
basename = File.basename(abs_path)
|
192
|
+
real_spec["checksums"][key.to_s][basename] = Digest::SHA1.hexdigest(File.read(abs_path))
|
193
|
+
basename #for map
|
194
|
+
end
|
195
|
+
when :worlds
|
196
|
+
real_spec["checksums"]["worlds"] = {}
|
197
|
+
value.map do |abs_path|
|
198
|
+
basename = File.basename(abs_path)
|
199
|
+
real_spec["checksums"]["worlds"][basename] = {
|
200
|
+
"description.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "description.xml"))),
|
201
|
+
"layer.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "layer.xml"))),
|
202
|
+
"world.xml" => Digest::SHA1.hexdigest(File.read(File.join(abs_path, "world.xml")))
|
203
|
+
}
|
204
|
+
basename #for map
|
205
|
+
end
|
206
|
+
else
|
207
|
+
value
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
#Create the spec
|
212
|
+
File.open(pkgdir + "#{pkgname}.yml", "w"){|file| YAML.dump(real_spec, file)}
|
213
|
+
|
214
|
+
puts "Compressing..."
|
215
|
+
pkg = Package.create(pkgdir)
|
216
|
+
puts "Copying..."
|
217
|
+
FileUtils.cp(pkg.path, "./")
|
218
|
+
#compressed_file_name returns only the name of the package file,
|
219
|
+
#no path. Expanding it therefore results in the current
|
220
|
+
#working directory prepended to it.
|
221
|
+
puts "Done. Your package is at #{File.expand_path(pkg.spec.compressed_file_name)}."
|
222
|
+
end
|
223
|
+
|
224
|
+
end
|
225
|
+
rescue Errors::BrokenPackageError => e
|
226
|
+
$stderr.puts("Failed to build SMC package:")
|
227
|
+
$stderr.puts(e.message)
|
228
|
+
if CUI.debug_mode?
|
229
|
+
$stderr.puts("Class: #{e.class}")
|
230
|
+
$stderr.puts("Message: #{e.message}")
|
231
|
+
$stderr.puts("Backtrace:")
|
232
|
+
$stderr.puts(e.backtrace.join("\n\t"))
|
233
|
+
end
|
234
|
+
return 1 #Exit code
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
private
|
239
|
+
|
240
|
+
#Queries the user for a set of file names in an uniform mannor.
|
241
|
+
#Pass in the pluralized symbol of the resource you want to query
|
242
|
+
#for, e.g. :levels or :graphics. Return value is an array of all
|
243
|
+
#found files which may be empty if no files were found.
|
244
|
+
def input_files(plural_name)
|
245
|
+
result = []
|
246
|
+
puts
|
247
|
+
puts "Enter the names of the #{plural_name} you want to include:"
|
248
|
+
loop do
|
249
|
+
print "> "
|
250
|
+
str = $stdin.gets.chomp
|
251
|
+
|
252
|
+
#Test if the user entered an empty line, i.e. wants to end the
|
253
|
+
#query for this question
|
254
|
+
if str.empty?
|
255
|
+
if result.empty?
|
256
|
+
print("No #{plural_name} specified. Is this correct?(y/n) ")
|
257
|
+
break if $stdin.gets.chomp.downcase == "y"
|
258
|
+
else
|
259
|
+
break
|
260
|
+
end
|
261
|
+
else #User entered something
|
262
|
+
str.split(",").each do |file|
|
263
|
+
file.strip! #Due to possible whitespace behind the comma
|
264
|
+
ary = get_file_paths(plural_name.to_s, file)
|
265
|
+
$stderr.puts("Warning: File(s) not found: #{file}. Ignoring.") if ary.empty?
|
266
|
+
result.concat(ary)
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
result
|
271
|
+
end
|
272
|
+
|
273
|
+
#Queries the user for a longer, but optional text that is returned. If
|
274
|
+
#the user enters no text beside the END marker, returns nil. Pass in
|
275
|
+
#what to tell the user is to be entered.
|
276
|
+
def input_desc(sym)
|
277
|
+
puts
|
278
|
+
puts("Enter the package's #{sym}. A single line containing containg")
|
279
|
+
puts("END")
|
280
|
+
puts("terminates the query. Enter END immediately if you don't want")
|
281
|
+
puts "a #{sym}."
|
282
|
+
result = ""
|
283
|
+
loop do
|
284
|
+
print "> "
|
285
|
+
str = $stdin.gets #No chomp here, the user may set spaces at the line end intentionally
|
286
|
+
if str == "END\n"
|
287
|
+
break
|
288
|
+
else
|
289
|
+
result << str
|
290
|
+
end
|
291
|
+
end
|
292
|
+
result.empty? ? nil : result
|
293
|
+
end
|
294
|
+
|
295
|
+
def get_file_paths(plural_name, path)
|
296
|
+
#Even on Windows we work with forward slash (Windows supports this,
|
297
|
+
#although it’s not well known)
|
298
|
+
path = path.gsub("\\", "/")
|
299
|
+
ary = []
|
300
|
+
#First check if it’s an absolute path
|
301
|
+
if RUBY_PLATFORM =~ /mswin|mingw|cygwin/ and path =~ /^[a-z]:/i
|
302
|
+
ary.replace(Dir.glob(path)) #Works even without an actual escape char like *
|
303
|
+
elsif path.start_with?("/")
|
304
|
+
ary.replace(Dir.glob(path))
|
305
|
+
else #OK, relative path
|
306
|
+
plural_name = "pixmaps" if plural_name == "graphics" #As always...
|
307
|
+
|
308
|
+
#The user level directory only contains levels, but for the
|
309
|
+
#sake of simplicity I treat it as if sounds etc existed there
|
310
|
+
#as well. It doesn’t hurt if not, because that just causes
|
311
|
+
#an empty array.
|
312
|
+
user_level_dir = CUI::USER_SMC_DIR + plural_name
|
313
|
+
smc_install_dir = @cui.local_repository.path + plural_name
|
314
|
+
global_files = Dir.glob(smc_install_dir.join(path).to_s)
|
315
|
+
user_files = Dir.glob(user_level_dir.join(path).to_s)
|
316
|
+
#In case a file with the same name exists in both paths,
|
317
|
+
#the user-level file overrides the SMC installation ones’s.
|
318
|
+
ary.replace(user_files)
|
319
|
+
global_files.each{|path| ary << path unless ary.any?{|p2| File.basename(path) == File.basename(p2)}}
|
320
|
+
end
|
321
|
+
ary
|
322
|
+
end
|
323
|
+
|
324
|
+
end
|
325
|
+
|
326
|
+
end
|
327
|
+
|
328
|
+
end
|
329
|
+
# vim:set ts=8 sts=2 sw=2 et: #
|
@@ -1,81 +1,117 @@
|
|
1
|
-
#Encoding: UTF-8
|
2
|
-
################################################################################
|
3
|
-
# This file is part of smc-get.
|
4
|
-
# Copyright (C) 2010-2011 Entertaining Software, Inc.
|
5
|
-
# Copyright (C) 2011 Marvin Gülker
|
6
|
-
#
|
7
|
-
# This program is free software: you can redistribute it and/or modify
|
8
|
-
# it under the terms of the GNU General Public License as published by
|
9
|
-
# the Free Software Foundation, either version 3 of the License, or
|
10
|
-
# (at your option) any later version.
|
11
|
-
#
|
12
|
-
# This program is distributed in the hope that it will be useful,
|
13
|
-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
-
# GNU General Public License for more details.
|
16
|
-
#
|
17
|
-
# You should have received a copy of the GNU General Public License
|
18
|
-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
-
################################################################################
|
20
|
-
|
21
|
-
module SmcGet
|
22
|
-
|
23
|
-
module CUICommands
|
24
|
-
|
25
|
-
#Class for invalid command-line argument errors.
|
26
|
-
class InvalidCommandline < Errors::SmcGetError
|
27
|
-
end
|
28
|
-
|
29
|
-
#This is the superclass of all CUI commands. To make your own command,
|
30
|
-
#subclass it an overwrite ::help, ::summary, #parse and #execute.
|
31
|
-
class Command
|
32
|
-
|
33
|
-
#The string returned from this method will be displayed to the
|
34
|
-
#user if he issues <tt>smc-get help YOURCOMMAND</tt>.
|
35
|
-
def self.help
|
36
|
-
"(nothing known)"
|
37
|
-
end
|
38
|
-
|
39
|
-
#One-line summary of the command that shows up in the COMMANDS
|
40
|
-
#section of <tt>smc-get help</tt>. Should not be longer than 78
|
41
|
-
#characters due to automatic indentation. You may have to insert
|
42
|
-
#tabs to make it
|
43
|
-
#issueing <tt>smc-get help</tt>.
|
44
|
-
def self.summary
|
45
|
-
""
|
46
|
-
end
|
47
|
-
|
48
|
-
#Creates a new instance of this command. Do not override this, or
|
49
|
-
#call at least +super+.
|
50
|
-
def initialize(args)
|
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
|
-
|
1
|
+
#Encoding: UTF-8
|
2
|
+
################################################################################
|
3
|
+
# This file is part of smc-get.
|
4
|
+
# Copyright (C) 2010-2011 Entertaining Software, Inc.
|
5
|
+
# Copyright (C) 2011 Marvin Gülker
|
6
|
+
#
|
7
|
+
# This program is free software: you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation, either version 3 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
19
|
+
################################################################################
|
20
|
+
|
21
|
+
module SmcGet
|
22
|
+
|
23
|
+
module CUICommands
|
24
|
+
|
25
|
+
#Class for invalid command-line argument errors.
|
26
|
+
class InvalidCommandline < Errors::SmcGetError
|
27
|
+
end
|
28
|
+
|
29
|
+
#This is the superclass of all CUI commands. To make your own command,
|
30
|
+
#subclass it an overwrite ::help, ::summary, #parse and #execute.
|
31
|
+
class Command
|
32
|
+
|
33
|
+
#The string returned from this method will be displayed to the
|
34
|
+
#user if he issues <tt>smc-get help YOURCOMMAND</tt>.
|
35
|
+
def self.help
|
36
|
+
"(nothing known)"
|
37
|
+
end
|
38
|
+
|
39
|
+
#One-line summary of the command that shows up in the COMMANDS
|
40
|
+
#section of <tt>smc-get help</tt>. Should not be longer than 78
|
41
|
+
#characters due to automatic indentation. You may have to insert
|
42
|
+
#tabs to make it display correctly; make sure to check the result by
|
43
|
+
#issueing <tt>smc-get help</tt>.
|
44
|
+
def self.summary
|
45
|
+
""
|
46
|
+
end
|
47
|
+
|
48
|
+
#Creates a new instance of this command. Do not override this, or
|
49
|
+
#call at least +super+.
|
50
|
+
def initialize(cui, args)
|
51
|
+
@cui = cui
|
52
|
+
parse(args)
|
53
|
+
end
|
54
|
+
|
55
|
+
#This method gets all commandline options relevant for this subcommand
|
56
|
+
#passed in the +args+ array. You should parse them destructively, i.e.
|
57
|
+
#after you finished parsing, +args+ should be an empty array.
|
58
|
+
#Set instance variables to save data.
|
59
|
+
#
|
60
|
+
#Note that SmcGet is not set up when this method is called, so you
|
61
|
+
#cannot do things like <tt>Package.new</tt>.
|
62
|
+
def parse(args)
|
63
|
+
raise(NotImplementedError, "#{__method__} has to be overriden in a subclass!")
|
64
|
+
end
|
65
|
+
|
66
|
+
#Execute the command. You can use the instance variables set in #parse.
|
67
|
+
#The method gets passed the parsed contents of smc-get's configuration
|
68
|
+
#files and commandline parameters; you can use this to make your command
|
69
|
+
#configurable via the configuration file, but make sure that
|
70
|
+
#1. The keys you use for your configuration don't already exist,
|
71
|
+
#2. options specified on the commandline take precedence over values
|
72
|
+
# set in the configuration file and
|
73
|
+
#3. you <b>do not alter</b> the hash.
|
74
|
+
def execute(config)
|
75
|
+
raise(NotImplementedError, "#{__method__} has to be overriden in a subclass!")
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
#Downloads the package identified by +pkgname+ and displays the progress to the
|
81
|
+
#user. Example:
|
82
|
+
# download_package("cool_world")
|
83
|
+
#downloads the package "cool_world.smcpak". You mustn’t specify the file
|
84
|
+
#extension <tt>.smcpak</tt>.
|
85
|
+
def download_package(pkgname)
|
86
|
+
pkg_file = "#{pkgname}.smcpak"
|
87
|
+
#Windows doesn't understand ANSI escape sequences, so we have to
|
88
|
+
#use the carriage return and reprint the whole line.
|
89
|
+
base_str = "\rDownloading %s... (%.2f%%)"
|
90
|
+
tries = 0
|
91
|
+
begin
|
92
|
+
tries += 1
|
93
|
+
path_to_package = @cui.remote_repository.fetch_package(pkg_file, SmcGet.temp_dir) do |bytes_total, bytes_done|
|
94
|
+
percent = ((bytes_done.to_f / bytes_total) * 100)
|
95
|
+
print "\r", " " * 80 #Clear everything written before
|
96
|
+
printf(base_str, pkg_file, percent)
|
97
|
+
end
|
98
|
+
rescue OpenURI::HTTPError => e #Thrown even in case of FTP and HTTPS
|
99
|
+
if tries >= 3
|
100
|
+
raise #Bubble up
|
101
|
+
else
|
102
|
+
$stderr.puts("ERROR: #{e.message}")
|
103
|
+
$stderr.puts("Retrying.")
|
104
|
+
retry
|
105
|
+
end
|
106
|
+
end
|
107
|
+
puts #Terminating \n
|
108
|
+
return path_to_package
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
# vim:set ts=8 sts=2 sw=2 et: #
|