1. Qiita
  2. 投稿
  3. Ruby

Ruby初心者を脱した人が悩みがちな、ちょっと特殊な記法・演算子・イディオム

  • 3
    いいね
  • 1
    コメント

初心者がRubyの基礎を覚えたところで他人のコードを読むようになると、*argsmap(&:to_s) のような謎の記法がでてきます。
この手の記法は名前がわからないとググラビリティが低くなるため、人によってはつまづきとなることが多いようです。
尋ねられることがたまにありますので、この機会にRails開発でよく使われるものを中心にまとめてみます。

引数

*args

変数名の冒頭に*(アスタリスク)が付いているもので、「可変長引数」または「splat引数」とかいいます。
引数を複数個設定でき、さらにメソッド内部で引数を配列として受け取ることができます。

def splatter(*args)
  p args
end

splatter('foo')
# => ['foo']

splatter('foo', 'bar', 'baz')
# => ['foo', 'bar', 'baz']

なお、慣用的にargumentsを表す*argsと名付けられることが多いですけど、ただの変数名なので*hogeでも何でも大丈夫です。

**opts

**(アスタリスク2つ)が最初にくると「キーワード引数」といい、メソッド内部にハッシュが渡されるようになります。
オプション設定が必要なメソッドで使われることが多いと思います。

def hello_with_option(msg, **opts)
  name = opts[:name]
  time = opts[:time] || Time.now
  puts "#{msg} #{name}@#{time}"
end

hello_with_option("Hello!", name: "nashirox")
# => Hello! nashirox@2017-02-07 16:58:46 +0900

デフォルト引数で空ハッシュ opts = {} を設定しても同じことになります。
キーワード引数はRuby2.0から導入されている比較的新し目の文法なので、こっちの書き方もよくみられるように思います。

&block

仮引数(例えばblock)に&を前置すると、「ブロック引数」となります。
メソッドはこの仮引数を介してブロックを受け取れるようになります。

def this_is_block(&block)
  block.call # yield でも同じ
end

this_is_block do
  p "This is my block"
end
# => "This is my block"

より詳細には、&を前置することでブロックをProcオブジェクト(手続きオブジェクト)に変換しています。

&:method_name

mapなどブロックで配列の中身を受け取るようなメソッドに、ブロック代わりに&を前置したメソッド(:method_name)を渡すことができます。

# 配列の要素をstringにして返す
[1, 2, 3].map(&:to_s)
# => ["1", "2", "3"]

# 上と同じ
[1, 2, 3].map { |i| i.to_s }
# => ["1", "2", "3"]
# 配列内の総和を得る
[1, 2, 3].inject(&:+)
# => 6

なぜこう言ったことができるかについてはProcに関する理解が必要なのでここでは割愛します、調べると色々出てくると思います。

参考:Procを制する者がRubyを制す(嘘)

演算子

||=

通称「nilガード」などと呼ばれ、初期化イディオムとして使われます。
左辺がtrueだったら何もせず、falseだったら右辺を代入します。

# 初期化されていない変数の呼び出し
foo
# => NameError: undefined local variable or method `foo' for main:Object


# fooが偽なので右辺が代入される
foo ||= true
foo
# => true

# fooが代入されて真となるので、右辺が代入されない
foo ||= 'hoge'
foo
# => true

以下は全て同じ結果です。

foo = foo || true
foo || (foo = true)

nilガードを使わないと、下記のようなif文になってしまい冗長ですね。

if foo != nil
  foo = foo
else
  foo = true
end

<=>

人によって(?)は未確認飛行物体のように見えるようで、「宇宙船演算子」や「UFO演算子」と通称されます。
左辺と右辺を比較して、左辺側が大きければ 1、逆に小さければ -1 を返す。双方等しければ 0、比較できなければ nil を返します。

100 <=> 200   # => -1
200 <=> 100   # =>  1
200 <=> 200   # =>  0
100 <=> '100' # => nil

なお自作クラスで比較演算したい場合、Comparable モジュールをクラスにインクルードし、<=> 演算子を定義することで可能になります。

class Human
  include Comparable
  attr_reader :age

  def initialize(name, age)
    @name = name
    @age  = age
  end

  def <=>(other)
    @age <=> other.age
  end
end

taro = Human.new("太郎", 25)
jiro = Human.new("次郎", 18)

p taro < jiro
# => false
p taro > jiro
# => true
p taro <=> jiro
# 1

->

ラムダ式で用いるリテラルで、「lambdaリテラル」や「アロー演算子」と呼ばれます。

# 定義
foo = -> (x) { x + x }
# 呼び出し
foo[5]   # => 10
foo.(5)  # => 10
foo.call 5 # => 10

# 通常のlambdaの書き方   
bar = lambda { |x| x + x }
bar.call 5 # => 10

Railsだとモデルのスコープで使われたりしていますね。

app/modes/comment.rb
class Comment < ActiveRecord::Base
  scope :created_before, ->(date) { where("created_at < ?", date) }
end

Hoge::Foo

::演算子を使うと、あるクラスまたはモジュールで定義された定数を外部から参照することができます。
Railsの定数管理の手法として、initializers下にアプリケーション全体で使う定数を配置する方法などがありますね。

config/initializers/constants.rb
module Constants
  AgeStart = 0
  AgeEnd   = 99
end

Constantsモジュールで登録可能年齢の上限と下限を定義して、モデルで呼び出します。

app/modles/customer.rb
class Customer < ActiveRecord::Base
  start = Constants::AgeStart #=> 0
  end   = Constants::AgeEnd   #=> 99
  validates :age, :inclusion => { :in => start..end }
end

なお、左辺無しの::Fooと書けたりもして、これはトップレベルの定数を呼び出しています。

class Object
  # Objectクラスに定数Fooを追加
  Foo = 'foo'
end

::Foo
# => "foo"

!!

すべてをtrueかfalseにします。
nilではなく、必ずfalseで返却したい場合などに使います。
(なお、エクスクラメーションマークが一つだと否定演算子ですね)

!!nil   #=> false
!!"abc" #=> true
!!false #=> false

どういうところに使うのかと、Railsだと次のよう使い方があるようです。

def logged_in?
  !!session[:user_id]
end

呼び方ですが、日本語圏では見当たらず、欧米では「double-bang」や「bang-bang」などと言うようです(初めて知った)。

組み込み変数

$で始まる変数はグローバル変数ですが、その中でもすでに値が入っている特殊な変数が「組み込み変数」です。
似たような記号が多くて覚えづらいのですが、最低限3つを押さえておけば良いと思います。

$0

現在実行中のRubyスクリプトの名前が入った変数です。

sample.rb
class Sample
  def show_file_name
    puts "This file name is #{$0}"
  end
end

Sample.new.show_file_name
$ ruby sample.rb
# => This file name is sample.rb

$1, $2, $3..$n

最後に成功したパターンマッチでn番目の括弧にマッチした文字列が入っています。

"foo bar baz" =~ /(f\w+)\s(\w+)/
p $1  #=> "foo"
p $2  #=> "bar"
p $3  #=> nil

$&

最後に成功した正規表現のパターンマッチでマッチした文字列が入っています。

"foo bar baz" =~ /(f\w+)\s(\w+)/
p $& #=> "foo bar"

組み込み定数

ENV

Rubyで標準で用意されている組み込み定数がいくつかありますが、Railsの開発でよく用いるのはENVのみです。
ENVは環境変数を表すオブジェクトで、ENV['KEY_NAME']のような形でKEY_NAMEと合致する値を取り出すことができます。
Railsではsecret.ymlの中でENV["SECRET_KEY_BASE"]のように環境変数を取り出しています。

config/secrets.yml
development:
  secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

test:
  secret_key_base: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

production:
  secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>

環境変数をどう設定するかは割愛します。

パーセント記法

%w( )、%i( )、%s( )など

「パーセント記法」と呼ばれ、配列作成などによく使われます。
解説は各所に上がっていますので、詳細は別記事などを参照してください

# %w 文字列配列
fruits = %w(apple orange mango)
# => ['apple', 'orange', 'mango']
# %i シンボル配列
fruits = %i(apple orange mango)
# => [:apple, :orange, :mango]

なお、文字列配列は%記法を用いて書くことが推奨されています

以上です。なにか足すようなものがあったら追加します。
それではよきRubyライフを!