diff options
Diffstat (limited to 'actionpack')
36 files changed, 657 insertions, 996 deletions
diff --git a/actionpack/README b/actionpack/README index 6090089bb9..e4ce4aa044 100644 --- a/actionpack/README +++ b/actionpack/README @@ -10,7 +10,7 @@ Action Pack implements these actions as public methods on Action Controllers and uses Action Views to implement the template rendering. Action Controllers are then responsible for handling all the actions relating to a certain part of an application. This grouping usually consists of actions for lists and for -CRUDs revolving around a single (or a few) model objects. So ContactController +CRUDs revolving around a single (or a few) model objects. So ContactsController would be responsible for listing contacts, creating, deleting, and updating contacts. A WeblogController could be responsible for both posts and comments. @@ -33,7 +33,7 @@ A short rundown of the major features: * Actions grouped in controller as methods instead of separate command objects and can therefore share helper methods - BlogController < ActionController::Base + CustomersController < ActionController::Base def show @customer = find_customer end @@ -42,7 +42,7 @@ A short rundown of the major features: @customer = find_customer @customer.attributes = params[:customer] @customer.save ? - redirect_to(:action => "display") : + redirect_to(:action => "show") : render(:action => "edit") end @@ -59,7 +59,7 @@ A short rundown of the major features: Title: <%= post.title %> <% end %> - All post titles: <%= @post.collect{ |p| p.title }.join ", " %> + All post titles: <%= @posts.collect{ |p| p.title }.join ", " %> <% unless @person.is_client? %> Not for clients to see... @@ -123,7 +123,7 @@ A short rundown of the major features: <%= text_field "post", "title", "size" => 30 %> <%= html_date_select(Date.today) %> <%= link_to "New post", :controller => "post", :action => "new" %> - <%= truncate(post.title, 25) %> + <%= truncate(post.title, :length => 25) %> {Learn more}[link:classes/ActionView/Helpers.html] @@ -177,21 +177,6 @@ A short rundown of the major features: {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html] -* Pagination for navigating lists of results - - # controller - def list - @pages, @people = - paginate :people, :order => 'last_name, first_name' - end - - # view - <%= link_to "Previous page", { :page => @pages.current.previous } if @pages.current.previous %> - <%= link_to "Next page", { :page => @pages.current.next } if @pages.current.next %> - - {Learn more}[link:classes/ActionController/Pagination.html] - - * Easy testing of both controller and rendered template through ActionController::TestCase class LoginControllerTest < ActionController::TestCase @@ -215,11 +200,11 @@ A short rundown of the major features: If Active Record is used as the model, you'll have the database debugging as well: - Processing WeblogController#create (for 127.0.0.1 at Sat Jun 19 14:04:23) - Params: {"controller"=>"weblog", "action"=>"create", + Processing PostsController#create (for 127.0.0.1 at Sat Jun 19 14:04:23) + Params: {"controller"=>"posts", "action"=>"create", "post"=>{"title"=>"this is good"} } SQL (0.000627) INSERT INTO posts (title) VALUES('this is good') - Redirected to http://test/weblog/display/5 + Redirected to http://example.com/posts/5 Completed in 0.221764 (4 reqs/sec) | DB: 0.059920 (27%) You specify a logger through a class method, such as: @@ -256,30 +241,6 @@ A short rundown of the major features: {Learn more}[link:classes/ActionController/Caching.html] -* Component requests from one controller to another - - class WeblogController < ActionController::Base - # Performs a method and then lets hello_world output its render - def delegate_action - do_other_stuff_before_hello_world - render_component :controller => "greeter", :action => "hello_world" - end - end - - class GreeterController < ActionController::Base - def hello_world - render_text "Hello World!" - end - end - - The same can be done in a view to do a partial rendering: - - Let's see a greeting: - <%= render_component :controller => "greeter", :action => "hello_world" %> - - {Learn more}[link:classes/ActionController/Components.html] - - * Powerful debugging mechanism for local requests All exceptions raised on actions performed on the request of a local user @@ -336,7 +297,7 @@ A short rundown of the major features: class WeblogController < ActionController::Base def create post = Post.create(params[:post]) - redirect_to :action => "display", :id => post.id + redirect_to :action => "show", :id => post.id end end @@ -362,7 +323,7 @@ methods: @posts = Post.find(:all) end - def display + def show @post = Post.find(params[:id]) end @@ -372,7 +333,7 @@ methods: def create @post = Post.create(params[:post]) - redirect_to :action => "display", :id => @post.id + redirect_to :action => "show", :id => @post.id end end @@ -385,47 +346,32 @@ request from the web-server (like to be Apache). And the templates look like this: - weblog/layout.erb: + weblog/layout.html.erb: <html><body> <%= yield %> </body></html> - weblog/index.erb: + weblog/index.html.erb: <% for post in @posts %> - <p><%= link_to(post.title, :action => "display", :id => post.id %></p> + <p><%= link_to(post.title, :action => "show", :id => post.id) %></p> <% end %> - weblog/display.erb: + weblog/show.html.erb: <p> - <b><%= post.title %></b><br/> - <b><%= post.content %></b> + <b><%= @post.title %></b><br/> + <b><%= @post.content %></b> </p> - weblog/new.erb: + weblog/new.html.erb: <%= form "post" %> This simple setup will list all the posts in the system on the index page, which is called by accessing /weblog/. It uses the form builder for the Active Record model to make the new screen, which in turn hands everything over to the create action (that's the default target for the form builder when given a -new model). After creating the post, it'll redirect to the display page using -an URL such as /weblog/display/5 (where 5 is the id of the post). - - -== Examples - -Action Pack ships with three examples that all demonstrate an increasingly -detailed view of the possibilities. First is blog_controller that is just a -single file for the whole MVC (but still split into separate parts). Second is -the debate_controller that uses separate template files and multiple screens. -Third is the address_book_controller that uses the layout feature to separate -template casing from content. - -Please note that you might need to change the "shebang" line to -#!/usr/local/env ruby, if your Ruby is not placed in /usr/local/bin/ruby +new model). After creating the post, it'll redirect to the show page using +an URL such as /weblog/5 (where 5 is the id of the post). -Also note that these examples are all for demonstrating using Action Pack on -its own. Not for when it's used inside of Rails. == Download @@ -460,4 +406,4 @@ And as Jim from Rake says: Feel free to submit commits or feature requests. If you send a patch, remember to update the corresponding unit tests. If fact, I prefer - new feature to be submitted in the form of new unit tests.
\ No newline at end of file + new feature to be submitted in the form of new unit tests. diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 2981f625a1..5d5d6b8c9c 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -46,10 +46,9 @@ module ActionController autoload :Base, 'action_controller/base' autoload :Benchmarking, 'action_controller/benchmarking' autoload :Caching, 'action_controller/caching' - autoload :CgiRequest, 'action_controller/cgi_process' - autoload :CgiResponse, 'action_controller/cgi_process' autoload :Cookies, 'action_controller/cookies' autoload :Dispatcher, 'action_controller/dispatcher' + autoload :Failsafe, 'action_controller/failsafe' autoload :Filters, 'action_controller/filters' autoload :Flash, 'action_controller/flash' autoload :Helpers, 'action_controller/helpers' @@ -89,6 +88,11 @@ module ActionController module Http autoload :Headers, 'action_controller/headers' end + + # DEPRECATE: Remove CGI support + autoload :CgiRequest, 'action_controller/cgi_process' + autoload :CgiResponse, 'action_controller/cgi_process' + autoload :CGIHandler, 'action_controller/cgi_process' end class CGI diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index c41b1a12cf..95cba0e411 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -83,15 +83,23 @@ module ActionController #:nodoc: end end - # Name can take one of three forms: - # * String: This would normally take the form of a path like "pages/45/notes" - # * Hash: Is treated as an implicit call to url_for, like { :controller => "pages", :action => "notes", :id => 45 } - # * Regexp: Will destroy all the matched fragments, example: - # %r{pages/\d*/notes} - # Ensure you do not specify start and finish in the regex (^$) because - # the actual filename matched looks like ./cache/filename/path.cache - # Regexp expiration is only supported on caches that can iterate over - # all keys (unlike memcached). + # Removes fragments from the cache. + # + # +key+ can take one of three forms: + # * String - This would normally take the form of a path, like + # <tt>"pages/45/notes"</tt>. + # * Hash - Treated as an implicit call to +url_for+, like + # <tt>{:controller => "pages", :action => "notes", :id => 45}</tt> + # * Regexp - Will remove any fragment that matches, so + # <tt>%r{pages/\d*/notes}</tt> might remove all notes. Make sure you + # don't use anchors in the regex (<tt>^</tt> or <tt>$</tt>) because + # the actual filename matched looks like + # <tt>./cache/filename/path.cache</tt>. Note: Regexp expiration is + # only supported on caches that can iterate over all keys (unlike + # memcached). + # + # +options+ is passed through to the cache store's <tt>delete</tt> + # method (or <tt>delete_matched</tt>, for Regexp keys.) def expire_fragment(key, options = nil) return unless cache_configured? diff --git a/actionpack/lib/action_controller/caching/pages.rb b/actionpack/lib/action_controller/caching/pages.rb index a70ed72f03..22e4fbec43 100644 --- a/actionpack/lib/action_controller/caching/pages.rb +++ b/actionpack/lib/action_controller/caching/pages.rb @@ -33,28 +33,26 @@ module ActionController #:nodoc: # # Additionally, you can expire caches using Sweepers that act on changes in the model to determine when a cache is supposed to be # expired. - # - # == Setting the cache directory - # - # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. - # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing - # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your - # web server to look in the new location for cached files. - # - # == Setting the cache extension - # - # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in - # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. - # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an - # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. module Pages def self.included(base) #:nodoc: base.extend(ClassMethods) base.class_eval do @@page_cache_directory = defined?(Rails.public_path) ? Rails.public_path : "" + ## + # :singleton-method: + # The cache directory should be the document root for the web server and is set using <tt>Base.page_cache_directory = "/document/root"</tt>. + # For Rails, this directory has already been set to Rails.public_path (which is usually set to <tt>RAILS_ROOT + "/public"</tt>). Changing + # this setting can be useful to avoid naming conflicts with files in <tt>public/</tt>, but doing so will likely require configuring your + # web server to look in the new location for cached files. cattr_accessor :page_cache_directory @@page_cache_extension = '.html' + ## + # :singleton-method: + # Most Rails requests do not have an extension, such as <tt>/weblog/new</tt>. In these cases, the page caching mechanism will add one in + # order to make it easy for the cached files to be picked up properly by the web server. By default, this cache extension is <tt>.html</tt>. + # If you want something else, like <tt>.php</tt> or <tt>.shtml</tt>, just set Base.page_cache_extension. In cases where a request already has an + # extension, such as <tt>.xml</tt> or <tt>.rss</tt>, page caching will not add an extension. This allows it to work well with RESTful apps. cattr_accessor :page_cache_extension end end diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index 45b51a7488..5d6988e1b1 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -1,184 +1,72 @@ require 'action_controller/cgi_ext' module ActionController #:nodoc: - class Base - # Process a request extracted from a CGI object and return a response. Pass false as <tt>session_options</tt> to disable - # sessions (large performance increase if sessions are not needed). The <tt>session_options</tt> are the same as for CGI::Session: - # - # * <tt>:database_manager</tt> - standard options are CGI::Session::FileStore, CGI::Session::MemoryStore, and CGI::Session::PStore - # (default). Additionally, there is CGI::Session::DRbStore and CGI::Session::ActiveRecordStore. Read more about these in - # lib/action_controller/session. - # * <tt>:session_key</tt> - the parameter name used for the session id. Defaults to '_session_id'. - # * <tt>:session_id</tt> - the session id to use. If not provided, then it is retrieved from the +session_key+ cookie, or - # automatically generated for a new session. - # * <tt>:new_session</tt> - if true, force creation of a new session. If not set, a new session is only created if none currently - # exists. If false, a new session is never created, and if none currently exists and the +session_id+ option is not set, - # an ArgumentError is raised. - # * <tt>:session_expires</tt> - the time the current session expires, as a Time object. If not set, the session will continue - # indefinitely. - # * <tt>:session_domain</tt> - the hostname domain for which this session is valid. If not set, defaults to the hostname of the - # server. - # * <tt>:session_secure</tt> - if +true+, this session will only work over HTTPS. - # * <tt>:session_path</tt> - the path for which this session applies. Defaults to the directory of the CGI script. - # * <tt>:cookie_only</tt> - if +true+ (the default), session IDs will only be accepted from cookies and not from - # the query string or POST parameters. This protects against session fixation attacks. - def self.process_cgi(cgi = CGI.new, session_options = {}) - new.process_cgi(cgi, session_options) - end - - def process_cgi(cgi, session_options = {}) #:nodoc: - process(CgiRequest.new(cgi, session_options), CgiResponse.new(cgi)).out - end - end - - class CgiRequest < AbstractRequest #:nodoc: - attr_accessor :cgi, :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, - :session_http_only=> true - } - - def initialize(cgi, session_options = {}) - @cgi = cgi - @session_options = session_options - @env = @cgi.__send__(:env_table) - super() - end - - def query_string - qs = @cgi.query_string if @cgi.respond_to?(:query_string) - if !qs.blank? - qs - else - super - end - end - - def body_stream #:nodoc: - @cgi.stdinput - end - - def cookies - @cgi.cookies.freeze - 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 + class CGIHandler + module ProperStream + def each + while line = gets + yield line end end - @session - end - def reset_session - @session.delete if defined?(@session) && @session.is_a?(CGI::Session) - @session = new_session - end - - def method_missing(method_id, *arguments) - @cgi.__send__(method_id, *arguments) rescue super - end - - private - # Delete an old session if it exists then create a new one. - def new_session - if @session_options == false - Hash.new + def read(*args) + if args.empty? + super || "" 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)) + super end end + end - def cookie_only? - session_options_with_string_keys['cookie_only'] - end + def self.dispatch_cgi(app, cgi, out = $stdout) + env = cgi.__send__(:env_table) + env.delete "HTTP_CONTENT_LENGTH" - 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 + cgi.stdinput.extend ProperStream - retry - else - raise - end - end + env["SCRIPT_NAME"] = "" if env["SCRIPT_NAME"] == "/" - def session_options_with_string_keys - @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys - end - end + env.update({ + "rack.version" => [0,1], + "rack.input" => cgi.stdinput, + "rack.errors" => $stderr, + "rack.multithread" => false, + "rack.multiprocess" => true, + "rack.run_once" => false, + "rack.url_scheme" => ["yes", "on", "1"].include?(env["HTTPS"]) ? "https" : "http" + }) - class CgiResponse < AbstractResponse #:nodoc: - def initialize(cgi) - @cgi = cgi - super() - end - - def out(output = $stdout) - output.binmode if output.respond_to?(:binmode) - output.sync = false if output.respond_to?(:sync=) + env["QUERY_STRING"] ||= "" + env["HTTP_VERSION"] ||= env["SERVER_PROTOCOL"] + env["REQUEST_PATH"] ||= "/" + env.delete "PATH_INFO" if env["PATH_INFO"] == "" + status, headers, body = app.call(env) begin - output.write(@cgi.header(@headers)) - - if @cgi.__send__(:env_table)['REQUEST_METHOD'] == 'HEAD' - return - elsif @body.respond_to?(:call) - # Flush the output now in case the @body Proc uses - # #syswrite. - output.flush if output.respond_to?(:flush) - @body.call(self, output) - else - output.write(@body) - end - - output.flush if output.respond_to?(:flush) - rescue Errno::EPIPE, Errno::ECONNRESET - # lost connection to parent process, ignore output + out.binmode if out.respond_to?(:binmode) + out.sync = false if out.respond_to?(:sync=) + + headers['Status'] = status.to_s + out.write(cgi.header(headers)) + + body.each { |part| + out.write part + out.flush if out.respond_to?(:flush) + } + ensure + body.close if body.respond_to?(:close) end end end + + class CgiRequest #:nodoc: + DEFAULT_SESSION_OPTIONS = { + :database_manager => CGI::Session::CookieStore, + :prefix => "ruby_sess.", + :session_path => "/", + :session_key => "_session_id", + :cookie_only => true, + :session_http_only => true + } + end end diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 47199af2b4..203f6b1683 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -24,8 +24,7 @@ module ActionController end end - # Backward-compatible class method takes CGI-specific args. Deprecated - # in favor of Dispatcher.new(output, request, response).dispatch. + # DEPRECATE: Remove CGI support def dispatch(cgi = nil, session_options = CgiRequest::DEFAULT_SESSION_OPTIONS, output = $stdout) new(output).dispatch_cgi(cgi, session_options) end @@ -43,60 +42,19 @@ module ActionController callback = ActiveSupport::Callbacks::Callback.new(:prepare_dispatch, block, :identifier => identifier) @prepare_dispatch_callbacks.replace_or_append!(callback) end - - # If the block raises, send status code as a last-ditch response. - def failsafe_response(fallback_output, status, originating_exception = nil) - yield - rescue Exception => exception - begin - log_failsafe_exception(status, originating_exception || exception) - body = failsafe_response_body(status) - fallback_output.write "Status: #{status}\r\nContent-Type: text/html\r\n\r\n#{body}" - nil - rescue Exception => failsafe_error # Logger or IO errors - $stderr.puts "Error during failsafe response: #{failsafe_error}" - $stderr.puts "(originally #{originating_exception})" if originating_exception - end - end - - private - def failsafe_response_body(status) - error_path = "#{error_file_path}/#{status.to_s[0..2]}.html" - - if File.exist?(error_path) - File.read(error_path) - else - "<html><body><h1>#{status}</h1></body></html>" - end - end - - def log_failsafe_exception(status, exception) - message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: #{status}\n" - message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception - failsafe_logger.fatal message - end - - def failsafe_logger - if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil? - ::RAILS_DEFAULT_LOGGER - else - Logger.new($stderr) - end - end end cattr_accessor :middleware self.middleware = MiddlewareStack.new - - cattr_accessor :error_file_path - self.error_file_path = Rails.public_path if defined?(Rails.public_path) + self.middleware.use "ActionController::Failsafe" include ActiveSupport::Callbacks define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch + # DEPRECATE: Remove arguments def initialize(output = $stdout, request = nil, response = nil) @output, @request, @response = output, request, response - @app = @@middleware.build(lambda { |env| self._call(env) }) + @app = @@middleware.build(lambda { |env| self.dup._call(env) }) end def dispatch_unlocked @@ -120,14 +78,9 @@ module ActionController end end + # DEPRECATE: Remove CGI support def dispatch_cgi(cgi, session_options) - if cgi ||= self.class.failsafe_response(@output, '400 Bad Request') { CGI.new } - @request = CgiRequest.new(cgi, session_options) - @response = CgiResponse.new(cgi) - dispatch - end - rescue Exception => exception - failsafe_rescue exception + CGIHandler.dispatch_cgi(self, cgi, @output) end def call(env) @@ -160,34 +113,24 @@ module ActionController Base.logger.flush end - def mark_as_test_request! - @test_request = true - self - end - - def test_request? - @test_request - end - def checkin_connections # Don't return connection (and peform implicit rollback) if this request is a part of integration test - return if test_request? + # TODO: This callback should have direct access to env + return if @request.key?("action_controller.test") ActiveRecord::Base.clear_active_connections! end protected def handle_request @controller = Routing::Routes.recognize(@request) - @controller.process(@request, @response).out(@output) + @controller.process(@request, @response).out end def failsafe_rescue(exception) - self.class.failsafe_response(@output, '500 Internal Server Error', exception) do - if @controller ||= (::ApplicationController rescue Base) - @controller.process_with_exception(@request, @response, exception).out(@output) - else - raise exception - end + if @controller ||= (::ApplicationController rescue Base) + @controller.process_with_exception(@request, @response, exception).out + else + raise exception end end end diff --git a/actionpack/lib/action_controller/failsafe.rb b/actionpack/lib/action_controller/failsafe.rb new file mode 100644 index 0000000000..bb6ef39470 --- /dev/null +++ b/actionpack/lib/action_controller/failsafe.rb @@ -0,0 +1,52 @@ +module ActionController + class Failsafe + cattr_accessor :error_file_path + self.error_file_path = Rails.public_path if defined?(Rails.public_path) + + def initialize(app) + @app = app + end + + def call(env) + @app.call(env) + rescue Exception => exception + # Reraise exception in test environment + if env["action_controller.test"] + raise exception + else + failsafe_response(exception) + end + end + + private + def failsafe_response(exception) + log_failsafe_exception(exception) + [500, {'Content-Type' => 'text/html'}, failsafe_response_body] + rescue Exception => failsafe_error # Logger or IO errors + $stderr.puts "Error during failsafe response: #{failsafe_error}" + end + + def failsafe_response_body + error_path = "#{self.class.error_file_path}/500.html" + if File.exist?(error_path) + File.read(error_path) + else + "<html><body><h1>500 Internal Server Error</h1></body></html>" + end + end + + def log_failsafe_exception(exception) + message = "/!\\ FAILSAFE /!\\ #{Time.now}\n Status: 500 Internal Server Error\n" + message << " #{exception}\n #{exception.backtrace.join("\n ")}" if exception + failsafe_logger.fatal(message) + end + + def failsafe_logger + if defined?(::RAILS_DEFAULT_LOGGER) && !::RAILS_DEFAULT_LOGGER.nil? + ::RAILS_DEFAULT_LOGGER + else + Logger.new($stderr) + end + end + end +end diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index 65e3eed678..eeabe5b845 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -9,13 +9,17 @@ module ActionController # multiple sessions and run them side-by-side, you can also mimic (to some # limited extent) multiple simultaneous users interacting with your system. # - # Typically, you will instantiate a new session using IntegrationTest#open_session, - # rather than instantiating Integration::Session directly. + # Typically, you will instantiate a new session using + # IntegrationTest#open_session, rather than instantiating + # Integration::Session directly. class Session include Test::Unit::Assertions include ActionController::TestCase::Assertions include ActionController::TestProcess + # Rack application to use + attr_accessor :application + # The integer HTTP status code of the last request. attr_reader :status @@ -57,7 +61,8 @@ module ActionController end # Create and initialize a new Session instance. - def initialize + def initialize(app) + @application = app reset! end @@ -76,11 +81,13 @@ module ActionController self.host = "www.example.com" self.remote_addr = "127.0.0.1" - self.accept = "text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5" + self.accept = "text/xml,application/xml,application/xhtml+xml," + + "text/html;q=0.9,text/plain;q=0.8,image/png," + + "*/*;q=0.5" unless defined? @named_routes_configured # install the named routes in this session instance. - klass = class<<self; self; end + klass = class << self; self; end Routing::Routes.install_helpers(klass) # the helpers are made protected by default--we make them public for @@ -94,7 +101,7 @@ module ActionController # # session.https! # session.https!(false) - def https!(flag=true) + def https!(flag = true) @https = flag end @@ -164,17 +171,21 @@ module ActionController # Performs a GET request with the given parameters. # - # - +path+: The URI (as a String) on which you want to perform a GET request. - # - +parameters+: The HTTP parameters that you want to pass. This may be +nil+, + # - +path+: The URI (as a String) on which you want to perform a GET + # request. + # - +parameters+: The HTTP parameters that you want to pass. This may + # be +nil+, # a Hash, or a String that is appropriately encoded - # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>). + # (<tt>application/x-www-form-urlencoded</tt> or + # <tt>multipart/form-data</tt>). # - +headers+: Additional HTTP headers to pass, as a Hash. The keys will # automatically be upcased, with the prefix 'HTTP_' added if needed. # - # This method returns an AbstractResponse object, which one can use to inspect - # the details of the response. Furthermore, if this method was called from an - # ActionController::IntegrationTest object, then that object's <tt>@response</tt> - # instance variable will point to the same response object. + # This method returns an AbstractResponse object, which one can use to + # inspect the details of the response. Furthermore, if this method was + # called from an ActionController::IntegrationTest object, then that + # object's <tt>@response</tt> instance variable will point to the same + # response object. # # You can also perform POST, PUT, DELETE, and HEAD requests with +post+, # +put+, +delete+, and +head+. @@ -182,22 +193,26 @@ module ActionController process :get, path, parameters, headers end - # Performs a POST request with the given parameters. See get() for more details. + # Performs a POST request with the given parameters. See get() for more + # details. def post(path, parameters = nil, headers = nil) process :post, path, parameters, headers end - # Performs a PUT request with the given parameters. See get() for more details. + # Performs a PUT request with the given parameters. See get() for more + # details. def put(path, parameters = nil, headers = nil) process :put, path, parameters, headers end - # Performs a DELETE request with the given parameters. See get() for more details. + # Performs a DELETE request with the given parameters. See get() for + # more details. def delete(path, parameters = nil, headers = nil) process :delete, path, parameters, headers end - # Performs a HEAD request with the given parameters. See get() for more details. + # Performs a HEAD request with the given parameters. See get() for more + # details. def head(path, parameters = nil, headers = nil) process :head, path, parameters, headers end @@ -212,7 +227,8 @@ module ActionController def xml_http_request(request_method, path, parameters = nil, headers = nil) headers ||= {} headers['X-Requested-With'] = 'XMLHttpRequest' - headers['Accept'] ||= 'text/javascript, text/html, application/xml, text/xml, */*' + headers['Accept'] ||= 'text/javascript, text/html, application/xml, ' + + 'text/xml, */*' process(request_method, path, parameters, headers) end @@ -221,7 +237,9 @@ module ActionController # Returns the URL for the given options, according to the rules specified # in the application's routes. def url_for(options) - controller ? controller.url_for(options) : generic_url_rewriter.rewrite(options) + controller ? + controller.url_for(options) : + generic_url_rewriter.rewrite(options) end private @@ -247,17 +265,34 @@ module ActionController data = nil end + env["QUERY_STRING"] ||= "" + + data = data.is_a?(IO) ? data : StringIO.new(data || '') + env.update( - "REQUEST_METHOD" => method.to_s.upcase, + "REQUEST_METHOD" => method.to_s.upcase, + "SERVER_NAME" => host, + "SERVER_PORT" => (https? ? "443" : "80"), + "HTTPS" => https? ? "on" : "off", + "rack.url_scheme" => https? ? "https" : "http", + "SCRIPT_NAME" => "", + "REQUEST_URI" => path, "HTTP_HOST" => host, "REMOTE_ADDR" => remote_addr, - "SERVER_PORT" => (https? ? "443" : "80"), "CONTENT_TYPE" => "application/x-www-form-urlencoded", "CONTENT_LENGTH" => data ? data.length.to_s : nil, "HTTP_COOKIE" => encode_cookies, - "HTTPS" => https? ? "on" : "off", - "HTTP_ACCEPT" => accept + "HTTP_ACCEPT" => accept, + + "rack.version" => [0,1], + "rack.input" => data, + "rack.errors" => StringIO.new, + "rack.multithread" => true, + "rack.multiprocess" => true, + "rack.run_once" => false, + + "action_controller.test" => true ) (headers || {}).each do |key, value| @@ -272,48 +307,43 @@ module ActionController ActionController::Base.clear_last_instantiation! - env['rack.input'] = data.is_a?(IO) ? data : StringIO.new(data || '') - @status, @headers, result_body = ActionController::Dispatcher.new.mark_as_test_request!.call(env) + app = Rack::Lint.new(@application) + + status, headers, body = app.call(env) @request_count += 1 - @controller = ActionController::Base.last_instantiation - @request = @controller.request - @response = @controller.response + if @controller = ActionController::Base.last_instantiation + @request = @controller.request + @response = @controller.response - # Decorate the response with the standard behavior of the TestResponse - # so that things like assert_response can be used in integration - # tests. - @response.extend(TestResponseBehavior) + # Decorate the response with the standard behavior of the + # TestResponse so that things like assert_response can be + # used in integration tests. + @response.extend(TestResponseBehavior) + end @html_document = nil - # Inject status back in for backwords compatibility with CGI - @headers['Status'] = @status - - @status, @status_message = @status.split(/ /) - @status = @status.to_i + @status = status.to_i + @status_message = StatusCodes::STATUS_CODES[@status] - cgi_headers = Hash.new { |h,k| h[k] = [] } - @headers.each do |key, value| - cgi_headers[key.downcase] << value - end - cgi_headers['set-cookie'] = cgi_headers['set-cookie'].first - @headers = cgi_headers + @headers = Rack::Utils::HeaderHash.new(headers) - @response.headers['cookie'] ||= [] - (@headers['set-cookie'] || []).each do |cookie| + (@headers['Set-Cookie'] || []).each do |cookie| name, value = cookie.match(/^([^=]*)=([^;]*);/)[1,2] @cookies[name] = value - - # Fake CGI cookie header - # DEPRECATE: Use response.headers["Set-Cookie"] instead - @response.headers['cookie'] << CGI::Cookie::new("name" => name, "value" => value) end - return status + @body = "" + body.each { |part| @body << part } + + return @status rescue MultiPartNeededException boundary = "----------XnJLe9ZIbbGUYtzPQJ16u1" - status = process(method, path, multipart_body(parameters, boundary), (headers || {}).merge({"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"})) + status = process(method, path, + multipart_body(parameters, boundary), + (headers || {}).merge( + {"CONTENT_TYPE" => "multipart/form-data; boundary=#{boundary}"})) return status end @@ -335,7 +365,7 @@ module ActionController "SERVER_PORT" => https? ? "443" : "80", "HTTPS" => https? ? "on" : "off" } - ActionController::UrlRewriter.new(ActionController::RackRequest.new(env), {}) + UrlRewriter.new(RackRequest.new(env), {}) end def name_with_prefix(prefix, name) @@ -349,9 +379,13 @@ module ActionController raise MultiPartNeededException elsif Hash === parameters return nil if parameters.empty? - parameters.map { |k,v| requestify(v, name_with_prefix(prefix, k)) }.join("&") + parameters.map { |k,v| + requestify(v, name_with_prefix(prefix, k)) + }.join("&") elsif Array === parameters - parameters.map { |v| requestify(v, name_with_prefix(prefix, "")) }.join("&") + parameters.map { |v| + requestify(v, name_with_prefix(prefix, "")) + }.join("&") elsif prefix.nil? parameters else @@ -458,7 +492,8 @@ EOF # can use this method to open multiple sessions that ought to be tested # simultaneously. def open_session - session = Integration::Session.new + application = ActionController::Dispatcher.new + session = Integration::Session.new(application) # delegate the fixture accessors back to the test instance extras = Module.new { attr_accessor :delegate, :test_result } @@ -466,12 +501,16 @@ EOF self.class.fixture_table_names.each do |table_name| name = table_name.tr(".", "_") next unless respond_to?(name) - extras.__send__(:define_method, name) { |*args| delegate.send(name, *args) } + extras.__send__(:define_method, name) { |*args| + delegate.send(name, *args) + } end end # delegate add_assertion to the test case - extras.__send__(:define_method, :add_assertion) { test_result.add_assertion } + extras.__send__(:define_method, :add_assertion) { + test_result.add_assertion + } session.extend(extras) session.delegate = self session.test_result = @_result @@ -599,7 +638,8 @@ EOF # would potentially have to set their values for both Test::Unit::TestCase # ActionController::IntegrationTest, since by the time the value is set on # TestCase, IntegrationTest has already been defined and cannot inherit - # changes to those variables. So, we make those two attributes copy-on-write. + # changes to those variables. So, we make those two attributes + # copy-on-write. class << self def use_transactional_fixtures=(flag) #:nodoc: diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb index 58d7b53e31..568f893c6c 100644 --- a/actionpack/lib/action_controller/rack_process.rb +++ b/actionpack/lib/action_controller/rack_process.rb @@ -55,14 +55,7 @@ module ActionController #:nodoc: end def cookies - return {} unless @env["HTTP_COOKIE"] - - unless @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] - @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] - @env["rack.request.cookie_hash"] = CGI::Cookie::parse(@env["rack.request.cookie_string"]) - end - - @env["rack.request.cookie_hash"] + Rack::Request.new(@env).cookies end def server_port @@ -164,7 +157,7 @@ end_msg @status || super end - def out(output = $stdout, &block) + def out(&block) # Nasty hack because CGI sessions are closed after the normal # prepare! statement set_cookies! diff --git a/actionpack/lib/action_controller/rescue.rb b/actionpack/lib/action_controller/rescue.rb index 9c24c3def4..d7b0e96c93 100644 --- a/actionpack/lib/action_controller/rescue.rb +++ b/actionpack/lib/action_controller/rescue.rb @@ -1,13 +1,19 @@ module ActionController #:nodoc: - # Actions that fail to perform as expected throw exceptions. These exceptions can either be rescued for the public view - # (with a nice user-friendly explanation) or for the developers view (with tons of debugging information). The developers view - # is already implemented by the Action Controller, but the public view should be tailored to your specific application. - # - # The default behavior for public exceptions is to render a static html file with the name of the error code thrown. If no such - # file exists, an empty response is sent with the correct status code. + # Actions that fail to perform as expected throw exceptions. These + # exceptions can either be rescued for the public view (with a nice + # user-friendly explanation) or for the developers view (with tons of + # debugging information). The developers view is already implemented by + # the Action Controller, but the public view should be tailored to your + # specific application. # - # You can override what constitutes a local request by overriding the <tt>local_request?</tt> method in your own controller. - # Custom rescue behavior is achieved by overriding the <tt>rescue_action_in_public</tt> and <tt>rescue_action_locally</tt> methods. + # The default behavior for public exceptions is to render a static html + # file with the name of the error code thrown. If no such file exists, an + # empty response is sent with the correct status code. + # + # You can override what constitutes a local request by overriding the + # <tt>local_request?</tt> method in your own controller. Custom rescue + # behavior is achieved by overriding the <tt>rescue_action_in_public</tt> + # and <tt>rescue_action_locally</tt> methods. module Rescue LOCALHOST = '127.0.0.1'.freeze @@ -32,6 +38,9 @@ module ActionController #:nodoc: 'ActionView::TemplateError' => 'template_error' } + RESCUES_TEMPLATE_PATH = ActionView::PathSet::Path.new( + "#{File.dirname(__FILE__)}/templates", true) + def self.included(base) #:nodoc: base.cattr_accessor :rescue_responses base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE) @@ -56,12 +65,15 @@ module ActionController #:nodoc: end protected - # Exception handler called when the performance of an action raises an exception. + # Exception handler called when the performance of an action raises + # an exception. def rescue_action(exception) - rescue_with_handler(exception) || rescue_action_without_handler(exception) + rescue_with_handler(exception) || + rescue_action_without_handler(exception) end - # Overwrite to implement custom logging of errors. By default logs as fatal. + # Overwrite to implement custom logging of errors. By default + # logs as fatal. def log_error(exception) #:doc: ActiveSupport::Deprecation.silence do if ActionView::TemplateError === exception @@ -75,16 +87,19 @@ module ActionController #:nodoc: end end - # Overwrite to implement public exception handling (for requests answering false to <tt>local_request?</tt>). By - # default will call render_optional_error_file. Override this method to provide more user friendly error messages. + # Overwrite to implement public exception handling (for requests + # answering false to <tt>local_request?</tt>). By default will call + # render_optional_error_file. Override this method to provide more + # user friendly error messages. def rescue_action_in_public(exception) #:doc: render_optional_error_file response_code_for_rescue(exception) end - - # Attempts to render a static error page based on the <tt>status_code</tt> thrown, - # or just return headers if no such file exists. For example, if a 500 error is - # being handled Rails will first attempt to render the file at <tt>public/500.html</tt>. - # If the file doesn't exist, the body of the response will be left empty. + + # Attempts to render a static error page based on the + # <tt>status_code</tt> thrown, or just return headers if no such file + # exists. For example, if a 500 error is being handled Rails will first + # attempt to render the file at <tt>public/500.html</tt>. If the file + # doesn't exist, the body of the response will be left empty. def render_optional_error_file(status_code) status = interpret_status(status_code) path = "#{Rails.public_path}/#{status[0,3]}.html" @@ -106,11 +121,13 @@ module ActionController #:nodoc: # a controller action. def rescue_action_locally(exception) @template.instance_variable_set("@exception", exception) - @template.instance_variable_set("@rescues_path", File.dirname(rescues_path("stub"))) - @template.instance_variable_set("@contents", @template.render(:file => template_path_for_local_rescue(exception))) + @template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH) + @template.instance_variable_set("@contents", + @template.render(:file => template_path_for_local_rescue(exception))) response.content_type = Mime::HTML - render_for_file(rescues_path("layout"), response_code_for_rescue(exception)) + render_for_file(rescues_path("layout"), + response_code_for_rescue(exception)) end def rescue_action_without_handler(exception) @@ -138,7 +155,7 @@ module ActionController #:nodoc: end def rescues_path(template_name) - "#{File.dirname(__FILE__)}/templates/rescues/#{template_name}.erb" + RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"] end def template_path_for_local_rescue(exception) @@ -150,8 +167,9 @@ module ActionController #:nodoc: end def clean_backtrace(exception) - defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ? - Rails.backtrace_cleaner.clean(exception.backtrace) : exception.backtrace + defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ? + Rails.backtrace_cleaner.clean(exception.backtrace) : + exception.backtrace end end end diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index b6cfe2dd68..e8988aa737 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -283,7 +283,12 @@ module ActionController # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action. # * <tt>:controller</tt> - Specify the controller name for the routes. # * <tt>:singular</tt> - Specify the singular name used in the member routes. - # * <tt>:requirements</tt> - Set custom routing parameter requirements. + # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either + # regular expressions (which must match for the route to match) or extra parameters. For example: + # + # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' } + # + # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller. # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes. # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example: # # products_path == '/productos' diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index efd474097e..da9b56fdf9 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -83,9 +83,11 @@ module ActionController # This sets up +blog+ as the default controller if no other is specified. # This means visiting '/' would invoke the blog controller. # - # More formally, you can define defaults in a route with the <tt>:defaults</tt> key. + # More formally, you can include arbitrary parameters in the route, thus: # - # map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' } + # map.connect ':controller/:action/:id', :action => 'show', :page => 'Dashboard' + # + # This will pass the :page parameter to all incoming requests that match this route. # # Note: The default routes, as provided by the Rails generator, make all actions in every # controller accessible via GET requests. You should consider removing them or commenting diff --git a/actionpack/lib/action_controller/session/active_record_store.rb b/actionpack/lib/action_controller/session/active_record_store.rb index 1e8eb57acb..fadf2a6b32 100644 --- a/actionpack/lib/action_controller/session/active_record_store.rb +++ b/actionpack/lib/action_controller/session/active_record_store.rb @@ -56,6 +56,8 @@ class CGI class ActiveRecordStore # The default Active Record class. class Session < ActiveRecord::Base + ## + # :singleton-method: # Customizable data column name. Defaults to 'data'. cattr_accessor :data_column_name self.data_column_name = 'data' @@ -166,17 +168,25 @@ class CGI # binary session data in a +text+ column. For higher performance, # store in a +blob+ column instead and forgo the Base64 encoding. class SqlBypass + ## + # :singleton-method: # Use the ActiveRecord::Base.connection by default. cattr_accessor :connection + ## + # :singleton-method: # The table name defaults to 'sessions'. cattr_accessor :table_name @@table_name = 'sessions' + ## + # :singleton-method: # The session id field defaults to 'session_id'. cattr_accessor :session_id_column @@session_id_column = 'session_id' + ## + # :singleton-method: # The data field defaults to 'data'. cattr_accessor :data_column @@data_column = 'data' diff --git a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb index b5483eccae..669da1b26e 100644 --- a/actionpack/lib/action_controller/templates/rescues/diagnostics.erb +++ b/actionpack/lib/action_controller/templates/rescues/diagnostics.erb @@ -6,6 +6,6 @@ </h1> <pre><%=h @exception.clean_message %></pre> -<%= render(:file => @rescues_path + "/_trace.erb") %> +<%= render :file => @rescues_path["rescues/_trace.erb"] %> -<%= render(:file => @rescues_path + "/_request_and_response.erb") %> +<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %> diff --git a/actionpack/lib/action_controller/templates/rescues/template_error.erb b/actionpack/lib/action_controller/templates/rescues/template_error.erb index 76fa3df89d..2e34e03bd5 100644 --- a/actionpack/lib/action_controller/templates/rescues/template_error.erb +++ b/actionpack/lib/action_controller/templates/rescues/template_error.erb @@ -15,7 +15,7 @@ <% @real_exception = @exception @exception = @exception.original_exception || @exception %> -<%= render(:file => @rescues_path + "/_trace.erb") %> +<%= render :file => @rescues_path["rescues/_trace.erb"] %> <% @exception = @real_exception %> -<%= render(:file => @rescues_path + "/_request_and_response.erb") %> +<%= render :file => @rescues_path["rescues/_request_and_response.erb"] %> diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index da1f283deb..33517ffb7b 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -5,7 +5,7 @@ module ActionView #:nodoc: class MissingTemplate < ActionViewError #:nodoc: def initialize(paths, path, template_format = nil) full_template_path = path.include?('.') ? path : "#{path}.erb" - display_paths = paths.join(':') + display_paths = paths.compact.join(":") template_type = (path =~ /layouts/i) ? 'layout' : 'template' super("Missing #{template_type} #{full_template_path} in view path #{display_paths}") end @@ -183,13 +183,17 @@ module ActionView #:nodoc: @@exempt_from_layout.merge(regexps) end + @@debug_rjs = false + ## + # :singleton-method: # Specify whether RJS responses should be wrapped in a try/catch block # that alert()s the caught exception (and then re-raises it). - @@debug_rjs = false cattr_accessor :debug_rjs - # A warning will be displayed whenever an action results in a cache miss on your view paths. @@warn_cache_misses = false + ## + # :singleton-method: + # A warning will be displayed whenever an action results in a cache miss on your view paths. cattr_accessor :warn_cache_misses attr_internal :request @@ -275,7 +279,7 @@ module ActionView #:nodoc: if defined? @template_format @template_format elsif controller && controller.respond_to?(:request) - @template_format = controller.request.template_format + @template_format = controller.request.template_format.to_sym else @template_format = :html end @@ -327,9 +331,6 @@ module ActionView #:nodoc: elsif (first_render = @_render_stack.first) && first_render.respond_to?(:format_and_extension) && (template = self.view_paths["#{template_file_name}.#{first_render.format_and_extension}"]) template - elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] - @template_format = :html - template else template = Template.new(template_path, view_paths) diff --git a/actionpack/lib/action_view/helpers/tag_helper.rb b/actionpack/lib/action_view/helpers/tag_helper.rb index 2bdd960f4c..3b301248ff 100644 --- a/actionpack/lib/action_view/helpers/tag_helper.rb +++ b/actionpack/lib/action_view/helpers/tag_helper.rb @@ -97,7 +97,7 @@ module ActionView # Returns an escaped version of +html+ without affecting existing escaped entities. # # ==== Examples - # escape_once("1 > 2 & 3") + # escape_once("1 < 2 & 3") # # => "1 < 2 & 3" # # escape_once("<< Accept & Checkout") diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb index ecb6103ade..9c8b8ade1e 100644 --- a/actionpack/lib/action_view/paths.rb +++ b/actionpack/lib/action_view/paths.rb @@ -41,7 +41,7 @@ module ActionView #:nodoc: class Path #:nodoc: attr_reader :path, :paths - delegate :to_s, :to_str, :hash, :inspect, :to => :path + delegate :hash, :inspect, :to => :path def initialize(path, load = false) raise ArgumentError, "path already is a Path class" if path.is_a?(Path) @@ -49,6 +49,14 @@ module ActionView #:nodoc: reload! if load end + def to_s + if defined?(RAILS_ROOT) + path.to_s.sub(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') + else + path.to_s + end + end + def ==(path) to_str == path.to_str end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 52378e049e..e32b7688d0 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -9,9 +9,9 @@ module ActionView #:nodoc: def initialize(template_path, load_paths = []) template_path = template_path.dup + @load_path, @filename = find_full_path(template_path, load_paths) @base_path, @name, @format, @extension = split(template_path) @base_path.to_s.gsub!(/\/$/, '') # Push to split method - @load_path, @filename = find_full_path(template_path, load_paths) # Extend with partial super powers extend RenderablePartial if @name =~ /^_/ diff --git a/actionpack/lib/action_view/template_handlers/erb.rb b/actionpack/lib/action_view/template_handlers/erb.rb index f8d3da15be..e3120ba267 100644 --- a/actionpack/lib/action_view/template_handlers/erb.rb +++ b/actionpack/lib/action_view/template_handlers/erb.rb @@ -3,6 +3,8 @@ module ActionView class ERB < TemplateHandler include Compilable + ## + # :singleton-method: # Specify trim mode for the ERB compiler. Defaults to '-'. # See ERb documentation for suitable values. cattr_accessor :erb_trim_mode diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template_handlers/rjs.rb index a700655c9a..41a1fddb47 100644 --- a/actionpack/lib/action_view/template_handlers/rjs.rb +++ b/actionpack/lib/action_view/template_handlers/rjs.rb @@ -4,6 +4,7 @@ module ActionView include Compilable def compile(template) + "@template_format = :html;" + "controller.response.content_type ||= Mime::JS;" + "update_page do |page|;#{template.source}\nend" end diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 10c65acd9a..7e7f488df6 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -67,7 +67,7 @@ class PageCachingTest < ActionController::TestCase def teardown FileUtils.rm_rf(File.dirname(FILE_STORE_PATH)) - + ActionController::Routing::Routes.clear! ActionController::Base.perform_caching = false end @@ -401,7 +401,7 @@ class ActionCacheTest < ActionController::TestCase def test_xml_version_of_resource_is_treated_as_different_cache with_routing do |set| - ActionController::Routing::Routes.draw do |map| + set.draw do |map| map.connect ':controller/:action.:format' map.connect ':controller/:action' end diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb deleted file mode 100644 index ac1c8abc59..0000000000 --- a/actionpack/test/controller/cgi_test.rb +++ /dev/null @@ -1,262 +0,0 @@ -require 'abstract_unit' - -class BaseCgiTest < Test::Unit::TestCase - def setup - @request_hash = { - "HTTP_MAX_FORWARDS" => "10", - "SERVER_NAME" => "glu.ttono.us:8007", - "FCGI_ROLE" => "RESPONDER", - "AUTH_TYPE" => "Basic", - "HTTP_X_FORWARDED_HOST" => "glu.ttono.us", - "HTTP_ACCEPT_CHARSET" => "UTF-8", - "HTTP_ACCEPT_ENCODING" => "gzip, deflate", - "HTTP_CACHE_CONTROL" => "no-cache, max-age=0", - "HTTP_PRAGMA" => "no-cache", - "HTTP_USER_AGENT" => "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", - "PATH_INFO" => "/homepage/", - "HTTP_ACCEPT_LANGUAGE" => "en", - "HTTP_NEGOTIATE" => "trans", - "HTTP_HOST" => "glu.ttono.us:8007", - "HTTP_REFERER" => "http://www.google.com/search?q=glu.ttono.us", - "HTTP_FROM" => "googlebot", - "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", - "REMOTE_HOST" => "google.com", - "REMOTE_IDENT" => "kevin", - "REMOTE_USER" => "kevin", - "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", - "PATH_TRANSLATED" => "/home/kevinc/sites/typo/public/homepage/", - "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"} - @cgi = CGI.new - class << @cgi; attr_accessor :env_table end - @cgi.env_table = @request_hash - @request = ActionController::CgiRequest.new(@cgi) - end - - def default_test; end - - private - - def set_content_data(data) - @request.env['REQUEST_METHOD'] = 'POST' - @request.env['CONTENT_LENGTH'] = data.length - @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - @request.env['RAW_POST_DATA'] = data - end -end - -class CgiRequestTest < BaseCgiTest - def test_proxy_request - assert_equal 'glu.ttono.us', @request.host_with_port - end - - def test_http_host - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash['HTTP_HOST'] = "rubyonrails.org:8080" - assert_equal "rubyonrails.org:8080", @request.host_with_port - - @request_hash['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" - assert_equal "www.secondhost.org", @request.host(true) - end - - def test_http_host_with_default_port_overrides_server_port - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash['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 - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash.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 - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash.delete "HTTP_HOST" - @request_hash.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 - @request_hash['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 - @request_hash['HTTP_X_FORWARDED_PROTO'] = "https" - @request_hash['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" - assert_equal "glu.ttono.us", @request.host_with_port - end - - def test_host_if_ipv6_reference - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash['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 - @request_hash.delete "HTTP_X_FORWARDED_HOST" - @request_hash['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" - assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host - end - - def test_cgi_environment_variables - assert_equal "Basic", @request.auth_type - assert_equal 0, @request.content_length - assert_equal nil, @request.content_type - assert_equal "CGI/1.1", @request.gateway_interface - assert_equal "*/*", @request.accept - assert_equal "UTF-8", @request.accept_charset - assert_equal "gzip, deflate", @request.accept_encoding - assert_equal "en", @request.accept_language - assert_equal "no-cache, max-age=0", @request.cache_control - assert_equal "googlebot", @request.from - assert_equal "glu.ttono.us", @request.host - assert_equal "trans", @request.negotiate - assert_equal "no-cache", @request.pragma - assert_equal "http://www.google.com/search?q=glu.ttono.us", @request.referer - assert_equal "Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en)", @request.user_agent - assert_equal "/homepage/", @request.path_info - assert_equal "/home/kevinc/sites/typo/public/homepage/", @request.path_translated - assert_equal "", @request.query_string - assert_equal "207.7.108.53", @request.remote_addr - assert_equal "google.com", @request.remote_host - assert_equal "kevin", @request.remote_ident - assert_equal "kevin", @request.remote_user - assert_equal :get, @request.request_method - assert_equal "/dispatch.fcgi", @request.script_name - assert_equal "glu.ttono.us:8007", @request.server_name - assert_equal 8007, @request.server_port - assert_equal "HTTP/1.1", @request.server_protocol - assert_equal "lighttpd", @request.server_software - end - - def test_cookie_syntax_resilience - cookies = CGI::Cookie::parse(@request_hash["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 CgiRequestParamsParsingTest < BaseCgiTest - def test_doesnt_break_when_content_type_has_charset - set_content_data 'flamenco=love' - - 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 CgiRequestContentTypeTest < BaseCgiTest - def test_html_content_type_verification - @request.env['CONTENT_TYPE'] = Mime::HTML.to_s - assert @request.content_type.verify_request? - end - - def test_xml_content_type_verification - @request.env['CONTENT_TYPE'] = Mime::XML.to_s - assert !@request.content_type.verify_request? - end -end - -class CgiRequestMethodTest < BaseCgiTest - def test_get - assert_equal :get, @request.request_method - end - - def test_post - @request.env['REQUEST_METHOD'] = 'POST' - assert_equal :post, @request.request_method - end - - def test_put - set_content_data '_method=put' - - assert_equal :put, @request.request_method - end - - def test_delete - set_content_data '_method=delete' - - assert_equal :delete, @request.request_method - end -end - -class CgiRequestNeedsRewoundTest < BaseCgiTest - def test_body_should_be_rewound - data = 'foo' - fake_cgi = Struct.new(:env_table, :query_string, :stdinput).new(@request_hash, '', StringIO.new(data)) - fake_cgi.env_table['CONTENT_LENGTH'] = data.length - fake_cgi.env_table['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' - - # Read the request body by parsing params. - request = ActionController::CgiRequest.new(fake_cgi) - request.request_parameters - - # Should have rewound the body. - assert_equal 0, request.body.pos - end -end - -uses_mocha 'CGI Response' do - class CgiResponseTest < BaseCgiTest - def setup - super - @cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n") - @response = ActionController::CgiResponse.new(@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 - @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 -end diff --git a/actionpack/test/controller/dispatcher_test.rb b/actionpack/test/controller/dispatcher_test.rb index 61bfb2b6e9..30dda87bb4 100644 --- a/actionpack/test/controller/dispatcher_test.rb +++ b/actionpack/test/controller/dispatcher_test.rb @@ -6,7 +6,6 @@ class DispatcherTest < Test::Unit::TestCase Dispatcher = ActionController::Dispatcher def setup - @output = StringIO.new ENV['REQUEST_METHOD'] = 'GET' # Clear callbacks as they are redefined by Dispatcher#define_dispatcher_callbacks @@ -16,7 +15,7 @@ class DispatcherTest < Test::Unit::TestCase Dispatcher.stubs(:require_dependency) - @dispatcher = Dispatcher.new(@output) + @dispatcher = Dispatcher.new end def teardown @@ -25,17 +24,17 @@ class DispatcherTest < Test::Unit::TestCase def test_clears_dependencies_after_dispatch_if_in_loading_mode ActiveSupport::Dependencies.expects(:clear).once - dispatch(@output, false) + dispatch(false) end def test_reloads_routes_before_dispatch_if_in_loading_mode ActionController::Routing::Routes.expects(:reload).once - dispatch(@output, false) + dispatch(false) end def test_clears_asset_tag_cache_before_dispatch_if_in_loading_mode ActionView::Helpers::AssetTagHelper::AssetTag::Cache.expects(:clear).once - dispatch(@output, false) + dispatch(false) end def test_leaves_dependencies_after_dispatch_if_not_in_loading_mode @@ -51,12 +50,16 @@ class DispatcherTest < Test::Unit::TestCase end def test_failsafe_response - CGI.expects(:new).raises('some multipart parsing failure') - Dispatcher.expects(:log_failsafe_exception) - - assert_nothing_raised { dispatch } - - assert_equal "Status: 400 Bad Request\r\nContent-Type: text/html\r\n\r\n<html><body><h1>400 Bad Request</h1></body></html>", @output.string + Dispatcher.any_instance.expects(:dispatch_unlocked).raises('b00m') + ActionController::Failsafe.any_instance.expects(:log_failsafe_exception) + + assert_nothing_raised do + assert_equal [ + 500, + {"Content-Type" => "text/html"}, + "<html><body><h1>500 Internal Server Error</h1></body></html>" + ], dispatch + end end def test_prepare_callbacks @@ -77,7 +80,7 @@ class DispatcherTest < Test::Unit::TestCase # Make sure they are only run once a = b = c = nil - @dispatcher.send :dispatch + dispatch assert_nil a || b || c end @@ -92,15 +95,10 @@ class DispatcherTest < Test::Unit::TestCase end private - def dispatch(output = @output, cache_classes = true) - controller = mock - controller.stubs(:process).returns(controller) - controller.stubs(:out).with(output).returns('response') - - ActionController::Routing::Routes.stubs(:recognize).returns(controller) - + def dispatch(cache_classes = true) + Dispatcher.any_instance.stubs(:handle_request).returns([200, {}, 'response']) Dispatcher.define_dispatcher_callbacks(cache_classes) - Dispatcher.dispatch(nil, {}, output) + @dispatcher.call({}) end def assert_subclasses(howmany, klass, message = klass.subclasses.inspect) diff --git a/actionpack/test/controller/integration_test.rb b/actionpack/test/controller/integration_test.rb index b39d35930d..4735b2927b 100644 --- a/actionpack/test/controller/integration_test.rb +++ b/actionpack/test/controller/integration_test.rb @@ -2,19 +2,13 @@ require 'abstract_unit' uses_mocha 'integration' do -module IntegrationSessionStubbing - def stub_integration_session(session) - session.stubs(:process) - session.stubs(:generic_url_rewriter) - end -end - class SessionTest < Test::Unit::TestCase - include IntegrationSessionStubbing + StubApp = lambda { |env| + [200, {"Content-Type" => "text/html"}, "Hello, World!"] + } def setup - @session = ActionController::Integration::Session.new - stub_integration_session(@session) + @session = ActionController::Integration::Session.new(StubApp) end def test_https_bang_works_and_sets_truth_by_default @@ -207,13 +201,10 @@ class SessionTest < Test::Unit::TestCase end class IntegrationTestTest < Test::Unit::TestCase - include IntegrationSessionStubbing - def setup @test = ::ActionController::IntegrationTest.new(:default_test) @test.class.stubs(:fixture_table_names).returns([]) @session = @test.open_session - stub_integration_session(@session) end def test_opens_new_session @@ -231,15 +222,15 @@ end # Tests that integration tests don't call Controller test methods for processing. # Integration tests have their own setup and teardown. class IntegrationTestUsesCorrectClass < ActionController::IntegrationTest - include IntegrationSessionStubbing - def self.fixture_table_names [] end def test_integration_methods_called reset! - stub_integration_session(@integration_session) + @integration_session.stubs(:generic_url_rewriter) + @integration_session.stubs(:process) + %w( get post head put delete ).each do |verb| assert_nothing_raised("'#{verb}' should use integration test methods") { __send__(verb, '/') } end @@ -281,13 +272,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/get' assert_equal 200, status assert_equal "OK", status_message - assert_equal "200 OK", response.headers["Status"] - assert_equal ["200 OK"], headers["status"] assert_response 200 assert_response :success assert_response :ok - assert_equal [], response.headers["cookie"] - assert_equal [], headers["cookie"] assert_equal({}, cookies) assert_equal "OK", response.body assert_kind_of HTML::Document, html_document @@ -300,13 +287,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest post '/post' assert_equal 201, status assert_equal "Created", status_message - assert_equal "201 Created", response.headers["Status"] - assert_equal ["201 Created"], headers["status"] assert_response 201 assert_response :success assert_response :created - assert_equal [], response.headers["cookie"] - assert_equal [], headers["cookie"] assert_equal({}, cookies) assert_equal "Created", response.body assert_kind_of HTML::Document, html_document @@ -321,17 +304,9 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/cookie_monster' assert_equal 410, status assert_equal "Gone", status_message - assert_equal "410 Gone", response.headers["Status"] - assert_equal ["410 Gone"], headers["status"] assert_response 410 assert_response :gone - assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], response.headers["Set-Cookie"] - assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers['set-cookie'] - assert_equal [ - CGI::Cookie::new("name" => "cookie_1", "value" => ""), - CGI::Cookie::new("name" => "cookie_3", "value" => "chocolate") - ], response.headers["cookie"] - assert_equal [], headers["cookie"] + assert_equal ["cookie_1=; path=/", "cookie_3=chocolate; path=/"], headers["Set-Cookie"] assert_equal({"cookie_1"=>"", "cookie_2"=>"oatmeal", "cookie_3"=>"chocolate"}, cookies) assert_equal "Gone", response.body end @@ -342,8 +317,6 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/redirect' assert_equal 302, status assert_equal "Found", status_message - assert_equal "302 Found", response.headers["Status"] - assert_equal ["302 Found"], headers["status"] assert_response 302 assert_response :redirect assert_response :found @@ -358,8 +331,6 @@ class IntegrationProcessTest < ActionController::IntegrationTest xhr :get, '/get' assert_equal 200, status assert_equal "OK", status_message - assert_equal "200 OK", response.headers["Status"] - assert_equal ["200 OK"], headers["status"] assert_response 200 assert_response :success assert_response :ok @@ -372,7 +343,7 @@ class IntegrationProcessTest < ActionController::IntegrationTest get '/get_with_params?foo=bar' assert_equal '/get_with_params?foo=bar', request.env["REQUEST_URI"] assert_equal '/get_with_params?foo=bar', request.request_uri - assert_equal nil, request.env["QUERY_STRING"] + assert_equal "", request.env["QUERY_STRING"] assert_equal 'foo=bar', request.query_string assert_equal 'bar', request.parameters['foo'] diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb index 7e8b0f9bf2..641ef9626e 100644 --- a/actionpack/test/controller/rack_test.rb +++ b/actionpack/test/controller/rack_test.rb @@ -153,12 +153,12 @@ class RackRequestTest < BaseRackTest def test_cookie_syntax_resilience cookies = @request.cookies - assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect - assert_equal ["yes"], cookies["is_admin"], cookies.inspect + assert_equal "c84ace84796670c052c6ceb2451fb0f2", cookies["_session_id"], cookies.inspect + assert_equal "yes", cookies["is_admin"], cookies.inspect alt_cookies = @alt_cookie_fmt_request.cookies - assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect - assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect + #assert_equal "c84ace847,96670c052c6ceb2451fb0f2", alt_cookies["_session_id"], alt_cookies.inspect + assert_equal "yes", alt_cookies["is_admin"], alt_cookies.inspect end end @@ -230,14 +230,13 @@ class RackResponseTest < BaseRackTest def setup super @response = ActionController::RackResponse.new(@request) - @output = StringIO.new('') end def test_simple_output @response.body = "Hello, World!" @response.prepare! - status, headers, body = @response.out(@output) + status, headers, body = @response.out assert_equal "200 OK", status assert_equal({ "Content-Type" => "text/html; charset=utf-8", @@ -258,7 +257,7 @@ class RackResponseTest < BaseRackTest end @response.prepare! - status, headers, body = @response.out(@output) + status, headers, body = @response.out assert_equal "200 OK", status assert_equal({"Content-Type" => "text/html; charset=utf-8", "Cache-Control" => "no-cache", "Set-Cookie" => []}, headers) @@ -274,7 +273,7 @@ class RackResponseTest < BaseRackTest @response.body = "Hello, World!" @response.prepare! - status, headers, body = @response.out(@output) + status, headers, body = @response.out assert_equal "200 OK", status assert_equal({ "Content-Type" => "text/html; charset=utf-8", @@ -294,7 +293,6 @@ class RackResponseHeadersTest < BaseRackTest def setup super @response = ActionController::RackResponse.new(@request) - @output = StringIO.new('') @response.headers['Status'] = "200 OK" end @@ -317,6 +315,6 @@ class RackResponseHeadersTest < BaseRackTest private def response_headers @response.prepare! - @response.out(@output)[1] + @response.out[1] end end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 972e425e35..c5496a9af5 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -246,13 +246,10 @@ class TestController < ActionController::Base :locals => { :local_name => name } end - def helper_method_to_render_to_string(*args) - render_to_string(*args) + def render_implicit_html_template end - helper_method :helper_method_to_render_to_string - def render_html_only_partial_within_inline - render :inline => "Hello world <%= helper_method_to_render_to_string :partial => 'test/partial_with_only_html_version' %>" + def render_explicit_html_template end def formatted_html_erb @@ -942,9 +939,22 @@ class RenderTest < ActionController::TestCase assert_equal "Goodbye, Local David", @response.body end - def test_rendering_html_only_partial_within_inline_with_js - get :render_html_only_partial_within_inline, :format => :js - assert_equal "Hello world partial with only html version", @response.body + def test_render_in_an_rjs_template_should_pick_html_templates_when_available + [:js, "js"].each do |format| + assert_nothing_raised do + get :render_implicit_html_template, :format => format + assert_equal %(document.write("Hello world\\n");), @response.body + end + end + end + + def test_explicitly_rendering_an_html_template_with_implicit_html_template_renders_should_be_possible_from_an_rjs_template + [:js, "js"].each do |format| + assert_nothing_raised do + get :render_explicit_html_template, :format => format + assert_equal %(document.write("Hello world\\n");), @response.body + end + end end def test_should_render_formatted_template diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index ba4a6da39b..71da50fab0 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -637,7 +637,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase input = { "customers[boston][first][name]" => [ "David" ], "something_else" => [ "blah" ], - "logo" => [ File.new(File.dirname(__FILE__) + "/cgi_test.rb").path ] + "logo" => [ File.new(File.dirname(__FILE__) + "/rack_test.rb").path ] } expected_output = { @@ -649,7 +649,7 @@ class UrlEncodedRequestParameterParsingTest < ActiveSupport::TestCase } }, "something_else" => "blah", - "logo" => File.new(File.dirname(__FILE__) + "/cgi_test.rb").path, + "logo" => File.new(File.dirname(__FILE__) + "/rack_test.rb").path, } assert_equal expected_output, ActionController::AbstractRequest.parse_request_parameters(input) diff --git a/actionpack/test/controller/session_fixation_test.rb b/actionpack/test/controller/session_fixation_test.rb index 164438c513..e8dc8bd295 100644 --- a/actionpack/test/controller/session_fixation_test.rb +++ b/actionpack/test/controller/session_fixation_test.rb @@ -1,20 +1,13 @@ require 'abstract_unit' - -class SessionFixationTest < Test::Unit::TestCase - class MockCGI < CGI #:nodoc: - attr_accessor :stdoutput, :env_table - - def initialize(env, data = '') - self.env_table = env - self.stdoutput = StringIO.new - super(nil, StringIO.new(data)) - end - end - +class SessionFixationTest < ActionController::IntegrationTest class TestController < ActionController::Base - session :session_key => '_myapp_session_id', :secret => CGI::Session.generate_unique_id, :except => :default_session_key - session :cookie_only => false, :only => :allow_session_fixation + session :session_key => '_myapp_session_id', + :secret => CGI::Session.generate_unique_id, + :except => :default_session_key + + session :cookie_only => false, + :only => :allow_session_fixation def default_session_key render :text => "default_session_key" @@ -36,54 +29,56 @@ class SessionFixationTest < Test::Unit::TestCase end def test_should_be_able_to_make_a_successful_request - cgi = mock_cgi_for_request_to(:custom_session_key, :id => 1) - - assert_nothing_raised do - @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi)) + with_test_route_set do + assert_nothing_raised do + get '/custom_session_key', :id => "1" + end + assert_equal 'custom_session_key: 1', @controller.response.body + assert_not_nil @controller.session end - assert_equal 'custom_session_key: 1', @controller.response.body - assert_not_nil @controller.session end def test_should_catch_session_fixation_attempt - cgi = mock_cgi_for_request_to(:custom_session_key, :_myapp_session_id => 42) - - assert_raises ActionController::CgiRequest::SessionFixationAttempt do - @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi)) + with_test_route_set do + assert_raises(ActionController::RackRequest::SessionFixationAttempt) do + get '/custom_session_key', :_myapp_session_id => "42" + end + assert_nil @controller.session end - assert_nil @controller.session end def test_should_not_catch_session_fixation_attempt_when_cookie_only_setting_is_disabled - cgi = mock_cgi_for_request_to(:allow_session_fixation, :_myapp_session_id => 42) - - assert_nothing_raised do - @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi)) + with_test_route_set do + assert_nothing_raised do + get '/allow_session_fixation', :_myapp_session_id => "42" + end + assert !@controller.response.body.blank? + assert_not_nil @controller.session end - assert ! @controller.response.body.blank? - assert_not_nil @controller.session end def test_should_catch_session_fixation_attempt_with_default_session_key - ActionController::Base.session_store = :p_store # using the default session_key is not possible with cookie store - cgi = mock_cgi_for_request_to(:default_session_key, :_session_id => 42) - - assert_raises ActionController::CgiRequest::SessionFixationAttempt do - @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi)) + # using the default session_key is not possible with cookie store + ActionController::Base.session_store = :p_store + + with_test_route_set do + assert_raises ActionController::RackRequest::SessionFixationAttempt do + get '/default_session_key', :_session_id => "42" + end + assert_nil @controller.response + assert_nil @controller.session end - assert @controller.response.body.blank? - assert_nil @controller.session - end - -private - - def mock_cgi_for_request_to(action, params = {}) - MockCGI.new({ - "REQUEST_METHOD" => "GET", - "QUERY_STRING" => "action=#{action}&#{params.to_query}", - "REQUEST_URI" => "/", - "SERVER_PORT" => "80", - "HTTP_HOST" => "testdomain.com" }, '') end + private + def with_test_route_set + with_routing do |set| + set.draw do |map| + map.with_options :controller => "session_fixation_test/test" do |c| + c.connect "/:action" + end + end + yield + end + end end diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 04e14d8eac..ac84e2dfcd 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -43,29 +43,29 @@ class ViewLoadPathsTest < ActionController::TestCase end def test_template_load_path_was_set_correctly - assert_equal [FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) end def test_controller_appends_view_path_correctly @controller.append_view_path 'foo' - assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths.map(&:to_s) @controller.append_view_path(%w(bar baz)) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) @controller.append_view_path(FIXTURE_LOAD_PATH) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) end def test_controller_prepends_view_path_correctly @controller.prepend_view_path 'baz' - assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) @controller.prepend_view_path(%w(foo bar)) - assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) @controller.prepend_view_path(FIXTURE_LOAD_PATH) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) end def test_template_appends_view_path_correctly @@ -73,10 +73,10 @@ class ViewLoadPathsTest < ActionController::TestCase class_view_paths = TestController.view_paths @controller.append_view_path 'foo' - assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo'], @controller.view_paths.map(&:to_s) @controller.append_view_path(%w(bar baz)) - assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths + assert_equal [FIXTURE_LOAD_PATH, 'foo', 'bar', 'baz'], @controller.view_paths.map(&:to_s) assert_equal class_view_paths, TestController.view_paths end @@ -85,10 +85,10 @@ class ViewLoadPathsTest < ActionController::TestCase class_view_paths = TestController.view_paths @controller.prepend_view_path 'baz' - assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal ['baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) @controller.prepend_view_path(%w(foo bar)) - assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths + assert_equal ['foo', 'bar', 'baz', FIXTURE_LOAD_PATH], @controller.view_paths.map(&:to_s) assert_equal class_view_paths, TestController.view_paths end @@ -130,12 +130,12 @@ class ViewLoadPathsTest < ActionController::TestCase A.view_paths = ['a/path'] - assert_equal ['a/path'], A.view_paths + assert_equal ['a/path'], A.view_paths.map(&:to_s) assert_equal A.view_paths, B.view_paths assert_equal original_load_paths, C.view_paths C.view_paths = [] assert_nothing_raised { C.view_paths << 'c/path' } - assert_equal ['c/path'], C.view_paths + assert_equal ['c/path'], C.view_paths.map(&:to_s) end end diff --git a/actionpack/test/controller/webservice_test.rb b/actionpack/test/controller/webservice_test.rb index 6d2b3e4f23..4c44ea4205 100644 --- a/actionpack/test/controller/webservice_test.rb +++ b/actionpack/test/controller/webservice_test.rb @@ -1,16 +1,6 @@ require 'abstract_unit' -class WebServiceTest < Test::Unit::TestCase - class MockCGI < CGI #:nodoc: - attr_accessor :stdoutput, :env_table - - def initialize(env, data = '') - self.env_table = env - self.stdoutput = StringIO.new - super(nil, StringIO.new(data)) - end - end - +class WebServiceTest < ActionController::IntegrationTest class TestController < ActionController::Base session :off @@ -22,7 +12,7 @@ class WebServiceTest < Test::Unit::TestCase end end - def dump_params_keys(hash=params) + def dump_params_keys(hash = params) hash.keys.sort.inject("") do |s, k| value = hash[k] value = Hash === value ? "(#{dump_params_keys(value)})" : "" @@ -33,7 +23,7 @@ class WebServiceTest < Test::Unit::TestCase def rescue_action(e) raise end end - + def setup @controller = TestController.new @default_param_parsers = ActionController::Base.param_parsers.dup @@ -44,185 +34,229 @@ class WebServiceTest < Test::Unit::TestCase end def test_check_parameters - process('GET') - assert_equal '', @controller.response.body + with_test_route_set do + get "/" + assert_equal '', @controller.response.body + end end def test_post_xml - process('POST', 'application/xml', '<entry attributed="true"><summary>content...</summary></entry>') - - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'content...', @controller.params["entry"]['summary'] - assert_equal 'true', @controller.params["entry"]['attributed'] + with_test_route_set do + post "/", '<entry attributed="true"><summary>content...</summary></entry>', + {'CONTENT_TYPE' => 'application/xml'} + + assert_equal 'entry', @controller.response.body + assert @controller.params.has_key?(:entry) + assert_equal 'content...', @controller.params["entry"]['summary'] + assert_equal 'true', @controller.params["entry"]['attributed'] + end end def test_put_xml - process('PUT', 'application/xml', '<entry attributed="true"><summary>content...</summary></entry>') + with_test_route_set do + put "/", '<entry attributed="true"><summary>content...</summary></entry>', + {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'content...', @controller.params["entry"]['summary'] - assert_equal 'true', @controller.params["entry"]['attributed'] + assert_equal 'entry', @controller.response.body + assert @controller.params.has_key?(:entry) + assert_equal 'content...', @controller.params["entry"]['summary'] + assert_equal 'true', @controller.params["entry"]['attributed'] + end end def test_put_xml_using_a_type_node - process('PUT', 'application/xml', '<type attributed="true"><summary>content...</summary></type>') + with_test_route_set do + put "/", '<type attributed="true"><summary>content...</summary></type>', + {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'type', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal 'content...', @controller.params["type"]['summary'] - assert_equal 'true', @controller.params["type"]['attributed'] + assert_equal 'type', @controller.response.body + assert @controller.params.has_key?(:type) + assert_equal 'content...', @controller.params["type"]['summary'] + assert_equal 'true', @controller.params["type"]['attributed'] + end end def test_put_xml_using_a_type_node_and_attribute - process('PUT', 'application/xml', '<type attributed="true"><summary type="boolean">false</summary></type>') + with_test_route_set do + put "/", '<type attributed="true"><summary type="boolean">false</summary></type>', + {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'type', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal false, @controller.params["type"]['summary'] - assert_equal 'true', @controller.params["type"]['attributed'] + assert_equal 'type', @controller.response.body + assert @controller.params.has_key?(:type) + assert_equal false, @controller.params["type"]['summary'] + assert_equal 'true', @controller.params["type"]['attributed'] + end end def test_post_xml_using_a_type_node - process('POST', 'application/xml', '<font attributed="true"><type>arial</type></font>') + with_test_route_set do + post "/", '<font attributed="true"><type>arial</type></font>', + {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'font', @controller.response.body - assert @controller.params.has_key?(:font) - assert_equal 'arial', @controller.params['font']['type'] - assert_equal 'true', @controller.params["font"]['attributed'] + assert_equal 'font', @controller.response.body + assert @controller.params.has_key?(:font) + assert_equal 'arial', @controller.params['font']['type'] + assert_equal 'true', @controller.params["font"]['attributed'] + end end def test_post_xml_using_a_root_node_named_type - process('POST', 'application/xml', '<type type="integer">33</type>') + with_test_route_set do + post "/", '<type type="integer">33</type>', + {'CONTENT_TYPE' => 'application/xml'} - assert @controller.params.has_key?(:type) - assert_equal 33, @controller.params['type'] + assert @controller.params.has_key?(:type) + assert_equal 33, @controller.params['type'] + end end def test_post_xml_using_an_attributted_node_named_type - ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } - process('POST', 'application/xml', '<request><type type="string">Arial,12</type><z>3</z></request>') + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } + post "/", '<request><type type="string">Arial,12</type><z>3</z></request>', + {'CONTENT_TYPE' => 'application/xml'} - assert_equal 'type, z', @controller.response.body - assert @controller.params.has_key?(:type) - assert_equal 'Arial,12', @controller.params['type'], @controller.params.inspect - assert_equal '3', @controller.params['z'], @controller.params.inspect + assert_equal 'type, z', @controller.response.body + assert @controller.params.has_key?(:type) + assert_equal 'Arial,12', @controller.params['type'], @controller.params.inspect + assert_equal '3', @controller.params['z'], @controller.params.inspect + end end def test_register_and_use_yaml - ActionController::Base.param_parsers[Mime::YAML] = Proc.new { |d| YAML.load(d) } - process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml) - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'loaded from yaml', @controller.params["entry"] + with_test_route_set do + ActionController::Base.param_parsers[Mime::YAML] = Proc.new { |d| YAML.load(d) } + post "/", {"entry" => "loaded from yaml"}.to_yaml, + {'CONTENT_TYPE' => 'application/x-yaml'} + + assert_equal 'entry', @controller.response.body + assert @controller.params.has_key?(:entry) + assert_equal 'loaded from yaml', @controller.params["entry"] + end end - + def test_register_and_use_yaml_as_symbol - ActionController::Base.param_parsers[Mime::YAML] = :yaml - process('POST', 'application/x-yaml', {"entry" => "loaded from yaml"}.to_yaml) - assert_equal 'entry', @controller.response.body - assert @controller.params.has_key?(:entry) - assert_equal 'loaded from yaml', @controller.params["entry"] + with_test_route_set do + ActionController::Base.param_parsers[Mime::YAML] = :yaml + post "/", {"entry" => "loaded from yaml"}.to_yaml, + {'CONTENT_TYPE' => 'application/x-yaml'} + + assert_equal 'entry', @controller.response.body + assert @controller.params.has_key?(:entry) + assert_equal 'loaded from yaml', @controller.params["entry"] + end end def test_register_and_use_xml_simple - ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } - process('POST', 'application/xml', '<request><summary>content...</summary><title>SimpleXml</title></request>' ) - assert_equal 'summary, title', @controller.response.body - assert @controller.params.has_key?(:summary) - assert @controller.params.has_key?(:title) - assert_equal 'content...', @controller.params["summary"] - assert_equal 'SimpleXml', @controller.params["title"] + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = Proc.new { |data| Hash.from_xml(data)['request'].with_indifferent_access } + post "/", '<request><summary>content...</summary><title>SimpleXml</title></request>', + {'CONTENT_TYPE' => 'application/xml'} + + assert_equal 'summary, title', @controller.response.body + assert @controller.params.has_key?(:summary) + assert @controller.params.has_key?(:title) + assert_equal 'content...', @controller.params["summary"] + assert_equal 'SimpleXml', @controller.params["title"] + end end def test_use_xml_ximple_with_empty_request - ActionController::Base.param_parsers[Mime::XML] = :xml_simple - assert_nothing_raised { process('POST', 'application/xml', "") } - assert_equal "", @controller.response.body + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = :xml_simple + assert_nothing_raised { post "/", "", {'CONTENT_TYPE' => 'application/xml'} } + assert_equal "", @controller.response.body + end end def test_dasherized_keys_as_xml - ActionController::Base.param_parsers[Mime::XML] = :xml_simple - process('POST', 'application/xml', "<first-key>\n<sub-key>...</sub-key>\n</first-key>", true) - assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body - assert_equal "...", @controller.params[:first_key][:sub_key] + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = :xml_simple + post "/?full=1", "<first-key>\n<sub-key>...</sub-key>\n</first-key>", + {'CONTENT_TYPE' => 'application/xml'} + assert_equal 'action, controller, first_key(sub_key), full', @controller.response.body + assert_equal "...", @controller.params[:first_key][:sub_key] + end end def test_typecast_as_xml - ActionController::Base.param_parsers[Mime::XML] = :xml_simple - process('POST', 'application/xml', <<-XML) - <data> - <a type="integer">15</a> - <b type="boolean">false</b> - <c type="boolean">true</c> - <d type="date">2005-03-17</d> - <e type="datetime">2005-03-17T21:41:07Z</e> - <f>unparsed</f> - <g type="integer">1</g> - <g>hello</g> - <g type="date">1974-07-25</g> - </data> - XML - params = @controller.params - assert_equal 15, params[:data][:a] - assert_equal false, params[:data][:b] - assert_equal true, params[:data][:c] - assert_equal Date.new(2005,3,17), params[:data][:d] - assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] - assert_equal "unparsed", params[:data][:f] - assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = :xml_simple + xml = <<-XML + <data> + <a type="integer">15</a> + <b type="boolean">false</b> + <c type="boolean">true</c> + <d type="date">2005-03-17</d> + <e type="datetime">2005-03-17T21:41:07Z</e> + <f>unparsed</f> + <g type="integer">1</g> + <g>hello</g> + <g type="date">1974-07-25</g> + </data> + XML + post "/", xml, {'CONTENT_TYPE' => 'application/xml'} + + params = @controller.params + assert_equal 15, params[:data][:a] + assert_equal false, params[:data][:b] + assert_equal true, params[:data][:c] + assert_equal Date.new(2005,3,17), params[:data][:d] + assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] + assert_equal "unparsed", params[:data][:f] + assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] + end end def test_entities_unescaped_as_xml_simple - ActionController::Base.param_parsers[Mime::XML] = :xml_simple - process('POST', 'application/xml', <<-XML) - <data><foo "bar's" & friends></data> - XML - assert_equal %(<foo "bar's" & friends>), @controller.params[:data] + with_test_route_set do + ActionController::Base.param_parsers[Mime::XML] = :xml_simple + xml = <<-XML + <data><foo "bar's" & friends></data> + XML + post "/", xml, {'CONTENT_TYPE' => 'application/xml'} + assert_equal %(<foo "bar's" & friends>), @controller.params[:data] + end end def test_typecast_as_yaml - ActionController::Base.param_parsers[Mime::YAML] = :yaml - process('POST', 'application/x-yaml', <<-YAML) - --- - data: - a: 15 - b: false - c: true - d: 2005-03-17 - e: 2005-03-17T21:41:07Z - f: unparsed - g: - - 1 - - hello - - 1974-07-25 - YAML - params = @controller.params - assert_equal 15, params[:data][:a] - assert_equal false, params[:data][:b] - assert_equal true, params[:data][:c] - assert_equal Date.new(2005,3,17), params[:data][:d] - assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] - assert_equal "unparsed", params[:data][:f] - assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] - end - - private - - def process(verb, content_type = 'application/x-www-form-urlencoded', data = '', full=false) - - cgi = MockCGI.new({ - 'REQUEST_METHOD' => verb, - 'CONTENT_TYPE' => content_type, - 'QUERY_STRING' => "action=assign_parameters&controller=webservicetest/test#{"&full=1" if full}", - "REQUEST_URI" => "/", - "HTTP_HOST" => 'testdomain.com', - "CONTENT_LENGTH" => data.size, - "SERVER_PORT" => "80", - "HTTPS" => "off"}, data) - - @controller.send(:process, ActionController::CgiRequest.new(cgi, {}), ActionController::CgiResponse.new(cgi)) - end - + with_test_route_set do + ActionController::Base.param_parsers[Mime::YAML] = :yaml + yaml = <<-YAML + --- + data: + a: 15 + b: false + c: true + d: 2005-03-17 + e: 2005-03-17T21:41:07Z + f: unparsed + g: + - 1 + - hello + - 1974-07-25 + YAML + post "/", yaml, {'CONTENT_TYPE' => 'application/x-yaml'} + params = @controller.params + assert_equal 15, params[:data][:a] + assert_equal false, params[:data][:b] + assert_equal true, params[:data][:c] + assert_equal Date.new(2005,3,17), params[:data][:d] + assert_equal Time.utc(2005,3,17,21,41,7), params[:data][:e] + assert_equal "unparsed", params[:data][:f] + assert_equal [1, "hello", Date.new(1974,7,25)], params[:data][:g] + end + end + + private + def with_test_route_set + with_routing do |set| + set.draw do |map| + map.with_options :controller => "web_service_test/test" do |c| + c.connect "/", :action => "assign_parameters" + end + end + yield + end + end end diff --git a/actionpack/test/fixtures/test/_one.html.erb b/actionpack/test/fixtures/test/_one.html.erb new file mode 100644 index 0000000000..f796291cb4 --- /dev/null +++ b/actionpack/test/fixtures/test/_one.html.erb @@ -0,0 +1 @@ +<%= render :partial => "two" %> world diff --git a/actionpack/test/fixtures/test/_two.html.erb b/actionpack/test/fixtures/test/_two.html.erb new file mode 100644 index 0000000000..5ab2f8a432 --- /dev/null +++ b/actionpack/test/fixtures/test/_two.html.erb @@ -0,0 +1 @@ +Hello
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs b/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs new file mode 100644 index 0000000000..4eb12fd6af --- /dev/null +++ b/actionpack/test/fixtures/test/render_explicit_html_template.js.rjs @@ -0,0 +1 @@ +page.call "document.write", render(:partial => "one.html.erb") diff --git a/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs b/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs new file mode 100644 index 0000000000..3d68041756 --- /dev/null +++ b/actionpack/test/fixtures/test/render_implicit_html_template.js.rjs @@ -0,0 +1 @@ +page.call "document.write", render(:partial => "one") diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 9e827abbca..0387a11de2 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -137,12 +137,6 @@ module RenderTestCases end # TODO: The reason for this test is unclear, improve documentation - def test_render_js_partial_and_fallback_to_erb_layout - @view.template_format = :js - assert_equal "Before (Josh)\n\nAfter", @view.render(:partial => "test/layout_for_partial", :locals => { :name => "Josh" }) - end - - # TODO: The reason for this test is unclear, improve documentation def test_render_missing_xml_partial_and_raise_missing_template @view.template_format = :xml assert_raise(ActionView::MissingTemplate) { @view.render(:partial => "test/layout_for_partial") } |