peekbot 0.0.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/dispatch.fcgi +15 -0
- data/m.bat +3 -0
- data/make_all_images.rb +51 -0
- data/peekbot.rb +599 -0
- data/peekbot_helper.rb +153 -0
- data/static/changes.txt +56 -0
- data/static/peekbot.png +0 -0
- data/static/style.css +67 -0
- metadata +92 -0
data/dispatch.fcgi
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
require 'rubygems'
|
3
|
+
require 'camping'
|
4
|
+
require 'camping/fastcgi'
|
5
|
+
require 'peekbot'
|
6
|
+
|
7
|
+
Camping::FastCGI.serve("peekbot.rb")
|
8
|
+
|
9
|
+
FCGI.each do |req|
|
10
|
+
# req.out << "Content-Type: text/html\r\n\r\nHello, World!<br>\n"
|
11
|
+
# req.env.keys.sort.each {|k| req.out << "#{k}=#{req.env[k]}<br>\n"}
|
12
|
+
camp_do(req)
|
13
|
+
# req.finish
|
14
|
+
end
|
15
|
+
|
data/m.bat
ADDED
data/make_all_images.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
#!/usr/local/bin/ruby
|
2
|
+
lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
|
3
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
4
|
+
|
5
|
+
lib_path=File.expand_path(File.dirname(__FILE__)+"//..//..//ripxplore//lib")
|
6
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
7
|
+
|
8
|
+
require 'RipXplore'
|
9
|
+
require 'FileCache'
|
10
|
+
PEEKBOT_URL_CACHE_DIR=File.dirname(__FILE__)+'/.image_cache'
|
11
|
+
NATIVE_FILE_CACHE_PATH='/static/native_files'
|
12
|
+
PEEKBOT_NATIVE_FILE_CACHE_DIR=File.dirname(__FILE__)+NATIVE_FILE_CACHE_PATH
|
13
|
+
|
14
|
+
LAST_PEEKBOT_UPDATE=File.stat(__FILE__).mtime
|
15
|
+
|
16
|
+
filecache=FileCache.new(PEEKBOT_URL_CACHE_DIR,PEEKBOT_NATIVE_FILE_CACHE_DIR)
|
17
|
+
filecache.make_file_system_image_list
|
18
|
+
all_image_urls=filecache.file_system_image_urls_in_cache
|
19
|
+
all_image_urls.reverse.each do |url|
|
20
|
+
puts "dumping #{url} at #{Time.now.to_s}"
|
21
|
+
begin
|
22
|
+
thread1=Thread.new do
|
23
|
+
image=filecache.get_file_system_image(url)
|
24
|
+
puts "got image"
|
25
|
+
image.pictures.each do |picture|
|
26
|
+
local_filename=filecache.force_native_file_to_cache(picture,:to_picture,picture.picture_format.to_s)
|
27
|
+
puts "made picture #{local_filename}"
|
28
|
+
end
|
29
|
+
image.texts.each do |text|
|
30
|
+
if text.respond_to?(:to_screendump) then
|
31
|
+
local_filename=filecache.force_native_file_to_cache(text,:to_screendump,"png")
|
32
|
+
puts "made screendump #{local_filename}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
60.times do
|
38
|
+
break unless thread1.alive?
|
39
|
+
sleep 1
|
40
|
+
end
|
41
|
+
if thread1.alive? then
|
42
|
+
puts "killing thread"
|
43
|
+
thread1.kill
|
44
|
+
end
|
45
|
+
rescue
|
46
|
+
puts $!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
|
data/peekbot.rb
ADDED
@@ -0,0 +1,599 @@
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'camping'
|
6
|
+
|
7
|
+
Markaby::Builder.set(:indent, 2)
|
8
|
+
|
9
|
+
lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
|
10
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
11
|
+
|
12
|
+
lib_path=File.expand_path(File.dirname(__FILE__)+"//..//..//ripxplore//lib")
|
13
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
14
|
+
|
15
|
+
require 'peekbot_helper'
|
16
|
+
|
17
|
+
require 'RipXplore'
|
18
|
+
|
19
|
+
PEEKBOT_URL_CACHE_DIR=File.dirname(__FILE__)+'/.image_cache'
|
20
|
+
NATIVE_FILE_CACHE_PATH='/static/native_files'
|
21
|
+
PEEKBOT_NATIVE_FILE_CACHE_DIR=File.dirname(__FILE__)+NATIVE_FILE_CACHE_PATH
|
22
|
+
|
23
|
+
LAST_PEEKBOT_UPDATE=File.stat(__FILE__).mtime
|
24
|
+
|
25
|
+
require 'FileCache'
|
26
|
+
@@filecache=FileCache.new(PEEKBOT_URL_CACHE_DIR,PEEKBOT_NATIVE_FILE_CACHE_DIR)
|
27
|
+
|
28
|
+
|
29
|
+
Camping.goes :PeekBot
|
30
|
+
|
31
|
+
@@images={}
|
32
|
+
def get_image(url)
|
33
|
+
if @@images[url].nil? then
|
34
|
+
|
35
|
+
@@images[url]=@@filecache.get_file_system_image(url)
|
36
|
+
end
|
37
|
+
@@images[url]
|
38
|
+
end
|
39
|
+
|
40
|
+
def local_mode?
|
41
|
+
remote_host=@env["REMOTE_ADDR"]
|
42
|
+
is_localhost=(remote_host=~/127\.0\.0\.1/)
|
43
|
+
is_localhost
|
44
|
+
end
|
45
|
+
def path_for_cached_native_file(native_file,method_to_cache,extension)
|
46
|
+
local_filename=@@filecache.force_native_file_to_cache(native_file,method_to_cache,extension)
|
47
|
+
local_filename.sub(PEEKBOT_NATIVE_FILE_CACHE_DIR,NATIVE_FILE_CACHE_PATH)
|
48
|
+
end
|
49
|
+
|
50
|
+
module PeekBot::Controllers
|
51
|
+
|
52
|
+
|
53
|
+
# The root slash shows the `index' view.
|
54
|
+
class Index < R '/'
|
55
|
+
def get
|
56
|
+
if local_mode? then
|
57
|
+
redirect R(NavigateArchive,"file://#{Dir.getwd}")
|
58
|
+
else
|
59
|
+
render :index
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
class HelpWanted < R '/help_wanted/'
|
65
|
+
def get
|
66
|
+
render :help_wanted
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class NavigateArchive < R '/navigate_archive/(.+)'
|
71
|
+
def get(archive_url)
|
72
|
+
@working_url=WorkingUrl.new(archive_url)
|
73
|
+
@images,@directories,@other_files=PeekBotHelper.get_archive_contents(@working_url.archive_url)
|
74
|
+
render :navigate_archive
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
class Static < R '/static/(.+)'
|
79
|
+
MIME_TYPES = {'.css' => 'text/css', '.js' => 'text/javascript', '.jpg' => 'image/jpeg,','.png' => 'image/png','.gif' => 'image/gif'}
|
80
|
+
PATH = File.expand_path(File.dirname(__FILE__))
|
81
|
+
def get(path)
|
82
|
+
@headers['Content-Type'] = MIME_TYPES[path[/\.\w+$/, 0]] || "text/plain"
|
83
|
+
unless path.include? ".." # prevent directory traversal attacks
|
84
|
+
@headers['X-Sendfile'] = "#{PATH}/static/#{path}"
|
85
|
+
else
|
86
|
+
@status = "403"
|
87
|
+
"403 - Invalid path"
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
|
93
|
+
class CatalogImage < R '/catalog_image/(.+)'
|
94
|
+
def get(image_url)
|
95
|
+
@working_url=WorkingUrl.split_image_url_into_parts(image_url)
|
96
|
+
@image=get_image(@working_url.image_url)
|
97
|
+
render :catalog_image
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class AllFilesOfType < R '/all_([^/]+)s/(.+)'
|
102
|
+
def get(filetype,image_url)
|
103
|
+
@working_url=WorkingUrl.split_image_url_into_parts(image_url)
|
104
|
+
@image=get_image(@working_url.image_url)
|
105
|
+
render "all_#{filetype}s".to_sym
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class NativeFile < R '/native_file/(.+)/(\d+)'
|
110
|
+
def get(image_url,native_file_index)
|
111
|
+
@display_mode=input.display_mode
|
112
|
+
@working_url=WorkingUrl.split_image_url_into_parts(image_url)
|
113
|
+
@native_file_index=native_file_index
|
114
|
+
@image=get_image(@working_url.image_url)
|
115
|
+
@native_file=@image.files[native_file_index.to_i]
|
116
|
+
render :native_file
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class SectorViewer < R '/sector_viewer/(.+)/(\d+)/(\d+)'
|
121
|
+
def get(image_url,track,sector)
|
122
|
+
@display_mode=input.display_mode
|
123
|
+
@working_url=WorkingUrl.split_image_url_into_parts(image_url)
|
124
|
+
@track=track.to_i
|
125
|
+
@sector=sector.to_i
|
126
|
+
@image=get_image(@working_url.image_url)
|
127
|
+
render :sector_viewer
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class RandomFile < R '/random_([^/]+)/'
|
132
|
+
def get(filetype)
|
133
|
+
@random_filetype=filetype
|
134
|
+
random_filetype_collection_name="#{@random_filetype}s".to_sym
|
135
|
+
@image=@@filecache.random_file_system_image(random_filetype_collection_name)
|
136
|
+
@working_url=WorkingUrl.split_image_url_into_parts(@image.filename)
|
137
|
+
random_filetype_collection=@image.send(random_filetype_collection_name)
|
138
|
+
random_index=rand(random_filetype_collection.length)
|
139
|
+
@native_file=random_filetype_collection[random_index]
|
140
|
+
render :random_file
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
# Any other page name gets sent to the view
|
145
|
+
# of the same name.
|
146
|
+
#
|
147
|
+
# /index -> Views#index
|
148
|
+
# /sample -> Views#sample
|
149
|
+
#
|
150
|
+
|
151
|
+
class Page < R '/(\w+)'
|
152
|
+
def get(page_name)
|
153
|
+
render page_name
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
module PeekBot::Views
|
161
|
+
|
162
|
+
# If you have a `layout' method like this, it
|
163
|
+
# will wrap the HTML in the other methods. The
|
164
|
+
# `self << yield' is where the HTML is inserted.
|
165
|
+
def layout
|
166
|
+
html do
|
167
|
+
head do
|
168
|
+
title 'peekbot'
|
169
|
+
link :href=>R(Static,'style.css'), :rel=>'stylesheet', :type=>'text/css'
|
170
|
+
end
|
171
|
+
body do
|
172
|
+
self << header
|
173
|
+
self << yield
|
174
|
+
self << footer
|
175
|
+
end
|
176
|
+
# footer { 'hello'}
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# The `index' view. Inside your views, you express
|
181
|
+
# the HTML in Ruby. See http://code.whytheluckystiff.net/markaby/.
|
182
|
+
def index
|
183
|
+
|
184
|
+
text "peekbot is an attempt to make vintage computing artifacts visible to the modern intarweb."
|
185
|
+
text "It is a web proxy that allows navigation through online collections of disk images. "
|
186
|
+
text "For disk images in formats which peekbot understands, the file system contained on that disk image can be explored."
|
187
|
+
br
|
188
|
+
br
|
189
|
+
text "Many of the common file types found in the file systems can be converted to a format that can be viewed in a browser."
|
190
|
+
text "For example, tokenised BASIC files can be listed as ASCII, or (some) old graphic formats converted to PNG."
|
191
|
+
text "There is also a track/sector viewer, and any file found on a disk image can be viewed as a hex dump."
|
192
|
+
br
|
193
|
+
br
|
194
|
+
text "peekbot is based on"
|
195
|
+
a 'ripxplore',:href=>'http://ripxplore.rubyforge.org/'
|
196
|
+
text ", a ruby library for identifying and extracting data from disk images."
|
197
|
+
br
|
198
|
+
br
|
199
|
+
text "To get started, you could "
|
200
|
+
a 'view a random picture',:href=>R(RandomFile,:picture)
|
201
|
+
text" or else navigate with peekbot through some online disk image archives by clicking on one of the archive descriptions below."
|
202
|
+
br
|
203
|
+
br
|
204
|
+
ul do
|
205
|
+
[
|
206
|
+
['http://cocomag.dyndns.org/swarc/',"Tim Lindner's Tandy Color Computer archive"],
|
207
|
+
['http://mirrors.apple2.org.za/ftp.apple.asimov.net/',"Asimov Apple 2 image collections mirrored at apple2.org.za"],
|
208
|
+
['http://mirrors.apple2.org.za/landover.no-ip.com/',"Landover Apple 2 image collections mirrored at apple2.org.za"],
|
209
|
+
# ['http://mirrors.apple2.org.za/',"Apple 2 image collections mirrored at apple2.org.za"],
|
210
|
+
['http://www.atariarchives.org/oldhackers/',"Old Hackers Atari User Group Disk Archive (at AtariArchives.org)"],
|
211
|
+
# ['http://www.atariarchives.org/swlibrary/',"Atari Software Library (at AtariArchives.org)"],
|
212
|
+
# ['http://www.cs.vu.nl/pub/ipoorten/atari.8bit/megazine/atr/','Atari Megazine archive'],
|
213
|
+
['ftp://ftp.whtech.com/emulators/pc99/',"TI 99/4A archive at ftp.whtech.com"],
|
214
|
+
['http://disk-images.jamtronix.com/ti99/',"Jamtronix mirror of TOSEC TI 99/4A collection"],
|
215
|
+
['http://disk-images.jamtronix.com/coco/',"Jamtronix mirror of Tandy Color Computer dsks"],
|
216
|
+
['http://www.classic-computers.org.nz/system-80/software_archive.htm',"Terry Stewart's collection of Dick Smith System 80 software"],
|
217
|
+
# ['http://disk-images.jamtronix.com/trs80/',"Jamtronix mirror of the Dutch TRS-80 usergroup collection (http://www.trs80.nl/)"],
|
218
|
+
# ['http://www.zimmers.net/anonftp/pub/cbm/c64/',"Bob Zimmerman's mirror of the FUNET C64 archive"],
|
219
|
+
# ['http://ftp.strassenbahn.tk:81/inntram-public/off-topic/c64/',"Strassenbahn.Tk C64 Archive"],
|
220
|
+
#
|
221
|
+
['http://www.zx81.nl/files.html','ZX81 archive at www.zx81.nl'],
|
222
|
+
].each do |archive_url|
|
223
|
+
li do
|
224
|
+
a archive_url[1], :href=> R(NavigateArchive,archive_url[0])
|
225
|
+
#br
|
226
|
+
#text "[#{archive_url[0]}]"
|
227
|
+
end
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
br
|
232
|
+
text "NB - peekbot does not archive any images itself - they are all downloaded into peekbot from the referenced archive on demand."
|
233
|
+
end
|
234
|
+
|
235
|
+
def nav_bar
|
236
|
+
|
237
|
+
return if @working_url.nil?
|
238
|
+
raise "security violation - only localhost can access file:// URLs" if @working_url.archive_url=~/^file:/ && ! (local_mode?)
|
239
|
+
raise 'invalid @working_url' unless @working_url.kind_of?(WorkingUrl)
|
240
|
+
|
241
|
+
if ! @working_url.archive_url.nil? then
|
242
|
+
text ' now peeking at : '
|
243
|
+
if @working_url.image_basename.nil? then
|
244
|
+
text URI.unescape(@working_url.archive_url)
|
245
|
+
a "[go direct]", :href=>@working_url.archive_url unless local_mode?
|
246
|
+
else
|
247
|
+
a @working_url.archive_url, :href=> R(NavigateArchive,@working_url.archive_url)
|
248
|
+
if @native_file.nil? then
|
249
|
+
a @working_url.image_basename, :href=> R(CatalogImage,@working_url.image_url)
|
250
|
+
else
|
251
|
+
a @working_url.image_basename, :href=> R(CatalogImage,@working_url.image_url)
|
252
|
+
text ' / '
|
253
|
+
text @native_file.filename
|
254
|
+
end
|
255
|
+
br
|
256
|
+
a "[download #{File.basename(@working_url.image_url)}]", :href=>@working_url.image_url unless local_mode?
|
257
|
+
end
|
258
|
+
end
|
259
|
+
end
|
260
|
+
def navigate_archive
|
261
|
+
|
262
|
+
table {
|
263
|
+
tr.header { td { text "Navigation Options"} }
|
264
|
+
@directories.each do |dirname|
|
265
|
+
tr.directory {
|
266
|
+
td {a URI.unescape(dirname), :href=> R(NavigateArchive,PeekBotHelper.combine_url(@working_url.archive_url,dirname).to_s)}
|
267
|
+
td {}
|
268
|
+
}
|
269
|
+
end
|
270
|
+
|
271
|
+
tr.header { td { text "Peekable Images"} }
|
272
|
+
@images.each do |filename|
|
273
|
+
tr.image {
|
274
|
+
td {
|
275
|
+
a 'explore', :href=> R(CatalogImage,PeekBotHelper.combine_url(@working_url.archive_url,filename))
|
276
|
+
if (! local_mode?) then
|
277
|
+
text ' | '
|
278
|
+
begin
|
279
|
+
a 'download', :href=> PeekBotHelper.combine_url(@working_url.archive_url,filename).to_s
|
280
|
+
rescue #nasty kludge but for the moment, just skip over filenames that URI doesn't like
|
281
|
+
end
|
282
|
+
end
|
283
|
+
}
|
284
|
+
td {text URI.unescape(filename) }
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
if @other_files.length>0 then
|
289
|
+
tr.header { td { text "Other Files"} }
|
290
|
+
@other_files.each do |filename|
|
291
|
+
tr.other_file {
|
292
|
+
td {
|
293
|
+
begin
|
294
|
+
a 'download', :href=> PeekBotHelper.combine_url(@working_url.archive_url,filename).to_s
|
295
|
+
rescue #nasty kludge but for the moment, just skip over filenames that URI doesn't like
|
296
|
+
end
|
297
|
+
}
|
298
|
+
td {text URI.unescape(filename)}
|
299
|
+
}
|
300
|
+
end
|
301
|
+
end
|
302
|
+
}
|
303
|
+
end
|
304
|
+
|
305
|
+
def native_file
|
306
|
+
h3 @native_file.filename
|
307
|
+
case @display_mode
|
308
|
+
when 'hex' then
|
309
|
+
pre.hex_dump @native_file.to_hex_dump
|
310
|
+
when 'to_picture', 'to_png' then
|
311
|
+
scale =600/@native_file.picture_width
|
312
|
+
scale=1 if scale<1
|
313
|
+
img :src=>path_for_cached_native_file(@native_file,:to_picture,@native_file.picture_format.to_s),:width=>@native_file.picture_width*scale,:height=>@native_file.picture_height*scale
|
314
|
+
when 'to_screendump' then
|
315
|
+
#~ scale =800/@native_file.screendump_width
|
316
|
+
#~ scale=1 if scale<1
|
317
|
+
#~ img :src=>path_for_cached_native_file(@native_file,:to_screendump,"png"),:width=>@native_file.screendump_width*scale,:height=>@native_file.screendump_height*scale
|
318
|
+
center {
|
319
|
+
img :src=>path_for_cached_native_file(@native_file,:to_screendump,"png"),:width=>'80%'
|
320
|
+
}
|
321
|
+
else
|
322
|
+
pre.text @native_file.send(@display_mode)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def all_pictures
|
327
|
+
h2 "VIEWING ALL PICTURES"
|
328
|
+
@image.pictures.each do |picture_file|
|
329
|
+
h3 picture_file.filename
|
330
|
+
scale =600/picture_file.picture_width
|
331
|
+
scale=1 if scale<1
|
332
|
+
img :src=>path_for_cached_native_file(picture_file,:to_picture,picture_file.picture_format.to_s),:width=>picture_file.picture_width*scale,:height=>picture_file.picture_height*scale, :alt=>picture_file.filename
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
def all_texts
|
337
|
+
h2 "VIEWING ALL TEXT FILES"
|
338
|
+
@image.texts.each do |text_file|
|
339
|
+
h3 text_file.filename
|
340
|
+
pre text_file.to_text
|
341
|
+
end
|
342
|
+
end
|
343
|
+
|
344
|
+
def random_file
|
345
|
+
h2 "RANDOM #{@random_filetype.to_s.upcase}"
|
346
|
+
native_file=@native_file
|
347
|
+
text "Peekbot chose this #{@random_filetype.to_s} at random for you. It was found on a disk image that appears to have been originally for the #{@image.host_system.full_name}. You may like to look at:"
|
348
|
+
ul {
|
349
|
+
if (native_file.respond_to?(:to_screendump)) then
|
350
|
+
i=@image.files.index(native_file)
|
351
|
+
li {a "how this file would have looked on the #{@image.host_system.full_name} ", :href=> R(NativeFile,@working_url.image_url,i)+"?display_mode=to_screendump"}
|
352
|
+
end
|
353
|
+
li {a "all #{@image.files.length} files on this disk image", :href=> R(CatalogImage,@working_url.image_url)}
|
354
|
+
if @image.pictures.length>1 then
|
355
|
+
li {a "the #{@image.pictures.length} pictures on this disk image", :href=> R(AllFilesOfType,:picture,@working_url.image_url)}
|
356
|
+
end
|
357
|
+
if @image.texts.length>1 then
|
358
|
+
li {a "the #{@image.texts.length} texts on this disk image", :href=> R(AllFilesOfType,:text,@working_url.image_url)}
|
359
|
+
end
|
360
|
+
|
361
|
+
[:picture,:text,:listing].each do |random_filetype|
|
362
|
+
li {a "#{random_filetype.to_s== @random_filetype.to_s ? "another" : "a"} random #{random_filetype.to_s}", :href=> R(RandomFile,random_filetype)}
|
363
|
+
end
|
364
|
+
}
|
365
|
+
|
366
|
+
h3 native_file.filename
|
367
|
+
if random_filetype.to_s==:picture.to_s then
|
368
|
+
scale =600/native_file.picture_width
|
369
|
+
scale=1 if scale<1
|
370
|
+
img :src=>path_for_cached_native_file(native_file,:to_picture,native_file.picture_format.to_s),:width=>native_file.picture_width*scale,:height=>native_file.picture_height*scale, :alt=>native_file.filename
|
371
|
+
else
|
372
|
+
pre native_file.send("to_#{random_filetype}".to_sym)
|
373
|
+
end
|
374
|
+
end
|
375
|
+
|
376
|
+
|
377
|
+
def image_header
|
378
|
+
div.image_attributes do
|
379
|
+
h2 'image summary'
|
380
|
+
table.image_info do
|
381
|
+
[:host_system,:image_format,:file_system,:track_count,:volume_name].each do |att|
|
382
|
+
if (@image.respond_to?(att)) then
|
383
|
+
att_value=@image.send(att)
|
384
|
+
tr {
|
385
|
+
td att.to_s
|
386
|
+
td att_value
|
387
|
+
} unless (att_value.nil? || att_value==0)
|
388
|
+
end
|
389
|
+
end
|
390
|
+
if @image.pictures.length>0 then
|
391
|
+
tr {
|
392
|
+
td "pictures"
|
393
|
+
td {
|
394
|
+
text @image.pictures.length
|
395
|
+
a "VIEW ALL", :href=> R(AllFilesOfType,:picture,@working_url.image_url)
|
396
|
+
}
|
397
|
+
}
|
398
|
+
end
|
399
|
+
if @image.texts.length>0 then
|
400
|
+
tr {
|
401
|
+
td "text files"
|
402
|
+
td {
|
403
|
+
text @image.texts.length
|
404
|
+
a "VIEW ALL", :href=> R(AllFilesOfType,:text,@working_url.image_url)
|
405
|
+
}
|
406
|
+
}
|
407
|
+
|
408
|
+
end
|
409
|
+
|
410
|
+
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
|
415
|
+
def track_list
|
416
|
+
div.track_list do
|
417
|
+
h3 'tracks'
|
418
|
+
@image.track_list.each do |track|
|
419
|
+
a "[%02X]" % track,:href=>R(SectorViewer,@working_url.image_url,track,@image.sectors_in_track(track)[0])+"\#sector_viewer",:rel=>"nofollow"
|
420
|
+
text " "
|
421
|
+
end
|
422
|
+
end unless @image.track_list.length==0
|
423
|
+
end
|
424
|
+
|
425
|
+
def sector_viewer
|
426
|
+
image_header
|
427
|
+
div.sector_viewer do
|
428
|
+
h2 "sector viewer"
|
429
|
+
h3 {a "sectors in track $#{"%02x" % track}",:name=>"sector_viewer"}
|
430
|
+
@image.sectors_in_track(track).each do |sector|
|
431
|
+
a "[%02X]" % sector,:href=>R(SectorViewer,@working_url.image_url,track,sector)+"\#sector_viewer",:rel=>"nofollow"
|
432
|
+
text " "
|
433
|
+
end
|
434
|
+
pre @image.dump_sector(track,sector)
|
435
|
+
|
436
|
+
end
|
437
|
+
|
438
|
+
track_list
|
439
|
+
end
|
440
|
+
def catalog_image
|
441
|
+
|
442
|
+
image_header
|
443
|
+
|
444
|
+
div.image_catalog do
|
445
|
+
h2 'all files'
|
446
|
+
|
447
|
+
table.image_catalog {
|
448
|
+
tr {
|
449
|
+
th 'View As'
|
450
|
+
th 'Filename'
|
451
|
+
th 'Type'
|
452
|
+
th 'Length (bytes)'
|
453
|
+
th 'Load Address'
|
454
|
+
}
|
455
|
+
|
456
|
+
@image.files.length.times do |i|
|
457
|
+
file=@image.files[i]
|
458
|
+
tr {
|
459
|
+
|
460
|
+
td {
|
461
|
+
a "HEX",:href=>R(NativeFile,@working_url.image_url,i)+"?display_mode=hex",:rel=>"nofollow"
|
462
|
+
[:to_listing,:to_text,:to_picture,:to_screendump].each do |display_mode|
|
463
|
+
if file.respond_to?(display_mode) then
|
464
|
+
text " | "
|
465
|
+
description=display_mode.to_s.sub("to_","").upcase
|
466
|
+
a description,:href=>R(NativeFile,@working_url.image_url,i)+"?display_mode=#{display_mode.to_s}"
|
467
|
+
end
|
468
|
+
end
|
469
|
+
}
|
470
|
+
td file.filename
|
471
|
+
td file.type_description
|
472
|
+
td "$%04X" % file.contents.length
|
473
|
+
td "$%04X" % file.load_address
|
474
|
+
}
|
475
|
+
end
|
476
|
+
}
|
477
|
+
end unless @image.files.length==0
|
478
|
+
track_list
|
479
|
+
end
|
480
|
+
|
481
|
+
def header
|
482
|
+
h1.header do
|
483
|
+
a :href=>'/' do
|
484
|
+
img :src=>'/static/peekbot.png', :height=>130, :width=>229
|
485
|
+
end
|
486
|
+
end
|
487
|
+
nav_bar
|
488
|
+
end
|
489
|
+
|
490
|
+
def footer
|
491
|
+
p.footer do
|
492
|
+
text 'peekbot '
|
493
|
+
a "version #{RipXplore::VERSION}" , :href=>'/version_info'
|
494
|
+
text ' powered by '
|
495
|
+
a 'ripxplore',:href=>'http://ripxplore.rubyforge.org/'
|
496
|
+
text ', '
|
497
|
+
a 'camping',:href=>'http://code.whytheluckystiff.net/camping/'
|
498
|
+
text ' & '
|
499
|
+
a 'ruby',:href=>'http://www.ruby-lang.org/'
|
500
|
+
i {
|
501
|
+
text "(last updated:#{LAST_PEEKBOT_UPDATE} : "
|
502
|
+
a 'change log',:href=>'/static/changes.txt'
|
503
|
+
text ")"
|
504
|
+
}
|
505
|
+
br
|
506
|
+
text 'whipped up by '
|
507
|
+
a 'jonno',:href=>'http://blog.jamtronix.com/'
|
508
|
+
text ' at '
|
509
|
+
a 'jamtronix dot com',:href=>'http://www.jamtronix.com/'
|
510
|
+
br
|
511
|
+
br
|
512
|
+
text "want to"
|
513
|
+
a "help build peekbot?" , :href=>'/help_wanted/'
|
514
|
+
|
515
|
+
end
|
516
|
+
end
|
517
|
+
|
518
|
+
|
519
|
+
def version_info
|
520
|
+
table {
|
521
|
+
tr {
|
522
|
+
td 'RipXplore Version'
|
523
|
+
td RipXplore::VERSION
|
524
|
+
}
|
525
|
+
tr {
|
526
|
+
td 'Image Format File Extensions'
|
527
|
+
td ImageFormat.all_possible_extensions.join(", ")
|
528
|
+
}
|
529
|
+
|
530
|
+
tr {
|
531
|
+
td 'File Systems'
|
532
|
+
td FileSystem.all_file_systems.join(", ")
|
533
|
+
}
|
534
|
+
|
535
|
+
tr {
|
536
|
+
td 'Native File Types'
|
537
|
+
td NativeFileType.all_native_file_types.join(", ")
|
538
|
+
}
|
539
|
+
}
|
540
|
+
end
|
541
|
+
|
542
|
+
def help_wanted
|
543
|
+
h1 "help wanted!"
|
544
|
+
text "peekbot is very much a work in progress. I am putting this version out
|
545
|
+
now as a 'proof of concept', and to see if anyone finds it intersting
|
546
|
+
enough to help work on it. "
|
547
|
+
br
|
548
|
+
br
|
549
|
+
text "Areas where assistance would be most valuable:"
|
550
|
+
br
|
551
|
+
br
|
552
|
+
text" 1) web design. peekbot uses camping, with markaby + css for layout, so
|
553
|
+
hopefully someone with css chops could make it look much slicker
|
554
|
+
without too much effort. I'm sure the UI and navigation could be much
|
555
|
+
improved as well - I am a humble backend hack so would be very happy
|
556
|
+
to work with anyone who knew how to make a web app easy to use. "
|
557
|
+
br
|
558
|
+
br
|
559
|
+
text"2) plug-ins for image formats (especially .sdk), file systems, and
|
560
|
+
native file types. My goal for ripxplore is to have everything in 100%
|
561
|
+
native ruby for maximum portability, and the framework is convoluted
|
562
|
+
and not yet well documented, so it's probably a bit early for anyone
|
563
|
+
but a masochist ruby freak to try writing a plugin unaided, but I
|
564
|
+
would be happy to work with anyone interested, or else if someone has
|
565
|
+
some images they'd particularly like to see peekbot interpret, and can
|
566
|
+
give specs or source code i can convert, that would be great too.
|
567
|
+
There is a heap of opporunity for the C64 particularly, including
|
568
|
+
converters to detect various image formats and render as PNG."
|
569
|
+
br
|
570
|
+
br
|
571
|
+
text"3) documentation - how to use the website, how to use ripxplore for
|
572
|
+
specialised conversion and extraction tasks & how to write a plugin."
|
573
|
+
br
|
574
|
+
br
|
575
|
+
text"If you are interested in any of the above, contact me vie email at jonno at jamtronix dot com"
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
if __FILE__ == $0 then
|
580
|
+
#run from command line
|
581
|
+
port=6502
|
582
|
+
require 'mongrel'
|
583
|
+
require 'mongrel/camping'
|
584
|
+
begin
|
585
|
+
config = Mongrel::Configurator.new do
|
586
|
+
listener :port => port do
|
587
|
+
uri "/", :handler => Mongrel::Camping::CampingHandler.new(PeekBot)
|
588
|
+
trap("INT") { stop }
|
589
|
+
run
|
590
|
+
end
|
591
|
+
end
|
592
|
+
puts "** peekbot is running at http://localhost:#{port}/"
|
593
|
+
config.join
|
594
|
+
rescue Errno::EADDRINUSE
|
595
|
+
puts "** ERROR : port #{port} in use - is peekbot already running?"
|
596
|
+
end
|
597
|
+
|
598
|
+
end
|
599
|
+
|
data/peekbot_helper.rb
ADDED
@@ -0,0 +1,153 @@
|
|
1
|
+
|
2
|
+
lib_path=File.expand_path(File.dirname(__FILE__)+"//..//ripxplore//lib")
|
3
|
+
$:.unshift(lib_path) unless $:.include?(lib_path)
|
4
|
+
|
5
|
+
require 'RipXplore'
|
6
|
+
require 'hpricot'
|
7
|
+
require 'open-uri'
|
8
|
+
|
9
|
+
WEB_PAGE_EXTENSIONS=['.html','.htm','.php','.asp','.aspx']
|
10
|
+
class PeekBotHelper
|
11
|
+
def PeekBotHelper.get_archive_contents(archive_url)
|
12
|
+
|
13
|
+
images=[]
|
14
|
+
directories=[]
|
15
|
+
other_files=[]
|
16
|
+
if (url_is_website?(archive_url)) then
|
17
|
+
path=URI.split(archive_url)[5]
|
18
|
+
directories<<'../' unless (path=="" || path=='/')
|
19
|
+
html=get_url(archive_url)
|
20
|
+
doc=Hpricot(html)
|
21
|
+
doc.search("a[@href]").each do |a|
|
22
|
+
href=a.attributes["href"]
|
23
|
+
href.sub!("/index.html","/")
|
24
|
+
next if href=~/:/ #skip anything that has a colon - we only want relative URLs
|
25
|
+
next if href=~/#/ #skip anything that has a hash - we don't want anchored links
|
26
|
+
next if href=~/\?/ #skip anything thats a query
|
27
|
+
next if href=~/^\.+$/ #skip . and ..
|
28
|
+
if (href=~/\w\/$/) || WEB_PAGE_EXTENSIONS.include?(File.extname(href))
|
29
|
+
# if !(href=~/^\//) then #directories end with a /, but skip absolute paths
|
30
|
+
|
31
|
+
directories<<(href)
|
32
|
+
# end
|
33
|
+
else
|
34
|
+
if FileSystemImage.is_file_system_image_filename?(href) then
|
35
|
+
images<<href
|
36
|
+
else
|
37
|
+
other_files<<href unless href=~/\/$/
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
elsif (url_is_ftpsite?(archive_url)) then
|
42
|
+
|
43
|
+
require 'net/ftp'
|
44
|
+
require 'uri'
|
45
|
+
ftp_uri=URI.parse(archive_url)
|
46
|
+
partial_path="/"
|
47
|
+
ftp_uri.path.split("/").each do |p|
|
48
|
+
next if p.length<1
|
49
|
+
directories<<partial_path
|
50
|
+
partial_path+=(p+"/")
|
51
|
+
end
|
52
|
+
ftp=Net::FTP.new(ftp_uri.host)
|
53
|
+
ftp.login(ftp_uri.user.nil? ? 'anonymous': ftp_uri.user,ftp_uri.password)
|
54
|
+
ftp.nlst(URI.unescape(ftp_uri.path)).each do |f|
|
55
|
+
#yes this is a horrible hack - assume that filenames with a . are files, if no dot then assume it's a directory
|
56
|
+
if f=~/\./ then
|
57
|
+
if FileSystemImage.is_file_system_image_filename?(f) then
|
58
|
+
images<<f
|
59
|
+
else
|
60
|
+
other_files<<f
|
61
|
+
end
|
62
|
+
else
|
63
|
+
f="/"+f unless f=~/\// #if we are at the root directory, make sure all paths start with a /
|
64
|
+
directories<<f
|
65
|
+
end
|
66
|
+
end
|
67
|
+
ftp.quit
|
68
|
+
else #assume it's a local file
|
69
|
+
dirname=PeekBotHelper.file_url_to_pathname(archive_url)
|
70
|
+
dirname+=File::Separator unless dirname=~/#{File::Separator}$/
|
71
|
+
# "stripped #{archive_url} to #{dirname}"
|
72
|
+
Dir.foreach(dirname) do |filename|
|
73
|
+
next if filename=="."
|
74
|
+
images<<filename if FileSystemImage.is_file_system_image_filename?(filename)
|
75
|
+
full_dir_path=file_url_to_pathname(combine_url("#{dirname}",filename))
|
76
|
+
directories<<filename if FileTest.directory?(full_dir_path)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
[images.sort,directories.sort,other_files.sort]
|
80
|
+
end
|
81
|
+
|
82
|
+
@@url_cache={}
|
83
|
+
def PeekBotHelper.get_url(url)
|
84
|
+
if @@url_cache[url].nil? then
|
85
|
+
@@url_cache[url]=open(url).read
|
86
|
+
end
|
87
|
+
@@url_cache[url]
|
88
|
+
end
|
89
|
+
|
90
|
+
def PeekBotHelper.url_is_website?(url)
|
91
|
+
url=~/^http[s]?:/ ?true : false
|
92
|
+
end
|
93
|
+
|
94
|
+
def PeekBotHelper.url_is_ftpsite?(url)
|
95
|
+
url=~/^ftp?:/ ?true : false
|
96
|
+
end
|
97
|
+
|
98
|
+
def PeekBotHelper.url_is_local_file?(url)
|
99
|
+
return true if (url=~/^file:/ ) || (url=~/^[^\/]*\\/ ) || (url[1].chr==':') || (url[0].chr==File::Separator)
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
def PeekBotHelper.file_url_to_pathname(url)
|
104
|
+
pathname=URI.unescape(url.sub(/file:\/+/,""))
|
105
|
+
# puts pathname
|
106
|
+
# puts pathname[1].chr
|
107
|
+
pathname="#{File::Separator}#{pathname}" unless ((pathname[1].chr==':') || (pathname[0].chr==File::Separator))
|
108
|
+
pathname
|
109
|
+
end
|
110
|
+
def PeekBotHelper.combine_url(first_url,second_url)
|
111
|
+
|
112
|
+
# URI::FTP.join seems broken (complains about missing typcode) so here's a horrible hacky replacement
|
113
|
+
# puts "joining #{first_url},#{second_url}"
|
114
|
+
if url_is_ftpsite?(first_url) then
|
115
|
+
ftp_uri=URI.parse(first_url)
|
116
|
+
result=URI::FTP.new('ftp',
|
117
|
+
"#{ftp_uri.user}:#{ftp_uri.password}",
|
118
|
+
ftp_uri.host, ftp_uri.port, nil,
|
119
|
+
second_url,
|
120
|
+
nil, nil,nil).to_s.sub("//:@","//")
|
121
|
+
elsif url_is_local_file?(first_url) then
|
122
|
+
dir_path=PeekBotHelper.file_url_to_pathname(first_url)
|
123
|
+
result="file://#{File.expand_path(second_url,dir_path)}"
|
124
|
+
else
|
125
|
+
result=URI.join(first_url,second_url)
|
126
|
+
end
|
127
|
+
# puts "RESULT: #{result}"
|
128
|
+
result
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
class WorkingUrl
|
133
|
+
attr_accessor :archive_url,:image_url
|
134
|
+
def initialize(archive_url=nil,image_url=nil)
|
135
|
+
#when running under Apache + mod_rewrite, the second / in http:// is getting stripped, so add it back in if necessary
|
136
|
+
@archive_url=archive_url.sub(/:\/([^\/])/,'://\1').gsub(' ','%20').gsub('#','%23').gsub('[','%5B').gsub(']','%5D') unless archive_url.nil?
|
137
|
+
@image_url=image_url.sub(/:\/([^\/])/,'://\1').gsub(' ','%20').gsub('#','%23').gsub('[','%5B').gsub(']','%5D') unless image_url.nil?
|
138
|
+
end
|
139
|
+
|
140
|
+
def image_basename
|
141
|
+
if image_url.nil? then
|
142
|
+
nil
|
143
|
+
else
|
144
|
+
URI.unescape(File.basename(image_url))
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
def WorkingUrl.split_image_url_into_parts(url)
|
150
|
+
return WorkingUrl.new(File.dirname(url)+"/",url)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
data/static/changes.txt
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PEEKBOT CHANGE LOG
|
2
|
+
|
3
|
+
2008-12-14
|
4
|
+
* initial support for ZX81
|
5
|
+
|
6
|
+
2008-12-13
|
7
|
+
* low-res Coco pictures
|
8
|
+
|
9
|
+
2008-12-07
|
10
|
+
* detokenise TI 99/4a BASIC files
|
11
|
+
|
12
|
+
2008-12-05
|
13
|
+
* eliminate timeout (500 camping error) the first time a random picture/text/listing was viewed when app restarted
|
14
|
+
|
15
|
+
2008-11-30
|
16
|
+
* added 'screendump' feature
|
17
|
+
|
18
|
+
2008-11-29
|
19
|
+
* now caches PNG & GIF files (rather than re-extracting from image on each view)
|
20
|
+
* added random text and listing feature
|
21
|
+
|
22
|
+
2008-11-28
|
23
|
+
* added RLE support
|
24
|
+
|
25
|
+
2008-11-25
|
26
|
+
* added GIF support
|
27
|
+
|
28
|
+
2008-11-25
|
29
|
+
* 'All Pictures' & 'All Texts' views
|
30
|
+
* Random Picture page
|
31
|
+
|
32
|
+
2008-11-24
|
33
|
+
* Apple 2 hires picture improvements
|
34
|
+
|
35
|
+
2008-11-21
|
36
|
+
* added support for Tandy Color Computer files (Tim Lindner)
|
37
|
+
|
38
|
+
2008-11-15
|
39
|
+
* fixed some TI 99/4a file system bugs
|
40
|
+
* added support for TI Artist images
|
41
|
+
|
42
|
+
|
43
|
+
2008-11-14
|
44
|
+
* added 'help wanted' page
|
45
|
+
* added support for FTP archives
|
46
|
+
* added link to TI 99/4a archive at ftp.whtech.com
|
47
|
+
* partial support for TI 99/4a formats
|
48
|
+
|
49
|
+
2008-11-13
|
50
|
+
* removed listing (disassembly) of binary files
|
51
|
+
* removed links to Apple 2 archives that aren't mainly DSK files
|
52
|
+
* made peekbot logo in page header clickable
|
53
|
+
* links to hex dumps now marked 'rel=nofollow' (no point getting a hex dump into google)
|
54
|
+
|
55
|
+
2008-11-09
|
56
|
+
* first public version
|
data/static/peekbot.png
ADDED
Binary file
|
data/static/style.css
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
body { background-color: #cde; color: #333; }
|
2
|
+
|
3
|
+
body, p, ol, ul, td, th {
|
4
|
+
font-family: verdana, arial, helvetica, sans-serif;
|
5
|
+
font-size: 22px;
|
6
|
+
line-height: 28px;
|
7
|
+
}
|
8
|
+
|
9
|
+
pre {
|
10
|
+
background-color: #eee;
|
11
|
+
padding: 10px;
|
12
|
+
font-size: 16px;
|
13
|
+
white-space: pre-wrap; /* css-3 */
|
14
|
+
white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */
|
15
|
+
white-space: -pre-wrap; /* Opera 4-6 */
|
16
|
+
white-space: -o-pre-wrap; /* Opera 7 */
|
17
|
+
word-wrap: break-word; /* Internet Explorer 5.5+ */
|
18
|
+
|
19
|
+
}
|
20
|
+
|
21
|
+
|
22
|
+
td,th {
|
23
|
+
padding-left: 10px;
|
24
|
+
padding-right: 10px;
|
25
|
+
}
|
26
|
+
|
27
|
+
tr.header {
|
28
|
+
background-color: #aaf;
|
29
|
+
}
|
30
|
+
pre.hex_dump {
|
31
|
+
background-color: #aaf;
|
32
|
+
}
|
33
|
+
|
34
|
+
p.footer {
|
35
|
+
font-size: 12px;
|
36
|
+
line-height: 16px;
|
37
|
+
}
|
38
|
+
|
39
|
+
h2 {
|
40
|
+
background-color: #bcd;
|
41
|
+
}
|
42
|
+
|
43
|
+
h3 {
|
44
|
+
background-color: #bcd;
|
45
|
+
}
|
46
|
+
|
47
|
+
img {
|
48
|
+
border: 0px;
|
49
|
+
}
|
50
|
+
|
51
|
+
li.directory {
|
52
|
+
background-color: #89a;
|
53
|
+
}
|
54
|
+
|
55
|
+
li.image {
|
56
|
+
background-color: #9ab;
|
57
|
+
}
|
58
|
+
|
59
|
+
li.other_file {
|
60
|
+
background-color: #abc;
|
61
|
+
}
|
62
|
+
|
63
|
+
a { color: #000; }
|
64
|
+
a:visited { color: #666; }
|
65
|
+
a:hover { color: #fff; background-color:#000; }
|
66
|
+
|
67
|
+
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: peekbot
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2008-12-20 00:00:00 +11:00
|
8
|
+
summary: a web app that allows browsing of disk images as used by vintage computer emulators
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: jonno@jamtronix.com
|
12
|
+
homepage: http://peekbot.rubyforge.org
|
13
|
+
rubyforge_project:
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: .
|
18
|
+
has_rdoc: false
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Jonno Downes
|
31
|
+
files:
|
32
|
+
- ./dispatch.fcgi
|
33
|
+
- ./m.bat
|
34
|
+
- ./make_all_images.rb
|
35
|
+
- ./peekbot.rb
|
36
|
+
- ./peekbot_helper.rb
|
37
|
+
- ./static
|
38
|
+
- ./static/changes.txt
|
39
|
+
- ./static/peekbot.png
|
40
|
+
- ./static/style.css
|
41
|
+
- static/changes.txt
|
42
|
+
- static/peekbot.png
|
43
|
+
- static/style.css
|
44
|
+
test_files: []
|
45
|
+
|
46
|
+
rdoc_options: []
|
47
|
+
|
48
|
+
extra_rdoc_files: []
|
49
|
+
|
50
|
+
executables:
|
51
|
+
- peekbot.rb
|
52
|
+
extensions: []
|
53
|
+
|
54
|
+
requirements: []
|
55
|
+
|
56
|
+
dependencies:
|
57
|
+
- !ruby/object:Gem::Dependency
|
58
|
+
name: hpricot
|
59
|
+
version_requirement:
|
60
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
61
|
+
requirements:
|
62
|
+
- - ">"
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: 0.0.0
|
65
|
+
version:
|
66
|
+
- !ruby/object:Gem::Dependency
|
67
|
+
name: ripxplore
|
68
|
+
version_requirement:
|
69
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: 0.0.3
|
74
|
+
version:
|
75
|
+
- !ruby/object:Gem::Dependency
|
76
|
+
name: camping
|
77
|
+
version_requirement:
|
78
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.0.0
|
83
|
+
version:
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: mongrel
|
86
|
+
version_requirement:
|
87
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
88
|
+
requirements:
|
89
|
+
- - ">="
|
90
|
+
- !ruby/object:Gem::Version
|
91
|
+
version: 0.3.10
|
92
|
+
version:
|