1 使用
action_controller base:
url_for :controller => 'posts', :action => nil
- def url_for(options = {})
- options ||= {}
- case options
- when String
- options
- when Hash
- @url.rewrite(rewrite_options(options))
- else
- polymorphic_url(options)
- end
- end
这个module主要提供了一个智能化的方法能够根据active record的model名字,这些方法对使用Restful风格的url很有作用。
- # Nested resources and/or namespaces are also supported, as illustrated in the example:
- #
- polymorphic_url([:admin, @article, @comment])
- #
- # results in:
- #
- admin_article_comment_url(@article, @comment)
在框架中使用的地方包括:
* url_for, so you can use it with a record as the argument, e.g.
url_for(@article)
* ActionView::Helpers::FormHelper uses <tt>polymorphic_path</tt>, so you can write
<tt>form_for(@article)</tt> without having to specify <tt>:url</tt> parameter for the form
action;
* <tt>redirect_to</tt> (which, in fact, uses <tt>url_for</tt>) so you can write
<tt>redirect_to(post)</tt> in your controllers;
* ActionView::Helpers::AtomFeedHelper, so you don't have to explicitly specify URLs
for feed entries.
除了polymorphic_url和polymorphic_path以外,还有一系列方法:
# * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt>
# * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt>
使用例子:
# edit_polymorphic_path(@post) # => "/posts/1/edit"
# formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
2 代码分析
- module PolymorphicRoutes
- # Constructs a call to a named RESTful route for the given record and returns the
- # resulting URL string. For example:
- #
- # # calls post_url(post)
- # polymorphic_url(post) # => "http://example.com/posts/1"
- #
- # ==== Options
- #
- # * <tt>:action</tt> - Specifies the action prefix for the named route:
- # <tt>:new</tt>, <tt>:edit</tt>, or <tt>:formatted</tt>. Default is no prefix.
- # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
- # Default is <tt>:url</tt>.
- #
- # ==== Examples
- #
- # # an Article record
- # polymorphic_url(record) # same as article_url(record)
- #
- # # a Comment record
- # polymorphic_url(record) # same as comment_url(record)
- #
- # # it recognizes new records and maps to the collection
- # record = Comment.new
- # polymorphic_url(record) # same as comments_url()
- #
- def polymorphic_url(record_or_hash_or_array, options = {})
- # 如果是Array则复制一份
- if record_or_hash_or_array.kind_of?(Array)
- record_or_hash_or_array = record_or_hash_or_array.dup
- end
- record = extract_record(record_or_hash_or_array)
- format = extract_format(record_or_hash_or_array, options)
- namespace = extract_namespace(record_or_hash_or_array)
- # 参数统一成Array
- args = case record_or_hash_or_array
- when Hash; [ record_or_hash_or_array ]
- when Array; record_or_hash_or_array.dup
- else [ record_or_hash_or_array ]
- end
- args << format if format
- inflection =
- case
- when options[:action].to_s == "new"
- args.pop
- :singular
- when record.respond_to?(:new_record?) && record.new_record?
- args.pop
- :plural
- else
- :singular
- end
- named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
- # 根据生成的route方法名来调用route方法
- send!(named_route, *args)
- end
- # Returns the path component of a URL for the given record. It uses
- # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
- def polymorphic_path(record_or_hash_or_array, options = {})
- options[:routing_type] = :path
- polymorphic_url(record_or_hash_or_array, options)
- end
- # 动态生成方法:
- # * <tt>edit_polymorphic_url</tt>, <tt>edit_polymorphic_path</tt>
# * <tt>new_polymorphic_url</tt>, <tt>new_polymorphic_path</tt># * <tt>formatted_polymorphic_url</tt>, <tt>formatted_polymorphic_path</tt>
- %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}")
- end
- def #{action}_polymorphic_path(record_or_hash)
- polymorphic_url(record_or_hash, :action => "#{action}", :routing_type => :path)
- end
- EOT
- end
- private
- # 方法前缀 就是:action指定的
- def action_prefix(options)
- options[:action] ? "#{options[:action]}_" : ""
- end
- # route方法是 path 或者是 url 方法, 没有指定默认是url
- def routing_type(options)
- options[:routing_type] || :url
- end
- # 创建route方法名 = :action + 命名空间 + 串联各个父model名 + 操作model名 + 'path'或者'url'
- def build_named_route_call(records, namespace, inflection, options = {})
- unless records.is_a?(Array)
- record = extract_record(records)
- route = ''
- else
- record = records.pop
- # 拼接除了最后一个record以外的records的名字
- route = records.inject("") do |string, parent|
- string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
- end
- end
- route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
- action_prefix(options) + namespace + route + routing_type(options).to_s
- end
- # 根据实例的类来获得record实例
- 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
- # 根据:action (:new 或 :edit 或 :formatted) 来获得格式
- def extract_format(record_or_hash_or_array, options)
- # 如果是formatted并且是一个Array 那么返回最后一个参数指定的格式
- # formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
- if options[:action].to_s == "formatted" && record_or_hash_or_array.is_a?(Array)
- record_or_hash_or_array.pop
- # 如果指定了:format
- elsif options[:format]
- options[:format]
- else
- nil
- end
- end
- # 抽取命名空间
- def extract_namespace(record_or_hash_or_array)
- returning "" do |namespace|
- # 如果是一个Array
- if record_or_hash_or_array.is_a?(Array)
- # 如果包含元素是String或者Symbol,那么删除,并且拼接到空间
- record_or_hash_or_array.delete_if do |record_or_namespace|
- if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
- namespace << "#{record_or_namespace}_"
- end
- end
- end
- end
- end
- end