actionpack 2.0.5 → 2.1.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 (210) hide show
  1. data/CHANGELOG +149 -7
  2. data/MIT-LICENSE +1 -1
  3. data/README +1 -1
  4. data/Rakefile +5 -6
  5. data/lib/action_controller.rb +2 -2
  6. data/lib/action_controller/assertions/model_assertions.rb +2 -1
  7. data/lib/action_controller/assertions/response_assertions.rb +4 -2
  8. data/lib/action_controller/assertions/routing_assertions.rb +3 -3
  9. data/lib/action_controller/assertions/selector_assertions.rb +30 -27
  10. data/lib/action_controller/assertions/tag_assertions.rb +3 -3
  11. data/lib/action_controller/base.rb +103 -129
  12. data/lib/action_controller/benchmarking.rb +3 -3
  13. data/lib/action_controller/caching.rb +41 -652
  14. data/lib/action_controller/caching/actions.rb +144 -0
  15. data/lib/action_controller/caching/fragments.rb +138 -0
  16. data/lib/action_controller/caching/pages.rb +154 -0
  17. data/lib/action_controller/caching/sql_cache.rb +18 -0
  18. data/lib/action_controller/caching/sweeping.rb +97 -0
  19. data/lib/action_controller/cgi_ext/cookie.rb +27 -23
  20. data/lib/action_controller/cgi_ext/stdinput.rb +1 -0
  21. data/lib/action_controller/cgi_process.rb +6 -4
  22. data/lib/action_controller/components.rb +7 -6
  23. data/lib/action_controller/cookies.rb +31 -19
  24. data/lib/action_controller/dispatcher.rb +51 -84
  25. data/lib/action_controller/filters.rb +295 -421
  26. data/lib/action_controller/flash.rb +1 -6
  27. data/lib/action_controller/headers.rb +31 -0
  28. data/lib/action_controller/helpers.rb +26 -9
  29. data/lib/action_controller/http_authentication.rb +1 -1
  30. data/lib/action_controller/integration.rb +65 -13
  31. data/lib/action_controller/layout.rb +24 -39
  32. data/lib/action_controller/mime_responds.rb +7 -3
  33. data/lib/action_controller/mime_type.rb +25 -9
  34. data/lib/action_controller/mime_types.rb +1 -1
  35. data/lib/action_controller/polymorphic_routes.rb +32 -17
  36. data/lib/action_controller/record_identifier.rb +10 -4
  37. data/lib/action_controller/request.rb +46 -30
  38. data/lib/action_controller/request_forgery_protection.rb +10 -9
  39. data/lib/action_controller/request_profiler.rb +29 -8
  40. data/lib/action_controller/rescue.rb +24 -24
  41. data/lib/action_controller/resources.rb +66 -23
  42. data/lib/action_controller/response.rb +2 -2
  43. data/lib/action_controller/routing.rb +113 -1229
  44. data/lib/action_controller/routing/builder.rb +204 -0
  45. data/lib/action_controller/{routing_optimisation.rb → routing/optimisations.rb} +13 -12
  46. data/lib/action_controller/routing/recognition_optimisation.rb +158 -0
  47. data/lib/action_controller/routing/route.rb +240 -0
  48. data/lib/action_controller/routing/route_set.rb +435 -0
  49. data/lib/action_controller/routing/routing_ext.rb +46 -0
  50. data/lib/action_controller/routing/segments.rb +283 -0
  51. data/lib/action_controller/session/active_record_store.rb +13 -8
  52. data/lib/action_controller/session/cookie_store.rb +20 -17
  53. data/lib/action_controller/session_management.rb +10 -3
  54. data/lib/action_controller/streaming.rb +45 -31
  55. data/lib/action_controller/test_case.rb +33 -23
  56. data/lib/action_controller/test_process.rb +39 -35
  57. data/lib/action_controller/url_rewriter.rb +18 -12
  58. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +1 -1
  59. data/lib/action_pack.rb +1 -1
  60. data/lib/action_pack/version.rb +2 -2
  61. data/lib/action_view.rb +11 -3
  62. data/lib/action_view/base.rb +73 -390
  63. data/lib/action_view/helpers/active_record_helper.rb +83 -62
  64. data/lib/action_view/helpers/asset_tag_helper.rb +101 -44
  65. data/lib/action_view/helpers/atom_feed_helper.rb +35 -7
  66. data/lib/action_view/helpers/benchmark_helper.rb +5 -3
  67. data/lib/action_view/helpers/cache_helper.rb +3 -2
  68. data/lib/action_view/helpers/capture_helper.rb +1 -2
  69. data/lib/action_view/helpers/date_helper.rb +104 -82
  70. data/lib/action_view/helpers/form_helper.rb +148 -75
  71. data/lib/action_view/helpers/form_options_helper.rb +44 -23
  72. data/lib/action_view/helpers/form_tag_helper.rb +22 -13
  73. data/lib/action_view/helpers/javascripts/controls.js +1 -1
  74. data/lib/action_view/helpers/javascripts/dragdrop.js +1 -1
  75. data/lib/action_view/helpers/javascripts/effects.js +1 -1
  76. data/lib/action_view/helpers/number_helper.rb +10 -3
  77. data/lib/action_view/helpers/prototype_helper.rb +61 -29
  78. data/lib/action_view/helpers/record_tag_helper.rb +3 -3
  79. data/lib/action_view/helpers/sanitize_helper.rb +23 -17
  80. data/lib/action_view/helpers/scriptaculous_helper.rb +86 -60
  81. data/lib/action_view/helpers/text_helper.rb +153 -125
  82. data/lib/action_view/helpers/url_helper.rb +83 -28
  83. data/lib/action_view/inline_template.rb +20 -0
  84. data/lib/action_view/partial_template.rb +70 -0
  85. data/lib/action_view/partials.rb +31 -73
  86. data/lib/action_view/template.rb +127 -0
  87. data/lib/action_view/template_error.rb +8 -7
  88. data/lib/action_view/template_finder.rb +177 -0
  89. data/lib/action_view/template_handler.rb +18 -1
  90. data/lib/action_view/template_handlers/builder.rb +10 -2
  91. data/lib/action_view/template_handlers/compilable.rb +128 -0
  92. data/lib/action_view/template_handlers/erb.rb +37 -2
  93. data/lib/action_view/template_handlers/rjs.rb +14 -1
  94. data/lib/action_view/test_case.rb +58 -0
  95. data/test/abstract_unit.rb +1 -1
  96. data/test/active_record_unit.rb +3 -6
  97. data/test/activerecord/active_record_store_test.rb +1 -2
  98. data/test/activerecord/render_partial_with_record_identification_test.rb +158 -41
  99. data/test/adv_attr_test.rb +20 -0
  100. data/test/controller/action_pack_assertions_test.rb +16 -19
  101. data/test/controller/addresses_render_test.rb +1 -1
  102. data/test/controller/assert_select_test.rb +13 -6
  103. data/test/controller/base_test.rb +48 -2
  104. data/test/controller/benchmark_test.rb +1 -2
  105. data/test/controller/caching_test.rb +282 -21
  106. data/test/controller/capture_test.rb +1 -1
  107. data/test/controller/cgi_test.rb +1 -1
  108. data/test/controller/components_test.rb +1 -1
  109. data/test/controller/content_type_test.rb +2 -2
  110. data/test/controller/cookie_test.rb +13 -2
  111. data/test/controller/custom_handler_test.rb +14 -10
  112. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -1
  113. data/test/controller/dispatcher_test.rb +31 -49
  114. data/test/controller/fake_controllers.rb +17 -0
  115. data/test/controller/fake_models.rb +6 -0
  116. data/test/controller/filter_params_test.rb +14 -8
  117. data/test/controller/filters_test.rb +44 -16
  118. data/test/controller/flash_test.rb +2 -2
  119. data/test/controller/header_test.rb +14 -0
  120. data/test/controller/helper_test.rb +19 -15
  121. data/test/controller/html-scanner/document_test.rb +1 -2
  122. data/test/controller/html-scanner/node_test.rb +1 -2
  123. data/test/controller/html-scanner/sanitizer_test.rb +8 -5
  124. data/test/controller/html-scanner/tag_node_test.rb +1 -2
  125. data/test/controller/html-scanner/text_node_test.rb +2 -3
  126. data/test/controller/html-scanner/tokenizer_test.rb +8 -2
  127. data/test/controller/http_authentication_test.rb +1 -1
  128. data/test/controller/integration_test.rb +14 -16
  129. data/test/controller/integration_upload_test.rb +43 -0
  130. data/test/controller/layout_test.rb +26 -6
  131. data/test/controller/mime_responds_test.rb +39 -7
  132. data/test/controller/mime_type_test.rb +29 -5
  133. data/test/controller/new_render_test.rb +105 -34
  134. data/test/controller/polymorphic_routes_test.rb +32 -20
  135. data/test/controller/record_identifier_test.rb +38 -2
  136. data/test/controller/redirect_test.rb +21 -1
  137. data/test/controller/render_test.rb +59 -15
  138. data/test/controller/request_forgery_protection_test.rb +92 -5
  139. data/test/controller/request_test.rb +64 -6
  140. data/test/controller/rescue_test.rb +22 -6
  141. data/test/controller/resources_test.rb +102 -14
  142. data/test/controller/routing_test.rb +231 -19
  143. data/test/controller/selector_test.rb +2 -2
  144. data/test/controller/send_file_test.rb +14 -3
  145. data/test/controller/session/cookie_store_test.rb +16 -4
  146. data/test/controller/session/mem_cache_store_test.rb +3 -4
  147. data/test/controller/session_fixation_test.rb +1 -1
  148. data/test/controller/session_management_test.rb +23 -1
  149. data/test/controller/test_test.rb +39 -18
  150. data/test/controller/url_rewriter_test.rb +35 -1
  151. data/test/controller/verification_test.rb +1 -1
  152. data/test/controller/view_paths_test.rb +15 -12
  153. data/test/controller/webservice_test.rb +48 -3
  154. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  155. data/test/fixtures/company.rb +1 -0
  156. data/test/fixtures/customers/_customer.html.erb +1 -0
  157. data/test/fixtures/db_definitions/sqlite.sql +6 -0
  158. data/test/fixtures/functional_caching/_partial.erb +3 -0
  159. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  160. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  161. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  162. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  163. data/test/fixtures/mascot.rb +3 -0
  164. data/test/fixtures/mascots.yml +4 -0
  165. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  166. data/test/fixtures/multipart/boundary_problem_file +10 -0
  167. data/test/fixtures/public/javascripts/application.js +1 -0
  168. data/test/fixtures/public/javascripts/controls.js +1 -0
  169. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  170. data/test/fixtures/public/javascripts/effects.js +1 -0
  171. data/test/fixtures/public/javascripts/prototype.js +1 -0
  172. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  173. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  174. data/test/fixtures/reply.rb +1 -0
  175. data/test/fixtures/shared.html.erb +1 -0
  176. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  177. data/test/fixtures/test/_customer_counter.erb +1 -0
  178. data/test/fixtures/test/_form.erb +1 -0
  179. data/test/fixtures/test/_labelling_form.erb +1 -0
  180. data/test/fixtures/test/_raise.html.erb +1 -0
  181. data/test/fixtures/test/greeting.js.rjs +1 -0
  182. data/test/fixtures/topics/_topic.html.erb +1 -0
  183. data/test/template/active_record_helper_test.rb +25 -8
  184. data/test/template/asset_tag_helper_test.rb +100 -17
  185. data/test/template/atom_feed_helper_test.rb +29 -1
  186. data/test/template/benchmark_helper_test.rb +10 -22
  187. data/test/template/date_helper_test.rb +455 -153
  188. data/test/template/erb_util_test.rb +10 -42
  189. data/test/template/form_helper_test.rb +192 -66
  190. data/test/template/form_options_helper_test.rb +19 -8
  191. data/test/template/form_tag_helper_test.rb +11 -8
  192. data/test/template/javascript_helper_test.rb +3 -9
  193. data/test/template/number_helper_test.rb +6 -3
  194. data/test/template/prototype_helper_test.rb +27 -40
  195. data/test/template/record_tag_helper_test.rb +54 -0
  196. data/test/template/sanitize_helper_test.rb +5 -6
  197. data/test/template/scriptaculous_helper_test.rb +7 -13
  198. data/test/template/tag_helper_test.rb +3 -6
  199. data/test/template/template_finder_test.rb +73 -0
  200. data/test/template/template_object_test.rb +95 -0
  201. data/test/template/test_test.rb +56 -0
  202. data/test/template/text_helper_test.rb +46 -33
  203. data/test/template/url_helper_test.rb +8 -10
  204. metadata +65 -12
  205. data/lib/action_view/compiled_templates.rb +0 -69
  206. data/test/action_view_test.rb +0 -44
  207. data/test/activerecord/fixtures_test.rb +0 -24
  208. data/test/controller/fragment_store_setting_test.rb +0 -47
  209. data/test/template/compiled_templates_test.rb +0 -197
  210. data/test/template/deprecate_ivars_test.rb +0 -51
@@ -6,10 +6,10 @@ module ActionView
6
6
 
7
7
  attr_reader :original_exception
8
8
 
9
- def initialize(base_path, file_path, assigns, source, original_exception)
10
- @base_path, @assigns, @source, @original_exception =
11
- base_path, assigns.dup, source, original_exception
12
- @file_path = file_path
9
+ def initialize(template, assigns, original_exception)
10
+ @base_path = template.base_path_for_exception
11
+ @assigns, @source, @original_exception = assigns.dup, template.source, original_exception
12
+ @file_path = template.filename
13
13
  @backtrace = compute_backtrace
14
14
  end
15
15
 
@@ -41,8 +41,9 @@ module ActionView
41
41
 
42
42
  indent = ' ' * indentation
43
43
  line_counter = start_on_line
44
-
45
- source_code[start_on_line..end_on_line].sum do |line|
44
+ return unless source_code = source_code[start_on_line..end_on_line]
45
+
46
+ source_code.sum do |line|
46
47
  line_counter += 1
47
48
  "#{indent}#{line_counter}: #{line}"
48
49
  end
@@ -73,7 +74,7 @@ module ActionView
73
74
  "#{source_extract}\n #{clean_backtrace.join("\n ")}\n\n"
74
75
  end
75
76
 
76
- # don't do anything nontrivial here. Any raised exception from here becomes fatal
77
+ # don't do anything nontrivial here. Any raised exception from here becomes fatal
77
78
  # (and can't be rescued).
78
79
  def backtrace
79
80
  @backtrace
@@ -0,0 +1,177 @@
1
+ module ActionView #:nodoc:
2
+ class TemplateFinder #:nodoc:
3
+
4
+ class InvalidViewPath < StandardError #:nodoc:
5
+ attr_reader :unprocessed_path
6
+ def initialize(path)
7
+ @unprocessed_path = path
8
+ super("Unprocessed view path found: #{@unprocessed_path.inspect}. Set your view paths with #append_view_path, #prepend_view_path, or #view_paths=.")
9
+ end
10
+ end
11
+
12
+ cattr_reader :processed_view_paths
13
+ @@processed_view_paths = Hash.new {|hash, key| hash[key] = []}
14
+
15
+ cattr_reader :file_extension_cache
16
+ @@file_extension_cache = Hash.new {|hash, key|
17
+ hash[key] = Hash.new {|hash, key| hash[key] = []}
18
+ }
19
+
20
+ class << self #:nodoc:
21
+
22
+ # This method is not thread safe. Mutex should be used whenever this is accessed from an instance method
23
+ def process_view_paths(*view_paths)
24
+ view_paths.flatten.compact.each do |dir|
25
+ next if @@processed_view_paths.has_key?(dir)
26
+ @@processed_view_paths[dir] = []
27
+
28
+ #
29
+ # Dir.glob("#{dir}/**/*/**") reads all the directories in view path and templates inside those directories
30
+ # Dir.glob("#{dir}/**") reads templates residing at top level of view path
31
+ #
32
+ (Dir.glob("#{dir}/**/*/**") | Dir.glob("#{dir}/**")).each do |file|
33
+ unless File.directory?(file)
34
+ @@processed_view_paths[dir] << file.split(dir).last.sub(/^\//, '')
35
+
36
+ # Build extension cache
37
+ extension = file.split(".").last
38
+ if template_handler_extensions.include?(extension)
39
+ key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '')
40
+ @@file_extension_cache[dir][key] << extension
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def update_extension_cache_for(extension)
48
+ @@processed_view_paths.keys.each do |dir|
49
+ Dir.glob("#{dir}/**/*.#{extension}").each do |file|
50
+ key = file.split(dir).last.sub(/^\//, '').sub(/\.(\w+)$/, '')
51
+ @@file_extension_cache[dir][key] << extension
52
+ end
53
+ end
54
+ end
55
+
56
+ def template_handler_extensions
57
+ ActionView::Template.template_handler_extensions
58
+ end
59
+
60
+ def reload!
61
+ view_paths = @@processed_view_paths.keys
62
+
63
+ @@processed_view_paths = Hash.new {|hash, key| hash[key] = []}
64
+ @@file_extension_cache = Hash.new {|hash, key|
65
+ hash[key] = Hash.new {|hash, key| hash[key] = []}
66
+ }
67
+
68
+ process_view_paths(view_paths)
69
+ end
70
+ end
71
+
72
+ attr_accessor :view_paths
73
+
74
+ def initialize(*args)
75
+ @template = args.shift
76
+
77
+ @view_paths = args.flatten
78
+ @view_paths = @view_paths.respond_to?(:find) ? @view_paths.dup : [*@view_paths].compact
79
+ check_view_paths(@view_paths)
80
+ end
81
+
82
+ def prepend_view_path(path)
83
+ @view_paths.unshift(*path)
84
+
85
+ self.class.process_view_paths(path)
86
+ end
87
+
88
+ def append_view_path(path)
89
+ @view_paths.push(*path)
90
+
91
+ self.class.process_view_paths(path)
92
+ end
93
+
94
+ def view_paths=(path)
95
+ @view_paths = path
96
+ self.class.process_view_paths(path)
97
+ end
98
+
99
+ def pick_template(template_path, extension)
100
+ file_name = "#{template_path}.#{extension}"
101
+ base_path = find_base_path_for(file_name)
102
+ base_path.blank? ? false : "#{base_path}/#{file_name}"
103
+ end
104
+ alias_method :template_exists?, :pick_template
105
+
106
+ def file_exists?(template_path)
107
+ # Clear the forward slash in the beginning if exists
108
+ template_path = template_path.sub(/^\//, '')
109
+
110
+ template_file_name, template_file_extension = path_and_extension(template_path)
111
+
112
+ if template_file_extension
113
+ template_exists?(template_file_name, template_file_extension)
114
+ else
115
+ template_exists?(template_file_name, pick_template_extension(template_path))
116
+ end
117
+ end
118
+
119
+ def find_base_path_for(template_file_name)
120
+ @view_paths.find { |path| @@processed_view_paths[path].include?(template_file_name) }
121
+ end
122
+
123
+ # Returns the view path that the full path resides in.
124
+ def extract_base_path_from(full_path)
125
+ @view_paths.find { |p| full_path[0..p.size - 1] == p }
126
+ end
127
+
128
+ # Gets the extension for an existing template with the given template_path.
129
+ # Returns the format with the extension if that template exists.
130
+ #
131
+ # pick_template_extension('users/show')
132
+ # # => 'html.erb'
133
+ #
134
+ # pick_template_extension('users/legacy')
135
+ # # => "rhtml"
136
+ #
137
+ def pick_template_extension(template_path)
138
+ if extension = find_template_extension_from_handler(template_path, @template.template_format) || find_template_extension_from_first_render
139
+ extension
140
+ elsif @template.template_format == :js && extension = find_template_extension_from_handler(template_path, :html)
141
+ @template.template_format = :html
142
+ extension
143
+ end
144
+ end
145
+
146
+ def find_template_extension_from_handler(template_path, template_format = @template.template_format)
147
+ formatted_template_path = "#{template_path}.#{template_format}"
148
+
149
+ view_paths.each do |path|
150
+ if (extensions = @@file_extension_cache[path][formatted_template_path]).any?
151
+ return "#{template_format}.#{extensions.first}"
152
+ elsif (extensions = @@file_extension_cache[path][template_path]).any?
153
+ return extensions.first.to_s
154
+ end
155
+ end
156
+ nil
157
+ end
158
+
159
+ # Splits the path and extension from the given template_path and returns as an array.
160
+ def path_and_extension(template_path)
161
+ template_path_without_extension = template_path.sub(/\.(\w+)$/, '')
162
+ [ template_path_without_extension, $1 ]
163
+ end
164
+
165
+ # Determine the template extension from the <tt>@first_render</tt> filename
166
+ def find_template_extension_from_first_render
167
+ File.basename(@template.first_render.to_s)[/^[^.]+\.(.+)$/, 1]
168
+ end
169
+
170
+ private
171
+ def check_view_paths(view_paths)
172
+ view_paths.each do |path|
173
+ raise InvalidViewPath.new(path) unless @@processed_view_paths.has_key?(path)
174
+ end
175
+ end
176
+ end
177
+ end
@@ -1,17 +1,34 @@
1
1
  module ActionView
2
2
  class TemplateHandler
3
+
3
4
  def self.line_offset
4
5
  0
5
6
  end
6
7
 
8
+ def self.compilable?
9
+ false
10
+ end
11
+
7
12
  def initialize(view)
8
13
  @view = view
9
14
  end
10
15
 
11
- def render(template, local_assigns)
16
+ def render(template)
12
17
  end
13
18
 
14
19
  def compile(template)
15
20
  end
21
+
22
+ def compilable?
23
+ self.class.compilable?
24
+ end
25
+
26
+ def line_offset
27
+ self.class.line_offset
28
+ end
29
+
30
+ # Called by CacheHelper#cache
31
+ def cache_fragment(block, name = {}, options = nil)
32
+ end
16
33
  end
17
34
  end
@@ -3,6 +3,8 @@ require 'builder'
3
3
  module ActionView
4
4
  module TemplateHandlers
5
5
  class Builder < TemplateHandler
6
+ include Compilable
7
+
6
8
  def self.line_offset
7
9
  2
8
10
  end
@@ -10,10 +12,16 @@ module ActionView
10
12
  def compile(template)
11
13
  content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller")
12
14
  "#{content_type_handler}.content_type ||= Mime::XML\n" +
13
- "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
14
- template +
15
+ "xml = ::Builder::XmlMarkup.new(:indent => 2)\n" +
16
+ template.source +
15
17
  "\nxml.target!\n"
16
18
  end
19
+
20
+ def cache_fragment(block, name = {}, options = nil)
21
+ @view.fragment_for(block, name, options) do
22
+ eval('xml.target!', block.binding)
23
+ end
24
+ end
17
25
  end
18
26
  end
19
27
  end
@@ -0,0 +1,128 @@
1
+ module ActionView
2
+ module TemplateHandlers
3
+ module Compilable
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethod
7
+
8
+ # Map method names to their compile time
9
+ base.cattr_accessor :compile_time
10
+ base.compile_time = {}
11
+
12
+ # Map method names to the names passed in local assigns so far
13
+ base.cattr_accessor :template_args
14
+ base.template_args = {}
15
+
16
+ # Count the number of inline templates
17
+ base.cattr_accessor :inline_template_count
18
+ base.inline_template_count = 0
19
+ end
20
+
21
+ module ClassMethod
22
+ # If a handler is mixin this module, set compilable to true
23
+ def compilable?
24
+ true
25
+ end
26
+ end
27
+
28
+ def render(template)
29
+ @view.send :execute, template
30
+ end
31
+
32
+ # Compile and evaluate the template's code
33
+ def compile_template(template)
34
+ return unless compile_template?(template)
35
+
36
+ render_symbol = assign_method_name(template)
37
+ render_source = create_template_source(template, render_symbol)
38
+ line_offset = self.template_args[render_symbol].size + self.line_offset
39
+
40
+ begin
41
+ file_name = template.filename || 'compiled-template'
42
+ ActionView::Base::CompiledTemplates.module_eval(render_source, file_name, -line_offset)
43
+ rescue Exception => e # errors from template code
44
+ if @view.logger
45
+ @view.logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
46
+ @view.logger.debug "Function body: #{render_source}"
47
+ @view.logger.debug "Backtrace: #{e.backtrace.join("\n")}"
48
+ end
49
+
50
+ raise ActionView::TemplateError.new(template, @view.assigns, e)
51
+ end
52
+
53
+ self.compile_time[render_symbol] = Time.now
54
+ # logger.debug "Compiled template #{file_name || template}\n ==> #{render_symbol}" if logger
55
+ end
56
+
57
+ private
58
+
59
+ # Method to check whether template compilation is necessary.
60
+ # The template will be compiled if the inline template or file has not been compiled yet,
61
+ # if local_assigns has a new key, which isn't supported by the compiled code yet,
62
+ # or if the file has changed on disk and checking file mods hasn't been disabled.
63
+ def compile_template?(template)
64
+ method_key = template.method_key
65
+ render_symbol = @view.method_names[method_key]
66
+
67
+ compile_time = self.compile_time[render_symbol]
68
+ if compile_time && supports_local_assigns?(render_symbol, template.locals)
69
+ if template.filename && !@view.cache_template_loading
70
+ template_changed_since?(template.filename, compile_time)
71
+ end
72
+ else
73
+ true
74
+ end
75
+ end
76
+
77
+ def assign_method_name(template)
78
+ @view.method_names[template.method_key] ||= compiled_method_name(template)
79
+ end
80
+
81
+ def compiled_method_name(template)
82
+ ['_run', self.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(template.filename)].compact.join('_').to_sym
83
+ end
84
+
85
+ def compiled_method_name_file_path_segment(file_name)
86
+ if file_name
87
+ s = File.expand_path(file_name)
88
+ s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT)
89
+ s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord }
90
+ s
91
+ else
92
+ (self.inline_template_count += 1).to_s
93
+ end
94
+ end
95
+
96
+ # Method to create the source code for a given template.
97
+ def create_template_source(template, render_symbol)
98
+ body = compile(template)
99
+
100
+ self.template_args[render_symbol] ||= {}
101
+ locals_keys = self.template_args[render_symbol].keys | template.locals.keys
102
+ self.template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h }
103
+
104
+ locals_code = ""
105
+ locals_keys.each do |key|
106
+ locals_code << "#{key} = local_assigns[:#{key}]\n"
107
+ end
108
+
109
+ "def #{render_symbol}(local_assigns)\n#{locals_code}#{body}\nend"
110
+ end
111
+
112
+ # Return true if the given template was compiled for a superset of the keys in local_assigns
113
+ def supports_local_assigns?(render_symbol, local_assigns)
114
+ local_assigns.empty? ||
115
+ ((args = self.template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) })
116
+ end
117
+
118
+ # Method to handle checking a whether a template has changed since last compile; isolated so that templates
119
+ # not stored on the file system can hook and extend appropriately.
120
+ def template_changed_since?(file_name, compile_time)
121
+ lstat = File.lstat(file_name)
122
+ compile_time < lstat.mtime ||
123
+ (lstat.symlink? && compile_time < File.stat(file_name).mtime)
124
+ end
125
+
126
+ end
127
+ end
128
+ end
@@ -2,19 +2,54 @@ require 'erb'
2
2
 
3
3
  class ERB
4
4
  module Util
5
- HTML_ESCAPE = { '&' => '&amp;', '"' => '&quot;', '>' => '&gt;', '<' => '&lt;' }
5
+ HTML_ESCAPE = { '&' => '&amp;', '>' => '&gt;', '<' => '&lt;', '"' => '&quot;' }
6
+ JSON_ESCAPE = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C' }
6
7
 
8
+ # A utility method for escaping HTML tag characters.
9
+ # This method is also aliased as <tt>h</tt>.
10
+ #
11
+ # In your ERb templates, use this method to escape any unsafe content. For example:
12
+ # <%=h @person.name %>
13
+ #
14
+ # ==== Example:
15
+ # puts html_escape("is a > 0 & a < 10?")
16
+ # # => is a &gt; 0 &amp; a &lt; 10?
7
17
  def html_escape(s)
8
18
  s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
9
19
  end
20
+
21
+ # A utility method for escaping HTML entities in JSON strings.
22
+ # This method is also aliased as <tt>j</tt>.
23
+ #
24
+ # In your ERb templates, use this method to escape any HTML entities:
25
+ # <%=j @person.to_json %>
26
+ #
27
+ # ==== Example:
28
+ # puts json_escape("is a > 0 & a < 10?")
29
+ # # => is a \u003E 0 \u0026 a \u003C 10?
30
+ def json_escape(s)
31
+ s.to_s.gsub(/[&"><]/) { |special| JSON_ESCAPE[special] }
32
+ end
33
+
34
+ alias j json_escape
35
+ module_function :j
36
+ module_function :json_escape
10
37
  end
11
38
  end
12
39
 
13
40
  module ActionView
14
41
  module TemplateHandlers
15
42
  class ERB < TemplateHandler
43
+ include Compilable
44
+
16
45
  def compile(template)
17
- ::ERB.new(template, nil, @view.erb_trim_mode).src
46
+ ::ERB.new(template.source, nil, @view.erb_trim_mode).src
47
+ end
48
+
49
+ def cache_fragment(block, name = {}, options = nil) #:nodoc:
50
+ @view.fragment_for(block, name, options) do
51
+ eval(ActionView::Base.erb_variable, block.binding)
52
+ end
18
53
  end
19
54
  end
20
55
  end