Hashによるオプション引数とActiveRecord::Base.extract_options_from_args!

以前、メソッドの引数としてHashを使うといいというエントリーを書きました。

その中で、特にオプション引数に対して有効ということを書きました。
今回は、オプション引数とActiveRecord::Base.extract_options_from_args!を組み合わせるとなかなか便利という話です

携帯の識別番号を設定するメソッド

参考はJpmobileのtestのソース

module ActionController
  class TestRequest < AbstractRequest
    attr_accessor :user_agent
  
    def guid
      @env["HTTP_X_DCMGUID"]
    end
  
    def guid=(id)
      @env["HTTP_X_DCMGUID"] = id
    end
  end
end

def set_ident(end_of_id = '1')
  @request.user_agent = "DoCoMo/2.0 D902i(c100;TB;W23H16;ser999999999999999;icc0000000000000000000#{end_of_id})"
  @request.guid = "xxxxxx#{end_of_id}"
end


ここに、identを出さないことをシミュレートする、guidだけ出すようにするという風に設定できるようにオプション引数を持たせようとします

def set_ident(end_of_id = '1', options = {})
  case 
  when !options[:no_icc]
    @request.user_agent = "DoCoMo/2.0 D902i(c100;TB;W23H16;ser999999999999999;icc0000000000000000000#{end_of_id})"
  when !options[:no_guid]
    @request.guid = "xxxxxx#{end_of_id}"    
  end
end

#icc, guidともにセットする
set_ident

#guidはセットしない
set_ident('1', :no_guid => true)


しかし、これだと、end_of_idに初期値がセットされている恩恵があまりない。オプション引数を設定する際には必ず第一引数を指定する必要がある。


そこでActiveRecord::Base.extract_options_from_args!を使う。ActiveRecord::Base.extract_options_from_args!の実装は次の通り

def extract_options_from_args!(args) #:nodoc:
  args.last.is_a?(Hash) ? args.pop : {}
end


つまり、渡された引数のうち、最後の引数がHashなら、それはオプション引数であるとあるとみなしている。これを使えばさっきのメソッドは以下のように改善できます。

def set_ident(*args)
  options = extract_options_from_args!(args)
  end_of_id = (args.first || '1')

  case 
  when !options[:no_icc]
    @request.user_agent = "DoCoMo/2.0 D902i(c100;TB;W23H16;ser999999999999999;icc0000000000000000000#{end_of_id})"
  when !options[:no_guid]
    @request.guid = "xxxxxx#{end_of_id}"    
  end
end

#icc, guidともにセットする
set_ident

#guidはセットしない
set_ident(:no_guid => true)


ActiveRecord::Base.extract_options_from_args!はなかなか使えると思う