Railsを使わずにConcern
だけ使う方法を調べていたところ、特定のクラスの名前空間以下にモジュールを作っている方法も気になったので調べてみました。
サンプルコード
Railsを使わずにActiveSupport
だけでConcern
を使うコードは以下です。
path_to/container.rb
require 'active_support'
ActiveSupport::Dependencies.autoload_paths << File.dirname(__FILE__)
class Container
include Container::Feature
end
Container.new.instance_feature #=> "Instance features are loaded!"
Container.class_feature #=> "Class features are loaded!"
path_to/container/feature.rb
module Container::Feature
extend ActiveSupport::Concern
included do
def instance_feature
p 'Instance features are loaded!'
end
end
class_methods do
def class_feature
p 'Class features are loaded!'
end
end
end
Railsを使っている時と違い、autoload_paths
に対象のディレクトリが自動で入ってないことでハマりました・・。
ここからContainer::Feature
という形で名前空間の中に入れたままConcern
を使っている時の感覚で別ファイルで書こうとすると以下の理由で困りました。
- モジュールを
include
する際にContainer::Feature
が定義されておらずエラーになる
(uninitialized constant Container::Feature (NameError)
が起きる) - 先に
Container::Feature
モジュールを定義するとContainer
クラスの名前空間がなくてエラーになる
(uninitialized constant Container (NameError)
が起きる)- 先に
Container
というモジュールを定義して名前空間を作ろうとするとクラス名と被ってエラーになる
- 先に
ActiveSupport
ではconst_missing
をオーバライドすることでこれらの問題を解決してくれていました。
ちなみにこれをActiveSupport
を使わずに同じ動作をする状態にするなら以下のようになります。
class Container
module Feature
def self.included base
base.extend ClassMethods
end
def instance_feature
p 'Instance features are loaded!'
end
module ClassMethods
def class_feature
p 'Class features are loaded!'
end
end
end
include Feature
end
Container.new.instance_feature #=> "Instance features are loaded!"
Container.class_feature #=> "Class features are loaded!"