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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b17a05c593a91658be1194fb29c9564dcd6e3fe2
4
- data.tar.gz: 153d3095509dd43d37e9297c963aea4e39ef2cbe
3
+ metadata.gz: 8a70fe0e0ab208cb5ca42d42d60633e1c03ec77a
4
+ data.tar.gz: 5aedc804b0e884c1a05d87d325bf4c28e4798c6c
5
5
  SHA512:
6
- metadata.gz: d3871a3650c8490435fd94d13148a5ba90c636779cb1235c94c0bb61d76ef35efa8cf5c504e29271a3e34bcc818c179ed8a709e1576ccbeb53fb8c4825da2272
7
- data.tar.gz: 0ab43b3207200bfc783a00d714b058d04220a1ab1dd333670b8c2d590530cab87564e9323f4023a6c8e48f63a50fd3cf82f842429ae395bdf8f192905fb035bd
6
+ metadata.gz: c9ea6a8f1ea687572e874af5d6ff92a06617bc7881d1337a307b64f63c125912e836a577944f156da1c340a8b11eecf78cf780bcef42be122d29fe3887f8f457
7
+ data.tar.gz: 6ef1c484659735450a053f3f3d5dddf7bc4f65c78bfe52e54d1930529938f3a99c8810c2b0ea69846fc11cb757727f9dbe2df43ae90aae60af1c5d0bac75a893
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 2.25.0 (2017-04-18)
2
+
3
+ * Add error_mail plugin, similar to error_email but using mail instead of net/smtp directly (jeremyevans)
4
+
1
5
  = 2.24.0 (2017-03-15)
2
6
 
3
7
  * Have h plugin use cgi/escape if available for faster escaping (jeremyevans)
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
@@ -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
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 2
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 24
7
+ RodaMinorVersion = 25
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -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.24.0
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-03-15 00:00:00.000000000 Z
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.8
457
+ rubygems_version: 2.6.11
454
458
  signing_key:
455
459
  specification_version: 4
456
460
  summary: Routing tree web toolkit