serve 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|