Pro Yearly is on sale from $80 to $50! »

Wrap every method with just one line

Wrap every method with just one line

Feature Flag 基盤構築の一環として実装したサーバーサイドロジックの紹介です。
Ruby において include するだけですべての method をトラップして実装を差し込む手法を用いて、クライアントにサーバーサイドロジックの一部を確認/上書きできる用になります。

089fe44e41bb1fa2d9421f919a99173c?s=128

Shimpei Otsubo

January 31, 2020
Tweet

Transcript

  1. ©2020 Wantedly, Inc. Wrap every method With just another line

    Meguro.rb#30 Jan 31, 2020 - Shimpei Otsubo
  2. ©2020 Wantedly, Inc. Shimpei Otsubo @potsbo Developer Experience Squad, Wantedly,

    Inc. Kubernetes Dev Tools CI / CD AuthN / Z Productivity Dvorak
  3. ©2020 Wantedly, Inc. AB ςετ͕೉͍͠໰୊ ςετ͕ͮ͠Β͘ͳΔ ࣗಈ΋खಈ΋ ޙΖଆͷϚΠΫϩαʔϏεͰߦ͏ͱ؅ཧෆೳ Ұ൪લͳΒport-forwardͱ͔Ͱߦ͚Δ͕ʜ ˞αʔϏεϝογϡͰղܾ͍ͨ͠ϨΠϠͰ΋͋Δʜ

    $4͕ࠞཚ ਓʹΑͬͯڍಈ͕ҟͳΔࣄ͕͋ͬͯ΋Կނ͔Θ͔Βͳ͍
  4. ©2020 Wantedly, Inc. AB ςετΛ؆୯ʹτάϧͰ͖Δextension࡞ͬͨ ˞ը૾͸αϯϓϧͰ͢ ͲΜͳflagͰࠓͷϖʔδ͕render͞Ε͔ͨΘ͔Δ ͲΜͳflagͰࠓͷϖʔδΛrenderͯ͠΄͍͔͠ࢦఆͰ͖Δ

  5. ©2020 Wantedly, Inc. Thread.current resp header middleware ͲΜͳ஋Λ࢖͔ͬͨʁΛfeedback ͲΜͳ஋Λ࢖ͬͯ΄͍͔͠ʁΛࢦఆ Thread.current

    req header Header Λհͯ͠ methodͷධՁ஋Λ read /write )FBEFSͳͷͰNJDSPTFSWJDFͰ఻ൖՄೳ call return
  6. ©2020 Wantedly, Inc. ڵຯର৅ͷNFUIPEʹ͢΂ͯΛIPPL͢Δඞཁ͕͋Δ ͜ͷͨΊʹ͸

  7. ©2020 Wantedly, Inc. class Experiment def self.enable?(name, target) # some

    consistent logic here end end Experiment.enable?(:show_new_button, current_user) # => true or false Wantedly ʹ͓͚ΔABςετͷํ๏ ಉҰͷuserʹରͯ͠͸ৗʹಉҰͷboolean͕ฦΔmethod
  8. ©2020 Wantedly, Inc. Thread.current͔Βuser͕ࢦఆͨ͠஋͕͋Ε͹ͦΕΛར༻ ͳ͚Ε͹Thread.currentʹར༻ͨ͠஋Λ٧ΊΔ class Experiment def self.enable?(name, target)

    # some consistent logic here end def self.wrapped_enable?(name, target) request_value = Thread.current[:request][name] return request_value unless request_value.nil? actual_value = enable?(name, target) Thread.current[:response][name] = actual_value end end ˞Thread.currentͱheaderͷؒΛͭͳ͙middlewareΛผ్༻ҙ
  9. ©2020 Wantedly, Inc. Renameͯͦ͠ͷ··࢖͑ΔΑ͏ʹ͢Δ class Experiment def self.real_enable?(name, target) #

    some consistent logic here end def self.enable?(name, target) request_value = Thread.current[:request][name] return request_value unless request_value.nil? actual_value = real_enable?(name, target) Thread.current[:response][name] = actual_value end end
  10. ©2020 Wantedly, Inc. ҰൠԽͯ͠ΈΔ class FeatureFlag def self.intercept(name) request_value =

    Thread.current[:request][name] return request_value unless request_value.nil? actual_value = yield Thread.current[:response][name] = actual_value end end class Experiment def self.real_enable?(name, target) # some consistent logic here end def self.enable?(name, target) FeatureFlag.intercept(name) do real_enable?(name, target) end end end ϒϩοΫͰғΈ͑͢͞Ε͹Ͳ͜Ͱ΋PWFSSJEFՄೳʂ
  11. ©2020 Wantedly, Inc. ଞͷػೳͷग़͠෼͚ʹ΋࢖͑Δ͸ͣ

  12. ©2020 Wantedly, Inc. Helperͷ͍ΖΜͳmethod ͰػೳΛग़͠෼͚͍ͯ͠Δ৔ॴ͕͋Δ module I18nHelper # the new

    landing page currently supports only Japanese!! def show_new_landing_page? I18n.locale == :ja end end
  13. ©2020 Wantedly, Inc. ϒϩοΫͰғΉ͚ͩͰXSBQͰ͖Δ class FeatureFlag def self.intercept(name) ... end

    end module I18nHelper # the new landing page currently supports only Japanese!! def show_new_landing_page? FeatureFlag.intercept(:show_new_landing_page?) do I18n.locale == :ja end end end
  14. ©2020 Wantedly, Inc. ϒϩοΫͰғΉ͚ͩͰXSBQͰ͖Δ class FeatureFlag def self.intercept(name) ... end

    end module I18nHelper # the new landing page currently supports only Japanese!! def show_new_landing_page? FeatureFlag.intercept(:show_new_landing_page?) do I18n.locale == :ja end end end ຖճॻ͘ͷ͸ྲྀੴʹ໘౗
  15. ©2020 Wantedly, Inc. class FeatureFlag def self.intercept(name) ... end end

    module I18nHelper # the new landing page currently supports only Japanese!! def show_new_landing_page? FeatureFlag.intercept(:show_new_landing_page?) do I18n.locale == :ja end end end खಈͰίʔυΛ͍͡ΔͷͰ͸ͳ͘
  16. ©2020 Wantedly, Inc. class FeatureFlag def self.intercept(name) ... end end

    module I18nHelper include FeatureFlag def show_new_landing_page? I18n.locale == :ja end end *ODMVEF͚ͩͰղܾͰ͖ͨΒ޾ͤ Ұߦ௥Ճ͢Δ͚ͩʂʂ
  17. ©2020 Wantedly, Inc. Ͱ͖ΔΑ͏ʹͨ͠ module FeatureFlag def self.included(base) base.class_eval do

    extend ClassMethods @_already_patched = [] @_patch_target = base end end module ClassMethods def intercept(name) ... end def method_added(name) if !@_already_patched.include?(name) && self == @_patch_target orig_method_name = "_#{base}_orig".to_sym @_already_patched << name @_already_patched << orig_method_name alias_method orig_method_name, name
  18. ©2020 Wantedly, Inc. ॳظԽ module FeatureFlag def self.included(base) base.class_eval do

    extend ClassMethods @_already_patched = [] @_patch_target = base end end module ClassMethods def intercept(name) ... end def method_added(name) if !@_already_patched.include?(name) && self == @_patch_target orig_method_name = "_#{base}_orig".to_sym @_already_patched << name @_already_patched << orig_method_name alias_method orig_method_name, name ͲͷNPEVMFͰJODMVEFͨ͠ͷ͔͓֮͑ͯ͘
  19. ©2020 Wantedly, Inc. module ClassMethods def intercept(name) ... end def

    method_added(name) if !@_already_patched.include?(name) && self == @_patch_target orig_method_name = "_#{base}_orig".to_sym @_already_patched << name @_already_patched << orig_method_name alias_method orig_method_name, name private orig_method_name define_method(name) do |*args| intercept(name) do send(old_name, *args) end end end super end end end ͢ͰʹXSBQ͍ͯ͠ͳ͍͔ͷ֬ೝ
  20. ©2020 Wantedly, Inc. module ClassMethods def intercept(name) ... end def

    method_added(name) if !@_already_patched.include?(name) && self == @_patch_target orig_method_name = "_#{base}_orig".to_sym @_already_patched << name @_already_patched << orig_method_name alias_method orig_method_name, name private orig_method_name define_method(name) do |*args| intercept(name) do send(old_name, *args) end end end super end end end طଘ࣮૷ͷୀආ
  21. ©2020 Wantedly, Inc. module ClassMethods def intercept(name) ... end def

    method_added(name) if !@_already_patched.include?(name) && self == @_patch_target orig_method_name = "_#{base}_orig".to_sym @_already_patched << name @_already_patched << orig_method_name alias_method orig_method_name, name private orig_method_name define_method(name) do |*args| intercept(name) do send(old_name, *args) end end end super end end end 8SBQ࣮૷
  22. ©2020 Wantedly, Inc. αʔόʔͷڍಈ͕ݟ͑Δม͑ΒΕΔͱָ *ODMVEF͚ͩͰ͢΂ͯͷNFUIPEXSBQ͸Մೳ ଞʹ΋͍Ζ͍Ζͳ֦ு͕Ͱ͖Δ ςετ΋σόοά΋ NFUIPE@BEEFEͰ௥Ճ͞ΕͨNFUIPEΛ͍͡Ε͹ྑ͍ XSBQ@POMZGPP CBSͱ͔΋࡞ΕΔ