sinatra-contrib 1.3.2 → 1.4.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/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
|
+
[](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
|
|