roda 3.11.0 → 3.12.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b7fc4e03ddcb9b4e5fa37af6c0a151aef49965ef251d8ab177e7b530ec034529
4
- data.tar.gz: 78ab54340363d9b963496e143809ca9f809c619d25f98faa08b08f9e3790ae19
3
+ metadata.gz: '0839d9d46fb688ab858d224dff6293be6572d9fc2938ea0696cb34bc6cb14949'
4
+ data.tar.gz: c3cbc4adc620b1f4776d8258e44751f7206837ba12a6a8e4917e5bf6d5a7efec
5
5
  SHA512:
6
- metadata.gz: 56b9bb6e1e03cfa079457238d532d9223fdd6adef8438954dfd601afb68b384945b528c2606a3100881f3154e94024ae46f286364c41fce4c23af624a1ebcee3
7
- data.tar.gz: 3856d03202576bd35465b29e975bb84fe23780695b87442c3ba8b09b7e36deb19cddf6a4ea5299249baf21c9e7b62b61e681221580b6d7a5af6b84858395d8a0
6
+ metadata.gz: 7e595d7c11c1486131b3e46b596c6d38a65dac41bb72e686fdbc260f629ce4a639543501e30754412d6252942204d8fa906579bb54a6ff224233dfb333610bfd
7
+ data.tar.gz: 97f598575e2a13907021cb9024cfa4c6c7c6e1ce68c69639794f89db068a7ee2c27b35bace18b568ae841ae51b6ebf6404c611f3f3f1692b48b95bf1207073a4
data/CHANGELOG CHANGED
@@ -1,3 +1,7 @@
1
+ = 3.12.0 (2018-09-14)
2
+
3
+ * Add common_logger plugin for common log support (jeremyevans)
4
+
1
5
  = 3.11.0 (2018-08-15)
2
6
 
3
7
  * Disable default compression of sessions over 128 bytes in the sessions plugin (jeremyevans)
@@ -0,0 +1,19 @@
1
+ = New Features
2
+
3
+ * A common_logger plugin has been added for common log support. This
4
+ offers about 30% better performance than Rack::CommonLogger, with
5
+ the following differences:
6
+
7
+ * When timing requests, doesn't consider middleware or proxy the
8
+ body, so timing information is just the time that Roda takes
9
+ to process the request.
10
+ * Only looks for "Content-Length" as a header, not different
11
+ capitalizations (Roda only uses "Content-Length" internally).
12
+ * Logs to $stderr instead of rack.errors in request environment
13
+ if a logger object is not explicitly passed.
14
+
15
+ = Other Improvements
16
+
17
+ * Internal before/after hook methods now use more descriptive names
18
+ for easier debugging, with a naming format designed to not
19
+ conflict with hook methods in external plugins.
@@ -13,7 +13,7 @@ class Roda
13
13
  # have added a _roda_after_* method.
14
14
  def include(*)
15
15
  res = super
16
- meths = private_instance_methods.grep(/\A_roda_after_\d\d\z/).sort.map{|s| "#{s}(res)"}.join(';')
16
+ meths = private_instance_methods.grep(/\A_roda_after_\d\d/).sort.map{|s| "#{s}(res)"}.join(';')
17
17
  class_eval("def _roda_after(res); #{meths} end", __FILE__, __LINE__)
18
18
  private :_roda_after
19
19
  res
@@ -31,7 +31,7 @@ class Roda
31
31
  # Build a _roda_before method that calls each _roda_before_* method
32
32
  # in order.
33
33
  def def_roda_before
34
- meths = private_instance_methods.grep(/\A_roda_before_\d\d\z/).sort.join(';')
34
+ meths = private_instance_methods.grep(/\A_roda_before_\d\d/).sort.join(';')
35
35
  class_eval("def _roda_before; #{meths} end", __FILE__, __LINE__)
36
36
  private :_roda_before
37
37
  end
@@ -89,7 +89,7 @@ class Roda
89
89
 
90
90
  # If the normal routing tree doesn't handle an action, try each class level route
91
91
  # to see if it matches.
92
- def _roda_after_10(result)
92
+ def _roda_after_10__class_level_routing(result)
93
93
  if result && result[0] == 404 && (v = result[2]).is_a?(Array) && v.empty?
94
94
  # Reset the response so it doesn't inherit the status or any headers from
95
95
  # the original response.
@@ -0,0 +1,70 @@
1
+ # frozen-string-literal: true
2
+
3
+ #
4
+ class Roda
5
+ module RodaPlugins
6
+ # The common_logger plugin adds common logger support to Roda
7
+ # applications, similar to Rack::CommonLogger, with the following
8
+ # differences:
9
+ #
10
+ # * Better performance
11
+ # * Doesn't include middleware timing
12
+ # * Doesn't proxy the body
13
+ # * Doesn't support different capitalization of the Content-Length response header
14
+ # * Logs to $stderr instead of env['rack.errors'] if explicit logger not passed
15
+ #
16
+ # Example:
17
+ #
18
+ # plugin :common_logger
19
+ # plugin :common_logger, $stdout
20
+ # plugin :common_logger, Logger.new('filename')
21
+ module CommonLogger
22
+ def self.load_dependencies(app, _=nil)
23
+ app.plugin :_after_hook
24
+ app.plugin :_before_hook
25
+ end
26
+
27
+ def self.configure(app, logger=nil)
28
+ app.opts[:common_logger] = logger || app.opts[:common_logger] || $stderr
29
+ app.opts[:common_logger_meth] = app.opts[:common_logger].method(logger.respond_to?(:write) ? :write : :<<)
30
+ end
31
+
32
+ if RUBY_VERSION >= '2.1'
33
+ # A timer object for calculating elapsed time.
34
+ def self.start_timer
35
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
36
+ end
37
+ else
38
+ # :nocov:
39
+ def self.start_timer # :nodoc:
40
+ Time.now
41
+ end
42
+ # :nocov:
43
+ end
44
+
45
+ module InstanceMethods
46
+ private
47
+
48
+ # Log request/response information in common log format to logger.
49
+ def _roda_after_90__common_logger(result)
50
+ elapsed_time = if timer = @_request_timer
51
+ '%0.4f' % (CommonLogger.start_timer - timer)
52
+ else
53
+ '-'
54
+ end
55
+
56
+ env = @_request.env
57
+
58
+ opts[:common_logger_meth].call("#{env['HTTP_X_FORWARDED_FOR'] || env["REMOTE_ADDR"] || "-"} - #{env["REMOTE_USER"] || "-"} [#{Time.now.strftime("%d/%b/%Y:%H:%M:%S %z")}] \"#{env["REQUEST_METHOD"]} #{env["PATH_INFO"]}#{"?#{env["QUERY_STRING"]}" if ((qs = env["QUERY_STRING"]) && !qs.empty?)} #{env["HTTP_VERSION"]}\" #{result[0]} #{((length = result[1]['Content-Length']) && (length unless length == '0')) || '-'} #{elapsed_time}\n")
59
+ end
60
+
61
+ # Create timer instance used for timing
62
+ def _roda_before_05__common_logger
63
+ @_request_timer = CommonLogger.start_timer
64
+ end
65
+ end
66
+ end
67
+
68
+ register_plugin(:common_logger, CommonLogger)
69
+ end
70
+ end
@@ -103,7 +103,7 @@ class Roda
103
103
 
104
104
  # If the routing doesn't raise an error, rotate the flash
105
105
  # hash in the session so the next request has access to it.
106
- def _roda_after_40(_)
106
+ def _roda_after_40__flash(_)
107
107
  if f = @_flash
108
108
  f = f.next
109
109
  if f.empty?
@@ -63,7 +63,7 @@ class Roda
63
63
 
64
64
  # Always use an empty response body for head requests, with a
65
65
  # content length of 0.
66
- def _roda_after_30(res)
66
+ def _roda_after_30__head(res)
67
67
  if res && @_request.head?
68
68
  body = res[2]
69
69
  if body.respond_to?(:close)
@@ -29,7 +29,7 @@ class Roda
29
29
  private
30
30
 
31
31
  # If the request is for a heartbeat path, return the heartbeat response.
32
- def _roda_before_20
32
+ def _roda_before_20__heartbeat
33
33
  if env['PATH_INFO'] == opts[:heartbeat_path]
34
34
  response = HEARTBEAT_RESPONSE.dup
35
35
  response[1] = Hash[response[1]]
@@ -79,14 +79,14 @@ class Roda
79
79
  private
80
80
 
81
81
  # Run after hooks.
82
- def _roda_after_90(res)
82
+ def _roda_after_80__hooks(res)
83
83
  if b = opts[:after_hook]
84
84
  instance_exec(res, &b)
85
85
  end
86
86
  end
87
87
 
88
88
  # Run before hooks.
89
- def _roda_before_10
89
+ def _roda_before_10__hooks
90
90
  if b = opts[:before_hook]
91
91
  instance_exec(&b)
92
92
  end
@@ -208,7 +208,7 @@ class Roda
208
208
  # If session information has been set in the request environment,
209
209
  # update the rack response headers to set the session cookie in
210
210
  # the response.
211
- def _roda_after_50(res)
211
+ def _roda_after_50__sessions(res)
212
212
  if res && (session = env['rack.session'])
213
213
  @_request.persist_session(res[1], session)
214
214
  end
@@ -109,7 +109,7 @@ class Roda
109
109
 
110
110
  # If there is a static routing method for the given path, call it
111
111
  # instead having the routing tree handle the request.
112
- def _roda_before_30
112
+ def _roda_before_30__static_routing
113
113
  r = @_request
114
114
  if route = self.class.static_route_for(r.request_method, r.path_info)
115
115
  r.static_route(&route)
@@ -48,7 +48,7 @@ class Roda
48
48
  private
49
49
 
50
50
  # If routing returns a response we have a handler for, call that handler.
51
- def _roda_after_20(result)
51
+ def _roda_after_20__status_handler(result)
52
52
  if result && (block = opts[:status_handler][result[0]]) && (v = result[2]).is_a?(Array) && v.empty?
53
53
  @_response.headers.clear
54
54
  result.replace(_call(&block))
@@ -4,7 +4,7 @@ class Roda
4
4
  RodaMajorVersion = 3
5
5
 
6
6
  # The minor version of Roda, updated for new feature releases of Roda.
7
- RodaMinorVersion = 11
7
+ RodaMinorVersion = 12
8
8
 
9
9
  # The patch version of Roda, updated only for bug fixes from the last
10
10
  # feature release.
@@ -0,0 +1,59 @@
1
+ require_relative "../spec_helper"
2
+
3
+ require 'logger'
4
+
5
+ describe "common_logger plugin" do
6
+ def cl_app(&block)
7
+ app(:common_logger, &block)
8
+ @logger = StringIO.new
9
+ @app.plugin :common_logger, @logger
10
+ end
11
+
12
+ it 'logs requests to given logger/stream' do
13
+ cl_app(&:path_info)
14
+
15
+ body.must_equal '/'
16
+ @logger.rewind
17
+ @logger.read.must_match /\A- - - \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "GET \/ " 200 1 0.\d\d\d\d\n\z/
18
+
19
+ @logger.rewind
20
+ @logger.truncate(0)
21
+ body('', 'HTTP_X_FORWARDED_FOR'=>'1.1.1.1', 'REMOTE_USER'=>'je', 'REQUEST_METHOD'=>'POST', 'QUERY_STRING'=>'', "HTTP_VERSION"=>'HTTP/1.1').must_equal ''
22
+ @logger.rewind
23
+ @logger.read.must_match /\A1\.1\.1\.1 - je \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "POST HTTP\/1.1" 200 - 0.\d\d\d\d\n\z/
24
+
25
+ @logger.rewind
26
+ @logger.truncate(0)
27
+ body('/b', 'REMOTE_ADDR'=>'1.1.1.2', 'QUERY_STRING'=>'foo=bar', "HTTP_VERSION"=>'HTTP/1.0').must_equal '/b'
28
+ @logger.rewind
29
+ @logger.read.must_match /\A1\.1\.1\.2 - - \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "GET \/b\?foo=bar HTTP\/1.0" 200 2 0.\d\d\d\d\n\z/
30
+
31
+ @app.plugin :common_logger, Logger.new(@logger)
32
+ @logger.rewind
33
+ @logger.truncate(0)
34
+ body.must_equal '/'
35
+ @logger.rewind
36
+ @logger.read.must_match /\A- - - \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "GET \/ " 200 1 0.\d\d\d\d\n\z/
37
+ end
38
+
39
+ it 'skips timer information if not available' do
40
+ cl_app do |r|
41
+ @_request_timer = nil
42
+ r.path_info
43
+ end
44
+
45
+ body.must_equal '/'
46
+ @logger.rewind
47
+ @logger.read.must_match /\A- - - \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "GET \/ " 200 1 -\n\z/
48
+ end
49
+
50
+ it 'skips length information if not available' do
51
+ cl_app do |r|
52
+ r.halt [500, {}, []]
53
+ end
54
+
55
+ body.must_equal ''
56
+ @logger.rewind
57
+ @logger.read.must_match /\A- - - \[\d\d\/[A-Z][a-z]{2}\/\d\d\d\d:\d\d:\d\d:\d\d [-+]\d\d\d\d\] "GET \/ " 500 - 0.\d\d\d\d\n\z/
58
+ end
59
+ 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: 3.11.0
4
+ version: 3.12.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: 2018-08-15 00:00:00.000000000 Z
11
+ date: 2018-09-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rack
@@ -207,6 +207,7 @@ extra_rdoc_files:
207
207
  - doc/release_notes/3.9.0.txt
208
208
  - doc/release_notes/3.10.0.txt
209
209
  - doc/release_notes/3.11.0.txt
210
+ - doc/release_notes/3.12.0.txt
210
211
  files:
211
212
  - CHANGELOG
212
213
  - MIT-LICENSE
@@ -252,6 +253,7 @@ files:
252
253
  - doc/release_notes/3.1.0.txt
253
254
  - doc/release_notes/3.10.0.txt
254
255
  - doc/release_notes/3.11.0.txt
256
+ - doc/release_notes/3.12.0.txt
255
257
  - doc/release_notes/3.2.0.txt
256
258
  - doc/release_notes/3.3.0.txt
257
259
  - doc/release_notes/3.4.0.txt
@@ -273,6 +275,7 @@ files:
273
275
  - lib/roda/plugins/chunked.rb
274
276
  - lib/roda/plugins/class_level_routing.rb
275
277
  - lib/roda/plugins/class_matchers.rb
278
+ - lib/roda/plugins/common_logger.rb
276
279
  - lib/roda/plugins/content_for.rb
277
280
  - lib/roda/plugins/content_security_policy.rb
278
281
  - lib/roda/plugins/cookies.rb
@@ -373,6 +376,7 @@ files:
373
376
  - spec/plugin/chunked_spec.rb
374
377
  - spec/plugin/class_level_routing_spec.rb
375
378
  - spec/plugin/class_matchers_spec.rb
379
+ - spec/plugin/common_logger_spec.rb
376
380
  - spec/plugin/content_for_spec.rb
377
381
  - spec/plugin/content_security_policy_spec.rb
378
382
  - spec/plugin/cookies_spec.rb