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.
@@ -1,272 +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
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}/#{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