actionpack 3.2.19 → 4.2.11.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 (244) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +412 -503
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +11 -294
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +52 -18
  7. data/lib/abstract_controller/callbacks.rb +87 -89
  8. data/lib/abstract_controller/collector.rb +17 -3
  9. data/lib/abstract_controller/helpers.rb +41 -14
  10. data/lib/abstract_controller/logger.rb +1 -2
  11. data/lib/abstract_controller/railties/routes_helpers.rb +3 -3
  12. data/lib/abstract_controller/rendering.rb +65 -118
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +7 -7
  15. data/lib/abstract_controller.rb +2 -10
  16. data/lib/action_controller/base.rb +61 -28
  17. data/lib/action_controller/caching/fragments.rb +30 -54
  18. data/lib/action_controller/caching.rb +38 -35
  19. data/lib/action_controller/log_subscriber.rb +35 -18
  20. data/lib/action_controller/metal/conditional_get.rb +103 -34
  21. data/lib/action_controller/metal/data_streaming.rb +20 -26
  22. data/lib/action_controller/metal/etag_with_template_digest.rb +50 -0
  23. data/lib/action_controller/metal/exceptions.rb +19 -6
  24. data/lib/action_controller/metal/flash.rb +41 -9
  25. data/lib/action_controller/metal/force_ssl.rb +70 -12
  26. data/lib/action_controller/metal/head.rb +30 -7
  27. data/lib/action_controller/metal/helpers.rb +11 -11
  28. data/lib/action_controller/metal/hide_actions.rb +0 -1
  29. data/lib/action_controller/metal/http_authentication.rb +140 -94
  30. data/lib/action_controller/metal/implicit_render.rb +1 -1
  31. data/lib/action_controller/metal/instrumentation.rb +11 -7
  32. data/lib/action_controller/metal/live.rb +328 -0
  33. data/lib/action_controller/metal/mime_responds.rb +161 -152
  34. data/lib/action_controller/metal/params_wrapper.rb +126 -81
  35. data/lib/action_controller/metal/rack_delegation.rb +10 -4
  36. data/lib/action_controller/metal/redirecting.rb +44 -41
  37. data/lib/action_controller/metal/renderers.rb +48 -19
  38. data/lib/action_controller/metal/rendering.rb +46 -11
  39. data/lib/action_controller/metal/request_forgery_protection.rb +250 -29
  40. data/lib/action_controller/metal/streaming.rb +30 -38
  41. data/lib/action_controller/metal/strong_parameters.rb +669 -0
  42. data/lib/action_controller/metal/testing.rb +12 -18
  43. data/lib/action_controller/metal/url_for.rb +31 -29
  44. data/lib/action_controller/metal.rb +31 -40
  45. data/lib/action_controller/model_naming.rb +12 -0
  46. data/lib/action_controller/railtie.rb +38 -18
  47. data/lib/action_controller/railties/helpers.rb +22 -0
  48. data/lib/action_controller/test_case.rb +359 -173
  49. data/lib/action_controller.rb +9 -16
  50. data/lib/action_dispatch/http/cache.rb +64 -11
  51. data/lib/action_dispatch/http/filter_parameters.rb +20 -10
  52. data/lib/action_dispatch/http/filter_redirect.rb +38 -0
  53. data/lib/action_dispatch/http/headers.rb +85 -17
  54. data/lib/action_dispatch/http/mime_negotiation.rb +55 -5
  55. data/lib/action_dispatch/http/mime_type.rb +167 -114
  56. data/lib/action_dispatch/http/mime_types.rb +2 -1
  57. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  58. data/lib/action_dispatch/http/parameters.rb +30 -46
  59. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  60. data/lib/action_dispatch/http/request.rb +108 -45
  61. data/lib/action_dispatch/http/response.rb +247 -48
  62. data/lib/action_dispatch/http/upload.rb +60 -29
  63. data/lib/action_dispatch/http/url.rb +135 -45
  64. data/lib/action_dispatch/journey/backwards.rb +5 -0
  65. data/lib/action_dispatch/journey/formatter.rb +166 -0
  66. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  67. data/lib/action_dispatch/journey/gtg/simulator.rb +47 -0
  68. data/lib/action_dispatch/journey/gtg/transition_table.rb +157 -0
  69. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  70. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  71. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  72. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  73. data/lib/action_dispatch/journey/nodes/node.rb +128 -0
  74. data/lib/action_dispatch/journey/parser.rb +198 -0
  75. data/lib/action_dispatch/journey/parser.y +49 -0
  76. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  77. data/lib/action_dispatch/journey/path/pattern.rb +193 -0
  78. data/lib/action_dispatch/journey/route.rb +125 -0
  79. data/lib/action_dispatch/journey/router/strexp.rb +27 -0
  80. data/lib/action_dispatch/journey/router/utils.rb +93 -0
  81. data/lib/action_dispatch/journey/router.rb +144 -0
  82. data/lib/action_dispatch/journey/routes.rb +80 -0
  83. data/lib/action_dispatch/journey/scanner.rb +61 -0
  84. data/lib/action_dispatch/journey/visitors.rb +221 -0
  85. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  86. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  87. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  88. data/lib/action_dispatch/journey.rb +5 -0
  89. data/lib/action_dispatch/middleware/callbacks.rb +16 -11
  90. data/lib/action_dispatch/middleware/cookies.rb +346 -125
  91. data/lib/action_dispatch/middleware/debug_exceptions.rb +52 -24
  92. data/lib/action_dispatch/middleware/exception_wrapper.rb +75 -9
  93. data/lib/action_dispatch/middleware/flash.rb +85 -72
  94. data/lib/action_dispatch/middleware/params_parser.rb +16 -31
  95. data/lib/action_dispatch/middleware/public_exceptions.rb +39 -14
  96. data/lib/action_dispatch/middleware/reloader.rb +16 -7
  97. data/lib/action_dispatch/middleware/remote_ip.rb +132 -40
  98. data/lib/action_dispatch/middleware/request_id.rb +3 -7
  99. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  100. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  101. data/lib/action_dispatch/middleware/session/cookie_store.rb +84 -29
  102. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  103. data/lib/action_dispatch/middleware/show_exceptions.rb +15 -44
  104. data/lib/action_dispatch/middleware/ssl.rb +72 -0
  105. data/lib/action_dispatch/middleware/stack.rb +6 -1
  106. data/lib/action_dispatch/middleware/static.rb +80 -23
  107. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +34 -0
  108. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  109. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +27 -0
  110. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +52 -0
  111. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  112. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb +16 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  114. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +133 -5
  115. data/lib/action_dispatch/middleware/templates/rescues/missing_template.html.erb +11 -0
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  118. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  119. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  122. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  124. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +200 -0
  125. data/lib/action_dispatch/railtie.rb +19 -6
  126. data/lib/action_dispatch/request/session.rb +193 -0
  127. data/lib/action_dispatch/request/utils.rb +35 -0
  128. data/lib/action_dispatch/routing/endpoint.rb +10 -0
  129. data/lib/action_dispatch/routing/inspector.rb +234 -0
  130. data/lib/action_dispatch/routing/mapper.rb +897 -436
  131. data/lib/action_dispatch/routing/polymorphic_routes.rb +213 -92
  132. data/lib/action_dispatch/routing/redirection.rb +97 -37
  133. data/lib/action_dispatch/routing/route_set.rb +432 -239
  134. data/lib/action_dispatch/routing/routes_proxy.rb +7 -4
  135. data/lib/action_dispatch/routing/url_for.rb +63 -34
  136. data/lib/action_dispatch/routing.rb +57 -89
  137. data/lib/action_dispatch/testing/assertions/dom.rb +2 -36
  138. data/lib/action_dispatch/testing/assertions/response.rb +24 -38
  139. data/lib/action_dispatch/testing/assertions/routing.rb +55 -54
  140. data/lib/action_dispatch/testing/assertions/selector.rb +2 -434
  141. data/lib/action_dispatch/testing/assertions/tag.rb +2 -137
  142. data/lib/action_dispatch/testing/assertions.rb +11 -7
  143. data/lib/action_dispatch/testing/integration.rb +88 -72
  144. data/lib/action_dispatch/testing/test_process.rb +9 -6
  145. data/lib/action_dispatch/testing/test_request.rb +13 -9
  146. data/lib/action_dispatch/testing/test_response.rb +1 -5
  147. data/lib/action_dispatch.rb +24 -21
  148. data/lib/action_pack/gem_version.rb +15 -0
  149. data/lib/action_pack/version.rb +5 -7
  150. data/lib/action_pack.rb +1 -1
  151. metadata +181 -292
  152. data/lib/abstract_controller/layouts.rb +0 -423
  153. data/lib/abstract_controller/view_paths.rb +0 -96
  154. data/lib/action_controller/caching/actions.rb +0 -185
  155. data/lib/action_controller/caching/pages.rb +0 -187
  156. data/lib/action_controller/caching/sweeping.rb +0 -97
  157. data/lib/action_controller/deprecated/integration_test.rb +0 -2
  158. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  159. data/lib/action_controller/deprecated.rb +0 -3
  160. data/lib/action_controller/metal/compatibility.rb +0 -65
  161. data/lib/action_controller/metal/responder.rb +0 -286
  162. data/lib/action_controller/metal/session_management.rb +0 -14
  163. data/lib/action_controller/railties/paths.rb +0 -25
  164. data/lib/action_controller/record_identifier.rb +0 -85
  165. data/lib/action_controller/vendor/html-scanner/html/document.rb +0 -68
  166. data/lib/action_controller/vendor/html-scanner/html/node.rb +0 -532
  167. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +0 -177
  168. data/lib/action_controller/vendor/html-scanner/html/selector.rb +0 -830
  169. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +0 -107
  170. data/lib/action_controller/vendor/html-scanner/html/version.rb +0 -11
  171. data/lib/action_controller/vendor/html-scanner.rb +0 -20
  172. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  173. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  174. data/lib/action_dispatch/middleware/head.rb +0 -18
  175. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  176. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +0 -31
  177. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +0 -26
  178. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +0 -10
  179. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +0 -2
  180. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +0 -15
  181. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +0 -17
  182. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +0 -2
  183. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  184. data/lib/action_view/asset_paths.rb +0 -142
  185. data/lib/action_view/base.rb +0 -220
  186. data/lib/action_view/buffers.rb +0 -43
  187. data/lib/action_view/context.rb +0 -36
  188. data/lib/action_view/flows.rb +0 -79
  189. data/lib/action_view/helpers/active_model_helper.rb +0 -50
  190. data/lib/action_view/helpers/asset_paths.rb +0 -7
  191. data/lib/action_view/helpers/asset_tag_helper.rb +0 -457
  192. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  193. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  194. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  195. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  196. data/lib/action_view/helpers/atom_feed_helper.rb +0 -200
  197. data/lib/action_view/helpers/cache_helper.rb +0 -64
  198. data/lib/action_view/helpers/capture_helper.rb +0 -203
  199. data/lib/action_view/helpers/controller_helper.rb +0 -25
  200. data/lib/action_view/helpers/csrf_helper.rb +0 -32
  201. data/lib/action_view/helpers/date_helper.rb +0 -1062
  202. data/lib/action_view/helpers/debug_helper.rb +0 -40
  203. data/lib/action_view/helpers/form_helper.rb +0 -1486
  204. data/lib/action_view/helpers/form_options_helper.rb +0 -658
  205. data/lib/action_view/helpers/form_tag_helper.rb +0 -685
  206. data/lib/action_view/helpers/javascript_helper.rb +0 -110
  207. data/lib/action_view/helpers/number_helper.rb +0 -622
  208. data/lib/action_view/helpers/output_safety_helper.rb +0 -38
  209. data/lib/action_view/helpers/record_tag_helper.rb +0 -111
  210. data/lib/action_view/helpers/rendering_helper.rb +0 -90
  211. data/lib/action_view/helpers/sanitize_helper.rb +0 -259
  212. data/lib/action_view/helpers/tag_helper.rb +0 -160
  213. data/lib/action_view/helpers/text_helper.rb +0 -426
  214. data/lib/action_view/helpers/translation_helper.rb +0 -91
  215. data/lib/action_view/helpers/url_helper.rb +0 -693
  216. data/lib/action_view/helpers.rb +0 -60
  217. data/lib/action_view/locale/en.yml +0 -160
  218. data/lib/action_view/log_subscriber.rb +0 -28
  219. data/lib/action_view/lookup_context.rb +0 -254
  220. data/lib/action_view/path_set.rb +0 -89
  221. data/lib/action_view/railtie.rb +0 -55
  222. data/lib/action_view/renderer/abstract_renderer.rb +0 -41
  223. data/lib/action_view/renderer/partial_renderer.rb +0 -415
  224. data/lib/action_view/renderer/renderer.rb +0 -54
  225. data/lib/action_view/renderer/streaming_template_renderer.rb +0 -106
  226. data/lib/action_view/renderer/template_renderer.rb +0 -94
  227. data/lib/action_view/template/error.rb +0 -128
  228. data/lib/action_view/template/handlers/builder.rb +0 -26
  229. data/lib/action_view/template/handlers/erb.rb +0 -125
  230. data/lib/action_view/template/handlers.rb +0 -50
  231. data/lib/action_view/template/resolver.rb +0 -272
  232. data/lib/action_view/template/text.rb +0 -30
  233. data/lib/action_view/template.rb +0 -337
  234. data/lib/action_view/test_case.rb +0 -245
  235. data/lib/action_view/testing/resolvers.rb +0 -50
  236. data/lib/action_view.rb +0 -84
  237. data/lib/sprockets/assets.rake +0 -99
  238. data/lib/sprockets/bootstrap.rb +0 -37
  239. data/lib/sprockets/compressors.rb +0 -83
  240. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  241. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  242. data/lib/sprockets/helpers.rb +0 -6
  243. data/lib/sprockets/railtie.rb +0 -62
  244. data/lib/sprockets/static_compiler.rb +0 -56
@@ -0,0 +1,157 @@
1
+ require 'action_dispatch/journey/nfa/dot'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module GTG # :nodoc:
6
+ class TransitionTable # :nodoc:
7
+ include Journey::NFA::Dot
8
+
9
+ attr_reader :memos
10
+
11
+ def initialize
12
+ @regexp_states = {}
13
+ @string_states = {}
14
+ @accepting = {}
15
+ @memos = Hash.new { |h,k| h[k] = [] }
16
+ end
17
+
18
+ def add_accepting(state)
19
+ @accepting[state] = true
20
+ end
21
+
22
+ def accepting_states
23
+ @accepting.keys
24
+ end
25
+
26
+ def accepting?(state)
27
+ @accepting[state]
28
+ end
29
+
30
+ def add_memo(idx, memo)
31
+ @memos[idx] << memo
32
+ end
33
+
34
+ def memo(idx)
35
+ @memos[idx]
36
+ end
37
+
38
+ def eclosure(t)
39
+ Array(t)
40
+ end
41
+
42
+ def move(t, a)
43
+ return [] if t.empty?
44
+
45
+ regexps = []
46
+
47
+ t.map { |s|
48
+ if states = @regexp_states[s]
49
+ regexps.concat states.map { |re, v| re === a ? v : nil }
50
+ end
51
+
52
+ if states = @string_states[s]
53
+ states[a]
54
+ end
55
+ }.compact.concat regexps
56
+ end
57
+
58
+ def as_json(options = nil)
59
+ simple_regexp = Hash.new { |h,k| h[k] = {} }
60
+
61
+ @regexp_states.each do |from, hash|
62
+ hash.each do |re, to|
63
+ simple_regexp[from][re.source] = to
64
+ end
65
+ end
66
+
67
+ {
68
+ regexp_states: simple_regexp,
69
+ string_states: @string_states,
70
+ accepting: @accepting
71
+ }
72
+ end
73
+
74
+ def to_svg
75
+ svg = IO.popen('dot -Tsvg', 'w+') { |f|
76
+ f.write(to_dot)
77
+ f.close_write
78
+ f.readlines
79
+ }
80
+ 3.times { svg.shift }
81
+ svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
82
+ end
83
+
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')
89
+ states = "function tt() { return #{to_json}; }"
90
+
91
+ fun_routes = paths.sample(3).map do |ast|
92
+ ast.map { |n|
93
+ case n
94
+ when Nodes::Symbol
95
+ case n.left
96
+ when ':id' then rand(100).to_s
97
+ when ':format' then %w{ xml json }.sample
98
+ else
99
+ 'omg'
100
+ end
101
+ when Nodes::Terminal then n.symbol
102
+ else
103
+ nil
104
+ end
105
+ }.compact.join
106
+ end
107
+
108
+ stylesheets = [fsm_css]
109
+ svg = to_svg
110
+ javascripts = [states, fsm_js]
111
+
112
+ # Annoying hack for 1.9 warnings
113
+ fun_routes = fun_routes
114
+ stylesheets = stylesheets
115
+ svg = svg
116
+ javascripts = javascripts
117
+
118
+ require 'erb'
119
+ template = ERB.new erb
120
+ template.result(binding)
121
+ end
122
+
123
+ def []=(from, to, sym)
124
+ to_mappings = states_hash_for(sym)[from] ||= {}
125
+ to_mappings[sym] = to
126
+ end
127
+
128
+ def states
129
+ ss = @string_states.keys + @string_states.values.flat_map(&:values)
130
+ rs = @regexp_states.keys + @regexp_states.values.flat_map(&:values)
131
+ (ss + rs).uniq
132
+ end
133
+
134
+ def transitions
135
+ @string_states.flat_map { |from, hash|
136
+ hash.map { |s, to| [from, s, to] }
137
+ } + @regexp_states.flat_map { |from, hash|
138
+ hash.map { |s, to| [from, s, to] }
139
+ }
140
+ end
141
+
142
+ private
143
+
144
+ def states_hash_for(sym)
145
+ case sym
146
+ when String
147
+ @string_states
148
+ when Regexp
149
+ @regexp_states
150
+ else
151
+ raise ArgumentError, 'unknown symbol: %s' % sym.class
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,76 @@
1
+ require 'action_dispatch/journey/nfa/transition_table'
2
+ require 'action_dispatch/journey/gtg/transition_table'
3
+
4
+ module ActionDispatch
5
+ module Journey # :nodoc:
6
+ module NFA # :nodoc:
7
+ class Visitor < Visitors::Visitor # :nodoc:
8
+ def initialize(tt)
9
+ @tt = tt
10
+ @i = -1
11
+ end
12
+
13
+ def visit_CAT(node)
14
+ left = visit(node.left)
15
+ right = visit(node.right)
16
+
17
+ @tt.merge(left.last, right.first)
18
+
19
+ [left.first, right.last]
20
+ end
21
+
22
+ def visit_GROUP(node)
23
+ from = @i += 1
24
+ left = visit(node.left)
25
+ to = @i += 1
26
+
27
+ @tt.accepting = to
28
+
29
+ @tt[from, left.first] = nil
30
+ @tt[left.last, to] = nil
31
+ @tt[from, to] = nil
32
+
33
+ [from, to]
34
+ end
35
+
36
+ def visit_OR(node)
37
+ from = @i += 1
38
+ children = node.children.map { |c| visit(c) }
39
+ to = @i += 1
40
+
41
+ children.each do |child|
42
+ @tt[from, child.first] = nil
43
+ @tt[child.last, to] = nil
44
+ end
45
+
46
+ @tt.accepting = to
47
+
48
+ [from, to]
49
+ end
50
+
51
+ def terminal(node)
52
+ from_i = @i += 1 # new state
53
+ to_i = @i += 1 # new state
54
+
55
+ @tt[from_i, to_i] = node
56
+ @tt.accepting = to_i
57
+ @tt.add_memo(to_i, node.memo)
58
+
59
+ [from_i, to_i]
60
+ end
61
+ end
62
+
63
+ class Builder # :nodoc:
64
+ def initialize(ast)
65
+ @ast = ast
66
+ end
67
+
68
+ def transition_table
69
+ tt = TransitionTable.new
70
+ Visitor.new(tt).accept(@ast)
71
+ tt
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,36 @@
1
+ # encoding: utf-8
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
@@ -0,0 +1,47 @@
1
+ require 'strscan'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module NFA # :nodoc:
6
+ class MatchData # :nodoc:
7
+ attr_reader :memos
8
+
9
+ def initialize(memos)
10
+ @memos = memos
11
+ end
12
+ end
13
+
14
+ class Simulator # :nodoc:
15
+ attr_reader :tt
16
+
17
+ def initialize(transition_table)
18
+ @tt = transition_table
19
+ end
20
+
21
+ def simulate(string)
22
+ input = StringScanner.new(string)
23
+ state = tt.eclosure(0)
24
+ until input.eos?
25
+ sym = input.scan(%r([/.?]|[^/.?]+))
26
+
27
+ # FIXME: tt.eclosure is not needed for the GTG
28
+ state = tt.eclosure(tt.move(state, sym))
29
+ end
30
+
31
+ acceptance_states = state.find_all { |s|
32
+ tt.accepting?(tt.eclosure(s).sort.last)
33
+ }
34
+
35
+ return if acceptance_states.empty?
36
+
37
+ memos = acceptance_states.flat_map { |x| tt.memo(x) }.compact
38
+
39
+ MatchData.new(memos)
40
+ end
41
+
42
+ alias :=~ :simulate
43
+ alias :match :simulate
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,163 @@
1
+ require 'action_dispatch/journey/nfa/dot'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module NFA # :nodoc:
6
+ class TransitionTable # :nodoc:
7
+ include Journey::NFA::Dot
8
+
9
+ attr_accessor :accepting
10
+ attr_reader :memos
11
+
12
+ def initialize
13
+ @table = Hash.new { |h,f| h[f] = {} }
14
+ @memos = {}
15
+ @accepting = nil
16
+ @inverted = nil
17
+ end
18
+
19
+ def accepting?(state)
20
+ accepting == state
21
+ end
22
+
23
+ def accepting_states
24
+ [accepting]
25
+ end
26
+
27
+ def add_memo(idx, memo)
28
+ @memos[idx] = memo
29
+ end
30
+
31
+ def memo(idx)
32
+ @memos[idx]
33
+ end
34
+
35
+ def []=(i, f, s)
36
+ @table[f][i] = s
37
+ end
38
+
39
+ def merge(left, right)
40
+ @memos[right] = @memos.delete(left)
41
+ @table[right] = @table.delete(left)
42
+ end
43
+
44
+ def states
45
+ (@table.keys + @table.values.flat_map(&:keys)).uniq
46
+ end
47
+
48
+ # Returns a generalized transition graph with reduced states. The states
49
+ # are reduced like a DFA, but the table must be simulated like an NFA.
50
+ #
51
+ # Edges of the GTG are regular expressions.
52
+ def generalized_table
53
+ gt = GTG::TransitionTable.new
54
+ marked = {}
55
+ state_id = Hash.new { |h,k| h[k] = h.length }
56
+ alphabet = self.alphabet
57
+
58
+ stack = [eclosure(0)]
59
+
60
+ until stack.empty?
61
+ state = stack.pop
62
+ next if marked[state] || state.empty?
63
+
64
+ marked[state] = true
65
+
66
+ alphabet.each do |alpha|
67
+ next_state = eclosure(following_states(state, alpha))
68
+ next if next_state.empty?
69
+
70
+ gt[state_id[state], state_id[next_state]] = alpha
71
+ stack << next_state
72
+ end
73
+ end
74
+
75
+ final_groups = state_id.keys.find_all { |s|
76
+ s.sort.last == accepting
77
+ }
78
+
79
+ final_groups.each do |states|
80
+ id = state_id[states]
81
+
82
+ gt.add_accepting(id)
83
+ save = states.find { |s|
84
+ @memos.key?(s) && eclosure(s).sort.last == accepting
85
+ }
86
+
87
+ gt.add_memo(id, memo(save))
88
+ end
89
+
90
+ gt
91
+ end
92
+
93
+ # Returns set of NFA states to which there is a transition on ast symbol
94
+ # +a+ from some state +s+ in +t+.
95
+ def following_states(t, a)
96
+ Array(t).flat_map { |s| inverted[s][a] }.uniq
97
+ end
98
+
99
+ # Returns set of NFA states to which there is a transition on ast symbol
100
+ # +a+ from some state +s+ in +t+.
101
+ def move(t, a)
102
+ Array(t).map { |s|
103
+ inverted[s].keys.compact.find_all { |sym|
104
+ sym === a
105
+ }.map { |sym| inverted[s][sym] }
106
+ }.flatten.uniq
107
+ end
108
+
109
+ def alphabet
110
+ inverted.values.flat_map(&:keys).compact.uniq.sort_by { |x| x.to_s }
111
+ end
112
+
113
+ # Returns a set of NFA states reachable from some NFA state +s+ in set
114
+ # +t+ on nil-transitions alone.
115
+ def eclosure(t)
116
+ stack = Array(t)
117
+ seen = {}
118
+ children = []
119
+
120
+ until stack.empty?
121
+ s = stack.pop
122
+ next if seen[s]
123
+
124
+ seen[s] = true
125
+ children << s
126
+
127
+ stack.concat(inverted[s][nil])
128
+ end
129
+
130
+ children.uniq
131
+ end
132
+
133
+ def transitions
134
+ @table.flat_map { |to, hash|
135
+ hash.map { |from, sym| [from, sym, to] }
136
+ }
137
+ end
138
+
139
+ private
140
+
141
+ def inverted
142
+ return @inverted if @inverted
143
+
144
+ @inverted = Hash.new { |h, from|
145
+ h[from] = Hash.new { |j, s| j[s] = [] }
146
+ }
147
+
148
+ @table.each { |to, hash|
149
+ hash.each { |from, sym|
150
+ if sym
151
+ sym = Nodes::Symbol === sym ? sym.regexp : sym.left
152
+ end
153
+
154
+ @inverted[from][sym] << to
155
+ }
156
+ }
157
+
158
+ @inverted
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,128 @@
1
+ require 'action_dispatch/journey/visitors'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module Nodes # :nodoc:
6
+ class Node # :nodoc:
7
+ include Enumerable
8
+
9
+ attr_accessor :left, :memo
10
+
11
+ def initialize(left)
12
+ @left = left
13
+ @memo = nil
14
+ end
15
+
16
+ def each(&block)
17
+ Visitors::Each.new(block).accept(self)
18
+ end
19
+
20
+ def to_s
21
+ Visitors::String.new.accept(self)
22
+ end
23
+
24
+ def to_dot
25
+ Visitors::Dot.new.accept(self)
26
+ end
27
+
28
+ def to_sym
29
+ name.to_sym
30
+ end
31
+
32
+ def name
33
+ left.tr '*:', ''
34
+ end
35
+
36
+ def type
37
+ raise NotImplementedError
38
+ end
39
+
40
+ def symbol?; false; end
41
+ def literal?; false; end
42
+ end
43
+
44
+ class Terminal < Node # :nodoc:
45
+ alias :symbol :left
46
+ end
47
+
48
+ class Literal < Terminal # :nodoc:
49
+ def literal?; true; end
50
+ def type; :LITERAL; end
51
+ end
52
+
53
+ class Dummy < Literal # :nodoc:
54
+ def initialize(x = Object.new)
55
+ super
56
+ end
57
+
58
+ def literal?; false; end
59
+ end
60
+
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
67
+ end
68
+
69
+ class Symbol < Terminal # :nodoc:
70
+ attr_accessor :regexp
71
+ alias :symbol :regexp
72
+
73
+ DEFAULT_EXP = /[^\.\/\?]+/
74
+ def initialize(left)
75
+ super
76
+ @regexp = DEFAULT_EXP
77
+ end
78
+
79
+ def default_regexp?
80
+ regexp == DEFAULT_EXP
81
+ end
82
+
83
+ def symbol?; true; end
84
+ end
85
+
86
+ class Unary < Node # :nodoc:
87
+ def children; [left] end
88
+ end
89
+
90
+ class Group < Unary # :nodoc:
91
+ def type; :GROUP; end
92
+ end
93
+
94
+ class Star < Unary # :nodoc:
95
+ def type; :STAR; end
96
+
97
+ def name
98
+ left.name.tr '*:', ''
99
+ end
100
+ end
101
+
102
+ class Binary < Node # :nodoc:
103
+ attr_accessor :right
104
+
105
+ def initialize(left, right)
106
+ super(left)
107
+ @right = right
108
+ end
109
+
110
+ def children; [left, right] end
111
+ end
112
+
113
+ class Cat < Binary # :nodoc:
114
+ def type; :CAT; end
115
+ end
116
+
117
+ class Or < Node # :nodoc:
118
+ attr_reader :children
119
+
120
+ def initialize(children)
121
+ @children = children
122
+ end
123
+
124
+ def type; :OR; end
125
+ end
126
+ end
127
+ end
128
+ end