ruby引数処理に使えるテクニック


# nilガード

```ruby
def nil_guard( val=nil )
  val ||= "default"
end

p nil_guard()
  # "default"
p nil_guard( "test" )
  # "test"
```

本来nilガードはその変数が未定義でもその場で定義もできちゃうところが強力ですが、引数処理にも使えます.
引数デフォルトに`val="default"`としてもいいですが、nilが格納された変数が明示的に渡される場面も多いのでnilを防ぎたい局面では有効です.

```ruby
p nil_guard( false )
  # "default"
```

falseを明示的に引数に渡したいときは注意です.

# キーワード引数で必須項目

ruby2.1以降ではキーワード引数の特定の属性を必須化できます.
ラベルの後にデフォルト値を書かなければ必須項目となります.

```ruby
def required_keyword_args(first_name:, last_name:, age: nil)
  "#{first_name}, #{last_name} (#{age.to_s})"
end

p required_keyword_args()
  # ArgumentError
p required_keyword_args(first_name: "Isaac")
  # ArgumentError
p required_keyword_args(first_name: "Isaac", last_name: "Newton")
  # "Isaac, Newton ()"
p required_keyword_args(first_name: "Isaac", last_name: "Newton", age: 30)
  # "Isaac, Newton (30)"
```

# 可変長引数（アスタリスク）

引数に`*`をつけると可変長になります.
以下例のように前方から必須化することもできます.

```ruby
def variadic(arg1, arg2, *others)
  p arg1
  p arg2
  p others
end

variadic(1,2,3,4,5)
  # 1
  # 2
  # [3, 4, 5]
variadic(1,2)
  # 1
  # 2
  # []
```

引数に`**`をつけるとキーワード引数をハッシュで受け取ることができます.

```ruby
def variadic_keyword(arg, *array_args, **hash_args)
  p arg
  p array_args
  p hash_args
end

variadic_keyword(1,2,3,4,5)
  # 1
  # [2, 3, 4, 5]
  # {}
variadic_keyword(1,2,3, april:"spring", july:"summer")
  # 1
  # [2, 3]
  # {:april=>"spring", :july=>"summer"}
variadic_keyword(1,april:"spring", july:"summer")
  # 1
  # []
  # {:april=>"spring", :july=>"summer"}
```

ちなみに変数定義時ではなく、変数呼び出し時に引数に`*`をつけると配列を引数として渡すことができます.
`**`をつければハッシュをキーワード引数として展開できます.

```ruby
primes = [2,3,5,7,11,13]
variadic(*primes)
  # 2
  # 3
  # [5, 7, 11, 13]
```

```ruby
monthes = {april:"spring",may:"spring"}
variadic_keyword(1,2,3,**monthes)
  # 1
  # [2, 3]
  # {:april=>"spring", :may=>"spring"}
```

# stringify_keys

rubyではhashのキーがstringかsymbolかを間違えてはまることがよくあります.
active supportの`stringify_keys`を使うとハッシュのキーをstringに変換できます.
どちらの形式でもうまく動くようにメソッドが設計されていると親切だと思います.

```ruby
require "active_support"
require "active_support/core_ext"

def stringify( hash )
  hash = hash.stringify_keys
end

p stringify( january:"winter", february:"winter" )
  # {"january"=>"winter", "february"=>"winter"}
```

ネストされたハッシュのキーをstringifyするには`deep_stringify_keys`を使います.

# symbolize_keys

`stringify_keys`の逆をやりたいときは`symbolize_keys`です.

```ruby
require "active_support"
require "active_support/core_ext"

def symbolize( hash )
  hash = hash.symbolize_keys
end

p symbolize( "january"=>"winter", "february"=>"winter" )
  # {:january=>"winter", :february=>"winter"}
```

ネストされたハッシュのキーをsymbolizeするには`deep_symbolize_keys`を使います.

# with_indifferent_access

active supportの`with_indifferent_access`メソッドを使うと、stringでもsymbolでもアクセスできるハッシュを作成できます.
ハッシュが拡張された`ActiveSupport::HashWithIndifferentAccess`オブジェクトが返されます.

```ruby
require "active_support"
require "active_support/core_ext"

def indifferent_access( hash )
  hash = hash.with_indifferent_access
  p hash[:january]
  p hash["january"]
  p hash[:february]
  p hash["february"]
  p hash[:march]
  p hash["march"]
end

indifferent_access( "january"=>"１月", "february"=>"２月", march: "３月" )
  # "１月"
  # "１月"
  # "２月"
  # "２月"
  # "３月"
  # "３月"
```


# hash delete

`Hash#delete`メソッドは、レシーバのハッシュからデータ削除し、削除されたvalueを返します. この特徴を使って、たとえば１つのキーワード引数集合の中から性質の異なる属性を抜き出すことができます.

```ruby
def hash_delete( hash )
  text_color = hash.delete(:text_color)
  p text_color
  p hash
end

hash_delete(first_name:"Issaac", last_name:"Newton", text_color:"red")
  # "red"
  # {:first_name=>"Issaac", :last_name=>"Newton"}
```

railsのform helperをラップしたメソッドを作って独自オプションを追加したいときなどに便利です.

```ruby
def custom_text_field( name, value, options={} )
  error_message = options.delete :error_message
  tag_html      = text_field_tag( name, value, options )
  wrap_with_validation_error( tag_html, error_message: error_message )
end
```

# assert_valid_keys

active supportの`assert_valid_keys`は許可するハッシュキーだけを通して、それ以外のものが含まれたら例外を投げます.

```ruby
require "active_support"
require "active_support/core_ext"

def assert_args( **args )
  args.assert_valid_keys(:first_name, :last_name)
end

assert_args( first_name: "Issaac" )
  # last_nameが足りないが例外にはならない
assert_args( first_name: "Issaac", last_name: "Newton" )
  # 許可リストのキーしか含まないため問題なし
assert_args( first_name: "Issaac", last_name: "Newton", age: 30 )
  # 許可リスト以外のキー(age)が含まれるため例外
  # Unknown key: :age. Valid keys are: :first_name, :last_name (ArgumentError)
```

# Environment

```
% ruby -v
ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-linux]
```

他にも引数処理に使える便利な方法があれば教えてください.