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,4 +1,3 @@
1
- require "active_support/core_ext/array/wrap"
2
1
  require "active_support/core_ext/enumerable"
3
2
 
4
3
  module ActionView
@@ -9,13 +8,16 @@ module ActionView
9
8
  class EncodingError < StandardError #:nodoc:
10
9
  end
11
10
 
11
+ class MissingRequestError < StandardError #:nodoc:
12
+ end
13
+
12
14
  class WrongEncodingError < EncodingError #:nodoc:
13
15
  def initialize(string, encoding)
14
16
  @string, @encoding = string, encoding
15
17
  end
16
18
 
17
19
  def message
18
- @string.force_encoding("BINARY")
20
+ @string.force_encoding(Encoding::ASCII_8BIT)
19
21
  "Your template was not saved as valid #{@encoding}. Please " \
20
22
  "either specify #{@encoding} as the encoding for your template " \
21
23
  "in your text editor, or mark the template with its " \
@@ -30,7 +32,7 @@ module ActionView
30
32
 
31
33
  def initialize(paths, path, prefixes, partial, details, *)
32
34
  @path = path
33
- prefixes = Array.wrap(prefixes)
35
+ prefixes = Array(prefixes)
34
36
  template_type = if partial
35
37
  "partial"
36
38
  elsif path =~ /layouts/i
@@ -56,9 +58,9 @@ module ActionView
56
58
 
57
59
  attr_reader :original_exception, :backtrace
58
60
 
59
- def initialize(template, assigns, original_exception)
61
+ def initialize(template, original_exception)
60
62
  super(original_exception.message)
61
- @template, @assigns, @original_exception = template, assigns.dup, original_exception
63
+ @template, @original_exception = template, original_exception
62
64
  @sub_templates = nil
63
65
  @backtrace = original_exception.backtrace
64
66
  end
@@ -76,7 +78,7 @@ module ActionView
76
78
  end
77
79
  end
78
80
 
79
- def source_extract(indentation = 0)
81
+ def source_extract(indentation = 0, output = :console)
80
82
  return unless num = line_number
81
83
  num = num.to_i
82
84
 
@@ -85,14 +87,10 @@ module ActionView
85
87
  start_on_line = [ num - SOURCE_CODE_RADIUS - 1, 0 ].max
86
88
  end_on_line = [ num + SOURCE_CODE_RADIUS - 1, source_code.length].min
87
89
 
88
- indent = ' ' * indentation
89
- line_counter = start_on_line
90
+ indent = end_on_line.to_s.size + indentation
90
91
  return unless source_code = source_code[start_on_line..end_on_line]
91
92
 
92
- source_code.sum do |line|
93
- line_counter += 1
94
- "#{indent}#{line_counter}: #{line}\n"
95
- end
93
+ formatted_code_for(source_code, start_on_line, indent, output)
96
94
  end
97
95
 
98
96
  def sub_template_of(template_path)
@@ -121,6 +119,18 @@ module ActionView
121
119
  'in '
122
120
  end + file_name
123
121
  end
122
+
123
+ def formatted_code_for(source_code, line_counter, indent, output)
124
+ start_value = (output == :html) ? {} : ""
125
+ source_code.inject(start_value) do |result, line|
126
+ line_counter += 1
127
+ if output == :html
128
+ result.update(line_counter.to_s => "%#{indent}s %s\n" % ["", line])
129
+ else
130
+ result << "%#{indent}s: %s\n" % [line_counter, line]
131
+ end
132
+ end
133
+ end
124
134
  end
125
135
  end
126
136
 
@@ -3,7 +3,7 @@ module ActionView
3
3
  class Builder
4
4
  # Default format used by Builder.
5
5
  class_attribute :default_format
6
- self.default_format = Mime::XML
6
+ self.default_format = :xml
7
7
 
8
8
  def call(template)
9
9
  require_engine
@@ -1,5 +1,4 @@
1
1
  require 'action_dispatch/http/mime_type'
2
- require 'active_support/core_ext/class/attribute'
3
2
  require 'erubis'
4
3
 
5
4
  module ActionView
@@ -7,12 +6,23 @@ module ActionView
7
6
  module Handlers
8
7
  class Erubis < ::Erubis::Eruby
9
8
  def add_preamble(src)
9
+ @newline_pending = 0
10
10
  src << "@output_buffer = output_buffer || ActionView::OutputBuffer.new;"
11
11
  end
12
12
 
13
13
  def add_text(src, text)
14
14
  return if text.empty?
15
- src << "@output_buffer.safe_concat('" << escape_text(text) << "');"
15
+
16
+ if text == "\n"
17
+ @newline_pending += 1
18
+ else
19
+ src << "@output_buffer.safe_append='"
20
+ src << "\n" * @newline_pending if @newline_pending > 0
21
+ src << escape_text(text)
22
+ src << "';"
23
+
24
+ @newline_pending = 0
25
+ end
16
26
  end
17
27
 
18
28
  # Erubis toggles <%= and <%== behavior when escaping is enabled.
@@ -29,24 +39,39 @@ module ActionView
29
39
  BLOCK_EXPR = /\s+(do|\{)(\s*\|[^|]*\|)?\s*\Z/
30
40
 
31
41
  def add_expr_literal(src, code)
42
+ flush_newline_if_pending(src)
32
43
  if code =~ BLOCK_EXPR
33
44
  src << '@output_buffer.append= ' << code
34
45
  else
35
- src << '@output_buffer.append= (' << code << ');'
46
+ src << '@output_buffer.append=(' << code << ');'
36
47
  end
37
48
  end
38
49
 
39
50
  def add_expr_escaped(src, code)
51
+ flush_newline_if_pending(src)
40
52
  if code =~ BLOCK_EXPR
41
53
  src << "@output_buffer.safe_append= " << code
42
54
  else
43
- src << "@output_buffer.safe_concat((" << code << ").to_s);"
55
+ src << "@output_buffer.safe_append=(" << code << ");"
44
56
  end
45
57
  end
46
58
 
59
+ def add_stmt(src, code)
60
+ flush_newline_if_pending(src)
61
+ super
62
+ end
63
+
47
64
  def add_postamble(src)
65
+ flush_newline_if_pending(src)
48
66
  src << '@output_buffer.to_s'
49
67
  end
68
+
69
+ def flush_newline_if_pending(src)
70
+ if @newline_pending > 0
71
+ src << "@output_buffer.safe_append='#{"\n" * @newline_pending}';"
72
+ @newline_pending = 0
73
+ end
74
+ end
50
75
  end
51
76
 
52
77
  class ERB
@@ -78,27 +103,23 @@ module ActionView
78
103
  end
79
104
 
80
105
  def call(template)
81
- if template.source.encoding_aware?
82
- # First, convert to BINARY, so in case the encoding is
83
- # wrong, we can still find an encoding tag
84
- # (<%# encoding %>) inside the String using a regular
85
- # expression
86
- template_source = template.source.dup.force_encoding("BINARY")
106
+ # First, convert to BINARY, so in case the encoding is
107
+ # wrong, we can still find an encoding tag
108
+ # (<%# encoding %>) inside the String using a regular
109
+ # expression
110
+ template_source = template.source.dup.force_encoding(Encoding::ASCII_8BIT)
87
111
 
88
- erb = template_source.gsub(ENCODING_TAG, '')
89
- encoding = $2
112
+ erb = template_source.gsub(ENCODING_TAG, '')
113
+ encoding = $2
90
114
 
91
- erb.force_encoding valid_encoding(template.source.dup, encoding)
115
+ erb.force_encoding valid_encoding(template.source.dup, encoding)
92
116
 
93
- # Always make sure we return a String in the default_internal
94
- erb.encode!
95
- else
96
- erb = template.source.dup
97
- end
117
+ # Always make sure we return a String in the default_internal
118
+ erb.encode!
98
119
 
99
120
  self.class.erb_implementation.new(
100
121
  erb,
101
- :escape => (self.class.escape_whitelist.include? template.mime_type),
122
+ :escape => (self.class.escape_whitelist.include? template.type),
102
123
  :trim => (self.class.erb_trim_mode == "-")
103
124
  ).src
104
125
  end
@@ -0,0 +1,11 @@
1
+ module ActionView
2
+ module Template::Handlers
3
+ class Raw
4
+ def call(template)
5
+ escaped = template.source.gsub(':', '\:')
6
+
7
+ '%q:' + escaped + ':;'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -4,10 +4,13 @@ module ActionView #:nodoc:
4
4
  module Handlers #:nodoc:
5
5
  autoload :ERB, 'action_view/template/handlers/erb'
6
6
  autoload :Builder, 'action_view/template/handlers/builder'
7
+ autoload :Raw, 'action_view/template/handlers/raw'
7
8
 
8
9
  def self.extended(base)
9
10
  base.register_default_template_handler :erb, ERB.new
10
11
  base.register_template_handler :builder, Builder.new
12
+ base.register_template_handler :raw, Raw.new
13
+ base.register_template_handler :ruby, :source.to_proc
11
14
  end
12
15
 
13
16
  @@template_handlers = {}
@@ -17,15 +20,15 @@ module ActionView #:nodoc:
17
20
  @@template_extensions ||= @@template_handlers.keys
18
21
  end
19
22
 
20
- # Register a class that knows how to handle template files with the given
21
- # extension. This can be used to implement new template types.
22
- # The constructor for the class must take the ActiveView::Base instance
23
- # as a parameter, and the class must implement a +render+ method that
24
- # takes the contents of the template to render as well as the Hash of
25
- # local assigns available to the template. The +render+ method ought to
26
- # return the rendered template as a string.
27
- def register_template_handler(extension, klass)
28
- @@template_handlers[extension.to_sym] = klass
23
+ # Register an object that knows how to handle template files with the given
24
+ # extensions. This can be used to implement new template types.
25
+ # The handler must respond to `:call`, which will be passed the template
26
+ # and should return the rendered template as a String.
27
+ def register_template_handler(*extensions, handler)
28
+ raise(ArgumentError, "Extension is required") if extensions.empty?
29
+ extensions.each do |extension|
30
+ @@template_handlers[extension.to_sym] = handler
31
+ end
29
32
  @@template_extensions = nil
30
33
  end
31
34
 
@@ -1,13 +1,15 @@
1
1
  require "pathname"
2
2
  require "active_support/core_ext/class"
3
- require "active_support/core_ext/io"
3
+ require "active_support/core_ext/class/attribute_accessors"
4
4
  require "action_view/template"
5
+ require "thread"
6
+ require "thread_safe"
5
7
 
6
8
  module ActionView
7
9
  # = Action View Resolver
8
10
  class Resolver
9
11
  # Keeps all information about view path and builds virtual path.
10
- class Path < String
12
+ class Path
11
13
  attr_reader :name, :prefix, :partial, :virtual
12
14
  alias_method :partial?, :partial
13
15
 
@@ -19,8 +21,76 @@ module ActionView
19
21
  end
20
22
 
21
23
  def initialize(name, prefix, partial, virtual)
22
- @name, @prefix, @partial = name, prefix, partial
23
- super(virtual)
24
+ @name = name
25
+ @prefix = prefix
26
+ @partial = partial
27
+ @virtual = virtual
28
+ end
29
+
30
+ def to_str
31
+ @virtual
32
+ end
33
+ alias :to_s :to_str
34
+ end
35
+
36
+ # Threadsafe template cache
37
+ class Cache #:nodoc:
38
+ class SmallCache < ThreadSafe::Cache
39
+ def initialize(options = {})
40
+ super(options.merge(:initial_capacity => 2))
41
+ end
42
+ end
43
+
44
+ # preallocate all the default blocks for performance/memory consumption reasons
45
+ PARTIAL_BLOCK = lambda {|cache, partial| cache[partial] = SmallCache.new}
46
+ PREFIX_BLOCK = lambda {|cache, prefix| cache[prefix] = SmallCache.new(&PARTIAL_BLOCK)}
47
+ NAME_BLOCK = lambda {|cache, name| cache[name] = SmallCache.new(&PREFIX_BLOCK)}
48
+ KEY_BLOCK = lambda {|cache, key| cache[key] = SmallCache.new(&NAME_BLOCK)}
49
+
50
+ # usually a majority of template look ups return nothing, use this canonical preallocated array to save memory
51
+ NO_TEMPLATES = [].freeze
52
+
53
+ def initialize
54
+ @data = SmallCache.new(&KEY_BLOCK)
55
+ end
56
+
57
+ # Cache the templates returned by the block
58
+ def cache(key, name, prefix, partial, locals)
59
+ if Resolver.caching?
60
+ @data[key][name][prefix][partial][locals] ||= canonical_no_templates(yield)
61
+ else
62
+ fresh_templates = yield
63
+ cached_templates = @data[key][name][prefix][partial][locals]
64
+
65
+ if templates_have_changed?(cached_templates, fresh_templates)
66
+ @data[key][name][prefix][partial][locals] = canonical_no_templates(fresh_templates)
67
+ else
68
+ cached_templates || NO_TEMPLATES
69
+ end
70
+ end
71
+ end
72
+
73
+ def clear
74
+ @data.clear
75
+ end
76
+
77
+ private
78
+
79
+ def canonical_no_templates(templates)
80
+ templates.empty? ? NO_TEMPLATES : templates
81
+ end
82
+
83
+ def templates_have_changed?(cached_templates, fresh_templates)
84
+ # if either the old or new template list is empty, we don't need to (and can't)
85
+ # compare modification times, and instead just check whether the lists are different
86
+ if cached_templates.blank? || fresh_templates.blank?
87
+ return fresh_templates.blank? != cached_templates.blank?
88
+ end
89
+
90
+ cached_templates_max_updated_at = cached_templates.map(&:updated_at).max
91
+
92
+ # if a template has changed, it will be now be newer than all the cached templates
93
+ fresh_templates.any? { |t| t.updated_at > cached_templates_max_updated_at }
24
94
  end
25
95
  end
26
96
 
@@ -32,15 +102,14 @@ module ActionView
32
102
  end
33
103
 
34
104
  def initialize
35
- @cached = Hash.new { |h1,k1| h1[k1] = Hash.new { |h2,k2|
36
- h2[k2] = Hash.new { |h3,k3| h3[k3] = Hash.new { |h4,k4| h4[k4] = {} } } } }
105
+ @cache = Cache.new
37
106
  end
38
107
 
39
108
  def clear_cache
40
- @cached.clear
109
+ @cache.clear
41
110
  end
42
111
 
43
- # Normalizes the arguments and passes it on to find_template.
112
+ # Normalizes the arguments and passes it on to find_templates.
44
113
  def find_all(name, prefix=nil, partial=false, details={}, key=nil, locals=[])
45
114
  cached(key, [name, prefix, partial], details, locals) do
46
115
  find_templates(name, prefix, partial, details)
@@ -49,7 +118,7 @@ module ActionView
49
118
 
50
119
  private
51
120
 
52
- delegate :caching?, :to => "self.class"
121
+ delegate :caching?, to: :class
53
122
 
54
123
  # This is what child classes implement. No defaults are needed
55
124
  # because Resolver guarantees that the arguments are present and
@@ -65,27 +134,18 @@ module ActionView
65
134
 
66
135
  # Handles templates caching. If a key is given and caching is on
67
136
  # always check the cache before hitting the resolver. Otherwise,
68
- # it always hits the resolver but check if the resolver is fresher
69
- # before returning it.
137
+ # it always hits the resolver but if the key is present, check if the
138
+ # resolver is fresher before returning it.
70
139
  def cached(key, path_info, details, locals) #:nodoc:
71
140
  name, prefix, partial = path_info
72
141
  locals = locals.map { |x| x.to_s }.sort!
73
142
 
74
- if key && caching?
75
- @cached[key][name][prefix][partial][locals] ||= decorate(yield, path_info, details, locals)
76
- else
77
- fresh = decorate(yield, path_info, details, locals)
78
- return fresh unless key
79
-
80
- scope = @cached[key][name][prefix][partial]
81
- cache = scope[locals]
82
- mtime = cache && cache.map(&:updated_at).max
83
-
84
- if !mtime || fresh.empty? || fresh.any? { |t| t.updated_at > mtime }
85
- scope[locals] = fresh
86
- else
87
- cache
143
+ if key
144
+ @cache.cache(key, name, prefix, partial, locals) do
145
+ decorate(yield, path_info, details, locals)
88
146
  end
147
+ else
148
+ decorate(yield, path_info, details, locals)
89
149
  end
90
150
  end
91
151
 
@@ -120,7 +180,13 @@ module ActionView
120
180
  def query(path, details, formats)
121
181
  query = build_query(path, details)
122
182
 
123
- template_paths = find_template_paths query
183
+ # deals with case-insensitive file systems.
184
+ sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
185
+
186
+ template_paths = Dir[query].reject { |filename|
187
+ File.directory?(filename) ||
188
+ !sanitizer[File.dirname(filename)].include?(filename)
189
+ }
124
190
 
125
191
  template_paths.map { |template|
126
192
  handler, format = extract_handler_and_format(template, formats)
@@ -133,26 +199,6 @@ module ActionView
133
199
  }
134
200
  end
135
201
 
136
- if RUBY_VERSION >= '2.2.0'
137
- def find_template_paths(query)
138
- Dir[query].reject { |filename|
139
- File.directory?(filename) ||
140
- # deals with case-insensitive file systems.
141
- !File.fnmatch(query, filename, File::FNM_EXTGLOB)
142
- }
143
- end
144
- else
145
- def find_template_paths(query)
146
- # deals with case-insensitive file systems.
147
- sanitizer = Hash.new { |h,dir| h[dir] = Dir["#{dir}/*"] }
148
-
149
- Dir[query].reject { |filename|
150
- File.directory?(filename) ||
151
- !sanitizer[File.dirname(filename)].include?(filename)
152
- }
153
- end
154
- end
155
-
156
202
  # Helper for building query glob string based on resolver's pattern.
157
203
  def build_query(path, details)
158
204
  query = @pattern.dup
@@ -185,13 +231,21 @@ module ActionView
185
231
  def extract_handler_and_format(path, default_formats)
186
232
  pieces = File.basename(path).split(".")
187
233
  pieces.shift
188
- handler = Template.handler_for_extension(pieces.pop)
189
- format = pieces.last && Mime[pieces.last]
234
+
235
+ extension = pieces.pop
236
+ unless extension
237
+ message = "The file #{path} did not specify a template handler. The default is currently ERB, " \
238
+ "but will change to RAW in the future."
239
+ ActiveSupport::Deprecation.warn message
240
+ end
241
+
242
+ handler = Template.handler_for_extension(extension)
243
+ format = pieces.last && Template::Types[pieces.last]
190
244
  [handler, format]
191
245
  end
192
246
  end
193
247
 
194
- # A resolver that loads files from the filesystem. It allows to set your own
248
+ # A resolver that loads files from the filesystem. It allows setting your own
195
249
  # resolving pattern. Such pattern can be a glob string supported by some variables.
196
250
  #
197
251
  # ==== Examples
@@ -201,13 +255,13 @@ module ActionView
201
255
  #
202
256
  # FileSystemResolver.new("/path/to/views", ":prefix/:action{.:locale,}{.:formats,}{.:handlers,}")
203
257
  #
204
- # This one allows you to keep files with different formats in seperated subdirectories,
258
+ # This one allows you to keep files with different formats in separate subdirectories,
205
259
  # eg. `users/new.html` will be loaded from `users/html/new.erb` or `users/new.html.erb`,
206
260
  # `users/new.js` from `users/js/new.erb` or `users/new.js.erb`, etc.
207
261
  #
208
262
  # FileSystemResolver.new("/path/to/views", ":prefix/{:formats/,}:action{.:locale,}{.:formats,}{.:handlers,}")
209
263
  #
210
- # If you don't specify pattern then the default will be used.
264
+ # If you don't specify a pattern then the default will be used.
211
265
  #
212
266
  # In order to use any of the customized resolvers above in a Rails application, you just need
213
267
  # to configure ActionController::Base.view_paths in an initializer, for example:
@@ -219,10 +273,10 @@ module ActionView
219
273
  #
220
274
  # ==== Pattern format and variables
221
275
  #
222
- # Pattern have to be a valid glob string, and it allows you to use the
276
+ # Pattern has to be a valid glob string, and it allows you to use the
223
277
  # following variables:
224
278
  #
225
- # * <tt>:prefix</tt> - usualy the controller path
279
+ # * <tt>:prefix</tt> - usually the controller path
226
280
  # * <tt>:action</tt> - name of the action
227
281
  # * <tt>:locale</tt> - possible locale versions
228
282
  # * <tt>:formats</tt> - possible request formats (for example html, json, xml...)
@@ -1,13 +1,13 @@
1
1
  module ActionView #:nodoc:
2
2
  # = Action View Text Template
3
3
  class Template
4
- class Text < String #:nodoc:
5
- attr_accessor :mime_type
4
+ class Text #:nodoc:
5
+ attr_accessor :type
6
6
 
7
- def initialize(string, mime_type = nil)
8
- super(string.to_s)
9
- @mime_type = Mime[mime_type] || mime_type if mime_type
10
- @mime_type ||= Mime::TEXT
7
+ def initialize(string, type = nil)
8
+ @string = string.to_s
9
+ @type = Types[type] || type if type
10
+ @type ||= Types[:text]
11
11
  end
12
12
 
13
13
  def identifier
@@ -18,12 +18,16 @@ module ActionView #:nodoc:
18
18
  'text template'
19
19
  end
20
20
 
21
+ def to_str
22
+ @string
23
+ end
24
+
21
25
  def render(*args)
22
- to_s
26
+ to_str
23
27
  end
24
28
 
25
29
  def formats
26
- [@mime_type.respond_to?(:ref) ? @mime_type.ref : @mime_type.to_s]
30
+ [@type.to_sym]
27
31
  end
28
32
  end
29
33
  end
@@ -0,0 +1,57 @@
1
+ require 'set'
2
+ require 'active_support/core_ext/class/attribute_accessors'
3
+
4
+ module ActionView
5
+ class Template
6
+ class Types
7
+ class Type
8
+ cattr_accessor :types
9
+ self.types = Set.new
10
+
11
+ def self.register(*t)
12
+ types.merge(t.map { |type| type.to_s })
13
+ end
14
+
15
+ register :html, :text, :js, :css, :xml, :json
16
+
17
+ def self.[](type)
18
+ return type if type.is_a?(self)
19
+
20
+ if type.is_a?(Symbol) || types.member?(type.to_s)
21
+ new(type)
22
+ end
23
+ end
24
+
25
+ attr_reader :symbol
26
+
27
+ def initialize(symbol)
28
+ @symbol = symbol.to_sym
29
+ end
30
+
31
+ delegate :to_s, :to_sym, :to => :symbol
32
+ alias to_str to_s
33
+
34
+ def ref
35
+ to_sym || to_s
36
+ end
37
+
38
+ def ==(type)
39
+ return false if type.blank?
40
+ symbol.to_sym == type.to_sym
41
+ end
42
+ end
43
+
44
+ cattr_accessor :type_klass
45
+
46
+ def self.delegate_to(klass)
47
+ self.type_klass = klass
48
+ end
49
+
50
+ delegate_to Type
51
+
52
+ def self.[](type)
53
+ type_klass[type]
54
+ end
55
+ end
56
+ end
57
+ end