diff options
Diffstat (limited to 'actionpack')
62 files changed, 1099 insertions, 667 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 4a24d2f8b9..9622029362 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,4 +1,9 @@ -*2.1.0 RC1 (May 11th, 2008)* +* Added Rack processor [Ezra Zygmuntowicz, Josh Peek] + + +*2.1.0 (May 31st, 2008)* + +* InstanceTag#default_time_from_options overflows to DateTime [Geoff Buesing] * Fixed that forgery protection can be used without session tracking (Peter Jones) [#139] diff --git a/actionpack/README b/actionpack/README index 2746c3cc43..6090089bb9 100644 --- a/actionpack/README +++ b/actionpack/README @@ -31,7 +31,7 @@ http://www.rubyonrails.org. A short rundown of the major features: * Actions grouped in controller as methods instead of separate command objects - and can therefore share helper methods. + and can therefore share helper methods BlogController < ActionController::Base def show @@ -168,7 +168,7 @@ A short rundown of the major features: {Learn more}[link:classes/ActionController/Base.html] -* Javascript and Ajax integration. +* Javascript and Ajax integration link_to_function "Greeting", "alert('Hello world!')" link_to_remote "Delete this post", :update => "posts", @@ -177,7 +177,7 @@ A short rundown of the major features: {Learn more}[link:classes/ActionView/Helpers/JavaScriptHelper.html] -* Pagination for navigating lists of results. +* Pagination for navigating lists of results # controller def list @@ -192,15 +192,9 @@ A short rundown of the major features: {Learn more}[link:classes/ActionController/Pagination.html] -* Easy testing of both controller and template result through TestRequest/Response - - class LoginControllerTest < Test::Unit::TestCase - def setup - @controller = LoginController.new - @request = ActionController::TestRequest.new - @response = ActionController::TestResponse.new - end +* Easy testing of both controller and rendered template through ActionController::TestCase + class LoginControllerTest < ActionController::TestCase def test_failing_authenticate process :authenticate, :user_name => "nop", :password => "" assert flash.has_key?(:alert) @@ -208,7 +202,7 @@ A short rundown of the major features: end end - {Learn more}[link:classes/ActionController/TestRequest.html] + {Learn more}[link:classes/ActionController/TestCase.html] * Automated benchmarking and integrated logging diff --git a/actionpack/Rakefile b/actionpack/Rakefile index 0147a5c1e8..b37f756c1e 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -76,7 +76,7 @@ spec = Gem::Specification.new do |s| s.has_rdoc = true s.requirements << 'none' - s.add_dependency('activesupport', '= 2.0.991' + PKG_BUILD) + s.add_dependency('activesupport', '= 2.1.0' + PKG_BUILD) s.require_path = 'lib' s.autorequire = 'action_controller' diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 810a5fb9b5..3c4a339d50 100755 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -53,6 +53,7 @@ require 'action_controller/streaming' require 'action_controller/session_management' require 'action_controller/http_authentication' require 'action_controller/components' +require 'action_controller/rack_process' require 'action_controller/record_identifier' require 'action_controller/request_forgery_protection' require 'action_controller/headers' diff --git a/actionpack/lib/action_controller/assertions/model_assertions.rb b/actionpack/lib/action_controller/assertions/model_assertions.rb index 0b4313055a..d25214bb66 100644 --- a/actionpack/lib/action_controller/assertions/model_assertions.rb +++ b/actionpack/lib/action_controller/assertions/model_assertions.rb @@ -1,7 +1,8 @@ module ActionController module Assertions module ModelAssertions - # Ensures that the passed record is valid by ActiveRecord standards and returns any error messages if it is not. + # Ensures that the passed record is valid by Active Record standards and + # returns any error messages if it is not. # # ==== Examples # diff --git a/actionpack/lib/action_controller/assertions/routing_assertions.rb b/actionpack/lib/action_controller/assertions/routing_assertions.rb index 2acd003243..491b72d586 100644 --- a/actionpack/lib/action_controller/assertions/routing_assertions.rb +++ b/actionpack/lib/action_controller/assertions/routing_assertions.rb @@ -59,7 +59,7 @@ module ActionController end end - # Asserts that the provided options can be used to generate the provided path. This is the inverse of #assert_recognizes. + # Asserts that the provided options can be used to generate the provided path. This is the inverse of +assert_recognizes+. # The +extras+ parameter is used to tell the request the names and values of additional request parameters that would be in # a query string. The +message+ parameter allows you to specify a custom error message for assertion failures. # @@ -96,8 +96,8 @@ module ActionController end # Asserts that path and options match both ways; in other words, it verifies that <tt>path</tt> generates - # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines #assert_recognizes - # and #assert_generates into one step. + # <tt>options</tt> and then that <tt>options</tt> generates <tt>path</tt>. This essentially combines +assert_recognizes+ + # and +assert_generates+ into one step. # # The +extras+ hash allows you to specify options that would normally be provided as a query string to the action. The # +message+ parameter allows you to specify a custom error message to display upon failure. diff --git a/actionpack/lib/action_controller/assertions/selector_assertions.rb b/actionpack/lib/action_controller/assertions/selector_assertions.rb index 9ef093acfc..d3594e711c 100644 --- a/actionpack/lib/action_controller/assertions/selector_assertions.rb +++ b/actionpack/lib/action_controller/assertions/selector_assertions.rb @@ -12,12 +12,12 @@ module ActionController NO_STRIP = %w{pre script style textarea} end - # Adds the #assert_select method for use in Rails functional + # Adds the +assert_select+ method for use in Rails functional # test cases, which can be used to make assertions on the response HTML of a controller - # action. You can also call #assert_select within another #assert_select to + # action. You can also call +assert_select+ within another +assert_select+ to # make assertions on elements selected by the enclosing assertion. # - # Use #css_select to select elements without making an assertions, either + # Use +css_select+ to select elements without making an assertions, either # from the response HTML or elements selected by the enclosing assertion. # # In addition to HTML responses, you can make the following assertions: @@ -44,8 +44,8 @@ module ActionController # base element and any of its children. Returns an empty array if no # match is found. # - # The selector may be a CSS selector expression (+String+), an expression - # with substitution values (+Array+) or an HTML::Selector object. + # The selector may be a CSS selector expression (String), an expression + # with substitution values (Array) or an HTML::Selector object. # # ==== Examples # # Selects all div tags @@ -114,8 +114,8 @@ module ActionController # starting from (and including) that element and all its children in # depth-first order. # - # If no element if specified, calling #assert_select will select from the - # response HTML. Calling #assert_select inside an #assert_select block will + # If no element if specified, calling +assert_select+ will select from the + # response HTML. Calling #assert_select inside an +assert_select+ block will # run the assertion for each element selected by the enclosing assertion. # # ==== Example @@ -130,7 +130,7 @@ module ActionController # assert_select "li" # end # - # The selector may be a CSS selector expression (+String+), an expression + # The selector may be a CSS selector expression (String), an expression # with substitution values, or an HTML::Selector object. # # === Equality Tests @@ -356,16 +356,16 @@ module ActionController # # === Using blocks # - # Without a block, #assert_select_rjs merely asserts that the response + # Without a block, +assert_select_rjs+ merely asserts that the response # contains one or more RJS statements that replace or update content. # - # With a block, #assert_select_rjs also selects all elements used in + # With a block, +assert_select_rjs+ also selects all elements used in # these statements and passes them to the block. Nested assertions are # supported. # - # Calling #assert_select_rjs with no arguments and using nested asserts + # Calling +assert_select_rjs+ with no arguments and using nested asserts # asserts that the HTML content is returned by one or more RJS statements. - # Using #assert_select directly makes the same assertion on the content, + # Using +assert_select+ directly makes the same assertion on the content, # but without distinguishing whether the content is returned in an HTML # or JavaScript. # @@ -601,7 +601,7 @@ module ActionController RJS_PATTERN_UNICODE_ESCAPED_CHAR = /\\u([0-9a-zA-Z]{4})/ end - # #assert_select and #css_select call this to obtain the content in the HTML + # +assert_select+ and +css_select+ call this to obtain the content in the HTML # page, or from all the RJS statements, depending on the type of response. def response_from_page_or_rjs() content_type = @response.content_type diff --git a/actionpack/lib/action_controller/assertions/tag_assertions.rb b/actionpack/lib/action_controller/assertions/tag_assertions.rb index 4ac489520a..90ba3668fb 100644 --- a/actionpack/lib/action_controller/assertions/tag_assertions.rb +++ b/actionpack/lib/action_controller/assertions/tag_assertions.rb @@ -91,7 +91,7 @@ module ActionController # :descendant => { :tag => "span", # :child => /hello world/ } # - # <b>Please note</b>: #assert_tag and #assert_no_tag only work + # <b>Please note</b>: +assert_tag+ and +assert_no_tag+ only work # with well-formed XHTML. They recognize a few tags as implicitly self-closing # (like br and hr and such) but will not work correctly with tags # that allow optional closing tags (p, li, td). <em>You must explicitly @@ -104,8 +104,8 @@ module ActionController end end - # Identical to #assert_tag, but asserts that a matching tag does _not_ - # exist. (See #assert_tag for a full discussion of the syntax.) + # Identical to +assert_tag+, but asserts that a matching tag does _not_ + # exist. (See +assert_tag+ for a full discussion of the syntax.) # # === Examples # # Assert that there is not a "div" containing a "p" diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index ea55fe42ce..6222c3205d 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -104,7 +104,7 @@ module ActionController #:nodoc: # end # # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action - # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the + # after executing code in the action. For example, the +index+ action of the GuestBookController would render the # template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable. # # Unlike index, the sign action will not render a template. After performing its main purpose (creating a @@ -118,10 +118,10 @@ module ActionController #:nodoc: # # Requests are processed by the Action Controller framework by extracting the value of the "action" key in the request parameters. # This value should hold the name of the action to be performed. Once the action has been identified, the remaining - # request parameters, the session (if one is available), and the full request with all the http headers are made available to + # request parameters, the session (if one is available), and the full request with all the HTTP headers are made available to # the action through instance variables. Then the action is performed. # - # The full request object is available with the request accessor and is primarily used to query for http headers. These queries + # The full request object is available with the request accessor and is primarily used to query for HTTP headers. These queries # are made by accessing the environment hash, like this: # # def server_ip @@ -291,10 +291,10 @@ module ActionController #:nodoc: cattr_accessor :allow_concurrency # Modern REST web services often need to submit complex data to the web application. - # The param_parsers hash lets you register handlers which will process the http body and add parameters to the - # <tt>params</tt> hash. These handlers are invoked for post and put requests. + # The <tt>@@param_parsers</tt> hash lets you register handlers which will process the HTTP body and add parameters to the + # <tt>params</tt> hash. These handlers are invoked for POST and PUT requests. # - # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated + # By default <tt>application/xml</tt> is enabled. A XmlSimple class with the same param name as the root will be instantiated # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one # action serve both regular forms and web service requests. # @@ -307,7 +307,7 @@ module ActionController #:nodoc: # # Note: Up until release 1.1 of Rails, Action Controller would default to using XmlSimple configured to discard the # root node for such requests. The new default is to keep the root, such that "<r><name>David</name></r>" results - # in params[:r][:name] for "David" instead of params[:name]. To get the old behavior, you can + # in <tt>params[:r][:name]</tt> for "David" instead of <tt>params[:name]</tt>. To get the old behavior, you can # re-register XmlSimple as application/xml handler ike this: # # ActionController::Base.param_parsers[Mime::XML] = @@ -347,7 +347,7 @@ module ActionController #:nodoc: cattr_accessor :optimise_named_routes self.optimise_named_routes = true - # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode. + # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode. class_inheritable_accessor :allow_forgery_protection self.allow_forgery_protection = true diff --git a/actionpack/lib/action_controller/caching/fragments.rb b/actionpack/lib/action_controller/caching/fragments.rb index e4d8678a07..578e031a17 100644 --- a/actionpack/lib/action_controller/caching/fragments.rb +++ b/actionpack/lib/action_controller/caching/fragments.rb @@ -2,7 +2,7 @@ module ActionController #:nodoc: module Caching # Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when # certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple - # parties. The caching is doing using the cache helper available in the Action View. A template with caching might look something like: + # parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like: # # <b>Hello <%= @name %></b> # <% cache do %> @@ -98,6 +98,17 @@ module ActionController #:nodoc: end end + # Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats) + def fragment_exist?(key, options = nil) + return unless cache_configured? + + key = fragment_cache_key(key) + + self.class.benchmark "Cached fragment exists?: #{key}" do + cache_store.exist?(key, options) + 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 } @@ -124,4 +135,4 @@ module ActionController #:nodoc: end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_controller/cgi_ext/cookie.rb b/actionpack/lib/action_controller/cgi_ext/cookie.rb index a244e2a39a..009ddd1c64 100644 --- a/actionpack/lib/action_controller/cgi_ext/cookie.rb +++ b/actionpack/lib/action_controller/cgi_ext/cookie.rb @@ -6,25 +6,24 @@ class CGI #:nodoc: attr_accessor :name, :value, :path, :domain, :expires attr_reader :secure, :http_only - # Create a new CGI::Cookie object. + # Creates a new CGI::Cookie object. # # The contents of the cookie can be specified as a +name+ and one # or more +value+ arguments. Alternatively, the contents can # be specified as a single hash argument. The possible keywords of # this hash are as follows: # - # name:: the name of the cookie. Required. - # value:: the cookie's value or list of values. - # path:: the path for which this cookie applies. Defaults to the - # base directory of the CGI script. - # domain:: the domain for which this cookie applies. - # expires:: the time at which this cookie expires, as a +Time+ object. - # secure:: whether this cookie is a secure cookie or not (default to - # false). Secure cookies are only transmitted to HTTPS - # servers. - # http_only:: whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP - # More details: http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx - # Defaults to false. + # * <tt>:name</tt> - The name of the cookie. Required. + # * <tt>:value</tt> - The cookie's value or list of values. + # * <tt>:path</tt> - The path for which this cookie applies. Defaults to the + # base directory of the CGI script. + # * <tt>:domain</tt> - The domain for which this cookie applies. + # * <tt>:expires</tt> - The time at which this cookie expires, as a Time object. + # * <tt>:secure</tt> - Whether this cookie is a secure cookie or not (defaults to + # +false+). Secure cookies are only transmitted to HTTPS servers. + # * <tt>:http_only</tt> - Whether this cookie can be accessed by client side scripts (e.g. document.cookie) or only over HTTP. + # More details in http://msdn2.microsoft.com/en-us/library/system.web.httpcookie.httponly.aspx. Defaults to +false+. + # # These keywords correspond to attributes of the cookie object. def initialize(name = '', *value) if name.kind_of?(String) @@ -56,17 +55,17 @@ class CGI #:nodoc: super(@value) end - # Set whether the Cookie is a secure cookie or not. + # Sets whether the Cookie is a secure cookie or not. def secure=(val) @secure = val == true end - # Set whether the Cookie is an HTTP only cookie or not. + # Sets whether the Cookie is an HTTP only cookie or not. def http_only=(val) @http_only = val == true end - # Convert the Cookie to its string representation. + # Converts the Cookie to its string representation. def to_s buf = '' buf << @name << '=' @@ -79,11 +78,17 @@ class CGI #:nodoc: buf end - # Parse a raw cookie string into a hash of cookie-name=>Cookie + # FIXME: work around broken 1.8.7 DelegateClass#respond_to? + def respond_to?(method, include_private = false) + return true if super(method) + return __getobj__.respond_to?(method, include_private) + end + + # Parses a raw cookie string into a hash of <tt>cookie-name => cookie-object</tt> # pairs. # # cookies = CGI::Cookie::parse("raw_cookie_string") - # # { "name1" => cookie1, "name2" => cookie2, ... } + # # => { "name1" => cookie1, "name2" => cookie2, ... } # def self.parse(raw_cookie) cookies = Hash.new([]) diff --git a/actionpack/lib/action_controller/cgi_ext/stdinput.rb b/actionpack/lib/action_controller/cgi_ext/stdinput.rb index b0ca63ef2f..5e9b6784af 100644 --- a/actionpack/lib/action_controller/cgi_ext/stdinput.rb +++ b/actionpack/lib/action_controller/cgi_ext/stdinput.rb @@ -16,6 +16,7 @@ module ActionController def initialize_with_stdinput(type = nil, stdinput = $stdin) @stdinput = stdinput + @stdinput.set_encoding(Encoding::BINARY) if @stdinput.respond_to?(:set_encoding) initialize_without_stdinput(type || 'query') end end diff --git a/actionpack/lib/action_controller/cgi_process.rb b/actionpack/lib/action_controller/cgi_process.rb index b529db8af4..8bc5e4c3a7 100644 --- a/actionpack/lib/action_controller/cgi_process.rb +++ b/actionpack/lib/action_controller/cgi_process.rb @@ -15,7 +15,7 @@ module ActionController #:nodoc: # * <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 + # * <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. @@ -65,6 +65,7 @@ module ActionController #:nodoc: # variable is already set, wrap it in a StringIO. def body if raw_post = env['RAW_POST_DATA'] + raw_post.force_encoding(Encoding::BINARY) if raw_post.respond_to?(:force_encoding) StringIO.new(raw_post) else @cgi.stdinput diff --git a/actionpack/lib/action_controller/dispatcher.rb b/actionpack/lib/action_controller/dispatcher.rb index 6e1e7a261f..b40f1ba9be 100644 --- a/actionpack/lib/action_controller/dispatcher.rb +++ b/actionpack/lib/action_controller/dispatcher.rb @@ -96,7 +96,7 @@ module ActionController include ActiveSupport::Callbacks define_callbacks :prepare_dispatch, :before_dispatch, :after_dispatch - def initialize(output, request = nil, response = nil) + def initialize(output = $stdout, request = nil, response = nil) @output, @request, @response = output, request, response end @@ -123,6 +123,12 @@ module ActionController failsafe_rescue exception end + def call(env) + @request = RackRequest.new(env) + @response = RackResponse.new + dispatch + end + def reload_application # Run prepare callbacks before every request in development mode run_callbacks :prepare_dispatch diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb index 6d0c83eb40..60d92d9b98 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/filters.rb @@ -100,10 +100,10 @@ module ActionController #:nodoc: # # Around filters wrap an action, executing code both before and after. # They may be declared as method references, blocks, or objects responding - # to #filter or to both #before and #after. + # to +filter+ or to both +before+ and +after+. # - # To use a method as an around_filter, pass a symbol naming the Ruby method. - # Yield (or block.call) within the method to run the action. + # To use a method as an +around_filter+, pass a symbol naming the Ruby method. + # Yield (or <tt>block.call</tt>) within the method to run the action. # # around_filter :catch_exceptions # @@ -115,9 +115,9 @@ module ActionController #:nodoc: # raise # end # - # To use a block as an around_filter, pass a block taking as args both + # To use a block as an +around_filter+, pass a block taking as args both # the controller and the action block. You can't call yield directly from - # an around_filter block; explicitly call the action block instead: + # an +around_filter+ block; explicitly call the action block instead: # # around_filter do |controller, action| # logger.debug "before #{controller.action_name}" @@ -125,7 +125,7 @@ module ActionController #:nodoc: # logger.debug "after #{controller.action_name}" # end # - # To use a filter object with around_filter, pass an object responding + # To use a filter object with +around_filter+, pass an object responding # to <tt>:filter</tt> or both <tt>:before</tt> and <tt>:after</tt>. With a # filter method, yield to the block as above: # @@ -137,7 +137,7 @@ module ActionController #:nodoc: # end # end # - # With before and after methods: + # With +before+ and +after+ methods: # # around_filter Authorizer.new # @@ -154,9 +154,9 @@ module ActionController #:nodoc: # end # end # - # If the filter has before and after methods, the before method will be - # called before the action. If before renders or redirects, the filter chain is - # halted and after will not be run. See Filter Chain Halting below for + # If the filter has +before+ and +after+ methods, the +before+ method will be + # called before the action. If +before+ renders or redirects, the filter chain is + # halted and +after+ will not be run. See Filter Chain Halting below for # an example. # # == Filter chain skipping @@ -215,7 +215,7 @@ module ActionController #:nodoc: # # <tt>before_filter</tt> and <tt>around_filter</tt> may halt the request # before a controller action is run. This is useful, for example, to deny - # access to unauthenticated users or to redirect from http to https. + # access to unauthenticated users or to redirect from HTTP to HTTPS. # Simply call render or redirect. After filters will not be executed if the filter # chain is halted. # @@ -241,10 +241,10 @@ module ActionController #:nodoc: # . / # #after (actual filter code is run, unless the around filter does not yield) # - # If #around returns before yielding, #after will still not be run. The #before - # filter and controller action will not be run. If #before renders or redirects, - # the second half of #around and will still run but #after and the - # action will not. If #around fails to yield, #after will not be run. + # If +around+ returns before yielding, +after+ will still not be run. The +before+ + # filter and controller action will not be run. If +before+ renders or redirects, + # the second half of +around+ and will still run but +after+ and the + # action will not. If +around+ fails to yield, +after+ will not be run. class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc: def append_filter_to_chain(filters, filter_type, &block) @@ -471,7 +471,7 @@ module ActionController #:nodoc: # Shorthand for append_after_filter since it's the most common. alias :after_filter :append_after_filter - # If you append_around_filter A.new, B.new, the filter chain looks like + # If you <tt>append_around_filter A.new, B.new</tt>, the filter chain looks like # # B#before # A#before @@ -479,13 +479,13 @@ module ActionController #:nodoc: # A#after # B#after # - # With around filters which yield to the action block, #before and #after + # With around filters which yield to the action block, +before+ and +after+ # are the code before and after the yield. def append_around_filter(*filters, &block) filter_chain.append_filter_to_chain(filters, :around, &block) end - # If you prepend_around_filter A.new, B.new, the filter chain looks like: + # If you <tt>prepend_around_filter A.new, B.new</tt>, the filter chain looks like: # # A#before # B#before @@ -493,13 +493,13 @@ module ActionController #:nodoc: # B#after # A#after # - # With around filters which yield to the action block, #before and #after + # With around filters which yield to the action block, +before+ and +after+ # are the code before and after the yield. def prepend_around_filter(*filters, &block) filter_chain.prepend_filter_to_chain(filters, :around, &block) end - # Shorthand for append_around_filter since it's the most common. + # Shorthand for +append_around_filter+ since it's the most common. alias :around_filter :append_around_filter # Removes the specified filters from the +before+ filter chain. Note that this only works for skipping method-reference diff --git a/actionpack/lib/action_controller/helpers.rb b/actionpack/lib/action_controller/helpers.rb index a8bead4d34..ce5e8be54c 100644 --- a/actionpack/lib/action_controller/helpers.rb +++ b/actionpack/lib/action_controller/helpers.rb @@ -20,7 +20,7 @@ module ActionController #:nodoc: end # The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+, - # +numbers+ and +ActiveRecord+ objects, to name a few. These helpers are available to all templates + # +numbers+ and Active Record objects, to name a few. These helpers are available to all templates # by default. # # In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to @@ -32,7 +32,7 @@ module ActionController #:nodoc: # controller which inherits from it. # # ==== Examples - # The +to_s+ method from the +Time+ class can be wrapped in a helper method to display a custom message if + # The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if # the Time object is blank: # # module FormattedTimeHelper @@ -41,7 +41,7 @@ module ActionController #:nodoc: # end # end # - # +FormattedTimeHelper+ can now be included in a controller, using the +helper+ class method: + # FormattedTimeHelper can now be included in a controller, using the +helper+ class method: # # class EventsController < ActionController::Base # helper FormattedTimeHelper @@ -74,22 +74,22 @@ module ActionController #:nodoc: # The +helper+ class method can take a series of helper module names, a block, or both. # - # * <tt>*args</tt>: One or more +Modules+, +Strings+ or +Symbols+, or the special symbol <tt>:all</tt>. + # * <tt>*args</tt>: One or more modules, strings or symbols, or the special symbol <tt>:all</tt>. # * <tt>&block</tt>: A block defining helper methods. # # ==== Examples - # When the argument is a +String+ or +Symbol+, the method will provide the "_helper" suffix, require the file + # When the argument is a string or symbol, the method will provide the "_helper" suffix, require the file # and include the module in the template class. The second form illustrates how to include custom helpers # when working with namespaced controllers, or other cases where the file containing the helper definition is not # in one of Rails' standard load paths: # helper :foo # => requires 'foo_helper' and includes FooHelper # helper 'resources/foo' # => requires 'resources/foo_helper' and includes Resources::FooHelper # - # When the argument is a +Module+, it will be included directly in the template class. + # When the argument is a module it will be included directly in the template class. # helper FooHelper # => includes FooHelper # # When the argument is the symbol <tt>:all</tt>, the controller will include all helpers from - # <tt>app/helpers/**/*.rb</tt> under +RAILS_ROOT+. + # <tt>app/helpers/**/*.rb</tt> under RAILS_ROOT. # helper :all # # Additionally, the +helper+ class method can receive and evaluate a block, making the methods defined available diff --git a/actionpack/lib/action_controller/integration.rb b/actionpack/lib/action_controller/integration.rb index a4bbee9c54..12e1b4d493 100644 --- a/actionpack/lib/action_controller/integration.rb +++ b/actionpack/lib/action_controller/integration.rb @@ -58,7 +58,7 @@ module ActionController class MultiPartNeededException < Exception end - # Create and initialize a new +Session+ instance. + # Create and initialize a new Session instance. def initialize reset! end @@ -100,7 +100,7 @@ module ActionController @https = flag end - # Return +true+ if the session is mimicing a secure HTTPS request. + # Return +true+ if the session is mimicking a secure HTTPS request. # # if session.https? # ... @@ -136,25 +136,25 @@ module ActionController end # Performs a GET request, following any subsequent redirect. - # See #request_via_redirect() for more information. + # See +request_via_redirect+ for more information. def get_via_redirect(path, parameters = nil, headers = nil) request_via_redirect(:get, path, parameters, headers) end # Performs a POST request, following any subsequent redirect. - # See #request_via_redirect() for more information. + # See +request_via_redirect+ for more information. def post_via_redirect(path, parameters = nil, headers = nil) request_via_redirect(:post, path, parameters, headers) end # Performs a PUT request, following any subsequent redirect. - # See #request_via_redirect() for more information. + # See +request_via_redirect+ for more information. def put_via_redirect(path, parameters = nil, headers = nil) request_via_redirect(:put, path, parameters, headers) end # Performs a DELETE request, following any subsequent redirect. - # See #request_via_redirect() for more information. + # See +request_via_redirect+ for more information. def delete_via_redirect(path, parameters = nil, headers = nil) request_via_redirect(:delete, path, parameters, headers) end @@ -166,12 +166,12 @@ module ActionController # Performs a GET request with the given parameters. The parameters may # be +nil+, a Hash, or a string that is appropriately encoded - # (application/x-www-form-urlencoded or multipart/form-data). The headers - # should be a hash. The keys will automatically be upcased, with the + # (<tt>application/x-www-form-urlencoded</tt> or <tt>multipart/form-data</tt>). + # The headers should be a hash. The keys will automatically be upcased, with the # prefix 'HTTP_' added if needed. # - # You can also perform POST, PUT, DELETE, and HEAD requests with #post, - # #put, #delete, and #head. + # You can also perform POST, PUT, DELETE, and HEAD requests with +post+, + # +put+, +delete+, and +head+. def get(path, parameters = nil, headers = nil) process :get, path, parameters, headers end @@ -228,6 +228,8 @@ module ActionController super + stdinput.set_encoding(Encoding::BINARY) if stdinput.respond_to?(:set_encoding) + stdinput.force_encoding(Encoding::BINARY) if stdinput.respond_to?(:force_encoding) @stdinput = stdinput.is_a?(IO) ? stdinput : StringIO.new(stdinput || '') end end @@ -382,6 +384,8 @@ module ActionController multipart_requestify(params).map do |key, value| if value.respond_to?(:original_filename) File.open(value.path) do |f| + f.set_encoding(Encoding::BINARY) if f.respond_to?(:set_encoding) + <<-EOF --#{boundary}\r Content-Disposition: form-data; name="#{key}"; filename="#{CGI.escape(value.original_filename)}"\r diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb index a17782cafc..1dbd8b9e6f 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime_responds.rb @@ -92,7 +92,7 @@ module ActionController #:nodoc: # with the remaining data. # # Note that you can define your own XML parameter parser which would allow you to describe multiple entities - # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow + # in a single request (i.e., by wrapping them all in a single root node), but if you just go with the flow # and accept Rails' defaults, life will be much easier. # # If you need to use a MIME type which isn't supported by default, you can register your own handlers in diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb index f43e2ba06d..fa123f7808 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_controller/mime_type.rb @@ -104,7 +104,7 @@ module Mime list[text_xml].name = Mime::XML.to_s end - # Look for more specific xml-based types and sort them ahead of app/xml + # Look for more specific XML-based types and sort them ahead of app/xml if app_xml idx = app_xml @@ -158,7 +158,7 @@ module Mime end end - # Returns true if ActionPack should check requests using this Mime Type for possible request forgery. See + # Returns true if Action Pack should check requests using this Mime Type for possible request forgery. See # ActionController::RequestForgerProtection. def verify_request? !@@unverifiable_types.include?(to_sym) diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index e2b7716aa2..509fa6a08e 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -1,6 +1,6 @@ module ActionController # Polymorphic URL helpers are methods for smart resolution to a named route call when - # given an ActiveRecord model instance. They are to be used in combination with + # given an Active Record model instance. They are to be used in combination with # ActionController::Resources. # # These methods are useful when you want to generate correct URL or path to a RESTful @@ -9,7 +9,9 @@ module ActionController # Nested resources and/or namespaces are also supported, as illustrated in the example: # # polymorphic_url([:admin, @article, @comment]) - # #-> results in: + # + # results in: + # # admin_article_comment_url(@article, @comment) # # == Usage within the framework @@ -38,11 +40,8 @@ module ActionController # # Example usage: # - # edit_polymorphic_path(@post) - # #=> /posts/1/edit - # - # formatted_polymorphic_path([@post, :pdf]) - # #=> /posts/1.pdf + # edit_polymorphic_path(@post) # => "/posts/1/edit" + # formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf" module PolymorphicRoutes # Constructs a call to a named RESTful route for the given record and returns the # resulting URL string. For example: diff --git a/actionpack/lib/action_controller/rack_process.rb b/actionpack/lib/action_controller/rack_process.rb new file mode 100644 index 0000000000..f42212e740 --- /dev/null +++ b/actionpack/lib/action_controller/rack_process.rb @@ -0,0 +1,321 @@ +require 'action_controller/cgi_ext' +require 'action_controller/session/cookie_store' + +module ActionController #:nodoc: + class RackRequest < AbstractRequest #:nodoc: + attr_accessor :env, :session_options + + class SessionFixationAttempt < StandardError #:nodoc: + end + + DEFAULT_SESSION_OPTIONS = { + :database_manager => CGI::Session::CookieStore, # store data in cookie + :prefix => "ruby_sess.", # prefix session file names + :session_path => "/", # available to all paths in app + :session_key => "_session_id", + :cookie_only => true + } unless const_defined?(:DEFAULT_SESSION_OPTIONS) + + def initialize(env, session_options = DEFAULT_SESSION_OPTIONS) + @session_options = session_options + @env = env + @cgi = CGIWrapper.new(self) + super() + end + + # The request body is an IO input stream. If the RAW_POST_DATA environment + # variable is already set, wrap it in a StringIO. + def body + if raw_post = env['RAW_POST_DATA'] + StringIO.new(raw_post) + else + @env['rack.input'] + end + end + + def key?(key) + @env.key? key + end + + def query_parameters + @query_parameters ||= self.class.parse_query_parameters(query_string) + end + + def request_parameters + @request_parameters ||= parse_formatted_request_parameters + end + + def cookies + return {} unless @env["HTTP_COOKIE"] + + if @env["rack.request.cookie_string"] == @env["HTTP_COOKIE"] + @env["rack.request.cookie_hash"] + else + @env["rack.request.cookie_string"] = @env["HTTP_COOKIE"] + # According to RFC 2109: + # If multiple cookies satisfy the criteria above, they are ordered in + # the Cookie header such that those with more specific Path attributes + # precede those with less specific. Ordering with respect to other + # attributes (e.g., Domain) is unspecified. + @env["rack.request.cookie_hash"] = + parse_query(@env["rack.request.cookie_string"], ';,').inject({}) { |h, (k,v)| + h[k] = Array === v ? v.first : v + h + } + end + end + + def host_with_port_without_standard_port_handling + if forwarded = @env["HTTP_X_FORWARDED_HOST"] + forwarded.split(/,\s?/).last + elsif http_host = @env['HTTP_HOST'] + http_host + elsif server_name = @env['SERVER_NAME'] + server_name + else + "#{env['SERVER_ADDR']}:#{env['SERVER_PORT']}" + end + end + + def host + host_with_port_without_standard_port_handling.sub(/:\d+$/, '') + end + + def port + if host_with_port_without_standard_port_handling =~ /:(\d+)$/ + $1.to_i + else + standard_port + end + end + + def remote_addr + @env['REMOTE_ADDR'] + end + + def session + unless defined?(@session) + if @session_options == false + @session = Hash.new + else + stale_session_check! do + if cookie_only? && query_parameters[session_options_with_string_keys['session_key']] + raise SessionFixationAttempt + end + case value = session_options_with_string_keys['new_session'] + when true + @session = new_session + when false + begin + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + # CGI::Session raises ArgumentError if 'new_session' == false + # and no session cookie or query param is present. + rescue ArgumentError + @session = Hash.new + end + when nil + @session = CGI::Session.new(@cgi, session_options_with_string_keys) + else + raise ArgumentError, "Invalid new_session option: #{value}" + end + @session['__valid_session'] + end + end + end + @session + end + + def reset_session + @session.delete if defined?(@session) && @session.is_a?(CGI::Session) + @session = new_session + end + + private + # Delete an old session if it exists then create a new one. + def new_session + if @session_options == false + Hash.new + else + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => false)).delete rescue nil + CGI::Session.new(@cgi, session_options_with_string_keys.merge("new_session" => true)) + end + end + + def cookie_only? + session_options_with_string_keys['cookie_only'] + end + + def stale_session_check! + yield + rescue ArgumentError => argument_error + if argument_error.message =~ %r{undefined class/module ([\w:]*\w)} + begin + # Note that the regexp does not allow $1 to end with a ':' + $1.constantize + rescue LoadError, NameError => const_error + raise ActionController::SessionRestoreError, <<-end_msg +Session contains objects whose class definition isn\'t available. +Remember to require the classes for all objects kept in the session. +(Original exception: #{const_error.message} [#{const_error.class}]) +end_msg + end + + retry + else + raise + end + end + + def session_options_with_string_keys + @session_options_with_string_keys ||= DEFAULT_SESSION_OPTIONS.merge(@session_options).stringify_keys + end + + # From Rack::Utils + def parse_query(qs, d = '&;') + params = {} + (qs || '').split(/[#{d}] */n).inject(params) { |h,p| + k, v = unescape(p).split('=',2) + if cur = params[k] + if cur.class == Array + params[k] << v + else + params[k] = [cur, v] + end + else + params[k] = v + end + } + + return params + end + + def unescape(s) + s.tr('+', ' ').gsub(/((?:%[0-9a-fA-F]{2})+)/n){ + [$1.delete('%')].pack('H*') + } + end + end + + class RackResponse < AbstractResponse #:nodoc: + attr_accessor :status + + def initialize + @writer = lambda { |x| @body << x } + @block = nil + super() + end + + def out(output = $stdout, &block) + @block = block + normalize_headers(@headers) + if [204, 304].include?(@status.to_i) + @headers.delete "Content-Type" + [status.to_i, @headers.to_hash, []] + else + [status.to_i, @headers.to_hash, self] + end + end + alias to_a out + + def each(&callback) + if @body.respond_to?(:call) + @writer = lambda { |x| callback.call(x) } + @body.call(self, self) + else + @body.each(&callback) + end + + @writer = callback + @block.call(self) if @block + end + + def write(str) + @writer.call str.to_s + str + end + + def close + @body.close if @body.respond_to?(:close) + end + + def empty? + @block == nil && @body.empty? + end + + private + def normalize_headers(options = "text/html") + if options.is_a?(String) + headers['Content-Type'] = options unless headers['Content-Type'] + else + headers['Content-Length'] = options.delete('Content-Length').to_s if options['Content-Length'] + + headers['Content-Type'] = options.delete('type') || "text/html" + headers['Content-Type'] += "; charset=" + options.delete('charset') if options['charset'] + + headers['Content-Language'] = options.delete('language') if options['language'] + headers['Expires'] = options.delete('expires') if options['expires'] + + @status = options.delete('Status') if options['Status'] + @status ||= 200 + # Convert 'cookie' header to 'Set-Cookie' headers. + # Because Set-Cookie header can appear more the once in the response body, + # we store it in a line break separated string that will be translated to + # multiple Set-Cookie header by the handler. + if cookie = options.delete('cookie') + cookies = [] + + case cookie + when Array then cookie.each { |c| cookies << c.to_s } + when Hash then cookie.each { |_, c| cookies << c.to_s } + else cookies << cookie.to_s + end + + @output_cookies.each { |c| cookies << c.to_s } if @output_cookies + + headers['Set-Cookie'] = [headers['Set-Cookie'], cookies].compact.join("\n") + end + + options.each { |k,v| headers[k] = v } + end + + "" + end + end + + class CGIWrapper < ::CGI + def initialize(request, *args) + @request = request + @args = *args + @input = request.body + + super *args + end + + def params + @params ||= @request.params + end + + def cookies + @request.cookies + end + + def query_string + @request.query_string + end + + # Used to wrap the normal args variable used inside CGI. + def args + @args + end + + # Used to wrap the normal env_table variable used inside CGI. + def env_table + @request.env + end + + # Used to wrap the normal stdinput variable used inside CGI. + def stdinput + @input + end + end +end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index d5ecbd9d29..914163a709 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -61,7 +61,7 @@ module ActionController request_method == :head end - # Provides acccess to the request's HTTP headers, for example: + # Provides access to the request's HTTP headers, for example: # request.headers["Content-Type"] # => "text/plain" def headers @headers ||= ActionController::Http::Headers.new(@env) @@ -231,7 +231,7 @@ EOM parts[0..-(tld_length+2)] end - # Return the query string, accounting for server idiosyncracies. + # Return the query string, accounting for server idiosyncrasies. def query_string if uri = @env['REQUEST_URI'] uri.split('?', 2)[1] || '' @@ -240,7 +240,7 @@ EOM end end - # Return the request URI, accounting for server idiosyncracies. + # Return the request URI, accounting for server idiosyncrasies. # WEBrick includes the full URL. IIS leaves REQUEST_URI blank. def request_uri if uri = @env['REQUEST_URI'] @@ -466,8 +466,8 @@ EOM parser.result end - def parse_multipart_form_parameters(body, boundary, content_length, env) - parse_request_parameters(read_multipart(body, boundary, content_length, env)) + def parse_multipart_form_parameters(body, boundary, body_size, env) + parse_request_parameters(read_multipart(body, boundary, body_size, env)) end def extract_multipart_boundary(content_type_with_parameters) @@ -519,7 +519,7 @@ EOM EOL = "\015\012" - def read_multipart(body, boundary, content_length, env) + def read_multipart(body, boundary, body_size, env) params = Hash.new([]) boundary = "--" + boundary quoted_boundary = Regexp.quote(boundary) @@ -529,8 +529,14 @@ EOM # start multipart/form-data body.binmode if defined? body.binmode + case body + when File + body.set_encoding(Encoding::BINARY) if body.respond_to?(:set_encoding) + when StringIO + body.string.force_encoding(Encoding::BINARY) if body.string.respond_to?(:force_encoding) + end boundary_size = boundary.size + EOL.size - content_length -= boundary_size + body_size -= boundary_size status = body.read(boundary_size) if nil == status raise EOFError, "no content body" @@ -541,7 +547,7 @@ EOM loop do head = nil content = - if 10240 < content_length + if 10240 < body_size UploadedTempfile.new("CGI") else UploadedStringIO.new @@ -563,24 +569,24 @@ EOM buf[0 ... (buf.size - (EOL + boundary + EOL).size)] = "" end - c = if bufsize < content_length + c = if bufsize < body_size body.read(bufsize) else - body.read(content_length) + body.read(body_size) end if c.nil? || c.empty? raise EOFError, "bad content body" end buf.concat(c) - content_length -= c.size + body_size -= c.size end buf = buf.sub(/\A((?:.|\n)*?)(?:[\r\n]{1,2})?#{quoted_boundary}([\r\n]{1,2}|--)/n) do content.print $1 if "--" == $2 - content_length = -1 + body_size = -1 end - boundary_end = $2.dup + boundary_end = $2.dup "" end @@ -607,7 +613,7 @@ EOM else params[name] = [content] end - break if content_length == -1 + break if body_size == -1 end raise EOFError, "bad boundary end of body part" unless boundary_end=~/--/ diff --git a/actionpack/lib/action_controller/request_forgery_protection.rb b/actionpack/lib/action_controller/request_forgery_protection.rb index 02c9d59d07..05a6d8bb79 100644 --- a/actionpack/lib/action_controller/request_forgery_protection.rb +++ b/actionpack/lib/action_controller/request_forgery_protection.rb @@ -17,7 +17,7 @@ module ActionController #:nodoc: # forged link from another site, is done by embedding a token based on the session (which an attacker wouldn't know) in all # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication - # scheme there anyway). Also, GET requests are not protected as these should be indempotent anyway. + # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway. # # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in diff --git a/actionpack/lib/action_controller/request_profiler.rb b/actionpack/lib/action_controller/request_profiler.rb index 8a18d194bd..a6471d0c08 100755 --- a/actionpack/lib/action_controller/request_profiler.rb +++ b/actionpack/lib/action_controller/request_profiler.rb @@ -17,13 +17,13 @@ module ActionController reset! end - def benchmark(n) + def benchmark(n, profiling = false) @quiet = true print ' ' result = Benchmark.realtime do n.times do |i| - run + run(profiling) print_progress(i) end end @@ -43,8 +43,15 @@ module ActionController script = File.read(script_path) source = <<-end_source - def run - #{script} + def run(profiling = false) + if profiling + RubyProf.resume do + #{script} + end + else + #{script} + end + old_request_count = request_count reset! self.request_count = old_request_count @@ -91,21 +98,22 @@ module ActionController def profile(sandbox) load_ruby_prof - results = RubyProf.profile { benchmark(sandbox) } + benchmark(sandbox, true) + results = RubyProf.stop show_profile_results results results end - def benchmark(sandbox) + def benchmark(sandbox, profiling = false) sandbox.request_count = 0 - elapsed = sandbox.benchmark(options[:n]).to_f + elapsed = sandbox.benchmark(options[:n], profiling).to_f count = sandbox.request_count.to_i puts '%.2f sec, %d requests, %d req/sec' % [elapsed, count, count / elapsed] end def warmup(sandbox) - Benchmark.realtime { sandbox.run } + Benchmark.realtime { sandbox.run(false) } end def default_options @@ -136,6 +144,7 @@ module ActionController protected def load_ruby_prof begin + gem 'ruby-prof', '>= 0.6.1' require 'ruby-prof' if mode = options[:measure] RubyProf.measure_mode = RubyProf.const_get(mode.upcase) diff --git a/actionpack/lib/action_controller/resources.rb b/actionpack/lib/action_controller/resources.rb index 26f75780c1..9fb1f9fa39 100644 --- a/actionpack/lib/action_controller/resources.rb +++ b/actionpack/lib/action_controller/resources.rb @@ -191,7 +191,7 @@ module ActionController # end # end # - # Along with the routes themselves, #resources generates named routes for use in + # Along with the routes themselves, +resources+ generates named routes for use in # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers: # # Named Route Helpers @@ -208,7 +208,7 @@ module ActionController # edit_message edit_message_url(id), hash_for_edit_message_url(id), # edit_message_path(id), hash_for_edit_message_path(id) # - # You can use these helpers instead of #url_for or methods that take #url_for parameters. For example: + # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example: # # redirect_to :controller => 'messages', :action => 'index' # # and @@ -406,7 +406,7 @@ module ActionController # end # end # - # Along with the routes themselves, #resource generates named routes for + # Along with the routes themselves, +resource+ generates named routes for # use in controllers and views. <tt>map.resource :account</tt> produces # these named routes and helpers: # diff --git a/actionpack/lib/action_controller/routing/builder.rb b/actionpack/lib/action_controller/routing/builder.rb index b1a98d1a51..4740113ed0 100644 --- a/actionpack/lib/action_controller/routing/builder.rb +++ b/actionpack/lib/action_controller/routing/builder.rb @@ -23,9 +23,9 @@ module ActionController # Accepts a "route path" (a string defining a route), and returns the array # of segments that corresponds to it. Note that the segment array is only # partially initialized--the defaults and requirements, for instance, need - # to be set separately, via the #assign_route_options method, and the - # #optional? method for each segment will not be reliable until after - # #assign_route_options is called, as well. + # to be set separately, via the +assign_route_options+ method, and the + # <tt>optional?</tt> method for each segment will not be reliable until after + # +assign_route_options+ is called, as well. def segments_for_route_path(path) rest, segments = path, [] diff --git a/actionpack/lib/action_controller/routing/segments.rb b/actionpack/lib/action_controller/routing/segments.rb index b142d18b47..864e068004 100644 --- a/actionpack/lib/action_controller/routing/segments.rb +++ b/actionpack/lib/action_controller/routing/segments.rb @@ -249,7 +249,7 @@ module ActionController end def extract_value - "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}" + "#{local_name} = hash[:#{key}] && hash[:#{key}].collect { |path_component| URI.escape(path_component.to_param, ActionController::Routing::Segment::UNSAFE_PCHAR) }.to_param #{"|| #{default.inspect}" if default}" end def default diff --git a/actionpack/lib/action_controller/session/cookie_store.rb b/actionpack/lib/action_controller/session/cookie_store.rb index ada1862c3e..b477c1f7da 100644 --- a/actionpack/lib/action_controller/session/cookie_store.rb +++ b/actionpack/lib/action_controller/session/cookie_store.rb @@ -34,7 +34,7 @@ require 'openssl' # to generate the HMAC message digest # such as 'MD5', 'RIPEMD160', 'SHA256', etc. # # To generate a secret key for an existing application, run -# `rake secret` and set the key in config/environment.rb. +# "rake secret" and set the key in config/environment.rb. # # Note that changing digest or secret invalidates all existing sessions! class CGI::Session::CookieStore diff --git a/actionpack/lib/action_controller/test_case.rb b/actionpack/lib/action_controller/test_case.rb index 77c6f26eac..f26e65ba34 100644 --- a/actionpack/lib/action_controller/test_case.rb +++ b/actionpack/lib/action_controller/test_case.rb @@ -15,6 +15,27 @@ module ActionController end end + # Superclass for Action Controller functional tests. Infers the controller under test from the test class name, + # and creates @controller, @request, @response instance variables. + # + # class WidgetsControllerTest < ActionController::TestCase + # def test_index + # get :index + # end + # end + # + # * @controller - WidgetController.new + # * @request - ActionController::TestRequest.new + # * @response - ActionController::TestResponse.new + # + # (Earlier versions of Rails required each functional test to subclass Test::Unit::TestCase and define + # @controller, @request, @response in +setup+.) + # + # If the controller cannot be inferred from the test class name, you can explicity set it with +tests+. + # + # class SpecialEdgeCaseWidgetsControllerTest < ActionController::TestCase + # tests WidgetController + # end class TestCase < ActiveSupport::TestCase # When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline # (bystepping the regular exception handling from rescue_action). If the request.remote_addr is anything else, the regular @@ -41,6 +62,8 @@ module ActionController @@controller_class = nil class << self + # Sets the controller class name. Useful if the name can't be inferred from test class. + # Expects +controller_class+ as a constant. Example: <tt>tests WidgetController</tt>. def tests(controller_class) self.controller_class = controller_class end diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index dcb6cdf4ca..f179d9b1c7 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -3,7 +3,7 @@ require 'action_controller/test_case' module ActionController #:nodoc: class Base - # Process a test request called with a +TestRequest+ object. + # Process a test request called with a TestRequest object. def self.process_test(request) new.process_test(request) end @@ -49,7 +49,7 @@ module ActionController #:nodoc: # Either the RAW_POST_DATA environment variable or the URL-encoded request # parameters. def raw_post - env['RAW_POST_DATA'] ||= url_encoded_request_parameters + env['RAW_POST_DATA'] ||= returning(url_encoded_request_parameters) { |b| b.force_encoding(Encoding::BINARY) if b.respond_to?(:force_encoding) } end def port=(number) @@ -222,7 +222,7 @@ module ActionController #:nodoc: !rendered_file.nil? end - # A shortcut to the flash. Returns an empyt hash if no session flash exists. + # A shortcut to the flash. Returns an empty hash if no session flash exists. def flash session['flash'] || {} end @@ -340,6 +340,7 @@ module ActionController #:nodoc: @content_type = content_type @original_filename = path.sub(/^.*#{File::SEPARATOR}([^#{File::SEPARATOR}]+)$/) { $1 } @tempfile = Tempfile.new(@original_filename) + @tempfile.set_encoding(Encoding::BINARY) if @tempfile.respond_to?(:set_encoding) @tempfile.binmode if binary FileUtils.copy_file(path, @tempfile.path) end @@ -357,7 +358,7 @@ module ActionController #:nodoc: module TestProcess def self.included(base) - # execute the request simulating a specific http method and set/volley the response + # execute the request simulating a specific HTTP method and set/volley the response %w( get post put delete head ).each do |method| base.class_eval <<-EOV, __FILE__, __LINE__ def #{method}(action, parameters = nil, session = nil, flash = nil) diff --git a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb index 1a3c770254..376bb87409 100644 --- a/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb +++ b/actionpack/lib/action_controller/vendor/html-scanner/html/selector.rb @@ -64,7 +64,7 @@ module HTML # # When using a combination of the above, the element name comes first # followed by identifier, class names, attributes, pseudo classes and - # negation in any order. Do not seprate these parts with spaces! + # negation in any order. Do not separate these parts with spaces! # Space separation is used for descendant selectors. # # For example: @@ -158,7 +158,7 @@ module HTML # * <tt>:not(selector)</tt> -- Match the element only if the element does not # match the simple selector. # - # As you can see, <tt>:nth-child<tt> pseudo class and its varient can get quite + # As you can see, <tt>:nth-child<tt> pseudo class and its variant can get quite # tricky and the CSS specification doesn't do a much better job explaining it. # But after reading the examples and trying a few combinations, it's easy to # figure out. diff --git a/actionpack/lib/action_pack/version.rb b/actionpack/lib/action_pack/version.rb index 70fc1ced8c..c67654d9a8 100644 --- a/actionpack/lib/action_pack/version.rb +++ b/actionpack/lib/action_pack/version.rb @@ -1,8 +1,8 @@ module ActionPack #:nodoc: module VERSION #:nodoc: MAJOR = 2 - MINOR = 0 - TINY = 991 + MINOR = 1 + TINY = 0 STRING = [MAJOR, MINOR, TINY].join('.') end diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 4840b2526d..c236666dcd 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -168,12 +168,12 @@ module ActionView #:nodoc: # Specify whether file modification times should be checked to see if a template needs recompilation @@cache_template_loading = false cattr_accessor :cache_template_loading - - # Specify whether file extension lookup should be cached, and whether template base path lookup should be cached. - # Should be +false+ for development environments. Defaults to +true+. - @@cache_template_extensions = true - cattr_accessor :cache_template_extensions + def self.cache_template_extensions=(*args) + ActiveSupport::Deprecation.warn("config.action_view.cache_template_extensions option has been deprecated and has no effect. " << + "Please remove it from your config files.", caller) + end + # 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 diff --git a/actionpack/lib/action_view/compiled_templates.rb b/actionpack/lib/action_view/compiled_templates.rb deleted file mode 100644 index 5a286432e3..0000000000 --- a/actionpack/lib/action_view/compiled_templates.rb +++ /dev/null @@ -1,69 +0,0 @@ -module ActionView - - # CompiledTemplates modules hold methods that have been compiled. - # Templates are compiled into these methods so that they do not need to be - # read and parsed for each request. - # - # Each template may be compiled into one or more methods. Each method accepts a given - # set of parameters which is used to implement local assigns passing. - # - # To use a compiled template module, create a new instance and include it into the class - # in which you want the template to be rendered. - class CompiledTemplates < Module - attr_reader :method_names - - def initialize - @method_names = Hash.new do |hash, key| - hash[key] = "__compiled_method_#{(hash.length + 1)}" - end - @mtimes = {} - end - - # Return the full key for the given identifier and argument names - def full_key(identifier, arg_names) - [identifier, arg_names] - end - - # Return the selector for this method or nil if it has not been compiled - def selector(identifier, arg_names) - key = full_key(identifier, arg_names) - method_names.key?(key) ? method_names[key] : nil - end - alias :compiled? :selector - - # Return the time at which the method for the given identifier and argument names was compiled. - def mtime(identifier, arg_names) - @mtimes[full_key(identifier, arg_names)] - end - - # Compile the provided source code for the given argument names and with the given initial line number. - # The identifier should be unique to this source. - # - # The file_name, if provided will appear in backtraces. If not provided, the file_name defaults - # to the identifier. - # - # This method will return the selector for the compiled version of this method. - def compile_source(identifier, arg_names, source, initial_line_number = 0, file_name = nil) - file_name ||= identifier - name = method_names[full_key(identifier, arg_names)] - arg_desc = arg_names.empty? ? '' : "(#{arg_names * ', '})" - fake_file_name = "#{file_name}#{arg_desc}" # Include the arguments for this version (for now) - - method_def = wrap_source(name, arg_names, source) - - begin - module_eval(method_def, fake_file_name, initial_line_number) - @mtimes[full_key(identifier, arg_names)] = Time.now - rescue Exception => e # errors from compiled source - e.blame_file! identifier - raise - end - name - end - - # Wrap the provided source in a def ... end block. - def wrap_source(name, arg_names, source) - "def #{name}(#{arg_names * ', '})\n#{source}\nend" - end - end -end diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_record_helper.rb index f3f204cc97..e788ebf359 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_record_helper.rb @@ -141,7 +141,7 @@ module ActionView # # error_messages_for 'user_common', 'user', :object_name => 'user' # - # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> paremeter which gives the actual + # If the objects cannot be located as instance variables, you can add an extra <tt>:object</tt> parameter which gives the actual # object (or array of objects to use): # # error_messages_for 'user', :object => @question.user diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index dfc7e2b3ed..e5a95a961c 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -11,8 +11,8 @@ module ActionView # === Using asset hosts # By default, Rails links to these assets on the current host in the public # folder, but you can direct Rails to link to assets from a dedicated assets server by - # setting ActionController::Base.asset_host in your environment.rb. For example, - # let's say your asset host is assets.example.com. + # setting ActionController::Base.asset_host in your <tt>config/environment.rb</tt>. For example, + # let's say your asset host is <tt>assets.example.com</tt>. # # ActionController::Base.asset_host = "assets.example.com" # image_tag("rails.png") @@ -22,8 +22,8 @@ module ActionView # # This is useful since browsers typically open at most two connections to a single host, # which means your assets often wait in single file for their turn to load. You can - # alleviate this by using a %d wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com") - # to automatically distribute asset requests among four hosts (e.g., assets0.example.com through assets3.example.com) + # alleviate this by using a <tt>%d</tt> wildcard in <tt>asset_host</tt> (for example, "assets%d.example.com") + # to automatically distribute asset requests among four hosts (e.g., "assets0.example.com" through "assets3.example.com") # so browsers will open eight connections rather than two. # # image_tag("rails.png") @@ -293,9 +293,9 @@ module ActionView end # Computes the path to a stylesheet asset in the public stylesheets directory. - # If the +source+ filename has no extension, .css will be appended. + # If the +source+ filename has no extension, <tt>.css</tt> will be appended. # Full paths from the document root will be passed through. - # Used internally by stylesheet_link_tag to build the stylesheet path. + # Used internally by +stylesheet_link_tag+ to build the stylesheet path. # # ==== Examples # stylesheet_path "style" # => /stylesheets/style.css @@ -309,7 +309,7 @@ module ActionView alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route # Returns a stylesheet link tag for the sources specified as arguments. If - # you don't specify an extension, .css will be appended automatically. + # you don't specify an extension, <tt>.css</tt> will be appended automatically. # You can modify the link attributes by passing a hash as the last argument. # # ==== Examples @@ -379,7 +379,7 @@ module ActionView # Computes the path to an image asset in the public images directory. # Full paths from the document root will be passed through. - # Used internally by image_tag to build the image path. + # Used internally by +image_tag+ to build the image path. # # ==== Examples # image_path("edit") # => /images/edit @@ -454,8 +454,8 @@ module ActionView end end - # Add the .ext if not present. Return full URLs otherwise untouched. - # Prefix with /dir/ if lacking a leading /. Account for relative URL + # Add the the extension +ext+ if not present. Return full URLs otherwise untouched. + # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL # roots. Rewrite the asset path for cache-busting asset ids. Include # asset host, if configured, with the correct request protocol. def compute_public_path(source, dir, ext = nil, include_host = true) @@ -502,9 +502,9 @@ module ActionView end end - # Pick an asset host for this source. Returns nil if no host is set, + # Pick an asset host for this source. Returns +nil+ if no host is set, # the host if no wildcard is set, the host interpolated with the - # numbers 0-3 if it contains %d (the number is the source hash mod 4), + # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), # or the value returned from invoking the proc if it's a proc. def compute_asset_host(source) if host = ActionController::Base.asset_host diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 8a9c8044ae..7ed6272898 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -689,7 +689,7 @@ module ActionView default[key] ||= time.send(key) end - Time.utc(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec]) + Time.utc_time(default[:year], default[:month], default[:day], default[:hour], default[:min], default[:sec]) end end end diff --git a/actionpack/lib/action_view/helpers/debug_helper.rb b/actionpack/lib/action_view/helpers/debug_helper.rb index 20de7e465f..ea70a697de 100644 --- a/actionpack/lib/action_view/helpers/debug_helper.rb +++ b/actionpack/lib/action_view/helpers/debug_helper.rb @@ -2,21 +2,28 @@ module ActionView module Helpers # Provides a set of methods for making it easier to debug Rails objects. module DebugHelper - # Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very - # readable way to inspect an object. + # Returns a YAML representation of +object+ wrapped with <pre> and </pre>. + # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead. + # Useful for inspecting an object at the time of rendering. # # ==== Example - # my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]} - # debug(my_hash) # - # => <pre class='debug_dump'>--- - # first: 1 - # second: two - # third: - # - 1 - # - 2 - # - 3 - # </pre> + # @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %> + # debug(@user) + # # => + # <pre class='debug_dump'>--- !ruby/object:User + # attributes: + # updated_at: + # username: testing + # + # age: 42 + # password: xyz + # created_at: + # attributes_cache: {} + # + # new_record: true + # </pre> + def debug(object) begin Marshal::dump(object) @@ -28,4 +35,4 @@ module ActionView end end end -end
\ No newline at end of file +end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b8600fe445..7d85799038 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -73,30 +73,81 @@ module ActionView # There are also methods for helping to build form tags in link:classes/ActionView/Helpers/FormOptionsHelper.html, # link:classes/ActionView/Helpers/DateHelper.html, and link:classes/ActionView/Helpers/ActiveRecordHelper.html module FormHelper - # Creates a form and a scope around a specific model object that is used as a base for questioning about - # values for the fields. + # Creates a form and a scope around a specific model object that is used as + # a base for questioning about values for the fields. # - # <% form_for :person, @person, :url => { :action => "update" } do |f| %> + # Rails provides succinct resource-oriented form generation with +form_for+ + # like this: + # + # <% form_for @offer do |f| %> + # <%= f.label :version, 'Version' %>: + # <%= f.text_field :version %><br /> + # <%= f.label :author, 'Author' %>: + # <%= f.text_field :author %><br /> + # <% end %> + # + # There, +form_for+ is able to generate the rest of RESTful form parameters + # based on introspection on the record, but to understand what it does we + # need to dig first into the alternative generic usage it is based upon. + # + # === Generic form_for + # + # The generic way to call +form_for+ yields a form builder around a model: + # + # <% form_for :person, :url => { :action => "update" } do |f| %> # <%= f.error_messages %> - # First name: <%= f.text_field :first_name %> - # Last name : <%= f.text_field :last_name %> - # Biography : <%= f.text_area :biography %> - # Admin? : <%= f.check_box :admin %> + # First name: <%= f.text_field :first_name %><br /> + # Last name : <%= f.text_field :last_name %><br /> + # Biography : <%= f.text_area :biography %><br /> + # Admin? : <%= f.check_box :admin %><br /> # <% end %> # - # Worth noting is that the form_for tag is called in a ERb evaluation block, not an ERb output block. So that's <tt><% %></tt>, - # not <tt><%= %></tt>. Also worth noting is that form_for yields a <tt>form_builder</tt> object, in this example as <tt>f</tt>, which emulates - # the API for the stand-alone FormHelper methods, but without the object name. So instead of <tt>text_field :person, :name</tt>, - # you get away with <tt>f.text_field :name</tt>. Notice that you can even do <tt><%= f.error_messages %></tt> to display the - # error messsages of the model object in question. + # There, the first argument is a symbol or string with the name of the + # object the form is about, and also the name of the instance variable the + # object is stored in. + # + # The form builder acts as a regular form helper that somehow carries the + # model. Thus, the idea is that + # + # <%= f.text_field :first_name %> + # + # gets expanded to # - # Even further, the form_for method allows you to more easily escape the instance variable convention. So while the stand-alone - # approach would require <tt>text_field :person, :name, :object => person</tt> - # to work with local variables instead of instance ones, the form_for calls remain the same. You simply declare once with - # <tt>:person, person</tt> and all subsequent field calls save <tt>:person</tt> and <tt>:object => person</tt>. + # <%= text_field :person, :first_name %> + # + # If the instance variable is not <tt>@person</tt> you can pass the actual + # record as the second argument: + # + # <% form_for :person, person, :url => { :action => "update" } do |f| %> + # ... + # <% end %> # - # Also note that form_for doesn't create an exclusive scope. It's still possible to use both the stand-alone FormHelper methods - # and methods from FormTagHelper. For example: + # In that case you can think + # + # <%= f.text_field :first_name %> + # + # gets expanded to + # + # <%= text_field :person, :first_name, :object => person %> + # + # You can even display error messages of the wrapped model this way: + # + # <%= f.error_messages %> + # + # In any of its variants, the rightmost argument to +form_for+ is an + # optional hash of options: + # + # * <tt>:url</tt> - The URL the form is submitted to. It takes the same fields + # you pass to +url_for+ or +link_to+. In particular you may pass here a + # named route directly as well. Defaults to the current action. + # * <tt>:html</tt> - Optional HTML attributes for the form tag. + # + # Worth noting is that the +form_for+ tag is called in a ERb evaluation block, + # not an ERb output block. So that's <tt><% %></tt>, not <tt><%= %></tt>. + # + # Also note that +form_for+ doesn't create an exclusive scope. It's still + # possible to use both the stand-alone FormHelper methods and methods from + # FormTagHelper. For example: # # <% form_for :person, @person, :url => { :action => "update" } do |f| %> # First name: <%= f.text_field :first_name %> @@ -105,42 +156,38 @@ module ActionView # Admin? : <%= check_box_tag "person[admin]", @person.company.admin? %> # <% end %> # - # Note: This also works for the methods in FormOptionHelper and DateHelper that are designed to work with an object as base, - # like FormOptionHelper#collection_select and DateHelper#datetime_select. - # - # HTML attributes for the form tag can be given as <tt>:html => {...}</tt>. For example: - # - # <% form_for :person, @person, :html => {:id => 'person_form'} do |f| %> - # ... - # <% end %> + # This also works for the methods in FormOptionHelper and DateHelper that are + # designed to work with an object as base, like FormOptionHelper#collection_select + # and DateHelper#datetime_select. # - # The above form will then have the <tt>id</tt> attribute with the value </tt>person_form</tt>, which you can then - # style with CSS or manipulate with JavaScript. + # === Resource-oriented style # - # === Relying on record identification + # As we said above, in addition to manually configuring the +form_for+ call, + # you can rely on automated resource identification, which will use the conventions + # and named routes of that approach. This is the preferred way to use +form_for+ + # nowadays. # - # In addition to manually configuring the form_for call, you can also rely on record identification, which will use - # the conventions and named routes of that approach. Examples: + # For example, if <tt>@post</tt> is an existing record you want to edit # - # <% form_for(@post) do |f| %> + # <% form_for @post do |f| %> # ... # <% end %> # - # This will expand to be the same as: + # is equivalent to something like: # # <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> # ... # <% end %> # - # And for new records: + # And for new records # # <% form_for(Post.new) do |f| %> # ... # <% end %> # - # This will expand to be the same as: + # expands to # - # <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> + # <% form_for :post, Post.new, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> # ... # <% end %> # @@ -150,7 +197,7 @@ module ActionView # ... # <% end %> # - # And for namespaced routes, like admin_post_url: + # And for namespaced routes, like +admin_post_url+: # # <% form_for([:admin, @post]) do |f| %> # ... @@ -277,13 +324,13 @@ module ActionView # # ==== Examples # label(:post, :title) - # #=> <label for="post_title">Title</label> + # # => <label for="post_title">Title</label> # # label(:post, :title, "A short title") - # #=> <label for="post_title">A short title</label> + # # => <label for="post_title">A short title</label> # # label(:post, :title, "A short title", :class => "title_label") - # #=> <label for="post_title" class="title_label">A short title</label> + # # => <label for="post_title" class="title_label">A short title</label> # def label(object_name, method, text = nil, options = {}) InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_label_tag(text, options) @@ -588,6 +635,8 @@ module ActionView value != 0 when String value == checked_value + when Array + value.include?(checked_value) else value.to_i != 0 end diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index bf65fe5574..c0cba24be4 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -119,7 +119,7 @@ module ActionView # end # end # - # Sample usage (selecting the associated +Author+ for an instance of +Post+, <tt>@post</tt>): + # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>): # collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true}) # # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return: @@ -144,10 +144,16 @@ module ActionView # In addition to the <tt>:include_blank</tt> option documented above, # this method also supports a <tt>:model</tt> option, which defaults # to TimeZone. This may be used by users to specify a different time - # zone model object. (See #time_zone_options_for_select for more + # zone model object. (See +time_zone_options_for_select+ for more # information.) + # + # You can also supply an array of TimeZone objects + # as +priority_zones+, so that they will be listed above the rest of the + # (long) list. (You can use TimeZone.us_zones as a convenience for + # obtaining a list of the US time zones.) + # # Finally, this method supports a <tt>:default</tt> option, which selects - # a default TimeZone if the object's time zone is nil. + # a default TimeZone if the object's time zone is +nil+. # # Examples: # time_zone_select( "user", "time_zone", nil, :include_blank => true) @@ -156,6 +162,8 @@ module ActionView # # time_zone_select( "user", 'time_zone', TimeZone.us_zones, :default => "Pacific Time (US & Canada)") # + # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ]) + # # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone) def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {}) InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options) @@ -164,7 +172,7 @@ module ActionView # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container # where the elements respond to first and last (such as a two-element array), the "lasts" serve as option values and # the "firsts" as option text. Hashes are turned into this form automatically, so the keys become "firsts" and values - # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +Selected+ + # become lasts. If +selected+ is specified, the matching "last" or element will get the selected option-tag. +selected+ # may also be an array of values to be selected when using a multiple select. # # Examples (call, result): @@ -209,24 +217,22 @@ module ActionView options_for_select(options, selected) end - # Returns a string of <tt><option></tt> tags, like <tt>#options_from_collection_for_select</tt>, but + # Returns a string of <tt><option></tt> tags, like <tt>options_from_collection_for_select</tt>, but # groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments. # # Parameters: - # +collection+:: An array of objects representing the <tt><optgroup></tt> tags - # +group_method+:: The name of a method which, when called on a member of +collection+, returns an - # array of child objects representing the <tt><option></tt> tags - # +group_label_method+:: The name of a method which, when called on a member of +collection+, returns a - # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag - # +option_key_method+:: The name of a method which, when called on a child object of a member of - # +collection+, returns a value to be used as the +value+ attribute for its - # <tt><option></tt> tag - # +option_value_method+:: The name of a method which, when called on a child object of a member of - # +collection+, returns a value to be used as the contents of its - # <tt><option></tt> tag - # +selected_key+:: A value equal to the +value+ attribute for one of the <tt><option></tt> tags, - # which will have the +selected+ attribute set. Corresponds to the return value - # of one of the calls to +option_key_method+. If +nil+, no selection is made. + # * +collection+ - An array of objects representing the <tt><optgroup></tt> tags. + # * +group_method+ - The name of a method which, when called on a member of +collection+, returns an + # array of child objects representing the <tt><option></tt> tags. + # * group_label_method+ - The name of a method which, when called on a member of +collection+, returns a + # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag. + # * +option_key_method+ - The name of a method which, when called on a child object of a member of + # +collection+, returns a value to be used as the +value+ attribute for its <tt><option></tt> tag. + # * +option_value_method+ - The name of a method which, when called on a child object of a member of + # +collection+, returns a value to be used as the contents of its <tt><option></tt> tag. + # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags, + # which will have the +selected+ attribute set. Corresponds to the return value of one of the calls + # to +option_key_method+. If +nil+, no selection is made. # # Example object structure for use with this method: # class Continent < ActiveRecord::Base @@ -265,9 +271,11 @@ module ActionView end end - # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to - # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so - # that they will be listed above the rest of the (long) list. + # Returns a string of option tags for most countries in the + # world (as defined in COUNTRIES). Supply a country name as + # +selected+ to have it marked as the selected option tag. You + # can also supply an array of countries as +priority_countries+, + # so that they will be listed above the rest of the (long) list. # # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag. def country_options_for_select(selected = nil, priority_countries = nil) @@ -292,8 +300,8 @@ module ActionView # a TimeZone. # # By default, +model+ is the TimeZone constant (which can be obtained - # in ActiveRecord as a value object). The only requirement is that the - # +model+ parameter be an object that responds to #all, and returns + # in Active Record as a value object). The only requirement is that the + # +model+ parameter be an object that responds to +all+, and returns # an array of objects that represent time zones. # # NOTE: Only the option tags are returned, you have to wrap this call in diff --git a/actionpack/lib/action_view/helpers/form_tag_helper.rb b/actionpack/lib/action_view/helpers/form_tag_helper.rb index 922a0662fe..ca58f4ba26 100644 --- a/actionpack/lib/action_view/helpers/form_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/form_tag_helper.rb @@ -3,7 +3,7 @@ require 'action_view/helpers/tag_helper' module ActionView module Helpers - # Provides a number of methods for creating form tags that doesn't rely on an ActiveRecord object assigned to the template like + # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like # FormHelper does. Instead, you provide the names and values manually. # # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying @@ -14,9 +14,9 @@ module ActionView # # ==== Options # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data". - # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post". - # If "put", "delete", or another verb is used, a hidden input with name _method - # is added to simulate the verb over post. + # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post". + # If "put", "delete", or another verb is used, a hidden input with name <tt>_method</tt> + # is added to simulate the verb over post. # * A list of parameters to feed to the URL the form will be posted to. # # ==== Examples diff --git a/actionpack/lib/action_view/helpers/javascript_helper.rb b/actionpack/lib/action_view/helpers/javascript_helper.rb index 1ea3cbd74e..ed931e064f 100644 --- a/actionpack/lib/action_view/helpers/javascript_helper.rb +++ b/actionpack/lib/action_view/helpers/javascript_helper.rb @@ -43,14 +43,23 @@ module ActionView end include PrototypeHelper - - # Returns a link that will trigger a JavaScript +function+ using the + + # Returns a link of the given +name+ that will trigger a JavaScript +function+ using the # onclick handler and return false after the fact. # + # The first argument +name+ is used as the link text. + # + # The next arguments are optional and may include the javascript function definition and a hash of html_options. + # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered # (instead of making an Ajax request first). # + # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button" + # + # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil + # + # # Examples: # link_to_function "Greeting", "alert('Hello world!')" # Produces: @@ -94,13 +103,21 @@ module ActionView ) end - # Returns a button that'll trigger a JavaScript +function+ using the + # Returns a button with the given +name+ text that'll trigger a JavaScript +function+ using the # onclick handler. # + # The first argument +name+ is used as the button's value or display text. + # + # The next arguments are optional and may include the javascript function definition and a hash of html_options. + # # The +function+ argument can be omitted in favor of an +update_page+ # block, which evaluates to a string when the template is rendered # (instead of making an Ajax request first). # + # The +html_options+ will accept a hash of html attributes for the link tag. Some examples are :class => "nav_button", :id => "articles_nav_button" + # + # Note: if you choose to specify the javascript function in a block, but would like to pass html_options, set the +function+ parameter to nil + # # Examples: # button_to_function "Greeting", "alert('Hello world!')" # button_to_function "Delete", "if (confirm('Really?')) do_delete()" diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 1a0e660d52..c731824f18 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -61,7 +61,7 @@ module ActionView # # == Designing your Rails actions for Ajax # When building your action handlers (that is, the Rails actions that receive your background requests), it's - # important to remember a few things. First, whatever your action would normall return to the browser, it will + # important to remember a few things. First, whatever your action would normally return to the browser, it will # return to the Ajax call. As such, you typically don't want to render with a layout. This call will cause # the layout to be transmitted back to your page, and, if you have a full HTML/CSS, will likely mess a lot of things up. # You can turn the layout off on particular actions by doing the following: @@ -595,8 +595,8 @@ module ActionView # JavaScript sent with a Content-type of "text/javascript". # # Create new instances with PrototypeHelper#update_page or with - # ActionController::Base#render, then call #insert_html, #replace_html, - # #remove, #show, #hide, #visual_effect, or any other of the built-in + # ActionController::Base#render, then call +insert_html+, +replace_html+, + # +remove+, +show+, +hide+, +visual_effect+, or any other of the built-in # methods on the yielded generator in any order you like to modify the # content and appearance of the current page. # @@ -687,7 +687,7 @@ module ActionView end end - # Returns an object whose <tt>#to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript + # Returns an object whose <tt>to_json</tt> evaluates to +code+. Use this to pass a literal JavaScript # expression as an argument to another JavaScriptGenerator method. def literal(code) ActiveSupport::JSON::Variable.new(code.to_s) @@ -1068,7 +1068,7 @@ module ActionView def build_observer(klass, name, options = {}) if options[:with] && (options[:with] !~ /[\{=(.]/) - options[:with] = "'#{options[:with]}=' + value" + options[:with] = "'#{options[:with]}=' + encodeURIComponent(value)" else options[:with] ||= 'value' unless options[:function] end @@ -1173,7 +1173,7 @@ module ActionView super(generator) end - # The JSON Encoder calls this to check for the #to_json method + # The JSON Encoder calls this to check for the +to_json+ method # Since it's a blank slate object, I suppose it responds to anything. def respond_to?(method) true diff --git a/actionpack/lib/action_view/helpers/record_tag_helper.rb b/actionpack/lib/action_view/helpers/record_tag_helper.rb index 40b66be79f..66c596f3a9 100644 --- a/actionpack/lib/action_view/helpers/record_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/record_tag_helper.rb @@ -2,7 +2,7 @@ module ActionView module Helpers module RecordTagHelper # Produces a wrapper DIV element with id and class parameters that - # relate to the specified ActiveRecord object. Usage example: + # relate to the specified Active Record object. Usage example: # # <% div_for(@person, :class => "foo") do %> # <%=h @person.name %> @@ -17,7 +17,7 @@ module ActionView end # content_tag_for creates an HTML element with id and class parameters - # that relate to the specified ActiveRecord object. For example: + # that relate to the specified Active Record object. For example: # # <% content_tag_for(:tr, @person) do %> # <td><%=h @person.first_name %></td> diff --git a/actionpack/lib/action_view/helpers/sanitize_helper.rb b/actionpack/lib/action_view/helpers/sanitize_helper.rb index 6c0a7ec25c..c3c03394ee 100644 --- a/actionpack/lib/action_view/helpers/sanitize_helper.rb +++ b/actionpack/lib/action_view/helpers/sanitize_helper.rb @@ -57,7 +57,7 @@ module ActionView self.class.white_list_sanitizer.sanitize(html, options) end - # Sanitizes a block of css code. Used by #sanitize when it comes across a style attribute + # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute. def sanitize_css(style) self.class.white_list_sanitizer.sanitize_css(style) end @@ -111,8 +111,8 @@ module ActionView end end - # Gets the HTML::FullSanitizer instance used by strip_tags. Replace with - # any object that responds to #sanitize + # Gets the HTML::FullSanitizer instance used by +strip_tags+. Replace with + # any object that responds to +sanitize+. # # Rails::Initializer.run do |config| # config.action_view.full_sanitizer = MySpecialSanitizer.new @@ -122,8 +122,8 @@ module ActionView @full_sanitizer ||= HTML::FullSanitizer.new end - # Gets the HTML::LinkSanitizer instance used by strip_links. Replace with - # any object that responds to #sanitize + # Gets the HTML::LinkSanitizer instance used by +strip_links+. Replace with + # any object that responds to +sanitize+. # # Rails::Initializer.run do |config| # config.action_view.link_sanitizer = MySpecialSanitizer.new @@ -133,8 +133,8 @@ module ActionView @link_sanitizer ||= HTML::LinkSanitizer.new end - # Gets the HTML::WhiteListSanitizer instance used by sanitize and sanitize_css. - # Replace with any object that responds to #sanitize + # Gets the HTML::WhiteListSanitizer instance used by sanitize and +sanitize_css+. + # Replace with any object that responds to +sanitize+. # # Rails::Initializer.run do |config| # config.action_view.white_list_sanitizer = MySpecialSanitizer.new @@ -144,7 +144,7 @@ module ActionView @white_list_sanitizer ||= HTML::WhiteListSanitizer.new end - # Adds valid HTML attributes that the #sanitize helper checks for URIs. + # Adds valid HTML attributes that the +sanitize+ helper checks for URIs. # # Rails::Initializer.run do |config| # config.action_view.sanitized_uri_attributes = 'lowsrc', 'target' @@ -154,7 +154,7 @@ module ActionView HTML::WhiteListSanitizer.uri_attributes.merge(attributes) end - # Adds to the Set of 'bad' tags for the #sanitize helper. + # Adds to the Set of 'bad' tags for the +sanitize+ helper. # # Rails::Initializer.run do |config| # config.action_view.sanitized_bad_tags = 'embed', 'object' @@ -163,7 +163,8 @@ module ActionView def sanitized_bad_tags=(attributes) HTML::WhiteListSanitizer.bad_tags.merge(attributes) end - # Adds to the Set of allowed tags for the #sanitize helper. + + # Adds to the Set of allowed tags for the +sanitize+ helper. # # Rails::Initializer.run do |config| # config.action_view.sanitized_allowed_tags = 'table', 'tr', 'td' @@ -173,7 +174,7 @@ module ActionView HTML::WhiteListSanitizer.allowed_tags.merge(attributes) end - # Adds to the Set of allowed html attributes for the #sanitize helper. + # Adds to the Set of allowed HTML attributes for the +sanitize+ helper. # # Rails::Initializer.run do |config| # config.action_view.sanitized_allowed_attributes = 'onclick', 'longdesc' @@ -183,7 +184,7 @@ module ActionView HTML::WhiteListSanitizer.allowed_attributes.merge(attributes) end - # Adds to the Set of allowed css properties for the #sanitize and #sanitize_css heleprs. + # Adds to the Set of allowed CSS properties for the #sanitize and +sanitize_css+ helpers. # # Rails::Initializer.run do |config| # config.action_view.sanitized_allowed_css_properties = 'expression' @@ -193,7 +194,7 @@ module ActionView HTML::WhiteListSanitizer.allowed_css_properties.merge(attributes) end - # Adds to the Set of allowed css keywords for the #sanitize and #sanitize_css helpers. + # Adds to the Set of allowed CSS keywords for the +sanitize+ and +sanitize_css+ helpers. # # Rails::Initializer.run do |config| # config.action_view.sanitized_allowed_css_keywords = 'expression' @@ -203,7 +204,7 @@ module ActionView HTML::WhiteListSanitizer.allowed_css_keywords.merge(attributes) end - # Adds to the Set of allowed shorthand css properties for the #sanitize and #sanitize_css helpers. + # Adds to the Set of allowed shorthand CSS properties for the +sanitize+ and +sanitize_css+ helpers. # # Rails::Initializer.run do |config| # config.action_view.sanitized_shorthand_css_properties = 'expression' @@ -213,7 +214,7 @@ module ActionView HTML::WhiteListSanitizer.shorthand_css_properties.merge(attributes) end - # Adds to the Set of allowed protocols for the #sanitize helper. + # Adds to the Set of allowed protocols for the +sanitize+ helper. # # Rails::Initializer.run do |config| # config.action_view.sanitized_allowed_protocols = 'ssh', 'feed' diff --git a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb index 12b4cfd3f8..b938c1a801 100644 --- a/actionpack/lib/action_view/helpers/scriptaculous_helper.rb +++ b/actionpack/lib/action_view/helpers/scriptaculous_helper.rb @@ -26,9 +26,9 @@ module ActionView # :url => { :action => "reload" }, # :complete => visual_effect(:highlight, "posts", :duration => 0.5) # - # If no element_id is given, it assumes "element" which should be a local + # If no +element_id+ is given, it assumes "element" which should be a local # variable in the generated JavaScript execution context. This can be - # used for example with drop_receiving_element: + # used for example with +drop_receiving_element+: # # <%= drop_receiving_element (...), :loading => visual_effect(:fade) %> # @@ -67,6 +67,7 @@ module ActionView # element as parameters. # # Example: + # # <%= sortable_element("my_list", :url => { :action => "order" }) %> # # In the example, the action gets a "my_list" array parameter @@ -79,60 +80,56 @@ module ActionView # # Additional +options+ are: # - # <tt>:format</tt>:: A regular expression to determine what to send - # as the serialized id to the server (the default - # is <tt>/^[^_]*_(.*)$/</tt>). - # - # <tt>:constraint</tt>:: Whether to constrain the dragging to either <tt>:horizontal</tt> - # or <tt>:vertical</tt> (or false to make it unconstrained). - # - # <tt>:overlap</tt>:: Calculate the item overlap in the <tt>:horizontal</tt> or - # <tt>:vertical</tt> direction. - # - # <tt>:tag</tt>:: Which children of the container element to treat as - # sortable (default is <tt>li</tt>). - # - # <tt>:containment</tt>:: Takes an element or array of elements to treat as - # potential drop targets (defaults to the original - # target element). - # - # <tt>:only</tt>:: A CSS class name or arry of class names used to filter - # out child elements as candidates. - # - # <tt>:scroll</tt>:: Determines whether to scroll the list during drag - # operations if the list runs past the visual border. - # - # <tt>:tree</tt>:: Determines whether to treat nested lists as part of the - # main sortable list. This means that you can create multi- - # layer lists, and not only sort items at the same level, - # but drag and sort items between levels. - # - # <tt>:hoverclass</tt>:: If set, the Droppable will have this additional CSS class - # when an accepted Draggable is hovered over it. - # - # <tt>:handle</tt>:: Sets whether the element should only be draggable by an - # embedded handle. The value may be a string referencing a - # CSS class value (as of script.aculo.us V1.5). The first - # child/grandchild/etc. element found within the element - # that has this CSS class value will be used as the handle. - # - # <tt>:ghosting</tt>:: Clones the element and drags the clone, leaving the original - # in place until the clone is dropped (default is <tt>false</tt>). - # - # <tt>:dropOnEmpty</tt>:: If set to true, the Sortable container will be made into - # a Droppable, that can receive a Draggable (as according to - # the containment rules) as a child element when there are no - # more elements inside (default is <tt>false</tt>). - # - # <tt>:onChange</tt>:: Called whenever the sort order changes while dragging. When - # dragging from one Sortable to another, the callback is - # called once on each Sortable. Gets the affected element as - # its parameter. - # - # <tt>:onUpdate</tt>:: Called when the drag ends and the Sortable's order is - # changed in any way. When dragging from one Sortable to - # another, the callback is called once on each Sortable. Gets - # the container as its parameter. + # * <tt>:format</tt> - A regular expression to determine what to send as the + # serialized id to the server (the default is <tt>/^[^_]*_(.*)$/</tt>). + # + # * <tt>:constraint</tt> - Whether to constrain the dragging to either + # <tt>:horizontal</tt> or <tt>:vertical</tt> (or false to make it unconstrained). + # + # * <tt>:overlap</tt> - Calculate the item overlap in the <tt>:horizontal</tt> + # or <tt>:vertical</tt> direction. + # + # * <tt>:tag</tt> - Which children of the container element to treat as + # sortable (default is <tt>li</tt>). + # + # * <tt>:containment</tt> - Takes an element or array of elements to treat as + # potential drop targets (defaults to the original target element). + # + # * <tt>:only</tt> - A CSS class name or arry of class names used to filter + # out child elements as candidates. + # + # * <tt>:scroll</tt> - Determines whether to scroll the list during drag + # operations if the list runs past the visual border. + # + # * <tt>:tree</tt> - Determines whether to treat nested lists as part of the + # main sortable list. This means that you can create multi-layer lists, + # and not only sort items at the same level, but drag and sort items + # between levels. + # + # * <tt>:hoverclass</tt> - If set, the Droppable will have this additional CSS class + # when an accepted Draggable is hovered over it. + # + # * <tt>:handle</tt> - Sets whether the element should only be draggable by an + # embedded handle. The value may be a string referencing a CSS class value + # (as of script.aculo.us V1.5). The first child/grandchild/etc. element + # found within the element that has this CSS class value will be used as + # the handle. + # + # * <tt>:ghosting</tt> - Clones the element and drags the clone, leaving + # the original in place until the clone is dropped (default is <tt>false</tt>). + # + # * <tt>:dropOnEmpty</tt> - If true the Sortable container will be made into + # a Droppable, that can receive a Draggable (as according to the containment + # rules) as a child element when there are no more elements inside (default + # is <tt>false</tt>). + # + # * <tt>:onChange</tt> - Called whenever the sort order changes while dragging. When + # dragging from one Sortable to another, the callback is called once on each + # Sortable. Gets the affected element as its parameter. + # + # * <tt>:onUpdate</tt> - Called when the drag ends and the Sortable's order is + # changed in any way. When dragging from one Sortable to another, the callback + # is called once on each Sortable. Gets the container as its parameter. # # See http://script.aculo.us for more documentation. def sortable_element(element_id, options = {}) @@ -170,8 +167,8 @@ module ActionView end # Makes the element with the DOM ID specified by +element_id+ receive - # dropped draggable elements (created by draggable_element). - # and make an AJAX call By default, the action called gets the DOM ID + # dropped draggable elements (created by +draggable_element+). + # and make an AJAX call. By default, the action called gets the DOM ID # of the element as parameter. # # Example: @@ -182,32 +179,30 @@ module ActionView # http://script.aculo.us for more documentation. # # Some of these +options+ include: - # <tt>:accept</tt>:: Set this to a string or an array of strings describing the - # allowable CSS classes that the draggable_element must have in order - # to be accepted by this drop_receiving_element. - # - # <tt>:confirm</tt>:: Adds a confirmation dialog. - # - # Example: - # :confirm => "Are you sure you want to do this?" - # - # <tt>:hoverclass</tt>:: If set, the drop_receiving_element will have this additional CSS class - # when an accepted draggable_element is hovered over it. - # - # <tt>:onDrop</tt>:: Called when a draggable_element is dropped onto this element. - # Override this callback with a javascript expression to - # change the default drop behavour. - # - # Example: - # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }" - # - # This callback gets three parameters: - # The +Draggable+ element, the +Droppable+ element and the - # +Event+ object. You can extract additional information about the - # drop - like if the Ctrl or Shift keys were pressed - from the +Event+ object. - # - # <tt>:with</tt>:: A JavaScript expression specifying the parameters for the XMLHttpRequest. - # Any expressions should return a valid URL query string. + # * <tt>:accept</tt> - Set this to a string or an array of strings describing the + # allowable CSS classes that the +draggable_element+ must have in order + # to be accepted by this +drop_receiving_element+. + # + # * <tt>:confirm</tt> - Adds a confirmation dialog. Example: + # + # :confirm => "Are you sure you want to do this?" + # + # * <tt>:hoverclass</tt> - If set, the +drop_receiving_element+ will have + # this additional CSS class when an accepted +draggable_element+ is + # hovered over it. + # + # * <tt>:onDrop</tt> - Called when a +draggable_element+ is dropped onto + # this element. Override this callback with a JavaScript expression to + # change the default drop behaviour. Example: + # + # :onDrop => "function(draggable_element, droppable_element, event) { alert('I like bananas') }" + # + # This callback gets three parameters: The Draggable element, the Droppable + # element and the Event object. You can extract additional information about + # the drop - like if the Ctrl or Shift keys were pressed - from the Event object. + # + # * <tt>:with</tt> - A JavaScript expression specifying the parameters for + # the XMLHttpRequest. Any expressions should return a valid URL query string. def drop_receiving_element(element_id, options = {}) javascript_tag(drop_receiving_element_js(element_id, options).chop!) end diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 9d220c546a..669a285424 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -464,11 +464,11 @@ module ActionView [-\w]+ # subdomain or domain (?:\.[-\w]+)* # remaining subdomains or domain (?::\d+)? # port - (?:/(?:(?:[~\w\+@%=-]|(?:[,.;:][^\s$]))+)?)* # path + (?:/(?:(?:[~\w\+@%=\(\)-]|(?:[,.;:][^\s$]))+)?)* # path (?:\?[\w\+@%&=.;-]+)? # query string (?:\#[\w\-]*)? # trailing anchor ) - ([[:punct:]]|\s|<|$) # trailing text + ([[:punct:]]|<|$|) # trailing text }x unless const_defined?(:AUTO_LINK_RE) # Turns all urls into clickable links. If a block is given, each url diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 38c8d18cb0..7bb189420b 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -10,7 +10,7 @@ module ActionView include JavaScriptHelper # Returns the URL for the set of +options+ provided. This takes the - # same options as url_for in ActionController (see the + # same options as +url_for+ in Action Controller (see the # documentation for ActionController::Base#url_for). Note that by default # <tt>:only_path</tt> is <tt>true</tt> so you'll get the relative /controller/action # instead of the fully qualified URL like http://example.com/controller/action. @@ -173,7 +173,7 @@ module ActionView # link_to "Nonsense search", searches_path(:foo => "bar", :baz => "quux") # # => <a href="/searches?foo=bar&baz=quux">Nonsense search</a> # - # The three options specfic to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows: + # The three options specific to +link_to+ (<tt>:confirm</tt>, <tt>:popup</tt>, and <tt>:method</tt>) are used as follows: # # link_to "Visit Other Site", "http://www.rubyonrails.org/", :confirm => "Are you sure?" # # => <a href="http://www.rubyonrails.org/" onclick="return confirm('Are you sure?');">Visit Other Site</a> diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 2cda3d94b5..369526188f 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -93,9 +93,9 @@ module ActionView #:nodoc: # Register a class that knows how to handle template files with the given # extension. This can be used to implement new template types. # The constructor for the class must take the ActiveView::Base instance - # as a parameter, and the class must implement a #render method that + # as a parameter, and the class must implement a +render+ method that # takes the contents of the template to render as well as the Hash of - # local assigns available to the template. The #render method ought to + # local assigns available to the template. The +render+ method ought to # return the rendered template as a string. def self.register_template_handler(extension, klass) @@template_handlers[extension.to_sym] = klass diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index b2e6589d81..16fedd9732 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -1,14 +1,6 @@ require 'active_support/test_case' module ActionView - class NonInferrableHelperError < ActionViewError - def initialize(name) - super "Unable to determine the helper to test from #{name}. " + - "You'll need to specify it using tests YourHelper in your " + - "test case definition" - end - end - class TestCase < ActiveSupport::TestCase class_inheritable_accessor :helper_class @@helper_class = nil @@ -29,7 +21,7 @@ module ActionView def determine_default_helper_class(name) name.sub(/Test$/, '').constantize rescue NameError - raise NonInferrableHelperError.new(name) + nil end end @@ -42,7 +34,9 @@ module ActionView setup :setup_with_helper_class def setup_with_helper_class - self.class.send(:include, helper_class) + if helper_class && !self.class.ancestors.include?(helper_class) + self.class.send(:include, helper_class) + end end class TestController < ActionController::Base diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 1db057580b..f152b1d19c 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -131,6 +131,10 @@ class AssertResponseWithUnexpectedErrorController < ActionController::Base def index raise 'FAIL' end + + def show + render :text => "Boom", :status => 500 + end end module Admin @@ -483,6 +487,16 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase rescue Test::Unit::AssertionFailedError => e assert e.message.include?('FAIL') end + + def test_assert_response_failure_response_with_no_exception + @controller = AssertResponseWithUnexpectedErrorController.new + get :show + assert_response :success + flunk 'Expected non-success response' + rescue Test::Unit::AssertionFailedError + rescue + flunk "assert_response failed to handle failure response with missing, but optional, exception." + end end class ActionPackHeaderTest < Test::Unit::TestCase diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 4aacb4a78a..f9b6b87bc6 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -232,7 +232,7 @@ class ActionCacheTest < Test::Unit::TestCase get :index cached_time = content_to_cache assert_equal cached_time, @response.body - assert_cache_exists 'hostname.com/action_caching_test' + assert fragment_exist?('hostname.com/action_caching_test') reset! get :index @@ -243,7 +243,7 @@ class ActionCacheTest < Test::Unit::TestCase get :destroy cached_time = content_to_cache assert_equal cached_time, @response.body - assert_cache_does_not_exist 'hostname.com/action_caching_test/destroy' + assert !fragment_exist?('hostname.com/action_caching_test/destroy') reset! get :destroy @@ -254,7 +254,7 @@ class ActionCacheTest < Test::Unit::TestCase get :with_layout cached_time = content_to_cache assert_not_equal cached_time, @response.body - assert_cache_exists 'hostname.com/action_caching_test/with_layout' + assert fragment_exist?('hostname.com/action_caching_test/with_layout') reset! get :with_layout @@ -266,14 +266,14 @@ class ActionCacheTest < Test::Unit::TestCase def test_action_cache_conditional_options @request.env['HTTP_ACCEPT'] = 'application/json' get :index - assert_cache_does_not_exist 'hostname.com/action_caching_test' + assert !fragment_exist?('hostname.com/action_caching_test') end def test_action_cache_with_custom_cache_path get :show cached_time = content_to_cache assert_equal cached_time, @response.body - assert_cache_exists 'test.host/custom/show' + assert fragment_exist?('test.host/custom/show') reset! get :show @@ -282,11 +282,11 @@ class ActionCacheTest < Test::Unit::TestCase def test_action_cache_with_custom_cache_path_in_block get :edit - assert_cache_exists 'test.host/edit' + assert fragment_exist?('test.host/edit') reset! get :edit, :id => 1 - assert_cache_exists 'test.host/1;edit' + assert fragment_exist?('test.host/1;edit') end def test_cache_expiration @@ -395,18 +395,8 @@ class ActionCacheTest < Test::Unit::TestCase @request.host = 'hostname.com' end - def assert_cache_exists(path) - full_path = cache_path(path) - assert File.exist?(full_path), "#{full_path.inspect} does not exist." - end - - def assert_cache_does_not_exist(path) - full_path = cache_path(path) - assert !File.exist?(full_path), "#{full_path.inspect} should not exist." - end - - def cache_path(path) - File.join(FILE_STORE_PATH, 'views', path + '.cache') + def fragment_exist?(path) + @controller.fragment_exist?(path) end def read_fragment(path) @@ -450,6 +440,19 @@ class FragmentCachingTest < Test::Unit::TestCase assert_nil @controller.read_fragment('name') end + def test_fragment_exist__with_caching_enabled + @store.write('views/name', 'value') + assert @controller.fragment_exist?('name') + assert !@controller.fragment_exist?('other_name') + end + + def test_fragment_exist__with_caching_disabled + ActionController::Base.perform_caching = false + @store.write('views/name', 'value') + assert !@controller.fragment_exist?('name') + assert !@controller.fragment_exist?('other_name') + end + def test_write_fragment__with_caching_enabled assert_nil @store.read('views/name') assert_equal 'value', @controller.write_fragment('name', 'value') @@ -494,7 +497,6 @@ class FragmentCachingTest < Test::Unit::TestCase assert_equal 'generated till now -> ', buffer end - def test_fragment_for @store.write('views/expensive', 'fragment content') fragment_computed = false diff --git a/actionpack/test/controller/cgi_test.rb b/actionpack/test/controller/cgi_test.rb index 87f72fda77..f0f3a4b826 100755 --- a/actionpack/test/controller/cgi_test.rb +++ b/actionpack/test/controller/cgi_test.rb @@ -114,3 +114,36 @@ class CgiRequestNeedsRewoundTest < BaseCgiTest assert_equal 0, request.body.pos end end + +class CgiResponseTest < BaseCgiTest + def setup + super + @fake_cgi.expects(:header).returns("HTTP/1.0 200 OK\nContent-Type: text/html\n") + @response = ActionController::CgiResponse.new(@fake_cgi) + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\nHello, World!", @output.string + end + + def test_head_request + @fake_cgi.env_table['REQUEST_METHOD'] = 'HEAD' + @response.body = "Hello, World!" + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n", @output.string + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + @response.out(@output) + assert_equal "HTTP/1.0 200 OK\nContent-Type: text/html\n01234", @output.string + end +end diff --git a/actionpack/test/controller/helper_test.rb b/actionpack/test/controller/helper_test.rb index 6dc77a4aaf..83e3b085e7 100644 --- a/actionpack/test/controller/helper_test.rb +++ b/actionpack/test/controller/helper_test.rb @@ -85,7 +85,7 @@ class HelperTest < Test::Unit::TestCase def test_helper_block_include assert_equal expected_helper_methods, missing_methods assert_nothing_raised { - @controller_class.helper { include TestHelper } + @controller_class.helper { include HelperTest::TestHelper } } assert [], missing_methods end diff --git a/actionpack/test/controller/rack_test.rb b/actionpack/test/controller/rack_test.rb new file mode 100644 index 0000000000..cd4151783e --- /dev/null +++ b/actionpack/test/controller/rack_test.rb @@ -0,0 +1,150 @@ +require 'abstract_unit' +require 'action_controller/rack_process' + +class BaseRackTest < Test::Unit::TestCase + def setup + @env = {"HTTP_MAX_FORWARDS"=>"10", "SERVER_NAME"=>"glu.ttono.us:8007", "FCGI_ROLE"=>"RESPONDER", "HTTP_X_FORWARDED_HOST"=>"glu.ttono.us", "HTTP_ACCEPT_ENCODING"=>"gzip, deflate", "HTTP_USER_AGENT"=>"Mozilla/5.0 (Macintosh; U; PPC Mac OS X; en) AppleWebKit/312.5.1 (KHTML, like Gecko) Safari/312.3.1", "PATH_INFO"=>"", "HTTP_ACCEPT_LANGUAGE"=>"en", "HTTP_HOST"=>"glu.ttono.us:8007", "SERVER_PROTOCOL"=>"HTTP/1.1", "REDIRECT_URI"=>"/dispatch.fcgi", "SCRIPT_NAME"=>"/dispatch.fcgi", "SERVER_ADDR"=>"207.7.108.53", "REMOTE_ADDR"=>"207.7.108.53", "SERVER_SOFTWARE"=>"lighttpd/1.4.5", "HTTP_COOKIE"=>"_session_id=c84ace84796670c052c6ceb2451fb0f2; is_admin=yes", "HTTP_X_FORWARDED_SERVER"=>"glu.ttono.us", "REQUEST_URI"=>"/admin", "DOCUMENT_ROOT"=>"/home/kevinc/sites/typo/public", "SERVER_PORT"=>"8007", "QUERY_STRING"=>"", "REMOTE_PORT"=>"63137", "GATEWAY_INTERFACE"=>"CGI/1.1", "HTTP_X_FORWARDED_FOR"=>"65.88.180.234", "HTTP_ACCEPT"=>"*/*", "SCRIPT_FILENAME"=>"/home/kevinc/sites/typo/public/dispatch.fcgi", "REDIRECT_STATUS"=>"200", "REQUEST_METHOD"=>"GET"} + # some Nokia phone browsers omit the space after the semicolon separator. + # some developers have grown accustomed to using comma in cookie values. + @alt_cookie_fmt_request_hash = {"HTTP_COOKIE"=>"_session_id=c84ace847,96670c052c6ceb2451fb0f2;is_admin=yes"} + @request = ActionController::RackRequest.new(@env) + end + + def default_test; end +end + + +class RackRequestTest < BaseRackTest + def test_proxy_request + assert_equal 'glu.ttono.us', @request.host_with_port + end + + def test_http_host + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org:8080" + assert_equal "rubyonrails.org:8080", @request.host_with_port + + @env['HTTP_X_FORWARDED_HOST'] = "www.firsthost.org, www.secondhost.org" + assert_equal "www.secondhost.org", @request.host + end + + def test_http_host_with_default_port_overrides_server_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "rubyonrails.org" + assert_equal "rubyonrails.org", @request.host_with_port + end + + def test_host_with_port_defaults_to_server_name_if_no_host_headers + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + assert_equal "glu.ttono.us:8007", @request.host_with_port + end + + def test_host_with_port_falls_back_to_server_addr_if_necessary + @env.delete "HTTP_X_FORWARDED_HOST" + @env.delete "HTTP_HOST" + @env.delete "SERVER_NAME" + assert_equal "207.7.108.53:8007", @request.host_with_port + end + + def test_host_with_port_if_http_standard_port_is_specified + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:80" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_with_port_if_https_standard_port_is_specified + @env['HTTP_X_FORWARDED_PROTO'] = "https" + @env['HTTP_X_FORWARDED_HOST'] = "glu.ttono.us:443" + assert_equal "glu.ttono.us", @request.host_with_port + end + + def test_host_if_ipv6_reference + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_host_if_ipv6_reference_with_port + @env.delete "HTTP_X_FORWARDED_HOST" + @env['HTTP_HOST'] = "[2001:1234:5678:9abc:def0::dead:beef]:8008" + assert_equal "[2001:1234:5678:9abc:def0::dead:beef]", @request.host + end + + def test_cookie_syntax_resilience + cookies = CGI::Cookie::parse(@env["HTTP_COOKIE"]); + assert_equal ["c84ace84796670c052c6ceb2451fb0f2"], cookies["_session_id"], cookies.inspect + assert_equal ["yes"], cookies["is_admin"], cookies.inspect + + alt_cookies = CGI::Cookie::parse(@alt_cookie_fmt_request_hash["HTTP_COOKIE"]); + assert_equal ["c84ace847,96670c052c6ceb2451fb0f2"], alt_cookies["_session_id"], alt_cookies.inspect + assert_equal ["yes"], alt_cookies["is_admin"], alt_cookies.inspect + end +end + + +class RackRequestParamsParsingTest < BaseRackTest + def test_doesnt_break_when_content_type_has_charset + data = 'flamenco=love' + @request.env['CONTENT_LENGTH'] = data.length + @request.env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + @request.env['RAW_POST_DATA'] = data + assert_equal({"flamenco"=> "love"}, @request.request_parameters) + end + + def test_doesnt_interpret_request_uri_as_query_string_when_missing + @request.env['REQUEST_URI'] = 'foo' + assert_equal({}, @request.query_parameters) + end +end + + +class RackRequestNeedsRewoundTest < BaseRackTest + def test_body_should_be_rewound + data = 'foo' + @env['rack.input'] = StringIO.new(data) + @env['CONTENT_LENGTH'] = data.length + @env['CONTENT_TYPE'] = 'application/x-www-form-urlencoded; charset=utf-8' + + # Read the request body by parsing params. + request = ActionController::RackRequest.new(@env) + request.request_parameters + + # Should have rewound the body. + assert_equal 0, request.body.pos + end +end + + +class RackResponseTest < BaseRackTest + def setup + super + @response = ActionController::RackResponse.new + @output = StringIO.new('') + end + + def test_simple_output + @response.body = "Hello, World!" + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["Hello, World!"], parts + end + + def test_streaming_block + @response.body = Proc.new do |response, output| + 5.times { |n| output.write(n) } + end + + status, headers, body = @response.out(@output) + assert_equal 200, status + assert_equal({"Content-Type" => "text/html", "Cache-Control" => "no-cache", "Set-Cookie" => ""}, headers) + + parts = [] + body.each { |part| parts << part } + assert_equal ["0", "1", "2", "3", "4"], parts + end +end diff --git a/actionpack/test/controller/routing_test.rb b/actionpack/test/controller/routing_test.rb index b28f7bcdff..5e5503fd52 100644 --- a/actionpack/test/controller/routing_test.rb +++ b/actionpack/test/controller/routing_test.rb @@ -50,6 +50,13 @@ class UriReservedCharactersRoutingTest < Test::Unit::TestCase :additional => ["add#{@segment}itional-1", "add#{@segment}itional-2"] } assert_equal options, @set.recognize_path("/controller/act#{@escaped}ion/var#{@escaped}iable/add#{@escaped}itional-1/add#{@escaped}itional-2") end + + def test_route_generation_allows_passing_non_string_values_to_generated_helper + assert_equal "/controller/action/variable/1/2", @set.generate(:controller => "controller", + :action => "action", + :variable => "variable", + :additional => [1, 2]) + end end class LegacyRouteSetTests < Test::Unit::TestCase diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index ba6c7f4299..38898a1f75 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -511,16 +511,26 @@ XML FILES_DIR = File.dirname(__FILE__) + '/../fixtures/multipart' + if RUBY_VERSION < '1.9' + READ_BINARY = 'rb' + READ_PLAIN = 'r' + else + READ_BINARY = 'rb:binary' + READ_PLAIN = 'r:binary' + end + def test_test_uploaded_file filename = 'mona_lisa.jpg' path = "#{FILES_DIR}/#{filename}" content_type = 'image/png' + expected = File.read(path) + expected.force_encoding(Encoding::BINARY) if expected.respond_to?(:force_encoding) file = ActionController::TestUploadedFile.new(path, content_type) assert_equal filename, file.original_filename assert_equal content_type, file.content_type assert_equal file.path, file.local_path - assert_equal File.read(path), file.read + assert_equal expected, file.read end def test_test_uploaded_file_with_binary @@ -529,10 +539,10 @@ XML content_type = 'image/png' binary_uploaded_file = ActionController::TestUploadedFile.new(path, content_type, :binary) - assert_equal File.open(path, 'rb').read, binary_uploaded_file.read + assert_equal File.open(path, READ_BINARY).read, binary_uploaded_file.read plain_uploaded_file = ActionController::TestUploadedFile.new(path, content_type) - assert_equal File.open(path, 'r').read, plain_uploaded_file.read + assert_equal File.open(path, READ_PLAIN).read, plain_uploaded_file.read end def test_fixture_file_upload_with_binary @@ -541,10 +551,10 @@ XML content_type = 'image/jpg' binary_file_upload = fixture_file_upload(path, content_type, :binary) - assert_equal File.open(path, 'rb').read, binary_file_upload.read + assert_equal File.open(path, READ_BINARY).read, binary_file_upload.read plain_file_upload = fixture_file_upload(path, content_type) - assert_equal File.open(path, 'r').read, plain_file_upload.read + assert_equal File.open(path, READ_PLAIN).read, plain_file_upload.read end def test_fixture_file_upload diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb deleted file mode 100644 index 73e7ec1d76..0000000000 --- a/actionpack/test/template/compiled_templates_test.rb +++ /dev/null @@ -1,192 +0,0 @@ -require 'abstract_unit' -require 'action_view/helpers/date_helper' -require 'action_view/compiled_templates' - -class CompiledTemplateTests < Test::Unit::TestCase - def setup - @ct = ActionView::CompiledTemplates.new - @v = Class.new - @v.send :include, @ct - @a = './test_compile_template_a.rhtml' - @b = './test_compile_template_b.rhtml' - @s = './test_compile_template_link.rhtml' - end - def teardown - [@a, @b, @s].each do |f| - FileUtils.rm(f) if File.exist?(f) || File.symlink?(f) - end - end - attr_reader :ct, :v - - def test_name_allocation - hi_world = ct.method_names['hi world'] - hi_sexy = ct.method_names['hi sexy'] - wish_upon_a_star = ct.method_names['I love seeing decent error messages'] - - assert_equal hi_world, ct.method_names['hi world'] - assert_equal hi_sexy, ct.method_names['hi sexy'] - assert_equal wish_upon_a_star, ct.method_names['I love seeing decent error messages'] - assert_equal 3, [hi_world, hi_sexy, wish_upon_a_star].uniq.length - end - - def test_wrap_source - assert_equal( - "def aliased_assignment(value)\nself.value = value\nend", - @ct.wrap_source(:aliased_assignment, [:value], 'self.value = value') - ) - - assert_equal( - "def simple()\nnil\nend", - @ct.wrap_source(:simple, [], 'nil') - ) - end - - def test_compile_source_single_method - selector = ct.compile_source('doubling method', [:a], 'a + a') - assert_equal 2, @v.new.send(selector, 1) - assert_equal 4, @v.new.send(selector, 2) - assert_equal -4, @v.new.send(selector, -2) - assert_equal 0, @v.new.send(selector, 0) - selector - end - - def test_compile_source_two_method - sel1 = test_compile_source_single_method # compile the method in the other test - sel2 = ct.compile_source('doubling method', [:a, :b], 'a + b + a + b') - assert_not_equal sel1, sel2 - - assert_equal 2, @v.new.send(sel1, 1) - assert_equal 4, @v.new.send(sel1, 2) - - assert_equal 6, @v.new.send(sel2, 1, 2) - assert_equal 32, @v.new.send(sel2, 15, 1) - end - - def test_mtime - t1 = Time.now - - test_compile_source_single_method - mtime = ct.mtime('doubling method', [:a]) - - assert mtime < Time.now - assert mtime > t1 - end - - uses_mocha 'test_compile_time' do - - def test_compile_time - t = Time.now - - File.open(@a, "w"){|f| f.puts @a} - File.open(@b, "w"){|f| f.puts @b} - # windows doesn't support symlinks (even under cygwin) - windows = (RUBY_PLATFORM =~ /win32/) - `ln -s #{@a} #{@s}` unless windows - - v = ActionView::Base.new - v.base_path = '.' - v.cache_template_loading = false - - ta = ActionView::Template.new(v, @a, false, {}) - tb = ActionView::Template.new(v, @b, false, {}) - ts = ActionView::Template.new(v, @s, false, {}) - - @handler_class = ActionView::Template.handler_class_for_extension(:rhtml) - @handler = @handler_class.new(v) - - # All templates were created at t+1 - File::Stat.any_instance.expects(:mtime).times(windows ? 2 : 3).returns(t + 1.second) - - # private methods template_changed_since? and compile_template? - # should report true for all since they have not been compiled - assert @handler.send(:template_changed_since?, @a, t) - assert @handler.send(:template_changed_since?, @b, t) - assert @handler.send(:template_changed_since?, @s, t) unless windows - - assert @handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # All templates are rendered at t+2 - Time.expects(:now).times(windows ? 2 : 3).returns(t + 2.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - a_n = v.method_names[@a] - b_n = v.method_names[@b] - s_n = v.method_names[@s] unless windows - # all of the files have changed since last compile - assert @handler.compile_time[a_n] > t - assert @handler.compile_time[b_n] > t - assert @handler.compile_time[s_n] > t unless windows - - # private methods template_changed_since? and compile_template? - # should report false for all since none have changed since compile - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns(t + 1.second) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert !@handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert !@handler.send(:compile_template?, ts) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # none of the files have changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert @handler.compile_time[s_n] < t + 3.seconds unless windows - - `rm #{@s}; ln -s #{@b} #{@s}` unless windows - # private methods template_changed_since? and compile_template? - # should report true for symlink since it has changed since compile - - # t + 3.seconds is for the symlink - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 9).returns( - *(windows ? [ t + 1.second, t + 1.second ] : - [ t + 1.second, t + 1.second, t + 3.second ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert !@handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert !@handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - # Only the symlink template gets rendered at t+3 - Time.stubs(:now).returns(t + 3.seconds) unless windows - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the symlink has changed since last compile - assert @handler.compile_time[a_n] < t + 3.seconds - assert @handler.compile_time[b_n] < t + 3.seconds - assert_equal @handler.compile_time[s_n], t + 3.seconds unless windows - - FileUtils.touch @b - # private methods template_changed_since? and compile_template? - # should report true for symlink and file at end of symlink - # since it has changed since last compile - # - # t+4 is for @b and also for the file that @s points to, which is @b - File::Stat.any_instance.expects(:mtime).times(windows ? 6 : 12).returns( - *(windows ? [ t + 1.second, t + 4.seconds ] : - [ t + 1.second, t + 4.seconds, t + 3.second, t + 4.seconds ]) * 3) - assert !@handler.send(:template_changed_since?, @a, @handler.compile_time[a_n]) - assert @handler.send(:template_changed_since?, @b, @handler.compile_time[b_n]) - assert @handler.send(:template_changed_since?, @s, @handler.compile_time[s_n]) unless windows - assert !@handler.send(:compile_template?, ta) - assert @handler.send(:compile_template?, tb) - assert @handler.send(:compile_template?, ts) unless windows - - Time.expects(:now).times(windows ? 1 : 2).returns(t + 5.seconds) - v.send(:render_template, ta) - v.send(:render_template, tb) - v.send(:render_template, ts) unless windows - # the file at the end of the symlink has changed since last compile - # both the symlink and the file at the end of it should be recompiled - assert @handler.compile_time[a_n] < t + 5.seconds - assert_equal @handler.compile_time[b_n], t + 5.seconds - assert_equal @handler.compile_time[s_n], t + 5.seconds unless windows - end - end -end diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index ae83c7bf47..0a7b19ba96 100755 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -1722,6 +1722,12 @@ class DateHelperTest < ActionView::TestCase assert_equal 2, dummy_instance_tag.send!(:default_time_from_options, :hour => 2).hour end end + + def test_instance_tag_default_time_from_options_handles_far_future_date + dummy_instance_tag = ActionView::Helpers::InstanceTag.new(1,2,3) + time = dummy_instance_tag.send!(:default_time_from_options, :year => 2050, :month => 2, :day => 10, :hour => 15, :min => 30, :sec => 45) + assert_equal 2050, time.year + end end protected diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 4538b6dc6f..af99e6243d 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -181,6 +181,17 @@ class FormHelperTest < ActionView::TestCase '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />', check_box("post", "secret?") ) + + @post.secret = ['0'] + assert_dom_equal( + '<input id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />', + check_box("post", "secret") + ) + @post.secret = ['1'] + assert_dom_equal( + '<input checked="checked" id="post_secret" name="post[secret]" type="checkbox" value="1" /><input name="post[secret]" type="hidden" value="0" />', + check_box("post", "secret") + ) end def test_check_box_with_explicit_checked_and_unchecked_values diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 9a1079b297..53a250f9d5 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -25,8 +25,6 @@ class Author::Nested < Author; end class PrototypeHelperBaseTest < ActionView::TestCase - tests ActionView::Helpers::PrototypeHelper - attr_accessor :template_format def setup @@ -219,9 +217,9 @@ class PrototypeHelperTest < PrototypeHelperBaseTest end def test_observe_field_using_with_option - expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + value})})\n//]]>\n</script>) + expected = %(<script type=\"text/javascript\">\n//<![CDATA[\nnew Form.Element.Observer('glass', 300, function(element, value) {new Ajax.Request('http://www.example.com/check_value', {asynchronous:true, evalScripts:true, parameters:'id=' + encodeURIComponent(value)})})\n//]]>\n</script>) assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => 'id') - assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + value") + assert_dom_equal expected, observe_field("glass", :frequency => 5.minutes, :url => { :action => "check_value" }, :with => "'id=' + encodeURIComponent(value)") end def test_observe_field_using_json_in_with_option diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 06e1fd1929..62cdca03d1 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -186,6 +186,7 @@ class TextHelperTest < ActionView::TestCase http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007 http://www.mail-archive.com/rails@lists.rubyonrails.org/ http://www.amazon.com/Testing-Equal-Sign-In-Path/ref=pd_bbs_sr_1?ie=UTF8&s=books&qid=1198861734&sr=8-1 + http://en.wikipedia.org/wiki/Sprite_(computer_graphics) ) urls.each do |url| @@ -262,6 +263,8 @@ class TextHelperTest < ActionView::TestCase assert_equal email2_result, auto_link(email2_raw) assert_equal '', auto_link(nil) assert_equal '', auto_link('') + assert_equal "#{link_result} #{link_result} #{link_result}", auto_link("#{link_raw} #{link_raw} #{link_raw}") + assert_equal '<a href="http://www.rubyonrails.com">Ruby On Rails</a>', auto_link('<a href="http://www.rubyonrails.com">Ruby On Rails</a>') end def test_auto_link_at_eol |