LoginSignup
13

More than 5 years have passed since last update.

includeしたメソッドで呼び出す定数のスコープの話

Last updated at Posted at 2016-02-10

include

モジュールのメソッドをクラスのメソッドとして取り込める便利なあれですね。

module M
  def m
    puts "Module M!"
  end
end

class A
  include M
end

A.new.m
#=> Module M!

定数利用

ところで、取り込まれるクラスの定数を利用したいとします。

module M
  def m
    puts CONST
  end
end

class A
  CONST = "Class A!"
  include M
end

しかし悲しい哉、これは例外を吐きます。

A.new.m
#=> uninitialized constant M::CONST (NameError)

定数を探すスコープがincludeされるモジュールのままなのですね。
これはいけない。

include先の定数を指定する

selfを使ってやれば望み通り、includeする側の定数を参照できます。


module M
  def m
    puts self.class::CONST
  end
end

class A
  CONST = "Class A!"
  include M
end

A.new.m #=> Class A!

class B
  CONST = "Class B!"
  include M
end

B.new.m #=> Class B!

extendでも同様に

selfが指し示すものがインスタンスではなくクラスそのものである点に注意しましょう。


module M
  def m
    puts self::CONST
  end
end

class C
  CONST = "Class C!"
  extend M
end

C.m #=> Class C!

includeでもextendでも同じように使いたい

selfClassModuleの時とそれ以外で場合分けする?

module M
  def m
    puts (self.is_a?(Class) || self.is_a?(Module) ? self : self.class)::CONST
  end
end

もうちょっと綺麗な方法がありそうな気もしますが……。
そもそも同じメソッドをインスタンスメソッドとしてもクラスメソッドとしても使いたいという状況があまり無いかも。

定数がない場合、事前に警告する

上記のやり方でincludeするクラスの定数を取得する事ができました。
しかしこの方法を取り、間違えて必要な定数の存在しないクラスにincludeしてしまった場合、そのメソッドが呼び出されるまで例外が飛びません

module M
  def m
    puts self.class::CONST
  end
end

class A
  include M # CONSTが必要なモジュールをincludeしてしまった!
end
# しかしこの段階では異常は起きない。

a = A.new

#
# 何か処理...
#

a.m #=> ここで例外!

これはバグの元ですね。
そこでincludedを利用して、クラス定義時にincludeの可否を決定してあげましょう。

module M
  def self.included(klass)
    klass::CONST
  rescue NameError
    raise "#{klass} is expected const CONST to include module M."
  end

  def m
    self.class::CONST
  end
end

これでクラス定義時に、必要な定数が無い場合例外を飛ばすようになりました。

ただし注意してください。
Rubyは極めて自由な言語なので、後からクラス内に定数を追加する事も可能です。

class A
  include M
end

# これは例外を吐く
# A.new.m

class A
  CONST = "Class A!"
end

A.new.m #=> Class A!

こうした機能を活用している場合、include時に必要な定数が無いからといって例外を投げるようでは困る事態もあるやもしれません。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13