revolt 0.5.1
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/README +150 -0
- data/Rakefile +197 -0
- data/bin/rv_find_levels.rb +186 -0
- data/bin/rv_install_level_urls.rb +191 -0
- data/bin/rv_install_levels.rb +76 -0
- data/examples/find_rv_track.rb +17 -0
- data/examples/install_rv_track.rb +28 -0
- data/lib/revolt/args.rb +46 -0
- data/lib/revolt/config.rb +5 -0
- data/lib/revolt/exceptions.rb +34 -0
- data/lib/revolt/fetcher/file_system.rb +31 -0
- data/lib/revolt/fetcher/www.rb +117 -0
- data/lib/revolt/fetcher.rb +30 -0
- data/lib/revolt/info.rb +40 -0
- data/lib/revolt/level.rb +298 -0
- data/lib/revolt/levels.rb +362 -0
- data/lib/revolt/logger.rb +24 -0
- data/lib/revolt/package/archive/analyzer/normalized.rb +50 -0
- data/lib/revolt/package/archive/analyzer/troubled.rb +97 -0
- data/lib/revolt/package/archive/analyzer.rb +34 -0
- data/lib/revolt/package/archive/zip/browser.rb +44 -0
- data/lib/revolt/package/archive.rb +35 -0
- data/lib/revolt/package/exe.rb +10 -0
- data/lib/revolt/package/installer/archive.rb +105 -0
- data/lib/revolt/package/installer/exe.rb +10 -0
- data/lib/revolt/package.rb +64 -0
- data/lib/revolt/util/fs_browser.rb +30 -0
- data/lib/revolt/util.rb +20 -0
- data/lib/revolt.rb +12 -0
- data/test/common.rb +78 -0
- data/test/fixtures/files/nodirs_track.zip +0 -0
- data/test/fixtures/files/readme.txt +1 -0
- data/test/fixtures/files/rickyd_track.zip +0 -0
- data/test/fixtures/files/standard_multi.zip +0 -0
- data/test/fixtures/files/standard_rev_track.zip +0 -0
- data/test/fixtures/files/standard_track.zip +0 -0
- data/test/fixtures/files/zips_inside.zip +0 -0
- data/test/fixtures/rv/gfx/levid.bmp +1 -0
- data/test/fixtures/rv/gfx/levid.bmq +1 -0
- data/test/fixtures/rv/gfx/levidrev.bmp +1 -0
- data/test/fixtures/rv/gfx/levidrev.bmq +1 -0
- data/test/fixtures/rv/gfx/tEsT Level.bmp +1 -0
- data/test/fixtures/rv/levels/levid/levid.inf +1 -0
- data/test/fixtures/rv/levels/levid/levid.w +1 -0
- data/test/fixtures/rv/levels/levid/levida.bmp +1 -0
- data/test/fixtures/rv/levels/levid/readme.txt +1 -0
- data/test/fixtures/rv/levels/levidrev/levidrev.inf +1 -0
- data/test/fixtures/rv/levels/levidrev/levidrev.w +1 -0
- data/test/fixtures/rv/levels/levidrev/levidreva.bmp +1 -0
- data/test/fixtures/rv/levels/levidrev/readme.txt +1 -0
- data/test/fixtures/rv/levels/levidrev/reversed/levidrev.cam +1 -0
- data/test/fixtures/rv/levels/levidrev/reversed/levidrev.fan +1 -0
- data/test/fixtures/rv/levels/levidrev/reversed/levidrev.fin +1 -0
- data/test/fixtures/rv/levels/test lEveL/TEst level.inf +4 -0
- data/test/fixtures/rv/levels/test lEveL/TeST level.w +1 -0
- data/test/fixtures/rv/levels/test lEveL/readme.txt +1 -0
- data/test/fixtures/rv/levels/test lEveL/reversed/TEst level.inf +1 -0
- data/test/fixtures/rv/levels/test lEveL/reversed/TeST level.w +1 -0
- data/test/fixtures/rv/levels/test lEveL/reversed/readme.txt +1 -0
- data/test/fixtures/rv/levels/test lEveL/reversed/test LEVELa.bmp +1 -0
- data/test/fixtures/rv/levels/test lEveL/test LEVELa.bmp +1 -0
- data/test/tc_archive_analyzer.rb +185 -0
- data/test/tc_args.rb +55 -0
- data/test/tc_args_external.rb +55 -0
- data/test/tc_file_system_fetcher.rb +26 -0
- data/test/tc_info.rb +23 -0
- data/test/tc_level.rb +182 -0
- data/test/tc_level_installer.rb +88 -0
- data/test/tc_level_installer_external.rb +124 -0
- data/test/tc_levels.rb +68 -0
- data/test/tc_package_track_installer.rb +174 -0
- data/test/ts_base.rb +14 -0
- data/test/ts_external.rb +7 -0
- metadata +133 -0
data/lib/revolt/level.rb
ADDED
@@ -0,0 +1,298 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'set'
|
3
|
+
require 'revolt/exceptions'
|
4
|
+
require 'revolt/logger'
|
5
|
+
require 'revolt/util'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
module ReVolt
|
9
|
+
# Level contains information about one Re-Volt level.
|
10
|
+
# A unique identifier is used which is a symbol from
|
11
|
+
# the directory name of the Level.
|
12
|
+
#
|
13
|
+
# The operations on Level are dynamic in nature, so that
|
14
|
+
# information about the Level are loaded only if it is
|
15
|
+
# requested. For example the .inf file is loaded only
|
16
|
+
# if information from it is required, such as the level's
|
17
|
+
# name.
|
18
|
+
#
|
19
|
+
# Level's identifier is its directory name in lowercase.
|
20
|
+
# The identifier passed to the various functions can
|
21
|
+
# be:
|
22
|
+
# - a lowercase symbol denoting the level name
|
23
|
+
# - a Level object, which own identifier is used to find the instance
|
24
|
+
# - a string that is the same as the directory name
|
25
|
+
#
|
26
|
+
# Internally all above identifiers are turned into a lowercase
|
27
|
+
# symbol.
|
28
|
+
#
|
29
|
+
# Usually Level is associated with a parent object, which is
|
30
|
+
# Levels. Many of the functions require this association to exist
|
31
|
+
# because otherwise the full path to the level directory, and
|
32
|
+
# thus its files, can not be determined
|
33
|
+
#
|
34
|
+
# The
|
35
|
+
class Level
|
36
|
+
include ReVolt::Logger
|
37
|
+
|
38
|
+
attr_accessor :parent
|
39
|
+
attr_reader :id
|
40
|
+
|
41
|
+
@@stock_ids = Set.new [
|
42
|
+
:nhood1,
|
43
|
+
:market2,
|
44
|
+
:muse2,
|
45
|
+
:garden1,
|
46
|
+
:toylite,
|
47
|
+
:wild_west1,
|
48
|
+
:toy2,
|
49
|
+
:ship1,
|
50
|
+
:nhood2,
|
51
|
+
:muse1,
|
52
|
+
:market1,
|
53
|
+
:wild_west2,
|
54
|
+
:ship2,
|
55
|
+
|
56
|
+
# Battle tags
|
57
|
+
:nhood1_battle,
|
58
|
+
:markar,
|
59
|
+
:bot_bat,
|
60
|
+
:muse_bat,
|
61
|
+
|
62
|
+
# Stunt track
|
63
|
+
:stunts
|
64
|
+
]
|
65
|
+
|
66
|
+
# Creates a new Level object that has the given identifier
|
67
|
+
def initialize(id, args = {})
|
68
|
+
@id = ReVolt::Level::to_level_id id
|
69
|
+
@parent = args[:parent]
|
70
|
+
@name = args[:name]
|
71
|
+
@inf = nil
|
72
|
+
@loaded = false
|
73
|
+
end
|
74
|
+
|
75
|
+
# Tries to create a level identifier from the passed
|
76
|
+
# object.
|
77
|
+
def self.to_level_id(id)
|
78
|
+
case
|
79
|
+
when id.is_a?(ReVolt::Level) then
|
80
|
+
ReVolt::Logger::debug("Level::to_level_id: from id Level #{id}")
|
81
|
+
id.id
|
82
|
+
when id.is_a?(Symbol) then
|
83
|
+
ReVolt::Logger::debug("Level::to_level_id: from id Symbol #{id}")
|
84
|
+
id
|
85
|
+
else
|
86
|
+
ReVolt::Logger::debug("Level::to_level_id: default action from id #{id}")
|
87
|
+
id.to_s.downcase.to_sym
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
# Returns true if the Level's .inf file has been loaded
|
92
|
+
def loaded?
|
93
|
+
@loaded
|
94
|
+
end
|
95
|
+
|
96
|
+
# Returns the name of the level
|
97
|
+
def name
|
98
|
+
reload unless @name
|
99
|
+
@name
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns true if the Level is a stock level.
|
103
|
+
# The comparison is done only on the basis of
|
104
|
+
# the Level's identifier.
|
105
|
+
def stock?
|
106
|
+
@@stock_ids.member?(@id)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Inverse of #stock?
|
110
|
+
def custom?
|
111
|
+
not stock?
|
112
|
+
end
|
113
|
+
|
114
|
+
# Returns true if there is a reversed version of this
|
115
|
+
# level. Basically just checks if the track has a
|
116
|
+
# reversed folder
|
117
|
+
def reverse?
|
118
|
+
ensure_parent
|
119
|
+
rpath = path + 'reversed'
|
120
|
+
rpath.directory?
|
121
|
+
end
|
122
|
+
|
123
|
+
# Returns the files belonging to this level, relative
|
124
|
+
# to the Re-Volt base directory. Pathname objects.
|
125
|
+
def files
|
126
|
+
self.dirs_and_files[1]
|
127
|
+
end
|
128
|
+
|
129
|
+
# Returns the directories belonging to this level,
|
130
|
+
# relative to the Re-Volt base directory
|
131
|
+
def dirs
|
132
|
+
self.dirs_and_files[0]
|
133
|
+
end
|
134
|
+
|
135
|
+
# Returns the files and directories belonging to this
|
136
|
+
# level in an array, in first element array of the directories
|
137
|
+
# and in second element array of the files. Each file
|
138
|
+
# and directory is a Pathname object.
|
139
|
+
def dirs_and_files
|
140
|
+
ensure_parent
|
141
|
+
|
142
|
+
# Level directories, and level files
|
143
|
+
d = []
|
144
|
+
f = []
|
145
|
+
# Level globs, and graphics globs
|
146
|
+
globs = [path + '**/*',
|
147
|
+
gfx_path + ReVolt::Util::caseinsensitiveglob(id.to_s + '.bm?')
|
148
|
+
]
|
149
|
+
debug("Level gfx glob: #{globs[1]}")
|
150
|
+
|
151
|
+
# Creates relative paths to base directory
|
152
|
+
globs.each do |g|
|
153
|
+
debug("Globbing: #{g}")
|
154
|
+
Pathname::glob(g) do |path|
|
155
|
+
debug("Inspecting file #{path}")
|
156
|
+
addto = path.directory?() ? d : f
|
157
|
+
addto << path.relative_path_from(@parent.base_path)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
debug("Returning #{d} and #{f}")
|
162
|
+
[d, f]
|
163
|
+
=begin
|
164
|
+
# The gfx files then
|
165
|
+
debug("Level path: #{path}")
|
166
|
+
debug("Level gfx path: #{gfx_path}")
|
167
|
+
debug("files: #{f.join(' ')}")
|
168
|
+
(f + d).each do |p|
|
169
|
+
debug("Level: relative path to RV base from #{p}")
|
170
|
+
p = relative_path_from(@parent.base_path)
|
171
|
+
debug("Level: relative path result: #{p}")
|
172
|
+
end
|
173
|
+
r = f.map do |f|
|
174
|
+
f.relative_path_from(@parent.base_path)
|
175
|
+
end
|
176
|
+
|
177
|
+
debug("files relative: #{r.join(' ')}")
|
178
|
+
|
179
|
+
r
|
180
|
+
=end
|
181
|
+
end
|
182
|
+
|
183
|
+
def path
|
184
|
+
ensure_parent
|
185
|
+
@parent.path + @id.to_s
|
186
|
+
end
|
187
|
+
|
188
|
+
# Returns the full name and path of the .inf file
|
189
|
+
# of this level.
|
190
|
+
def inf_path
|
191
|
+
ensure_parent
|
192
|
+
path + (@id.to_s + '.inf')
|
193
|
+
end
|
194
|
+
|
195
|
+
# Returns the path to the graphics files of the Level
|
196
|
+
def gfx_path
|
197
|
+
ensure_parent
|
198
|
+
@parent.gfx_path
|
199
|
+
end
|
200
|
+
|
201
|
+
# Convenience function for moving this Level
|
202
|
+
# to given Levels. See Levels::move_level_to
|
203
|
+
def move_to(levels, args = {})
|
204
|
+
ensure_parent
|
205
|
+
raise(ArgumentError,
|
206
|
+
"Not ReVolt::Levels") unless levels.is_a? ReVolt::Levels
|
207
|
+
@parent.move_level_to(id, levels, args)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Convenience function for copying this Level
|
211
|
+
# to given Levels. See Levels::copy_level_to
|
212
|
+
def copy_to(levels, args = {})
|
213
|
+
ensure_parent
|
214
|
+
raise(ArgumentError,
|
215
|
+
"Not ReVolt::Levels") unless levels.is_a? ReVolt::Levels
|
216
|
+
@parent.copy_level_to(id, levels, args)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Convenience function for deleting this Level
|
220
|
+
# from given Levels. See Levels::delete
|
221
|
+
def delete(args = {})
|
222
|
+
ensure_parent
|
223
|
+
@parent.delete(id, args)
|
224
|
+
end
|
225
|
+
|
226
|
+
# Reloads the details of the Level (from .inf file)
|
227
|
+
def reload
|
228
|
+
raise "ReVolt::Levels instance must be specified before Level can be loaded" unless @parent
|
229
|
+
|
230
|
+
inf_file = Pathname.new(@parent.path)
|
231
|
+
inf_file += id.to_s
|
232
|
+
inf_file += id.to_s + ".inf"
|
233
|
+
debug "Loading inf file: #{inf_file}"
|
234
|
+
|
235
|
+
if !inf_file.file?
|
236
|
+
raise(InfFileNotFoundError.new(@parent, self, inf_file),
|
237
|
+
"Inf file not found from #{inf_file}")
|
238
|
+
end
|
239
|
+
|
240
|
+
@inf = {}
|
241
|
+
open(inf_file).each do |line|
|
242
|
+
# Remove comments:
|
243
|
+
debug "Level::reload .inf line: #{line}"
|
244
|
+
line.sub!(/;.*/, '')
|
245
|
+
if line =~ /^(\w+)\s+([^\s].*)$/
|
246
|
+
key, value = $1, $2
|
247
|
+
value.rstrip!
|
248
|
+
key.downcase!
|
249
|
+
# Replace 'string' values with string
|
250
|
+
value.gsub!(/'([^']*)'/, '\1')
|
251
|
+
@inf[key.intern] = value
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
@name = @inf[:name]
|
256
|
+
@loaded = true
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns a hash of the name/value pairs from
|
260
|
+
# the .inf file. The key of the hash is a lowercase
|
261
|
+
# symbol of the corresponding entry in the .inf file
|
262
|
+
#
|
263
|
+
# For example getting the STARTGRID from .inf file:
|
264
|
+
# level.inf[:startgrid]
|
265
|
+
def inf
|
266
|
+
reload unless loaded?
|
267
|
+
@inf
|
268
|
+
end
|
269
|
+
|
270
|
+
def <=>(o)
|
271
|
+
id === o.id # .to_s.casecmp(o.id.to_s)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Turns the level's id (and possibly name if it has been loaded)
|
275
|
+
# to a human readable string
|
276
|
+
def to_s
|
277
|
+
if loaded?
|
278
|
+
"%s (%s)" % [id.to_s, @name]
|
279
|
+
else
|
280
|
+
id.to_s
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def to_sym
|
285
|
+
id
|
286
|
+
end
|
287
|
+
|
288
|
+
def to_symbol
|
289
|
+
id
|
290
|
+
end
|
291
|
+
|
292
|
+
private
|
293
|
+
def ensure_parent
|
294
|
+
c = caller.first
|
295
|
+
raise "ReVolt::Levels instance must be specified before #{c} can be called" unless @parent
|
296
|
+
end
|
297
|
+
end # Levels
|
298
|
+
end # ReVolt
|
@@ -0,0 +1,362 @@
|
|
1
|
+
require 'pathname'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'revolt/info'
|
4
|
+
require 'revolt/level'
|
5
|
+
require 'revolt/package'
|
6
|
+
require 'revolt/logger'
|
7
|
+
require 'revolt/logger'
|
8
|
+
require 'revolt/args'
|
9
|
+
|
10
|
+
module ReVolt
|
11
|
+
# Is used to handle the levels of Re-Volt installation.
|
12
|
+
# Possible uses:
|
13
|
+
# - Install a track into it
|
14
|
+
# - Delete a track from it
|
15
|
+
# - Move a track to another level directory
|
16
|
+
# - Copy a track to another level directory
|
17
|
+
# - Create a new levels directory structure
|
18
|
+
# - Iterate through all installed tracks etc.
|
19
|
+
#
|
20
|
+
# The Levels and their handling is dynamic. In practice
|
21
|
+
# this means that the library only loads as much as is
|
22
|
+
# needed about the levels to supply the requested information.
|
23
|
+
# As an example, at first when the level is created nothing
|
24
|
+
# is loaded. When the user requests a certain Level, then
|
25
|
+
# all the directories in the levels are read and made into
|
26
|
+
# Level classes. But the actual Level's .inf file and detailed
|
27
|
+
# information is loaded only when such detailed information
|
28
|
+
# is needed such as requesting the name of the level.
|
29
|
+
# This makes the operation very fast.
|
30
|
+
#
|
31
|
+
# The downside is that the Levels can contain entries
|
32
|
+
# that are not really tracks, but some garbage files in the
|
33
|
+
# levels folder.
|
34
|
+
class Levels
|
35
|
+
include ReVolt::Logger
|
36
|
+
include Enumerable
|
37
|
+
|
38
|
+
# Pathname objects
|
39
|
+
attr_accessor :path, :gfx_path, :base_path
|
40
|
+
|
41
|
+
# Creates new Levels instance with given Re-Volt
|
42
|
+
# base path. The actual levels directory
|
43
|
+
# will be in base_rv_path/levels and the
|
44
|
+
# graphics files in base_rv_path/gfx
|
45
|
+
def initialize(base_rv_path, args = {})
|
46
|
+
@base_path = Pathname.new(base_rv_path)
|
47
|
+
@path = @base_path + "levels"
|
48
|
+
@gfx_path = @base_path + "gfx"
|
49
|
+
@path_checked = false
|
50
|
+
|
51
|
+
@levels = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
# Just another way of saying new.
|
55
|
+
#
|
56
|
+
# Example: levels = ReVolt::Levels.at('tmpdir/test')
|
57
|
+
def self.at(*a)
|
58
|
+
new(*a)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Creates new Levels instance of the levels
|
62
|
+
# in the standard Re-Volt installation directory.
|
63
|
+
# The directory is detected from registry
|
64
|
+
def self.installed(args = {})
|
65
|
+
new(ReVolt::Info::path, args)
|
66
|
+
end
|
67
|
+
|
68
|
+
# Iterates through every Level in the directory
|
69
|
+
# returning it to the block. Note that this
|
70
|
+
# method returns all directories in the levels
|
71
|
+
# direcotry as a Level object, so if there is
|
72
|
+
# garbage directories the returned Level object
|
73
|
+
# might throw an exception if the directory is
|
74
|
+
# not an actual level.
|
75
|
+
#
|
76
|
+
# For a bit slower versions that validate the Level
|
77
|
+
# before returning it see each_level method.
|
78
|
+
def each
|
79
|
+
ensure_levels
|
80
|
+
|
81
|
+
@levels.each do |key, level|
|
82
|
+
yield level
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Only returns valid levels (those with .inf file),
|
87
|
+
# Slower than each because the inf file will be loaded,
|
88
|
+
# but if any information from .inf (such as name) is
|
89
|
+
# needed anyway there is no difference
|
90
|
+
def each_level
|
91
|
+
ensure_levels
|
92
|
+
@levels.each do |key, level|
|
93
|
+
begin
|
94
|
+
level.reload # Throws InfFileNotFoundError if not level
|
95
|
+
rescue ReVolt::InfFileNotFoundError => e
|
96
|
+
debug "Levels::each_level: folder #{e.level.path} is not track"
|
97
|
+
else
|
98
|
+
yield level
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Like each_level but only returns custom levels
|
104
|
+
def each_custom
|
105
|
+
each_level do |level|
|
106
|
+
if level.custom?
|
107
|
+
yield level
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Like each_level but only returns stock levels
|
113
|
+
def each_stock
|
114
|
+
each_level do |level|
|
115
|
+
if level.stock?
|
116
|
+
yield level
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
# Returns a reference to a Level object matching
|
122
|
+
# the given level_id. Returns nil if no matching
|
123
|
+
# level.
|
124
|
+
def [](level_id)
|
125
|
+
ensure_levels
|
126
|
+
debug("Levels::[]: #{level_id}")
|
127
|
+
@levels[ReVolt::Level::to_level_id(level_id)]
|
128
|
+
end
|
129
|
+
|
130
|
+
# Creates the directory needed for levels
|
131
|
+
# if it doesn't exist.
|
132
|
+
def create_dir_structure
|
133
|
+
@base_path.mkdir if !@base_path.exist?
|
134
|
+
@path.mkdir if !@path.exist?
|
135
|
+
@gfx_path.mkdir if !@gfx_path.exist?
|
136
|
+
|
137
|
+
ensure_paths
|
138
|
+
end
|
139
|
+
|
140
|
+
# Installs a track package to the Levels.
|
141
|
+
# The package can be on local filesystem or
|
142
|
+
# an external one for which a Fetcher can be
|
143
|
+
# created. So for example an URL can be given
|
144
|
+
# and the track is downloaded and installed.
|
145
|
+
def install(src, args = {})
|
146
|
+
usesrc = src.chomp if src.is_a? String
|
147
|
+
usesrc ||= src
|
148
|
+
fetcher = Fetcher.for(usesrc, args)
|
149
|
+
fetcher.run(args)
|
150
|
+
|
151
|
+
ids = ReVolt::Package::install_track(fetcher.target, @base_path, args)
|
152
|
+
end
|
153
|
+
|
154
|
+
# A convenience function for #install. Extracts all urls from
|
155
|
+
# a string and calls install on them. Useful for example
|
156
|
+
# for installing all track URLs pasted to a chat
|
157
|
+
def install_urls(src, args = {})
|
158
|
+
# Returns nil if any call to install returns
|
159
|
+
# nil as a sign that not all package installers
|
160
|
+
# could provide ids of installed tracks
|
161
|
+
ids = []
|
162
|
+
URI.extract(src, ['http', 'https']) do |url|
|
163
|
+
debug "Levels::install_urls: #{url}"
|
164
|
+
url = ReVolt::call_changer_arg(ReVolt::ARG_INSTALL_URL, args, url)
|
165
|
+
ReVolt::arg_ex_watch(ReVolt::ARG_INSTALL_URL_EX, args, url) {
|
166
|
+
debug "Levels::install_urls2: #{url}"
|
167
|
+
if url
|
168
|
+
id_ret = install(url, args)
|
169
|
+
debug "Levels::install_urls: install returned ids #{id_ret}"
|
170
|
+
ids = nil unless id_ret
|
171
|
+
ids += id_ret if id_ret && ids
|
172
|
+
end
|
173
|
+
}
|
174
|
+
end
|
175
|
+
ids
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns true if the level exists
|
179
|
+
def member?(level)
|
180
|
+
ensure_levels
|
181
|
+
|
182
|
+
ret = @levels.has_key?(ReVolt::Level::to_level_id(level))
|
183
|
+
end
|
184
|
+
def include?(*args); member?(*args); end
|
185
|
+
|
186
|
+
# Adds a Level instance object. Doesn't copy any files.
|
187
|
+
def add(level, args = {})
|
188
|
+
ensure_levels
|
189
|
+
raise ArgumentError, "not level: #{level}" unless level.is_a? ReVolt::Level
|
190
|
+
raise "Already exists #{level}" if @levels.has_key?(level.id)
|
191
|
+
@levels[level.id] = level
|
192
|
+
level.parent = self
|
193
|
+
debug("Levels add: #{level.id}")
|
194
|
+
level
|
195
|
+
end
|
196
|
+
|
197
|
+
# Just removes the level from Levels, without deleting the
|
198
|
+
# files on filesystem
|
199
|
+
def remove(idpar, args = {})
|
200
|
+
ensure_levels
|
201
|
+
|
202
|
+
id = ReVolt::Level::to_level_id(idpar)
|
203
|
+
raise "Levels::remove: path #{path} #{idpar} don't exist" unless @levels.has_key?(id)
|
204
|
+
level = @levels[id]
|
205
|
+
|
206
|
+
debug("Level class: #{level.class}")
|
207
|
+
@levels.delete(level.id)
|
208
|
+
level.parent = nil
|
209
|
+
debug("Levels removed: #{level.id}")
|
210
|
+
level
|
211
|
+
end
|
212
|
+
|
213
|
+
# Removes from Levels and deletes the files
|
214
|
+
def delete(id_orig, args = {})
|
215
|
+
level = remove(id_orig)
|
216
|
+
begin
|
217
|
+
# Remove takes away parent association, so restore it
|
218
|
+
# temporarily so that files can be deleted
|
219
|
+
level.parent = self
|
220
|
+
(dirs, files) = level.dirs_and_files
|
221
|
+
files.each do |file|
|
222
|
+
src_file = base_path + file
|
223
|
+
debug("Deleting file #{src_file}")
|
224
|
+
src_file.delete
|
225
|
+
end
|
226
|
+
dirs.each do |dir|
|
227
|
+
src_dir = base_path + dir
|
228
|
+
debug("Deleting directory #{dir}")
|
229
|
+
src_dir.rmdir
|
230
|
+
end
|
231
|
+
debug("Deleting dir #{level.path}")
|
232
|
+
level.path.rmdir
|
233
|
+
ensure
|
234
|
+
level.parent = nil
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
238
|
+
# Copies a Level identified by level_id and all its files to
|
239
|
+
# another Levels object. If matching Level did not exist, raises
|
240
|
+
# InvalidLevelIdError
|
241
|
+
def copy_level_to(level_id, levels, args = {})
|
242
|
+
ensure_levels
|
243
|
+
|
244
|
+
level_files_to(level_id, levels, args) do |file, dst_dir|
|
245
|
+
debug("Levels: copying file #{file} to #{dst_dir}")
|
246
|
+
FileUtils.cp(file, dst_dir)
|
247
|
+
end
|
248
|
+
|
249
|
+
id = ReVolt::Level::to_level_id(level_id)
|
250
|
+
levels.add(ReVolt::Level.new(id))
|
251
|
+
end
|
252
|
+
|
253
|
+
# Moves a Level identified by level_id and all its files to
|
254
|
+
# another Levels object. If matching Level did not exist, raises
|
255
|
+
# InvalidLevelIdError
|
256
|
+
def move_level_to(id_orig, levels, args = {})
|
257
|
+
ensure_levels
|
258
|
+
|
259
|
+
level_files_to(id_orig, levels, args) do |file, dst_dir|
|
260
|
+
debug("Levels: moving file #{file} to #{dst_dir}")
|
261
|
+
FileUtils.mv(file, dst_dir)
|
262
|
+
end
|
263
|
+
|
264
|
+
id = ReVolt::Level::to_level_id(id_orig)
|
265
|
+
level = @levels[id]
|
266
|
+
|
267
|
+
# Remove the source level directory and any other directory
|
268
|
+
# possibly remaining
|
269
|
+
level.path.rmtree if level.path.exist?
|
270
|
+
remove(level)
|
271
|
+
levels.add(level)
|
272
|
+
end
|
273
|
+
|
274
|
+
# Reloads all the levels. Essentially discards the Level objects
|
275
|
+
# that have been loaded. This means that the references you had
|
276
|
+
# before reload to Level objects should not be used anymore
|
277
|
+
def reload
|
278
|
+
@levels = nil
|
279
|
+
end
|
280
|
+
|
281
|
+
# Number of levels
|
282
|
+
def size
|
283
|
+
ensure_levels
|
284
|
+
@levels.size
|
285
|
+
end
|
286
|
+
def length
|
287
|
+
size
|
288
|
+
end
|
289
|
+
|
290
|
+
def level_files_to(id_orig, levels, args = {}) # :nodoc:
|
291
|
+
raise ArgumentError unless levels.respond_to?(:base_path)
|
292
|
+
# raise ArgumentError unless levels.respond_to?(:add)
|
293
|
+
# raise ArgumentError unless levels.respond_to?(:remove)
|
294
|
+
|
295
|
+
ensure_levels
|
296
|
+
|
297
|
+
id = ReVolt::Level::to_level_id(id_orig)
|
298
|
+
|
299
|
+
# If level already exists in target
|
300
|
+
# then remove it to replace with the new instance
|
301
|
+
if levels.member?(id)
|
302
|
+
levels.remove(id)
|
303
|
+
end
|
304
|
+
|
305
|
+
raise(InvalidLevelIdError,
|
306
|
+
"No level for id #{id_orig}") unless id && @levels.has_key?(id)
|
307
|
+
level = @levels[id]
|
308
|
+
|
309
|
+
(dirs, files) = level.dirs_and_files
|
310
|
+
|
311
|
+
# Prepare needed directories
|
312
|
+
dirs.each do |p|
|
313
|
+
tp = levels.base_path + p
|
314
|
+
debug "Levels: preparing path #{tp}"
|
315
|
+
tp.mkpath
|
316
|
+
end
|
317
|
+
|
318
|
+
# The actual level directory is not with the
|
319
|
+
# above command, so create it if necessary
|
320
|
+
level_dir = levels.path + level.id.to_s
|
321
|
+
level_dir.mkdir if !level_dir.directory?
|
322
|
+
|
323
|
+
dst_path = levels.base_path
|
324
|
+
debug("Levels dst path: #{dst_path}")
|
325
|
+
level.files.each do |file|
|
326
|
+
src_file = base_path + file
|
327
|
+
debug("File parent: #{file.parent}")
|
328
|
+
dst_dir = dst_path + file.parent
|
329
|
+
debug("Levels: dst dir: #{dst_dir}")
|
330
|
+
dst_dir.mkpath unless dst_dir.exist?
|
331
|
+
yield(src_file, dst_dir)
|
332
|
+
end
|
333
|
+
|
334
|
+
id
|
335
|
+
end
|
336
|
+
|
337
|
+
private
|
338
|
+
def ensure_paths
|
339
|
+
return if @path_checked
|
340
|
+
raise IOError, "#{@path} does not exist" if !@path.exist?
|
341
|
+
raise IOError, "#{@path} is not dir" if !@path.directory?
|
342
|
+
raise IOError, "#{@gfx_path} does not exist" if !@gfx_path.exist?
|
343
|
+
raise IOError, "#{@gfx_path} is not dir" if !@gfx_path.directory?
|
344
|
+
@path_checked = true
|
345
|
+
end
|
346
|
+
|
347
|
+
def ensure_levels
|
348
|
+
ensure_paths
|
349
|
+
|
350
|
+
return if @levels
|
351
|
+
|
352
|
+
debug("Levels: finding all from #{path}")
|
353
|
+
@levels = {}
|
354
|
+
@path.children.select {|f| f.directory?}.each do |path_level|
|
355
|
+
id = path_level.basename
|
356
|
+
level = Level.new(id, { :parent => self })
|
357
|
+
add(level)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
end # Levels
|
362
|
+
end # ReVolt
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'logger'
|
2
|
+
require 'revolt/config'
|
3
|
+
|
4
|
+
module ReVolt
|
5
|
+
module Logger
|
6
|
+
# if Config::LOGGING
|
7
|
+
# puts "Initializing logging"
|
8
|
+
#end
|
9
|
+
@@log = nil
|
10
|
+
module_function
|
11
|
+
def enable
|
12
|
+
@@log = ::Logger.new($stderr) unless @@log
|
13
|
+
end
|
14
|
+
def disable
|
15
|
+
@@log = nil
|
16
|
+
end
|
17
|
+
|
18
|
+
def debug(*a)
|
19
|
+
@@log.debug(*a) if @@log
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|