From ef21e013338461f33bf85f5cf6edd84b5ce9b6fe Mon Sep 17 00:00:00 2001 From: Michael Koziarski Date: Sat, 31 May 2008 16:39:32 -0700 Subject: Remove dead code, and the tests for it. --- actionpack/lib/action_view/compiled_templates.rb | 69 -------- .../test/template/compiled_templates_test.rb | 192 --------------------- 2 files changed, 261 deletions(-) delete mode 100644 actionpack/lib/action_view/compiled_templates.rb delete mode 100644 actionpack/test/template/compiled_templates_test.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/compiled_templates.rb b/actionpack/lib/action_view/compiled_templates.rb deleted file mode 100644 index 5a286432e3..0000000000 --- a/actionpack/lib/action_view/compiled_templates.rb +++ /dev/null @@ -1,69 +0,0 @@ -module ActionView - - # CompiledTemplates modules hold methods that have been compiled. - # Templates are compiled into these methods so that they do not need to be - # read and parsed for each request. - # - # Each template may be compiled into one or more methods. Each method accepts a given - # set of parameters which is used to implement local assigns passing. - # - # To use a compiled template module, create a new instance and include it into the class - # in which you want the template to be rendered. - class CompiledTemplates < Module - attr_reader :method_names - - def initialize - @method_names = Hash.new do |hash, key| - hash[key] = "__compiled_method_#{(hash.length + 1)}" - end - @mtimes = {} - end - - # Return the full key for the given identifier and argument names - def full_key(identifier, arg_names) - [identifier, arg_names] - end - - # Return the selector for this method or nil if it has not been compiled - def selector(identifier, arg_names) - key = full_key(identifier, arg_names) - method_names.key?(key) ? method_names[key] : nil - end - alias :compiled? :selector - - # Return the time at which the method for the given identifier and argument names was compiled. - def mtime(identifier, arg_names) - @mtimes[full_key(identifier, arg_names)] - end - - # Compile the provided source code for the given argument names and with the given initial line number. - # The identifier should be unique to this source. - # - # The file_name, if provided will appear in backtraces. If not provided, the file_name defaults - # to the identifier. - # - # This method will return the selector for the compiled version of this method. - def compile_source(identifier, arg_names, source, initial_line_number = 0, file_name = nil) - file_name ||= identifier - name = method_names[full_key(identifier, arg_names)] - arg_desc = arg_names.empty? ? '' : "(#{arg_names * ', '})" - fake_file_name = "#{file_name}#{arg_desc}" # Include the arguments for this version (for now) - - method_def = wrap_source(name, arg_names, source) - - begin - module_eval(method_def, fake_file_name, initial_line_number) - @mtimes[full_key(identifier, arg_names)] = Time.now - rescue Exception => e # errors from compiled source - e.blame_file! identifier - raise - end - name - end - - # Wrap the provided source in a def ... end block. - def wrap_source(name, arg_names, source) - "def #{name}(#{arg_names * ', '})\n#{source}\nend" - end - end -end diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb deleted file mode 100644 index 73e7ec1d76..0000000000 --- a/actionpack/test/template/compiled_templates_test.rb +++ /dev/null @@ -1,192 +0,0 @@ -require 'abstract_unit' -require 'action_view/helpers/date_helper' -require 'action_view/compiled_templates' - -class CompiledTemplateTests < Test::Unit::TestCase - def setup - @ct = ActionView::CompiledTemplates.new - @v = Class.new - @v.send :include, @ct - @a = './test_compile_template_a.rhtml' - @b = './test_compile_template_b.rhtml' - @s = './test_compile_template_link.rhtml' - end - def teardown - [@a, @b, @s].each do |f| - FileUtils.rm(f) if File.exist?(f) || File.symlink?(f) - end - end - attr_reader :ct, :v - - def test_name_allocation - hi_world = ct.method_names['hi world'] - hi_sexy = ct.method_names['hi sexy'] - wish_upon_a_star = ct.method_names['I love seeing decent error messages'] - - assert_equal hi_world, ct.method_names['hi world'] - assert_equal hi_sexy, ct.method_names['hi sexy'] - assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages'] - assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length - end - - def test_wrap_source - assert_equal( - "def aliased_assignment(value)\nself.value = value\nend", - @ct.wrap_source(:aliased_assignment, [:value], 'self.value = value') - ) - - assert_equal( - "def simple()\nnil\nend", - @ct.wrap_source(:simple, [], 'nil') - ) - end - - def test_compile_source_single_method - selector = ct.compile_source('doubling method', [:a], 'a + a') - assert_equal 2, @v.new.send(selector, 1) - assert_equal 4, @v.new.send(selector, 2) - assert_equal -4, @v.new.send(selector, -2) - assert_equal 0, @v.new.send(selector, 0) - selector - end - - def test_compile_source_two_method - sel1 = test_compile_source_single_method # compile the method in the other test - sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b') - assert_not_equal sel1, sel2 - - assert_equal 2, @v.new.send(sel1, 1) - assert_equal 4, @v.new.send(sel1, 2) - - assert_equal 6, @v.new.send(sel2, 1, 2) - assert_equal 32, @v.new.send(sel2, 15, 1) - end - - def test_mtime - t1 = Time.now - - test_compile_source_single_method - mtime = ct.mtime('doubling method', [:a]) - - assert mtime < Time.now - assert mtime > t1 - end - - uses_mocha 'test_compile_time' do - - def test_compile_time - t = Time.now - - File.open(@a, "w"){|f| f.puts @a} - File.open(@b, "w"){|f| f.puts @b} - # windows doesn't support symlinks (even under cygwin) - windows = (RUBY_PLATFORM =~ /win32/) - `ln -s #{@a} #{@s}` unless windows - - v = ActionView::Base.new - v.base_path = '.' - v.cache_template_loading = false - - ta = ActionView::Template.new(v, @a, false, {}) - tb = ActionView::Template.new(v, @b, false, {}) - ts = ActionView::Template.new(v, @s, false, {}) - - @handler_class = ActionView::Template.handler_class_for_extension(:rhtml) - @handler = @handler_class.new(v) - - # All templates were created at t+1 - File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second) - - # private methods template_changed_since? and compile_template? - # should report true for all since they have not been compiled - assert @handler.send(:template_changed_since?, @a, t) - assert @handler.send(:template_changed_since?, @b, t) - assert @handler.send(:template_changed_since?, @s, t) unless windows - - assert @handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # All templates are rendered at t+2 - Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - a_n = v.method_names[@a] - b_n = v.method_names[@b] - s_n = v.method_names[@s] unless windows - # all of the files have changed since last compile - assert @handler.compile_time[a_n] > t - assert @handler.compile_time[b_n] > t - assert @handler.compile_time[s_n] > t unless windows - - # private methods template_changed_since? and compile_template? - # should report false for all since none have changed since compile - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(t + 1.second) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert !@handler.send(:compile_template?, ts) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # none of the files have changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert @handler.compile_time[s_n] < t + 3.seconds unless windows - - `rm #{@s}; ln -s #{@b} #{@s}` unless windows - # private methods template_changed_since? and compile_template? - # should report true for symlink since it has changed since compile - - # t + 3.seconds is for the symlink - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 9).returns( - *(windows ? [ t + 1.second, t + 1.second ] : - [ t + 1.second, t + 1.second, t + 3.second ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # Only the symlink template gets rendered at t+3 - Time.stubs(:now).returns(t + 3.seconds) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the symlink has changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert_equal @handler.compile_time[s_n], t + 3.seconds unless windows - - FileUtils.touch @b - # private methods template_changed_since? and compile_template? - # should report true for symlink and file at end of symlink - # since it has changed since last compile - # - # t+4 is for @b and also for the file that @s points to, which is @b - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns( - *(windows ? [ t + 1.second, t + 4.seconds ] : - [ t + 1.second, t + 4.seconds, t + 3.second, t + 4.seconds ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the file at the end of the symlink has changed since last compile - # both the symlink and the file at the end of it should be recompiled - assert @handler.compile_time[a_n] < t + 5.seconds - assert_equal @handler.compile_time[b_n], t + 5.seconds - assert_equal @handler.compile_time[s_n], t + 5.seconds unless windows - end - end -end -- cgit v1.2.3 From f9db7695fe3c148c8d1077f1564e5b94d126b83b Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Sat, 31 May 2008 17:03:03 -0700 Subject: Making ready for release of 2.1 --- actionpack/CHANGELOG | 4 ++-- actionpack/Rakefile | 2 +- actionpack/lib/action_pack/version.rb | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 5c4bfbf3c9..cb684a925d 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,6 +1,6 @@ -* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] +*2.1.0 (May 31st, 2008)* -*2.1.0 RC1 (May 11th, 2008)* +* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] * Fixed that forgery protection can be used without session tracking (Peter Jones) [#139] diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 0147a5c1e8..b37f756c1e 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 70fc1ced8c..c67654d9a8 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,8 +1,8 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end -- cgit v1.2.3 From 06cb20708be13fbf736447aa0e5e6dd7d64c8b5d Mon Sep 17 00:00:00 2001 From: Ezra Zygmuntowicz Date: Sun, 1 Jun 2008 11:25:11 -0700 Subject: Added Rack processor Signed-off-by: Joshua Peek --- actionpack/CHANGELOG | 3 + actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/dispatcher.rb | 8 +- actionpack/lib/action_controller/rack_process.rb | 321 +++++++++++++++++++++++ actionpack/test/controller/cgi_test.rb | 33 +++ actionpack/test/controller/rack_test.rb | 150 +++++++++++ 6 files changed, 515 insertions(+), 1 deletion(-) create mode 100644 actionpack/lib/action_controller/rack_process.rb create mode 100644 actionpack/test/controller/rack_test.rb (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index cb684a925d..9622029362 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,6 @@ +* Added Rack processor [Ezra Zygmuntowicz, Josh Peek] + + *2.1.0 (May 31st, 2008)* * InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 810a5fb9b5..3c4a339d50 100755 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -53,6 +53,7 @@ require 'action_controller/streaming' require 'action_controller/session_management' require 'action_controller/http_authentication' require 'action_controller/components' +require 'action_controller/rack_process' require 'action_controller/record_identifier' require 'action_controller/request_forgery_protection' require 'action_controller/headers' diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 6e1e7a261f..b40f1ba9be 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -96,7 +96,7 @@ module ActionController include ActiveSupport::Callbacks define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch - def initialize(output, request = nil, response = nil) + def initialize(output = $stdout, request = nil, response = nil) @output, @request, @response = output, request, response end @@ -123,6 +123,12 @@ module ActionController failsafe_rescue exception end + def call(env) + @request = RackRequest.new(env) + @response = RackResponse.new + dispatch + end + def reload_application # Run prepare callbacks before every request in development mode run_callbacks :prepare_dispatch diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb new file mode 100644 index 0000000000..16625519b6 --- /dev/null +++ b/actionpack/lib/action_controller/rack_process.rb @@ -0,0 +1,321 @@ +require 'action_controller/cgi_ext' +require 'action_controller/session/cookie_store' + +module ActionController #:nodoc: + class RackRequest < AbstractRequest #:nodoc: + attr_accessor :env, :session_options + + class SessionFixationAttempt < StandardError #:nodoc: + end + + DEFAULT_SESSION_OPTIONS = { + :database_manager => CGI::Session::CookieStore, # store data in cookie + :prefix => "ruby_sess.", # prefix session file names + :session_path => "/", # available to all paths in app + :session_key => "_session_id", + :cookie_only => true + } unless const_defined?(:DEFAULT_SESSION_OPTIONS) + + def initialize(env, session_options = DEFAULT_SESSION_OPTIONS) + @session_options = session_options + @env = env + @cgi = CGIWrapper.new(self) + super() + end + + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. + def body + if raw_post = env['RAW_POST_DATA'] + StringIO.new(raw_post) + else + @env['rack.input'] + end + end + + def key?(key) + @env.key? key + end + + def query_parameters + @query_parameters ||= self.class.parse_query_parameters(query_string) + end + + def request_parameters + @request_parameters ||= parse_formatted_request_parameters + end + + def cookies + return {} unless @env["HTTP_COOKIE"] + + if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] + @env["rack.request.cookie_hash"] + else + @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] + # According to RFC 2109: + # If multiple cookies satisfy the criteria above, they are ordered in + # the Cookie header such that those with more specific Path attributes + # precede those with less specific. Ordering with respect to other + # attributes (e.g., Domain) is unspecified. + @env["rack.request.cookie_hash"] = + parse_query(@env["rack.request.cookie_string"], ';,').inject({}) { |h, (k,v)| + h[k] = Array === v ? v.first : v + h + } + end + end + + def host_with_port_without_standard_port_handling + if forwarded = @env["HTTP_X_FORWARDED_HOST"] + forwarded.split(/,\s?/).last + elsif http_host = @env['HTTP_HOST'] + http_host + elsif server_name = @env['SERVER_NAME'] + server_name + else + "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + end + end + + def host + host_with_port_without_standard_port_handling.sub(/:\d+$/, '') + end + + def port + if host_with_port_without_standard_port_handling =~ /:(\d+)$/ + $1.to_i + else + standard_port + end + end + + def remote_addr + @env['REMOTE_ADDR'] + end + + def session + unless defined?(@session) + if @session_options == false + @session = Hash.new + else + stale_session_check! do + if cookie_only? && query_parameters[session_options_with_string_keys['session_key']] + raise SessionFixationAttempt + end + case value = session_options_with_string_keys['new_session'] + when true + @session = new_session + when false + begin + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + # CGI::Session raises ArgumentError if 'new_session' == false + # and no session cookie or query param is present. + rescue ArgumentError + @session = Hash.new + end + when nil + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + else + raise ArgumentError, "Invalid new_session option: #{value}" + end + @session['__valid_session'] + end + end + end + @session + end + + def reset_session + @session.delete if defined?(@session) && @session.is_a?(CGI::Session) + @session = new_session + end + + private + # Delete an old session if it exists then create a new one. + def new_session + if @session_options == false + Hash.new + else + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true)) + end + end + + def cookie_only? + session_options_with_string_keys['cookie_only'] + end + + def stale_session_check! + yield + rescue ArgumentError => argument_error + if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} + begin + # Note that the regexp does not allow $1 to end with a ':' + $1.constantize + rescue LoadError, NameError => const_error + raise ActionController::SessionRestoreError, <<-end_msg +Session contains objects whose class definition isn\'t available. +Remember to require the classes for all objects kept in the session. +(Original exception: #{const_error.message} [#{const_error.class}]) +end_msg + end + + retry + else + raise + end + end + + def session_options_with_string_keys + @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys + end + + # From Rack::Utils + def parse_query(qs, d = '&;') + params = {} + (qs || '').split(/[#{d}] */n).inject(params) { |h,p| + k, v = unescape(p).split('=',2) + if cur = params[k] + if cur.class == Array + params[k] << v + else + params[k] = [cur, v] + end + else + params[k] = v + end + } + + return params + end + + def unescape(s) + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ + [$1.delete('%')].pack('H*') + } + end + end + + class RackResponse < AbstractResponse #:nodoc: + attr_accessor :status + + def initialize + @writer = lambda { |x| @body << x } + @block = nil + super() + end + + def out(output = $stdout, &block) + @block = block + normalize_headers(@headers) + if [204, 304].include?(@status.to_i) + @headers.delete "Content-Type" + [status.to_i, @headers.to_hash, []] + else + [status.to_i, @headers.to_hash, self] + end + end + alias to_a out + + def each(&callback) + if @body.respond_to?(:call) + @writer = lambda { |x| callback.call(x) } + @body.call(self, self) + else + @body.each(&callback) + end + + @writer = callback + @block.call(self) if @block + end + + def write(str) + @writer.call str.to_s + str + end + + def close + @body.close if @body.respond_to?(:close) + end + + def empty? + @block == nil && @body.empty? + end + + private + def normalize_headers(options = "text/html") + if options.is_a?(String) + headers['Content-Type'] = options unless headers['Content-Type'] + else + headers['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length'] + + headers['Content-Type'] = options.delete('type') || "text/html" + headers['Content-Type'] += "; charset=" + options.delete('charset') if options['charset'] + + headers['Content-Language'] = options.delete('language') if options['language'] + headers['Expires'] = options.delete('expires') if options['expires'] + + @status = options.delete('Status') if options['Status'] + @status ||= 200 + # Convert 'cookie' header to 'Set-Cookie' headers. + # Because Set-Cookie header can appear more the once in the response body, + # we store it in a line break seperated string that will be translated to + # multiple Set-Cookie header by the handler. + if cookie = options.delete('cookie') + cookies = [] + + case cookie + when Array then cookie.each { |c| cookies << c.to_s } + when Hash then cookie.each { |_, c| cookies << c.to_s } + else cookies << cookie.to_s + end + + @output_cookies.each { |c| cookies << c.to_s } if @output_cookies + + headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n") + end + + options.each { |k,v| headers[k] = v } + end + + "" + end + end + + class CGIWrapper < ::CGI + def initialize(request, *args) + @request = request + @args = *args + @input = request.body + + super *args + end + + def params + @params ||= @request.params + end + + def cookies + @request.cookies + end + + def query_string + @request.query_string + end + + # Used to wrap the normal args variable used inside CGI. + def args + @args + end + + # Used to wrap the normal env_table variable used inside CGI. + def env_table + @request.env + end + + # Used to wrap the normal stdinput variable used inside CGI. + def stdinput + @input + end + end +end diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb index 87f72fda77..f0f3a4b826 100755 --- a/actionpack/test/controller/cgi_test.rb +++ b/actionpack/test/controller/cgi_test.rb @@ -114,3 +114,36 @@ class CgiRequestNeedsRewoundTest < BaseCgiTest assert_equal 0, request.body.pos end end + +class CgiResponseTest < BaseCgiTest + def setup + super + @fake_cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n") + @response = ActionController::CgiResponse.new(@fake_cgi) + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string + end + + def test_head_request + @fake_cgi.env_table['REQUEST_METHOD'] = 'HEAD' + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string + end +end diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb new file mode 100644 index 0000000000..cd4151783e --- /dev/null +++ b/actionpack/test/controller/rack_test.rb @@ -0,0 +1,150 @@ +require 'abstract_unit' +require 'action_controller/rack_process' + +class BaseRackTest < Test::Unit::TestCase + def setup + @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"} + # some Nokia phone browsers omit the space after the semicolon separator. + # some developers have grown accustomed to using comma in cookie values. + @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"} + @request = ActionController::RackRequest.new(@env) + end + + def default_test; end +end + + +class RackRequestTest < BaseRackTest + def test_proxy_request + assert_equal 'glu.ttono.us', @request.host_with_port + end + + def test_http_host + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org:8080" + assert_equal "rubyonrails.org:8080", @request.host_with_port + + @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" + assert_equal "www.secondhost.org", @request.host + end + + def test_http_host_with_default_port_overrides_server_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org" + assert_equal "rubyonrails.org", @request.host_with_port + end + + def test_host_with_port_defaults_to_server_name_if_no_host_headers + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + assert_equal "glu.ttono.us:8007", @request.host_with_port + end + + def test_host_with_port_falls_back_to_server_addr_if_necessary + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + @env.delete "SERVER_NAME" + assert_equal "207.7.108.53:8007", @request.host_with_port + end + + def test_host_with_port_if_http_standard_port_is_specified + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_with_port_if_https_standard_port_is_specified + @env['HTTP_X_FORWARDED_PROTO'] = "https" + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_if_ipv6_reference + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_host_if_ipv6_reference_with_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_cookie_syntax_resilience + cookies = CGI::Cookie::parse(@env["HTTP_COOKIE"]); + assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect + assert_equal ["yes"], cookies["is_admin"], cookies.inspect + + alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]); + assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect + assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect + end +end + + +class RackRequestParamsParsingTest < BaseRackTest + def test_doesnt_break_when_content_type_has_charset + data = 'flamenco=love' + @request.env['CONTENT_LENGTH'] = data.length + @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + @request.env['RAW_POST_DATA'] = data + assert_equal({"flamenco"=> "love"}, @request.request_parameters) + end + + def test_doesnt_interpret_request_uri_as_query_string_when_missing + @request.env['REQUEST_URI'] = 'foo' + assert_equal({}, @request.query_parameters) + end +end + + +class RackRequestNeedsRewoundTest < BaseRackTest + def test_body_should_be_rewound + data = 'foo' + @env['rack.input'] = StringIO.new(data) + @env['CONTENT_LENGTH'] = data.length + @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + + # Read the request body by parsing params. + request = ActionController::RackRequest.new(@env) + request.request_parameters + + # Should have rewound the body. + assert_equal 0, request.body.pos + end +end + + +class RackResponseTest < BaseRackTest + def setup + super + @response = ActionController::RackResponse.new + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["0", "1", "2", "3", "4"], parts + end +end -- cgit v1.2.3 From 92050f6c6f586b2a73aeb61da4f41b9822bbcf6d Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Mon, 2 Jun 2008 21:02:26 -0500 Subject: Ensure Rack processor reads CGI output_cookies for the session cookie. --- actionpack/lib/action_controller/dispatcher.rb | 2 +- actionpack/lib/action_controller/rack_process.rb | 10 +++-- actionpack/test/controller/rack_test.rb | 56 ++++++++++++++++++++++-- 3 files changed, 60 insertions(+), 8 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index b40f1ba9be..7162fb8b1f 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -125,7 +125,7 @@ module ActionController def call(env) @request = RackRequest.new(env) - @response = RackResponse.new + @response = RackResponse.new(@request) dispatch end diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 16625519b6..d5fb78c44d 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -4,6 +4,7 @@ require 'action_controller/session/cookie_store' module ActionController #:nodoc: class RackRequest < AbstractRequest #:nodoc: attr_accessor :env, :session_options + attr_reader :cgi class SessionFixationAttempt < StandardError #:nodoc: end @@ -199,7 +200,8 @@ end_msg class RackResponse < AbstractResponse #:nodoc: attr_accessor :status - def initialize + def initialize(request) + @request = request @writer = lambda { |x| @body << x } @block = nil super() @@ -270,9 +272,9 @@ end_msg else cookies << cookie.to_s end - @output_cookies.each { |c| cookies << c.to_s } if @output_cookies + @request.cgi.output_cookies.each { |c| cookies << c.to_s } if @request.cgi.output_cookies - headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n") + headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].flatten.compact end options.each { |k,v| headers[k] = v } @@ -283,6 +285,8 @@ end_msg end class CGIWrapper < ::CGI + attr_reader :output_cookies + def initialize(request, *args) @request = request @args = *args diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb index cd4151783e..026b0195d1 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/controller/rack_test.rb @@ -3,7 +3,36 @@ require 'action_controller/rack_process' class BaseRackTest < Test::Unit::TestCase def setup - @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"} + @env = { + "HTTP_MAX_FORWARDS" => "10", + "SERVER_NAME" => "glu.ttono.us:8007", + "FCGI_ROLE" => "RESPONDER", + "HTTP_X_FORWARDED_HOST" => "glu.ttono.us", + "HTTP_ACCEPT_ENCODING" => "gzip, deflate", + "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", + "PATH_INFO" => "", + "HTTP_ACCEPT_LANGUAGE" => "en", + "HTTP_HOST" => "glu.ttono.us:8007", + "SERVER_PROTOCOL" => "HTTP/1.1", + "REDIRECT_URI" => "/dispatch.fcgi", + "SCRIPT_NAME" => "/dispatch.fcgi", + "SERVER_ADDR" => "207.7.108.53", + "REMOTE_ADDR" => "207.7.108.53", + "SERVER_SOFTWARE" => "lighttpd/1.4.5", + "HTTP_COOKIE" => "_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", + "HTTP_X_FORWARDED_SERVER" => "glu.ttono.us", + "REQUEST_URI" => "/admin", + "DOCUMENT_ROOT" => "/home/kevinc/sites/typo/public", + "SERVER_PORT" => "8007", + "QUERY_STRING" => "", + "REMOTE_PORT" => "63137", + "GATEWAY_INTERFACE" => "CGI/1.1", + "HTTP_X_FORWARDED_FOR" => "65.88.180.234", + "HTTP_ACCEPT" => "*/*", + "SCRIPT_FILENAME" => "/home/kevinc/sites/typo/public/dispatch.fcgi", + "REDIRECT_STATUS" => "200", + "REQUEST_METHOD" => "GET" + } # some Nokia phone browsers omit the space after the semicolon separator. # some developers have grown accustomed to using comma in cookie values. @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"} @@ -118,7 +147,7 @@ end class RackResponseTest < BaseRackTest def setup super - @response = ActionController::RackResponse.new + @response = ActionController::RackResponse.new(@request) @output = StringIO.new('') end @@ -127,7 +156,7 @@ class RackResponseTest < BaseRackTest status, headers, body = @response.out(@output) assert_equal 200, status - assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers) parts = [] body.each { |part| parts << part } @@ -141,10 +170,29 @@ class RackResponseTest < BaseRackTest status, headers, body = @response.out(@output) assert_equal 200, status - assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers) parts = [] body.each { |part| parts << part } assert_equal ["0", "1", "2", "3", "4"], parts end + + def test_set_session_cookie + cookie = CGI::Cookie.new({"name" => "name", "value" => "Josh"}) + @request.cgi.send :instance_variable_set, '@output_cookies', [cookie] + + @response.body = "Hello, World!" + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({ + "Content-Type" => "text/html", + "Cache-Control" => "no-cache", + "Set-Cookie" => ["name=Josh; path="] + }, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end end -- cgit v1.2.3 From c08547d2266c75f0a82d06dd91c6d0500740e12e Mon Sep 17 00:00:00 2001 From: Joshua Peek Date: Tue, 3 Jun 2008 13:32:53 -0500 Subject: Namespace Inflector, Dependencies, OrderedOptions, and TimeZone under ActiveSupport [#238 state:resolved] --- actionpack/lib/action_controller/dispatcher.rb | 2 +- actionpack/lib/action_controller/routing.rb | 2 +- actionpack/test/controller/dispatcher_test.rb | 4 ++-- actionpack/test/controller/integration_upload_test.rb | 2 +- actionpack/test/controller/routing_test.rb | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 7162fb8b1f..fe4f6b4a7e 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -141,7 +141,7 @@ module ActionController # be reloaded on the next request without restarting the server. def cleanup_application ActiveRecord::Base.reset_subclasses if defined?(ActiveRecord) - Dependencies.clear + ActiveSupport::Dependencies.clear ActiveRecord::Base.clear_reloadable_connections! if defined?(ActiveRecord) end diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index 6aa266513d..8846dcc504 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -369,7 +369,7 @@ module ActionController Routes = RouteSet.new - ::Inflector.module_eval do + ActiveSupport::Inflector.module_eval do # Ensures that routes are reloaded when Rails inflections are updated. def inflections_with_route_reloading(&block) returning(inflections_without_route_reloading(&block)) { diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index eea0813ed5..911fcab67b 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -27,14 +27,14 @@ class DispatcherTest < Test::Unit::TestCase def test_clears_dependencies_after_dispatch_if_in_loading_mode ActionController::Routing::Routes.expects(:reload).once - Dependencies.expects(:clear).once + ActiveSupport::Dependencies.expects(:clear).once dispatch(@output, false) end def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode ActionController::Routing::Routes.expects(:reload).never - Dependencies.expects(:clear).never + ActiveSupport::Dependencies.expects(:clear).never dispatch end diff --git a/actionpack/test/controller/integration_upload_test.rb b/actionpack/test/controller/integration_upload_test.rb index 33df1131cb..4af9b7e697 100644 --- a/actionpack/test/controller/integration_upload_test.rb +++ b/actionpack/test/controller/integration_upload_test.rb @@ -28,7 +28,7 @@ class SessionUploadTest < ActionController::IntegrationTest # end def test_post_with_upload uses_mocha "test_post_with_upload" do - Dependencies.stubs(:load?).returns(false) + ActiveSupport::Dependencies.stubs(:load?).returns(false) with_routing do |set| set.draw do |map| map.update 'update', :controller => "upload_test", :action => "update", :method => :post diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index 5e5503fd52..068d71a8f8 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -2392,10 +2392,10 @@ uses_mocha 'route loading' do end def test_adding_inflections_forces_reload - Inflector::Inflections.instance.expects(:uncountable).with('equipment') + ActiveSupport::Inflector::Inflections.instance.expects(:uncountable).with('equipment') routes.expects(:reload!) - Inflector.inflections { |inflect| inflect.uncountable('equipment') } + ActiveSupport::Inflector.inflections { |inflect| inflect.uncountable('equipment') } end def test_load_with_configuration -- cgit v1.2.3 From d54d90f2b590c763fe710482a9b993923fe03ec0 Mon Sep 17 00:00:00 2001 From: josevalim Date: Tue, 3 Jun 2008 14:02:51 -0500 Subject: Allow caches_action to accept a layout option [#198 state:resolved] Signed-off-by: Joshua Peek --- actionpack/CHANGELOG | 2 ++ actionpack/lib/action_controller/caching/actions.rb | 20 +++++++++++++++++--- actionpack/test/controller/caching_test.rb | 15 +++++++++++++++ 3 files changed, 34 insertions(+), 3 deletions(-) (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 9622029362..ba2b16849c 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,3 +1,5 @@ +* Allow caches_action to accept a layout option [José Valim] + * Added Rack processor [Ezra Zygmuntowicz, Josh Peek] diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 1ef9e60a21..c4b0a97a33 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -40,6 +40,8 @@ module ActionController #:nodoc: # controller.send(:list_url, c.params[:id]) } # end # + # If you pass :layout => false, it will only cache your action content. It is useful when your layout has dynamic information. + # module Actions def self.included(base) #:nodoc: base.extend(ClassMethods) @@ -54,7 +56,8 @@ module ActionController #:nodoc: def caches_action(*actions) return unless cache_configured? options = actions.extract_options! - around_filter(ActionCacheFilter.new(:cache_path => options.delete(:cache_path)), {:only => actions}.merge(options)) + cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path)) + around_filter(cache_filter, {:only => actions}.merge(options)) end end @@ -81,7 +84,9 @@ module ActionController #:nodoc: if cache = controller.read_fragment(cache_path.path) controller.rendered_action_cache = true set_content_type!(controller, cache_path.extension) - controller.send!(:render_for_text, cache) + options = { :text => cache } + options.merge!(:layout => true) if cache_layout? + controller.send!(:render, options) false else controller.action_cache_path = cache_path @@ -90,7 +95,8 @@ module ActionController #:nodoc: def after(controller) return if controller.rendered_action_cache || !caching_allowed(controller) - controller.write_fragment(controller.action_cache_path.path, controller.response.body) + action_content = cache_layout? ? content_for_layout(controller) : controller.response.body + controller.write_fragment(controller.action_cache_path.path, action_content) end private @@ -105,6 +111,14 @@ module ActionController #:nodoc: def caching_allowed(controller) controller.request.get? && controller.response.headers['Status'].to_i == 200 end + + def cache_layout? + @options[:layout] == false + end + + def content_for_layout(controller) + controller.response.layout && controller.response.template.instance_variable_get('@content_for_layout') + end end class ActionCachePath diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index f9b6b87bc6..c6d61bb504 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -156,6 +156,7 @@ class ActionCachingTestController < ActionController::Base caches_action :show, :cache_path => 'http://test.host/custom/show' caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" } caches_action :with_layout + caches_action :layout_false, :layout => false layout 'talk_from_action.erb' @@ -181,6 +182,7 @@ class ActionCachingTestController < ActionController::Base alias_method :show, :index alias_method :edit, :index alias_method :destroy, :index + alias_method :layout_false, :with_layout def expire expire_action :controller => 'action_caching_test', :action => 'index' @@ -263,6 +265,19 @@ class ActionCacheTest < Test::Unit::TestCase assert_equal @response.body, read_fragment('hostname.com/action_caching_test/with_layout') end + def test_action_cache_with_layout_and_layout_cache_false + get :layout_false + cached_time = content_to_cache + assert_not_equal cached_time, @response.body + assert fragment_exist?('hostname.com/action_caching_test/layout_false') + reset! + + get :layout_false + assert_not_equal cached_time, @response.body + + assert_equal cached_time, read_fragment('hostname.com/action_caching_test/layout_false') + end + def test_action_cache_conditional_options @request.env['HTTP_ACCEPT'] = 'application/json' get :index -- cgit v1.2.3