roda 3.11.0 → 3.12.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
  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