smc-get 0.1.0 → 0.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,80 @@
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 class allows for easy compression and decompression of .smcpak
24
+ #files. Please note that during this process no validations are performed,
25
+ #i.e. you have to ensure that the directory you want to compress is in
26
+ #smc package layout and the .smcpak files you want to decompress are
27
+ #really smc packages.
28
+ class PackageArchive
29
+
30
+ #The location of this archive.
31
+ attr_reader :path
32
+
33
+ #Compresses all files in +directory+ into a TAR.XZ file with the
34
+ #extension ".smcpak".
35
+ #Returns an object of this class.
36
+ # smcpak = PackageArchive.compress("mypackage")
37
+ # puts smcpak.path #=> mypackage.smcpak
38
+ def self.compress(directory, goal_file)
39
+ directory = Pathname.new(directory).expand_path
40
+ tar_file = Pathname.new("#{goal_file}.tar").expand_path
41
+ xz_file = Pathname.new(goal_file).expand_path
42
+
43
+ Dir.chdir(directory.parent) do
44
+ tar_file.open("wb") do |file|
45
+ Archive::Tar::Minitar.pack(directory.relative_path_from(Pathname.new(".").expand_path).to_s, file)
46
+ end
47
+ end
48
+ XZ.compress_file(tar_file, xz_file)
49
+
50
+ tar_file.delete #We don’t need it anymore
51
+
52
+ new(xz_file)
53
+ end
54
+
55
+ #Creates a new PackageArchive from an existing .smcpak file.
56
+ def initialize(archive)
57
+ @path = Pathname.new(archive)
58
+ end
59
+
60
+ #Decompresses this archive into +directory+, creating a subdirectory
61
+ #named after the archive without the extension, and returns the path
62
+ #to that subdirectory (a Pathname object).
63
+ # smcpak = PackageArchive.new("mypackage.smcpak")
64
+ # puts smcpak.decompress #=> #<Pathname:mypackage>
65
+ def decompress(directory)
66
+ directory = Pathname.new(directory)
67
+ tar_file = directory + @path.basename.to_s.sub(/\.smcpak$/, ".tar")
68
+ dest_dir = directory + tar_file.basename.to_s.sub(/\.tar$/, "")
69
+
70
+ XZ.decompress_file(@path.to_s, tar_file.to_s)
71
+ Archive::Tar::Minitar.unpack(tar_file.to_s, dest_dir.to_s)
72
+
73
+ tar_file.delete #We don’ŧ need it anymore
74
+
75
+ dest_dir
76
+ end
77
+
78
+ end
79
+
80
+ end
@@ -0,0 +1,253 @@
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 PackageSpecification object is a mostly informational object
24
+ #directly related to Package objects. That is, each and every
25
+ #Package instance has a Package#spec method that will retrieve
26
+ #the PackageSpecification, an instance of this class, for you:
27
+ #
28
+ # puts pkg.spec.title #=> Cool package
29
+ #
30
+ #Instances of this class can be understood as the parsed content of
31
+ #a package’s specification file. It has getter and setter methods
32
+ #reflecting the keys that are allowed in the specification (see
33
+ #the smcpak.rdoc file), but unless you want to build smc packages,
34
+ #there’s no need for you to use the setter methods.
35
+ class PackageSpecification
36
+
37
+ #The keys listed here must be mentioned inside a package spec,
38
+ #otherwise the package is considered broken.
39
+ SPEC_MANDATORY_KEYS = [:title, :last_update, :authors, :difficulty, :description, :checksums].freeze
40
+
41
+ ##
42
+ # :attr_accessor: title
43
+ #The package’s title.
44
+
45
+ ##
46
+ # :attr_accessor: last_update
47
+ #The time (an instance of class Time) indicating when the package
48
+ #was last updated.
49
+
50
+ ##
51
+ # :attr_accessor: authors
52
+ #The authors of this package. An array.
53
+
54
+ ##
55
+ # :attr_accessor: difficulty
56
+ #The difficulty of this package as a string.
57
+
58
+ ##
59
+ # :attr_accessor: description
60
+ #The description of this package.
61
+
62
+ ##
63
+ # :attr_accessor: install_message
64
+ #A message to display during installation of this package or nil if
65
+ #no message shall be displayed.
66
+
67
+ ##
68
+ # :attr_accessor: remove_message
69
+ #A message to display during removing of this package or nil if no
70
+ #message shall be displayed.
71
+
72
+ ##
73
+ # :attr_accessor: dependecies
74
+ #An array of package names this package depends on, i.e. packages that
75
+ #need to be installed before this one can be installed. The array is
76
+ #empty if no dependecies exist.
77
+
78
+ ##
79
+ # :attr_accessor: levels
80
+ #An array of level file names (strings).
81
+
82
+ ##
83
+ # :attr_accessor: music
84
+ #An array of music file names (strings).
85
+
86
+ ##
87
+ # :attr_accessor: sounds
88
+ #An array of sound file names (strings).
89
+
90
+ ##
91
+ # :attr_accessor: graphics
92
+ #An array of graphic file names (strings).
93
+
94
+ ##
95
+ # :attr_accessor: worlds
96
+ #An array of graphic file names (strings).
97
+
98
+ ##
99
+ # :attr_accessor: checksums
100
+ #A hash that maps each filename in this package to it’s SHA1 checksum.
101
+
102
+ #The name of the package this specification is used in, without any
103
+ #file extension.
104
+ attr_reader :name
105
+
106
+ ##
107
+ # :attr_reader: compressed_file_name
108
+ #The name of the compressed file this specification should belong to.
109
+ #The same as name, but the extension .smcpak was appended.
110
+
111
+ ##
112
+ # :attr_reader: spec_file_name
113
+ #The name of the specification file. The same as name, but
114
+ #the extension .yml was appended.
115
+
116
+ #Creates a PackageSpecification by directly reading a complete
117
+ #spec from a YAML file.
118
+ #==Parameter
119
+ #[path] The path to the file to read.
120
+ #==Return value
121
+ #An instance of this class.
122
+ #==Raises
123
+ #[InvalidSpecification] +path+ was not found or was malformatted.
124
+ #==Example
125
+ # path = remote_repo.fetch_spec("cool_pkg.yml")
126
+ # spec = SmcGet::PackageSpecification.from_file(path)
127
+ # puts spec.title #=> "Cool package"
128
+ #==Remarks
129
+ #This method may be useful if you don’t need a full-blown
130
+ #Package object and just want to deal with it’s most important
131
+ #attributes.
132
+ def self.from_file(path)
133
+ info = nil
134
+ begin
135
+ info = YAML.load_file(path.to_s)
136
+ rescue Errno::ENOENT => e
137
+ raise(Errors::InvalidSpecification, "File '#{path}' doesn't exist!")
138
+ rescue => e
139
+ raise(Errors::InvalidSpecification, "Invalid YAML: #{e.message}")
140
+ end
141
+
142
+ spec = new(File.basename(path).sub(/\.yml$/, ""))
143
+ info.each_pair do |key, value|
144
+ spec.send(:"#{key}=", value)
145
+ end
146
+ #TODO: Convert the strings in :checksums to strings, except the
147
+ #filenames, those should be strings. Anyone???
148
+
149
+ raise(Errors::InvalidSpecification, spec.validate.first) unless spec.valid?
150
+
151
+ spec
152
+ end
153
+
154
+ #Returns the matching package name from the package specification’s name
155
+ #by replacing the .yml extension with .smcpak.
156
+ #==Return value
157
+ #A string ending in ".smcpak".
158
+ #==Example
159
+ # p SmcGet::PackageSpecification.spec2pkg("cool_pkg.yml") #=> "cool_pkg.smcpak")
160
+ def self.spec2pkg(spec_file_name) # :nodoc:
161
+ spec_file_name.to_s.sub(/\.yml$/, ".smcpak")
162
+ end
163
+
164
+ #Returns the matching specification file name from the package’s name
165
+ #by replacing the .smcpak extension with .yml.
166
+ #==Return value
167
+ #A string ending in ".yml"
168
+ #==Example
169
+ # p SmcGet::PackageSpecification.pkg2spec("cool_pkg.smcpak") #=> "cool_pkg.yml"
170
+ def self.pkg2spec(package_file_name) # :nodoc:
171
+ package_file_name.to_s.sub(/\.smcpak$/, ".yml")
172
+ end
173
+
174
+ def initialize(pkg_name)
175
+ @info = {:dependencies => [], :levels => [], :music => [], :sounds => [], :graphics => [], :worlds => []}
176
+ @name = pkg_name
177
+ end
178
+
179
+ #See attribute.
180
+ def compressed_file_name # :nodoc:
181
+ "#@name.smcpak"
182
+ end
183
+
184
+ #See attribute.
185
+ def spec_file_name # :nodoc:
186
+ "#@name.yml"
187
+ end
188
+
189
+ [:title, :last_update, :authors, :difficulty, :description, :install_message, :remove_message, :dependencies, :levels, :music, :sounds, :graphics, :worlds, :checksums].each do |sym|
190
+ define_method(sym){@info[sym]}
191
+ define_method(:"#{sym}="){|val| @info[sym] = val}
192
+ end
193
+
194
+ def [](sym)
195
+ if respond_to?(sym)
196
+ send(sym)
197
+ else
198
+ raise(IndexError, "No such specification key: #{sym}!")
199
+ end
200
+ end
201
+
202
+ def valid?
203
+ validate.empty?
204
+ end
205
+
206
+ def validate
207
+ errors = []
208
+
209
+ SPEC_MANDATORY_KEYS.each do |sym|
210
+ errors << "Mandatory key #{sym} is missing!" unless @info.has_key?(sym)
211
+ end
212
+
213
+ errors
214
+ end
215
+
216
+ #Compares two specifications. They are considered equal if all their
217
+ #attributes (levels, difficulty, etc.) are equal.
218
+ def ==(other)
219
+ return false unless other.respond_to? :info
220
+ @info == other.info
221
+ end
222
+
223
+ #Saves the package specification in YAML format into a file.
224
+ #==Parameter
225
+ #[directory] The directory where to save the file to. The filename is automatically
226
+ # detected from the attributes set for the specification.
227
+ #==Raises
228
+ #[InvalidSpecification] The specification was not valid, i.e. contained incorrect
229
+ # or missing values.
230
+ #==Example
231
+ # p spec.name #=> "cool_pkg"
232
+ # spec.save(".")
233
+ # p File.file?("cool_pkg.yml") #=> true
234
+ def save(directory)
235
+ raise(Errors::InvalidSpecification, validate.first) unless valid?
236
+
237
+ path = Pathname.new(directory) + "#{@name}.yml"
238
+ #Turn the spec keys for serialization into strings
239
+ hsh = {}
240
+ @info.each_pair{|k, v| hsh[k.to_s] = v}
241
+ path.open("w"){|f| YAML.dump(hsh, f)}
242
+ end
243
+
244
+ protected
245
+
246
+ #Returns the complete internal information hash.
247
+ def info
248
+ @info
249
+ end
250
+
251
+ end
252
+
253
+ end
@@ -0,0 +1,272 @@
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 RemoteRepository represents a web location from which <tt>smc-get</tt>
24
+ #is able to download packages. The structure of those repositories is
25
+ #explained in the smcpak.rdoc file in the <i>Repostories</i> section.
26
+ class RemoteRepository < Repository
27
+
28
+ #Name of the remote directory containing the <tt>.smcpak</tt> files.
29
+ PACKAGES_DIR = "packages"
30
+ #Name of the remote directory containing the packages specifications.
31
+ SPECS_DIR = "specs"
32
+ #Name of the file containg the names of all packages in this repository.
33
+ LIST_FILE = "packages.lst"
34
+ #Number of bytes to read while downloading a file.
35
+ CHUNK_SIZE = 1024
36
+
37
+ ##
38
+ # :attr_reader: packages
39
+ #Returns an array of remote Packages that contains all packages
40
+ #in this repository. This is a very time-intensive operation, depending
41
+ #on how many packages the repository contains, because it downloads all
42
+ #the package specifications of all the packages. It’s not a good idea
43
+ #to call this.
44
+
45
+ #An URI object representing the repository’s URI.
46
+ attr_reader :uri
47
+ #A list of all package names (without an extension).
48
+ attr_reader :packages_list
49
+
50
+ #Creates a "new" Repository.
51
+ #==Parameters
52
+ #[uri] The URI of the repository, something like
53
+ # "\http://my-host.org/smc-repo".
54
+ #==Raises
55
+ #[InvalidRepository] The repository is either nonexistant or doesn’t obey
56
+ # the repository structure format.
57
+ #==Return value
58
+ #The newly created repository.
59
+ #==Usage
60
+ # r = Repository.new("http://myrepo.org")
61
+ # r = Repository.new("https://my-host.org/smc-repo")
62
+ # r = Repository.new("ftp://ftp.my-host.org/smc")
63
+ def initialize(uri)
64
+ @uri = URI.parse(uri)
65
+ #Download the packages list. Usually it’s small enough to fit into RAM.
66
+ begin
67
+ @packages_list = open(@uri + LIST_FILE){|tmpfile| tmpfile.read}.split
68
+ rescue SocketError, Errno::ECONNREFUSED, OpenURI::HTTPError => e #open-uri raises HTTPError even in case of other protocols
69
+ raise(Errors::InvalidRepository.new(@uri), e.message)
70
+ end
71
+ end
72
+
73
+ #Downloads the given package specification from this repository and
74
+ #places it in +directory+.
75
+ #
76
+ #If the file already exists, it’s overwritten.
77
+ #==Parameters
78
+ #[spec_file] The package specification file you want to download. It must
79
+ # end with the <tt>.yml</tt> extension.
80
+ #[directory] (".") The directory where you want to download the file to.
81
+ # Created if it doesn’t exist.
82
+ #==Raises
83
+ #[NoSuchResourceError] The package name for this specification couldn’t be
84
+ # found in the repository. Note that the spec may be
85
+ # there despite of this, but packages not listed
86
+ # in the repository’s contents file are treated as if
87
+ # they weren’t there.
88
+ #==Return value
89
+ #The path to the downloaded file as a Pathname object.
90
+ #==Usage
91
+ # my_repo.fetch_spec("mypackage.yml", "/home/freak/Downloads")
92
+ #
93
+ # my_repo.fetch_spec("mypackage.yml")
94
+ def fetch_spec(spec_file, directory = ".")
95
+ directory = Pathname.new(directory)
96
+ pkg_name = spec_file.sub(/\.yml$/, "")
97
+ goal_file = directory + spec_file
98
+
99
+ unless @packages_list.include?(pkg_name)
100
+ raise(Errors::NoSuchResourceError.new(:spec, spec_file), "Package '#{pkg_name}' not found in the repository!")
101
+ end
102
+
103
+ directory.mktree unless directory.directory?
104
+
105
+ #I am quite sure that URI#merge has a bug. Example:
106
+ # uri = URI.parse("http://www.ruby-lang.org")
107
+ #Now try to append the path test/test2:
108
+ # uri2 = uri + "test" + "test2"
109
+ #What do you think contains uri2? This:
110
+ # http://www.ruby-lang.org/test2
111
+ #Something missing, eh? Even more surprising, this one works as
112
+ #expected:
113
+ # uri + "test/test2"
114
+ #The second one is the workaround I use in the following line.
115
+ open(@uri.merge("#{SPECS_DIR}/#{spec_file}")) do |tempfile|
116
+ File.open(goal_file, "w") do |file|
117
+ file.write(tempfile.read) #Specs almost ever are small enough to fit in RAM
118
+ end
119
+ end
120
+ goal_file
121
+ end
122
+
123
+ #Downloads the given package from this repository and places it in
124
+ #+directory+. Yields the package’s total size in bytes and how many
125
+ #bytes have already been downloaded. If the size is unknown for some
126
+ #reason, +bytes_total+ is nil. For the last block call, +bytes_total+
127
+ #and +bytes_done+ are guaranteed to be equal, except if +bytes_total+
128
+ #couldn’t be determined in which case it’s still nil.
129
+ #
130
+ #If the file does already exist in +directory+, it is overwritten.
131
+ #==Parameters
132
+ #[pkg_file] The package file you want to download. It must end in
133
+ # the <tt>.smcpak</tt> extension.
134
+ #[directory] (".") The directory where you want to download the file to.
135
+ # Created if it doesn’t exist.
136
+ #==Raises
137
+ #[NoSuchPackageError] The package name for this package couldn’t be
138
+ # found in the repository. Note that the package may be
139
+ # there despite of this, but packages not listed
140
+ # in the repository’s contents file are treated as if
141
+ # they weren’t there.
142
+ #[OpenURI::HTTPError] Connection error.
143
+ #==Return value
144
+ #The path to the downloaded file as a Pathname object.
145
+ #==Usage
146
+ # my_repo.fetch_package("mypackage.smcpak", "/home/freak/downloads")
147
+ #
148
+ # my_repo.fetch_package("mypackage.smcpak") do |bytes_total, bytes_done|
149
+ # print("\rDownloaded #{bytes_done} bytes of #{bytes_total}.") if bytes_total
150
+ # end
151
+ def fetch_package(pkg_file, directory = ".")
152
+ directory = Pathname.new(directory)
153
+ pkg_name = pkg_file.sub(/\.smcpak$/, "")
154
+ goal_file = directory + pkg_file
155
+
156
+ unless @packages_list.include?(pkg_name)
157
+ raise(Errors::NoSuchPackageError.new(pkg_name), "ERROR: Package '#{pkg_name}' not found in the repository!")
158
+ end
159
+
160
+ directory.mktree unless directory.directory?
161
+
162
+ bytes_total = nil
163
+ size_proc = lambda{|content_length| bytes_total = content_length}
164
+ prog_proc = lambda{|bytes_done| yield(bytes_total, bytes_done)}
165
+
166
+ #See the source of #fetch_spec for an explanation on the obscure
167
+ #URI concatenation.
168
+ open(@uri + "#{PACKAGES_DIR}/#{pkg_file}", "rb", content_length_proc: size_proc, progress_proc: prog_proc) do |tempfile|
169
+ #The packages may be too big for fitting into RAM, therefore we’re going
170
+ #to read and write the packages chunk by chunk. Btw. please notice me
171
+ #if you find a SMC package that’s larger than 4 GiB! I’d be curious
172
+ #about what it contains!
173
+ File.open(goal_file, "wb") do |file|
174
+ while chunk = tempfile.read(CHUNK_SIZE)
175
+ file.write(chunk)
176
+ end
177
+ end
178
+ end
179
+
180
+ goal_file
181
+ end
182
+
183
+ #Not implemented yet.
184
+ def install(package, &block)
185
+ raise(NotImplementedError, "Can't automatically upload to remote repositories yet!")
186
+ end
187
+
188
+ #Not implemented yet.
189
+ def uninstall(pkg_name)
190
+ raise(NotImplementedError, "Can't automatically remove from remote repositories yet!")
191
+ end
192
+
193
+ #Returns the URI of the remote repository.
194
+ def to_s
195
+ @uri.to_s
196
+ end
197
+
198
+ #See attribute.
199
+ # def packages # :nodoc:
200
+ # @packages_list.map do |pkg_name|
201
+ # Package.from_repository(self, "#{pkg_name}.yml")
202
+ # end
203
+ # end
204
+
205
+ #True if a package with the given name (without the .smcpak extension)
206
+ #exists in the repository.
207
+ def contain?(pkg)
208
+ if pkg.kind_of? Package
209
+ @packages_list.include?(pkg.spec.name)
210
+ else
211
+ @packages_list.include?(pkg_name)
212
+ end
213
+ end
214
+ alias contains? contain?
215
+
216
+ #call-seq:
217
+ # search(regexp [, *attributes ]){|pkgname|...}
218
+ #
219
+ #Searches for a specific package and yields each candidate to the
220
+ #given block.
221
+ #==Parameters
222
+ #[regexp] The Regular Expression to use as the search pattern.
223
+ #[*attributes] (<tt>[:name]</tt>) A list of all attributes to match
224
+ # the Regular Expression against (note that only
225
+ # passing :name is siginificantly faster, because
226
+ # there’s no need to download the specs).
227
+ # Possible attributes:
228
+ # * name
229
+ # * title
230
+ # * authors
231
+ # * difficulty
232
+ # * description
233
+ # * levels
234
+ # * music
235
+ # * sounds
236
+ # * graphics
237
+ # * worlds
238
+ #[pkgname] *Blockargument*. The package name (not title!) of
239
+ # a package matching the search criteria.
240
+ #==Examples
241
+ # rp.search(/cool/){|pkgname| p pkgname} #=> "cool_levels"
242
+ # rp.search(/Luiji/i, :authors){|pkgname| p pkgname} #=> All packages created by Luiji or luiji...
243
+ def search(regexp, *attributes)
244
+ attributes << :name if attributes.empty? #Default value
245
+ if attributes == [:name] #Good, no need to download all the specs
246
+ @packages_list.each{|name| yield(name) if name =~ regexp}
247
+ else #OK, so we need to download one spec after the other...
248
+ @packages_list.each do |pkgname|
249
+ spec = PackageSpecification.from_file(fetch_spec("#{pkgname}.yml", SmcGet.temp_dir))
250
+ attributes.each do |att|
251
+ case att
252
+ when :name then yield(pkgname) if spec.name =~ regexp
253
+ when :title then yield(pkgname) if spec.title =~ regexp
254
+ when :authors then yield(pkgname) if spec.authors.any?{|a| a =~ regexp}
255
+ when :difficulty then yield(pkgname) if spec.difficulty =~ regexp
256
+ when :description then yield(pkgname) if spec.description =~ regexp
257
+ when :levels then yield(pkgname) if spec.levels.any?{|l| l =~ regexp}
258
+ when :music then yield(pkgname) if spec.music.any?{|m| m =~ regexp}
259
+ when :sounds then yield(pkgname) if spec.sound.any?{|s| s =~ regexp}
260
+ when :graphics then yield(pkgname) if spec.graphics.any?{|g| g =~ regexp}
261
+ when :worlds then yield(pkgname) if spec.worlds.any?{|w| w =~ regexp}
262
+ else
263
+ $stderr.puts("Warning: Unknown attribute #{att}, ignoring it.")
264
+ end #case
265
+ end #attributes.each
266
+ end # @packages.each
267
+ end #if only :name
268
+ end #search
269
+
270
+ end #RemoteRepository
271
+
272
+ end #SmcGet