diff options
Diffstat (limited to 'actionpack/lib')
-rw-r--r-- | actionpack/lib/abstract_controller/base.rb | 13 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal.rb | 11 | ||||
-rw-r--r-- | actionpack/lib/action_controller/metal/rack_delegation.rb | 4 | ||||
-rw-r--r-- | actionpack/lib/action_controller/polymorphic_routes.rb | 3 | ||||
-rw-r--r-- | actionpack/lib/action_controller/record_identifier.rb | 14 | ||||
-rw-r--r--[-rwxr-xr-x] | actionpack/lib/action_dispatch/http/request.rb | 0 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/callbacks.rb | 8 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/railtie.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_dispatch/routing/mapper.rb | 446 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/date_helper.rb | 6 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_options_helper.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/text_helper.rb | 32 | ||||
-rw-r--r-- | actionpack/lib/action_view/render/partials.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/test_case.rb | 1 |
16 files changed, 343 insertions, 205 deletions
diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index 4d7b4019d8..8a8337858b 100644 --- a/actionpack/lib/abstract_controller/base.rb +++ b/actionpack/lib/abstract_controller/base.rb @@ -1,4 +1,5 @@ require 'active_support/configurable' +require 'active_support/descendants_tracker' require 'active_support/core_ext/module/anonymous' module AbstractController @@ -10,6 +11,7 @@ module AbstractController attr_internal :action_name include ActiveSupport::Configurable + extend ActiveSupport::DescendantsTracker class << self attr_reader :abstract @@ -21,17 +23,6 @@ module AbstractController @abstract = true end - def inherited(klass) - ::AbstractController::Base.descendants << klass.to_s - super - end - - # A list of all descendants of AbstractController::Base. This is - # useful for initializers which need to add behavior to all controllers. - def descendants - @descendants ||= [] - end - # A list of all internal methods for a controller. This finds the first # abstract superclass of a controller, and gets a list of all public # instance methods on that abstract class. Public instance methods of diff --git a/actionpack/lib/action_controller/metal.rb b/actionpack/lib/action_controller/metal.rb index 775a5002e2..159d1f0748 100644 --- a/actionpack/lib/action_controller/metal.rb +++ b/actionpack/lib/action_controller/metal.rb @@ -52,8 +52,7 @@ module ActionController class Metal < AbstractController::Base abstract! - # :api: public - attr_internal :params, :env + attr_internal :env # Returns the last part of the controller's name, underscored, without the ending # "Controller". For instance, MyApp::MyPostsController would return "my_posts" for @@ -85,6 +84,14 @@ module ActionController super end + def params + @_params ||= request.parameters + end + + def params=(val) + @_params = val + end + # Basic implementations for content_type=, location=, and headers are # provided to reduce the dependency on the RackDelegation module # in Renderer and Redirector. diff --git a/actionpack/lib/action_controller/metal/rack_delegation.rb b/actionpack/lib/action_controller/metal/rack_delegation.rb index 508ea6e2b7..544b4989c7 100644 --- a/actionpack/lib/action_controller/metal/rack_delegation.rb +++ b/actionpack/lib/action_controller/metal/rack_delegation.rb @@ -14,10 +14,6 @@ module ActionController super(action, request) end - def params - @_params ||= @_request.parameters - end - def response_body=(body) response.body = body if response super diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index 7f2eb4306b..bee50a7a3b 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -11,7 +11,7 @@ module ActionController # polymorphic_url([:admin, @article, @comment]) # # results in: - # + # # admin_article_comment_url(@article, @comment) # # == Usage within the framework @@ -166,6 +166,7 @@ module ActionController route << RecordIdentifier.__send__("plural_class_name", record) route = route.singularize if inflection == :singular route << "_" + route << "index_" if RecordIdentifier.uncountable?(record) && inflection == :plural end action_prefix(options) + route + routing_type(options).to_s diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 907c369218..d20c3b64c5 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -1,7 +1,7 @@ require 'active_support/core_ext/module' module ActionController - # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or + # The record identifier encapsulates a number of naming conventions for dealing with records, like Active Records or # Active Resources or pretty much any other model type that has an id. These patterns are then used to try elevate # the view actions to a higher logical level. Example: # @@ -28,7 +28,7 @@ module ActionController # end # # As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know - # that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming + # that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming # convention and allows you to write less code if you follow it. module RecordIdentifier extend self @@ -59,7 +59,7 @@ module ActionController # If you need to address multiple instances of the same class in the same view, you can prefix the dom_id: # # dom_id(Post.find(45), :edit) # => "edit_post_45" - def dom_id(record, prefix = nil) + def dom_id(record, prefix = nil) if record_id = record_key_for_dom_id(record) "#{dom_class(record, prefix)}#{JOIN}#{record_id}" else @@ -102,6 +102,14 @@ module ActionController model_name_from_record_or_class(record_or_class).singular end + # Identifies whether the class name of a record or class is uncountable. Examples: + # + # uncountable?(Sheep) # => true + # uncountable?(Post) => false + def uncountable?(record_or_class) + plural_class_name(record_or_class) == singular_class_name(record_or_class) + end + private def model_name_from_record_or_class(record_or_class) (record_or_class.is_a?(Class) ? record_or_class : record_or_class.class).model_name diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 98f4f5ae18..98f4f5ae18 100755..100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb diff --git a/actionpack/lib/action_dispatch/middleware/callbacks.rb b/actionpack/lib/action_dispatch/middleware/callbacks.rb index d07841218a..e4ae480bfb 100644 --- a/actionpack/lib/action_dispatch/middleware/callbacks.rb +++ b/actionpack/lib/action_dispatch/middleware/callbacks.rb @@ -8,7 +8,7 @@ module ActionDispatch class Callbacks include ActiveSupport::Callbacks - define_callbacks :call, :terminator => "result == false", :rescuable => true + define_callbacks :call, :rescuable => true define_callbacks :prepare, :scope => :name # Add a preparation callback. Preparation callbacks are run before every @@ -37,12 +37,12 @@ module ActionDispatch def initialize(app, prepare_each_request = false) @app, @prepare_each_request = app, prepare_each_request - run_callbacks(:prepare) + _run_prepare_callbacks end def call(env) - run_callbacks(:call) do - run_callbacks(:prepare) if @prepare_each_request + _run_call_callbacks do + _run_prepare_callbacks if @prepare_each_request @app.call(env) end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 0a6d2bfc8a..e095b51342 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -122,7 +122,7 @@ module ActionDispatch end def render(status, body) - [status, {'Content-Type' => 'text/html', 'Content-Length' => body.length.to_s}, [body]] + [status, {'Content-Type' => 'text/html', 'Content-Length' => body.bytesize.to_s}, [body]] end def public_path diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 38da44d7e7..ed93211255 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -10,7 +10,7 @@ module ActionDispatch # Prepare dispatcher callbacks and run 'prepare' callbacks initializer "action_dispatch.prepare_dispatcher" do |app| - ActionDispatch::Callbacks.to_prepare { app.routes_reloader.reload_if_changed } + ActionDispatch::Callbacks.to_prepare { app.routes_reloader.execute_if_updated } end end end
\ No newline at end of file diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7b79b6bde3..c31f681411 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -33,7 +33,7 @@ module ActionDispatch end class Mapping #:nodoc: - IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor] + IGNORE_OPTIONS = [:to, :as, :controller, :action, :via, :on, :constraints, :defaults, :only, :except, :anchor, :shallow, :shallow_path, :shallow_prefix] def initialize(set, scope, args) @set, @scope = set, scope @@ -102,7 +102,7 @@ module ActionDispatch end def requirements - @requirements ||= (@options[:constraints] || {}).tap do |requirements| + @requirements ||= (@options[:constraints].is_a?(Hash) ? @options[:constraints] : {}).tap do |requirements| requirements.reverse_merge!(@scope[:constraints]) if @scope[:constraints] @options.each { |k, v| requirements[k] = v if v.is_a?(Regexp) } end @@ -343,7 +343,7 @@ module ActionDispatch def namespace(path) path = path.to_s - scope(:path => path, :name_prefix => path, :module => path) { yield } + scope(:path => path, :name_prefix => path, :module => path, :shallow_path => path, :shallow_prefix => path) { yield } end def constraints(constraints = {}) @@ -378,10 +378,18 @@ module ActionDispatch Mapper.normalize_path("#{parent}/#{child}") end + def merge_shallow_path_scope(parent, child) + Mapper.normalize_path("#{parent}/#{child}") + end + def merge_name_prefix_scope(parent, child) parent ? "#{parent}_#{child}" : child end + def merge_shallow_prefix_scope(parent, child) + parent ? "#{parent}_#{child}" : child + end + def merge_module_scope(parent, child) parent ? "#{parent}/#{child}" : child end @@ -409,11 +417,13 @@ module ActionDispatch def merge_options_scope(parent, child) (parent || {}).merge(child) end + + def merge_shallow_scope(parent, child) + child ? true : false + end end module Resources - CRUD_ACTIONS = [:index, :show, :create, :update, :destroy] #:nodoc: - class Resource #:nodoc: def self.default_actions [:index, :create, :new, :show, :update, :destroy, :edit] @@ -442,15 +452,6 @@ module ActionDispatch end end - def action_type(action) - case action - when :index, :create - :collection - when :show, :update, :destroy - :member - end - end - def name options[:as] || @name end @@ -463,34 +464,19 @@ module ActionDispatch name.to_s.singularize end - def member_prefix - ':id' - end - def member_name singular end + alias_method :nested_name, :member_name + # Checks for uncountable plurals, and appends "_index" if they're. def collection_name - uncountable? ? "#{plural}_index" : plural + singular == plural ? "#{plural}_index" : plural end - def uncountable? - singular == plural - end - - def name_for_action(action) - case action_type(action) - when :collection - collection_name - when :member - member_name - end - end - - def id_segment - ":#{singular}_id" + def shallow? + options[:shallow] ? true : false end def constraints @@ -506,21 +492,43 @@ module ActionDispatch end def collection_options - (options || {}).dup.tap do |options| - options.delete(:id) - options[:constraints] = options[:constraints].dup if options[:constraints] - options[:constraints].delete(:id) if options[:constraints].is_a?(Hash) + (options || {}).dup.tap do |opts| + opts.delete(:id) + opts[:constraints] = options[:constraints].dup if options[:constraints] + opts[:constraints].delete(:id) if options[:constraints].is_a?(Hash) end end - def nested_prefix - id_segment + def nested_path + "#{path}/:#{singular}_id" end def nested_options - options = { :name_prefix => member_name } - options["#{singular}_id".to_sym] = id_constraint if id_constraint? - options + {}.tap do |opts| + opts[:name_prefix] = member_name + opts["#{singular}_id".to_sym] = id_constraint if id_constraint? + opts[:options] = { :shallow => shallow? } unless options[:shallow].nil? + end + end + + def resource_scope + [{ :controller => controller }] + end + + def collection_scope + [path, collection_options] + end + + def member_scope + ["#{path}/:id", options] + end + + def new_scope + [path] + end + + def nested_scope + [nested_path, nested_options] end end @@ -533,27 +541,28 @@ module ActionDispatch super end - def action_type(action) - case action - when :show, :create, :update, :destroy - :member - end + def member_name + name end + alias_method :collection_name, :member_name - def member_prefix - '' + def nested_path + path end - def member_name - name + def nested_options + {}.tap do |opts| + opts[:name_prefix] = member_name + opts[:options] = { :shallow => shallow? } unless @options[:shallow].nil? + end end - def nested_prefix - '' + def shallow? + false end - def nested_options - { :name_prefix => member_name } + def member_scope + [path, options] end end @@ -565,28 +574,25 @@ module ActionDispatch def resource(*resources, &block) options = resources.extract_options! options = (@scope[:options] || {}).merge(options) + options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow) if apply_common_behavior_for(:resource, resources, options, &block) return self end - resource = SingletonResource.new(resources.pop, options) - - scope(:path => resource.path, :controller => resource.controller) do - with_scope_level(:resource, resource) do + resource_scope(SingletonResource.new(resources.pop, options)) do + yield if block_given? - yield if block_given? + collection_scope do + post :create if parent_resource.actions.include?(:create) + get :new if parent_resource.actions.include?(:new) + end - with_scope_level(:member) do - scope(resource.options) do - get :show if resource.actions.include?(:show) - post :create if resource.actions.include?(:create) - put :update if resource.actions.include?(:update) - delete :destroy if resource.actions.include?(:destroy) - get :new, :as => resource.name if resource.actions.include?(:new) - get :edit, :as => resource.name if resource.actions.include?(:edit) - end - end + member_scope do + get :show if parent_resource.actions.include?(:show) + put :update if parent_resource.actions.include?(:update) + delete :destroy if parent_resource.actions.include?(:destroy) + get :edit if parent_resource.actions.include?(:edit) end end @@ -596,35 +602,26 @@ module ActionDispatch def resources(*resources, &block) options = resources.extract_options! options = (@scope[:options] || {}).merge(options) + options[:shallow] = true if @scope[:shallow] && !options.has_key?(:shallow) if apply_common_behavior_for(:resources, resources, options, &block) return self end - resource = Resource.new(resources.pop, options) - - scope(:path => resource.path, :controller => resource.controller) do - with_scope_level(:resources, resource) do - yield if block_given? + resource_scope(Resource.new(resources.pop, options)) do + yield if block_given? - with_scope_level(:collection) do - scope(resource.collection_options) do - get :index if resource.actions.include?(:index) - post :create if resource.actions.include?(:create) - get :new, :as => resource.singular if resource.actions.include?(:new) - end - end + collection_scope do + get :index if parent_resource.actions.include?(:index) + post :create if parent_resource.actions.include?(:create) + get :new if parent_resource.actions.include?(:new) + end - with_scope_level(:member) do - scope(':id') do - scope(resource.options) do - get :show if resource.actions.include?(:show) - put :update if resource.actions.include?(:update) - delete :destroy if resource.actions.include?(:destroy) - get :edit, :as => resource.singular if resource.actions.include?(:edit) - end - end - end + member_scope do + get :show if parent_resource.actions.include?(:show) + put :update if parent_resource.actions.include?(:update) + delete :destroy if parent_resource.actions.include?(:destroy) + get :edit if parent_resource.actions.include?(:edit) end end @@ -636,10 +633,8 @@ module ActionDispatch raise ArgumentError, "can't use collection outside resources scope" end - with_scope_level(:collection) do - scope(:name_prefix => parent_resource.collection_name, :as => "") do - yield - end + collection_scope do + yield end end @@ -648,10 +643,8 @@ module ActionDispatch raise ArgumentError, "can't use member outside resource(s) scope" end - with_scope_level(:member) do - scope(parent_resource.member_prefix, :name_prefix => parent_resource.member_name, :as => "") do - yield - end + member_scope do + yield end end @@ -659,10 +652,12 @@ module ActionDispatch unless resource_scope? raise ArgumentError, "can't use new outside resource(s) scope" end - + with_scope_level(:new) do - scope(new_scope_prefix, :name_prefix => parent_resource.member_name, :as => "") do - yield + scope(*parent_resource.new_scope) do + scope(action_path(:new)) do + yield + end end end end @@ -673,8 +668,18 @@ module ActionDispatch end with_scope_level(:nested) do - scope(parent_resource.nested_prefix, parent_resource.nested_options) do - yield + if parent_resource.shallow? + with_exclusive_scope do + if @scope[:shallow_path].blank? + scope(*parent_resource.nested_scope) { yield } + else + scope(@scope[:shallow_path], :name_prefix => @scope[:shallow_prefix]) do + scope(*parent_resource.nested_scope) { yield } + end + end + end + else + scope(*parent_resource.nested_scope) { yield } end end end @@ -687,63 +692,79 @@ module ActionDispatch end end + def shallow + scope(:shallow => true) do + yield + end + end + def match(*args) options = args.extract_options! options[:anchor] = true unless options.key?(:anchor) if args.length > 1 - args.each { |path| match(path, options) } + args.each { |path| match(path, options.dup) } return self end - path_names = options.delete(:path_names) + if [:collection, :member, :new].include?(options[:on]) + args.push(options) - if args.first.is_a?(Symbol) - action = args.first - if CRUD_ACTIONS.include?(action) - begin - old_path = @scope[:path] - @scope[:path] = "#{@scope[:path]}(.:format)" - return match(options.reverse_merge( - :to => action, - :as => parent_resource.name_for_action(action) - )) - ensure - @scope[:path] = old_path - end - else - with_exclusive_name_prefix(action_name_prefix(action, options)) do - return match("#{action_path(action, path_names)}(.:format)", options.reverse_merge(:to => action)) - end + case options.delete(:on) + when :collection + return collection { match(*args) } + when :member + return member { match(*args) } + when :new + return new { match(*args) } end end - args.push(options) - - case options.delete(:on) - when :collection - return collection { match(*args) } - when :member + if @scope[:scope_level] == :resource + args.push(options) return member { match(*args) } - when :new - return new { match(*args) } end - if @scope[:scope_level] == :resource - return member { match(*args) } + path_names = options.delete(:path_names) + + if args.first.is_a?(Symbol) + path = path_for_action(args.first, path_names) + options = options_for_action(args.first, options) + + with_exclusive_scope do + return super(path, options) + end + elsif resource_method_scope? + path = path_for_custom_action + options[:as] = name_for_action(options[:as]) if options[:as] + args.push(options) + + with_exclusive_scope do + scope(path) do + return super + end + end end if resource_scope? raise ArgumentError, "can't define route directly in resource(s) scope" end + args.push(options) super end def root(options={}) - options[:on] ||= :collection if @scope[:scope_level] == :resources - super(options) + if @scope[:scope_level] == :resources + with_scope_level(:nested) do + scope(parent_resource.path, :name_prefix => parent_resource.collection_name) do + super(options) + end + end + else + super(options) + end end protected @@ -752,15 +773,6 @@ module ActionDispatch end private - def action_path(name, path_names = nil) - path_names ||= @scope[:path_names] - path_names[name.to_sym] || name.to_s - end - - def action_name_prefix(action, options = {}) - (options[:on] == :new || @scope[:scope_level] == :new) ? "#{action}_new" : action - end - def apply_common_behavior_for(method, resources, options, &block) if resources.length > 1 resources.each { |r| send(method, r, options, &block) } @@ -784,27 +796,24 @@ module ActionDispatch false end - def new_scope_prefix - @scope[:path_names][:new] || 'new' - end - def resource_scope? [:resource, :resources].include?(@scope[:scope_level]) end - def with_exclusive_name_prefix(prefix) + def resource_method_scope? + [:collection, :member, :new].include?(@scope[:scope_level]) + end + + def with_exclusive_scope begin - old_name_prefix = @scope[:name_prefix] + old_name_prefix, old_path = @scope[:name_prefix], @scope[:path] + @scope[:name_prefix], @scope[:path] = nil, nil - if !old_name_prefix.blank? - @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}" - else - @scope[:name_prefix] = prefix.to_s + with_scope_level(:exclusive) do + yield end - - yield ensure - @scope[:name_prefix] = old_name_prefix + @scope[:name_prefix], @scope[:path] = old_name_prefix, old_path end end @@ -816,6 +825,125 @@ module ActionDispatch @scope[:scope_level] = old @scope[:scope_level_resource] = old_resource end + + def resource_scope(resource) + with_scope_level(resource.is_a?(SingletonResource) ? :resource : :resources, resource) do + scope(*parent_resource.resource_scope) do + yield + end + end + end + + def collection_scope + with_scope_level(:collection) do + scope(*parent_resource.collection_scope) do + yield + end + end + end + + def member_scope + with_scope_level(:member) do + scope(*parent_resource.member_scope) do + yield + end + end + end + + def path_for_action(action, path_names) + case action + when :index, :create + "#{@scope[:path]}(.:format)" + when :show, :update, :destroy + if parent_resource.shallow? + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id(.:format)" + else + "#{@scope[:path]}(.:format)" + end + when :new + "#{@scope[:path]}/#{action_path(:new)}(.:format)" + when :edit + if parent_resource.shallow? + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)" + else + "#{@scope[:path]}/#{action_path(:edit)}(.:format)" + end + else + case @scope[:scope_level] + when :collection, :new + "#{@scope[:path]}/#{action_path(action)}(.:format)" + else + if parent_resource.shallow? + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" + else + "#{@scope[:path]}/#{action_path(action)}(.:format)" + end + end + end + end + + def path_for_custom_action + case @scope[:scope_level] + when :collection, :new + @scope[:path] + else + if parent_resource.shallow? + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" + else + @scope[:path] + end + end + end + + def action_path(name, path_names = nil) + path_names ||= @scope[:path_names] + path_names[name.to_sym] || name.to_s + end + + def options_for_action(action, options) + options.reverse_merge( + :to => action, + :as => name_for_action(action) + ) + end + + def name_for_action(action) + name_prefix = @scope[:name_prefix].blank? ? "" : "#{@scope[:name_prefix]}_" + shallow_prefix = @scope[:shallow_prefix].blank? ? "" : "#{@scope[:shallow_prefix]}_" + + case action + when :index, :create + "#{name_prefix}#{parent_resource.collection_name}" + when :show, :update, :destroy + if parent_resource.shallow? + "#{shallow_prefix}#{parent_resource.member_name}" + else + "#{name_prefix}#{parent_resource.member_name}" + end + when :edit + if parent_resource.shallow? + "edit_#{shallow_prefix}#{parent_resource.member_name}" + else + "edit_#{name_prefix}#{parent_resource.member_name}" + end + when :new + "new_#{name_prefix}#{parent_resource.member_name}" + else + case @scope[:scope_level] + when :collection + "#{action}_#{name_prefix}#{parent_resource.collection_name}" + when :new + "#{action}_new_#{name_prefix}#{parent_resource.member_name}" + else + if parent_resource.shallow? + "#{action}_#{shallow_prefix}#{parent_resource.member_name}" + else + "#{action}_#{name_prefix}#{parent_resource.member_name}" + end + end + end + end + end include Base diff --git a/actionpack/lib/action_view/helpers/date_helper.rb b/actionpack/lib/action_view/helpers/date_helper.rb index 6f387bc95a..7d7b6a1d91 100644 --- a/actionpack/lib/action_view/helpers/date_helper.rb +++ b/actionpack/lib/action_view/helpers/date_helper.rb @@ -896,8 +896,10 @@ module ActionView # Returns the separator for a given datetime component def separator(type) case type - when :month, :day - @options[:date_separator] + when :month + @options[:discard_month] ? "" : @options[:date_separator] + when :day + @options[:discard_day] ? "" : @options[:date_separator] when :hour (@options[:discard_year] && @options[:discard_day]) ? "" : @options[:datetime_separator] when :minute diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index a8887a804e..8efed98bd2 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -303,7 +303,7 @@ module ActionView args.unshift object end - options[:html][:remote] = true if options.delete(:remote) + (options[:html] ||= {})[:remote] = true if options.delete(:remote) output = form_tag(options.delete(:url) || {}, options.delete(:html) || {}) output << fields_for(object_name, *(args << options), &proc) diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index c564d30302..6f9d14de8b 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -399,7 +399,7 @@ module ActionView options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">" options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) options_for_select += '</optgroup>' - end + end.html_safe end # Returns a string of <tt><option></tt> tags, like <tt>options_for_select</tt>, but diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 9afa989453..0be8a2c36e 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -40,7 +40,10 @@ module ActionView # for a total length not exceeding <tt>:length</tt>. # # Pass a <tt>:separator</tt> to truncate +text+ at a natural break. - # Pass a <tt>:safe</tt> value as "true" to not to escape the content. + # + # The result is not marked as HTML-safe, so will be subject to the default escaping when + # used in views, unless wrapped by <tt>raw()</tt>. Care should be taken if +text+ contains HTML tags + # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags). # # ==== Examples # @@ -57,12 +60,6 @@ module ActionView # # => "And they f... (continued)" # # truncate("<p>Once upon a time in a world far far away</p>") - # # => "<p>Once upon a time i..." - # - # truncate("<p>Once upon a time in a world far far away</p>", :safe => true) - # # => "<p>Once upon a time in a wo..." - # - # truncate("<p>Once upon a time in a world far far away</p>".html_safe) # # => "<p>Once upon a time in a wo..." # # You can still use <tt>truncate</tt> with the old API that accepts the @@ -85,7 +82,6 @@ module ActionView options.reverse_merge!(:length => 30) - text = h(text) unless text.html_safe? || options[:safe] text.truncate(options.delete(:length), options) if text end @@ -117,13 +113,13 @@ module ActionView end options.reverse_merge!(:highlighter => '<strong class="highlight">\1</strong>') - text = h(text) unless text.html_safe? || options[:safe] + text = sanitize(text) unless options[:sanitize] == false if text.blank? || phrases.blank? text else match = Array(phrases).map { |p| Regexp.escape(p) }.join('|') text.gsub(/(#{match})(?!(?:[^<]*?)(?:["'])[^<>]*>)/i, options[:highlighter]) - end + end.html_safe end # Extracts an excerpt from +text+ that matches the first instance of +phrase+. @@ -253,9 +249,9 @@ module ActionView # simple_format("Look ma! A class!", :class => 'description') # # => "<p class='description'>Look ma! A class!</p>" def simple_format(text, html_options={}, options={}) - text = '' if text.nil? + text = ''.html_safe if text.nil? start_tag = tag('p', html_options, true) - text = h(text) unless text.html_safe? || options[:safe] + text = sanitize(text) unless options[:sanitize] == false text.gsub!(/\r\n?/, "\n") # \r\n and \r -> \n text.gsub!(/\n\n+/, "</p>\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1<br />') # 1 newline -> br @@ -499,7 +495,11 @@ module ActionView link_text = block_given?? yield(href) : href href = 'http://' + href unless scheme - content_tag(:a, link_text, link_attributes.merge('href' => href), !(options[:safe] || text.html_safe?)) + punctuation.reverse.join('') + unless options[:sanitize] == false + link_text = sanitize(link_text) + href = sanitize(href) + end + content_tag(:a, link_text, link_attributes.merge('href' => href), !!options[:sanitize]) + punctuation.reverse.join('') end end.html_safe end @@ -514,7 +514,11 @@ module ActionView text.html_safe else display_text = (block_given?) ? yield(text) : text - display_text = h(display_text) unless options[:safe] + + unless options[:sanitize] == false + text = sanitize(text) + display_text = sanitize(display_text) unless text == display_text + end mail_to text, display_text, html_options end end diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index a2c191c580..459aae94a2 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -318,7 +318,7 @@ module ActionView object.class.model_name.partial_path.dup.tap do |partial| path = @view.controller_path - partial.insert(0, "#{File.dirname(path)}/") if path.include?(?/) + partial.insert(0, "#{File.dirname(path)}/") if partial.include?(?/) && path.include?(?/) end end end diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index dc7f901f07..b698b4cfec 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -85,6 +85,7 @@ module ActionView def setup_with_controller @controller = ActionView::TestCase::TestController.new + @request = @controller.request @output_buffer = ActiveSupport::SafeBuffer.new @rendered = '' |