From 01f3f0dbae3e5ecfd18109a0f2ec37ed0d767a8e Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Wed, 16 Jun 2010 22:30:06 +0200 Subject: use RDoc 2.2 to generate the documentation of individual components --- actionpack/Rakefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/Rakefile b/actionpack/Rakefile index f3bd7dfc10..aed5278e38 100644 --- a/actionpack/Rakefile +++ b/actionpack/Rakefile @@ -1,4 +1,5 @@ -require 'rubygems' +gem 'rdoc', '= 2.2' +require 'rdoc' require 'rake' require 'rake/testtask' require 'rake/rdoctask' -- cgit v1.2.3 From a424808df95c5ebe66d02f0d743e741a1864baa7 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 17 Jun 2010 08:39:13 +0200 Subject: truncate() should not try to produce HTML-safe output As discussed in Lighthouse ticket #4825 and ticket #4878, the truncate() method cannot guarantee safe output for all possible inputs/offsets, so it is best to leave the output unsafe so that it gets escaped when used in a view. Signed-off-by: Santiago Pastorino Signed-off-by: David Heinemeier Hansson --- actionpack/lib/action_view/helpers/text_helper.rb | 12 ++++-------- actionpack/test/template/text_helper_test.rb | 18 ++++-------------- 2 files changed, 8 insertions(+), 22 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 3b37fd6b57..654f3c89f3 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -39,7 +39,10 @@ module ActionView # for a total length not exceeding :length. # # Pass a :separator to truncate +text+ at a natural break. - # Pass a :safe 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 raw(). Care should be taken if +text+ contains HTML tags + # or entities, because truncation may produce invalid HTML (such as unbalanced or incomplete tags). # # ==== Examples # @@ -56,12 +59,6 @@ module ActionView # # => "And they f... (continued)" # # truncate("

Once upon a time in a world far far away

") - # # => "<p>Once upon a time i..." - # - # truncate("

Once upon a time in a world far far away

", :safe => true) - # # => "

Once upon a time in a wo..." - # - # truncate("

Once upon a time in a world far far away

".html_safe) # # => "

Once upon a time in a wo..." # # You can still use truncate with the old API that accepts the @@ -84,7 +81,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 diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index b0a4c2a9cc..1cd6a5941d 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -52,8 +52,8 @@ class TextHelperTest < ActionView::TestCase assert_equal "

test with safe string

", simple_format(" test with safe string ".html_safe) end - def test_truncate_should_be_html_safe - assert truncate("Hello World!", :length => 12).html_safe? + def test_truncate_should_not_be_html_safe + assert !truncate("Hello World!", :length => 12).html_safe? end def test_truncate @@ -61,18 +61,8 @@ class TextHelperTest < ActionView::TestCase assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) end - def test_truncate_should_escape_unsafe_input - assert_equal "Hello <...", truncate("Hello World!!", :length => 12) - end - - def test_truncate_should_not_escape_input_if_safe_option - assert_equal "Hello code!World!", :length => 12, :safe => true) - assert_equal "Hello code!World!!", :length => 12, :safe => true) - end - - def test_truncate_should_not_escape_safe_input - assert_equal "Hello code!World!".html_safe, :length => 12) - assert_equal "Hello code!World!!".html_safe, :length => 12) + def test_truncate_should_not_escape_input + assert_equal "Hello code!World!!", :length => 12) end def test_truncate_should_use_default_length_of_30 -- cgit v1.2.3 From 7240a960f2e515316ac4d962482f678b67b83b8c Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 17 Jun 2010 07:37:33 -0300 Subject: excerpt shoudn't return safe output test added [#4878] Signed-off-by: David Heinemeier Hansson --- actionpack/test/template/text_helper_test.rb | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'actionpack') diff --git a/actionpack/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 1cd6a5941d..82c81ddf05 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -207,6 +207,10 @@ class TextHelperTest < ActionView::TestCase assert_nil excerpt("This is a beautiful morning", "day") end + def test_excerpt_should_not_be_html_safe + assert !excerpt('This is a beautiful! morning', 'beautiful', 5).html_safe? + end + def test_excerpt_in_borderline_cases assert_equal("", excerpt("", "", 0)) assert_equal("a", excerpt("a", "a", 0)) -- cgit v1.2.3 From 6db9558416cf2133adacbe1634f28c25bfe618d6 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Thu, 10 Jun 2010 18:31:28 +0100 Subject: Add shallow routing option to new router [#3765 status:committed] Signed-off-by: David Heinemeier Hansson --- actionpack/CHANGELOG | 10 + actionpack/lib/action_dispatch/routing/mapper.rb | 404 ++++++++++++++--------- actionpack/test/dispatch/routing_test.rb | 125 +++++++ 3 files changed, 383 insertions(+), 156 deletions(-) (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index 967bd76025..cee79c6c11 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,15 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* +* Add shallow routes back to the new router [Diego Carrion] + + resources :posts do + shallow do + resources :comments + end + end + + You can now use comment_path for /comments/1 instead of post_comment_path for /posts/1/comments/1. + * Add support for multi-subdomain session by setting cookie host in session cookie so you can share session between www.example.com, example.com and user.example.com. #4818 [Guillermo Álvarez] * Removed textilize, textilize_without_paragraph and markdown helpers. [Santiago Pastorino] diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 7b79b6bde3..46304b0336 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] def initialize(set, scope, args) @set, @scope = set, scope @@ -409,11 +409,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 +444,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 +456,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 - end - - def uncountable? - singular == plural - end - - def name_for_action(action) - case action_type(action) - when :collection - collection_name - when :member - member_name - end + singular == plural ? "#{plural}_index" : plural end - def id_segment - ":#{singular}_id" + def shallow? + options[:shallow] ? true : false end def constraints @@ -506,21 +484,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 +533,27 @@ module ActionDispatch super end - def action_type(action) - case action - when :show, :create, :update, :destroy - :member - end + def member_name + name end - 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 +565,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 +593,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 +624,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 +634,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,9 +643,9 @@ 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 + scope(*parent_resource.new_scope) do yield end end @@ -673,8 +657,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[:module].blank? + scope(*parent_resource.nested_scope) { yield } + else + scope(@scope[:module], :name_prefix => @scope[:module].tr('/', '_')) do + scope(*parent_resource.nested_scope) { yield } + end + end + end + else + scope(*parent_resource.nested_scope) { yield } end end end @@ -687,63 +681,69 @@ 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 match(path, options) + 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(:collection) do + scope(parent_resource.path, :name_prefix => parent_resource.collection_name) do + super(options) + end + end + else + super(options) + end end protected @@ -752,15 +752,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 +775,18 @@ 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 with_exclusive_scope begin - old_name_prefix = @scope[:name_prefix] - - if !old_name_prefix.blank? - @scope[:name_prefix] = "#{prefix}_#{@scope[:name_prefix]}" - else - @scope[:name_prefix] = prefix.to_s - end + old_name_prefix, old_path = @scope[:name_prefix], @scope[:path] + @scope[:name_prefix], @scope[:path] = nil, nil yield ensure - @scope[:name_prefix] = old_name_prefix + @scope[:name_prefix], @scope[:path] = old_name_prefix, old_path end end @@ -816,6 +798,116 @@ 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[:module]}/#{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[:module]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)" + else + "#{@scope[:path]}/#{action_path(:edit)}(.:format)" + end + else + case @scope[:scope_level] + when :collection + "#{@scope[:path]}/#{action_path(action)}(.:format)" + when :new + "#{@scope[:path]}/#{action_path(:new)}/#{action_path(action)}(.:format)" + else + if parent_resource.shallow? + "#{@scope[:module]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" + else + "#{@scope[:path]}/#{action_path(action)}(.:format)" + end + 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[:module].blank? ? "" : "#{@scope[:module].tr('/', '_')}_" + + case action + when :index + "#{name_prefix}#{parent_resource.collection_name}" + when :show + 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}" + when :update, :create, :destroy + nil + 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/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index e13960e0dc..e294703e72 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -142,6 +142,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resources :comments, :except => :destroy end + shallow do + namespace :api do + resources :teams do + resources :players + resource :captain + end + end + end + + resources :threads, :shallow => true do + resource :owner + resources :messages do + resources :comments do + member do + post :preview + end + end + end + end + resources :sheep resources :clients do @@ -1132,6 +1152,111 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_shallow_nested_resources + with_test_routes do + + get '/api/teams' + assert_equal 'api/teams#index', @response.body + assert_equal '/api/teams', api_teams_path + + get '/api/teams/new' + assert_equal 'api/teams#new', @response.body + assert_equal '/api/teams/new', new_api_team_path + + get '/api/teams/1' + assert_equal 'api/teams#show', @response.body + assert_equal '/api/teams/1', api_team_path(:id => '1') + + get '/api/teams/1/edit' + assert_equal 'api/teams#edit', @response.body + assert_equal '/api/teams/1/edit', edit_api_team_path(:id => '1') + + get '/api/teams/1/players' + assert_equal 'api/players#index', @response.body + assert_equal '/api/teams/1/players', api_team_players_path(:team_id => '1') + + get '/api/teams/1/players/new' + assert_equal 'api/players#new', @response.body + assert_equal '/api/teams/1/players/new', new_api_team_player_path(:team_id => '1') + + get '/api/players/2' + assert_equal 'api/players#show', @response.body + assert_equal '/api/players/2', api_player_path(:id => '2') + + get '/api/players/2/edit' + assert_equal 'api/players#edit', @response.body + assert_equal '/api/players/2/edit', edit_api_player_path(:id => '2') + + get '/api/teams/1/captain' + assert_equal 'api/captains#show', @response.body + assert_equal '/api/teams/1/captain', api_team_captain_path(:team_id => '1') + + get '/api/teams/1/captain/new' + assert_equal 'api/captains#new', @response.body + assert_equal '/api/teams/1/captain/new', new_api_team_captain_path(:team_id => '1') + + get '/api/teams/1/captain/edit' + assert_equal 'api/captains#edit', @response.body + assert_equal '/api/teams/1/captain/edit', edit_api_team_captain_path(:team_id => '1') + + get '/threads' + assert_equal 'threads#index', @response.body + assert_equal '/threads', threads_path + + get '/threads/new' + assert_equal 'threads#new', @response.body + assert_equal '/threads/new', new_thread_path + + get '/threads/1' + assert_equal 'threads#show', @response.body + assert_equal '/threads/1', thread_path(:id => '1') + + get '/threads/1/edit' + assert_equal 'threads#edit', @response.body + assert_equal '/threads/1/edit', edit_thread_path(:id => '1') + + get '/threads/1/owner' + assert_equal 'owners#show', @response.body + assert_equal '/threads/1/owner', thread_owner_path(:thread_id => '1') + + get '/threads/1/messages' + assert_equal 'messages#index', @response.body + assert_equal '/threads/1/messages', thread_messages_path(:thread_id => '1') + + get '/threads/1/messages/new' + assert_equal 'messages#new', @response.body + assert_equal '/threads/1/messages/new', new_thread_message_path(:thread_id => '1') + + get '/messages/2' + assert_equal 'messages#show', @response.body + assert_equal '/messages/2', message_path(:id => '2') + + get '/messages/2/edit' + assert_equal 'messages#edit', @response.body + assert_equal '/messages/2/edit', edit_message_path(:id => '2') + + get '/messages/2/comments' + assert_equal 'comments#index', @response.body + assert_equal '/messages/2/comments', message_comments_path(:message_id => '2') + + get '/messages/2/comments/new' + assert_equal 'comments#new', @response.body + assert_equal '/messages/2/comments/new', new_message_comment_path(:message_id => '2') + + get '/comments/3' + assert_equal 'comments#show', @response.body + assert_equal '/comments/3', comment_path(:id => '3') + + get '/comments/3/edit' + assert_equal 'comments#edit', @response.body + assert_equal '/comments/3/edit', edit_comment_path(:id => '3') + + post '/comments/3/preview' + assert_equal 'comments#preview', @response.body + assert_equal '/comments/3/preview', preview_comment_path(:id => '3') + end + end + private def with_test_routes yield -- cgit v1.2.3 From 84d387bc0f3f3f6641b08d0ce40e924f09105c19 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Thu, 17 Jun 2010 12:56:15 -0300 Subject: Make text_helpers methods which return valid html to return it as safe and sanitize the input always unless :sanitize => false is set [#4825 state:committed] Signed-off-by: David Heinemeier Hansson --- actionpack/lib/action_view/helpers/text_helper.rb | 20 +++++-- actionpack/test/template/text_helper_test.rb | 68 ++++++++++------------- 2 files changed, 42 insertions(+), 46 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/text_helper.rb b/actionpack/lib/action_view/helpers/text_helper.rb index 654f3c89f3..c7f96597b9 100644 --- a/actionpack/lib/action_view/helpers/text_helper.rb +++ b/actionpack/lib/action_view/helpers/text_helper.rb @@ -112,13 +112,13 @@ module ActionView end options.reverse_merge!(:highlighter => '\1') - 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+. @@ -248,9 +248,9 @@ module ActionView # simple_format("Look ma! A class!", :class => 'description') # # => "

Look ma! A class!

" 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+/, "

\n\n#{start_tag}") # 2+ newline -> paragraph text.gsub!(/([^\n]\n)(?=[^\n])/, '\1
') # 1 newline -> br @@ -494,7 +494,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 @@ -509,7 +513,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/test/template/text_helper_test.rb b/actionpack/test/template/text_helper_test.rb index 82c81ddf05..d173c5be0a 100644 --- a/actionpack/test/template/text_helper_test.rb +++ b/actionpack/test/template/text_helper_test.rb @@ -19,6 +19,10 @@ class TextHelperTest < ActionView::TestCase assert_equal 'foobar', output_buffer end + def test_simple_format_should_be_html_safe + assert simple_format(" test with html tags ").html_safe? + end + def test_simple_format assert_equal "

", simple_format(nil) @@ -36,26 +40,18 @@ class TextHelperTest < ActionView::TestCase assert_equal %Q(

para 1

\n\n

para 2

), simple_format("para 1\n\npara 2", :class => 'test') end - def test_simple_format_should_be_html_safe - assert simple_format(" test with html tags ").html_safe? + def test_simple_format_should_sanitize_input_when_sanitize_option_is_not_false + assert_equal "

test with unsafe string

", simple_format(" test with unsafe string ") end - def test_simple_format_should_escape_unsafe_input - assert_equal "

<b> test with unsafe string </b><script>code!</script>

", simple_format(" test with unsafe string ") - end - - def test_simple_format_should_not_escape_input_if_safe_option - assert_equal "

test with unsafe string

", simple_format(" test with unsafe string ", {}, :safe => true) - end - - def test_simple_format_should_not_escape_safe_input - assert_equal "

test with safe string

", simple_format(" test with safe string ".html_safe) + def test_simple_format_should_not_sanitize_input_when_sanitize_option_is_false + assert_equal "

test with unsafe string

", simple_format(" test with unsafe string ", {}, :sanitize => false) end def test_truncate_should_not_be_html_safe assert !truncate("Hello World!", :length => 12).html_safe? end - + def test_truncate assert_equal "Hello World!", truncate("Hello World!", :length => 12) assert_equal "Hello Wor...", truncate("Hello World!!", :length => 12) @@ -128,24 +124,17 @@ class TextHelperTest < ActionView::TestCase assert_equal ' ', highlight(' ', 'blank text is returned verbatim') end - def test_highlight_should_escape_unsafe_input + def test_highlight_should_sanitize_input assert_equal( - "This is a beautiful morning<script>code!</script>", + "This is a beautiful morning", highlight("This is a beautiful morning", "beautiful") ) end - def test_highlight_should_not_escape_input_if_safe_option + def test_highlight_should_not_sanitize_if_sanitize_option_if_false assert_equal( "This is a beautiful morning", - highlight("This is a beautiful morning", "beautiful", :safe => true) - ) - end - - def test_highlight_should_not_escape_safe_input - assert_equal( - "This is a beautiful morning", - highlight("This is a beautiful morning".html_safe, "beautiful") + highlight("This is a beautiful morning", "beautiful", :sanitize => false) ) end @@ -179,23 +168,23 @@ class TextHelperTest < ActionView::TestCase def test_highlight_with_html assert_equal( - "<p>This is a beautiful morning, but also a beautiful day</p>", + "

This is a beautiful morning, but also a beautiful day

", highlight("

This is a beautiful morning, but also a beautiful day

", "beautiful") ) assert_equal( - "<p>This is a <em>beautiful</em> morning, but also a beautiful day</p>", + "

This is a beautiful morning, but also a beautiful day

", highlight("

This is a beautiful morning, but also a beautiful day

", "beautiful") ) assert_equal( - "<p>This is a <em class="error">beautiful</em> morning, but also a beautiful <span class="last">day</span></p>", + "

This is a beautiful morning, but also a beautiful day

", highlight("

This is a beautiful morning, but also a beautiful day

", "beautiful") ) assert_equal( - "<p class="beautiful">This is a beautiful morning, but also a beautiful day</p>", + "

This is a beautiful morning, but also a beautiful day

", highlight("

This is a beautiful morning, but also a beautiful day

", "beautiful") ) assert_equal( - "<p>This is a beautiful <a href="http://example.com/beautiful#top?what=beautiful%20morning&when=now+then">morning</a>, but also a beautiful day</p>", + "

This is a beautiful morning, but also a beautiful day

", highlight("

This is a beautiful morning, but also a beautiful day

", "beautiful") ) end @@ -317,9 +306,13 @@ class TextHelperTest < ActionView::TestCase end end - def generate_result(link_text, href = nil) + def generate_result(link_text, href = nil, escape = false) href ||= link_text - %{#{CGI::escapeHTML link_text}} + if escape + %{#{CGI::escapeHTML link_text}} + else + %{#{link_text}} + end end def test_auto_link_should_be_html_safe @@ -424,19 +417,14 @@ class TextHelperTest < ActionView::TestCase assert_equal %(

#{link10_result} Link

), auto_link("

#{link10_raw} Link

") end - def test_auto_link_should_sanitize_unsafe_input - link_raw = %{http://www.rubyonrails.com?id=1&num=2} - assert_equal %{http://www.rubyonrails.com?id=1&num=2}, auto_link(link_raw) - end - - def test_auto_link_should_sanitize_unsafe_input + def test_auto_link_should_sanitize_input_when_sanitize_option_is_not_false link_raw = %{http://www.rubyonrails.com?id=1&num=2} - assert_equal %{http://www.rubyonrails.com?id=1&num=2}, auto_link(link_raw, :safe => true) + assert_equal %{http://www.rubyonrails.com?id=1&num=2}, auto_link(link_raw) end - def test_auto_link_should_not_sanitize_safe_input + def test_auto_link_should_not_sanitize_input_when_sanitize_option_is_false link_raw = %{http://www.rubyonrails.com?id=1&num=2} - assert_equal %{http://www.rubyonrails.com?id=1&num=2}, auto_link(link_raw.html_safe) + assert_equal %{http://www.rubyonrails.com?id=1&num=2}, auto_link(link_raw, :sanitize => false) end def test_auto_link_other_protocols -- cgit v1.2.3 From a55d83292f9dbc34368e3cb91d99eb5b4aa4fa78 Mon Sep 17 00:00:00 2001 From: Jeremy Kemper Date: Thu, 17 Jun 2010 22:18:09 -0700 Subject: Credit for the heavy lifting! --- actionpack/CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index cee79c6c11..a00f63dcd4 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,6 +1,6 @@ *Rails 3.0.0 [Release Candidate] (unreleased)* -* Add shallow routes back to the new router [Diego Carrion] +* Add shallow routes back to the new router [Diego Carrion, Andrew White] resources :posts do shallow do -- cgit v1.2.3 From 9d3eeb905341aaad942ceb0e47bd04cced34d031 Mon Sep 17 00:00:00 2001 From: Josh Kalderimis Date: Fri, 18 Jun 2010 18:25:07 +0200 Subject: fix for :shallow in router not generating helpers for create, update, and destroy actions when :only or :except are used [#4900 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_dispatch/routing/mapper.rb | 7 ++--- actionpack/test/dispatch/routing_test.rb | 40 +++++++++++++++++++++++- 2 files changed, 42 insertions(+), 5 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 46304b0336..95e56566a3 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -536,6 +536,7 @@ module ActionDispatch def member_name name end + alias_method :collection_name, :member_name def nested_path path @@ -874,9 +875,9 @@ module ActionDispatch shallow_prefix = @scope[:module].blank? ? "" : "#{@scope[:module].tr('/', '_')}_" case action - when :index + when :index, :create "#{name_prefix}#{parent_resource.collection_name}" - when :show + when :show, :update, :destroy if parent_resource.shallow? "#{shallow_prefix}#{parent_resource.member_name}" else @@ -890,8 +891,6 @@ module ActionDispatch end when :new "new_#{name_prefix}#{parent_resource.member_name}" - when :update, :create, :destroy - nil else case @scope[:scope_level] when :collection diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index e294703e72..0b3bbcc86b 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -142,6 +142,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resources :comments, :except => :destroy end + resource :past, :only => :destroy + resource :present, :only => :update + resource :future, :only => :create + resources :relationships, :only => [:create, :destroy] + resources :friendships, :only => [:update] + shallow do namespace :api do resources :teams do @@ -729,6 +735,38 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_resource_routes_only_create_update_destroy + with_test_routes do + delete '/past' + assert_equal 'pasts#destroy', @response.body + assert_equal '/past', past_path + + put '/present' + assert_equal 'presents#update', @response.body + assert_equal '/present', present_path + + post '/future' + assert_equal 'futures#create', @response.body + assert_equal '/future', future_path + end + end + + def test_resources_routes_only_create_update_destroy + with_test_routes do + post '/relationships' + assert_equal 'relationships#create', @response.body + assert_equal '/relationships', relationships_path + + delete '/relationships/1' + assert_equal 'relationships#destroy', @response.body + assert_equal '/relationships/1', relationship_path(1) + + put '/friendships/1' + assert_equal 'friendships#update', @response.body + assert_equal '/friendships/1', friendship_path(1) + end + end + def test_resource_with_slugs_in_ids with_test_routes do get '/posts/rails-rocks' @@ -843,7 +881,7 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest assert_equal '/account/admin/subscription', account_admin_subscription_path end end - + def test_namespace_nested_in_resources with_test_routes do get '/clients/1/google/account' -- cgit v1.2.3 From a186431414de8a0f0db9f60254f421a3536cee12 Mon Sep 17 00:00:00 2001 From: David Genord II Date: Fri, 18 Jun 2010 15:40:20 -0400 Subject: form_for without :html and with :remote should not error [#4902 state:committed] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_view/helpers/form_helper.rb | 2 +- actionpack/test/template/form_helper_test.rb | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index b3db3151d3..a49daab98b 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -302,7 +302,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/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 2f3869994c..8de1e782c0 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -644,6 +644,26 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end + def test_form_for_with_remote_without_html + assert_deprecated do + form_for(:post, @post, :remote => true) do |f| + concat f.text_field(:title) + concat f.text_area(:body) + concat f.check_box(:secret) + end + end + + expected = + "
" + + "" + + "" + + "" + + "" + + "
" + + assert_dom_equal expected, output_buffer + end + def test_form_for_without_object form_for(:post, :html => { :id => 'create-post' }) do |f| concat f.text_field(:title) -- cgit v1.2.3 From bf59717b43fc7c186f9acebac27ba9ac7d59dd93 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 19 Jun 2010 07:23:57 +0100 Subject: Custom resource routes should be scoped [#3765] Signed-off-by: Jeremy Kemper --- actionpack/lib/action_dispatch/routing/mapper.rb | 43 ++++++++++++++++++---- actionpack/test/dispatch/routing_test.rb | 47 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 95e56566a3..0018b6485b 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -647,7 +647,9 @@ module ActionDispatch with_scope_level(:new) do scope(*parent_resource.new_scope) do - yield + scope(action_path(:new)) do + yield + end end end end @@ -723,7 +725,17 @@ module ActionDispatch options = options_for_action(args.first, options) with_exclusive_scope do - return match(path, options) + 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 @@ -737,7 +749,7 @@ module ActionDispatch def root(options={}) if @scope[:scope_level] == :resources - with_scope_level(:collection) do + with_scope_level(:nested) do scope(parent_resource.path, :name_prefix => parent_resource.collection_name) do super(options) end @@ -780,12 +792,18 @@ module ActionDispatch [:resource, :resources].include?(@scope[:scope_level]) end + def resource_method_scope? + [:collection, :member, :new].include?(@scope[:scope_level]) + end + def with_exclusive_scope begin old_name_prefix, old_path = @scope[:name_prefix], @scope[:path] @scope[:name_prefix], @scope[:path] = nil, nil - yield + with_scope_level(:exclusive) do + yield + end ensure @scope[:name_prefix], @scope[:path] = old_name_prefix, old_path end @@ -844,10 +862,8 @@ module ActionDispatch end else case @scope[:scope_level] - when :collection + when :collection, :new "#{@scope[:path]}/#{action_path(action)}(.:format)" - when :new - "#{@scope[:path]}/#{action_path(:new)}/#{action_path(action)}(.:format)" else if parent_resource.shallow? "#{@scope[:module]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" @@ -858,6 +874,19 @@ module ActionDispatch end end + def path_for_custom_action + case @scope[:scope_level] + when :collection, :new + @scope[:path] + else + if parent_resource.shallow? + "#{@scope[:module]}/#{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 diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 0b3bbcc86b..899990c69d 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -180,6 +180,33 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + resources :customers do + get "recent" => "customers#recent", :as => :recent, :on => :collection + get "profile" => "customers#profile", :as => :profile, :on => :member + post "preview" => "customers#preview", :as => :preview, :on => :new + resource :avatar do + get "thumbnail(.:format)" => "avatars#thumbnail", :as => :thumbnail, :on => :member + end + resources :invoices do + get "outstanding" => "invoices#outstanding", :as => :outstanding, :on => :collection + get "overdue", :to => :overdue, :on => :collection + get "print" => "invoices#print", :as => :print, :on => :member + post "preview" => "invoices#preview", :as => :preview, :on => :new + end + resources :notes, :shallow => true do + get "preview" => "notes#preview", :as => :preview, :on => :new + get "print" => "notes#print", :as => :print, :on => :member + end + end + + namespace :api do + resources :customers do + get "recent" => "customers#recent", :as => :recent, :on => :collection + get "profile" => "customers#profile", :as => :profile, :on => :member + post "preview" => "customers#preview", :as => :preview, :on => :new + end + end + match 'sprockets.js' => ::TestRoutingMapper::SprocketsApp match 'people/:id/update', :to => 'people#update', :as => :update_person @@ -1295,6 +1322,26 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_custom_resource_routes_are_scoped + with_test_routes do + assert_equal '/customers/recent', recent_customers_path + assert_equal '/customers/1/profile', profile_customer_path(:id => '1') + assert_equal '/customers/new/preview', preview_new_customer_path + assert_equal '/customers/1/avatar/thumbnail.jpg', thumbnail_customer_avatar_path(:customer_id => '1', :format => :jpg) + assert_equal '/customers/1/invoices/outstanding', outstanding_customer_invoices_path(:customer_id => '1') + assert_equal '/customers/1/invoices/2/print', print_customer_invoice_path(:customer_id => '1', :id => '2') + assert_equal '/customers/1/invoices/new/preview', preview_new_customer_invoice_path(:customer_id => '1') + assert_equal '/customers/1/notes/new/preview', preview_new_customer_note_path(:customer_id => '1') + assert_equal '/notes/1/print', print_note_path(:id => '1') + assert_equal '/api/customers/recent', recent_api_customers_path + assert_equal '/api/customers/1/profile', profile_api_customer_path(:id => '1') + assert_equal '/api/customers/new/preview', preview_new_api_customer_path + + get '/customers/1/invoices/overdue' + assert_equal 'invoices#overdue', @response.body + end + end + private def with_test_routes yield -- cgit v1.2.3 From 033e0a041f10ef4d4aa8ebb576560df20b971026 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 19 Jun 2010 17:15:21 +0200 Subject: ActiveRecord and ActionPack now use the new descendants implementation. --- actionpack/lib/abstract_controller/base.rb | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/abstract_controller/base.rb b/actionpack/lib/abstract_controller/base.rb index e1027840ef..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 descendents 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 -- cgit v1.2.3 From f368b21cb6e45268359c0f0b8beda175e3b40eae Mon Sep 17 00:00:00 2001 From: wycats Date: Sat, 19 Jun 2010 08:19:00 -0700 Subject: Remove a relic of #request being in RackDelegation --- actionpack/lib/action_controller/metal.rb | 11 +++++++++-- actionpack/lib/action_controller/metal/rack_delegation.rb | 4 ---- 2 files changed, 9 insertions(+), 6 deletions(-) (limited to 'actionpack') 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 -- cgit v1.2.3 From 728b9eccad99d22028577e8b06433e8344b15d01 Mon Sep 17 00:00:00 2001 From: Wincent Colaiuta Date: Thu, 17 Jun 2010 09:17:31 +0200 Subject: option_groups_from_collection_for_select should return HTML-safe string [#4879 state:resolved] Signed-off-by: Xavier Noria --- .../lib/action_view/helpers/form_options_helper.rb | 2 +- .../test/template/form_options_helper_test.rb | 30 +++++++++------------- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/helpers/form_options_helper.rb b/actionpack/lib/action_view/helpers/form_options_helper.rb index fe71d2cdf7..e48580e0ad 100644 --- a/actionpack/lib/action_view/helpers/form_options_helper.rb +++ b/actionpack/lib/action_view/helpers/form_options_helper.rb @@ -398,7 +398,7 @@ module ActionView options_for_select += "" options_for_select += options_from_collection_for_select(eval("group.#{group_method}"), option_key_method, option_value_method, selected_key) options_for_select += '' - end + end.html_safe end # Returns a string of tags, like options_for_select, but diff --git a/actionpack/test/template/form_options_helper_test.rb b/actionpack/test/template/form_options_helper_test.rb index 19b73aa810..65b5f5ccc1 100644 --- a/actionpack/test/template/form_options_helper_test.rb +++ b/actionpack/test/template/form_options_helper_test.rb @@ -177,17 +177,16 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_option_groups_from_collection_for_select - @continents = [ - Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), - Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) - ] - assert_dom_equal( "\n\n", - option_groups_from_collection_for_select(@continents, "countries", "continent_name", "country_id", "country_name", "dk") + option_groups_from_collection_for_select(dummy_continents, "countries", "continent_name", "country_id", "country_name", "dk") ) end + def test_option_groups_from_collection_for_select_returns_html_safe_string + assert option_groups_from_collection_for_select(dummy_continents, "countries", "continent_name", "country_id", "country_name", "dk").html_safe? + end + def test_grouped_options_for_select_with_array assert_dom_equal( "\n\n", @@ -824,31 +823,21 @@ class FormOptionsHelperTest < ActionView::TestCase end def test_grouped_collection_select - @continents = [ - Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), - Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) - ] - @post = Post.new @post.origin = 'dk' assert_dom_equal( %Q{}, - grouped_collection_select("post", "origin", @continents, :countries, :continent_name, :country_id, :country_name) + grouped_collection_select("post", "origin", dummy_continents, :countries, :continent_name, :country_id, :country_name) ) end def test_grouped_collection_select_under_fields_for - @continents = [ - Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), - Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) - ] - @post = Post.new @post.origin = 'dk' output_buffer = fields_for :post, @post do |f| - concat f.grouped_collection_select("origin", @continents, :countries, :continent_name, :country_id, :country_name) + concat f.grouped_collection_select("origin", dummy_continents, :countries, :continent_name, :country_id, :country_name) end assert_dom_equal( @@ -864,4 +853,9 @@ class FormOptionsHelperTest < ActionView::TestCase Post.new("Babe went home", "Babe", "To a little house", "shh!"), Post.new("Cabe went home", "Cabe", "To a little house", "shh!") ] end + + def dummy_continents + [ Continent.new("", [Country.new("", ""), Country.new("so", "Somalia")] ), + Continent.new("Europe", [Country.new("dk", "Denmark"), Country.new("ie", "Ireland")] ) ] + end end -- cgit v1.2.3 From 312f43324159fbcd8749cd331ed7d6500a714a83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Sat, 19 Jun 2010 17:51:29 +0200 Subject: Clear DescendantsTracker on each request. --- actionpack/lib/action_dispatch/middleware/callbacks.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'actionpack') 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 -- cgit v1.2.3 From 65ce3d12971afd15de6ea22a2fc5af3ba1faf124 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 19 Jun 2010 20:52:55 +0100 Subject: Accept an object for :constraints option [#4904 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 2 +- actionpack/test/dispatch/routing_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 0018b6485b..5d6147c48a 100644 --- a/actionpack/lib/action_dispatch/routing/mapper.rb +++ b/actionpack/lib/action_dispatch/routing/mapper.rb @@ -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 diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index 899990c69d..c4cdc4b2a8 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -68,6 +68,8 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get 'admin/accounts' => "queenbee#accounts" end + get 'admin/passwords' => "queenbee#passwords", :constraints => ::TestRoutingMapper::IpRestrictor + scope 'pt', :name_prefix => 'pt' do resources :projects, :path_names => { :edit => 'editar', :new => 'novo' }, :path => 'projetos' do post :preview, :on => :new @@ -501,6 +503,12 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest get '/admin/accounts', {}, {'REMOTE_ADDR' => '10.0.0.100'} assert_equal 'pass', @response.headers['X-Cascade'] + + get '/admin/passwords', {}, {'REMOTE_ADDR' => '192.168.1.100'} + assert_equal 'queenbee#passwords', @response.body + + get '/admin/passwords', {}, {'REMOTE_ADDR' => '10.0.0.100'} + assert_equal 'pass', @response.headers['X-Cascade'] end end -- cgit v1.2.3 From ed3f042e99949526f483d1f567e40031deea33d3 Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 19 Jun 2010 22:35:54 +0100 Subject: Make polymorphic_url and scaffolding work with uncountable resources [#3930 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- .../lib/action_controller/polymorphic_routes.rb | 3 ++- .../lib/action_controller/record_identifier.rb | 14 ++++++++++--- .../test/activerecord/polymorphic_routes_test.rb | 1 + .../test/controller/record_identifier_test.rb | 23 ++++++++++++++++++++-- 4 files changed, 35 insertions(+), 6 deletions(-) (limited to 'actionpack') 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/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb index 9f5e8ec657..6e1e6cdd20 100644 --- a/actionpack/test/activerecord/polymorphic_routes_test.rb +++ b/actionpack/test/activerecord/polymorphic_routes_test.rb @@ -381,6 +381,7 @@ class PolymorphicRoutesTest < ActionController::TestCase with_test_routes do @series.save assert_equal "http://example.com/series/#{@series.id}", polymorphic_url(@series) + assert_equal "http://example.com/series", polymorphic_url(Series.new) end end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 813dedc80d..6a84475758 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -13,6 +13,19 @@ class Comment end end +class Sheep + extend ActiveModel::Naming + include ActiveModel::Conversion + + attr_reader :id + def to_key; id ? [id] : nil end + def save; @id = 1 end + def new_record?; @id.nil? end + def name + @id.nil? ? 'new sheep' : "sheep ##{@id}" + end +end + class Comment::Nested < Comment; end class Test::Unit::TestCase @@ -20,7 +33,7 @@ class Test::Unit::TestCase def comments_url 'http://www.example.com/comments' end - + def comment_url(comment) "http://www.example.com/comments/#{comment.id}" end @@ -35,6 +48,7 @@ class RecordIdentifierTest < Test::Unit::TestCase @record = @klass.new @singular = 'comment' @plural = 'comments' + @uncountable = Sheep end def test_dom_id_with_new_record @@ -58,7 +72,7 @@ class RecordIdentifierTest < Test::Unit::TestCase def test_dom_class assert_equal @singular, dom_class(@record) end - + def test_dom_class_with_prefix assert_equal "custom_prefix_#{@singular}", dom_class(@record, :custom_prefix) end @@ -79,6 +93,11 @@ class RecordIdentifierTest < Test::Unit::TestCase assert_equal @plural, plural_class_name(@klass) end + def test_uncountable + assert_equal true, uncountable?(@uncountable) + assert_equal false, uncountable?(@klass) + end + private def method_missing(method, *args) RecordIdentifier.send(method, *args) -- cgit v1.2.3 From 1f84061c5c271647dc5f7f1311e365e134130e0f Mon Sep 17 00:00:00 2001 From: Andrew White Date: Sat, 19 Jun 2010 12:53:09 +0100 Subject: Don't use module to work out shallow name prefix and path as it may not accurately reflect the actual namespace [#4899 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/routing/mapper.rb | 26 ++++++++++++++++-------- actionpack/test/dispatch/routing_test.rb | 17 +++++++++++++++- 2 files changed, 33 insertions(+), 10 deletions(-) (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/routing/mapper.rb b/actionpack/lib/action_dispatch/routing/mapper.rb index 5d6147c48a..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, :shallow] + 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 @@ -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 @@ -662,10 +670,10 @@ module ActionDispatch with_scope_level(:nested) do if parent_resource.shallow? with_exclusive_scope do - if @scope[:module].blank? + if @scope[:shallow_path].blank? scope(*parent_resource.nested_scope) { yield } else - scope(@scope[:module], :name_prefix => @scope[:module].tr('/', '_')) do + scope(@scope[:shallow_path], :name_prefix => @scope[:shallow_prefix]) do scope(*parent_resource.nested_scope) { yield } end end @@ -848,7 +856,7 @@ module ActionDispatch "#{@scope[:path]}(.:format)" when :show, :update, :destroy if parent_resource.shallow? - "#{@scope[:module]}/#{parent_resource.path}/:id(.:format)" + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id(.:format)" else "#{@scope[:path]}(.:format)" end @@ -856,7 +864,7 @@ module ActionDispatch "#{@scope[:path]}/#{action_path(:new)}(.:format)" when :edit if parent_resource.shallow? - "#{@scope[:module]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)" + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(:edit)}(.:format)" else "#{@scope[:path]}/#{action_path(:edit)}(.:format)" end @@ -866,7 +874,7 @@ module ActionDispatch "#{@scope[:path]}/#{action_path(action)}(.:format)" else if parent_resource.shallow? - "#{@scope[:module]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id/#{action_path(action)}(.:format)" else "#{@scope[:path]}/#{action_path(action)}(.:format)" end @@ -880,7 +888,7 @@ module ActionDispatch @scope[:path] else if parent_resource.shallow? - "#{@scope[:module]}/#{parent_resource.path}/:id" + "#{@scope[:shallow_path]}/#{parent_resource.path}/:id" else @scope[:path] end @@ -901,7 +909,7 @@ module ActionDispatch def name_for_action(action) name_prefix = @scope[:name_prefix].blank? ? "" : "#{@scope[:name_prefix]}_" - shallow_prefix = @scope[:module].blank? ? "" : "#{@scope[:module].tr('/', '_')}_" + shallow_prefix = @scope[:shallow_prefix].blank? ? "" : "#{@scope[:shallow_prefix]}_" case action when :index, :create diff --git a/actionpack/test/dispatch/routing_test.rb b/actionpack/test/dispatch/routing_test.rb index c4cdc4b2a8..495255c22b 100644 --- a/actionpack/test/dispatch/routing_test.rb +++ b/actionpack/test/dispatch/routing_test.rb @@ -278,8 +278,11 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest resource :dashboard, :constraints => { :ip => /192\.168\.1\.\d{1,3}/ } - scope :module => 'api' do + scope :module => :api do resource :token + resources :errors, :shallow => true do + resources :notices + end end scope :path => 'api' do @@ -1350,6 +1353,18 @@ class TestRoutingMapper < ActionDispatch::IntegrationTest end end + def test_shallow_nested_routes_ignore_module + with_test_routes do + get '/errors/1/notices' + assert_equal 'api/notices#index', @response.body + assert_equal '/errors/1/notices', error_notices_path(:error_id => '1') + + get '/notices/1' + assert_equal 'api/notices#show', @response.body + assert_equal '/notices/1', notice_path(:id => '1') + end + end + private def with_test_routes yield -- cgit v1.2.3 From 86002a0dfe45c972a2498e33a6a2a882804603bd Mon Sep 17 00:00:00 2001 From: David Chelimsky Date: Fri, 18 Jun 2010 22:20:10 -0500 Subject: Support render_template in view tests. Useful for specifying which partials are rendered under different conditions. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [#4903 state:resolved] Signed-off-by: José Valim --- actionpack/lib/action_view/test_case.rb | 1 + actionpack/test/template/test_case_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+) (limited to 'actionpack') diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 15d424be74..306f59f9ca 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -84,6 +84,7 @@ module ActionView def setup_with_controller @controller = ActionView::TestCase::TestController.new + @request = @controller.request @output_buffer = ActiveSupport::SafeBuffer.new @rendered = '' diff --git a/actionpack/test/template/test_case_test.rb b/actionpack/test/template/test_case_test.rb index 9b50ea8a42..c365aec841 100644 --- a/actionpack/test/template/test_case_test.rb +++ b/actionpack/test/template/test_case_test.rb @@ -218,4 +218,12 @@ module ActionView end end end + + class RenderTemplateTest < ActionView::TestCase + test "render template" do + controller.controller_path = "test" + render(:template => "test/calling_partial_with_layout") + assert_template "partial_for_use_in_layout" + end + end end -- cgit v1.2.3 From 95a8f252c028c94b70cce4888bce42b7e9e30786 Mon Sep 17 00:00:00 2001 From: rohit Date: Wed, 16 Jun 2010 18:50:50 +0530 Subject: remove executable permission from files that don't need it. [#4802 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/http/request.rb | 0 1 file changed, 0 insertions(+), 0 deletions(-) mode change 100755 => 100644 actionpack/lib/action_dispatch/http/request.rb (limited to 'actionpack') diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb old mode 100755 new mode 100644 -- cgit v1.2.3 From bb6cd6d3ec02996c7dd11a4bb96381da18b7de8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tarsoly=20Andr=C3=A1s?= Date: Sat, 29 May 2010 22:09:14 +0200 Subject: Use Rack::Utils.bytesize when calculating content-length of exception pages. [#4727 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'actionpack') 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 -- cgit v1.2.3 From 96f5e59a26329aa972b8061a818410b0b378f306 Mon Sep 17 00:00:00 2001 From: Jan De Poorter Date: Mon, 7 Jun 2010 15:03:07 +0200 Subject: Make sure a namespaced <%= render form %> still renders the _form partial [#4784 state:resolved] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: José Valim --- actionpack/lib/action_view/render/partials.rb | 2 +- actionpack/test/controller/render_test.rb | 11 +++++++++++ actionpack/test/fixtures/fun/games/_form.erb | 1 + 3 files changed, 13 insertions(+), 1 deletion(-) create mode 100644 actionpack/test/fixtures/fun/games/_form.erb (limited to 'actionpack') diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index 85f67d4f14..fd896c263d 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -316,7 +316,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/test/controller/render_test.rb b/actionpack/test/controller/render_test.rb index e3ed097c67..a57a12f271 100644 --- a/actionpack/test/controller/render_test.rb +++ b/actionpack/test/controller/render_test.rb @@ -7,6 +7,10 @@ module Fun # :ported: def hello_world end + + def nested_partial_with_form_builder + render :partial => ActionView::Helpers::FormBuilder.new(:post, nil, view_context, {}, Proc.new {}) + end end end @@ -1230,6 +1234,13 @@ class RenderTest < ActionController::TestCase assert_match(/