Ruby で mixin したモジュールを削除する gem をつくった
Ruby でメタプログラミングをやっているとどうしても『mixin したモジュールを削除したい』という欲求にかられるのでつくりました。
インストール
$ gem install unmixer
使い方
require "unmixer" # Unmixer は refinements を使用してるので using して使用します using Unmixer module M1; end module M2; end module M3; end class X include M1 prepend M2 end p X.ancestors # => [M2, X, M1, Object, Kernel, BasicObject] # include したモジュールを削除 X.instance_eval { uninclude M1 } p X.ancestors # => [M2, X, Object, Kernel, BasicObject] # include したモジュール以外は削除できない X.instance_eval { uninclude M2 } p X.ancestors # => [M2, X, Object, Kernel, BasicObject] # prepend したモジュールを削除 X.instance_eval { unprepend M2 } p X.ancestors # => [X, Object, Kernel, BasicObject] X.extend M3 p X.singleton_class.ancestors # => [#<Class:X>, M3, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] # extend したモジュールを削除 X.unextend M3 p X.singleton_class.ancestors # => [#<Class:X>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] # #extend にブロックを渡した場合、ブロック内でのみ mixin される X.extend M1 do # mixin only in block. p X.singleton_class.ancestors # => [#<Class:X>, M1, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] p X.singleton_class.ancestors.include? M1 # => true end p X.singleton_class.ancestors # => [#<Class:X>, #<Class:Object>, #<Class:BasicObject>, Class, Module, Object, Kernel, BasicObject] p X.singleton_class.ancestors.include? M1 # => false
元々は uninclude という gem があったのですが、こちらは比較的新しい Ruby では動作しなかったのでそれを焼き直した形になります。
現状は、Ruby 2.1 〜2.4 であれば動作すると思います。
ただ、実装が結構無理なりなので今後のサポートや意図しない動作が発生する可能性はあります。
今回初めて Ruby で C拡張のコードを書いたんですが、やはり C言語つらいですね…。
最初は C 側で全部を実装していたんですが、途中でつらくなったので C 側は最小限の処理のみ実装して結局 Ruby 側でガシガシ実装しました。
多少不安なところはあるんですが、やはり mixin を削除できる機能はかなり便利です。
言語仕様側で導入されないかなあ…。