diff options
author | Jeremy Kemper <jeremy@bitsweat.net> | 2007-06-05 19:10:59 +0000 |
---|---|---|
committer | Jeremy Kemper <jeremy@bitsweat.net> | 2007-06-05 19:10:59 +0000 |
commit | 5dd3db86157ce2bd08c4ec07826d3aaf5c29f458 (patch) | |
tree | dfaa4c5731772c1d5a9f59d76fa3a76cbb4b51df /actionpack | |
parent | b83efadb32fe55ba7a7d23d650a8adc1a351eab7 (diff) | |
download | rails-5dd3db86157ce2bd08c4ec07826d3aaf5c29f458.tar.gz rails-5dd3db86157ce2bd08c4ec07826d3aaf5c29f458.tar.bz2 rails-5dd3db86157ce2bd08c4ec07826d3aaf5c29f458.zip |
Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to. Closes #6432.
git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@6951 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
Diffstat (limited to 'actionpack')
-rw-r--r-- | actionpack/CHANGELOG | 2 | ||||
-rw-r--r-- | actionpack/lib/action_controller/polymorphic_routes.rb | 64 | ||||
-rw-r--r-- | actionpack/lib/action_controller/record_identifier.rb | 2 | ||||
-rw-r--r-- | actionpack/lib/action_view/helpers/form_helper.rb | 29 | ||||
-rw-r--r-- | actionpack/test/template/form_helper_test.rb | 57 | ||||
-rw-r--r-- | actionpack/test/template/url_helper_test.rb | 65 |
6 files changed, 174 insertions, 45 deletions
diff --git a/actionpack/CHANGELOG b/actionpack/CHANGELOG index e155be9d46..3f77234d03 100644 --- a/actionpack/CHANGELOG +++ b/actionpack/CHANGELOG @@ -1,5 +1,7 @@ *SVN* +* Resources: url_for([parent, child]) generates /parents/1/children/2 for the nested resource. Likewise with the other simply helpful methods like form_for and link_to. #6432 [mhw, Jonathan Vaught] + * Assume html format when rendering partials in RJS. #8076 [Rick] * Don't double-escape url_for in views. #8144 [Rich Collins, Josh Peek] diff --git a/actionpack/lib/action_controller/polymorphic_routes.rb b/actionpack/lib/action_controller/polymorphic_routes.rb index c60b533205..6c21bc27c5 100644 --- a/actionpack/lib/action_controller/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/polymorphic_routes.rb @@ -1,31 +1,29 @@ module ActionController module PolymorphicRoutes - def polymorphic_url(record_or_hash, options = {}) - record = extract_record(record_or_hash) - - case - when options[:action] == "new" - send( - action_prefix(options) + RecordIdentifier.singular_class_name(record) + routing_type(options) - ) - - when record.respond_to?(:new_record?) && record.new_record? - send( - action_prefix(options) + RecordIdentifier.plural_class_name(record) + routing_type(options) - ) - - else - send( - action_prefix(options) + RecordIdentifier.singular_class_name(record) + routing_type(options), record_or_hash - ) - end + def polymorphic_url(record_or_hash_or_array, options = {}) + record = extract_record(record_or_hash_or_array) + + args = [] + inflection = + case + when options[:action] == "new" + :singular + when record.respond_to?(:new_record?) && record.new_record? + :plural + else + args = [record_or_hash_or_array] + :singular + end + + named_route = build_named_route_call(record_or_hash_or_array, inflection, options) + send(named_route, *args) end - def polymorphic_path(record_or_hash) - polymorphic_url(record_or_hash, :routing_type => :path) + def polymorphic_path(record_or_hash_or_array) + polymorphic_url(record_or_hash_or_array, :routing_type => :path) end - %w( edit new formatted ).each do |action| + %w(edit new formatted).each do |action| module_eval <<-EOT, __FILE__, __LINE__ def #{action}_polymorphic_url(record_or_hash) polymorphic_url(record_or_hash, :action => "#{action}") @@ -44,11 +42,27 @@ module ActionController end def routing_type(options) - "_#{options[:routing_type] || "url"}" + "#{options[:routing_type] || "url"}" + end + + def build_named_route_call(records, inflection, options = {}) + records = Array.new([extract_record(records)]) unless records.is_a?(Array) + base_segment = "#{RecordIdentifier.send("#{inflection}_class_name", records.pop)}_" + + method_root = records.reverse.inject(base_segment) do |string, name| + segment = "#{RecordIdentifier.send("singular_class_name", name)}_" + segment << string + end + + action_prefix(options) + method_root + routing_type(options) end - def extract_record(record_or_hash) - record_or_hash.is_a?(Hash) ? record_or_hash[:id] : record_or_hash + def extract_record(record_or_hash_or_array) + case record_or_hash_or_array + when Array: record_or_hash_or_array.last + when Hash: record_or_hash_or_array[:id] + else record_or_hash_or_array + end end end end diff --git a/actionpack/lib/action_controller/record_identifier.rb b/actionpack/lib/action_controller/record_identifier.rb index 1a60becff3..bdf2753a79 100644 --- a/actionpack/lib/action_controller/record_identifier.rb +++ b/actionpack/lib/action_controller/record_identifier.rb @@ -25,7 +25,7 @@ module ActionController # end # end # - # As the example above shows, you can stop caring to a large extend what the actual id of the post is. You just know + # 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 # convention and allows you to write less code if you follow it. module RecordIdentifier diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 7325aad398..1a4aa0ca05 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -163,9 +163,14 @@ module ActionView case record_or_name when String, Symbol object_name = record_or_name + when Array + object = record_or_name.last + object_name = ActionController::RecordIdentifier.singular_class_name(object) + apply_form_for_options!(object, options, *record_or_name) + args.unshift object else - object = record_or_name - object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name) + object = record_or_name + object_name = ActionController::RecordIdentifier.singular_class_name(object) apply_form_for_options!(object, options) args.unshift object end @@ -174,18 +179,18 @@ module ActionView fields_for(object_name, *(args << options), &proc) concat('</form>', proc.binding) end - - def apply_form_for_options!(object, options) #:nodoc: - html_options = if object.respond_to?(:new_record?) && object.new_record? - { :class => dom_class(object, :new), :id => dom_id(object), :method => :post } - else - { :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put } - end - + + def apply_form_for_options!(object, options, *nested_objects) #:nodoc: + html_options = + if object.respond_to?(:new_record?) && object.new_record? + { :class => dom_class(object, :new), :id => dom_id(object), :method => :post } + else + { :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put } + end + options[:html] ||= {} options[:html].reverse_merge!(html_options) - - options[:url] ||= polymorphic_path(object) + options[:url] ||= polymorphic_path(object, *nested_objects) end # Creates a scope around a specific model object like form_for, but doesn't create the form tags themselves. This makes diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index c9e7aeaeb3..c504190960 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -15,8 +15,21 @@ silence_warnings do @new_record end end + + class Comment + attr_reader :id + attr_reader :post_id + def save; @id = 1; @post_id = 1 end + def new_record?; @id.nil? end + def name + @id.nil? ? 'new comment' : "comment ##{@id}" + end + end end +class Comment::Nested < Comment; end + + class FormHelperTest < Test::Unit::TestCase include ActionView::Helpers::FormHelper include ActionView::Helpers::FormTagHelper @@ -28,6 +41,7 @@ class FormHelperTest < Test::Unit::TestCase def setup @post = Post.new + @comment = Comment.new def @post.errors() Class.new{ def on(field); "can't be empty" if field == "author_name"; end @@ -579,6 +593,25 @@ class FormHelperTest < Test::Unit::TestCase assert_equal expected, _erbout end + def test_form_for_with_existing_object_in_list + @post.new_record = false + @comment.save + _erbout = '' + form_for([@post, @comment]) {} + + expected = %(<form action="#{comment_path(@post, @comment)}" class="edit_comment" id="edit_comment_1" method="post"><div style="margin:0;padding:0"><input name="_method" type="hidden" value="put" /></div></form>) + assert_dom_equal expected, _erbout + end + + def test_form_for_with_new_object_in_list + @post.new_record = false + _erbout = '' + form_for([@post, @comment]) {} + + expected = %(<form action="#{comments_path(@post)}" class="new_comment" id="new_comment" method="post"></form>) + assert_dom_equal expected, _erbout + end + def test_form_for_with_existing_object_and_custom_url _erbout = '' @@ -600,11 +633,27 @@ class FormHelperTest < Test::Unit::TestCase protected - def polymorphic_path(record) - if record.new_record? - "/posts" + def comments_path(post) + "/posts/#{post.id}/comments" + end + + def comment_path(post, comment) + "/posts/#{post.id}/comments/#{comment.id}" + end + + def polymorphic_path(object, *nested_objects) + if nested_objects.empty? + if object.new_record? + "/posts" + else + "/posts/#{object.id}" + end else - "/posts/#{record.id}" + if object.new_record? + "/posts/123/comments" + else + "/posts/123/comments/#{object.id}" + end end end end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index 43ec8ca0d3..1a5521b3e4 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -375,6 +375,22 @@ class Workshop end end +class Session + attr_accessor :id, :workshop_id, :new_record + + def initialize(id, new_record) + @id, @new_record = id, new_record + end + + def new_record? + @new_record + end + + def to_s + id.to_s + end +end + class PolymorphicControllerTest < Test::Unit::TestCase class WorkshopsController < ActionController::Base self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"] @@ -394,15 +410,36 @@ class PolymorphicControllerTest < Test::Unit::TestCase def rescue_action(e) raise e end end + class SessionsController < ActionController::Base + self.view_paths = ["#{File.dirname(__FILE__)}/../fixtures/"] + + def self.controller_path; 'sessions' end + + def index + @workshop = Workshop.new(params[:workshop_id], false) + @session = Session.new(1, true) + render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" + end + + def show + @workshop = Workshop.new(params[:workshop_id], false) + @session = Session.new(params[:id], false) + render :inline => "<%= url_for([@workshop, @session]) %>\n<%= link_to('Session', [@workshop, @session]) %>" + end + + def rescue_action(e) raise e end + end + include ActionView::Helpers::UrlHelper def setup @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new - @controller = WorkshopsController.new end def test_new_resource + @controller = WorkshopsController.new + with_restful_routing do get :index assert_equal "/workshops\n<a href=\"/workshops\">Workshop</a>", @response.body @@ -410,19 +447,41 @@ class PolymorphicControllerTest < Test::Unit::TestCase end def test_existing_resource + @controller = WorkshopsController.new + with_restful_routing do get :show, :id => 1 assert_equal "/workshops/1\n<a href=\"/workshops/1\">Workshop</a>", @response.body end end + def test_new_nested_resource + @controller = SessionsController.new + + with_restful_routing do + get :index, :workshop_id => 1 + assert_equal "/workshops/1/sessions\n<a href=\"/workshops/1/sessions\">Session</a>", @response.body + end + end + + def test_existing_nested_resource + @controller = SessionsController.new + + with_restful_routing do + get :show, :workshop_id => 1, :id => 1 + assert_equal "/workshops/1/sessions/1\n<a href=\"/workshops/1/sessions/1\">Session</a>", @response.body + end + end + protected def with_restful_routing with_routing do |set| set.draw do |map| - map.resources :workshops + map.resources :workshops do |w| + w.resources :sessions + end end yield end end -end
\ No newline at end of file +end |