YASD-TECH
YASD TECH
# Ruby

定数の参照とダブルコロン(スコープ解決演算子)

投稿日:2026/8/5

更新日:2026/8/5

ttitleImage

定数の参照とダブルコロン(スコープ解決演算子)

:: は「定数を参照するための演算子」。メソッド呼び出しの . と対になっている。
クラス名・モジュール名そのものも定数なので、Types::Enum::OrderStatusEnum のようなネームスペースの区切りにも同じ :: が使われる。

結論

  • . はメソッド、:: は定数。Order.VISIBLE_STATUSESNoMethodError になる
  • 同じクラスの中からは :: が要らない。定数は外側のスコープへ順に探しに行くので、自分の定数が先に見つかる
  • クラス名・モジュール名も定数。Types::Enum::X は「定数の入れ子を掘っていく」だけで、Order::VISIBLE_STATUSES と同じ仕組み
  • Rails の autoload はこの入れ子とディレクトリ構造を対応させている

最小の例

注文(Order)に「一覧画面に出すステータス」の定数を持たせる。

ruby
class Order < ApplicationRecord
  VISIBLE_STATUSES = %w[pending paid shipped].freeze
end
ruby
Order::VISIBLE_STATUSES  # => ["pending", "paid", "shipped"]
Order.VISIBLE_STATUSES   # => NoMethodError: undefined method 'VISIBLE_STATUSES' for class Order

VISIBLE_STATUSES はメソッドではなく定数なので、. では呼べない。

演算子 対象
. メソッド Order.statusesorder.status
:: 定数 Order::VISIBLE_STATUSES

内側からは :: が不要

定数は、書かれた場所から外側のスコープへ順に探しに行く。だから同じクラスの中ならそのまま書ける。

ruby
class Order < ApplicationRecord
  VISIBLE_STATUSES = %w[pending paid shipped].freeze

  def visible?
    VISIBLE_STATUSES.include?(status)   # 同じクラスの中なので、そのまま書ける
  end
end

別のクラスから見るときは、どこの定数かを明示する必要がある。

ruby
class Types::Enum::OrderStatusEnum < Types::BaseEnum
  Order::VISIBLE_STATUSES.each { |status| value status }
  # ^^^^^ 「Order クラスの中にある VISIBLE_STATUSES」
end

レシーバを省略できるメソッド(self.statusstatus と書けるやつ)と同じ構図で、
内側からは省略できて、外からは所属を明示する

探索の順番はこうなっている。

flowchart TD
    A["VISIBLE_STATUSES と書いた場所"] --> B["レキシカルスコープ<br/>Module.nesting を内側から外側へ"]
    B -->|見つからない| C["継承チェーン<br/>superclass の定数"]
    C -->|見つからない| D["トップレベル(Object)"]
    D -->|見つからない| E["NameError:<br/>uninitialized constant"]

Module.nesting は「今どのモジュールの中にいるか」のリストで、これが探索の最初の候補になる。


クラス名・モジュール名も定数

Ruby では大文字始まりの識別子はすべて定数。クラスもモジュールも例外ではない。

ruby
Order.class            # => Class   (Order は Class オブジェクトを指す定数)
Types.class            # => Module
defined?(Order)        # => "constant"

つまりネームスペースは、定数の中に定数が入っているだけ。

ruby
Types::Enum::OrderStatusEnum
# ^^^^^ 定数(Module)
#        ^^^^ その中の定数(Module)
#              ^^^^^^^^^^^^^^^ さらにその中の定数(Class)
flowchart LR
    T["Types<br/>(Module)"] --> E["Enum<br/>(Module)"]
    E --> C["OrderStatusEnum<br/>(Class)"]
    O["Order<br/>(Class)"] --> V["VISIBLE_STATUSES<br/>(Array)"]

Order::VISIBLE_STATUSES を辿るのと Types::Enum を辿るのは、まったく同じ動作。「定数の入れ子を掘っていく」だけ。


Rails の autoload とディレクトリ構造

この入れ子はファイル配置とそのまま対応する。Zeitwerk(Rails の autoloader)はこの規約でファイルを探す。

app/graphql/types/enum/order_status_enum.rb

class Types::Enum::OrderStatusEnum

ディレクトリ = モジュールの入れ子、ファイル名(snake_case)= クラス名(CamelCase)。
定数が見つからないエラーが出たときは、まずこの対応がズレていないかを疑うとよい。


ハマりどころ

① コンパクト記法だと外側のスコープが効かない

class Types::Enum::OrderStatusEnum という書き方(コンパクト記法)では、TypesTypes::Enum はレキシカルスコープに入らない

ruby
class Order; NAME = 'top-level Order'; end
module Types
  module Enum
    class Order; NAME = 'Types::Enum::Order'; end
  end
end
ruby
# コンパクト記法
class Types::Enum::OrderStatusEnum
  Module.nesting  # => [Types::Enum::OrderStatusEnum]  ← 自分だけ
  Order::NAME     # => "top-level Order"               ← 近いはずの Types::Enum::Order は見えない
end

# ネスト記法
module Types
  module Enum
    class OrderStatusEnum
      Module.nesting  # => [Types::Enum::OrderStatusEnum, Types::Enum, Types]
      Order::NAME     # => "Types::Enum::Order"        ← 近い方が勝つ
    end
  end
end

同名の定数がネームスペース内外にあるとき、記法を変えただけで参照先が変わる
明示したいときは ::Order と先頭に :: を付けるとトップレベルから引ける。

なお superclass の定数は、どちらの記法でも継承チェーン経由で見つかる(レキシカルスコープとは別の経路)。

:: でメソッドも呼べてしまう

文法上は Order::statuses のようにメソッドも呼べるが、やらない。読み手が「定数だろう」と誤解するため。

ruby
Order::statuses  # 動くが書かない

rubocop も Style/ColonMethodCall で警告する。メソッドは .、定数は :: で統一するのが慣例。

private_constant で外から隠せる

ruby
class Foo
  SECRET = 'x'
  private_constant :SECRET
end

Foo::SECRET   # => NameError: private constant Foo::SECRET referenced

他クラスから参照させる前提の定数(GraphQL の enum 生成に使うものなど)は、公開したままにしておく。

freeze を付ける理由

定数は「再代入すると警告が出る」だけで、中身の変更は止められない

ruby
VISIBLE_STATUSES = %w[pending paid shipped]
VISIBLE_STATUSES << 'canceled'   # 通ってしまう(警告も出ない)

配列やハッシュの定数には .freeze を付けて、破壊的変更を FrozenError にしておく。


関連

Index

  • 定数の参照とダブルコロン(スコープ解決演算子)
  • 結論
  • 最小の例
  • 内側からは :: が不要
  • クラス名・モジュール名も定数
  • Rails の autoload とディレクトリ構造
  • ハマりどころ
  • ① コンパクト記法だと外側のスコープが効かない
  • ② :: でメソッドも呼べてしまう
  • ③ private_constant で外から隠せる
  • ④ freeze を付ける理由
  • 関連