nano-sharp-gem 0.0.1
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.
- checksums.yaml +7 -0
- data/cuba-4.0.3/CHANGELOG +71 -0
- data/cuba-4.0.3/CONTRIBUTING +19 -0
- data/cuba-4.0.3/LICENSE +23 -0
- data/cuba-4.0.3/README.md +781 -0
- data/cuba-4.0.3/cuba.gemspec +18 -0
- data/cuba-4.0.3/examples/config.ru +18 -0
- data/cuba-4.0.3/examples/measure.rb +17 -0
- data/cuba-4.0.3/examples/rack-response.ru +21 -0
- data/cuba-4.0.3/examples/views/home.mote +7 -0
- data/cuba-4.0.3/examples/views/layout.mote +11 -0
- data/cuba-4.0.3/lib/cuba/capybara.rb +13 -0
- data/cuba-4.0.3/lib/cuba/render.rb +63 -0
- data/cuba-4.0.3/lib/cuba/safe/csrf.rb +47 -0
- data/cuba-4.0.3/lib/cuba/safe/secure_headers.rb +43 -0
- data/cuba-4.0.3/lib/cuba/safe.rb +23 -0
- data/cuba-4.0.3/lib/cuba/test.rb +11 -0
- data/cuba-4.0.3/lib/cuba.rb +436 -0
- data/cuba-4.0.3/makefile +4 -0
- data/cuba-4.0.3/test/accept.rb +77 -0
- data/cuba-4.0.3/test/captures.rb +162 -0
- data/cuba-4.0.3/test/composition.rb +103 -0
- data/cuba-4.0.3/test/cookie.rb +34 -0
- data/cuba-4.0.3/test/csrf.rb +140 -0
- data/cuba-4.0.3/test/extension.rb +21 -0
- data/cuba-4.0.3/test/helper.rb +11 -0
- data/cuba-4.0.3/test/host.rb +29 -0
- data/cuba-4.0.3/test/integration.rb +114 -0
- data/cuba-4.0.3/test/match.rb +86 -0
- data/cuba-4.0.3/test/middleware.rb +47 -0
- data/cuba-4.0.3/test/number.rb +36 -0
- data/cuba-4.0.3/test/on.rb +157 -0
- data/cuba-4.0.3/test/param.rb +66 -0
- data/cuba-4.0.3/test/path.rb +86 -0
- data/cuba-4.0.3/test/plugin.rb +68 -0
- data/cuba-4.0.3/test/rack.rb +22 -0
- data/cuba-4.0.3/test/redirect.rb +21 -0
- data/cuba-4.0.3/test/render.rb +128 -0
- data/cuba-4.0.3/test/root.rb +83 -0
- data/cuba-4.0.3/test/run.rb +23 -0
- data/cuba-4.0.3/test/safe.rb +74 -0
- data/cuba-4.0.3/test/segment.rb +45 -0
- data/cuba-4.0.3/test/session.rb +21 -0
- data/cuba-4.0.3/test/settings.rb +52 -0
- data/cuba-4.0.3/test/views/about.erb +1 -0
- data/cuba-4.0.3/test/views/about.str +1 -0
- data/cuba-4.0.3/test/views/content-yield.erb +1 -0
- data/cuba-4.0.3/test/views/custom/abs_path.mote +1 -0
- data/cuba-4.0.3/test/views/frag.mote +1 -0
- data/cuba-4.0.3/test/views/home.erb +2 -0
- data/cuba-4.0.3/test/views/home.mote +1 -0
- data/cuba-4.0.3/test/views/home.str +2 -0
- data/cuba-4.0.3/test/views/layout-alternative.erb +2 -0
- data/cuba-4.0.3/test/views/layout-yield.erb +3 -0
- data/cuba-4.0.3/test/views/layout.erb +2 -0
- data/cuba-4.0.3/test/views/layout.mote +2 -0
- data/cuba-4.0.3/test/views/layout.str +2 -0
- data/cuba-4.0.3/test/views/test.erb +1 -0
- data/cuba-4.0.3/test/with.rb +42 -0
- data/nano-sharp-gem.gemspec +11 -0
- metadata +99 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
require "delegate"
|
|
2
|
+
require "rack"
|
|
3
|
+
require "rack/session"
|
|
4
|
+
|
|
5
|
+
class Cuba
|
|
6
|
+
SLASH = "/".freeze
|
|
7
|
+
EMPTY = "".freeze
|
|
8
|
+
SEGMENT = "([^\\/]+)".freeze
|
|
9
|
+
DEFAULT = "text/html; charset=utf-8".freeze
|
|
10
|
+
REGEXES = Hash.new { |h, pattern| h[pattern] = /\A\/(#{pattern})(\/|\z)/ }
|
|
11
|
+
|
|
12
|
+
class Response
|
|
13
|
+
LOCATION = "location".freeze
|
|
14
|
+
|
|
15
|
+
module ContentType
|
|
16
|
+
HTML = "text/html".freeze # :nodoc:
|
|
17
|
+
TEXT = "text/plain".freeze # :nodoc:
|
|
18
|
+
JSON = "application/json".freeze # :nodoc:
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
attr_accessor :status
|
|
22
|
+
|
|
23
|
+
attr :body
|
|
24
|
+
attr :headers
|
|
25
|
+
|
|
26
|
+
def initialize(headers = {})
|
|
27
|
+
@status = nil
|
|
28
|
+
@headers = headers
|
|
29
|
+
@body = []
|
|
30
|
+
@length = 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def [](key)
|
|
34
|
+
@headers[key]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def []=(key, value)
|
|
38
|
+
@headers[key] = value
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def write(str)
|
|
42
|
+
s = str.to_s
|
|
43
|
+
|
|
44
|
+
@length += s.bytesize
|
|
45
|
+
@headers[Rack::CONTENT_LENGTH] = @length.to_s
|
|
46
|
+
@body << s
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Write response body as text/plain
|
|
50
|
+
def text(str)
|
|
51
|
+
@headers[Rack::CONTENT_TYPE] = ContentType::TEXT
|
|
52
|
+
write(str)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Write response body as text/html
|
|
56
|
+
def html(str)
|
|
57
|
+
@headers[Rack::CONTENT_TYPE] = ContentType::HTML
|
|
58
|
+
write(str)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Write response body as application/json
|
|
62
|
+
def json(str)
|
|
63
|
+
@headers[Rack::CONTENT_TYPE] = ContentType::JSON
|
|
64
|
+
write(str)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def redirect(path, status = 302)
|
|
68
|
+
@headers[LOCATION] = path
|
|
69
|
+
@status = status
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def finish
|
|
73
|
+
[@status, @headers, @body]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def set_cookie(key, value)
|
|
77
|
+
Rack::Utils.set_cookie_header!(@headers, key, value)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def delete_cookie(key, value = {})
|
|
81
|
+
Rack::Utils.delete_cookie_header!(@headers, key, value)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def self.reset!
|
|
86
|
+
@app = nil
|
|
87
|
+
@prototype = nil
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def self.app
|
|
91
|
+
@app ||= Rack::Builder.new
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def self.use(middleware, *args, **kwargs, &block)
|
|
95
|
+
app.use(middleware, *args, **kwargs, &block)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def self.define(&block)
|
|
99
|
+
app.run new(&block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def self.prototype
|
|
103
|
+
@prototype ||= app.to_app
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.call(env)
|
|
107
|
+
prototype.call(env)
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def self.plugin(mixin)
|
|
111
|
+
include mixin
|
|
112
|
+
extend mixin::ClassMethods if defined?(mixin::ClassMethods)
|
|
113
|
+
|
|
114
|
+
mixin.setup(self) if mixin.respond_to?(:setup)
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def self.settings
|
|
118
|
+
@settings ||= {}
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
def self.deepclone(obj)
|
|
122
|
+
Marshal.load(Marshal.dump(obj))
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def self.inherited(child)
|
|
126
|
+
child.settings.replace(deepclone(settings))
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
attr :env
|
|
130
|
+
attr :req
|
|
131
|
+
attr :res
|
|
132
|
+
attr :captures
|
|
133
|
+
|
|
134
|
+
def initialize(&blk)
|
|
135
|
+
@blk = blk
|
|
136
|
+
@captures = []
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def settings
|
|
140
|
+
self.class.settings
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def call(env)
|
|
144
|
+
dup.call!(env)
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def call!(env)
|
|
148
|
+
@env = env
|
|
149
|
+
@req = settings[:req].new(env)
|
|
150
|
+
@res = settings[:res].new(settings[:default_headers].dup)
|
|
151
|
+
|
|
152
|
+
# This `catch` statement will either receive a
|
|
153
|
+
# rack response tuple via a `halt`, or will
|
|
154
|
+
# fall back to issuing a 404.
|
|
155
|
+
#
|
|
156
|
+
# When it `catch`es a throw, the return value
|
|
157
|
+
# of this whole `call!` method will be the
|
|
158
|
+
# rack response tuple, which is exactly what we want.
|
|
159
|
+
catch(:halt) do
|
|
160
|
+
instance_eval(&@blk)
|
|
161
|
+
|
|
162
|
+
not_found
|
|
163
|
+
res.finish
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def session
|
|
168
|
+
env["rack.session"] || raise(RuntimeError,
|
|
169
|
+
"You're missing a session handler. You can get started " +
|
|
170
|
+
"by adding Cuba.use Rack::Session::Cookie")
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
# The heart of the path / verb / any condition matching.
|
|
174
|
+
#
|
|
175
|
+
# @example
|
|
176
|
+
#
|
|
177
|
+
# on get do
|
|
178
|
+
# res.write "GET"
|
|
179
|
+
# end
|
|
180
|
+
#
|
|
181
|
+
# on get, "signup" do
|
|
182
|
+
# res.write "Signup"
|
|
183
|
+
# end
|
|
184
|
+
#
|
|
185
|
+
# on "user/:id" do |uid|
|
|
186
|
+
# res.write "User: #{uid}"
|
|
187
|
+
# end
|
|
188
|
+
#
|
|
189
|
+
# on "styles", extension("css") do |file|
|
|
190
|
+
# res.write render("styles/#{file}.sass")
|
|
191
|
+
# end
|
|
192
|
+
#
|
|
193
|
+
def on(*args, &block)
|
|
194
|
+
try do
|
|
195
|
+
# For every block, we make sure to reset captures so that
|
|
196
|
+
# nesting matchers won't mess with each other's captures.
|
|
197
|
+
@captures = []
|
|
198
|
+
|
|
199
|
+
# We stop evaluation of this entire matcher unless
|
|
200
|
+
# each and every `arg` defined for this matcher evaluates
|
|
201
|
+
# to a non-false value.
|
|
202
|
+
#
|
|
203
|
+
# Short circuit examples:
|
|
204
|
+
# on true, false do
|
|
205
|
+
#
|
|
206
|
+
# # PATH_INFO=/user
|
|
207
|
+
# on true, "signup"
|
|
208
|
+
return unless args.all? { |arg| match(arg) }
|
|
209
|
+
|
|
210
|
+
# The captures we yield here were generated and assembled
|
|
211
|
+
# by evaluating each of the `arg`s above. Most of these
|
|
212
|
+
# are carried out by #consume.
|
|
213
|
+
yield(*captures)
|
|
214
|
+
|
|
215
|
+
if res.status.nil?
|
|
216
|
+
if res.body.empty?
|
|
217
|
+
not_found
|
|
218
|
+
else
|
|
219
|
+
res.headers[Rack::CONTENT_TYPE] ||= DEFAULT
|
|
220
|
+
res.status = 200
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
halt(res.finish)
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
# @private Used internally by #on to ensure that SCRIPT_NAME and
|
|
229
|
+
# PATH_INFO are reset to their proper values.
|
|
230
|
+
def try
|
|
231
|
+
script, path = env[Rack::SCRIPT_NAME], env[Rack::PATH_INFO]
|
|
232
|
+
|
|
233
|
+
yield
|
|
234
|
+
|
|
235
|
+
ensure
|
|
236
|
+
env[Rack::SCRIPT_NAME], env[Rack::PATH_INFO] = script, path
|
|
237
|
+
end
|
|
238
|
+
private :try
|
|
239
|
+
|
|
240
|
+
def consume(pattern)
|
|
241
|
+
matchdata = env[Rack::PATH_INFO].match(REGEXES[pattern])
|
|
242
|
+
|
|
243
|
+
return false unless matchdata
|
|
244
|
+
|
|
245
|
+
path, *vars = matchdata.captures
|
|
246
|
+
|
|
247
|
+
env[Rack::SCRIPT_NAME] += "/#{path}"
|
|
248
|
+
env[Rack::PATH_INFO] = "#{vars.pop}#{matchdata.post_match}"
|
|
249
|
+
|
|
250
|
+
captures.push(*vars)
|
|
251
|
+
end
|
|
252
|
+
private :consume
|
|
253
|
+
|
|
254
|
+
def match(matcher, segment = SEGMENT)
|
|
255
|
+
case matcher
|
|
256
|
+
when String then consume(matcher.gsub(/:\w+/, segment))
|
|
257
|
+
when Regexp then consume(matcher)
|
|
258
|
+
when Symbol then consume(segment)
|
|
259
|
+
when Proc then matcher.call
|
|
260
|
+
else
|
|
261
|
+
matcher
|
|
262
|
+
end
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# A matcher for files with a certain extension.
|
|
266
|
+
#
|
|
267
|
+
# @example
|
|
268
|
+
# # PATH_INFO=/style/app.css
|
|
269
|
+
# on "style", extension("css") do |file|
|
|
270
|
+
# res.write file # writes app
|
|
271
|
+
# end
|
|
272
|
+
def extension(ext = "\\w+")
|
|
273
|
+
lambda { consume("([^\\/]+?)\.#{ext}\\z") }
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Ensures that certain request parameters are present. Acts like a
|
|
277
|
+
# precondition / assertion for your route. A default value can be
|
|
278
|
+
# provided as a second argument. In that case, it always matches
|
|
279
|
+
# and the result is either the parameter or the default value.
|
|
280
|
+
#
|
|
281
|
+
# @example
|
|
282
|
+
# # POST with data like user[fname]=John&user[lname]=Doe
|
|
283
|
+
# on "signup", param("user") do |atts|
|
|
284
|
+
# User.create(atts)
|
|
285
|
+
# end
|
|
286
|
+
#
|
|
287
|
+
# on "login", param("username", "guest") do |username|
|
|
288
|
+
# # If not provided, username == "guest"
|
|
289
|
+
# end
|
|
290
|
+
def param(key, default = nil)
|
|
291
|
+
value = req.params[key.to_s] || default
|
|
292
|
+
|
|
293
|
+
lambda { captures << value unless value.to_s.empty? }
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Useful for matching against the request host (i.e. HTTP_HOST).
|
|
297
|
+
#
|
|
298
|
+
# @example
|
|
299
|
+
# on host("account1.example.com"), "api" do
|
|
300
|
+
# res.write "You have reached the API of account1."
|
|
301
|
+
# end
|
|
302
|
+
def host(hostname)
|
|
303
|
+
hostname === req.host
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# If you want to match against the HTTP_ACCEPT value.
|
|
307
|
+
#
|
|
308
|
+
# @example
|
|
309
|
+
# # HTTP_ACCEPT=application/xml
|
|
310
|
+
# on accept("application/xml") do
|
|
311
|
+
# # automatically set to application/xml.
|
|
312
|
+
# res.write res["Content-Type"]
|
|
313
|
+
# end
|
|
314
|
+
def accept(mimetype)
|
|
315
|
+
lambda do
|
|
316
|
+
accept = String(env["HTTP_ACCEPT"]).split(",")
|
|
317
|
+
|
|
318
|
+
if accept.any? { |s| s.strip == mimetype }
|
|
319
|
+
res[Rack::CONTENT_TYPE] = mimetype
|
|
320
|
+
end
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
# Syntactic sugar for providing catch-all matches.
|
|
325
|
+
#
|
|
326
|
+
# @example
|
|
327
|
+
# on default do
|
|
328
|
+
# res.write "404"
|
|
329
|
+
# end
|
|
330
|
+
def default
|
|
331
|
+
true
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Access the root of the application.
|
|
335
|
+
#
|
|
336
|
+
# @example
|
|
337
|
+
#
|
|
338
|
+
# # GET /
|
|
339
|
+
# on root do
|
|
340
|
+
# res.write "Home"
|
|
341
|
+
# end
|
|
342
|
+
def root
|
|
343
|
+
env[Rack::PATH_INFO] == SLASH || env[Rack::PATH_INFO] == EMPTY
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
# Syntatic sugar for providing HTTP Verb matching.
|
|
347
|
+
#
|
|
348
|
+
# @example
|
|
349
|
+
# on get, "signup" do
|
|
350
|
+
# end
|
|
351
|
+
#
|
|
352
|
+
# on post, "signup" do
|
|
353
|
+
# end
|
|
354
|
+
def get; req.get? end
|
|
355
|
+
def post; req.post? end
|
|
356
|
+
def put; req.put? end
|
|
357
|
+
def patch; req.patch? end
|
|
358
|
+
def delete; req.delete? end
|
|
359
|
+
def head; req.head? end
|
|
360
|
+
def options; req.options? end
|
|
361
|
+
def link; req.link? end
|
|
362
|
+
def unlink; req.unlink? end
|
|
363
|
+
def trace; req.trace? end
|
|
364
|
+
|
|
365
|
+
# If you want to halt the processing of an existing handler
|
|
366
|
+
# and continue it via a different handler.
|
|
367
|
+
#
|
|
368
|
+
# @example
|
|
369
|
+
# def redirect(*args)
|
|
370
|
+
# run Cuba.new { on(default) { res.redirect(*args) }}
|
|
371
|
+
# end
|
|
372
|
+
#
|
|
373
|
+
# on "account" do
|
|
374
|
+
# redirect "/login" unless session["uid"]
|
|
375
|
+
#
|
|
376
|
+
# res.write "Super secure account info."
|
|
377
|
+
# end
|
|
378
|
+
def run(app)
|
|
379
|
+
halt app.call(req.env)
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
def halt(response)
|
|
383
|
+
throw :halt, response
|
|
384
|
+
end
|
|
385
|
+
|
|
386
|
+
# Adds ability to pass information to a nested Cuba application.
|
|
387
|
+
# It receives two parameters: a hash that represents the passed
|
|
388
|
+
# information and a block. The #vars method is used to retrieve
|
|
389
|
+
# a hash with the passed information.
|
|
390
|
+
#
|
|
391
|
+
# class Platforms < Cuba
|
|
392
|
+
# define do
|
|
393
|
+
# platform = vars[:platform]
|
|
394
|
+
#
|
|
395
|
+
# on default do
|
|
396
|
+
# res.write(platform) # => "heroku" or "salesforce"
|
|
397
|
+
# end
|
|
398
|
+
# end
|
|
399
|
+
# end
|
|
400
|
+
#
|
|
401
|
+
# Cuba.define do
|
|
402
|
+
# on "(heroku|salesforce)" do |platform|
|
|
403
|
+
# with(platform: platform) do
|
|
404
|
+
# run(Platforms)
|
|
405
|
+
# end
|
|
406
|
+
# end
|
|
407
|
+
# end
|
|
408
|
+
#
|
|
409
|
+
def with(dict = {})
|
|
410
|
+
old, env["cuba.vars"] = vars, vars.merge(dict)
|
|
411
|
+
yield
|
|
412
|
+
ensure
|
|
413
|
+
env["cuba.vars"] = old
|
|
414
|
+
end
|
|
415
|
+
|
|
416
|
+
# Returns a hash with the information set by the #with method.
|
|
417
|
+
#
|
|
418
|
+
# with(role: "admin", site: "main") do
|
|
419
|
+
# on default do
|
|
420
|
+
# res.write(vars.inspect)
|
|
421
|
+
# end
|
|
422
|
+
# end
|
|
423
|
+
# # => '{:role=>"admin", :site=>"main"}'
|
|
424
|
+
#
|
|
425
|
+
def vars
|
|
426
|
+
env["cuba.vars"] ||= {}
|
|
427
|
+
end
|
|
428
|
+
|
|
429
|
+
def not_found
|
|
430
|
+
res.status = 404
|
|
431
|
+
end
|
|
432
|
+
end
|
|
433
|
+
|
|
434
|
+
Cuba.settings[:req] = Rack::Request
|
|
435
|
+
Cuba.settings[:res] = Cuba::Response
|
|
436
|
+
Cuba.settings[:default_headers] = {}
|
data/cuba-4.0.3/makefile
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
test "accept mimetypes" do
|
|
4
|
+
Cuba.define do
|
|
5
|
+
on accept("application/xml") do
|
|
6
|
+
res.write res["content-type"]
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
env = { "HTTP_ACCEPT" => "application/xml",
|
|
11
|
+
"SCRIPT_NAME" => "/", "PATH_INFO" => "/post" }
|
|
12
|
+
|
|
13
|
+
_, _, body = Cuba.call(env)
|
|
14
|
+
|
|
15
|
+
assert_response body, ["application/xml"]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
test "tests don't fail when you don't specify an accept type" do
|
|
19
|
+
Cuba.define do
|
|
20
|
+
on accept("application/xml") do
|
|
21
|
+
res.write res["Content-Type"]
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
on default do
|
|
25
|
+
res.write "Default action"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
_, _, body = Cuba.call({})
|
|
30
|
+
|
|
31
|
+
assert_response body, ["Default action"]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
test "accept HTML mimetype" do
|
|
35
|
+
Cuba.define do
|
|
36
|
+
on accept("text/html") do
|
|
37
|
+
res.write Cuba::Response::ContentType::HTML
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
env = { "HTTP_ACCEPT" => "text/html",
|
|
42
|
+
"SCRIPT_NAME" => "/", "PATH_INFO" => "/post" }
|
|
43
|
+
|
|
44
|
+
_, _, body = Cuba.call(env)
|
|
45
|
+
|
|
46
|
+
assert_response body, ["text/html"]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
test "accept TEXT mimetype" do
|
|
50
|
+
Cuba.define do
|
|
51
|
+
on accept("text/plain") do
|
|
52
|
+
res.write Cuba::Response::ContentType::TEXT
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
env = { "HTTP_ACCEPT" => "text/plain",
|
|
57
|
+
"SCRIPT_NAME" => "/", "PATH_INFO" => "/post" }
|
|
58
|
+
|
|
59
|
+
_, _, body = Cuba.call(env)
|
|
60
|
+
|
|
61
|
+
assert_response body, ["text/plain"]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
test "accept JSON mimetype" do
|
|
65
|
+
Cuba.define do
|
|
66
|
+
on accept("application/json") do
|
|
67
|
+
res.write Cuba::Response::ContentType::JSON
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
env = { "HTTP_ACCEPT" => "application/json",
|
|
72
|
+
"SCRIPT_NAME" => "/", "PATH_INFO" => "/get" }
|
|
73
|
+
|
|
74
|
+
_, _, body = Cuba.call(env)
|
|
75
|
+
|
|
76
|
+
assert_response body, ["application/json"]
|
|
77
|
+
end
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
require File.expand_path("helper", File.dirname(__FILE__))
|
|
2
|
+
|
|
3
|
+
test "doesn't yield HOST" do
|
|
4
|
+
Cuba.define do
|
|
5
|
+
on host("example.com") do |*args|
|
|
6
|
+
res.write args.size
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
env = { "HTTP_HOST" => "example.com" }
|
|
11
|
+
|
|
12
|
+
_, _, resp = Cuba.call(env)
|
|
13
|
+
|
|
14
|
+
assert_response resp, ["0"]
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
test "doesn't yield the verb" do
|
|
18
|
+
Cuba.define do
|
|
19
|
+
on get do |*args|
|
|
20
|
+
res.write args.size
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
env = { "REQUEST_METHOD" => "GET" }
|
|
25
|
+
|
|
26
|
+
_, _, resp = Cuba.call(env)
|
|
27
|
+
|
|
28
|
+
assert_response resp, ["0"]
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
test "doesn't yield the path" do
|
|
32
|
+
Cuba.define do
|
|
33
|
+
on get, "home" do |*args|
|
|
34
|
+
res.write args.size
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/home",
|
|
39
|
+
"SCRIPT_NAME" => "/" }
|
|
40
|
+
|
|
41
|
+
_, _, resp = Cuba.call(env)
|
|
42
|
+
|
|
43
|
+
assert_response resp, ["0"]
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
test "yields the segment" do
|
|
47
|
+
Cuba.define do
|
|
48
|
+
on get, "user", :id do |id|
|
|
49
|
+
res.write id
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/user/johndoe",
|
|
54
|
+
"SCRIPT_NAME" => "/" }
|
|
55
|
+
|
|
56
|
+
_, _, resp = Cuba.call(env)
|
|
57
|
+
|
|
58
|
+
assert_response resp, ["johndoe"]
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
test "yields a number" do
|
|
62
|
+
Cuba.define do
|
|
63
|
+
on get, "user", :id do |id|
|
|
64
|
+
res.write id
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/user/101",
|
|
69
|
+
"SCRIPT_NAME" => "/" }
|
|
70
|
+
|
|
71
|
+
_, _, resp = Cuba.call(env)
|
|
72
|
+
|
|
73
|
+
assert_response resp, ["101"]
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
test "yield a file name with a matching extension" do
|
|
77
|
+
Cuba.define do
|
|
78
|
+
on get, "css", extension("css") do |file|
|
|
79
|
+
res.write file
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/css/app.css",
|
|
84
|
+
"SCRIPT_NAME" => "/" }
|
|
85
|
+
|
|
86
|
+
_, _, resp = Cuba.call(env)
|
|
87
|
+
|
|
88
|
+
assert_response resp, ["app"]
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
test "yields a segment per nested block" do
|
|
92
|
+
Cuba.define do
|
|
93
|
+
on :one do |one|
|
|
94
|
+
on :two do |two|
|
|
95
|
+
on :three do |three|
|
|
96
|
+
res.write one
|
|
97
|
+
res.write two
|
|
98
|
+
res.write three
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/one/two/three",
|
|
105
|
+
"SCRIPT_NAME" => "/" }
|
|
106
|
+
|
|
107
|
+
_, _, resp = Cuba.call(env)
|
|
108
|
+
|
|
109
|
+
assert_response resp, ["one", "two", "three"]
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
test "consumes a slash if needed" do
|
|
113
|
+
Cuba.define do
|
|
114
|
+
on get, "(.+\\.css)" do |file|
|
|
115
|
+
res.write file
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
env = { "REQUEST_METHOD" => "GET", "PATH_INFO" => "/foo/bar.css",
|
|
120
|
+
"SCRIPT_NAME" => "/" }
|
|
121
|
+
|
|
122
|
+
_, _, resp = Cuba.call(env)
|
|
123
|
+
|
|
124
|
+
assert_response resp, ["foo/bar.css"]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
test "regex captures in string format" do
|
|
128
|
+
Cuba.define do
|
|
129
|
+
on get, "posts/(\\d+)-(.*)" do |id, slug|
|
|
130
|
+
res.write id
|
|
131
|
+
res.write slug
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
env = { "REQUEST_METHOD" => "GET",
|
|
137
|
+
"PATH_INFO" => "/posts/123-postal-service",
|
|
138
|
+
"SCRIPT_NAME" => "/" }
|
|
139
|
+
|
|
140
|
+
_, _, resp = Cuba.call(env)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
assert_response resp, ["123", "postal-service"]
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
test "regex captures in regex format" do
|
|
147
|
+
Cuba.define do
|
|
148
|
+
on get, %r{posts/(\d+)-(.*)} do |id, slug|
|
|
149
|
+
res.write id
|
|
150
|
+
res.write slug
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
env = { "REQUEST_METHOD" => "GET",
|
|
155
|
+
"PATH_INFO" => "/posts/123-postal-service",
|
|
156
|
+
"SCRIPT_NAME" => "/" }
|
|
157
|
+
|
|
158
|
+
_, _, resp = Cuba.call(env)
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
assert_response resp, ["123", "postal-service"]
|
|
162
|
+
end
|