actionpack 4.2.10 → 7.2.0.rc1

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 (202) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +86 -600
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +13 -14
  5. data/lib/abstract_controller/asset_paths.rb +5 -1
  6. data/lib/abstract_controller/base.rb +166 -136
  7. data/lib/abstract_controller/caching/fragments.rb +149 -0
  8. data/lib/abstract_controller/caching.rb +68 -0
  9. data/lib/abstract_controller/callbacks.rb +126 -57
  10. data/lib/abstract_controller/collector.rb +13 -15
  11. data/lib/abstract_controller/deprecator.rb +9 -0
  12. data/lib/abstract_controller/error.rb +8 -0
  13. data/lib/abstract_controller/helpers.rb +181 -132
  14. data/lib/abstract_controller/logger.rb +5 -1
  15. data/lib/abstract_controller/railties/routes_helpers.rb +10 -3
  16. data/lib/abstract_controller/rendering.rb +56 -56
  17. data/lib/abstract_controller/translation.rb +29 -15
  18. data/lib/abstract_controller/url_for.rb +15 -11
  19. data/lib/abstract_controller.rb +21 -5
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +154 -0
  22. data/lib/action_controller/base.rb +219 -155
  23. data/lib/action_controller/caching.rb +28 -68
  24. data/lib/action_controller/deprecator.rb +9 -0
  25. data/lib/action_controller/form_builder.rb +55 -0
  26. data/lib/action_controller/log_subscriber.rb +35 -22
  27. data/lib/action_controller/metal/allow_browser.rb +119 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +259 -122
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +9 -5
  32. data/lib/action_controller/metal/data_streaming.rb +87 -104
  33. data/lib/action_controller/metal/default_headers.rb +21 -0
  34. data/lib/action_controller/metal/etag_with_flash.rb +22 -0
  35. data/lib/action_controller/metal/etag_with_template_digest.rb +35 -26
  36. data/lib/action_controller/metal/exceptions.rb +71 -24
  37. data/lib/action_controller/metal/flash.rb +26 -19
  38. data/lib/action_controller/metal/head.rb +45 -36
  39. data/lib/action_controller/metal/helpers.rb +80 -64
  40. data/lib/action_controller/metal/http_authentication.rb +297 -244
  41. data/lib/action_controller/metal/implicit_render.rb +57 -9
  42. data/lib/action_controller/metal/instrumentation.rb +76 -64
  43. data/lib/action_controller/metal/live.rb +238 -176
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +177 -166
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +145 -118
  48. data/lib/action_controller/metal/permissions_policy.rb +38 -0
  49. data/lib/action_controller/metal/rate_limiting.rb +62 -0
  50. data/lib/action_controller/metal/redirecting.rb +203 -64
  51. data/lib/action_controller/metal/renderers.rb +108 -65
  52. data/lib/action_controller/metal/rendering.rb +216 -56
  53. data/lib/action_controller/metal/request_forgery_protection.rb +496 -163
  54. data/lib/action_controller/metal/rescue.rb +19 -21
  55. data/lib/action_controller/metal/streaming.rb +179 -138
  56. data/lib/action_controller/metal/strong_parameters.rb +1058 -382
  57. data/lib/action_controller/metal/testing.rb +11 -17
  58. data/lib/action_controller/metal/url_for.rb +37 -21
  59. data/lib/action_controller/metal.rb +236 -138
  60. data/lib/action_controller/railtie.rb +89 -11
  61. data/lib/action_controller/railties/helpers.rb +5 -1
  62. data/lib/action_controller/renderer.rb +161 -0
  63. data/lib/action_controller/template_assertions.rb +13 -0
  64. data/lib/action_controller/test_case.rb +425 -497
  65. data/lib/action_controller.rb +44 -22
  66. data/lib/action_dispatch/constants.rb +34 -0
  67. data/lib/action_dispatch/deprecator.rb +9 -0
  68. data/lib/action_dispatch/http/cache.rb +119 -63
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +364 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +36 -34
  72. data/lib/action_dispatch/http/filter_redirect.rb +24 -12
  73. data/lib/action_dispatch/http/headers.rb +66 -31
  74. data/lib/action_dispatch/http/mime_negotiation.rb +106 -75
  75. data/lib/action_dispatch/http/mime_type.rb +196 -136
  76. data/lib/action_dispatch/http/mime_types.rb +25 -7
  77. data/lib/action_dispatch/http/parameters.rb +97 -45
  78. data/lib/action_dispatch/http/permissions_policy.rb +187 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +6 -0
  80. data/lib/action_dispatch/http/request.rb +299 -170
  81. data/lib/action_dispatch/http/response.rb +311 -160
  82. data/lib/action_dispatch/http/upload.rb +52 -23
  83. data/lib/action_dispatch/http/url.rb +201 -125
  84. data/lib/action_dispatch/journey/formatter.rb +110 -50
  85. data/lib/action_dispatch/journey/gtg/builder.rb +37 -50
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +20 -17
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +96 -36
  88. data/lib/action_dispatch/journey/nfa/dot.rb +5 -14
  89. data/lib/action_dispatch/journey/nodes/node.rb +100 -20
  90. data/lib/action_dispatch/journey/parser.rb +19 -17
  91. data/lib/action_dispatch/journey/parser.y +4 -3
  92. data/lib/action_dispatch/journey/parser_extras.rb +14 -4
  93. data/lib/action_dispatch/journey/path/pattern.rb +79 -63
  94. data/lib/action_dispatch/journey/route.rb +108 -44
  95. data/lib/action_dispatch/journey/router/utils.rb +41 -29
  96. data/lib/action_dispatch/journey/router.rb +64 -57
  97. data/lib/action_dispatch/journey/routes.rb +23 -21
  98. data/lib/action_dispatch/journey/scanner.rb +28 -17
  99. data/lib/action_dispatch/journey/visitors.rb +100 -54
  100. data/lib/action_dispatch/journey/visualizer/fsm.js +49 -24
  101. data/lib/action_dispatch/journey/visualizer/index.html.erb +1 -1
  102. data/lib/action_dispatch/journey.rb +7 -5
  103. data/lib/action_dispatch/log_subscriber.rb +25 -0
  104. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  105. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  106. data/lib/action_dispatch/middleware/callbacks.rb +7 -6
  107. data/lib/action_dispatch/middleware/cookies.rb +471 -328
  108. data/lib/action_dispatch/middleware/debug_exceptions.rb +149 -66
  109. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  110. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  111. data/lib/action_dispatch/middleware/exception_wrapper.rb +275 -73
  112. data/lib/action_dispatch/middleware/executor.rb +32 -0
  113. data/lib/action_dispatch/middleware/flash.rb +143 -101
  114. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  115. data/lib/action_dispatch/middleware/public_exceptions.rb +36 -27
  116. data/lib/action_dispatch/middleware/reloader.rb +10 -92
  117. data/lib/action_dispatch/middleware/remote_ip.rb +133 -107
  118. data/lib/action_dispatch/middleware/request_id.rb +29 -15
  119. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  120. data/lib/action_dispatch/middleware/session/abstract_store.rb +49 -27
  121. data/lib/action_dispatch/middleware/session/cache_store.rb +33 -16
  122. data/lib/action_dispatch/middleware/session/cookie_store.rb +86 -80
  123. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +15 -3
  124. data/lib/action_dispatch/middleware/show_exceptions.rb +66 -36
  125. data/lib/action_dispatch/middleware/ssl.rb +134 -36
  126. data/lib/action_dispatch/middleware/stack.rb +109 -44
  127. data/lib/action_dispatch/middleware/static.rb +159 -90
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +7 -24
  132. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +1 -1
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +46 -36
  136. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.html.erb +12 -0
  137. data/lib/action_dispatch/middleware/templates/rescues/blocked_host.text.erb +9 -0
  138. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +26 -7
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +3 -3
  140. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.html.erb +24 -0
  141. data/lib/action_dispatch/middleware/templates/rescues/invalid_statement.text.erb +16 -0
  142. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +139 -15
  143. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.html.erb +23 -0
  144. data/lib/action_dispatch/middleware/templates/rescues/missing_exact_template.text.erb +3 -0
  145. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +6 -6
  146. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +7 -7
  147. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +9 -9
  148. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +1 -1
  149. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +4 -4
  150. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +1 -1
  151. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +7 -4
  152. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +125 -93
  153. data/lib/action_dispatch/railtie.rb +44 -16
  154. data/lib/action_dispatch/request/session.rb +159 -69
  155. data/lib/action_dispatch/request/utils.rb +97 -23
  156. data/lib/action_dispatch/routing/endpoint.rb +11 -2
  157. data/lib/action_dispatch/routing/inspector.rb +195 -106
  158. data/lib/action_dispatch/routing/mapper.rb +1338 -955
  159. data/lib/action_dispatch/routing/polymorphic_routes.rb +234 -201
  160. data/lib/action_dispatch/routing/redirection.rb +78 -51
  161. data/lib/action_dispatch/routing/route_set.rb +460 -374
  162. data/lib/action_dispatch/routing/routes_proxy.rb +36 -12
  163. data/lib/action_dispatch/routing/url_for.rb +172 -124
  164. data/lib/action_dispatch/routing.rb +159 -158
  165. data/lib/action_dispatch/system_test_case.rb +206 -0
  166. data/lib/action_dispatch/system_testing/browser.rb +84 -0
  167. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  168. data/lib/action_dispatch/system_testing/server.rb +33 -0
  169. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  170. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  171. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  172. data/lib/action_dispatch/testing/assertions/response.rb +71 -39
  173. data/lib/action_dispatch/testing/assertions/routing.rb +228 -103
  174. data/lib/action_dispatch/testing/assertions.rb +9 -6
  175. data/lib/action_dispatch/testing/integration.rb +486 -306
  176. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  177. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  178. data/lib/action_dispatch/testing/test_process.rb +35 -22
  179. data/lib/action_dispatch/testing/test_request.rb +29 -34
  180. data/lib/action_dispatch/testing/test_response.rb +48 -15
  181. data/lib/action_dispatch.rb +82 -40
  182. data/lib/action_pack/gem_version.rb +8 -4
  183. data/lib/action_pack/version.rb +6 -2
  184. data/lib/action_pack.rb +21 -18
  185. metadata +146 -56
  186. data/lib/action_controller/caching/fragments.rb +0 -103
  187. data/lib/action_controller/metal/force_ssl.rb +0 -97
  188. data/lib/action_controller/metal/hide_actions.rb +0 -40
  189. data/lib/action_controller/metal/rack_delegation.rb +0 -32
  190. data/lib/action_controller/middleware.rb +0 -39
  191. data/lib/action_controller/model_naming.rb +0 -12
  192. data/lib/action_dispatch/http/parameter_filter.rb +0 -72
  193. data/lib/action_dispatch/journey/backwards.rb +0 -5
  194. data/lib/action_dispatch/journey/nfa/builder.rb +0 -76
  195. data/lib/action_dispatch/journey/nfa/simulator.rb +0 -47
  196. data/lib/action_dispatch/journey/nfa/transition_table.rb +0 -163
  197. data/lib/action_dispatch/journey/router/strexp.rb +0 -27
  198. data/lib/action_dispatch/middleware/params_parser.rb +0 -60
  199. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +0 -27
  200. data/lib/action_dispatch/testing/assertions/dom.rb +0 -3
  201. data/lib/action_dispatch/testing/assertions/selector.rb +0 -3
  202. data/lib/action_dispatch/testing/assertions/tag.rb +0 -3
@@ -1,4 +1,8 @@
1
- require 'action_dispatch/journey/nfa/dot'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/journey/nfa/dot"
2
6
 
3
7
  module ActionDispatch
4
8
  module Journey # :nodoc:
@@ -8,11 +12,15 @@ module ActionDispatch
8
12
 
9
13
  attr_reader :memos
10
14
 
15
+ DEFAULT_EXP = /[^.\/?]+/
16
+ DEFAULT_EXP_ANCHORED = /\A#{DEFAULT_EXP}\Z/
17
+
11
18
  def initialize
12
- @regexp_states = {}
13
- @string_states = {}
14
- @accepting = {}
15
- @memos = Hash.new { |h,k| h[k] = [] }
19
+ @stdparam_states = {}
20
+ @regexp_states = {}
21
+ @string_states = {}
22
+ @accepting = {}
23
+ @memos = Hash.new { |h, k| h[k] = [] }
16
24
  end
17
25
 
18
26
  def add_accepting(state)
@@ -39,24 +47,58 @@ module ActionDispatch
39
47
  Array(t)
40
48
  end
41
49
 
42
- def move(t, a)
50
+ def move(t, full_string, start_index, end_index)
43
51
  return [] if t.empty?
44
52
 
45
- regexps = []
53
+ next_states = []
46
54
 
47
- t.map { |s|
48
- if states = @regexp_states[s]
49
- regexps.concat states.map { |re, v| re === a ? v : nil }
55
+ tok = full_string.slice(start_index, end_index - start_index)
56
+ token_matches_default_component = DEFAULT_EXP_ANCHORED.match?(tok)
57
+
58
+ t.each { |s, previous_start|
59
+ if previous_start.nil?
60
+ # In the simple case of a "default" param regex do this fast-path and add all
61
+ # next states.
62
+ if token_matches_default_component && states = @stdparam_states[s]
63
+ states.each { |re, v| next_states << [v, nil].freeze if !v.nil? }
64
+ end
65
+
66
+ # When we have a literal string, we can just pull the next state
67
+ if states = @string_states[s]
68
+ next_states << [states[tok], nil].freeze unless states[tok].nil?
69
+ end
50
70
  end
51
71
 
52
- if states = @string_states[s]
53
- states[a]
72
+ # For regexes that aren't the "default" style, they may potentially not be
73
+ # terminated by the first "token" [./?], so we need to continue to attempt to
74
+ # match this regexp as well as any successful paths that continue out of it.
75
+ # both paths could be valid.
76
+ if states = @regexp_states[s]
77
+ slice_start = if previous_start.nil?
78
+ start_index
79
+ else
80
+ previous_start
81
+ end
82
+
83
+ slice_length = end_index - slice_start
84
+ curr_slice = full_string.slice(slice_start, slice_length)
85
+
86
+ states.each { |re, v|
87
+ # if we match, we can try moving past this
88
+ next_states << [v, nil].freeze if !v.nil? && re.match?(curr_slice)
89
+ }
90
+
91
+ # and regardless, we must continue accepting tokens and retrying this regexp. we
92
+ # need to remember where we started as well so we can take bigger slices.
93
+ next_states << [s, slice_start].freeze
54
94
  end
55
- }.compact.concat regexps
95
+ }
96
+
97
+ next_states
56
98
  end
57
99
 
58
100
  def as_json(options = nil)
59
- simple_regexp = Hash.new { |h,k| h[k] = {} }
101
+ simple_regexp = Hash.new { |h, k| h[k] = {} }
60
102
 
61
103
  @regexp_states.each do |from, hash|
62
104
  hash.each do |re, to|
@@ -65,90 +107,108 @@ module ActionDispatch
65
107
  end
66
108
 
67
109
  {
68
- regexp_states: simple_regexp,
69
- string_states: @string_states,
70
- accepting: @accepting
110
+ regexp_states: simple_regexp,
111
+ string_states: @string_states,
112
+ stdparam_states: @stdparam_states,
113
+ accepting: @accepting
71
114
  }
72
115
  end
73
116
 
74
117
  def to_svg
75
- svg = IO.popen('dot -Tsvg', 'w+') { |f|
118
+ svg = IO.popen("dot -Tsvg", "w+") { |f|
76
119
  f.write(to_dot)
77
120
  f.close_write
78
121
  f.readlines
79
122
  }
80
123
  3.times { svg.shift }
81
- svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
124
+ svg.join.sub(/width="[^"]*"/, "").sub(/height="[^"]*"/, "")
82
125
  end
83
126
 
84
- def visualizer(paths, title = 'FSM')
85
- viz_dir = File.join File.dirname(__FILE__), '..', 'visualizer'
86
- fsm_js = File.read File.join(viz_dir, 'fsm.js')
87
- fsm_css = File.read File.join(viz_dir, 'fsm.css')
88
- erb = File.read File.join(viz_dir, 'index.html.erb')
127
+ def visualizer(paths, title = "FSM")
128
+ viz_dir = File.join __dir__, "..", "visualizer"
129
+ fsm_js = File.read File.join(viz_dir, "fsm.js")
130
+ fsm_css = File.read File.join(viz_dir, "fsm.css")
131
+ erb = File.read File.join(viz_dir, "index.html.erb")
89
132
  states = "function tt() { return #{to_json}; }"
90
133
 
91
134
  fun_routes = paths.sample(3).map do |ast|
92
- ast.map { |n|
135
+ ast.filter_map { |n|
93
136
  case n
94
137
  when Nodes::Symbol
95
138
  case n.left
96
- when ':id' then rand(100).to_s
97
- when ':format' then %w{ xml json }.sample
139
+ when ":id" then rand(100).to_s
140
+ when ":format" then %w{ xml json }.sample
98
141
  else
99
- 'omg'
142
+ "omg"
100
143
  end
101
144
  when Nodes::Terminal then n.symbol
102
145
  else
103
146
  nil
104
147
  end
105
- }.compact.join
148
+ }.join
106
149
  end
107
150
 
108
151
  stylesheets = [fsm_css]
109
152
  svg = to_svg
110
153
  javascripts = [states, fsm_js]
111
154
 
112
- # Annoying hack for 1.9 warnings
113
155
  fun_routes = fun_routes
114
156
  stylesheets = stylesheets
115
157
  svg = svg
116
158
  javascripts = javascripts
117
159
 
118
- require 'erb'
160
+ require "erb"
119
161
  template = ERB.new erb
120
162
  template.result(binding)
121
163
  end
122
164
 
123
165
  def []=(from, to, sym)
124
166
  to_mappings = states_hash_for(sym)[from] ||= {}
167
+ case sym
168
+ when Regexp
169
+ # we must match the whole string to a token boundary
170
+ if sym == DEFAULT_EXP
171
+ sym = DEFAULT_EXP_ANCHORED
172
+ else
173
+ sym = /\A#{sym}\Z/
174
+ end
175
+ when Symbol
176
+ # account for symbols in the constraints the same as strings
177
+ sym = sym.to_s
178
+ end
125
179
  to_mappings[sym] = to
126
180
  end
127
181
 
128
182
  def states
129
183
  ss = @string_states.keys + @string_states.values.flat_map(&:values)
184
+ ps = @stdparam_states.keys + @stdparam_states.values.flat_map(&:values)
130
185
  rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
131
- (ss + rs).uniq
186
+ (ss + ps + rs).uniq
132
187
  end
133
188
 
134
189
  def transitions
135
190
  @string_states.flat_map { |from, hash|
136
191
  hash.map { |s, to| [from, s, to] }
192
+ } + @stdparam_states.flat_map { |from, hash|
193
+ hash.map { |s, to| [from, s, to] }
137
194
  } + @regexp_states.flat_map { |from, hash|
138
195
  hash.map { |s, to| [from, s, to] }
139
196
  }
140
197
  end
141
198
 
142
199
  private
143
-
144
200
  def states_hash_for(sym)
145
201
  case sym
146
- when String
202
+ when String, Symbol
147
203
  @string_states
148
204
  when Regexp
149
- @regexp_states
205
+ if sym == DEFAULT_EXP
206
+ @stdparam_states
207
+ else
208
+ @regexp_states
209
+ end
150
210
  else
151
- raise ArgumentError, 'unknown symbol: %s' % sym.class
211
+ raise ArgumentError, "unknown symbol: %s" % sym.class
152
212
  end
153
213
  end
154
214
  end
@@ -1,4 +1,6 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
2
4
 
3
5
  module ActionDispatch
4
6
  module Journey # :nodoc:
@@ -9,18 +11,7 @@ module ActionDispatch
9
11
  " #{from} -> #{to} [label=\"#{sym || 'ε'}\"];"
10
12
  }
11
13
 
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
14
+ <<-eodot
24
15
  digraph nfa {
25
16
  rankdir=LR;
26
17
  node [shape = doublecircle];
@@ -28,7 +19,7 @@ digraph nfa {
28
19
  node [shape = circle];
29
20
  #{edges.join "\n"}
30
21
  }
31
- eodot
22
+ eodot
32
23
  end
33
24
  end
34
25
  end
@@ -1,7 +1,70 @@
1
- require 'action_dispatch/journey/visitors'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/journey/visitors"
2
6
 
3
7
  module ActionDispatch
4
8
  module Journey # :nodoc:
9
+ class Ast # :nodoc:
10
+ attr_reader :names, :path_params, :tree, :wildcard_options, :terminals
11
+ alias :root :tree
12
+
13
+ def initialize(tree, formatted)
14
+ @tree = tree
15
+ @path_params = []
16
+ @names = []
17
+ @symbols = []
18
+ @stars = []
19
+ @terminals = []
20
+ @wildcard_options = {}
21
+
22
+ visit_tree(formatted)
23
+ end
24
+
25
+ def requirements=(requirements)
26
+ # inject any regexp requirements for `star` nodes so they can be determined
27
+ # nullable, which requires knowing if the regex accepts an empty string.
28
+ (symbols + stars).each do |node|
29
+ re = requirements[node.to_sym]
30
+ node.regexp = re if re
31
+ end
32
+ end
33
+
34
+ def route=(route)
35
+ terminals.each { |n| n.memo = route }
36
+ end
37
+
38
+ def glob?
39
+ stars.any?
40
+ end
41
+
42
+ private
43
+ attr_reader :symbols, :stars
44
+
45
+ def visit_tree(formatted)
46
+ tree.each do |node|
47
+ if node.symbol?
48
+ path_params << node.to_sym
49
+ names << node.name
50
+ symbols << node
51
+ elsif node.star?
52
+ stars << node
53
+
54
+ if formatted != false
55
+ # Add a constraint for wildcard route to make it non-greedy and match the
56
+ # optional format part of the route by default.
57
+ wildcard_options[node.name.to_sym] ||= /.+?/m
58
+ end
59
+ end
60
+
61
+ if node.terminal?
62
+ terminals << node
63
+ end
64
+ end
65
+ end
66
+ end
67
+
5
68
  module Nodes # :nodoc:
6
69
  class Node # :nodoc:
7
70
  include Enumerable
@@ -14,15 +77,15 @@ module ActionDispatch
14
77
  end
15
78
 
16
79
  def each(&block)
17
- Visitors::Each.new(block).accept(self)
80
+ Visitors::Each::INSTANCE.accept(self, block)
18
81
  end
19
82
 
20
83
  def to_s
21
- Visitors::String.new.accept(self)
84
+ Visitors::String::INSTANCE.accept(self, "")
22
85
  end
23
86
 
24
87
  def to_dot
25
- Visitors::Dot.new.accept(self)
88
+ Visitors::Dot::INSTANCE.accept(self)
26
89
  end
27
90
 
28
91
  def to_sym
@@ -30,7 +93,7 @@ module ActionDispatch
30
93
  end
31
94
 
32
95
  def name
33
- left.tr '*:', ''
96
+ -left.tr("*:", "")
34
97
  end
35
98
 
36
99
  def type
@@ -39,10 +102,15 @@ module ActionDispatch
39
102
 
40
103
  def symbol?; false; end
41
104
  def literal?; false; end
105
+ def terminal?; false; end
106
+ def star?; false; end
107
+ def cat?; false; end
108
+ def group?; false; end
42
109
  end
43
110
 
44
111
  class Terminal < Node # :nodoc:
45
112
  alias :symbol :left
113
+ def terminal?; true; end
46
114
  end
47
115
 
48
116
  class Literal < Terminal # :nodoc:
@@ -58,28 +126,28 @@ module ActionDispatch
58
126
  def literal?; false; end
59
127
  end
60
128
 
61
- %w{ Symbol Slash Dot }.each do |t|
62
- class_eval <<-eoruby, __FILE__, __LINE__ + 1
63
- class #{t} < Terminal;
64
- def type; :#{t.upcase}; end
65
- end
66
- eoruby
129
+ class Slash < Terminal # :nodoc:
130
+ def type; :SLASH; end
131
+ end
132
+
133
+ class Dot < Terminal # :nodoc:
134
+ def type; :DOT; end
67
135
  end
68
136
 
69
137
  class Symbol < Terminal # :nodoc:
70
138
  attr_accessor :regexp
71
139
  alias :symbol :regexp
140
+ attr_reader :name
72
141
 
73
- DEFAULT_EXP = /[^\.\/\?]+/
74
- def initialize(left)
75
- super
76
- @regexp = DEFAULT_EXP
77
- end
78
-
79
- def default_regexp?
80
- regexp == DEFAULT_EXP
142
+ DEFAULT_EXP = /[^.\/?]+/
143
+ GREEDY_EXP = /(.+)/
144
+ def initialize(left, regexp = DEFAULT_EXP)
145
+ super(left)
146
+ @regexp = regexp
147
+ @name = -left.tr("*:", "")
81
148
  end
82
149
 
150
+ def type; :SYMBOL; end
83
151
  def symbol?; true; end
84
152
  end
85
153
 
@@ -89,13 +157,24 @@ module ActionDispatch
89
157
 
90
158
  class Group < Unary # :nodoc:
91
159
  def type; :GROUP; end
160
+ def group?; true; end
92
161
  end
93
162
 
94
163
  class Star < Unary # :nodoc:
164
+ attr_accessor :regexp
165
+
166
+ def initialize(left)
167
+ super(left)
168
+
169
+ # By default wildcard routes are non-greedy and must match something.
170
+ @regexp = /.+?/m
171
+ end
172
+
173
+ def star?; true; end
95
174
  def type; :STAR; end
96
175
 
97
176
  def name
98
- left.name.tr '*:', ''
177
+ left.name.tr "*:", ""
99
178
  end
100
179
  end
101
180
 
@@ -111,6 +190,7 @@ module ActionDispatch
111
190
  end
112
191
 
113
192
  class Cat < Binary # :nodoc:
193
+ def cat?; true; end
114
194
  def type; :CAT; end
115
195
  end
116
196
 
@@ -1,32 +1,34 @@
1
1
  #
2
2
  # DO NOT MODIFY!!!!
3
- # This file is automatically generated by Racc 1.4.11
4
- # from Racc grammer file "".
5
- #
3
+ # This file is automatically generated by Racc 1.4.16 from
4
+ # Racc grammar file "".
5
+
6
+ # :markup: markdown
6
7
 
7
8
  require 'racc/parser.rb'
8
9
 
10
+ # :stopdoc:
9
11
 
10
- require 'action_dispatch/journey/parser_extras'
12
+ require "action_dispatch/journey/parser_extras"
11
13
  module ActionDispatch
12
14
  module Journey
13
15
  class Parser < Racc::Parser
14
16
  ##### State transition tables begin ###
15
17
 
16
18
  racc_action_table = [
17
- 13, 15, 14, 7, 21, 16, 8, 19, 13, 15,
18
- 14, 7, 17, 16, 8, 13, 15, 14, 7, 24,
19
- 16, 8, 13, 15, 14, 7, 19, 16, 8 ]
19
+ 13, 15, 14, 7, 19, 16, 8, 19, 13, 15,
20
+ 14, 7, 17, 16, 8, 13, 15, 14, 7, 21,
21
+ 16, 8, 13, 15, 14, 7, 24, 16, 8 ]
20
22
 
21
23
  racc_action_check = [
22
- 2, 2, 2, 2, 17, 2, 2, 2, 0, 0,
23
- 0, 0, 1, 0, 0, 19, 19, 19, 19, 20,
24
- 19, 19, 7, 7, 7, 7, 22, 7, 7 ]
24
+ 2, 2, 2, 2, 22, 2, 2, 2, 19, 19,
25
+ 19, 19, 1, 19, 19, 7, 7, 7, 7, 17,
26
+ 7, 7, 0, 0, 0, 0, 20, 0, 0 ]
25
27
 
26
28
  racc_action_pointer = [
27
- 6, 12, -2, nil, nil, nil, nil, 20, nil, nil,
28
- nil, nil, nil, nil, nil, nil, nil, 4, nil, 13,
29
- 13, nil, 17, nil, nil ]
29
+ 20, 12, -2, nil, nil, nil, nil, 13, nil, nil,
30
+ nil, nil, nil, nil, nil, nil, nil, 19, nil, 6,
31
+ 20, nil, -5, nil, nil ]
30
32
 
31
33
  racc_action_default = [
32
34
  -19, -19, -2, -3, -4, -5, -6, -19, -10, -11,
@@ -162,7 +164,7 @@ def _reduce_9(val, _values)
162
164
  end
163
165
 
164
166
  def _reduce_10(val, _values)
165
- Star.new(Symbol.new(val.last))
167
+ Star.new(Symbol.new(val.last, Symbol::GREEDY_EXP))
166
168
  end
167
169
 
168
170
  # reduce 11 omitted
@@ -174,7 +176,7 @@ end
174
176
  # reduce 14 omitted
175
177
 
176
178
  def _reduce_15(val, _values)
177
- Slash.new('/')
179
+ Slash.new(val.first)
178
180
  end
179
181
 
180
182
  def _reduce_16(val, _values)
@@ -194,5 +196,5 @@ def _reduce_none(val, _values)
194
196
  end
195
197
 
196
198
  end # class Parser
197
- end # module Journey
198
- end # module ActionDispatch
199
+ end # module Journey
200
+ end # module ActionDispatch
@@ -21,7 +21,7 @@ rule
21
21
  | expression OR or { Or.new([val.first, val.last]) }
22
22
  ;
23
23
  star
24
- : STAR { Star.new(Symbol.new(val.last)) }
24
+ : STAR { Star.new(Symbol.new(val.last, Symbol::GREEDY_EXP)) }
25
25
  ;
26
26
  terminal
27
27
  : symbol
@@ -30,7 +30,7 @@ rule
30
30
  | dot
31
31
  ;
32
32
  slash
33
- : SLASH { Slash.new('/') }
33
+ : SLASH { Slash.new(val.first) }
34
34
  ;
35
35
  symbol
36
36
  : SYMBOL { Symbol.new(val.first) }
@@ -45,5 +45,6 @@ rule
45
45
  end
46
46
 
47
47
  ---- header
48
+ # :stopdoc:
48
49
 
49
- require 'action_dispatch/journey/parser_extras'
50
+ require "action_dispatch/journey/parser_extras"
@@ -1,11 +1,20 @@
1
- require 'action_dispatch/journey/scanner'
2
- require 'action_dispatch/journey/nodes/node'
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/journey/scanner"
6
+ require "action_dispatch/journey/nodes/node"
3
7
 
4
8
  module ActionDispatch
5
- module Journey # :nodoc:
6
- class Parser < Racc::Parser # :nodoc:
9
+ # :stopdoc:
10
+ module Journey
11
+ class Parser < Racc::Parser
7
12
  include Journey::Nodes
8
13
 
14
+ def self.parse(string)
15
+ new.parse string
16
+ end
17
+
9
18
  def initialize
10
19
  @scanner = Scanner.new
11
20
  end
@@ -20,4 +29,5 @@ module ActionDispatch
20
29
  end
21
30
  end
22
31
  end
32
+ # :startdoc:
23
33
  end