Upgrade to Pro — share decks privately, control downloads, hide ads and more …

ActiveSupport::Concernで開くメタプログラミングの扉 #heiseirubykaigi / The door of meta-programing is opened by ActiveSupport::Concern

Shu OGAWARA
December 14, 2019

ActiveSupport::Concernで開くメタプログラミングの扉 #heiseirubykaigi / The door of meta-programing is opened by ActiveSupport::Concern

2019/12/14の平成Ruby会議01で発表したスライドです。

複数の誤りが発見されたので、2020/05/03に修正しました。詳しくはスライド内の変更履歴をご覧ください。

Shu OGAWARA

December 14, 2019
Tweet

More Decks by Shu OGAWARA

Other Decks in Technology

Transcript

  1. มߋཤྺ  ❑  ❏ ฏ੒3VCZձٞ ॳ൛ެ։ ❑  ❏

    JODMVEFͱFYUFOEΛऔΓҧ͍͑ͯͨՕॴ͕ ෳ਺͋ͬͨͷΛमਖ਼ ❏ ޡͬͨαϯϓϧίʔυΛमਖ਼
  2. 

  3. 

  4. Ϟδϡʔϧ͕ґଘ͢ΔϞδϡʔϧ module SecondLevelModule def second_method p 'second' end end module

    FirstLevelModule extend SecondLevelModule def first_method self.class.second_method p 'first' end end class BaseClass include FirstLevelModule end BaseClass.new.first_method # => NoMethodError JODMVEFͨ͠ϞδϡʔϧͷதͰ ͞ΒʹผͷϞδϡʔϧͰఆٛ͞Εͨ ΫϥεϝιουΛݺͼग़͢ͱ /P.FUIPE&SSPS
  5. Ϟδϡʔϧ͕ґଘ͢ΔϞδϡʔϧ module SecondLevelModule def second_method p 'second’ end end module

    FirstLevelModule include SecondLevelModule def first_method self.class.second_method p 'first’ end end class BaseClass extend SecondLevelModule include FirstLevelModule end BaseClass.new.first_method # => “second” # “first” ͜ΕͳΒͪΌΜͱಈ͘
  6. ᶃΫϥεϝιουΛಉ࣌ʹఆٛ module M def self.included(base) base.extend ClassMethods base.class_eval do scope

    :disabled, -> { where(disabled: true) } end end module ClassMethods ... end end JODMVEF͞Εͨͱ͖ʹൃՐ Ҿ༻ɿ"DUJWF4VQQPSU$PODFSO IUUQTBQJSVCZPOSBJMTPSHDMBTTFT"DUJWF4VQQPSU$PODFSOIUNM
  7. ᶃΫϥεϝιουΛಉ࣌ʹఆٛ module M def self.included(base) base.extend ClassMethods base.class_eval do scope

    :disabled, -> { where(disabled: true) } end end module ClassMethods ... end end ΫϥεϚΫϩͱΫϥεϝιουΛ ແཧ΍ΓJODMVEF͞ΕͨઌͰ ఆٛ͢Δ
  8. ᶄϞδϡʔϧ͕ґଘ͢ΔϞδϡʔϧ module SecondLevelModule ... end module FirstLevelModule extend SecondLevelModule def

    self.included(base) base.send :extend, SecondLevelModule end ... end class BaseClass include FirstLevelModule ... end BaseClass.new.first_method # => “second” # “first” ґଘϞδϡʔϧΛແཧ΍Γ FYUFOE͢Δ
  9. ᶃ ϞδϡʔϧͰશ෦ੜ΍ͤΔ module M extend ActiveSupport::Concern included do scope :disabled,

    -> { where(disabled: true) } end class_methods do ... end end ΫϥεϚΫϩΛ *ODMVEFEͷϒϩοΫʹॻ͘
  10. ᶃ ϞδϡʔϧͰશ෦ੜ΍ͤΔ module M extend ActiveSupport::Concern included do scope :disabled,

    -> { where(disabled: true) } end class_methods do ... end end ΫϥεϝιουΛ DMBTT@NFUIPETͷϒϩοΫʹॻ͘ $MBTT.FUIPETϞδϡʔϧ΋Մ
  11. ᶄ ґଘϞδϡʔϧಡࠐෆཁ module SecondLevelModule extend ActiveSupport::Concern def second_method p 'second'

    end end module FirstLevelModule extend ActiveSupport::Concern extend SecondLevelModule def first_method self.class.second_method p 'first' end end class BaseClass include FirstLevelModule end BaseClass.new.first_method # => “second” # “first” ґଘϞδϡʔϧΛ FYUFOE͠ͳͯ͘΋ ͪΌΜͱಈ͘
  12. "4$͸͜ΕΛͨͬͨߦͰ࣮ݱ # frozen_string_literal: true module ActiveSupport module Concern class MultipleIncludedBlocks

    < StandardError def initialize super "Cannot define multiple 'included' blocks for a Concern" end end def self.extended(base) base.instance_variable_set(:@_dependencies, []) end def append_features(base) if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false else return false if base < self @_dependencies.each { |dep| base.include(dep) } super base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block) if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end def class_methods(&class_methods_module_definition) mod = const_defined?(:ClassMethods, false) ? const_get(:ClassMethods) : const_set(:ClassMethods, Module.new) mod.module_eval(&class_methods_module_definition) end end end
  13. ᶃϞδϡʔϧͰશ෦ੜ΍ͤΔ ͷίʔυ def included(base = nil, &block) if base.nil? if

    instance_variable_defined?(:@_included_block) if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end def class_methods(&class_methods_module_definition) mod = const_defined?(:ClassMethods, false) ? const_get(:ClassMethods) : const_set(:ClassMethods, Module.new) mod.module_eval(&class_methods_module_definition) end
  14. DMBTT@NFUIPET ϝιου def class_methods(&class_methods_module_definition) mod = const_defined?(:ClassMethods, false) ? const_get(:ClassMethods)

    : const_set(:ClassMethods, Module.new) mod.module_eval(&class_methods_module_definition) end $MBTT.FUIPET͕͋Ε͹୅ೖ ͳ͚Ε͹ఆٛ
  15. DMBTT@NFUIPET ϝιου def class_methods(&class_methods_module_definition) mod = const_defined?(:ClassMethods, false) ? const_get(:ClassMethods)

    : const_set(:ClassMethods, Module.new) mod.module_eval(&class_methods_module_definition) end DMBTT@NFUIPET͕ಋ͘ ϒϩοΫͷத਎Λ $MBTT.FUIPETͷείʔϓͰධՁ ʹ$MBTT.FUIPETʹϝιου͕ੜ͑Δ
  16. JODMVEFEϝιου def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block)

    if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end
  17. JODMVEFEͷຊདྷͷ࢖͍ํ module Foo def self.included(mod) p "#{mod} include #{self}" end

    end class Bar include Foo end # => "Bar include Foo" TFMGJODMVEFEͷܗͰɺ JODMVEF͞Εͨͱ͖ͷ ಈ࡞Λॻ͘ Ҿ༻ɿJOTUBODFNFUIPE.PEVMFJODMVEFE 3VCZ  IUUQTSVSFNBDMFBSDPEFDPNNFUIPE.PEVMFJJODMVEFEIUNM
  18. JODMVEFEϝιου def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block)

    if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end Ϩγʔό͕͋Δ ʹຊདྷͷ࢖͍ํΛ͍ͯ͠Δ
  19. JODMVEFEϝιου def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block)

    if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end Ϩγʔόͳ͠ͷJODMVEFE͕ ݸҎ্͋ͬͨΒΤϥʔ
  20. JODMVEFEϝιου def included(base = nil, &block) if base.nil? if instance_variable_defined?(:@_included_block)

    if @_included_block.source_location != block.source_location raise MultipleIncludedBlocks end else @_included_block = block end else super end end !@JODMVEFE@CMPDL ʹϒϩοΫΛ֨ೲ
  21. ΫϥεϝιουͱΫϥεϚΫϩΛ ੜ΍͢෦෼ def append_features(base) if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false

    else return false if base < self @_dependencies.each { |dep| base.include(dep) } super base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end CBTFFYUFOEͰ $MBTT.FUIPETΛΫϥεϝιουͱͯ͠ੜ΍͠ CBTFDMBTT@FWBMͰ !@JODMVEFE@CMPDLΛΫϥεϚΫϩͱ࣮ͯ͠ߦͨ͠
  22. ᶄґଘϞδϡʔϧಡࠐෆཁ ͷίʔυ def self.extended(base) base.instance_variable_set(:@_dependencies, []) end def append_features(base) if

    base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false else return false if base < self @_dependencies.each { |dep| base.include(dep) } super base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end
  23. ᶄґଘϞδϡʔϧಡࠐෆཁ ͷίʔυ ❑ TFMGFYUFOEFE ϝιου ❏ 3VCZ૊ࠐɿFYUFOE͞ΕΔͱൃՐ͢Δ ❏ ʮ"4$ΛFYUFOEͨ͠ʯͱϚʔΩϯά ❑

    BQQFOE@GFBUVSFT ϝιου ❏ 3VCZ૊ࠐɿJODMVEFͷ࣮ମ ❏ Ϟδϡʔϧͷಡࠐ࣌ʹґଘϞδϡʔϧ΋ ҰॹʹJODMVEF͢Δ
  24. BQQFOE@GFBUVSFT ϝιου def append_features(base) if base.instance_variable_defined?(:@_dependencies) base.instance_variable_get(:@_dependencies) << self false

    else return false if base < self @_dependencies.each { |dep| base.include(dep) } super base.extend const_get(:ClassMethods) if const_defined?(:ClassMethods) base.class_eval(&@_included_block) if instance_variable_defined?(:@_included_block) end end
  25.  "4$ΛFYUFOE ;ͭ͏ͷΫϥε ᶃ $ΛJODMVEF ᶄ "ΛJODMVEF ᶅ $ͷ!@EFQʹ "Λ௥Ճ

    !@EFQ <"> ᶆ #ΛJODMVEF " # $ % NPEVMF NPEVMF NPEVMF DMBTT
  26.  "4$ΛFYUFOE ;ͭ͏ͷΫϥε !@EFQ <"> ᶇ !@EFQΛ%ʹJODMVEF ᶈ $Λ%ʹJODMVEF ᶉ

    $ͷ$MBTT.FUIPET  !@JODMVEFE@CMPDLΛ %ʹੜ΍͢ " # $ % NPEVMF NPEVMF NPEVMF DMBTT