roda 2.24.0 → 2.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +4 -0
- data/README.rdoc +44 -0
- data/doc/release_notes/2.25.0.txt +14 -0
- data/lib/roda/plugins/error_email.rb +6 -1
- data/lib/roda/plugins/error_mail.rb +133 -0
- data/lib/roda/plugins/flash.rb +8 -0
- data/lib/roda/version.rb +1 -1
- data/spec/matchers_spec.rb +33 -0
- data/spec/plugin/error_mail_spec.rb +93 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8a70fe0e0ab208cb5ca42d42d60633e1c03ec77a
|
4
|
+
data.tar.gz: 5aedc804b0e884c1a05d87d325bf4c28e4798c6c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c9ea6a8f1ea687572e874af5d6ff92a06617bc7881d1337a307b64f63c125912e836a577944f156da1c340a8b11eecf78cf780bcef42be122d29fe3887f8f457
|
7
|
+
data.tar.gz: 6ef1c484659735450a053f3f3d5dddf7bc4f65c78bfe52e54d1930529938f3a99c8810c2b0ea69846fc11cb757727f9dbe2df43ae90aae60af1c5d0bac75a893
|
data/CHANGELOG
CHANGED
data/README.rdoc
CHANGED
@@ -415,6 +415,50 @@ If +false+ or +nil+ is given directly as a matcher, it doesn't match anything.
|
|
415
415
|
|
416
416
|
Everything else matches anything.
|
417
417
|
|
418
|
+
== Optional segments
|
419
|
+
|
420
|
+
There are multiple ways you can handle optional segments in Roda. For example,
|
421
|
+
let's say you want to accept both +/items/123+ and +/items/123/456+, with 123 being
|
422
|
+
the item's id, and 456 being some optional data.
|
423
|
+
|
424
|
+
The simplest way to handle this is by treating this as two separate routes with a
|
425
|
+
shared branch:
|
426
|
+
|
427
|
+
r.on "items", :id do |item_id|
|
428
|
+
# Shared code for branch here
|
429
|
+
|
430
|
+
# /items/123/456
|
431
|
+
r.is :opt_data do |optional_data|
|
432
|
+
end
|
433
|
+
|
434
|
+
# /items/123
|
435
|
+
r.is do
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
This works well for many cases, but there are also cases where you really want to
|
440
|
+
treat it as one route with an optional segment. One simple way to do that is to
|
441
|
+
use a parameter instead of an optional segment (e.g. +/items/123?opt=456+).
|
442
|
+
|
443
|
+
r.is "items", :id do |item_id|
|
444
|
+
optional_data = r['opt']
|
445
|
+
end
|
446
|
+
|
447
|
+
However, if you really do want to use a optional segment, there are a couple different
|
448
|
+
ways to use matchers to do so. One is using an array matcher where the last element
|
449
|
+
is true:
|
450
|
+
|
451
|
+
r.is "items", :id, [:opt_data, true] do |item_id, optional_data|
|
452
|
+
end
|
453
|
+
|
454
|
+
Note that this technically yields only one argument instead of two arguments if the
|
455
|
+
optional segment isn't provided.
|
456
|
+
|
457
|
+
An alternative way to implement this is via a regexp:
|
458
|
+
|
459
|
+
r.is "items", /(\d+)(?:\/(\d+))?/ do |item_id, optional_data|
|
460
|
+
end
|
461
|
+
|
418
462
|
== Status codes
|
419
463
|
|
420
464
|
When it comes time to finalize a response,
|
@@ -0,0 +1,14 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An error_mail plugin has been added for reporting exceptions raised
|
4
|
+
via email. This is similar to the existing error_email plugin, but
|
5
|
+
uses the mail library instead of net/smtp directly. If you are
|
6
|
+
already using the mail library and the error_email plugin in your
|
7
|
+
application, it's recommended to switch to the error_mail plugin.
|
8
|
+
Example:
|
9
|
+
|
10
|
+
plugin :error_mail, :to=>'to@example.com', :from=>'from@example.com'
|
11
|
+
plugin :error_handler do |e|
|
12
|
+
error_mail(e)
|
13
|
+
'Internal Server Error'
|
14
|
+
end
|
@@ -14,6 +14,11 @@ class Roda
|
|
14
14
|
# 'Internal Server Error'
|
15
15
|
# end
|
16
16
|
#
|
17
|
+
# It is similar to the error_mail plugin, except that it uses net/smtp
|
18
|
+
# directly instead of using the mail library. If you are not already using the
|
19
|
+
# mail library in your application, it makes sense to use error_email
|
20
|
+
# instead of error_mail.
|
21
|
+
#
|
17
22
|
# Options:
|
18
23
|
#
|
19
24
|
# :from :: The From address to use in the email (required)
|
@@ -34,7 +39,7 @@ class Roda
|
|
34
39
|
module ErrorEmail
|
35
40
|
OPTS = {}.freeze
|
36
41
|
DEFAULTS = {
|
37
|
-
:headers=>
|
42
|
+
:headers=>OPTS,
|
38
43
|
:host=>'localhost',
|
39
44
|
# :nocov:
|
40
45
|
:emailer=>lambda{|h| Net::SMTP.start(h[:host]){|s| s.send_message(h[:message], h[:from], h[:to])}},
|
@@ -0,0 +1,133 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
|
3
|
+
require 'mail'
|
4
|
+
|
5
|
+
class Roda
|
6
|
+
module RodaPlugins
|
7
|
+
# The error_mail plugin adds an +error_mail+ instance method that
|
8
|
+
# send an email related to the exception. This is most useful if you are
|
9
|
+
# also using the error_handler plugin:
|
10
|
+
#
|
11
|
+
# plugin :error_mail, :to=>'to@example.com', :from=>'from@example.com'
|
12
|
+
# plugin :error_handler do |e|
|
13
|
+
# error_mail(e)
|
14
|
+
# 'Internal Server Error'
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# It is similar to the error_email plugin, except that it uses the mail
|
18
|
+
# library instead of net/smtp directly. If you are already using the
|
19
|
+
# mail library in your application, it makes sense to use error_mail
|
20
|
+
# instead of error_email.
|
21
|
+
#
|
22
|
+
# Options:
|
23
|
+
#
|
24
|
+
# :from :: The From address to use in the email (required)
|
25
|
+
# :headers :: A hash of additional headers to use in the email (default: empty hash)
|
26
|
+
# :prefix :: A prefix to use in the email's subject line (default: no prefix)
|
27
|
+
# :to :: The To address to use in the email (required)
|
28
|
+
#
|
29
|
+
# The subject of the error email shows the exception class and message.
|
30
|
+
# The body of the error email shows the backtrace of the error and the
|
31
|
+
# request environment, as well the request params and session variables (if any).
|
32
|
+
# You can also call error_mail with a plain string instead of an exception,
|
33
|
+
# in which case the string is used as the subject, and no backtrace is included.
|
34
|
+
#
|
35
|
+
# Note that emailing on every error as shown above is only appropriate
|
36
|
+
# for low traffic web applications. For high traffic web applications,
|
37
|
+
# use an error reporting service instead of this plugin.
|
38
|
+
module ErrorMail
|
39
|
+
OPTS = {}.freeze
|
40
|
+
|
41
|
+
# Set default opts for plugin. See ErrorEmail module RDoc for options.
|
42
|
+
def self.configure(app, opts=OPTS)
|
43
|
+
app.opts[:error_mail] = email_opts = (app.opts[:error_mail] || OPTS).merge(opts).freeze
|
44
|
+
unless email_opts[:to] && email_opts[:from]
|
45
|
+
raise RodaError, "must provide :to and :from options to error_mail plugin"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
module InstanceMethods
|
50
|
+
# Send an email for the given error. +exception+ is usually an exception
|
51
|
+
# instance, but it can be a plain string which is used as the subject for
|
52
|
+
# the email.
|
53
|
+
def error_mail(exception)
|
54
|
+
_error_mail(exception).deliver!
|
55
|
+
end
|
56
|
+
|
57
|
+
# The content of the email to send, include the headers and the body.
|
58
|
+
# Takes the same argument as #error_mail.
|
59
|
+
def error_mail_content(exception)
|
60
|
+
_error_mail(exception).to_s
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def _error_mail(e)
|
66
|
+
email_opts = self.class.opts[:error_mail]
|
67
|
+
subject = if e.respond_to?(:message)
|
68
|
+
"#{e.class}: #{e.message}"
|
69
|
+
else
|
70
|
+
e.to_s
|
71
|
+
end
|
72
|
+
subject = "#{email_opts[:prefix]}#{subject}"
|
73
|
+
|
74
|
+
format = lambda{|h| h.map{|k, v| "#{k.inspect} => #{v.inspect}"}.sort.join("\n")}
|
75
|
+
|
76
|
+
message = String.new
|
77
|
+
message << <<END
|
78
|
+
Path: #{request.path}
|
79
|
+
|
80
|
+
END
|
81
|
+
if e.respond_to?(:backtrace)
|
82
|
+
message << <<END
|
83
|
+
Backtrace:
|
84
|
+
|
85
|
+
#{e.backtrace.join("\n")}
|
86
|
+
END
|
87
|
+
end
|
88
|
+
|
89
|
+
message << <<END
|
90
|
+
|
91
|
+
ENV:
|
92
|
+
|
93
|
+
#{format[env]}
|
94
|
+
END
|
95
|
+
|
96
|
+
unless request.params.empty?
|
97
|
+
message << <<END
|
98
|
+
|
99
|
+
Params:
|
100
|
+
|
101
|
+
#{format[request.params]}
|
102
|
+
END
|
103
|
+
end
|
104
|
+
|
105
|
+
if env['rack.session']
|
106
|
+
message << <<END
|
107
|
+
|
108
|
+
Session:
|
109
|
+
|
110
|
+
#{format[session]}
|
111
|
+
END
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
Mail.new do
|
116
|
+
from email_opts[:from]
|
117
|
+
to email_opts[:to]
|
118
|
+
subject subject
|
119
|
+
body message
|
120
|
+
|
121
|
+
if headers = email_opts[:headers]
|
122
|
+
headers.each do |k,v|
|
123
|
+
header[k] = v
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
register_plugin(:error_mail, ErrorMail)
|
132
|
+
end
|
133
|
+
end
|
data/lib/roda/plugins/flash.rb
CHANGED
@@ -27,6 +27,14 @@ class Roda
|
|
27
27
|
# end
|
28
28
|
# end
|
29
29
|
# end
|
30
|
+
#
|
31
|
+
# You can modify the flash for the current request (instead of
|
32
|
+
# the next request) by using the +flash.now+ method:
|
33
|
+
#
|
34
|
+
# r.get do
|
35
|
+
# flash.now['a'] = 'b'
|
36
|
+
# flash['a'] # = >'b'
|
37
|
+
# end
|
30
38
|
module Flash
|
31
39
|
# The internal session key used to store the flash.
|
32
40
|
KEY = :_flash
|
data/lib/roda/version.rb
CHANGED
data/spec/matchers_spec.rb
CHANGED
@@ -275,6 +275,39 @@ describe "matchers" do
|
|
275
275
|
status('/123bard').must_equal 404
|
276
276
|
end
|
277
277
|
|
278
|
+
it "should allow arrays to match optional segments with splats" do
|
279
|
+
app do |r|
|
280
|
+
r.on :foo, [:bar, true] do |*m|
|
281
|
+
m.inspect
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
body('/123').must_equal '["123"]'
|
286
|
+
body('/123/456').must_equal '["123", "456"]'
|
287
|
+
end
|
288
|
+
|
289
|
+
it "should allow arrays to match optional segments with separate arguments" do
|
290
|
+
app do |r|
|
291
|
+
r.on :foo, [:bar, true] do |f, b|
|
292
|
+
[f, b].inspect
|
293
|
+
end
|
294
|
+
end
|
295
|
+
|
296
|
+
body('/123').must_equal '["123", nil]'
|
297
|
+
body('/123/456').must_equal '["123", "456"]'
|
298
|
+
end
|
299
|
+
|
300
|
+
it "should allow regexp to match optional segments with separate arguments" do
|
301
|
+
app do |r|
|
302
|
+
r.on /(\d+)(?:\/(\d+))?/ do |f, b|
|
303
|
+
[f, b].inspect
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
body('/123').must_equal '["123", nil]'
|
308
|
+
body('/123/456').must_equal '["123", "456"]'
|
309
|
+
end
|
310
|
+
|
278
311
|
it "should have array capture match string if match" do
|
279
312
|
app do |r|
|
280
313
|
r.on %w'p q' do |id|
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require File.expand_path("spec_helper", File.dirname(File.dirname(__FILE__)))
|
2
|
+
|
3
|
+
begin
|
4
|
+
require 'mail'
|
5
|
+
rescue LoadError
|
6
|
+
warn "mail not installed, skipping mail plugin test"
|
7
|
+
else
|
8
|
+
Mail.defaults do
|
9
|
+
delivery_method :test
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "error_mail plugin" do
|
13
|
+
def app(opts={})
|
14
|
+
@emails = emails = [] unless defined?(@emails)
|
15
|
+
@app ||= super(:bare) do
|
16
|
+
plugin :error_mail, {:to=>'t', :from=>'f'}.merge(opts)
|
17
|
+
|
18
|
+
route do |r|
|
19
|
+
r.get('noerror'){error_mail("Problem"); 'g'}
|
20
|
+
raise ArgumentError, 'bad foo' rescue error_mail($!)
|
21
|
+
'e'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
before do
|
27
|
+
Mail::TestMailer.deliveries.clear
|
28
|
+
end
|
29
|
+
|
30
|
+
def email
|
31
|
+
Mail::TestMailer.deliveries.last
|
32
|
+
end
|
33
|
+
|
34
|
+
it "adds error_mail method for emailing exceptions" do
|
35
|
+
app
|
36
|
+
body('rack.input'=>StringIO.new, 'QUERY_STRING'=>'b=c', 'rack.session'=>{'d'=>'e'}).must_equal 'e'
|
37
|
+
email.to.must_equal ['t']
|
38
|
+
email.from.must_equal ['f']
|
39
|
+
email.header.to_s.must_match(/^Subject: ArgumentError: bad foo/)
|
40
|
+
email.body.to_s.must_match(/^Backtrace:$.+^ENV:$.+^"rack\.input" => .+^Params:$\s+^"b" => "c"$\s+^Session:$\s+^"d" => "e"$/m)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "have error_mail method support string arguments" do
|
44
|
+
app
|
45
|
+
body('/noerror', 'rack.input'=>StringIO.new, 'QUERY_STRING'=>'b=c', 'rack.session'=>{'d'=>'e'}).must_equal 'g'
|
46
|
+
email.to.must_equal ['t']
|
47
|
+
email.from.must_equal ['f']
|
48
|
+
email.header.to_s.must_match(/^Subject: Problem/)
|
49
|
+
email.body.to_s.must_match(/^ENV:$.+^"rack\.input" => .+^Params:$\s+^"b" => "c"$\s+^Session:$\s+^"d" => "e"$/m)
|
50
|
+
email.body.to_s.wont_include('Backtrace')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "supports error_mail_content for the content of the email" do
|
54
|
+
app.route do |r|
|
55
|
+
raise ArgumentError, 'bad foo' rescue error_mail_content($!)
|
56
|
+
end
|
57
|
+
b = body('rack.input'=>StringIO.new, 'QUERY_STRING'=>'b=c', 'rack.session'=>{'d'=>'e'})
|
58
|
+
b.must_match(/^Subject: ArgumentError: bad foo/)
|
59
|
+
b.must_match(/^Backtrace:.+^ENV:.+^"rack\.input" => .+^Params:\s+^"b" => "c"\s+^Session:\s+^"d" => "e"/m)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "adds :prefix option to subject line" do
|
63
|
+
app(:prefix=>'TEST ')
|
64
|
+
body('rack.input'=>StringIO.new).must_equal 'e'
|
65
|
+
email.header.to_s.must_match(/^Subject: TEST ArgumentError/)
|
66
|
+
end
|
67
|
+
|
68
|
+
it "uses :headers option for additional headers" do
|
69
|
+
app(:headers=>{'Foo'=>'Bar', 'Baz'=>'Quux'})
|
70
|
+
body('rack.input'=>StringIO.new).must_equal 'e'
|
71
|
+
email.header.to_s.must_match(/^Foo: Bar/)
|
72
|
+
email.header.to_s.must_match(/^Baz: Quux/)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "requires the :to and :from options" do
|
76
|
+
proc{app :from=>nil}.must_raise(Roda::RodaError)
|
77
|
+
proc{app :to=>nil}.must_raise(Roda::RodaError)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "works correctly in subclasses" do
|
81
|
+
@app = Class.new(app)
|
82
|
+
@app.route do |r|
|
83
|
+
raise ArgumentError rescue error_mail($!)
|
84
|
+
'e'
|
85
|
+
end
|
86
|
+
body('rack.input'=>StringIO.new).must_equal 'e'
|
87
|
+
email.to.must_equal ['t']
|
88
|
+
email.from.must_equal ['f']
|
89
|
+
email.header.to_s.must_match(/^Subject: ArgumentError: ArgumentError/)
|
90
|
+
email.body.to_s.must_match(/^Backtrace:$.+^ENV:$.+^"rack\.input" => .+/m)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: roda
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.25.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-04-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rack
|
@@ -204,6 +204,7 @@ extra_rdoc_files:
|
|
204
204
|
- doc/release_notes/2.22.0.txt
|
205
205
|
- doc/release_notes/2.23.0.txt
|
206
206
|
- doc/release_notes/2.24.0.txt
|
207
|
+
- doc/release_notes/2.25.0.txt
|
207
208
|
files:
|
208
209
|
- CHANGELOG
|
209
210
|
- MIT-LICENSE
|
@@ -232,6 +233,7 @@ files:
|
|
232
233
|
- doc/release_notes/2.22.0.txt
|
233
234
|
- doc/release_notes/2.23.0.txt
|
234
235
|
- doc/release_notes/2.24.0.txt
|
236
|
+
- doc/release_notes/2.25.0.txt
|
235
237
|
- doc/release_notes/2.3.0.txt
|
236
238
|
- doc/release_notes/2.4.0.txt
|
237
239
|
- doc/release_notes/2.5.0.txt
|
@@ -263,6 +265,7 @@ files:
|
|
263
265
|
- lib/roda/plugins/environments.rb
|
264
266
|
- lib/roda/plugins/error_email.rb
|
265
267
|
- lib/roda/plugins/error_handler.rb
|
268
|
+
- lib/roda/plugins/error_mail.rb
|
266
269
|
- lib/roda/plugins/flash.rb
|
267
270
|
- lib/roda/plugins/h.rb
|
268
271
|
- lib/roda/plugins/halt.rb
|
@@ -353,6 +356,7 @@ files:
|
|
353
356
|
- spec/plugin/environments_spec.rb
|
354
357
|
- spec/plugin/error_email_spec.rb
|
355
358
|
- spec/plugin/error_handler_spec.rb
|
359
|
+
- spec/plugin/error_mail_spec.rb
|
356
360
|
- spec/plugin/flash_spec.rb
|
357
361
|
- spec/plugin/h_spec.rb
|
358
362
|
- spec/plugin/halt_spec.rb
|
@@ -450,7 +454,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
450
454
|
version: '0'
|
451
455
|
requirements: []
|
452
456
|
rubyforge_project:
|
453
|
-
rubygems_version: 2.6.
|
457
|
+
rubygems_version: 2.6.11
|
454
458
|
signing_key:
|
455
459
|
specification_version: 4
|
456
460
|
summary: Routing tree web toolkit
|