smc-get 0.1.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/COPYING +674 -0
- data/README.rdoc +148 -0
- data/VERSION.txt +1 -0
- data/bin/smc-get +31 -0
- data/config/smc-get.yml +32 -0
- data/lib/smc_get.rb +21 -0
- data/lib/smc_get/cui.rb +289 -0
- data/lib/smc_get/cui_commands/command.rb +81 -0
- data/lib/smc_get/cui_commands/getinfo.rb +91 -0
- data/lib/smc_get/cui_commands/help.rb +64 -0
- data/lib/smc_get/cui_commands/install.rb +91 -0
- data/lib/smc_get/cui_commands/list.rb +80 -0
- data/lib/smc_get/cui_commands/search.rb +109 -0
- data/lib/smc_get/cui_commands/uninstall.rb +78 -0
- data/lib/smc_get/cui_commands/version.rb +57 -0
- data/lib/smc_get/errors.rb +113 -0
- data/lib/smc_get/gui.rb +19 -0
- data/lib/smc_get/package.rb +279 -0
- data/lib/smc_get/smc_get.rb +139 -0
- data/test/test_smc-get-cui.rb +121 -0
- data/test/test_smc-get-lib.rb +170 -0
- metadata +87 -0
@@ -0,0 +1,78 @@
|
|
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 UninstallCommand < Command
|
26
|
+
|
27
|
+
def self.help
|
28
|
+
<<EOF
|
29
|
+
#{File.basename($0)} uninstall PACKAGES
|
30
|
+
|
31
|
+
Removes PACKAGES from your set of downloaded packages.
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.summary
|
36
|
+
"uninstall\tUninstalls one or more packages."
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse(args)
|
40
|
+
CUI.debug("Parsing #{args.count} args for uninstall.")
|
41
|
+
raise(InvalidCommandline, "No package given.") if args.empty?
|
42
|
+
@pkg_names = []
|
43
|
+
until args.empty?
|
44
|
+
arg = args.shift
|
45
|
+
#case arg
|
46
|
+
#when "-c", "--my-arg" then ...
|
47
|
+
#else
|
48
|
+
@pkg_names << arg
|
49
|
+
$stderr.puts("Unkown argument #{arg}. Treating it as a package.") if arg.start_with?("-")
|
50
|
+
#end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def execute(config)
|
55
|
+
CUI.debug("Executing uninstall.")
|
56
|
+
@pkg_names.each do |pkg_name|
|
57
|
+
pkg = Package.new(pkg_name)
|
58
|
+
puts "Uninstalling #{pkg}."
|
59
|
+
unless pkg.installed?
|
60
|
+
$stderr.puts "#{pkg} is not installed. Skipping."
|
61
|
+
next
|
62
|
+
end
|
63
|
+
#Windows doesn't understand ANSI escape sequences, so we have to
|
64
|
+
#use the carriage return and reprint the whole line.
|
65
|
+
base_str = "\rRemoving %s... (%.2f%%)"
|
66
|
+
pkg.uninstall do |part, percent_part|
|
67
|
+
print "\r", " " * 80 #Clear everything written before
|
68
|
+
printf(base_str, part, percent_part)
|
69
|
+
end
|
70
|
+
puts
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,57 @@
|
|
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 VersionCommand < Command
|
26
|
+
|
27
|
+
def self.help
|
28
|
+
<<EOF
|
29
|
+
USAGE: #{File.basename($0)} version
|
30
|
+
|
31
|
+
Shows the version of #{File.basename($0)} and the copyright statement.
|
32
|
+
EOF
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.summary
|
36
|
+
"version\tShows smc-get's version and copyright."
|
37
|
+
end
|
38
|
+
|
39
|
+
def parse(args)
|
40
|
+
raise(InvalidCommandline, "Too many arguments.") unless args.empty?
|
41
|
+
end
|
42
|
+
|
43
|
+
def execute(config)
|
44
|
+
puts "This is #{File.basename($0)}, version #{VERSION}."
|
45
|
+
puts
|
46
|
+
puts "#{File.basename($0)} Copyright (C) 2010-2011 Luiji Maryo"
|
47
|
+
puts "#{File.basename($0)} Copyright (C) 2011 Marvin Gülker"
|
48
|
+
puts "This program comes with ABSOLUTELY NO WARRANTY."
|
49
|
+
puts "This is free software, and you are welcome to redistribute it"
|
50
|
+
puts "under certain conditions; see the COPYING file for information."
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,113 @@
|
|
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
|
+
#This module contains all errors messages that are specific to smc-get.
|
24
|
+
module Errors
|
25
|
+
|
26
|
+
#Superclass for all errors in this library.
|
27
|
+
class SmcGetError < StandardError
|
28
|
+
end
|
29
|
+
|
30
|
+
#Raises when you did not call SmcGet.setup.
|
31
|
+
class LibraryNotInitialized < SmcGetError
|
32
|
+
|
33
|
+
#Throws an exception of this class with an appropriate error
|
34
|
+
#message if smc-get has not been initialized correctly.
|
35
|
+
def self.throw_if_needed!
|
36
|
+
if SmcGet.datadir.nil? or SmcGet.repo_url.nil?
|
37
|
+
raise(self, "You have to setup smc-get first!")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
# Raised when the class is initialized with a non-existant settings file.
|
44
|
+
class CannotFindSettings < SmcGetError
|
45
|
+
# The path to the settings file that was specified.
|
46
|
+
attr_reader :settings_path
|
47
|
+
|
48
|
+
# Create a new instance of the exception with the settings path.
|
49
|
+
def initialize(settings_path)
|
50
|
+
@settings_path = settings_path
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Raised when a package call is made but the specified package cannot be
|
55
|
+
# found.
|
56
|
+
class NoSuchPackageError < SmcGetError
|
57
|
+
# The name of the package that could not be found.
|
58
|
+
attr_reader :package_name
|
59
|
+
|
60
|
+
# Create a new instance of the exception with the specified package name.
|
61
|
+
def initialize(name)
|
62
|
+
@package_name = name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Raised when a package call is made but one of the resources of the
|
67
|
+
# specified package is missing.
|
68
|
+
class NoSuchResourceError < SmcGetError
|
69
|
+
# The type of resource (should be either :music, :graphic, or :level).
|
70
|
+
attr_reader :resource_type
|
71
|
+
# The name of the resource (i.e. mylevel.lvl or Stuff/Cheeseburger.png).
|
72
|
+
attr_reader :resource_name
|
73
|
+
|
74
|
+
# Create a new instance of the exception with the specified resource type
|
75
|
+
# and name. Type should either be :music, :graphic, or :level.
|
76
|
+
def initialize(type, name)
|
77
|
+
@resource_type = type
|
78
|
+
@resource_name = name
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns true if the resource type is :music. False otherwise.
|
82
|
+
def is_music?
|
83
|
+
@resource_type == :music
|
84
|
+
end
|
85
|
+
|
86
|
+
# Returns true if the resource type is :graphic. False otherwise.
|
87
|
+
def is_graphic?
|
88
|
+
@resource_type == :graphic
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns true if the resource type is :level. False otherwise.
|
92
|
+
def is_level?
|
93
|
+
@resource_type == :level
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Raised when a call to download() fails.
|
98
|
+
class DownloadFailedError < SmcGetError
|
99
|
+
# The URL that failed to download (including everything after /raw/master
|
100
|
+
# only).
|
101
|
+
attr_reader :download_url
|
102
|
+
|
103
|
+
def initialize(url)
|
104
|
+
@download_url = url
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
#Raised when SmcGet.download timed out.
|
109
|
+
class ConnectionTimedOutError < DownloadFailedError
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
end
|
data/lib/smc_get/gui.rb
ADDED
@@ -0,0 +1,19 @@
|
|
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
|
+
################################################################################
|
@@ -0,0 +1,279 @@
|
|
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
|
+
#An object of this class represents a package. Wheather it is installed or
|
24
|
+
#not, doesn't matter (call #installed? to find out), but everything you
|
25
|
+
#want to manage your packages can be found here. For example, to install
|
26
|
+
#a remote package, do:
|
27
|
+
# pkg = SmcGet::Package.new("mypackage")
|
28
|
+
# pkg.install
|
29
|
+
#Don't forget to set up smc-get before you use the library:
|
30
|
+
# require "smc_get/"
|
31
|
+
# SmcGet.setup(
|
32
|
+
# "https://github.com/Luiji/Secret-Maryo-Chronicles-Contributed-Levels/raw/master/",
|
33
|
+
# "dir/where/you/hava/smc/installed"
|
34
|
+
# end
|
35
|
+
class Package
|
36
|
+
|
37
|
+
#The package specification file for this packages. This file may not
|
38
|
+
#exist if the package is not installed. This is a Pathname object.
|
39
|
+
attr_reader :spec_file
|
40
|
+
#The name of this package.
|
41
|
+
attr_reader :name
|
42
|
+
|
43
|
+
class << self
|
44
|
+
|
45
|
+
#Searches through the package repostitory and returns an array
|
46
|
+
#of matching package objects.
|
47
|
+
#
|
48
|
+
#Pass in the regular expression to search for (or a string, which
|
49
|
+
#then is treated as a regular expression without anchors), the
|
50
|
+
#keys of the specification to search through as an array of symbols,
|
51
|
+
#and wheather you want to query only locally installed packages (by
|
52
|
+
#default, only remote packages are searched).+query_fields+ indicates
|
53
|
+
#which fields of the package specification shall be searched. You can
|
54
|
+
#pass them as an array of symbols. +only_local+ causes smc-get to
|
55
|
+
#do a local search instead of a remote one.
|
56
|
+
#
|
57
|
+
#With solely :pkgname specified, just the specifications for the packages
|
58
|
+
#whose package file names match +regexp+ are downloaded, causing a
|
59
|
+
#massive speedup.
|
60
|
+
def search(regexp, query_fields = [:pkgname], only_local = false)
|
61
|
+
regexp = Regexp.new(Regexp.escape(regexp)) if regexp.kind_of? String
|
62
|
+
ary = []
|
63
|
+
|
64
|
+
list = if only_local
|
65
|
+
Errors::LibraryNotInitialized.throw_if_needed!
|
66
|
+
installed_packages.map(&:name)
|
67
|
+
else
|
68
|
+
Tempfile.open("smc-get") do |listfile|
|
69
|
+
SmcGet.download(PACKAGE_LIST_FILE, listfile.path)
|
70
|
+
listfile.readlines.map(&:chomp)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
list.each do |pkg_name|
|
75
|
+
pkg = Package.new(pkg_name)
|
76
|
+
#If the user wants to query just the pkgname, we can save
|
77
|
+
#much time by not downloading all the package specs.
|
78
|
+
if query_fields == [:pkgname]
|
79
|
+
ary << pkg if pkg_name =~ regexp
|
80
|
+
else
|
81
|
+
spec = only_local ? pkg.spec : pkg.getinfo
|
82
|
+
query_fields.each do |field|
|
83
|
+
if field == :pkgname #This field is not _inside_ the spec.
|
84
|
+
ary << pkg if pkg_name =~ regexp
|
85
|
+
else
|
86
|
+
#First to_s: Convert Symbol to string used in the specs.
|
87
|
+
#Second to_s: Ensure array values such as "author" are
|
88
|
+
# handled correctly.
|
89
|
+
ary << pkg if spec[field.to_s].to_s =~ regexp
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
ary
|
95
|
+
end
|
96
|
+
|
97
|
+
#Returns a list of all currently installed packages as an array of
|
98
|
+
#Package objects.
|
99
|
+
def installed_packages
|
100
|
+
Errors::LibraryNotInitialized.throw_if_needed!
|
101
|
+
specs_dir = SmcGet.datadir + PACKAGE_SPECS_DIR
|
102
|
+
specs_dir.mkpath unless specs_dir.directory?
|
103
|
+
|
104
|
+
#We need to chdir here, because Dir.glob returns the path
|
105
|
+
#relative to the current working directory and it should be
|
106
|
+
#a bit more performant if I don't have to strip off the relative
|
107
|
+
#prefix of the filenames (which are the names of the packages + .yml).
|
108
|
+
Dir.chdir(specs_dir.to_s) do
|
109
|
+
Dir.glob("**/*.yml").map{|filename| new(filename.match(/\.yml$/).pre_match)}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
end
|
114
|
+
|
115
|
+
#Creates a new package object from it's name. This doesn't do anything,
|
116
|
+
#especially it doesn't install the package. It just creates an object for
|
117
|
+
#you you can use to inspect or install pacakges. It doesn't even check if
|
118
|
+
#the package name is valid.
|
119
|
+
def initialize(package_name)
|
120
|
+
Errors::LibraryNotInitialized.throw_if_needed!
|
121
|
+
@name = package_name
|
122
|
+
@spec_file = SmcGet.datadir.join(PACKAGE_SPECS_DIR, "#{@name}.yml")
|
123
|
+
end
|
124
|
+
|
125
|
+
# Install a package from the repository. Yields the name of the file
|
126
|
+
# currently being downloaded, how many percent of that
|
127
|
+
# file have already been downloaded and wheather or not this is a retry (if
|
128
|
+
# so, the exception object is yielded, otherwise false).
|
129
|
+
# The maximum number of retries is specified via the +max_tries+ parameter.
|
130
|
+
def install(max_tries = 3) # :yields: file, percent_file, retrying
|
131
|
+
try = 1 #For avoiding retrying infinitely
|
132
|
+
begin
|
133
|
+
SmcGet.download("packages/#{@name}.yml", SmcGet.datadir + PACKAGE_SPECS_DIR + "#{@name}.yml") do |file, percent_done|
|
134
|
+
yield(file, percent_done, false) if block_given?
|
135
|
+
end
|
136
|
+
rescue Errors::DownloadFailedError => e
|
137
|
+
if try > max_tries
|
138
|
+
File.delete(SmcGet.datadir + PACKAGE_SPECS_DIR + "#{@name}.yml") #There is an empty file otherwise
|
139
|
+
if e.class == Errors::ConnectionTimedOutError
|
140
|
+
raise #Bubble up
|
141
|
+
else
|
142
|
+
raise(Errors::NoSuchPackageError.new(@name), "ERROR: Package not found in the repository: #{@name}.")
|
143
|
+
end
|
144
|
+
else
|
145
|
+
try += 1
|
146
|
+
yield(e.download_url, 0, e) if block_given?
|
147
|
+
retry
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
pkgdata = YAML.load_file(SmcGet.datadir + PACKAGE_SPECS_DIR + "#{@name}.yml")
|
152
|
+
package_parts = [["levels", :level], ["music", :music], ["graphics", :graphic]]
|
153
|
+
|
154
|
+
package_parts.each_with_index do |ary, i|
|
155
|
+
part, sym = ary
|
156
|
+
try = 1
|
157
|
+
|
158
|
+
if pkgdata.has_key?(part)
|
159
|
+
pkgdata[part].each do |filename|
|
160
|
+
begin
|
161
|
+
SmcGet.download("#{part}/#{filename}", SmcGet.datadir + SmcGet.const_get(:"PACKAGE_#{part.upcase}_DIR") + filename) do |file, percent_done|
|
162
|
+
yield(file, percent_done, false) if block_given?
|
163
|
+
end
|
164
|
+
rescue Errors::DownloadFailedError => e
|
165
|
+
if try > max_tries
|
166
|
+
if e.class == Errors::ConnectionTimedOutError
|
167
|
+
raise #Bubble up
|
168
|
+
else
|
169
|
+
raise(Errors::NoSuchResourceError.new(sym, error.download_url), "ERROR: #{sym.capitalize} not found in the repository: #{filename}.")
|
170
|
+
end
|
171
|
+
else
|
172
|
+
try += 1
|
173
|
+
yield(e.download_url, 0, e) if block_given?
|
174
|
+
retry
|
175
|
+
end #if try > max_tries
|
176
|
+
end #begin
|
177
|
+
end #each part
|
178
|
+
end #has_key?
|
179
|
+
end #each part and sym
|
180
|
+
end #install
|
181
|
+
|
182
|
+
# Uninstall a package from the local database. If a block is given,
|
183
|
+
# it is yielded the package part currently being deleted and
|
184
|
+
# how many percent of the files have already been deleted for the current package
|
185
|
+
# part.
|
186
|
+
def uninstall
|
187
|
+
package_file = SmcGet.datadir + PACKAGE_SPECS_DIR + "#{@name}.yml"
|
188
|
+
begin
|
189
|
+
pkgdata = YAML.load_file(package_file)
|
190
|
+
rescue Errno::ENOENT
|
191
|
+
raise(Errors::NoSuchPackageError.new(@name), "ERROR: Local package not found: #{@name}.")
|
192
|
+
end
|
193
|
+
|
194
|
+
%w[music graphics levels].each_with_index do |part, part_index|
|
195
|
+
if pkgdata.has_key? part
|
196
|
+
total_files = pkgdata[part].count
|
197
|
+
pkgdata[part].each_with_index do |filename, index|
|
198
|
+
begin
|
199
|
+
File.delete(SmcGet.datadir + SmcGet.const_get("PACKAGE_#{part.upcase}_DIR") + filename)
|
200
|
+
rescue Errno::ENOENT
|
201
|
+
end
|
202
|
+
yield(part, ((index + 1) / total_files) * 100) if block_given? #+1, because index is 0-based
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
#Delete the package file
|
208
|
+
File.delete(package_file)
|
209
|
+
|
210
|
+
#Delete the directories the package file was placed in IF THEY'RE EMPTY.
|
211
|
+
rel_dir, file = package_file.relative_path_from(SmcGet.datadir + PACKAGE_SPECS_DIR).split
|
212
|
+
#rel_dir now holds the path difference between the package directory
|
213
|
+
#and the package spec file. If it is ".", no further dirs have been
|
214
|
+
#introduced.
|
215
|
+
return if rel_dir == Pathname.new(".")
|
216
|
+
#For simplifying the deletion procedure, we change the working directory
|
217
|
+
#to the package spec dir. Otherwise we'd have to keep track of both
|
218
|
+
#the absolute and relative paths, this way just of the latter.
|
219
|
+
Dir.chdir(SmcGet.datadir.join(PACKAGE_SPECS_DIR).to_s) do
|
220
|
+
#Remove from the inmost to the outmost directory, so that
|
221
|
+
#empty directories contained in directories just containg that
|
222
|
+
#empty directory don't get prohibited from deletion.
|
223
|
+
rel_dir.ascend do |dir|
|
224
|
+
dir.rmdir if dir.children.empty?
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
#Returns true if the package is installed locally. Returns false
|
231
|
+
#otherwise.
|
232
|
+
def installed?
|
233
|
+
SmcGet.datadir.join(PACKAGE_SPECS_DIR, "#{@name}.yml").file?
|
234
|
+
end
|
235
|
+
|
236
|
+
#Get package information on a remote package. This method never
|
237
|
+
#retrieves any information from a locally installed package, look
|
238
|
+
#at #spec for that. Return value is the package specification in form
|
239
|
+
#of a hash.
|
240
|
+
#
|
241
|
+
#WARNING: This function is not thread-safe.
|
242
|
+
def getinfo
|
243
|
+
yaml = nil
|
244
|
+
Tempfile.open('pkgdata') do |tmp|
|
245
|
+
begin
|
246
|
+
SmcGet.download("packages/#{@name}.yml", tmp.path)
|
247
|
+
rescue Errors::DownloadFailedError
|
248
|
+
raise(Errors::NoSuchPackageError.new(@name), "ERROR: Package not found in the repository: #{@name}")
|
249
|
+
end
|
250
|
+
yaml = YAML.load_file(tmp.path)
|
251
|
+
end
|
252
|
+
return yaml
|
253
|
+
end
|
254
|
+
|
255
|
+
#Retrieves the package specification from a locally installed package
|
256
|
+
#in form of a hash. In order to fetch information from a remote package,
|
257
|
+
#you have to use the #getinfo method.
|
258
|
+
def spec
|
259
|
+
if installed?
|
260
|
+
YAML.load_file(@spec_file)
|
261
|
+
else
|
262
|
+
raise(Errors::NoSuchPackageError, "ERROR: Package not installed locally: #@name.")
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
#Returns the name of the package.
|
267
|
+
def to_s
|
268
|
+
@name
|
269
|
+
end
|
270
|
+
|
271
|
+
#Human-readable description of form
|
272
|
+
# #<SmcGet::Package package_name (installation_status)>
|
273
|
+
def inspect
|
274
|
+
"#<#{self.class} #{@name} (#{installed? ? 'installed' : 'not installed'})>"
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|