Slide 1

Slide 1 text

ܧঝͱϝλϓϩάϥϛϯάຬࡌͳ ΞϓϦέʔγϣϯίʔυͰ΋ΞΫγϣϯͱ ϑΟϧλʹ೰·ͳ͍ͨΊͷGemΛ࡞ͬͨ࿩ 2020.10.03. Kaigi on Rails STAY HOME Edition @makicamel

Slide 2

Slide 2 text

w!NBLJDBNFM઒ݪສق wɹ w3VCZͱϏʔϧɹɹͱ͓ञ͕޷͖ ࣗݾ঺հ

Slide 3

Slide 3 text

͓खݩͷRails ΞϓϦʹ BQQMJDBUJPO@DPOUSPMMFSSC ͍ͭ͋͘Γ·͔͢ʁ ಥવͰ͕͢

Slide 4

Slide 4 text

˞೥݄೔ݱࡏ "/%1"% $ rails stats +----------------------+--------+--------+---------+---------+-----+-------+ | Name | Lines | LOC | Classes | Methods | M/C | LOC/M | +----------------------+--------+--------+---------+---------+-----+-------+ | Controllers | 66567 | 52928 | 1030 | 5473 | 5 | 7 | +----------------------+--------+--------+---------+---------+-----+-------+ $ find . -name application_controller.rb | wc -l 3 ׂ͕ ApplicationController # => 325

Slide 5

Slide 5 text

•֤֊૚͝ͱʹ application_controller.rb ͕͍Δ •֤ ApplicationController ͸ ਌֊૚ͷ ApplicationController Λܧঝ ͞Βʹ •ڞ௨ॲཧΛϞδϡʔϧԽ֤ͯ͠ॴͰ include •ϝλϓϩຬࡌ "/%1"% ˞೥݄೔ݱࡏ

Slide 6

Slide 6 text

Α͋͘Δޫܠ ɹ͜ͷϑΝΠϧΛमਖ਼͢Δͧ BXFTPNF@DPOUSPMMFSSC

Slide 7

Slide 7 text

ɹݖݶνΣοΫΛ֬ೝ͠Α͏ ᶃ ᶄ ᶅ ᶆ Α͋͘Δޫܠ BXFTPNF@DPOUSPMMFSSC •before_action ΛνΣοΫ

Slide 8

Slide 8 text

ɹ͑ͬ class Foo::Bar::Baz::ApplicationController < Foo::Bar::ApplicationController include Foo::Common::Bar::Baz::AwesomeController end Α͋͘Δޫܠ BXFTPNF@DPOUSPMMFSSC

Slide 9

Slide 9 text

BXFTPNF@DPOUSPMMFSSC ɹ͑ͬ͑ͬ class Foo::Bar::Baz::ApplicationController < Foo::Bar::ApplicationController include Foo::Common::Bar::Baz::AwesomeController end module Foo::Common::Bar::Baz::AwesomeController include Common::Bar::Baz::AwesomeController end Α͋͘Δޫܠ

Slide 10

Slide 10 text

Α͋͘Δޫܠ খ͞ͳमਖ਼΍ௐࠪͳͷʹ ܧঝؔ܎Λ֬ೝ͢Δ͚ͩͰർฐ͢Δ

Slide 11

Slide 11 text

ͱ͍͏Θ͚Ͱ

Slide 12

Slide 12 text

࡞ͬͨ NBLJDBNFMBDUJPO@USBDFS IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFS

Slide 13

Slide 13 text

[2020-09-27T03:25:43.018298 #1] [“APPLIED",:set_turbolinks_location_header_from _session, “/usr/local/bundle/gems/turbolinks-… redirection.rb”, 43] [2020-09-27T03:25:43.019410 #1] ["APPLIED", :verify_authenticity_token, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 211] [2020-09-27T03:25:43.021131 #1] ["APPLIED", :require_login, “/myapp/app/ controllers/awesome_controller.rb", 17] [2020-09-27T03:25:43.022063 #1] ["NO_APPLIED", :set_awesome, “/myapp/app/ controllers/awesome_controller.rb", 25] [2020-09-27T03:25:43.023716 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.025547 #1] ["ACTION", :index, “/myapp/app/controllers/ awesome_controller.rb", 7] [2020-09-27T03:25:43.026297 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.027203 #1] ["APPLIED", :store_location, “/myapp/app/ controllers/awesome_controller.rb", 27] [2020-09-27T03:25:43.030074 #1] ["APPLIED", :verify_same_origin_request, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 240] class AwesomeController < ApplicationController before_action :require_login before_action :set_awesome, only: :show around_action :with_readonly after_action :store_location def index # ... end def show # ... end private def require_login # ... end # ... ˞ϩάͷҰ෦Λলུ͍ͯ͠·͢ɻྫ͸3&"%.&ʹ͋Γ·͢ɻ IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFS ActionTracer ΞΫηε࣌ʹϑΟϧλΛॻ͖ग़͢

Slide 14

Slide 14 text

˞ϩάͷҰ෦Λলུ͍ͯ͠·͢ɻྫ͸3&"%.&ʹ͋Γ·͢ɻ IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFS ActionTracer •ΞϓϦέʔγϣϯίʔυ͚ͩͰ͸ͳ͘ GemͷϑΟϧλ΋هࡌ [2020-09-27T03:25:43.018298 #1] [“APPLIED",:set_turbolinks_location_header_from _session, “/usr/local/bundle/gems/turbolinks-… redirection.rb”, 43] [2020-09-27T03:25:43.019410 #1] ["APPLIED", :verify_authenticity_token, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 211] [2020-09-27T03:25:43.021131 #1] ["APPLIED", :require_login, “/myapp/app/ controllers/awesome_controller.rb", 17] [2020-09-27T03:25:43.022063 #1] ["NO_APPLIED", :set_awesome, “/myapp/app/ controllers/awesome_controller.rb", 25] [2020-09-27T03:25:43.023716 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.025547 #1] ["ACTION", :index, “/myapp/app/controllers/ awesome_controller.rb", 7] [2020-09-27T03:25:43.026297 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.027203 #1] ["APPLIED", :store_location, “/myapp/app/ controllers/awesome_controller.rb", 27] [2020-09-27T03:25:43.030074 #1] ["APPLIED", :verify_same_origin_request, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 240] Turbolinks Rails ΞϓϦέʔγϣϯ ݺ͹ΕͨΞΫγϣϯ

Slide 15

Slide 15 text

˞ϩάͷҰ෦Λলུ͍ͯ͠·͢ɻྫ͸3&"%.&ʹ͋Γ·͢ɻ IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFS ActionTracer •ϑΝΠϧύεɺߦ਺ [2020-09-27T03:25:43.018298 #1] [“APPLIED",:set_turbolinks_location_header_from _session, “/usr/local/bundle/gems/turbolinks-… redirection.rb”, 43] [2020-09-27T03:25:43.019410 #1] ["APPLIED", :verify_authenticity_token, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 211] [2020-09-27T03:25:43.021131 #1] ["APPLIED", :require_login, “/myapp/app/ controllers/awesome_controller.rb", 17] [2020-09-27T03:25:43.022063 #1] ["NO_APPLIED", :set_awesome, “/myapp/app/ controllers/awesome_controller.rb", 25] [2020-09-27T03:25:43.023716 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.025547 #1] ["ACTION", :index, “/myapp/app/controllers/ awesome_controller.rb", 7] [2020-09-27T03:25:43.026297 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.027203 #1] ["APPLIED", :store_location, “/myapp/app/ controllers/awesome_controller.rb", 27] [2020-09-27T03:25:43.030074 #1] ["APPLIED", :verify_same_origin_request, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 240]

Slide 16

Slide 16 text

˞ϩάͷҰ෦Λলུ͍ͯ͠·͢ɻྫ͸3&"%.&ʹ͋Γ·͢ɻ IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFS ActionTracer •ͦͷϑΟϧλ͕ ࣮ࡍʹద༻͞Ε͔ͨ൱͔ •etc … [2020-09-27T03:25:43.018298 #1] [“APPLIED",:set_turbolinks_location_header_from _session, “/usr/local/bundle/gems/turbolinks-… redirection.rb”, 43] [2020-09-27T03:25:43.019410 #1] ["APPLIED", :verify_authenticity_token, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 211] [2020-09-27T03:25:43.021131 #1] ["APPLIED", :require_login, “/myapp/app/ controllers/awesome_controller.rb", 17] [2020-09-27T03:25:43.022063 #1] ["NO_APPLIED", :set_awesome, “/myapp/app/ controllers/awesome_controller.rb", 25] [2020-09-27T03:25:43.023716 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.025547 #1] ["ACTION", :index, “/myapp/app/controllers/ awesome_controller.rb", 7] [2020-09-27T03:25:43.026297 #1] ["APPLIED", :with_readonly, “/myapp/app/ controllers/awesome_controller.rb", 21] [2020-09-27T03:25:43.027203 #1] ["APPLIED", :store_location, “/myapp/app/ controllers/awesome_controller.rb", 27] [2020-09-27T03:25:43.030074 #1] ["APPLIED", :verify_same_origin_request, “/usr/ local/bundle/gems/actionpack-…/ request_forgery_protection.rb”, 240]

Slide 17

Slide 17 text

ActionTracerͷͭ͘Γ͔ͨ

Slide 18

Slide 18 text

ActionTracer •ίʔυϕʔε͸খ͍͞ʢ200ߦະຬʣ

Slide 19

Slide 19 text

TracePoint DMBTT5SBDF1PJOUc3VCZϦϑΝϨϯεϚχϡΞϧ IUUQTEPDTSVCZMBOHPSHKBDMBTT5SBDF1PJOUIUNM

Slide 20

Slide 20 text

TracePoint •ॲཧΛొ࿥͓͖ͯ͠ɺ֤छΠϕϯτΛ͖͔͚ͬʹ࣮ߦͰ͖Δ •ΠϕϯτྫʢҰ෦ʣ •:line •:call •:return •:c_call •:c_return ࣜͷධՁ Ruby Ͱهड़͞Εͨϝιουͷݺͼग़͠ Ruby Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ C Ͱهड़͞Εͨϝιουͷݺͼग़͠ C Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ

Slide 21

Slide 21 text

TracePoint class AwesomeController < ApplicationController def index TracePoint.trace(:call) do |tp| p "#{tp.method_id}@#{tp.path}:#{tp.lineno}" end end # ...

Slide 22

Slide 22 text

No content

Slide 23

Slide 23 text

TracePoint •ॲཧΛొ࿥͓͖ͯ͠ɺ֤छΠϕϯτΛ͖͔͚ͬʹ࣮ߦͰ͖Δ •ΠϕϯτྫʢҰ෦ʣ •:line •:call •:return ࣜͷධՁ Ruby Ͱهड़͞Εͨϝιουͷݺͼग़͠ Ruby Ͱهड़͞Εͨϝιουݺͼग़͔͠ΒͷϦλʔϯ ΞϓϦέʔγϣϯίʔυ΋ Rails ͷίʔυ΋۠ผ͞Εͳ͍ ͍͍ײ͡ʹநग़͢ΔͱΑ͍ͷͰ͸

Slide 24

Slide 24 text

RailsͷίʔυΛ ಡΜͰΈΑ͏

Slide 25

Slide 25 text

before_action :login_required ͕࣮ߦ͞ΕΔ·Ͱ •Callback ͷొ࿥ •Callback ͷ࣮ߦ •Callback ͷొ࿥ •Callback ͷ࣮ߦ

Slide 26

Slide 26 text

ओͳొ৔ਓ෺ •ActiveSupport::Callbacks •AbstractController::Callbacks

Slide 27

Slide 27 text

ActiveSupport::Callbacks module ActiveSupport module Callbacks module Conditionals # :nodoc: module Filters class Callback #:nodoc:# class CallTemplate # :nodoc: class CallbackSequence # :nodoc: class CallbackChain #:nodoc:# module ClassMethods DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC

Slide 28

Slide 28 text

AbstractController::Callbacks •AbstractController ͸ ApplicationController ͷ਌Ϋϥε ApplicationController < ActionController::Base < ActionController::Metal < AbstractController::Base

Slide 29

Slide 29 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCFEDDDGBDUJPOQBDLMJCBCTUSBDU@DPOUSPMMFSDBMMCBDLTSC module AbstractController module Callbacks # ... included do define_callbacks( :process_action, terminator: ->(controller, result_lambda) { result_lambda.call if result_lambda.is_a?(Proc) controller.performed? }, skip_after_callbacks_if_terminated: true ) end # ... AbstractController

Slide 30

Slide 30 text

module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def define_callbacks(*names) options = names.extract_options! names.each do |name| name = name.to_sym set_callbacks name, CallbackChain.new(name, options) module_eval <<-RUBY, __FILE__, __LINE__ + 1 def _run_#{name}_callbacks(&block) run_callbacks #{name.inspect}, &block end def self._#{name}_callbacks get_callbacks(#{name.inspect}) end def self._#{name}_callbacks=(value) set_callbacks(#{name.inspect}, value) end def _#{name}_callbacks __callbacks[#{name.inspect}] end RUBY end end ClassMethods

Slide 31

Slide 31 text

CallbackChain DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods class CallbackChain #:nodoc:# include Enumerable # ... def initialize(name, config) @name = name @config = { scope: [:kind], terminator: default_terminator }.merge!(config) @chain = [] @callbacks = nil @mutex = Mutex.new end # ... end # An Array with a compile method. CallbackΠϯελϯε͕ೖΔ ഑ྻϥΠΫͳΫϥε

Slide 32

Slide 32 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def define_callbacks(*names) options = names.extract_options! names.each do |name| name = name.to_sym set_callbacks name, CallbackChain.new(name, options) module_eval <<-RUBY, __FILE__, __LINE__ + 1 def _run_#{name}_callbacks(&block) run_callbacks #{name.inspect}, &block end def self._#{name}_callbacks get_callbacks(#{name.inspect}) end def self._#{name}_callbacks=(value) set_callbacks(#{name.inspect}, value) end def _#{name}_callbacks __callbacks[#{name.inspect}] end RUBY end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods ClassMethods

Slide 33

Slide 33 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def set_callbacks(name, callbacks) # :nodoc: self.__callbacks = __callbacks.merge(name.to_sym => callbacks) end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods ClassMethods

Slide 34

Slide 34 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCFEDDDGBDUJPOQBDLMJCBCTUSBDU@DPOUSPMMFSDBMMCBDLTSC module AbstractController module Callbacks module ClassMethods [:before, :after, :around].each do |callback| define_method "#{callback}_action" do |*names, &blk| _insert_callbacks(names, blk) do |name, options| set_callback(:process_action, callback, name, options) end end # ... end end end end AbstractController

Slide 35

Slide 35 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def set_callback(name, *filter_list, &block) type, filters, options = normalize_callback_params(filter_list, block) # ... self_chain = get_callbacks name mapped = filters.map do |filter| Callback.build(self_chain, filter, type, options) end __update_callbacks(name) do |target, chain| options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) target.set_callbacks name, chain end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods ClassMethods

Slide 36

Slide 36 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC class Callback #:nodoc:# # ... def initialize(name,filter,kind,options,chain_config) @chain_config = chain_config @name = name # => :process_action @kind = kind # => :before @filter = filter # => :login_required @key = compute_identifier filter # => :login_required @if = Array(options[:if]) # => :[#] @unless = Array(options[:unless]) # => [] end # ... module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Callback ίʔϧόοΫຊମ before_action :login_required, except: :destroy ͷ৔߹

Slide 37

Slide 37 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def set_callback(name, *filter_list, &block) type, filters, options = normalize_callback_params(filter_list, block) # ... self_chain = get_callbacks name mapped = filters.map do |filter| Callback.build(self_chain, filter, type, options) end __update_callbacks(name) do |target, chain| options[:prepend] ? chain.prepend(*mapped) : chain.append(*mapped) target.set_callbacks name, chain end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods ClassMethods

Slide 38

Slide 38 text

before_action :login_required ͕࣮ߦ͞ΕΔ·Ͱ •Callback ͷొ࿥ •Callback ͷ࣮ߦ •Callback ͷొ࿥ ɹɹɹɹɹɹɹɹ

Slide 39

Slide 39 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCFEDDDGBDUJPOQBDLMJCBCTUSBDU@DPOUSPMMFSDBMMCBDLTSC AbstractController module AbstractController module Callbacks # ... def process_action(*args) run_callbacks(:process_action) do super end end end end

Slide 40

Slide 40 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def run_callbacks(kind) callbacks = __callbacks[kind.to_sym] # ... next_sequence = callbacks.compile invoke_sequence = Proc.new do # ... end # Common case: no 'around' callbacks defined if next_sequence.final? # ... else invoke_sequence.call end # ... end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Callbacks

Slide 41

Slide 41 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC class CallbackChain #:nodoc:# # ... def compile @callbacks || @mutex.synchronize do final_sequence = CallbackSequence.new @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback| callback.apply callback_sequence end end end # ... end # An Array with a compile method. module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallbackChain CallbackΠϯελϯε͕ೖΔ഑ྻΛ ࣋ͭΫϥε

Slide 42

Slide 42 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC class CallbackSequence # :nodoc: def initialize( nested = nil, call_template = nil, user_conditions = nil ) @nested = nested @call_template = call_template @user_conditions = user_conditions @before = [] @after = [] end # ... end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallbackSequence ॱং௨ΓʹίʔϧόοΫΛอ؅͠ ࣮ߦ͍ͯ͘͠Ϋϥε

Slide 43

Slide 43 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC class CallbackChain #:nodoc:# # ... def compile @callbacks || @mutex.synchronize do final_sequence = CallbackSequence.new @callbacks ||= @chain.reverse.inject(final_sequence) do |callback_sequence, callback| callback.apply callback_sequence end end end # ... end # An Array with a compile method. module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallbackChain CallbackΠϯελϯε͕ೖΔ഑ྻΛ ࣋ͭΫϥε

Slide 44

Slide 44 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def apply(callback_sequence) user_conditions = conditions_lambdas user_callback = CallTemplate.build(@filter, self) case kind when :before Filters::Before.build( callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter ) when :after Filters::After.build( # before ͱಉ ) when :around callback_sequence.around( user_callback, user_conditions ) end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Callback ίʔϧόοΫຊମ

Slide 45

Slide 45 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC class CallTemplate # :nodoc: # ... def self.build(filter, callback) case filter when Symbol new(nil, filter, [], nil) when String new( nil, :instance_exec, [:value], compile_lambda(filter) ) # ... end end # ... end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallTemplate ίʔϧόοΫΛ࣮ߦ͢Δ ProcΛ࡞ΔΫϥε

Slide 46

Slide 46 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def apply(callback_sequence) user_conditions = conditions_lambdas user_callback = CallTemplate.build(@filter, self) case kind when :before Filters::Before.build( callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter ) when :after Filters::After.build( # before ͱಉ ) when :around callback_sequence.around( user_callback, user_conditions ) end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Callback ίʔϧόοΫຊମ

Slide 47

Slide 47 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC module Filters # ... class Before def self.build( callback_sequence, user_callback, user_conditions, chain_config, filter ) halted_lambda = chain_config[:terminator] if user_conditions.any? # ... else halting( callback_sequence, user_callback, halted_lambda, filter ) end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Filters if, unlessΛνΣοΫ͠ඞཁͳΒ ίʔϧόοΫΛhalt͢ΔΫϥε

Slide 48

Slide 48 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def self.halting( callback_sequence, user_callback, halted_lambda, filter ) callback_sequence.before do |env| target = env.target value = env.value halted = env.halted unless halted result_lambda = -> { user_callback.call target, value } env.halted = halted_lambda.call(target, result_lambda) if env.halted target.send :halted_callback_hook, filter end end env end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods Filters if, unlessΛνΣοΫ͠ඞཁͳΒ ίʔϧόοΫΛhalt͢ΔΫϥε

Slide 49

Slide 49 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods def self.halting( callback_sequence, user_callback, halted_lambda, filter ) callback_sequence.before do |env| target = env.target value = env.value halted = env.halted unless halted result_lambda = -> { user_callback.call target, value } env.halted = halted_lambda.call(target, result_lambda) if env.halted target.send :halted_callback_hook, filter end end env end end def apply(callback_sequence) # ... user_callback = CallTemplate.build # ... case kind when :before Filters::Before.build( callback_sequence, user_callback.make_lambda, user_conditions, chain_config, @filter ) Filters

Slide 50

Slide 50 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def make_lambda lambda do |target, value, &block| target, block, method, *arguments = expand(target, value, block) target.send(method, *arguments, &block) end end module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallTemplate ίʔϧόοΫΛ࣮ߦ͢Δ ProcΛ࡞ΔΫϥε

Slide 51

Slide 51 text

DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def expand(target, value, block) result = @arguments.map { |arg| case arg when :value; value when :target; target when :block; block || raise(ArgumentError) end } result.unshift @method_name result.unshift @override_block || block result.unshift @override_target || target # target, block, method, *arguments = result # target.send(method, *arguments, &block) result end ϦΫΤετΛड͚ͨίϯτϩʔϥ before_actionͰొ࿥ͨ͠
 Πϯελϯεϝιου module ActiveSupport module Callbacks module Conditionals module Filters class Callback class CallTemplate class CallbackSequence class CallbackChain module ClassMethods CallTemplate ίʔϧόοΫΛ࣮ߦ͢Δ ProcΛ࡞ΔΫϥε AwesomeController.new.send(:login_required) ͱҰॹʂʂ

Slide 52

Slide 52 text

Slide 53

Slide 53 text

No content

Slide 54

Slide 54 text

TracePoint ͍Βͳ͘ͳ͍ʁ

Slide 55

Slide 55 text

औΕͨ class AwesomeController < ApplicationController def index p self.__callbacks[:process_action].class # => ActiveSupport::Callbacks::CallbackChain p self.__callbacks[:process_action].send(:chain).first # => #[:kind], # :terminator=>#, # :skip_after_callbacks_if_terminated=>true}, # @filter=:require_login, # @if=[#], # @key=:require_login, # @kind=:before, # @name=:process_action, # @unless=[]> end end

Slide 56

Slide 56 text

5JNFUBCMFc,BJHJPO3BJMT IUUQTLBJHJPOSBJMTPSHUJNFUBCMF Ϡό͍

Slide 57

Slide 57 text

ͱΓ͋͑ͣ։ൃΛਐΊΔ ࢖Θͳ͔ͬͨΒँΖ͏

Slide 58

Slide 58 text

AbstractController::Callbacks module AbstractController module Callbacks # ... def process_action(*args) run_callbacks(:process_action) do super end end end end ϞϯΩʔύονͰ͜ͷݺͼग़͠ޙʹ ͍͍ײ͡ʹॻ͖ग़ͯ͋͛ͨ͠ΒΑͦ͞͏

Slide 59

Slide 59 text

ϞϯΩʔύον module ActionTracer module MonkeyPatches module AbstractController module Callbacks def process_action(*args) ActionTracer.log(self) do super end end end end end end AbstractController::Callbacks#process_action ΛΦʔόʔϥΠυ

Slide 60

Slide 60 text

͍͍ײ͡ʹॻ͖ग़͢ module ActionTracer class << self def log(controller) result = yield Filters.build(controller).print result end end end

Slide 61

Slide 61 text

͍͍ײ͡ʹॻ͖ग़͢ class ActionTracer::Filters def self.build(controller) # ... raw_filters = controller.__callbacks[:process_action].send(:chain) raw_filters.group_by(&:kind).each do |kind, filter| filters[kind] = filter.map(&:raw_filter).map do |f| Filter.new(f, method: f.is_a?(Symbol) ? controller.method(f) : f) end end # ... end def print (@before + @around).map(&:to_a).each do |filter| ActionTracer.logger.info filter end # ... end end

Slide 62

Slide 62 text

I, [2020-09-23T01:51:32.772847 #2] INFO -- : [:verify_authenticity_token, “/usr/local/bundle/gems/actionpack-5.1.7/lib/action_controller/ metal/request_forgery_protection.rb", 211] ͍͍ײ͡ʹॻ͖ग़͢ class ActionTracer::Filter # ... def to_a [@filter, *@method.source_location] end end Ͱ͖ͨ

Slide 63

Slide 63 text

खݩͷ͓࢓ࣄͰ࢖ͬͯΈΔ •࠷ߴ •ࠓ·ͰͻͱͭͣͭܧঝݩΛͨͲͬͯ֬ೝ͍ͯͨ͠ͷ͸ͳΜͩͬͨͷ͔ •RSpec Ͱ࢖ͬͯ΋ศརͱ͔ɺϢʔεέʔεΛࢥ͍ͭ͘ •ཁ๬΋ग़ͯ͘Δ •PR ʹهࡌ͢ΔͷʹϑΟϧλ໊͚ͩ map ͓ͯ͠खܰʹίϐʔ͍ͨ͠ •࣮ࡍʹϑΟϧλ͕ద༻͞Ε͔ͨ΋஌Γ͍ͨ

Slide 64

Slide 64 text

ͦ͜Ͱ TracePoint Α͔ͬͨ

Slide 65

Slide 65 text

ActiveSupport::Callbacks DBMMCBDLTSCcSBJMTSBJMT IUUQTHJUIVCDPNSBJMTSBJMTCMPCDBBDUJWFTVQQPSUMJCBDUJWF@TVQQPSUDBMMCBDLTSC def expand(target, value, block) result = @arguments.map { |arg| case arg when :value; value when :target; target when :block; block || raise(ArgumentError) end } result.unshift @method_name result.unshift @override_block || block result.unshift @override_target || target # target, block, method, *arguments = result # target.send(method, *arguments, &block) result end expand ͕ݺ͹ΕΔͷ͸ ࣮ࡍʹ࣮ߦ͞ΕΔίʔϧόοΫ͚ͩ

Slide 66

Slide 66 text

module ActionTracer def self.filter_collector @filter_collector ||= TracePoint.new(:return) do |tp| # NOTE: ActiveSupport::Callbacks::CallTemplate is a private class if tp.method_id == :expand && tp.defined_class == ActiveSupport::Callbacks::CallTemplate if tp.return_value&.first.is_a? ActionController::Base case tp.return_value[2] # ... when Symbol # filter is a method applied_filters << tp.return_value[2] end end end end end def self.applied_filters @applied_filters ||= [] end end BDUJPO@USBDFSSCcNBLJDBNFMBDUJPO@USBDFS IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFSCMPCGMJCBDUJPO@USBDFSBDUJPO@USBDFSSC

Slide 67

Slide 67 text

ग़ྗͰ͖ͨ I, [2020-09-27T03:25:43.018298 #1] INFO -- : [“APPLIED",:set_turbolinks_location_header_from _session, “/usr/local/bundle/gems/turbolinks-redirection.rb”, 43] I, [2020-09-27T03:25:43.019410 #1] INFO -- : ["APPLIED", :verify_authenticity_token, “/usr/local/bundle/gems/actionpack-…/request_forgery_protection.rb”, 211] I, [2020-09-27T03:25:43.021131 #1] INFO -- : ["APPLIED", :require_login, “/myapp/app/controllers/awesome_controller.rb", 17] I, [2020-09-27T03:25:43.022063 #1] INFO -- : ["NO_APPLIED", :set_awesome, “/myapp/app/controllers/awesome_controller.rb", 25] I, [2020-09-27T03:25:43.023716 #1] INFO -- : ["APPLIED", :with_readonly, “/myapp/app/controllers/awesome_controller.rb", 21] I, [2020-09-27T03:25:43.025547 #1] INFO -- : ["ACTION", :index, “/myapp/app/controllers/awesome_controller.rb", 7] I, [2020-09-27T03:25:43.026297 #1] INFO -- : ["APPLIED", :with_readonly, “/myapp/app/controllers/awesome_controller.rb", 21] I, [2020-09-27T03:25:43.027203 #1] INFO -- : ["APPLIED", :store_location, “/myapp/app/controllers/awesome_controller.rb", 27] I, [2020-09-27T03:25:43.030074 #1] INFO -- : ["APPLIED", :verify_same_origin_request, “/usr/local/bundle/gems/actionpack-…/request_forgery_protection.rb”, 240]

Slide 68

Slide 68 text

;ΓฦΓ

Slide 69

Slide 69 text

;ΓฦΓ •Gem Λ࡞Δͷʹඞཁͳ෺ͬͯԿͩΖ͏ •ٕज़ͱ஌ࣝʁ •ΞΠσΟΞʁ

Slide 70

Slide 70 text

ٕज़ͱ஌ࣝʁ •ActionTracer Ͱ΍ͬͨ͜ͱ͸΄΅ Rails ͷίʔυϦʔσΟϯά •ٕज़ྗ͕͋Δʹӽͨ͜͠ͱ͸ͳ͍ʢͦΕ͸ͦ͏ʣ •ٕज़͕͋Δ͔Β Gem Λ࡞ΔͷͰ͸ͳ͘࡞Δ͜ͱͰྗ͕͍͍ͭͯ͘ •ඞཁ͸ൃ໌ͱ੒௕ͷ฼

Slide 71

Slide 71 text

ྫ͑͹ •TracePoint ΛԿ΋ߟ͑ͣʹ࢖͏ͱॏ͘ͳΔ •શͯͷΠϕϯτͰൃՐ͢ΔͷͰ •࣮ߦ࣌ʹΦϓγϣϯΛ౉ͯ͠࢖͍͍͚ͨ࣌ͩ࢖͍͍ͨ •bundle exec rails server -actiontracer on Έ͍ͨͳײ͡ʁ

Slide 72

Slide 72 text

೉ͦ͠͏ •·ͨϞϯΩʔύον…ʁ •࠷࣮ۙߦ࣌Φϓγϣϯ౉ͨ͠ྫΛݟͨͧ LLVCVOSTQFDPQFOBQJ IUUQTHJUIVCDPNLLVCVOSTQFDPQFOBQJ

Slide 73

Slide 73 text

rspec-openapi •RSpec ͷϦΫΤετ spec ͔Β OpenAPI ͷεΩʔϚΛ࡞੒͢Δ Gem •͜Εͩʂ LLVCVOSTQFDPQFOBQJ IUUQTHJUIVCDPNLLVCVOSTQFDPQFOBQJ

Slide 74

Slide 74 text

rspec-openapi PQFOBQJSCcLLVCVOSTQFDPQFOBQJ IUUQTHJUIVCDPNLLVCVOSTQFDPQFOBQJCMPCGEEMJCSTQFDPQFOBQJSC require 'rspec/openapi/version' require 'rspec/openapi/hooks' if ENV['OPENAPI'] module RSpec::OpenAPI # ... end

Slide 75

Slide 75 text

ActionTracer BDUJPO@USBDFSSCcNBLJDBNFMBDUJPO@USBDFS IUUQTHJUIVCDPNNBLJDBNFMBDUJPO@USBDFSCMPCCGEBMJCBDUJPO@USBDFSSC require "action_tracer/version" require "action_tracer/railtie" if ENV["ACTION_TRACER"] require "action_tracer/filters" require "action_tracer/logger" require "action_tracer/action_tracer" ੈͷதʹ͸ແ਺ͷ͓खຊίʔυ͕͋Δ

Slide 76

Slide 76 text

ͱʹ͔͘ॻ͘ •౴͑ͷͳ͍ঢ়ଶͰॻ͖࢝ΊΔͱ͏·͘ॻ͚ͳ͍ •Ԛ͍ίʔυΛॻ͜͏ͱ͢ΔͱϞϠοͱ͢Δ •ʮ·ͣ͸Ԛͯ͘΋ॻ͍͔ͯΒ͖Ε͍ʹ͢Δʯͱࣗ෼ʹݴ͍ฉ͔ͤΔ •ॻ͘ͱ೴ΈͦͷϝϞϦ͕ۭ͘ͷͰࢥߟ͕ਐΉ •git ͷྺ࢙͸վมՄೳ

Slide 77

Slide 77 text

Πϯλʔωοτʹฉ͘ •ʮgem ࡞Γํʯ ͸͡Ίͯͷࣗ࡞HFNʮ)FMMP 5BNBʯΛग़ྗͯ͠ΈΔ!DPF@c2JJUB IUUQTRJJUBDPNDPF@JUFNTFBGFGBCF

Slide 78

Slide 78 text

ΞΠσΟΞʁ ;0;0ςΫϊϩδʔζͷٕज़ސ໰؛઒ࢯɺদాࢯɺ.BU[ࢯʹฉ͘ΤϯδχΞਓੜɻʙͱʹ͔͘ॻ͘ɺॻ͘͜ͱΛָ͠Ήʙ IUUQTUFDICMPH[P[PDPNFOUSZ@JOUFSWJFX ݱ৔ͷࠓ͋Δ՝୊Λ ͖ͪΜͱΦʔϓϯͳίʔυͰղܾ͢Δ

Slide 79

Slide 79 text

ɹ૸ͬͯΔϑΟϧλ͕Θ͔Βͳ͍ͷͭΒ͍Ͱ͢Ͷ ɹͭΒ͍Ͱ͢Ͷ… ɹҰཡͰݟ͍ͨͰ͢Ͷ ɹͦΕͩʂʂ ʮݱ৔ͷࠓ͋Δ՝୊ʯ

Slide 80

Slide 80 text

•ΞϓϦ͕஗͍ •CI ͕஗͍ •ϦϦʔε࡞ۀ͕஗͍ •։ൃ଎౓͕஗͍ •ίʔυ͕ಡΈʹ͍͘ •ίʔυ͕Ԛ͍ •etc etc… ʮݱ৔ͷࠓ͋Δ՝୊ʯ ෆద੾ͳΤϥʔϋϯυϦϯά ଐਓԽ͍ͯ͠ΔΤϥʔ؂ࢹ ແବͳSQLͷൃߦ ίʔσΟϯάϧʔϧ͕গͳ͍ ແடংͳϝλϓϩ େྔͷσουίʔυ ࢖ΘΕ͍ͯͳ͍ΞΫγϣϯ େྔͷApplicationController ਖ਼نԽ͞Ε͍ͯͳ͍ςʔϒϧ ൿ఻ͷλϨscope ௒ઈFatModel γεςϜεϖοΫ͕গͳ͍ ແବͳςετσʔλͷ࡞੒ ΦϨΦϨϙϦϞʔϑΟοΫςʔϒϧ ߦ͖ա͗ͨڞ௨Խ

Slide 81

Slide 81 text

•ͲΜͳݱ৔ʹ΋՝୊͸͋Δ •՝୊ͷΞϓϩʔνํ๏͸৭ʑ •ࠜຊղܾɾԠٸख౰ •ͭΒΈΛݴޠԽ͢ΔͱରԠํ๏͕ੜ·ΕΔͷͰޱʹग़͢ ʮݱ৔ͷࠓ͋Δ՝୊ʯ

Slide 82

Slide 82 text

ετϨεʹහײʹͳΔ •ϓϩάϥϚͷࡾେඒಙ •ଵଦɾ୹ؾɾၗຫ OE4UBUFPGUIF0OJPOc-BSSZ8BMM IUUQTXXXQFSMDPNQVCTIPXPOJPOIUNM ఘΊͣʹେਓʹͳΒͣʹΧοͱͳͬͯ΍Δ

Slide 83

Slide 83 text

࡞Δͷ͸ָ͍͠ •ϥΠϒϥϦͷίʔυΛಡΉػձ͕૿͑Δ •ʮ͜ͷؒݟͨʂʯ͕૿͑Δ •ͦͷύλʔϯ͕ͦͷύλʔϯͰ͋Δཧ༝Λߟ͑Δͷ΋ָ͍͠ •Α͍ύλʔϯ͕ࣗ෼ͷதʹ஝ੵ͞Ε͍͖ͯͦ͏

Slide 84

Slide 84 text

࡞Δͷ͸ָ͍͠ •ࣗ෼͕Ұ൪ͷϢʔβʔͰ͋Δಓ۩ •࢖͏ͱ࠷ߴʹؾ͍͍࣋ͪ •ਓ͕࢖ͬͯتΜͰ͘ΕΔͱخ͍͠

Slide 85

Slide 85 text

·ͱΊ •࡞Δͷ͸ָ͍͠ •ٕज़ྗ͸ޙ͔Β͍ͭͯ͘Δ •՝୊͸൒ܘ 5m ͷ਎ͷճΓʹ͖ͬͱ͋Δ

Slide 86

Slide 86 text

͝ਗ਼ௌ͋Γ͕ͱ͏͍͟͝·ͨ͠