serve 1.0.0 → 1.1.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/CHANGELOG.rdoc +11 -0
- data/Gemfile +17 -14
- data/Gemfile.lock +53 -26
- data/LICENSE +1 -1
- data/QUICKSTART.rdoc +137 -27
- data/README.rdoc +115 -23
- data/VERSION +1 -1
- data/bin/serve +3 -3
- data/lib/serve.rb +2 -5
- data/lib/serve/application.rb +7 -6
- data/lib/serve/handlers/dynamic_handler.rb +40 -42
- data/lib/serve/handlers/less_handler.rb +19 -0
- data/lib/serve/handlers/sass_handler.rb +5 -0
- data/lib/serve/out.rb +19 -1
- data/lib/serve/project.rb +2 -2
- data/lib/serve/rack.rb +58 -18
- data/lib/serve/router.rb +43 -0
- data/lib/serve/templates/config.ru +14 -10
- data/lib/serve/view_helpers.rb +36 -24
- data/spec/fixtures/directory/index.html +1 -0
- data/spec/fixtures/hello.html +1 -0
- data/spec/fixtures/stylesheets/application.sass +3 -0
- data/spec/router_spec.rb +60 -0
- data/spec/spec_helper.rb +0 -1
- metadata +50 -39
- data/lib/serve/file_resolver.rb +0 -48
- data/lib/serve/handlers/markdown_handler.rb +0 -10
- data/lib/serve/handlers/static_handler.rb +0 -22
- data/lib/serve/handlers/textile_handler.rb +0 -10
- data/lib/serve/rails.rb +0 -4
- data/lib/serve/rails/configuration.rb +0 -69
- data/lib/serve/rails/mount.rb +0 -38
- data/lib/serve/rails/routing.rb +0 -25
- data/lib/serve/rails/serve_controller.rb +0 -52
- data/lib/serve/response_cache.rb +0 -170
- data/spec/response_cache_spec.rb +0 -248
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
1.
|
1
|
+
1.1.0
|
data/bin/serve
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
2
3
|
lib = File.dirname(__FILE__) + '/../lib'
|
3
|
-
require 'rubygems'
|
4
4
|
|
5
|
-
|
6
|
-
require 'active_support/all'
|
5
|
+
require 'rubygems'
|
7
6
|
|
8
7
|
if File.file?(lib + '/serve/version.rb')
|
9
8
|
$LOAD_PATH << lib
|
10
9
|
else
|
11
10
|
gem 'serve'
|
12
11
|
end
|
12
|
+
|
13
13
|
require 'serve'
|
14
14
|
require 'serve/application'
|
15
15
|
|
data/lib/serve.rb
CHANGED
@@ -8,14 +8,11 @@ module Kernel
|
|
8
8
|
end
|
9
9
|
|
10
10
|
require 'serve/version'
|
11
|
-
require 'serve/
|
11
|
+
require 'serve/router'
|
12
12
|
require 'serve/handlers/file_type_handler'
|
13
|
-
require 'serve/handlers/textile_handler'
|
14
|
-
require 'serve/handlers/markdown_handler'
|
15
13
|
require 'serve/handlers/dynamic_handler'
|
16
14
|
require 'serve/handlers/sass_handler'
|
15
|
+
require 'serve/handlers/less_handler'
|
17
16
|
require 'serve/handlers/email_handler'
|
18
17
|
require 'serve/handlers/redirect_handler'
|
19
|
-
require 'serve/handlers/static_handler'
|
20
|
-
require 'serve/response_cache'
|
21
18
|
require 'serve/rack'
|
data/lib/serve/application.rb
CHANGED
@@ -78,8 +78,9 @@ module Serve
|
|
78
78
|
" #{program} command [arguments] [options]",
|
79
79
|
" ",
|
80
80
|
"Examples:",
|
81
|
-
" #{program} # start
|
82
|
-
" #{program} 2100 # start
|
81
|
+
" #{program} # start Serve in current directory",
|
82
|
+
" #{program} 2100 # start Serve on port 2100",
|
83
|
+
" #{program} /path/to/project # start Serve for a specific directory",
|
83
84
|
" #{program} create mockups # create a Serve project in mockups directory",
|
84
85
|
" #{program} convert mockups # convert a Compass project in mockups",
|
85
86
|
" ",
|
@@ -93,9 +94,9 @@ module Serve
|
|
93
94
|
" ",
|
94
95
|
" http://localhost:4000/",
|
95
96
|
" ",
|
96
|
-
" If the haml, redcloth, or bluecloth gems are installed the command can
|
97
|
-
" Haml, Sass, SCSS, Textile, and Markdown for documents with haml,
|
98
|
-
" textile, and markdown file extensions.",
|
97
|
+
" If the haml, slim, redcloth, or bluecloth gems are installed the command can ",
|
98
|
+
" handle Haml, Slim, Sass, SCSS, Textile, and Markdown for documents with haml, ",
|
99
|
+
" slim, sass, scss, textile, and markdown file extensions.",
|
99
100
|
" ",
|
100
101
|
" If the Rails command script/server exists in the current directory the ",
|
101
102
|
" script will start that instead.",
|
@@ -216,7 +217,7 @@ module Serve
|
|
216
217
|
end
|
217
218
|
|
218
219
|
def run_rack_app
|
219
|
-
system "rackup -p #{options[:port]} -o #{options[:address]} -E #{options[:environment]} #{rack_config}"
|
220
|
+
system "rackup -p #{options[:port]} -o #{options[:address]} -E #{options[:environment]} '#{rack_config}'"
|
220
221
|
end
|
221
222
|
|
222
223
|
def run_server
|
@@ -1,8 +1,19 @@
|
|
1
1
|
require 'serve/view_helpers'
|
2
|
+
require 'tilt'
|
2
3
|
|
3
4
|
module Serve #:nodoc:
|
4
5
|
class DynamicHandler < FileTypeHandler #:nodoc:
|
5
|
-
|
6
|
+
|
7
|
+
def self.extensions
|
8
|
+
# Get extensions from Tilt, ugly but it works
|
9
|
+
@extensions ||= (Tilt.mappings.map { |k,v| ["#{k}", "html.#{k}"] } << ["slim", "html.slim"]).flatten
|
10
|
+
end
|
11
|
+
|
12
|
+
def extensions
|
13
|
+
self.class.extensions
|
14
|
+
end
|
15
|
+
|
16
|
+
extension *extensions
|
6
17
|
|
7
18
|
def process(request, response)
|
8
19
|
response.headers['content-type'] = content_type
|
@@ -28,7 +39,8 @@ module Serve #:nodoc:
|
|
28
39
|
layout = nil
|
29
40
|
until layout or path == "/"
|
30
41
|
path = File.dirname(path)
|
31
|
-
possible_layouts =
|
42
|
+
possible_layouts = extensions.map do |ext|
|
43
|
+
l = "_layout.#{ext}"
|
32
44
|
possible_layout = File.join(root, path, l)
|
33
45
|
File.file?(possible_layout) ? possible_layout : false
|
34
46
|
end
|
@@ -44,54 +56,44 @@ module Serve #:nodoc:
|
|
44
56
|
end
|
45
57
|
end
|
46
58
|
|
47
|
-
module ERB #:nodoc:
|
48
|
-
class Engine #:nodoc:
|
49
|
-
def initialize(string, options = {})
|
50
|
-
@erb = ::ERB.new(string, nil, '-', '@erbout')
|
51
|
-
@erb.filename = options[:filename]
|
52
|
-
end
|
53
|
-
|
54
|
-
def render(context, &block)
|
55
|
-
# we have to keep track of the old erbout variable for nested renders
|
56
|
-
# because ERB#result will set it to an empty string before it renders
|
57
|
-
old_erbout = context.instance_variable_get('@erbout')
|
58
|
-
result = @erb.result(context.instance_eval { binding })
|
59
|
-
context.instance_variable_set('@erbout', old_erbout)
|
60
|
-
result
|
61
|
-
end
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
59
|
class Parser #:nodoc:
|
66
|
-
attr_accessor :context, :script_filename
|
60
|
+
attr_accessor :context, :script_filename, :script_extension, :engine
|
67
61
|
|
68
62
|
def initialize(context)
|
69
63
|
@context = context
|
70
64
|
@context.parser = self
|
71
65
|
end
|
72
66
|
|
73
|
-
def parse_file(filename)
|
74
|
-
old_script_filename = @script_filename
|
67
|
+
def parse_file(filename, locals={})
|
68
|
+
old_script_filename, old_script_extension, old_engine = @script_filename, @script_extension, @engine
|
69
|
+
|
75
70
|
@script_filename = filename
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
ERB::Engine.new(lines, :filename => filename)
|
86
|
-
else
|
87
|
-
raise 'extension not supported'
|
71
|
+
|
72
|
+
ext = File.extname(filename).sub(/^\.html\.|^\./, '').downcase
|
73
|
+
|
74
|
+
if ext == 'slim' # Ugly, but works
|
75
|
+
if Thread.list.size > 1
|
76
|
+
warn "WARN: serve autoloading 'slim' in a non thread-safe way; " +
|
77
|
+
"explicit require 'slim' suggested."
|
78
|
+
end
|
79
|
+
require 'slim'
|
88
80
|
end
|
89
|
-
|
81
|
+
|
82
|
+
@script_extension = ext
|
83
|
+
|
84
|
+
@engine = Tilt[ext].new(filename, nil, :outvar => '@_out_buf')
|
85
|
+
|
86
|
+
raise "#{ext} extension not supported" if @engine.nil?
|
87
|
+
|
88
|
+
@engine.render(context, locals) do |*args|
|
90
89
|
context.get_content_for(*args)
|
91
90
|
end
|
91
|
+
ensure
|
92
92
|
@script_filename = old_script_filename
|
93
|
-
|
93
|
+
@script_extension = old_script_extension
|
94
|
+
@engine = old_engine
|
94
95
|
end
|
96
|
+
|
95
97
|
end
|
96
98
|
|
97
99
|
class Context #:nodoc:
|
@@ -103,11 +105,7 @@ module Serve #:nodoc:
|
|
103
105
|
@content = ''
|
104
106
|
end
|
105
107
|
|
106
|
-
def _erbout
|
107
|
-
@erbout
|
108
|
-
end
|
109
|
-
|
110
108
|
include Serve::ViewHelpers
|
111
109
|
end
|
112
110
|
end
|
113
|
-
end
|
111
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Serve #:nodoc:
|
2
|
+
|
3
|
+
# TODO: Figure out how to remove the Less Handler in favor of Tilt
|
4
|
+
# The Less handler seems to be necessary to keep Tilt from applying a layout
|
5
|
+
# to Less files. Any one know how to turn this Tilt feature off?
|
6
|
+
|
7
|
+
class LessHandler < FileTypeHandler #:nodoc:
|
8
|
+
extension 'less'
|
9
|
+
|
10
|
+
def parse(string)
|
11
|
+
require 'less'
|
12
|
+
Less.parse(string)
|
13
|
+
end
|
14
|
+
|
15
|
+
def content_type
|
16
|
+
'text/css'
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,4 +1,9 @@
|
|
1
1
|
module Serve #:nodoc:
|
2
|
+
|
3
|
+
# TODO: Figure out how to remove the Sass Handler in favor of Tilt
|
4
|
+
# The Sass handler seems to be necessary to keep Tilt from applying a layout
|
5
|
+
# to Sass files. Any one know how to turn this Tilt feature off?
|
6
|
+
|
2
7
|
class SassHandler < FileTypeHandler #:nodoc:
|
3
8
|
extension 'sass', 'scss'
|
4
9
|
|
data/lib/serve/out.rb
CHANGED
@@ -3,6 +3,13 @@ module Serve #:nodoc:
|
|
3
3
|
# Utility methods for handling output to the terminal
|
4
4
|
module Out #:nodoc:
|
5
5
|
|
6
|
+
COLORS = {
|
7
|
+
:clear => 0,
|
8
|
+
:red => 31,
|
9
|
+
:green => 32,
|
10
|
+
:yellow => 33
|
11
|
+
}
|
12
|
+
|
6
13
|
def stdout
|
7
14
|
@stdout ||= $stdout
|
8
15
|
end
|
@@ -24,7 +31,7 @@ module Serve #:nodoc:
|
|
24
31
|
end
|
25
32
|
|
26
33
|
def print(*args)
|
27
|
-
stderr.print(*args)
|
34
|
+
stderr.print(color_msg(:green, *args))
|
28
35
|
end
|
29
36
|
|
30
37
|
def log_action(name, message)
|
@@ -34,5 +41,16 @@ module Serve #:nodoc:
|
|
34
41
|
puts message
|
35
42
|
end
|
36
43
|
|
44
|
+
def color_msg(pigment, *args)
|
45
|
+
msg = ''
|
46
|
+
msg << color(pigment)
|
47
|
+
msg << "#{args.join(' ')}"
|
48
|
+
msg << color(:clear)
|
49
|
+
msg
|
50
|
+
end
|
51
|
+
|
52
|
+
def color(pigment)
|
53
|
+
"\e[#{COLORS[pigment.to_sym]}m"
|
54
|
+
end
|
37
55
|
end
|
38
56
|
end
|
data/lib/serve/project.rb
CHANGED
@@ -64,7 +64,7 @@ module Serve #:nodoc:
|
|
64
64
|
views
|
65
65
|
).each { |path| make_path path }
|
66
66
|
create_file 'config.ru', read_template('config.ru')
|
67
|
-
create_file 'LICENSE', read_template('
|
67
|
+
create_file 'LICENSE', read_template('license')
|
68
68
|
create_file '.gitignore', read_template('gitignore')
|
69
69
|
create_file 'compass.config', read_template('compass.config')
|
70
70
|
create_file 'README.markdown', read_template('README.markdown')
|
@@ -173,4 +173,4 @@ module Serve #:nodoc:
|
|
173
173
|
end
|
174
174
|
|
175
175
|
end
|
176
|
-
end
|
176
|
+
end
|
data/lib/serve/rack.rb
CHANGED
@@ -3,20 +3,35 @@ require 'rack'
|
|
3
3
|
|
4
4
|
module Serve
|
5
5
|
class Request < Rack::Request
|
6
|
+
# Returns a Hash of the query params for a request's URL.
|
6
7
|
def query
|
7
8
|
@query ||= Rack::Utils.parse_nested_query(query_string)
|
8
9
|
end
|
9
10
|
|
11
|
+
# Returns the protocol part of the URL for a request: "http://", "https://", etc.
|
10
12
|
def protocol
|
11
13
|
@scheme ||= scheme + "://"
|
12
14
|
end
|
13
15
|
|
16
|
+
# Returns a specialized hash of the headers on the request.
|
14
17
|
def headers
|
15
18
|
@headers ||= Headers.new(@env)
|
16
19
|
end
|
20
|
+
|
21
|
+
# Returns the value of the "REQUEST_URI" environment variable.
|
22
|
+
def request_uri
|
23
|
+
@env["REQUEST_URI"].to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
# Set the value of the "REQUEST_URI" environment variable.
|
27
|
+
def request_uri=(s)
|
28
|
+
@env["REQUEST_URI"] = s.to_s
|
29
|
+
end
|
17
30
|
end
|
18
31
|
|
19
32
|
class Response < Rack::Response
|
33
|
+
|
34
|
+
# Set the body of a response.
|
20
35
|
def body=(value)
|
21
36
|
# needed for Ruby 1.9
|
22
37
|
if value.respond_to? :each
|
@@ -25,9 +40,11 @@ module Serve
|
|
25
40
|
super([value])
|
26
41
|
end
|
27
42
|
end
|
43
|
+
|
28
44
|
end
|
29
45
|
|
30
|
-
#
|
46
|
+
# A specialized hash for the environment variables on a request.
|
47
|
+
# Borrowed from ActionDispatch in Rails.
|
31
48
|
class Headers < Hash
|
32
49
|
extend ActiveSupport::Memoizable
|
33
50
|
|
@@ -57,36 +74,59 @@ module Serve
|
|
57
74
|
end
|
58
75
|
|
59
76
|
class RackAdapter
|
77
|
+
|
78
|
+
# Initialize a Rack endpoint for Serve with the root path to
|
79
|
+
# the views directory.
|
60
80
|
def initialize(root)
|
61
81
|
@root = root
|
62
82
|
end
|
63
83
|
|
84
|
+
# Called by Rack to process a request.
|
64
85
|
def call(env)
|
65
86
|
request = Request.new(env)
|
66
87
|
response = Response.new()
|
67
88
|
process(request, response).to_a
|
68
89
|
end
|
69
90
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
91
|
+
protected
|
92
|
+
|
93
|
+
# Process the request and response. Paths are transformed so that
|
94
|
+
# URLs without extensions and directory indexes work.
|
95
|
+
def process(request, response)
|
96
|
+
path = Serve::Router.resolve(@root, request.path_info)
|
97
|
+
if path
|
98
|
+
# Fetch the file handler for a file with a given extension/
|
99
|
+
ext = File.extname(path)[1..-1]
|
100
|
+
handler = Serve::FileTypeHandler.handlers[ext]
|
101
|
+
if handler
|
102
|
+
# Handler exists? Process the request and response.
|
103
|
+
handler.new(@root, path).process(request, response)
|
104
|
+
response
|
105
|
+
else
|
106
|
+
# Handler doesn't exist? Rewrite the request to use the new path.
|
107
|
+
# This allows Rack::Cascade or Passenger to deliver a file that is
|
108
|
+
# not handled by one of the Serve handlers.
|
109
|
+
rewrite(request, response, path)
|
110
|
+
end
|
78
111
|
else
|
79
|
-
|
112
|
+
# Return a 404 response.
|
113
|
+
not_found(request, response)
|
80
114
|
end
|
81
|
-
else
|
82
|
-
default(request, response)
|
83
115
|
end
|
84
|
-
|
116
|
+
|
117
|
+
# Returns a 404 response.
|
118
|
+
def not_found(request, response)
|
119
|
+
response.status = 404
|
120
|
+
response.body = "Not found!"
|
121
|
+
response
|
122
|
+
end
|
123
|
+
|
124
|
+
# Rewrite the request to use a new path. Return a 404 response so that Rack::Cascade works.
|
125
|
+
def rewrite(request, response, path)
|
126
|
+
request.request_uri = path + request.query_string
|
127
|
+
request.path_info = path
|
128
|
+
not_found(request, response)
|
129
|
+
end
|
85
130
|
|
86
|
-
def default(request, response)
|
87
|
-
response.status = 404
|
88
|
-
response.body = "Not found!"
|
89
|
-
response
|
90
|
-
end
|
91
131
|
end
|
92
132
|
end
|
data/lib/serve/router.rb
ADDED
@@ -0,0 +1,43 @@
|
|
1
|
+
module Serve
|
2
|
+
module Router
|
3
|
+
|
4
|
+
# Resolve a path to a valid file name in root path. Return nil if no
|
5
|
+
# file exists for that path.
|
6
|
+
def self.resolve(root, path)
|
7
|
+
path = normalize_path(path)
|
8
|
+
|
9
|
+
return if path.nil? # If it's not a valid path, return nothing.
|
10
|
+
|
11
|
+
full_path = File.join(root, path)
|
12
|
+
|
13
|
+
case
|
14
|
+
when File.file?(full_path)
|
15
|
+
# A file exists! Return the matching path.
|
16
|
+
path
|
17
|
+
when File.directory?(full_path)
|
18
|
+
# It's a directory? Try a directory index.
|
19
|
+
resolve(root, File.join(path, 'index'))
|
20
|
+
when path.ends_with?('.css')
|
21
|
+
# CSS not found? Try SCSS or Sass.
|
22
|
+
alternates = %w{.scss .sass}.map { |ext| path.sub(/\.css\Z/, ext) }
|
23
|
+
sass_path = alternates.find do |p|
|
24
|
+
File.file?(File.join(root, p))
|
25
|
+
end
|
26
|
+
else
|
27
|
+
# Still no luck? Check to see if a file with an extension exists by that name.
|
28
|
+
# TODO: Return a path with an extension based on priority, not just the first found.
|
29
|
+
result = Dir.glob(full_path + ".*", File::FNM_CASEFOLD).first
|
30
|
+
result.sub(/^#{root}/, '').sub(/^\//, '') if result && File.file?(result)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def self.normalize_path(path)
|
37
|
+
path = File.join(path) # path may be array
|
38
|
+
path = path.sub(%r{/\Z}, '') # remove trailing slash
|
39
|
+
path unless path =~ /\.\./ # guard against evil paths
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|