heel 0.6.0 → 1.0.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 → HISTORY} +3 -3
- data/LICENSE +1 -1
- data/README +28 -26
- data/bin/heel +5 -1
- data/data/error.rhtml +1 -1
- data/data/listing.rhtml +2 -2
- data/gemspec.rb +42 -0
- data/lib/heel.rb +18 -13
- data/lib/heel/configuration.rb +64 -0
- data/lib/heel/directory_indexer.rb +114 -0
- data/lib/heel/error_response.rb +43 -0
- data/lib/heel/logger.rb +42 -0
- data/lib/heel/mime_map.rb +87 -0
- data/lib/heel/rackapp.rb +142 -0
- data/lib/heel/request.rb +66 -0
- data/lib/heel/server.rb +253 -219
- data/lib/heel/version.rb +21 -16
- data/spec/configuration_spec.rb +20 -0
- data/spec/directory_indexer_spec.rb +34 -0
- data/spec/rackapp_spec.rb +43 -0
- data/spec/server_spec.rb +107 -108
- data/tasks/announce.rake +42 -0
- data/tasks/config.rb +103 -0
- data/tasks/distribution.rake +52 -0
- data/tasks/documentation.rake +35 -0
- data/tasks/rspec.rb +33 -0
- data/tasks/rubyforge.rb +52 -0
- data/tasks/utils.rb +85 -0
- metadata +52 -44
- data/lib/heel/dir_handler.rb +0 -310
- data/lib/heel/error_handler.rb +0 -30
- data/lib/heel/gemspec.rb +0 -56
- data/lib/heel/specification.rb +0 -128
- data/spec/dir_handler_spec.rb +0 -128
- data/spec/error_handler_spec.rb +0 -29
@@ -0,0 +1,52 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the BSD license. See LICENSE for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'tasks/config'
|
7
|
+
|
8
|
+
#-------------------------------------------------------------------------------
|
9
|
+
# Distribution and Packaging
|
10
|
+
#-------------------------------------------------------------------------------
|
11
|
+
if pkg_config = Configuration.for_if_exist?("packaging") then
|
12
|
+
|
13
|
+
require 'gemspec'
|
14
|
+
require 'rake/gempackagetask'
|
15
|
+
require 'rake/contrib/sshpublisher'
|
16
|
+
|
17
|
+
namespace :dist do
|
18
|
+
|
19
|
+
Rake::GemPackageTask.new(Heel::GEM_SPEC) do |pkg|
|
20
|
+
pkg.need_tar = pkg_config.formats.tgz
|
21
|
+
pkg.need_zip = pkg_config.formats.zip
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Install as a gem"
|
25
|
+
task :install => [:clobber, :package] do
|
26
|
+
sh "sudo gem install -y pkg/#{Heel::GEM_SPEC.full_name}.gem"
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Uninstall gem"
|
30
|
+
task :uninstall do
|
31
|
+
sh "sudo gem uninstall -i #{Heel::GEM_SPEC.name} -x"
|
32
|
+
end
|
33
|
+
|
34
|
+
desc "dump gemspec"
|
35
|
+
task :gemspec do
|
36
|
+
puts Heel::GEM_SPEC.to_ruby
|
37
|
+
end
|
38
|
+
|
39
|
+
desc "reinstall gem"
|
40
|
+
task :reinstall => [:uninstall, :repackage, :install]
|
41
|
+
|
42
|
+
desc "distribute copiously"
|
43
|
+
task :copious => [:package] do
|
44
|
+
Rake::SshFilePublisher.new('jeremy@copiousfreetime.org',
|
45
|
+
'/var/www/vhosts/www.copiousfreetime.org/htdocs/gems/gems',
|
46
|
+
'pkg',"#{Heel::GEM_SPEC.full_name}.gem").upload
|
47
|
+
sh "ssh jeremy@copiousfreetime.org rake -f /var/www/vhosts/www.copiousfreetime.org/htdocs/gems/Rakefile"
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the BSD license. See LICENSE for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'tasks/config'
|
7
|
+
|
8
|
+
#-----------------------------------------------------------------------
|
9
|
+
# Documentation
|
10
|
+
#-----------------------------------------------------------------------
|
11
|
+
|
12
|
+
if rdoc_config = Configuration.for_if_exist?('rdoc') then
|
13
|
+
|
14
|
+
namespace :doc do
|
15
|
+
|
16
|
+
require 'rake/rdoctask'
|
17
|
+
|
18
|
+
# generating documentation locally
|
19
|
+
Rake::RDocTask.new do |rdoc|
|
20
|
+
rdoc.rdoc_dir = rdoc_config.output_dir
|
21
|
+
rdoc.options = rdoc_config.options
|
22
|
+
rdoc.rdoc_files = rdoc_config.files
|
23
|
+
rdoc.title = rdoc_config.title
|
24
|
+
rdoc.main = rdoc_config.main
|
25
|
+
end
|
26
|
+
|
27
|
+
if rubyforge_config = Configuration.for_if_exist?('rubyforge') then
|
28
|
+
desc "Deploy the RDoc documentation to #{rubyforge_config.rdoc_location}"
|
29
|
+
task :deploy => :rerdoc do
|
30
|
+
sh "rsync -zav --delete #{rdoc_config.output_dir}/ #{rubyforge_config.rdoc_location}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/tasks/rspec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the BSD license. See LICENSE for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'tasks/config'
|
7
|
+
|
8
|
+
#-------------------------------------------------------------------------------
|
9
|
+
# configuration for running rspec. This shows up as the test:default task
|
10
|
+
#-------------------------------------------------------------------------------
|
11
|
+
|
12
|
+
if spec_config = Configuration.for_if_exist?('test') then
|
13
|
+
|
14
|
+
namespace :test do
|
15
|
+
|
16
|
+
task :default => :spec
|
17
|
+
|
18
|
+
require 'spec/rake/spectask'
|
19
|
+
Spec::Rake::SpecTask.new do |r|
|
20
|
+
r.rcov = spec_config.ruby_opts
|
21
|
+
r.libs = [ Heel::Configuration.lib_path,
|
22
|
+
Heel::Configuration.root_dir ]
|
23
|
+
r.spec_files = spec_config.files
|
24
|
+
r.spec_opts = spec_config.options
|
25
|
+
|
26
|
+
if rcov_config = Configuration.for_if_exist?('rcov') then
|
27
|
+
r.rcov = true
|
28
|
+
r.rcov_dir = rcov_config.output_dir
|
29
|
+
r.rcov_opts = rcov_config.rcov_opts
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/tasks/rubyforge.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the BSD license. See LICENSE for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'tasks/config'
|
7
|
+
|
8
|
+
#-----------------------------------------------------------------------
|
9
|
+
# Rubyforge additions to the task library
|
10
|
+
#-----------------------------------------------------------------------
|
11
|
+
if rf_conf = Configuration.for_if_exist?('rubyforge') then
|
12
|
+
require 'rubyforge'
|
13
|
+
|
14
|
+
proj_conf = Configuration.for('project')
|
15
|
+
|
16
|
+
namespace :dist do
|
17
|
+
desc "Release files to rubyforge"
|
18
|
+
task :rubyforge => [:clean, :package] do
|
19
|
+
|
20
|
+
rubyforge = ::RubyForge.new
|
21
|
+
|
22
|
+
# make sure this release doesn't already exist
|
23
|
+
releases = rubyforge.autoconfig['release_ids']
|
24
|
+
if releases.has_key?(proj_conf.name) and releases[proj_conf.name][Heel::VERSION] then
|
25
|
+
abort("Release #{Heel::VERSION} already exists! Unable to release.")
|
26
|
+
end
|
27
|
+
|
28
|
+
config = rubyforge.userconfig
|
29
|
+
config["release_notes"] = proj_conf.description
|
30
|
+
config["release_changes"] = Utils.release_notes_from(proj_conf.history)[Heel::VERSION]
|
31
|
+
config["Prefomatted"] = true
|
32
|
+
|
33
|
+
puts "Uploading to rubyforge..."
|
34
|
+
files = FileList[File.join("pkg","#{proj_conf.name}-#{Heel::VERSION}*.*")].to_a
|
35
|
+
rubyforge.login
|
36
|
+
rubyforge.add_release(rf_conf.project, proj_conf.name, Heel::VERSION, *files)
|
37
|
+
puts "done."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
namespace :announce do
|
42
|
+
desc "Post news of #{proj_conf.name} to #{rf_conf.project} on rubyforge"
|
43
|
+
task :rubyforge do
|
44
|
+
subject, title, body, urls = announcement
|
45
|
+
rubyforge = RubyForge.new
|
46
|
+
rubyforge.login
|
47
|
+
rubyforge.post_news(rf_conf.project, subject, "#{title}\n\n#{urls}\n\n#{body}")
|
48
|
+
puts "Posted to rubyforge"
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
data/tasks/utils.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2007, 2008 Jeremy Hinegardner
|
3
|
+
# All rights reserved. Licensed under the BSD license. See LICENSE for details
|
4
|
+
#++
|
5
|
+
|
6
|
+
require 'heel/version'
|
7
|
+
|
8
|
+
#-------------------------------------------------------------------------------
|
9
|
+
# Additions to the Configuration class that are useful
|
10
|
+
#-------------------------------------------------------------------------------
|
11
|
+
class Configuration
|
12
|
+
class << self
|
13
|
+
def exist?( name )
|
14
|
+
Configuration::Table.has_key?( name )
|
15
|
+
end
|
16
|
+
|
17
|
+
def for_if_exist?( name )
|
18
|
+
if self.exist?( name ) then
|
19
|
+
self.for( name )
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
#-------------------------------------------------------------------------------
|
26
|
+
# some useful utilitiy methods for the tasks
|
27
|
+
#-------------------------------------------------------------------------------
|
28
|
+
module Utils
|
29
|
+
class << self
|
30
|
+
|
31
|
+
# Try to load the given _library_ using the built-in require, but do not
|
32
|
+
# raise a LoadError if unsuccessful. Returns +true+ if the _library_ was
|
33
|
+
# successfully loaded; returns +false+ otherwise.
|
34
|
+
#
|
35
|
+
def try_require( lib )
|
36
|
+
require lib
|
37
|
+
true
|
38
|
+
rescue LoadError
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
# partition an rdoc file into sections, and return the text of the section
|
43
|
+
# given.
|
44
|
+
def section_of(file, section_name)
|
45
|
+
File.read(file).split(/^(?==)/).each do |section|
|
46
|
+
lines = section.split("\n")
|
47
|
+
return lines[1..-1].join("\n").strip if lines.first =~ /#{section_name}/i
|
48
|
+
end
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
|
52
|
+
# Get an array of all the changes in the application for a particular
|
53
|
+
# release. This is done by looking in the history file and grabbing the
|
54
|
+
# information for the most recent release. The history file is assumed to
|
55
|
+
# be in RDoc format and version release are 2nd tier sections separated by
|
56
|
+
# '== Version X.Y.Z'
|
57
|
+
#
|
58
|
+
# returns:: A hash of notes keyed by version number
|
59
|
+
#
|
60
|
+
def release_notes_from(history_file)
|
61
|
+
releases = {}
|
62
|
+
File.read(history_file).split(/^(?==)/).each do |section|
|
63
|
+
lines = section.split("\n")
|
64
|
+
md = %r{Version ((\w+\.)+\w+)}.match(lines.first)
|
65
|
+
next unless md
|
66
|
+
releases[md[1]] = lines[1..-1].join("\n").strip
|
67
|
+
end
|
68
|
+
return releases
|
69
|
+
end
|
70
|
+
|
71
|
+
# return a hash of useful information for the latest release
|
72
|
+
# urls, subject, title, description and latest release notes
|
73
|
+
#
|
74
|
+
def announcement
|
75
|
+
cfg = Configuration.for("project")
|
76
|
+
{
|
77
|
+
:subject => "#{cfg.name} #{Heel::VERSION} Released",
|
78
|
+
:title => "#{cfg.name} version #{Heel::VERSION} has been released.",
|
79
|
+
:urls => "#{cfg.homepage}",
|
80
|
+
:description => "#{cfg.description.rstrip}",
|
81
|
+
:release_notes => Utils.release_notes_from(cfg.history)[Heel::VERSION].rstrip
|
82
|
+
}
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end # << self
|
metadata
CHANGED
@@ -1,34 +1,25 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: heel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
platform:
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Hinegardner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date:
|
13
|
-
default_executable:
|
12
|
+
date: 2008-04-21 00:00:00 -06:00
|
13
|
+
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
16
|
+
name: thin
|
17
17
|
version_requirement:
|
18
18
|
version_requirements: !ruby/object:Gem::Requirement
|
19
19
|
requirements:
|
20
20
|
- - ">="
|
21
21
|
- !ruby/object:Gem::Version
|
22
|
-
version:
|
23
|
-
version:
|
24
|
-
- !ruby/object:Gem::Dependency
|
25
|
-
name: launchy
|
26
|
-
version_requirement:
|
27
|
-
version_requirements: !ruby/object:Gem::Requirement
|
28
|
-
requirements:
|
29
|
-
- - ">="
|
30
|
-
- !ruby/object:Gem::Version
|
31
|
-
version: 0.3.0
|
22
|
+
version: 0.7.0
|
32
23
|
version:
|
33
24
|
- !ruby/object:Gem::Dependency
|
34
25
|
name: mime-types
|
@@ -40,48 +31,60 @@ dependencies:
|
|
40
31
|
version: "1.15"
|
41
32
|
version:
|
42
33
|
- !ruby/object:Gem::Dependency
|
43
|
-
name:
|
34
|
+
name: launchy
|
44
35
|
version_requirement:
|
45
36
|
version_requirements: !ruby/object:Gem::Requirement
|
46
37
|
requirements:
|
47
38
|
- - ">="
|
48
39
|
- !ruby/object:Gem::Version
|
49
|
-
version: 0.
|
40
|
+
version: 0.3.1
|
50
41
|
version:
|
51
42
|
- !ruby/object:Gem::Dependency
|
52
|
-
name:
|
43
|
+
name: coderay
|
53
44
|
version_requirement:
|
54
45
|
version_requirements: !ruby/object:Gem::Requirement
|
55
46
|
requirements:
|
56
47
|
- - ">="
|
57
48
|
- !ruby/object:Gem::Version
|
58
|
-
version: 0.7.
|
49
|
+
version: 0.7.4.215
|
59
50
|
version:
|
60
|
-
description: Heel is a
|
61
|
-
email: jeremy
|
51
|
+
description: Heel is a small static web server for use when you need a quick web server for a directory. Once the server is running, heel will use {launchy}[http://copiousfreetime.rubyforge.org/launchy/] to open your browser at the URL of your document root. Heel is built using {Rack}[http://rack.rubyforge.org] and {Thin}[http://code.macournoyer.com/thin/] % heel >> Thin web server (v0.8.1 codename Rebel Porpoise) >> Threaded mode OFF >> Maximum connections set to 1024 >> Listening on 0.0.0.0:4331, CTRL+C to stop Launching your browser... Or run it in the background % heel --daemonize Created /Users/jeremy/.heel heel's PID (/Users/jeremy/.heel/heel.pid) and log file (/Users/jeremy/.heel/heel.log) are stored here Launching your browser at http://0.0.0.0:4331/ % heel --kill Sending TERM to process 3304 Done.
|
52
|
+
email: jeremy at hinegardner dot org
|
62
53
|
executables:
|
63
54
|
- heel
|
64
55
|
extensions: []
|
65
56
|
|
66
57
|
extra_rdoc_files:
|
67
|
-
- CHANGES
|
68
|
-
- LICENSE
|
69
58
|
- README
|
70
|
-
|
71
|
-
- spec/dir_handler_spec.rb
|
72
|
-
- spec/error_handler_spec.rb
|
73
|
-
- spec/server_spec.rb
|
74
|
-
- spec/spec_helper.rb
|
75
|
-
- CHANGES
|
59
|
+
- HISTORY
|
76
60
|
- LICENSE
|
77
|
-
-
|
78
|
-
- lib/heel/
|
79
|
-
- lib/heel/
|
80
|
-
- lib/heel/
|
61
|
+
- lib/heel/configuration.rb
|
62
|
+
- lib/heel/directory_indexer.rb
|
63
|
+
- lib/heel/error_response.rb
|
64
|
+
- lib/heel/logger.rb
|
65
|
+
- lib/heel/mime_map.rb
|
66
|
+
- lib/heel/rackapp.rb
|
67
|
+
- lib/heel/request.rb
|
68
|
+
- lib/heel/server.rb
|
69
|
+
- lib/heel/version.rb
|
70
|
+
- lib/heel.rb
|
71
|
+
files:
|
72
|
+
- bin/heel
|
73
|
+
- lib/heel/configuration.rb
|
74
|
+
- lib/heel/directory_indexer.rb
|
75
|
+
- lib/heel/error_response.rb
|
76
|
+
- lib/heel/logger.rb
|
77
|
+
- lib/heel/mime_map.rb
|
78
|
+
- lib/heel/rackapp.rb
|
79
|
+
- lib/heel/request.rb
|
81
80
|
- lib/heel/server.rb
|
82
|
-
- lib/heel/specification.rb
|
83
81
|
- lib/heel/version.rb
|
84
82
|
- lib/heel.rb
|
83
|
+
- spec/configuration_spec.rb
|
84
|
+
- spec/directory_indexer_spec.rb
|
85
|
+
- spec/rackapp_spec.rb
|
86
|
+
- spec/server_spec.rb
|
87
|
+
- spec/spec_helper.rb
|
85
88
|
- data/css
|
86
89
|
- data/css/coderay-cycnus.css
|
87
90
|
- data/css/coderay-murphy.css
|
@@ -106,7 +109,17 @@ files:
|
|
106
109
|
- data/famfamfam/readme.html
|
107
110
|
- data/famfamfam/readme.txt
|
108
111
|
- data/listing.rhtml
|
109
|
-
-
|
112
|
+
- README
|
113
|
+
- HISTORY
|
114
|
+
- LICENSE
|
115
|
+
- tasks/announce.rake
|
116
|
+
- tasks/distribution.rake
|
117
|
+
- tasks/documentation.rake
|
118
|
+
- tasks/config.rb
|
119
|
+
- tasks/rspec.rb
|
120
|
+
- tasks/rubyforge.rb
|
121
|
+
- tasks/utils.rb
|
122
|
+
- gemspec.rb
|
110
123
|
has_rdoc: true
|
111
124
|
homepage: http://copiousfreetime.rubyforge.org/heel/
|
112
125
|
post_install_message:
|
@@ -115,15 +128,13 @@ rdoc_options:
|
|
115
128
|
- --inline-source
|
116
129
|
- --main
|
117
130
|
- README
|
118
|
-
- --title
|
119
|
-
- "'heel -- A mongrel based static file webserver.'"
|
120
131
|
require_paths:
|
121
132
|
- lib
|
122
133
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
134
|
requirements:
|
124
135
|
- - ">="
|
125
136
|
- !ruby/object:Gem::Version
|
126
|
-
version:
|
137
|
+
version: "0"
|
127
138
|
version:
|
128
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
140
|
requirements:
|
@@ -137,9 +148,6 @@ rubyforge_project: copiousfreetime
|
|
137
148
|
rubygems_version: 0.9.5
|
138
149
|
signing_key:
|
139
150
|
specification_version: 2
|
140
|
-
summary:
|
141
|
-
test_files:
|
142
|
-
|
143
|
-
- spec/error_handler_spec.rb
|
144
|
-
- spec/server_spec.rb
|
145
|
-
- spec/spec_helper.rb
|
151
|
+
summary: Heel is a small static web server for use when you need a quick web server for a directory
|
152
|
+
test_files: []
|
153
|
+
|
data/lib/heel/dir_handler.rb
DELETED
@@ -1,310 +0,0 @@
|
|
1
|
-
require 'heel'
|
2
|
-
require 'mime/types'
|
3
|
-
require 'erb'
|
4
|
-
require 'coderay'
|
5
|
-
require 'coderay/helpers/file_type'
|
6
|
-
|
7
|
-
module Heel
|
8
|
-
|
9
|
-
# A refactored version of Mongrel::DirHandler using the mime-types
|
10
|
-
# gem and a prettier directory listing.
|
11
|
-
class DirHandler < ::Mongrel::HttpHandler
|
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
|
23
|
-
|
24
|
-
# if any other mime types are needed, add them directly via the
|
25
|
-
# mime-types calls.
|
26
|
-
ADDITIONAL_MIME_TYPES = [
|
27
|
-
# [ content-type , [ array, of, filename, extentions] ]
|
28
|
-
["images/svg+xml", ["svg"]],
|
29
|
-
["video/x-flv", ["flv"]],
|
30
|
-
["application/x-shockwave-flash", ["swf"]],
|
31
|
-
["text/plain", ["rb", "rhtml"]],
|
32
|
-
]
|
33
|
-
|
34
|
-
ICONS_BY_MIME_TYPE = {
|
35
|
-
"text/plain" => "page_white_text.png",
|
36
|
-
"image" => "picture.png",
|
37
|
-
"pdf" => "page_white_acrobat.png",
|
38
|
-
"xml" => "page_white_code.png",
|
39
|
-
"compress" => "compress.png",
|
40
|
-
"gzip" => "compress.png",
|
41
|
-
"zip" => "compress.png",
|
42
|
-
"application/xhtml+xml" => "xhtml.png",
|
43
|
-
"application/word" => "page_word.png",
|
44
|
-
"application/excel" => "page_excel.png",
|
45
|
-
"application/powerpoint" => "page_white_powerpoint.png",
|
46
|
-
"text/html" => "html.png",
|
47
|
-
"application" => "application.png",
|
48
|
-
"text" => "page_white_text.png",
|
49
|
-
:directory => "folder.png",
|
50
|
-
:default => "page_white.png",
|
51
|
-
}
|
52
|
-
|
53
|
-
def initialize(options = {})
|
54
|
-
@ignore_globs = options[:ignore_globs] || %w( *~ .htaccess . )
|
55
|
-
@document_root = options[:document_root] || Dir.pwd
|
56
|
-
@directory_listing_allowed = options[:directory_listing_allowed] || true
|
57
|
-
@directory_index_html = options[:directory_index_html] || "index.html"
|
58
|
-
@using_icons = options[:using_icons] || true
|
59
|
-
@icon_url = options[:icon_url] || "/heel_icons"
|
60
|
-
@reload_template_changes = options[:reload_template_changes] || false
|
61
|
-
@highlighting = options[:highlighting] || false
|
62
|
-
reload_template
|
63
|
-
|
64
|
-
ADDITIONAL_MIME_TYPES.each do |mt|
|
65
|
-
if MIME::Types[mt.first].size == 0 then
|
66
|
-
type = MIME::Type.from_array(mt)
|
67
|
-
MIME::Types.add(type)
|
68
|
-
else
|
69
|
-
type = MIME::Types[mt.first].first
|
70
|
-
mt[1].each do |ext|
|
71
|
-
type.extensions << ext unless type.extensions.include?(ext)
|
72
|
-
end
|
73
|
-
# have to reindex if new extensions added
|
74
|
-
MIME::Types.index_extensions(type)
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
@default_mime_type = MIME::Types["application/octet-stream"].first
|
79
|
-
end
|
80
|
-
|
81
|
-
def directory_listing_allowed?
|
82
|
-
return !!@directory_listing_allowed
|
83
|
-
end
|
84
|
-
|
85
|
-
def using_icons?
|
86
|
-
return !!@using_icons
|
87
|
-
end
|
88
|
-
|
89
|
-
def icon_for(mime_type)
|
90
|
-
icon = nil
|
91
|
-
[:content_type, :sub_type, :media_type].each do |t|
|
92
|
-
icon = ICONS_BY_MIME_TYPE[mime_type.send(t)]
|
93
|
-
return icon if icon
|
94
|
-
end
|
95
|
-
icon = ICONS_BY_MIME_TYPE[:default]
|
96
|
-
end
|
97
|
-
|
98
|
-
def reload_template_changes?
|
99
|
-
return @reload_template_changes
|
100
|
-
end
|
101
|
-
|
102
|
-
def reload_template
|
103
|
-
fname = File.join(APP_DATA_DIR,"listing.rhtml")
|
104
|
-
fstat = File.stat(fname)
|
105
|
-
@template_mtime ||= fstat.mtime
|
106
|
-
if @template.nil? or fstat.mtime > @template_mtime then
|
107
|
-
@template = ::ERB.new(File.read(fname))
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# determine how to respond to the request, it will either be a
|
112
|
-
# directory listing, a file return, or an error return.
|
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)
|
117
|
-
|
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
|
-
return 403
|
133
|
-
elsif highlighting and (stat.size > 0) 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
|
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
|
142
|
-
|
143
|
-
rescue => error
|
144
|
-
if error.kind_of?(Errno::ENOENT) then
|
145
|
-
return 404
|
146
|
-
end
|
147
|
-
log "ERROR: Unknown, check out the backtrace"
|
148
|
-
log error.backtrace.join("\n")
|
149
|
-
return 500
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def should_ignore?(fname)
|
154
|
-
ignore_globs.each do |glob|
|
155
|
-
return true if File.fnmatch(glob,fname)
|
156
|
-
end
|
157
|
-
false
|
158
|
-
end
|
159
|
-
|
160
|
-
# send a directory listing back to the client
|
161
|
-
def respond_with_directory_listing(req_path,request,response)
|
162
|
-
base_uri = ::Mongrel::HttpRequest.unescape(request.params[Mongrel::Const::REQUEST_URI])
|
163
|
-
entries = []
|
164
|
-
Dir.entries(req_path).each do |entry|
|
165
|
-
next if should_ignore?(entry)
|
166
|
-
next if req_path == document_root and entry == ".."
|
167
|
-
|
168
|
-
stat = File.stat(File.join(req_path,entry))
|
169
|
-
entry_data = OpenStruct.new
|
170
|
-
|
171
|
-
entry_data.name = entry == ".." ? "Parent Directory" : entry
|
172
|
-
entry_data.link = entry
|
173
|
-
entry_data.size = num_to_bytes(stat.size)
|
174
|
-
entry_data.last_modified = stat.mtime.strftime("%Y-%m-%d %H:%M:%S")
|
175
|
-
|
176
|
-
if stat.directory? then
|
177
|
-
entry_data.content_type = "Directory"
|
178
|
-
entry_data.size = "-"
|
179
|
-
entry_data.name += "/"
|
180
|
-
if using_icons? then
|
181
|
-
entry_data.icon_url = File.join(icon_url, ICONS_BY_MIME_TYPE[:directory])
|
182
|
-
end
|
183
|
-
else
|
184
|
-
entry_data.mime_type = MIME::Types.of(entry).first || default_mime_type
|
185
|
-
entry_data.content_type = entry_data.mime_type.content_type
|
186
|
-
if using_icons? then
|
187
|
-
entry_data.icon_url = File.join(icon_url, icon_for(entry_data.mime_type))
|
188
|
-
end
|
189
|
-
end
|
190
|
-
entries << entry_data
|
191
|
-
end
|
192
|
-
|
193
|
-
entries = entries.sort_by { |e| e.link }
|
194
|
-
res_bytes = 0
|
195
|
-
response.start(200) do |head,out|
|
196
|
-
head['Content-Type'] = 'text/html'
|
197
|
-
res_bytes = out.write(template.result(binding))
|
198
|
-
end
|
199
|
-
return res_bytes
|
200
|
-
end
|
201
|
-
|
202
|
-
|
203
|
-
# send the file back with the appropriate mimetype
|
204
|
-
def respond_with_send_file(path,method,request,response)
|
205
|
-
stat = File.stat(path)
|
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
|
212
|
-
response.send_status(stat.size)
|
213
|
-
response.send_header
|
214
|
-
|
215
|
-
if method == ::Mongrel::Const::GET then
|
216
|
-
response.send_file(path,stat.size < ::Mongrel::Const::CHUNK_SIZE * 2)
|
217
|
-
end
|
218
|
-
|
219
|
-
return stat.size
|
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="/heel_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
|
248
|
-
|
249
|
-
# process the request, returning either the file, a directory
|
250
|
-
# listing (if allowed) or an appropriate error
|
251
|
-
def process(request, response)
|
252
|
-
method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET
|
253
|
-
if ( method == Mongrel::Const::GET ) or ( method == Mongrel::Const::HEAD ) then
|
254
|
-
|
255
|
-
reload_template if reload_template_changes
|
256
|
-
|
257
|
-
req_path = File.expand_path(File.join(@document_root,
|
258
|
-
::Mongrel::HttpRequest.unescape(request.params[Mongrel::Const::PATH_INFO])),
|
259
|
-
@document_root)
|
260
|
-
res_type = how_to_respond(req_path,::Mongrel::HttpRequest.query_parse(request.params['QUERY_STRING']))
|
261
|
-
res_size = 0
|
262
|
-
case res_type
|
263
|
-
when :directory_listing
|
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)
|
267
|
-
when String
|
268
|
-
res_size = respond_with_send_file(res_type,method,request,response)
|
269
|
-
when Integer
|
270
|
-
response.status = res_type
|
271
|
-
end
|
272
|
-
|
273
|
-
|
274
|
-
# invalid method
|
275
|
-
else
|
276
|
-
response.start(403) { |head,out| out.write("Only HEAD and GET requests are honored.") }
|
277
|
-
end
|
278
|
-
log_line = [ request.params[Mongrel::Const::REMOTE_ADDR], "-", "-", "[#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %Z")}]" ]
|
279
|
-
log_line << "\"#{method}"
|
280
|
-
log_line << request.params['REQUEST_URI']
|
281
|
-
log_line << "#{request.params['HTTP_VERSION']}\""
|
282
|
-
log_line << response.status
|
283
|
-
log_line << res_size
|
284
|
-
|
285
|
-
log log_line.join(' ')
|
286
|
-
end
|
287
|
-
|
288
|
-
# essentially this is strfbytes from facets
|
289
|
-
def num_to_bytes(num,fmt="%.2f")
|
290
|
-
case
|
291
|
-
when num < 1024
|
292
|
-
"#{num} bytes"
|
293
|
-
when num < 1024**2
|
294
|
-
"#{fmt % (num.to_f / 1024)} KB"
|
295
|
-
when num < 1024**3
|
296
|
-
"#{fmt % (num.to_f / 1024**2)} MB"
|
297
|
-
when num < 1024**4
|
298
|
-
"#{fmt % (num.to_f / 1024**3)} GB"
|
299
|
-
when num < 1024**5
|
300
|
-
"#{fmt % (num.to_f / 1024**4)} TB"
|
301
|
-
else
|
302
|
-
"#{num} bytes"
|
303
|
-
end
|
304
|
-
end
|
305
|
-
|
306
|
-
def log(msg)
|
307
|
-
STDERR.print msg, "\n"
|
308
|
-
end
|
309
|
-
end
|
310
|
-
end
|