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 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