omg-actionpack 8.0.0.alpha1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+