sinatra 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sinatra might be problematic. Click here for more details.
- data/CHANGELOG +1 -0
- data/LICENSE +22 -0
- data/Manifest +53 -0
- data/README +99 -0
- data/RakeFile +36 -0
- data/Rakefile +36 -0
- data/examples/hello/hello.rb +30 -0
- data/examples/hello/views/hello.erb +1 -0
- data/examples/todo/todo.rb +38 -0
- data/files/default_index.erb +42 -0
- data/files/error.erb +9 -0
- data/files/logo.png +0 -0
- data/files/not_found.erb +52 -0
- data/lib/sinatra.rb +47 -0
- data/lib/sinatra/context.rb +88 -0
- data/lib/sinatra/context/renderer.rb +75 -0
- data/lib/sinatra/core_ext/array.rb +5 -0
- data/lib/sinatra/core_ext/class.rb +49 -0
- data/lib/sinatra/core_ext/hash.rb +7 -0
- data/lib/sinatra/core_ext/kernel.rb +16 -0
- data/lib/sinatra/core_ext/metaid.rb +18 -0
- data/lib/sinatra/core_ext/module.rb +11 -0
- data/lib/sinatra/core_ext/symbol.rb +5 -0
- data/lib/sinatra/dispatcher.rb +27 -0
- data/lib/sinatra/dsl.rb +163 -0
- data/lib/sinatra/environment.rb +15 -0
- data/lib/sinatra/event.rb +184 -0
- data/lib/sinatra/irb.rb +55 -0
- data/lib/sinatra/loader.rb +31 -0
- data/lib/sinatra/logger.rb +22 -0
- data/lib/sinatra/options.rb +43 -0
- data/lib/sinatra/rack_ext/request.rb +15 -0
- data/lib/sinatra/route.rb +65 -0
- data/lib/sinatra/server.rb +54 -0
- data/lib/sinatra/sessions.rb +21 -0
- data/lib/sinatra/test_methods.rb +55 -0
- data/sinatra.gemspec +60 -0
- data/site/index.htm +100 -0
- data/site/index.html +100 -0
- data/site/logo.png +0 -0
- data/test/helper.rb +17 -0
- data/test/sinatra/dispatcher_test.rb +91 -0
- data/test/sinatra/event_test.rb +37 -0
- data/test/sinatra/renderer_test.rb +47 -0
- data/test/sinatra/request_test.rb +21 -0
- data/test/sinatra/route_test.rb +21 -0
- data/test/sinatra/static_files/foo.txt +1 -0
- data/test/sinatra/static_files_test.rb +41 -0
- data/test/sinatra/url_test.rb +18 -0
- data/vendor/erb/init.rb +3 -0
- data/vendor/erb/lib/erb.rb +41 -0
- data/vendor/haml/init.rb +3 -0
- data/vendor/haml/lib/haml.rb +41 -0
- metadata +121 -0
@@ -0,0 +1,15 @@
|
|
1
|
+
module Sinatra
|
2
|
+
module Environment
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def prepare
|
6
|
+
Options.parse!(ARGV)
|
7
|
+
end
|
8
|
+
|
9
|
+
def prepare_loggers(logger = Logger.new(open(Options.log_file, 'w')))
|
10
|
+
[Server, EventContext, Event, Dispatcher].each do |klass|
|
11
|
+
klass.logger = logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
module Sinatra
|
2
|
+
|
3
|
+
module EventManager # :nodoc:
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def reset!
|
7
|
+
@events.clear if @events
|
8
|
+
end
|
9
|
+
|
10
|
+
def events
|
11
|
+
@events || []
|
12
|
+
end
|
13
|
+
|
14
|
+
def register_event(event)
|
15
|
+
(@events ||= []) << event
|
16
|
+
end
|
17
|
+
|
18
|
+
def determine_event(verb, path, if_nil = :present_error)
|
19
|
+
event = events.find(method(if_nil)) do |e|
|
20
|
+
e.verb == verb && e.recognize(path)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def present_error
|
25
|
+
determine_event(:get, '404', :not_found)
|
26
|
+
end
|
27
|
+
|
28
|
+
def not_found
|
29
|
+
Event.new(:get, 'not_found', false) do
|
30
|
+
status 404
|
31
|
+
|
32
|
+
if request.path_info == '/' && request.request_method == 'GET'
|
33
|
+
erb :default_index, :views_directory => SINATRA_ROOT + '/files'
|
34
|
+
else
|
35
|
+
erb :not_found, :views_directory => SINATRA_ROOT + '/files'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
class Event # :nodoc:
|
43
|
+
|
44
|
+
cattr_accessor :logger
|
45
|
+
cattr_accessor :after_filters
|
46
|
+
|
47
|
+
self.after_filters = []
|
48
|
+
|
49
|
+
def self.after_attend(filter)
|
50
|
+
after_filters << filter
|
51
|
+
end
|
52
|
+
|
53
|
+
after_attend :log_event
|
54
|
+
|
55
|
+
attr_reader :path, :verb
|
56
|
+
|
57
|
+
def initialize(verb, path, register = true, &block)
|
58
|
+
@verb = verb
|
59
|
+
@path = path
|
60
|
+
@route = Route.new(path)
|
61
|
+
@block = block
|
62
|
+
EventManager.register_event(self) if register
|
63
|
+
end
|
64
|
+
|
65
|
+
def attend(request)
|
66
|
+
request.params.merge!(@route.params)
|
67
|
+
context = EventContext.new(request)
|
68
|
+
begin
|
69
|
+
result = context.instance_eval(&@block) if @block
|
70
|
+
context.body context.body || result || ''
|
71
|
+
rescue => e
|
72
|
+
context.error e
|
73
|
+
end
|
74
|
+
run_through_after_filters(context)
|
75
|
+
context
|
76
|
+
end
|
77
|
+
alias :call :attend
|
78
|
+
|
79
|
+
def recognize(path)
|
80
|
+
@route.recognize(path)
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def run_through_after_filters(context)
|
86
|
+
after_filters.each { |filter| context.send(filter) }
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
90
|
+
|
91
|
+
class StaticEvent < Event # :nodoc:
|
92
|
+
|
93
|
+
def initialize(path, root, register = true)
|
94
|
+
@root = root
|
95
|
+
super(:get, path, register)
|
96
|
+
end
|
97
|
+
|
98
|
+
def recognize(path)
|
99
|
+
File.exists?(physical_path_for(path))
|
100
|
+
end
|
101
|
+
|
102
|
+
def physical_path_for(path)
|
103
|
+
path.gsub(/^#{@path}/, @root)
|
104
|
+
end
|
105
|
+
|
106
|
+
def attend(request)
|
107
|
+
@filename = physical_path_for(request.path_info)
|
108
|
+
context = EventContext.new(request)
|
109
|
+
context.body self
|
110
|
+
context.header 'Content-Type' => MIME_TYPES[File.extname(@filename)[1..-1]]
|
111
|
+
context.header 'Content-Length' => File.size(@filename).to_s
|
112
|
+
context
|
113
|
+
end
|
114
|
+
|
115
|
+
def each
|
116
|
+
File.open(@filename, "rb") do |file|
|
117
|
+
while part = file.read(8192)
|
118
|
+
yield part
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
# :stopdoc:
|
124
|
+
# From WEBrick.
|
125
|
+
MIME_TYPES = {
|
126
|
+
"ai" => "application/postscript",
|
127
|
+
"asc" => "text/plain",
|
128
|
+
"avi" => "video/x-msvideo",
|
129
|
+
"bin" => "application/octet-stream",
|
130
|
+
"bmp" => "image/bmp",
|
131
|
+
"class" => "application/octet-stream",
|
132
|
+
"cer" => "application/pkix-cert",
|
133
|
+
"crl" => "application/pkix-crl",
|
134
|
+
"crt" => "application/x-x509-ca-cert",
|
135
|
+
#"crl" => "application/x-pkcs7-crl",
|
136
|
+
"css" => "text/css",
|
137
|
+
"dms" => "application/octet-stream",
|
138
|
+
"doc" => "application/msword",
|
139
|
+
"dvi" => "application/x-dvi",
|
140
|
+
"eps" => "application/postscript",
|
141
|
+
"etx" => "text/x-setext",
|
142
|
+
"exe" => "application/octet-stream",
|
143
|
+
"gif" => "image/gif",
|
144
|
+
"htm" => "text/html",
|
145
|
+
"html" => "text/html",
|
146
|
+
"jpe" => "image/jpeg",
|
147
|
+
"jpeg" => "image/jpeg",
|
148
|
+
"jpg" => "image/jpeg",
|
149
|
+
"lha" => "application/octet-stream",
|
150
|
+
"lzh" => "application/octet-stream",
|
151
|
+
"mov" => "video/quicktime",
|
152
|
+
"mpe" => "video/mpeg",
|
153
|
+
"mpeg" => "video/mpeg",
|
154
|
+
"mpg" => "video/mpeg",
|
155
|
+
"pbm" => "image/x-portable-bitmap",
|
156
|
+
"pdf" => "application/pdf",
|
157
|
+
"pgm" => "image/x-portable-graymap",
|
158
|
+
"png" => "image/png",
|
159
|
+
"pnm" => "image/x-portable-anymap",
|
160
|
+
"ppm" => "image/x-portable-pixmap",
|
161
|
+
"ppt" => "application/vnd.ms-powerpoint",
|
162
|
+
"ps" => "application/postscript",
|
163
|
+
"qt" => "video/quicktime",
|
164
|
+
"ras" => "image/x-cmu-raster",
|
165
|
+
"rb" => "text/plain",
|
166
|
+
"rd" => "text/plain",
|
167
|
+
"rtf" => "application/rtf",
|
168
|
+
"sgm" => "text/sgml",
|
169
|
+
"sgml" => "text/sgml",
|
170
|
+
"tif" => "image/tiff",
|
171
|
+
"tiff" => "image/tiff",
|
172
|
+
"txt" => "text/plain",
|
173
|
+
"xbm" => "image/x-xbitmap",
|
174
|
+
"xls" => "application/vnd.ms-excel",
|
175
|
+
"xml" => "text/xml",
|
176
|
+
"xpm" => "image/x-xpixmap",
|
177
|
+
"xwd" => "image/x-xwindowdump",
|
178
|
+
"zip" => "application/zip",
|
179
|
+
}
|
180
|
+
# :startdoc:
|
181
|
+
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
data/lib/sinatra/irb.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Sinatra
|
2
|
+
|
3
|
+
# Sinatra Irb is entered via <tt>ruby myapp.rb -c</tt> (replace myapp.rb with your app filename)
|
4
|
+
#
|
5
|
+
# Be sure to also check out Sinatra::TestMethods for more cool stuff when your in Irb
|
6
|
+
#
|
7
|
+
module Irb
|
8
|
+
extend self
|
9
|
+
|
10
|
+
# taken from merb
|
11
|
+
def start! #:nodoc:
|
12
|
+
|
13
|
+
Object.send(:include, TestMethods) # added to allow post_to in console
|
14
|
+
|
15
|
+
Object.class_eval do
|
16
|
+
# Reload all Sinatra and App specific files
|
17
|
+
def reload!
|
18
|
+
Loader.reload!
|
19
|
+
end
|
20
|
+
|
21
|
+
# Show the +body+ with result info in your text editor!!! Great Job!
|
22
|
+
def show!(editor = nil)
|
23
|
+
editor = editor || ENV['EDITOR']
|
24
|
+
IO.popen(editor, 'w') do |f|
|
25
|
+
f.puts "<!--"
|
26
|
+
f.puts result_info
|
27
|
+
f.puts "-->"
|
28
|
+
f.puts
|
29
|
+
f.puts body
|
30
|
+
end
|
31
|
+
end
|
32
|
+
alias :mate :show!
|
33
|
+
|
34
|
+
def result_info #:nodoc:
|
35
|
+
info = <<-end_info
|
36
|
+
# Status: #{status}
|
37
|
+
# Headers: #{headers.inspect}
|
38
|
+
end_info
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
ARGV.clear # Avoid passing args to IRB
|
43
|
+
require 'irb'
|
44
|
+
require 'irb/completion'
|
45
|
+
def exit
|
46
|
+
exit!
|
47
|
+
end
|
48
|
+
if File.exists? ".irbrc"
|
49
|
+
ENV['IRBRC'] = ".irbrc"
|
50
|
+
end
|
51
|
+
IRB.start
|
52
|
+
exit!
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module Loader
|
5
|
+
extend self
|
6
|
+
|
7
|
+
def reload!
|
8
|
+
silence_warnings do
|
9
|
+
EventManager.reset!
|
10
|
+
load_files loaded_files
|
11
|
+
load $0
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def load_files(*files)
|
16
|
+
files = files.flatten
|
17
|
+
files = files.first if files.first.is_a? Set
|
18
|
+
|
19
|
+
files.each do |file|
|
20
|
+
file = File.expand_path(file)
|
21
|
+
load file
|
22
|
+
loaded_files << file
|
23
|
+
end
|
24
|
+
end
|
25
|
+
alias_method :load_file, :load_files
|
26
|
+
|
27
|
+
def loaded_files
|
28
|
+
@loaded_files ||= Set.new
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Sinatra
|
2
|
+
|
3
|
+
class Logger
|
4
|
+
|
5
|
+
def initialize(steam)
|
6
|
+
@stream = steam
|
7
|
+
end
|
8
|
+
|
9
|
+
%w(info debug error warn).each do |n|
|
10
|
+
define_method n do |message|
|
11
|
+
@stream.puts message
|
12
|
+
@stream.flush
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def exception(e)
|
17
|
+
error "#{e.message}:\n\t#{e.backtrace.join("\n\t")}"
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
module Options
|
5
|
+
extend self
|
6
|
+
|
7
|
+
attr_with_default :port, 4567
|
8
|
+
attr_with_default :environment, :development
|
9
|
+
attr_with_default :console, nil
|
10
|
+
|
11
|
+
def parse!(args)
|
12
|
+
return if @environment == :test
|
13
|
+
OptionParser.new do |opts|
|
14
|
+
opts.on '-p port', '--port port', 'Set the port (default is 4567)' do |port|
|
15
|
+
@port = port
|
16
|
+
end
|
17
|
+
opts.on '-e environment', 'Set the environment (default if development)' do |env|
|
18
|
+
@environment = env.intern
|
19
|
+
end
|
20
|
+
opts.on '-c', '--console', 'Run in console mode' do
|
21
|
+
@console = true
|
22
|
+
end
|
23
|
+
opts.on '-h', '--help', '-?', 'Show this message' do
|
24
|
+
puts opts
|
25
|
+
exit!
|
26
|
+
end
|
27
|
+
end.parse!(ARGV)
|
28
|
+
end
|
29
|
+
|
30
|
+
def log_file
|
31
|
+
# TODO find a better way that this
|
32
|
+
if File.basename($0, '.rb') == 'rake_test_loader' # hack to satisfy rake
|
33
|
+
'%s.log' % environment
|
34
|
+
else
|
35
|
+
File.dirname($0) + ('/%s.log' % environment)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def set_environment(env)
|
40
|
+
@environment = env
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module Sinatra
|
2
|
+
|
3
|
+
class Route
|
4
|
+
|
5
|
+
SYMBOL_FIND = /:[a-z_]+/.freeze
|
6
|
+
PARENTHETICAL_SEGMENT_STRING = "([^\/.,;?]+)".freeze
|
7
|
+
|
8
|
+
attr_reader :regex, :params
|
9
|
+
|
10
|
+
def initialize(template)
|
11
|
+
@template = template.to_s.strip
|
12
|
+
@default_params = { :format => 'html' }
|
13
|
+
@params = {}
|
14
|
+
extract_keys
|
15
|
+
genereate_route
|
16
|
+
end
|
17
|
+
|
18
|
+
def recognize(path)
|
19
|
+
@params.clear
|
20
|
+
|
21
|
+
param_values = path.match(@regex).captures.compact rescue nil
|
22
|
+
|
23
|
+
if param_values
|
24
|
+
keys = @keys.size < param_values.size ? @keys.concat([:format]) : @keys
|
25
|
+
@params = @default_params.merge(@keys.zip(param_values).to_hash)
|
26
|
+
true
|
27
|
+
else
|
28
|
+
false
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def extract_keys
|
35
|
+
@keys = @template.scan(SYMBOL_FIND).map { |raw| eval(raw) }
|
36
|
+
@keys
|
37
|
+
end
|
38
|
+
|
39
|
+
def genereate_route_without_format
|
40
|
+
template = @template.dup
|
41
|
+
template.gsub!(/\.:format$/, '')
|
42
|
+
to_regex_route(template)
|
43
|
+
end
|
44
|
+
|
45
|
+
def genereate_route_with_format
|
46
|
+
template = @template.dup
|
47
|
+
if template =~ /\.:format$|\.([\w\d]+)$/
|
48
|
+
@default_params[:format] = $1 if $1
|
49
|
+
else
|
50
|
+
template << '.:format'
|
51
|
+
end
|
52
|
+
to_regex_route(template)
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_regex_route(template)
|
56
|
+
/^#{template.gsub(/\./, '\.').gsub(SYMBOL_FIND, PARENTHETICAL_SEGMENT_STRING)}$/
|
57
|
+
end
|
58
|
+
|
59
|
+
def genereate_route
|
60
|
+
@regex = Regexp.union(genereate_route_without_format, genereate_route_with_format)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
class Server
|
6
|
+
|
7
|
+
cattr_accessor :logger
|
8
|
+
cattr_accessor :running
|
9
|
+
|
10
|
+
def start
|
11
|
+
begin
|
12
|
+
tail_thread = tail(Options.log_file)
|
13
|
+
Rack::Handler::Mongrel.run(Sinatra::Session::Cookie.new(Dispatcher.new), :Port => Options.port) do |server|
|
14
|
+
puts "== Sinatra has taken the stage on port #{server.port}!"
|
15
|
+
trap("INT") do
|
16
|
+
server.stop
|
17
|
+
self.class.running = false
|
18
|
+
puts "\n== Sinatra has ended his set (crowd applauds)"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
self.class.running = true
|
22
|
+
rescue => e
|
23
|
+
logger.exception e
|
24
|
+
ensure
|
25
|
+
tail_thread.kill if tail_thread
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def tail(log_file)
|
32
|
+
FileUtils.touch(log_file)
|
33
|
+
cursor = File.size(log_file)
|
34
|
+
last_checked = Time.now
|
35
|
+
tail_thread = Thread.new do
|
36
|
+
File.open(log_file, 'r') do |f|
|
37
|
+
loop do
|
38
|
+
f.seek cursor
|
39
|
+
if f.mtime > last_checked
|
40
|
+
last_checked = f.mtime
|
41
|
+
contents = f.read
|
42
|
+
cursor += contents.length
|
43
|
+
print contents
|
44
|
+
end
|
45
|
+
sleep 1
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
tail_thread
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|