roda 2.24.0 → 2.25.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.
- 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
|