actionpack 4.2.8 → 5.2.4.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (166) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +285 -444
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +6 -7
  5. data/lib/abstract_controller.rb +12 -5
  6. data/lib/abstract_controller/asset_paths.rb +2 -0
  7. data/lib/abstract_controller/base.rb +45 -49
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/{action_controller → abstract_controller}/caching/fragments.rb +78 -15
  10. data/lib/abstract_controller/callbacks.rb +47 -31
  11. data/lib/abstract_controller/collector.rb +8 -11
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +25 -25
  14. data/lib/abstract_controller/logger.rb +2 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +4 -2
  16. data/lib/abstract_controller/rendering.rb +42 -41
  17. data/lib/abstract_controller/translation.rb +10 -7
  18. data/lib/abstract_controller/url_for.rb +2 -0
  19. data/lib/action_controller.rb +29 -21
  20. data/lib/action_controller/api.rb +149 -0
  21. data/lib/action_controller/api/api_rendering.rb +16 -0
  22. data/lib/action_controller/base.rb +27 -19
  23. data/lib/action_controller/caching.rb +14 -57
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +10 -15
  26. data/lib/action_controller/metal.rb +98 -83
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +118 -44
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +3 -3
  31. data/lib/action_controller/metal/data_streaming.rb +27 -46
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +20 -13
  34. data/lib/action_controller/metal/exceptions.rb +8 -14
  35. data/lib/action_controller/metal/flash.rb +4 -3
  36. data/lib/action_controller/metal/force_ssl.rb +23 -21
  37. data/lib/action_controller/metal/head.rb +21 -19
  38. data/lib/action_controller/metal/helpers.rb +24 -14
  39. data/lib/action_controller/metal/http_authentication.rb +64 -57
  40. data/lib/action_controller/metal/implicit_render.rb +62 -8
  41. data/lib/action_controller/metal/instrumentation.rb +19 -21
  42. data/lib/action_controller/metal/live.rb +90 -106
  43. data/lib/action_controller/metal/mime_responds.rb +33 -46
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +61 -53
  46. data/lib/action_controller/metal/redirecting.rb +49 -28
  47. data/lib/action_controller/metal/renderers.rb +87 -44
  48. data/lib/action_controller/metal/rendering.rb +72 -50
  49. data/lib/action_controller/metal/request_forgery_protection.rb +203 -92
  50. data/lib/action_controller/metal/rescue.rb +9 -16
  51. data/lib/action_controller/metal/streaming.rb +12 -10
  52. data/lib/action_controller/metal/strong_parameters.rb +582 -165
  53. data/lib/action_controller/metal/testing.rb +2 -17
  54. data/lib/action_controller/metal/url_for.rb +19 -10
  55. data/lib/action_controller/railtie.rb +28 -10
  56. data/lib/action_controller/railties/helpers.rb +2 -0
  57. data/lib/action_controller/renderer.rb +117 -0
  58. data/lib/action_controller/template_assertions.rb +11 -0
  59. data/lib/action_controller/test_case.rb +280 -411
  60. data/lib/action_dispatch.rb +27 -19
  61. data/lib/action_dispatch/http/cache.rb +93 -47
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +26 -20
  64. data/lib/action_dispatch/http/filter_redirect.rb +10 -11
  65. data/lib/action_dispatch/http/headers.rb +55 -22
  66. data/lib/action_dispatch/http/mime_negotiation.rb +60 -41
  67. data/lib/action_dispatch/http/mime_type.rb +134 -121
  68. data/lib/action_dispatch/http/mime_types.rb +20 -6
  69. data/lib/action_dispatch/http/parameter_filter.rb +25 -11
  70. data/lib/action_dispatch/http/parameters.rb +98 -39
  71. data/lib/action_dispatch/http/rack_cache.rb +2 -0
  72. data/lib/action_dispatch/http/request.rb +200 -118
  73. data/lib/action_dispatch/http/response.rb +225 -110
  74. data/lib/action_dispatch/http/upload.rb +12 -6
  75. data/lib/action_dispatch/http/url.rb +110 -28
  76. data/lib/action_dispatch/journey.rb +7 -5
  77. data/lib/action_dispatch/journey/formatter.rb +55 -32
  78. data/lib/action_dispatch/journey/gtg/builder.rb +7 -5
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +3 -9
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +17 -16
  81. data/lib/action_dispatch/journey/nfa/builder.rb +5 -3
  82. data/lib/action_dispatch/journey/nfa/dot.rb +13 -13
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +3 -1
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +5 -48
  85. data/lib/action_dispatch/journey/nodes/node.rb +18 -6
  86. data/lib/action_dispatch/journey/parser.rb +23 -22
  87. data/lib/action_dispatch/journey/parser.y +3 -2
  88. data/lib/action_dispatch/journey/parser_extras.rb +12 -4
  89. data/lib/action_dispatch/journey/path/pattern.rb +50 -44
  90. data/lib/action_dispatch/journey/route.rb +106 -28
  91. data/lib/action_dispatch/journey/router.rb +35 -23
  92. data/lib/action_dispatch/journey/router/utils.rb +20 -11
  93. data/lib/action_dispatch/journey/routes.rb +18 -16
  94. data/lib/action_dispatch/journey/scanner.rb +18 -15
  95. data/lib/action_dispatch/journey/visitors.rb +99 -52
  96. data/lib/action_dispatch/middleware/callbacks.rb +1 -2
  97. data/lib/action_dispatch/middleware/cookies.rb +304 -193
  98. data/lib/action_dispatch/middleware/debug_exceptions.rb +152 -57
  99. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  100. data/lib/action_dispatch/middleware/exception_wrapper.rb +68 -69
  101. data/lib/action_dispatch/middleware/executor.rb +21 -0
  102. data/lib/action_dispatch/middleware/flash.rb +78 -54
  103. data/lib/action_dispatch/middleware/public_exceptions.rb +27 -25
  104. data/lib/action_dispatch/middleware/reloader.rb +5 -91
  105. data/lib/action_dispatch/middleware/remote_ip.rb +41 -31
  106. data/lib/action_dispatch/middleware/request_id.rb +17 -9
  107. data/lib/action_dispatch/middleware/session/abstract_store.rb +41 -25
  108. data/lib/action_dispatch/middleware/session/cache_store.rb +24 -14
  109. data/lib/action_dispatch/middleware/session/cookie_store.rb +72 -67
  110. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -2
  111. data/lib/action_dispatch/middleware/show_exceptions.rb +26 -22
  112. data/lib/action_dispatch/middleware/ssl.rb +114 -36
  113. data/lib/action_dispatch/middleware/stack.rb +31 -44
  114. data/lib/action_dispatch/middleware/static.rb +57 -50
  115. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +2 -14
  116. data/lib/action_dispatch/middleware/templates/rescues/{_source.erb → _source.html.erb} +0 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +1 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +1 -1
  122. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +4 -4
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +64 -64
  125. data/lib/action_dispatch/railtie.rb +19 -11
  126. data/lib/action_dispatch/request/session.rb +106 -59
  127. data/lib/action_dispatch/request/utils.rb +67 -24
  128. data/lib/action_dispatch/routing.rb +17 -18
  129. data/lib/action_dispatch/routing/endpoint.rb +9 -2
  130. data/lib/action_dispatch/routing/inspector.rb +58 -67
  131. data/lib/action_dispatch/routing/mapper.rb +734 -447
  132. data/lib/action_dispatch/routing/polymorphic_routes.rb +161 -139
  133. data/lib/action_dispatch/routing/redirection.rb +36 -26
  134. data/lib/action_dispatch/routing/route_set.rb +321 -291
  135. data/lib/action_dispatch/routing/routes_proxy.rb +32 -5
  136. data/lib/action_dispatch/routing/url_for.rb +65 -25
  137. data/lib/action_dispatch/system_test_case.rb +147 -0
  138. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  139. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  140. data/lib/action_dispatch/system_testing/server.rb +31 -0
  141. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  142. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  143. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  144. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  145. data/lib/action_dispatch/testing/assertions.rb +6 -4
  146. data/lib/action_dispatch/testing/assertions/response.rb +45 -20
  147. data/lib/action_dispatch/testing/assertions/routing.rb +30 -26
  148. data/lib/action_dispatch/testing/integration.rb +347 -209
  149. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  150. data/lib/action_dispatch/testing/test_process.rb +28 -22
  151. data/lib/action_dispatch/testing/test_request.rb +27 -34
  152. data/lib/action_dispatch/testing/test_response.rb +35 -7
  153. data/lib/action_pack.rb +4 -2
  154. data/lib/action_pack/gem_version.rb +5 -3
  155. data/lib/action_pack/version.rb +3 -1
  156. metadata +56 -39
  157. data/lib/action_controller/metal/hide_actions.rb +0 -40
  158. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  159. data/lib/action_controller/middleware.rb +0 -39
  160. data/lib/action_controller/model_naming.rb +0 -12
  161. data/lib/action_dispatch/journey/backwards.rb +0 -5
  162. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  163. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  164. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  165. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  166. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  module Journey # :nodoc:
3
5
  class Router # :nodoc:
@@ -5,7 +7,7 @@ module ActionDispatch
5
7
  # Normalizes URI path.
6
8
  #
7
9
  # Strips off trailing slash and ensures there is a leading slash.
8
- # Also converts downcase url encoded string to uppercase.
10
+ # Also converts downcase URL encoded string to uppercase.
9
11
  #
10
12
  # normalize_path("/foo") # => "/foo"
11
13
  # normalize_path("/foo/") # => "/foo"
@@ -13,22 +15,25 @@ module ActionDispatch
13
15
  # normalize_path("") # => "/"
14
16
  # normalize_path("/%ab") # => "/%AB"
15
17
  def self.normalize_path(path)
16
- path = "/#{path}"
17
- path.squeeze!('/')
18
- path.sub!(%r{/+\Z}, '')
18
+ path ||= ""
19
+ encoding = path.encoding
20
+ path = "/#{path}".dup
21
+ path.squeeze!("/".freeze)
22
+ path.sub!(%r{/+\Z}, "".freeze)
19
23
  path.gsub!(/(%[a-f0-9]{2})/) { $1.upcase }
20
- path = '/' if path == ''
24
+ path = "/".dup if path == "".freeze
25
+ path.force_encoding(encoding)
21
26
  path
22
27
  end
23
28
 
24
29
  # URI path and fragment escaping
25
- # http://tools.ietf.org/html/rfc3986
30
+ # https://tools.ietf.org/html/rfc3986
26
31
  class UriEncoder # :nodoc:
27
32
  ENCODE = "%%%02X".freeze
28
33
  US_ASCII = Encoding::US_ASCII
29
34
  UTF_8 = Encoding::UTF_8
30
- EMPTY = "".force_encoding(US_ASCII).freeze
31
- DEC2HEX = (0..255).to_a.map{ |i| ENCODE % i }.map{ |s| s.force_encoding(US_ASCII) }
35
+ EMPTY = "".dup.force_encoding(US_ASCII).freeze
36
+ DEC2HEX = (0..255).to_a.map { |i| ENCODE % i }.map { |s| s.force_encoding(US_ASCII) }
32
37
 
33
38
  ALPHA = "a-zA-Z".freeze
34
39
  DIGIT = "0-9".freeze
@@ -55,12 +60,12 @@ module ActionDispatch
55
60
 
56
61
  def unescape_uri(uri)
57
62
  encoding = uri.encoding == US_ASCII ? UTF_8 : uri.encoding
58
- uri.gsub(ESCAPED) { [$&[1, 2].hex].pack('C') }.force_encoding(encoding)
63
+ uri.gsub(ESCAPED) { |match| [match[1, 2].hex].pack("C") }.force_encoding(encoding)
59
64
  end
60
65
 
61
- protected
66
+ private
62
67
  def escape(component, pattern)
63
- component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
68
+ component.gsub(pattern) { |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
64
69
  end
65
70
 
66
71
  def percent_encode(unsafe)
@@ -84,6 +89,10 @@ module ActionDispatch
84
89
  ENCODER.escape_fragment(fragment.to_s)
85
90
  end
86
91
 
92
+ # Replaces any escaped sequences with their unescaped representations.
93
+ #
94
+ # uri = "/topics?title=Ruby%20on%20Rails"
95
+ # unescape_uri(uri) #=> "/topics?title=Ruby on Rails"
87
96
  def self.unescape_uri(uri)
88
97
  ENCODER.unescape_uri(uri)
89
98
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionDispatch
2
4
  module Journey # :nodoc:
3
5
  # The Routing table. Contains all routes for a system. Routes can be
@@ -5,13 +7,13 @@ module ActionDispatch
5
7
  class Routes # :nodoc:
6
8
  include Enumerable
7
9
 
8
- attr_reader :routes, :named_routes
10
+ attr_reader :routes, :custom_routes, :anchored_routes
9
11
 
10
12
  def initialize
11
13
  @routes = []
12
- @named_routes = {}
13
14
  @ast = nil
14
- @partitioned_routes = nil
15
+ @anchored_routes = []
16
+ @custom_routes = []
15
17
  @simulator = nil
16
18
  end
17
19
 
@@ -34,36 +36,37 @@ module ActionDispatch
34
36
 
35
37
  def clear
36
38
  routes.clear
37
- named_routes.clear
39
+ anchored_routes.clear
40
+ custom_routes.clear
38
41
  end
39
42
 
40
- def partitioned_routes
41
- @partitioned_routes ||= routes.partition do |r|
42
- r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
43
+ def partition_route(route)
44
+ if route.path.anchored && route.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
45
+ anchored_routes << route
46
+ else
47
+ custom_routes << route
43
48
  end
44
49
  end
45
50
 
46
51
  def ast
47
52
  @ast ||= begin
48
- asts = partitioned_routes.first.map(&:ast)
49
- Nodes::Or.new(asts) unless asts.empty?
53
+ asts = anchored_routes.map(&:ast)
54
+ Nodes::Or.new(asts)
50
55
  end
51
56
  end
52
57
 
53
58
  def simulator
59
+ return if ast.nil?
54
60
  @simulator ||= begin
55
61
  gtg = GTG::Builder.new(ast).transition_table
56
62
  GTG::Simulator.new(gtg)
57
63
  end
58
64
  end
59
65
 
60
- # Add a route to the routing table.
61
- def add_route(app, path, conditions, defaults, name = nil)
62
- route = Route.new(name, app, path, conditions, defaults)
63
-
64
- route.precedence = routes.length
66
+ def add_route(name, mapping)
67
+ route = mapping.make_route name, routes.length
65
68
  routes << route
66
- named_routes[name] = route if name && !named_routes[name]
69
+ partition_route(route)
67
70
  clear_cache!
68
71
  route
69
72
  end
@@ -72,7 +75,6 @@ module ActionDispatch
72
75
 
73
76
  def clear_cache!
74
77
  @ast = nil
75
- @partitioned_routes = nil
76
78
  @simulator = nil
77
79
  end
78
80
  end
@@ -1,4 +1,6 @@
1
- require 'strscan'
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
2
4
 
3
5
  module ActionDispatch
4
6
  module Journey # :nodoc:
@@ -35,22 +37,23 @@ module ActionDispatch
35
37
  def scan
36
38
  case
37
39
  # /
38
- when text = @ss.scan(/\//)
39
- [:SLASH, text]
40
+ when @ss.skip(/\//)
41
+ [:SLASH, "/"]
42
+ when @ss.skip(/\(/)
43
+ [:LPAREN, "("]
44
+ when @ss.skip(/\)/)
45
+ [:RPAREN, ")"]
46
+ when @ss.skip(/\|/)
47
+ [:OR, "|"]
48
+ when @ss.skip(/\./)
49
+ [:DOT, "."]
50
+ when text = @ss.scan(/:\w+/)
51
+ [:SYMBOL, text]
40
52
  when text = @ss.scan(/\*\w+/)
41
53
  [:STAR, text]
42
- when text = @ss.scan(/(?<!\\)\(/)
43
- [:LPAREN, text]
44
- when text = @ss.scan(/(?<!\\)\)/)
45
- [:RPAREN, text]
46
- when text = @ss.scan(/\|/)
47
- [:OR, text]
48
- when text = @ss.scan(/\./)
49
- [:DOT, text]
50
- when text = @ss.scan(/(?<!\\):\w+/)
51
- [:SYMBOL, text]
52
- when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\:|\\\(|\\\))+/)
53
- [:LITERAL, text.tr('\\', '')]
54
+ when text = @ss.scan(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
55
+ text.tr! "\\", ""
56
+ [:LITERAL, text]
54
57
  # any char
55
58
  when text = @ss.scan(/./)
56
59
  [:LITERAL, text]
@@ -1,12 +1,13 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActionDispatch
4
- module Journey # :nodoc:
4
+ # :stopdoc:
5
+ module Journey
5
6
  class Format
6
7
  ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
7
8
  ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
8
9
 
9
- class Parameter < Struct.new(:name, :escaper)
10
+ Parameter = Struct.new(:name, :escaper) do
10
11
  def escape(value); escaper.call value; end
11
12
  end
12
13
 
@@ -23,7 +24,7 @@ module ActionDispatch
23
24
  @children = []
24
25
  @parameters = []
25
26
 
26
- parts.each_with_index do |object,i|
27
+ parts.each_with_index do |object, i|
27
28
  case object
28
29
  when Journey::Format
29
30
  @children << i
@@ -39,7 +40,7 @@ module ActionDispatch
39
40
  @parameters.each do |index|
40
41
  param = parts[index]
41
42
  value = hash[param.name]
42
- return ''.freeze unless value
43
+ return "".freeze unless value
43
44
  parts[index] = param.escape value
44
45
  end
45
46
 
@@ -59,7 +60,7 @@ module ActionDispatch
59
60
 
60
61
  private
61
62
 
62
- def visit node
63
+ def visit(node)
63
64
  send(DISPATCH_CACHE[node.type], node)
64
65
  end
65
66
 
@@ -92,6 +93,45 @@ module ActionDispatch
92
93
  end
93
94
  end
94
95
 
96
+ class FunctionalVisitor # :nodoc:
97
+ DISPATCH_CACHE = {}
98
+
99
+ def accept(node, seed)
100
+ visit(node, seed)
101
+ end
102
+
103
+ def visit(node, seed)
104
+ send(DISPATCH_CACHE[node.type], node, seed)
105
+ end
106
+
107
+ def binary(node, seed)
108
+ visit(node.right, visit(node.left, seed))
109
+ end
110
+ def visit_CAT(n, seed); binary(n, seed); end
111
+
112
+ def nary(node, seed)
113
+ node.children.inject(seed) { |s, c| visit(c, s) }
114
+ end
115
+ def visit_OR(n, seed); nary(n, seed); end
116
+
117
+ def unary(node, seed)
118
+ visit(node.left, seed)
119
+ end
120
+ def visit_GROUP(n, seed); unary(n, seed); end
121
+ def visit_STAR(n, seed); unary(n, seed); end
122
+
123
+ def terminal(node, seed); seed; end
124
+ def visit_LITERAL(n, seed); terminal(n, seed); end
125
+ def visit_SYMBOL(n, seed); terminal(n, seed); end
126
+ def visit_SLASH(n, seed); terminal(n, seed); end
127
+ def visit_DOT(n, seed); terminal(n, seed); end
128
+
129
+ instance_methods(false).each do |pim|
130
+ next unless pim =~ /^visit_(.*)$/
131
+ DISPATCH_CACHE[$1.to_sym] = pim
132
+ end
133
+ end
134
+
95
135
  class FormatBuilder < Visitor # :nodoc:
96
136
  def accept(node); Journey::Format.new(super); end
97
137
  def terminal(node); [node.left]; end
@@ -116,106 +156,113 @@ module ActionDispatch
116
156
  end
117
157
  end
118
158
 
119
- # Loop through the requirements AST
120
- class Each < Visitor # :nodoc:
121
- attr_reader :block
122
-
123
- def initialize(block)
124
- @block = block
125
- end
126
-
127
- def visit(node)
159
+ # Loop through the requirements AST.
160
+ class Each < FunctionalVisitor # :nodoc:
161
+ def visit(node, block)
128
162
  block.call(node)
129
163
  super
130
164
  end
165
+
166
+ INSTANCE = new
131
167
  end
132
168
 
133
- class String < Visitor # :nodoc:
169
+ class String < FunctionalVisitor # :nodoc:
134
170
  private
135
171
 
136
- def binary(node)
137
- [visit(node.left), visit(node.right)].join
138
- end
172
+ def binary(node, seed)
173
+ visit(node.right, visit(node.left, seed))
174
+ end
139
175
 
140
- def nary(node)
141
- node.children.map { |c| visit(c) }.join '|'
142
- end
176
+ def nary(node, seed)
177
+ last_child = node.children.last
178
+ node.children.inject(seed) { |s, c|
179
+ string = visit(c, s)
180
+ string << "|" unless last_child == c
181
+ string
182
+ }
183
+ end
143
184
 
144
- def terminal(node)
145
- node.left
146
- end
185
+ def terminal(node, seed)
186
+ seed + node.left
187
+ end
147
188
 
148
- def visit_GROUP(node)
149
- "(#{visit(node.left)})"
150
- end
189
+ def visit_GROUP(node, seed)
190
+ visit(node.left, seed.dup << "(") << ")"
191
+ end
192
+
193
+ INSTANCE = new
151
194
  end
152
195
 
153
- class Dot < Visitor # :nodoc:
196
+ class Dot < FunctionalVisitor # :nodoc:
154
197
  def initialize
155
198
  @nodes = []
156
199
  @edges = []
157
200
  end
158
201
 
159
- def accept(node)
202
+ def accept(node, seed = [[], []])
160
203
  super
204
+ nodes, edges = seed
161
205
  <<-eodot
162
206
  digraph parse_tree {
163
207
  size="8,5"
164
208
  node [shape = none];
165
209
  edge [dir = none];
166
- #{@nodes.join "\n"}
167
- #{@edges.join("\n")}
210
+ #{nodes.join "\n"}
211
+ #{edges.join("\n")}
168
212
  }
169
213
  eodot
170
214
  end
171
215
 
172
216
  private
173
217
 
174
- def binary(node)
175
- node.children.each do |c|
176
- @edges << "#{node.object_id} -> #{c.object_id};"
177
- end
218
+ def binary(node, seed)
219
+ seed.last.concat node.children.map { |c|
220
+ "#{node.object_id} -> #{c.object_id};"
221
+ }
178
222
  super
179
223
  end
180
224
 
181
- def nary(node)
182
- node.children.each do |c|
183
- @edges << "#{node.object_id} -> #{c.object_id};"
184
- end
225
+ def nary(node, seed)
226
+ seed.last.concat node.children.map { |c|
227
+ "#{node.object_id} -> #{c.object_id};"
228
+ }
185
229
  super
186
230
  end
187
231
 
188
- def unary(node)
189
- @edges << "#{node.object_id} -> #{node.left.object_id};"
232
+ def unary(node, seed)
233
+ seed.last << "#{node.object_id} -> #{node.left.object_id};"
190
234
  super
191
235
  end
192
236
 
193
- def visit_GROUP(node)
194
- @nodes << "#{node.object_id} [label=\"()\"];"
237
+ def visit_GROUP(node, seed)
238
+ seed.first << "#{node.object_id} [label=\"()\"];"
195
239
  super
196
240
  end
197
241
 
198
- def visit_CAT(node)
199
- @nodes << "#{node.object_id} [label=\"○\"];"
242
+ def visit_CAT(node, seed)
243
+ seed.first << "#{node.object_id} [label=\"○\"];"
200
244
  super
201
245
  end
202
246
 
203
- def visit_STAR(node)
204
- @nodes << "#{node.object_id} [label=\"*\"];"
247
+ def visit_STAR(node, seed)
248
+ seed.first << "#{node.object_id} [label=\"*\"];"
205
249
  super
206
250
  end
207
251
 
208
- def visit_OR(node)
209
- @nodes << "#{node.object_id} [label=\"|\"];"
252
+ def visit_OR(node, seed)
253
+ seed.first << "#{node.object_id} [label=\"|\"];"
210
254
  super
211
255
  end
212
256
 
213
- def terminal(node)
257
+ def terminal(node, seed)
214
258
  value = node.left
215
259
 
216
- @nodes << "#{node.object_id} [label=\"#{value}\"];"
260
+ seed.first << "#{node.object_id} [label=\"#{value}\"];"
261
+ seed
217
262
  end
263
+ INSTANCE = new
218
264
  end
219
265
  end
220
266
  end
267
+ # :startdoc:
221
268
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module ActionDispatch
3
4
  # Provides callbacks to be executed before and after dispatching the request.
@@ -7,8 +8,6 @@ module ActionDispatch
7
8
  define_callbacks :call
8
9
 
9
10
  class << self
10
- delegate :to_prepare, :to_cleanup, :to => "ActionDispatch::Reloader"
11
-
12
11
  def before(*args, &block)
13
12
  set_callback(:call, :before, *args, &block)
14
13
  end