sinatra-contrib 1.3.2 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +9 -8
- data/lib/sinatra/capture.rb +87 -5
- data/lib/sinatra/config_file.rb +19 -3
- data/lib/sinatra/content_for.rb +2 -2
- data/lib/sinatra/contrib/setup.rb +2 -2
- data/lib/sinatra/cookies.rb +4 -4
- data/lib/sinatra/decompile.rb +13 -5
- data/lib/sinatra/extension.rb +5 -5
- data/lib/sinatra/json.rb +26 -30
- data/lib/sinatra/link_header.rb +2 -2
- data/lib/sinatra/multi_route.rb +6 -0
- data/lib/sinatra/namespace.rb +9 -6
- data/lib/sinatra/reloader.rb +7 -7
- data/lib/sinatra/respond_with.rb +18 -14
- data/lib/sinatra/streaming.rb +6 -6
- data/lib/sinatra/test_helpers.rb +1 -1
- data/sinatra-contrib.gemspec +5 -22
- data/spec/capture_spec.rb +14 -1
- data/spec/config_file/key_value.yml.erb +6 -0
- data/spec/config_file_spec.rb +10 -0
- data/spec/content_for_spec.rb +3 -3
- data/spec/cookies_spec.rb +26 -6
- data/spec/json_spec.rb +5 -3
- data/spec/multi_route_spec.rb +28 -13
- data/spec/namespace_spec.rb +294 -283
- data/spec/respond_with_spec.rb +10 -0
- data/spec/spec_helper.rb +1 -0
- metadata +8 -25
- data/spec/content_for/footer.erb +0 -3
- data/spec/content_for/footer.erubis +0 -3
- data/spec/content_for/footer.haml +0 -2
- data/spec/content_for/footer.slim +0 -2
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Build Status](https://secure.travis-ci.org/sinatra/sinatra-contrib.png)](http://travis-ci.org/sinatra/sinatra-contrib)
|
2
|
+
|
1
3
|
Collection of common Sinatra extensions, semi-officially supported.
|
2
4
|
|
3
5
|
# Goals
|
@@ -6,13 +8,6 @@ Collection of common Sinatra extensions, semi-officially supported.
|
|
6
8
|
* High code quality, high test coverage
|
7
9
|
* Include plugins people usually ask for a lot
|
8
10
|
|
9
|
-
# TODO
|
10
|
-
|
11
|
-
* Write documentation, integrate into Sinatra website
|
12
|
-
* Finish imports and rewrites
|
13
|
-
* Wrap up first release
|
14
|
-
* Find contributors (both code and docs)
|
15
|
-
|
16
11
|
# Included extensions
|
17
12
|
|
18
13
|
## Common Extensions
|
@@ -46,7 +41,7 @@ Currently included:
|
|
46
41
|
|
47
42
|
* `sinatra/namespace`: Adds namespace support to Sinatra.
|
48
43
|
|
49
|
-
* `sinatra/respond_with`: Choose action and/or template
|
44
|
+
* `sinatra/respond_with`: Choose action and/or template automatically
|
50
45
|
depending on the incoming request. Adds helpers `respond_to` and
|
51
46
|
`respond_with`.
|
52
47
|
|
@@ -70,6 +65,12 @@ Currently included:
|
|
70
65
|
* `sinatra/test_helpers`: Helper methods to ease testing your Sinatra
|
71
66
|
application. Partly extracted from Sinatra. Testing framework agnostic
|
72
67
|
|
68
|
+
# Installation
|
69
|
+
Add `gem 'sinatra-contrib'` to *Gemfile*, then execute `bundle install`.
|
70
|
+
|
71
|
+
If you don't use Bundler, install the gem manually by executing `gem install sinatra-contrib` in your command line.
|
72
|
+
|
73
|
+
|
73
74
|
# Usage
|
74
75
|
|
75
76
|
## Classic Style
|
data/lib/sinatra/capture.rb
CHANGED
@@ -3,21 +3,103 @@ require 'sinatra/engine_tracking'
|
|
3
3
|
require 'backports'
|
4
4
|
|
5
5
|
module Sinatra
|
6
|
+
#
|
7
|
+
# = Sinatra::Capture
|
8
|
+
#
|
9
|
+
# Extension that enables blocks inside other extensions.
|
10
|
+
# It currently works for erb, slim and haml.
|
11
|
+
# Enables mixing of different template languages.
|
12
|
+
#
|
13
|
+
# Example:
|
14
|
+
#
|
15
|
+
# # in hello_world.erb
|
16
|
+
#
|
17
|
+
# Say
|
18
|
+
# <% a = capture do %>World<% end %>
|
19
|
+
# Hello <%= a %>!
|
20
|
+
#
|
21
|
+
# # in hello_world.slim
|
22
|
+
#
|
23
|
+
# | Say
|
24
|
+
# - a = capture do
|
25
|
+
# | World
|
26
|
+
# | Hello #{a}!
|
27
|
+
#
|
28
|
+
# # in hello_world.haml
|
29
|
+
#
|
30
|
+
# Say
|
31
|
+
# - a = capture do
|
32
|
+
# World
|
33
|
+
# Hello #{a.strip}!
|
34
|
+
#
|
35
|
+
#
|
36
|
+
# You can also use nested blocks.
|
37
|
+
#
|
38
|
+
# Example
|
39
|
+
#
|
40
|
+
# # in hello_world.erb
|
41
|
+
#
|
42
|
+
# Say
|
43
|
+
# <% a = capture do %>
|
44
|
+
# <% b = capture do %>World<% end %>
|
45
|
+
# <%= b %>!
|
46
|
+
# <% end %>
|
47
|
+
# Hello <%= a.strip %>
|
48
|
+
#
|
49
|
+
#
|
50
|
+
# The main advantage of capture is mixing of different template engines.
|
51
|
+
#
|
52
|
+
# Example
|
53
|
+
#
|
54
|
+
# # in mix_me_up.slim
|
55
|
+
#
|
56
|
+
# - two = capture do
|
57
|
+
# - erb "<%= 1 + 1 %>"
|
58
|
+
# | 1 + 1 = #{two}
|
59
|
+
#
|
60
|
+
# == Usage
|
61
|
+
#
|
62
|
+
# === Classic Application
|
63
|
+
#
|
64
|
+
# In a classic application simply require the helpers, and start using them:
|
65
|
+
#
|
66
|
+
# require "sinatra"
|
67
|
+
# require "sinatra/capture"
|
68
|
+
#
|
69
|
+
# # The rest of your classic application code goes here...
|
70
|
+
#
|
71
|
+
# === Modular Application
|
72
|
+
#
|
73
|
+
# In a modular application you need to require the helpers, and then tell
|
74
|
+
# the application you will use them:
|
75
|
+
#
|
76
|
+
# require "sinatra/base"
|
77
|
+
# require "sinatra/capture"
|
78
|
+
#
|
79
|
+
# class MyApp < Sinatra::Base
|
80
|
+
# helpers Sinatra::Capture
|
81
|
+
#
|
82
|
+
# # The rest of your modular application code goes here...
|
83
|
+
# end
|
84
|
+
#
|
6
85
|
module Capture
|
7
86
|
include Sinatra::EngineTracking
|
8
87
|
|
9
88
|
DUMMIES = {
|
10
|
-
:haml
|
11
|
-
:
|
12
|
-
:slim
|
89
|
+
:haml => "!= capture_haml(*args, &block)",
|
90
|
+
:erubis => "<% @capture = yield(*args) %>",
|
91
|
+
:slim => "== yield(*args)"
|
13
92
|
}
|
14
93
|
|
15
|
-
DUMMIES[:erubis] = DUMMIES[:erb]
|
16
|
-
|
17
94
|
def capture(*args, &block)
|
18
95
|
@capture = nil
|
19
96
|
if current_engine == :ruby
|
20
97
|
result = block[*args]
|
98
|
+
elsif current_engine == :erb
|
99
|
+
@_out_buf, _buf_was = '', @_out_buf
|
100
|
+
block[*args]
|
101
|
+
result = eval('@_out_buf', block.binding)
|
102
|
+
@_out_buf = _buf_was
|
21
103
|
else
|
22
104
|
buffer = eval '_buf if defined?(_buf)', block.binding
|
23
105
|
old_buffer = buffer.dup if buffer
|
data/lib/sinatra/config_file.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'sinatra/base'
|
2
2
|
require 'yaml'
|
3
|
+
require 'erb'
|
3
4
|
|
4
5
|
module Sinatra
|
5
6
|
|
@@ -10,7 +11,7 @@ module Sinatra
|
|
10
11
|
# the files contains specific environment settings and it will use the
|
11
12
|
# corresponding to the current one.
|
12
13
|
#
|
13
|
-
#
|
14
|
+
# You can access those options through +settings+ within the application. If
|
14
15
|
# you try to get the value for a setting that hasn't been defined in the
|
15
16
|
# config file for the current environment, you will get whatever it was set
|
16
17
|
# to in the application.
|
@@ -94,10 +95,23 @@ module Sinatra
|
|
94
95
|
#
|
95
96
|
# Be aware that if you have a different environment, besides development,
|
96
97
|
# test and production, you will also need to adjust the +environments+
|
97
|
-
# setting. For instance, when
|
98
|
+
# setting, otherwise the settings will not load. For instance, when
|
99
|
+
# you also have a staging environment:
|
98
100
|
#
|
99
101
|
# set :environments, %w{development test production staging}
|
100
102
|
#
|
103
|
+
# If you wish to provide defaults that may be shared among all the environments,
|
104
|
+
# this can be done by using one of the existing environments as the default using
|
105
|
+
# the YAML alias, and then overwriting values in the other environments:
|
106
|
+
#
|
107
|
+
# development: &common_settings
|
108
|
+
# foo: 'foo'
|
109
|
+
# bar: 'bar'
|
110
|
+
#
|
111
|
+
# production:
|
112
|
+
# <<: *common_settings
|
113
|
+
# bar: 'baz' # override the default value
|
114
|
+
#
|
101
115
|
module ConfigFile
|
102
116
|
|
103
117
|
# When the extension is registered sets the +environments+ setting to the
|
@@ -114,7 +128,9 @@ module Sinatra
|
|
114
128
|
paths.each do |pattern|
|
115
129
|
Dir.glob(pattern) do |file|
|
116
130
|
$stderr.puts "loading config file '#{file}'" if logging?
|
117
|
-
|
131
|
+
document = IO.read(file)
|
132
|
+
document = ERB.new(document).result if file.split('.').include?('erb')
|
133
|
+
yaml = config_for_env(YAML.load(document)) || {}
|
118
134
|
yaml.each_pair do |key, value|
|
119
135
|
for_env = config_for_env(value)
|
120
136
|
set key, for_env unless value and for_env.nil? and respond_to? key
|
data/lib/sinatra/content_for.rb
CHANGED
@@ -78,7 +78,7 @@ module Sinatra
|
|
78
78
|
def content_for(key, &block)
|
79
79
|
content_blocks[key.to_sym] << capture_later(&block)
|
80
80
|
end
|
81
|
-
|
81
|
+
|
82
82
|
# Check if a block of content with the given key was defined. For
|
83
83
|
# example:
|
84
84
|
#
|
@@ -100,7 +100,7 @@ module Sinatra
|
|
100
100
|
# <%= yield_content :head %>
|
101
101
|
# </head>
|
102
102
|
#
|
103
|
-
# Would render everything you declared with <tt>content_for
|
103
|
+
# Would render everything you declared with <tt>content_for
|
104
104
|
# :head</tt> before closing the <tt><head></tt> tag.
|
105
105
|
#
|
106
106
|
# You can also pass values to the content blocks by passing them
|
@@ -24,9 +24,9 @@ module Sinatra
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def registered(base)
|
27
|
-
@extensions.each do |
|
27
|
+
@extensions.each do |method, list|
|
28
28
|
list = list.map { |name| Sinatra.const_get name }
|
29
|
-
base.send(
|
29
|
+
base.send(method, *list) unless base == ::Sinatra::Application
|
30
30
|
end
|
31
31
|
end
|
32
32
|
end
|
data/lib/sinatra/cookies.rb
CHANGED
@@ -11,7 +11,7 @@ module Sinatra
|
|
11
11
|
# Allows you to read cookies:
|
12
12
|
#
|
13
13
|
# get '/' do
|
14
|
-
# "value: #{
|
14
|
+
# "value: #{cookies[:something]}"
|
15
15
|
# end
|
16
16
|
#
|
17
17
|
# And of course to write cookies:
|
@@ -42,7 +42,7 @@ module Sinatra
|
|
42
42
|
# === Modular Application
|
43
43
|
#
|
44
44
|
# In a modular application you need to require the helpers, and then tell
|
45
|
-
# the application
|
45
|
+
# the application to use them:
|
46
46
|
#
|
47
47
|
# require "sinatra/base"
|
48
48
|
# require "sinatra/cookies"
|
@@ -111,7 +111,7 @@ module Sinatra
|
|
111
111
|
|
112
112
|
def delete(key)
|
113
113
|
result = self[key]
|
114
|
-
@response.delete_cookie(key.to_s)
|
114
|
+
@response.delete_cookie(key.to_s, @options)
|
115
115
|
result
|
116
116
|
end
|
117
117
|
|
@@ -305,7 +305,7 @@ module Sinatra
|
|
305
305
|
key, value = line.split(';', 2).first.to_s.split('=', 2)
|
306
306
|
next if key.nil?
|
307
307
|
key = Rack::Utils.unescape(key)
|
308
|
-
if line
|
308
|
+
if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/
|
309
309
|
@deleted << key
|
310
310
|
else
|
311
311
|
@deleted.delete key
|
data/lib/sinatra/decompile.rb
CHANGED
@@ -60,7 +60,7 @@ module Sinatra
|
|
60
60
|
# end
|
61
61
|
# end
|
62
62
|
#
|
63
|
-
# Will return the internal Regexp if unable to reconstruct the pattern,
|
63
|
+
# Will return the internal Regexp if it's unable to reconstruct the pattern,
|
64
64
|
# which likely indicates that a Regexp was used in the first place.
|
65
65
|
#
|
66
66
|
# You can also use this to check whether you could actually use a string
|
@@ -70,17 +70,25 @@ module Sinatra
|
|
70
70
|
def decompile(pattern, keys = nil, *)
|
71
71
|
# Everything in here is basically just the reverse of
|
72
72
|
# Sinatra::Base#compile
|
73
|
+
#
|
74
|
+
# Sinatra 2.0 will come with a mechanism for this, making this obsolete.
|
73
75
|
pattern, keys = pattern if pattern.respond_to? :to_ary
|
74
76
|
keys, str = keys.try(:dup), pattern.inspect
|
75
77
|
return pattern unless str.start_with? '/' and str.end_with? '/'
|
76
|
-
str.gsub!
|
78
|
+
str.gsub! /^\/(\^|\\A)?|(\$|\\z)?\/$/, ''
|
77
79
|
str.gsub! encoded(' '), ' '
|
78
80
|
return pattern if str =~ /^[\.\+]/
|
79
|
-
str.gsub!
|
81
|
+
str.gsub! '((?:[^\.\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)'
|
82
|
+
str.gsub! '((?:[^\/?#%]|(?:%[^2].|%[2][^Ee]))+)', '([^\/?#]+)'
|
83
|
+
str.gsub! /\([^\(\)]*\)|\([^\(\)]*\([^\(\)]*\)[^\(\)]*\)/ do |part|
|
80
84
|
case part
|
81
85
|
when '(.*?)'
|
82
86
|
return pattern if keys.shift != 'splat'
|
83
87
|
'*'
|
88
|
+
when /^\(\?\:(\\*.)\|%[\w\[\]]+\)$/
|
89
|
+
$1
|
90
|
+
when /^\(\?\:(%\d+)\|([^\)]+|\([^\)]+\))\)$/
|
91
|
+
URI.unescape($1)
|
84
92
|
when '([^\/?#]+)'
|
85
93
|
return pattern if keys.empty?
|
86
94
|
":" << keys.shift
|
@@ -102,8 +110,8 @@ module Sinatra
|
|
102
110
|
|
103
111
|
def encoded(char)
|
104
112
|
return super if defined? super
|
105
|
-
enc = URI.
|
106
|
-
enc = "(?:#{
|
113
|
+
enc = URI.escape(char)
|
114
|
+
enc = "(?:#{escaped(char, enc).join('|')})" if enc == char
|
107
115
|
enc = "(?:#{enc}|#{encoded('+')})" if char == " "
|
108
116
|
enc
|
109
117
|
end
|
data/lib/sinatra/extension.rb
CHANGED
@@ -6,12 +6,12 @@ module Sinatra
|
|
6
6
|
# = Sinatra::Extension
|
7
7
|
#
|
8
8
|
# <tt>Sinatra::Extension</tt> is a mixin that provides some syntactic sugar
|
9
|
-
# for your extensions.
|
10
|
-
#
|
11
|
-
#
|
12
|
-
# define a
|
9
|
+
# for your extensions. It allows you to call almost any
|
10
|
+
# <tt>Sinatra::Base</tt> method directly inside your extension
|
11
|
+
# module. This means you can use +get+ to define a route, +before+
|
12
|
+
# to define a before filter, +set+ to define a setting and so on.
|
13
13
|
#
|
14
|
-
# Is important to be aware that this mixin remembers the
|
14
|
+
# Is important to be aware that this mixin remembers the method calls you
|
15
15
|
# make, and then, when your extension is registered, replays them on the
|
16
16
|
# Sinatra application that has been extended. In order to do that, it
|
17
17
|
# defines a <tt>registered</tt> method, so, if your extension defines one
|
data/lib/sinatra/json.rb
CHANGED
@@ -44,8 +44,8 @@ module Sinatra
|
|
44
44
|
#
|
45
45
|
# === Encoders
|
46
46
|
#
|
47
|
-
#
|
48
|
-
# respond to that message, will use its own
|
47
|
+
# By default it will try to call +to_json+ on the object, but if it doesn't
|
48
|
+
# respond to that message, it will use its own rather simple encoder. You can
|
49
49
|
# easily change that anyways. To use +JSON+, simply require it:
|
50
50
|
#
|
51
51
|
# require 'json'
|
@@ -90,43 +90,39 @@ module Sinatra
|
|
90
90
|
module JSON
|
91
91
|
class << self
|
92
92
|
def encode(object)
|
93
|
-
|
93
|
+
::MultiJson.dump(object)
|
94
94
|
end
|
95
|
+
end
|
95
96
|
|
96
|
-
|
97
|
+
def json(object, options = {})
|
98
|
+
content_type resolve_content_type(options)
|
99
|
+
resolve_encoder_action object, resolve_encoder(options)
|
100
|
+
end
|
97
101
|
|
98
|
-
|
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
|
102
|
+
private
|
108
103
|
|
109
|
-
|
110
|
-
|
111
|
-
end
|
104
|
+
def resolve_content_type(options = {})
|
105
|
+
options[:content_type] || settings.json_content_type
|
112
106
|
end
|
113
107
|
|
114
|
-
def
|
115
|
-
|
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
|
108
|
+
def resolve_encoder(options = {})
|
109
|
+
options[:json_encoder] || settings.json_encoder
|
122
110
|
end
|
123
|
-
|
111
|
+
|
112
|
+
def resolve_encoder_action(object, encoder)
|
113
|
+
[:encode, :generate].each do |method|
|
114
|
+
return encoder.send(method, object) if encoder.respond_to? method
|
115
|
+
end
|
116
|
+
if encoder.is_a? Symbol
|
117
|
+
object.__send__(encoder)
|
118
|
+
else
|
119
|
+
fail "#{encoder} does not respond to #generate nor #encode"
|
120
|
+
end #if
|
121
|
+
end #resolve_encoder_action
|
122
|
+
end #JSON
|
124
123
|
|
125
124
|
Base.set :json_encoder do
|
126
|
-
|
127
|
-
return JSON if defined? JSON
|
128
|
-
return :to_json if {}.respond_to? :to_json and [].respond_to? :to_json
|
129
|
-
Sinatra::JSON
|
125
|
+
::MultiJson
|
130
126
|
end
|
131
127
|
|
132
128
|
Base.set :json_content_type, :json
|
data/lib/sinatra/link_header.rb
CHANGED
@@ -53,8 +53,8 @@ module Sinatra
|
|
53
53
|
#
|
54
54
|
module LinkHeader
|
55
55
|
##
|
56
|
-
#
|
57
|
-
# prefetch given resources (only supported by Opera and Firefox at the
|
56
|
+
# Sets 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
58
|
# moment).
|
59
59
|
def prefetch(*urls)
|
60
60
|
link(:prefetch, *urls)
|
data/lib/sinatra/multi_route.rb
CHANGED
data/lib/sinatra/namespace.rb
CHANGED
@@ -14,7 +14,7 @@ module Sinatra
|
|
14
14
|
#
|
15
15
|
# == Usage
|
16
16
|
#
|
17
|
-
# Once you have loaded the extension (see below), you use the +namespace+
|
17
|
+
# Once you have loaded the extension (see below), you can use the +namespace+
|
18
18
|
# method to define namespaces in your application.
|
19
19
|
#
|
20
20
|
# You can define a namespace by a path prefix:
|
@@ -177,18 +177,21 @@ module Sinatra
|
|
177
177
|
end
|
178
178
|
|
179
179
|
def errors
|
180
|
-
base.errors.merge(
|
180
|
+
base.errors.merge(namespace_errors)
|
181
181
|
end
|
182
182
|
|
183
183
|
def namespace_errors
|
184
184
|
@errors
|
185
185
|
end
|
186
|
-
|
186
|
+
|
187
187
|
def error(*codes, &block)
|
188
188
|
args = Sinatra::Base.send(:compile!, "ERROR", /^#{@pattern}/, block)
|
189
189
|
codes = codes.map { |c| Array(c) }.flatten
|
190
190
|
codes << Exception if codes.empty?
|
191
|
-
codes.each
|
191
|
+
codes.each do |c|
|
192
|
+
errors = @errors[c] ||= []
|
193
|
+
errors << args
|
194
|
+
end
|
192
195
|
end
|
193
196
|
|
194
197
|
def respond_to(*args)
|
@@ -262,8 +265,8 @@ module Sinatra
|
|
262
265
|
result
|
263
266
|
end
|
264
267
|
|
265
|
-
def method_missing(
|
266
|
-
base.send(
|
268
|
+
def method_missing(method, *args, &block)
|
269
|
+
base.send(method, *args, &block)
|
267
270
|
end
|
268
271
|
end
|
269
272
|
|