投稿日:2026/8/5
更新日:2026/8/5

:: は「定数を参照するための演算子」。メソッド呼び出しの . と対になっている。
クラス名・モジュール名そのものも定数なので、Types::Enum::OrderStatusEnum のようなネームスペースの区切りにも同じ :: が使われる。
. はメソッド、:: は定数。Order.VISIBLE_STATUSES は NoMethodError になる:: が要らない。定数は外側のスコープへ順に探しに行くので、自分の定数が先に見つかるTypes::Enum::X は「定数の入れ子を掘っていく」だけで、Order::VISIBLE_STATUSES と同じ仕組み注文(Order)に「一覧画面に出すステータス」の定数を持たせる。
class Order < ApplicationRecord
VISIBLE_STATUSES = %w[pending paid shipped].freeze
end
Order::VISIBLE_STATUSES # => ["pending", "paid", "shipped"]
Order.VISIBLE_STATUSES # => NoMethodError: undefined method 'VISIBLE_STATUSES' for class Order
VISIBLE_STATUSES はメソッドではなく定数なので、. では呼べない。
| 演算子 | 対象 | 例 |
|---|---|---|
. |
メソッド | Order.statuses、order.status |
:: |
定数 | Order::VISIBLE_STATUSES |
:: が不要定数は、書かれた場所から外側のスコープへ順に探しに行く。だから同じクラスの中ならそのまま書ける。
class Order < ApplicationRecord
VISIBLE_STATUSES = %w[pending paid shipped].freeze
def visible?
VISIBLE_STATUSES.include?(status) # 同じクラスの中なので、そのまま書ける
end
end
別のクラスから見るときは、どこの定数かを明示する必要がある。
class Types::Enum::OrderStatusEnum < Types::BaseEnum
Order::VISIBLE_STATUSES.each { |status| value status }
# ^^^^^ 「Order クラスの中にある VISIBLE_STATUSES」
end
レシーバを省略できるメソッド(self.status を status と書けるやつ)と同じ構図で、
内側からは省略できて、外からは所属を明示する。
探索の順番はこうなっている。
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 では大文字始まりの識別子はすべて定数。クラスもモジュールも例外ではない。
Order.class # => Class (Order は Class オブジェクトを指す定数)
Types.class # => Module
defined?(Order) # => "constant"
つまりネームスペースは、定数の中に定数が入っているだけ。
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 を辿るのは、まったく同じ動作。「定数の入れ子を掘っていく」だけ。
この入れ子はファイル配置とそのまま対応する。Zeitwerk(Rails の autoloader)はこの規約でファイルを探す。
app/graphql/types/enum/order_status_enum.rb
↓
class Types::Enum::OrderStatusEnum
ディレクトリ = モジュールの入れ子、ファイル名(snake_case)= クラス名(CamelCase)。
定数が見つからないエラーが出たときは、まずこの対応がズレていないかを疑うとよい。
class Types::Enum::OrderStatusEnum という書き方(コンパクト記法)では、Types や Types::Enum はレキシカルスコープに入らない。
class Order; NAME = 'top-level Order'; end
module Types
module Enum
class Order; NAME = 'Types::Enum::Order'; end
end
end
# コンパクト記法
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 のようにメソッドも呼べるが、やらない。読み手が「定数だろう」と誤解するため。
Order::statuses # 動くが書かない
rubocop も Style/ColonMethodCall で警告する。メソッドは .、定数は :: で統一するのが慣例。
private_constant で外から隠せるclass Foo
SECRET = 'x'
private_constant :SECRET
end
Foo::SECRET # => NameError: private constant Foo::SECRET referenced
他クラスから参照させる前提の定数(GraphQL の enum 生成に使うものなど)は、公開したままにしておく。
freeze を付ける理由定数は「再代入すると警告が出る」だけで、中身の変更は止められない。
VISIBLE_STATUSES = %w[pending paid shipped]
VISIBLE_STATUSES << 'canceled' # 通ってしまう(警告も出ない)
配列やハッシュの定数には .freeze を付けて、破壊的変更を FrozenError にしておく。
Order.statuses はメソッドなので . で呼ぶ