読者です 読者をやめる 読者になる 読者になる

WEB系エンジニアの勉強日記

Baby steps to Giant strides!

ActionDispatch::Http::URLのコードリーディング

概要

ActionDispatch::Http::URLモジュールに諸々追加している。

pickup

class << self でクラスメソッドをまとめて定義している。


hostからドメインを取得する。 Array#last は要素数を指定できる。

def extract_domain_from(host, tld_length)
  host.split('.').last(1 + tld_length).join('.')
end

Arrayの添え字部分にlengthを指定することで、部分取得できる

def extract_subdomains_from(host, tld_length)
    parts = host.split('.')
    parts[0..-(tld_length + 2)]
end

url_for: アプリケーションが参照するURLを生成

http://railsdoc.com/references/url_for


Hash#to_params

ActiveSupportでHash#to_paramsが定義されている。 HashをURLのパラメーター形式に変換する。

Enumerable#collectのendに.で戻り値の配列が参照できるんですね。

class Hash
  # Returns a string representation of the receiver suitable for use as a URL
  # query string:
  #
  #   {name: 'David', nationality: 'Danish'}.to_query
  #   # => "name=David&nationality=Danish"
  #
  # An optional namespace can be passed to enclose key names:
  #
  #   {name: 'David', nationality: 'Danish'}.to_query('user')
  #   # => "user%5Bname%5D=David&user%5Bnationality%5D=Danish"
  #
  # The string pairs "key=value" that conform the query string
  # are sorted lexicographically in ascending order.
  #
  # This method is also aliased as +to_param+.
  def to_query(namespace = nil)
    collect do |key, value|
      unless (value.is_a?(Hash) || value.is_a?(Array)) && value.empty?
        value.to_query(namespace ? "#{namespace}[#{key}]" : key)
      end
    end.compact.sort! * '&'
  end

  alias_method :to_param, :to_query
end

path周りのノーマライズ処理は、ActionDispatch::Journey::Route::Utilsに定義されている。

def escape(component, pattern)
  component.gsub(pattern){ |unsafe| percent_encode(unsafe) }.force_encoding(US_ASCII)
end

URLに使用可能な文字以外をエスケープする処理 gsubにブロックを渡すことで、置換処理にヒットした部分を使うことができる。

URL#add_anchorのエスケープ処理に使われているので調べてみた。


add_trailing_slash 末尾にスラッシュを追加する処理

queryが入っているかどうかで条件分けしている。

def add_trailing_slash(path)
          # includes querysting
          if path.include?('?')
            path.sub!(/\?/, '/\&')
          # does not have a .format
          elsif !path.include?(".")
            path.sub!(/[^\/]\z|\A\z/, '\&/')
          end
        end

normalize_protocol protocolをnormalizeする処理。 case分に正規表現をして、$1で参照している。

def normalize_protocol(protocol)
          case protocol
          when nil
            "http://"
          when false, "//"
            "//"
          when PROTOCOL_REGEXP
            "#{$1}://"
          else
            raise ArgumentError, "Invalid :protocol option: #{protocol.inspect}"
          end
        end

url method. コメントの書き方がすごくわかりやすい。

# Returns the complete URL used for this request.
      #
      #   class Request < Rack::Request
      #     include ActionDispatch::Http::URL
      #   end
      #
      #   req = Request.new 'HTTP_HOST' => 'example.com'
      #   req.url # => "http://example.com"
      def url
        protocol + host_with_port + fullpath
      end

x_forward_hostの扱いについて

http://qiita.com/mechamogera/items/32db29aa0db91df704ba


port @portが定義されていなければ、ホストデータ等からポート情報を取得するという処理だけど、このbeginの使い方の情報が検索しても見つからなかった。

def port
        @port ||= begin
          if raw_host_with_port =~ /:(\d+)$/
            $1.to_i
          else
            standard_port
          end
        end
      end