環境
Ruby 2.5.0
Rails 5.2.0
元々がJava使いだったもので、RoRを使っていても複雑な処理などは親子関係を使って処理を共通化できるか検討することが多い。
concern等を検討することももちろんあるが、バッチでいろんな種類の集計処理をするときなどは集計用親クラスをどーんと作ってしまった方が恩恵が多い気がする。
ということで、今回は継承の話。
以下のようなクラス群があったとする。
親クラス
1 2 3 4 5 6 7 8 |
class BaseTask self.abstract_class = true def daily_task(date) # 独自処理は本メソッドをオーバーライドすること raise NotImplementedError.new("You must implement #{self.class}##{__method__}") end end |
子クラス
1 2 3 4 5 |
class BookListTask < BaseTask def daily_task(date) puts "#{date}日に存在する本の一覧を出力する処理" end end |
1 2 3 |
class EmployeeTask < BaseTask # 共通処理 end |
孫クラス
1 2 3 4 5 |
class EmployeeListTask < EmployeeTask def daily_task(date) puts "#{date}日に所属中の従業員一覧を出力する処理" end end |
1 2 3 4 5 |
class salaryListTask < EmployeeTask def daily_task(date) puts "#{date}日に所属中の従業員の給料一覧を出力する処理" end end |
このような作りだと、当然呼び出す側の心理としては親クラスのdaily_task
を1発呼んで全ての集計をまとめて行いたい。
ということで、以下にexec_daily_task
を用意した。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
class BaseTask self.abstract_class = true def daily_task(date) # 独自処理は本メソッドをオーバーライドすること raise NotImplementedError.new("You must implement #{self.class}##{__method__}") end def self.exec_daily_task(date) date = Date.yesterday # 子クラス、孫クラスを呼び出してループ self.descendants.each{|klass| # 抽象クラスでない場合のみdaily_task実行 if !klass.abstract_class klass.new.daily_task date end } end end |
しかし、このexec_daily_task
を呼び出しても何も起きない。沈黙。完全スルー。
あれれー?と思いデバッグしたところ、self.descendants
が空の配列になっていた。そら何も起きんわ。
どうやらdescendants
を使うには、読み込み先のファイルが事前にロードされている必要があるんですね。
なので、以下のようにrequire_dependency
を足してあげましょう。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
class BaseTask self.abstract_class = true def daily_task(date) # 独自処理は本メソッドをオーバーライドすること raise NotImplementedError.new("You must implement #{self.class}##{__method__}") end def self.exec_daily_task(date) # 必要なもののみ読み込むように。親子クラスはまとめて同一フォルダに入れた方が効率的。 Dir[Rails.root.join('path', 'to', 'dir', '*.rb')].each do |file| require_dependency file end date = Date.yesterday # 子クラス、孫クラスを呼び出してループ self.descendants.each{|klass| # 抽象クラスでない場合のみdaily_task実行 if !klass.abstract_class klass.new.daily_task date end } end end |
これで無事継承先のメソッドを呼び出せました。