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,88 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/context/renderer'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
class EventContext
|
6
|
+
|
7
|
+
cattr_accessor :logger
|
8
|
+
attr_reader :request
|
9
|
+
|
10
|
+
include Sinatra::Renderer
|
11
|
+
|
12
|
+
def initialize(request) #:nodoc:
|
13
|
+
@request = request
|
14
|
+
@headers = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
# Sets or returns the status
|
18
|
+
def status(value = nil)
|
19
|
+
@status = value if value
|
20
|
+
@status || 200
|
21
|
+
end
|
22
|
+
|
23
|
+
# Sets or returns the body
|
24
|
+
# *Usage*
|
25
|
+
# body 'test'
|
26
|
+
# or
|
27
|
+
# body do
|
28
|
+
# 'test'
|
29
|
+
# end
|
30
|
+
# both are the same
|
31
|
+
#
|
32
|
+
def body(value = nil, &block)
|
33
|
+
@body = value if value
|
34
|
+
@body = block.call if block
|
35
|
+
@body
|
36
|
+
end
|
37
|
+
|
38
|
+
# Renders an exception to +body+ and sets status to 500
|
39
|
+
def error(value = nil)
|
40
|
+
if value
|
41
|
+
status 500
|
42
|
+
@error = value
|
43
|
+
erb :error, :views_directory => SINATRA_ROOT + '/files/'
|
44
|
+
end
|
45
|
+
@error
|
46
|
+
end
|
47
|
+
|
48
|
+
# Sets or returns response headers
|
49
|
+
#
|
50
|
+
# *Usage*
|
51
|
+
# header 'Content-Type' => 'text/html'
|
52
|
+
# header 'Foo' => 'Bar'
|
53
|
+
# or
|
54
|
+
# headers 'Content-Type' => 'text/html',
|
55
|
+
# 'Foo' => 'Bar'
|
56
|
+
#
|
57
|
+
# Whatever blows your hair back
|
58
|
+
def headers(value = nil)
|
59
|
+
@headers.merge!(value) if value
|
60
|
+
@headers
|
61
|
+
end
|
62
|
+
alias :header :headers
|
63
|
+
|
64
|
+
# Returns a Hash of session data. Keys are symbolized
|
65
|
+
def session
|
66
|
+
request.env['rack.session']
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns a Hash of params. Keys are symbolized
|
70
|
+
def params
|
71
|
+
@params ||= @request.params.symbolize_keys
|
72
|
+
end
|
73
|
+
|
74
|
+
# Redirect to a url
|
75
|
+
def redirect(path)
|
76
|
+
logger.info "Redirecting to: #{path}"
|
77
|
+
status 302
|
78
|
+
header 'Location' => path
|
79
|
+
end
|
80
|
+
|
81
|
+
def log_event #:nodoc:
|
82
|
+
logger.info "#{request.request_method} #{request.path_info} | Status: #{status} | Params: #{params.inspect}"
|
83
|
+
logger.exception(error) if error
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
Layouts = Hash.new # :nodoc:
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
# The magic or rendering happens here. This is included in Sinatra::EventContext on load.
|
6
|
+
#
|
7
|
+
# These methods are the foundation for Sinatra::Erb and Sinatra::Haml and allow you to quickly
|
8
|
+
# create custom wrappers for your favorite rendering engines outside of erb and haml.
|
9
|
+
|
10
|
+
module Renderer
|
11
|
+
|
12
|
+
DEFAULT_OPTIONS = {
|
13
|
+
:views_directory => 'views',
|
14
|
+
:layout => :layout
|
15
|
+
}
|
16
|
+
|
17
|
+
|
18
|
+
# Renders templates from a string or file and handles their layouts:
|
19
|
+
#
|
20
|
+
# Example:
|
21
|
+
# module MyRenderer
|
22
|
+
# def my(template, options, &layout)
|
23
|
+
# render(template, :my, options, &layout)
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# def render_my(template)
|
27
|
+
# template.capitalize # It capitalizes templates!!!!! WOW!
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
# Sinatra::EventContext.send :include, MyRenderer
|
31
|
+
#
|
32
|
+
# get '/' do
|
33
|
+
# my "something"
|
34
|
+
# end
|
35
|
+
#
|
36
|
+
# get_it '/' # => 'Something'
|
37
|
+
#
|
38
|
+
# The second method is named render_extname. render will call this dynamicly
|
39
|
+
#
|
40
|
+
# paramaters:
|
41
|
+
# * +template+ If String, renders the string. If Symbol, reads from file with the basename of the Symbol; uses +renderer+ for extension.
|
42
|
+
# * +renderer+ A symbol defining the render_ method to call and the extension append to +template+ when looking in the +views_directory+
|
43
|
+
# * +options+ An optional Hash of options (see next section)
|
44
|
+
#
|
45
|
+
# options:
|
46
|
+
# * +:views_directory+ Allows you to override the default 'views' directory an look for the template in another
|
47
|
+
# * +:layout+ Which layout to use (see Sinatra::Dsl). false to force a render with no layout. Defaults to :default layout
|
48
|
+
#
|
49
|
+
def render(template, renderer, options = {})
|
50
|
+
options = DEFAULT_OPTIONS.merge(options)
|
51
|
+
|
52
|
+
layout = block_given? ? yield : Layouts[options[:layout]]
|
53
|
+
|
54
|
+
result_method = 'render_%s' % renderer
|
55
|
+
|
56
|
+
if layout
|
57
|
+
send(result_method, layout) { send(result_method, determine_template(template, renderer, options)) }
|
58
|
+
else
|
59
|
+
send(result_method, determine_template(template, renderer, options))
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
protected
|
64
|
+
|
65
|
+
def determine_template(template, ext, options)
|
66
|
+
if template.is_a?(Symbol)
|
67
|
+
File.read("%s/%s.%s" % [options[:views_directory], template, ext])
|
68
|
+
else
|
69
|
+
template
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# Extends the class object with class and instance accessors for class attributes,
|
2
|
+
# just like the native attr* accessors for instance attributes.
|
3
|
+
class Class # :nodoc:
|
4
|
+
def cattr_reader(*syms)
|
5
|
+
syms.flatten.each do |sym|
|
6
|
+
next if sym.is_a?(Hash)
|
7
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
8
|
+
unless defined? @@#{sym}
|
9
|
+
@@#{sym} = nil
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.#{sym}
|
13
|
+
@@#{sym}
|
14
|
+
end
|
15
|
+
|
16
|
+
def #{sym}
|
17
|
+
@@#{sym}
|
18
|
+
end
|
19
|
+
EOS
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def cattr_writer(*syms)
|
24
|
+
options = syms.last.is_a?(Hash) ? syms.pop : {}
|
25
|
+
syms.flatten.each do |sym|
|
26
|
+
class_eval(<<-EOS, __FILE__, __LINE__)
|
27
|
+
unless defined? @@#{sym}
|
28
|
+
@@#{sym} = nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.#{sym}=(obj)
|
32
|
+
@@#{sym} = obj
|
33
|
+
end
|
34
|
+
|
35
|
+
#{"
|
36
|
+
def #{sym}=(obj)
|
37
|
+
@@#{sym} = obj
|
38
|
+
end
|
39
|
+
" unless options[:instance_writer] == false }
|
40
|
+
EOS
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def cattr_accessor(*syms)
|
45
|
+
cattr_reader(*syms)
|
46
|
+
cattr_writer(*syms)
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Kernel
|
2
|
+
# (Taken from ActiveSupport)
|
3
|
+
# Sets $VERBOSE to nil for the duration of the block and back to its original value afterwards.
|
4
|
+
#
|
5
|
+
# silence_warnings do
|
6
|
+
# value = noisy_call # no warning voiced
|
7
|
+
# end
|
8
|
+
#
|
9
|
+
# noisy_call # warning voiced
|
10
|
+
def silence_warnings
|
11
|
+
old_verbose, $VERBOSE = $VERBOSE, nil
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
$VERBOSE = old_verbose
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# Compliments to why for this:
|
2
|
+
# http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
|
3
|
+
|
4
|
+
class Object
|
5
|
+
# The hidden singleton lurks behind everyone
|
6
|
+
def metaclass; class << self; self; end; end
|
7
|
+
def meta_eval &blk; metaclass.instance_eval &blk; end
|
8
|
+
|
9
|
+
# Adds methods to a metaclass
|
10
|
+
def meta_def name, &blk
|
11
|
+
meta_eval { define_method name, &blk }
|
12
|
+
end
|
13
|
+
|
14
|
+
# Defines an instance method within a class
|
15
|
+
def class_def name, &blk
|
16
|
+
class_eval { define_method name, &blk }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Sinatra
|
2
|
+
|
3
|
+
class Dispatcher
|
4
|
+
|
5
|
+
cattr_accessor :logger
|
6
|
+
|
7
|
+
def default_headers
|
8
|
+
{ 'Content-Type' => 'text/html' }
|
9
|
+
end
|
10
|
+
|
11
|
+
def call(env)
|
12
|
+
Loader.reload! if Options.environment == :development
|
13
|
+
|
14
|
+
@request = Rack::Request.new(env)
|
15
|
+
|
16
|
+
event = EventManager.determine_event(
|
17
|
+
@request.request_method.downcase.intern,
|
18
|
+
@request.path_info
|
19
|
+
)
|
20
|
+
|
21
|
+
result = event.attend(@request)
|
22
|
+
[result.status, default_headers.merge(result.headers), result.body]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/lib/sinatra/dsl.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
|
2
|
+
module Sinatra
|
3
|
+
|
4
|
+
module Dsl
|
5
|
+
|
6
|
+
# Define an Event that responds to a +path+ on GET method
|
7
|
+
#
|
8
|
+
# The +path+ can be a template (i.e. '/:foo/bar/:baz'). When recognized, it will add <tt>:foo</tt> and <tt>:baz</tt> to +params+ with their values.
|
9
|
+
#
|
10
|
+
# Example:
|
11
|
+
# # Going RESTful
|
12
|
+
#
|
13
|
+
# get '/' do
|
14
|
+
# .. show stuff ..
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# post '/' do
|
18
|
+
# .. add stuff ..
|
19
|
+
# redirect '/'
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# put '/:id' do
|
23
|
+
# .. update params[:id] ..
|
24
|
+
# redirect '/'
|
25
|
+
# end
|
26
|
+
#
|
27
|
+
# delete '/:id' do
|
28
|
+
# .. delete params[:id] ..
|
29
|
+
# redirect '/'
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
|
33
|
+
#
|
34
|
+
def get(path, &block)
|
35
|
+
Sinatra::Event.new(:get, path, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Same as get but responds to POST
|
39
|
+
def post(path, &block)
|
40
|
+
Sinatra::Event.new(:post, path, &block)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Same as get but responds to PUT
|
44
|
+
#
|
45
|
+
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
|
46
|
+
def put(path, &block)
|
47
|
+
Sinatra::Event.new(:put, path, &block)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Same as get but responds to DELETE
|
51
|
+
#
|
52
|
+
# BIG NOTE: PUT and DELETE are trigged when POSTing to their paths with a <tt>_method</tt> param whose value is PUT or DELETE
|
53
|
+
def delete(path, &block)
|
54
|
+
Sinatra::Event.new(:delete, path, &block)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Run given block after each Event's execution
|
58
|
+
# Example:
|
59
|
+
# after_attend do
|
60
|
+
# logger.debug "After event attend!"
|
61
|
+
# end
|
62
|
+
def after_attend(filter_name = nil, &block)
|
63
|
+
Sinatra::Event.after_attend(filter_name, &block)
|
64
|
+
end
|
65
|
+
|
66
|
+
# Add methods to each event for use during execution
|
67
|
+
#
|
68
|
+
# Example:
|
69
|
+
# helpers do
|
70
|
+
# def foo
|
71
|
+
# 'foo!'
|
72
|
+
# end
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# get '/bar' do
|
76
|
+
# foo
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# get_it '/bar' # => 'foo!'
|
80
|
+
#
|
81
|
+
def helpers(&block)
|
82
|
+
Sinatra::EventContext.class_eval(&block)
|
83
|
+
end
|
84
|
+
|
85
|
+
# Maps a path to a physical directory containing static files
|
86
|
+
#
|
87
|
+
# Example:
|
88
|
+
# static '/p', 'public'
|
89
|
+
#
|
90
|
+
def static(path, root)
|
91
|
+
Sinatra::StaticEvent.new(path, root)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Execute block if in development mode (Used for configuration)
|
95
|
+
def development
|
96
|
+
yield if Sinatra::Options.environment == :development
|
97
|
+
end
|
98
|
+
|
99
|
+
# Execute block if in production mode (Used for configuration)
|
100
|
+
def production
|
101
|
+
yield if Sinatra::Options.environment == :production
|
102
|
+
end
|
103
|
+
|
104
|
+
# Execute block if in test mode (Used for configuration)
|
105
|
+
def test
|
106
|
+
yield if Sinatra::Options.environment == :test
|
107
|
+
end
|
108
|
+
|
109
|
+
# Define named layouts (default name is <tt>:layout</tt>)
|
110
|
+
#
|
111
|
+
# Examples:
|
112
|
+
# # Default layout in Erb
|
113
|
+
# layout do
|
114
|
+
# '-- <%= yield %> --'
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# # Named layout in Haml
|
118
|
+
# layout :for_haml do
|
119
|
+
# '== XXXX #{yield} XXXX'
|
120
|
+
# end
|
121
|
+
#
|
122
|
+
# # Loads layout named <tt>:"foo.erb"</tt> from file (default behaviour if block is omitted)
|
123
|
+
# layout 'foo.erb' # looks for foo.erb. This is odd an is being re-thought
|
124
|
+
#
|
125
|
+
# def layout(name = :layout, options = {})
|
126
|
+
# Layouts[name] = unless block_given?
|
127
|
+
# File.read("%s/%s" % [options[:views_directory] || 'views', name])
|
128
|
+
# else
|
129
|
+
# yield
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# Cool trick:
|
134
|
+
#
|
135
|
+
# # Send a one-time layout to renderer method
|
136
|
+
# get '/cooltrick' do
|
137
|
+
# erb 'wicked' do
|
138
|
+
# 'Cool <%= yield %> Trick'
|
139
|
+
# end
|
140
|
+
# end
|
141
|
+
#
|
142
|
+
# get_it '/cooltrick' # => 'Cool wicked Trick'
|
143
|
+
#
|
144
|
+
def layout(name = :layout, options = {})
|
145
|
+
Layouts[name] = unless block_given?
|
146
|
+
File.read("%s/%s" % [options[:views_directory] || 'views', name])
|
147
|
+
else
|
148
|
+
yield
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
# Turn sessions <tt>:on</tt> or <tt>:off</tt>
|
153
|
+
#
|
154
|
+
# NOTE: There is currently no way to turn it on or off per Event... patches anyone?)
|
155
|
+
def sessions(on_off)
|
156
|
+
Sinatra::Session::Cookie.use = on_off
|
157
|
+
end
|
158
|
+
|
159
|
+
end
|
160
|
+
|
161
|
+
end
|
162
|
+
|
163
|
+
include Sinatra::Dsl
|