Changeset 6731
- Timestamp:
- 05/14/07 17:30:35 (2 years ago)
- Files:
-
- trunk/actionpack/CHANGELOG (modified) (1 diff)
- trunk/actionpack/lib/action_controller/base.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_controller/polymorphic_routes.rb (modified) (1 diff)
- trunk/actionpack/lib/action_controller/record_identifier.rb (modified) (1 diff)
- trunk/actionpack/lib/action_view/helpers/form_helper.rb (modified) (2 diffs)
- trunk/actionpack/lib/action_view/helpers/prototype_helper.rb (modified) (1 diff)
- trunk/actionpack/lib/action_view/helpers/record_identification_helper.rb (modified) (1 diff)
- trunk/actionpack/lib/action_view/helpers/record_tag_helper.rb (modified) (1 diff)
- trunk/actionpack/test/controller/record_identifier_test.rb (modified) (4 diffs)
- trunk/actionpack/test/template/form_helper_test.rb (modified) (4 diffs)
- trunk/actionpack/test/template/prototype_helper_test.rb (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
- Modified
- Copied
- Moved
trunk/actionpack/CHANGELOG
r6730 r6731 1 1 *SVN* 2 3 * Added record identifications to FormHelper#form_for and PrototypeHelper#remote_form_for [DHH]. Examples: 4 5 <% form_for(@post) do |f| %> 6 ... 7 <% end %> 8 9 This will expand to be the same as: 10 11 <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> 12 ... 13 <% end %> 14 15 And for new records: 16 17 <% form_for(Post.new) do |f| %> 18 ... 19 <% end %> 20 21 This will expand to be the same as: 22 23 <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> 24 ... 25 <% end %> 2 26 3 27 * Rationalize route path escaping according to RFC 2396 section 3.3. #7544, #8307. [Jeremy Kemper, chrisroos, begemot, jugend] trunk/actionpack/lib/action_controller/base.rb
r6729 r6731 1000 1000 # 1001 1001 # * <tt>Hash</tt>: The URL will be generated by calling url_for with the +options+. 1002 # * <tt>Record</tt>: The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record. 1002 1003 # * <tt>String starting with protocol:// (like http://)</tt>: Is passed straight through as the target for redirection. 1003 1004 # * <tt>String not containing a protocol</tt>: The current protocol and host is prepended to the string. … … 1007 1008 # Examples: 1008 1009 # redirect_to :action => "show", :id => 5 1010 # redirect_to post 1009 1011 # redirect_to "http://www.rubyonrails.org" 1010 1012 # redirect_to "/images/screenshot.jpg" trunk/actionpack/lib/action_controller/polymorphic_routes.rb
r6729 r6731 8 8 case 9 9 when options[:action] == "new" 10 url_writer.send(action_prefix(options) + RecordIdentifier.singular_class_name(record) + routing_type(options)) 10 url_writer.send( 11 action_prefix(options) + RecordIdentifier.singular_class_name(record) + routing_type(options) 12 ) 11 13 12 14 when record.respond_to?(:new_record?) && record.new_record? 13 url_writer.send(RecordIdentifier.plural_class_name(record) + routing_type(options)) 15 url_writer.send( 16 action_prefix(options) + RecordIdentifier.plural_class_name(record) + routing_type(options) 17 ) 14 18 15 19 else trunk/actionpack/lib/action_controller/record_identifier.rb
r6633 r6731 64 64 # dom_class(Post.new(:id => 45), :edit) # => "edit_post_45" 65 65 def dom_id(record, prefix = nil) 66 prefix ||= 'new' unless record.id 66 prefix ||= 'new' unless record.id 67 67 [ prefix, singular_class_name(record), record.id ].compact * '_' 68 68 end trunk/actionpack/lib/action_view/helpers/form_helper.rb
r6689 r6731 103 103 # The above form will then have the <tt>id</tt> attribute with the value </tt>person_form</tt>, which you can then 104 104 # style with CSS or manipulate with JavaScript. 105 # 106 # === Relying on record identification 107 # 108 # In addition to manually configuring the form_for call, you can also rely on record identification, which will use 109 # the conventions and named routes of that approach. Examples: 110 # 111 # <% form_for(@post) do |f| %> 112 # ... 113 # <% end %> 114 # 115 # This will expand to be the same as: 116 # 117 # <% form_for :post, @post, :url => post_path(@post), :html => { :method => :put, :class => "edit_post", :id => "edit_post_45" } do |f| %> 118 # ... 119 # <% end %> 120 # 121 # And for new records: 122 # 123 # <% form_for(Post.new) do |f| %> 124 # ... 125 # <% end %> 126 # 127 # This will expand to be the same as: 128 # 129 # <% form_for :post, @post, :url => posts_path, :html => { :class => "new_post", :id => "new_post" } do |f| %> 130 # ... 131 # <% end %> 132 # 133 # You can also overwrite the individual conventions, like this: 134 # 135 # <% form_for(@post, :url => super_post_path(@post)) do |f| %> 136 # ... 137 # <% end %> 138 # 139 # === Customized form builders 105 140 # 106 141 # You can also build forms using a customized FormBuilder class. Subclass FormBuilder and override or define some more helpers, … … 121 156 # 122 157 # If you don't need to attach a form to a model instance, then check out FormTagHelper#form_tag. 123 def form_for( object_name, *args, &proc)158 def form_for(record_or_name, *args, &proc) 124 159 raise ArgumentError, "Missing block" unless block_given? 160 125 161 options = args.last.is_a?(Hash) ? args.pop : {} 162 163 case record_or_name 164 when String, Symbol 165 object_name = record_or_name 166 else 167 object = record_or_name 168 object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name) 169 apply_form_for_options!(object, options) 170 end 171 126 172 concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding) 127 173 fields_for(object_name, *(args << options), &proc) 128 174 concat('</form>', proc.binding) 175 end 176 177 def apply_form_for_options!(object, options) #:nodoc: 178 html_options = if object.respond_to?(:new_record?) && object.new_record? 179 { :class => dom_class(object, :new), :id => dom_id(object), :method => :post } 180 else 181 { :class => dom_class(object, :edit), :id => dom_id(object, :edit), :method => :put } 182 end 183 184 options[:html] ||= {} 185 options[:html].reverse_merge!(html_options) 186 187 options[:url] ||= polymorphic_path(object, self) 129 188 end 130 189 trunk/actionpack/lib/action_view/helpers/prototype_helper.rb
r6633 r6731 182 182 183 183 # Works like form_remote_tag, but uses form_for semantics. 184 def remote_form_for( object_name, *args, &proc)184 def remote_form_for(record_or_name, *args, &proc) 185 185 options = args.last.is_a?(Hash) ? args.pop : {} 186 187 case record_or_name 188 when String, Symbol 189 object_name = record_or_name 190 else 191 object = record_or_name 192 object_name = ActionController::RecordIdentifier.singular_class_name(record_or_name) 193 apply_form_for_options!(object, options) 194 end 195 186 196 concat(form_remote_tag(options), proc.binding) 187 197 fields_for(object_name, *(args << options), &proc) trunk/actionpack/lib/action_view/helpers/record_identification_helper.rb
r6633 r6731 1 module RecordIdentificationHelper 2 # See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view. 3 def partial_path(*args, &block) 4 ActionController::RecordIdentifier.partial_path(*args, &block) 5 end 1 module ActionView 2 module Helpers 3 module RecordIdentificationHelper 4 # See ActionController::RecordIdentifier.partial_path -- this is just a delegate to that for convenient access in the view. 5 def partial_path(*args, &block) 6 ActionController::RecordIdentifier.partial_path(*args, &block) 7 end 6 8 7 # See ActionController::RecordIdentifier.dom_class -- this is just a delegate to that for convenient access in the view.8 def dom_class(*args, &block)9 ActionController::RecordIdentifier.dom_class(*args, &block)10 end9 # See ActionController::RecordIdentifier.dom_class -- this is just a delegate to that for convenient access in the view. 10 def dom_class(*args, &block) 11 ActionController::RecordIdentifier.dom_class(*args, &block) 12 end 11 13 12 # See ActionController::RecordIdentifier.dom_id -- this is just a delegate to that for convenient access in the view. 13 def dom_id(*args, &block) 14 ActionController::RecordIdentifier.dom_id(*args, &block) 14 # See ActionController::RecordIdentifier.dom_id -- this is just a delegate to that for convenient access in the view. 15 def dom_id(*args, &block) 16 ActionController::RecordIdentifier.dom_id(*args, &block) 17 end 18 end 15 19 end 16 20 end trunk/actionpack/lib/action_view/helpers/record_tag_helper.rb
r6633 r6731 1 module RecordTagHelper 2 # Produces a wrapper DIV element with id and class parameters that 3 # relate to the specified ActiveRecord object. Usage example: 4 # 5 # <% div_for(@person, :class => "foo") do %> 6 # <%=h @person.name %> 7 # <% end %> 8 # 9 # produces: 10 # 11 # <div id="person_123" class="person foo"> Joe Bloggs </div> 12 # 13 def div_for(record, *args, &block) 14 content_tag_for(:div, record, *args, &block) 15 end 1 module ActionView 2 module Helpers 3 module RecordTagHelper 4 # Produces a wrapper DIV element with id and class parameters that 5 # relate to the specified ActiveRecord object. Usage example: 6 # 7 # <% div_for(@person, :class => "foo") do %> 8 # <%=h @person.name %> 9 # <% end %> 10 # 11 # produces: 12 # 13 # <div id="person_123" class="person foo"> Joe Bloggs </div> 14 # 15 def div_for(record, *args, &block) 16 content_tag_for(:div, record, *args, &block) 17 end 16 18 17 # content_tag_for creates an HTML element with id and class parameters 18 # that relate to the specified ActiveRecord object. For example: 19 # 20 # <% content_tag_for(:tr, @person) do %> 21 # <td><%=h @person.first_name %></td> 22 # <td><%=h @person.last_name %></td> 23 # <% end %> 24 # 25 # would produce hthe following HTML (assuming @person is an instance of 26 # a Person object, with an id value of 123): 27 # 28 # <tr id="person_123" class="person">....</tr> 29 # 30 # If you require the HTML id attribute to have a prefix, you can specify it: 31 # 32 # <% content_tag_for(:tr, @person, :foo) do %> ... 33 # 34 # produces: 35 # 36 # <tr id="foo_person_123" class="person">... 37 # 38 # content_tag_for also accepts a hash of options, which will be converted to 39 # additional HTML attributes. If you specify a +:class+ value, it will be combined 40 # with the default class name for your object. For example: 41 # 42 # <% content_tag_for(:li, @person, :class => "bar") %>... 43 # 44 # produces: 45 # 46 # <li id="person_123" class="person bar">... 47 # 48 def content_tag_for(tag_name, record, *args, &block) 49 prefix = args.first.is_a?(Hash) ? nil : args.shift 50 options = args.first.is_a?(Hash) ? args.shift : {} 51 concat content_tag(tag_name, capture(&block), 52 options.merge({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) })), 53 block.binding 19 # content_tag_for creates an HTML element with id and class parameters 20 # that relate to the specified ActiveRecord object. For example: 21 # 22 # <% content_tag_for(:tr, @person) do %> 23 # <td><%=h @person.first_name %></td> 24 # <td><%=h @person.last_name %></td> 25 # <% end %> 26 # 27 # would produce hthe following HTML (assuming @person is an instance of 28 # a Person object, with an id value of 123): 29 # 30 # <tr id="person_123" class="person">....</tr> 31 # 32 # If you require the HTML id attribute to have a prefix, you can specify it: 33 # 34 # <% content_tag_for(:tr, @person, :foo) do %> ... 35 # 36 # produces: 37 # 38 # <tr id="foo_person_123" class="person">... 39 # 40 # content_tag_for also accepts a hash of options, which will be converted to 41 # additional HTML attributes. If you specify a +:class+ value, it will be combined 42 # with the default class name for your object. For example: 43 # 44 # <% content_tag_for(:li, @person, :class => "bar") %>... 45 # 46 # produces: 47 # 48 # <li id="person_123" class="person bar">... 49 # 50 def content_tag_for(tag_name, record, *args, &block) 51 prefix = args.first.is_a?(Hash) ? nil : args.shift 52 options = args.first.is_a?(Hash) ? args.shift : {} 53 concat content_tag(tag_name, capture(&block), 54 options.merge({ :class => "#{dom_class(record)} #{options[:class]}".strip, :id => dom_id(record, prefix) })), 55 block.binding 56 end 57 end 54 58 end 55 59 end trunk/actionpack/test/controller/record_identifier_test.rb
r6633 r6731 1 1 require File.dirname(__FILE__) + '/../abstract_unit' 2 2 3 class Post3 class Comment 4 4 attr_reader :id 5 5 def save; @id = 1 end 6 6 def new_record?; @id.nil? end 7 7 def name 8 @id.nil? ? 'new post' : "post ##{@id}"8 @id.nil? ? 'new comment' : "comment ##{@id}" 9 9 end 10 class Nested < Post; end11 10 end 11 12 class Comment::Nested < Comment; end 12 13 13 14 class Test::Unit::TestCase 14 15 protected 15 def posts_url16 'http://www.example.com/ posts'16 def comments_url 17 'http://www.example.com/comments' 17 18 end 18 19 19 def post_url(post)20 "http://www.example.com/ posts/#{post.id}"20 def comment_url(comment) 21 "http://www.example.com/comments/#{comment.id}" 21 22 end 22 23 end … … 27 28 28 29 def setup 29 @klass = Post30 @klass = Comment 30 31 @record = @klass.new 31 @singular = ' post'32 @plural = ' posts'32 @singular = 'comment' 33 @plural = 'comments' 33 34 end 34 35 … … 54 55 expected = "#{@plural}/#{@singular}" 55 56 assert_equal expected, partial_path(@record) 56 assert_equal expected, partial_path( Post)57 assert_equal expected, partial_path(Comment) 57 58 end 58 59 … … 89 90 class NestedRecordIdentifierTest < RecordIdentifierTest 90 91 def setup 91 @klass = Post::Nested92 @klass = Comment::Nested 92 93 @record = @klass.new 93 @singular = ' post_nested'94 @plural = ' post_nesteds'94 @singular = 'comment_nested' 95 @plural = 'comment_nesteds' 95 96 end 96 97 97 98 def test_partial_path 98 expected = " post/nesteds/nested"99 expected = "comment/nesteds/nested" 99 100 assert_equal expected, partial_path(@record) 100 assert_equal expected, partial_path( Post::Nested)101 assert_equal expected, partial_path(Comment::Nested) 101 102 end 102 103 end trunk/actionpack/test/template/form_helper_test.rb
r6729 r6731 1 1 require "#{File.dirname(__FILE__)}/../abstract_unit" 2 3 silence_warnings do 4 Post = Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) 5 Post.class_eval do 6 alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) 7 alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) 8 alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) 9 10 def new_record=(boolean) 11 @new_record = boolean 12 end 13 14 def new_record? 15 @new_record 16 end 17 end 18 end 2 19 3 20 class FormHelperTest < Test::Unit::TestCase … … 8 25 include ActionView::Helpers::TextHelper 9 26 include ActionView::Helpers::ActiveRecordHelper 10 11 silence_warnings do 12 Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on, :cost) 13 Post.class_eval do 14 alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) 15 alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) 16 alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) 17 end 18 end 27 include ActionView::Helpers::RecordIdentificationHelper 19 28 20 29 def setup … … 547 556 548 557 expected = "<form action=\"/posts/123\" method=\"post\"></form>" 558 assert_equal expected, _erbout 559 end 560 561 def test_form_for_with_existing_object 562 _erbout = '' 563 564 form_for(@post) do |f| end 565 566 expected = "<form action=\"/posts/123\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>" 567 assert_equal expected, _erbout 568 end 569 570 def test_form_for_with_new_object 571 _erbout = '' 572 573 post = Post.new 574 post.new_record = true 575 def post.id() nil end 576 577 form_for(post) do |f| end 578 579 expected = "<form action=\"/posts\" class=\"new_post\" id=\"new_post\" method=\"post\"></form>" 580 assert_equal expected, _erbout 581 end 582 583 def test_form_for_with_existing_object_and_custom_url 584 _erbout = '' 585 586 form_for(@post, :url => "/super_posts") do |f| end 587 588 expected = "<form action=\"/super_posts\" class=\"edit_post\" id=\"edit_post_123\" method=\"post\"><div style=\"margin:0;padding:0\"><input name=\"_method\" type=\"hidden\" value=\"put\" /></div></form>" 589 assert_equal expected, _erbout 549 590 end 550 591 … … 562 603 protected 563 604 def polymorphic_path(record, url_writer) 564 "/posts/#{record.id}" 605 if record.new_record? 606 "/posts" 607 else 608 "/posts/#{record.id}" 609 end 565 610 end 566 611 end trunk/actionpack/test/template/prototype_helper_test.rb
r6729 r6731 2 2 3 3 Bunny = Struct.new(:Bunny, :id) 4 5 class Author 6 attr_reader :id 7 def save; @id = 1 end 8 def new_record?; @id.nil? end 9 def name 10 @id.nil? ? 'new author' : "author ##{@id}" 11 end 12 end 13 14 class Author::Nested < Author; end 15 4 16 5 17 module BaseTest … … 14 26 include ActionView::Helpers::FormHelper 15 27 include ActionView::Helpers::CaptureHelper 28 include ActionView::Helpers::RecordIdentificationHelper 16 29 17 30 def setup … … 42 55 include BaseTest 43 56 57 def setup 58 @record = Author.new 59 super 60 end 61 44 62 def test_link_to_remote 45 assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote out post</a>),46 link_to_remote("Remote out post", { :url => { :action => "whatnot" }}, { :class => "fine" })47 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.reponseText)}}); return false;\">Remote out post</a>),48 link_to_remote("Remote out post", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" })49 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.reponseText)}}); return false;\">Remote out post</a>),50 link_to_remote("Remote out post", :success => "alert(request.reponseText)", :url => { :action => "whatnot" })51 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}}); return false;\">Remote out post</a>),52 link_to_remote("Remote out post", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" })53 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}}); return false;\">Remote out post</a>),54 link_to_remote("Remote out post", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' })63 assert_dom_equal %(<a class=\"fine\" href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true}); return false;\">Remote outauthor</a>), 64 link_to_remote("Remote outauthor", { :url => { :action => "whatnot" }}, { :class => "fine" }) 65 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onComplete:function(request){alert(request.reponseText)}}); return false;\">Remote outauthor</a>), 66 link_to_remote("Remote outauthor", :complete => "alert(request.reponseText)", :url => { :action => "whatnot" }) 67 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onSuccess:function(request){alert(request.reponseText)}}); return false;\">Remote outauthor</a>), 68 link_to_remote("Remote outauthor", :success => "alert(request.reponseText)", :url => { :action => "whatnot" }) 69 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}}); return false;\">Remote outauthor</a>), 70 link_to_remote("Remote outauthor", :failure => "alert(request.reponseText)", :url => { :action => "whatnot" }) 71 assert_dom_equal %(<a href=\"#\" onclick=\"new Ajax.Request('http://www.example.com/whatnot?a=10&b=20', {asynchronous:true, evalScripts:true, onFailure:function(request){alert(request.reponseText)}}); return false;\">Remote outauthor</a>), 72 link_to_remote("Remote outauthor", :failure => "alert(request.reponseText)", :url => { :action => "whatnot", :a => '10', :b => '20' }) 55 73 end 56 74 … … 81 99 assert_dom_equal %(<form action=\"http://www.example.com/fast\" method=\"post\" onsubmit=\"new Ajax.Updater('glass_of_beer', 'http://www.example.com/fast', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;\">Hello world!</form>), _erbout 82 100 end 83 101 102 def test_remote_form_for_with_record_identification_with_new_record 103 _erbout = '' 104 remote_form_for(@record, {:html => { :id => 'create-author' }}) {} 105 106 expected = %(<form action='#{authors_url}' onsubmit="new Ajax.Request('#{authors_url}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' id='create-author' method='post'></form>) 107 assert_dom_equal expected, _erbout 108 end 109 110 def test_remote_form_for_with_record_identification_without_html_options 111 _erbout = '' 112 remote_form_for(@record) {} 113 114 expected = %(<form action='#{authors_url}' onsubmit="new Ajax.Request('#{authors_url}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='new_author' method='post' id='new_author'></form>) 115 assert_dom_equal expected, _erbout 116 end 117 118 def test_remote_form_for_with_record_identification_with_existing_record 119 @record.save 120 _erbout = '' 121 remote_form_for(@record) {} 122 123 expected = %(<form action='#{author_url(@record)}' id='edit_author_1' method='post' onsubmit="new Ajax.Request('#{author_url(@record)}', {asynchronous:true, evalScripts:true, parameters:Form.serialize(this)}); return false;" class='edit_author'><div style='margin:0;padding:0'><input name='_method' type='hidden' value='put' /></div></form>) 124 assert_dom_equal expected, _erbout 125 end 126 84 127 def test_on_callbacks 85 128 callbacks = [:uninitialized, :loading, :loaded, :interactive, :complete, :success, :failure] … … 162 205 end 163 206 207 208 protected 209 def author_url(record) 210 "/authors/#{record.id}" 211 end 212 213 def authors_url 214 "/authors" 215 end 216 217 def polymorphic_path(record, url_writer) 218 if record.new_record? 219 "/authors" 220 else 221 "/authors/#{record.id}" 222 end 223 end 164 224 end 165 225