diff options
| author | Pratik Naik <pratiknaik@gmail.com> | 2008-07-12 21:39:36 +0100 | 
|---|---|---|
| committer | Pratik Naik <pratiknaik@gmail.com> | 2008-07-12 21:39:36 +0100 | 
| commit | 0cfa3574d599f3bc134cd13fa00d8f22809dd67b (patch) | |
| tree | b8af1a893ba900c9901c9b1fb7a9a2246aba67c6 /actionpack | |
| parent | ef67bd481bacd1ff36a79b7f23f3c08f12f6cbe1 (diff) | |
| parent | 99cc85bc099a757cdd44e4f5f1be4972ab124e0d (diff) | |
| download | rails-0cfa3574d599f3bc134cd13fa00d8f22809dd67b.tar.gz rails-0cfa3574d599f3bc134cd13fa00d8f22809dd67b.tar.bz2 rails-0cfa3574d599f3bc134cd13fa00d8f22809dd67b.zip  | |
Merge commit 'mainstream/master'
Conflicts:
	actionpack/lib/action_controller/base.rb
	railties/lib/rails_generator/commands.rb
Diffstat (limited to 'actionpack')
67 files changed, 1072 insertions, 1138 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 0a5afddf04..5b7bfe9c30 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,34 @@  *Edge* +* Set config.action_view.warn_cache_misses = true to receive a warning if you perform an action that results in an expensive disk operation that could be cached [Josh Peek] + +* Refactor template preloading. New abstractions include Renderable mixins and a refactored Template class [Josh Peek] + +* Changed ActionView::TemplateHandler#render API method signature to render(template, local_assigns = {}) [Josh Peek] + +* Changed PrototypeHelper#submit_to_remote to PrototypeHelper#button_to_remote to stay consistent with link_to_remote (submit_to_remote still works as an alias) #8994 [clemens] + +* Add :recursive option to javascript_include_tag and stylesheet_link_tag to be used along with :all. #480 [Damian Janowski] + +* Allow users to disable the use of the Accept header [Michael Koziarski] + +    The accept header is poorly implemented by browsers and causes strange +	errors when used on public sites where crawlers make requests too.  You +	can use formatted urls (e.g. /people/1.xml) to support API clients in a +	much simpler way. + +	To disable the header you need to set: + +	config.action_controller.use_accept_header = false + +* Do not stat template files in production mode before rendering. You will no longer be able to modify templates in production mode without restarting the server [Josh Peek] + +* Deprecated TemplateHandler line offset [Josh Peek] + +* Allow caches_action to accept cache store options. #416. [José Valim]. Example: +   +  caches_action :index, :redirected, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour +  * Remove define_javascript_functions, javascript_include_tag and friends are far superior. [Michael Koziarski]  * Deprecate :use_full_path render option. The supplying the option no longer has an effect [Josh Peek] @@ -14,6 +43,16 @@  * Made ActionView::Base#render_file private [Josh Peek] +* Refactor and simplify the implementation of assert_redirected_to.  Arguments are now normalised relative to the controller being tested,  not the root of the application.  [Michael Koziarski] + +  This could cause some erroneous test failures if you were redirecting between controllers +  in different namespaces and wrote your assertions relative to the root of the application. + +* Remove follow_redirect from controller functional tests. + +	If you want to follow redirects you can use integration tests.  The functional test +	version was only useful if you were using redirect_to :id=>... +  * Fix polymorphic_url with singleton resources.  #461 [Tammer Saleh]  * Replaced TemplateFinder abstraction with ViewLoadPaths [Josh Peek] diff --git a/actionpack/lib/action_controller/assertions/response_assertions.rb b/actionpack/lib/action_controller/assertions/response_assertions.rb index 3deda0b45a..cb36405700 100644 --- a/actionpack/lib/action_controller/assertions/response_assertions.rb +++ b/actionpack/lib/action_controller/assertions/response_assertions.rb @@ -56,74 +56,24 @@ module ActionController        #   # assert that the redirection was to the named route login_url        #   assert_redirected_to login_url        # +      #   # assert that the redirection was to the url for @customer +      #   assert_redirected_to @customer +      #        def assert_redirected_to(options = {}, message=nil)          clean_backtrace do            assert_response(:redirect, message)            return true if options == @response.redirected_to -          ActionController::Routing::Routes.reload if ActionController::Routing::Routes.empty? - -          begin -            url  = {} -            original = { :expected => options, :actual => @response.redirected_to.is_a?(Symbol) ? @response.redirected_to : @response.redirected_to.dup } -            original.each do |key, value| -              if value.is_a?(Symbol) -                value = @controller.respond_to?(value, true) ? @controller.send(value) : @controller.send("hash_for_#{value}_url") -              end - -              unless value.is_a?(Hash) -                request = case value -                  when NilClass    then nil -                  when /^\w+:\/\// then recognized_request_for(%r{^(\w+://.*?(/|$|\?))(.*)$} =~ value ? $3 : nil) -                  else                  recognized_request_for(value) -                end -                value = request.path_parameters if request -              end - -              if value.is_a?(Hash) # stringify 2 levels of hash keys -                if name = value.delete(:use_route) -                  route = ActionController::Routing::Routes.named_routes[name] -                  value.update(route.parameter_shell) -                end - -                value.stringify_keys! -                value.values.select { |v| v.is_a?(Hash) }.collect { |v| v.stringify_keys! } -                if key == :expected && value['controller'] == @controller.controller_name && original[:actual].is_a?(Hash) -                  original[:actual].stringify_keys! -                  value.delete('controller') if original[:actual]['controller'].nil? || original[:actual]['controller'] == value['controller'] -                end -              end - -              if value.respond_to?(:[]) && value['controller'] -                value['controller'] = value['controller'].to_s -                if key == :actual && value['controller'].first != '/' && !value['controller'].include?('/') -                  new_controller_path = ActionController::Routing.controller_relative_to(value['controller'], @controller.class.controller_path) -                  value['controller'] = new_controller_path if value['controller'] != new_controller_path && ActionController::Routing.possible_controllers.include?(new_controller_path) && @response.redirected_to.is_a?(Hash) -                end -                value['controller'] = value['controller'][1..-1] if value['controller'].first == '/' # strip leading hash -              end -              url[key] = value -            end - -            @response_diff = url[:actual].diff(url[:expected]) if url[:actual] -            msg = build_message(message, "expected a redirect to <?>, found one to <?>, a difference of <?> ", url[:expected], url[:actual], @response_diff) - -            assert_block(msg) do -              url[:expected].keys.all? do |k| -                if k == :controller then url[:expected][k] == ActionController::Routing.controller_relative_to(url[:actual][k], @controller.class.controller_path) -                else parameterize(url[:expected][k]) == parameterize(url[:actual][k]) -                end -              end -            end -          rescue ActionController::RoutingError # routing failed us, so match the strings only. -            msg = build_message(message, "expected a redirect to <?>, found one to <?>", options, @response.redirect_url) -            url_regexp = %r{^(\w+://.*?(/|$|\?))(.*)$} -            eurl, epath, url, path = [options, @response.redirect_url].collect do |url| -              u, p = (url_regexp =~ url) ? [$1, $3] : [nil, url] -              [u, (p.first == '/') ? p : '/' + p] -            end.flatten +           +          # Support partial arguments for hash redirections +          if options.is_a?(Hash) && @response.redirected_to.is_a?(Hash) +            return true if options.all? {|(key, value)| @response.redirected_to[key] == value} +          end +           +          redirected_to_after_normalisation = normalize_argument_to_redirection(@response.redirected_to) +          options_after_normalisation       = normalize_argument_to_redirection(options) -            assert_equal(eurl, url, msg) if eurl && url -            assert_equal(epath, path, msg) if epath && path +          if redirected_to_after_normalisation != options_after_normalisation +            flunk "Expected response to be a redirect to <#{options_after_normalisation}> but was a redirect to <#{redirected_to_after_normalisation}>"            end          end        end @@ -143,30 +93,31 @@ module ActionController              if expected.nil?                !@response.rendered_with_file?              else -              expected == rendered +              rendered.match(expected)              end            end          end        end        private -        # Recognizes the route for a given path. -        def recognized_request_for(path, request_method = nil) -          path = "/#{path}" unless path.first == '/' - -          # Assume given controller -          request = ActionController::TestRequest.new({}, {}, nil) -          request.env["REQUEST_METHOD"] = request_method.to_s.upcase if request_method -          request.path   = path - -          ActionController::Routing::Routes.recognize(request) -          request -        end          # Proxy to to_param if the object will respond to it.          def parameterize(value)            value.respond_to?(:to_param) ? value.to_param : value          end + +        def normalize_argument_to_redirection(fragment) +          after_routing = @controller.url_for(fragment) +          if after_routing =~ %r{^\w+://.*} +            after_routing +          else +            # FIXME - this should probably get removed. +            if after_routing.first != '/' +              after_routing = '/' + after_routing +            end +            @request.protocol + @request.host_with_port + after_routing +          end +        end      end    end  end diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 00d97793ee..df94f78f18 100755 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -340,7 +340,17 @@ module ActionController #:nodoc:      cattr_accessor :optimise_named_routes      self.optimise_named_routes = true -    # Controls whether request forgery protection is turned on or not. Turned off by default only in test mode. +    # Indicates whether the response format should be determined by examining the Accept HTTP header, +    # or by using the simpler params + ajax rules. +    # +    # If this is set to +true+ (the default) then +respond_to+ and +Request#format+ will take the Accept +    # header into account.  If it is set to false then the request format will be determined solely +    # by examining params[:format].  If params format is missing, the format will be either HTML or +    # Javascript depending on whether the request is an AJAX request. +    cattr_accessor :use_accept_header +    self.use_accept_header = true + +    # Controls whether request forgergy 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 @@ -402,7 +412,7 @@ module ActionController #:nodoc:        # More methods can be hidden using <tt>hide_actions</tt>.        def hidden_actions          unless read_inheritable_attribute(:hidden_actions) -          write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map(&:to_s)) +          write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map { |m| m.to_s })          end          read_inheritable_attribute(:hidden_actions) @@ -410,18 +420,18 @@ module ActionController #:nodoc:        # Hide each of the given methods from being callable as actions.        def hide_action(*names) -        write_inheritable_attribute(:hidden_actions, hidden_actions | names.map(&:to_s)) +        write_inheritable_attribute(:hidden_actions, hidden_actions | names.map { |name| name.to_s })        end -      ## View load paths determine the bases from which template references can be made. So a call to -      ## render("test/template") will be looked up in the view load paths array and the closest match will be -      ## returned. +      # View load paths determine the bases from which template references can be made. So a call to +      # render("test/template") will be looked up in the view load paths array and the closest match will be +      # returned.        def view_paths          @view_paths || superclass.view_paths        end        def view_paths=(value) -        @view_paths = ActionView::ViewLoadPaths.new(Array(value)) if value +        @view_paths = ActionView::Base.process_view_paths(value) if value        end        # Adds a view_path to the front of the view_paths array. @@ -603,7 +613,8 @@ module ActionController #:nodoc:        #        # This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>        # would have slashed-off the path components after the changed action. -      def url_for(options = {}) #:doc: +      def url_for(options = {}) +        options ||= {}          case options            when String              options @@ -641,7 +652,7 @@ module ActionController #:nodoc:        end        def view_paths=(value) -        @template.view_paths = ViewLoadPaths.new(value) +        @template.view_paths = ActionView::Base.process_view_paths(value)        end        # Adds a view_path to the front of the view_paths array. @@ -1042,29 +1053,31 @@ module ActionController #:nodoc:            status = 302          end +        response.redirected_to= options +        logger.info("Redirected to #{options}") if logger && logger.info? +          case options            when %r{^\w+://.*} -            raise DoubleRenderError if performed? -            logger.info("Redirected to #{options}") if logger && logger.info? -            response.redirect(options, interpret_status(status)) -            response.redirected_to = options -            @performed_redirect = true - +            redirect_to_full_url(options, status)            when String -            redirect_to(request.protocol + request.host_with_port + options, :status=>status) - +            redirect_to_full_url(request.protocol + request.host_with_port + options, status)            when :back -            request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"], :status=>status) : raise(RedirectBackError) - -          when Hash -            redirect_to(url_for(options), :status=>status) -            response.redirected_to = options - +            if referer = request.headers["Referer"] +              redirect_to(referer, :status=>status) +            else +              raise RedirectBackError +            end            else -            redirect_to(url_for(options), :status=>status) +            redirect_to_full_url(url_for(options), status)          end        end +      def redirect_to_full_url(url, status) +        raise DoubleRenderError if performed? +        response.redirect(url, interpret_status(status)) +        @performed_redirect = true +      end +        # Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that        # intermediate caches shouldn't cache the response.        # @@ -1188,7 +1201,7 @@ module ActionController #:nodoc:        end        def self.action_methods -        @action_methods ||= Set.new(public_instance_methods.map(&:to_s)) - hidden_actions +        @action_methods ||= Set.new(public_instance_methods.map { |m| m.to_s }) - hidden_actions        end        def add_variables_to_assigns @@ -1235,9 +1248,8 @@ module ActionController #:nodoc:        end        def template_exempt_from_layout?(template_name = default_template_name) -        extension = @template && @template.pick_template_extension(template_name) -        name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name -        @@exempt_from_layout.any? { |ext| name_with_extension =~ ext } +        template_name = @template.pick_template(template_name).to_s if @template +        @@exempt_from_layout.any? { |ext| template_name =~ ext }        end        def default_template_name(action_name = self.action_name) diff --git a/actionpack/lib/action_controller/caching/actions.rb b/actionpack/lib/action_controller/caching/actions.rb index 65a36f7f98..f3535f8330 100644 --- a/actionpack/lib/action_controller/caching/actions.rb +++ b/actionpack/lib/action_controller/caching/actions.rb @@ -27,13 +27,15 @@ module ActionController #:nodoc:      # You can set modify the default action cache path by passing a :cache_path option.  This will be passed directly to ActionCachePath.path_for.  This is handy      # for actions with multiple possible routes that should be cached differently.  If a block is given, it is called with the current controller instance.      # -    # And you can also use :if to pass a Proc that specifies when the action should be cached. +    # And you can also use :if (or :unless) to pass a Proc that specifies when the action should be cached. +    # +    # Finally, if you are using memcached, you can also pass :expires_in.      #      #   class ListsController < ApplicationController      #     before_filter :authenticate, :except => :public      #     caches_page   :public      #     caches_action :index, :if => Proc.new { |c| !c.request.format.json? } # cache if is not a JSON request -    #     caches_action :show, :cache_path => { :project => 1 } +    #     caches_action :show, :cache_path => { :project => 1 }, :expires_in => 1.hour      #     caches_action :feed, :cache_path => Proc.new { |controller|      #       controller.params[:user_id] ?      #         controller.send(:user_list_url, c.params[:user_id], c.params[:id]) : @@ -56,8 +58,10 @@ module ActionController #:nodoc:          def caches_action(*actions)            return unless cache_configured?            options = actions.extract_options! -          cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path)) -          around_filter(cache_filter, {:only => actions}.merge(options)) +          filter_options = { :only => actions, :if => options.delete(:if), :unless => options.delete(:unless) } + +          cache_filter = ActionCacheFilter.new(:layout => options.delete(:layout), :cache_path => options.delete(:cache_path), :store_options => options) +          around_filter(cache_filter, filter_options)          end        end @@ -80,8 +84,8 @@ module ActionController #:nodoc:          end          def before(controller) -          cache_path = ActionCachePath.new(controller, path_options_for(controller, @options)) -          if cache = controller.read_fragment(cache_path.path) +          cache_path = ActionCachePath.new(controller, path_options_for(controller, @options.slice(:cache_path))) +          if cache = controller.read_fragment(cache_path.path, @options[:store_options])              controller.rendered_action_cache = true              set_content_type!(controller, cache_path.extension)              options = { :text => cache } @@ -96,7 +100,7 @@ module ActionController #:nodoc:          def after(controller)            return if controller.rendered_action_cache || !caching_allowed(controller)            action_content = cache_layout? ? content_for_layout(controller) : controller.response.body -          controller.write_fragment(controller.action_cache_path.path, action_content) +          controller.write_fragment(controller.action_cache_path.path, action_content, @options[:store_options])          end          private @@ -162,10 +166,7 @@ module ActionController #:nodoc:              # If there's no extension in the path, check request.format              if extension.nil? -              extension = request.format.to_sym.to_s -              if extension=='all' -                extension = nil -              end +              extension = request.cache_format              end              extension            end diff --git a/actionpack/lib/action_controller/filters.rb b/actionpack/lib/action_controller/filters.rb index 60d92d9b98..fc63890d13 100644 --- a/actionpack/lib/action_controller/filters.rb +++ b/actionpack/lib/action_controller/filters.rb @@ -7,6 +7,225 @@ module ActionController #:nodoc:        end      end +    class FilterChain < ActiveSupport::Callbacks::CallbackChain #:nodoc: +      def append_filter_to_chain(filters, filter_type, &block) +        pos = find_filter_append_position(filters, filter_type) +        update_filter_chain(filters, filter_type, pos, &block) +      end + +      def prepend_filter_to_chain(filters, filter_type, &block) +        pos = find_filter_prepend_position(filters, filter_type) +        update_filter_chain(filters, filter_type, pos, &block) +      end + +      def create_filters(filters, filter_type, &block) +        filters, conditions = extract_options(filters, &block) +        filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) } +        filters +      end + +      def skip_filter_in_chain(*filters, &test) +        filters, conditions = extract_options(filters) +        filters.each do |filter| +          if callback = find(filter) then delete(callback) end +        end if conditions.empty? +        update_filter_in_chain(filters, :skip => conditions, &test) +      end + +      private +        def update_filter_chain(filters, filter_type, pos, &block) +          new_filters = create_filters(filters, filter_type, &block) +          insert(pos, new_filters).flatten! +        end + +        def find_filter_append_position(filters, filter_type) +          # appending an after filter puts it at the end of the call chain +          # before and around filters go before the first after filter in the chain +          unless filter_type == :after +            each_with_index do |f,i| +              return i if f.after? +            end +          end +          return -1 +        end + +        def find_filter_prepend_position(filters, filter_type) +          # prepending a before or around filter puts it at the front of the call chain +          # after filters go before the first after filter in the chain +          if filter_type == :after +            each_with_index do |f,i| +              return i if f.after? +            end +            return -1 +          end +          return 0 +        end + +        def find_or_create_filter(filter, filter_type, options = {}) +          update_filter_in_chain([filter], options) + +          if found_filter = find(filter) { |f| f.type == filter_type } +            found_filter +          else +            filter_kind = case +            when filter.respond_to?(:before) && filter_type == :before +              :before +            when filter.respond_to?(:after) && filter_type == :after +              :after +            else +              :filter +            end + +            case filter_type +            when :before +              BeforeFilter.new(filter_kind, filter, options) +            when :after +              AfterFilter.new(filter_kind, filter, options) +            else +              AroundFilter.new(filter_kind, filter, options) +            end +          end +        end + +        def update_filter_in_chain(filters, options, &test) +          filters.map! { |f| block_given? ? find(f, &test) : find(f) } +          filters.compact! + +          map! do |filter| +            if filters.include?(filter) +              new_filter = filter.dup +              new_filter.update_options!(options) +              new_filter +            else +              filter +            end +          end +        end +    end + +    class Filter < ActiveSupport::Callbacks::Callback #:nodoc: +      def initialize(kind, method, options = {}) +        super +        update_options! options +      end + +      def before? +        self.class == BeforeFilter +      end + +      def after? +        self.class == AfterFilter +      end + +      def around? +        self.class == AroundFilter +      end + +      # Make sets of strings from :only/:except options +      def update_options!(other) +        if other +          convert_only_and_except_options_to_sets_of_strings(other) +          if other[:skip] +            convert_only_and_except_options_to_sets_of_strings(other[:skip]) +          end +        end + +        options.update(other) +      end + +      private +        def should_not_skip?(controller) +          if options[:skip] +            !included_in_action?(controller, options[:skip]) +          else +            true +          end +        end + +        def included_in_action?(controller, options) +          if options[:only] +            options[:only].include?(controller.action_name) +          elsif options[:except] +            !options[:except].include?(controller.action_name) +          else +            true +          end +        end + +        def should_run_callback?(controller) +          should_not_skip?(controller) && included_in_action?(controller, options) && super +        end + +        def convert_only_and_except_options_to_sets_of_strings(opts) +          [:only, :except].each do |key| +            if values = opts[key] +              opts[key] = Array(values).map(&:to_s).to_set +            end +          end +        end +    end + +    class AroundFilter < Filter #:nodoc: +      def type +        :around +      end + +      def call(controller, &block) +        if should_run_callback?(controller) +          method = filter_responds_to_before_and_after? ? around_proc : self.method + +          # For around_filter do |controller, action| +          if method.is_a?(Proc) && method.arity == 2 +            evaluate_method(method, controller, block) +          else +            evaluate_method(method, controller, &block) +          end +        else +          block.call +        end +      end + +      private +        def filter_responds_to_before_and_after? +          method.respond_to?(:before) && method.respond_to?(:after) +        end + +        def around_proc +          Proc.new do |controller, action| +            method.before(controller) + +            if controller.send!(:performed?) +              controller.send!(:halt_filter_chain, method, :rendered_or_redirected) +            else +              begin +                action.call +              ensure +                method.after(controller) +              end +            end +          end +        end +    end + +    class BeforeFilter < Filter #:nodoc: +      def type +        :before +      end + +      def call(controller, &block) +        super +        if controller.send!(:performed?) +          controller.send!(:halt_filter_chain, method, :rendered_or_redirected) +        end +      end +    end + +    class AfterFilter < Filter #:nodoc: +      def type +        :after +      end +    end +      # Filters enable controllers to run shared pre- and post-processing code for its actions. These filters can be used to do      # authentication, caching, or auditing before the intended action is performed. Or to do localization or output      # compression after the action has been performed. Filters have access to the request, response, and all the instance @@ -245,201 +464,6 @@ module ActionController #:nodoc:      # 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) -        pos = find_filter_append_position(filters, filter_type) -        update_filter_chain(filters, filter_type, pos, &block) -      end - -      def prepend_filter_to_chain(filters, filter_type, &block) -        pos = find_filter_prepend_position(filters, filter_type) -        update_filter_chain(filters, filter_type, pos, &block) -      end - -      def create_filters(filters, filter_type, &block) -        filters, conditions = extract_options(filters, &block) -        filters.map! { |filter| find_or_create_filter(filter, filter_type, conditions) } -        filters -      end - -      def skip_filter_in_chain(*filters, &test) -        filters, conditions = extract_options(filters) -        filters.each do |filter| -          if callback = find(filter) then delete(callback) end -        end if conditions.empty? -        update_filter_in_chain(filters, :skip => conditions, &test) -      end - -      private -        def update_filter_chain(filters, filter_type, pos, &block) -          new_filters = create_filters(filters, filter_type, &block) -          insert(pos, new_filters).flatten! -        end - -        def find_filter_append_position(filters, filter_type) -          # appending an after filter puts it at the end of the call chain -          # before and around filters go before the first after filter in the chain -          unless filter_type == :after -            each_with_index do |f,i| -              return i if f.after? -            end -          end -          return -1 -        end - -        def find_filter_prepend_position(filters, filter_type) -          # prepending a before or around filter puts it at the front of the call chain -          # after filters go before the first after filter in the chain -          if filter_type == :after -            each_with_index do |f,i| -              return i if f.after? -            end -            return -1 -          end -          return 0 -        end - -        def find_or_create_filter(filter, filter_type, options = {}) -          update_filter_in_chain([filter], options) - -          if found_filter = find(filter) { |f| f.type == filter_type } -            found_filter -          else -            filter_kind = case -            when filter.respond_to?(:before) && filter_type == :before -              :before -            when filter.respond_to?(:after) && filter_type == :after -              :after -            else -              :filter -            end - -            case filter_type -            when :before -              BeforeFilter.new(filter_kind, filter, options) -            when :after -              AfterFilter.new(filter_kind, filter, options) -            else -              AroundFilter.new(filter_kind, filter, options) -            end -          end -        end - -        def update_filter_in_chain(filters, options, &test) -          filters.map! { |f| block_given? ? find(f, &test) : find(f) } -          filters.compact! - -          map! do |filter| -            if filters.include?(filter) -              new_filter = filter.dup -              new_filter.options.merge!(options) -              new_filter -            else -              filter -            end -          end -        end -    end - -    class Filter < ActiveSupport::Callbacks::Callback #:nodoc: -      def before? -        self.class == BeforeFilter -      end - -      def after? -        self.class == AfterFilter -      end - -      def around? -        self.class == AroundFilter -      end - -      private -        def should_not_skip?(controller) -          if options[:skip] -            !included_in_action?(controller, options[:skip]) -          else -            true -          end -        end - -        def included_in_action?(controller, options) -          if options[:only] -            Array(options[:only]).map(&:to_s).include?(controller.action_name) -          elsif options[:except] -            !Array(options[:except]).map(&:to_s).include?(controller.action_name) -          else -            true -          end -        end - -        def should_run_callback?(controller) -          should_not_skip?(controller) && included_in_action?(controller, options) && super -        end -    end - -    class AroundFilter < Filter #:nodoc: -      def type -        :around -      end - -      def call(controller, &block) -        if should_run_callback?(controller) -          method = filter_responds_to_before_and_after? ? around_proc : self.method - -          # For around_filter do |controller, action| -          if method.is_a?(Proc) && method.arity == 2 -            evaluate_method(method, controller, block) -          else -            evaluate_method(method, controller, &block) -          end -        else -          block.call -        end -      end - -      private -        def filter_responds_to_before_and_after? -          method.respond_to?(:before) && method.respond_to?(:after) -        end - -        def around_proc -          Proc.new do |controller, action| -            method.before(controller) - -            if controller.send!(:performed?) -              controller.send!(:halt_filter_chain, method, :rendered_or_redirected) -            else -              begin -                action.call -              ensure -                method.after(controller) -              end -            end -          end -        end -    end - -    class BeforeFilter < Filter #:nodoc: -      def type -        :before -      end - -      def call(controller, &block) -        super -        if controller.send!(:performed?) -          controller.send!(:halt_filter_chain, method, :rendered_or_redirected) -        end -      end -    end - -    class AfterFilter < Filter #:nodoc: -      def type -        :after -      end -    end -      module ClassMethods        # The passed <tt>filters</tt> will be appended to the filter_chain and        # will execute before the action on this controller is performed. @@ -545,13 +569,21 @@ module ActionController #:nodoc:        # Returns all the before filters for this class and all its ancestors.        # This method returns the actual filter that was assigned in the controller to maintain existing functionality.        def before_filters #:nodoc: -        filter_chain.select(&:before?).map(&:method) +        filters = [] +        filter_chain.each do |filter| +          filters << filter.method if filter.before? +        end +        filters        end        # Returns all the after filters for this class and all its ancestors.        # This method returns the actual filter that was assigned in the controller to maintain existing functionality.        def after_filters #:nodoc: -        filter_chain.select(&:after?).map(&:method) +        filters = [] +        filter_chain.each do |filter| +          filters << filter.method if filter.after? +        end +        filters        end      end diff --git a/actionpack/lib/action_controller/layout.rb b/actionpack/lib/action_controller/layout.rb index d0c717ff67..8b6febe254 100644 --- a/actionpack/lib/action_controller/layout.rb +++ b/actionpack/lib/action_controller/layout.rb @@ -304,7 +304,7 @@ module ActionController #:nodoc:        end        def layout_directory?(layout_name) -        @template.view_paths.find_template_file_for_path("#{File.join('layouts', layout_name)}.#{@template.template_format}.erb") ? true : false +        @template.file_exists?("#{File.join('layouts', layout_name)}.#{@template.template_format}")        end    end  end diff --git a/actionpack/lib/action_controller/mime_responds.rb b/actionpack/lib/action_controller/mime_responds.rb index 1dbd8b9e6f..29294476f7 100644 --- a/actionpack/lib/action_controller/mime_responds.rb +++ b/actionpack/lib/action_controller/mime_responds.rb @@ -114,7 +114,11 @@ module ActionController #:nodoc:          @request    = controller.request          @response   = controller.response -        @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts) +        if ActionController::Base.use_accept_header +          @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts) +        else +          @mime_type_priority = [@request.format] +        end          @order     = []          @responses = {} diff --git a/actionpack/lib/action_controller/mime_type.rb b/actionpack/lib/action_controller/mime_type.rb index fa123f7808..a7215e6ea3 100644 --- a/actionpack/lib/action_controller/mime_type.rb +++ b/actionpack/lib/action_controller/mime_type.rb @@ -72,57 +72,61 @@ module Mime        end        def parse(accept_header) -        # keep track of creation order to keep the subsequent sort stable -        list = [] -        accept_header.split(/,/).each_with_index do |header, index|  -          params, q = header.split(/;\s*q=/)        -          if params -            params.strip!           -            list << AcceptItem.new(index, params, q) unless params.empty? +        if accept_header !~ /,/ +          [Mime::Type.lookup(accept_header)] +        else +          # keep track of creation order to keep the subsequent sort stable +          list = [] +          accept_header.split(/,/).each_with_index do |header, index|  +            params, q = header.split(/;\s*q=/)        +            if params +              params.strip!           +              list << AcceptItem.new(index, params, q) unless params.empty? +            end            end -        end -        list.sort! +          list.sort! -        # Take care of the broken text/xml entry by renaming or deleting it -        text_xml = list.index("text/xml") -        app_xml = list.index(Mime::XML.to_s) +          # Take care of the broken text/xml entry by renaming or deleting it +          text_xml = list.index("text/xml") +          app_xml = list.index(Mime::XML.to_s) -        if text_xml && app_xml -          # set the q value to the max of the two -          list[app_xml].q = [list[text_xml].q, list[app_xml].q].max +          if text_xml && app_xml +            # set the q value to the max of the two +            list[app_xml].q = [list[text_xml].q, list[app_xml].q].max -          # make sure app_xml is ahead of text_xml in the list -          if app_xml > text_xml -            list[app_xml], list[text_xml] = list[text_xml], list[app_xml] -            app_xml, text_xml = text_xml, app_xml -          end +            # make sure app_xml is ahead of text_xml in the list +            if app_xml > text_xml +              list[app_xml], list[text_xml] = list[text_xml], list[app_xml] +              app_xml, text_xml = text_xml, app_xml +            end -          # delete text_xml from the list -          list.delete_at(text_xml) +            # delete text_xml from the list +            list.delete_at(text_xml) -        elsif text_xml -          list[text_xml].name = Mime::XML.to_s -        end +          elsif text_xml +            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 -          app_xml_type = list[app_xml] +          if app_xml +            idx = app_xml +            app_xml_type = list[app_xml] -          while(idx < list.length) -            type = list[idx] -            break if type.q < app_xml_type.q -            if type.name =~ /\+xml$/ -              list[app_xml], list[idx] = list[idx], list[app_xml] -              app_xml = idx +            while(idx < list.length) +              type = list[idx] +              break if type.q < app_xml_type.q +              if type.name =~ /\+xml$/ +                list[app_xml], list[idx] = list[idx], list[app_xml] +                app_xml = idx +              end +              idx += 1              end -            idx += 1            end -        end -        list.map! { |i| Mime::Type.lookup(i.name) }.uniq! -        list +          list.map! { |i| Mime::Type.lookup(i.name) }.uniq! +          list +        end        end      end diff --git a/actionpack/lib/action_controller/request.rb b/actionpack/lib/action_controller/request.rb index 2cd9672e1b..c42f113d2c 100755 --- a/actionpack/lib/action_controller/request.rb +++ b/actionpack/lib/action_controller/request.rb @@ -82,21 +82,34 @@ module ActionController      # Returns the accepted MIME type for the request      def accepts        @accepts ||= -        if @env['HTTP_ACCEPT'].to_s.strip.empty? -          [ content_type, Mime::ALL ].compact # make sure content_type being nil is not included -        else -          Mime::Type.parse(@env['HTTP_ACCEPT']) +        begin +          header = @env['HTTP_ACCEPT'].to_s.strip + +          if header.empty? +            [content_type, Mime::ALL].compact +          else +            Mime::Type.parse(header) +          end          end      end -    # Returns the Mime type for the format used in the request. If there is no format available, the first of the  -    # accept types will be used. Examples: +    # Returns the Mime type for the format used in the request.      #      #   GET /posts/5.xml   | request.format => Mime::XML      #   GET /posts/5.xhtml | request.format => Mime::HTML -    #   GET /posts/5       | request.format => request.accepts.first (usually Mime::HTML for browsers) +    #   GET /posts/5       | request.format => Mime::HTML or MIME::JS, or request.accepts.first depending on the value of <tt>ActionController::Base.use_accept_header</tt>      def format -      @format ||= parameters[:format] ? Mime::Type.lookup_by_extension(parameters[:format]) : accepts.first +      @format ||= begin +        if parameters[:format] +          Mime::Type.lookup_by_extension(parameters[:format]) +        elsif ActionController::Base.use_accept_header +          accepts.first +        elsif xhr? +          Mime::Type.lookup_by_extension("js") +        else +          Mime::Type.lookup_by_extension("html") +        end +      end      end @@ -116,19 +129,26 @@ module ActionController        @format = Mime::Type.lookup_by_extension(parameters[:format])      end +    # Returns a symbolized version of the <tt>:format</tt> parameter of the request. +    # If no format is given it returns <tt>:js</tt>for AJAX requests and <tt>:html</tt> +    # otherwise.      def template_format        parameter_format = parameters[:format] -      case -      when parameter_format.blank? && !xhr? -        :html -      when parameter_format.blank? && xhr? +      if parameter_format +        parameter_format.to_sym +      elsif xhr?          :js        else -        parameter_format.to_sym +        :html        end      end +    def cache_format +      parameter_format = parameters[:format] +      parameter_format && parameter_format.to_sym +    end +      # Returns true if the request's "X-Requested-With" header contains      # "XMLHttpRequest". (The Prototype Javascript library sends this header with      # every Ajax request.) diff --git a/actionpack/lib/action_controller/routing.rb b/actionpack/lib/action_controller/routing.rb index 8846dcc504..dfbaa53b7c 100644 --- a/actionpack/lib/action_controller/routing.rb +++ b/actionpack/lib/action_controller/routing.rb @@ -88,6 +88,10 @@ module ActionController    #    #   map.connect ':controller/:action/:id', :action => 'show', :defaults => { :page => 'Dashboard' }    # +  # Note: The default routes, as provided by the Rails generator, make all actions in every +  # controller accessible via GET requests. You should consider removing them or commenting +  # them out if you're using named routes and resources. +  #    # == Named routes    #    # Routes can be named with the syntax <tt>map.name_of_route options</tt>, diff --git a/actionpack/lib/action_controller/test_process.rb b/actionpack/lib/action_controller/test_process.rb index f179d9b1c7..caf7253424 100644 --- a/actionpack/lib/action_controller/test_process.rb +++ b/actionpack/lib/action_controller/test_process.rb @@ -207,13 +207,9 @@ module ActionController #:nodoc:      # Returns the template path of the file which was used to      # render this response (or nil)  -    def rendered_file(with_controller=false) -      unless template.first_render.nil? -        unless with_controller -          template.first_render -        else -          template.first_render.split('/').last || template.first_render -        end +    def rendered_file(with_controller = false) +      if template.first_render +        template.first_render.to_s        end      end @@ -404,15 +400,6 @@ module ActionController #:nodoc:      end      alias xhr :xml_http_request -    def follow_redirect -      redirected_controller = @response.redirected_to[:controller] -      if redirected_controller && redirected_controller != @controller.controller_name -        raise "Can't follow redirects outside of current controller (from #{@controller.controller_name} to #{redirected_controller})" -      end - -      get(@response.redirected_to.delete(:action), @response.redirected_to.stringify_keys) -    end -      def assigns(key = nil)         if key.nil?           @response.template.assigns  diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 49768fe264..9ab615c7a5 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -21,14 +21,14 @@  # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.  #++ +  require 'action_view/template_handlers' -require 'action_view/template_file' -require 'action_view/view_load_paths' +require 'action_view/renderable' +require 'action_view/renderable_partial' -require 'action_view/renderer'  require 'action_view/template' -require 'action_view/partial_template'  require 'action_view/inline_template' +require 'action_view/paths'  require 'action_view/base'  require 'action_view/partials' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 64e0ab575f..fb82443060 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -3,6 +3,12 @@ module ActionView #:nodoc:    end    class MissingTemplate < ActionViewError #:nodoc: +    def initialize(paths, path, template_format = nil) +      full_template_path = path.include?('.') ? path : "#{path}.erb" +      display_paths = paths.join(':') +      template_type = (path =~ /layouts/i) ? 'layout' : 'template' +      super("Missing #{template_type} #{full_template_path} in view path #{display_paths}") +    end    end    # Action View templates can be written in three ways. If the template file has a <tt>.erb</tt> (or <tt>.rhtml</tt>) extension then it uses a mixture of ERb @@ -151,7 +157,6 @@ module ActionView #:nodoc:    #    # See the ActionView::Helpers::PrototypeHelper::GeneratorMethods documentation for more details.    class Base -    extend TemplateHandlers      include ERB::Util      attr_accessor :base_path, :assigns, :template_extension, :first_render @@ -166,7 +171,7 @@ module ActionView #:nodoc:        delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB'      end -    # Specify whether file modification times should be checked to see if a template needs recompilation +    # Specify whether templates should be cached. Otherwise the file we be read everytime it is accessed.      @@cache_template_loading = false      cattr_accessor :cache_template_loading @@ -180,6 +185,10 @@ module ActionView #:nodoc:      @@debug_rjs = false      cattr_accessor :debug_rjs +    # A warning will be displayed whenever an action results in a cache miss on your view paths. +    @@warn_cache_misses = false +    cattr_accessor :warn_cache_misses +      attr_internal :request      delegate :request_forgery_protection_token, :template, :params, :session, :cookies, :response, :headers, @@ -190,12 +199,6 @@ module ActionView #:nodoc:      end      include CompiledTemplates -    # Maps inline templates to their method names -    cattr_accessor :method_names -    @@method_names = {} -    # Map method names to the names passed in local assigns so far -    @@template_args = {} -      # Cache public asset paths      cattr_reader :computed_public_paths      @@computed_public_paths = {} @@ -213,6 +216,10 @@ module ActionView #:nodoc:        return helpers      end +    def self.process_view_paths(value) +      ActionView::PathSet.new(Array(value)) +    end +      def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:        @assigns = assigns_for_first_render        @assigns_added = nil @@ -223,12 +230,14 @@ module ActionView #:nodoc:      attr_reader :view_paths      def view_paths=(paths) -      @view_paths = ViewLoadPaths.new(Array(paths)) +      @view_paths = self.class.process_view_paths(paths)      end      # Renders the template present at <tt>template_path</tt> (relative to the view_paths array).      # The hash in <tt>local_assigns</tt> is made available as local variables.      def render(options = {}, local_assigns = {}, &block) #:nodoc: +      local_assigns ||= {} +        if options.is_a?(String)          render_file(options, nil, local_assigns)        elsif options == :update @@ -263,13 +272,9 @@ module ActionView #:nodoc:        template_path.split('/').last[0,1] != '_'      end -    # Returns a symbolized version of the <tt>:format</tt> parameter of the request, -    # or <tt>:html</tt> by default. -    # -    # EXCEPTION: If the <tt>:format</tt> parameter is not set, the Accept header will be examined for -    # whether it contains the JavaScript mime type as its first priority. If that's the case, -    # it will be used. This ensures that Ajax applications can use the same URL to support both -    # JavaScript and non-JavaScript users. +    # The format to be used when choosing between multiple templates with +    # the same name but differing formats.  See +Request#template_format+ +    # for more details.      def template_format        return @template_format if @template_format @@ -281,21 +286,50 @@ module ActionView #:nodoc:      end      def file_exists?(template_path) -      view_paths.template_exists?(template_file_from_name(template_path)) +      pick_template(template_path) ? true : false +    rescue MissingTemplate +      false      end      # Gets the extension for an existing template with the given template_path.      # Returns the format with the extension if that template exists.      # -    #   pick_template_extension('users/show') -    #   # => 'html.erb' +    #   pick_template('users/show') +    #   # => 'users/show.html.erb'      # -    #   pick_template_extension('users/legacy') -    #   # => "rhtml" +    #   pick_template('users/legacy') +    #   # => 'users/legacy.rhtml'      # -    def pick_template_extension(template_path) -      if template = template_file_from_name(template_path) -        template.extension +    def pick_template(template_path) +      path = template_path.sub(/^\//, '') +      if m = path.match(/(.*)\.(\w+)$/) +        template_file_name, template_file_extension = m[1], m[2] +      else +        template_file_name = path +      end + +      # OPTIMIZE: Checks to lookup template in view path +      if template = self.view_paths["#{template_file_name}.#{template_format}"] +        template +      elsif template = self.view_paths[template_file_name] +        template +      elsif first_render && template = self.view_paths["#{template_file_name}.#{first_render.extension}"] +        template +      elsif template_format == :js && template = self.view_paths["#{template_file_name}.html"] +        @template_format = :html +        template +      else +        template = Template.new(template_path, view_paths) + +        if self.class.warn_cache_misses && logger = ActionController::Base.logger +          logger.debug "[PERFORMANCE] Rendering a template that was " + +            "not found in view path. Templates outside the view path are " + +            "not cached and result in expensive disk operations. Move this " +  +            "file into #{view_paths.join(':')} or add the folder to your " +  +            "view path list" +        end + +        template        end      end @@ -303,6 +337,10 @@ module ActionView #:nodoc:        # Renders the template present at <tt>template_path</tt>. The hash in <tt>local_assigns</tt>        # is made available as local variables.        def render_file(template_path, use_full_path = nil, local_assigns = {}) #:nodoc: +        unless use_full_path == nil +          ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller) +        end +          if defined?(ActionMailer) && defined?(ActionMailer::Base) && controller.is_a?(ActionMailer::Base) && !template_path.include?("/")            raise ActionViewError, <<-END_ERROR    Due to changes in ActionMailer, you need to provide the mailer_name along with the template name. @@ -316,11 +354,12 @@ module ActionView #:nodoc:            END_ERROR          end -        Template.new(self, template_path, use_full_path, local_assigns).render_template +        template = pick_template(template_path) +        template.render_template(self, local_assigns)        end        def render_inline(text, local_assigns = {}, type = nil) -        InlineTemplate.new(self, text, local_assigns, type).render +        InlineTemplate.new(text, type).render(self, local_assigns)        end        def wrap_content_for_layout(content) @@ -343,33 +382,10 @@ module ActionView #:nodoc:          @assigns.each { |key, value| instance_variable_set("@#{key}", value) }        end -      def execute(template) -        send(template.method, template.locals) do |*names| +      def execute(template, local_assigns = {}) +        send(template.method(local_assigns), local_assigns) do |*names|            instance_variable_get "@content_for_#{names.first || 'layout'}"          end        end - -      def template_file_from_name(template_name) -        template_name = TemplateFile.from_path(template_name) -        pick_template(template_name) unless template_name.extension -      end - -      def pick_template(file) -        if f = self.view_paths.find_template_file_for_path(file.dup_with_extension(template_format)) || file_from_first_render(file) -          f -        elsif template_format == :js && f = self.view_paths.find_template_file_for_path(file.dup_with_extension(:html)) -          @template_format = :html -          f -        else -          nil -        end -      end - -      # Determine the template extension from the <tt>@first_render</tt> filename -      def file_from_first_render(file) -        if extension = File.basename(@first_render.to_s)[/^[^.]+\.(.+)$/, 1] -          file.dup_with_extension(extension) -        end -      end    end  end diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb index 0122de47af..bf13945844 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb @@ -209,6 +209,10 @@ module ActionView        # Note that the default javascript files will be included first. So Prototype and Scriptaculous are available to        # all subsequently included files.        # +      # If you want Rails to search in all the subdirectories under javascripts, you should explicitly set <tt>:recursive</tt>: +      # +      #   javascript_include_tag :all, :recursive => true +      #        # == Caching multiple javascripts into one        #        # You can also cache multiple javascripts into one file, which requires less HTTP connections to download and can better be @@ -235,18 +239,23 @@ module ActionView        #        #   javascript_include_tag "prototype", "cart", "checkout", :cache => "shop" # when ActionController::Base.perform_caching is true =>        #     <script type="text/javascript" src="/javascripts/shop.js"></script> +      # +      # The <tt>:recursive</tt> option is also available for caching: +      # +      #   javascript_include_tag :all, :cache => true, :recursive => true        def javascript_include_tag(*sources)          options = sources.extract_options!.stringify_keys          cache   = options.delete("cache") +        recursive = options.delete("recursive")          if ActionController::Base.perform_caching && cache            joined_javascript_name = (cache == true ? "all" : cache) + ".js"            joined_javascript_path = File.join(JAVASCRIPTS_DIR, joined_javascript_name) -          write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources)) unless File.exists?(joined_javascript_path) +          write_asset_file_contents(joined_javascript_path, compute_javascript_paths(sources, recursive)) unless File.exists?(joined_javascript_path)            javascript_src_tag(joined_javascript_name, options)          else -          expand_javascript_sources(sources).collect { |source| javascript_src_tag(source, options) }.join("\n") +          expand_javascript_sources(sources, recursive).collect { |source| javascript_src_tag(source, options) }.join("\n")          end        end @@ -332,13 +341,17 @@ module ActionView        #     <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />        #     <link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />        # -      # You can also include all styles in the stylesheet directory using <tt>:all</tt> as the source: +      # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:        #        #   stylesheet_link_tag :all # =>        #     <link href="/stylesheets/style1.css"  media="screen" rel="stylesheet" type="text/css" />        #     <link href="/stylesheets/styleB.css"  media="screen" rel="stylesheet" type="text/css" />        #     <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" type="text/css" />        # +      # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>: +      # +      #   stylesheet_link_tag :all, :recursive => true +      #        # == Caching multiple stylesheets into one        #        # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be @@ -362,18 +375,23 @@ module ActionView        #        #   stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when ActionController::Base.perform_caching is true =>        #     <link href="/stylesheets/payment.css"  media="screen" rel="stylesheet" type="text/css" /> +      # +      # The <tt>:recursive</tt> option is also available for caching: +      # +      #   stylesheet_link_tag :all, :cache => true, :recursive => true        def stylesheet_link_tag(*sources)          options = sources.extract_options!.stringify_keys          cache   = options.delete("cache") +        recursive = options.delete("recursive")          if ActionController::Base.perform_caching && cache            joined_stylesheet_name = (cache == true ? "all" : cache) + ".css"            joined_stylesheet_path = File.join(STYLESHEETS_DIR, joined_stylesheet_name) -          write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources)) unless File.exists?(joined_stylesheet_path) +          write_asset_file_contents(joined_stylesheet_path, compute_stylesheet_paths(sources, recursive)) unless File.exists?(joined_stylesheet_path)            stylesheet_tag(joined_stylesheet_name, options)          else -          expand_stylesheet_sources(sources).collect { |source| stylesheet_tag(source, options) }.join("\n") +          expand_stylesheet_sources(sources, recursive).collect { |source| stylesheet_tag(source, options) }.join("\n")          end        end @@ -556,18 +574,19 @@ module ActionView            tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => html_escape(path_to_stylesheet(source)) }.merge(options), false, false)          end -        def compute_javascript_paths(sources) -          expand_javascript_sources(sources).collect { |source| compute_public_path(source, 'javascripts', 'js', false) } +        def compute_javascript_paths(*args) +          expand_javascript_sources(*args).collect { |source| compute_public_path(source, 'javascripts', 'js', false) }          end -        def compute_stylesheet_paths(sources) -          expand_stylesheet_sources(sources).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) } +        def compute_stylesheet_paths(*args) +          expand_stylesheet_sources(*args).collect { |source| compute_public_path(source, 'stylesheets', 'css', false) }          end -        def expand_javascript_sources(sources) +        def expand_javascript_sources(sources, recursive = false)            if sources.include?(:all) -            all_javascript_files = Dir[File.join(JAVASCRIPTS_DIR, '*.js')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort -            @@all_javascript_sources ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq +            all_javascript_files = collect_asset_files(JAVASCRIPTS_DIR, ('**' if recursive), '*.js') +            @@all_javascript_sources ||= {} +            @@all_javascript_sources[recursive] ||= ((determine_source(:defaults, @@javascript_expansions).dup & all_javascript_files) + all_javascript_files).uniq            else              expanded_sources = sources.collect do |source|                determine_source(source, @@javascript_expansions) @@ -577,9 +596,10 @@ module ActionView            end          end -        def expand_stylesheet_sources(sources) +        def expand_stylesheet_sources(sources, recursive)            if sources.first == :all -            @@all_stylesheet_sources ||= Dir[File.join(STYLESHEETS_DIR, '*.css')].collect { |file| File.basename(file).gsub(/\.\w+$/, '') }.sort +            @@all_stylesheet_sources ||= {} +            @@all_stylesheet_sources[recursive] ||= collect_asset_files(STYLESHEETS_DIR, ('**' if recursive), '*.css')            else              sources.collect do |source|                determine_source(source, @@stylesheet_expansions) @@ -604,6 +624,14 @@ module ActionView            FileUtils.mkdir_p(File.dirname(joined_asset_path))            File.open(joined_asset_path, "w+") { |cache| cache.write(join_asset_file_contents(asset_paths)) }          end + +        def collect_asset_files(*path) +          dir = path.first + +          Dir[File.join(*path.compact)].collect do |file| +            file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '') +          end.sort +        end      end    end  end diff --git a/actionpack/lib/action_view/helpers/cache_helper.rb b/actionpack/lib/action_view/helpers/cache_helper.rb index c2aab5aa72..930c397785 100644 --- a/actionpack/lib/action_view/helpers/cache_helper.rb +++ b/actionpack/lib/action_view/helpers/cache_helper.rb @@ -32,7 +32,7 @@ module ActionView        #      <i>Topics listed alphabetically</i>        #    <% end %>        def cache(name = {}, options = nil, &block) -        handler = Base.handler_class_for_extension(current_render_extension.to_sym) +        handler = Template.handler_class_for_extension(current_render_extension.to_sym)          handler.new(@controller).cache_fragment(block, name, options)        end      end diff --git a/actionpack/lib/action_view/helpers/capture_helper.rb b/actionpack/lib/action_view/helpers/capture_helper.rb index 990c30b90d..720e2da8cc 100644 --- a/actionpack/lib/action_view/helpers/capture_helper.rb +++ b/actionpack/lib/action_view/helpers/capture_helper.rb @@ -34,9 +34,8 @@ module ActionView          # Return captured buffer in erb.          if block_called_from_erb?(block)            with_output_buffer { block.call(*args) } - -        # Return block result otherwise, but protect buffer also.          else +          # Return block result otherwise, but protect buffer also.            with_output_buffer { return block.call(*args) }          end        end diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 17497481e6..0735ed07ee 100755 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -159,7 +159,10 @@ module ActionView        # Returns a set of select tags (one for hour, minute and optionally second) pre-selected for accessing a specified        # time-based attribute (identified by +method+) on an object assigned to the template (identified by +object+).        # You can include the seconds with <tt>:include_seconds</tt>. -      #  +      # +      # This method will also generate 3 input hidden tags, for the actual year, month and day unless the option +      # <tt>:ignore_date</tt> is set to +true+. +      #        # If anything is passed in the html_options hash it will be applied to every select tag in the set.        #        # ==== Examples @@ -655,7 +658,7 @@ module ActionView            order.reverse.each do |param|              # Send hidden fields for discarded elements once output has started              # This ensures AR can reconstruct valid dates using ParseDate -            next if discard[param] && date_or_time_select.empty? +            next if discard[param] && (date_or_time_select.empty? || options[:ignore_date])              date_or_time_select.insert(0, self.send("select_#{param}", datetime, options_with_prefix(position[param], options.merge(:use_hidden => discard[param])), html_options))              date_or_time_select.insert(0, diff --git a/actionpack/lib/action_view/helpers/prototype_helper.rb b/actionpack/lib/action_view/helpers/prototype_helper.rb index 9f6e550c09..576ca84bcc 100644 --- a/actionpack/lib/action_view/helpers/prototype_helper.rb +++ b/actionpack/lib/action_view/helpers/prototype_helper.rb @@ -397,7 +397,7 @@ module ActionView        #  # Generates: <input name="create_btn" onclick="new Ajax.Request('/testing/create',         #  #     {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});         #  #     return false;" type="button" value="Create" /> -      #  <%= submit_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %> +      #  <%= button_to_remote 'create_btn', 'Create', :url => { :action => 'create' } %>        #        #  # Submit to the remote action update and update the DIV succeed or fail based        #  # on the success or failure of the request @@ -405,11 +405,13 @@ module ActionView        #  # Generates: <input name="update_btn" onclick="new Ajax.Updater({success:'succeed',failure:'fail'},         #  #      '/testing/update', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)});         #  #      return false;" type="button" value="Update" /> -      #  <%= submit_to_remote 'update_btn', 'Update', :url => { :action => 'update' }, +      #  <%= button_to_remote 'update_btn', 'Update', :url => { :action => 'update' },        #     :update => { :success => "succeed", :failure => "fail" }        #        # <tt>options</tt> argument is the same as in form_remote_tag. -      def submit_to_remote(name, value, options = {}) +      # +      # Note: This method used to be called submit_to_remote, but that's now just an alias for button_to_remote +      def button_to_remote(name, value, options = {})          options[:with] ||= 'Form.serialize(this.form)'          options[:html] ||= {} @@ -420,6 +422,7 @@ module ActionView          tag("input", options[:html], false)        end +      alias_method :submit_to_remote, :button_to_remote        # Returns '<tt>eval(request.responseText)</tt>' which is the JavaScript function        # that +form_remote_tag+ can call in <tt>:complete</tt> to evaluate a multiple diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index a6c48737e9..3e3452b615 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -27,7 +27,7 @@ module ActionView        #   %>        def concat(string, unused_binding = nil)          if unused_binding -          ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed.  Please remove it from your views and helpers.") +          ActiveSupport::Deprecation.warn("The binding argument of #concat is no longer needed.  Please remove it from your views and helpers.", caller)          end          output_buffer << string diff --git a/actionpack/lib/action_view/helpers/url_helper.rb b/actionpack/lib/action_view/helpers/url_helper.rb index 89166fe19a..94e1f1d33a 100644 --- a/actionpack/lib/action_view/helpers/url_helper.rb +++ b/actionpack/lib/action_view/helpers/url_helper.rb @@ -63,17 +63,15 @@ module ActionView        #   # calls @workshop.to_s        #   # => /workshops/5        def url_for(options = {}) +        options ||= {}          case options          when Hash -          show_path =  options[:host].nil? ? true : false -          options = { :only_path => show_path }.update(options.symbolize_keys) +          options = { :only_path => options[:host].nil? }.update(options.symbolize_keys)            escape  = options.key?(:escape) ? options.delete(:escape) : true            url     = @controller.send(:url_for, options)          when String            escape = true            url    = options -        when NilClass -          url = @controller.send(:url_for, nil)          else            escape = false            url    = polymorphic_path(options) @@ -468,7 +466,7 @@ module ActionView          email_address_obfuscated.gsub!(/\./, html_options.delete("replace_dot")) if html_options.has_key?("replace_dot")          if encode == "javascript" -          "document.write('#{content_tag("a", name || email_address, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c| +          "document.write('#{content_tag("a", name || email_address_obfuscated, html_options.merge({ "href" => "mailto:"+email_address+extras }))}');".each_byte do |c|              string << sprintf("%%%x", c)            end            "<script type=\"#{Mime::JS}\">eval(unescape('#{string}'))</script>" diff --git a/actionpack/lib/action_view/inline_template.rb b/actionpack/lib/action_view/inline_template.rb index 19ab92ce1a..5e00cef13f 100644 --- a/actionpack/lib/action_view/inline_template.rb +++ b/actionpack/lib/action_view/inline_template.rb @@ -1,16 +1,19 @@  module ActionView #:nodoc:    class InlineTemplate #:nodoc: -    include Renderer +    include Renderable -    def initialize(view, source, locals = {}, type = nil) -      @view = view +    attr_reader :source, :extension, :method_segment +    def initialize(source, type = nil)        @source = source        @extension = type -      @locals = locals || {} - -      @method_key = @source -      @handler = Base.handler_class_for_extension(@extension).new(@view) +      @method_segment = "inline_#{@source.hash.abs}"      end + +    private +      # Always recompile inline templates +      def recompile?(local_assigns) +        true +      end    end  end diff --git a/actionpack/lib/action_view/partial_template.rb b/actionpack/lib/action_view/partial_template.rb deleted file mode 100644 index 72f831e937..0000000000 --- a/actionpack/lib/action_view/partial_template.rb +++ /dev/null @@ -1,69 +0,0 @@ -module ActionView #:nodoc: -  class PartialTemplate < Template #:nodoc: -    attr_reader :variable_name, :object, :as - -    def initialize(view, partial_path, object = nil, locals = {}, as = nil) -      @view_controller = view.controller if view.respond_to?(:controller) -      @as = as -      set_path_and_variable_name!(partial_path) -      super(view, @path, nil, locals) -      add_object_to_local_assigns!(object) - -      # This is needed here in order to compile template with knowledge of 'counter' -      initialize_counter! - -      # Prepare early. This is a performance optimization for partial collections -      prepare! -    end - -    def render -      ActionController::Base.benchmark("Rendered #{@path.path_without_format_and_extension}", Logger::DEBUG, false) do -        super -      end -    end - -    def render_member(object) -      @locals[:object] = @locals[@variable_name] = object -      @locals[as] = object if as - -      template = render_template -      @locals[@counter_name] += 1 -      @locals.delete(as) -      @locals.delete(@variable_name) -      @locals.delete(:object) - -      template -    end - -    def counter=(num) -      @locals[@counter_name] = num -    end - -    private -      def add_object_to_local_assigns!(object) -        @locals[:object] ||= -          @locals[@variable_name] ||= object || @view_controller.instance_variable_get("@#{variable_name}") -        @locals[as] ||= @locals[:object] if as -      end - -      def set_path_and_variable_name!(partial_path) -        if partial_path.include?('/') -          @variable_name = File.basename(partial_path) -          @path = "#{File.dirname(partial_path)}/_#{@variable_name}" -        elsif @view_controller -          @variable_name = partial_path -          @path = "#{@view_controller.class.controller_path}/_#{@variable_name}" -        else -          @variable_name = partial_path -          @path = "_#{@variable_name}" -        end - -        @variable_name = @variable_name.sub(/\..*$/, '').to_sym -      end - -      def initialize_counter! -        @counter_name ||= "#{@variable_name}_counter".to_sym -        @locals[@counter_name] = 0 -      end -  end -end diff --git a/actionpack/lib/action_view/partials.rb b/actionpack/lib/action_view/partials.rb index 7c6c98d611..116d61e13b 100644 --- a/actionpack/lib/action_view/partials.rb +++ b/actionpack/lib/action_view/partials.rb @@ -104,10 +104,12 @@ module ActionView    module Partials      private        def render_partial(partial_path, object_assigns = nil, local_assigns = {}) #:nodoc: +        local_assigns ||= {} +          case partial_path          when String, Symbol, NilClass -          # Render the template -          ActionView::PartialTemplate.new(self, partial_path, object_assigns, local_assigns).render_template +          variable_name, path = partial_pieces(partial_path) +          pick_template(path).render_partial(self, variable_name, object_assigns, local_assigns)          when ActionView::Helpers::FormBuilder            builder_partial_path = partial_path.class.to_s.demodulize.underscore.sub(/_builder$/, '')            render_partial(builder_partial_path, object_assigns, (local_assigns || {}).merge(builder_partial_path.to_sym => partial_path)) @@ -128,31 +130,43 @@ module ActionView          local_assigns = local_assigns ? local_assigns.clone : {}          spacer = partial_spacer_template ? render(:partial => partial_spacer_template) : '' +        _partial_pieces = {} +        _templates = {} -        if partial_path.nil? -          render_partial_collection_with_unknown_partial_path(collection, local_assigns, as) -        else -          render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as) -        end.join(spacer) -      end +        index = 0 +        collection.map do |object| +          _partial_path ||= partial_path || ActionController::RecordIdentifier.partial_path(object, controller.class.controller_path) +          variable_name, path = _partial_pieces[_partial_path] ||= partial_pieces(_partial_path) +          template = _templates[path] ||= pick_template(path) -      def render_partial_collection_with_known_partial_path(collection, partial_path, local_assigns, as) -        template = ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as) -        collection.map do |element| -          template.render_member(element) -        end +          local_assigns["#{variable_name}_counter".to_sym] = index +          local_assigns[:object] = local_assigns[variable_name] = object +          local_assigns[as] = object if as + +          result = template.render_partial(self, variable_name, object, local_assigns) + +          local_assigns.delete(as) +          local_assigns.delete(variable_name) +          local_assigns.delete(:object) +          index += 1 + +          result +        end.join(spacer)        end -      def render_partial_collection_with_unknown_partial_path(collection, local_assigns, as) -        templates = Hash.new -        i = 0 -        collection.map do |element| -          partial_path = ActionController::RecordIdentifier.partial_path(element, controller.class.controller_path) -          template = templates[partial_path] ||= ActionView::PartialTemplate.new(self, partial_path, nil, local_assigns, as) -          template.counter = i -          i += 1 -          template.render_member(element) +      def partial_pieces(partial_path) +        if partial_path.include?('/') +          variable_name = File.basename(partial_path) +          path = "#{File.dirname(partial_path)}/_#{variable_name}" +        elsif respond_to?(:controller) +          variable_name = partial_path +          path = "#{controller.class.controller_path}/_#{variable_name}" +        else +          variable_name = partial_path +          path = "_#{variable_name}"          end +        variable_name = variable_name.sub(/\..*$/, '').to_sym +        return variable_name, path        end    end  end diff --git a/actionpack/lib/action_view/paths.rb b/actionpack/lib/action_view/paths.rb new file mode 100644 index 0000000000..b0ab7d0c67 --- /dev/null +++ b/actionpack/lib/action_view/paths.rb @@ -0,0 +1,96 @@ +module ActionView #:nodoc: +  class PathSet < Array #:nodoc: +    def self.type_cast(obj) +      if obj.is_a?(String) +        if Base.warn_cache_misses && defined?(Rails) && Rails.initialized? +          Rails.logger.debug "[PERFORMANCE] Processing view path during a " + +            "request. This an expense disk operation that should be done at " + +            "boot. You can manually process this view path with " + +            "ActionView::Base.process_view_paths(#{obj.inspect}) and set it " + +            "as your view path" +        end +        Path.new(obj) +      else +        obj +      end +    end + +    class Path #:nodoc: +      attr_reader :path, :paths +      delegate :to_s, :to_str, :inspect, :to => :path + +      def initialize(path) +        @path = path.freeze +        reload! +      end + +      def ==(path) +        to_str == path.to_str +      end + +      def [](path) +        @paths[path] +      end + +      # Rebuild load path directory cache +      def reload! +        @paths = {} + +        templates_in_path do |template| +          @paths[template.path] = template +          @paths[template.path_without_extension] ||= template +        end + +        @paths.freeze +      end + +      private +        def templates_in_path +          (Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**")).each do |file| +            unless File.directory?(file) +              template = Template.new(file.split("#{self}/").last, self) +              # Eager load memoized methods and freeze cached template +              template.freeze if Base.cache_template_loading +              yield template +            end +          end +        end +    end + +    def initialize(*args) +      super(*args).map! { |obj| self.class.type_cast(obj) } +    end + +    def reload! +      each { |path| path.reload! } +    end + +    def <<(obj) +      super(self.class.type_cast(obj)) +    end + +    def push(*objs) +      delete_paths!(objs) +      super(*objs.map { |obj| self.class.type_cast(obj) }) +    end + +    def unshift(*objs) +      delete_paths!(objs) +      super(*objs.map { |obj| self.class.type_cast(obj) }) +    end + +    def [](template_path) +      each do |path| +        if template = path[template_path] +          return template +        end +      end +      nil +    end + +    private +      def delete_paths!(paths) +        paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } } +      end +  end +end diff --git a/actionpack/lib/action_view/renderable.rb b/actionpack/lib/action_view/renderable.rb new file mode 100644 index 0000000000..2c4302146f --- /dev/null +++ b/actionpack/lib/action_view/renderable.rb @@ -0,0 +1,79 @@ +module ActionView +  module Renderable +    # NOTE: The template that this mixin is beening include into is frozen +    # So you can not set or modify any instance variables + +    def self.included(base) +      @@mutex = Mutex.new +    end + +    # NOTE: Exception to earlier notice. Ensure this is called before freeze +    def handler +      @handler ||= Template.handler_class_for_extension(extension) +    end + +    # NOTE: Exception to earlier notice. Ensure this is called before freeze +    def compiled_source +      @compiled_source ||= handler.new(nil).compile(self) if handler.compilable? +    end + +    def render(view, local_assigns = {}) +      view.first_render ||= self +      view.send(:evaluate_assigns) +      view.current_render_extension = extension +      compile(local_assigns) if handler.compilable? +      handler.new(view).render(self, local_assigns) +    end + +    def method(local_assigns) +      if local_assigns && local_assigns.any? +        local_assigns_keys = "locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}" +      end +      ['_run', extension, method_segment, local_assigns_keys].compact.join('_').to_sym +    end + +    private +      # Compile and evaluate the template's code +      def compile(local_assigns) +        render_symbol = method(local_assigns) + +        @@mutex.synchronize do +          return false unless recompile?(render_symbol) + +          locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join + +          source = <<-end_src +            def #{render_symbol}(local_assigns) +              old_output_buffer = output_buffer;#{locals_code};#{compiled_source} +            ensure +              self.output_buffer = old_output_buffer +            end +          end_src + +          begin +            file_name = respond_to?(:filename) ? filename : 'compiled-template' +            ActionView::Base::CompiledTemplates.module_eval(source, file_name, 0) +          rescue Exception => e # errors from template code +            if logger = ActionController::Base.logger +              logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" +              logger.debug "Function body: #{source}" +              logger.debug "Backtrace: #{e.backtrace.join("\n")}" +            end + +            raise ActionView::TemplateError.new(self, {}, e) +          end +        end +      end + +      # Method to check whether template compilation is necessary. +      # The template will be compiled if the file has not been compiled yet, or +      # if local_assigns has a new key, which isn't supported by the compiled code yet. +      def recompile?(symbol) +        unless Base::CompiledTemplates.instance_methods.include?(symbol) && Base.cache_template_loading +          true +        else +          false +        end +      end +  end +end diff --git a/actionpack/lib/action_view/renderable_partial.rb b/actionpack/lib/action_view/renderable_partial.rb new file mode 100644 index 0000000000..6a17b50a14 --- /dev/null +++ b/actionpack/lib/action_view/renderable_partial.rb @@ -0,0 +1,19 @@ +module ActionView +  module RenderablePartial +    # NOTE: The template that this mixin is beening include into is frozen +    # So you can not set or modify any instance variables + +    def render(view, local_assigns = {}) +      ActionController::Base.benchmark("Rendered #{path_without_format_and_extension}", Logger::DEBUG, false) do +        super +      end +    end + +    def render_partial(view, variable_name, object = nil, local_assigns = {}, as = nil) +      object ||= view.controller.instance_variable_get("@#{variable_name}") if view.respond_to?(:controller) +      local_assigns[:object] ||= local_assigns[variable_name] ||= object +      local_assigns[as] ||= local_assigns[:object] if as +      render_template(view, local_assigns) +    end +  end +end diff --git a/actionpack/lib/action_view/renderer.rb b/actionpack/lib/action_view/renderer.rb deleted file mode 100644 index e6c64d2749..0000000000 --- a/actionpack/lib/action_view/renderer.rb +++ /dev/null @@ -1,29 +0,0 @@ -module ActionView -  module Renderer -    # TODO: Local assigns should not be tied to template instance -    attr_accessor :locals - -    # TODO: These readers should be private -    attr_reader :filename, :source, :handler, :method_key, :method - -    def render -      prepare! -      @handler.render(self) -    end - -    private -      def prepare! -        unless @prepared -          @view.send(:evaluate_assigns) -          @view.current_render_extension = @extension - -          if @handler.compilable? -            @handler.compile_template(self) # compile the given template, if necessary -            @method = @view.method_names[method_key] # Set the method name for this template and run it -          end - -          @prepared = true -        end -      end -  end -end diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index 8142232c8f..03f9234289 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -1,79 +1,112 @@  module ActionView #:nodoc: -  class Template #:nodoc: -    include Renderer +  class Template +    extend TemplateHandlers +    include Renderable -    class << self -      # TODO: Deprecate -      delegate :register_template_handler, :to => 'ActionView::Base' +    attr_accessor :filename, :load_path, :base_path, :name, :format, :extension +    delegate :to_s, :to => :path + +    def initialize(template_path, load_paths = []) +      template_path = template_path.dup +      @base_path, @name, @format, @extension = split(template_path) +      @base_path.to_s.gsub!(/\/$/, '') # Push to split method +      @load_path, @filename = find_full_path(template_path, load_paths) + +      # Extend with partial super powers +      extend RenderablePartial if @name =~ /^_/      end -    attr_reader :path, :extension +    def freeze +      # Eager load memoized methods +      format_and_extension +      path +      path_without_extension +      path_without_format_and_extension +      source +      method_segment -    def initialize(view, path, use_full_path = nil, locals = {}) -      unless use_full_path == nil -        ActiveSupport::Deprecation.warn("use_full_path option has been deprecated and has no affect.", caller) -      end +      # Eager load memoized methods from Renderable +      handler +      compiled_source -      @view = view -      @paths = view.view_paths +      instance_variables.each { |ivar| ivar.freeze } -      @original_path = path -      @path = TemplateFile.from_path(path) -      @view.first_render ||= @path.to_s +      super +    end -      set_extension_and_file_name +    def format_and_extension +      @format_and_extension ||= (extensions = [format, extension].compact.join(".")).blank? ? nil : extensions +    end + +    def path +      @path ||= [base_path, [name, format, extension].compact.join('.')].compact.join('/') +    end -      @method_key = @filename -      @locals = locals || {} -      @handler = Base.handler_class_for_extension(@extension).new(@view) +    def path_without_extension +      @path_without_extension ||= [base_path, [name, format].compact.join('.')].compact.join('/')      end -    def render_template -      render +    def path_without_format_and_extension +      @path_without_format_and_extension ||= [base_path, name].compact.join('/') +    end + +    def source +      @source ||= File.read(@filename) +    end + +    def method_segment +      unless @method_segment +        segment = File.expand_path(@filename) +        segment.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT) +        segment.gsub!(/([^a-zA-Z0-9_])/) { $1.ord } +        @method_segment = segment +      end + +      @method_segment +    end + +    def render_template(view, local_assigns = {}) +      render(view, local_assigns)      rescue Exception => e        raise e unless filename        if TemplateError === e          e.sub_template_of(filename)          raise e        else -        raise TemplateError.new(self, @view.assigns, e) +        raise TemplateError.new(self, view.assigns, e)        end      end -    def source -      @source ||= File.read(self.filename) -    end - -    def base_path_for_exception -      (@paths.find_load_path_for_path(@path) || @paths.first).to_s -    end -      private -      def set_extension_and_file_name -        @extension = @path.extension - -        unless @extension -          @path = @view.send(:template_file_from_name, @path) -          raise_missing_template_exception unless @path -          @extension = @path.extension -        end +      def valid_extension?(extension) +        Template.template_handler_extensions.include?(extension) +      end -        if p = @paths.find_template_file_for_path(path) -          @path = p -          @filename = @path.full_path -          @extension = @path.extension -          raise_missing_template_exception if @filename.blank? -        else -          @filename = @original_path -          raise_missing_template_exception unless File.exist?(@filename) +      def find_full_path(path, load_paths) +        load_paths = Array(load_paths) + [nil] +        load_paths.each do |load_path| +          file = [load_path, path].compact.join('/') +          return load_path, file if File.exist?(file)          end +        raise MissingTemplate.new(load_paths, path)        end -      def raise_missing_template_exception -        full_template_path = @original_path.include?('.') ? @original_path : "#{@original_path}.#{@view.template_format}.erb" -        display_paths = @paths.join(':') -        template_type = (@original_path =~ /layouts/i) ? 'layout' : 'template' -        raise MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}" +      # Returns file split into an array +      #   [base_path, name, format, extension] +      def split(file) +        if m = file.match(/^(.*\/)?([^\.]+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/) +          if m[5] # Mulipart formats +            [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]] +          elsif m[4] # Single format +            [m[1], m[2], m[3], m[4]] +          else +            if valid_extension?(m[3]) # No format +              [m[1], m[2], nil, m[3]] +            else # No extension +              [m[1], m[2], m[3], nil] +            end +          end +        end        end    end  end diff --git a/actionpack/lib/action_view/template_error.rb b/actionpack/lib/action_view/template_error.rb index 65d80362b5..35fc07bdb2 100644 --- a/actionpack/lib/action_view/template_error.rb +++ b/actionpack/lib/action_view/template_error.rb @@ -7,7 +7,7 @@ module ActionView      attr_reader :original_exception      def initialize(template, assigns, original_exception) -      @base_path = template.base_path_for_exception +      @base_path = template.base_path        @assigns, @source, @original_exception = assigns.dup, template.source, original_exception        @file_path = template.filename        @backtrace = compute_backtrace @@ -105,6 +105,6 @@ module ActionView  end  if defined?(Exception::TraceSubstitutions) -  Exception::TraceSubstitutions << [/:in\s+`_run_(html|xml).*'\s*$/, ''] +  Exception::TraceSubstitutions << [/:in\s+`_run_.*'\s*$/, '']    Exception::TraceSubstitutions << [%r{^\s*#{Regexp.escape RAILS_ROOT}/}, ''] if defined?(RAILS_ROOT)  end diff --git a/actionpack/lib/action_view/template_file.rb b/actionpack/lib/action_view/template_file.rb deleted file mode 100644 index c38e8ed122..0000000000 --- a/actionpack/lib/action_view/template_file.rb +++ /dev/null @@ -1,88 +0,0 @@ -module ActionView #:nodoc: -  # TemplateFile abstracts the pattern of querying a file path for its -  # path with or without its extension. The path is only the partial path -  # from the load path root e.g. "hello/index.html.erb" not -  # "app/views/hello/index.html.erb" -  class TemplateFile -    def self.from_path(path) -      path.is_a?(self) ? path : new(path) -    end - -    def self.from_full_path(load_path, full_path) -      file = new(full_path.split(load_path).last) -      file.load_path = load_path -      file.freeze -    end - -    attr_accessor :load_path, :base_path, :name, :format, :extension -    delegate :to_s, :inspect, :to => :path - -    def initialize(path) -      path = path.dup - -      # Clear the forward slash in the beginning -      trim_forward_slash!(path) - -      @base_path, @name, @format, @extension = split(path) -    end - -    def freeze -      @load_path.freeze -      @base_path.freeze -      @name.freeze -      @format.freeze -      @extension.freeze -      super -    end - -    def format_and_extension -      extensions = [format, extension].compact.join(".") -      extensions.blank? ? nil : extensions -    end - -    def full_path -      if load_path -        "#{load_path}/#{path}" -      else -        path -      end -    end - -    def path -      base_path.to_s + [name, format, extension].compact.join(".") -    end - -    def path_without_extension -      base_path.to_s + [name, format].compact.join(".") -    end - -    def path_without_format_and_extension -      "#{base_path}#{name}" -    end - -    def dup_with_extension(extension) -      file = dup -      file.extension = extension ? extension.to_s : nil -      file -    end - -    private -      def trim_forward_slash!(path) -        path.sub!(/^\//, '') -      end - -      # Returns file split into an array -      #   [base_path, name, format, extension] -      def split(file) -        if m = file.match(/^(.*\/)?(\w+)\.?(\w+)?\.?(\w+)?\.?(\w+)?$/) -          if m[5] # Mulipart formats -            [m[1], m[2], "#{m[3]}.#{m[4]}", m[5]] -          elsif m[4] # Single format -            [m[1], m[2], m[3], m[4]] -          else # No format -            [m[1], m[2], nil, m[3]] -          end -        end -      end -  end -end diff --git a/actionpack/lib/action_view/template_handler.rb b/actionpack/lib/action_view/template_handler.rb index 39e578e586..1afea21f67 100644 --- a/actionpack/lib/action_view/template_handler.rb +++ b/actionpack/lib/action_view/template_handler.rb @@ -1,9 +1,5 @@  module ActionView    class TemplateHandler -    def self.line_offset -      0 -    end -      def self.compilable?        false      end @@ -12,7 +8,7 @@ module ActionView        @view = view      end -    def render(template) +    def render(template, local_assigns = {})      end      def compile(template) @@ -22,10 +18,6 @@ module ActionView        self.class.compilable?      end -    def line_offset -      self.class.line_offset -    end -      # Called by CacheHelper#cache      def cache_fragment(block, name = {}, options = nil)      end diff --git a/actionpack/lib/action_view/template_handlers/builder.rb b/actionpack/lib/action_view/template_handlers/builder.rb index ee02ce1a6f..cbe53e11d8 100644 --- a/actionpack/lib/action_view/template_handlers/builder.rb +++ b/actionpack/lib/action_view/template_handlers/builder.rb @@ -5,17 +5,12 @@ module ActionView      class Builder < TemplateHandler        include Compilable -      def self.line_offset -        2 -      end -        def compile(template) -        content_type_handler = (@view.send!(:controller).respond_to?(:response) ? "controller.response" : "controller") - -        "#{content_type_handler}.content_type ||= Mime::XML\n" + -          "xml = ::Builder::XmlMarkup.new(:indent => 2)\n" + +        # ActionMailer does not have a response +        "controller.respond_to?(:response) && controller.response.content_type ||= Mime::XML;" + +          "xml = ::Builder::XmlMarkup.new(:indent => 2);" +            template.source + -          "\nxml.target!\n" +          ";xml.target!;"        end        def cache_fragment(block, name = {}, options = nil) diff --git a/actionpack/lib/action_view/template_handlers/compilable.rb b/actionpack/lib/action_view/template_handlers/compilable.rb index f436ebbe45..a0ebaefeef 100644 --- a/actionpack/lib/action_view/template_handlers/compilable.rb +++ b/actionpack/lib/action_view/template_handlers/compilable.rb @@ -1,21 +1,8 @@  module ActionView    module TemplateHandlers      module Compilable -        def self.included(base)          base.extend ClassMethod - -        # Map method names to their compile time -        base.cattr_accessor :compile_time -        base.compile_time = {} - -        # Map method names to the names passed in local assigns so far -        base.cattr_accessor :template_args -        base.template_args = {} - -        # Count the number of inline templates -        base.cattr_accessor :inline_template_count -        base.inline_template_count = 0        end        module ClassMethod @@ -24,111 +11,10 @@ module ActionView            true          end        end -       -      def render(template) -        @view.send :execute, template -      end - -      # Compile and evaluate the template's code -      def compile_template(template) -        return unless compile_template?(template) - -        render_symbol = assign_method_name(template) -        render_source = create_template_source(template, render_symbol) -        line_offset   = self.template_args[render_symbol].size + self.line_offset - -        begin -          file_name = template.filename || 'compiled-template' -          ActionView::Base::CompiledTemplates.module_eval(render_source, file_name, -line_offset) -        rescue Exception => e  # errors from template code -          if Base.logger -            Base.logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" -            Base.logger.debug "Function body: #{render_source}" -            Base.logger.debug "Backtrace: #{e.backtrace.join("\n")}" -          end - -          raise ActionView::TemplateError.new(template, @view.assigns, e) -        end - -        self.compile_time[render_symbol] = Time.now -        # logger.debug "Compiled template #{file_name || template}\n  ==> #{render_symbol}" if logger -      end - -      private - -      # Method to check whether template compilation is necessary. -      # The template will be compiled if the inline template or file has not been compiled yet, -      # if local_assigns has a new key, which isn't supported by the compiled code yet, -      # or if the file has changed on disk and checking file mods hasn't been disabled. -      def compile_template?(template) -        method_key    = template.method_key -        render_symbol = @view.method_names[method_key] - -        compile_time = self.compile_time[render_symbol] -        if compile_time && supports_local_assigns?(render_symbol, template.locals) -          if template.filename && !@view.cache_template_loading -            template_changed_since?(template.filename, compile_time) -          end -        else -          true -        end -      end - -      def assign_method_name(template) -        @view.method_names[template.method_key] ||= compiled_method_name(template) -      end -      def compiled_method_name(template) -        ['_run', self.class.to_s.demodulize.underscore, compiled_method_name_file_path_segment(template.filename)].compact.join('_').to_sym +      def render(template, local_assigns = {}) +        @view.send(:execute, template, local_assigns)        end - -      def compiled_method_name_file_path_segment(file_name) -        if file_name -          s = File.expand_path(file_name) -          s.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}/, '') if defined?(RAILS_ROOT) -          s.gsub!(/([^a-zA-Z0-9_])/) { $1.ord } -          s -        else -          (self.inline_template_count += 1).to_s -        end -      end - -      # Method to create the source code for a given template. -      def create_template_source(template, render_symbol) -        body = compile(template) - -        self.template_args[render_symbol] ||= {} -        locals_keys = self.template_args[render_symbol].keys | template.locals.keys -        self.template_args[render_symbol] = locals_keys.inject({}) { |h, k| h[k] = true; h } - -        locals_code = "" -        locals_keys.each do |key| -          locals_code << "#{key} = local_assigns[:#{key}]\n" -        end - -        <<-end_src -          def #{render_symbol}(local_assigns) -            old_output_buffer = output_buffer;#{locals_code}#{body} -          ensure -            self.output_buffer = old_output_buffer -          end -        end_src -      end - -      # Return true if the given template was compiled for a superset of the keys in local_assigns -      def supports_local_assigns?(render_symbol, local_assigns) -        local_assigns.empty? || -          ((args = self.template_args[render_symbol]) && local_assigns.all? { |k,_| args.has_key?(k) }) -      end - -      # Method to handle checking a whether a template has changed since last compile; isolated so that templates -      # not stored on the file system can hook and extend appropriately. -      def template_changed_since?(file_name, compile_time) -        lstat = File.lstat(file_name) -        compile_time < lstat.mtime || -          (lstat.symlink? && compile_time < File.stat(file_name).mtime) -      end -      end    end  end diff --git a/actionpack/lib/action_view/template_handlers/rjs.rb b/actionpack/lib/action_view/template_handlers/rjs.rb index 5854e33fed..3892bf1d6e 100644 --- a/actionpack/lib/action_view/template_handlers/rjs.rb +++ b/actionpack/lib/action_view/template_handlers/rjs.rb @@ -3,13 +3,9 @@ module ActionView      class RJS < TemplateHandler        include Compilable -      def self.line_offset -        2 -      end -        def compile(template) -        "controller.response.content_type ||= Mime::JS\n" + -        "update_page do |page|\n#{template.source}\nend" +        "controller.response.content_type ||= Mime::JS;" + +          "update_page do |page|;#{template.source}\nend"        end        def cache_fragment(block, name = {}, options = nil) #:nodoc: diff --git a/actionpack/lib/action_view/view_load_paths.rb b/actionpack/lib/action_view/view_load_paths.rb deleted file mode 100644 index 6e439a009c..0000000000 --- a/actionpack/lib/action_view/view_load_paths.rb +++ /dev/null @@ -1,103 +0,0 @@ -module ActionView #:nodoc: -  class ViewLoadPaths < Array #:nodoc: -    def self.type_cast(obj) -      obj.is_a?(String) ? LoadPath.new(obj) : obj -    end - -    class LoadPath #:nodoc: -      attr_reader :path, :paths -      delegate :to_s, :to_str, :inspect, :to => :path - -      def initialize(path) -        @path = path.freeze -        reload! -      end - -      def ==(path) -        to_str == path.to_str -      end - -      # Rebuild load path directory cache -      def reload! -        @paths = {} - -        files.each do |file| -          @paths[file.path] = file -          @paths[file.path_without_extension] ||= file -        end - -        @paths.freeze -      end - -      def find_template_file_for_partial_path(template_path, template_format) -        @paths["#{template_path}.#{template_format}"] || -          @paths[template_path] || -          @paths[template_path.gsub(/\..*$/, '')] -      end - -      private -        # Get all the files and directories in the path -        def files_in_path -          Dir.glob("#{@path}/**/*/**") | Dir.glob("#{@path}/**") -        end - -        # Create an array of all the files within the path -        def files -          files_in_path.map do |file| -            TemplateFile.from_full_path(@path, file) unless File.directory?(file) -          end.compact -        end -    end - -    def initialize(*args) -      super(*args).map! { |obj| self.class.type_cast(obj) } -    end - -    def reload! -      each { |path| path.reload! } -    end - -    def <<(obj) -      super(self.class.type_cast(obj)) -    end - -    def push(*objs) -      delete_paths!(objs) -      super(*objs.map { |obj| self.class.type_cast(obj) }) -    end - -    def unshift(*objs) -      delete_paths!(objs) -      super(*objs.map { |obj| self.class.type_cast(obj) }) -    end - -    def template_exists?(file) -      find_load_path_for_path(file) ? true : false -    end - -    def find_load_path_for_path(file) -      find { |path| path.paths[file.to_s] } -    end - -    def find_template_file_for_path(template_path) -      template_path_without_extension, template_extension = path_and_extension(template_path.to_s) -      each do |path| -        if f = path.find_template_file_for_partial_path(template_path_without_extension, template_extension) -          return f -        end -      end -      nil -    end - -    private -      def delete_paths!(paths) -        paths.each { |p1| delete_if { |p2| p1.to_s == p2.to_s } } -      end - -      # Splits the path and extension from the given template_path and returns as an array. -      def path_and_extension(template_path) -        template_path_without_extension = template_path.sub(/\.(\w+)$/, '') -        [template_path_without_extension, $1] -      end -  end -end diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 70f6a28a9c..0d2e0f273a 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -22,7 +22,9 @@ ActiveSupport::Deprecation.debug = true  ActionController::Base.logger = nil  ActionController::Routing::Routes.reload rescue nil -FIXTURE_LOAD_PATH = ActionView::ViewLoadPaths::LoadPath.new(File.join(File.dirname(__FILE__), 'fixtures')) +ActionView::Base.cache_template_loading = true +FIXTURE_LOAD_PATH = File.join(File.dirname(__FILE__), 'fixtures') +ActionController::Base.view_paths = FIXTURE_LOAD_PATH  # Wrap tests that use Mocha and skip if unavailable.  def uses_mocha(test_name) diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index af2725a99b..a82a1a3023 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -41,8 +41,6 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base    end  end -RenderPartialWithRecordIdentificationController.view_paths = [FIXTURE_LOAD_PATH] -  class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase    fixtures :developers, :projects, :developers_projects, :topics, :replies, :companies, :mascots @@ -56,26 +54,31 @@ class RenderPartialWithRecordIdentificationTest < ActiveRecordTestCase    def test_rendering_partial_with_has_many_and_belongs_to_association      get :render_with_has_many_and_belongs_to_association      assert_template 'projects/_project' +    assert_equal 'Active RecordActive Controller', @response.body    end    def test_rendering_partial_with_has_many_association      get :render_with_has_many_association      assert_template 'replies/_reply' +    assert_equal 'Birdman is better!', @response.body    end    def test_rendering_partial_with_named_scope      get :render_with_named_scope      assert_template 'replies/_reply' +    assert_equal 'Birdman is better!Nuh uh!', @response.body    end    def test_render_with_record      get :render_with_record      assert_template 'developers/_developer' +    assert_equal 'David', @response.body    end    def test_render_with_record_collection      get :render_with_record_collection      assert_template 'developers/_developer' +    assert_equal 'DavidJamisfixture_3fixture_4fixture_5fixture_6fixture_7fixture_8fixture_9fixture_10Jamis', @response.body    end    def test_rendering_partial_with_has_one_association @@ -118,8 +121,6 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base    end  end -RenderPartialWithRecordIdentificationController.view_paths = [FIXTURE_LOAD_PATH] -  class Game < Struct.new(:name, :id)    def to_param      id.to_s @@ -137,8 +138,6 @@ module Fun      end    end -  NestedController.view_paths = [FIXTURE_LOAD_PATH] -    module Serious      class NestedDeeperController < ActionController::Base        def render_with_record_in_deeper_nested_controller @@ -149,8 +148,6 @@ module Fun          render :partial => [ Game.new("Chess"), Game.new("Sudoku"), Game.new("Solitaire") ]        end      end - -    NestedDeeperController.view_paths = [FIXTURE_LOAD_PATH]    end  end @@ -165,11 +162,13 @@ class RenderPartialWithRecordIdentificationAndNestedControllersTest < ActiveReco    def test_render_with_record_in_nested_controller      get :render_with_record_in_nested_controller      assert_template 'fun/games/_game' +    assert_equal 'Pong', @response.body    end    def test_render_with_record_collection_in_nested_controller      get :render_with_record_collection_in_nested_controller      assert_template 'fun/games/_game' +    assert_equal 'PongTank', @response.body    end  end @@ -184,10 +183,12 @@ class RenderPartialWithRecordIdentificationAndNestedDeeperControllersTest < Acti    def test_render_with_record_in_deeper_nested_controller      get :render_with_record_in_deeper_nested_controller      assert_template 'fun/serious/games/_game' +    assert_equal 'Chess', @response.body    end    def test_render_with_record_collection_in_deeper_nested_controller      get :render_with_record_collection_in_deeper_nested_controller      assert_template 'fun/serious/games/_game' +    assert_equal 'ChessSudokuSolitaire', @response.body    end  end diff --git a/actionpack/test/controller/action_pack_assertions_test.rb b/actionpack/test/controller/action_pack_assertions_test.rb index 7a90a9408e..610e196362 100644 --- a/actionpack/test/controller/action_pack_assertions_test.rb +++ b/actionpack/test/controller/action_pack_assertions_test.rb @@ -164,14 +164,6 @@ module Admin    end  end -# --------------------------------------------------------------------------- - - -# tell the controller where to find its templates but start from parent -# directory of test_request_response to simulate the behaviour of a -# production environment -ActionPackAssertionsController.view_paths = [FIXTURE_LOAD_PATH] -  # a test case to exercise the new capabilities TestRequest & TestResponse  class ActionPackAssertionsControllerTest < Test::Unit::TestCase    # let's get this party started @@ -232,7 +224,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase        process :redirect_to_named_route        assert_redirected_to 'http://test.host/route_one'        assert_redirected_to route_one_url -      assert_redirected_to :route_one_url      end    end @@ -253,9 +244,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase        assert_raise(Test::Unit::AssertionFailedError) do          assert_redirected_to route_two_url        end -      assert_raise(Test::Unit::AssertionFailedError) do -        assert_redirected_to :route_two_url -      end      end    end @@ -419,22 +407,6 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase      assert_equal "Mr. David", @response.body    end -  def test_follow_redirect -    process :redirect_to_action -    assert_redirected_to :action => "flash_me" - -    follow_redirect -    assert_equal 1, @request.parameters["id"].to_i - -    assert "Inconceivable!", @response.body -  end - -  def test_follow_redirect_outside_current_action -    process :redirect_to_controller -    assert_redirected_to :controller => "elsewhere", :action => "flash_me" - -    assert_raises(RuntimeError, "Can't follow redirects outside of current controller (elsewhere)") { follow_redirect } -  end    def test_assert_redirection_fails_with_incorrect_controller      process :redirect_to_controller @@ -448,14 +420,16 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase      assert_redirected_to :controller => 'action_pack_assertions', :action => "flash_me", :id => 1, :params => { :panda => 'fun' }    end -  def test_redirected_to_url_leadling_slash +  def test_redirected_to_url_leading_slash      process :redirect_to_path      assert_redirected_to '/some/path'    end +    def test_redirected_to_url_no_leadling_slash      process :redirect_to_path      assert_redirected_to 'some/path'    end +    def test_redirected_to_url_full_url      process :redirect_to_path      assert_redirected_to 'http://test.host/some/path' @@ -475,7 +449,7 @@ class ActionPackAssertionsControllerTest < Test::Unit::TestCase    def test_redirected_to_with_nested_controller      @controller = Admin::InnerModuleController.new      get :redirect_to_absolute_controller -    assert_redirected_to :controller => 'content' +    assert_redirected_to :controller => '/content'      get :redirect_to_fellow_controller      assert_redirected_to :controller => 'admin/user' diff --git a/actionpack/test/controller/addresses_render_test.rb b/actionpack/test/controller/addresses_render_test.rb index df87182082..b26cae24fb 100644 --- a/actionpack/test/controller/addresses_render_test.rb +++ b/actionpack/test/controller/addresses_render_test.rb @@ -19,8 +19,6 @@ class AddressesTestController < ActionController::Base    def self.controller_path; "addresses"; end  end -AddressesTestController.view_paths = [FIXTURE_LOAD_PATH] -  class AddressesTest < Test::Unit::TestCase    def setup      @controller = AddressesTestController.new diff --git a/actionpack/test/controller/caching_test.rb b/actionpack/test/controller/caching_test.rb index 0140654155..2e98837a35 100644 --- a/actionpack/test/controller/caching_test.rb +++ b/actionpack/test/controller/caching_test.rb @@ -6,7 +6,6 @@ CACHE_DIR = 'test_cache'  FILE_STORE_PATH = File.join(File.dirname(__FILE__), '/../temp/', CACHE_DIR)  ActionController::Base.page_cache_directory = FILE_STORE_PATH  ActionController::Base.cache_store = :file_store, FILE_STORE_PATH -ActionController::Base.view_paths = [FIXTURE_LOAD_PATH]  class PageCachingTestController < ActionController::Base    caches_page :ok, :no_content, :if => Proc.new { |c| !c.request.format.json? } @@ -131,8 +130,7 @@ class PageCachingTest < Test::Unit::TestCase    end    def test_page_caching_conditional_options -    @request.env['HTTP_ACCEPT'] = 'application/json' -    get :ok +    get :ok, :format=>'json'      assert_page_not_cached :ok    end @@ -152,7 +150,7 @@ end  class ActionCachingTestController < ActionController::Base -  caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? } +  caches_action :index, :redirected, :forbidden, :if => Proc.new { |c| !c.request.format.json? }, :expires_in => 1.hour    caches_action :show, :cache_path => 'http://test.host/custom/show'    caches_action :edit, :cache_path => Proc.new { |c| c.params[:id] ? "http://test.host/#{c.params[:id]};edit" : "http://test.host/edit" }    caches_action :with_layout @@ -188,6 +186,7 @@ class ActionCachingTestController < ActionController::Base      expire_action :controller => 'action_caching_test', :action => 'index'      render :nothing => true    end +    def expire_xml      expire_action :controller => 'action_caching_test', :action => 'index', :format => 'xml'      render :nothing => true @@ -218,6 +217,7 @@ class ActionCachingMockController      Object.new.instance_eval(<<-EVAL)        def path; '#{@mock_path}' end        def format; 'all' end +      def cache_format; nil end        self      EVAL    end @@ -284,9 +284,19 @@ class ActionCacheTest < Test::Unit::TestCase    end    def test_action_cache_conditional_options +    old_use_accept_header = ActionController::Base.use_accept_header +    ActionController::Base.use_accept_header = true      @request.env['HTTP_ACCEPT'] = 'application/json'      get :index      assert !fragment_exist?('hostname.com/action_caching_test') +    ActionController::Base.use_accept_header = old_use_accept_header +  end + +  def test_action_cache_with_store_options +    MockTime.expects(:now).returns(12345).once +    @controller.expects(:read_fragment).with('hostname.com/action_caching_test', :expires_in => 1.hour).once +    @controller.expects(:write_fragment).with('hostname.com/action_caching_test', '12345.0', :expires_in => 1.hour).once +    get :index    end    def test_action_cache_with_custom_cache_path @@ -406,12 +416,6 @@ class ActionCacheTest < Test::Unit::TestCase        assert_equal 'application/xml', @response.content_type        reset! -      @request.env['HTTP_ACCEPT'] = "application/xml" -      get :index -      assert_equal cached_time, @response.body -      assert_equal 'application/xml', @response.content_type -      reset! -        get :expire_xml        reset! @@ -631,8 +635,6 @@ class FunctionalCachingController < ActionController::Base    end  end -FunctionalCachingController.view_paths = [FIXTURE_LOAD_PATH] -  class FunctionalFragmentCachingTest < Test::Unit::TestCase    def setup      ActionController::Base.perform_caching = true diff --git a/actionpack/test/controller/capture_test.rb b/actionpack/test/controller/capture_test.rb index 87f9ce8ab3..5ded6a5d26 100644 --- a/actionpack/test/controller/capture_test.rb +++ b/actionpack/test/controller/capture_test.rb @@ -23,8 +23,6 @@ class CaptureController < ActionController::Base    def rescue_action(e) raise end  end -CaptureController.view_paths = [FIXTURE_LOAD_PATH] -  class CaptureTest < Test::Unit::TestCase    def setup      @controller = CaptureController.new diff --git a/actionpack/test/controller/components_test.rb b/actionpack/test/controller/components_test.rb index 82c55483dd..71e8a18071 100644 --- a/actionpack/test/controller/components_test.rb +++ b/actionpack/test/controller/components_test.rb @@ -119,7 +119,7 @@ class ComponentsTest < Test::Unit::TestCase    def test_component_redirect_redirects      get :calling_redirected -    assert_redirected_to :action => "being_called" +    assert_redirected_to :controller=>"callee", :action => "being_called"    end    def test_component_multiple_redirect_redirects diff --git a/actionpack/test/controller/content_type_test.rb b/actionpack/test/controller/content_type_test.rb index 2019b4a2d0..d457d13aef 100644 --- a/actionpack/test/controller/content_type_test.rb +++ b/actionpack/test/controller/content_type_test.rb @@ -45,8 +45,6 @@ class ContentTypeController < ActionController::Base    def rescue_action(e) raise end  end -ContentTypeController.view_paths = [FIXTURE_LOAD_PATH] -  class ContentTypeTest < Test::Unit::TestCase    def setup      @controller = ContentTypeController.new @@ -114,6 +112,20 @@ class ContentTypeTest < Test::Unit::TestCase      assert_equal Mime::HTML, @response.content_type      assert_equal "utf-8", @response.charset    end +end + +class AcceptBasedContentTypeTest < ActionController::TestCase + +  tests ContentTypeController + +  def setup +    ActionController::Base.use_accept_header = true +  end + +  def teardown +    ActionController::Base.use_accept_header = false +  end +    def test_render_default_content_types_for_respond_to      @request.env["HTTP_ACCEPT"] = Mime::HTML.to_s diff --git a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb index f485500b7f..86555a77df 100644 --- a/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb +++ b/actionpack/test/controller/deprecation/deprecated_base_methods_test.rb @@ -13,8 +13,6 @@ class DeprecatedBaseMethodsTest < Test::Unit::TestCase      def rescue_action(e) raise e end    end -  Target.view_paths = [FIXTURE_LOAD_PATH] -    def setup      @request    = ActionController::TestRequest.new      @response   = ActionController::TestResponse.new diff --git a/actionpack/test/controller/layout_test.rb b/actionpack/test/controller/layout_test.rb index 52ab72bb99..92b6aa4f2f 100644 --- a/actionpack/test/controller/layout_test.rb +++ b/actionpack/test/controller/layout_test.rb @@ -34,13 +34,13 @@ end  class MabView < ActionView::TemplateHandler    def initialize(view)    end -   -  def render(template) + +  def render(template, local_assigns)      template.source    end  end -ActionView::Base.register_template_handler :mab, MabView +ActionView::Template::register_template_handler :mab, MabView  class LayoutAutoDiscoveryTest < Test::Unit::TestCase    def setup @@ -63,6 +63,7 @@ class LayoutAutoDiscoveryTest < Test::Unit::TestCase    end    def test_third_party_template_library_auto_discovers_layout +    ThirdPartyTemplateLibraryController.view_paths.reload!      @controller = ThirdPartyTemplateLibraryController.new      get :hello      assert_equal 'layouts/third_party_template_library', @controller.active_layout diff --git a/actionpack/test/controller/mime_responds_test.rb b/actionpack/test/controller/mime_responds_test.rb index fb2519563d..1701431858 100644 --- a/actionpack/test/controller/mime_responds_test.rb +++ b/actionpack/test/controller/mime_responds_test.rb @@ -162,10 +162,9 @@ class RespondToController < ActionController::Base      end  end -RespondToController.view_paths = [FIXTURE_LOAD_PATH] -  class MimeControllerTest < Test::Unit::TestCase    def setup +    ActionController::Base.use_accept_header = true      @request    = ActionController::TestRequest.new      @response   = ActionController::TestResponse.new @@ -173,6 +172,10 @@ class MimeControllerTest < Test::Unit::TestCase      @request.host = "www.example.com"    end +  def teardown +    ActionController::Base.use_accept_header = false +  end +    def test_html      @request.env["HTTP_ACCEPT"] = "text/html"      get :js_or_html diff --git a/actionpack/test/controller/new_render_test.rb b/actionpack/test/controller/new_render_test.rb index b2691d981b..d2a3a2b0b0 100644 --- a/actionpack/test/controller/new_render_test.rb +++ b/actionpack/test/controller/new_render_test.rb @@ -465,9 +465,6 @@ class NewRenderTestController < ActionController::Base      end  end -NewRenderTestController.view_paths = [FIXTURE_LOAD_PATH] -Fun::GamesController.view_paths = [FIXTURE_LOAD_PATH] -  class NewRenderTest < Test::Unit::TestCase    def setup      @controller = NewRenderTestController.new @@ -489,6 +486,11 @@ class NewRenderTest < Test::Unit::TestCase      assert_equal "<html>Hello world!</html>", @response.body    end +  def test_renders_default_template_for_missing_action +    get :'hyphen-ated' +    assert_template 'test/hyphen-ated' +  end +    def test_do_with_render      get :render_hello_world      assert_template "test/hello_world" diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 0e85347bad..28da5c6163 100755 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -168,21 +168,6 @@ class RedirectTest < Test::Unit::TestCase      assert_redirected_to :action => "other_host", :only_path => false, :host => 'other.test.host'    end -  def test_redirect_error_with_pretty_diff -    get :host_redirect -    assert_response :redirect -    begin -      assert_redirected_to :action => "other_host", :only_path => true -    rescue Test::Unit::AssertionFailedError => err -      expected_msg, redirection_msg, diff_msg = err.message.scan(/<\{[^\}]+\}>/).collect { |s| s[2..-3] } -      assert_match %r("only_path"=>false),        redirection_msg -      assert_match %r("host"=>"other.test.host"), redirection_msg -      assert_match %r("action"=>"other_host"),    redirection_msg -      assert_match %r("only_path"=>false),        diff_msg -      assert_match %r("host"=>"other.test.host"), diff_msg -    end -  end -    def test_module_redirect      get :module_redirect      assert_response :redirect @@ -235,9 +220,16 @@ class RedirectTest < Test::Unit::TestCase      get :redirect_to_existing_record      assert_equal "http://test.host/workshops/5", redirect_to_url +    assert_redirected_to Workshop.new(5, false)      get :redirect_to_new_record      assert_equal "http://test.host/workshops", redirect_to_url +    assert_redirected_to Workshop.new(5, true) +  end + +  def test_redirect_with_partial_params +    get :module_redirect +    assert_redirected_to :action => 'hello_world'    end    def test_redirect_to_nil @@ -283,7 +275,7 @@ module ModuleTest      def test_module_redirect_using_options        get :module_redirect        assert_response :redirect -      assert_redirected_to :controller => 'redirect', :action => "hello_world" +      assert_redirected_to :controller => '/redirect', :action => "hello_world"      end    end  end diff --git a/actionpack/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index 10264dadaa..a857810b78 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -217,9 +217,6 @@ class TestController < ActionController::Base      end  end -TestController.view_paths = [FIXTURE_LOAD_PATH] -Fun::GamesController.view_paths = [FIXTURE_LOAD_PATH] -  class RenderTest < Test::Unit::TestCase    def setup      @request    = ActionController::TestRequest.new diff --git a/actionpack/test/controller/request_test.rb b/actionpack/test/controller/request_test.rb index 20f3fd4d7b..932c0e21a1 100644 --- a/actionpack/test/controller/request_test.rb +++ b/actionpack/test/controller/request_test.rb @@ -386,7 +386,7 @@ class RequestTest < Test::Unit::TestCase    def test_nil_format      @request.instance_eval { @parameters = { :format => nil } } -    @request.env["HTTP_ACCEPT"] = "text/javascript" +    @request.env["HTTP_X_REQUESTED_WITH"] = "XMLHttpRequest"      assert_equal Mime::JS, @request.format    end diff --git a/actionpack/test/controller/resources_test.rb b/actionpack/test/controller/resources_test.rb index 0d089d0f23..0f7924649a 100644 --- a/actionpack/test/controller/resources_test.rb +++ b/actionpack/test/controller/resources_test.rb @@ -28,18 +28,16 @@ module Backoffice  end  class ResourcesTest < Test::Unit::TestCase -   -      # The assertions in these tests are incompatible with the hash method    # optimisation.  This could indicate user level problems    def setup      ActionController::Base.optimise_named_routes = false    end -   -  def tear_down + +  def teardown      ActionController::Base.optimise_named_routes = true    end -   +    def test_should_arrange_actions      resource = ActionController::Resources::Resource.new(:messages,        :collection => { :rss => :get, :reorder => :post, :csv => :post }, @@ -159,14 +157,14 @@ class ResourcesTest < Test::Unit::TestCase    def test_with_collection_actions_and_name_prefix      actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete } -   +      with_restful_routing :messages, :path_prefix => '/threads/:thread_id', :name_prefix => "thread_", :collection => actions do        assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|          actions.each do |action, method|            assert_recognizes(options.merge(:action => action), :path => "/threads/1/messages/#{action}", :method => method)          end        end -   +        assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|          actions.keys.each do |action|            assert_named_route "/threads/1/messages/#{action}", "#{action}_thread_messages_path", :action => action @@ -177,14 +175,14 @@ class ResourcesTest < Test::Unit::TestCase    def test_with_collection_action_and_name_prefix_and_formatted      actions = { 'a' => :get, 'b' => :put, 'c' => :post, 'd' => :delete } -   +      with_restful_routing :messages, :path_prefix => '/threads/:thread_id', :name_prefix => "thread_", :collection => actions do        assert_restful_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|          actions.each do |action, method|            assert_recognizes(options.merge(:action => action, :format => 'xml'), :path => "/threads/1/messages/#{action}.xml", :method => method)          end        end -   +        assert_restful_named_routes_for :messages, :path_prefix => 'threads/1/', :name_prefix => 'thread_', :options => { :thread_id => '1' } do |options|          actions.keys.each do |action|            assert_named_route "/threads/1/messages/#{action}.xml", "formatted_#{action}_thread_messages_path", :action => action, :format => 'xml' @@ -279,7 +277,7 @@ class ResourcesTest < Test::Unit::TestCase        end      end    end -   +    def test_with_new_action_with_name_prefix      with_restful_routing :messages, :new => { :preview => :post }, :path_prefix => '/threads/:thread_id', :name_prefix => 'thread_' do        preview_options = {:action => 'preview', :thread_id => '1'} @@ -293,7 +291,7 @@ class ResourcesTest < Test::Unit::TestCase        end      end    end -   +    def test_with_formatted_new_action_with_name_prefix      with_restful_routing :messages, :new => { :preview => :post }, :path_prefix => '/threads/:thread_id', :name_prefix => 'thread_' do        preview_options = {:action => 'preview', :thread_id => '1', :format => 'xml'} @@ -307,7 +305,7 @@ class ResourcesTest < Test::Unit::TestCase        end      end    end -   +    def test_override_new_method      with_restful_routing :messages do        assert_restful_routes_for :messages do |options| @@ -524,9 +522,9 @@ class ResourcesTest < Test::Unit::TestCase          map.resources :messages, :collection => {:search => :get}, :new => {:preview => :any}, :name_prefix => 'thread_', :path_prefix => '/threads/:thread_id'          map.resource :account, :member => {:login => :get}, :new => {:preview => :any}, :name_prefix => 'admin_', :path_prefix => '/admin'        end -       +        action_separator = ActionController::Base.resource_action_separator -       +        assert_simply_restful_for :messages, :name_prefix => 'thread_', :path_prefix => 'threads/1/', :options => { :thread_id => '1' }        assert_named_route "/threads/1/messages#{action_separator}search", "search_thread_messages_path", {}        assert_named_route "/threads/1/messages/new", "new_thread_message_path", {} @@ -623,7 +621,7 @@ class ResourcesTest < Test::Unit::TestCase        assert_simply_restful_for :products, :controller => "backoffice/products"      end    end -   +    def test_nested_resources_using_namespace      with_routing do |set|        set.draw do |map| @@ -795,7 +793,7 @@ class ResourcesTest < Test::Unit::TestCase        yield options[:options] if block_given?      end -     +      def assert_singleton_routes_for(singleton_name, options = {})        options[:options] ||= {}        options[:options][:controller] = options[:controller] || singleton_name.to_s.pluralize @@ -855,7 +853,7 @@ class ResourcesTest < Test::Unit::TestCase        actual =  @controller.send(route, options) rescue $!.class.name        assert_equal expected, actual, "Error on route: #{route}(#{options.inspect})"      end -     +      def assert_resource_methods(expected, resource, action_method, method)        assert_equal expected.length, resource.send("#{action_method}_methods")[method].size, "#{resource.send("#{action_method}_methods")[method].inspect}"        expected.each do |action| diff --git a/actionpack/test/controller/send_file_test.rb b/actionpack/test/controller/send_file_test.rb index ddec51d173..c003abf094 100644 --- a/actionpack/test/controller/send_file_test.rb +++ b/actionpack/test/controller/send_file_test.rb @@ -19,8 +19,6 @@ class SendFileController < ActionController::Base    def rescue_action(e) raise end  end -SendFileController.view_paths = [FIXTURE_LOAD_PATH] -  class SendFileTest < Test::Unit::TestCase    include TestFileUtils diff --git a/actionpack/test/controller/test_test.rb b/actionpack/test/controller/test_test.rb index 38898a1f75..b624005a57 100644 --- a/actionpack/test/controller/test_test.rb +++ b/actionpack/test/controller/test_test.rb @@ -566,24 +566,6 @@ XML      assert_raise(RuntimeError) { ActionController::TestUploadedFile.new('non_existent_file') }    end -  def test_assert_follow_redirect_to_same_controller -    with_foo_routing do |set| -      get :redirect_to_same_controller -      assert_response :redirect -      assert_redirected_to :controller => 'test_test/test', :action => 'test_uri', :id => 5 -      assert_nothing_raised { follow_redirect } -    end -  end - -  def test_assert_follow_redirect_to_different_controller -    with_foo_routing do |set| -      get :redirect_to_different_controller -      assert_response :redirect -      assert_redirected_to :controller => 'fail', :id => 5 -      assert_raise(RuntimeError) { follow_redirect } -    end -  end -    def test_redirect_url_only_cares_about_location_header      get :create      assert_response :created diff --git a/actionpack/test/controller/view_paths_test.rb b/actionpack/test/controller/view_paths_test.rb index 9401c87d10..85fa58a45b 100644 --- a/actionpack/test/controller/view_paths_test.rb +++ b/actionpack/test/controller/view_paths_test.rb @@ -1,8 +1,6 @@  require 'abstract_unit'  class ViewLoadPathsTest < Test::Unit::TestCase -  ActionController::Base.view_paths = [FIXTURE_LOAD_PATH] -    class TestController < ActionController::Base      def self.controller_path() "test" end      def rescue_action(e) raise end @@ -146,18 +144,4 @@ class ViewLoadPathsTest < Test::Unit::TestCase      assert_nothing_raised { C.view_paths << 'c/path' }      assert_equal ['c/path'], C.view_paths    end - -  def test_find_template_file_for_path -    assert_equal "test/hello_world.erb", @controller.view_paths.find_template_file_for_path("test/hello_world.erb").to_s -    assert_equal "test/hello.builder", @controller.view_paths.find_template_file_for_path("test/hello.builder").to_s -    assert_equal nil, @controller.view_paths.find_template_file_for_path("test/missing.erb") -  end - -  def test_view_paths_find_template_file_for_path -    assert_equal "test/formatted_html_erb.html.erb", @controller.view_paths.find_template_file_for_path("test/formatted_html_erb.html").to_s -    assert_equal "test/formatted_xml_erb.xml.erb", @controller.view_paths.find_template_file_for_path("test/formatted_xml_erb.xml").to_s -    assert_equal "test/hello_world.erb", @controller.view_paths.find_template_file_for_path("test/hello_world.html").to_s -    assert_equal "test/hello_world.erb", @controller.view_paths.find_template_file_for_path("test/hello_world.xml").to_s -    assert_equal nil, @controller.view_paths.find_template_file_for_path("test/missing.html") -  end  end diff --git a/actionpack/test/fixtures/developers/_developer.erb b/actionpack/test/fixtures/developers/_developer.erb new file mode 100644 index 0000000000..904a3137e7 --- /dev/null +++ b/actionpack/test/fixtures/developers/_developer.erb @@ -0,0 +1 @@ +<%= developer.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/fun/games/_game.erb b/actionpack/test/fixtures/fun/games/_game.erb new file mode 100644 index 0000000000..d51b7b3ebc --- /dev/null +++ b/actionpack/test/fixtures/fun/games/_game.erb @@ -0,0 +1 @@ +<%= game.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/fun/serious/games/_game.erb b/actionpack/test/fixtures/fun/serious/games/_game.erb new file mode 100644 index 0000000000..d51b7b3ebc --- /dev/null +++ b/actionpack/test/fixtures/fun/serious/games/_game.erb @@ -0,0 +1 @@ +<%= game.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/projects/_project.erb b/actionpack/test/fixtures/projects/_project.erb new file mode 100644 index 0000000000..480c4c2af3 --- /dev/null +++ b/actionpack/test/fixtures/projects/_project.erb @@ -0,0 +1 @@ +<%= project.name %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/public/javascripts/subdir/subdir.js b/actionpack/test/fixtures/public/javascripts/subdir/subdir.js new file mode 100644 index 0000000000..9d23a67aa1 --- /dev/null +++ b/actionpack/test/fixtures/public/javascripts/subdir/subdir.js @@ -0,0 +1 @@ +// subdir js diff --git a/actionpack/test/fixtures/public/stylesheets/subdir/subdir.css b/actionpack/test/fixtures/public/stylesheets/subdir/subdir.css new file mode 100644 index 0000000000..241152a905 --- /dev/null +++ b/actionpack/test/fixtures/public/stylesheets/subdir/subdir.css @@ -0,0 +1 @@ +/* subdir.css */ diff --git a/actionpack/test/fixtures/replies/_reply.erb b/actionpack/test/fixtures/replies/_reply.erb new file mode 100644 index 0000000000..68baf548d8 --- /dev/null +++ b/actionpack/test/fixtures/replies/_reply.erb @@ -0,0 +1 @@ +<%= reply.content %>
\ No newline at end of file diff --git a/actionpack/test/fixtures/test/hyphen-ated.erb b/actionpack/test/fixtures/test/hyphen-ated.erb new file mode 100644 index 0000000000..cd0875583a --- /dev/null +++ b/actionpack/test/fixtures/test/hyphen-ated.erb @@ -0,0 +1 @@ +Hello world! diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb index 4a8117a88a..020e112fd0 100644 --- a/actionpack/test/template/asset_tag_helper_test.rb +++ b/actionpack/test/template/asset_tag_helper_test.rb @@ -83,6 +83,7 @@ class AssetTagHelperTest < ActionView::TestCase      %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" type="text/javascript"></script>\n<script src="/elsewhere/cools.js" type="text/javascript"></script>),      %(javascript_include_tag(:defaults)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),      %(javascript_include_tag(:all)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>), +    %(javascript_include_tag(:all, :recursive => true)) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>),      %(javascript_include_tag(:defaults, "test")) => %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/test.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>),      %(javascript_include_tag("test", :defaults)) => %(<script src="/javascripts/test.js" type="text/javascript"></script>\n<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>)    } @@ -108,6 +109,7 @@ class AssetTagHelperTest < ActionView::TestCase      %(stylesheet_link_tag("dir/file")) => %(<link href="/stylesheets/dir/file.css" media="screen" rel="stylesheet" type="text/css" />),      %(stylesheet_link_tag("style", :media => "all")) => %(<link href="/stylesheets/style.css" media="all" rel="stylesheet" type="text/css" />),      %(stylesheet_link_tag(:all)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />), +    %(stylesheet_link_tag(:all, :recursive => true)) => %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />),      %(stylesheet_link_tag(:all, :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="all" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="all" rel="stylesheet" type="text/css" />),      %(stylesheet_link_tag("random.styles", "/css/stylish")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" type="text/css" />\n<link href="/css/stylish.css" media="screen" rel="stylesheet" type="text/css" />),      %(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" type="text/css" />) @@ -343,6 +345,27 @@ class AssetTagHelperTest < ActionView::TestCase      FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'cache', 'money.js'))    end +  def test_caching_javascript_include_tag_with_all_and_recursive_puts_defaults_at_the_start_of_the_file +    ENV["RAILS_ASSET_ID"] = "" +    ActionController::Base.asset_host = 'http://a0.example.com' +    ActionController::Base.perform_caching = true + +    assert_dom_equal( +      %(<script src="http://a0.example.com/javascripts/combined.js" type="text/javascript"></script>), +      javascript_include_tag(:all, :cache => "combined", :recursive => true) +    ) + +    assert File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js')) + +    assert_equal( +      %(// prototype js\n\n// effects js\n\n// dragdrop js\n\n// controls js\n\n// application js\n\n// bank js\n\n// robber js\n\n// subdir js\n\n\n// version.1.0 js), +      IO.read(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js')) +    ) + +  ensure +    FileUtils.rm_f(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'combined.js')) +  end +    def test_caching_javascript_include_tag_with_all_puts_defaults_at_the_start_of_the_file      ENV["RAILS_ASSET_ID"] = ""      ActionController::Base.asset_host = 'http://a0.example.com' @@ -373,6 +396,11 @@ class AssetTagHelperTest < ActionView::TestCase        javascript_include_tag(:all, :cache => true)      ) +    assert_dom_equal( +      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>), +      javascript_include_tag(:all, :cache => true, :recursive => true) +    ) +      assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'all.js'))      assert_dom_equal( @@ -380,6 +408,11 @@ class AssetTagHelperTest < ActionView::TestCase        javascript_include_tag(:all, :cache => "money")      ) +    assert_dom_equal( +      %(<script src="/javascripts/prototype.js" type="text/javascript"></script>\n<script src="/javascripts/effects.js" type="text/javascript"></script>\n<script src="/javascripts/dragdrop.js" type="text/javascript"></script>\n<script src="/javascripts/controls.js" type="text/javascript"></script>\n<script src="/javascripts/application.js" type="text/javascript"></script>\n<script src="/javascripts/bank.js" type="text/javascript"></script>\n<script src="/javascripts/robber.js" type="text/javascript"></script>\n<script src="/javascripts/subdir/subdir.js" type="text/javascript"></script>\n<script src="/javascripts/version.1.0.js" type="text/javascript"></script>), +      javascript_include_tag(:all, :cache => "money", :recursive => true) +    ) +      assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'money.js'))    end @@ -432,6 +465,11 @@ class AssetTagHelperTest < ActionView::TestCase        stylesheet_link_tag(:all, :cache => true)      ) +    assert_dom_equal( +      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />), +      stylesheet_link_tag(:all, :cache => true, :recursive => true) +    ) +      assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'all.css'))      assert_dom_equal( @@ -439,6 +477,11 @@ class AssetTagHelperTest < ActionView::TestCase        stylesheet_link_tag(:all, :cache => "money")      ) +    assert_dom_equal( +      %(<link href="/stylesheets/bank.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/robber.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" type="text/css" />\n<link href="/stylesheets/version.1.0.css" media="screen" rel="stylesheet" type="text/css" />), +      stylesheet_link_tag(:all, :cache => "money", :recursive => true) +    ) +      assert !File.exist?(File.join(ActionView::Helpers::AssetTagHelper::STYLESHEETS_DIR, 'money.css'))    end  end diff --git a/actionpack/test/template/date_helper_test.rb b/actionpack/test/template/date_helper_test.rb index 3faa363459..8b4e94c67f 100755 --- a/actionpack/test/template/date_helper_test.rb +++ b/actionpack/test/template/date_helper_test.rb @@ -1198,6 +1198,21 @@ class DateHelperTest < ActionView::TestCase      assert_dom_equal expected, time_select("post", "written_on")    end +  def test_time_select_without_date_hidden_fields +    @post = Post.new +    @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) + +    expected = %(<select id="post_written_on_4i" name="post[written_on(4i)]">\n) +    0.upto(23) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 15}>#{leading_zero_on_single_digits(i)}</option>\n) } +    expected << "</select>\n" +    expected << " : " +    expected << %(<select id="post_written_on_5i" name="post[written_on(5i)]">\n) +    0.upto(59) { |i| expected << %(<option value="#{leading_zero_on_single_digits(i)}"#{' selected="selected"' if i == 16}>#{leading_zero_on_single_digits(i)}</option>\n) } +    expected << "</select>\n" + +    assert_dom_equal expected, time_select("post", "written_on", :ignore_date => true) +  end +    def test_time_select_with_seconds      @post = Post.new      @post.written_on = Time.local(2004, 6, 15, 15, 16, 35) diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index 60b83b476d..1d9bc5eb9b 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -201,9 +201,9 @@ class PrototypeHelperTest < PrototypeHelperBaseTest    end -  def test_submit_to_remote +  def test_button_to_remote      assert_dom_equal %(<input name=\"More beer!\" onclick=\"new Ajax.Updater('empty_bottle', 'http://www.example.com/', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this.form)}); return false;\" type=\"button\" value=\"1000000\" />), -      submit_to_remote("More beer!", 1_000_000, :update => "empty_bottle") +      button_to_remote("More beer!", 1_000_000, :update => "empty_bottle")    end    def test_observe_field diff --git a/actionpack/test/template/render_test.rb b/actionpack/test/template/render_test.rb index 0dcf88da83..cc5b4900dc 100644 --- a/actionpack/test/template/render_test.rb +++ b/actionpack/test/template/render_test.rb @@ -4,7 +4,7 @@ require 'controller/fake_models'  class ViewRenderTest < Test::Unit::TestCase    def setup      @assigns = { :secret => 'in the sauce' } -    @view = ActionView::Base.new([FIXTURE_LOAD_PATH], @assigns) +    @view = ActionView::Base.new(ActionController::Base.view_paths, @assigns)    end    def test_render_file @@ -95,18 +95,18 @@ class ViewRenderTest < Test::Unit::TestCase    end    class CustomHandler < ActionView::TemplateHandler -    def render(template) -      [template.source, template.locals].inspect +    def render(template, local_assigns) +      [template.source, local_assigns].inspect      end    end    def test_render_inline_with_custom_type -    ActionView::Base.register_template_handler :foo, CustomHandler +    ActionView::Template.register_template_handler :foo, CustomHandler      assert_equal '["Hello, World!", {}]', @view.render(:inline => "Hello, World!", :type => :foo)    end    def test_render_inline_with_locals_and_custom_type -    ActionView::Base.register_template_handler :foo, CustomHandler +    ActionView::Template.register_template_handler :foo, CustomHandler      assert_equal '["Hello, <%= name %>!", {:name=>"Josh"}]', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)    end @@ -115,18 +115,17 @@ class ViewRenderTest < Test::Unit::TestCase      def compile(template)        "@output_buffer = ''\n" + -        "@output_buffer << 'locals: #{template.locals.inspect}, '\n" +          "@output_buffer << 'source: #{template.source.inspect}'\n"      end    end    def test_render_inline_with_compilable_custom_type -    ActionView::Base.register_template_handler :foo, CompilableCustomHandler -    assert_equal 'locals: {}, source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo) +    ActionView::Template.register_template_handler :foo, CompilableCustomHandler +    assert_equal 'source: "Hello, World!"', @view.render(:inline => "Hello, World!", :type => :foo)    end    def test_render_inline_with_locals_and_compilable_custom_type -    ActionView::Base.register_template_handler :foo, CompilableCustomHandler -    assert_equal 'locals: {:name=>"Josh"}, source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo) +    ActionView::Template.register_template_handler :foo, CompilableCustomHandler +    assert_equal 'source: "Hello, <%= name %>!"', @view.render(:inline => "Hello, <%= name %>!", :locals => { :name => "Josh" }, :type => :foo)    end  end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 3d5f7eae11..91d5c6ffb5 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -292,6 +292,7 @@ class UrlHelperTest < ActionView::TestCase      assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">My email</a>", mail_to("me@domain.com", "My email", :encode => "hex", :replace_at => "(at)")      assert_dom_equal "<a href=\"mailto:%6d%65@%64%6f%6d%61%69%6e.%63%6f%6d\">me(at)domain(dot)com</a>", mail_to("me@domain.com", nil, :encode => "hex", :replace_at => "(at)", :replace_dot => "(dot)")      assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%4d%79%20%65%6d%61%69%6c%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", "My email", :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)") +    assert_dom_equal "<script type=\"text/javascript\">eval(unescape('%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%61%20%68%72%65%66%3d%22%6d%61%69%6c%74%6f%3a%6d%65%40%64%6f%6d%61%69%6e%2e%63%6f%6d%22%3e%6d%65%28%61%74%29%64%6f%6d%61%69%6e%28%64%6f%74%29%63%6f%6d%3c%2f%61%3e%27%29%3b'))</script>", mail_to("me@domain.com", nil, :encode => "javascript", :replace_at => "(at)", :replace_dot => "(dot)")    end    def protect_against_forgery? @@ -301,8 +302,6 @@ end  class UrlHelperWithControllerTest < ActionView::TestCase    class UrlHelperController < ActionController::Base -    self.view_paths = [FIXTURE_LOAD_PATH] -      def self.controller_path; 'url_helper_with_controller' end      def show_url_for @@ -313,6 +312,10 @@ class UrlHelperWithControllerTest < ActionView::TestCase        render :inline => "<%= show_named_route_#{params[:kind]} %>"      end +    def nil_url_for +      render :inline => '<%= url_for(nil) %>' +    end +      def rescue_action(e) raise e end    end @@ -329,7 +332,7 @@ class UrlHelperWithControllerTest < ActionView::TestCase      assert_equal '/url_helper_with_controller/show_url_for', @response.body    end -  def test_named_route_shows_host_and_path +  def test_named_route_url_shows_host_and_path      with_url_helper_routing do        get :show_named_route, :kind => 'url'        assert_equal 'http://test.host/url_helper_with_controller/show_named_route', @response.body @@ -343,6 +346,11 @@ class UrlHelperWithControllerTest < ActionView::TestCase      end    end +  def test_url_for_nil_returns_current_path +    get :nil_url_for +    assert_equal '/url_helper_with_controller/nil_url_for', @response.body +  end +    protected      def with_url_helper_routing        with_routing do |set| @@ -356,8 +364,6 @@ end  class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase    class TasksController < ActionController::Base -    self.view_paths = [FIXTURE_LOAD_PATH] -      def self.controller_path; 'tasks' end      def index @@ -448,8 +454,6 @@ end  class PolymorphicControllerTest < ActionView::TestCase    class WorkshopsController < ActionController::Base -    self.view_paths = [FIXTURE_LOAD_PATH] -      def self.controller_path; 'workshops' end      def index @@ -466,8 +470,6 @@ class PolymorphicControllerTest < ActionView::TestCase    end    class SessionsController < ActionController::Base -    self.view_paths = [FIXTURE_LOAD_PATH] -      def self.controller_path; 'sessions' end      def index  | 
