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

Wrap every method with just one line

Wrap every method with just one line

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

Shimpei Otsubo

January 31, 2020
Tweet

More Decks by Shimpei Otsubo

Other Decks in Programming

Transcript

  1. ©2020 Wantedly, Inc.
    Wrap every method
    With just another line
    Meguro.rb#30
    Jan 31, 2020 - Shimpei Otsubo

    View Slide

  2. ©2020 Wantedly, Inc.
    Shimpei Otsubo @potsbo
    Developer Experience Squad, Wantedly, Inc.
    Kubernetes
    Dev Tools
    CI / CD
    AuthN / Z
    Productivity Dvorak

    View Slide

  3. ©2020 Wantedly, Inc.
    AB ςετ͕೉͍͠໰୊
    ςετ͕ͮ͠Β͘ͳΔ
    ࣗಈ΋खಈ΋
    ޙΖଆͷϚΠΫϩαʔϏεͰߦ͏ͱ؅ཧෆೳ
    Ұ൪લͳΒport-forwardͱ͔Ͱߦ͚Δ͕ʜ
    ˞αʔϏεϝογϡͰղܾ͍ͨ͠ϨΠϠͰ΋͋Δʜ
    $4͕ࠞཚ
    ਓʹΑͬͯڍಈ͕ҟͳΔࣄ͕͋ͬͯ΋Կނ͔Θ͔Βͳ͍

    View Slide

  4. ©2020 Wantedly, Inc.
    AB ςετΛ؆୯ʹτάϧͰ͖Δextension࡞ͬͨ
    ˞ը૾͸αϯϓϧͰ͢
    ͲΜͳflagͰࠓͷϖʔδ͕render͞Ε͔ͨΘ͔Δ
    ͲΜͳflagͰࠓͷϖʔδΛrenderͯ͠΄͍͔͠ࢦఆͰ͖Δ

    View Slide

  5. ©2020 Wantedly, Inc.
    Thread.current
    resp header
    middleware
    ͲΜͳ஋Λ࢖͔ͬͨʁΛfeedback
    ͲΜͳ஋Λ࢖ͬͯ΄͍͔͠ʁΛࢦఆ
    Thread.current
    req header
    Header Λհͯ͠ methodͷධՁ஋Λ read /write
    )FBEFSͳͷͰNJDSPTFSWJDFͰ఻ൖՄೳ
    call
    return

    View Slide

  6. ©2020 Wantedly, Inc.
    ڵຯର৅ͷNFUIPEʹ͢΂ͯΛIPPL͢Δඞཁ͕͋Δ
    ͜ͷͨΊʹ͸

    View Slide

  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

    View Slide

  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Λผ్༻ҙ

    View Slide

  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

    View Slide

  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Մೳʂ

    View Slide

  11. ©2020 Wantedly, Inc.
    ଞͷػೳͷग़͠෼͚ʹ΋࢖͑Δ͸ͣ

    View Slide

  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

    View Slide

  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

    View Slide

  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
    ຖճॻ͘ͷ͸ྲྀੴʹ໘౗

    View Slide

  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
    खಈͰίʔυΛ͍͡ΔͷͰ͸ͳ͘

    View Slide

  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͚ͩͰղܾͰ͖ͨΒ޾ͤ
    Ұߦ௥Ճ͢Δ͚ͩʂʂ

    View Slide

  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 [email protected]_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

    View Slide

  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 [email protected]_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ͨ͠ͷ͔͓֮͑ͯ͘

    View Slide

  19. ©2020 Wantedly, Inc.
    module ClassMethods
    def intercept(name)
    ...
    end
    def method_added(name)
    if [email protected]_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͍ͯ͠ͳ͍͔ͷ֬ೝ

    View Slide

  20. ©2020 Wantedly, Inc.
    module ClassMethods
    def intercept(name)
    ...
    end
    def method_added(name)
    if [email protected]_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
    طଘ࣮૷ͷୀආ

    View Slide

  21. ©2020 Wantedly, Inc.
    module ClassMethods
    def intercept(name)
    ...
    end
    def method_added(name)
    if [email protected]_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࣮૷

    View Slide

  22. ©2020 Wantedly, Inc.
    αʔόʔͷڍಈ͕ݟ͑Δม͑ΒΕΔͱָ
    *ODMVEF͚ͩͰ͢΂ͯͷNFUIPEXSBQ͸Մೳ
    ଞʹ΋͍Ζ͍Ζͳ֦ு͕Ͱ͖Δ
    ςετ΋σόοά΋
    [email protected]Ͱ௥Ճ͞ΕͨNFUIPEΛ͍͡Ε͹ྑ͍
    [email protected] CBSͱ͔΋࡞ΕΔ

    View Slide