Changeset 8741
- Timestamp:
- 01/27/08 01:18:47 (7 months ago)
- Files:
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/lib/action_controller/polymorphic_routes.rb
r7719 r8741 1 1 module ActionController 2 # Polymorphic URL helpers are methods for smart resolution to a named route call when 3 # given an ActiveRecord model instance. They are to be used in combination with 4 # ActionController::Resources. 5 # 6 # These methods are useful when you want to generate correct URL or path to a RESTful 7 # resource without having to know the exact type of the record in question. 8 # 9 # Nested resources and/or namespaces are also supported, as illustrated in the example: 10 # 11 # polymorphic_url([:admin, @article, @comment]) 12 # #-> results in: 13 # admin_article_comment_url(@article, @comment) 14 # 15 # == Usage within the framework 16 # 17 # Polymorphic URL helpers are used in a number of places throughout the Rails framework: 18 # 19 # * <tt>url_for</tt>, so you can use it with a record as the argument, e.g. 20 # <tt>url_for(@article)</tt>; 21 # * ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write 22 # <tt>form_for(@article)</tt> without having to specify :url parameter for the form 23 # action; 24 # * <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write 25 # <tt>redirect_to(post)</tt> in your controllers; 26 # * ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs 27 # for feed entries. 28 # 29 # == Prefixed polymorphic helpers 30 # 31 # In addition to <tt>polymorphic_url</tt> and <tt>polymorphic_path</tt> methods, a 32 # number of prefixed helpers are available as a shorthand to <tt>:action => "..."</tt> 33 # in options. Those are: 34 # 35 # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt> 36 # * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt> 37 # * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt> 38 # 39 # Example usage: 40 # 41 # edit_polymorphic_path(@post) 42 # #=> /posts/1/edit 43 # 44 # formatted_polymorphic_path([@post, :pdf]) 45 # #=> /posts/1.pdf 2 46 module PolymorphicRoutes 47 # Constructs a call to a named RESTful route for the given record and returns the 48 # resulting URL string. For example: 49 # 50 # polymorphic_url(post) 51 # # calls post_url(post) #=> "http://example.com/posts/1" 52 # 53 # ==== Options 54 # * <tt>:action</tt> -- specifies the action prefix for the named route: 55 # <tt>:new</tt>, <tt>:edit</tt> or <tt>:formatted</tt>. Default is no prefix. 56 # * <tt>:routing_type</tt> -- <tt>:path</tt> or <tt>:url</tt> (default <tt>:url</tt>). 57 # 58 # ==== Examples 59 # 60 # # an Article record 61 # polymorphic_url(record) #-> article_url(record) 62 # 63 # # a Comment record 64 # polymorphic_url(record) #-> comment_url(record) 65 # 66 # # it recognizes new records and maps to the collection 67 # record = Comment.new 68 # polymorphic_url(record) #-> comments_url() 69 # 3 70 def polymorphic_url(record_or_hash_or_array, options = {}) 4 record = extract_record(record_or_hash_or_array)5 71 record = extract_record(record_or_hash_or_array) 72 format = (options[:action].to_s == "formatted" and record_or_hash_or_array.pop) 6 73 namespace = extract_namespace(record_or_hash_or_array) 7 74 … … 12 79 end 13 80 81 args << format if format 82 14 83 inflection = 15 84 case 16 when options[:action] == "new"85 when options[:action].to_s == "new" 17 86 args.pop 18 87 :singular … … 28 97 end 29 98 30 def polymorphic_path(record_or_hash_or_array) 31 polymorphic_url(record_or_hash_or_array, :routing_type => :path) 99 # Returns the path component of a URL for the given record. It uses 100 # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>. 101 def polymorphic_path(record_or_hash_or_array, options = {}) 102 options[:routing_type] = :path 103 polymorphic_url(record_or_hash_or_array, options) 32 104 end 33 105 … … 44 116 end 45 117 46 47 118 private 48 119 def action_prefix(options) … … 51 122 52 123 def routing_type(options) 53 "#{options[:routing_type] || "url"}"124 options[:routing_type] || :url 54 125 end 55 126 56 127 def build_named_route_call(records, namespace, inflection, options = {}) 57 records = Array.new([extract_record(records)]) unless records.is_a?(Array) 58 base_segment = "#{RecordIdentifier.send!("#{inflection}_class_name", records.pop)}_" 59 60 method_root = records.reverse.inject(base_segment) do |string, name| 61 segment = "#{RecordIdentifier.send!("singular_class_name", name)}_" 62 segment << string 128 unless records.is_a?(Array) 129 record = extract_record(records) 130 route = '' 131 else 132 record = records.pop 133 route = records.inject("") do |string, parent| 134 string << "#{RecordIdentifier.send!("singular_class_name", parent)}_" 135 end 63 136 end 64 137 65 action_prefix(options) + namespace + method_root + routing_type(options) 138 route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_" 139 140 action_prefix(options) + namespace + route + routing_type(options).to_s 66 141 end 67 142 … … 79 154 record_or_hash_or_array.delete_if do |record_or_namespace| 80 155 if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol) 81 namespace << "#{record_or_namespace .to_s}_"156 namespace << "#{record_or_namespace}_" 82 157 end 83 158 end trunk/actionpack/test/controller/polymorphic_routes_test.rb
r8564 r8741 6 6 def new_record?; @id.nil? end 7 7 def name 8 @id.nil? ? 'new post' : "post ##{@id}" 8 model = self.class.name.downcase 9 @id.nil? ? "new #{model}" : "#{model} ##{@id}" 9 10 end 10 11 end 11 12 12 class Comment 13 attr_reader :id 13 class Comment < Article 14 14 def post_id; 1 end 15 def save; @id = 1 end 16 def new_record?; @id.nil? end 17 def name 18 @id.nil? ? 'new comment' : "comment ##{@id}" 15 end 16 17 class Tag < Article 18 def comment_id; 1 end 19 end 20 21 # TODO: test nested models 22 class Comment::Nested < Comment; end 23 24 uses_mocha 'polymorphic URL helpers' do 25 class PolymorphicRoutesTest < Test::Unit::TestCase 26 27 include ActionController::PolymorphicRoutes 28 29 def setup 30 @article = Article.new 31 @comment = Comment.new 32 end 33 34 def test_with_record 35 @article.save 36 expects(:article_url).with(@article) 37 polymorphic_url(@article) 38 end 39 40 def test_with_new_record 41 expects(:articles_url).with() 42 @article.expects(:new_record?).returns(true) 43 polymorphic_url(@article) 44 end 45 46 def test_with_record_and_action 47 expects(:new_article_url).with() 48 @article.expects(:new_record?).never 49 polymorphic_url(@article, :action => 'new') 50 end 51 52 def test_url_helper_prefixed_with_new 53 expects(:new_article_url).with() 54 new_polymorphic_url(@article) 55 end 56 57 def test_url_helper_prefixed_with_edit 58 @article.save 59 expects(:edit_article_url).with(@article) 60 edit_polymorphic_url(@article) 61 end 62 63 def test_formatted_url_helper 64 expects(:formatted_article_url).with(@article, :pdf) 65 formatted_polymorphic_url([@article, :pdf]) 66 end 67 68 # TODO: should this work? 69 def xtest_format_option 70 @article.save 71 expects(:article_url).with(@article, :format => :pdf) 72 polymorphic_url(@article, :format => :pdf) 73 end 74 75 def test_with_nested 76 @comment.save 77 expects(:article_comment_url).with(@article, @comment) 78 polymorphic_url([@article, @comment]) 79 end 80 81 def test_with_nested_unsaved 82 expects(:article_comments_url).with(@article) 83 polymorphic_url([@article, @comment]) 84 end 85 86 def test_new_with_array_and_namespace 87 expects(:new_admin_article_url).with() 88 polymorphic_url([:admin, @article], :action => 'new') 89 end 90 91 def test_unsaved_with_array_and_namespace 92 expects(:admin_articles_url).with() 93 polymorphic_url([:admin, @article]) 94 end 95 96 def test_nested_unsaved_with_array_and_namespace 97 @article.save 98 expects(:admin_article_url).with(@article) 99 polymorphic_url([:admin, @article]) 100 expects(:admin_article_comments_url).with(@article) 101 polymorphic_url([:admin, @article, @comment]) 102 end 103 104 def test_nested_with_array_and_namespace 105 @comment.save 106 expects(:admin_article_comment_url).with(@article, @comment) 107 polymorphic_url([:admin, @article, @comment]) 108 109 # a ridiculously long named route tests correct ordering of namespaces and nesting: 110 @tag = Tag.new 111 @tag.save 112 expects(:site_admin_article_comment_tag_url).with(@article, @comment, @tag) 113 polymorphic_url([:site, :admin, @article, @comment, @tag]) 114 end 115 116 # TODO: Needs to be updated to correctly know about whether the object is in a hash or not 117 def xtest_with_hash 118 expects(:article_url).with(@article) 119 @article.save 120 polymorphic_url(:id => @article) 121 end 122 123 def test_polymorphic_path_accepts_options 124 expects(:new_article_path).with() 125 polymorphic_path(@article, :action => :new) 126 end 127 19 128 end 20 129 end 21 22 class Comment::Nested < Comment; end23 24 class Test::Unit::TestCase25 protected26 def articles_url27 'http://www.example.com/articles'28 end29 alias_method :new_article_url, :articles_url30 31 def article_url(article)32 "http://www.example.com/articles/#{article.id}"33 end34 35 def article_comments_url(article)36 "http://www.example.com/articles/#{article.id}/comments"37 end38 39 def article_comment_url(article, comment)40 "http://www.example.com/articles/#{article.id}/comments/#{comment.id}"41 end42 43 def admin_articles_url44 "http://www.example.com/admin/articles"45 end46 alias_method :new_admin_article_url, :admin_articles_url47 48 def admin_article_url(article)49 "http://www.example.com/admin/articles/#{article.id}"50 end51 52 def admin_article_comments_url(article)53 "http://www.example.com/admin/articles/#{article.id}/comments"54 end55 56 def admin_article_comment_url(article, comment)57 "http://www.example.com/admin/test/articles/#{article.id}/comments/#{comment.id}"58 end59 end60 61 62 class PolymorphicRoutesTest < Test::Unit::TestCase63 include ActionController::PolymorphicRoutes64 65 def setup66 @article = Article.new67 @comment = Comment.new68 end69 70 def test_with_record71 assert_equal(articles_url, polymorphic_url(@article, :action => 'new'))72 assert_equal(articles_url, polymorphic_url(@article))73 @article.save74 assert_equal(article_url(@article), polymorphic_url(@article))75 end76 77 # TODO: Needs to be updated to correctly know about whether the object is in a hash or not78 def xtest_with_hash79 @article.save80 assert_equal(article_url(@article), polymorphic_url(:id => @article))81 end82 83 def test_with_array84 assert_equal(article_comments_url(@article), polymorphic_url([@article, @comment]))85 @comment.save86 assert_equal(article_comment_url(@article, @comment), polymorphic_url([@article, @comment]))87 end88 89 def test_with_array_and_namespace90 assert_equal(admin_articles_url, polymorphic_url([:admin, @article], :action => 'new'))91 assert_equal(admin_articles_url, polymorphic_url([:admin, @article]))92 @article.save93 assert_equal(admin_article_url(@article), polymorphic_url([:admin, @article]))94 assert_equal(admin_article_comments_url(@article), polymorphic_url([:admin, @article, @comment]))95 @comment.save96 assert_equal(admin_article_comment_url(@article, @comment), polymorphic_url([:admin, @article, @comment]))97 end98 end