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. 1.

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

    Meguro.rb#30 Jan 31, 2020 - Shimpei Otsubo
  2. 2.

    ©2020 Wantedly, Inc. Shimpei Otsubo @potsbo Developer Experience Squad, Wantedly,

    Inc. Kubernetes Dev Tools CI / CD AuthN / Z Productivity Dvorak
  3. 5.

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

    req header Header Λհͯ͠ methodͷධՁ஋Λ read /write )FBEFSͳͷͰNJDSPTFSWJDFͰ఻ൖՄೳ call return
  4. 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
  5. 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Λผ్༻ҙ
  6. 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
  7. 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Մೳʂ
  8. 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
  9. 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
  10. 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 ຖճॻ͘ͷ͸ྲྀੴʹ໘౗
  11. 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 खಈͰίʔυΛ͍͡ΔͷͰ͸ͳ͘
  12. 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͚ͩͰղܾͰ͖ͨΒ޾ͤ Ұߦ௥Ճ͢Δ͚ͩʂʂ
  13. 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
  14. 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ͨ͠ͷ͔͓֮͑ͯ͘
  15. 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͍ͯ͠ͳ͍͔ͷ֬ೝ
  16. 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 طଘ࣮૷ͷୀආ
  17. 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࣮૷