1
0
Fork 0
mirror of https://github.com/sinatra/sinatra synced 2023-03-27 23:18:01 -04:00

breaking out into separate files

This commit is contained in:
Blake Mizerany 2007-11-26 21:42:20 -08:00
parent eb41d0a97e
commit 68f364e60c
6 changed files with 377 additions and 368 deletions

View file

@ -10,378 +10,15 @@ end
require "rack"
require File.dirname(__FILE__) + '/sinatra/application'
require File.dirname(__FILE__) + '/sinatra/event_context'
require File.dirname(__FILE__) + '/sinatra/route'
require File.dirname(__FILE__) + '/sinatra/error'
require File.dirname(__FILE__) + '/sinatra/mime_types'
require File.dirname(__FILE__) + '/sinatra/core_ext'
require File.dirname(__FILE__) + '/sinatra/halt_results'
require File.dirname(__FILE__) + '/sinatra/logger'
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = old_verbose
end
class String
def to_param
URI.escape(self)
end
def from_param
URI.unescape(self)
end
end
class Hash
def to_params
map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
end
def symbolize_keys
self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
end
def pass(*keys)
reject { |k,v| !keys.include?(k) }
end
end
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
class Array
def to_hash
self.inject({}) { |h, (k, v)| h[k] = v; h }
end
def to_proc
Proc.new { |*args| args.shift.__send__(self[0], args + self[1..-1]) }
end
end
class Proc
def block
self
end
end
module Enumerable
def eject(&block)
find { |e| result = block[e] and break result }
end
end
module Sinatra
extend self
attr_accessor :logger
def run
begin
puts "== Sinatra has taken the stage on port #{Sinatra.config[:port]} for #{Sinatra.config[:env]}"
require 'pp'
Rack::Handler::Mongrel.run(Sinatra, :Port => Sinatra.config[:port]) do |server|
trap(:INT) do
server.stop
puts "\n== Sinatra has ended his set (crowd applauds)"
end
end
rescue Errno::EADDRINUSE => e
puts "== Someone is already performing on port #{Sinatra.config[:port]}!"
end
end
class EventContext
attr_reader :request, :response, :route_params
def logger
Sinatra.logger
end
def initialize(request, response, route_params)
@request, @response, @route_params =
request, response, route_params
end
def params
@params ||= request.params.merge(route_params).symbolize_keys
end
def complete(b)
self.instance_eval(&b)
end
# redirect to another url It can be like /foo/bar
# for redirecting within your same app. Or it can
# be a fully qualified url to another site.
def redirect(url)
logger.info "Redirecting to: #{url}"
status(302)
headers.merge!('Location' => url)
return ''
end
def method_missing(name, *args)
if args.size == 1 && response.respond_to?("#{name}=")
response.send("#{name}=", args.first)
else
response.send(name, *args)
end
end
end
def setup_logger
self.logger = Sinatra::Logger.new(
config[:root] + "/#{Sinatra.config[:env]}.log"
)
end
def setup_default_events!
error 500 do
"<h2>#{$!.message}</h2>#{$!.backtrace.join("<br/>")}"
end
error 404 do
"<h1>Not Found</h1>"
end
end
def request_types
@request_types ||= [:get, :put, :post, :delete]
end
def routes
@routes ||= Hash.new do |hash, key|
hash[key] = [] if request_types.include?(key)
end
end
def filters
unless @filters
@filters = Hash.new do |hash, key|
hash[key] = Hash.new { |hash, key| hash[key] = [] }
end
end
@filters
end
def config
@config ||= default_config.dup
end
def config=(c)
@config = c
end
def development?
config[:env] == :development
end
def default_config
@default_config ||= {
:run => true,
:port => 4567,
:raise_errors => false,
:env => :development,
:root => Dir.pwd,
:default_static_mime_type => 'text/plain',
:default_params => { :format => 'html' }
}
end
def determine_route(verb, path)
routes[verb].eject { |r| r.match(path) } || routes[404]
end
def content_type_for(path)
ext = File.extname(path)[1..-1]
Sinatra.mime_types[ext] || config[:default_static_mime_type]
end
def serve_static_file(path)
path = Sinatra.config[:root] + '/public' + path
if File.file?(path)
headers = {
'Content-Type' => Array(content_type_for(path)),
'Content-Length' => Array(File.size(path))
}
[200, headers, File.read(path)]
end
end
def call(env)
reload! if Sinatra.development?
time = Time.now
request = Rack::Request.new(env)
if found = serve_static_file(request.path_info)
log_request_and_response(time, request, Rack::Response.new(found))
return found
end
response = Rack::Response.new
route = determine_route(
request.request_method.downcase.to_sym,
request.path_info
)
context = EventContext.new(request, response, route.params)
context.status = nil
begin
context = handle_with_filters(route.groups, context, &route.block)
context.status ||= route.default_status
rescue => e
raise e if config[:raise_errors]
route = Sinatra.routes[500]
context.status 500
context.body Array(context.instance_eval(&route.block))
ensure
log_request_and_response(time, request, response)
logger.flush
end
context.finish
end
def define_route(verb, path, options = {}, &b)
routes[verb] << route = Route.new(path, Array(options[:groups]), &b)
route
end
def define_error(code, &b)
routes[code] = Error.new(code, &b)
end
def define_filter(type, group, &b)
filters[type][group] << b
end
def reset!
self.config = nil
routes.clear
filters.clear
setup_default_events!
end
def reload!
reset!
self.config[:reloading] = true
load $0
self.config[:reloading] = false
end
protected
def log_request_and_response(time, request, response)
now = Time.now
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
logger.info %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} %
[
request.env["REMOTE_ADDR"] || "-",
request.env["REMOTE_USER"] || "-",
now.strftime("%d/%b/%Y %H:%M:%S"),
request.env["REQUEST_METHOD"],
request.env["PATH_INFO"],
request.env["QUERY_STRING"].empty? ?
"" :
"?" + request.env["QUERY_STRING"],
request.env["HTTP_VERSION"],
response.status.to_s[0..3].to_i,
(response.body.length.zero? ? "-" : response.body.length.to_s),
now - time
]
end
def handle_with_filters(groups, cx, &b)
caught = catch(:halt) do
filters_for(:before, groups).each { |x| cx.instance_eval(&x) }
[:complete, b]
end
caught = catch(:halt) do
caught.to_result(cx)
end
result = caught.to_result(cx) if caught
filters_for(:after, groups).each { |x| cx.instance_eval(&x) }
cx.body Array(result.to_s)
cx
end
def filters_for(type, groups)
filters[type][:all] + groups.inject([]) do |m, g|
m + filters[type][g]
end
end
class Route
URI_CHAR = '[^/?:,&#]'.freeze unless defined?(URI_CHAR)
PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
attr_reader :block, :path
def initialize(path, groups = :all, &b)
@path, @groups, @block = path, Array(groups), b
@param_keys = []
@struct = Struct.new(:path, :groups, :block, :params, :default_status)
regex = path.to_s.gsub(PARAM) do
@param_keys << $1.intern
"(#{URI_CHAR}+)"
end
if path =~ /:format$/
@pattern = /^#{regex}$/
else
@param_keys << :format
@pattern = /^#{regex}(?:\.(#{URI_CHAR}+))?$/
end
end
def match(path)
return nil unless path =~ @pattern
params = @param_keys.zip($~.captures.compact.map(&:from_param)).to_hash
@struct.new(@path, @groups, @block, include_format(params), 200)
end
def include_format(h)
h.delete(:format) unless h[:format]
Sinatra.config[:default_params].merge(h)
end
def pretty_print(pp)
pp.text "{Route: #{@pattern} : [#{@param_keys.map(&:inspect).join(",")}] : #{@groups.join(",")} }"
end
end
class Error
attr_reader :block
def initialize(code, &b)
@code, @block = code, b
end
def default_status
@code
end
def params; {}; end
def groups; []; end
end
end
def get(*paths, &b)
options = Hash === paths.last ? paths.pop : {}
paths.map { |path| Sinatra.define_route(:get, path, options, &b) }

218
lib/sinatra/application.rb Normal file
View file

@ -0,0 +1,218 @@
module Sinatra
extend self
def method_missing(name, *args, &b)
Application.send(name, *args, &b)
end
module Application
extend self
attr_accessor :logger
def run
begin
puts "== Sinatra has taken the stage on port #{Sinatra.config[:port]} for #{Sinatra.config[:env]}"
require 'pp'
Rack::Handler::Mongrel.run(Sinatra, :Port => Sinatra.config[:port]) do |server|
trap(:INT) do
server.stop
puts "\n== Sinatra has ended his set (crowd applauds)"
end
end
rescue Errno::EADDRINUSE => e
puts "== Someone is already performing on port #{Sinatra.config[:port]}!"
end
end
def setup_logger
self.logger = Sinatra::Logger.new(
config[:root] + "/#{Sinatra.config[:env]}.log"
)
end
def setup_default_events!
error 500 do
"<h2>#{$!.message}</h2>#{$!.backtrace.join("<br/>")}"
end
error 404 do
"<h1>Not Found</h1>"
end
end
def request_types
@request_types ||= [:get, :put, :post, :delete]
end
def routes
@routes ||= Hash.new do |hash, key|
hash[key] = [] if request_types.include?(key)
end
end
def filters
unless @filters
@filters = Hash.new do |hash, key|
hash[key] = Hash.new { |hash, key| hash[key] = [] }
end
end
@filters
end
def config
@config ||= default_config.dup
end
def config=(c)
@config = c
end
def development?
config[:env] == :development
end
def default_config
@default_config ||= {
:run => true,
:port => 4567,
:raise_errors => false,
:env => :development,
:root => Dir.pwd,
:default_static_mime_type => 'text/plain',
:default_params => { :format => 'html' }
}
end
def determine_route(verb, path)
routes[verb].eject { |r| r.match(path) } || routes[404]
end
def content_type_for(path)
ext = File.extname(path)[1..-1]
Sinatra.mime_types[ext] || config[:default_static_mime_type]
end
def serve_static_file(path)
path = Sinatra.config[:root] + '/public' + path
if File.file?(path)
headers = {
'Content-Type' => Array(content_type_for(path)),
'Content-Length' => Array(File.size(path))
}
[200, headers, File.read(path)]
end
end
def call(env)
reload! if Sinatra.development?
time = Time.now
request = Rack::Request.new(env)
if found = serve_static_file(request.path_info)
log_request_and_response(time, request, Rack::Response.new(found))
return found
end
response = Rack::Response.new
route = determine_route(
request.request_method.downcase.to_sym,
request.path_info
)
context = EventContext.new(request, response, route.params)
context.status = nil
begin
context = handle_with_filters(route.groups, context, &route.block)
context.status ||= route.default_status
rescue => e
raise e if config[:raise_errors]
route = Sinatra.routes[500]
context.status 500
context.body Array(context.instance_eval(&route.block))
ensure
log_request_and_response(time, request, response)
logger.flush
end
context.finish
end
def define_route(verb, path, options = {}, &b)
routes[verb] << route = Route.new(path, Array(options[:groups]), &b)
route
end
def define_error(code, &b)
routes[code] = Error.new(code, &b)
end
def define_filter(type, group, &b)
filters[type][group] << b
end
def reset!
self.config = nil
routes.clear
filters.clear
setup_default_events!
end
def reload!
reset!
self.config[:reloading] = true
load $0
self.config[:reloading] = false
end
protected
def log_request_and_response(time, request, response)
now = Time.now
# Common Log Format: http://httpd.apache.org/docs/1.3/logs.html#common
# lilith.local - - [07/Aug/2006 23:58:02] "GET / HTTP/1.1" 500 -
# %{%s - %s [%s] "%s %s%s %s" %d %s\n} %
logger.info %{%s - %s [%s] "%s %s%s %s" %d %s %0.4f\n} %
[
request.env["REMOTE_ADDR"] || "-",
request.env["REMOTE_USER"] || "-",
now.strftime("%d/%b/%Y %H:%M:%S"),
request.env["REQUEST_METHOD"],
request.env["PATH_INFO"],
request.env["QUERY_STRING"].empty? ?
"" :
"?" + request.env["QUERY_STRING"],
request.env["HTTP_VERSION"],
response.status.to_s[0..3].to_i,
(response.body.length.zero? ? "-" : response.body.length.to_s),
now - time
]
end
def handle_with_filters(groups, cx, &b)
caught = catch(:halt) do
filters_for(:before, groups).each { |x| cx.instance_eval(&x) }
[:complete, b]
end
caught = catch(:halt) do
caught.to_result(cx)
end
result = caught.to_result(cx) if caught
filters_for(:after, groups).each { |x| cx.instance_eval(&x) }
cx.body Array(result.to_s)
cx
end
def filters_for(type, groups)
filters[type][:all] + groups.inject([]) do |m, g|
m + filters[type][g]
end
end
end
end

58
lib/sinatra/core_ext.rb Normal file
View file

@ -0,0 +1,58 @@
def silence_warnings
old_verbose, $VERBOSE = $VERBOSE, nil
yield
ensure
$VERBOSE = old_verbose
end
class String
def to_param
URI.escape(self)
end
def from_param
URI.unescape(self)
end
end
class Hash
def to_params
map { |k,v| "#{k}=#{URI.escape(v)}" }.join('&')
end
def symbolize_keys
self.inject({}) { |h,(k,v)| h[k.to_sym] = v; h }
end
def pass(*keys)
reject { |k,v| !keys.include?(k) }
end
end
class Symbol
def to_proc
Proc.new { |*args| args.shift.__send__(self, *args) }
end
end
class Array
def to_hash
self.inject({}) { |h, (k, v)| h[k] = v; h }
end
def to_proc
Proc.new { |*args| args.shift.__send__(self[0], args + self[1..-1]) }
end
end
class Proc
def block
self
end
end
module Enumerable
def eject(&block)
find { |e| result = block[e] and break result }
end
end

17
lib/sinatra/error.rb Normal file
View file

@ -0,0 +1,17 @@
class Error
attr_reader :block
def initialize(code, &b)
@code, @block = code, b
end
def default_status
@code
end
def params; {}; end
def groups; []; end
end

View file

@ -0,0 +1,40 @@
class Sinatra::EventContext
attr_reader :request, :response, :route_params
def logger
Sinatra.logger
end
def initialize(request, response, route_params)
@request, @response, @route_params =
request, response, route_params
end
def params
@params ||= request.params.merge(route_params).symbolize_keys
end
def complete(b)
self.instance_eval(&b)
end
# redirect to another url It can be like /foo/bar
# for redirecting within your same app. Or it can
# be a fully qualified url to another site.
def redirect(url)
logger.info "Redirecting to: #{url}"
status(302)
headers.merge!('Location' => url)
return ''
end
def method_missing(name, *args)
if args.size == 1 && response.respond_to?("#{name}=")
response.send("#{name}=", args.first)
else
response.send(name, *args)
end
end
end

39
lib/sinatra/route.rb Normal file
View file

@ -0,0 +1,39 @@
class Sinatra::Route
URI_CHAR = '[^/?:,&#]'.freeze unless defined?(URI_CHAR)
PARAM = /:(#{URI_CHAR}+)/.freeze unless defined?(PARAM)
attr_reader :block, :path
def initialize(path, groups = :all, &b)
@path, @groups, @block = path, Array(groups), b
@param_keys = []
@struct = Struct.new(:path, :groups, :block, :params, :default_status)
regex = path.to_s.gsub(PARAM) do
@param_keys << $1.intern
"(#{URI_CHAR}+)"
end
if path =~ /:format$/
@pattern = /^#{regex}$/
else
@param_keys << :format
@pattern = /^#{regex}(?:\.(#{URI_CHAR}+))?$/
end
end
def match(path)
return nil unless path =~ @pattern
params = @param_keys.zip($~.captures.compact.map(&:from_param)).to_hash
@struct.new(@path, @groups, @block, include_format(params), 200)
end
def include_format(h)
h.delete(:format) unless h[:format]
Sinatra.config[:default_params].merge(h)
end
def pretty_print(pp)
pp.text "{Route: #{@pattern} : [#{@param_keys.map(&:inspect).join(",")}] : #{@groups.join(",")} }"
end
end