accept = ENV["HTTP_ACCEPT"] def accept.to_a split(/\s*,\s*/).inject([]) do |ret, value| media_type, qs = value.split(/\s*;\s*q=/) ret << [media_type, qs ? qs.to_f : 1.to_f] end end def accept.negotiate(servable) # servable= [{:media_type => "application/xhtml+xml", :q => 1.0, :flavour => "xhtml"}, ...] media_types = to_a.collect {|i| i[0] } servable.collect {|i| [i[:media_type], i[:q], i[:flavour]] }.select {|i| media_types.include?(i[0]) }.sort_by {|i| (i[1] * 1000) - i[0].count("*") + i[0].count(";") + i[0].count("+") }.pop
こんなの書いた。なにがなんでもメソッドチェーンしたかったので、特異メソッドで。普通に関数(的メソッド)でいい気もする。
accept#negotiateはコメントにあるようなハッシュを要素にもつ配列を引数に受け取り、それとUAのAcceptの値を比較して、UAが要求するメディアタイプとサーバが提供できるメディアタイプの積集合を得る。それをqvalueの値でソートして最大になったメディアタイプ(とqvalueとflavourの組)を返す。みたいなかんじ。
あ、qvalueをかけあわせてない…。だめじゃん。要再考。
def accept.negotiate(servable) servable.map {|i| [i[:media_type], i[:q], i[:flavour]] }.sort_by {|i| ((to_a.assoc(i[0]) || 0) * i[1] * 1000) - i[0].count("*") + i[0].count("+") + i[0].count(";") }.pop end
実質、Enumerable#sort_byだけで実装できた。なんという…。