heel 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,4 +1,14 @@
1
1
  = Changelog
2
+ == Version 0.5.0 - 2007-11-19
3
+
4
+ * Add in code highlighting support via coderay
5
+ * extract CSS files to a resource
6
+ * increase test coverage
7
+
8
+ == Version 0.4.1 - 2007-11-15
9
+
10
+ * Fix bug [#15645] - not starting up on windows
11
+
2
12
  == Version 0.4.0 - 2007-11-04
3
13
 
4
14
  * Added common log format output to terminal or logfile as appropriate.
data/README CHANGED
@@ -10,19 +10,51 @@ Heel is a small static file web server based on {mongrel}[http://mongrel.rubyfor
10
10
  for use when you need a quick web server for a directory. Once the server is running heel will use
11
11
  {launchy}[http://copiousfreetime.rubyforge.org/launchy/] to open your browser at the URL of your document root.
12
12
 
13
+ % heel
14
+ ** Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart).
15
+ ** heel running at http://127.0.0.1:4331 with document root /Users/jeremy/Projects/heel
16
+ ** Use Ctrl-C to stop.
17
+ ** Launching your browser...
18
+ 127.0.0.1 - - [19/Nov/2007:23:23:33 MST] "GET / HTTP/1.1" 200 8374
19
+ 127.0.0.1 - - [19/Nov/2007:23:23:33 MST] "GET /css/listing.css HTTP/1.1" 200 984
20
+ 127.0.0.1 - - [19/Nov/2007:23:23:33 MST] "GET /icons/application.png HTTP/1.1" 200 464
21
+ 127.0.0.1 - - [19/Nov/2007:23:23:33 MST] "GET /icons/folder.png HTTP/1.1" 200 537
22
+ 127.0.0.1 - - [19/Nov/2007:23:23:33 MST] "GET /favicon.ico HTTP/1.1" 404 0
23
+
24
+ Or run it in the background
25
+
26
+ % heel --daemonize
27
+ Created /Users/jeremy/.heel
28
+ heel's PID (/Users/jeremy/.heel/heel.pid) and log file (/Users/jeremy/.heel/heel.log) are stored here
29
+
30
+ % heel --kill
31
+ Sending TERM to process 1086
32
+ Done.
33
+
34
+ == FEATURES
35
+
36
+ * Automatic launching of your browser to the URL it is serving with {launchy}[http://copiousfreetime.rubyforge.org/launchy/]
37
+ * Automatic syntax highlighting of source code files with {coderay}[http://coderay.rubychan.de/]
38
+ * Run in the foreground or daemonized
39
+ * Bind to any address and port (default is 127.0.0.1:4331)
40
+
13
41
  == SYNOPSIS:
14
42
 
15
43
  Usage: heel [options]
16
44
 
17
45
  -a, --address ADDRESS Address to bind to
18
- (default: 0.0.0.0)
46
+ (default: 127.0.0.1)
19
47
  -d, --daemonize Run daemonized in the background
20
48
  -h, --help Display this text
21
49
  -k, --kill Kill an existing daemonized heel process
50
+ --[no-]highlighting Turn on or off syntax highlighting
51
+ (default: on)
52
+ --[no-]launch-browser Turn on or off automatic browser launch
53
+ (default: on)
22
54
  -p, --port PORT Port to bind to
23
- (default: 4331)
55
+ (default: 4331)
24
56
  -r, --root ROOT Set the document root
25
- (default: current working directory)
57
+ (default: current working directory)
26
58
  -v, --version Show version
27
59
 
28
60
 
@@ -30,15 +62,16 @@ for use when you need a quick web server for a directory. Once the server is ru
30
62
 
31
63
  === For running:
32
64
 
33
- * mongrel
65
+ * {coderay}[http://coderay.rubychan.de/]
34
66
  * {launchy}[http://copiousfreetime.rubyforge.org/launchy/] >= 0.1.1
35
- * mime-types
67
+ * {mime-types}[http://mime-types.rubyforge.org/]
68
+ * {mongrel}[http://mongrel.rubyforge.org/]
69
+ * {rake}[http://rake.rubyforge.org/] >= 0.7.1
36
70
 
37
71
  === For development:
38
72
 
39
- * rake >= 0.7.1
40
- * rcov >= 0.7.0.1
41
- * rspec >= 0.7.3
73
+ * {rcov}[http://eigenclass.org/hiki.rb?rcov] >= 0.7.0.1
74
+ * {rspec}[http://rspec.rubyforge.org/] >= 0.7.3
42
75
 
43
76
  == INSTALL:
44
77
 
@@ -49,7 +82,7 @@ for use when you need a quick web server for a directory. Once the server is ru
49
82
  * Zed Shaw for {mongrel}[http://mongrel.rubyforge.org]
50
83
  * http://www.famfamfam.com/ for amazing icons
51
84
 
52
- == LICENSE:
85
+ == BSD LICENSE:
53
86
 
54
87
  Copyright (c) 2007, Jeremy Hinegardner
55
88
 
@@ -1,20 +1,25 @@
1
1
  require 'heel'
2
2
  require 'mime/types'
3
3
  require 'erb'
4
+ require 'coderay'
5
+ require 'coderay/helpers/file_type'
4
6
 
5
7
  module Heel
6
8
 
7
9
  # A refactored version of Mongrel::DirHandler using the mime-types
8
10
  # gem and a prettier directory listing.
9
11
  class DirHandler < ::Mongrel::HttpHandler
10
- attr_reader :document_root
11
- attr_reader :directory_index_html
12
- attr_reader :icon_url
13
- attr_reader :default_mime_type
14
- attr_reader :ignore_globs
15
- attr_reader :reload_template_changes
16
- attr_reader :template
17
- attr_reader :template_mtime
12
+ attr_reader :document_root
13
+ attr_reader :directory_index_html
14
+ attr_reader :icon_url
15
+ attr_reader :default_mime_type
16
+ attr_reader :highlighting
17
+ attr_reader :ignore_globs
18
+ attr_reader :reload_template_changes
19
+ attr_reader :template
20
+ attr_reader :template_mtime
21
+ attr_accessor :listener
22
+ attr_reader :request_notify
18
23
 
19
24
  # if any other mime types are needed, add them directly via the
20
25
  # mime-types calls.
@@ -53,6 +58,7 @@ module Heel
53
58
  @using_icons = options[:using_icons] || true
54
59
  @icon_url = options[:icon_url] || "/icons"
55
60
  @reload_template_changes = options[:reload_template_changes] || false
61
+ @highlighting = options[:highlighting] || false
56
62
  reload_template
57
63
 
58
64
  ADDITIONAL_MIME_TYPES.each do |mt|
@@ -99,48 +105,48 @@ module Heel
99
105
  @template_mtime ||= fstat.mtime
100
106
  if @template.nil? or fstat.mtime > @template_mtime then
101
107
  @template = ::ERB.new(File.read(fname))
102
- # log "Reloaded #{fname}"
103
108
  end
104
109
  end
105
110
 
106
111
  # determine how to respond to the request, it will either be a
107
112
  # directory listing, a file return, or an error return.
108
- def how_to_respond(req_path)
109
- if req_path.index(document_root) == 0 then
110
- begin
111
- stat = File.stat(req_path)
113
+ def how_to_respond(req_path,query_params = {})
114
+ return 403 unless req_path.index(document_root) == 0
115
+ begin
116
+ stat = File.stat(req_path)
112
117
 
113
- # if it is a directory, we either return the
114
- # directory index file, or a directory listing
115
- if stat.directory? then
116
- dir_index = File.join(req_path,directory_index_html)
117
- if File.exists?(dir_index) then
118
- return dir_index
119
- elsif directory_listing_allowed?
120
- return :directory_listing
121
- end
122
-
123
- # if it is a file and readable, make sure that the
124
- # path is a legal path
125
- elsif stat.file? and stat.readable? then
126
- if should_ignore?(File.basename(req_path)) then
127
- return 403
128
- end
129
- return req_path
130
- else
131
- # TODO: debug log
118
+ # if it is a directory, we either return the
119
+ # directory index file, or a directory listing
120
+ if stat.directory? then
121
+ dir_index = File.join(req_path,directory_index_html)
122
+ if File.exists?(dir_index) then
123
+ return dir_index
124
+ elsif directory_listing_allowed?
125
+ return :directory_listing
126
+ end
127
+
128
+ # if it is a file and readable, make sure that the
129
+ # path is a legal path
130
+ elsif stat.file? and stat.readable? then
131
+ if should_ignore?(File.basename(req_path)) then
132
132
  return 403
133
+ elsif highlighting and not %w(false off).include? query_params['highlighting'].to_s.downcase
134
+ ft = ::FileType[req_path,true]
135
+ return :highlighted_file if ft and ft != :html
133
136
  end
137
+ return req_path
138
+ else
139
+ log "ERROR: #{req_path} is not a directory or a readable file"
140
+ return 403
141
+ end
134
142
 
135
- rescue => error
136
- if error.kind_of?(Errno::ENOENT) then
137
- return 404
138
- end
139
- # TODO: debug log
140
- return 500
143
+ rescue => error
144
+ if error.kind_of?(Errno::ENOENT) then
145
+ return 404
141
146
  end
142
- else
143
- return 403
147
+ log "ERROR: Unknown, check out the stacktrace"
148
+ log error
149
+ return 500
144
150
  end
145
151
  end
146
152
 
@@ -193,45 +199,52 @@ module Heel
193
199
  return res_bytes
194
200
  end
195
201
 
196
- # this method is essentially the send_file method from
197
- # Mongrel::DirHandler
202
+
203
+ # send the file back with the appropriate mimetype
198
204
  def respond_with_send_file(path,method,request,response)
199
205
  stat = File.stat(path)
200
- mtime = stat.mtime
201
- etag = ::Mongrel::Const::ETAG_FORMAT % [mtime.to_i, stat.size, stat.ino]
202
-
203
- modified_since = request.params[::Mongrel::Const::HTTP_IF_MODIFIED_SINCE]
204
- none_match = request.params[::Mongrel::Const::HTTP_IF_NONE_MATCH]
205
-
206
- last_response_time = Time.httpddate(modified_since) rescue nil
207
-
208
- same_response = case
209
- when modified_since && !last_response_time : false
210
- when modified_since && last_response_time > Time.now : false
211
- when modified_since && mtime > last_response_time : false
212
- when none_match && none_match == "*" : false
213
- when none_match && !none_match.strip.split(/\s*,\s*/).include?(etag) : false
214
- else modified_since || none_match
215
- end
216
- header = response.header
217
- header[::Mongrel::Const::ETAG] = etag
218
-
219
- if same_response then
220
- response.start(304) {}
221
- else
222
- response.status = 200
223
- header[::Mongrel::Const::LAST_MODIFIED] = mtime.httpdate
224
- header[::Mongrel::Const::CONTENT_TYPE] = (MIME::Types.of(path).first || default_mime_type).to_s
225
- end
226
-
206
+ header = response.header
207
+
208
+ header[::Mongrel::Const::LAST_MODIFIED] = stat.mtime
209
+ header[::Mongrel::Const::CONTENT_TYPE] = (MIME::Types.of(path).first || default_mime_type).to_s
210
+
211
+ response.status = 200
227
212
  response.send_status(stat.size)
228
213
  response.send_header
229
-
214
+
230
215
  if method == ::Mongrel::Const::GET then
231
216
  response.send_file(path,stat.size < ::Mongrel::Const::CHUNK_SIZE * 2)
232
217
  end
218
+
233
219
  return stat.size
234
220
  end
221
+
222
+ #
223
+ # send back the file marked up by code ray
224
+ def respond_with_highlighted_file(path,request,response)
225
+ res_bytes = 0
226
+ response.start(200) do |head,out|
227
+ head[::Mongrel::Const::CONTENT_TYPE] = 'text/html'
228
+ bytes = CodeRay.scan_file(path,:auto).html
229
+ res_bytes = out.write(<<-EOM)
230
+ <html>
231
+ <head>
232
+ <title>#{path}</title>
233
+ <!-- CodeRay syntax highlighting CSS -->
234
+ <link rel="stylesheet" href="/css/coderay-cycnus.css" type="text/css" />
235
+ </head>
236
+ <body>
237
+ <div class="CodeRay">
238
+ <pre>
239
+ #{CodeRay.scan_file(path,:auto).html({:line_numbers => :inline})}
240
+ </pre>
241
+ </div>
242
+ </body>
243
+ </html>
244
+ EOM
245
+ end
246
+ return res_bytes
247
+ end
235
248
 
236
249
  # process the request, returning either the file, a directory
237
250
  # listing (if allowed) or an appropriate error
@@ -244,12 +257,13 @@ module Heel
244
257
  req_path = File.expand_path(File.join(@document_root,
245
258
  ::Mongrel::HttpRequest.unescape(request.params[Mongrel::Const::PATH_INFO])),
246
259
  @document_root)
247
- res_type = how_to_respond(req_path)
260
+ res_type = how_to_respond(req_path,::Mongrel::HttpRequest.query_parse(request.params['QUERY_STRING']))
248
261
  res_size = 0
249
-
250
262
  case res_type
251
263
  when :directory_listing
252
264
  res_size = respond_with_directory_listing(req_path,request,response)
265
+ when :highlighted_file
266
+ res_size = respond_with_highlighted_file(req_path,request,response)
253
267
  when String
254
268
  res_size = respond_with_send_file(res_type,method,request,response)
255
269
  when Integer
@@ -5,7 +5,10 @@ module Heel
5
5
 
6
6
  class ErrorHandler < ::Mongrel::HttpHandler
7
7
 
8
- attr_reader :template
8
+ attr_reader :template
9
+ attr_accessor :listener
10
+ attr_reader :request_notify
11
+
9
12
 
10
13
  def initialize(options = {})
11
14
  @template = ::ERB.new File.read(File.join(APP_RESOURCE_DIR,"error.rhtml"))
data/lib/heel/gemspec.rb CHANGED
@@ -36,6 +36,8 @@ module Heel
36
36
  spec.add_dependency("mongrel", ">= 1.0.1")
37
37
  spec.add_dependency("launchy", ">= 0.3.0")
38
38
  spec.add_dependency("mime-types", ">= 1.15")
39
+ spec.add_dependency("coderay", ">= 0.7.4.215")
40
+ spec.add_dependency("rake", ">= 0.7.3")
39
41
 
40
42
  spec.required_ruby_version = ">= 1.8.5"
41
43
 
data/lib/heel/server.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  require 'heel'
2
2
  require 'ostruct'
3
- require 'daemons/daemonize'
4
3
  require 'launchy'
5
4
  require 'tmpdir'
6
5
  require 'fileutils'
@@ -53,6 +52,7 @@ module Heel
53
52
  @default_options.port = 4331
54
53
  @default_options.document_root = Dir.pwd
55
54
  @default_options.daemonize = false
55
+ @default_options.highlighting = true
56
56
  @default_options.kill = false
57
57
  @default_options.launch_browser = true
58
58
  end
@@ -76,7 +76,7 @@ module Heel
76
76
  op.separator ""
77
77
 
78
78
  op.on("-a", "--address ADDRESS", "Address to bind to",
79
- "(default: #{default_options.address})") do |add|
79
+ " (default: #{default_options.address})") do |add|
80
80
  @parsed_options.address = add
81
81
  end
82
82
 
@@ -92,12 +92,18 @@ module Heel
92
92
  @parsed_options.kill = true
93
93
  end
94
94
 
95
- op.on("-l", "--[no-]launch-browser", "Control automatically launching a browser") do |l|
95
+ op.on("--[no-]highlighting", "Turn on or off syntax highlighting",
96
+ " (default: on)") do |highlighting|
97
+ @parsed_options.highlighting = highlighting
98
+ end
99
+
100
+ op.on("--[no-]launch-browser", "Turn on or off automatic browser launch",
101
+ " (default: on)") do |l|
96
102
  @parsed_options.launch_browser = l
97
103
  end
98
104
 
99
105
  op.on("-p", "--port PORT", Integer, "Port to bind to",
100
- "(default: #{default_options.port})") do |port|
106
+ " (default: #{default_options.port})") do |port|
101
107
  @parsed_options.port = port
102
108
  end
103
109
 
@@ -171,8 +177,7 @@ module Heel
171
177
  if not File.exists?(default_directory) then
172
178
  FileUtils.mkdir_p(default_directory)
173
179
  @stdout.puts "Created #{default_directory}"
174
- @stdout.puts "PID file #{pid_file} is stored here"
175
- @stdout.puts "along with the log #{log_file}"
180
+ @stdout.puts "heel's PID (#{pid_file}) and log file (#{log_file}) are stored here"
176
181
  end
177
182
  end
178
183
 
@@ -184,9 +189,12 @@ module Heel
184
189
  setup_heel_dir
185
190
 
186
191
  # capture method/variables into a local context so they can be used inside the Configurator block
192
+ c_address = options.address
193
+ c_port = options.port
187
194
  c_document_root = options.document_root
188
195
  c_background_me = options.daemonize
189
196
  c_default_dir = default_directory
197
+ c_highlighting = options.highlighting
190
198
  c_pid_file = pid_file
191
199
  c_log_file = log_file
192
200
 
@@ -202,15 +210,23 @@ module Heel
202
210
  daemonize({:cwd => c_default_dir, :log_file => c_log_file})
203
211
  end
204
212
 
205
- listener do
206
- uri "/", :handler => stats
207
- uri "/", :handler => Heel::DirHandler.new({:document_root => c_document_root})
208
- uri "/", :handler => Heel::ErrorHandler.new
209
- uri "/icons", :handler => Heel::DirHandler.new({ :document_root =>
210
- File.join(APP_RESOURCE_DIR, "famfamfam", "icons")})
211
- uri "/status", :handler => ::Mongrel::StatusHandler.new(:stats_filter => stats)
213
+ begin
214
+
215
+ listener do
216
+ uri "/", :handler => stats
217
+ uri "/", :handler => Heel::DirHandler.new({:document_root => c_document_root,
218
+ :highlighting => c_highlighting })
219
+ uri "/", :handler => Heel::ErrorHandler.new
220
+ uri "/css", :handler => Heel::DirHandler.new({:document_root =>
221
+ File.join(APP_RESOURCE_DIR, "css")})
222
+ uri "/icons", :handler => Heel::DirHandler.new({ :document_root =>
223
+ File.join(APP_RESOURCE_DIR, "famfamfam", "icons")})
224
+ uri "/status", :handler => ::Mongrel::StatusHandler.new(:stats_filter => stats)
225
+ end
226
+ rescue Errno::EADDRINUSE
227
+ log "ERROR: Address (#{c_address}:#{c_port}) is already in use, please check running processes or run `heel --kill'"
228
+ exit 1
212
229
  end
213
-
214
230
  setup_signals
215
231
  end
216
232
 
@@ -225,6 +241,9 @@ module Heel
225
241
 
226
242
  if options.launch_browser then
227
243
  config.log "Launching your browser..."
244
+ if c_background_me then
245
+ puts "Launching your browser to http://#{options.address}:#{options.port}/"
246
+ end
228
247
  ::Launchy.open("http://#{options.address}:#{options.port}/")
229
248
  end
230
249
 
data/lib/heel/version.rb CHANGED
@@ -3,7 +3,7 @@ require 'heel'
3
3
  module Heel
4
4
  class Version
5
5
  MAJOR = 0
6
- MINOR = 4
6
+ MINOR = 5
7
7
  BUILD = 0
8
8
 
9
9
  class << self
@@ -0,0 +1,104 @@
1
+ .CodeRay {
2
+ background-color: #f8f8f8;
3
+ /* border: 1px solid silver;
4
+ */ font-family: 'Courier New', 'Terminal', monospace;
5
+ color: #100;
6
+ }
7
+ .CodeRay pre { margin: 0px }
8
+
9
+ div.CodeRay { }
10
+
11
+ span.CodeRay { white-space: pre; border: 0px; padding: 2px }
12
+
13
+ table.CodeRay { border-collapse: collapse; width: 100%; padding: 2px }
14
+ table.CodeRay td { padding: 2px 4px; vertical-align: top }
15
+
16
+ .CodeRay .line_numbers, .CodeRay .no {
17
+ background-color: #def;
18
+ color: gray;
19
+ text-align: right;
20
+ }
21
+ .CodeRay .line_numbers tt { font-weight: bold }
22
+ .CodeRay .no { padding: 0px 4px }
23
+ .CodeRay .code { width: 100% }
24
+
25
+ ol.CodeRay { font-size: 10pt }
26
+ ol.CodeRay li { white-space: pre }
27
+
28
+ .CodeRay .code pre { overflow: auto }
29
+
30
+ .CodeRay .af { color:#00C }
31
+ .CodeRay .an { color:#007 }
32
+ .CodeRay .av { color:#700 }
33
+ .CodeRay .aw { color:#C00 }
34
+ .CodeRay .bi { color:#509; font-weight:bold }
35
+ .CodeRay .c { color:#888 }
36
+
37
+ .CodeRay .ch { color:#04D }
38
+ .CodeRay .ch .k { color:#04D }
39
+ .CodeRay .ch .dl { color:#039 }
40
+
41
+ .CodeRay .cl { color:#B06; font-weight:bold }
42
+ .CodeRay .co { color:#036; font-weight:bold }
43
+ .CodeRay .cr { color:#0A0 }
44
+ .CodeRay .cv { color:#369 }
45
+ .CodeRay .df { color:#099; font-weight:bold }
46
+ .CodeRay .di { color:#088; font-weight:bold }
47
+ .CodeRay .dl { color:black }
48
+ .CodeRay .do { color:#970 }
49
+ .CodeRay .ds { color:#D42; font-weight:bold }
50
+ .CodeRay .e { color:#666; font-weight:bold }
51
+ .CodeRay .en { color:#800; font-weight:bold }
52
+ .CodeRay .er { color:#F00; background-color:#FAA }
53
+ .CodeRay .ex { color:#F00; font-weight:bold }
54
+ .CodeRay .fl { color:#60E; font-weight:bold }
55
+ .CodeRay .fu { color:#06B; font-weight:bold }
56
+ .CodeRay .gv { color:#d70; font-weight:bold }
57
+ .CodeRay .hx { color:#058; font-weight:bold }
58
+ .CodeRay .i { color:#00D; font-weight:bold }
59
+ .CodeRay .ic { color:#B44; font-weight:bold }
60
+
61
+ .CodeRay .il { background: #eee }
62
+ .CodeRay .il .il { background: #ddd }
63
+ .CodeRay .il .il .il { background: #ccc }
64
+ .CodeRay .il .idl { font-weight: bold; color: #888 }
65
+
66
+ .CodeRay .in { color:#B2B; font-weight:bold }
67
+ .CodeRay .iv { color:#33B }
68
+ .CodeRay .la { color:#970; font-weight:bold }
69
+ .CodeRay .lv { color:#963 }
70
+ .CodeRay .oc { color:#40E; font-weight:bold }
71
+ .CodeRay .on { color:#000; font-weight:bold }
72
+ .CodeRay .op { }
73
+ .CodeRay .pc { color:#038; font-weight:bold }
74
+ .CodeRay .pd { color:#369; font-weight:bold }
75
+ .CodeRay .pp { color:#579 }
76
+ .CodeRay .pt { color:#339; font-weight:bold }
77
+ .CodeRay .r { color:#080; font-weight:bold }
78
+
79
+ .CodeRay .rx { background-color:#fff0ff }
80
+ .CodeRay .rx .k { color:#808 }
81
+ .CodeRay .rx .dl { color:#404 }
82
+ .CodeRay .rx .mod { color:#C2C }
83
+ .CodeRay .rx .fu { color:#404; font-weight: bold }
84
+
85
+ .CodeRay .s { background-color:#fff0f0 }
86
+ .CodeRay .s .s { background-color:#ffe0e0 }
87
+ .CodeRay .s .s .s { background-color:#ffd0d0 }
88
+ .CodeRay .s .k { color:#D20 }
89
+ .CodeRay .s .dl { color:#710 }
90
+
91
+ .CodeRay .sh { background-color:#f0fff0 }
92
+ .CodeRay .sh .k { color:#2B2 }
93
+ .CodeRay .sh .dl { color:#161 }
94
+
95
+ .CodeRay .sy { color:#A60 }
96
+ .CodeRay .sy .k { color:#A60 }
97
+ .CodeRay .sy .dl { color:#630 }
98
+
99
+ .CodeRay .ta { color:#070 }
100
+ .CodeRay .tf { color:#070; font-weight:bold }
101
+ .CodeRay .ts { color:#D70; font-weight:bold }
102
+ .CodeRay .ty { color:#339; font-weight:bold }
103
+ .CodeRay .v { color:#036 }
104
+ .CodeRay .xt { color:#444 }
@@ -0,0 +1,98 @@
1
+ .CodeRay {
2
+ background-color: #001129;
3
+ border: 1px solid silver;
4
+ font-family: 'Courier New', 'Terminal', monospace;
5
+ color: #C0C0C0;
6
+ }
7
+ .CodeRay pre { margin: 0px; }
8
+
9
+ div.CodeRay { }
10
+
11
+ span.CodeRay { white-space: pre; border: 0px; padding: 2px; }
12
+
13
+ table.CodeRay { border-collapse: collapse; width: 100%; padding: 2px; }
14
+ table.CodeRay td { padding: 2px 4px; vertical-align: top; }
15
+
16
+ .CodeRay .line_numbers, .CodeRay .no {
17
+ background-color: #001129;
18
+ color: gray;
19
+ text-align: right;
20
+ }
21
+ .CodeRay .line_numbers tt { font-weight: bold; }
22
+ .CodeRay .no { padding: 0px 4px; }
23
+ .CodeRay .code { width: 100%; }
24
+
25
+ ol.CodeRay { font-size: 10pt; }
26
+ ol.CodeRay li { white-space: pre; }
27
+
28
+ .CodeRay .code pre { overflow: auto; }
29
+
30
+ .CodeRay .af { color:#00C; }
31
+ .CodeRay .an { color:#007; }
32
+ .CodeRay .av { color:#700; }
33
+ .CodeRay .aw { color:#C00; }
34
+ .CodeRay .bi { color:#509; font-weight:bold; }
35
+ .CodeRay .c { color:#666; }
36
+
37
+ .CodeRay .ch { color:#88F; }
38
+ .CodeRay .ch .k { color:#04D; }
39
+ .CodeRay .ch .dl { color:#039; }
40
+
41
+ .CodeRay .cl { color:#e9e; font-weight:bold; }
42
+ .CodeRay .co { color:#5ED; font-weight:bold; }
43
+ .CodeRay .cr { color:#0A0; }
44
+ .CodeRay .cv { color:#ccf; }
45
+ .CodeRay .df { color:#099; font-weight:bold; }
46
+ .CodeRay .di { color:#088; font-weight:bold; }
47
+ .CodeRay .dl { color:black; }
48
+ .CodeRay .do { color:#970; }
49
+ .CodeRay .ds { color:#D42; font-weight:bold; }
50
+ .CodeRay .e { color:#666; font-weight:bold; }
51
+ .CodeRay .er { color:#F00; background-color:#FAA; }
52
+ .CodeRay .ex { color:#F00; font-weight:bold; }
53
+ .CodeRay .fl { color:#60E; font-weight:bold; }
54
+ .CodeRay .fu { color:#5ed; font-weight:bold; }
55
+ .CodeRay .gv { color:#f84; }
56
+ .CodeRay .hx { color:#058; font-weight:bold; }
57
+ .CodeRay .i { color:#66f; font-weight:bold; }
58
+ .CodeRay .ic { color:#B44; font-weight:bold; }
59
+ .CodeRay .il { }
60
+ .CodeRay .in { color:#B2B; font-weight:bold; }
61
+ .CodeRay .iv { color:#aaf; }
62
+ .CodeRay .la { color:#970; font-weight:bold; }
63
+ .CodeRay .lv { color:#963; }
64
+ .CodeRay .oc { color:#40E; font-weight:bold; }
65
+ .CodeRay .on { color:#000; font-weight:bold; }
66
+ .CodeRay .op { }
67
+ .CodeRay .pc { color:#08f; font-weight:bold; }
68
+ .CodeRay .pd { color:#369; font-weight:bold; }
69
+ .CodeRay .pp { color:#579; }
70
+ .CodeRay .pt { color:#66f; font-weight:bold; }
71
+ .CodeRay .r { color:#5de; font-weight:bold; }
72
+
73
+ .CodeRay .rx { background-color:#221133; }
74
+ .CodeRay .rx .k { color:#f8f; }
75
+ .CodeRay .rx .dl { color:#f0f; }
76
+ .CodeRay .rx .mod { color:#f0b; }
77
+ .CodeRay .rx .fu { color:#404; font-weight: bold; }
78
+
79
+ .CodeRay .s { background-color:#331122; }
80
+ .CodeRay .s .s { background-color:#ffe0e0; }
81
+ .CodeRay .s .s .s { background-color:#ffd0d0; }
82
+ .CodeRay .s .k { color:#F88; }
83
+ .CodeRay .s .dl { color:#f55; }
84
+
85
+ .CodeRay .sh { background-color:#f0fff0; }
86
+ .CodeRay .sh .k { color:#2B2; }
87
+ .CodeRay .sh .dl { color:#161; }
88
+
89
+ .CodeRay .sy { color:#Fc8; }
90
+ .CodeRay .sy .k { color:#Fc8; }
91
+ .CodeRay .sy .dl { color:#F84; }
92
+
93
+ .CodeRay .ta { color:#070; }
94
+ .CodeRay .tf { color:#070; font-weight:bold; }
95
+ .CodeRay .ts { color:#D70; font-weight:bold; }
96
+ .CodeRay .ty { color:#339; font-weight:bold; }
97
+ .CodeRay .v { color:#036; }
98
+ .CodeRay .xt { color:#444; }
@@ -0,0 +1,38 @@
1
+ body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #e0e0e0;}
2
+ div.wrap {
3
+ background: transparent;
4
+ text-align: left;
5
+ width: 400px;
6
+ margin-top: 100px;
7
+ margin-left: auto;
8
+ margin-right: auto;
9
+ }
10
+ div.box {
11
+ border-left: 1px solid #333;
12
+ border-right: 1px solid #333;
13
+ background: #FFF;
14
+ display: block;
15
+ }
16
+ img { padding-right: 20px;}
17
+ h1 { display: inline; font-weight: bold; }
18
+ h2 { display: inline; font-weight: bold; padding-left: 25px;text-align: center;}
19
+ div.header { font-size: x-large; }
20
+ div.content { font-size: x-large; border: 0; padding: 10px 25px 10px 25px;}
21
+ div.footer { text-align: right; width: 400px; }
22
+
23
+ .curved { background: transparent;}
24
+ .curved .c1, .curved .c2, .curved .c3, .curved .c4 {
25
+ display: block;
26
+ overflow: hidden;
27
+ height: 1px;
28
+ font-size: 1px;
29
+ }
30
+ .curved .c2, .curved .c3, .curved .c4 {
31
+ background: #fff;
32
+ border-left: 1px solid #000;
33
+ border-right: 1px solid #000;
34
+ }
35
+ .c1 { margin: 0 5px 0 5px; background: #000; }
36
+ .c2 { margin: 0 3px 0 3px; border-left: 2px solid #000; border-right: 2px solid #000; }
37
+ .c3 { margin: 0 2px 0 2px; }
38
+ .c4 { margin: 0 1px 0 1px; }
@@ -0,0 +1,29 @@
1
+ a, a:active { text-decoration: none; color: blue;}
2
+ a:visited { color: purple;}
3
+ a:hover, a:focus { text-decoration: underline; color: red;}
4
+ body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #eee; }
5
+ .c { text-align: center; }
6
+ table { border-collapse: collapse; border-spacing: 0px; width: 100%;}
7
+ tr { background-color: #ccf;}
8
+ tr:hover { background-color: #faa;}
9
+ td, th {
10
+ border-bottom: 1px solid #ccc;
11
+ padding: 0em .3em 0em .3em ;
12
+ color: #666;
13
+ text-align: left;
14
+ vertical-align: middle;
15
+ }
16
+ thead th {
17
+ border-top: 2px solid #666;
18
+ border-bottom: 2px solid #666;
19
+ font-weight: bold;
20
+ background: #aaa;
21
+ color: #fff;
22
+ text-transform: uppercase;
23
+ }
24
+ tr.odd { background-color: #cfc; color: #333;}
25
+ tr.odd:hover { background-color: #faa; color: #333;}
26
+ td.n { text-align: right; padding-right: 2em;}
27
+ div.header { }
28
+ div.content { background: #FFF; width: 100%;}
29
+ div.footer { border-top: 2px solid #333; text-align: right;}
@@ -3,46 +3,7 @@
3
3
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4
4
  <head>
5
5
  <title>Error <%= status %> </title>
6
- <style type="text/css">
7
- body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #e0e0e0;}
8
- div.wrap {
9
- background: transparent;
10
- text-align: left;
11
- width: 400px;
12
- margin-top: 100px;
13
- margin-left: auto;
14
- margin-right: auto;
15
- }
16
- div.box {
17
- border-left: 1px solid #333;
18
- border-right: 1px solid #333;
19
- background: #FFF;
20
- display: block;
21
- }
22
- img { padding-right: 20px;}
23
- h1 { display: inline; font-weight: bold; }
24
- h2 { display: inline; font-weight: bold; padding-left: 25px;text-align: center;}
25
- div.header { font-size: x-large; }
26
- div.content { font-size: x-large; border: 0; padding: 10px 25px 10px 25px;}
27
- div.footer { text-align: right; width: 400px; }
28
-
29
- .curved { background: transparent;}
30
- .curved .c1, .curved .c2, .curved .c3, .curved .c4 {
31
- display: block;
32
- overflow: hidden;
33
- height: 1px;
34
- font-size: 1px;
35
- }
36
- .curved .c2, .curved .c3, .curved .c4 {
37
- background: #fff;
38
- border-left: 1px solid #000;
39
- border-right: 1px solid #000;
40
- }
41
- .c1 { margin: 0 5px 0 5px; background: #000; }
42
- .c2 { margin: 0 3px 0 3px; border-left: 2px solid #000; border-right: 2px solid #000; }
43
- .c3 { margin: 0 2px 0 2px; }
44
- .c4 { margin: 0 1px 0 1px; }
45
- </style>
6
+ <link rel="stylesheet" href="/css/error.css" type="text/css" />
46
7
  </head>
47
8
  <body>
48
9
  <div class="wrap">
Binary file
@@ -3,37 +3,7 @@
3
3
  <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
4
4
  <head>
5
5
  <title>Index of <%= base_uri %></title>
6
- <style type="text/css">
7
- a, a:active { text-decoration: none; color: blue;}
8
- a:visited { color: purple;}
9
- a:hover, a:focus { text-decoration: underline; color: red;}
10
- body { font-family: Verdana, Arial, Helvetica, sans-serif; background-color: #eee; }
11
- .c { text-align: center; }
12
- table { border-collapse: collapse; border-spacing: 0px; width: 100%;}
13
- tr { background-color: #ccf;}
14
- tr:hover { background-color: #faa;}
15
- td, th {
16
- border-bottom: 1px solid #ccc;
17
- padding: 0em .3em 0em .3em ;
18
- color: #666;
19
- text-align: left;
20
- vertical-align: middle;
21
- }
22
- thead th {
23
- border-top: 2px solid #666;
24
- border-bottom: 2px solid #666;
25
- font-weight: bold;
26
- background: #aaa;
27
- color: #fff;
28
- text-transform: uppercase;
29
- }
30
- tr.odd { background-color: #cfc; color: #333;}
31
- tr.odd:hover { background-color: #faa; color: #333;}
32
- td.n { text-align: right; padding-right: 2em;}
33
- div.header { }
34
- div.content { background: #FFF; width: 100%;}
35
- div.footer { border-top: 2px solid #333; text-align: right;}
36
- </style>
6
+ <link rel="stylesheet" href="/css/listing.css" type="text/css" />
37
7
  </head>
38
8
  <body>
39
9
  <div class="header">
@@ -52,6 +22,9 @@
52
22
  <img src="<%= entry.icon_url %>" width="16" height="16" alt="icon for type <%= entry.content_type %>" />
53
23
  <% end %>
54
24
  <a href="<%= "#{base_uri.chomp('/')}/#{entry.link}" %>"><%= entry.name %></a>
25
+ <% if highlighting and entry.content_type != 'Directory' then %>
26
+ (<a href="<%= "#{base_uri.chomp('/')}/#{entry.link}?highlighting=false" %>">download</a>)
27
+ <% end %>
55
28
  </td>
56
29
  <td><%= entry.last_modified %></td>
57
30
  <td class="<%= entry.content_type == "Directory" ? 'c' : 'n' %>" ><%= entry.size %></td>
@@ -4,7 +4,7 @@ require 'stringio'
4
4
 
5
5
  describe Heel::DirHandler do
6
6
  before(:each) do
7
- @handler = Heel::DirHandler.new
7
+ @handler = Heel::DirHandler.new({:highlighting => true})
8
8
  @classifier = Mongrel::URIClassifier.new
9
9
  @classifier.register("/",1)
10
10
  @socket = StringIO.new
@@ -26,7 +26,80 @@ describe Heel::DirHandler do
26
26
  @response.finished
27
27
 
28
28
  @response.status.should == 200
29
- @response.body.string.should =~ /Index of/m
29
+ @socket.string.should =~ /Index of/m
30
+ end
31
+
32
+ it "should return an existing directory index page if it exists" do
33
+ File.open("index.html", "w") { |f| f.write('delete me') }
34
+ @params[Mongrel::Const::REQUEST_URI] = "/"
35
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
36
+
37
+ @params[Mongrel::Const::PATH_INFO] = path_info
38
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
39
+
40
+ @handler.process(@request,@response)
41
+ @response.finished
42
+ File.unlink("index.html")
43
+
44
+ @response.status.should == 200
45
+ @socket.string.should =~ /delete me/m
46
+ end
47
+
48
+ it "should return a 403 if a request for an ignorable file is made" do
49
+ File.open(".htaccess", "w") { |f| f.write('delete me') }
50
+ @params[Mongrel::Const::REQUEST_URI] = "/.htaccess"
51
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
52
+
53
+ @params[Mongrel::Const::PATH_INFO] = path_info
54
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
55
+
56
+ @handler.process(@request,@response)
57
+ @response.finished
58
+
59
+ File.unlink(".htaccess")
60
+ @response.status.should == 403
61
+
62
+ end
63
+
64
+ it "should return a 403 if an invalid request method is used" do
65
+ @params[Mongrel::Const::REQUEST_METHOD] = "PUT"
66
+ @params[Mongrel::Const::REQUEST_URI] = "/"
67
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
68
+
69
+ @params[Mongrel::Const::PATH_INFO] = path_info
70
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
71
+
72
+ @handler.process(@request,@response)
73
+ @response.finished
74
+
75
+ @response.status.should == 403
76
+ @socket.string.should =~ /Only HEAD and GET requests are honored./m
77
+ end
78
+
79
+ it "should format a ruby file and return it as a content-type text/html" do
80
+ @params[Mongrel::Const::REQUEST_URI] = "/spec/" + File.basename(__FILE__)
81
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
82
+
83
+ @params[Mongrel::Const::PATH_INFO] = path_info
84
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
85
+ @handler.process(@request,@response)
86
+ @response.finished
87
+ @response.status.should == 200
88
+ @socket.string.should =~ /Content-Type: text\/html/m
89
+
90
+ end
91
+
92
+ it "should parse the highlighting cgi parameter and return non-highlighted text if highlighting=off" do
93
+ @params[Mongrel::Const::REQUEST_URI] = "/spec/" + File.basename(__FILE__)
94
+ @params['QUERY_STRING'] = "highlighting=off"
95
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
96
+
97
+ @params[Mongrel::Const::PATH_INFO] = path_info
98
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
99
+ @handler.process(@request,@response)
100
+ @response.finished
101
+ @response.status.should == 200
102
+ @socket.string.should =~ /Content-Type: text\/plain/m
30
103
  end
31
104
 
32
105
  it "should return icons appropriately for unknown mime_type" do
@@ -36,4 +109,20 @@ describe Heel::DirHandler do
36
109
  it "should test if templates need to be reloaded" do
37
110
  @handler.reload_template_changes?.should == false
38
111
  end
112
+
113
+ it "should return 403 if we access something that exists but is not a readable file" do
114
+ File.open("deleteme.html", "w") { |f| f.write('delete me') }
115
+ File.chmod(0111, "deleteme.html")
116
+ @params[Mongrel::Const::REQUEST_URI] = "/deleteme.html"
117
+ junk1,path_info,junk2 = @classifier.resolve(@params[Mongrel::Const::REQUEST_URI])
118
+
119
+ @params[Mongrel::Const::PATH_INFO] = path_info
120
+ @request = Mongrel::HttpRequest.new(@params,@socket,nil)
121
+
122
+ @handler.process(@request,@response)
123
+ @response.finished
124
+ File.unlink("deleteme.html")
125
+
126
+ @response.status.should == 403
127
+ end
39
128
  end
@@ -21,6 +21,7 @@ describe Heel::ErrorHandler do
21
21
 
22
22
  @handler.process(@request,@response)
23
23
  @response.finished
24
+
24
25
  @response.status.should == 404
25
26
  @socket.string.should =~ /Not Found/m
26
27
 
data/spec/server_spec.rb CHANGED
@@ -63,6 +63,12 @@ describe Heel::Server do
63
63
  server.options.port.should == 4242
64
64
  end
65
65
 
66
+ it "should allow the highlighting option to be unset" do
67
+ server = Heel::Server.new(%w[--no-highlighting])
68
+ server.merge_options
69
+ server.options.highlighting.should == false
70
+ end
71
+
66
72
  it "should set no-launch-browser option and kill option" do
67
73
  server = Heel::Server.new(%w[--no-launch-browser])
68
74
  server.merge_options
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.2
3
3
  specification_version: 1
4
4
  name: heel
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.4.0
7
- date: 2007-11-04 00:00:00 -04:00
6
+ version: 0.5.0
7
+ date: 2007-11-19 00:00:00 -07:00
8
8
  summary: A mongrel based static file webserver.
9
9
  require_paths:
10
10
  - lib
@@ -43,11 +43,17 @@ files:
43
43
  - lib/heel/specification.rb
44
44
  - lib/heel/version.rb
45
45
  - lib/heel.rb
46
+ - resources/css
47
+ - resources/css/coderay-cycnus.css
48
+ - resources/css/coderay-murphy.css
49
+ - resources/css/error.css
50
+ - resources/css/listing.css
46
51
  - resources/error.rhtml
47
52
  - resources/famfamfam
48
53
  - resources/famfamfam/icons
49
54
  - resources/famfamfam/icons/application.png
50
55
  - resources/famfamfam/icons/compress.png
56
+ - resources/famfamfam/icons/error.png
51
57
  - resources/famfamfam/icons/folder.png
52
58
  - resources/famfamfam/icons/html.png
53
59
  - resources/famfamfam/icons/page_excel.png
@@ -112,3 +118,21 @@ dependencies:
112
118
  - !ruby/object:Gem::Version
113
119
  version: "1.15"
114
120
  version:
121
+ - !ruby/object:Gem::Dependency
122
+ name: coderay
123
+ version_requirement:
124
+ version_requirements: !ruby/object:Gem::Version::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 0.7.4.215
129
+ version:
130
+ - !ruby/object:Gem::Dependency
131
+ name: rake
132
+ version_requirement:
133
+ version_requirements: !ruby/object:Gem::Version::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: 0.7.3
138
+ version: