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 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 depending automatically
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
@@ -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 => "!= capture_haml(*args, &block)",
11
- :erb => "<% @capture = yield(*args) %>",
12
- :slim => "== yield(*args)"
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
@@ -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
- # Within the application you can access those options through +settings+. If
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 you also have a staging environment:
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
- yaml = config_for_env(YAML.load_file(file)) || {}
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
@@ -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 |meth, list|
27
+ @extensions.each do |method, list|
28
28
  list = list.map { |name| Sinatra.const_get name }
29
- base.send(meth, *list) unless base == ::Sinatra::Application
29
+ base.send(method, *list) unless base == ::Sinatra::Application
30
30
  end
31
31
  end
32
32
  end
@@ -11,7 +11,7 @@ module Sinatra
11
11
  # Allows you to read cookies:
12
12
  #
13
13
  # get '/' do
14
- # "value: #{cookie[:something]}"
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 you will use them:
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.include? "expires=Thu, 01-Jan-1970 00:00:00 GMT"
308
+ if line =~ /expires=Thu, 01[-\s]Jan[-\s]1970/
309
309
  @deleted << key
310
310
  else
311
311
  @deleted.delete key
@@ -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! /\([^\(\)]*\)/ do |part|
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.encode(char)
106
- enc = "(?:#{Regexp.escape enc}|#{URI.encode char, /./})" if enc == char
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
@@ -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. It allows you to call directly inside your extension
10
- # module almost any <tt>Sinatra::Base</tt> method. This means you can use
11
- # +get+ to define a route, +before+ to define a before filter, +set+ to
12
- # define a setting, a so on.
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 methods calls you
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
@@ -44,8 +44,8 @@ module Sinatra
44
44
  #
45
45
  # === Encoders
46
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
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
- enc object, Array, Hash
93
+ ::MultiJson.dump(object)
94
94
  end
95
+ end
95
96
 
96
- private
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
- 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
102
+ private
108
103
 
109
- def map(o, wrapper, &block)
110
- wrapper % o.map(&block).join(',')
111
- end
104
+ def resolve_content_type(options = {})
105
+ options[:content_type] || settings.json_content_type
112
106
  end
113
107
 
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
108
+ def resolve_encoder(options = {})
109
+ options[:json_encoder] || settings.json_encoder
122
110
  end
123
- end
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
- 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
125
+ ::MultiJson
130
126
  end
131
127
 
132
128
  Base.set :json_content_type, :json
@@ -53,8 +53,8 @@ module Sinatra
53
53
  #
54
54
  module LinkHeader
55
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
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)
@@ -19,6 +19,12 @@ module Sinatra
19
19
  # # ...
20
20
  # end
21
21
  #
22
+ # Or for multiple verbs and multiple routes:
23
+ #
24
+ # route :get, :post, ['/foo', '/bar'] do
25
+ # # ...
26
+ # end
27
+ #
22
28
  # Or even for custom verbs:
23
29
  #
24
30
  # route 'LIST', '/' do
@@ -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(@errors)
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 { |c| @errors[c] = args }
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(meth, *args, &block)
266
- base.send(meth, *args, &block)
268
+ def method_missing(method, *args, &block)
269
+ base.send(method, *args, &block)
267
270
  end
268
271
  end
269
272