actionpack 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (40) hide show
  1. data/CHANGELOG +59 -26
  2. data/Rakefile +1 -1
  3. data/lib/action_controller/base.rb +6 -16
  4. data/lib/action_controller/benchmarking.rb +5 -4
  5. data/lib/action_controller/caching.rb +2 -2
  6. data/lib/action_controller/cgi_ext/cookie.rb +1 -5
  7. data/lib/action_controller/cookies.rb +3 -3
  8. data/lib/action_controller/dispatcher.rb +1 -1
  9. data/lib/action_controller/helpers.rb +2 -3
  10. data/lib/action_controller/integration.rb +29 -14
  11. data/lib/action_controller/layout.rb +2 -1
  12. data/lib/action_controller/request.rb +1 -1
  13. data/lib/action_controller/request_forgery_protection.rb +7 -1
  14. data/lib/action_controller/rescue.rb +1 -1
  15. data/lib/action_controller/routing.rb +12 -9
  16. data/lib/action_controller/session/cookie_store.rb +3 -0
  17. data/lib/action_pack/version.rb +1 -1
  18. data/lib/action_view.rb +6 -0
  19. data/lib/action_view/base.rb +83 -140
  20. data/lib/action_view/helpers/asset_tag_helper.rb +68 -46
  21. data/lib/action_view/helpers/text_helper.rb +2 -2
  22. data/lib/action_view/template_handler.rb +17 -0
  23. data/lib/action_view/template_handlers/builder.rb +19 -0
  24. data/lib/action_view/template_handlers/erb.rb +21 -0
  25. data/lib/action_view/template_handlers/rjs.rb +14 -0
  26. data/test/action_view_test.rb +18 -0
  27. data/test/controller/cgi_test.rb +4 -4
  28. data/test/controller/cookie_test.rb +1 -1
  29. data/test/controller/filters_test.rb +1 -0
  30. data/test/controller/helper_test.rb +8 -6
  31. data/test/controller/integration_test.rb +41 -11
  32. data/test/controller/new_render_test.rb +2 -2
  33. data/test/controller/render_test.rb +31 -10
  34. data/test/controller/request_test.rb +2 -1
  35. data/test/controller/routing_test.rb +9 -3
  36. data/test/fixtures/test/{hello_world.builder → hello_world_from_rxml.builder} +0 -0
  37. data/test/template/asset_tag_helper_test.rb +45 -12
  38. data/test/template/compiled_templates_test.rb +14 -12
  39. data/test/template/erb_util_test.rb +56 -0
  40. metadata +56 -43
@@ -31,7 +31,7 @@ module ActionView
31
31
  # stylesheet_include_tag("application")
32
32
  # => <link href="http://assets3.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
33
33
  #
34
- # To do this, you can either setup four actual hosts, or you can use wildcard DNS to CNAME
34
+ # To do this, you can either setup 4 actual hosts, or you can use wildcard DNS to CNAME
35
35
  # the wildcard to a single asset host. You can read more about setting up your DNS CNAME records from
36
36
  # your ISP.
37
37
  #
@@ -39,6 +39,32 @@ module ActionView
39
39
  # for server load balancing. See http://www.die.net/musings/page_load_time/
40
40
  # for background.
41
41
  #
42
+ # Alternatively, you can exert more control over the asset host by setting <tt>asset_host</tt> to a proc
43
+ # that takes a single source argument. This is useful if you are unable to setup 4 actual hosts or have
44
+ # fewer/more than 4 hosts. The example proc below generates http://assets1.example.com and
45
+ # http://assets2.example.com randomly.
46
+ #
47
+ # ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" }
48
+ # image_tag("rails.png")
49
+ # => <img src="http://assets2.example.com/images/rails.png" alt="Rails" />
50
+ # stylesheet_include_tag("application")
51
+ # => <link href="http://assets1.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
52
+ #
53
+ # The proc takes a single <tt>source</tt> parameter which is the path of the source asset. This can be used to
54
+ # generate a particular asset host depending on the asset path.
55
+ #
56
+ # ActionController::Base.asset_host = Proc.new { |source|
57
+ # if source.starts_with?('/images')
58
+ # "http://images.example.com"
59
+ # else
60
+ # "http://assets.example.com"
61
+ # end
62
+ # }
63
+ # image_tag("rails.png")
64
+ # => <img src="http://images.example.com/images/rails.png" alt="Rails" />
65
+ # stylesheet_include_tag("application")
66
+ # => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
67
+ #
42
68
  # === Using asset timestamps
43
69
  #
44
70
  # By default, Rails will append all asset paths with that asset's timestamp. This allows you to set a cache-expiration date for the
@@ -201,23 +227,10 @@ module ActionView
201
227
  joined_javascript_name = (cache == true ? "all" : cache) + ".js"
202
228
  joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name)
203
229
 
204
- if !file_exist?(joined_javascript_path)
205
- File.open(joined_javascript_path, "w+") do |cache|
206
- javascript_paths = expand_javascript_sources(sources).collect do |source|
207
- compute_public_path(source, 'javascripts', 'js', false)
208
- end
209
-
210
- cache.write(join_asset_file_contents(javascript_paths))
211
- end
212
- end
213
-
214
- content_tag("script", "", {
215
- "type" => Mime::JS, "src" => path_to_javascript(joined_javascript_name)
216
- }.merge(options))
230
+ write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources))
231
+ javascript_src_tag(joined_javascript_name, options)
217
232
  else
218
- expand_javascript_sources(sources).collect do |source|
219
- content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
220
- end.join("\n")
233
+ expand_javascript_sources(sources).collect { |source| javascript_src_tag(source, options) }.join("\n")
221
234
  end
222
235
  end
223
236
 
@@ -311,28 +324,10 @@ module ActionView
311
324
  joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"
312
325
  joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name)
313
326
 
314
- if !file_exist?(joined_stylesheet_path)
315
- File.open(joined_stylesheet_path, "w+") do |cache|
316
- stylesheet_paths = expand_stylesheet_sources(sources).collect do |source|
317
- compute_public_path(source, 'stylesheets', 'css', false)
318
- end
319
-
320
- cache.write(join_asset_file_contents(stylesheet_paths))
321
- end
322
- end
323
-
324
- tag("link", {
325
- "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen",
326
- "href" => html_escape(path_to_stylesheet(joined_stylesheet_name))
327
- }.merge(options), false, false)
327
+ write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources))
328
+ stylesheet_tag(joined_stylesheet_name, options)
328
329
  else
329
- options.delete("cache")
330
-
331
- expand_stylesheet_sources(sources).collect do |source|
332
- tag("link", {
333
- "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source))
334
- }.merge(options), false, false)
335
- end.join("\n")
330
+ expand_stylesheet_sources(sources).collect { |source| stylesheet_tag(source, options) }.join("\n")
336
331
  end
337
332
  end
338
333
 
@@ -416,19 +411,18 @@ module ActionView
416
411
  # Add the .ext if not present. Return full URLs otherwise untouched.
417
412
  # Prefix with /dir/ if lacking a leading /. Account for relative URL
418
413
  # roots. Rewrite the asset path for cache-busting asset ids. Include
419
- # a single or wildcarded asset host, if configured, with the correct
420
- # request protocol.
414
+ # asset host, if configured, with the correct request protocol.
421
415
  def compute_public_path(source, dir, ext = nil, include_host = true)
422
416
  has_request = @controller.respond_to?(:request)
423
417
 
424
418
  cache_key =
425
419
  if has_request
426
420
  [ @controller.request.protocol,
427
- ActionController::Base.asset_host,
421
+ ActionController::Base.asset_host.to_s,
428
422
  @controller.request.relative_url_root,
429
423
  dir, source, ext, include_host ].join
430
424
  else
431
- [ ActionController::Base.asset_host,
425
+ [ ActionController::Base.asset_host.to_s,
432
426
  dir, source, ext, include_host ].join
433
427
  end
434
428
 
@@ -461,11 +455,16 @@ module ActionView
461
455
  end
462
456
 
463
457
  # Pick an asset host for this source. Returns nil if no host is set,
464
- # the host if no wildcard is set, or the host interpolated with the
465
- # numbers 0-3 if it contains %d. The number is the source hash mod 4.
458
+ # the host if no wildcard is set, the host interpolated with the
459
+ # numbers 0-3 if it contains %d (the number is the source hash mod 4),
460
+ # or the value returned from invoking the proc if it's a proc.
466
461
  def compute_asset_host(source)
467
462
  if host = ActionController::Base.asset_host
468
- host % (source.hash % 4)
463
+ if host.is_a?(Proc)
464
+ host.call(source)
465
+ else
466
+ host % (source.hash % 4)
467
+ end
469
468
  end
470
469
  end
471
470
 
@@ -492,7 +491,23 @@ module ActionView
492
491
  source << "?#{asset_id}" if !asset_id.blank?
493
492
  end
494
493
 
495
- def expand_javascript_sources(sources)
494
+ def javascript_src_tag(source, options)
495
+ content_tag("script", "", { "type" => Mime::JS, "src" => path_to_javascript(source) }.merge(options))
496
+ end
497
+
498
+ def stylesheet_tag(source, options)
499
+ tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)
500
+ end
501
+
502
+ def compute_javascript_paths(sources)
503
+ expand_javascript_sources(sources).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }
504
+ end
505
+
506
+ def compute_stylesheet_paths(sources)
507
+ expand_stylesheet_sources(sources).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }
508
+ end
509
+
510
+ def expand_javascript_sources(sources)
496
511
  case
497
512
  when sources.include?(:all)
498
513
  all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).split(".", 0).first }.sort
@@ -521,6 +536,13 @@ module ActionView
521
536
  def join_asset_file_contents(paths)
522
537
  paths.collect { |path| File.read(File.join(ASSETS_DIR, path.split("?").first)) }.join("\n\n")
523
538
  end
539
+
540
+ def write_asset_file_contents(joined_asset_path, asset_paths)
541
+ unless file_exist?(joined_asset_path)
542
+ FileUtils.mkdir_p(File.dirname(joined_asset_path))
543
+ File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }
544
+ end
545
+ end
524
546
  end
525
547
  end
526
548
  end
@@ -287,8 +287,8 @@ module ActionView
287
287
  #
288
288
  # ==== Examples
289
289
  # auto_link("Go to http://www.rubyonrails.org and say hello to david@loudthinking.com")
290
- # # => "Go to <a href="http://www.rubyonrails.org">http://www.rubyonrails.org</a> and
291
- # # say hello to <a href="mailto:david@loudthinking.com">david@loudthinking.com</a>"
290
+ # # => "Go to <a href=\"http://www.rubyonrails.org\">http://www.rubyonrails.org</a> and
291
+ # # say hello to <a href=\"mailto:david@loudthinking.com\">david@loudthinking.com</a>"
292
292
  #
293
293
  # auto_link("Visit http://www.loudthinking.com/ or e-mail david@loudthinking.com", :urls)
294
294
  # # => "Visit <a href=\"http://www.loudthinking.com/\">http://www.loudthinking.com/</a>
@@ -0,0 +1,17 @@
1
+ module ActionView
2
+ class TemplateHandler
3
+ def self.line_offset
4
+ 0
5
+ end
6
+
7
+ def initialize(view)
8
+ @view = view
9
+ end
10
+
11
+ def render(template, local_assigns)
12
+ end
13
+
14
+ def compile(template)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,19 @@
1
+ require 'builder'
2
+
3
+ module ActionView
4
+ module TemplateHandlers
5
+ class Builder < TemplateHandler
6
+ def self.line_offset
7
+ 2
8
+ end
9
+
10
+ def compile(template)
11
+ content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller")
12
+ "#{content_type_handler}.content_type ||= Mime::XML\n" +
13
+ "xml = Builder::XmlMarkup.new(:indent => 2)\n" +
14
+ template +
15
+ "\nxml.target!\n"
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require 'erb'
2
+
3
+ class ERB
4
+ module Util
5
+ HTML_ESCAPE = { '&' => '&amp;', '"' => '&quot;', '>' => '&gt;', '<' => '&lt;' }
6
+
7
+ def html_escape(s)
8
+ s.to_s.gsub(/[&"><]/) { |special| HTML_ESCAPE[special] }
9
+ end
10
+ end
11
+ end
12
+
13
+ module ActionView
14
+ module TemplateHandlers
15
+ class ERB < TemplateHandler
16
+ def compile(template)
17
+ ::ERB.new(template, nil, @view.erb_trim_mode).src
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,14 @@
1
+ module ActionView
2
+ module TemplateHandlers
3
+ class RJS < TemplateHandler
4
+ def self.line_offset
5
+ 2
6
+ end
7
+
8
+ def compile(template)
9
+ "controller.response.content_type ||= Mime::JS\n" +
10
+ "update_page do |page|\n#{template}\nend"
11
+ end
12
+ end
13
+ end
14
+ end
@@ -23,4 +23,22 @@ class ActionViewTests < Test::Unit::TestCase
23
23
  assert_equal expectation, base.send(:find_template_extension_from_first_render)
24
24
  end
25
25
  end
26
+
27
+ def test_should_report_file_exists_correctly
28
+ base = ActionView::Base.new
29
+
30
+ assert_nil base.send(:find_template_extension_from_first_render)
31
+
32
+ assert_equal false, base.send(:file_exists?, 'test.rhtml')
33
+ assert_equal false, base.send(:file_exists?, 'test.rb')
34
+
35
+ base.instance_variable_set('@first_render', 'foo.rb')
36
+
37
+ assert_equal 'rb', base.send(:find_template_extension_from_first_render)
38
+
39
+ assert_equal false, base.send(:file_exists?, 'baz')
40
+ assert_equal false, base.send(:file_exists?, 'baz.rb')
41
+
42
+ end
43
+
26
44
  end
@@ -72,12 +72,12 @@ class CgiRequestTest < BaseCgiTest
72
72
 
73
73
  def test_cookie_syntax_resilience
74
74
  cookies = CGI::Cookie::parse(@request_hash["HTTP_COOKIE"]);
75
- assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"]
76
- assert_equal ["yes"], cookies["is_admin"]
75
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect
76
+ assert_equal ["yes"], cookies["is_admin"], cookies.inspect
77
77
 
78
78
  alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]);
79
- assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"]
80
- assert_equal ["yes"], alt_cookies["is_admin"]
79
+ assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect
80
+ assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect
81
81
  end
82
82
  end
83
83
 
@@ -67,7 +67,7 @@ class CookieTest < Test::Unit::TestCase
67
67
  def test_setting_cookie_with_http_only
68
68
  get :authenticate_with_http_only
69
69
  assert_equal [ CGI::Cookie::new("name" => "user_name", "value" => "david", "http_only" => true) ], @response.headers["cookie"]
70
- assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"].to_s
70
+ assert_equal CGI::Cookie::new("name" => "user_name", "value" => "david", "path" => "/", "http_only" => true).to_s, @response.headers["cookie"][0].to_s
71
71
  end
72
72
 
73
73
  def test_multiple_cookies
@@ -1,5 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../abstract_unit'
2
2
 
3
+ # FIXME: crashes Ruby 1.9
3
4
  class FilterTest < Test::Unit::TestCase
4
5
  class TestController < ActionController::Base
5
6
  before_filter :ensure_login
@@ -1,6 +1,6 @@
1
1
  require File.dirname(__FILE__) + '/../abstract_unit'
2
2
 
3
- silence_warnings { ActionController::Helpers::HELPERS_DIR = File.dirname(__FILE__) + '/../fixtures/helpers' }
3
+ ActionController::Helpers::HELPERS_DIR.replace File.dirname(__FILE__) + '/../fixtures/helpers'
4
4
 
5
5
  class TestController < ActionController::Base
6
6
  attr_accessor :delegate_attr
@@ -130,23 +130,25 @@ class HelperTest < Test::Unit::TestCase
130
130
  end
131
131
 
132
132
  def test_all_helpers
133
+ methods = ApplicationController.master_helper_module.instance_methods.map(&:to_s)
134
+
133
135
  # abc_helper.rb
134
- assert ApplicationController.master_helper_module.instance_methods.include?("bare_a")
136
+ assert methods.include?('bare_a')
135
137
 
136
138
  # fun/games_helper.rb
137
- assert ApplicationController.master_helper_module.instance_methods.include?("stratego")
139
+ assert methods.include?('stratego')
138
140
 
139
141
  # fun/pdf_helper.rb
140
- assert ApplicationController.master_helper_module.instance_methods.include?("foobar")
142
+ assert methods.include?('foobar')
141
143
  end
142
144
 
143
145
  private
144
146
  def expected_helper_methods
145
- TestHelper.instance_methods
147
+ TestHelper.instance_methods.map(&:to_s)
146
148
  end
147
149
 
148
150
  def master_helper_methods
149
- @controller_class.master_helper_module.instance_methods
151
+ @controller_class.master_helper_module.instance_methods.map(&:to_s)
150
152
  end
151
153
 
152
154
  def missing_methods
@@ -49,28 +49,49 @@ class SessionTest < Test::Unit::TestCase
49
49
  assert_equal 200, @session.follow_redirect!
50
50
  end
51
51
 
52
- def test_get_via_redirect
53
- path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
54
-
55
- @session.expects(:get).with(path,args,headers)
52
+ def test_request_via_redirect_uses_given_method
53
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
54
+ @session.expects(:put).with(path, args, headers)
55
+ @session.stubs(:redirect?).returns(false)
56
+ @session.request_via_redirect(:put, path, args, headers)
57
+ end
56
58
 
59
+ def test_request_via_redirect_follows_redirects
60
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
57
61
  @session.stubs(:redirect?).returns(true, true, false)
58
62
  @session.expects(:follow_redirect!).times(2)
63
+ @session.request_via_redirect(:get, path, args, headers)
64
+ end
59
65
 
66
+ def test_request_via_redirect_returns_status
67
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue"}
68
+ @session.stubs(:redirect?).returns(false)
60
69
  @session.stubs(:status).returns(200)
61
- assert_equal 200, @session.get_via_redirect(path, args, headers)
70
+ assert_equal 200, @session.request_via_redirect(:get, path, args, headers)
62
71
  end
63
72
 
64
- def test_post_via_redirect
73
+ def test_get_via_redirect
65
74
  path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
75
+ @session.expects(:request_via_redirect).with(:get, path, args, headers)
76
+ @session.get_via_redirect(path, args, headers)
77
+ end
66
78
 
67
- @session.expects(:post).with(path,args,headers)
79
+ def test_post_via_redirect
80
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
81
+ @session.expects(:request_via_redirect).with(:post, path, args, headers)
82
+ @session.post_via_redirect(path, args, headers)
83
+ end
68
84
 
69
- @session.stubs(:redirect?).returns(true, true, false)
70
- @session.expects(:follow_redirect!).times(2)
85
+ def test_put_via_redirect
86
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
87
+ @session.expects(:request_via_redirect).with(:put, path, args, headers)
88
+ @session.put_via_redirect(path, args, headers)
89
+ end
71
90
 
72
- @session.stubs(:status).returns(200)
73
- assert_equal 200, @session.post_via_redirect(path, args, headers)
91
+ def test_delete_via_redirect
92
+ path = "/somepath"; args = {:id => '1'}; headers = {"X-Test-Header" => "testvalue" }
93
+ @session.expects(:request_via_redirect).with(:delete, path, args, headers)
94
+ @session.delete_via_redirect(path, args, headers)
74
95
  end
75
96
 
76
97
  def test_url_for_with_controller
@@ -179,6 +200,15 @@ class SessionTest < Test::Unit::TestCase
179
200
  @session.expects(:process).with(:head,path,params,headers_after_xhr)
180
201
  @session.xml_http_request(:head,path,params,headers)
181
202
  end
203
+
204
+ def test_xml_http_request_override_accept
205
+ path = "/index"; params = "blah"; headers = {:location => 'blah', "Accept" => "application/xml"}
206
+ headers_after_xhr = headers.merge(
207
+ "X-Requested-With" => "XMLHttpRequest"
208
+ )
209
+ @session.expects(:process).with(:post,path,params,headers_after_xhr)
210
+ @session.xml_http_request(:post,path,params,headers)
211
+ end
182
212
  end
183
213
 
184
214
  class IntegrationTestTest < Test::Unit::TestCase
@@ -241,11 +241,11 @@ class NewRenderTestController < ActionController::Base
241
241
  end
242
242
 
243
243
  def hello_world_from_rxml_using_action
244
- render :action => "hello_world.builder"
244
+ render :action => "hello_world_from_rxml.builder"
245
245
  end
246
246
 
247
247
  def hello_world_from_rxml_using_template
248
- render :template => "test/hello_world.builder"
248
+ render :template => "test/hello_world_from_rxml.builder"
249
249
  end
250
250
 
251
251
  def head_with_location_header