smc-get 0.2.0.beta1 → 0.3.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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