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.
Files changed (79) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +135 -0
  3. data/Rakefile +75 -0
  4. data/ideas.md +29 -0
  5. data/lib/sinatra/capture.rb +42 -0
  6. data/lib/sinatra/config_file.rb +151 -0
  7. data/lib/sinatra/content_for.rb +111 -0
  8. data/lib/sinatra/contrib.rb +39 -0
  9. data/lib/sinatra/contrib/all.rb +2 -0
  10. data/lib/sinatra/contrib/setup.rb +53 -0
  11. data/lib/sinatra/contrib/version.rb +45 -0
  12. data/lib/sinatra/cookies.rb +331 -0
  13. data/lib/sinatra/decompile.rb +113 -0
  14. data/lib/sinatra/engine_tracking.rb +96 -0
  15. data/lib/sinatra/extension.rb +95 -0
  16. data/lib/sinatra/json.rb +134 -0
  17. data/lib/sinatra/link_header.rb +132 -0
  18. data/lib/sinatra/multi_route.rb +81 -0
  19. data/lib/sinatra/namespace.rb +282 -0
  20. data/lib/sinatra/reloader.rb +384 -0
  21. data/lib/sinatra/respond_with.rb +245 -0
  22. data/lib/sinatra/streaming.rb +267 -0
  23. data/lib/sinatra/test_helpers.rb +87 -0
  24. data/sinatra-contrib.gemspec +125 -0
  25. data/spec/capture_spec.rb +80 -0
  26. data/spec/config_file/key_value.yml +6 -0
  27. data/spec/config_file/missing_env.yml +4 -0
  28. data/spec/config_file/with_envs.yml +7 -0
  29. data/spec/config_file/with_nested_envs.yml +11 -0
  30. data/spec/config_file_spec.rb +44 -0
  31. data/spec/content_for/different_key.erb +1 -0
  32. data/spec/content_for/different_key.erubis +1 -0
  33. data/spec/content_for/different_key.haml +2 -0
  34. data/spec/content_for/different_key.slim +2 -0
  35. data/spec/content_for/layout.erb +1 -0
  36. data/spec/content_for/layout.erubis +1 -0
  37. data/spec/content_for/layout.haml +1 -0
  38. data/spec/content_for/layout.slim +1 -0
  39. data/spec/content_for/multiple_blocks.erb +4 -0
  40. data/spec/content_for/multiple_blocks.erubis +4 -0
  41. data/spec/content_for/multiple_blocks.haml +8 -0
  42. data/spec/content_for/multiple_blocks.slim +8 -0
  43. data/spec/content_for/multiple_yields.erb +3 -0
  44. data/spec/content_for/multiple_yields.erubis +3 -0
  45. data/spec/content_for/multiple_yields.haml +3 -0
  46. data/spec/content_for/multiple_yields.slim +3 -0
  47. data/spec/content_for/passes_values.erb +1 -0
  48. data/spec/content_for/passes_values.erubis +1 -0
  49. data/spec/content_for/passes_values.haml +1 -0
  50. data/spec/content_for/passes_values.slim +1 -0
  51. data/spec/content_for/same_key.erb +1 -0
  52. data/spec/content_for/same_key.erubis +1 -0
  53. data/spec/content_for/same_key.haml +2 -0
  54. data/spec/content_for/same_key.slim +2 -0
  55. data/spec/content_for/takes_values.erb +1 -0
  56. data/spec/content_for/takes_values.erubis +1 -0
  57. data/spec/content_for/takes_values.haml +3 -0
  58. data/spec/content_for/takes_values.slim +3 -0
  59. data/spec/content_for_spec.rb +201 -0
  60. data/spec/cookies_spec.rb +782 -0
  61. data/spec/decompile_spec.rb +44 -0
  62. data/spec/extension_spec.rb +33 -0
  63. data/spec/json_spec.rb +115 -0
  64. data/spec/link_header_spec.rb +100 -0
  65. data/spec/multi_route_spec.rb +45 -0
  66. data/spec/namespace/foo.erb +1 -0
  67. data/spec/namespace/nested/foo.erb +1 -0
  68. data/spec/namespace_spec.rb +623 -0
  69. data/spec/okjson.rb +581 -0
  70. data/spec/reloader/app.rb.erb +40 -0
  71. data/spec/reloader_spec.rb +441 -0
  72. data/spec/respond_with/bar.erb +1 -0
  73. data/spec/respond_with/bar.json.erb +1 -0
  74. data/spec/respond_with/foo.html.erb +1 -0
  75. data/spec/respond_with/not_html.sass +2 -0
  76. data/spec/respond_with_spec.rb +289 -0
  77. data/spec/spec_helper.rb +6 -0
  78. data/spec/streaming_spec.rb +436 -0
  79. metadata +256 -0
@@ -0,0 +1,111 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/capture'
3
+
4
+ module Sinatra
5
+
6
+ # = Sinatra::ContentFor
7
+ #
8
+ # <tt>Sinatra::ContentFor</tt> is a set of helpers that allows you to capture
9
+ # blocks inside views to be rendered later during the request. The most
10
+ # common use is to populate different parts of your layout from your view.
11
+ #
12
+ # The currently supported engines are: Erb, Erubis, Haml and Slim.
13
+ #
14
+ # == Usage
15
+ #
16
+ # You call +content_for+, generally from a view, to capture a block of markup
17
+ # giving it an identifier:
18
+ #
19
+ # # index.erb
20
+ # <% content_for :some_key do %>
21
+ # <chunk of="html">...</chunk>
22
+ # <% end %>
23
+ #
24
+ # Then, you call +yield_content+ with that identifier, generally from a
25
+ # layout, to render the captured block:
26
+ #
27
+ # # layout.erb
28
+ # <%= yield_content :some_key %>
29
+ #
30
+ # === Classic Application
31
+ #
32
+ # To use the helpers in a classic application all you need to do is require
33
+ # them:
34
+ #
35
+ # require "sinatra"
36
+ # require "sinatra/content_for"
37
+ #
38
+ # # Your classic application code goes here...
39
+ #
40
+ # === Modular Application
41
+ #
42
+ # To use the helpers in a modular application you need to require them, and
43
+ # then, tell the application you will use them:
44
+ #
45
+ # require "sinatra/base"
46
+ # require "sinatra/content_for"
47
+ #
48
+ # class MyApp < Sinatra::Base
49
+ # register Sinatra::ContentFor
50
+ #
51
+ # # The rest of your modular application code goes here...
52
+ # end
53
+ #
54
+ # == And How Is This Useful?
55
+ #
56
+ # For example, some of your views might need a few javascript tags and
57
+ # stylesheets, but you don't want to force this files in all your pages.
58
+ # Then you can put <tt><% yield_content :scripts_and_styles %></tt> on your
59
+ # layout, inside the <head> tag, and each view can call <tt>content_for</tt>
60
+ # setting the appropriate set of tags that should be added to the layout.
61
+ #
62
+ module ContentFor
63
+ include Capture
64
+
65
+ # Capture a block of content to be rendered later. For example:
66
+ #
67
+ # <% content_for :head do %>
68
+ # <script type="text/javascript" src="/foo.js"></script>
69
+ # <% end %>
70
+ #
71
+ # You can call +content_for+ multiple times with the same key
72
+ # (in the example +:head+), and when you render the blocks for
73
+ # that key all of them will be rendered, in the same order you
74
+ # captured them.
75
+ #
76
+ # Your blocks can also receive values, which are passed to them
77
+ # by <tt>yield_content</tt>
78
+ def content_for(key, &block)
79
+ content_blocks[key.to_sym] << capture_later(&block)
80
+ end
81
+
82
+ # Render the captured blocks for a given key. For example:
83
+ #
84
+ # <head>
85
+ # <title>Example</title>
86
+ # <%= yield_content :head %>
87
+ # </head>
88
+ #
89
+ # Would render everything you declared with <tt>content_for
90
+ # :head</tt> before closing the <tt><head></tt> tag.
91
+ #
92
+ # You can also pass values to the content blocks by passing them
93
+ # as arguments after the key:
94
+ #
95
+ # <%= yield_content :head, 1, 2 %>
96
+ #
97
+ # Would pass <tt>1</tt> and <tt>2</tt> to all the blocks registered
98
+ # for <tt>:head</tt>.
99
+ def yield_content(key, *args)
100
+ content_blocks[key.to_sym].map { |b| capture(*args, &b) }.join
101
+ end
102
+
103
+ private
104
+
105
+ def content_blocks
106
+ @content_blocks ||= Hash.new {|h,k| h[k] = [] }
107
+ end
108
+ end
109
+
110
+ helpers ContentFor
111
+ end
@@ -0,0 +1,39 @@
1
+ require 'sinatra/contrib/setup'
2
+
3
+ module Sinatra
4
+ module Contrib
5
+ ##
6
+ # Common middleware that doesn't bring run time overhead if not used
7
+ # or breaks if external dependencies are missing. Will extend
8
+ # Sinatra::Application by default.
9
+ module Common
10
+ register :ConfigFile
11
+ register :MultiRoute
12
+ register :Namespace
13
+ register :RespondWith
14
+
15
+ helpers :Capture
16
+ helpers :ContentFor
17
+ helpers :Cookies
18
+ helpers :EngineTracking
19
+ helpers :JSON
20
+ helpers :LinkHeader
21
+ helpers :Streaming
22
+ end
23
+
24
+ ##
25
+ # Other extensions you don't want to be loaded unless needed.
26
+ module Custom
27
+ # register :Compass
28
+ register :Decompile
29
+ register :Reloader
30
+ end
31
+
32
+ ##
33
+ # Stuff that aren't Sinatra extensions, technically.
34
+ autoload :Extension
35
+ autoload :TestHelpers
36
+ end
37
+
38
+ register Sinatra::Contrib::Common
39
+ end
@@ -0,0 +1,2 @@
1
+ require 'sinatra/contrib'
2
+ Sinatra.register Sinatra::Contrib::All
@@ -0,0 +1,53 @@
1
+ require 'sinatra/base'
2
+ require 'sinatra/contrib/version'
3
+ require 'backports'
4
+
5
+ module Sinatra
6
+ module Contrib
7
+ module Loader
8
+ def extensions
9
+ @extensions ||= {:helpers => [], :register => []}
10
+ end
11
+
12
+ def register(name, path = nil)
13
+ autoload name, path, :register
14
+ end
15
+
16
+ def helpers(name, path = nil)
17
+ autoload name, path, :helpers
18
+ end
19
+
20
+ def autoload(name, path = nil, method = nil)
21
+ path ||= "sinatra/#{name.to_s.underscore}"
22
+ extensions[method] << name if method
23
+ Sinatra.autoload(name, path)
24
+ end
25
+
26
+ def registered(base)
27
+ @extensions.each do |meth, list|
28
+ list = list.map { |name| Sinatra.const_get name }
29
+ base.send(meth, *list) unless base == ::Sinatra::Application
30
+ end
31
+ end
32
+ end
33
+
34
+ module Common
35
+ extend Loader
36
+ end
37
+
38
+ module Custom
39
+ extend Loader
40
+ end
41
+
42
+ module All
43
+ def self.registered(base)
44
+ base.register Common, Custom
45
+ end
46
+ end
47
+
48
+ extend Loader
49
+ def self.registered(base)
50
+ base.register Common, Custom
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,45 @@
1
+ module Sinatra
2
+ module Contrib
3
+ def self.version
4
+ VERSION
5
+ end
6
+
7
+ module VERSION
8
+ extend Comparable
9
+
10
+ MAJOR = 1
11
+ MINOR = 3
12
+ TINY = 0
13
+ SIGNATURE = [MAJOR, MINOR, TINY]
14
+ STRING = SIGNATURE.join '.'
15
+
16
+ def self.major; MAJOR end
17
+ def self.minor; MINOR end
18
+ def self.tiny; TINY end
19
+ def self.to_s; STRING end
20
+
21
+ def self.hash
22
+ STRING.hash
23
+ end
24
+
25
+ def self.<=>(other)
26
+ other = other.split('.').map { |i| i.to_i } if other.respond_to? :split
27
+ SIGNATURE <=> Array(other)
28
+ end
29
+
30
+ def self.inspect
31
+ STRING.inspect
32
+ end
33
+
34
+ def self.respond_to?(meth, *)
35
+ return true if super
36
+ meth.to_s !~ /^__|^to_str$/ and STRING.respond_to? meth
37
+ end
38
+
39
+ def self.method_missing(meth, *args, &block)
40
+ return super unless STRING.respond_to?(meth)
41
+ STRING.send(meth, *args, &block)
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,331 @@
1
+ require 'sinatra/base'
2
+ require 'backports'
3
+
4
+ module Sinatra
5
+ # = Sinatra::Cookies
6
+ #
7
+ # Easy way to deal with cookies
8
+ #
9
+ # == Usage
10
+ #
11
+ # Allows you to read cookies:
12
+ #
13
+ # get '/' do
14
+ # "value: #{cookie[:something]}"
15
+ # end
16
+ #
17
+ # And of course to write cookies:
18
+ #
19
+ # get '/set' do
20
+ # cookies[:something] = 'foobar'
21
+ # redirect to('/')
22
+ # end
23
+ #
24
+ # And generally behaves like a hash:
25
+ #
26
+ # get '/demo' do
27
+ # cookies.merge! 'foo' => 'bar', 'bar' => 'baz'
28
+ # cookies.keep_if { |key, value| key.start_with? 'b' }
29
+ # foo, bar = cookies.values_at 'foo', 'bar'
30
+ # "size: #{cookies.length}"
31
+ # end
32
+ #
33
+ # === Classic Application
34
+ #
35
+ # In a classic application simply require the helpers, and start using them:
36
+ #
37
+ # require "sinatra"
38
+ # require "sinatra/cookies"
39
+ #
40
+ # # The rest of your classic application code goes here...
41
+ #
42
+ # === Modular Application
43
+ #
44
+ # In a modular application you need to require the helpers, and then tell
45
+ # the application you will use them:
46
+ #
47
+ # require "sinatra/base"
48
+ # require "sinatra/link_header"
49
+ #
50
+ # class MyApp < Sinatra::Base
51
+ # helpers Sinatra::Cookies
52
+ #
53
+ # # The rest of your modular application code goes here...
54
+ # end
55
+ #
56
+ module Cookies
57
+ class Jar
58
+ include Enumerable
59
+ attr_reader :options
60
+
61
+ def initialize(app)
62
+ @response_string = nil
63
+ @response_hash = {}
64
+ @response = app.response
65
+ @request = app.request
66
+ @deleted = []
67
+
68
+ @options = {
69
+ :path => @request.script_name,
70
+ :domain => @request.host,
71
+ :secure => @request.secure?,
72
+ :httponly => true
73
+ }
74
+
75
+ @options[:path] = '/' if @options[:path].to_s.empty?
76
+
77
+ if app.settings.respond_to? :cookie_options
78
+ @options.merge! app.settings.cookie_options
79
+ end
80
+ end
81
+
82
+ def ==(other)
83
+ other.respond_to? :to_hash and to_hash == other.to_hash
84
+ end
85
+
86
+ def [](key)
87
+ response_cookies[key.to_s] || request_cookies[key.to_s]
88
+ end
89
+
90
+ def []=(key, value)
91
+ @response.set_cookie key.to_s, @options.merge(:value => value)
92
+ end
93
+
94
+ def assoc(key)
95
+ to_hash.assoc(key.to_s)
96
+ end if Hash.method_defined? :assoc
97
+
98
+ def clear
99
+ each_key { |k| delete(k) }
100
+ end
101
+
102
+ def compare_by_identity?
103
+ false
104
+ end
105
+
106
+ def default
107
+ nil
108
+ end
109
+
110
+ alias default_proc default
111
+
112
+ def delete(key)
113
+ result = self[key]
114
+ @response.delete_cookie(key.to_s)
115
+ result
116
+ end
117
+
118
+ def delete_if
119
+ return enum_for(__method__) unless block_given?
120
+ each { |k, v| delete(k) if yield(k, v) }
121
+ self
122
+ end
123
+
124
+ def each(&block)
125
+ return enum_for(__method__) unless block_given?
126
+ to_hash.each(&block)
127
+ end
128
+
129
+ def each_key(&block)
130
+ return enum_for(__method__) unless block_given?
131
+ to_hash.each_key(&block)
132
+ end
133
+
134
+ alias each_pair each
135
+
136
+ def each_value(&block)
137
+ return enum_for(__method__) unless block_given?
138
+ to_hash.each_value(&block)
139
+ end
140
+
141
+ def empty?
142
+ to_hash.empty?
143
+ end
144
+
145
+ def fetch(key, &block)
146
+ response_cookies.fetch(key.to_s) do
147
+ request_cookies.fetch(key.to_s, &block)
148
+ end
149
+ end
150
+
151
+ def flatten
152
+ to_hash.flatten
153
+ end if Hash.method_defined? :flatten
154
+
155
+ def has_key?(key)
156
+ response_cookies.has_key? key.to_s or request_cookies.has_key? key.to_s
157
+ end
158
+
159
+ def has_value?(value)
160
+ response_cookies.has_value? value or request_cookies.has_value? value
161
+ end
162
+
163
+ def hash
164
+ to_hash.hash
165
+ end
166
+
167
+ alias include? has_key?
168
+ alias member? has_key?
169
+
170
+ def index(value)
171
+ warn "Hash#index is deprecated; use Hash#key" if RUBY_VERSION > '1.9'
172
+ key(value)
173
+ end
174
+
175
+ def inspect
176
+ "<##{self.class}: #{to_hash.inspect[1..-2]}>"
177
+ end
178
+
179
+ def invert
180
+ to_hash.invert
181
+ end if Hash.method_defined? :invert
182
+
183
+ def keep_if
184
+ return enum_for(__method__) unless block_given?
185
+ delete_if { |*a| not yield(*a) }
186
+ end
187
+
188
+ def key(value)
189
+ to_hash.key(value)
190
+ end
191
+
192
+ alias key? has_key?
193
+
194
+ def keys
195
+ to_hash.keys
196
+ end
197
+
198
+ def length
199
+ to_hash.length
200
+ end
201
+
202
+ def merge(other, &block)
203
+ to_hash.merge(other, &block)
204
+ end
205
+
206
+ def merge!(other)
207
+ other.each_pair do |key, value|
208
+ if block_given? and include? key
209
+ self[key] = yield(key.to_s, self[key], value)
210
+ else
211
+ self[key] = value
212
+ end
213
+ end
214
+ end
215
+
216
+ def rassoc(value)
217
+ to_hash.rassoc(value)
218
+ end
219
+
220
+ def rehash
221
+ response_cookies.rehash
222
+ request_cookies.rehash
223
+ self
224
+ end
225
+
226
+ def reject(&block)
227
+ return enum_for(__method__) unless block_given?
228
+ to_hash.reject(&block)
229
+ end
230
+
231
+ alias reject! delete_if
232
+
233
+ def replace(other)
234
+ select! { |k, v| other.include?(k) or other.include?(k.to_s) }
235
+ merge! other
236
+ end
237
+
238
+ def select(&block)
239
+ return enum_for(__method__) unless block_given?
240
+ to_hash.select(&block)
241
+ end
242
+
243
+ alias select! keep_if if Hash.method_defined? :select!
244
+
245
+ def shift
246
+ key, value = to_hash.shift
247
+ delete(key)
248
+ [key, value]
249
+ end
250
+
251
+ alias size length
252
+
253
+ def sort(&block)
254
+ to_hash.sort(&block)
255
+ end if Hash.method_defined? :sort
256
+
257
+ alias store []=
258
+
259
+ def to_hash
260
+ request_cookies.merge(response_cookies)
261
+ end
262
+
263
+ def to_a
264
+ to_hash.to_a
265
+ end
266
+
267
+ def to_s
268
+ to_hash.to_s
269
+ end
270
+
271
+ alias update merge!
272
+ alias value? has_value?
273
+
274
+ def values
275
+ to_hash.values
276
+ end
277
+
278
+ def values_at(*list)
279
+ list.map { |k| self[k] }
280
+ end
281
+
282
+ private
283
+
284
+ def warn(message)
285
+ super "#{caller.first[/^[^:]:\d+:/]} warning: #{message}"
286
+ end
287
+
288
+ def deleted
289
+ parse_response
290
+ @deleted
291
+ end
292
+
293
+ def response_cookies
294
+ parse_response
295
+ @response_hash
296
+ end
297
+
298
+ def parse_response
299
+ string = @response['Set-Cookie']
300
+ return if @response_string == string
301
+
302
+ hash = {}
303
+
304
+ string.each_line do |line|
305
+ key, value = line.split(';', 2).first.to_s.split('=', 2)
306
+ next if key.nil?
307
+ key = Rack::Utils.unescape(key)
308
+ if line.include? "expires=Thu, 01-Jan-1970 00:00:00 GMT"
309
+ @deleted << key
310
+ else
311
+ @deleted.delete key
312
+ hash[key] = value
313
+ end
314
+ end
315
+
316
+ @response_hash.replace hash
317
+ @response_string = string
318
+ end
319
+
320
+ def request_cookies
321
+ @request.cookies.reject { |key, value| deleted.include? key }
322
+ end
323
+ end
324
+
325
+ def cookies
326
+ @cookies ||= Jar.new(self)
327
+ end
328
+ end
329
+
330
+ helpers Cookies
331
+ end