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
@@ -1,19 +1,28 @@
1
+ require 'active_support/core_ext/module/attribute_accessors'
2
+ require 'active_support/core_ext/hash/slice'
3
+
1
4
  module ActionDispatch
2
5
  module Http
3
6
  module URL
7
+ IP_HOST_REGEXP = /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/
8
+ HOST_REGEXP = /(^.*:\/\/)?([^:]+)(?::(\d+$))?/
9
+ PROTOCOL_REGEXP = /^([^:]+)(:)?(\/\/)?$/
10
+
4
11
  mattr_accessor :tld_length
5
12
  self.tld_length = 1
6
13
 
7
14
  class << self
8
15
  def extract_domain(host, tld_length = @@tld_length)
9
- return nil unless named_host?(host)
10
- host.split('.').last(1 + tld_length).join('.')
16
+ host.split('.').last(1 + tld_length).join('.') if named_host?(host)
11
17
  end
12
18
 
13
19
  def extract_subdomains(host, tld_length = @@tld_length)
14
- return [] unless named_host?(host)
15
- parts = host.split('.')
16
- parts[0..-(tld_length+2)]
20
+ if named_host?(host)
21
+ parts = host.split('.')
22
+ parts[0..-(tld_length + 2)]
23
+ else
24
+ []
25
+ end
17
26
  end
18
27
 
19
28
  def extract_subdomain(host, tld_length = @@tld_length)
@@ -21,42 +30,62 @@ module ActionDispatch
21
30
  end
22
31
 
23
32
  def url_for(options = {})
24
- unless options[:host].present? || options[:only_path].present?
33
+ options = options.dup
34
+ path = options.delete(:script_name).to_s.chomp("/")
35
+ path << options.delete(:path).to_s
36
+
37
+ params = options[:params].is_a?(Hash) ? options[:params] : options.slice(:params)
38
+ params.reject! { |_,v| v.to_param.nil? }
39
+
40
+ result = build_host_url(options)
41
+ if options[:trailing_slash]
42
+ if path.include?('?')
43
+ result << path.sub(/\?/, '/\&')
44
+ else
45
+ result << path.sub(/[^\/]\z|\A\z/, '\&/')
46
+ end
47
+ else
48
+ result << path
49
+ end
50
+ result << "?#{params.to_query}" unless params.empty?
51
+ result << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
52
+ result
53
+ end
54
+
55
+ private
56
+
57
+ def build_host_url(options)
58
+ if options[:host].blank? && options[:only_path].blank?
25
59
  raise ArgumentError, 'Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true'
26
60
  end
27
61
 
28
- rewritten_url = ""
62
+ result = ""
29
63
 
30
64
  unless options[:only_path]
31
- unless options[:protocol] == false
32
- rewritten_url << (options[:protocol] || "http")
33
- rewritten_url << ":" unless rewritten_url.match(%r{:|//})
65
+ if match = options[:host].match(HOST_REGEXP)
66
+ options[:protocol] ||= match[1] unless options[:protocol] == false
67
+ options[:host] = match[2]
68
+ options[:port] = match[3] unless options.key?(:port)
34
69
  end
35
- rewritten_url << "//" unless rewritten_url.match("//")
36
- rewritten_url << rewrite_authentication(options)
37
- rewritten_url << host_or_subdomain_and_domain(options)
38
- rewritten_url << ":#{options.delete(:port)}" if options[:port]
39
- end
40
70
 
41
- path = options.delete(:path) || ''
71
+ options[:protocol] = normalize_protocol(options)
72
+ options[:host] = normalize_host(options)
73
+ options[:port] = normalize_port(options)
42
74
 
43
- params = options[:params] || {}
44
- params.reject! {|k,v| v.to_param.nil? }
45
-
46
- if options[:trailing_slash] && !path.ends_with?('/')
47
- rewritten_url << path.sub(/(\?|\z)/) { "/" + $& }
48
- else
49
- rewritten_url << path
75
+ result << options[:protocol]
76
+ result << rewrite_authentication(options)
77
+ result << options[:host]
78
+ result << ":#{options[:port]}" if options[:port]
50
79
  end
51
- rewritten_url << "?#{params.to_query}" unless params.empty?
52
- rewritten_url << "##{Journey::Router::Utils.escape_fragment(options[:anchor].to_param.to_s)}" if options[:anchor]
53
- rewritten_url
80
+ result
54
81
  end
55
82
 
56
- private
57
-
58
83
  def named_host?(host)
59
- !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host))
84
+ host && IP_HOST_REGEXP !~ host
85
+ end
86
+
87
+ def same_host?(options)
88
+ (options[:subdomain] == true || !options.key?(:subdomain)) && options[:domain].nil?
60
89
  end
61
90
 
62
91
  def rewrite_authentication(options)
@@ -67,19 +96,53 @@ module ActionDispatch
67
96
  end
68
97
  end
69
98
 
70
- def host_or_subdomain_and_domain(options)
71
- return options[:host] if !named_host?(options[:host]) || (options[:subdomain].nil? && options[:domain].nil?)
99
+ def normalize_protocol(options)
100
+ case options[:protocol]
101
+ when nil
102
+ "http://"
103
+ when false, "//"
104
+ "//"
105
+ when PROTOCOL_REGEXP
106
+ "#{$1}://"
107
+ else
108
+ raise ArgumentError, "Invalid :protocol option: #{options[:protocol].inspect}"
109
+ end
110
+ end
111
+
112
+ def normalize_host(options)
113
+ return options[:host] if !named_host?(options[:host]) || same_host?(options)
72
114
 
73
115
  tld_length = options[:tld_length] || @@tld_length
74
116
 
75
117
  host = ""
76
- unless options[:subdomain] == false
77
- host << (options[:subdomain] || extract_subdomain(options[:host], tld_length)).to_param
78
- host << "."
118
+ if options[:subdomain] == true || !options.key?(:subdomain)
119
+ host << extract_subdomain(options[:host], tld_length).to_param
120
+ elsif options[:subdomain].present?
121
+ host << options[:subdomain].to_param
79
122
  end
123
+ host << "." unless host.empty?
80
124
  host << (options[:domain] || extract_domain(options[:host], tld_length))
81
125
  host
82
126
  end
127
+
128
+ def normalize_port(options)
129
+ return nil if options[:port].nil? || options[:port] == false
130
+
131
+ case options[:protocol]
132
+ when "//"
133
+ nil
134
+ when "https://"
135
+ options[:port].to_i == 443 ? nil : options[:port]
136
+ else
137
+ options[:port].to_i == 80 ? nil : options[:port]
138
+ end
139
+ end
140
+ end
141
+
142
+ def initialize(env)
143
+ super
144
+ @protocol = nil
145
+ @port = nil
83
146
  end
84
147
 
85
148
  # Returns the complete URL used for this request.
@@ -171,7 +234,7 @@ module ActionDispatch
171
234
  # such as 2 to catch <tt>"www"</tt> instead of <tt>"www.rubyonrails"</tt>
172
235
  # in "www.rubyonrails.co.uk".
173
236
  def subdomain(tld_length = @@tld_length)
174
- subdomains(tld_length).join(".")
237
+ ActionDispatch::Http::URL.extract_subdomain(host, tld_length)
175
238
  end
176
239
  end
177
240
  end
@@ -0,0 +1,5 @@
1
+ module Rack # :nodoc:
2
+ Mount = ActionDispatch::Journey::Router
3
+ Mount::RouteSet = ActionDispatch::Journey::Router
4
+ Mount::RegexpWithNamedGroups = ActionDispatch::Journey::Path::Pattern
5
+ end
@@ -0,0 +1,146 @@
1
+ require 'action_controller/metal/exceptions'
2
+
3
+ module ActionDispatch
4
+ module Journey
5
+ # The Formatter class is used for formatting URLs. For example, parameters
6
+ # passed to +url_for+ in rails will eventually call Formatter#generate.
7
+ class Formatter # :nodoc:
8
+ attr_reader :routes
9
+
10
+ def initialize(routes)
11
+ @routes = routes
12
+ @cache = nil
13
+ end
14
+
15
+ def generate(type, name, options, recall = {}, parameterize = nil)
16
+ constraints = recall.merge(options)
17
+ missing_keys = []
18
+
19
+ match_route(name, constraints) do |route|
20
+ parameterized_parts = extract_parameterized_parts(route, options, recall, parameterize)
21
+ next if !name && route.requirements.empty? && route.parts.empty?
22
+
23
+ missing_keys = missing_keys(route, parameterized_parts)
24
+ next unless missing_keys.empty?
25
+ params = options.dup.delete_if do |key, _|
26
+ parameterized_parts.key?(key) || route.defaults.key?(key)
27
+ end
28
+
29
+ return [route.format(parameterized_parts), params]
30
+ end
31
+
32
+ message = "No route matches #{constraints.inspect}"
33
+ message << " missing required keys: #{missing_keys.inspect}" if name
34
+
35
+ raise ActionController::UrlGenerationError, message
36
+ end
37
+
38
+ def clear
39
+ @cache = nil
40
+ end
41
+
42
+ private
43
+
44
+ def extract_parameterized_parts(route, options, recall, parameterize = nil)
45
+ parameterized_parts = recall.merge(options)
46
+
47
+ keys_to_keep = route.parts.reverse.drop_while { |part|
48
+ !options.key?(part) || (options[part] || recall[part]).nil?
49
+ } | route.required_parts
50
+
51
+ (parameterized_parts.keys - keys_to_keep).each do |bad_key|
52
+ parameterized_parts.delete(bad_key)
53
+ end
54
+
55
+ if parameterize
56
+ parameterized_parts.each do |k, v|
57
+ parameterized_parts[k] = parameterize.call(k, v)
58
+ end
59
+ end
60
+
61
+ parameterized_parts.keep_if { |_, v| v }
62
+ parameterized_parts
63
+ end
64
+
65
+ def named_routes
66
+ routes.named_routes
67
+ end
68
+
69
+ def match_route(name, options)
70
+ if named_routes.key?(name)
71
+ yield named_routes[name]
72
+ else
73
+ routes = non_recursive(cache, options.to_a)
74
+
75
+ hash = routes.group_by { |_, r| r.score(options) }
76
+
77
+ hash.keys.sort.reverse_each do |score|
78
+ next if score < 0
79
+
80
+ hash[score].sort_by { |i, _| i }.each do |_, route|
81
+ yield route
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ def non_recursive(cache, options)
88
+ routes = []
89
+ stack = [cache]
90
+
91
+ while stack.any?
92
+ c = stack.shift
93
+ routes.concat(c[:___routes]) if c.key?(:___routes)
94
+
95
+ options.each do |pair|
96
+ stack << c[pair] if c.key?(pair)
97
+ end
98
+ end
99
+
100
+ routes
101
+ end
102
+
103
+ # Returns an array populated with missing keys if any are present.
104
+ def missing_keys(route, parts)
105
+ missing_keys = []
106
+ tests = route.path.requirements
107
+ route.required_parts.each { |key|
108
+ if tests.key?(key)
109
+ missing_keys << key unless /\A#{tests[key]}\Z/ === parts[key]
110
+ else
111
+ missing_keys << key unless parts[key]
112
+ end
113
+ }
114
+ missing_keys
115
+ end
116
+
117
+ def possibles(cache, options, depth = 0)
118
+ cache.fetch(:___routes) { [] } + options.find_all { |pair|
119
+ cache.key?(pair)
120
+ }.map { |pair|
121
+ possibles(cache[pair], options, depth + 1)
122
+ }.flatten(1)
123
+ end
124
+
125
+ # Returns +true+ if no missing keys are present, otherwise +false+.
126
+ def verify_required_parts!(route, parts)
127
+ missing_keys(route, parts).empty?
128
+ end
129
+
130
+ def build_cache
131
+ root = { ___routes: [] }
132
+ routes.each_with_index do |route, i|
133
+ leaf = route.required_defaults.inject(root) do |h, tuple|
134
+ h[tuple] ||= {}
135
+ end
136
+ (leaf[:___routes] ||= []) << [i, route]
137
+ end
138
+ root
139
+ end
140
+
141
+ def cache
142
+ @cache ||= build_cache
143
+ end
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,162 @@
1
+ require 'action_dispatch/journey/gtg/transition_table'
2
+
3
+ module ActionDispatch
4
+ module Journey # :nodoc:
5
+ module GTG # :nodoc:
6
+ class Builder # :nodoc:
7
+ DUMMY = Nodes::Dummy.new
8
+
9
+ attr_reader :root, :ast, :endpoints
10
+
11
+ def initialize(root)
12
+ @root = root
13
+ @ast = Nodes::Cat.new root, DUMMY
14
+ @followpos = nil
15
+ end
16
+
17
+ def transition_table
18
+ dtrans = TransitionTable.new
19
+ marked = {}
20
+ state_id = Hash.new { |h,k| h[k] = h.length }
21
+
22
+ start = firstpos(root)
23
+ dstates = [start]
24
+ until dstates.empty?
25
+ s = dstates.shift
26
+ next if marked[s]
27
+ marked[s] = true # mark s
28
+
29
+ s.group_by { |state| symbol(state) }.each do |sym, ps|
30
+ u = ps.map { |l| followpos(l) }.flatten
31
+ next if u.empty?
32
+
33
+ if u.uniq == [DUMMY]
34
+ from = state_id[s]
35
+ to = state_id[Object.new]
36
+ dtrans[from, to] = sym
37
+
38
+ dtrans.add_accepting(to)
39
+ ps.each { |state| dtrans.add_memo(to, state.memo) }
40
+ else
41
+ dtrans[state_id[s], state_id[u]] = sym
42
+
43
+ if u.include?(DUMMY)
44
+ to = state_id[u]
45
+
46
+ accepting = ps.find_all { |l| followpos(l).include?(DUMMY) }
47
+
48
+ accepting.each { |accepting_state|
49
+ dtrans.add_memo(to, accepting_state.memo)
50
+ }
51
+
52
+ dtrans.add_accepting(state_id[u])
53
+ end
54
+ end
55
+
56
+ dstates << u
57
+ end
58
+ end
59
+
60
+ dtrans
61
+ end
62
+
63
+ def nullable?(node)
64
+ case node
65
+ when Nodes::Group
66
+ true
67
+ when Nodes::Star
68
+ true
69
+ when Nodes::Or
70
+ node.children.any? { |c| nullable?(c) }
71
+ when Nodes::Cat
72
+ nullable?(node.left) && nullable?(node.right)
73
+ when Nodes::Terminal
74
+ !node.left
75
+ when Nodes::Unary
76
+ nullable?(node.left)
77
+ else
78
+ raise ArgumentError, 'unknown nullable: %s' % node.class.name
79
+ end
80
+ end
81
+
82
+ def firstpos(node)
83
+ case node
84
+ when Nodes::Star
85
+ firstpos(node.left)
86
+ when Nodes::Cat
87
+ if nullable?(node.left)
88
+ firstpos(node.left) | firstpos(node.right)
89
+ else
90
+ firstpos(node.left)
91
+ end
92
+ when Nodes::Or
93
+ node.children.map { |c| firstpos(c) }.flatten.uniq
94
+ when Nodes::Unary
95
+ firstpos(node.left)
96
+ when Nodes::Terminal
97
+ nullable?(node) ? [] : [node]
98
+ else
99
+ raise ArgumentError, 'unknown firstpos: %s' % node.class.name
100
+ end
101
+ end
102
+
103
+ def lastpos(node)
104
+ case node
105
+ when Nodes::Star
106
+ firstpos(node.left)
107
+ when Nodes::Or
108
+ node.children.map { |c| lastpos(c) }.flatten.uniq
109
+ when Nodes::Cat
110
+ if nullable?(node.right)
111
+ lastpos(node.left) | lastpos(node.right)
112
+ else
113
+ lastpos(node.right)
114
+ end
115
+ when Nodes::Terminal
116
+ nullable?(node) ? [] : [node]
117
+ when Nodes::Unary
118
+ lastpos(node.left)
119
+ else
120
+ raise ArgumentError, 'unknown lastpos: %s' % node.class.name
121
+ end
122
+ end
123
+
124
+ def followpos(node)
125
+ followpos_table[node]
126
+ end
127
+
128
+ private
129
+
130
+ def followpos_table
131
+ @followpos ||= build_followpos
132
+ end
133
+
134
+ def build_followpos
135
+ table = Hash.new { |h, k| h[k] = [] }
136
+ @ast.each do |n|
137
+ case n
138
+ when Nodes::Cat
139
+ lastpos(n.left).each do |i|
140
+ table[i] += firstpos(n.right)
141
+ end
142
+ when Nodes::Star
143
+ lastpos(n).each do |i|
144
+ table[i] += firstpos(n)
145
+ end
146
+ end
147
+ end
148
+ table
149
+ end
150
+
151
+ def symbol(edge)
152
+ case edge
153
+ when Journey::Nodes::Symbol
154
+ edge.regexp
155
+ else
156
+ edge.left
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -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