actionpack 3.2.22.5 → 4.0.0.beta1

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 (265) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +641 -418
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller.rb +1 -8
  6. data/lib/abstract_controller/asset_paths.rb +2 -2
  7. data/lib/abstract_controller/base.rb +39 -37
  8. data/lib/abstract_controller/callbacks.rb +101 -82
  9. data/lib/abstract_controller/collector.rb +7 -3
  10. data/lib/abstract_controller/helpers.rb +23 -11
  11. data/lib/abstract_controller/layouts.rb +68 -73
  12. data/lib/abstract_controller/logger.rb +1 -2
  13. data/lib/abstract_controller/rendering.rb +22 -13
  14. data/lib/abstract_controller/translation.rb +16 -1
  15. data/lib/abstract_controller/url_for.rb +6 -6
  16. data/lib/abstract_controller/view_paths.rb +1 -1
  17. data/lib/action_controller.rb +15 -6
  18. data/lib/action_controller/base.rb +46 -22
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/caching/fragments.rb +23 -53
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  23. data/lib/action_controller/log_subscriber.rb +11 -8
  24. data/lib/action_controller/metal.rb +16 -30
  25. data/lib/action_controller/metal/conditional_get.rb +76 -32
  26. data/lib/action_controller/metal/data_streaming.rb +20 -26
  27. data/lib/action_controller/metal/exceptions.rb +19 -6
  28. data/lib/action_controller/metal/flash.rb +24 -9
  29. data/lib/action_controller/metal/force_ssl.rb +32 -9
  30. data/lib/action_controller/metal/head.rb +25 -4
  31. data/lib/action_controller/metal/helpers.rb +6 -9
  32. data/lib/action_controller/metal/hide_actions.rb +1 -2
  33. data/lib/action_controller/metal/http_authentication.rb +105 -87
  34. data/lib/action_controller/metal/implicit_render.rb +1 -1
  35. data/lib/action_controller/metal/instrumentation.rb +2 -1
  36. data/lib/action_controller/metal/live.rb +141 -0
  37. data/lib/action_controller/metal/mime_responds.rb +161 -47
  38. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  39. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  40. data/lib/action_controller/metal/redirecting.rb +15 -20
  41. data/lib/action_controller/metal/renderers.rb +11 -9
  42. data/lib/action_controller/metal/rendering.rb +8 -0
  43. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  44. data/lib/action_controller/metal/responder.rb +20 -19
  45. data/lib/action_controller/metal/streaming.rb +12 -18
  46. data/lib/action_controller/metal/strong_parameters.rb +516 -0
  47. data/lib/action_controller/metal/testing.rb +13 -18
  48. data/lib/action_controller/metal/url_for.rb +27 -25
  49. data/lib/action_controller/model_naming.rb +12 -0
  50. data/lib/action_controller/railtie.rb +33 -17
  51. data/lib/action_controller/railties/helpers.rb +22 -0
  52. data/lib/action_controller/record_identifier.rb +18 -72
  53. data/lib/action_controller/test_case.rb +215 -123
  54. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  55. data/lib/action_dispatch.rb +27 -19
  56. data/lib/action_dispatch/http/cache.rb +63 -11
  57. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  58. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  59. data/lib/action_dispatch/http/headers.rb +27 -19
  60. data/lib/action_dispatch/http/mime_negotiation.rb +25 -2
  61. data/lib/action_dispatch/http/mime_type.rb +145 -113
  62. data/lib/action_dispatch/http/mime_types.rb +1 -1
  63. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  64. data/lib/action_dispatch/http/parameters.rb +12 -5
  65. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  66. data/lib/action_dispatch/http/request.rb +49 -18
  67. data/lib/action_dispatch/http/response.rb +129 -35
  68. data/lib/action_dispatch/http/upload.rb +60 -17
  69. data/lib/action_dispatch/http/url.rb +53 -31
  70. data/lib/action_dispatch/journey.rb +5 -0
  71. data/lib/action_dispatch/journey/backwards.rb +5 -0
  72. data/lib/action_dispatch/journey/formatter.rb +146 -0
  73. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  74. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  75. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  76. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  77. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  78. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  79. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  80. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  81. data/lib/action_dispatch/journey/parser.rb +206 -0
  82. data/lib/action_dispatch/journey/parser.y +47 -0
  83. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  84. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  85. data/lib/action_dispatch/journey/route.rb +116 -0
  86. data/lib/action_dispatch/journey/router.rb +164 -0
  87. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  88. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  89. data/lib/action_dispatch/journey/routes.rb +75 -0
  90. data/lib/action_dispatch/journey/scanner.rb +61 -0
  91. data/lib/action_dispatch/journey/visitors.rb +189 -0
  92. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  93. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  94. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  95. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  96. data/lib/action_dispatch/middleware/cookies.rb +168 -57
  97. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  98. data/lib/action_dispatch/middleware/exception_wrapper.rb +27 -3
  99. data/lib/action_dispatch/middleware/flash.rb +58 -58
  100. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  101. data/lib/action_dispatch/middleware/public_exceptions.rb +31 -14
  102. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  103. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  104. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  105. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  106. data/lib/action_dispatch/middleware/session/cache_store.rb +3 -3
  107. data/lib/action_dispatch/middleware/session/cookie_store.rb +81 -7
  108. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  109. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  110. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  111. data/lib/action_dispatch/middleware/stack.rb +6 -1
  112. data/lib/action_dispatch/middleware/static.rb +5 -24
  113. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  114. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  115. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +3 -3
  116. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  117. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +121 -5
  118. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  119. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  120. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  121. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  122. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  123. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  124. data/lib/action_dispatch/railtie.rb +16 -6
  125. data/lib/action_dispatch/request/session.rb +181 -0
  126. data/lib/action_dispatch/routing.rb +41 -40
  127. data/lib/action_dispatch/routing/inspector.rb +240 -0
  128. data/lib/action_dispatch/routing/mapper.rb +501 -273
  129. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  130. data/lib/action_dispatch/routing/redirection.rb +46 -29
  131. data/lib/action_dispatch/routing/route_set.rb +203 -164
  132. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  133. data/lib/action_dispatch/routing/url_for.rb +48 -33
  134. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  135. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  136. data/lib/action_dispatch/testing/assertions/routing.rb +40 -39
  137. data/lib/action_dispatch/testing/assertions/selector.rb +15 -20
  138. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  139. data/lib/action_dispatch/testing/integration.rb +41 -22
  140. data/lib/action_dispatch/testing/test_process.rb +9 -6
  141. data/lib/action_dispatch/testing/test_request.rb +7 -3
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_pack/version.rb +4 -4
  144. data/lib/action_view.rb +17 -8
  145. data/lib/action_view/base.rb +15 -34
  146. data/lib/action_view/buffers.rb +1 -1
  147. data/lib/action_view/context.rb +4 -4
  148. data/lib/action_view/dependency_tracker.rb +91 -0
  149. data/lib/action_view/digestor.rb +85 -0
  150. data/lib/action_view/flows.rb +1 -4
  151. data/lib/action_view/helpers.rb +2 -4
  152. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  153. data/lib/action_view/helpers/asset_tag_helper.rb +211 -353
  154. data/lib/action_view/helpers/asset_url_helper.rb +354 -0
  155. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  156. data/lib/action_view/helpers/cache_helper.rb +150 -18
  157. data/lib/action_view/helpers/capture_helper.rb +42 -29
  158. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  159. data/lib/action_view/helpers/date_helper.rb +268 -247
  160. data/lib/action_view/helpers/debug_helper.rb +10 -11
  161. data/lib/action_view/helpers/form_helper.rb +904 -547
  162. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  163. data/lib/action_view/helpers/form_tag_helper.rb +188 -88
  164. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  165. data/lib/action_view/helpers/number_helper.rb +148 -354
  166. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  167. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  168. data/lib/action_view/helpers/rendering_helper.rb +2 -4
  169. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  170. data/lib/action_view/helpers/tag_helper.rb +43 -37
  171. data/lib/action_view/helpers/tags.rb +39 -0
  172. data/lib/action_view/helpers/tags/base.rb +148 -0
  173. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  174. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  175. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  176. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  177. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  178. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  179. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  180. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  181. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  182. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  183. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  184. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  185. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  186. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  187. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  188. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  189. data/lib/action_view/helpers/tags/label.rb +65 -0
  190. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  191. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  192. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  193. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  194. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  195. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  196. data/lib/action_view/helpers/tags/select.rb +41 -0
  197. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  198. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  199. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  200. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  201. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  202. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  203. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  204. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  205. data/lib/action_view/helpers/text_helper.rb +126 -113
  206. data/lib/action_view/helpers/translation_helper.rb +32 -16
  207. data/lib/action_view/helpers/url_helper.rb +200 -271
  208. data/lib/action_view/locale/en.yml +1 -105
  209. data/lib/action_view/log_subscriber.rb +6 -4
  210. data/lib/action_view/lookup_context.rb +15 -39
  211. data/lib/action_view/model_naming.rb +12 -0
  212. data/lib/action_view/path_set.rb +9 -39
  213. data/lib/action_view/railtie.rb +6 -22
  214. data/lib/action_view/record_identifier.rb +84 -0
  215. data/lib/action_view/renderer/abstract_renderer.rb +10 -19
  216. data/lib/action_view/renderer/partial_renderer.rb +144 -81
  217. data/lib/action_view/renderer/renderer.rb +2 -19
  218. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  219. data/lib/action_view/renderer/template_renderer.rb +14 -13
  220. data/lib/action_view/routing_url_for.rb +107 -0
  221. data/lib/action_view/template.rb +22 -21
  222. data/lib/action_view/template/error.rb +22 -12
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/handlers/builder.rb +1 -1
  225. data/lib/action_view/template/handlers/erb.rb +11 -16
  226. data/lib/action_view/template/handlers/raw.rb +11 -0
  227. data/lib/action_view/template/resolver.rb +111 -83
  228. data/lib/action_view/template/text.rb +12 -8
  229. data/lib/action_view/template/types.rb +57 -0
  230. data/lib/action_view/test_case.rb +66 -43
  231. data/lib/action_view/testing/resolvers.rb +3 -2
  232. data/lib/action_view/vendor/html-scanner.rb +20 -0
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  235. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +18 -7
  236. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +1 -1
  237. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  238. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  239. metadata +135 -125
  240. data/lib/action_controller/caching/actions.rb +0 -185
  241. data/lib/action_controller/caching/pages.rb +0 -187
  242. data/lib/action_controller/caching/sweeping.rb +0 -97
  243. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  244. data/lib/action_controller/metal/compatibility.rb +0 -65
  245. data/lib/action_controller/metal/session_management.rb +0 -14
  246. data/lib/action_controller/railties/paths.rb +0 -25
  247. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  248. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  249. data/lib/action_dispatch/middleware/head.rb +0 -18
  250. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  251. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  252. data/lib/action_view/asset_paths.rb +0 -142
  253. data/lib/action_view/helpers/asset_paths.rb +0 -7
  254. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  255. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  256. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  257. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  258. data/lib/sprockets/assets.rake +0 -99
  259. data/lib/sprockets/bootstrap.rb +0 -37
  260. data/lib/sprockets/compressors.rb +0 -83
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  263. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  264. data/lib/sprockets/railtie.rb +0 -62
  265. data/lib/sprockets/static_compiler.rb +0 -56
@@ -0,0 +1,44 @@
1
+ require 'strscan'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module GTG # :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 = [0]
24
+ while sym = input.scan(%r([/.?]|[^/.?]+))
25
+ state = tt.move(state, sym)
26
+ end
27
+
28
+ acceptance_states = state.find_all { |s|
29
+ tt.accepting? s
30
+ }
31
+
32
+ return if acceptance_states.empty?
33
+
34
+ memos = acceptance_states.map { |x| tt.memo(x) }.flatten.compact
35
+
36
+ MatchData.new(memos)
37
+ end
38
+
39
+ alias :=~ :simulate
40
+ alias :match :simulate
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,156 @@
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 = Hash.new { |h,k| h[k] = {} }
13
+ @string_states = Hash.new { |h,k| h[k] = {} }
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
+ move_string(t, a).concat(move_regexp(t, a))
44
+ end
45
+
46
+ def to_json
47
+ require 'json'
48
+
49
+ simple_regexp = Hash.new { |h,k| h[k] = {} }
50
+
51
+ @regexp_states.each do |from, hash|
52
+ hash.each do |re, to|
53
+ simple_regexp[from][re.source] = to
54
+ end
55
+ end
56
+
57
+ JSON.dump({
58
+ regexp_states: simple_regexp,
59
+ string_states: @string_states,
60
+ accepting: @accepting
61
+ })
62
+ end
63
+
64
+ def to_svg
65
+ svg = IO.popen('dot -Tsvg', 'w+') { |f|
66
+ f.write(to_dot)
67
+ f.close_write
68
+ f.readlines
69
+ }
70
+ 3.times { svg.shift }
71
+ svg.join.sub(/width="[^"]*"/, '').sub(/height="[^"]*"/, '')
72
+ end
73
+
74
+ def visualizer(paths, title = 'FSM')
75
+ viz_dir = File.join File.dirname(__FILE__), '..', 'visualizer'
76
+ fsm_js = File.read File.join(viz_dir, 'fsm.js')
77
+ fsm_css = File.read File.join(viz_dir, 'fsm.css')
78
+ erb = File.read File.join(viz_dir, 'index.html.erb')
79
+ states = "function tt() { return #{to_json}; }"
80
+
81
+ fun_routes = paths.shuffle.first(3).map do |ast|
82
+ ast.map { |n|
83
+ case n
84
+ when Nodes::Symbol
85
+ case n.left
86
+ when ':id' then rand(100).to_s
87
+ when ':format' then %w{ xml json }.shuffle.first
88
+ else
89
+ 'omg'
90
+ end
91
+ when Nodes::Terminal then n.symbol
92
+ else
93
+ nil
94
+ end
95
+ }.compact.join
96
+ end
97
+
98
+ stylesheets = [fsm_css]
99
+ svg = to_svg
100
+ javascripts = [states, fsm_js]
101
+
102
+ # Annoying hack for 1.9 warnings
103
+ fun_routes = fun_routes
104
+ stylesheets = stylesheets
105
+ svg = svg
106
+ javascripts = javascripts
107
+
108
+ require 'erb'
109
+ template = ERB.new erb
110
+ template.result(binding)
111
+ end
112
+
113
+ def []=(from, to, sym)
114
+ case sym
115
+ when String
116
+ @string_states[from][sym] = to
117
+ when Regexp
118
+ @regexp_states[from][sym] = to
119
+ else
120
+ raise ArgumentError, 'unknown symbol: %s' % sym.class
121
+ end
122
+ end
123
+
124
+ def states
125
+ ss = @string_states.keys + @string_states.values.map(&:values).flatten
126
+ rs = @regexp_states.keys + @regexp_states.values.map(&:values).flatten
127
+ (ss + rs).uniq
128
+ end
129
+
130
+ def transitions
131
+ @string_states.map { |from, hash|
132
+ hash.map { |s, to| [from, s, to] }
133
+ }.flatten(1) + @regexp_states.map { |from, hash|
134
+ hash.map { |s, to| [from, s, to] }
135
+ }.flatten(1)
136
+ end
137
+
138
+ private
139
+
140
+ def move_regexp(t, a)
141
+ return [] if t.empty?
142
+
143
+ t.map { |s|
144
+ @regexp_states[s].map { |re, v| re === a ? v : nil }
145
+ }.flatten.compact.uniq
146
+ end
147
+
148
+ def move_string(t, a)
149
+ return [] if t.empty?
150
+
151
+ t.map { |s| @string_states[s][a] }.compact
152
+ end
153
+ end
154
+ end
155
+ end
156
+ 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.map { |k, memos|
20
+ # (memos || []).map { |v| " #{k} -> #{v.object_id};" }
21
+ #}.flatten.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.map { |x| tt.memo(x) }.flatten.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.map(&:keys).flatten).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).map { |s| inverted[s][a] }.flatten.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.map(&:keys).flatten.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.map { |to, hash|
135
+ hash.map { |from, sym| [from, sym, to] }
136
+ }.flatten(1)
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