Rails源代码分析(31):ActionController::Routing(2) PolymorphicRoutes

1 使用
action_controller base:

url_for :controller => 'posts', :action => nil
  1.       def url_for(options = {})
  2.         options ||= {}
  3.         case options
  4.           when String
  5.             options
  6.           when Hash
  7.             @url.rewrite(rewrite_options(options))
  8.           else
  9.             polymorphic_url(options)
  10.         end
  11.       end


这个module主要提供了一个智能化的方法能够根据active record的model名字,这些方法对使用Restful风格的url很有作用。
  1.   # Nested resources and/or namespaces are also supported, as illustrated in the example:
  2.   #
  3.      polymorphic_url([:admin@article@comment])
  4.   #
  5.   # results in:
  6.   #   
  7.      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 代码分析
  1.   module PolymorphicRoutes
  2.     # Constructs a call to a named RESTful route for the given record and returns the
  3.     # resulting URL string. For example:
  4.     #
  5.     #   # calls post_url(post)
  6.     #   polymorphic_url(post) # => "http://example.com/posts/1"
  7.     #
  8.     # ==== Options
  9.     #
  10.     # * <tt>:action</tt> - Specifies the action prefix for the named route:
  11.     #    <tt>:new</tt>, <tt>:edit</tt>, or <tt>:formatted</tt>. Default is no prefix.
  12.     # * <tt>:routing_type</tt> - Allowed values are <tt>:path</tt> or <tt>:url</tt>.
  13.     #   Default is <tt>:url</tt>.
  14.     #
  15.     # ==== Examples
  16.     #
  17.     #   # an Article record
  18.     #   polymorphic_url(record)  # same as article_url(record)
  19.     #
  20.     #   # a Comment record
  21.     #   polymorphic_url(record)  # same as comment_url(record)
  22.     #
  23.     #   # it recognizes new records and maps to the collection
  24.     #   record = Comment.new
  25.     #   polymorphic_url(record)  # same as comments_url()
  26.     #
  27.     def polymorphic_url(record_or_hash_or_array, options = {})
  28.       # 如果是Array则复制一份
  29.      if record_or_hash_or_array.kind_of?(Array)
  30.         record_or_hash_or_array = record_or_hash_or_array.dup
  31.       end
  32.       record    = extract_record(record_or_hash_or_array)
  33.       format    = extract_format(record_or_hash_or_array, options)
  34.       namespace = extract_namespace(record_or_hash_or_array)
  35.       
  36.       # 参数统一成Array
  37.       args = case record_or_hash_or_array
  38.         when Hash;  [ record_or_hash_or_array ]
  39.         when Array; record_or_hash_or_array.dup
  40.         else        [ record_or_hash_or_array ]
  41.       end
  42.       args << format if format
  43.       inflection =
  44.         case
  45.         when options[:action].to_s == "new"
  46.           args.pop
  47.           :singular
  48.         when record.respond_to?(:new_record?) && record.new_record?
  49.           args.pop
  50.           :plural
  51.         else
  52.           :singular
  53.         end
  54.       
  55.       named_route = build_named_route_call(record_or_hash_or_array, namespace, inflection, options)
  56.       # 根据生成的route方法名来调用route方法
  57.       send!(named_route, *args)
  58.     end
  59.     # Returns the path component of a URL for the given record. It uses
  60.     # <tt>polymorphic_url</tt> with <tt>:routing_type => :path</tt>.
  61.     def polymorphic_path(record_or_hash_or_array, options = {})
  62.       options[:routing_type] = :path
  63.       polymorphic_url(record_or_hash_or_array, options)
  64.     end
  65.     # 动态生成方法:
  66.     # * <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>
  67.     %w(edit new formatted).each do |action|
  68.       module_eval <<-EOT, __FILE__, __LINE__
  69.         def #{action}_polymorphic_url(record_or_hash)
  70.           polymorphic_url(record_or_hash, :action => "#{action}")
  71.         end
  72.         def #{action}_polymorphic_path(record_or_hash)
  73.           polymorphic_url(record_or_hash, :action => "#{action}":routing_type => :path)
  74.         end
  75.       EOT
  76.     end
  77.     private
  78.       # 方法前缀 就是:action指定的
  79.       def action_prefix(options)
  80.         options[:action] ? "#{options[:action]}_" : ""
  81.       end
  82.       # route方法是 path 或者是 url 方法, 没有指定默认是url
  83.      def routing_type(options)
  84.         options[:routing_type] || :url
  85.       end
  86.       # 创建route方法名 = :action + 命名空间 + 串联各个父model名 + 操作model名 + 'path'或者'url'
  87.      def build_named_route_call(records, namespace, inflection, options = {})
  88.         unless records.is_a?(Array)
  89.           record = extract_record(records)
  90.           route  = ''
  91.         else
  92.           record = records.pop
  93.           # 拼接除了最后一个record以外的records的名字
  94.           route = records.inject(""do |string, parent|
  95.             string << "#{RecordIdentifier.send!("singular_class_name", parent)}_"
  96.           end
  97.         end
  98.         route << "#{RecordIdentifier.send!("#{inflection}_class_name", record)}_"
  99.         action_prefix(options) + namespace + route + routing_type(options).to_s
  100.       end
  101.       # 根据实例的类来获得record实例
  102.      def extract_record(record_or_hash_or_array)
  103.         case record_or_hash_or_array
  104.           when Array; record_or_hash_or_array.last
  105.           when Hash;  record_or_hash_or_array[:id]
  106.           else        record_or_hash_or_array
  107.         end
  108.       end
  109.       
  110.       # 根据:action (:new 或 :edit 或 :formatted) 来获得格式
  111.       def extract_format(record_or_hash_or_array, options)
  112.         # 如果是formatted并且是一个Array 那么返回最后一个参数指定的格式
  113.         #  formatted_polymorphic_path([@post, :pdf]) # => "/posts/1.pdf"
  114.         if options[:action].to_s == "formatted" && record_or_hash_or_array.is_a?(Array)
  115.           record_or_hash_or_array.pop
  116.         # 如果指定了:format
  117.         elsif options[:format]
  118.           options[:format]
  119.         else
  120.           nil
  121.         end
  122.       end
  123.       
  124.       # 抽取命名空间
  125.       def extract_namespace(record_or_hash_or_array)
  126.         returning "" do |namespace|
  127.           # 如果是一个Array
  128.           if record_or_hash_or_array.is_a?(Array)
  129.             # 如果包含元素是String或者Symbol,那么删除,并且拼接到空间
  130.             record_or_hash_or_array.delete_if do |record_or_namespace|
  131.               if record_or_namespace.is_a?(String) || record_or_namespace.is_a?(Symbol)
  132.                 namespace << "#{record_or_namespace}_"
  133.               end
  134.             end
  135.           end  
  136.         end
  137.       end
  138.   end













  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值