2020年6月12日金曜日

Ruby で Python のデコレータっぽいことをする

概要

Python のデコレータは関数の前後処理を実装することができる便利な機能です
Ruby でも同じことがしたいと思い試してみました

環境

  • macOS 10.15.5
  • Ruby 2.7.1p83

サンプルコード

  • vim hooks.rb
module Hooks
  def before(*method_names)
    to_prepend = Module.new do
      method_names.each do |name| 
        define_method(name) do |*args, &block|
          puts "before #{name}"
          super(*args,&block)
        end
      end
    end
    prepend to_prepend
  end
end

説明

最大のポイントは prepend を使っているところです
クラス (Example) が持っているメソッドをモジュール (Module.new したモジュール) で再度 define_method します
そしてそのモジュールを prepend することでクラスで呼ばれる同名のメソッドが呼ばれた際に prepend したモジュールで定義されたメソッドがコールされるという仕組みです

確認用スクリプト

  • vim test.rb
require './hooks'

class Example
  extend Hooks
  before :foo, :bar

  def foo
    puts "in foo"
  end

  def bar
    puts "in bar"
  end
end

a = Example.new
a.foo
  • ruby test.rb

=> before foo in foo

after の場合は

おそらく以下のような感じでいいと思います

module Hooks
  def after(*method_names)
    to_prepend = Module.new do
      method_names.each do |name| 
        define_method(name) do |*args, &block|
          super(*args,&block)
          puts "after #{name}"
        end
      end
    end
    prepend to_prepend
  end
end

最後に

Ruby で Python のデコレータっぽい処理を試してみました
Ruby にはデコレータパターンと呼ばれるデザインパターンが存在しますがこれは既存クラスの拡張っぽい機能なので少し異なります

参考サイト

0 件のコメント:

コメントを投稿