actionpack 5.2.3

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 (170) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +429 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller.rb +27 -0
  6. data/lib/abstract_controller/asset_paths.rb +12 -0
  7. data/lib/abstract_controller/base.rb +265 -0
  8. data/lib/abstract_controller/caching.rb +66 -0
  9. data/lib/abstract_controller/caching/fragments.rb +166 -0
  10. data/lib/abstract_controller/callbacks.rb +212 -0
  11. data/lib/abstract_controller/collector.rb +43 -0
  12. data/lib/abstract_controller/error.rb +6 -0
  13. data/lib/abstract_controller/helpers.rb +194 -0
  14. data/lib/abstract_controller/logger.rb +14 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +20 -0
  16. data/lib/abstract_controller/rendering.rb +127 -0
  17. data/lib/abstract_controller/translation.rb +31 -0
  18. data/lib/abstract_controller/url_for.rb +35 -0
  19. data/lib/action_controller.rb +66 -0
  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 +276 -0
  23. data/lib/action_controller/caching.rb +46 -0
  24. data/lib/action_controller/form_builder.rb +50 -0
  25. data/lib/action_controller/log_subscriber.rb +78 -0
  26. data/lib/action_controller/metal.rb +256 -0
  27. data/lib/action_controller/metal/basic_implicit_render.rb +13 -0
  28. data/lib/action_controller/metal/conditional_get.rb +274 -0
  29. data/lib/action_controller/metal/content_security_policy.rb +52 -0
  30. data/lib/action_controller/metal/cookies.rb +16 -0
  31. data/lib/action_controller/metal/data_streaming.rb +152 -0
  32. data/lib/action_controller/metal/etag_with_flash.rb +18 -0
  33. data/lib/action_controller/metal/etag_with_template_digest.rb +57 -0
  34. data/lib/action_controller/metal/exceptions.rb +53 -0
  35. data/lib/action_controller/metal/flash.rb +61 -0
  36. data/lib/action_controller/metal/force_ssl.rb +99 -0
  37. data/lib/action_controller/metal/head.rb +60 -0
  38. data/lib/action_controller/metal/helpers.rb +123 -0
  39. data/lib/action_controller/metal/http_authentication.rb +519 -0
  40. data/lib/action_controller/metal/implicit_render.rb +73 -0
  41. data/lib/action_controller/metal/instrumentation.rb +107 -0
  42. data/lib/action_controller/metal/live.rb +312 -0
  43. data/lib/action_controller/metal/mime_responds.rb +313 -0
  44. data/lib/action_controller/metal/parameter_encoding.rb +51 -0
  45. data/lib/action_controller/metal/params_wrapper.rb +293 -0
  46. data/lib/action_controller/metal/redirecting.rb +133 -0
  47. data/lib/action_controller/metal/renderers.rb +181 -0
  48. data/lib/action_controller/metal/rendering.rb +122 -0
  49. data/lib/action_controller/metal/request_forgery_protection.rb +445 -0
  50. data/lib/action_controller/metal/rescue.rb +28 -0
  51. data/lib/action_controller/metal/streaming.rb +223 -0
  52. data/lib/action_controller/metal/strong_parameters.rb +1086 -0
  53. data/lib/action_controller/metal/testing.rb +16 -0
  54. data/lib/action_controller/metal/url_for.rb +58 -0
  55. data/lib/action_controller/railtie.rb +89 -0
  56. data/lib/action_controller/railties/helpers.rb +24 -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 +629 -0
  60. data/lib/action_dispatch.rb +112 -0
  61. data/lib/action_dispatch/http/cache.rb +222 -0
  62. data/lib/action_dispatch/http/content_security_policy.rb +272 -0
  63. data/lib/action_dispatch/http/filter_parameters.rb +84 -0
  64. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  65. data/lib/action_dispatch/http/headers.rb +132 -0
  66. data/lib/action_dispatch/http/mime_negotiation.rb +175 -0
  67. data/lib/action_dispatch/http/mime_type.rb +342 -0
  68. data/lib/action_dispatch/http/mime_types.rb +50 -0
  69. data/lib/action_dispatch/http/parameter_filter.rb +86 -0
  70. data/lib/action_dispatch/http/parameters.rb +126 -0
  71. data/lib/action_dispatch/http/rack_cache.rb +63 -0
  72. data/lib/action_dispatch/http/request.rb +430 -0
  73. data/lib/action_dispatch/http/response.rb +519 -0
  74. data/lib/action_dispatch/http/upload.rb +84 -0
  75. data/lib/action_dispatch/http/url.rb +350 -0
  76. data/lib/action_dispatch/journey.rb +7 -0
  77. data/lib/action_dispatch/journey/formatter.rb +189 -0
  78. data/lib/action_dispatch/journey/gtg/builder.rb +164 -0
  79. data/lib/action_dispatch/journey/gtg/simulator.rb +41 -0
  80. data/lib/action_dispatch/journey/gtg/transition_table.rb +158 -0
  81. data/lib/action_dispatch/journey/nfa/builder.rb +78 -0
  82. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  83. data/lib/action_dispatch/journey/nfa/simulator.rb +49 -0
  84. data/lib/action_dispatch/journey/nfa/transition_table.rb +120 -0
  85. data/lib/action_dispatch/journey/nodes/node.rb +140 -0
  86. data/lib/action_dispatch/journey/parser.rb +199 -0
  87. data/lib/action_dispatch/journey/parser.y +50 -0
  88. data/lib/action_dispatch/journey/parser_extras.rb +31 -0
  89. data/lib/action_dispatch/journey/path/pattern.rb +198 -0
  90. data/lib/action_dispatch/journey/route.rb +203 -0
  91. data/lib/action_dispatch/journey/router.rb +156 -0
  92. data/lib/action_dispatch/journey/router/utils.rb +102 -0
  93. data/lib/action_dispatch/journey/routes.rb +82 -0
  94. data/lib/action_dispatch/journey/scanner.rb +64 -0
  95. data/lib/action_dispatch/journey/visitors.rb +268 -0
  96. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  97. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  98. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  99. data/lib/action_dispatch/middleware/callbacks.rb +36 -0
  100. data/lib/action_dispatch/middleware/cookies.rb +685 -0
  101. data/lib/action_dispatch/middleware/debug_exceptions.rb +205 -0
  102. data/lib/action_dispatch/middleware/debug_locks.rb +124 -0
  103. data/lib/action_dispatch/middleware/exception_wrapper.rb +147 -0
  104. data/lib/action_dispatch/middleware/executor.rb +21 -0
  105. data/lib/action_dispatch/middleware/flash.rb +300 -0
  106. data/lib/action_dispatch/middleware/public_exceptions.rb +57 -0
  107. data/lib/action_dispatch/middleware/reloader.rb +12 -0
  108. data/lib/action_dispatch/middleware/remote_ip.rb +183 -0
  109. data/lib/action_dispatch/middleware/request_id.rb +43 -0
  110. data/lib/action_dispatch/middleware/session/abstract_store.rb +92 -0
  111. data/lib/action_dispatch/middleware/session/cache_store.rb +54 -0
  112. data/lib/action_dispatch/middleware/session/cookie_store.rb +118 -0
  113. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +28 -0
  114. data/lib/action_dispatch/middleware/show_exceptions.rb +62 -0
  115. data/lib/action_dispatch/middleware/ssl.rb +150 -0
  116. data/lib/action_dispatch/middleware/stack.rb +116 -0
  117. data/lib/action_dispatch/middleware/static.rb +130 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +22 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +27 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  123. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  124. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  125. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  126. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +21 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +161 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  136. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  137. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  138. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  139. data/lib/action_dispatch/railtie.rb +55 -0
  140. data/lib/action_dispatch/request/session.rb +234 -0
  141. data/lib/action_dispatch/request/utils.rb +78 -0
  142. data/lib/action_dispatch/routing.rb +260 -0
  143. data/lib/action_dispatch/routing/endpoint.rb +17 -0
  144. data/lib/action_dispatch/routing/inspector.rb +225 -0
  145. data/lib/action_dispatch/routing/mapper.rb +2267 -0
  146. data/lib/action_dispatch/routing/polymorphic_routes.rb +352 -0
  147. data/lib/action_dispatch/routing/redirection.rb +201 -0
  148. data/lib/action_dispatch/routing/route_set.rb +890 -0
  149. data/lib/action_dispatch/routing/routes_proxy.rb +69 -0
  150. data/lib/action_dispatch/routing/url_for.rb +236 -0
  151. data/lib/action_dispatch/system_test_case.rb +147 -0
  152. data/lib/action_dispatch/system_testing/browser.rb +49 -0
  153. data/lib/action_dispatch/system_testing/driver.rb +59 -0
  154. data/lib/action_dispatch/system_testing/server.rb +31 -0
  155. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +96 -0
  156. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +31 -0
  157. data/lib/action_dispatch/system_testing/test_helpers/undef_methods.rb +26 -0
  158. data/lib/action_dispatch/testing/assertion_response.rb +47 -0
  159. data/lib/action_dispatch/testing/assertions.rb +24 -0
  160. data/lib/action_dispatch/testing/assertions/response.rb +107 -0
  161. data/lib/action_dispatch/testing/assertions/routing.rb +222 -0
  162. data/lib/action_dispatch/testing/integration.rb +652 -0
  163. data/lib/action_dispatch/testing/request_encoder.rb +55 -0
  164. data/lib/action_dispatch/testing/test_process.rb +50 -0
  165. data/lib/action_dispatch/testing/test_request.rb +71 -0
  166. data/lib/action_dispatch/testing/test_response.rb +53 -0
  167. data/lib/action_pack.rb +26 -0
  168. data/lib/action_pack/gem_version.rb +17 -0
  169. data/lib/action_pack/version.rb +10 -0
  170. metadata +318 -0
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/journey/gtg/transition_table"
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ module GTG # :nodoc:
8
+ class Builder # :nodoc:
9
+ DUMMY = Nodes::Dummy.new
10
+
11
+ attr_reader :root, :ast, :endpoints
12
+
13
+ def initialize(root)
14
+ @root = root
15
+ @ast = Nodes::Cat.new root, DUMMY
16
+ @followpos = nil
17
+ end
18
+
19
+ def transition_table
20
+ dtrans = TransitionTable.new
21
+ marked = {}
22
+ state_id = Hash.new { |h, k| h[k] = h.length }
23
+
24
+ start = firstpos(root)
25
+ dstates = [start]
26
+ until dstates.empty?
27
+ s = dstates.shift
28
+ next if marked[s]
29
+ marked[s] = true # mark s
30
+
31
+ s.group_by { |state| symbol(state) }.each do |sym, ps|
32
+ u = ps.flat_map { |l| followpos(l) }
33
+ next if u.empty?
34
+
35
+ if u.uniq == [DUMMY]
36
+ from = state_id[s]
37
+ to = state_id[Object.new]
38
+ dtrans[from, to] = sym
39
+
40
+ dtrans.add_accepting(to)
41
+ ps.each { |state| dtrans.add_memo(to, state.memo) }
42
+ else
43
+ dtrans[state_id[s], state_id[u]] = sym
44
+
45
+ if u.include?(DUMMY)
46
+ to = state_id[u]
47
+
48
+ accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
49
+
50
+ accepting.each { |accepting_state|
51
+ dtrans.add_memo(to, accepting_state.memo)
52
+ }
53
+
54
+ dtrans.add_accepting(state_id[u])
55
+ end
56
+ end
57
+
58
+ dstates << u
59
+ end
60
+ end
61
+
62
+ dtrans
63
+ end
64
+
65
+ def nullable?(node)
66
+ case node
67
+ when Nodes::Group
68
+ true
69
+ when Nodes::Star
70
+ true
71
+ when Nodes::Or
72
+ node.children.any? { |c| nullable?(c) }
73
+ when Nodes::Cat
74
+ nullable?(node.left) && nullable?(node.right)
75
+ when Nodes::Terminal
76
+ !node.left
77
+ when Nodes::Unary
78
+ nullable?(node.left)
79
+ else
80
+ raise ArgumentError, "unknown nullable: %s" % node.class.name
81
+ end
82
+ end
83
+
84
+ def firstpos(node)
85
+ case node
86
+ when Nodes::Star
87
+ firstpos(node.left)
88
+ when Nodes::Cat
89
+ if nullable?(node.left)
90
+ firstpos(node.left) | firstpos(node.right)
91
+ else
92
+ firstpos(node.left)
93
+ end
94
+ when Nodes::Or
95
+ node.children.flat_map { |c| firstpos(c) }.uniq
96
+ when Nodes::Unary
97
+ firstpos(node.left)
98
+ when Nodes::Terminal
99
+ nullable?(node) ? [] : [node]
100
+ else
101
+ raise ArgumentError, "unknown firstpos: %s" % node.class.name
102
+ end
103
+ end
104
+
105
+ def lastpos(node)
106
+ case node
107
+ when Nodes::Star
108
+ firstpos(node.left)
109
+ when Nodes::Or
110
+ node.children.flat_map { |c| lastpos(c) }.uniq
111
+ when Nodes::Cat
112
+ if nullable?(node.right)
113
+ lastpos(node.left) | lastpos(node.right)
114
+ else
115
+ lastpos(node.right)
116
+ end
117
+ when Nodes::Terminal
118
+ nullable?(node) ? [] : [node]
119
+ when Nodes::Unary
120
+ lastpos(node.left)
121
+ else
122
+ raise ArgumentError, "unknown lastpos: %s" % node.class.name
123
+ end
124
+ end
125
+
126
+ def followpos(node)
127
+ followpos_table[node]
128
+ end
129
+
130
+ private
131
+
132
+ def followpos_table
133
+ @followpos ||= build_followpos
134
+ end
135
+
136
+ def build_followpos
137
+ table = Hash.new { |h, k| h[k] = [] }
138
+ @ast.each do |n|
139
+ case n
140
+ when Nodes::Cat
141
+ lastpos(n.left).each do |i|
142
+ table[i] += firstpos(n.right)
143
+ end
144
+ when Nodes::Star
145
+ lastpos(n).each do |i|
146
+ table[i] += firstpos(n)
147
+ end
148
+ end
149
+ end
150
+ table
151
+ end
152
+
153
+ def symbol(edge)
154
+ case edge
155
+ when Journey::Nodes::Symbol
156
+ edge.regexp
157
+ else
158
+ edge.left
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "strscan"
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ module GTG # :nodoc:
8
+ class MatchData # :nodoc:
9
+ attr_reader :memos
10
+
11
+ def initialize(memos)
12
+ @memos = memos
13
+ end
14
+ end
15
+
16
+ class Simulator # :nodoc:
17
+ attr_reader :tt
18
+
19
+ def initialize(transition_table)
20
+ @tt = transition_table
21
+ end
22
+
23
+ def memos(string)
24
+ input = StringScanner.new(string)
25
+ state = [0]
26
+ while sym = input.scan(%r([/.?]|[^/.?]+))
27
+ state = tt.move(state, sym)
28
+ end
29
+
30
+ acceptance_states = state.find_all { |s|
31
+ tt.accepting? s
32
+ }
33
+
34
+ return yield if acceptance_states.empty?
35
+
36
+ acceptance_states.flat_map { |x| tt.memo(x) }.compact
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,158 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/journey/nfa/dot"
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ module GTG # :nodoc:
8
+ class TransitionTable # :nodoc:
9
+ include Journey::NFA::Dot
10
+
11
+ attr_reader :memos
12
+
13
+ def initialize
14
+ @regexp_states = {}
15
+ @string_states = {}
16
+ @accepting = {}
17
+ @memos = Hash.new { |h, k| h[k] = [] }
18
+ end
19
+
20
+ def add_accepting(state)
21
+ @accepting[state] = true
22
+ end
23
+
24
+ def accepting_states
25
+ @accepting.keys
26
+ end
27
+
28
+ def accepting?(state)
29
+ @accepting[state]
30
+ end
31
+
32
+ def add_memo(idx, memo)
33
+ @memos[idx] << memo
34
+ end
35
+
36
+ def memo(idx)
37
+ @memos[idx]
38
+ end
39
+
40
+ def eclosure(t)
41
+ Array(t)
42
+ end
43
+
44
+ def move(t, a)
45
+ return [] if t.empty?
46
+
47
+ regexps = []
48
+
49
+ t.map { |s|
50
+ if states = @regexp_states[s]
51
+ regexps.concat states.map { |re, v| re === a ? v : nil }
52
+ end
53
+
54
+ if states = @string_states[s]
55
+ states[a]
56
+ end
57
+ }.compact.concat regexps
58
+ end
59
+
60
+ def as_json(options = nil)
61
+ simple_regexp = Hash.new { |h, k| h[k] = {} }
62
+
63
+ @regexp_states.each do |from, hash|
64
+ hash.each do |re, to|
65
+ simple_regexp[from][re.source] = to
66
+ end
67
+ end
68
+
69
+ {
70
+ regexp_states: simple_regexp,
71
+ string_states: @string_states,
72
+ accepting: @accepting
73
+ }
74
+ end
75
+
76
+ def to_svg
77
+ svg = IO.popen("dot -Tsvg", "w+") { |f|
78
+ f.write(to_dot)
79
+ f.close_write
80
+ f.readlines
81
+ }
82
+ 3.times { svg.shift }
83
+ svg.join.sub(/width="[^"]*"/, "").sub(/height="[^"]*"/, "")
84
+ end
85
+
86
+ def visualizer(paths, title = "FSM")
87
+ viz_dir = File.join __dir__, "..", "visualizer"
88
+ fsm_js = File.read File.join(viz_dir, "fsm.js")
89
+ fsm_css = File.read File.join(viz_dir, "fsm.css")
90
+ erb = File.read File.join(viz_dir, "index.html.erb")
91
+ states = "function tt() { return #{to_json}; }"
92
+
93
+ fun_routes = paths.sample(3).map do |ast|
94
+ ast.map { |n|
95
+ case n
96
+ when Nodes::Symbol
97
+ case n.left
98
+ when ":id" then rand(100).to_s
99
+ when ":format" then %w{ xml json }.sample
100
+ else
101
+ "omg"
102
+ end
103
+ when Nodes::Terminal then n.symbol
104
+ else
105
+ nil
106
+ end
107
+ }.compact.join
108
+ end
109
+
110
+ stylesheets = [fsm_css]
111
+ svg = to_svg
112
+ javascripts = [states, fsm_js]
113
+
114
+ fun_routes = fun_routes
115
+ stylesheets = stylesheets
116
+ svg = svg
117
+ javascripts = javascripts
118
+
119
+ require "erb"
120
+ template = ERB.new erb
121
+ template.result(binding)
122
+ end
123
+
124
+ def []=(from, to, sym)
125
+ to_mappings = states_hash_for(sym)[from] ||= {}
126
+ to_mappings[sym] = to
127
+ end
128
+
129
+ def states
130
+ ss = @string_states.keys + @string_states.values.flat_map(&:values)
131
+ rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
132
+ (ss + rs).uniq
133
+ end
134
+
135
+ def transitions
136
+ @string_states.flat_map { |from, hash|
137
+ hash.map { |s, to| [from, s, to] }
138
+ } + @regexp_states.flat_map { |from, hash|
139
+ hash.map { |s, to| [from, s, to] }
140
+ }
141
+ end
142
+
143
+ private
144
+
145
+ def states_hash_for(sym)
146
+ case sym
147
+ when String
148
+ @string_states
149
+ when Regexp
150
+ @regexp_states
151
+ else
152
+ raise ArgumentError, "unknown symbol: %s" % sym.class
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_dispatch/journey/nfa/transition_table"
4
+ require "action_dispatch/journey/gtg/transition_table"
5
+
6
+ module ActionDispatch
7
+ module Journey # :nodoc:
8
+ module NFA # :nodoc:
9
+ class Visitor < Visitors::Visitor # :nodoc:
10
+ def initialize(tt)
11
+ @tt = tt
12
+ @i = -1
13
+ end
14
+
15
+ def visit_CAT(node)
16
+ left = visit(node.left)
17
+ right = visit(node.right)
18
+
19
+ @tt.merge(left.last, right.first)
20
+
21
+ [left.first, right.last]
22
+ end
23
+
24
+ def visit_GROUP(node)
25
+ from = @i += 1
26
+ left = visit(node.left)
27
+ to = @i += 1
28
+
29
+ @tt.accepting = to
30
+
31
+ @tt[from, left.first] = nil
32
+ @tt[left.last, to] = nil
33
+ @tt[from, to] = nil
34
+
35
+ [from, to]
36
+ end
37
+
38
+ def visit_OR(node)
39
+ from = @i += 1
40
+ children = node.children.map { |c| visit(c) }
41
+ to = @i += 1
42
+
43
+ children.each do |child|
44
+ @tt[from, child.first] = nil
45
+ @tt[child.last, to] = nil
46
+ end
47
+
48
+ @tt.accepting = to
49
+
50
+ [from, to]
51
+ end
52
+
53
+ def terminal(node)
54
+ from_i = @i += 1 # new state
55
+ to_i = @i += 1 # new state
56
+
57
+ @tt[from_i, to_i] = node
58
+ @tt.accepting = to_i
59
+ @tt.add_memo(to_i, node.memo)
60
+
61
+ [from_i, to_i]
62
+ end
63
+ end
64
+
65
+ class Builder # :nodoc:
66
+ def initialize(ast)
67
+ @ast = ast
68
+ end
69
+
70
+ def transition_table
71
+ tt = TransitionTable.new
72
+ Visitor.new(tt).accept(@ast)
73
+ tt
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module NFA # :nodoc:
6
+ module Dot # :nodoc:
7
+ def to_dot
8
+ edges = transitions.map { |from, sym, to|
9
+ " #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
10
+ }
11
+
12
+ # memo_nodes = memos.values.flatten.map { |n|
13
+ # label = n
14
+ # if Journey::Route === n
15
+ # label = "#{n.verb.source} #{n.path.spec}"
16
+ # end
17
+ # " #{n.object_id} [label=\"#{label}\", shape=box];"
18
+ # }
19
+ # memo_edges = memos.flat_map { |k, memos|
20
+ # (memos || []).map { |v| " #{k} -> #{v.object_id};" }
21
+ # }.uniq
22
+
23
+ <<-eodot
24
+ digraph nfa {
25
+ rankdir=LR;
26
+ node [shape = doublecircle];
27
+ #{accepting_states.join ' '};
28
+ node [shape = circle];
29
+ #{edges.join "\n"}
30
+ }
31
+ eodot
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end