omg-actionpack 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (187) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +129 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +57 -0
  5. data/lib/abstract_controller/asset_paths.rb +14 -0
  6. data/lib/abstract_controller/base.rb +299 -0
  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 +265 -0
  10. data/lib/abstract_controller/collector.rb +44 -0
  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 +243 -0
  14. data/lib/abstract_controller/logger.rb +16 -0
  15. data/lib/abstract_controller/railties/routes_helpers.rb +25 -0
  16. data/lib/abstract_controller/rendering.rb +126 -0
  17. data/lib/abstract_controller/translation.rb +42 -0
  18. data/lib/abstract_controller/url_for.rb +37 -0
  19. data/lib/abstract_controller.rb +36 -0
  20. data/lib/action_controller/api/api_rendering.rb +18 -0
  21. data/lib/action_controller/api.rb +155 -0
  22. data/lib/action_controller/base.rb +332 -0
  23. data/lib/action_controller/caching.rb +49 -0
  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 +96 -0
  27. data/lib/action_controller/metal/allow_browser.rb +123 -0
  28. data/lib/action_controller/metal/basic_implicit_render.rb +17 -0
  29. data/lib/action_controller/metal/conditional_get.rb +341 -0
  30. data/lib/action_controller/metal/content_security_policy.rb +86 -0
  31. data/lib/action_controller/metal/cookies.rb +20 -0
  32. data/lib/action_controller/metal/data_streaming.rb +154 -0
  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 +59 -0
  36. data/lib/action_controller/metal/exceptions.rb +106 -0
  37. data/lib/action_controller/metal/flash.rb +67 -0
  38. data/lib/action_controller/metal/head.rb +67 -0
  39. data/lib/action_controller/metal/helpers.rb +129 -0
  40. data/lib/action_controller/metal/http_authentication.rb +565 -0
  41. data/lib/action_controller/metal/implicit_render.rb +67 -0
  42. data/lib/action_controller/metal/instrumentation.rb +120 -0
  43. data/lib/action_controller/metal/live.rb +398 -0
  44. data/lib/action_controller/metal/logging.rb +22 -0
  45. data/lib/action_controller/metal/mime_responds.rb +337 -0
  46. data/lib/action_controller/metal/parameter_encoding.rb +84 -0
  47. data/lib/action_controller/metal/params_wrapper.rb +312 -0
  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 +251 -0
  51. data/lib/action_controller/metal/renderers.rb +181 -0
  52. data/lib/action_controller/metal/rendering.rb +260 -0
  53. data/lib/action_controller/metal/request_forgery_protection.rb +667 -0
  54. data/lib/action_controller/metal/rescue.rb +33 -0
  55. data/lib/action_controller/metal/streaming.rb +183 -0
  56. data/lib/action_controller/metal/strong_parameters.rb +1546 -0
  57. data/lib/action_controller/metal/testing.rb +25 -0
  58. data/lib/action_controller/metal/url_for.rb +65 -0
  59. data/lib/action_controller/metal.rb +339 -0
  60. data/lib/action_controller/railtie.rb +149 -0
  61. data/lib/action_controller/railties/helpers.rb +26 -0
  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 +691 -0
  65. data/lib/action_controller.rb +80 -0
  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 +249 -0
  69. data/lib/action_dispatch/http/content_disposition.rb +47 -0
  70. data/lib/action_dispatch/http/content_security_policy.rb +365 -0
  71. data/lib/action_dispatch/http/filter_parameters.rb +80 -0
  72. data/lib/action_dispatch/http/filter_redirect.rb +50 -0
  73. data/lib/action_dispatch/http/headers.rb +134 -0
  74. data/lib/action_dispatch/http/mime_negotiation.rb +187 -0
  75. data/lib/action_dispatch/http/mime_type.rb +389 -0
  76. data/lib/action_dispatch/http/mime_types.rb +54 -0
  77. data/lib/action_dispatch/http/parameters.rb +119 -0
  78. data/lib/action_dispatch/http/permissions_policy.rb +189 -0
  79. data/lib/action_dispatch/http/rack_cache.rb +67 -0
  80. data/lib/action_dispatch/http/request.rb +498 -0
  81. data/lib/action_dispatch/http/response.rb +556 -0
  82. data/lib/action_dispatch/http/upload.rb +107 -0
  83. data/lib/action_dispatch/http/url.rb +344 -0
  84. data/lib/action_dispatch/journey/formatter.rb +226 -0
  85. data/lib/action_dispatch/journey/gtg/builder.rb +149 -0
  86. data/lib/action_dispatch/journey/gtg/simulator.rb +50 -0
  87. data/lib/action_dispatch/journey/gtg/transition_table.rb +217 -0
  88. data/lib/action_dispatch/journey/nfa/dot.rb +27 -0
  89. data/lib/action_dispatch/journey/nodes/node.rb +208 -0
  90. data/lib/action_dispatch/journey/parser.rb +103 -0
  91. data/lib/action_dispatch/journey/path/pattern.rb +209 -0
  92. data/lib/action_dispatch/journey/route.rb +189 -0
  93. data/lib/action_dispatch/journey/router/utils.rb +105 -0
  94. data/lib/action_dispatch/journey/router.rb +151 -0
  95. data/lib/action_dispatch/journey/routes.rb +82 -0
  96. data/lib/action_dispatch/journey/scanner.rb +70 -0
  97. data/lib/action_dispatch/journey/visitors.rb +267 -0
  98. data/lib/action_dispatch/journey/visualizer/fsm.css +30 -0
  99. data/lib/action_dispatch/journey/visualizer/fsm.js +159 -0
  100. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  101. data/lib/action_dispatch/journey.rb +7 -0
  102. data/lib/action_dispatch/log_subscriber.rb +25 -0
  103. data/lib/action_dispatch/middleware/actionable_exceptions.rb +46 -0
  104. data/lib/action_dispatch/middleware/assume_ssl.rb +27 -0
  105. data/lib/action_dispatch/middleware/callbacks.rb +38 -0
  106. data/lib/action_dispatch/middleware/cookies.rb +719 -0
  107. data/lib/action_dispatch/middleware/debug_exceptions.rb +206 -0
  108. data/lib/action_dispatch/middleware/debug_locks.rb +129 -0
  109. data/lib/action_dispatch/middleware/debug_view.rb +73 -0
  110. data/lib/action_dispatch/middleware/exception_wrapper.rb +350 -0
  111. data/lib/action_dispatch/middleware/executor.rb +32 -0
  112. data/lib/action_dispatch/middleware/flash.rb +318 -0
  113. data/lib/action_dispatch/middleware/host_authorization.rb +171 -0
  114. data/lib/action_dispatch/middleware/public_exceptions.rb +64 -0
  115. data/lib/action_dispatch/middleware/reloader.rb +16 -0
  116. data/lib/action_dispatch/middleware/remote_ip.rb +199 -0
  117. data/lib/action_dispatch/middleware/request_id.rb +50 -0
  118. data/lib/action_dispatch/middleware/server_timing.rb +78 -0
  119. data/lib/action_dispatch/middleware/session/abstract_store.rb +112 -0
  120. data/lib/action_dispatch/middleware/session/cache_store.rb +66 -0
  121. data/lib/action_dispatch/middleware/session/cookie_store.rb +129 -0
  122. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +34 -0
  123. data/lib/action_dispatch/middleware/show_exceptions.rb +88 -0
  124. data/lib/action_dispatch/middleware/ssl.rb +180 -0
  125. data/lib/action_dispatch/middleware/stack.rb +194 -0
  126. data/lib/action_dispatch/middleware/static.rb +192 -0
  127. data/lib/action_dispatch/middleware/templates/rescues/_actions.html.erb +13 -0
  128. data/lib/action_dispatch/middleware/templates/rescues/_actions.text.erb +0 -0
  129. data/lib/action_dispatch/middleware/templates/rescues/_message_and_suggestions.html.erb +22 -0
  130. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb +17 -0
  131. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.text.erb +23 -0
  132. data/lib/action_dispatch/middleware/templates/rescues/_source.html.erb +36 -0
  133. data/lib/action_dispatch/middleware/templates/rescues/_source.text.erb +8 -0
  134. data/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb +62 -0
  135. data/lib/action_dispatch/middleware/templates/rescues/_trace.text.erb +9 -0
  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 +35 -0
  139. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.text.erb +9 -0
  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 +284 -0
  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 +11 -0
  146. data/lib/action_dispatch/middleware/templates/rescues/missing_template.text.erb +3 -0
  147. data/lib/action_dispatch/middleware/templates/rescues/routing_error.html.erb +32 -0
  148. data/lib/action_dispatch/middleware/templates/rescues/routing_error.text.erb +11 -0
  149. data/lib/action_dispatch/middleware/templates/rescues/template_error.html.erb +20 -0
  150. data/lib/action_dispatch/middleware/templates/rescues/template_error.text.erb +7 -0
  151. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.html.erb +6 -0
  152. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.text.erb +3 -0
  153. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +19 -0
  154. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +232 -0
  155. data/lib/action_dispatch/railtie.rb +77 -0
  156. data/lib/action_dispatch/request/session.rb +283 -0
  157. data/lib/action_dispatch/request/utils.rb +109 -0
  158. data/lib/action_dispatch/routing/endpoint.rb +19 -0
  159. data/lib/action_dispatch/routing/inspector.rb +323 -0
  160. data/lib/action_dispatch/routing/mapper.rb +2372 -0
  161. data/lib/action_dispatch/routing/polymorphic_routes.rb +363 -0
  162. data/lib/action_dispatch/routing/redirection.rb +218 -0
  163. data/lib/action_dispatch/routing/route_set.rb +958 -0
  164. data/lib/action_dispatch/routing/routes_proxy.rb +66 -0
  165. data/lib/action_dispatch/routing/url_for.rb +244 -0
  166. data/lib/action_dispatch/routing.rb +262 -0
  167. data/lib/action_dispatch/system_test_case.rb +206 -0
  168. data/lib/action_dispatch/system_testing/browser.rb +75 -0
  169. data/lib/action_dispatch/system_testing/driver.rb +85 -0
  170. data/lib/action_dispatch/system_testing/server.rb +33 -0
  171. data/lib/action_dispatch/system_testing/test_helpers/screenshot_helper.rb +164 -0
  172. data/lib/action_dispatch/system_testing/test_helpers/setup_and_teardown.rb +23 -0
  173. data/lib/action_dispatch/testing/assertion_response.rb +48 -0
  174. data/lib/action_dispatch/testing/assertions/response.rb +114 -0
  175. data/lib/action_dispatch/testing/assertions/routing.rb +343 -0
  176. data/lib/action_dispatch/testing/assertions.rb +25 -0
  177. data/lib/action_dispatch/testing/integration.rb +694 -0
  178. data/lib/action_dispatch/testing/request_encoder.rb +60 -0
  179. data/lib/action_dispatch/testing/test_helpers/page_dump_helper.rb +35 -0
  180. data/lib/action_dispatch/testing/test_process.rb +57 -0
  181. data/lib/action_dispatch/testing/test_request.rb +73 -0
  182. data/lib/action_dispatch/testing/test_response.rb +58 -0
  183. data/lib/action_dispatch.rb +147 -0
  184. data/lib/action_pack/gem_version.rb +19 -0
  185. data/lib/action_pack/version.rb +12 -0
  186. data/lib/action_pack.rb +27 -0
  187. metadata +375 -0
@@ -0,0 +1,151 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "action_dispatch/journey/router/utils"
6
+ require "action_dispatch/journey/routes"
7
+ require "action_dispatch/journey/formatter"
8
+
9
+ before = $-w
10
+ $-w = false
11
+ require "action_dispatch/journey/parser"
12
+ $-w = before
13
+
14
+ require "action_dispatch/journey/route"
15
+ require "action_dispatch/journey/path/pattern"
16
+
17
+ module ActionDispatch
18
+ module Journey # :nodoc:
19
+ class Router # :nodoc:
20
+ attr_accessor :routes
21
+
22
+ def initialize(routes)
23
+ @routes = routes
24
+ end
25
+
26
+ def eager_load!
27
+ # Eagerly trigger the simulator's initialization so it doesn't happen during a
28
+ # request cycle.
29
+ simulator
30
+ nil
31
+ end
32
+
33
+ def serve(req)
34
+ find_routes(req) do |match, parameters, route|
35
+ set_params = req.path_parameters
36
+ path_info = req.path_info
37
+ script_name = req.script_name
38
+
39
+ unless route.path.anchored
40
+ req.script_name = (script_name.to_s + match.to_s).chomp("/")
41
+ req.path_info = match.post_match
42
+ req.path_info = "/" + req.path_info unless req.path_info.start_with? "/"
43
+ end
44
+
45
+ tmp_params = set_params.merge route.defaults
46
+ parameters.each_pair { |key, val|
47
+ tmp_params[key] = val.force_encoding(::Encoding::UTF_8)
48
+ }
49
+
50
+ req.path_parameters = tmp_params
51
+ req.route_uri_pattern = route.path.spec.to_s
52
+
53
+ _, headers, _ = response = route.app.serve(req)
54
+
55
+ if "pass" == headers[Constants::X_CASCADE]
56
+ req.script_name = script_name
57
+ req.path_info = path_info
58
+ req.path_parameters = set_params
59
+ next
60
+ end
61
+
62
+ return response
63
+ end
64
+
65
+ [404, { Constants::X_CASCADE => "pass" }, ["Not Found"]]
66
+ end
67
+
68
+ def recognize(rails_req)
69
+ find_routes(rails_req) do |match, parameters, route|
70
+ unless route.path.anchored
71
+ rails_req.script_name = match.to_s
72
+ rails_req.path_info = match.post_match
73
+ rails_req.path_info = "/" + rails_req.path_info unless rails_req.path_info.start_with? "/"
74
+ end
75
+
76
+ parameters = route.defaults.merge parameters
77
+ yield(route, parameters)
78
+ end
79
+ end
80
+
81
+ def visualizer
82
+ tt = GTG::Builder.new(ast).transition_table
83
+ groups = partitioned_routes.first.map(&:ast).group_by(&:to_s)
84
+ asts = groups.values.map(&:first)
85
+ tt.visualizer(asts)
86
+ end
87
+
88
+ private
89
+ def partitioned_routes
90
+ routes.partition { |r|
91
+ r.path.anchored && r.path.requirements_anchored?
92
+ }
93
+ end
94
+
95
+ def ast
96
+ routes.ast
97
+ end
98
+
99
+ def simulator
100
+ routes.simulator
101
+ end
102
+
103
+ def custom_routes
104
+ routes.custom_routes
105
+ end
106
+
107
+ def filter_routes(path)
108
+ return [] unless ast
109
+ simulator.memos(path) { [] }
110
+ end
111
+
112
+ def find_routes(req)
113
+ path_info = req.path_info
114
+ routes = filter_routes(path_info).concat custom_routes.find_all { |r|
115
+ r.path.match?(path_info)
116
+ }
117
+
118
+ if req.head?
119
+ routes = match_head_routes(routes, req)
120
+ else
121
+ routes.select! { |r| r.matches?(req) }
122
+ end
123
+
124
+ routes.sort_by!(&:precedence)
125
+
126
+ routes.each { |r|
127
+ match_data = r.path.match(path_info)
128
+ path_parameters = {}
129
+ match_data.names.each_with_index { |name, i|
130
+ val = match_data[i + 1]
131
+ path_parameters[name.to_sym] = Utils.unescape_uri(val) if val
132
+ }
133
+ yield [match_data, path_parameters, r]
134
+ }
135
+ end
136
+
137
+ def match_head_routes(routes, req)
138
+ head_routes = routes.select { |r| r.requires_matching_verb? && r.matches?(req) }
139
+ return head_routes unless head_routes.empty?
140
+
141
+ begin
142
+ req.request_method = "GET"
143
+ routes.select! { |r| r.matches?(req) }
144
+ routes
145
+ ensure
146
+ req.request_method = "HEAD"
147
+ end
148
+ end
149
+ end
150
+ end
151
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ module Journey # :nodoc:
7
+ # The Routing table. Contains all routes for a system. Routes can be added to
8
+ # the table by calling Routes#add_route.
9
+ class Routes # :nodoc:
10
+ include Enumerable
11
+
12
+ attr_reader :routes, :custom_routes, :anchored_routes
13
+
14
+ def initialize(routes = [])
15
+ @routes = routes
16
+ @ast = nil
17
+ @anchored_routes = []
18
+ @custom_routes = []
19
+ @simulator = nil
20
+ end
21
+
22
+ def empty?
23
+ routes.empty?
24
+ end
25
+
26
+ def length
27
+ routes.length
28
+ end
29
+ alias :size :length
30
+
31
+ def last
32
+ routes.last
33
+ end
34
+
35
+ def each(&block)
36
+ routes.each(&block)
37
+ end
38
+
39
+ def clear
40
+ routes.clear
41
+ anchored_routes.clear
42
+ custom_routes.clear
43
+ end
44
+
45
+ def partition_route(route)
46
+ if route.path.anchored && route.path.requirements_anchored?
47
+ anchored_routes << route
48
+ else
49
+ custom_routes << route
50
+ end
51
+ end
52
+
53
+ def ast
54
+ @ast ||= begin
55
+ nodes = anchored_routes.map(&:ast)
56
+ Nodes::Or.new(nodes)
57
+ end
58
+ end
59
+
60
+ def simulator
61
+ @simulator ||= begin
62
+ gtg = GTG::Builder.new(ast).transition_table
63
+ GTG::Simulator.new(gtg)
64
+ end
65
+ end
66
+
67
+ def add_route(name, mapping)
68
+ route = mapping.make_route name, routes.length
69
+ routes << route
70
+ partition_route(route)
71
+ clear_cache!
72
+ route
73
+ end
74
+
75
+ private
76
+ def clear_cache!
77
+ @ast = nil
78
+ @simulator = nil
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ require "strscan"
6
+
7
+ module ActionDispatch
8
+ module Journey # :nodoc:
9
+ class Scanner # :nodoc:
10
+ STATIC_TOKENS = Array.new(150)
11
+ STATIC_TOKENS[".".ord] = :DOT
12
+ STATIC_TOKENS["/".ord] = :SLASH
13
+ STATIC_TOKENS["(".ord] = :LPAREN
14
+ STATIC_TOKENS[")".ord] = :RPAREN
15
+ STATIC_TOKENS["|".ord] = :OR
16
+ STATIC_TOKENS[":".ord] = :SYMBOL
17
+ STATIC_TOKENS["*".ord] = :STAR
18
+ STATIC_TOKENS.freeze
19
+
20
+ class Scanner < StringScanner
21
+ unless method_defined?(:peek_byte) # https://github.com/ruby/strscan/pull/89
22
+ def peek_byte
23
+ string.getbyte(pos)
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize
29
+ @scanner = nil
30
+ @length = nil
31
+ end
32
+
33
+ def scan_setup(str)
34
+ @scanner = Scanner.new(str)
35
+ end
36
+
37
+ def next_token
38
+ return if @scanner.eos?
39
+
40
+ until token = scan || @scanner.eos?; end
41
+ token
42
+ end
43
+
44
+ def last_string
45
+ -@scanner.string.byteslice(@scanner.pos - @length, @length)
46
+ end
47
+
48
+ def last_literal
49
+ last_str = @scanner.string.byteslice(@scanner.pos - @length, @length)
50
+ last_str.tr! "\\", ""
51
+ -last_str
52
+ end
53
+
54
+ private
55
+ def scan
56
+ next_byte = @scanner.peek_byte
57
+ case
58
+ when (token = STATIC_TOKENS[next_byte])
59
+ @scanner.pos += 1
60
+ @length = @scanner.skip(/\w+/).to_i + 1 if token == :SYMBOL || token == :STAR
61
+ token
62
+ when @length = @scanner.skip(/(?:[\w%\-~!$&'*+,;=@]|\\[:()])+/)
63
+ :LITERAL
64
+ when @length = @scanner.skip(/./)
65
+ :LITERAL
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,267 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionDispatch
6
+ # :stopdoc:
7
+ module Journey
8
+ class Format
9
+ ESCAPE_PATH = ->(value) { Router::Utils.escape_path(value) }
10
+ ESCAPE_SEGMENT = ->(value) { Router::Utils.escape_segment(value) }
11
+
12
+ Parameter = Struct.new(:name, :escaper) do
13
+ def escape(value); escaper.call value; end
14
+ end
15
+
16
+ def self.required_path(symbol)
17
+ Parameter.new symbol, ESCAPE_PATH
18
+ end
19
+
20
+ def self.required_segment(symbol)
21
+ Parameter.new symbol, ESCAPE_SEGMENT
22
+ end
23
+
24
+ def initialize(parts)
25
+ @parts = parts
26
+ @children = []
27
+ @parameters = []
28
+
29
+ parts.each_with_index do |object, i|
30
+ case object
31
+ when Journey::Format
32
+ @children << i
33
+ when Parameter
34
+ @parameters << i
35
+ end
36
+ end
37
+ end
38
+
39
+ def evaluate(hash)
40
+ parts = @parts.dup
41
+
42
+ @parameters.each do |index|
43
+ param = parts[index]
44
+ value = hash[param.name]
45
+ return "" if value.nil?
46
+ parts[index] = param.escape value
47
+ end
48
+
49
+ @children.each { |index| parts[index] = parts[index].evaluate(hash) }
50
+
51
+ parts.join
52
+ end
53
+ end
54
+
55
+ module Visitors # :nodoc:
56
+ class Visitor # :nodoc:
57
+ DISPATCH_CACHE = {}
58
+
59
+ def accept(node)
60
+ visit(node)
61
+ end
62
+
63
+ private
64
+ def visit(node)
65
+ send(DISPATCH_CACHE[node.type], node)
66
+ end
67
+
68
+ def binary(node)
69
+ visit(node.left)
70
+ visit(node.right)
71
+ end
72
+ def visit_CAT(n); binary(n); end
73
+
74
+ def nary(node)
75
+ node.children.each { |c| visit(c) }
76
+ end
77
+ def visit_OR(n); nary(n); end
78
+
79
+ def unary(node)
80
+ visit(node.left)
81
+ end
82
+ def visit_GROUP(n); unary(n); end
83
+ def visit_STAR(n); unary(n); end
84
+
85
+ def terminal(node); end
86
+ def visit_LITERAL(n); terminal(n); end
87
+ def visit_SYMBOL(n); terminal(n); end
88
+ def visit_SLASH(n); terminal(n); end
89
+ def visit_DOT(n); terminal(n); end
90
+
91
+ private_instance_methods(false).each do |pim|
92
+ next unless pim =~ /^visit_(.*)$/
93
+ DISPATCH_CACHE[$1.to_sym] = pim
94
+ end
95
+ end
96
+
97
+ class FunctionalVisitor # :nodoc:
98
+ DISPATCH_CACHE = {}
99
+
100
+ def accept(node, seed)
101
+ visit(node, seed)
102
+ end
103
+
104
+ def visit(node, seed)
105
+ send(DISPATCH_CACHE[node.type], node, seed)
106
+ end
107
+
108
+ def binary(node, seed)
109
+ visit(node.right, visit(node.left, seed))
110
+ end
111
+ def visit_CAT(n, seed); binary(n, seed); end
112
+
113
+ def nary(node, seed)
114
+ node.children.inject(seed) { |s, c| visit(c, s) }
115
+ end
116
+ def visit_OR(n, seed); nary(n, seed); end
117
+
118
+ def unary(node, seed)
119
+ visit(node.left, seed)
120
+ end
121
+ def visit_GROUP(n, seed); unary(n, seed); end
122
+ def visit_STAR(n, seed); unary(n, seed); end
123
+
124
+ def terminal(node, seed); seed; end
125
+ def visit_LITERAL(n, seed); terminal(n, seed); end
126
+ def visit_SYMBOL(n, seed); terminal(n, seed); end
127
+ def visit_SLASH(n, seed); terminal(n, seed); end
128
+ def visit_DOT(n, seed); terminal(n, seed); end
129
+
130
+ instance_methods(false).each do |pim|
131
+ next unless pim =~ /^visit_(.*)$/
132
+ DISPATCH_CACHE[$1.to_sym] = pim
133
+ end
134
+ end
135
+
136
+ class FormatBuilder < Visitor # :nodoc:
137
+ def accept(node); Journey::Format.new(super); end
138
+ def terminal(node); [node.left]; end
139
+
140
+ def binary(node)
141
+ visit(node.left) + visit(node.right)
142
+ end
143
+
144
+ def visit_GROUP(n); [Journey::Format.new(unary(n))]; end
145
+
146
+ def visit_STAR(n)
147
+ [Journey::Format.required_path(n.left.to_sym)]
148
+ end
149
+
150
+ def visit_SYMBOL(n)
151
+ symbol = n.to_sym
152
+ if symbol == :controller
153
+ [Journey::Format.required_path(symbol)]
154
+ else
155
+ [Journey::Format.required_segment(symbol)]
156
+ end
157
+ end
158
+ end
159
+
160
+ # Loop through the requirements AST.
161
+ class Each < FunctionalVisitor # :nodoc:
162
+ def visit(node, block)
163
+ block.call(node)
164
+ super
165
+ end
166
+
167
+ INSTANCE = new
168
+ end
169
+
170
+ class String < FunctionalVisitor # :nodoc:
171
+ private
172
+ def binary(node, seed)
173
+ visit(node.right, visit(node.left, seed))
174
+ end
175
+
176
+ def nary(node, seed)
177
+ last_child = node.children.last
178
+ node.children.inject(seed) { |s, c|
179
+ string = visit(c, s)
180
+ string << "|" unless last_child == c
181
+ string
182
+ }
183
+ end
184
+
185
+ def terminal(node, seed)
186
+ seed + node.left
187
+ end
188
+
189
+ def visit_GROUP(node, seed)
190
+ visit(node.left, seed.dup << "(") << ")"
191
+ end
192
+
193
+ INSTANCE = new
194
+ end
195
+
196
+ class Dot < FunctionalVisitor # :nodoc:
197
+ def initialize
198
+ @nodes = []
199
+ @edges = []
200
+ end
201
+
202
+ def accept(node, seed = [[], []])
203
+ super
204
+ nodes, edges = seed
205
+ <<-eodot
206
+ digraph parse_tree {
207
+ size="8,5"
208
+ node [shape = none];
209
+ edge [dir = none];
210
+ #{nodes.join "\n"}
211
+ #{edges.join("\n")}
212
+ }
213
+ eodot
214
+ end
215
+
216
+ private
217
+ def binary(node, seed)
218
+ seed.last.concat node.children.map { |c|
219
+ "#{node.object_id} -> #{c.object_id};"
220
+ }
221
+ super
222
+ end
223
+
224
+ def nary(node, seed)
225
+ seed.last.concat node.children.map { |c|
226
+ "#{node.object_id} -> #{c.object_id};"
227
+ }
228
+ super
229
+ end
230
+
231
+ def unary(node, seed)
232
+ seed.last << "#{node.object_id} -> #{node.left.object_id};"
233
+ super
234
+ end
235
+
236
+ def visit_GROUP(node, seed)
237
+ seed.first << "#{node.object_id} [label=\"()\"];"
238
+ super
239
+ end
240
+
241
+ def visit_CAT(node, seed)
242
+ seed.first << "#{node.object_id} [label=\"○\"];"
243
+ super
244
+ end
245
+
246
+ def visit_STAR(node, seed)
247
+ seed.first << "#{node.object_id} [label=\"*\"];"
248
+ super
249
+ end
250
+
251
+ def visit_OR(node, seed)
252
+ seed.first << "#{node.object_id} [label=\"|\"];"
253
+ super
254
+ end
255
+
256
+ def terminal(node, seed)
257
+ value = node.left
258
+
259
+ seed.first << "#{node.object_id} [label=\"#{value}\"];"
260
+ seed
261
+ end
262
+ INSTANCE = new
263
+ end
264
+ end
265
+ end
266
+ # :startdoc:
267
+ end
@@ -0,0 +1,30 @@
1
+ body {
2
+ font-family: "Helvetica Neue", Helvetica, Arial, Sans-Serif;
3
+ margin: 0;
4
+ }
5
+
6
+ h1 {
7
+ font-size: 2.0em; font-weight: bold; text-align: center;
8
+ color: white; background-color: black;
9
+ padding: 5px 0;
10
+ margin: 0 0 20px;
11
+ }
12
+
13
+ h2 {
14
+ text-align: center;
15
+ display: none;
16
+ font-size: 0.5em;
17
+ }
18
+
19
+ .clearfix {display: inline-block; }
20
+ .input { overflow: show;}
21
+ .instruction { color: #666; padding: 0 30px 20px; font-size: 0.9em}
22
+ .instruction p { padding: 0 0 5px; }
23
+ .instruction li { padding: 0 10px 5px; }
24
+
25
+ .form { background: #EEE; padding: 20px 30px; border-radius: 5px; margin-left: auto; margin-right: auto; width: 500px; margin-bottom: 20px}
26
+ .form p, .form form { text-align: center }
27
+ .form form {padding: 0 10px 5px; }
28
+ .form .fun_routes { font-size: 0.9em;}
29
+ .form .fun_routes a { margin: 0 5px 0 0; }
30
+