pirj-sinatra-contrib 1.3.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/LICENSE +20 -0
- data/README.md +135 -0
- data/Rakefile +61 -0
- data/ideas.md +29 -0
- data/lib/sinatra/capture.rb +42 -0
- data/lib/sinatra/config_file.rb +151 -0
- data/lib/sinatra/content_for.rb +111 -0
- data/lib/sinatra/contrib.rb +39 -0
- data/lib/sinatra/contrib/all.rb +2 -0
- data/lib/sinatra/contrib/setup.rb +53 -0
- data/lib/sinatra/contrib/version.rb +45 -0
- data/lib/sinatra/decompile.rb +113 -0
- data/lib/sinatra/engine_tracking.rb +96 -0
- data/lib/sinatra/extension.rb +95 -0
- data/lib/sinatra/json.rb +134 -0
- data/lib/sinatra/link_header.rb +132 -0
- data/lib/sinatra/namespace.rb +282 -0
- data/lib/sinatra/reloader.rb +384 -0
- data/lib/sinatra/respond_with.rb +245 -0
- data/lib/sinatra/streaming.rb +267 -0
- data/lib/sinatra/test_helpers.rb +87 -0
- data/sinatra-contrib.gemspec +121 -0
- data/spec/capture_spec.rb +80 -0
- data/spec/config_file/key_value.yml +6 -0
- data/spec/config_file/missing_env.yml +4 -0
- data/spec/config_file/with_envs.yml +7 -0
- data/spec/config_file/with_nested_envs.yml +11 -0
- data/spec/config_file_spec.rb +44 -0
- data/spec/content_for/different_key.erb +1 -0
- data/spec/content_for/different_key.erubis +1 -0
- data/spec/content_for/different_key.haml +2 -0
- data/spec/content_for/different_key.slim +2 -0
- data/spec/content_for/layout.erb +1 -0
- data/spec/content_for/layout.erubis +1 -0
- data/spec/content_for/layout.haml +1 -0
- data/spec/content_for/layout.slim +1 -0
- data/spec/content_for/multiple_blocks.erb +4 -0
- data/spec/content_for/multiple_blocks.erubis +4 -0
- data/spec/content_for/multiple_blocks.haml +8 -0
- data/spec/content_for/multiple_blocks.slim +8 -0
- data/spec/content_for/multiple_yields.erb +3 -0
- data/spec/content_for/multiple_yields.erubis +3 -0
- data/spec/content_for/multiple_yields.haml +3 -0
- data/spec/content_for/multiple_yields.slim +3 -0
- data/spec/content_for/passes_values.erb +1 -0
- data/spec/content_for/passes_values.erubis +1 -0
- data/spec/content_for/passes_values.haml +1 -0
- data/spec/content_for/passes_values.slim +1 -0
- data/spec/content_for/same_key.erb +1 -0
- data/spec/content_for/same_key.erubis +1 -0
- data/spec/content_for/same_key.haml +2 -0
- data/spec/content_for/same_key.slim +2 -0
- data/spec/content_for/takes_values.erb +1 -0
- data/spec/content_for/takes_values.erubis +1 -0
- data/spec/content_for/takes_values.haml +3 -0
- data/spec/content_for/takes_values.slim +3 -0
- data/spec/content_for_spec.rb +201 -0
- data/spec/decompile_spec.rb +44 -0
- data/spec/extension_spec.rb +33 -0
- data/spec/json_spec.rb +115 -0
- data/spec/link_header_spec.rb +100 -0
- data/spec/namespace/foo.erb +1 -0
- data/spec/namespace/nested/foo.erb +1 -0
- data/spec/namespace_spec.rb +623 -0
- data/spec/okjson.rb +581 -0
- data/spec/reloader/app.rb.erb +40 -0
- data/spec/reloader_spec.rb +441 -0
- data/spec/respond_with/bar.erb +1 -0
- data/spec/respond_with/bar.json.erb +1 -0
- data/spec/respond_with/foo.html.erb +1 -0
- data/spec/respond_with/not_html.sass +2 -0
- data/spec/respond_with_spec.rb +289 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/streaming_spec.rb +436 -0
- metadata +252 -0
data/lib/sinatra/json.rb
ADDED
@@ -0,0 +1,134 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
# = Sinatra::JSON
|
6
|
+
#
|
7
|
+
# <tt>Sinatra::JSON</tt> adds a helper method, called +json+, for (obviously)
|
8
|
+
# json generation.
|
9
|
+
#
|
10
|
+
# == Usage
|
11
|
+
#
|
12
|
+
# === Classic Application
|
13
|
+
#
|
14
|
+
# In a classic application simply require the helper, and start using it:
|
15
|
+
#
|
16
|
+
# require "sinatra"
|
17
|
+
# require "sinatra/json"
|
18
|
+
#
|
19
|
+
# # define a route that uses the helper
|
20
|
+
# get '/' do
|
21
|
+
# json :foo => 'bar'
|
22
|
+
# end
|
23
|
+
#
|
24
|
+
# # The rest of your classic application code goes here...
|
25
|
+
#
|
26
|
+
# === Modular Application
|
27
|
+
#
|
28
|
+
# In a modular application you need to require the helper, and then tell the
|
29
|
+
# application you will use it:
|
30
|
+
#
|
31
|
+
# require "sinatra/base"
|
32
|
+
# require "sinatra/json"
|
33
|
+
#
|
34
|
+
# class MyApp < Sinatra::Base
|
35
|
+
# helpers Sinatra::JSON
|
36
|
+
#
|
37
|
+
# # define a route that uses the helper
|
38
|
+
# get '/' do
|
39
|
+
# json :foo => 'bar'
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# # The rest of your modular application code goes here...
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# === Encoders
|
46
|
+
#
|
47
|
+
# Per default it will try to call +to_json+ on the object, but if it doesn't
|
48
|
+
# respond to that message, will use its own, rather simple encoder. You can
|
49
|
+
# easily change that anyways. To use +JSON+, simply require it:
|
50
|
+
#
|
51
|
+
# require 'json'
|
52
|
+
#
|
53
|
+
# The same goes for <tt>Yajl::Encoder</tt>:
|
54
|
+
#
|
55
|
+
# require 'yajl'
|
56
|
+
#
|
57
|
+
# For other encoders, besides requiring them, you need to define the
|
58
|
+
# <tt>:json_encoder</tt> setting. For instance, for the +Whatever+ encoder:
|
59
|
+
#
|
60
|
+
# require 'whatever'
|
61
|
+
# set :json_encoder, Whatever
|
62
|
+
#
|
63
|
+
# To force +json+ to simply call +to_json+ on the object:
|
64
|
+
#
|
65
|
+
# set :json_encoder, :to_json
|
66
|
+
#
|
67
|
+
# Actually, it can call any method:
|
68
|
+
#
|
69
|
+
# set :json_encoder, :my_fancy_json_method
|
70
|
+
#
|
71
|
+
# === Content-Type
|
72
|
+
#
|
73
|
+
# It will automatically set the content type to "application/json". As
|
74
|
+
# usual, you can easily change that, with the <tt>:json_content_type</tt>
|
75
|
+
# setting:
|
76
|
+
#
|
77
|
+
# set :json_content_type, :js
|
78
|
+
#
|
79
|
+
# === Overriding the Encoder and the Content-Type
|
80
|
+
#
|
81
|
+
# The +json+ helper will also take two options <tt>:encoder</tt> and
|
82
|
+
# <tt>:content_type</tt>. The values of this options are the same as the
|
83
|
+
# <tt>:json_encoder</tt> and <tt>:json_content_type</tt> settings,
|
84
|
+
# respectively. You can also pass those to the json method:
|
85
|
+
#
|
86
|
+
# get '/' do
|
87
|
+
# json({:foo => 'bar'}, :encoder => :to_json, :content_type => :js)
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
module JSON
|
91
|
+
class << self
|
92
|
+
def encode(object)
|
93
|
+
enc object, Array, Hash
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def enc(o, *a)
|
99
|
+
o = o.to_s if o.is_a? Symbol
|
100
|
+
fail "invalid: #{o.inspect}" unless a.empty? or a.include? o.class
|
101
|
+
case o
|
102
|
+
when Float then o.nan? || o.infinite? ? 'null' : o.inspect
|
103
|
+
when TrueClass, FalseClass, NilClass, Numeric, String then o.inspect
|
104
|
+
when Array then map(o, "[%s]") { |e| enc(e) }
|
105
|
+
when Hash then map(o, "{%s}") { |k,v| enc(k, String) + ":" + enc(v) }
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def map(o, wrapper, &block)
|
110
|
+
wrapper % o.map(&block).join(',')
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
def json(object, options = {})
|
115
|
+
encoder = options[:encoder] || settings.json_encoder
|
116
|
+
content_type options[:content_type] || settings.json_content_type
|
117
|
+
if encoder.respond_to? :encode then encoder.encode(object)
|
118
|
+
elsif encoder.respond_to? :generate then encoder.generate(object)
|
119
|
+
elsif encoder.is_a? Symbol then object.__send__(encoder)
|
120
|
+
else fail "#{encoder} does not respond to #generate nor #encode"
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
Base.set :json_encoder do
|
126
|
+
return Yajl::Encoder if defined? Yajl::Encoder
|
127
|
+
return JSON if defined? JSON
|
128
|
+
return :to_json if {}.respond_to? :to_json and [].respond_to? :to_json
|
129
|
+
Sinatra::JSON
|
130
|
+
end
|
131
|
+
|
132
|
+
Base.set :json_content_type, :json
|
133
|
+
helpers JSON
|
134
|
+
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
|
3
|
+
module Sinatra
|
4
|
+
|
5
|
+
# = Sinatra::LinkHeader
|
6
|
+
#
|
7
|
+
# <tt>Sinatra::LinkHeader</tt> adds a set of helper methods to generate link
|
8
|
+
# HTML tags and their corresponding Link HTTP headers.
|
9
|
+
#
|
10
|
+
# == Usage
|
11
|
+
#
|
12
|
+
# Once you had set up the helpers in your application (see below), you will
|
13
|
+
# be able to call the following methods from inside your route handlers,
|
14
|
+
# filters and templates:
|
15
|
+
#
|
16
|
+
# +prefetch+::
|
17
|
+
# Sets the Link HTTP headers and returns HTML tags to prefetch the given
|
18
|
+
# resources.
|
19
|
+
#
|
20
|
+
# +stylesheet+::
|
21
|
+
# Sets the Link HTTP headers and returns HTML tags to use the given
|
22
|
+
# stylesheets.
|
23
|
+
#
|
24
|
+
# +link+::
|
25
|
+
# Sets the Link HTTP headers and returns the corresponding HTML tags
|
26
|
+
# for the given resources.
|
27
|
+
#
|
28
|
+
# +link_headers+::
|
29
|
+
# Returns the corresponding HTML tags for the current Link HTTP headers.
|
30
|
+
#
|
31
|
+
# === Classic Application
|
32
|
+
#
|
33
|
+
# In a classic application simply require the helpers, and start using them:
|
34
|
+
#
|
35
|
+
# require "sinatra"
|
36
|
+
# require "sinatra/link_header"
|
37
|
+
#
|
38
|
+
# # The rest of your classic application code goes here...
|
39
|
+
#
|
40
|
+
# === Modular Application
|
41
|
+
#
|
42
|
+
# In a modular application you need to require the helpers, and then tell
|
43
|
+
# the application you will use them:
|
44
|
+
#
|
45
|
+
# require "sinatra/base"
|
46
|
+
# require "sinatra/link_header"
|
47
|
+
#
|
48
|
+
# class MyApp < Sinatra::Base
|
49
|
+
# helpers Sinatra::LinkHeader
|
50
|
+
#
|
51
|
+
# # The rest of your modular application code goes here...
|
52
|
+
# end
|
53
|
+
#
|
54
|
+
module LinkHeader
|
55
|
+
##
|
56
|
+
# Set Link HTTP header and returns HTML tags for telling the browser to
|
57
|
+
# prefetch given resources (only supported by Opera and Firefox at the
|
58
|
+
# moment).
|
59
|
+
def prefetch(*urls)
|
60
|
+
link(:prefetch, *urls)
|
61
|
+
end
|
62
|
+
|
63
|
+
##
|
64
|
+
# Sets Link HTTP header and returns HTML tags for using stylesheets.
|
65
|
+
def stylesheet(*urls)
|
66
|
+
urls << {} unless urls.last.respond_to? :to_hash
|
67
|
+
urls.last[:type] ||= mime_type(:css)
|
68
|
+
link(:stylesheet, *urls)
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Sets Link HTTP header and returns corresponding HTML tags.
|
73
|
+
#
|
74
|
+
# Example:
|
75
|
+
#
|
76
|
+
# # Sets header:
|
77
|
+
# # Link: </foo>; rel="next"
|
78
|
+
# # Returns String:
|
79
|
+
# # '<link href="/foo" rel="next" />'
|
80
|
+
# link '/foo', :rel => :next
|
81
|
+
#
|
82
|
+
# # Multiple URLs
|
83
|
+
# link :stylesheet, '/a.css', '/b.css'
|
84
|
+
def link(*urls)
|
85
|
+
opts = urls.last.respond_to?(:to_hash) ? urls.pop : {}
|
86
|
+
opts[:rel] = urls.shift unless urls.first.respond_to? :to_str
|
87
|
+
options = opts.map { |k, v| " #{k}=#{v.to_s.inspect}" }
|
88
|
+
html_pattern = "<link href=\"%s\"#{options.join} />"
|
89
|
+
http_pattern = ["<%s>", *options].join ";"
|
90
|
+
link = (response["Link"] ||= "")
|
91
|
+
|
92
|
+
urls.map do |url|
|
93
|
+
link << "\n" unless link.empty?
|
94
|
+
link << (http_pattern % url)
|
95
|
+
html_pattern % url
|
96
|
+
end.join "\n"
|
97
|
+
end
|
98
|
+
|
99
|
+
##
|
100
|
+
# Takes the current value of th Link header(s) and generates HTML tags
|
101
|
+
# from it.
|
102
|
+
#
|
103
|
+
# Example:
|
104
|
+
#
|
105
|
+
# get '/' do
|
106
|
+
# # You can of course use fancy helpers like #link, #stylesheet
|
107
|
+
# # or #prefetch
|
108
|
+
# response["Link"] = '</foo>; rel="next"'
|
109
|
+
# haml :some_page
|
110
|
+
# end
|
111
|
+
#
|
112
|
+
# __END__
|
113
|
+
#
|
114
|
+
# @@ layout
|
115
|
+
# %head= link_headers
|
116
|
+
# %body= yield
|
117
|
+
def link_headers
|
118
|
+
yield if block_given?
|
119
|
+
return "" unless response.include? "Link"
|
120
|
+
response["Link"].lines.map do |line|
|
121
|
+
url, *opts = line.split(';').map(&:strip)
|
122
|
+
"<link href=\"#{url[1..-2]}\" #{opts.join " "} />"
|
123
|
+
end.join "\n"
|
124
|
+
end
|
125
|
+
|
126
|
+
def self.registered(base)
|
127
|
+
puts "WARNING: #{self} is a helpers module, not an extension."
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
helpers LinkHeader
|
132
|
+
end
|
@@ -0,0 +1,282 @@
|
|
1
|
+
require 'backports'
|
2
|
+
require 'sinatra/base'
|
3
|
+
require 'sinatra/decompile'
|
4
|
+
|
5
|
+
module Sinatra
|
6
|
+
|
7
|
+
# = Sinatra::Namespace
|
8
|
+
#
|
9
|
+
# <tt>Sinatra::Namespace</tt> is an extension that adds namespaces to an
|
10
|
+
# application. This namespaces will allow you to share a path prefix for the
|
11
|
+
# routes within the namespace, and define filters, conditions and error
|
12
|
+
# handlers exclusively for them. Besides that, you can also register helpers
|
13
|
+
# and extensions that will be used only within the namespace.
|
14
|
+
#
|
15
|
+
# == Usage
|
16
|
+
#
|
17
|
+
# Once you have loaded the extension (see below), you use the +namespace+
|
18
|
+
# method to define namespaces in your application.
|
19
|
+
#
|
20
|
+
# You can define a namespace by a path prefix:
|
21
|
+
#
|
22
|
+
# namespace '/blog' do
|
23
|
+
# get() { haml :blog }
|
24
|
+
# get '/:entry_permalink' do
|
25
|
+
# @entry = Entry.find_by_permalink!(params[:entry_permalink])
|
26
|
+
# haml :entry
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# # More blog routes...
|
30
|
+
# end
|
31
|
+
#
|
32
|
+
# by a condition:
|
33
|
+
#
|
34
|
+
# namespace :host_name => 'localhost' do
|
35
|
+
# get('/admin/dashboard') { haml :dashboard }
|
36
|
+
# get('/admin/login') { haml :login }
|
37
|
+
#
|
38
|
+
# # More admin routes...
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# or both:
|
42
|
+
#
|
43
|
+
# namespace '/admin', :host_name => 'localhost' do
|
44
|
+
# get('/dashboard') { haml :dashboard }
|
45
|
+
# get('/login') { haml :login }
|
46
|
+
# post('/login') { login_user }
|
47
|
+
#
|
48
|
+
# # More admin routes...
|
49
|
+
# end
|
50
|
+
#
|
51
|
+
# When you define a filter or an error handler, or register an extension or a
|
52
|
+
# set of helpers within a namespace, they only affect the routes defined in
|
53
|
+
# it. For instance, lets define a before filter to prevent the access of
|
54
|
+
# unauthorized users to the admin section of the application:
|
55
|
+
#
|
56
|
+
# namespace '/admin' do
|
57
|
+
# helpers AdminHelpers
|
58
|
+
# before { authenticate unless request.path_info == '/admin/login' }
|
59
|
+
#
|
60
|
+
# get '/dashboard' do
|
61
|
+
# # Only authenticated users can access here...
|
62
|
+
# haml :dashboard
|
63
|
+
# end
|
64
|
+
#
|
65
|
+
# # More admin routes...
|
66
|
+
# end
|
67
|
+
#
|
68
|
+
# get '/' do
|
69
|
+
# # Any user can access here...
|
70
|
+
# haml :index
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# Well, they actually also affect the nested namespaces:
|
74
|
+
#
|
75
|
+
# namespace '/admin' do
|
76
|
+
# helpers AdminHelpers
|
77
|
+
# before { authenticate unless request.path_info == '/admin/login' }
|
78
|
+
#
|
79
|
+
# namespace '/users' do
|
80
|
+
# get do
|
81
|
+
# # Only authenticated users can access here...
|
82
|
+
# @users = User.all
|
83
|
+
# haml :users
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# # More user admin routes...
|
87
|
+
# end
|
88
|
+
#
|
89
|
+
# # More admin routes...
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# === Classic Application Setup
|
93
|
+
#
|
94
|
+
# To be able to use namespaces in a classic application all you need to do is
|
95
|
+
# require the extension:
|
96
|
+
#
|
97
|
+
# require "sinatra"
|
98
|
+
# require "sinatra/namespace"
|
99
|
+
#
|
100
|
+
# # The rest of your classic application code goes here...
|
101
|
+
#
|
102
|
+
# === Modular Application Setup
|
103
|
+
#
|
104
|
+
# To be able to use namespaces in a modular application all you need to do is
|
105
|
+
# require the extension, and then, register it:
|
106
|
+
#
|
107
|
+
# require "sinatra/base"
|
108
|
+
# require "sinatra/namespace"
|
109
|
+
#
|
110
|
+
# class MyApp < Sinatra::Base
|
111
|
+
# register Sinatra::Namespace
|
112
|
+
#
|
113
|
+
# # The rest of your modular application code goes here...
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
module Namespace
|
117
|
+
def self.new(base, pattern, conditions = {}, &block)
|
118
|
+
Module.new do
|
119
|
+
extend NamespacedMethods
|
120
|
+
include InstanceMethods
|
121
|
+
@base, @extensions = base, []
|
122
|
+
@pattern, @conditions = compile(pattern, conditions)
|
123
|
+
@templates = Hash.new { |h,k| @base.templates[k] }
|
124
|
+
namespace = self
|
125
|
+
before { extend(@namespace = namespace) }
|
126
|
+
class_eval(&block)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
module InstanceMethods
|
131
|
+
def settings
|
132
|
+
@namespace
|
133
|
+
end
|
134
|
+
|
135
|
+
def template_cache
|
136
|
+
super.fetch(:nested, @namespace) { Tilt::Cache.new }
|
137
|
+
end
|
138
|
+
|
139
|
+
def error_block!(*keys)
|
140
|
+
if block = keys.inject(nil) { |b,k| b ||= @namespace.errors[k] }
|
141
|
+
instance_eval(&block)
|
142
|
+
else
|
143
|
+
super
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
module SharedMethods
|
149
|
+
def namespace(pattern, conditions = {}, &block)
|
150
|
+
Sinatra::Namespace.new(self, pattern, conditions, &block)
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
module NamespacedMethods
|
155
|
+
include SharedMethods
|
156
|
+
include Sinatra::Decompile
|
157
|
+
attr_reader :base, :templates
|
158
|
+
|
159
|
+
def self.prefixed(*names)
|
160
|
+
names.each { |n| define_method(n) { |*a, &b| prefixed(n, *a, &b) }}
|
161
|
+
end
|
162
|
+
|
163
|
+
prefixed :before, :after, :delete, :get, :head, :options, :patch, :post, :put
|
164
|
+
|
165
|
+
def helpers(*extensions, &block)
|
166
|
+
class_eval(&block) if block_given?
|
167
|
+
include(*extensions) if extensions.any?
|
168
|
+
end
|
169
|
+
|
170
|
+
def register(*extensions, &block)
|
171
|
+
extensions << Module.new(&block) if block_given?
|
172
|
+
@extensions += extensions
|
173
|
+
extensions.each do |extension|
|
174
|
+
extend extension
|
175
|
+
extension.registered(self) if extension.respond_to?(:registered)
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
def invoke_hook(name, *args)
|
180
|
+
@extensions.each { |e| e.send(name, *args) if e.respond_to?(name) }
|
181
|
+
end
|
182
|
+
|
183
|
+
def errors
|
184
|
+
@errors ||= {}
|
185
|
+
end
|
186
|
+
|
187
|
+
def not_found(&block)
|
188
|
+
error(404, &block)
|
189
|
+
end
|
190
|
+
|
191
|
+
def error(codes = Exception, &block)
|
192
|
+
[*codes].each { |c| errors[c] = block }
|
193
|
+
end
|
194
|
+
|
195
|
+
def respond_to(*args)
|
196
|
+
return @conditions[:provides] || base.respond_to if args.empty?
|
197
|
+
@conditions[:provides] = args
|
198
|
+
end
|
199
|
+
|
200
|
+
def set(key, value = self, &block)
|
201
|
+
raise ArgumentError, "may not set #{key}" if key != :views
|
202
|
+
return key.each { |k,v| set(k, v) } if block.nil? and value == self
|
203
|
+
block ||= proc { value }
|
204
|
+
singleton_class.send(:define_method, key, &block)
|
205
|
+
end
|
206
|
+
|
207
|
+
def enable(*opts)
|
208
|
+
opts.each { |key| set(key, true) }
|
209
|
+
end
|
210
|
+
|
211
|
+
def disable(*opts)
|
212
|
+
opts.each { |key| set(key, false) }
|
213
|
+
end
|
214
|
+
|
215
|
+
def template(name, &block)
|
216
|
+
filename, line = caller_locations.first
|
217
|
+
templates[name] = [block, filename, line.to_i]
|
218
|
+
end
|
219
|
+
|
220
|
+
def layout(name=:layout, &block)
|
221
|
+
template name, &block
|
222
|
+
end
|
223
|
+
|
224
|
+
private
|
225
|
+
|
226
|
+
def app
|
227
|
+
base.respond_to?(:base) ? base.base : base
|
228
|
+
end
|
229
|
+
|
230
|
+
def compile(pattern, conditions, default_pattern = nil)
|
231
|
+
if pattern.respond_to? :to_hash
|
232
|
+
conditions = conditions.merge pattern.to_hash
|
233
|
+
pattern = nil
|
234
|
+
end
|
235
|
+
base_pattern, base_conditions = @pattern, @conditions
|
236
|
+
pattern ||= default_pattern
|
237
|
+
base_pattern ||= base.pattern if base.respond_to? :pattern
|
238
|
+
base_conditions ||= base.conditions if base.respond_to? :conditions
|
239
|
+
[ prefixed_path(base_pattern, pattern),
|
240
|
+
(base_conditions || {}).merge(conditions) ]
|
241
|
+
end
|
242
|
+
|
243
|
+
def prefixed_path(a, b)
|
244
|
+
return a || b || // unless a and b
|
245
|
+
a, b = decompile(a), decompile(b) unless a.class == b.class
|
246
|
+
a, b = regexpify(a), regexpify(b) unless a.class == b.class
|
247
|
+
path = a.class.new "#{a}#{b}"
|
248
|
+
path = /^#{path}$/ if path.is_a? Regexp and base == app
|
249
|
+
path
|
250
|
+
end
|
251
|
+
|
252
|
+
def regexpify(pattern)
|
253
|
+
pattern = Sinatra::Base.send(:compile, pattern).first.inspect
|
254
|
+
pattern.gsub! /^\/(\^|\\A)?|(\$|\\Z)?\/$/, ''
|
255
|
+
Regexp.new pattern
|
256
|
+
end
|
257
|
+
|
258
|
+
def prefixed(method, pattern = nil, conditions = {}, &block)
|
259
|
+
default = '*' if method == :before or method == :after
|
260
|
+
pattern, conditions = compile pattern, conditions, default
|
261
|
+
result = base.send(method, pattern, conditions, &block)
|
262
|
+
invoke_hook :route_added, method.to_s.upcase, pattern, block
|
263
|
+
result
|
264
|
+
end
|
265
|
+
|
266
|
+
def method_missing(meth, *args, &block)
|
267
|
+
base.send(meth, *args, &block)
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
271
|
+
module BaseMethods
|
272
|
+
include SharedMethods
|
273
|
+
end
|
274
|
+
|
275
|
+
def self.extend_object(base)
|
276
|
+
base.extend BaseMethods
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
register Sinatra::Namespace
|
281
|
+
Delegator.delegate :namespace
|
282
|
+
end
|