heel 0.4.0 → 0.5.0

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/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: