actionpack 3.2.19 → 4.0.0

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 (263) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +850 -401
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -288
  5. data/lib/abstract_controller/asset_paths.rb +2 -2
  6. data/lib/abstract_controller/base.rb +39 -37
  7. data/lib/abstract_controller/callbacks.rb +101 -82
  8. data/lib/abstract_controller/collector.rb +7 -3
  9. data/lib/abstract_controller/helpers.rb +25 -13
  10. data/lib/abstract_controller/layouts.rb +74 -74
  11. data/lib/abstract_controller/logger.rb +1 -2
  12. data/lib/abstract_controller/rendering.rb +30 -13
  13. data/lib/abstract_controller/translation.rb +16 -1
  14. data/lib/abstract_controller/url_for.rb +6 -6
  15. data/lib/abstract_controller/view_paths.rb +1 -1
  16. data/lib/abstract_controller.rb +1 -8
  17. data/lib/action_controller/base.rb +46 -22
  18. data/lib/action_controller/caching/fragments.rb +23 -53
  19. data/lib/action_controller/caching.rb +46 -33
  20. data/lib/action_controller/deprecated/integration_test.rb +3 -0
  21. data/lib/action_controller/deprecated.rb +5 -1
  22. data/lib/action_controller/log_subscriber.rb +16 -8
  23. data/lib/action_controller/metal/conditional_get.rb +76 -32
  24. data/lib/action_controller/metal/data_streaming.rb +20 -26
  25. data/lib/action_controller/metal/exceptions.rb +19 -6
  26. data/lib/action_controller/metal/flash.rb +24 -9
  27. data/lib/action_controller/metal/force_ssl.rb +70 -12
  28. data/lib/action_controller/metal/head.rb +25 -4
  29. data/lib/action_controller/metal/helpers.rb +5 -9
  30. data/lib/action_controller/metal/hide_actions.rb +0 -1
  31. data/lib/action_controller/metal/http_authentication.rb +107 -83
  32. data/lib/action_controller/metal/implicit_render.rb +1 -1
  33. data/lib/action_controller/metal/instrumentation.rb +2 -1
  34. data/lib/action_controller/metal/live.rb +175 -0
  35. data/lib/action_controller/metal/mime_responds.rb +161 -47
  36. data/lib/action_controller/metal/params_wrapper.rb +112 -74
  37. data/lib/action_controller/metal/rack_delegation.rb +9 -3
  38. data/lib/action_controller/metal/redirecting.rb +15 -20
  39. data/lib/action_controller/metal/renderers.rb +11 -9
  40. data/lib/action_controller/metal/rendering.rb +9 -1
  41. data/lib/action_controller/metal/request_forgery_protection.rb +112 -19
  42. data/lib/action_controller/metal/responder.rb +20 -19
  43. data/lib/action_controller/metal/streaming.rb +12 -18
  44. data/lib/action_controller/metal/strong_parameters.rb +520 -0
  45. data/lib/action_controller/metal/testing.rb +13 -18
  46. data/lib/action_controller/metal/url_for.rb +28 -25
  47. data/lib/action_controller/metal.rb +17 -32
  48. data/lib/action_controller/model_naming.rb +12 -0
  49. data/lib/action_controller/railtie.rb +33 -17
  50. data/lib/action_controller/railties/helpers.rb +22 -0
  51. data/lib/action_controller/record_identifier.rb +18 -72
  52. data/lib/action_controller/test_case.rb +251 -131
  53. data/lib/action_controller/vendor/html-scanner.rb +4 -19
  54. data/lib/action_controller.rb +15 -6
  55. data/lib/action_dispatch/http/cache.rb +63 -11
  56. data/lib/action_dispatch/http/filter_parameters.rb +18 -8
  57. data/lib/action_dispatch/http/filter_redirect.rb +37 -0
  58. data/lib/action_dispatch/http/headers.rb +49 -17
  59. data/lib/action_dispatch/http/mime_negotiation.rb +24 -1
  60. data/lib/action_dispatch/http/mime_type.rb +154 -100
  61. data/lib/action_dispatch/http/mime_types.rb +1 -1
  62. data/lib/action_dispatch/http/parameter_filter.rb +44 -46
  63. data/lib/action_dispatch/http/parameters.rb +28 -28
  64. data/lib/action_dispatch/http/rack_cache.rb +2 -3
  65. data/lib/action_dispatch/http/request.rb +64 -18
  66. data/lib/action_dispatch/http/response.rb +130 -35
  67. data/lib/action_dispatch/http/upload.rb +63 -20
  68. data/lib/action_dispatch/http/url.rb +98 -35
  69. data/lib/action_dispatch/journey/backwards.rb +5 -0
  70. data/lib/action_dispatch/journey/formatter.rb +146 -0
  71. data/lib/action_dispatch/journey/gtg/builder.rb +162 -0
  72. data/lib/action_dispatch/journey/gtg/simulator.rb +44 -0
  73. data/lib/action_dispatch/journey/gtg/transition_table.rb +156 -0
  74. data/lib/action_dispatch/journey/nfa/builder.rb +76 -0
  75. data/lib/action_dispatch/journey/nfa/dot.rb +36 -0
  76. data/lib/action_dispatch/journey/nfa/simulator.rb +47 -0
  77. data/lib/action_dispatch/journey/nfa/transition_table.rb +163 -0
  78. data/lib/action_dispatch/journey/nodes/node.rb +124 -0
  79. data/lib/action_dispatch/journey/parser.rb +206 -0
  80. data/lib/action_dispatch/journey/parser.y +47 -0
  81. data/lib/action_dispatch/journey/parser_extras.rb +23 -0
  82. data/lib/action_dispatch/journey/path/pattern.rb +196 -0
  83. data/lib/action_dispatch/journey/route.rb +124 -0
  84. data/lib/action_dispatch/journey/router/strexp.rb +24 -0
  85. data/lib/action_dispatch/journey/router/utils.rb +54 -0
  86. data/lib/action_dispatch/journey/router.rb +166 -0
  87. data/lib/action_dispatch/journey/routes.rb +75 -0
  88. data/lib/action_dispatch/journey/scanner.rb +61 -0
  89. data/lib/action_dispatch/journey/visitors.rb +197 -0
  90. data/lib/action_dispatch/journey/visualizer/fsm.css +34 -0
  91. data/lib/action_dispatch/journey/visualizer/fsm.js +134 -0
  92. data/lib/action_dispatch/journey/visualizer/index.html.erb +52 -0
  93. data/lib/action_dispatch/journey.rb +5 -0
  94. data/lib/action_dispatch/middleware/callbacks.rb +9 -4
  95. data/lib/action_dispatch/middleware/cookies.rb +259 -114
  96. data/lib/action_dispatch/middleware/debug_exceptions.rb +26 -17
  97. data/lib/action_dispatch/middleware/exception_wrapper.rb +29 -3
  98. data/lib/action_dispatch/middleware/flash.rb +58 -58
  99. data/lib/action_dispatch/middleware/params_parser.rb +14 -29
  100. data/lib/action_dispatch/middleware/public_exceptions.rb +30 -14
  101. data/lib/action_dispatch/middleware/reloader.rb +6 -6
  102. data/lib/action_dispatch/middleware/remote_ip.rb +145 -39
  103. data/lib/action_dispatch/middleware/request_id.rb +2 -6
  104. data/lib/action_dispatch/middleware/session/abstract_store.rb +22 -20
  105. data/lib/action_dispatch/middleware/session/cookie_store.rb +82 -28
  106. data/lib/action_dispatch/middleware/session/mem_cache_store.rb +8 -3
  107. data/lib/action_dispatch/middleware/show_exceptions.rb +12 -45
  108. data/lib/action_dispatch/middleware/ssl.rb +70 -0
  109. data/lib/action_dispatch/middleware/stack.rb +6 -1
  110. data/lib/action_dispatch/middleware/static.rb +2 -1
  111. data/lib/action_dispatch/middleware/templates/rescues/_request_and_response.erb +14 -11
  112. data/lib/action_dispatch/middleware/templates/rescues/_source.erb +25 -0
  113. data/lib/action_dispatch/middleware/templates/rescues/_trace.erb +7 -9
  114. data/lib/action_dispatch/middleware/templates/rescues/diagnostics.erb +15 -9
  115. data/lib/action_dispatch/middleware/templates/rescues/layout.erb +127 -5
  116. data/lib/action_dispatch/middleware/templates/rescues/missing_template.erb +7 -2
  117. data/lib/action_dispatch/middleware/templates/rescues/routing_error.erb +30 -15
  118. data/lib/action_dispatch/middleware/templates/rescues/template_error.erb +39 -13
  119. data/lib/action_dispatch/middleware/templates/rescues/unknown_action.erb +6 -2
  120. data/lib/action_dispatch/middleware/templates/routes/_route.html.erb +16 -0
  121. data/lib/action_dispatch/middleware/templates/routes/_table.html.erb +144 -0
  122. data/lib/action_dispatch/railtie.rb +16 -6
  123. data/lib/action_dispatch/request/session.rb +181 -0
  124. data/lib/action_dispatch/routing/inspector.rb +240 -0
  125. data/lib/action_dispatch/routing/mapper.rb +540 -291
  126. data/lib/action_dispatch/routing/polymorphic_routes.rb +16 -20
  127. data/lib/action_dispatch/routing/redirection.rb +46 -29
  128. data/lib/action_dispatch/routing/route_set.rb +207 -164
  129. data/lib/action_dispatch/routing/routes_proxy.rb +2 -0
  130. data/lib/action_dispatch/routing/url_for.rb +48 -33
  131. data/lib/action_dispatch/routing.rb +48 -83
  132. data/lib/action_dispatch/testing/assertions/dom.rb +3 -13
  133. data/lib/action_dispatch/testing/assertions/response.rb +32 -40
  134. data/lib/action_dispatch/testing/assertions/routing.rb +42 -41
  135. data/lib/action_dispatch/testing/assertions/selector.rb +17 -22
  136. data/lib/action_dispatch/testing/assertions/tag.rb +20 -23
  137. data/lib/action_dispatch/testing/integration.rb +65 -51
  138. data/lib/action_dispatch/testing/test_process.rb +9 -6
  139. data/lib/action_dispatch/testing/test_request.rb +7 -3
  140. data/lib/action_dispatch.rb +21 -15
  141. data/lib/action_pack/version.rb +7 -6
  142. data/lib/action_pack.rb +1 -1
  143. data/lib/action_view/base.rb +15 -34
  144. data/lib/action_view/buffers.rb +7 -1
  145. data/lib/action_view/context.rb +4 -4
  146. data/lib/action_view/dependency_tracker.rb +93 -0
  147. data/lib/action_view/digestor.rb +85 -0
  148. data/lib/action_view/flows.rb +1 -4
  149. data/lib/action_view/helpers/active_model_helper.rb +3 -4
  150. data/lib/action_view/helpers/asset_tag_helper.rb +215 -352
  151. data/lib/action_view/helpers/asset_url_helper.rb +355 -0
  152. data/lib/action_view/helpers/atom_feed_helper.rb +13 -10
  153. data/lib/action_view/helpers/cache_helper.rb +150 -18
  154. data/lib/action_view/helpers/capture_helper.rb +44 -31
  155. data/lib/action_view/helpers/csrf_helper.rb +0 -2
  156. data/lib/action_view/helpers/date_helper.rb +269 -248
  157. data/lib/action_view/helpers/debug_helper.rb +10 -11
  158. data/lib/action_view/helpers/form_helper.rb +931 -537
  159. data/lib/action_view/helpers/form_options_helper.rb +341 -166
  160. data/lib/action_view/helpers/form_tag_helper.rb +190 -90
  161. data/lib/action_view/helpers/javascript_helper.rb +23 -16
  162. data/lib/action_view/helpers/number_helper.rb +148 -329
  163. data/lib/action_view/helpers/output_safety_helper.rb +3 -3
  164. data/lib/action_view/helpers/record_tag_helper.rb +17 -22
  165. data/lib/action_view/helpers/rendering_helper.rb +2 -2
  166. data/lib/action_view/helpers/sanitize_helper.rb +3 -6
  167. data/lib/action_view/helpers/tag_helper.rb +46 -33
  168. data/lib/action_view/helpers/tags/base.rb +147 -0
  169. data/lib/action_view/helpers/tags/check_box.rb +64 -0
  170. data/lib/action_view/helpers/tags/checkable.rb +16 -0
  171. data/lib/action_view/helpers/tags/collection_check_boxes.rb +43 -0
  172. data/lib/action_view/helpers/tags/collection_helpers.rb +83 -0
  173. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +36 -0
  174. data/lib/action_view/helpers/tags/collection_select.rb +28 -0
  175. data/lib/action_view/helpers/tags/color_field.rb +25 -0
  176. data/lib/action_view/helpers/tags/date_field.rb +13 -0
  177. data/lib/action_view/helpers/tags/date_select.rb +72 -0
  178. data/lib/action_view/helpers/tags/datetime_field.rb +22 -0
  179. data/lib/action_view/helpers/tags/datetime_local_field.rb +19 -0
  180. data/lib/action_view/helpers/tags/datetime_select.rb +8 -0
  181. data/lib/action_view/helpers/tags/email_field.rb +8 -0
  182. data/lib/action_view/helpers/tags/file_field.rb +8 -0
  183. data/lib/action_view/helpers/tags/grouped_collection_select.rb +29 -0
  184. data/lib/action_view/helpers/tags/hidden_field.rb +8 -0
  185. data/lib/action_view/helpers/tags/label.rb +65 -0
  186. data/lib/action_view/helpers/tags/month_field.rb +13 -0
  187. data/lib/action_view/helpers/tags/number_field.rb +18 -0
  188. data/lib/action_view/helpers/tags/password_field.rb +12 -0
  189. data/lib/action_view/helpers/tags/radio_button.rb +31 -0
  190. data/lib/action_view/helpers/tags/range_field.rb +8 -0
  191. data/lib/action_view/helpers/tags/search_field.rb +24 -0
  192. data/lib/action_view/helpers/tags/select.rb +40 -0
  193. data/lib/action_view/helpers/tags/tel_field.rb +8 -0
  194. data/lib/action_view/helpers/tags/text_area.rb +18 -0
  195. data/lib/action_view/helpers/tags/text_field.rb +29 -0
  196. data/lib/action_view/helpers/tags/time_field.rb +13 -0
  197. data/lib/action_view/helpers/tags/time_select.rb +8 -0
  198. data/lib/action_view/helpers/tags/time_zone_select.rb +20 -0
  199. data/lib/action_view/helpers/tags/url_field.rb +8 -0
  200. data/lib/action_view/helpers/tags/week_field.rb +13 -0
  201. data/lib/action_view/helpers/tags.rb +39 -0
  202. data/lib/action_view/helpers/text_helper.rb +130 -114
  203. data/lib/action_view/helpers/translation_helper.rb +32 -16
  204. data/lib/action_view/helpers/url_helper.rb +211 -270
  205. data/lib/action_view/helpers.rb +2 -4
  206. data/lib/action_view/locale/en.yml +1 -105
  207. data/lib/action_view/log_subscriber.rb +6 -4
  208. data/lib/action_view/lookup_context.rb +15 -28
  209. data/lib/action_view/model_naming.rb +12 -0
  210. data/lib/action_view/path_set.rb +8 -20
  211. data/lib/action_view/railtie.rb +6 -22
  212. data/lib/action_view/record_identifier.rb +84 -0
  213. data/lib/action_view/renderer/abstract_renderer.rb +25 -19
  214. data/lib/action_view/renderer/partial_renderer.rb +158 -81
  215. data/lib/action_view/renderer/renderer.rb +8 -12
  216. data/lib/action_view/renderer/streaming_template_renderer.rb +2 -5
  217. data/lib/action_view/renderer/template_renderer.rb +12 -10
  218. data/lib/action_view/routing_url_for.rb +107 -0
  219. data/lib/action_view/template/error.rb +22 -12
  220. data/lib/action_view/template/handlers/builder.rb +1 -1
  221. data/lib/action_view/template/handlers/erb.rb +40 -19
  222. data/lib/action_view/template/handlers/raw.rb +11 -0
  223. data/lib/action_view/template/handlers.rb +12 -9
  224. data/lib/action_view/template/resolver.rb +107 -53
  225. data/lib/action_view/template/text.rb +12 -8
  226. data/lib/action_view/template/types.rb +57 -0
  227. data/lib/action_view/template.rb +25 -23
  228. data/lib/action_view/test_case.rb +67 -42
  229. data/lib/{action_controller → action_view}/vendor/html-scanner/html/document.rb +0 -0
  230. data/lib/{action_controller → action_view}/vendor/html-scanner/html/node.rb +12 -12
  231. data/lib/{action_controller → action_view}/vendor/html-scanner/html/sanitizer.rb +13 -2
  232. data/lib/{action_controller → action_view}/vendor/html-scanner/html/selector.rb +9 -9
  233. data/lib/{action_controller → action_view}/vendor/html-scanner/html/tokenizer.rb +1 -1
  234. data/lib/{action_controller → action_view}/vendor/html-scanner/html/version.rb +0 -0
  235. data/lib/action_view/vendor/html-scanner.rb +20 -0
  236. data/lib/action_view.rb +17 -8
  237. metadata +184 -214
  238. data/lib/action_controller/caching/actions.rb +0 -185
  239. data/lib/action_controller/caching/pages.rb +0 -187
  240. data/lib/action_controller/caching/sweeping.rb +0 -97
  241. data/lib/action_controller/deprecated/performance_test.rb +0 -1
  242. data/lib/action_controller/metal/compatibility.rb +0 -65
  243. data/lib/action_controller/metal/session_management.rb +0 -14
  244. data/lib/action_controller/railties/paths.rb +0 -25
  245. data/lib/action_dispatch/middleware/best_standards_support.rb +0 -30
  246. data/lib/action_dispatch/middleware/body_proxy.rb +0 -30
  247. data/lib/action_dispatch/middleware/head.rb +0 -18
  248. data/lib/action_dispatch/middleware/rescue.rb +0 -26
  249. data/lib/action_dispatch/testing/performance_test.rb +0 -10
  250. data/lib/action_view/asset_paths.rb +0 -142
  251. data/lib/action_view/helpers/asset_paths.rb +0 -7
  252. data/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb +0 -146
  253. data/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb +0 -93
  254. data/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb +0 -193
  255. data/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +0 -148
  256. data/lib/sprockets/assets.rake +0 -99
  257. data/lib/sprockets/bootstrap.rb +0 -37
  258. data/lib/sprockets/compressors.rb +0 -83
  259. data/lib/sprockets/helpers/isolated_helper.rb +0 -13
  260. data/lib/sprockets/helpers/rails_helper.rb +0 -182
  261. data/lib/sprockets/helpers.rb +0 -6
  262. data/lib/sprockets/railtie.rb +0 -62
  263. data/lib/sprockets/static_compiler.rb +0 -56
@@ -0,0 +1,196 @@
1
+ module ActionDispatch
2
+ module Journey # :nodoc:
3
+ module Path # :nodoc:
4
+ class Pattern # :nodoc:
5
+ attr_reader :spec, :requirements, :anchored
6
+
7
+ def initialize(strexp)
8
+ parser = Journey::Parser.new
9
+
10
+ @anchored = true
11
+
12
+ case strexp
13
+ when String
14
+ @spec = parser.parse(strexp)
15
+ @requirements = {}
16
+ @separators = "/.?"
17
+ when Router::Strexp
18
+ @spec = parser.parse(strexp.path)
19
+ @requirements = strexp.requirements
20
+ @separators = strexp.separators.join
21
+ @anchored = strexp.anchor
22
+ else
23
+ raise ArgumentError, "Bad expression: #{strexp}"
24
+ end
25
+
26
+ @names = nil
27
+ @optional_names = nil
28
+ @required_names = nil
29
+ @re = nil
30
+ @offsets = nil
31
+ end
32
+
33
+ def ast
34
+ @spec.grep(Nodes::Symbol).each do |node|
35
+ re = @requirements[node.to_sym]
36
+ node.regexp = re if re
37
+ end
38
+
39
+ @spec.grep(Nodes::Star).each do |node|
40
+ node = node.left
41
+ node.regexp = @requirements[node.to_sym] || /(.+)/
42
+ end
43
+
44
+ @spec
45
+ end
46
+
47
+ def names
48
+ @names ||= spec.grep(Nodes::Symbol).map { |n| n.name }
49
+ end
50
+
51
+ def required_names
52
+ @required_names ||= names - optional_names
53
+ end
54
+
55
+ def optional_names
56
+ @optional_names ||= spec.grep(Nodes::Group).map { |group|
57
+ group.grep(Nodes::Symbol)
58
+ }.flatten.map { |n| n.name }.uniq
59
+ end
60
+
61
+ class RegexpOffsets < Journey::Visitors::Visitor # :nodoc:
62
+ attr_reader :offsets
63
+
64
+ def initialize(matchers)
65
+ @matchers = matchers
66
+ @capture_count = [0]
67
+ end
68
+
69
+ def visit(node)
70
+ super
71
+ @capture_count
72
+ end
73
+
74
+ def visit_SYMBOL(node)
75
+ node = node.to_sym
76
+
77
+ if @matchers.key?(node)
78
+ re = /#{@matchers[node]}|/
79
+ @capture_count.push((re.match('').length - 1) + (@capture_count.last || 0))
80
+ else
81
+ @capture_count << (@capture_count.last || 0)
82
+ end
83
+ end
84
+ end
85
+
86
+ class AnchoredRegexp < Journey::Visitors::Visitor # :nodoc:
87
+ def initialize(separator, matchers)
88
+ @separator = separator
89
+ @matchers = matchers
90
+ @separator_re = "([^#{separator}]+)"
91
+ super()
92
+ end
93
+
94
+ def accept(node)
95
+ %r{\A#{visit node}\Z}
96
+ end
97
+
98
+ def visit_CAT(node)
99
+ [visit(node.left), visit(node.right)].join
100
+ end
101
+
102
+ def visit_SYMBOL(node)
103
+ node = node.to_sym
104
+
105
+ return @separator_re unless @matchers.key?(node)
106
+
107
+ re = @matchers[node]
108
+ "(#{re})"
109
+ end
110
+
111
+ def visit_GROUP(node)
112
+ "(?:#{visit node.left})?"
113
+ end
114
+
115
+ def visit_LITERAL(node)
116
+ Regexp.escape(node.left)
117
+ end
118
+ alias :visit_DOT :visit_LITERAL
119
+
120
+ def visit_SLASH(node)
121
+ node.left
122
+ end
123
+
124
+ def visit_STAR(node)
125
+ re = @matchers[node.left.to_sym] || '.+'
126
+ "(#{re})"
127
+ end
128
+ end
129
+
130
+ class UnanchoredRegexp < AnchoredRegexp # :nodoc:
131
+ def accept(node)
132
+ %r{\A#{visit node}}
133
+ end
134
+ end
135
+
136
+ class MatchData # :nodoc:
137
+ attr_reader :names
138
+
139
+ def initialize(names, offsets, match)
140
+ @names = names
141
+ @offsets = offsets
142
+ @match = match
143
+ end
144
+
145
+ def captures
146
+ (length - 1).times.map { |i| self[i + 1] }
147
+ end
148
+
149
+ def [](x)
150
+ idx = @offsets[x - 1] + x
151
+ @match[idx]
152
+ end
153
+
154
+ def length
155
+ @offsets.length
156
+ end
157
+
158
+ def post_match
159
+ @match.post_match
160
+ end
161
+
162
+ def to_s
163
+ @match.to_s
164
+ end
165
+ end
166
+
167
+ def match(other)
168
+ return unless match = to_regexp.match(other)
169
+ MatchData.new(names, offsets, match)
170
+ end
171
+ alias :=~ :match
172
+
173
+ def source
174
+ to_regexp.source
175
+ end
176
+
177
+ def to_regexp
178
+ @re ||= regexp_visitor.new(@separators, @requirements).accept spec
179
+ end
180
+
181
+ private
182
+
183
+ def regexp_visitor
184
+ @anchored ? AnchoredRegexp : UnanchoredRegexp
185
+ end
186
+
187
+ def offsets
188
+ return @offsets if @offsets
189
+
190
+ viz = RegexpOffsets.new(@requirements)
191
+ @offsets = viz.accept(spec)
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,124 @@
1
+ module ActionDispatch
2
+ module Journey # :nodoc:
3
+ class Route # :nodoc:
4
+ attr_reader :app, :path, :defaults, :name
5
+
6
+ attr_reader :constraints
7
+ alias :conditions :constraints
8
+
9
+ attr_accessor :precedence
10
+
11
+ ##
12
+ # +path+ is a path constraint.
13
+ # +constraints+ is a hash of constraints to be applied to this route.
14
+ def initialize(name, app, path, constraints, defaults = {})
15
+ @name = name
16
+ @app = app
17
+ @path = path
18
+
19
+ @constraints = constraints
20
+ @defaults = defaults
21
+ @required_defaults = nil
22
+ @required_parts = nil
23
+ @parts = nil
24
+ @decorated_ast = nil
25
+ @precedence = 0
26
+ end
27
+
28
+ def ast
29
+ @decorated_ast ||= begin
30
+ decorated_ast = path.ast
31
+ decorated_ast.grep(Nodes::Terminal).each { |n| n.memo = self }
32
+ decorated_ast
33
+ end
34
+ end
35
+
36
+ def requirements # :nodoc:
37
+ # needed for rails `rake routes`
38
+ path.requirements.merge(@defaults).delete_if { |_,v|
39
+ /.+?/ == v
40
+ }
41
+ end
42
+
43
+ def segments
44
+ path.names
45
+ end
46
+
47
+ def required_keys
48
+ required_parts + required_defaults.keys
49
+ end
50
+
51
+ def score(constraints)
52
+ required_keys = path.required_names
53
+ supplied_keys = constraints.map { |k,v| v && k.to_s }.compact
54
+
55
+ return -1 unless (required_keys - supplied_keys).empty?
56
+
57
+ score = (supplied_keys & path.names).length
58
+ score + (required_defaults.length * 2)
59
+ end
60
+
61
+ def parts
62
+ @parts ||= segments.map { |n| n.to_sym }
63
+ end
64
+ alias :segment_keys :parts
65
+
66
+ def format(path_options)
67
+ path_options.delete_if do |key, value|
68
+ value.to_s == defaults[key].to_s && !required_parts.include?(key)
69
+ end
70
+
71
+ Visitors::Formatter.new(path_options).accept(path.spec)
72
+ end
73
+
74
+ def optimized_path
75
+ Visitors::OptimizedPath.new.accept(path.spec)
76
+ end
77
+
78
+ def optional_parts
79
+ path.optional_names.map { |n| n.to_sym }
80
+ end
81
+
82
+ def required_parts
83
+ @required_parts ||= path.required_names.map { |n| n.to_sym }
84
+ end
85
+
86
+ def required_default?(key)
87
+ (constraints[:required_defaults] || []).include?(key)
88
+ end
89
+
90
+ def required_defaults
91
+ @required_defaults ||= @defaults.dup.delete_if do |k,_|
92
+ parts.include?(k) || !required_default?(k)
93
+ end
94
+ end
95
+
96
+ def matches?(request)
97
+ constraints.all? do |method, value|
98
+ next true unless request.respond_to?(method)
99
+
100
+ case value
101
+ when Regexp, String
102
+ value === request.send(method).to_s
103
+ when Array
104
+ value.include?(request.send(method))
105
+ when TrueClass
106
+ request.send(method).present?
107
+ when FalseClass
108
+ request.send(method).blank?
109
+ else
110
+ value === request.send(method)
111
+ end
112
+ end
113
+ end
114
+
115
+ def ip
116
+ constraints[:ip] || //
117
+ end
118
+
119
+ def verb
120
+ constraints[:request_method] || //
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,24 @@
1
+ module ActionDispatch
2
+ module Journey # :nodoc:
3
+ class Router # :nodoc:
4
+ class Strexp # :nodoc:
5
+ class << self
6
+ alias :compile :new
7
+ end
8
+
9
+ attr_reader :path, :requirements, :separators, :anchor
10
+
11
+ def initialize(path, requirements, separators, anchor = true)
12
+ @path = path
13
+ @requirements = requirements
14
+ @separators = separators
15
+ @anchor = anchor
16
+ end
17
+
18
+ def names
19
+ @path.scan(/:\w+/).map { |s| s.tr(':', '') }
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ require 'uri'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ class Router # :nodoc:
6
+ class Utils # :nodoc:
7
+ # Normalizes URI path.
8
+ #
9
+ # Strips off trailing slash and ensures there is a leading slash.
10
+ #
11
+ # normalize_path("/foo") # => "/foo"
12
+ # normalize_path("/foo/") # => "/foo"
13
+ # normalize_path("foo") # => "/foo"
14
+ # normalize_path("") # => "/"
15
+ def self.normalize_path(path)
16
+ path = "/#{path}"
17
+ path.squeeze!('/')
18
+ path.sub!(%r{/+\Z}, '')
19
+ path = '/' if path == ''
20
+ path
21
+ end
22
+
23
+ # URI path and fragment escaping
24
+ # http://tools.ietf.org/html/rfc3986
25
+ module UriEscape # :nodoc:
26
+ # Symbol captures can generate multiple path segments, so include /.
27
+ reserved_segment = '/'
28
+ reserved_fragment = '/?'
29
+ reserved_pchar = ':@&=+$,;%'
30
+
31
+ safe_pchar = "#{URI::REGEXP::PATTERN::UNRESERVED}#{reserved_pchar}"
32
+ safe_segment = "#{safe_pchar}#{reserved_segment}"
33
+ safe_fragment = "#{safe_pchar}#{reserved_fragment}"
34
+ UNSAFE_SEGMENT = Regexp.new("[^#{safe_segment}]", false).freeze
35
+ UNSAFE_FRAGMENT = Regexp.new("[^#{safe_fragment}]", false).freeze
36
+ end
37
+
38
+ Parser = URI.const_defined?(:Parser) ? URI::Parser.new : URI
39
+
40
+ def self.escape_path(path)
41
+ Parser.escape(path.to_s, UriEscape::UNSAFE_SEGMENT)
42
+ end
43
+
44
+ def self.escape_fragment(fragment)
45
+ Parser.escape(fragment.to_s, UriEscape::UNSAFE_FRAGMENT)
46
+ end
47
+
48
+ def self.unescape_uri(uri)
49
+ Parser.unescape(uri)
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,166 @@
1
+ require 'action_dispatch/journey/router/utils'
2
+ require 'action_dispatch/journey/router/strexp'
3
+ require 'action_dispatch/journey/routes'
4
+ require 'action_dispatch/journey/formatter'
5
+
6
+ before = $-w
7
+ $-w = false
8
+ require 'action_dispatch/journey/parser'
9
+ $-w = before
10
+
11
+ require 'action_dispatch/journey/route'
12
+ require 'action_dispatch/journey/path/pattern'
13
+
14
+ module ActionDispatch
15
+ module Journey # :nodoc:
16
+ class Router # :nodoc:
17
+ class RoutingError < ::StandardError # :nodoc:
18
+ end
19
+
20
+ # :nodoc:
21
+ VERSION = '2.0.0'
22
+
23
+ class NullReq # :nodoc:
24
+ attr_reader :env
25
+ def initialize(env)
26
+ @env = env
27
+ end
28
+
29
+ def request_method
30
+ env['REQUEST_METHOD']
31
+ end
32
+
33
+ def path_info
34
+ env['PATH_INFO']
35
+ end
36
+
37
+ def ip
38
+ env['REMOTE_ADDR']
39
+ end
40
+
41
+ def [](k)
42
+ env[k]
43
+ end
44
+ end
45
+
46
+ attr_reader :request_class, :formatter
47
+ attr_accessor :routes
48
+
49
+ def initialize(routes, options)
50
+ @options = options
51
+ @params_key = options[:parameters_key]
52
+ @request_class = options[:request_class] || NullReq
53
+ @routes = routes
54
+ end
55
+
56
+ def call(env)
57
+ env['PATH_INFO'] = Utils.normalize_path(env['PATH_INFO'])
58
+
59
+ find_routes(env).each do |match, parameters, route|
60
+ script_name, path_info, set_params = env.values_at('SCRIPT_NAME',
61
+ 'PATH_INFO',
62
+ @params_key)
63
+
64
+ unless route.path.anchored
65
+ env['SCRIPT_NAME'] = (script_name.to_s + match.to_s).chomp('/')
66
+ env['PATH_INFO'] = match.post_match
67
+ end
68
+
69
+ env[@params_key] = (set_params || {}).merge parameters
70
+
71
+ status, headers, body = route.app.call(env)
72
+
73
+ if 'pass' == headers['X-Cascade']
74
+ env['SCRIPT_NAME'] = script_name
75
+ env['PATH_INFO'] = path_info
76
+ env[@params_key] = set_params
77
+ next
78
+ end
79
+
80
+ return [status, headers, body]
81
+ end
82
+
83
+ return [404, {'X-Cascade' => 'pass'}, ['Not Found']]
84
+ end
85
+
86
+ def recognize(req)
87
+ find_routes(req.env).each do |match, parameters, route|
88
+ unless route.path.anchored
89
+ req.env['SCRIPT_NAME'] = match.to_s
90
+ req.env['PATH_INFO'] = match.post_match.sub(/^([^\/])/, '/\1')
91
+ end
92
+
93
+ yield(route, nil, parameters)
94
+ end
95
+ end
96
+
97
+ def visualizer
98
+ tt = GTG::Builder.new(ast).transition_table
99
+ groups = partitioned_routes.first.map(&:ast).group_by { |a| a.to_s }
100
+ asts = groups.values.map { |v| v.first }
101
+ tt.visualizer(asts)
102
+ end
103
+
104
+ private
105
+
106
+ def partitioned_routes
107
+ routes.partitioned_routes
108
+ end
109
+
110
+ def ast
111
+ routes.ast
112
+ end
113
+
114
+ def simulator
115
+ routes.simulator
116
+ end
117
+
118
+ def custom_routes
119
+ partitioned_routes.last
120
+ end
121
+
122
+ def filter_routes(path)
123
+ return [] unless ast
124
+ data = simulator.match(path)
125
+ data ? data.memos : []
126
+ end
127
+
128
+ def find_routes env
129
+ req = request_class.new(env)
130
+
131
+ routes = filter_routes(req.path_info).concat custom_routes.find_all { |r|
132
+ r.path.match(req.path_info)
133
+ }
134
+ routes.concat get_routes_as_head(routes)
135
+
136
+ routes.sort_by!(&:precedence).select! { |r| r.matches?(req) }
137
+
138
+ routes.map! { |r|
139
+ match_data = r.path.match(req.path_info)
140
+ match_names = match_data.names.map { |n| n.to_sym }
141
+ match_values = match_data.captures.map { |v| v && Utils.unescape_uri(v) }
142
+ info = Hash[match_names.zip(match_values).find_all { |_, y| y }]
143
+
144
+ [match_data, r.defaults.merge(info), r]
145
+ }
146
+ end
147
+
148
+ def get_routes_as_head(routes)
149
+ precedence = (routes.map(&:precedence).max || 0) + 1
150
+ routes = routes.select { |r|
151
+ r.verb === "GET" && !(r.verb === "HEAD")
152
+ }.map! { |r|
153
+ Route.new(r.name,
154
+ r.app,
155
+ r.path,
156
+ r.conditions.merge(request_method: "HEAD"),
157
+ r.defaults).tap do |route|
158
+ route.precedence = r.precedence + precedence
159
+ end
160
+ }
161
+ routes.flatten!
162
+ routes
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,75 @@
1
+ module ActionDispatch
2
+ module Journey # :nodoc:
3
+ # The Routing table. Contains all routes for a system. Routes can be
4
+ # added to the table by calling Routes#add_route.
5
+ class Routes # :nodoc:
6
+ include Enumerable
7
+
8
+ attr_reader :routes, :named_routes
9
+
10
+ def initialize
11
+ @routes = []
12
+ @named_routes = {}
13
+ @ast = nil
14
+ @partitioned_routes = nil
15
+ @simulator = nil
16
+ end
17
+
18
+ def length
19
+ routes.length
20
+ end
21
+ alias :size :length
22
+
23
+ def last
24
+ routes.last
25
+ end
26
+
27
+ def each(&block)
28
+ routes.each(&block)
29
+ end
30
+
31
+ def clear
32
+ routes.clear
33
+ end
34
+
35
+ def partitioned_routes
36
+ @partitioned_routes ||= routes.partition do |r|
37
+ r.path.anchored && r.ast.grep(Nodes::Symbol).all?(&:default_regexp?)
38
+ end
39
+ end
40
+
41
+ def ast
42
+ @ast ||= begin
43
+ asts = partitioned_routes.first.map(&:ast)
44
+ Nodes::Or.new(asts) unless asts.empty?
45
+ end
46
+ end
47
+
48
+ def simulator
49
+ @simulator ||= begin
50
+ gtg = GTG::Builder.new(ast).transition_table
51
+ GTG::Simulator.new(gtg)
52
+ end
53
+ end
54
+
55
+ # Add a route to the routing table.
56
+ def add_route(app, path, conditions, defaults, name = nil)
57
+ route = Route.new(name, app, path, conditions, defaults)
58
+
59
+ route.precedence = routes.length
60
+ routes << route
61
+ named_routes[name] = route if name && !named_routes[name]
62
+ clear_cache!
63
+ route
64
+ end
65
+
66
+ private
67
+
68
+ def clear_cache!
69
+ @ast = nil
70
+ @partitioned_routes = nil
71
+ @simulator = nil
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,61 @@
1
+ require 'strscan'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ class Scanner # :nodoc:
6
+ def initialize
7
+ @ss = nil
8
+ end
9
+
10
+ def scan_setup(str)
11
+ @ss = StringScanner.new(str)
12
+ end
13
+
14
+ def eos?
15
+ @ss.eos?
16
+ end
17
+
18
+ def pos
19
+ @ss.pos
20
+ end
21
+
22
+ def pre_match
23
+ @ss.pre_match
24
+ end
25
+
26
+ def next_token
27
+ return if @ss.eos?
28
+
29
+ until token = scan || @ss.eos?; end
30
+ token
31
+ end
32
+
33
+ private
34
+
35
+ def scan
36
+ case
37
+ # /
38
+ when text = @ss.scan(/\//)
39
+ [:SLASH, text]
40
+ when text = @ss.scan(/\*\w+/)
41
+ [:STAR, text]
42
+ when text = @ss.scan(/\(/)
43
+ [:LPAREN, text]
44
+ when text = @ss.scan(/\)/)
45
+ [:RPAREN, text]
46
+ when text = @ss.scan(/\|/)
47
+ [:OR, text]
48
+ when text = @ss.scan(/\./)
49
+ [:DOT, text]
50
+ when text = @ss.scan(/:\w+/)
51
+ [:SYMBOL, text]
52
+ when text = @ss.scan(/[\w%\-~]+/)
53
+ [:LITERAL, text]
54
+ # any char
55
+ when text = @ss.scan(/./)
56
+ [:LITERAL, text]
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end