smc-get 0.2.0.beta1 → 0.3.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 +53 -31
- data/README.rdoc +162 -162
- data/VERSION.txt +1 -1
- data/bin/smc-checksum +44 -44
- data/bin/smc-checksum~ +2 -0
- data/bin/smc-get +31 -31
- data/bin/smc-repo-conv +205 -0
- data/bin/smc-repo-conv~ +155 -0
- data/config/smc-get.yml +32 -32
- data/config/smc-get.yml~ +32 -0
- data/lib/smc_get.rb +22 -22
- data/lib/smc_get/cui.rb +325 -325
- data/lib/smc_get/cui_commands/build.rb +297 -329
- data/lib/smc_get/cui_commands/command.rb +181 -117
- data/lib/smc_get/cui_commands/getinfo.rb +91 -91
- data/lib/smc_get/cui_commands/help.rb +64 -64
- data/lib/smc_get/cui_commands/install.rb +101 -101
- data/lib/smc_get/cui_commands/list.rb +101 -101
- data/lib/smc_get/cui_commands/search.rb +106 -106
- data/lib/smc_get/cui_commands/server.rb +76 -0
- data/lib/smc_get/cui_commands/uninstall.rb +77 -80
- data/lib/smc_get/cui_commands/update.rb +107 -119
- data/lib/smc_get/cui_commands/version.rb +57 -57
- data/lib/smc_get/errors.rb +166 -140
- data/lib/smc_get/gui.rb +19 -19
- data/lib/smc_get/local_repository.rb +290 -277
- data/lib/smc_get/package.rb +240 -260
- data/lib/smc_get/package_archive.rb +186 -80
- data/lib/smc_get/package_specification.rb +257 -253
- data/lib/smc_get/remote_repository.rb +272 -272
- data/lib/smc_get/repository.rb +9 -9
- data/lib/smc_get/smc_get.rb +118 -117
- data/smcpak.rdoc +331 -331
- data/test/test_smc-get-cui.rb +122 -122
- data/test/test_smc-get-lib.rb +171 -171
- metadata +25 -22
@@ -1,277 +1,290 @@
|
|
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
|
-
#A LocalRepository contains all the packages that are installed locally, i.e.
|
24
|
-
#that have been downloaded and added to your local SMC installation.
|
25
|
-
#It provides the exact same methods as RemoteRepository, but works completely
|
26
|
-
#local and therefore doesn’t need an internet connection as RemoteRepository does.
|
27
|
-
#
|
28
|
-
#The only notable difference is that instances of this class have some
|
29
|
-
#attributes different from those defined for RemoteRepository, but usually
|
30
|
-
#you shouldn’t have to worry about that.
|
31
|
-
#
|
32
|
-
#Concluding, you’ll find the documentation of most of the methods of this class
|
33
|
-
#in the documentation of the RemoteRepository class, because duplicating docs
|
34
|
-
#doesn’t make much sense.
|
35
|
-
class LocalRepository < Repository
|
36
|
-
|
37
|
-
#Directory where the package specs are kept.
|
38
|
-
SPECS_DIR = Pathname.new("packages")
|
39
|
-
#Directory where downloaded packages are cached.
|
40
|
-
CACHE_DIR = Pathname.new("cache")
|
41
|
-
#Directory where the packages’ level files are kept.
|
42
|
-
CONTRIB_LEVELS_DIR = Pathname.new("levels") #Levels in subdirectories are currently not recognized by SMC
|
43
|
-
#Directory where the packages’ music files are kept.
|
44
|
-
CONTRIB_MUSIC_DIR = Pathname.new("music") + "contrib-music"
|
45
|
-
#Directory where the packages’ graphic files are kept.
|
46
|
-
CONTRIB_GRAPHICS_DIR = Pathname.new("pixmaps") + "contrib-graphics"
|
47
|
-
#Directory where the packages’ sound files are kept.
|
48
|
-
CONTRIB_SOUNDS_DIR = Pathname.new("sounds") + "contrib-sounds"
|
49
|
-
#Directory where the packages’ world files are kept
|
50
|
-
CONTRIB_WORLDS_DIR = Pathname.new("world") #Worlds in subdirectores are currently not recognized by SMC
|
51
|
-
|
52
|
-
#Root path of the local repository. Should be the same as your SMC’s
|
53
|
-
#installation path.
|
54
|
-
attr_reader :path
|
55
|
-
#This repository’s specs dir.
|
56
|
-
attr_reader :specs_dir
|
57
|
-
#This repository’s cache dir.
|
58
|
-
attr_reader :cache_dir
|
59
|
-
#This repository’s package levels dir.
|
60
|
-
attr_reader :contrib_level_dir
|
61
|
-
#This repository’s package music dir.
|
62
|
-
attr_reader :contrib_music_dir
|
63
|
-
#This repository’s package graphics dir.
|
64
|
-
attr_reader :contrib_graphics_dir
|
65
|
-
#This repository’s package sounds dir.
|
66
|
-
attr_reader :contrib_sounds_dir
|
67
|
-
#This repository’s package worlds dir.
|
68
|
-
attr_reader :contrib_worlds_dir
|
69
|
-
#An array of PackageSpecification objects containing the specs of
|
70
|
-
#all packages installed in this repository.
|
71
|
-
attr_reader :package_specs
|
72
|
-
|
73
|
-
#"Creates" a new local repository whose root is located at the given +path+.
|
74
|
-
#When instanciating this class, you should point it to the root of your
|
75
|
-
#SMC installation’s *share* directory, e.g. <b>/usr/share/smc</b>.
|
76
|
-
#==Parameter
|
77
|
-
#[path] The path to your SMC installation.
|
78
|
-
#==Return value
|
79
|
-
#The newly created LocalRepository.
|
80
|
-
#==Example
|
81
|
-
# lr = SmcGet::LocalRepository.new("/usr/share/smc")
|
82
|
-
#==Remarks
|
83
|
-
#smc-get requires some additional directories in your SMC installation,
|
84
|
-
#namely (where +smc+ is your SMC’s *share* directory):
|
85
|
-
# * smc/packages
|
86
|
-
# * smc/music/contrib-music
|
87
|
-
# * smc/sounds/contrib-sounds
|
88
|
-
# * smc/pixmaps/contrib-graphics
|
89
|
-
#These will be created when you call this method, so make sure
|
90
|
-
#you have the appropriate permissions for these directories or
|
91
|
-
#you’ll get an Errno::EACCES exception when calling ::new.
|
92
|
-
def initialize(path)
|
93
|
-
@path = Pathname.new(path)
|
94
|
-
@specs_dir = @path + SPECS_DIR
|
95
|
-
@cache_dir = @path + CACHE_DIR
|
96
|
-
@levels_dir = @path + CONTRIB_LEVELS_DIR
|
97
|
-
@music_dir = @path + CONTRIB_MUSIC_DIR
|
98
|
-
@graphics_dir = @path + CONTRIB_GRAPHICS_DIR
|
99
|
-
@sounds_dir = @path + CONTRIB_SOUNDS_DIR
|
100
|
-
@worlds_dir = @path + CONTRIB_WORLDS_DIR
|
101
|
-
|
102
|
-
#Create the directories if they’re not there yet
|
103
|
-
[@specs_dir, @cache_dir, @levels_dir, @music_dir, @graphics_dir, @sounds_dir, @worlds_dir].each do |dir|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
raise(Errors::
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
directory
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
directory
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
directory
|
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
|
-
#Delete
|
203
|
-
|
204
|
-
|
205
|
-
contrib_dir
|
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
|
-
end
|
250
|
-
|
251
|
-
|
252
|
-
def
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
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
|
+
#A LocalRepository contains all the packages that are installed locally, i.e.
|
24
|
+
#that have been downloaded and added to your local SMC installation.
|
25
|
+
#It provides the exact same methods as RemoteRepository, but works completely
|
26
|
+
#local and therefore doesn’t need an internet connection as RemoteRepository does.
|
27
|
+
#
|
28
|
+
#The only notable difference is that instances of this class have some
|
29
|
+
#attributes different from those defined for RemoteRepository, but usually
|
30
|
+
#you shouldn’t have to worry about that.
|
31
|
+
#
|
32
|
+
#Concluding, you’ll find the documentation of most of the methods of this class
|
33
|
+
#in the documentation of the RemoteRepository class, because duplicating docs
|
34
|
+
#doesn’t make much sense.
|
35
|
+
class LocalRepository < Repository
|
36
|
+
|
37
|
+
#Directory where the package specs are kept.
|
38
|
+
SPECS_DIR = Pathname.new("packages")
|
39
|
+
#Directory where downloaded packages are cached.
|
40
|
+
CACHE_DIR = Pathname.new("cache")
|
41
|
+
#Directory where the packages’ level files are kept.
|
42
|
+
CONTRIB_LEVELS_DIR = Pathname.new("levels") #Levels in subdirectories are currently not recognized by SMC
|
43
|
+
#Directory where the packages’ music files are kept.
|
44
|
+
CONTRIB_MUSIC_DIR = Pathname.new("music") + "contrib-music"
|
45
|
+
#Directory where the packages’ graphic files are kept.
|
46
|
+
CONTRIB_GRAPHICS_DIR = Pathname.new("pixmaps") + "contrib-graphics"
|
47
|
+
#Directory where the packages’ sound files are kept.
|
48
|
+
CONTRIB_SOUNDS_DIR = Pathname.new("sounds") + "contrib-sounds"
|
49
|
+
#Directory where the packages’ world files are kept
|
50
|
+
CONTRIB_WORLDS_DIR = Pathname.new("world") #Worlds in subdirectores are currently not recognized by SMC
|
51
|
+
|
52
|
+
#Root path of the local repository. Should be the same as your SMC’s
|
53
|
+
#installation path.
|
54
|
+
attr_reader :path
|
55
|
+
#This repository’s specs dir.
|
56
|
+
attr_reader :specs_dir
|
57
|
+
#This repository’s cache dir.
|
58
|
+
attr_reader :cache_dir
|
59
|
+
#This repository’s package levels dir.
|
60
|
+
attr_reader :contrib_level_dir
|
61
|
+
#This repository’s package music dir.
|
62
|
+
attr_reader :contrib_music_dir
|
63
|
+
#This repository’s package graphics dir.
|
64
|
+
attr_reader :contrib_graphics_dir
|
65
|
+
#This repository’s package sounds dir.
|
66
|
+
attr_reader :contrib_sounds_dir
|
67
|
+
#This repository’s package worlds dir.
|
68
|
+
attr_reader :contrib_worlds_dir
|
69
|
+
#An array of PackageSpecification objects containing the specs of
|
70
|
+
#all packages installed in this repository.
|
71
|
+
attr_reader :package_specs
|
72
|
+
|
73
|
+
#"Creates" a new local repository whose root is located at the given +path+.
|
74
|
+
#When instanciating this class, you should point it to the root of your
|
75
|
+
#SMC installation’s *share* directory, e.g. <b>/usr/share/smc</b>.
|
76
|
+
#==Parameter
|
77
|
+
#[path] The path to your SMC installation.
|
78
|
+
#==Return value
|
79
|
+
#The newly created LocalRepository.
|
80
|
+
#==Example
|
81
|
+
# lr = SmcGet::LocalRepository.new("/usr/share/smc")
|
82
|
+
#==Remarks
|
83
|
+
#smc-get requires some additional directories in your SMC installation,
|
84
|
+
#namely (where +smc+ is your SMC’s *share* directory):
|
85
|
+
# * smc/packages
|
86
|
+
# * smc/music/contrib-music
|
87
|
+
# * smc/sounds/contrib-sounds
|
88
|
+
# * smc/pixmaps/contrib-graphics
|
89
|
+
#These will be created when you call this method, so make sure
|
90
|
+
#you have the appropriate permissions for these directories or
|
91
|
+
#you’ll get an Errno::EACCES exception when calling ::new.
|
92
|
+
def initialize(path)
|
93
|
+
@path = Pathname.new(path)
|
94
|
+
@specs_dir = @path + SPECS_DIR
|
95
|
+
@cache_dir = @path + CACHE_DIR
|
96
|
+
@levels_dir = @path + CONTRIB_LEVELS_DIR
|
97
|
+
@music_dir = @path + CONTRIB_MUSIC_DIR
|
98
|
+
@graphics_dir = @path + CONTRIB_GRAPHICS_DIR
|
99
|
+
@sounds_dir = @path + CONTRIB_SOUNDS_DIR
|
100
|
+
@worlds_dir = @path + CONTRIB_WORLDS_DIR
|
101
|
+
|
102
|
+
#Create the directories if they’re not there yet
|
103
|
+
[@specs_dir, @cache_dir, @levels_dir, @music_dir, @graphics_dir, @sounds_dir, @worlds_dir].each do |dir|
|
104
|
+
begin
|
105
|
+
dir.mkpath unless dir.directory?
|
106
|
+
rescue Errno::EACCES #This is fatal, don’t raise InvalidRepository
|
107
|
+
$stderr.puts("FATAL: Can't create the directory #{dir}, do you have write permissions there? ")
|
108
|
+
exit 2
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
@package_specs = []
|
113
|
+
@specs_dir.children.each do |spec_path|
|
114
|
+
next unless spec_path.to_s.end_with?(".yml")
|
115
|
+
@package_specs << PackageSpecification.from_file(spec_path)
|
116
|
+
end
|
117
|
+
rescue Errors::InvalidSpecification => e
|
118
|
+
raise(Errors::InvalidRepository.new(@path), "Repository contains an invalid specification: #{e.message}")
|
119
|
+
rescue => e
|
120
|
+
raise(Errors::InvalidRepository.new(@path), e.message)
|
121
|
+
end
|
122
|
+
|
123
|
+
def fetch_spec(spec_file, directory = ".")
|
124
|
+
directory = Pathname.new(directory)
|
125
|
+
|
126
|
+
spec_file_path = @specs_dir + spec_file
|
127
|
+
raise(Errors::NoSuchResourceError.new(:spec, spec_file), "Package specification '#{spec_file}' not found in the local repository '#{to_s}'!") unless spec_file_path.file?
|
128
|
+
|
129
|
+
directory.mktree unless directory.directory?
|
130
|
+
|
131
|
+
#No need to really "fetch" the spec--this is a *local* repository.
|
132
|
+
FileUtils.cp(spec_file_path, directory)
|
133
|
+
directory + spec_file
|
134
|
+
end
|
135
|
+
|
136
|
+
def fetch_package(pkg_file, directory = ".")
|
137
|
+
directory = Pathname.new(directory)
|
138
|
+
|
139
|
+
pkg_file_path = @cache_dir + pkg_file
|
140
|
+
raise(Errors::NoSuchPackageError.new(pkg_file.sub(/\.smcpak/, "")), "Package file '#{pkg_file}' not found in this repository's cache!") unless pkg_file_path.file?
|
141
|
+
|
142
|
+
directory.mktree unless directory.directory?
|
143
|
+
|
144
|
+
#No need to really "fetch" the package--this is a *local* repository
|
145
|
+
FileUtils.cp(pkg_file_path, directory)
|
146
|
+
directory + pkg_file
|
147
|
+
end
|
148
|
+
|
149
|
+
#Installs a package into this local repository in a way that SMC will find
|
150
|
+
#it’s contents.
|
151
|
+
#==Parameter
|
152
|
+
#[package] An instance of class Package. The package to install,
|
153
|
+
#==Example
|
154
|
+
# lr.install(a_package)
|
155
|
+
#==Remarks
|
156
|
+
#Ensure you have write permissions to the repository, otherwise
|
157
|
+
#you’ll get an Errno::EACCES exception from this method.
|
158
|
+
def install(package)
|
159
|
+
path = package.decompress(SmcGet.temp_dir) + package.spec.name
|
160
|
+
|
161
|
+
unless package.spec.valid?
|
162
|
+
raise(Errors::InvalidSpecification, package.spec.validate.first)
|
163
|
+
end
|
164
|
+
|
165
|
+
package.spec.save(@specs_dir)
|
166
|
+
|
167
|
+
FileUtils.cp_r(path.join(Package::LEVELS_DIR).children, @levels_dir)
|
168
|
+
FileUtils.cp_r(path.join(Package::MUSIC_DIR).children, @music_dir)
|
169
|
+
FileUtils.cp_r(path.join(Package::GRAPHICS_DIR).children, @graphics_dir)
|
170
|
+
FileUtils.cp_r(path.join(Package::SOUNDS_DIR).children, @sounds_dir)
|
171
|
+
FileUtils.cp_r(path.join(Package::WORLDS_DIR).children, @worlds_dir)
|
172
|
+
|
173
|
+
FileUtils.cp(package.path, @cache_dir)
|
174
|
+
|
175
|
+
@package_specs << package.spec #This package is now installed and therefore the spec must be in that array
|
176
|
+
end
|
177
|
+
|
178
|
+
#call-seq:
|
179
|
+
# uninstall(pkg_name)
|
180
|
+
# uninstall(pkg_name){|path| ...}
|
181
|
+
#
|
182
|
+
#Uninstalls a package by removing all files it owns. This method checks the
|
183
|
+
#checksums specified in the respective package specifications and if it
|
184
|
+
#detects a user-modified file, the given block is invoked. If the block
|
185
|
+
#evaluates to a truth value, the modified file is copied to a file
|
186
|
+
#in the same directory as the original with ".MODIFIED" just before the
|
187
|
+
#file extension. The blockless form always discards all modified files.
|
188
|
+
#==Parameters
|
189
|
+
#[pkg_name] The name of the package (without the .smcpak extension) to remove.
|
190
|
+
#[full_path] *Blockargument*. The full Pathname of a file that has been modified.
|
191
|
+
#==Example
|
192
|
+
# # Delete a package and save all modified files
|
193
|
+
# rr.uninstall("cool-world"){|file| true}
|
194
|
+
# # Delete a package and discard all modified files
|
195
|
+
# rr.uninstall("cool-world")
|
196
|
+
def uninstall(pkg_name)
|
197
|
+
spec = @package_specs.find{|spec| spec.name == pkg_name}
|
198
|
+
|
199
|
+
[:levels, :music, :sounds, :graphics].each do |sym|
|
200
|
+
contrib_dir = @path + self.class.const_get(:"CONTRIB_#{sym.upcase}_DIR")
|
201
|
+
|
202
|
+
#Delete all the files
|
203
|
+
files = spec[sym]
|
204
|
+
files.each do |filename|
|
205
|
+
full_path = contrib_dir + filename
|
206
|
+
#Check if the file was modified
|
207
|
+
if block_given? and Digest::SHA1.hexdigest(File.read(full_path)) != spec[:checksums][sym.to_s][filename] #to_s as the keys are strings there, see PackageSpecification.from_file
|
208
|
+
if yield(full_path) #Getting a truth value from the block means copying
|
209
|
+
FileUtils.cp(full_path, full_path.parent + filename.sub(/\.(.*?)$/, '.MODIFIED.\1'))
|
210
|
+
end
|
211
|
+
end
|
212
|
+
File.delete(full_path)
|
213
|
+
end
|
214
|
+
|
215
|
+
#Delete now empty directories
|
216
|
+
loop do
|
217
|
+
empty_dirs = []
|
218
|
+
contrib_dir.find do |path|
|
219
|
+
next if path == contrib_dir #We surely don’t want to delete the toplevel dir.
|
220
|
+
empty_dirs << path if path.directory? and path.children.empty?
|
221
|
+
end
|
222
|
+
#If no empty directories are present anymore, break out of the loop.
|
223
|
+
break if empty_dirs.empty?
|
224
|
+
#Otherwise delete the empty directories and redo the process, because
|
225
|
+
#the parent directories could be empty now.
|
226
|
+
empty_dirs.each{|path| FileUtils.rmdir(path)}
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
#Delete worlds as well. Worlds can’t reside in subdirectories, therefore it’s unnecessary
|
231
|
+
#to check for the empty-directory thing.
|
232
|
+
spec[:worlds].each do |dirname|
|
233
|
+
full_path = @worlds_dir + dirname
|
234
|
+
#Check if any of the world’s files has been modified
|
235
|
+
["description.xml", "layer.xml", "world.xml"].each do |wfile|
|
236
|
+
full_wfile_path = full_path + wfile
|
237
|
+
if block_given? and Digest::SHA1.hexdigest(File.read(full_wfile_path)) != spec[:checksums]["worlds"][dirname][wfile] #"worlds" is a string for technical reasons, see PackageSpecification.from_file
|
238
|
+
if yield(full_wfile_path) #Getting a truth value from the block means copying
|
239
|
+
FileUtils.cp_r(full_path, full_path.parent + "#{dirname}.MODIFIED")
|
240
|
+
break #Break from the inner iteration, we just need to copy once
|
241
|
+
end
|
242
|
+
end
|
243
|
+
end
|
244
|
+
FileUtils.rm_r(full_path)
|
245
|
+
end
|
246
|
+
|
247
|
+
File.delete(@specs_dir + spec.spec_file_name) #Remove the spec itself
|
248
|
+
@package_specs.delete(spec) #Otherwise we have a stale package in the array
|
249
|
+
end
|
250
|
+
|
251
|
+
#Returns the path this repository refers to.
|
252
|
+
def to_s
|
253
|
+
@path.to_s
|
254
|
+
end
|
255
|
+
|
256
|
+
def contain?(pkg)
|
257
|
+
if pkg.kind_of? Package
|
258
|
+
@package_specs.include?(pkg.spec)
|
259
|
+
else
|
260
|
+
@package_specs.any?{|spec| spec.name == pkg}
|
261
|
+
end
|
262
|
+
end
|
263
|
+
alias contains? contain?
|
264
|
+
|
265
|
+
def search(regexp, *attributes)
|
266
|
+
attributes << :name if attributes.empty? #Default value
|
267
|
+
|
268
|
+
@package_specs.each do |spec|
|
269
|
+
attributes.each do |att|
|
270
|
+
case att
|
271
|
+
when :name then yield(spec.name) if spec.name =~ regexp
|
272
|
+
when :title then yield(spec.name) if spec.title =~ regexp
|
273
|
+
when :authors then yield(spec.name) if spec.authors.any?{|a| a =~ regexp}
|
274
|
+
when :difficulty then yield(spec.name) if spec.difficulty =~ regexp
|
275
|
+
when :description then yield(spec.name) if spec.description =~ regexp
|
276
|
+
when :levels then yield(spec.name) if spec.levels.any?{|l| l =~ regexp}
|
277
|
+
when :music then yield(spec.name) if spec.music.any?{|m| m =~ regexp}
|
278
|
+
when :sounds then yield(spec.name) if spec.sound.any?{|s| s =~ regexp}
|
279
|
+
when :graphics then yield(spec.name) if spec.graphics.any?{|g| g =~ regexp}
|
280
|
+
when :worlds then yield(spec.name) if spec.worlds.any?{|w| w =~ regexp}
|
281
|
+
else
|
282
|
+
$stderr.puts("Warning: Unknown attribute #{att}, ignoring it.")
|
283
|
+
end #case
|
284
|
+
end #attributes.each
|
285
|
+
end # @package_specs.each
|
286
|
+
end #search
|
287
|
+
|
288
|
+
end #LocalRepository
|
289
|
+
|
290
|
+
end
|