ããã«ã¡ã¯ããšã³ãžãã¢ã®æ²³äºã§ãã 7æ11ã«æ¥ã«éå¬ããã Google Developers ML summit Tokyo ã®åå å ±åèšäºã§ãã åå ã®èæ¯ ãµãŒãã¹ã®èŠæš¡ãæ¡å€§ããŠããããšãããŒã¿éèšåºç€ãæ§ç¯ããããšã ããã·ã¥ ããŒããæŽåããããšãªã©ãããæ¬¡ã¯åæãèªååã§ãããšè¯ãã®ã§ã¯ãªãããšå人çã«èããŠããŠãæ
å ±åéã®ããã«åå ããŸããã Jeff Deam æ°ã«ããããŒããŒãã»ãã·ã§ã³ Google ã® AI ç ç©¶ããŒã Google Brain ã®ããããåãã Jeff Dean æ°ã®ã»ãã·ã§ã³ã§ãã æ©æ¢°åŠç¿ ãå¿
èŠãšããåé¡ã«å¯ŸããŠå°éå®¶ãå°ãªããããšããå顿èãããçŸåšã® solution = ML expert + data + computation ãšããé¢ä¿æ§ããå°æ¥çã«ã¯ solution = data + computation ã«ããããšãã匷ãã¢ãããŒã·ã§ã³ããã£ãŠããããšãäŒãã£ãŠããŸããã ãã®ãããªæèããç ç©¶ãé²ãã§ãã AutoML ã¯ãããŒã¿ã«é©åãã æ©æ¢°åŠç¿ ã¢ãã«ãèªåã§æ¢çŽ¢ããŸãã ç»åã» èªç¶èšèª ã»ã㌠ãã«ã㌠ã¿ãªã©ãæ§ã
ãªåœ¢åŒã®ããŒã¿ã«å¯ŸããŠææãåºãŠããŠããããã§ãã ç»åèªèã«ãããŠãAutoML ã®èªåæ¢çŽ¢ã«ããã¢ãã«ãå°éå®¶ãæ§ç¯ãããã®ããé«ç²ŸåºŠãåºãããšãããã®ã§ããäžã®ã¹ã©ã€ã㯠ãã¡ãã®è«æ ã«èŒã£ãŠããŸãã ãã¡ãã¯ æ©æ¢°åŠç¿ ã¢ãã«ã®ç²ŸåºŠãç«¶ã ã³ã³ããã£ã·ã§ã³ ã§ãã㌠ãã«ã㌠ã¿èª²é¡ã«ãã㊠AutoML ããŒã ã2äœã«å
¥è³ãã話ã§ããå ±åèšäºã¯ ãã¡ã ã§ãã ä»ã«ã æ©æ¢°åŠç¿ å
šè¬ã®è©±ããAIéçºã«ãããã¬ã€ããªã©ãããŸããããç·ã㊠AutoML ãåŒ·ãæšããŠããå°è±¡ãåããŸããã æ©æ¢°åŠç¿ ã®ååéã§æ¬¡ã
ãšææãåºããŠãã Google ãããã®äžç芳ãã©ã®ã¬ãã«ã§å®çŸããã®ããèãããšã¯ã¯ã¯ã¯ããŸããã Auto ML ã«ã€ã㊠ç¶ã㊠ã¯ã©ãŠã ãµãŒãã¹ãšããŠã® AutoML é¢é£ã®ã»ãã·ã§ã³ã«åå ããŸããã 粟床ã®é«ãã¢ãã«ãäœããšããç¹ã匷調ãããã¡ã ãããããã¯ã·ã§ã³ãžã®é
ä¿¡ãããã®åŸã®éçšãŸã§ãå¹
åºãã«ããŒãããã®ã ãšããããšã説æããŠããŸããã åŠç¿æã«åªå
ããèŠçŽ ã®èšå®ãã¢ãã«ã®ããŒãžã§ã³ç®¡çæ©èœãªã©ãå«ãã æ©æ¢°åŠç¿ ã®ã¯ãŒã¯ãããŒã«ãããŠå±äººæ§ãæããããšã«ç¹ãããããªã®ã§ããããã¯ã·ã§ã³ç°å¢ã§ æ©æ¢°åŠç¿ ã·ã¹ãã ãéçšããéã«ã¯éžæè¢ãšããŠå
¥ã£ãŠãããã ãªãšããå°è±¡ãåããŸããã AutoML Vision 掻çšäºäŸ Twitter ã§è©±é¡ã«ãªã£ã ã©ãŒã¡ã³äºé åé¡åšã®éçºè
ã§ããåäºããã®çºè¡šã§ãããšããšèªåã§å®è£
ã»ãã¥ãŒãã³ã°ããã¢ãã«ãš AutoML Vision ã§åŠç¿ããã¢ãã«ãæ¯èŒããŠã¿ããšãã話ã§ãã åäºãããèªåã§åŠç¿ããã¢ãã«ã¯ãææ°ã®ææ³ãè«æããåãå
¥ãã€ã€ã¢ãã«æ§ç¯ãããŒã¿æ¡åŒµã»ãã©ã¡ãŒã¿ãã¥ãŒãã³ã°ããããã®ã§ãåé¡ç²ŸåºŠã¯ 99% ãè¶
ãããšããããšã§ãéåžžã«ç²ŸåºŠãè¯ãã¢ãã«ãåºæ¥äžãã£ãŠããŸãã 察ãã AutoML ã®ææã§ãããéçãŸã§åŠç¿ãããçµæããªããš 98% ã®ç²ŸåºŠãéæããŸããã æ¬åœã«ã㌠ã¿ã»ãã ãš ã¢ãããŒã·ã§ã³ ã ãã§ãå°éå®¶ããã¥ãŒãã³ã°ããã¢ãã«ã«å¹æµãããã®ãåºæ¥äžãããšã¯ããšæåããŸãããããããªããä»ã¯ãŸã å®ç§ã§ã¯ãªããåŠç¿æéãé·ããã¢ãã«ãµã€ãºã倧ãããŠæšè«æéãé
ããããã§ããŠç²ŸåºŠã¯ãŸã è² ããŠããŸãã PoCçãªãã®ã¯ããã§ãµã¯ããšç«ã¡äžããŠããããã¯ã·ã§ã³ã«ä¹ãããšãªã£ããå°éå®¶ããã¥ãŒãã³ã°ãããã®ã䜿ãã®ãè¯ãããã§ãã ãŸãšã æ©æ¢°åŠç¿ ç ç©¶ã®æå
端ãè¡ãäŒæ¥ã®äžã®äººéã®è©±ãçŽæ¥èããæ©äŒã¯ãšãŠã貎éã§ãå€ãã®åŠã³ããããŸããã å°éå®¶ãããªããŠã æ©æ¢°åŠç¿ ãããšããã¡ãã»ãŒãžãè²ã
ãªå Žé¢ããèŠãããããã«ãªããŸããã誰ã§ã粟床ã®é«ãã¢ãã«ãäœãããšããããšã ãããã®çæã§ã¯ãªããåŠç¿ããé
ä¿¡ã»éçšãŸã§ã ã¯ã©ãŠã ãµãŒãã¹ã®ãšã³ã·ã¹ãã ã§ãŸãšããŠåãæ±ããããã«ããããšããæå³åããå
ŒããŠããŸãã 粟床é¢ã§ã¯ãŸã ãŸã å°éå®¶ã«å£ããšããã¯ãããã®ã®ã æ©æ¢°åŠç¿ æè¡ã®ç ç©¶ã¯æ¥ã
ããŸãããã鲿©ããŠããã®ã§ãä»åŸã«æåŸ
ãã€ã€ãã£ããã¢ãããæ ããªãããã«ãããã§ããã
ããã«ã¡ã¯ïŒ æè¿ãé«éåã«ããã£ãŠããRailsãšã³ãžãã¢ã®ã·ã¥ãŒã«( @shule517 )ã§ããéããªã£ãæã®æåãå端ãªãã§ãããïŒ ã¯ããã« äžå
·å調æ»ã£ãŠãã¡ããã¡ã倧å€ãããªãã§ããïŒ åé¡ã®åå ãåãã£ãŠããªããŠãæå
ã§äžå
·åãåçŸã§ããªãæã¯ã調æ»ãããªãé£ããã§ãããã®ãããäžå
·åãçºçããã¿ã€ãã³ã°ã«ã§ããã ãå€ãã®ãããã°æ
å ±ãæ®ããããªããŸãããïŒ ãšããããšã§ãä»åã®ããŒãã¯ãäžå
·å調æ»ã®æéãççž®ããããã®ä»çµã¿ãã«ã€ããŠã§ãïŒ Bugsnagã§äŸå€ãã©ãã§çºçããã®ãããåããïŒ ã ã¹ã¿ã¡ã³ã®éçºç°å¢ã«ã€ã㊠ãã§ç޹ä»ãããŸããããRailsã®ãšã©ãŒç£èŠã« Bugsnag ãšãããµãŒãã¹ã䜿ã£ãŠããŸããSlackãšã®é£æºãã§ãããªã¢ã«ã¿ã€ã ã«éç¥ãé£ãã§ããã®ã§ãããã«åé¡ã«æ°ã¥ãããšãã§ããŸãïŒ å®éã®ç»é¢ã¯ãã¡ãã§ãã Bugsnagã§åããå
容 ã©ãã§ãã©ããªäŸå€ãçºçããã®ã äŸå€ãèžãã ãŠãŒã¶ãŒã¯èª°ãªã®ã ã©ããªç°å¢ãªã®ã(OS / ãã©ãŠã¶ã®çš®é¡) ãªã©ãªã©ãBugsnagãå°å
¥ãããšããããã®æ
å ±ãéããŠãããŸãïŒ ãšãŠã䟿å©ïŒïŒ ã§ããããå°ããããã°æ
å ±ãã»ããïŒ äžå
·åã調æ»ãããšãªããšãBugsnagã§ãããªæ
å ±ãèŠãããšå¬ããã§ãããïŒ äŸãã°ã»ã»ã» ã¡ãœããã«æž¡ã£ãŠããåŒæ°ã¯ã©ããªå€ïŒ ã¢ãã«ã®idã¯ïŒ ã¢ãã«ã«ä¿åãããŠããå€ã¯ïŒ ä»åã¯ãã®ïŒã€ã®æ
å ±ãéç¥ããä»çµã¿ãäœã£ãŠãããŸããäžå
·åã®åçŸæ¹æ³ãé 匵ã£ãŠæ¢ãããããBugsnagãèŠããåå ãããã«åããããã«ãããã§ãããïŒ èª¿æ»â  Bugsnagã®å
¬åŒããã¥ã¡ã³ããèªã Bugsnagã®å
¬åŒããã¥ã¡ã³ã( bugsnag DOCS - Rails integration guide )ã確èªãããšãæ°ããã¿ãã远å ããŠããã¡ãã§èšå®ããæ
å ±ãèŒããããããã§ãïŒ ããã«è¿œå ã®ãããã°æ
å ±ãèŒããããããè¯ãããã§ãã class ApplicationController < ActionController :: Base before_bugsnag_notify :add_diagnostics_to_bugsnag # Your controller code here private def add_diagnostics_to_bugsnag (report) report.add_tab( :diagnostics , { product : current_product.name }) end end 次ã«ãããã°æ
å ±ãã©ã®ããã«ååŸãããæ€èšããŠãããŸãããã ã¡ãœããã«æž¡ã£ãŠããåŒæ°ã¯ã©ããªå€ïŒ ã¢ãã«ã®idã¯ããã€ïŒ ã¢ãã«ã«ä¿åãããŠããå€ã¯ïŒ ãããŒããã ã©ããããè¯ããã ããïŒ åšãã®ãµãŒãã¹ãªã©ã§ã¯ã©ãããŠããã ããïŒããšæãã2ã€ã®gem(Better Errors / New Relic)ã®ã³ãŒããèªãã§ã¿ãŸããã 調æ»â¡Â BetterErrors ã® gem ãåèã«ãã ã¿ããªãäžè©±ã«ãªã£ãŠããã§ããã Railsã®ãããã°ãšããã°ããã§ããããBetterErrorsã®ã³ã³ãœãŒã«ã«å€æ°åãå
¥åããã°ãå€ãåºåã§ããã®ã§ãåèã«ã§ãããïŒ Better Errors â» BetterErrors ãã ç»åãåŒçš â BetterErrorsã®ã³ã³ãœãŒã«ã¯ãbinding.eval(str)ãå®è¡ããŠãã ã module BetterErrors module REPL class Basic def initialize (binding, _exception) @binding = binding end def send_input (str) [execute(str), " >> " , "" ] end private def execute (str) " => #{ @binding .eval(str).inspect }\n" rescue Exception => e " !! #{ e.inspect rescue e.class.to_s rescue " Exception "}\n" end end end end â¡ ãã®Bindingã¯ãExceptionãæ¡åŒµãäŸå€ã¯ã©ã¹ã«ä¿æããŠãã module BetterErrors module ExceptionExtension prepend_features Exception def set_backtrace (*) if caller_locations.none? { |loc| loc.path == __FILE__ } @__better_errors_bindings_stack = :: Kernel .binding.callers.drop( 1 ) end super end def __better_errors_bindings_stack @__better_errors_bindings_stack || [] end end end ãªãã»ã©ãªãã»ã©ãäŸå€å
ã®BindingãååŸããããšãã§ããã°ãåŒæ°ãModelã®æ
å ±ãåãããã§ããïŒ èª¿æ»â¢Â New Relic ã® gem ãåèã«ãã æ¬¡ã«ç®ãã€ããã®ã¯ãããã©ãŒãã³ã¹ã®ç£èŠã§ãã€ããäžè©±ã«ãªã£ãŠããNew Relicã§ããDBã®æ€çŽ¢æéãªã©ã®åŠçæéã现ããããŒã¿ãåã£ãŠããã®ã§ãåèã«ãªããããgemã®ã³ãŒããèªãã§ã¿ããšãActiveRecordã§ãã䜿ãããŠããã¡ãœããé(saveãªã©)ã«æž¬å®çšã®åŠçãåã蟌ãã§ããŸããããŸããã€ãã³ããããã¯ããŠãåŠçãåã蟌ãã§ããã¿ããã§ããæã£ã以äžã«æ³¥èãããããïŒ New Relic â» New Relic ãã ç»åãåŒçš # encoding: utf-8 # This file is distributed under New Relic's license terms. # See https://github.com/newrelic/rpm/blob/master/LICENSE for complete details. require ' new_relic/agent/prepend_supportability ' module NewRelic module Agent module Instrumentation module ActiveRecordPrepend ACTIVE_RECORD = ' ActiveRecord ' .freeze module BaseExtensions def save (*args, &amp;blk) :: NewRelic :: Agent .with_database_metric_name( self .class.name, nil , ACTIVE_RECORD ) do super end end def save! (*args, &amp;blk) :: NewRelic :: Agent .with_database_metric_name( self .class.name, nil , ACTIVE_RECORD ) do super end end end # saveãšåãããã«äžèšã®ã¡ãœããã®å®è£
ããããŠããïŒç¥ # - touch # - update_all # - delete_all # - destroy_all # - calculate # - pluck end end end end ãªãã»ã©ãªãã»ã©ãäŸå€ã®ã€ãã³ããååŸã§ããã°ããªããšããªãããã§ããïŒ èª¿æ»â£Â TracePointã§äŸå€ãããã¯ãã Rubyã®ã€ãã³ãããã¯ãæ¢ããŠã¿ããšãTracePointã¯ã©ã¹ã䜿ããšäŸå€çºçæã®ã€ãã³ããããã¯ã§ããã¿ããã§ãïŒ ãã㟠- TracePointã¯ã©ã¹ TracePoint .new( :raise ) do |tp| # ãããã¯å
ã¯äŸå€ãçºçããæã«åã tp.raised_exception # ExceptionãååŸã§ãã end ã§ããTracePointã£ãŠé
ãã®ã§ã¯ïŒ åçŽã«TracePointãå®è¡ãããšãçŽ8åé
ããªãïŒïŒ (TraceãªãïŒ45.1ms â TraceããïŒ370.2ms) trace = TracePoint .new {} 10 .times.map { Benchmark .realtime { 1000000 .times { 1 + 2 } } }.sum/ 10 # Traceãªã => 0.04516370000201277 10 .times.map { Benchmark .realtime { trace.enable { 1000000 .times { 1 + 2 } } } }.sum/ 10 # Traceãã => 0.3702467000024626 äŸå€ã ããããã¯ããã°ã圱é¿ããªãïŒ ã€ãã³ãããã¯ã®å¯Ÿè±¡ãäŸå€ã ãã«éå®ããã°ãéåžžã®åŠçã«ã¯åœ±é¿ããããŸããïŒïŒ (TraceãªãïŒ42.9ms â TraceããïŒ42.5ms) trace_raise = TracePoint .new( :raise ) {} 10 .times.map { Benchmark .realtime { 1000000 .times { 1 + 2 } } }.sum/ 10 # Traceãªã => 0.04297380000061821 10 .times.map { Benchmark .realtime { trace_raise.enable { 1000000 .times { 1 + 2 } } } }.sum/ 10 # Traceãã => 0.04251920000097016 ãããªãé床ãåé¡ãªãããã§ããïŒ ããã§ææãæããŸããïŒïŒ 調æ»çµæã®ãŸãšã Bugsnagã«æ°ããã¿ãã远å ããŠãæ
å ±ã远å ã§ãã äŸå€çºçå
ã®BindingãååŸã§ããã°ãåŒæ°ãModelã®ããŒã¿ãååŸã§ãã TracePointã§äŸå€ã®ã€ãã³ããããã¯ããŠãäŸå€çºçå
ã®BindingãååŸã§ãã Bugsnagã«ãããã°æ
å ±ã远å ã§ããŸããïŒ å®æïŒïŒ 調æ»çµæãå
ã«ãBugsnagã®éç¥å
容ã«ãããã°æ
å ±ãå¢ãããŠã¿ãŸããã 察å¿ããå
容 Bugsnagã®ããŒãžã«ModelAttributesã¿ãã远å ïŒ ã¡ãœããã«æž¡ããåŒæ°ã®å€ã確èªã§ããããã«ãªã£ãïŒ Modelã§äŸå€ãçºçããå Žåã¯ãidãåã«ã©ã ã®å€ãèŠããããã«ãªã£ãïŒ ãã£ããŒïŒ ããã§ãã©ã®Modelã§äŸå€ãèµ·ãããã¯åãããã©ãã©ã®ã¬ã³ãŒããåãããªãã£ãŠããããšãç¡ããªããŸããåŒæ°ã«æž¡ã£ãŠãããã©ã¡ãŒã¿ãåããã®ã§ãäžå
·åã®èª¿æ»ãæãããã§ãïŒ æçµçãªã³ãŒãã¯ãã¡ãïŒ èª¬æã®ããã«ã³ãŒããç°¡ç¥åãã解説ã³ã¡ã³ãã远å ããŠãããŸãã å®éã«ã¯ãæ©å¯æ
å ±ã«é¢ããéšåã¯Bugsnagã§éç¥ããªããªã©ã®å¯Ÿå¿ãè¡ã£ãŠããŸãã äŸå€æã«ãäŸå€çºçç®æã®bindingãä¿æãã # äŸå€çºçã€ãã³ããããã¯ãã TracePoint .trace( :raise ) do |trace_point| exception = trace_point.raised_exception # äŸå€ã®çºçå
ã®æ
å ±ãååŸããããBindingãä¿æãã if trace_point.binding.respond_to?( :callers ) bindings = trace_point.binding.callers.drop( 1 ) else bindings = [trace_point.binding] end # exception.error_bindingsã«ãäŸå€çºçå
ã®bindingãä¿æããã exception.define_singleton_method( :error_bindings ) do bindings end end Controllerã§äŸå€ãçºçããæã«ãBugsnagãéç¥ãã class ApplicationController < ActionController :: Base rescue_from Exception , with : :notify_bugsnag # Controllerå
ã§äŸå€ãçºçããå Žåã«ãBugsnagãéç¥ãã def notify_bugsnag (exception) BugsnagReporter .notify(exception) raise exception end end äŸå€ã®çºçç®æã®åŒæ°ããActiveRecordã®ã¢ãã«æ
å ±ãBugsnagãžéç¥ãã module BugsnagReporter class << self def notify (exception) Bugsnag .notify(exception) do |report| # Bugsnagã«æ°ããã¿ãã远å ãã report.add_tab( :model_attributes , models : model_attributes(exception)) end skip_bugsnag(exception) # äºéã§éç¥ãããªãããã«éç¥ãã¹ããããã end def skip_bugsnag (exception) def exception. skip_bugsnag true end end def model_attributes (exception) # äŸå€ã«ä¿æããŠãããbindingã䜿ã exception.error_bindings.map do |binding| instance = binding.eval( ' self ' ) debug = {} # ActiveRecordïŒïŒModelã®äžã§äŸå€ãçºçããå Žåã¯ãã«ã©ã ã®ããŒã¿ãåºåãã debug.store( "#{ instance.class.name.downcase } _attributes " , instance.attributes) if instance.respond_to?( :attributes ) # äŸå€çºçå Žæã®ããŒã«ã«å€æ°ãåºå debug.store( :local_variables , local_valiables(binding)) # äŸå€çºçå Žæã®ã€ã³ã¹ã¿ã³ã¹å€æ°ãåºå debug.store( :instance_variables , instance_variables(instance)) [trace_path(binding), debug] end .compact.to_h end # äŸå€çºçå Žæã®ããŒã«ã«å€æ°ãååŸ def local_valiables (binding) binding.local_variables.map { |name| [name, format(binding.local_variable_get(name))] }.to_h end # äŸå€çºçå Žæã®ã€ã³ã¹ã¿ã³ã¹å€æ°ãååŸ def instance_variables (instance) instance.instance_variables.map { |name| [name, format(instance.instance_variable_get(name))] }.to_h end # ããŒã¿ãèªã¿ãããããã«å å·¥ def format (value) return value.attributes if value.respond_to?( :attributes ) return value.to_sql if value.respond_to?( :to_sql ) value end # äŸå€çºçå Žæã®ãã¡ã€ã«ãã¹ãçæãã def trace_path (binding) instance = binding.eval( ' self ' ) method_name = binding.eval( ' __method__ ' ) file = binding.eval( ' __FILE__ ' ) line = binding.eval( ' __LINE__ ' ) "#{ instance.class.name } # #{ method_name } - #{ file } : #{ line }" end end end ãããã« Bugsnagãæ¡åŒµããŠãäžå
·åã®èª¿æ»ã楜ã«ããä»çµã¿ã«ã€ããŠè§£èª¬ããŸããããã®å¯Ÿå¿ã¯ãåãäžå
·åã®åå ãçªãæ¢ãããŸã§ã«æéãããã£ãŠããŸã£ã倱æãããäžåºŠèµ·ãããªãããã«ããããã®æ¹åã®ïŒã€ã§ãããã®ããã«ã ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ã®è¡åæé ã«ãããã倱æã«åãåãããå®è·µã§ããèªåèªèº«ããããŠãµãŒãã¹ãæ¹åïŒæé·ããŠããããšã³ãžãã¢ãåéããŠããŸãïŒ ãã²ããŸã㯠ãšã³ãžãã¢æ¡çšãµã€ã ãèŠãŠã¿ãŠãã ããïŒ ããã°ãæåŸãŸã§èªãã§ããã ããŠãããããšãããããŸããïŒïŒ
ã¹ã¿ã¡ã³ ãšã³ãžãã¢ã®æŸè°·( @uuushiro )ã§ãã 2019幎6æ12æ¥(æ°Ž)ã6æ14æ¥(é)ã« AWS ã®æ¥æ¬æå€§çŽã®ã«ã³ãã¡ã¬ã³ã¹ AWS Summit Tokyo ã å¹åŒµã¡ãã» ã§éå¬ãããŸããããã®ã€ãã³ã2æ¥ç®ã«å®æœããã ã AWS Startup Architecture Of The Year 2019ããšããã³ã³ãã¹ãã®ãã¡ã€ããªã¹ããšããŠã¹ã¿ã¡ã³ãéžåºãããããšãããããã£ãããªããš3æ¥éãã«ã§åå ããŠããŸããã1æ¥ç®ãã3æ¥ç®ãŸã§ã§ãããããå°è±¡ã«æ®ã£ãåºæ¥äºããäŒãããããšæããŸãã çŽ æŽãããæ©äŒãé ãã1æ¥ç® å¹åŒµã¡ãã» ã§éå¬ãšããããšãããããããã®èŠæš¡ãšçãäžããã§ããã ãã ã1æ¥ç®ã¯ã»ãšãã©äŒå Žã§éããæéããªãã£ãã®ã§ã»ãã·ã§ã³ã¯èããªãã£ãã®ã§ããã AWS äž»å¬ã®Meeting of MindsãšããæåŸ
å¶ãã£ããŒã«åå ããŠããŸããã ãã®ãã£ããŒã§ã¯ã Amazon WorkSpaces ã AWS Glue ã®ãããŒãžã£ãŒã®æ¹ã
ããWEBæ¥çã§èåãªäŒæ¥ã®CTOããšã³ãžãã¢ã®æ¹ã
ãšãã£ãã°ããã«ã話ããããšãã§ããŸããã AWS ãšããäžçæåç·ã§æŽ»èºãããšã³ãžãã¢ã®æ¹ã
ã®èŒããããã£ãªã¢è©±ããã°ããŒãã«ãªããŒã ãªãã§ã¯ã®ãããžã¡ã³ãã«ãããèŠåŽè©±ãå
šãŠãèªåã®æ³åãé¥ãã«è¶
ããã¹ã±ãŒã«ã§ãèããŠããã ãã§è奮ããŸããã詳现ã¯ããä»¥äžæžããŸããããæ®éã§ã¯æãå±ããªããããªåŠã³ãããããæã¡åž°ãããšãã§ããŸãããä»åã® Meeting of Minds ã¯æ¥æ¬ã§ã¯ç¬¬1åç®ãšã®ããšã§ã次åããã£ããæ¯éåå ãããŠããã ãããå¬ããã§ãã æåŸã«å
šå¡ã§ãã·ã£ãªã ã€ãã«ã³ã³ãã¹ãåœæ¥ã®2æ¥ç® ã³ã³ãã¹ãåœæ¥ã§ãã Startup Architecture Of The Year ã®ãã¡ã€ããªã¹ãã®ç¹å
žãšããŠãStartup Centralå
ã§ã®ãœãªã¥ãŒã·ã§ã³å±ç€ºããŒã¹ãåºå±ã§ããæš©å©ãé ããŸãããåŒç€Ÿã®CTOå°æãšããšã³ãžãã¢ã®æŽ¥ç°ãå¿æŽã«é§ãã€ããŠãããŠãäžç·ã«ããŒã¹ã®èšå¶ãããŸãããã¡ãªã¿ã«ããã®ããŒã¹ã§ã¢ããŒã«ãã ã¢ãŒããã¯ã㣠ãŒã®ãã¹ã¿ãŒã¯ãåŒç€Ÿãã¶ã€ããŒã®æŸæ¬ãæäŒã£ãŠãããŸããïŒ Startup Architecture Of The Year ã§ã¯äžã®åçã®ããã«ãããã«å±ç€ºãããå ã¢ãŒããã¯ã㣠å³ã«æç¥šçšã®IoTãã¿ã³ãèšçœ®ãããŠãããããã·ã¥ãæãå€ãã£ã ã¢ãŒããã¯ã㣠ããªãŒãã£ãšã³ã¹è³ãç²åŸã§ãããšããäŒç»ããããŸããã 17æããã®æ¬çªã«å
ç«ã£ãŠãå±ç€ºäŒå Žã§15åéãããã®æ©äŒãé ããã®ã§ã ã¢ãŒããã¯ã㣠ã®ã¢ããŒã«ãšã¹ã¿ã¡ã³ãšã³ãžãã¢ããŒã ã®ç޹ä»ããããŠããã ããŸããã ã¹ã¿ã¡ã³ã®ããŒã¹ã«ç«ã¡å¯ã£ãŠãã ãã£ãæ¹ããªãŒãã£ãšã³ã¹è³ã®ãã¿ã³ãæŒããŠãã ãã£ãæ¹ã15åããããèããŠãã ãã£ãæ¹ãããããšãããããŸããïŒ ãããŠãã€ãã«æ¬çªã§ãïŒ ä»åã¹ã¿ã¡ã³ããšã³ããªãŒãã ã¢ãŒããã¯ã㣠㯠TUNAG ã®ETLåºç€(ããŒã¿åŠçåºç€)ã§ããããã® ã¢ãŒããã¯ã㣠ãŒã§å·¥å€«ãããã€ã³ãã¯å€§ããïŒã€ã§ãã 1ã€ç®ã¯ãS3ãããŒã¿ã¬ã€ã¯ãšããŠæŽ»çšããããšã§ããçµç¹ãšã³ã²ãŒãžã¡ã³ããšããã確ç«ããåæææšããŸã ãªãåéã«ãããŠãããŒã¿ã¬ã€ã¯ã®ãã㪠ã¢ãžã£ã€ã« ãªåæç°å¢ã®æ§ç¯ã¯ TUNAG ã®ããžãã¹äžéèŠãªéµãšãªã£ãŠããŸããããŸããS3ã«ä¿åããããšã§ã倿§ãªãã©ãŒãããã®å®å
šãªä¿åã»é«ãå¯çšæ§ãšã¹ã±ãŒã©ããªãã£ã»åŸé課éã«ããã³ã¹ãåæžã»ä» AWS ãµãŒãã¹ãšã®å®¹æãªé£æºãªã©ã®ã¡ãªãããç²åŸããããšãã§ããŸããã 2ã€ç®ã¯ããããŒãžãã»ãµãŒãã¹ãšãµãŒããŒã¬ã¹ãçµã¿åãããŠETLåºç€ãæ§ç¯ããããšã§ããå
šãŠåŸé課éå¶ã®ãµãŒãã¹ãªã®ã§ãå¢å ããããŒã¿éã«åãããŠæäœéã®æéã§ã·ã¹ãã ã皌åãããããšãã§ããŸãããŸãããããŒãžãã»ãµãŒããŒã¬ã¹ã®ã¡ãªãããæå€§éã«æŽ»çšããæ§ç¯ã»éçšã³ã¹ããæå°éã«æããªããå€§èŠæš¡ãªããŒã¿ã«å¯Ÿå¿å¯èœãªåºç€ã«ãªããŸããã ã¢ãŒããã¯ã㣠ã®è©³çްã«ã€ããŠã¯ã AWS Start Up ããã° ã埡芧ãã ããïŒ çµæãšããŠã¯ãã°ã©ã³ããªãåè³ããããšãã§ããŸããïŒ ä»åè©äŸ¡ããŠããã ããETLåºç€ããã«æŽ»çšããããŒã¿ã«åºã¥ããçµç¹ã®ãšã³ã²ãŒãžã¡ã³ãåæãããäžå±€é²ããŠãããããšæããŸãããŸãããã®åè³ããã£ããã«ã¹ã¿ã¡ã³ã® ã¢ãŒããã¯ã㣠ã«èå³ãæã£ãŠããããšã³ãžãã¢ã®æ¹ãå¢ããã°å¬ãããªãšæããŸããé¢ä¿è
ã®çããŸãå¿æŽããŠãã ãã£ãçããŸãæ¬åœã«ããããšãããããŸããã ä»åã®ãããã§äœ¿çšããã¹ã©ã€ãã¯ãã¡ãã§ãã https://speakerdeck.com/uuushiro/tunag-false-etlji-pan-aws-summit-startup-architecture-of-the-year-2019 ã°ã©ã³ããªã®å¯è³ãšã㊠re:inventãã¢ãã±ãã ãé ããã®ã§ãããre:inventã¯ãã£ãšè¡ããããšæã£ãŠããæµ·å€ã«ã³ãã¡ã¬ã³ã¹ã®1ã€ã ã£ãã®ã§ãšãŠãå¬ããã§ãããã¢ãã±ãããšããããšã§ãCTOå°æãš2人ã§è¡ã£ãŠããŸãïŒ re:inventã«è¡ãããæ¹ãæ¯éã©ã¹ãã¬ã¹ã§ãäŒãããŸãããïŒ ãã£ãšèœã¡çããŠã»ãã·ã§ã³ãèãã3æ¥ç® 3æ¥ç®ã¯ããããããã£ããã»ãã·ã§ã³ãèãããšãã§ããŸããã ãšãã«å°è±¡ã«æ®ã£ãŠããã»ãã·ã§ã³ã¯ä»¥äžã§ãã * Amazon RDS ã«ããããããã©ãŒãã³ã¹æé©åãšãããã©ãŒãã³ã¹ç®¡ç * Kubernetes on AWS ïŒ Amazon EKSå®è·µå
¥éïŒ * ãµãŒãã¹ã¡ãã·ã¥ã¯æ¬åœã«å¿
èŠãªã®ããäœã解決ããã®ã * Amazon Pinpoint ã§ãŠãŒã¶ãŒãæŽãã§é¢ã㪠TUNAGã®ã·ã¹ãã ã«ãããŠãçŽè¿ã«èª²é¡ã«ãªã£ãŠããããŒãœãã©ã€ãºãããéç¥åºç€( AWS Pinpoint)ããã³ã³ããæè¡ã»ãã€ã¯ããµãŒãã¹ãªã©TUNAGã®å°æ¥çãªå±æã«ãªã£ãŠããæè¡ã®è©±ãèããŠãããå
·äœçã«TUNAGã®æè¡çããŒãããããã€ã¡ãŒãžã§ãã3æ¥ç®ã§ããã ãŸãšã 3æ¥éãšãŠãå
å®ããæéãéãããŸãããç¹ã«ã¹ã¿ãŒãã¢ãããšããæ¬ãã§åå ããåãMeeting of Minds ãã Startup Architecture Of The Year ãªã©æ¬åœã«ããçµéšãããããšãã§ããŸãããre:inventã«ãåå ã§ãããšããããšã§äžå±€ AWS ãžã®ã¢ãããŒã·ã§ã³ãäžãããŸããããã£ãšãã£ãšäžæã AWS ã䜿ãåããããè¯ã ã¢ãŒããã¯ã㣠ã远æ±ããŠãããããšæããŸãã ã¹ã¿ã¡ã³ã§ã¯ããšã³ãžãã¢ã®æé·æ¯æŽãè¡ãäžç°ãšããŠãæè¡æžã®è³Œå
¥è²»ããã«ã³ãã¡ã¬ã³ã¹åå è²»ã®è£å©ã®å¶åºŠããããŸããä»åã® AWS Summitã¯åºåŒµæ±ããšããŠåå è²»ã»äº€éè²»ã»å®¿æ³è²»ãäŒç€Ÿã«è² æ
ããŠããããŸããã ã¹ã¿ã¡ã³ã§ã¯ AWS ãªã© ã¯ã©ãŠã ã®é²åã®æ³¢ã«äžæãä¹ããªãããäžç·ã«TUNAGãè¯ãããŠãããšã³ãžãã¢ãçµ¶è³åéäžã§ãïŒïŒèå³ãããæ¹ã¯ Wantedly ãããé£çµ¡ãã ããã
TL;DR (æŠèŠ) â GitHub ã® Webhook ãçšããŠãPullRequest ã merge ããããšãã«ã WordPress ã® ããŒããèªåæŽæ°ããèšå®ã§ãã ãã®èšå®ãããããšã§ããã¶ã€ããŒã WordPress ããŒããä¿®æ£ããéã®ãæ¬çªãžã®åæ ã®æéã倧å¹
ã«åæžã§ããLPOçã®ãµã€ãã®æ¹åãããããé²ãããã«ãªããŸãã â èæ¯ â åºå ±ãæ¡çšæ
åœã§Webãµã€ããæŽæ°ããããããããã« WordPress ã§ããµã€ããæ§ç¯ããããšããããŸãããã®å Žåããµã€ãã®ãã¶ã€ã³ã¯ã WordPress ã® ããŒã ãèªäœããããšã§è¡ããŸãã â ã¹ã¿ã¡ã³ã§ã¯ãããŒãã¯ã瀟å
ã® Webãã¶ã€ã㌠ãäœã Github ã§ç®¡çããŠããŸããã WordPress ãžã® åæ ã¯ã WordPress ã®ãã¡ã€ã«ç®¡çã® ãã©ã°ã€ã³ ã§ãã File Manager ãçšãããŠãæŽæ°ãããã¡ã€ã«ãæåã§ã¢ããããŒãããŠããŸããã ãã®æ¹æ³ã¯ãæéãããããããã¹ãèµ·ãããããã¹ããŒãã§ã¯ãããŸããã ââ WordPress ãš Github ãšã®é£æºã¯ã WP Pusher ã®ãããªãµãŒãã¹ã§å®çŸã§ããŸãããããŒãã Github ã® Private repository ã§ç®¡çããŠããããšãããã ãããªãã«è²»çšãããã ããšãããã Github Webhook ãçšããŠèªäœããŠã¿ãããšã«ããŸããã â Github ã§ PulLRequest ã Merge ããããšãã«ã WordPress ã«åæ ããæé â Git pull ããããã®èšå® â ãŸãã WordPress ã皌åããŠãããµãŒããŒã«ãŠã Github ãªããžã㪠ãã git pull ããŠã WordPress ããŒããååŸã§ããããã«ããå¿
èŠããããŸãã â Wordpress ã皌åãããµãŒããŒã«ãŠã OpenSSH ã® éµãã€ãããŸãã â $ ssh-keygen -t rsa -f id_rsa_wordpress çæãããå
¬ééµã Github ã® Deploy Keys ã«èšå®ããŸãã â â â æ¬¡ã«ã httpd ãµãŒã㌠ã®å®è¡æš©éã§ãgit pull ã§ããããã«ããŸãã â ããã¯ã Github Webhook ã§ WordPress ãåäœãã PHP 颿°ãåŒã³åºããŸããã WordPress ã¯ã apache ãŠãŒã¶ãŒã§åäœããŠããããã apache ãŠãŒã¶ãŒã§ git pull ã§ããªããš ããŒããæŽæ°ããããšãã§ããªãããã§ãã â ä»åã¯ã以äžã®å
容㮠/home/ apache /bin/git- ssh .sh ãã€ãããgit pull æã« GIT_ SSH ç°å¢å€æ° ã«ãŠæå®ããããã«ããŠããŸãã â #!/bin/sh ssh -oStrictHostKeyChecking = no -oUserKnownHostsFile = /dev/null -i /home/ < user_name > /.ssh/id_rsa " $@ " â git- ssh .sh ã§ã¯ãäžèšã®åŸåã¡ãã»ãŒãžã衚瀺ãããŠãgit pull ã倱æããªãããã«ãStrictHostKeyChecking ã off ã«ãããªã©ã®ãªãã·ã§ã³ãã€ããŠããŸãã â @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ â â 以äžã®ããã«ã wordpress theme ãååšãã git repository ã§ãgit pull ã§ããã°æºåã¯å®äºã§ãã â $ sudo -u apache GIT_SSH =/home/apache/bin/git-ssh.sh git pull origin master From github.com: < theme repository > * branch master - > FETCH_HEAD Already up-to-date. â WordPress ã« action ã远å â â WordPress ã® wp_ ajax _nopriv ãå©çšããããšã§ã Github hook ããä»»æã® PHP 颿°ãå®è¡ããããšãã§ããŸãã â wp_ajax_nopriv {$ REQUEST[âactionâ]} | Hook | WordPress Developer Resources â ä»åã¯ãäžèšã®é¢æ°ã theme ãã£ã¬ã¯ã ãªã® functions. php ã«è¿œå ããããšã§ã https://your-wordpress-site/wp-admin/admin-ajax.php?action=wp_ajax_nopriv_update_theme ã«ã¢ã¯ã»ã¹ããã°ãgit pull ãããããã«ããŸãã â â /** * Add deploy hook endpoint */ add_action('wp_ajax_nopriv_update_theme', 'update_theme_from_github'); function update_theme_from_github() { $secret = "XXXXXXXXXXXXXXXXXXXXXXXXXXXX"; $posted = file_get_contents('php://input'); if ( empty ($posted)) { return; } $signature = 'sha1='.hash_hmac('sha1', $posted, $secret); if ($_SERVER['HTTP_X_HUB_SIGNATURE'] !== $signature) { header('HTTP/1.1 403 Forbidden'); error_log('Signatures didn\'t match... '); return; } $json = json_decode($posted); error_log(__FUNCTION__.": \n".var_export($json, true)); if ($json- > ref === 'refs/heads/master') { $git_root = dirname(__FILE__); exec("cd ${git_root} &amp;&amp; GIT_SSH=/home/apache/bin/git-ssh.sh git pull origin master 2 > &amp; 1", $out); error_log(join("\n", $out)); echo join("\n", $out); } else { error_log(__FUNCTION__.": nothing for ".$json- > ref); } } â ãã®ãšãã GitHub ã® Webhook ã® Secret æ©èœ ãå©çšããããšã§ã GitHub ã® Webhook ããã®ã¢ã¯ã»ã¹æã®ã¿ git pull ããããã«ããŠããŸãã ãããã° ããããã«ããã«ã âerror_log ã§ãwebhook ã® payload ãåºåããŠããŸãããäžèŠã ã£ããæ¶ããŠãã ããã GitHub ã« Webhook ã远å â ããšã¯ãããŒãã® ãªããžã㪠ã«ãŠãWebhook ãèšå®ããã°ã宿ã§ãã â â â ãŸãšã â ãããŸã§ãæŽæ°ã®éã¯ããã¡ã€ã«ãäžã€äžã€ã¢ããããŒãããŠããã®ãã GitHub WebHook ã«ãã£ãŠãgit pull ããããšã§ãPullRequest ã Merge ããããšã§ã WordPress ã«åæ ãããããã«ãªããŸããã â ããã«ãã£ãŠã WordPress ã®æŽæ°ã®æéã倧å¹
ã«æžã£ããããLPO ãªã©ã®çްããæ¹åãããé²ãããã«ãªããŸããããŸãã GitHub Flow ã§ WordPress ããŒããæŽæ°ãããããŒã確ç«ããããšã§ãåã
ã®ä¿®æ£ã«å¯ŸããŠãããŒã ã¬ãã¥ãŒãå®çããããŠããŠã®å
±æããã¹ã®äœæžãéæããããšãã§ããŸããã â ã»ããšãå¬ããããšã ããã§ãïŒ â ãã ãã httpd ãåäœããæš©éã§ãgit pull ãããããã ã¬ã³ã¿ã«ãµãŒã ãŒãªã©ã®ç€Ÿå€å«ããè€æ°ã®ã¢ã«ãŠã³ããå
±åããç°å¢ã§ã¯ãã»ãã¥ãªãã£ãš Webhook ã«ãã git pull ã¯ãå
±åã§ããªããããããŸããããã®å Žåã¯ã WP Pusher ãªã©ã® ãã©ã°ã€ã³ ãå
¥ããŸãããã â â åèã«ãããµã€ã â WordPress ããŒãã Git 管çããŠèªåãããã€ãã - Qiita GitHubã®WebhookãPHPã§åãåãç·Žç¿ - tanaka's Programming Memo GitHubã®WebhookãPHPã§èªäœããæã«æžãæ€èšŒã³ãŒã - Qiita WordPress Git deployments with WP Pusher
ã¹ã¿ã¡ã³ã®ãã¶ã€ããŒã® ( @kiyoshifuwa )ã§ãã å
æ¥ ã¹ã¿ã¡ã³ã®ç»å£è³æãã³ãã¬ãŒãã Keynote ã§äœæããå
šç€Ÿå¡ã«å±éãè¡ããŸããã äœæã®æé ãæ°ãã€ããããšãå±éåŸã®ãã©ããŒãšããŠè¡ã£ãããšãªã©ãã玹ä»ããŸãã ãªãäœã£ãã æè¿ãã¹ã¿ã¡ã³ç€Ÿå¡ïŒäž»ã«ãšã³ãžãã¢ïŒã倧ã㪠ã»ã ããŒãã«ã³ãã¡ã¬ã³ã¹ã§ç»å£ããæ©äŒãå¢ããŠããŸããã ãããŸã§ã¯ç»å£è
ãæ¯åºŠ0ããäœæããŠããŸãããã ãã³ããã®çµ±äž äŒç€ŸãšããŠã®çµ±äžæãæãããã å®å®ããã¯ãªãªã㣠åã
ã®ãã¶ã€ã³ã¹ãã«ã«äŸåããªãããã«ããã è³æäœæã® å·¥æ° åæž ç»å£è
ã«ã¯è³æããã話ãå
å®¹ã«æ³šåããŠããããã äžèšãéæããããã«ãã¹ã¿ã¡ã³å
±éã®ç»å£è³æãã³ãã¬ãŒããäœæããããšã«ããŸããã ã©ã®ããã«äœã£ãã ã©ããªã¬ã€ã¢ãŠãã®ãã¿ãŒã³ãå¿
èŠãªã®ãææ¡ãããã®ã§ãæŽãåºãã®ããã« ãšã³ãžãã¢ãäœã£ãç»å£è³æã®ã©ããããããå
šãŠã®ããŒãžã«æé©ãªãã¶ã€ã³ãé©çšãããŸããã ãã®éãããããã®æ
å ±ã¯äžŠåã®é¢ä¿ïŒããç®æ¡æžããé©åãïŒæé©ãªã®ã¯ãããŒã§ã¯ãªããïŒããªã©ã詳现ãŸã§èªèãããåãããŸããã å®éã«äœã£ãã¹ã©ã€ã㯠ãã¡ã ã§ãã ãããŠãå¿
ã䜿ããã®ããã䜿ããã®ããã³ãã¬ãŒãã«èœãšã蟌ã¿ãŸããã ã§ãããã®ããã¡ã â²ãã³ãã¬ãŒããšäœ¿çšäŸïŒäžéšæç²ïŒ çšæããã¹ã¿ã€ã«ã¯ä»¥äžã®éãã§ãã è¡šçŽ èªå·±çŽ¹ä» ã¢ãžã§ã³ã ç®æ¡æžã ç®æ¡æžãïŒãã§ãã¯ãªã¹ãïŒ çªå·ä»ããªã¹ã ãã㌠ç»å+ããã¹ã ç»å+ããã¹ãïŒèª¬æä»ãïŒ ïŒè£è¶³ïŒ ã¢ãžã§ã³ã ã®æ°ã¯3ã5ã€ãŸã§å¯Ÿå¿ããŠããŸãã ãšã³ãžãã¢ã¯å³ãã³ãŒããèŠããããšãå€ãã®ã§ãçžŠãæšªã«å€§ãã䜿ããã¹ã¿ã€ã«ãçšæããŸããã ã¹ã¿ã¡ã³ã®ã»ãšãã©ã Mac ãŠãŒã¶ãŒãªã®ã§ã Keynote ãã¡ã€ã«ã®ã¿äœæããŸããã çŽè¿ã§å¿
èŠãªè³æã®èŠä»¶ã«åããã4:3ã§äœæããŸãããåŸã
16:9ãäœæããäºå®ã§ãã çšæããããŒã¿ã®è©³çް 䜿çšäŸãšãã¹ã¿ãŒã¹ã©ã€ããå
¥ã£ãŠãããããã³ãã¬ãŒã.keyããçšæããŸããã ç»å£è
ã¯ã以äžã®æé ã§ç»å£è³æãäœæããŸãã ããã³ãã¬ãŒã.keyããè€è£œ â äžèŠãªäœ¿çšäŸãåé€ â 䜿ãã䜿çšäŸã¯æžãæã ãŸãã¯ãã¹ã¿ãŒã¹ã©ã€ãããæ°èŠäœæ ãŸããã«ã©ãŒãã¬ãã. clr ããçšæããŸããã ããã¯ç»å£è
ã®PCã®ã~/Library/Colors/ãã«è¿œå ããŸãã ç»å£è³æäœææã«è²ã倿Žãããå Žåã¯ãçšæãããã¬ããããè²ãéžãã§ããããŸãã æ°ãã€ããããš ãã³ãã¬ãŒã䜿çšã®ããŒãã«ãäžããªããããã«ãŒã«ã詳现ãŸã§åºããããªãããã«æ°ãã€ããŸããã ãã¬ã€ãã«æ²¿ã£ãŠæŽåããŠãããšã ãäŒããŠãããšã¯èªç±ã«äœ¿ã£ãŠããã£ãŠããŸãã è³æäœæåŸã®ãã¶ ã€ã³ãã§ ãã¯ãå¿
é ã§ã¯ãªããç»å£è
æ¬äººãå¿
èŠãšããå Žåã®ã¿ç¢ºèªããããã«ããŸããã å±éåŸã®ãã©ããŒãšã¢ããããŒã ãã³ãã¬ãŒãã®ããŒã¿ãå±éåŸã瀟å
å匷äŒã§ç»å£è³æã®äœãæ¹ã®è©³çްã説æããŸããã ãã¶ ã€ã³ãã§ ãã¯ã Keynote ã®äœ¿ãæ¹èª¬æã¯ãèŠæãããã°åå¥ã§è¡ãããã«ããŠããŸãã ãŸããçŸç¶ã®ãã®ã¯å®æåœ¢ã§ãªããéæã¢ããããŒãããããŠæ¹åãç¶ããããšããæšãäŒããŸããã ã¹ã¿ã¡ã³ç€Ÿå¡ãããã³ãã¬ãŒã.keyãã䜿çšããŠç»å£è³æãäœã£ãéã¯ãæ¹åç¹ãæ°ãã«å¢ãããããã¹ã¿ãŒã¹ã©ã€ããªã©ããã£ãŒãããã¯ããŠããã£ãŠããŸãã å®éã«ãã®ãã³ãã¬ãŒããå©çšããŠãè³æãäœæãããšã³ãžãã¢ããã¯äžèšã®ãã£ãŒãããã¯ãããããŸããã ãã³ãã¬ãŒãããã®ãŸãŸäœ¿ã£ãŠè³æãäœãã ãã§ããã¶ã€ã³çã«æŽã£ãè³æã«ãªã楜ã ã£ãã ãã¶ã€ã³ãæèããå¿
èŠããªããããçºè¡šå
容ã®ãã©ãã·ã¥ã¢ããã«éäžã§ããã æå³ãããšãããç»å£è
ãçºè¡šå
å®¹ã«æ³šåã§ããã®ã¯è¯ãã£ãã®ã§ãæ¹åç¹ãªã©ãããã£ãŠã¢ããããŒããç¶ããŠãããŸãã ãŸãšã 以äžãç»å£è³æãã³ãã¬ãŒãã®äœæããå±éãŸã§ã«è¡ã£ãããšãã玹ä»ããŸãããè³æãã³ãã¬ãŒãäœæããæ€èšã®æ¹ã®åèã«ãªãã°å¹žãã§ãã æ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åã仲éãåéããŠããŸãããã²ãæ°è»œã«è©±ãèãã«æ¥ãŠãã ããïŒ Wantedly 㯠ãã¡ã
ã¹ã¿ã¡ã³ ãšã³ãžãã¢ã®æŸè°·( @uuushiro )ã§ãã6/8ã«éå¬ããã åå€å±RubyäŒè°04 ã§RubyÃAWS Lambdaã®å
容ã«ã€ããŠçºè¡šããŠããŸãããçºè¡šæã«å£é ã§è£è¶³ããŠããå
容ãå«ããŠä»åèšäºã«ããŸããããã¡ãã®ã¹ã©ã€ãã¯å³ãå€ãã«èª¬æãããŠããã®ã§åèã«ããŠããã ããã°å¹žãã§ãã https://speakerdeck.com/uuushiro/ruby-x-aws-lambdade-sabaresufalsedao-ru-tunagfen-xi-ji-pan-falseshi-li-womotoni TL;DR ã¹ã¿ã¡ã³ãéå¶ããŠãããµãŒãã¹ãTUNAGãã«ãããããŒã¿åŠçåºç€ã«AWS Lambdaãå°å
¥ããããšã§ãç°¡åã«äžŠååŠçãå®çŸãããŒã¿åŠçæéã倧å¹
ã«ççž®ããããšãã§ãããµãŒããŒéçšã³ã¹ãããè§£æŸãããŸãããä»åã¯Ruby à AWS Lambda à SAM ãå©çšããã¢ããªã±ãŒã·ã§ã³ã®äœææ¹æ³ãèªåãã¹ãã«ã€ããŠå
±æããŸãã AWS Lambda掻çšã®èæ¯ ã¹ã¿ã¡ã³ãéå¶ããŠãããµãŒãã¹ããTUNAGãã¯äŒç€Ÿãšç€Ÿå¡ã»ç€Ÿå¡å士ã®âšãšã³ã²ãŒãžã¡ã³ã(ä¿¡é Œé¢ä¿)åäžãâšç®çãšããäŒæ¥åãSNSã§ããTUNAGã§çºçããã³ãã¥ãã±ãŒã·ã§ã³ãªã©ã®ããŒã¿ã¯ãäŒç€Ÿãžã®é¢å¿ã瀟å¡å士ã®é¢ä¿æ§ãåæ ãããŠããŸãããã®ããŒã¿ãåæããããšã§ã瀟å
ã®ãšã³ã²ãŒãžã¡ã³ãç¶æ³ãå®éçã«æž¬å®ããããšãã§ããŸãããã®ããŒã¿ãšããæ ¹æ ã«åºã¥ãã瀟å
掻æ§åæœçã®PDCAãåãããšãã§ãããšãããšããã TUNAG ã®åŒ·ã¿ã®1ã€ã§ãããã®ãããããŒã¿åŠçåºç€ã¯ããããã®TUNAGã®ããžãã¹äžéèŠãªéµã«ãªã£ãŠããŸãã ãããããšã³ã²ãŒãžã¡ã³ãã®ãããªå®æ§çãªãã®ãæ°å€åãããšããããšã¯ãŸã ãŸã äžç¢ºå®æ§ãé«ãã§ãããã®ããé »ç¹ã«åæææšã®èŠçŽããããããšãããã®ã§ãããéå»å
šæéåã®ææšã®åéèšãšããªããšæ¢åã®ããŒã¿åŠçåºç€ã§ã¯8æéã»ã©ã®æéãããã£ãŠããŸãã詊è¡é¯èª€ã®åæ°ã«å¶éããããŸãããããã§AWS Lambdaã®ãªãŒãã¹ã±ãŒã«ã掻çšããããšã§ãè€æ°ã®EC2ã€ã³ã¹ã¿ã³ã¹ã®ç®¡çãããã«äžŠååŠçãå®çŸã§ããéèšæéãççž®ããããšãã§ããŸããããŸããã¹ã¿ã¡ã³ã§ã¯Rubyã«æ
£ããŠãããšã³ãžãã¢ãå€ããšããçç±ãããAWS Lambdaã§ã¯Ruby ã©ã³ã¿ã€ã ãå©çšããŸããããã®èšäºã§ã¯ãRuby à AWS Lambda à SAM ãå©çšããã¢ããªã±ãŒã·ã§ã³ã®äœæåã³èªåãã¹ãã®æ¹æ³ã«ã€ããŠå
±æããŸãã 管çãã¬ãŒã ã¯ãŒã¯ SAMãå©çšããéçº ããŒã¿åŠçåºç€ã®ããã«äžå®ã®èŠæš¡ä»¥äžã®ã·ã¹ãã ããµãŒããŒã¬ã¹ã§æ§ç¯ããå Žåãè€æ°ã®AWS Lambda颿°ã管çããå¿
èŠãããç
©éã«ãªããã¡ã§ãããããŠRubyã®ã³ãŒããLambdaã«ãããã€ããéã«ããS3ãžãã¡ã€ã«ãã¢ããããŒããAWS LambdaãšçŽä»ããäœæ¥ãå¿
èŠã§æéãšæéãããã£ãŠããŸããŸãããã®ãããªæ©ã¿ã解決ããŠãããã®ããAWS SAM(Serverless Application Model)ã§ãã AWS SAM(Serverless Application Model) SAMã¯ä»¥äžã®ç¹åŸŽãæã€ããµãŒããŒã¬ã¹ã¢ããªã±ãŒã·ã§ã³ãæ§ç¯ããããã®ãã¬ãŒã ã¯ãŒã¯ã§ãã * ãµãŒããŒã¬ã¹ã¢ããªã±ãŒã·ã§ã³ã®ç®¡çãã¬ãŒã ã¯ãŒã¯ * CloudFormationã®ãµãŒããŒã¬ã¹ãªãœãŒã¹ç¹åã®æ¡åŒµ * ã¢ããªã±ãŒã·ã§ã³ã®ãã«ãã»ããã±ãŒãžã³ã°ã»ãããã€ã³ãã³ããæäŸ * ããŒã«ã«ã§AWS Lambdaã®æ§ç¯ã»ãã¹ãã»ãããã°ãå¯èœ ãã®ãããè€æ°ã®Lambda颿°ãCloudFormationã®ããã«ã³ãŒãã§ç®¡çããããšãã§ãããŸãCLIã§ãããã€ãç°¡åã«å®è¡ã§ããŸããå
·äœçãªSAMã®äœ¿ãæ¹ãèŠãŠãããŸãã SAMãšã¯ SAMã®éçºãã㌠以äžã®ããã«ã sam init --runtime ruby2.5 ãšããããšã§ã©ã³ã¿ã€ã ãRubyã«æå®ãããã¢ããªã±ãŒã·ã§ã³ã®ãµã³ãã«ã®é圢ãäœæãããŸããããã§çæããã template.yml ãšãããã¡ã€ã«ã«ã¯AWSã®ãµãŒããŒã¬ã¹ãªãœãŒã¹ãèšè¿°ãããŠããã以äžã® AWS::Serverless::Function ã¯AWS Lambdaã®é¢æ°ã衚ããŠããŸãã 以äžã®SAMã³ãã³ã package ãå®è¡ãããšãå¯Ÿè±¡é¢æ°ã®ãã©ã«ã(CodeUriããããã£)ã® .zip ãã¡ã€ã«ãäœæãS3ãžã¢ããããŒãããããã«ããŒã«ã«ã®çæç©ãžã®åç
§ãã¢ããããŒãããS3ã®URLã«çœ®ãæãã packaged.yamlãšãããã¡ã€ã«ãäœæãããŸãã $ sam package --s3-bucket tunag-sam-temlate-store ãããŠä»¥äžã® deployã³ãã³ããå®è¡ãããšããã³ãã¬ãŒãã«èšè¿°ãããŠããéãã®ãªãœãŒã¹ãAWSäžã«æ§ç¯ãããŸãã $ sam deploy \ --template-file packaged.yaml \ --stack-name sample-functions \ --capabilities CAPABILITY_NAMED_IAM Gemã®æ±ãã«ã€ã㊠Rubyã§AWS Lambdaã®ã¢ããªã±ãŒã·ã§ã³ãäœæããéã«ããéåžžã®Rubyã¢ããªã±ãŒã·ã§ã³éçºãšåãããã«äŸ¿å©ãªGemã§éçºå¹çãäžãããã§ããäžèšã®sam buildã³ãã³ãã¯ãã¢ããªã±ãŒã·ã§ã³å
ã®é¢æ°ãç¹°ãè¿ãåŠçããGemfileãããšã«äŸåé¢ä¿ã解決ããLambdaã«ãããã€ã§ããææç©ã.aws-sam / buildãã©ã«ããŒã«æžã蟌ã¿ãŸãã $ sam build Gemfileã«å¿ããŠäžèšã®ã³ãã³ããå®è¡ããããããææç©ã®ãã£ã¬ã¯ããªæ§æã¯ä»¥äžã®ããã«ãªããŸãã $ bundle install --deployment --without development test AWS Lambdaã§ã¯ã以äžã®ããã« vendor/bundleãgemã®æ¢çŽ¢ãã£ã¬ã¯ããªå¯Ÿè±¡(GEM_PATH)ã§ãããããsam build ã³ãã³ãã§çæãããææç©ããããã€ããã ãã§AWS LambdaããGemãå©çšããããšãã§ããŸãã # GEM_PATH $LAMBDA_TASK_ROOT/vendor/bundle/ruby/2.5.0 /opt/ruby/gems/2.5.0 ãã€ãã£ãæ¡åŒµãããGemã®æ±ãã«ã€ã㊠ãã€ãã£ãæ¡åŒµãããGemã¯ãLambdaã®å®è¡ç°å¢ãšåçç°å¢ã§ææç©ãçæããå¿
èŠããããŸãã äŸãã°ãnokogiri ãäŸåããŠãã libxml2 ãš libxslt ã¯Lambdaã®ã€ã¡ãŒãžã«å«ãŸããŠããã®ã§ã以äžã®ã³ãã³ãã§ãã«ãå¯èœã§ãã buildã³ãã³ãã« --use-containerãªãã·ã§ã³ãã€ãããšãLambdaã®å®è¡ç°å¢ãšåçç°å¢ã®ã³ã³ããå
ã§ã§ãã«ããå®è¡ããŠãããŸãã $ sam build --use-container ããã«å¯ŸããŠãmysql2 ãäŸåããŠãã mysql-devel ã¯AWS Lambdaã®ã€ã¡ãŒãžã«å«ãŸããŠããªããããLambdaã€ã¡ãŒãžã³ã³ããã§å¿
èŠãªããã±ãŒãžãã€ã³ã¹ããŒã«ããäžã§bundle installãããå¿
èŠããããŸãã $ docker run -v `pwd`:/var/task -it lambci/lambda:build-ruby2.5 \ /bin/bash -c "yum -y install mysql-devel; bundle install --path=ruby/gems;" ãã®ãšããå¿
èŠãªå
±æã©ã€ãã©ãª(libmysqlclient)ã¯ã LD_LIBRARY_PATHã«é
眮ããããšã§Lambdaããèªã¿èŸŒãããšãã§ããŸãã $ LD_LIBRARY_PATH: /var/task:/var/task/lib:/opt/lib AWS Lambda Layers è€æ°ã®AWS Lambdaã®é¢æ°ãçµã¿åãããŠã·ã¹ãã ãæ§ç¯ããŠãããšãè€æ°ã®é¢æ°ã®éã§å
±éã®ããžãã¯ã³ãŒããã§ãŠããŸãã AWS Lambdaã§ã¯ãããããã®é¢æ°ã«å
±éããã³ãŒããããããã®é¢æ°ã«å¯Ÿãã¢ããããŒãããå¿
èŠããããŸãããŸããããžãã¯ãæŽæ°ãããã³ã« activesupport ã mysql2 ã®Gemã®ãã¡ã€ã«çŸ€ãªã©ã倿Žããªããã¡ã€ã«ãã¢ããããŒãããå¿
èŠããããŸãããã®çµæãã¢ããããŒããããã¡ã€ã«ã®å®¹éãç¡é§ã«å¢ãã容éã®å¶éããããã€æéã®é
å»¶ãªã©åé¡ãåºãŠããŸããããã§ AWS Lambda Layers ãšããæ©èœã圹ã«ç«ã¡ãŸããAWS Lambda Layersã¯ä»¥äžã®ç¹åŸŽããããŸãã è€æ°ã®AWS Lambdaã§ã³ãŒããå
±æã§ããä»çµã¿ LambdaãåŒã³åºããšLayersãã³ã³ããã®/opté
äžã«ããŠã³ãããã sam build ã¯çŸæç¹ã§éå¯Ÿå¿ AWS Lambda Layers ã¯ãå
±æã³ãŒãã管çããããã³ãŒãã®å€æŽé »åºŠãå°ãªãã©ã€ãã©ãªãGemãé
åžãããããå Žåã«ç¹ã«äŸ¿å©ã§ããLayersã®ããã±ãŒãžãè€æ°ã®Lambda颿°ã«æ·»ä»ããŠäœ¿çšããããšãã§ãããããAWS Lambda颿°ã®ããžãã¹ããžãã¯ãåçŽåãããäŸåé¢ä¿ç®¡çã容æã«ãªãããããã€ããã±ãŒãžã®ãµã€ãºãå°ããã§ããŸãã ãŸããAWS Lambdaã®ãããã€ããã±ãŒãžã®æå€§ãµã€ãºã¯50 MBã§ãããæå€§5ã€ã®Layersãã¢ã¿ããããããšã§ã250MBãŸã§äžéãå¢ããããšãã§ããŸãããŸããGemãå
±æã³ãŒããªã©ã®äŸåé¢ä¿ãšããžãã¹ããžãã¯ã®éã§ãé¢å¿äºã®åé¢ã匷å¶ã§ããããšãLayersãå©çšããããšã®å¯æ¬¡çãªã¡ãªããã§ããããšæããŸãã AWS Lambda Layers åèURL å
±éã³ãŒãçšãLayersã§æ±ãæ¹æ³ã¯ã以äžã®ããã« template.yamlã« AWS::Serverless::LayerVersionãšãããªãœãŒã¹ãå®çŸ©ããAWS::Serverless::Functionã®Layersããããã£ããåç
§ããã ãã§ãã Layersã«ãããRubyã©ã€ãã©ãªã®æ¢çŽ¢ãã¹(RUBY_LIB)㯠ruby/lib ãªã®ã§ã以äžã®ãããªæ§æã§ã³ãŒããé
眮ããã°ãLambdaããLayersãéããŠèªã¿èŸŒãããšãã§ããŸãã Layersã«ãããGemã®æ¢çŽ¢ãã¹(GEM_PATH)㯠ruby/gems/2.5.0 ãªã®ã§ã以äžã®ãããªæ§æã§Gemãé
眮ããã°ãLambdaããLayersãéããŠèªã¿èŸŒãããšãã§ããŸãã img2lambdaã䜿ã£ãLayersã®ãããã€(ããŸã) Layersãäœæããéã«ãäŸåé¢ä¿ã®ç®¡çãªã©ãå€å°ç
©éã«ãªã£ãŠããŸããããã®ç®¡çãããå°ãç°¡åã«ããæ¹æ³ã¯ç¡ãããšè²ã
æ¢ããŠããäžã§ãAWSãæäŸããŠãã img2lambda ãšããã©ã€ãã©ãªããã£ãã®ã§ç޹ä»ããŸãã ããã¯ãDockerã€ã¡ãŒãžãLambdaãLayersã«å€æããããã€ã§ããããŒã«ã§ããããå©çšããããšã§äŸåé¢ä¿ãã€ã¡ãŒãžã«éã蟌ãâšå€æŽãããã°åãã«ããšãã圢ã§ç°¡åã«ç®¡çã§ããããã«ãªããŸãããšãŠã䟿å©ãªã®ã§äœ¿ã£ãŠã¿ããã£ãã®ã§ãããåŸè¿°ããSAMã®AWS Lambda Layersã®ããŒã«ã«å®è¡æ©èœãå©çšã§ããªããªã£ãŠããŸãã®ã§ãä»åã¯æ¡çšãèŠéããŸããã img2lambda èªåãã¹ã AWS Lambdaã®ã³ãŒãã«ã€ããŠãæ®éã®ã¢ããªã±ãŒã·ã§ã³ãšåæ§ã«èªåãã¹ããæžããŠãããŸãããã ãAWS Lambdaãšããç°å¢ã®ç¹æ§ãèæ
®ãã以äžã®ãããªæ¹éã§èªåãã¹ããè¡ãããšã«ããŸããã ã¯ã©ãŠãäžã§ã®æ€èšŒã¯ãããã€ãªã©ãå«ãæéãæããéããã®ã§ãªãã¹ã ããŒã«ã«ã§ãã¹ã ããã AWS Lambdaã¯ä»ã®AWSãµãŒãã¹ãšã®é£æºãå€ãçµ±åãã¹ããéèŠã«ãªã IAMæš©éãªã©ã®ãã¯ã©ãŠãã§ããæ€èšŒã§ããªããã®ã«éãã¯ã©ãŠãäžã§ãã¹ããè¡ã ä»åã¯AWS LambdaãããŒã«ã«ã§ãã¹ããããæ¹æ³ã«ã€ããŠãã©ãŒã«ã¹ããŸããAWS Lambdaã®ããŒã«ã«ãã¹ãã«ãããŠã以äž3ç¹ã®ãã€ã³ããèæ
®ããã¹ããäœæããŸããã AWS Lambdaç¹æã®äŸåãæé€ãåäœãã¹ããè¡ããããã³ãŒãã«ãã AWSãµãŒãã¹ã«äŸåããããžãã¯ã¯ã¹ã¿ããå©çšãã ããŒã«ã«ã«Lambdaã®ãšã³ããã€ã³ããèµ·åããAWS Lambdaã»Layersã®çµã¿åããã§ãã¹ããã å®éã«ãTUNAGã®ããŒã¿åŠçåºç€ã§æ³å®ããããã¹ãã±ãŒã¹ãšããŠãAthenaãžã®ã¯ãšãªã倱æããå ŽåãèããŠã¿ãŸãããã¹ãã®æé ãšããŠã以äžã®1~3ãã³ãŒããšå
±ã«ç€ºããŸãã â Athenaãžã®ã¯ãšãªå®è¡APIãå©ã â¡ ã¯ãšãªã¯éåæçã«åŠçãããããâšå®è¡IDãããšã«å®è¡ç¶æ³ãåãåããã ⢠ã¯ãšãªã®çµæãååŸãç¶æ
ãâš âFAILEDâãªãäŸå€ãæãã ãŸãããã§1ã€ç®ã®ãã€ã³ãã AWS Lambdaç¹æã®äŸåãæé€ãåäœãã¹ããè¡ãããããã ãèæ
®ãã以äžã®ããã«Lambdaã«äŸåãããã³ãã©ãŒã®åŒæ°ãªã©ã®ç®æããããžãã¯ãåãé¢ããŸãã ããããããšã§ä»¥äžã®ããã«ãLambdaã«äŸåããªãã³ãŒãã«ãªããéåžžã®Rubyã®ã¯ã©ã¹ãšããŠãã¹ããå¯èœã«ãªããŸããã ç¶ããŠãAthenaã®ãããªä»AWSãµãŒãã¹ã«äŸåããããžãã¯ãã©ã®ããã«ãã¹ããããã°ããã§ãããããããã§ã2ã€ãã®ãã€ã³ãã® AWSãµãŒãã¹ã«äŸåããããžãã¯ã¯ã¹ã¿ããå©çš ãå®è·µããŸããAWSãæäŸãããAWS SDK for Ruby ã«ã¯ 䟿å©ãªã¹ã¿ãæ©èœããããAPIã¢ã¯ã»ã¹ã®ã¬ã¹ãã³ã¹ã»ãšã©ãŒãç°¡åã«ã¹ã¿ãããããšãã§ããAWS APIã¯ã©ã€ã¢ã³ããå®è¡ããããã«æ¯ãèããŸãã ã¯ã©ã€ã¢ã³ããã¹ã¿ãããæ¹æ³ã¯ã以äžã®ããã«ãã¯ã©ã€ã¢ã³ããªããžã§ã¯ãäœææã« stub_responsesãã©ã¡ãŒã¿ã«trueãèšå®ããŸãããããŠãstub_dateã¡ãœããã§ã¹ã¿ãããŒã¿ãäœæããstub_responsesã¡ãœããã®åŒæ°ã«èšå®ããããšã§ãç¹å®ã®APIåŒã³åºãã¡ãœããã®ã¬ã¹ãã³ã¹ã«ã¹ã¿ãããŒã¿ãèšå®ããããšãã§ããŸãã ããã§äœæããã¹ã¿ããªããžã§ã¯ããåŒæ°ã§ãã¹ãå¯Ÿè±¡ã«æž¡ãããšã§ãããŒã«ã«ã§AWSãµãŒãã¹äŸåã®ãã¹ããã§ããããã«ãªããŸããã AWS Lambdaã®ãã¹ãã«ã€ããŠã¯ @t_wada ããã®è³æ Testable Lambda ããšãŠãåèã«ãªã£ãã®ã§ãèå³ã®ããæ¹ã¯æ¯éèŠãŠã¿ãŠãã ããããã®è³æã®äžã§å©çšãããŠãã LocalStackãšãããããŒã«ã«ã«AWSãµãŒãã¹ãšåçã«æ¯ãèããšã³ããã€ã³ããæäŸããŠããããµãŒãã¹ãå©çšããŠãã¹ããããŠãè¯ãã£ãã®ã§ãããä»åããŒã¿åŠçåºç€ã«å¿
èŠãªAWS Athenaã»Glueã¯âšéãµããŒãã ã£ããããAWS SDK for Ruby ã®ã¹ã¿ãæ©èœãå©çšããŸããã ãããŠ3ã€ç®ã®ãã€ã³ãã ããŒã«ã«ã«Lambdaã®ãšã³ããã€ã³ããèµ·åããAWS Lambdaã»Layersã®çµã¿åããã§ãã¹ã ãããŸããããŒã«ã«ã§AWS Lambdaã»Layers ãçµã¿åãããŠãã¹ããããæ¹æ³ãšããŠãSAMã«ã¯ããŒã«ã«ã§Lambdaã®ãšã³ããã€ã³ããèµ·åããã³ãã³ããçšæãããŠããŸãã $ sam local start-lambda äžèšã®ã³ãã³ããå®è¡ãã以äžã®ããã«ãšã³ããã€ã³ããããŒã«ã«ã«åãããã¹ã察象ã®Lambdaãinvokeããããšã§ãLayersãå«ããAWS Lambdaã®ãã¹ããããŒã«ã«ã§å®è¡å¯èœã«ãªããŸãã ãŸãšã 以äžãRuby à AWS Lambda à SAMã«ãããã¢ããªã±ãŒã·ã§ã³äœæã»èªåãã¹ãã«ã€ããŠèª¬æããŸãããéåžžã®Rubyã¢ããªã±ãŒã·ã§ã³éçºãšæ¯ã¹ããšèæ
®ããã¹ãäºé
ãå€ããæ±ºããŠç°¡åã§ã¯ãããŸããã§ãããããããä»åã®ããŒã¿åŠçåºç€ã«ãããŠãAWS Lambdaã®ããªãŒãã¹ã±ãŒã«ãããåŸéèª²éæ§ã«ããã³ã¹ãããã©ãŒãã³ã¹ã®åäžããããµãŒããŒéçšã³ã¹ãããã®è§£æŸããšããã¡ãªããã¯ããã®ãããªã³ã¹ããåªã«äžåã£ãã®ã§ã¯ãªãããšèããŠããŸãã ãŸãä»å玹ä»ããéçºã»èªåãã¹ãæ¹æ³ããã¹ãã ãšã¯æããŸããã®ã§äŸãã°ãSAMã§ã¯ãªãServerless Frameworkãªããã£ãšç°¡åã«ã§ãããããã®ããŒã«äŸ¿å©ã ãããšããããŸãããæ¯éãã£ãŒãããã¯ããã ãããšå¬ããã§ãã ã¹ã¿ã¡ã³ã§ã¯ãRailsã¢ããªã±ãŒã·ã§ã³ã®éçºåã³ãåºç€ã·ã¹ãã ãAWSã§æ§ç¯ãããšã³ãžãã¢ã倧åéäžã§ãïŒèå³ãããããæ¹ã¯æ¯éãã¡ãã® ãšã³ãžãã¢æ¡çšãµã€ã ããæ°æ¥œã«ãé£çµ¡ãã ããã
ã¹ã¿ã¡ã³ ãšã³ãžãã¢ã®æŽ¥ç°ã§ããã¹ã¿ã¡ã³ã§éå¶ããŠãããµãŒãã¹ããTUNAGãã§ã¯ãæ¯æ¥ãããŒã¿ããŒã¹ã®"ãã®æ¥ã®ç¶æ
"ãå¥ã
ã®ããŒã¿ããŒã¹ãšããŠæ®ããŠããŸããããã¡ãã®éçšãã AWS ã®S3ãGlueãAthenaãå©çšããŠçœ®ãæããã®ã§ããããã®äžã§å©çšããã MySQL äºæAuroraãããS3äžãžã®ããŒã¿æœåºçš ã¹ã¯ãªãã ã®ç޹ä»ãããããŸãã TL;DR (æŠèŠ) TUNAGã§ã¯ãããŒã¿ããŒã¹ãšããŠã Amazon Aurora ( MySQL äºæ)ã䜿çšããŠããŸãããµãŒãã¹å©çšç¶æ³ã®åæãªã©ã§ãéå»ã®ç¹å®æç¹ã§ã®ããŒã¿ããŒã¹ãå¿
èŠãšãªããããããŸãŸã§ã¯ãæ¥æ¬¡ã§ããŒã¿ããŒã¹ã®ã³ããŒãäœæããäžã€ã®RDS ã€ã³ã¹ã¿ã³ã¹ å
ã«è¿œå ããŠããŸããã ãã®åœ¢åŒã§ã¯ãå
šãŠã䜿ãæ
£ããããŒã¿ããŒã¹äžã§å®çµã§ãããããæ©èœã®æ§ç¯ã¯çŽ æ©ãè¡ããã®ã§ãããããŒã¿ç·éãæ¥ã
å¢ããåŠçæéãæ¬¡ç¬¬ã«å¢å€§ããŠããŸããããªãããã®åå ã§åŠçã倱æããå Žåã®å詊è¡ã«ãããæéãå«ãããšèš±å®¹ã§ããªãé·ãã«ãªãã€ã€ãã£ãããã以äžã®ããã«çœ®ãæããŠããŸãã Auroraã®ã¯ããŒã³æ©èœ ãå©çšããŠç¹å®æç¹ã§ã®DBã¯ããŒã³ãäœæããäžã§ã AWS Glueãžã§ãã§åããŒãã«ã®å
容ã parquet 圢åŒã§S3äžã«æœåºããããŒã¿ã®å©çšã¯ Athena çµç±ãšããŠããŸãã ä»å玹ä»ããã®ã¯ãå³ã®é»è²ãéšåã§äœ¿çšããGlueãžã§ãã® ã¹ã¯ãªãã ã§ãã Glueãå©çšããŠS3ã«ãã¡ã€ã«ãäœæãã AWS Glue ã¯æœåºã倿ãããŒã (ETL)ãè¡ããããŒãžãåã®ãµãŒãã¹ã§ããä»åã¯ãAuroraã®ããŒãã«ããã®ãŸãŸã®åœ¢ã§ãã¡ã€ã«ãšããŠåºåããã ããªã®ã§ã AWS Consoleã®ãAdd jobããã¿ã³ãããã³ãŒããäžè¡ãæžããã«åŠçãäœæããããšãå¯èœã§ãã ãã ããã®å Žåã ãŸããåºåå
ã®ããŒã¿ããŒã¹ã«å¯Ÿã ã¯ããŒã©ãŒ ãèšå®ãããŒã¿ã«ã¿ãã°ãäœæ ããŒãã«æ°ã ãGlueã®ãžã§ããäœæ ä»åŸãããŒãã«ã®è¿œå ããã£ãå Žåãæåã§åå¥ã«ãžã§ãã远å ãšãã£ãæé ãå¿
èŠãšãªããŸããå
ã«ãªãããŒã¿ããŒã¹ãžã®ããŒãã«è¿œå ã¯ãããªãã®é »åºŠã§çºçããããããã®ãã³ã«ãžã§ããäœæããã®ã¯å¬ãããããŸãããããã§ãçæããã ã¹ã¯ãªãã ãããŒã¹ã«ã mysql ã®information_schemaãåç
§ããŠãããŒã¿ããŒã¹å
ã®å
šãŠã®ããŒãã«ã調ã¹ãŠæœåºãããã倿ŽããŸããã import sys import datetime from awsglue.transforms import * from awsglue.utils import getResolvedOptions from pyspark.context import SparkContext from awsglue.context import GlueContext from awsglue.job import Job # ãžã§ãã®ãã©ã¡ãŒã¿ãŒãååŸ arg_keys = [ 'JOB_NAME' , 'jdbc_url' , 'db_user' , 'db_password' , 'bucket_root' , 'db_schema' ] args = getResolvedOptions(sys.argv, arg_keys) (job_name, jdbc_url, db_user, db_password, bucket_root, db_schema) = [args[k] for k in arg_keys] # Athenaã§å©çšããéãããŒãã£ã·ã§ã³åå²ããããããå¹Žææ¥ãååŸããŠãã now = datetime.datetime.now() date = [now.year, now.month, now.day] sc = SparkContext() glueContext = GlueContext(sc) spark = glueContext.spark_session # å
šããŒãã«ã察象ãšããããããŸããinformation_schemaã®ããŒãã«æ
å ±ãååŸ df = glueContext.create_dynamic_frame_from_options( 'mysql' , connection_options={ "url" : jdbc_url + "information_schema" , "user" : db_user, "password" : db_password, "dbtable" : "tables" } ) # æœåºããå¿
èŠããªãããŒãã«ã¯filterã§åŒŸããŠãããar_internal_metadataã¯RailsãèªåçæããããŒãã« target_tables = df.toDF().filter( "TABLE_SCHEMA = '{0}' AND TABLE_NAME &lt;&gt; 'ar_internal_metadata'" .format(db_schema)).collect() # 1ããŒãã«ãã€æœåº for row in target_tables: table_name = row[ 'TABLE_NAME' ] ds = glueContext.create_dynamic_frame_from_options( 'mysql' , connection_options={ "url" : jdbc_url + "dbname" , "user" : db_user, "password" : db_password, "dbtable" : table_name }) # S3äžã«dynamic frameãæžãåºãããã¹ã«ã¯ãããŒãã£ã·ã§ã³ãä»å ããŠãã glueContext.write_dynamic_frame.from_options( frame=ds, connection_type= "s3" , connection_options={ "path" : bucket_root + table_name + "/YEAR={0[0]:0&gt;4}/MONTH={0[1]:0&gt;2}/DAY={0[2]:0&gt;2}" .format(date)}, format = "parquet" ) å®è¡æ°ã調æŽãã Glueã®ãžã§ãã«ã¯ãMaximum capacityãšããé
ç®ããããã¹ã±ãŒã«ã¢ãŠãããããã«äœ¿çšã§ããããŒã¿åŠçåäœ(DPU)ãæå®ã§ããŸããããã¯ããã©ã«ãã§ã¯10ã«èšå®ãããŠããã®ã§ããã DPU ã®å®¹éèšç»ã®ã¢ãã¿ãªã³ã° ãåèã«ã¡ããªã¯ã¹ã確èªãããšãããå
è¿°ã® ã¹ã¯ãªãã ã®æžãæ¹ã§ã¯DPUã䜿ãåããŠããªãã£ãã®ã§ãé©åœãšæãããæ°åã«æžãããŠèšå®ãçŽããŸããã 1 ã€ã® DPU ã¯ã¢ããªã±ãŒã·ã§ã³ãã¹ã¿ãŒçšã«äºçŽãããŠããŸãã9 åã® DPU ã¯ãããã 2 ã€ã®ãšã°ãŒãã¥ã¿ãŒãå®è¡ãã1 ã€ã®ãšã°ãŒãã¥ã¿ãŒã¯ Spark ãã©ã€ããŒçšã«äºçŽãããŠããŸãããã®ãããå²ãåœãŠããããšã°ãŒãã¥ã¿ãŒã®æå€§æ°ã¯ã2*9 - 1 = 17 ã§ãã DPU ã®å®¹éèšç»ã®ã¢ãã¿ãªã³ã° ã«ä»¥äžã®ããã«æžãããŠããã®ã§ããšã°ãŒãã¥ã¿ãŒã®æ°ã¯ãDPU x 2 - 3ã«ãªããšæãããŸããã€ãŸããDPU2ã ãšå©çšã§ãããšã°ãŒãã¥ãŒã¿æ°ã¯1ã§ãããDPU3ã ãš3ã§ãäžåã«ãªããŸããããããšã°ãŒãã¥ãŒã¿ãŒæé3ã§çµããåŠçããããšãããšãDPU2ã§ã¯3/1ã§3æéãDPU3ãªã3/3ã§1æéã§çµãããŸããGlueã®èª²éã¯DPUæéããããªã®ã§ãDPU3(3 x 1æé = 3)ã§ããã°ãDPU2(2 x 3æé = 6)ã®åé¡ã§æžãèšç®ã§ããMaximum capacityã¯ãå¿
èŠãšããããšã°ãŒãã¥ã¿ãŒã®æ°ãäžåããªãç¯å²ã§å€§ããªå€ãèšå®ãããšãåŠçãæ©ãããã€ãè²»çšãæããããããšã«ãªããšæãããŸãã ãŸãšã Glueãå©çšããŠããŒã¿ãæœåºããéšåã玹ä»ãããŠããã ããŸããããçæããããã¡ã€ã«å©çšããŠãAthenaçµç±ã§éèšãçµæãåæçšããŒã¿ããŒã¹ã«ããŒããããªã©ãåŸç¶ã®åŠç㯠AWS Lambdaã§ Ruby ã¹ã¯ãªãã ã䜿ã£ãŠå®è£
ããŠããŸãããã¡ãã«ã€ããŠã¯ã @uuushiro ã åå€å±RubyäŒè°04 ã§çºè¡šããŸãã®ã§ããããããã°ãè¶ããã ããã ãŸãã AWS Summit Tokyoå
ã§è¡ãããã Startup Architecture of the Year 2019 ã«ãåºå ŽãããŠããã ããŸãã®ã§ããããããé¡ãããŸãã
æ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§Webããã³ããšã³ãã»ãµãŒããŒãµã€ãã®éçºãããŠããŸããæ²³äºãšç³ããŸãã æ¬èšäºã§ã¯ react-chartjs-2 ã®äœ¿ãæ¹ã«ã€ããŠã®è§£èª¬ãããŸããReact ã§ã°ã©ããæžãããšã«ãªã£ããã©å§ãæ¹ãåãããªããªã©ãšãã£ããšãã«åèã«ããŠããã ããã°å¹žãã§ãã TL;DR (æŠèŠ) ã°ã©ãæç»ã®ããã®ã©ã€ãã©ãªã§ãã Chart.js ã React ã® ã³ã³ããŒãã³ã ãšããŠå©çšã§ããããã«ãªãã©ã€ãã©ãªãreact-chartjs-2 ã®è§£èª¬èšäºã§ããã°ã©ãã«è¡šç€ºããããŒã¿ã ã€ã³ã¿ã©ã¯ãã£ã ã«åãæ¿ããããå³ã綺éºã«èŠããããã« Plugin ãçšããå®è£
ã解説ããŸãã ã°ã©ãã®æç» åæãšããŠã玹ä»ããã©ã€ãã©ãªã¯æ¢ã«ã€ã³ã¹ããŒã«ãããŠãããã®ãšããŸãã ãŸããç°¡åã®ãã ã³ã³ããŒãã³ã ã¯åå²ããã«1ã€ã® ã³ã³ããŒãã³ã å
ã§å®çµããããã«ããŠããŸãã ãŸãã¯åºæ¬åœ¢ã§ããLine ã Bar ãšãã£ã ã³ã³ããŒãã³ã ã«å¯Ÿããdata ãš options(å¿
èŠã§ããã°) ãæž¡ããŸãã import React, { Component } from 'react' ; import { Line } from 'react-chartjs-2' ; class SampleChart extends Component { render { const data = { labels: [ 'April' , 'May' , 'June' , 'July' , 'August' , 'September' ] , datasets: [ { data: [ 67, 79, 52, 41, 66, 43 ] , // çç¥ } , ] , } ; const options = { // çç¥ } ; return ( <Line data= { data } options= { options } /> ); } } 以äžã®ãããªã°ã©ãã衚瀺ãããŸãã 䜿çšã§ãã ã³ã³ããŒãã³ã ã®çš®é¡ã¯ react-chartjs-2 ã®ã¬ããžã㪠ãã data ãš options ã®åœ¢åŒã«ã€ããŠã¯ã æ¬å®¶ chart.js ã®ããã¥ã¡ã³ã ãåç
§ããŠãã ããã ããŒã¿ã®ååŸãšã°ã©ãã®æŽæ° data ã labels ãåçã«ååŸãããå Žé¢ã®ã»ããå€ãããšæãã®ã§ããããã state ãšããŠç®¡çããŸãã å®éã®å©çšã·ãŒã³ãšããŠããšããã°ã©ããæ¥ä»ãèµ·ç¹ãšããŠããŒã¿ãååŸããŠãããšãããã®èµ·ç¹ãšãªãæ¥ä»ãæå®ããŠã°ã©ããæŽæ°ãããããšãã£ãç¶æ³ãæ³å®ããŠã¿ãŸãã ãŸããããŒãžã®è¡šç€ºæã«ã¯ componentDidMount() å
ã§ããŒã¿ãååŸããŸãã äœããã®æ¹æ³ã§æ¥ä»ãæŽæ°ãããšããŠããã®æ¥ä»ã state ã§ããã° ã³ã³ããŒãã³ã ãåæç»ãããŸãããã®çŽåŸã«åŒã³åºãããã©ã€ããµã€ã¯ã«ã¡ãœãããšã㊠componentDidUpdate() ããããããã§ãã®æ¥ä»ã«å¿ããŠããŒã¿ãåååŸããŸãã 以äžããã®å®è£
äŸã§ããDatePicker ã¯ãã«ã¬ã³ããŒããæ¥ä»ãéžæãããã㪠ã³ã³ããŒãã³ã ãæ³å®ããŠããŸãã import React, { Component, Fragment } from 'react' ; import moment from 'moment' ; class SampleChart extends Component { constructor(props) { super (props) this .state = { date: moment() } ; } componentDidMount() { this .getData(); } componentDidUpdate(prevProps, prevState) { if (prevState.date !== this .state.date) { this .getData(); } } getData() { const { data, labels } = fetchData( this .state.date); //æ¥ä»ã«åºã¥ããäœããã®ããŒã¿ååŸåŠç this .setState( { data: data, labels: labels } ); } render { // çç¥ åçš®å€æ°ã®å®çŸ©ç return ( <Fragment> <DatePicker date= { date } onDateChange= { (newDate) => { return this .setState( { date: newDate } ); }} /> //äœããã®æ¥ä»å€æŽåŠç <Line data= { chartData } options= { chartOptions } /> </Fragment> ); } } if (prevState.date !== this.state.date) { ... } ã®éšåã§æŽæ°åã®æ¥ä»ãšæŽæ°åŸã®æ¥ä»ãæ¯èŒããŠããŠãç°ãªã£ãŠããã°ããŒã¿ãåååŸãããŸãã ã¯ãªãã¯ã€ãã³ãã®è¿œå ã°ã©ãèŠçŽ ãã¯ãªãã¯ããŠãã¯ãªãã¯ãããã©ãã«ïŒæ¥ä»ãªã©ïŒã«å¿ããŠåŠçãããããšãã®ããã«ãã¯ãªãã¯ã€ãã³ãã远å ããããšãã§ããŸãã 以äžã¯ãã¯ãªãã¯ããã°ã©ãèŠçŽ ã«å¯Ÿå¿ããã©ãã«ã®ã€ã³ããã¯ã¹ãã³ã³ãœãŒã«ã«åºåãããµã³ãã«ã§ããã€ã³ããã¯ã¹ã¯ elements[0]._index ã§ååŸããããšãã§ããŸãã import React, { Component } from 'react' ; import { Bar } from 'react-chartjs-2' ; class SampleChart extends Component { handleClick(elements) { if (elements.length === 0) return ; console.log(elements [ 0 ] ._index); } render { return ( <Bar data= { data } options= { options } onElementsClick= { (elements) => this .handleClick(elements) } /> ); } } æ¡åŒµæ©èœ ã®å©çš Chart.js ã¯ããã ãã§ããªããªã 衚çŸã®èªç± 床ãé«ããã®ã§ãããããã«ãã®å¹
ãåºããããã® æ¡åŒµæ©èœ ïŒextensionsïŒãçšæãããŠããŸãã extensions ãçšãããšè¿œå ã®ã°ã©ããã¿ãŒã³ã䜿ããããã«ãªã£ãããããåã£ã衚瀺ãªãã·ã§ã³ãèšå®ã§ããããã«ãªããŸããä»åã¯ãã®äžã® chartjs-plugin-datalabels ã玹ä»ããŸãã chartjs-plugin-datalabels ã¯ãã®åã®éãã©ãã«ã«é¢ããæ¡åŒµã§ãã©ã®ãããªçš®é¡ã®ã°ã©ãã«å¯ŸããŠãèªç±ãªäœçœ®ã«ã©ãã«ã衚瀺ã§ããããã«ãªããŸãã æ°å€å·®ã倧ãããªããšå°ããæ¹ã®æ°å€ãææ¡ãã«ãããªãã®ã§ããããšèŠãŠãããããã«ãããã§ãããã® ãã©ã°ã€ã³ ã䜿ã£ãŠæ£ã°ã©ãã®çµç«¯ã«æ°åã衚瀺ããŠã¿ãããšæããŸãã ã©ãã«ã®è¡šç€ºã«é¢ãããã©ã¡ãŒã¿ã¯ãããããããŸãããä»åã®å®è£
ã®ããã«ã¯ anchor ã align ãé¢ä¿ããŠããŸãã å
·äœçã«ã¯ãanchor ã§ã°ã©ãã®å
é ãäžå¿ãçµç«¯ã®ã©ãã«ã©ãã«ã眮ããã®åºæºç¹ãæå®ããalign ã§ anchor ã§æå®ããåºæºç¹ããã©ã®æ¹åã«ã©ãã«ãèšçœ®ããããæå®ããŸãã ããããçµã¿åãããŠã anchor: 'end', align: 'right' ãšããããšã§ãçµç«¯ã®å³åŽã«è¡šç€ºããŸãã å®è£
ã¯ä»¥äžã®ããã«ãªããŸãã import 'chartjs-plugin-datalabels' ; const chartOptions = { plugins: { datalabels: { display: true , anchor: 'end' , align: 'right' , formatter(value) { if (value === null || value === 0) { return '' ; } return `$ { value } %` } , } , layout: { padding: { right: 50, } , } , // çç¥ } ; ã©ãã«ã衚瀺ããäœçœããå¿
èŠãã»ããã£ãã®ã§å³åŽã« padding ãå
¥ããŠããŸãã ãŸãã衚瀺ããå€ã«åäœãä»ããã3æ¡ããšã«ã«ã³ãã§åºåããªã©ã®è¡šç€ºåœ¢åŒã¯ã formatter ã®äžã§èšè¿°ããããšãã§ããŸãã ä»åã¯æšªåãæ£ã°ã©ãã䜿ãããã®ã§ã HorizontalBar ã³ã³ããŒãã³ã ã䜿ããŸãã render ( <HorizontalBar data= { chartData } options= { chartOptions } /> ) ãããå®éã«è¡šç€ºãããšæ¬¡ã®ããã«ãªããŸãã ãŸãšã æ¬èšäºã§ã¯ãreact-chartjs-2 ã®äœ¿ãæ¹ããåºç€ããå¿çšãŸã§ç޹ä»ããŸãããåºæ¬çãªæ©èœãããã©ã«ãã§æã£ãŠããªããããéåžžã«æè»ãªã©ã€ãã©ãªã§ãããã®ã§ãã°ã©ããå®è£
ããéã¯ãã²ã«ã¹ã¿ãã€ãºããŠäœ¿ã£ãŠã¿ãŠãã ããã ãŸããæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã§ã¯äžç·ã«åã仲éãåéããŠããŸãããæ°è»œã«ã話ãèãã«æ¥ãŠããã ããã°ãšæã£ãŠããŸãïŒ åå€å±ããäžçãžïŒHRtechãµãŒãã¹ã®èªç€Ÿéçºãšã³ãžãã¢ãšè©±ããŸãããïŒ åè Chart.js react-chartjs-2 Github Chart.js popular extensions
ããã«ã¡ã¯ïŒã¹ã¿ã¡ã³ã§ TUNAG ã® iOS / Android ã¢ããªéçº ãæ
åœããŠãã @temoki ã§ãã CTOã®å°æã ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ãäœã£ãŠãããTUNAGãã®æè¡çãªè§£èª¬ ã§ TUNAG å
šäœã®ã㯠ãã ãžãŒ ã¹ã¿ãã¯ã«ã€ããŠã話ããŠããŸãããä»å㯠iOS ã¢ããªã«ãã©ãŒã«ã¹ãåœãŠãŠãäŒãããããšæããŸãã èšèª TUNAG ã® iOS ã¢ããªã¯ãã¹ãŠ Swift ã§æžãããŠããŸããSwift ã®ããŒãžã§ã³ã¢ããã«ãããã«å¯Ÿå¿ããŠãããããçŸæç¹ã§ææ°ã® Swift 5 ã䜿çšããŠããŸããSwift 㯠Optional åã«ãã Null Safety ãå®çŸããŠãããTUNAG iOS ã¢ããªã§ã¯ä»ã®ãšãã Null Pointer Exception ã«ããã¯ã©ãã·ã¥ã¬ããŒãã¯å±ããããšããããŸããïŒçŽ æŽãããïŒã Swift ã¯åæã®ããŒãžã§ã³ïŒãïŒããããŸã§ã¯ãã°ãããŒãžã§ã³ã¢ããããšã«ç Žå£çã«ä»æ§ãå€ãã£ãŠããã®ã«èŠåŽããŸããããããŒãžã§ã³ïŒä»¥éã¯ä»æ§ãå®å®ããå®å¿ããŠå©çšã§ããããã«ãªããŸãããã ãµãŒãã¹ TUNAG 㯠AWS + Ruby on Rails ã§æ§ç¯ãããŠããŸãããmBaas (mobile backend as a service) ã® Firebase ãå©çšããŠããŸããç¹ã«ä»å¹Žã®ïŒæã«æ£åŒãªãªãŒã¹ãããã°ããã® Cloud Firestore ã®ãããã§ãéåžžã«å³ææ§ã®é«ããã£ããæ©èœãå®çŸããããšãã§ããŸããããŸããCloud Firestore ã¯ãªãã©ã€ã³å©çšã®ããã«ååŸæžã¿ã®ããŒã¿ãèªåçã«ããŒã«ã«ã¹ãã¬ãŒãžã«ãã£ãã·ã¥ããŠãããŸãããããŒã¿ãã©ãããæèããããšãªãã¢ã¯ã»ã¹ã§ããç¹ããšãŠã䟿å©ã§ãã Cloud Firestore ã ãã§ãªããTUNAG ãšé£æºãããŠãŒã¶ãŒèªèšŒã®ããã« Authentication ãã¯ã©ãã·ã¥ã¬ããŒãã£ã³ã°ã®ããã« Crashlyitics ãªã©ãFirebase ãåºã掻çšããŠããŸãã ãŸããã¢ããªãžã®ããã·ã¥éç¥é
ä¿¡ã«ã¯ NIFTY CLOUD mobile backend ãçŸåšå©çšããŠããŸãããããã Firebase ã® Cloud Messaging ãžã®ç§»è¡ãæ€èšäžã§ãã (2020/2/5 远èš) ã¢ããªãžã®ããã·ã¥éç¥é
ä¿¡ã¯ã2019幎9æã« Firebase ã® Cloud Messaging ã«ç§»è¡ããé
ä¿¡éåºŠãæ Œæ®µã«åäžããŸããã ã©ã€ãã©ãª iOS ã¢ããªã§ã¯ãHTTPãããã¯ãŒãã³ã°ã¯ Almofire ãäžå¿ãšããç»åã®èªã¿èŸŒã¿ã« AlamofireImage ãWeb API ã¯ã©ã€ã¢ã³ãã®å®è£
ã®ããã« Almofire ã®ã©ãããŒã§ãã Moya ã䜿çšããŠããŸããã¢ããªããã¯æ§ã
㪠Web API ã䜿çšããŸãããMoya ã¯ãã® API 仿§ã«æ²¿ã£ãŠå®£èšçã«å®è£
ããŠããããšããããæ°ã«å
¥ãã§ãã ãŸããå
æ¥ TUNAG iOSã¢ããªã®ãã£ããæ©èœãVIPERã¢ãŒããã¯ãã£ã§éçºãã話 ã«ãæžããŸããããããŒã¿ã¬ã€ã€ãŒã§ Cloud Firestore ããåãåããªã¢ã«ã¿ã€ã ã¢ããããŒãã«å¯ŸããŠãªã¢ã¯ãã£ãã«åŠçããããã« Reactive X ã® Swift å®è£
ã§ãã RxSwift ã䜿çšããŠããŸãã ãã®ä»ã«ã¯ Swiftã³ãŒã ã®éçè§£æã® SwiftLint ãç»åãStoryboardã®ãããªãªãœãŒã¹ãã¿ã€ãã»ãŒãã«æ±ãããã« SwiftGen ãªã©ã®éçºæ¯æŽã®ããã®ããŒã«ã掻çšããã³ãŒãå質ã確ä¿ããŠããŸãã ãããã®ã©ã€ãã©ãªã¯åºæ¬çã« Carthage ã§ã€ã³ã¹ããŒã«ããããšã§éçºäžã®ãã«ãæéã®ççž®ããŠããŸãããCarthage ã«é察å¿ã®ã©ã€ãã©ãªã®ã¿ Cocoapods ãå©çšããŠããŸãã ããŒã¿ãã¹ã TUNAG ã¯ã¹ã¿ã¡ã³ã®ã¡ã³ããŒèªèº«ããããŒãŠãŒã¶ãŒã§ãããã®ããã¢ããªã®æ°ããŒãžã§ã³ã¯åžžã«ç€Ÿå
ã¡ã³ããŒã§ã®ãã¹ãã宿œããŠããŸãã éçºäžã®ã¹ããŒãžã³ã°ç°å¢ã§åäœããã¢ããªã¯ã Fabric ã® Beta ãšãããµãŒãã¹ã«ããéçºã¡ã³ããŒã® iOS ã ãã€ã¹ ã«ãããã€ããŠããŸãããã® Fabric 㯠Twitter 瀟ãæäŸããŠããã¢ãã€ã«ã¢ããªã®éçºæ¯æŽãµãŒãã¹ã§ããã Google 瀟ã«å£²åŽãããŠçŸåš Firebase ãžã®çµ±åãé²ããããŠããç¶æ
ã§ããCrashlytics ã Fabric ãæäŸãããµãŒãã¹ã®äžã€ã§ããããFirebase ãžã®çµ±åãã»ãŒå®äºããŠããŸããBeta ã Firebase App Distribution ãšããŠçµ±åãããäºå®ã§ãããã®ãã Beta ã«ãã ããŒã¿ãã¹ã äžã«ã¢ããªãã¯ã©ãã·ã¥ããå ŽåããCrashlytics ã«ããã¡ãããšã¬ããŒããããã®ã§å©ãããŸãã (2020/2/5 远èš) Firebase App Distribution ãããŒã¿ãªãªãŒã¹ãããããã2019幎12æã« Fabric Beta ããã®ç§»è¡ããŸããã æ°ããŒãžã§ã³ã®éçºã®çµç€ã§ã¯ãéçºã¡ã³ããŒã ãã§ã¯ãªã瀟å
ã®ããããã®ã¡ã³ããŒã«ãŠæ®æ®µå©çšããŠãã£ãŒãããã¯ããŠãããããã«ããããã¯ã·ã§ã³ç°å¢ã§åäœããã¢ããªã Apple å
¬åŒã® Test Flight ã§ç€Ÿå
ã¡ã³ããŒã«é
ä¿¡ããŠããŸãã ãããŠãã¹ãããªãªãŒã¹ã®ããã®ã¢ããªã®ãã«ãã«ã¯ Fastlane ã䜿çšããŠããŸããFastlane ã«ã¯ Beta ã Testflight ãžã®ã¢ããªã®ã¢ããããŒãããã¯ã©ãã·ã¥è§£æçšã®ã·ã³ãã«ãã¡ã€ã«ã®ã¢ããããŒããªã©ã®æ©èœãæã£ãŠããããããã«ããããããã€ãŸã§ã®äžé£ã®æµããã³ãŒãã§å®çŸ©ããŠèªåå®è¡ã§ããŠããŸããŸãã ãããã« ãããã§ããã§ãããããTUNAG ã® iOS ã¢ããªã®éçºã¯ãã®ãããªã¢ãã³ãªç°å¢ã§è¡ãªã£ãŠããŸããããã¯ïŒå®ã¯ïŒãã®å幎éãããã§æŽåããŠãããã®ã§ãä»åŸã¯ Android ã¢ããªã®æ¹ãåŸ¹åºæ¹åããŠããäºå®ã§ãã ãã®ãããªã¢ãã³ãªç°å¢ã§ã® iOS ã¢ããªéçº ããä»åŸã® Android ã¢ããªéçº ç°å¢ãã©ã®ããã«æ¹åãããŠããã®ãã«èå³ãæãããæ¹ã¯ã ãã¡ã ããæ°è»œã«ãé£çµ¡ãã ããïŒ
æŠèŠ ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã®å°æ( @lifework_tech )ã§ãã ã¹ã¿ã¡ã³ã®åå€å±ãªãã£ã¹ã¯ã æ±æµ·éæ°å¹¹ç· ã®é«æ¶äžã«ããå庫ã®ãããªåºã空éã«ã ãã®ããã倧ããªæº ãã¿ããªã§ã·ã§ã¢ããŠäœ¿ã£ãŠããŸãã æºãåºããŠã暪å¹
ãšå¥¥è¡ããããããããšã³ãžãã¢ããã¶ã€ããŒãåšç±ãããããã¯ãéšã®ã¿ããªã¯ããããã®å¥œã¿ã®ç°å¢ã«ããŠå¿«é©ã«ä»äºãããŠããŸãã ä»åãäœäººãã«ããã ããã®ç°å¢ã«ã€ããŠã€ã³ã¿ãã¥ãŒããŸããã®ã§ãã玹ä»ãããŠãã ããã æšæºçãªæº ãŸãã¯ãã¹ã¿ã¡ã³æšæºç°å¢ã ã¹ã¿ã¡ã³ã®ãšã³ãžã㢠or ãã¶ã€ããŒã¯ã MacBook Pro 15ã€ã³ã + LG 27ã€ã³ã 4Kãã£ã¹ãã¬ã€ + Apple Magic Keybord ãããã¯ãMagic Trackpad ãšããç°å¢ãæšæºã§ããããŒããŒããããŠã¹ã¯æã¡èŸŒãã§ãè¯ããããšã§ç޹ä»ããŠããããã«èªåã§ã«ã¹ã¿ãã€ãºããŠãã人ãããŸãã ããŒããŒãåž«å (ãã€ãã£ãããŒã ã®ãšã³ãžãã¢) ãã€ãã£ããšã³ãžãã¢ã®Kããã®ãã¹ã¯ã§ãã Kããã«ãã£ãŠããã¥ã¢ã«ããŒããŒãã®è¯ããåžæããããããåæã«ããŒããŒãåž«å ãšæã£ãŠããŸãã ã¿ã€ãã³ã°ã«ããè©ç²éªšã®çã¿ãè§£æ¶ããããã«ã æ¥æ¬èªé
å ã®è²Žéãªåå²ããŒããŒã MiSTEL BAROCCO MD600 ã䜿çšããŠããŸããæ¥æ¬èªå°åããªãããªã¬ã³ãžã®ã¢ã¯ã»ã³ãã«ã©ãŒããæŽèœã§ãã ãã®ããŒããŒãã¯ãããã Mac çšã«æé©åãããŠããããã§ã¯ãªãããã䜿çšããã«ãŒãœã«ããŒãè±æ°ã»ããªããŒããªããããããŒããŒãæ¬äœã®ã«ã¹ã¿ãã€ãºæ©èœãš Karabiner Elements ãé§äœ¿ã㊠Mac ã® æ¥æ¬èªé
å ã«æ¥µåè¿ã¥ããŠããŸãã ããŒã ã¬ã¹ã ã¯é«ãããŽã£ãããªã®ã§ FILCO ã®åå²ããŒããŒãçšã®ãã® ããã§ã€ã¹ã æ®æ®µã¯ Macbook ã£ãœãçãäžã«é
眮ãã Magic Trackpad ã§æäœãè³æäœæãªã©å¿
èŠã«å¿ããŠå³åŽã® Magic Mouse ã䜿ããŸãã CTOã®å°æ ãã©ããã ã®ãããªäŸ¿å©ãªéå
·ãã ãããšã³ãžãã¢ã§ãããããšæã£ãŠããŠãæºã«ã¯ãã€ã ãã©ããã ãããŸãã ãã® ãã©ããã ã¯ãå°è¥å( ããã ) ãªãã§ããç€Ÿå¡æ
è¡ã§è¡ã£ã éŠ¬ç± å®¿ ã®æ°èžåºã§è³Œå
¥ããŸããã ããŠã¹ãçãäžã«çœ®ããšãæã䌞ã°ããªããŠãæäœã§ããã®ã§ãå³è
ã®ç²ãå
·åãæžããã§ãã è
ãåã«äŒžã°ããšãè
ãæ¯ããããã«çåã䜿ãã®ã§ç²ãããããäœã®è¿ãã«ããŠã¹ã眮ããšç²ãã«ãããšããžã ã®ãã¬ãŒããŒã«ã¢ã ãã€ã¹ é ããŠããããããŠããŸãã è©ãéããæ¹ãç²ãã«ãããšã®ããŒããŒãåž«å ã®èŠãã§ãMagic Keybord ã2å°ã Karabiner-Elements ã§é£åãããŠããŸãã ããŠã¹ãããã¯ãsteeleseries ã® ã²ãŒãã³ã°ããŠã¹ãããã§ãããªã衚é¢ãåºããŠããŠã¹ãããæ»ããããé·å¹Žäœ¿ã£ãŠããŸãã æã¡åããçã§åžãé¢ããããšãå€ããã±ãŒãã«ã®æãå·®ããæ¥œã«ãªãããã«ã Belkin Thunderbolt 3 Express Dock ã䜿ã£ãŠããŸãã ãã¶ã€ã㌠Mãã ããŠã¹ãããããåããããããåºãããŠã¹ãããããããŠããŸãã æã
iPad Proããã©ããæžãããã®ããŒããå°å·ç©ã眮ãã®ã§ ãªãã¹ãã¹ãããªãããŠãããŸãã SREããŒã ã®ãšã³ãžã㢠Tãã Ubuntu ã䜿çšããŠããããšããã£ãŠãHHKB JPé
åã«èŠªæ ãã©ãã¯ããŒã« ãšããæšæºçãªæ§æã§ã æåã® ãªã¹ãã¬ã¹ã ã¯èãçããªããªãããã«ãããŠããŸã ãã©ãã¯ããŒã« ã Logicool M570 ãã ELECOM EX-G ã«æ¿ãã以å€ã¯ã7幎ã»ã©ãã®æ§æã§ã ããŒã«ã¯M570ã®æ¹ã奜ã¿ãªã®ã§ãããŒã«ã®ã¿M570ã®ã«å
¥ãæ¿ããŠäœ¿ã£ãŠããŸã äœã£ãããŒã«ã¯åš(3æ³)ã®ããã¡ããšããŠæŽ»èºããŠããŸã ãŸãšã ãšã³ãžãã¢ã«ãšã£ãŠãéçºç°å¢ã®å¿«é©ãã¯éåžžã«å€§åã§ãç¹ã«äžçªè§Šããããšã®å€ãããŒããŒãããã®é
眮ã¯çãããã ããããããŸãããã ã玹ä»ãã以å€ã«ãã ThinkPad ãã©ãã¯ãã€ã³ãã»ããŒããŒã ã䜿ã£ãŠãã人ãããããããããå¿«é©ãªç°å¢ãæŽããŠããŸãã ã¹ã¿ã¡ã³åå€å±ãªãã£ã¹ã¯ããŸãã« ãã³ãã£ãŒ ãšããæãã® æ°å¹¹ç·ã®é«æ¶äžã«ããã¬ã¬ãŒãžã®ãããªãªãã£ã¹ ã§ãåèªã®æºä»¥å€ã«ãã ãã¢ãã ãå匷äŒã«ã䜿ãããªããŒããã€ãã³ã°ããŒãã«(äžå³)ããã£ãããšãåãããã工倫ãè©°ãŸã£ããªãã£ã¹ã§ãã ã©ããªãªãã£ã¹ãèŠãŠã¿ãããªãšæã£ãŠãã ãã£ããããã² @lifework_tech ãžãæ°è»œã«ãé£çµ¡ãã ããããªãã£ã¹ããæ¡å
ããŸãïŒ
ã¯ããã« ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ iOS/Android ã¢ããªã®ãšã³ãžãã¢ãããŠãã @temoki ã§ãã æšå¹Žã®10æã«ã¹ã¿ã¡ã³ã«ãžã§ã€ã³ããŠããã®ç§ã®æåã®ããã·ã§ã³ã¯ã TUNAG iOS ã¢ããªã®ãã£ããæ©èœã®éçºãããžã§ã¯ãã§ãããæ¬èšäºã§ã¯ãã®ãã£ããæ©èœéçºãããžã§ã¯ãã«ãããŠæ¡çšãã VIPER ãšããã¢ãŒããã¯ãã£ã«ã€ããŠç޹ä»ãããã£ããæ©èœã®åçãªãªãŒã¹ããããã€ãã®æ©èœæ¹åãçµãçŸåšã«ãããææããäŒãããããšæããŸãã VIPERã®æŠèŠ VIPERãšã¯ïŒ ç§ã VIPER ãç¥ã£ãã®ã¯ objc.io ã® Architecting iOS Apps with VIPER ãšããèšäºã§ãããããèªãã° VIPER ã«ã€ããŠäžéãçè§£ããããšãã§ããŸããã端çã«èª¬æããããã«æ¬¡ã®æãåŒçšããŸãã VIPER is an application of Clean Architecture to iOS apps. The word VIPER is a backronym for View, Interactor, Presenter, Entity, and Routing. Clean Architecture divides an appâs logical structure into distinct layers of responsibility. This makes it easier to isolate dependencies (e.g. your database) and to test the interactions at the boundaries between layers: ã€ãŸã次ã®ãšããã§ãïŒæ¥æ¬èªã«ç¿»èš³ããã ãã§ããïŒã VIPER 㯠iOS ã¢ããªã±ãŒã·ã§ã³ã« Clean Architecture ãé©çšãããã® VIPERãšããèšèã¯ã V iewã I nteractorã P resenterã E ntitiy ããã³ R outing ã衚ã ãã¯ããã ã¢ããªã±ãŒã·ã§ã³ã®è«çæ§é ã責任ã®ç°ãªãã¬ã€ã€ãŒã«åå²ããããšã§ïŒããŒã¿ããŒã¹çã®ïŒäŸåé¢ä¿ãåé¢ããã¬ã€ã€ãŒéã®å¢çã§çžäºäœçšããã¹ãããããšãç°¡åã«ãªã VIPERã®ã¡ã€ã³ããŒã VIPER ã¯æ¬¡ã®ïŒã€ã®ããŒãã«åé¡ããããããããæç¢ºãªè²¬åãæã¡ãŸãã V iew Presenter ããã®æç€ºã衚瀺ãã ãŠãŒã¶ãŒå
¥åã Presenter ã«äŒãã I nteractor ã¢ããªã±ãŒã·ã§ã³ã®äžã®äžã€ã®ãŠãŒã¹ã±ãŒã¹ïŒç»é¢åäœãªã©ïŒã衚çŸãã ããŒã¿æäœãšããžãã¹ããžãã¯ãå«ã P resenter Interactor ããåããšã£ãããŒã¿ã衚瀺ããããã®æºåãè¡ã ãŠãŒã¶ãŒå
¥åã«åå¿ã㊠Interactor ã«æ°ããããŒã¿ãèŠæ±ãã ãã®ãããªãã¥ãŒããžãã¯ãå«ãããiOS ã® UIKit ã«ã¯äŸåããªã E ntity Interactor ã®ã¿ã«äœ¿çšãããåçŽãªã¢ãã«ãªããžã§ã¯ã R outer ç»é¢ãã©ã®ããã«è¡šç€ºããããã®ç»é¢é·ç§»ããžã㯠ãã®è¡šç€ºããç»é¢ã®çæïŒäŸåæ§æ³šå
¥ïŒ ãã㯠Passive View æ¹åŒã® MVP (Model View Presenter) ã¢ãŒããã¯ãã£ãããŒã¹ã«ãªã£ãŠããã倧ãããªããã¡ãª Presenter ããç»é¢é·ç§»ãšçæã®è²¬åã Router ã«ããããŠç»é¢åºæã®ãŠãŒã¹ã±ãŒã¹ã Interactor ã«åé¢ããŠããã®ãç¹åŸŽçã§ãã ããããã®ããŒãã®ã€ãªããã¯äžå³ã®ããã«ãªããŸãïŒ objc.io ã®èšäºããã®åŒçšïŒã TUNAG ã¢ããªãžã®é©çš ãããã㯠TUNAG iOS ã¢ããªãžã® VIPER é©çšã«ã€ããŠã話ããŠãããŸãã ãªã VIPER ãæ¡çšããã®ãïŒ TUNAG ã®ãã£ããã¯è€æ°ã®ç»é¢ã§æ§æãããŸãããã¡ã€ã³ãšãªããã£ããã«ãŒã ç»é¢ã®ã¹ã¯ãªãŒã³ã·ã§ããããã¡ãã§ãã ã¡ã³ã·ã§ã³ãçµµæåãã¹ã¿ã³ããåçæçš¿ããã¡ã€ã«æ·»ä»ãªã©ããããã®æ©èœããããŸããTUNAG ã®ãã£ããã¯ç°¡æçãªãã®ã§ã¯ãªããã¡ã³ããŒéã®æ¥åžžã³ãã¥ãã±ãŒã·ã§ã³ãããžãã¹çšéãšããŠãå©çšããããã®ã§ãã®ã§ããã®ïŒã€ã®ç»é¢ã ãã§ããšãŠãå€ãã®è¡šç€ºãã¿ãŒã³ãšå
¥åãã¿ãŒã³ããããŸãããã㊠TUNAG ãšããäºæ¥ã¯é·ãç¶ç¶ããŠãããã®ã§ãã®ã§ãä»åŸã®å€æŽãªã©ã®ã¡ã³ããã³ã¹ã®ããšãèãããšæåããé©åã«ã¢ãžã¥ãŒã«ãåå²ããŠããå¿
èŠããããŸãã VIPER ã¯ç¹å®ã®ãã©ãããã©ãŒã ãã©ã€ãã©ãªã«äŸåããŠãããã®ã§ã¯ãªãããªããžã§ã¯ãæåã§ã¢ããªã±ãŒã·ã§ã³ãé©åã«èšèšããŠããããã®ã¬ã€ãã©ã€ã³ã®ãããªãã®ã§ãããã®ãããéçºããŒã å
ã§ã¢ãžã¥ãŒã«åå²ã®æ¹éãå
±éèªèãšããŠéçºãé²ããŠããããšæãããããVIPER ãæ¡çšããããšã«ããŸããã ãŸããéçºããŒã ã«ã¯éçºçµéšã®å°ãªãè¥æã¡ã³ããŒããããVIPER ã§ã®æ§ç¯ãå®è·µããŠããããšã§ã¡ã³ããŒã®èšèšååäžã«ã€ãªããã®ã§ã¯ãªãããšããæåŸ
ããããŸããã TUNAG ã¢ããªã® VIPERã«ããæ§æ æçµçã«ãTUNAG ã¢ããªã®æ§æã¯äžå³ã®ããã«ãªããŸãããåããŒãã§ <~> ãšèšè¿°ããŠãããã®ã¯ããã®ããŒããæºæ ãã Protocol ã§ããããããã®ããŒãã¯ããã® Protocol ã«ããæ¥ç¶ãããããšã«ãªããŸãã View, Presenter, Interactor, Router ã¯åºæ¬çã«ç»é¢åäœã«çšæããŸããç»é¢ãéçºãããšãã¯ããã®ç»é¢ã«ãããåããŒãã®åœ¹å²ã Protocol ã§å®çŸ©ãããããã Contract (å¥çŽ) ãšããŠãŸãšãããã¡ã€ã«ãäœãããã«ããŸãããããããããšã§ãã®ç»é¢ã®æ©èœããŸãšãŸã£ãã«ã¿ãã°ãã§ããŸãã以äžããã¿ã¹ã¯ãªã¹ãç»é¢ãäœããšããæã® Contract ã®äŸãšãªããŸãããããŠãRouter ãããããã®äŸåæ§ã泚å
¥ãããŸãšããŠäžã€ã®ç»é¢ã¢ãžã¥ãŒã«ãšããŠçµã¿ç«ãŠãŸãã /// ã¿ã¹ã¯ãªã¹ãç»é¢ View protocol TaskListView : class { // Dependency var presenter : TaskListPresentation! { get } /// ã¿ã¹ã¯ãªã¹ãã®èªã¿èŸŒã¿äžã®è¡šç€ºããã func showLoadingState () /// ã¿ã¹ã¯ãªã¹ãã衚瀺ãã func showTaskList (_ taskList : [ TaskCellModel ] ) /// ã¿ã¹ã¯ãªã¹ããäžã€ããªãç¶æ
ã衚瀺ãã func showEmptyState () } /// ã¿ã¹ã¯ãªã¹ãç»é¢ Presentation protocol TaskListPresentation : Presentation { // Dependency var view : TaskListView? { get } var interactor : TaskListUseCase! { get } var router : TaskListRouter! { get } /// ã¿ã¹ã¯ã»ã«ãã¿ããããã func taskCellDidTap (taskId : Int ) /// ã¿ã¹ã¯è¿œå ãã¿ã³ãã¿ããããã func addTaskButtonDidTap () } /// ã¿ã¹ã¯ãªã¹ãç»é¢ UseCase protocol TaskListUseCase : class { // Dependency var output : TaskListInteractorOutput? { get } /// ãã¹ãŠã®ã¿ã¹ã¯ãªã¹ããååŸãã func fetchAllTaskList () } /// ã¿ã¹ã¯ãªã¹ãç»é¢ InteractorOutput protocol TaskListInteractorOutput : class { /// ãã¹ãŠã®ã¿ã¹ã¯ãªã¹ããåºåãã func outputAllTaskList (_ taskList : [ TaskModel ] ) } /// ã¿ã¹ã¯ãªã¹ãç»é¢ Wireframe protocol TaskListWireframe { /// ã¿ã¹ã¯ãªã¹ãç»é¢ã®ãã¥ãŒã³ã³ãããŒã©ãŒ var viewController : UIViewController? { get } /// ã¿ã¹ã¯äœæç»é¢ã衚瀺ãã func presentCreateTaskView () } VIPER ã«ããåºæ¬æ§æã«å ããããŒã¿ã¬ã€ã€ãŒã«ã¯ Repository ãã¿ãŒã³ãé©çšããŠããŸããTUNAG ã®ãã£ããæ©èœã«ãããããŒã¿ãœãŒã¹ã¯ã Firebase ã® Cloud FirestoreãTUNAG ã® REST APIãããŒã«ã«ã¹ãã¬ãŒãžãšå€å²ã«ããããããRepository ãã¿ãŒã³ã«ããããããé èœããŠããŸããRepository ã¯ç¹å®ã®ãŠãŒã¹ã±ãŒã¹ã«äŸåãããäŸãã°ãŠãŒã¶ãŒæ
å ±ããã£ããã¡ãã»ãŒãžæ
å ±ã®ãããªã·ã³ãã«ãªããŒã¿ã® CRUD ã®ã¿ã責åãšãããããè€æ°ã®ç»é¢ãã䜿çšãããŸãã ãŸããCloud Firestore ã«ãããŠãŒã¶ãŒæ
å ±ããã£ããã¡ãã»ãŒãžæ
å ±ãªã©ã®ãªã¢ã«ã¿ã€ã ã¢ããããŒããåãåããããããããŒãžã㊠Interactor ã«æµããŠããããã« RxSwift ã䜿çšããŠããŸããMVVM ã¢ãŒããã¯ãã£ãŒãæ¡çšããŠããããã§ã¯ãªãã§ãããOS æšæºã§ã¯ãªãç¹å®ã®ã©ã€ãã©ãªã«ã¢ãã±ãŒã·ã§ã³å
šäœãäŸåããããšãé¿ãããããRxSwift 㯠Interactor ããå
ã®ãã¬ãŒã³ããŒã·ã§ã³å±€ã§ã¯äœ¿çšããªãããã«ããŠããŸãã è¯ãã£ãç¹/æªãã£ãç¹ ãã®ãããªæ§æã§2018幎10æãããã£ããã®éçºãã¯ããã2019幎1æåã«åçããªãªãŒã¹ããŸããããã®åŸçްããã¢ããããŒãããã€ã€ããã®4æã«ã¯ãã£ããã¡ãã»ãŒãžã®æ€çŽ¢ã®ãããªå€§ããªæ©èœè¿œå ã®éçºãè¡ãªã£ãŠããŸããVIPER ãæ¡çšããéçºãå§ããŠå幎çµã£ãçŸæç¹ã«ããããå人çã«è¯ãã£ãã»æªãã£ããšæããŠããç¹ãç®æ¡æžãã§ãŸãšããŸãã è¯ãã£ãç¹ ç»é¢å®è£
æã«åè¿°ã® Contract ãã¡ã€ã«ãçšæããããšã§ãã©ã®ããŒãã¯äœã«é¢äžããŠãäœã«é¢äžããªãã®ãïŒãšåžžã«èããªããå®è£
ããŠããããšã«ãªããèªåãããŒã ã¡ã³ããŒããªããžã§ã¯ãæåã§ã®èšèšåãæ Œæ®µã«äžãã£ããšãã宿ããã£ãïŒããããèããŠã¿ããš SOLIDã®åå ãå¿ å®ã«å®è·µããŠããããšã«ãªã£ãŠããïŒ åããŒãã®è²¬åãæç¢ºã§ãããããã®éã Protocol ã«ããççµåã«ãªã£ãŠããããšã§ããŠããããã¹ãããšãŠãæžãããã ãã£ããã®éçºã¯æäœéã®æ©èœãå®è£
ããåŸã
ã«èä»ãããŠããã¹ã¿ã€ã«ã§è¡ãªã£ãŠãã£ãããè¶³ãç®ãéåžžã«ããããã æ©èœè¿œå åŸã®ã³ãŒãã® Diff ãäœã®æ©èœè¿œå ã倿Žãè¡ãªã£ããããšãŠãããããããã£ããããPR ã®ã¬ãã¥ãŒããããã åçãªãªãŒã¹ã®åã«QAããŒã ã«ãããã¹ãã瀟å
ã§ã®Î²ãã¹ãã宿œããããæ©èœã®ããªã¥ãŒã ã®ããã«ã¯äžå
·åæ€åºæ°ãéåžžã«å°ãªãã£ã æªãã£ãç¹ ïŒã€ã®ç»é¢ãäœãã®ã«å€ãã®ã¯ã©ã¹ã®ãã¡ã€ã«ãå¿
èŠã§ããã®ã¯ãã¯ãé¢åã ã£ãïŒ äŸãã° Swift-VIPER-Module ã®ãããªèªåçæããŒã«ã詊ãã¹ãã ã£ãïŒ ãã㯠VIPER ã®åé¡ã§ã¯ãªãç§ã®åé¡ã ããåçãªãªãŒã¹æã«éšåçã«ãããŠããããã¹ããæžãããšãã§ããªãã£ãïŒãã ãããã®åŸåŸã
ã«ãã¹ãã远å ããŠãã£ãŠããããå®è£
ã³ãŒãã倿Žãããšããã¹ããæžããŠãããŠããã®ã¯ VIPER ã®ã¡ãªããïŒ ã³ãŒãéã§èŠãææ ãã£ããæ©èœã®åçãªãªãŒã¹ã®çŽåã«ãVIPER é©çšã®æ¯ãè¿ãã®æå³ããããŠãéçºçæããåçãªãªãŒã¹ã®éã«æžãã Swift ã³ãŒãéãéèšããŠã¿ããŸãããçµæã¯æ¬¡ã®ãšããã§ãã æ°èŠãã¡ã€ã«æ°: çŽ200 ïŒâã¯ã©ã¹æ°ïŒ 远å /倿ŽLOC: çŽ10,000 åçŽã«èšç®ãããšãïŒãã¡ã€ã«ããã 50 LOC ãããã«ãªã£ãŠããŸããVIPER ã§ä»ã®ããŒãã® Mediator ãšããŠåã Presenter ã¯æ¯èŒçã«ã³ãŒãéãå€ããªããã¡ãªã®ã§ããããã£ããæ©èœã§ãã£ãšãè€éãªãã£ããã«ãŒã ç»é¢ïŒããªã VIPER ãæ¡çšããã®ãïŒãã§ãèŠãããã¹ã¯ãªãŒã³ã·ã§ããã®ç»é¢ïŒã® Presenter ã«ãããŠã 500 LOC çšåºŠã§åããããšãã§ããŠããŸããã ããã VIPER ãæ¡çšããããšã§èšèšãããŸããã£ãææã ãšæã£ãŠããŸãã ãããã« ã¹ã¿ã¡ã³ã§ã®ç§ã®æåã®ããã·ã§ã³ã¯ãŸããã®ãã£ããæ©èœã§ããããšãŠã倧ããªæ©èœãªã®ã§ãããããããšããäžå®ããªãããããæåã«ãã®ãã㪠VIPER ã«ããèšèšæ¹éãåºããããã«ãããã£ãŠïŒç»é¢äœãçµããé ã« VIPER ã®è¯ãã宿ãããã以éã¯ç¹ã«äžå®ãªãæ·¡ã
ãšæ©èœéçºãé²ããŠããããšãã§ããŸããããã®éçºæéã¯ç§ã®ãšã³ãžãã¢äººçã®äžã§ãç¹ã«æ¿å¯ãªãã®ã«ãªããŸããã®ã§ããã®æ¯ãè¿ããããªãããã®èšäºãæžããŠããŸãã TUNAG iOS ã¢ããªã®ãã£ããéçºã¯äžæŠèœã¡çããŸããããTUNAG ã® iOS/Android ã¢ããªãšããŠããã¹ãããšã¯ãŸã ãŸã å€ããåŒãç¶ãããè¯ãã¢ããªã«ããŠããããã«äžç·ã«ãªã£ãŠéçºããŠããããšã³ãžãã¢ãå¿
èŠã§ããããèå³ããããŸããã ãã¡ã ãããé£çµ¡ãã ãããäžç·ã«æ¿å¯ãªãšã³ãžãã¢äººçãéããŸãããïŒ
ããã«ã¡ã¯ïŒãã¹ã¿ã¡ã³ã§ iOS / Android ã¢ããªãšã³ãžãã¢ãšã㊠ã€ã³ã¿ãŒã³ ãããŠããã«ãŒã @khaki_lit ã§ãïŒ ã¹ã¿ã¡ã³éçºããã°ã«ç»å Žããã®ã¯åããŠã«ãªããŸãïŒçŸåšã¯äž»ã«TUNAGã® Android çãéçºããŠããŸããïŒä»¥å㯠iOS çã®TUNAGãã£ããã®éçºã«ãå°ãé¢ãã£ãŠããŸããïŒ try! Swift ãšã¯TUNAGã® iOS ã¢ããªã§ã䜿çšãããŠããSwiftãšããèšèªã®åœéçãªã«ã³ãã¡ã¬ã³ã¹ãšãªããŸãïŒç§ã¯2æ¥ç®(26æ¥)ã®æ¹ã«åå ããŠããŸããïŒåæ¥ã®ã¬ããŒã㯠@temoki ã ãã¡ã ã®èšäºã§æžããŠããŸãïŒ è¿ããŠé ãäžç èªåã¯try! Swiftã®ãããªåœéçãªã«ã³ãã¡ã¬ã³ã¹ã«åå ããã®ã¯åããŠã ã£ãã®ã§ããïŒä»åæããã®ã äžçã®è¿ããšé ã ã§ãïŒ äžçã®è¿ã èªåãã¡ãšåãããã«Swiftãšããèšèªã䜿ã£ãŠ iOS ã¢ããªãéçºããŠãããšã³ãžãã¢ãäžçäžã«ãããããããã ãšèªèãïŒäžçã®è¿ããæããŸããïŒç§ã¯æ®æ®µ OSS ãªã©ã«åå ããŠããªãããïŒã¢ããªãéçºããŠããŠäžçãæããããšã¯ãããŸããã§ãããïŒSwiftã®ã³ãã¥ããã£ã¯äžçäžã«åºãã£ãŠããŠïŒèªåããã®äžå¡ãªãã ãšä»ååå ããŠå®æããŸããïŒèšèãåœç±ãéã人ã
ãšãSwiftããšããå
±éèšèªãéããŠè©±ããããšã«æåããŸããïŒ äžçã®é ã ãã®äžæ¹ã§äžçãžã®é ããåæã«çæããŸããïŒ äžçã§æŽ»èºãããšã³ãžãã¢ã®çºè¡šã¯æ¬åœã«ã©ããããããŠïŒä»ã®èªåã«ã¯çè§£ã§ããªããããªã¬ãã«ã®è©±ãããïŒèªåã®æªçããçªãã€ããããŸããïŒ å人çã«è¡æãåããã¹ããŒã«ãŒã18æ³ã§çºè¡šæé«æ ¡ã忥ããã°ãããšãã @kateinoigakun ããã§ãïŒ èªåã¯å€§åŠçã§è¥ãããŸã ãŸã ããããã ãšã°ããæã£ãŠããŸãããïŒèªåããè¥ãäžä»£ãäžççãªã«ã³ãã¡ã¬ã³ã¹ã§è±èªã§å ã
ãšçºè¡šããŠããã®ãçã§èŠãããšã§èªåèªèº«ãã£ãšåãã€ããŠãã®ãããªã«ã³ãã¡ã¬ã³ã¹ã«ç«ã¡ããïŒãšåŒ·ãæããŸããïŒ å°è±¡æ·±ãçºè¡š ã©ããçŽ æŽãããã£ãã®ã§ããïŒç¹ã«å°è±¡ã«æ®ã£ãŠããã®ã Mayuko Inoue ããã®çºè¡šã§ãïŒåœŒå¥³ã¯Swiftã®æè¡çãªè©±ã§ã¯ãªã iOS ããããã ãŒã®å€æ§æ§ ã®è©±ããããŠããŸããïŒ iOS ããããã ãŒã®å€æ§æ§ïŒãšæããããããããŸãããïŒäºå® iOS ã¢ããªã¯ Mac ã§ããéçºããããšãã§ããïŒ ããããã ãŒç»é²ã«ãå€é¡ãªãéãå¿
èŠãª iOS éçºè
ã¯æ¬§ç±³ãšã¢ãžã¢ã«ããã»ãšãã©ããªãããã§ãïŒæ Œå·®åé¡ã¯å人ã®åã§ã¯ã©ãããããã§ããŸãããïŒåœŒå¥³ã¯èªãã®ãšã³ãžãã¢ãšããŠã®ç掻ã YouTube ãéããŠç޹ä»ããããšã§ ãšã³ãžãã¢ã®çæ
ãç¥ã£ãŠãããåãçµã¿ãããŠããŸãïŒ ã·ãªã³ã³ãã¬ãŒ ã®ãšã³ãžãã¢ã®1æ¥ã玹ä»ãã ãã¡ãã®åç» ãªã© èªåãã¡ã玹ä»ããããšã§ç¥ããªã人ã«ãšã£ãŠã¯è¬ã«æºã¡ããšã³ãžãã¢ãšããè·æ¥ãå°ãã§ãç¥ã£ãŠãããïŒãšã³ãžãã¢ã«å¯ŸããããŒãã«ãäžããåãçµã¿ã§ãïŒ ã¹ã¿ã¡ã³èªèº«ããã®æè¡ããã°ãéããŠã¹ã¿ã¡ã³ã®ãããã¯ãéšåã³ãšã³ãžãã¢ã®æ¥åžžã䜿ã£ãŠããæè¡ãªã©ã玹ä»ãïŒå°ãã§ãã¹ã¿ã¡ã³ã«èå³ãæã£ãŠããããããšèããŠãããããªã³ã¯ããéšåããããŸããïŒãããŠããããå人ã§ãçºä¿¡ããŠãããããšæ¹ããŠæããŸããïŒ æåŸã« ç§ãåå ããã®ã¯2æ¥ç®ãš3æ¥ç®ã®ã¯ãŒã¯ã·ã§ããã§ãããæ®æ®µåå€å±ã§ã¯åŸãããªããããªåºæ¿ãããããåããŸããïŒ2æ¥ç®ã®ã¢ãã¿ãŒã㌠ãã£ãŒ ãïŒæ¥ç®ã®ã¯ãŒã¯ã·ã§ããã§æ±äº¬ãæµ·å€ã®ãšã³ãžãã¢ã®æ¹ãšã話ãã§ããã®ããšããµã€ãã£ã³ã°ãªçµéšã§ããïŒ
ã¯ããã« 4/18ã4/20ã«éå¬ãããRubyKaigiã«ãã¹ã¿ã¡ã³ãšã³ãžã㢠@mmoto99299415 (åçå·Š) ãš @uuushiro (åçå³)ã®2åã§åå ããŠããŸããããã®ã¬ããŒãèšäºã«ãªããŸãã ã»ãã·ã§ã³ ããã€ãæ°ã«ãªã£ãã»ãã·ã§ã³ã玹ä»ããŸãã 1æ¥ç® Building Serverless Applications in Ruby with AWS Lambda AWS SDK for Ruby ããŒã ã®@alexwwoodããã«ããã»ãã·ã§ã³ã§ããã æè»æ§ã»ã¹ã±ãŒã©ããªãã£ã»é«å¯çšæ§ã»ã»ãã¥ã¢ã»åŸé課éå¶ãšãã£ããµãŒããŒã¬ã¹ã®å©ç¹ã®èª¬æãããå®éã« AWS SAM CLI ã» AWS SDK for Ruby ã» aws -record ãçšãããµãŒããŒã¬ã¹ã¢ããªã±ãŒã·ã§ã³ã®ã©ã€ããã¢ã®å®æœãŸã§äžå¯§ã«èª¬æããŠãããŸãããã¹ã¿ã¡ã³ã§ãåæåºç€ã® ã³ã³ããŒãã³ã ã®äžã€ã« Ruby ãå®è¡ãã AWS LambdaããããŸããå¥ã®æ©äŒã«ããã¯ããã°ã§ç޹ä»ããããšæããŸãã https://speakerdeck.com/awood45/building-serverless-applications-in-ruby-with-aws-lambda GraphQL Migration: A Proper Use Case for Metaprogramming? Square Incã®ãšã³ãžãã¢ã§ãã@gao_shawneeããã«ããã»ãã·ã§ã³ã GraphQLãæ±ãã«ãããããµãŒããŒãµã€ãåŽã§ã¯ãã¢ãã«ã«å¯Ÿå¿ããTypeã»Fieldãå®çŸ©ããå¿
èŠããããŸããå ããŠãQueryã«ããããŒã¿ãååŸãããããQueryTypeå
ã«å¿
èŠãšãªãFieldãšãããã«å¯Ÿå¿ããã¡ãœãããå®çŸ©ããŸãã察象ãšããã¢ãã«ãå¢ãããšãTypeã»Fieldã»ã¡ãœããã®å®çŸ©ãäžç·ã«å¢å ããã ãã ãFatã«ãªã£ãŠãããŸãã ã¡ã¿ããã°ã©ãã³ã° ãå©çšããããšã§ãåçã«Typeã»Fieldã»ã¡ãœãããå®çŸ©ããŠãã®åé¡ãé²ããšããå
容ã®ã»ãã·ã§ã³ã§ããã ã¹ã¿ã¡ã³ã§ã¯GraphQLãæ¡çšãããµãŒãã¹ã¯ãŸã ãããŸããããä»åŸå°å
¥ããå Žåã¯åãåé¡ã«çŽé¢ããã®ã§ããšãŠãåèã«ãªããŸããã https://speakerdeck.com/shawneegao/ruby-kaigi-slides 2æ¥ç® Yabeda: Monitoring monogatari eBaymag.com ãéçšããŠãããšã³ãžãã¢ã®@Envekããã«ããã»ãã·ã§ã³ã§ããã eBaymagã§ã¯å€§èŠæš¡ãªSidekiqã®éçšãããŠãããããã§åŠãã çµéšã«ã€ããŠã® ããŒã¯ ã§ãããäŸãã°ãSidekiqã®ãžã§ããé
ããªã£ããšããããããªãé
ããªã£ãã®ããã©ãããèããã¹ãããåå ãèŠã€ããã«ããã£ãŠå¿
èŠãªçè«ã¯ãªã«ãïŒãšãã£ããããªçŸå®äžçã§ã®èª²é¡ã«å¯ŸããŠå
·äœçãªæ¹æ³ãç¥ãããšãã§ããŸãããã¹ã¿ã¡ã³ã§ã¯Sidekiqã®æ°å€ã®ç£èŠã«ã¯Sidekiqæšæºã® ããã·ã¥ ããŒãããå©çšããŠããŸããã§ãããããã®ã»ãã·ã§ã³ã§ã¯ãprometheus-client ãšãã gem ãå©çšããŠã¢ããªã±ãŒã·ã§ã³ã«ç°¡åã« Prometheus ã§ç£èŠããããã®ã¡ããªãã¯ã远å ããGrafanaã«ãã£ãŠã¯ã©ããäœæãå¯èŠåãããšããããšãå®è·µããŠããŸãããã¢ããªã±ãŒã·ã§ã³ãæé·ã«äŒŽã£ãŠãç£èŠãã¹ãã¡ããªã¯ã¹ãå¢ããŠããããšã¯ä»åŸã®åèã«ãªãããã§ããã https://docs.google.com/presentation/d/1i8N_OcnQJ9SE6wdqzqV-vp_IYRxQRpEtJ0tpluYtCvo/edit#slide=id.g5675f30aae_1_230 3æ¥ç® Cleaning up a huge ruby application Cookpad Incã®ãšã³ãžãã¢ã§ãã@riseshiaããã®ã»ãã·ã§ã³ã äžèŠãªã³ãŒããåé€ããããšã§ãã³ãŒãã®å¯èªæ§ãé«ãŸããã¢ããªã±ãŒã·ã§ã³ã®èªã¿èŸŒã¿æéãæ¹åã§ãããšããå
容ã§ããã å
·äœçã«ã¯æ¬¡ã®ãããªåãçµã¿ã玹ä»ãããŠããŸããã - ã³ãŒããåé€ããããã®ä»çµã¿ã¥ããïŒã©ã®ããã«ã¡ã³ããŒã« ã¢ãµã€ ã³ãããïŒ - ãœãŒã¹ã³ãŒã ãå®è¡ãããŠãããã©ããã®èª¿æ»æ¹æ³ â logã®ååŸ - 詳现ãªlogã®ååŸæ¹æ³ïŒInstructionSequense, oneshot coverageïŒ ã³ãŒããåé€ããããšã¯ãææãããã«çŸããªããããã©ãããŠãåªå
é äœãåŸãã«ãªããŸããã§ããã®äžã§ãä»çµã¿åããå®è·µããŠããæ¹æ³ã¯åèã«ãªããŸããã ïŒå¹Žéã§å®éã«æ°äžè¡ã®ã³ãŒããåé€ã§ããããšããææãã¿ãŠãæ¯æ¥ã®ç©ã¿éãã®éèŠããæããŸããã https://speakerdeck.com/riseshia/cleaning-up-a-huge-ruby-application ããŒã¹ åäŒæ¥ã®ããŒã¹ãèŠãŠåããŸããã æ®æ®µãRubyMineã䜿ã£ãŠéçºããŠããã®ã§ããããã®ãšãã£ã¿ãæäŸããŠããJetBrainsãããŒã¹ãåºããŠããŸãããæ§ã
㪠ããã«ã㣠ãæããé¢çœãã£ãã§ãã åãåå€å±ãã㯠ãšã€ããŒã ãããŒã¹ãåºããŠãããã¯ãã°ãçŠå²¡ã§è©±ãããããšãã§ããŠå¬ããã£ãã§ãã ä»ã«ãããŒã¹ãåã£ãã®ã§ããããšã³ãžãã¢ã®æ¹ãšãã£ãã話ãã§ããŠãææçŸ©ãªæéãéãããŸããã ææ³ æŸè°·(@uuushiro) ååå ã®RubyKaigiãšãŠã楜ããã£ãã§ããç¹ã«å°è±¡ã«æ®ã£ãã®ã¯ã3æ¥ç®ã®ã Ruby Committers vs the Worldããšããã Ruby ã³ããã¿ã®æ¹ãã¡ãã¹ããŒãžäžã§ããããã® Ruby ã«ã€ããŠæ§ã
ãªè°è«ãç¹°ãåºãããšãã£ãã³ãŒããŒã§ãããã®ã³ãŒããŒã®äžã§ã¯ãã³ããã¿ã®çããã® Ruby ãè¯ãããããšããæããšãçµ¶ãéãªãåªåãéããŠãããšããéçšãå£éèŠãããšãã§ããæ¹ããŠæ¯ããŠãã ãã£ãŠããæ¹ã
ã«æè¬ã®æ°æã¡ãæ¹§ããŸããã ã³ããã¿ã®æ¹åå£«ã®æãåããæ¥æ¬èªã§äº€ããããã®ãæ¥æ¬çºç¥¥ã® ããã°ã©ãã³ã°èšèª ãªãã§ã¯ã ãšæããŸããããã°ããŒãã«ãªä»²éãã¡ãšã³ãã¥ãã±ãŒã·ã§ã³ããšããRubyKaigiããã£ãšæ¥œãããããã«è±èªã®å£ãä¹ãè¶ããŠæ¥å¹Žã®RubyKaigiãè¿ãããã§ãããŸãã Ruby ãæ¯ããŠããåŽãšå©çšããŠããåŽã®éã«å€§ããæè¡çãªå£ãæããŸãããç§èªèº«ã Ruby ãæ¯ããŠããæ¹ã
ãšåãç®ç·ã§ Ruby ã®æªæ¥ãèããããããã«ããšã³ãžãã¢ãšããŠãã£ãšæé·ããããšæããŸããã ã¹ã¿ã¡ã³ã®åµæ¥äºæ¥ãTUNAGãã¯ã Ruby ãå©çšãæé·ããŠããŸãããã¹ã¿ã¡ã³ãšããŠã Ruby ã«ãäžè©±ã«ãªãã ãã§ã¯ãªããæ©è¿ããããŠããããšæããŸãã æ¥å¹Žã®RubyKaigiã¯ æŸæ¬åž ãšããããšã§ãåå€å±ããè¿ãã®ã§ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ã¿ããªã§åå ããŸãã RubyKaigiã§ãäžè©±ã«ãªã£ãçãããæ¬åœã«ããããšãããããŸãããæ¥å¹Žããããããé¡ãããŸãã ããã¢ã(@mmoto99299415) RubyKaigiã«åå ããå
å®ããæéãéããããšãã§ããŸãããåã»ãã·ã§ã³ãéããŠãæ®æ®µã®éçºã§ã¯ç¥ãããšã®ã§ããªãå
容ã«è§Šããæ·±ãæãäžãããã£ãããåŸãããŸãããïŒãã ãæ¥æ¬èªéèš³ãç¡ãã®ã¯èŸãã£ãæ±ïŒ æãå¿ã«æ®ã£ãã»ãã·ã§ã³ã¯ãèªåãæŸè°·ãšåãã Ruby Committers vs the Worldãã§ãã äºææ§ãæšãŠãŠã§ãã¢ã°ã¬ãã·ãã«ä»æ§ãå€ããŠããã¹ããïŒãšããè°é¡ããversion upã§è¿œå ãããæ°ããæ©èœïŒå³ä»£å
¥ãã€ãã¬ãŒãã®ãããã¯ãã©ã¡ãŒã¿ãŒïŒã«ãããŠãæ§ã
ãªæèŠã亀ããããŠããŸããã Ruby ãéçºããäžã§ã®è°è«ãç®ã®åœããã«ããŠã1RubyistãšããŠè峿·±ãã£ãã§ãã ãããŸã§ã Ruby ãšããèšèªã§åœããåã®ããã«éçºããŠããŸãããããã®è£åŽã«ããã³ããã¿ãå®å®çã®versionãæäŸããã¡ã³ãããŒã®æ¹ã
ã®ãããã§ãæ®æ®µã®éçºãæãç«ã£ãŠããããšãèã§æããããšãã§ããŸããã å ããŠãRubyKaigiãéããŠãä»ã®ãšã³ãžãã¢ãšäº€æµãæãŠãããšããããå¬ããã£ãã§ããã¢ããªã±ãŒã·ã§ã³ãäœãã«ãããã ã¢ãŒããã¯ã㣠ãæè¡éžå®ãéçºäœå¶ãªã©ãäžç·ã«è°è«ã§ãã仲éãå¢ããã®ã¯ãããè¯ãã£ãã§ãã ããŸã ã¹ã¿ã¡ã³ã¯ã¡ããã©4æã«çŠå²¡æ¯ç€Ÿãç«ã¡äžããã®ã§ãçŠå²¡æ¯ç€Ÿã®ã¡ã³ããŒãšããããéãé£ã¹ãŠããŸããïŒ çŠå²¡ã§ãã¹ã¿ã¡ã³ããããããé¡ãããŸãã ãããã« ä»åãRubyKaigiã«ã¯æ¥åãšããŠåå ããŠããã宿æ³è²»ãšäº€éè²»ãäŒç€Ÿã«è² æ
ããŠããããŸããã ã¹ã¿ã¡ã³ã§ã¯ Ruby ãæžãæ©äŒããããããããŸãïŒçµ¶è³ Rubyist ãåéäžã§ãïŒïŒèå³ãããæ¹ã¯ Wantedly ãããé£çµ¡ãã ããã
ããã«ã¡ã¯ãWeb ã¢ããªã±ãŒã·ã§ã³ãšã³ãžãã¢ã®ããã¢ãã§ãã æ®æ®µã¯ TUNAG ãšãããäŒæ¥ãã³ãã¥ããã£ã察象ãšãããµãŒãã¹ã®éçºããŠããŸãã ä»åã®ããã°ã§ã¯ãTUNAGã®ãŠãŒã¶ãŒç»é²ãå®è£
ãããšãã«æ¡çšãããRails ã® FormObject ãåãäžããŸãã ç®æ¬¡ ã¯ããã« FormObject æ¡çšäŸ ActiveModel::Model ãããã« ã¯ããã« ãŠãŒã¶ãŒç»é²ã«ãããããŠãŒã¶ãŒã ãã§ãªãããã®ä»å±æ
å ±ãæ±ã Model ïŒæå±ãªã©ïŒãåæã«ä¿åããå¿
èŠããããŸããã ããããåã
ã§åŠçãããšãåããããªåŠçã Controller ã§ç¹°ãè¿ãæžãããšã«ãªããèŠéããæªããªããŸãã Rails ã® FormObject ãæ¡çšããããšã§ãè€æ°ã® Model ãäžç·ã«ä¿åã§ãã Controller ã®è¥å€§åãé²ãããšãã§ããŸããã ãã®èšäºã§ã¯ FormObject ãšãã®æ¡çšäŸãã玹ä»ãããŠããã ããŸãã FormObject åäžã®ãã©ãŒã éä¿¡ã§è€æ°ã® ActiveRecord ã¢ãã«ãæŽæ°ãããå Žåã«ããã®æ°žç¶åããžãã¯ãã«ãã»ã«åã§ãããã¶ã€ã³ãã¿ãŒã³ã§ãã ActiveModel::Model ãšããã¢ãžã¥ãŒã«ã include ããããšã§å©çšã§ããŸãã ã¡ãªãã ããžãã¹ããžãã¯ã Controller ã«åºãªããããåã¬ã€ã€ãŒã®å¯èªæ§ãè¯ããªã çš®é¡ã®ç°ãªãè€æ° Model ãïŒã€ã® Model ãšããŠæ±ãã validation ãããããã æž¡ã£ãŠãã parameter ã FormObject ã®ã¯ã©ã¹å
ã§ parse ã§ãã DBã«äŸåããªãã€ã³ã¹ã¿ã³ã¹ã§ããActive Recordãšåãã€ã³ã¿ãŒãã§ãŒã¹ã§æ±ããïŒéç¥ãªã©ïŒ æ¡çšäŸ æ¡çšããå ãŠãŒã¶ãŒã®ã¿ãæ°èŠäœæããå ŽåãController ã¯ä»¥äžã«ãªããŸãã # ãŠãŒã¶ãŒã®æ°èŠäœæãã©ãŒã ã衚瀺 def new @user = User .new end # ãŠãŒã¶ãŒãäœæ def create @user = User .new(user_params) if @user .valid? @user .save end end æ±ã Model ãå¢ãããš... # ãŠãŒã¶ãŒãæå±ãäœæã®æ°èŠäœæãã©ãŒã ã衚瀺 def new @user = User .new @department = Department .new @address = Address .new . . . end # ãŠãŒã¶ãŒãäœæ def create @user = User .new(user_params) @department = Department .new(deparment_params) @address = Address .new(address_params) . . . if @user .valid? &amp;&amp; @department .valid? &amp;&amp; @address .valid? ... @user .save @department .save @address .save . . . end end åããããªåŠçãå¢ããŠãController ãèŠèŸããªããŸãã æ¡çšããåŸ User, Department, Address...ããŸãšãããããForm::Registration ãšãã FormObject ã®ã¯ã©ã¹ãäœããŸãã Controller ã®åŠç # ç»é²ã®æ°èŠäœæãã©ãŒã def new @registration = Form :: Registration .new end # ç»é²ã®å®è¡ def create @registration = Form :: Registration .new(registration_params) if @registration .valid? @registration .save # User ãš Department ãš Address ã create ããã end end Form::Registrationã®FormObjectã¯ã©ã¹ class Form :: Registration include ActiveModel :: Model attr_accessor :user , :department , :address validate :validate_user validate :validate_department validate :validate_address def initialize ( params : {}) @user = User .new(params[ :user_params ]) @department = Department .new(params[ :department_params ]) @address = Address .new(params[ :address_params ]) end def save @user .save @department .save @address .save end . . . end ãŸãšããŠvalidation ããããããparameter ã FormObject ã®ã¯ã©ã¹å
ã§ parse ã§ããŸãã ãŸããå Model ã® attributes ã§ã¯ãªããã©ãä¿åãããã©ããã®å€å®ã«å¿
èŠãª parameter ãããã§åãåãã Form::Registeration ã® validation ãšããŠè¿œå ããããšãã§ããŸãã ãã®ããã« FormObject ã䜿ãã«ã¯ãForm::Registration ã§ include ããŠãã ActiveModel::Model ãå¿
èŠã§ãã ActiveModel::Model ActiveModel::Model ã include ããããšã§ãèªåã§å®çŸ©ããã¯ã©ã¹ã FormObject ãšã㊠Model ã®ããã«æ±ãããšãã§ããŸãã äžãèŠããšã module ActiveModel module Model extend ActiveSupport :: Concern include ActiveModel :: AttributeAssignment include ActiveModel :: Validations include ActiveModel :: Conversion included do extend ActiveModel :: Naming extend ActiveModel :: Translation end . . . end end ActiveModel é¢é£ã®ã¢ãžã¥ãŒã«ã include , extend ãããŠããŸãã errors, valid? ãªã©ã®ã€ã³ã¹ã¿ã³ã¹ã¡ãœããããæ±ãã ActiveModel::Validations ãã ãšã©ãŒã¡ãã»ãŒãžã翻蚳ã§ãã ActiveModel::Translation ãå®çŸ©ãããŠããŸãã Model ã®ããã«ã€ã³ã¹ã¿ã³ã¹ã¡ãœãããã¯ã©ã¹ã¡ãœãããåŒã¶ããšãã§ããŸãã ãããã« Rails ã® FormObject ãåãäžããŸããã MVC ã¢ãŒããã¯ãã£ã ãã§ã¯ç¶ºéºã«ã³ãŒãã£ã³ã°ã§ããªãéšåããå¥ã®ã¬ã€ã€ãŒã«åãåºãããšã§ãå¯èªæ§ãé«ããããšãã§ããŸããããæ©äŒãããã°ã詊ãã«äœ¿ã£ãŠã¿ãŠãã ããã æåŸãŸã§èªãã§ããã ããããããšãããããŸããã ã¹ã¿ã¡ã³ã§ã¯ ãšã³ãžã㢠ãåéããŠããŸããèå³ãããæ¹ã¯ãã²ãé£çµ¡ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ãšã³ãžãã¢ã®æŸè°·ã§ãã çµç¹ã®ãšã³ã²ãŒãžã¡ã³ããé«ãããããã¯ã TUNAG(ããã°) ãéçºããŠããŸãã éçºã»éçšã«é¢ããäžã§æ¥ã
æãã®ã¯ãã¢ããªã±ãŒã·ã§ã³ã®ç®¡çãããã·ã³ãã«ã«ããã»ãã¥ã¢ã§ä¿¡é Œæ§ãé«ããã¹ã±ãŒã©ãã«ã«éçšããããšã容æã«ããããšããããšã§ãã AWS Systems Manager ã«ã¯ããããã®ããŒãºãæºããå€ãã®æ©èœãããããšãç¥ãå®éã«å°å
¥ããŠã¿ãŸãããç°¡åã«å°å
¥ããããšãã§ããéçšäžå€§ããªå¹æãåŸãããšãã§ããã®ã§å
±æãããŠããã ããŸãã TL;DR (æŠèŠ) ã¹ã¿ã¡ã³ã®Systems ManagerãæŽ»çšããããšã§ã SSH ã¬ã¹ã§å®å
šãªãªãã¬ãŒã·ã§ã³ãå®çŸããç£æ»å¯èœãªæäœãã°ãä¿åããçŸç¶ã®ãµãŒããŒã®ç¶æ
ãç°¡åã«ææ¡ã§ããããã«ãªããŸãããå
·äœçãªæŽ»çšäºäŸãšãšãã«ç޹ä»ããŸãã ç®æ¬¡ ã¹ã¿ã¡ã³ã«ãããæŽ»çšäºäŸ ã»ãã·ã§ã³ãããŒãžã£ãŒã§SSHã¢ã¯ã»ã¹ã®å»æ¢ ãªãŒãã¡ãŒã·ã§ã³ã§æ¥åžžã¿ã¹ã¯ã1ã¯ãªãã¯ã§èªåå ã€ãã³ããªã§ã€ã³ã¹ã¿ã³ã¹ã®ç¶æ
ãç°¡åã«ææ¡ ãŸãšã ã¹ã¿ã¡ã³ã«ãããæŽ»çšäºäŸ AWS Systems Managerãæã€æ©èœã®ãã¡ãã¹ã¿ã¡ã³ã§æŽ»çšããŠããæ©èœãšãã®éçšã«ã€ããŠèª¬æããŸãã ã»ãã·ã§ã³ãããŒãžã£ãŒã§ SSH ã¢ã¯ã»ã¹ã®å»æ¢ ã»ãã·ã§ã³ãããŒãžã£ãŒã®æ©èœã®èª¬æã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ããã確èªãã ããã ã»ãã·ã§ã³ãããŒãžã£ãŒ ã䜿çšããŠã ã€ã³ã¿ã©ã¯ãã£ã ãªã¯ã³ã¯ãªãã¯ãã©ãŠã¶ããŒã¹ã®ã·ã§ã«ããŸã㯠AWS CLI ãä»ã㊠Amazon EC2 ã€ã³ã¹ã¿ã³ã¹ ã管çã§ããŸããã»ãã·ã§ã³ãããŒãžã£ãŒ ã¯ãã€ã³ããŠã³ãããŒããéããããèžã¿å°ãã¹ããç¶æãããã SSH ããŒã管çãããããããšãªããå®å
šã§ç£æ»å¯èœãª ã€ã³ã¹ã¿ã³ã¹ ã®ç®¡çãæäŸããŸããã»ãã·ã§ã³ãããŒãžã£ãŒ ã¯ã Amazon EC2 ã€ã³ã¹ã¿ã³ã¹ ãžã®ç°¡åãªã¯ã³ã¯ãªãã¯ã® ã¯ãã¹ãã©ãããã©ãŒã ã¢ã¯ã»ã¹ããšã³ããŠãŒã¶ãŒã«æäŸãã€ã€ã ã€ã³ã¹ã¿ã³ã¹ ãžã®å¶åŸ¡ãããã¢ã¯ã»ã¹ã峿 Œãªã»ãã¥ãªãã£ã ã©ã¯ ãã£ã¹ã ã€ã³ã¹ã¿ã³ã¹ ã¢ã¯ã»ã¹ã®è©³çްãå«ããå®å
šã«ç£æ»å¯èœãªãã°ãå¿
èŠãšããäŒæ¥ããªã·ãŒã«æºæ ããããšã容æã«ããŸãã AWS Systems Manager Session Manager - AWS Systems Manager ç°¡åã«èª¬æãããšã SSH æ¥ç¶ããã«ã AWS ãããžã¡ã³ãã³ã³ãœãŒã«ïŒãããã¯AWSCLIïŒäžã§EC2 ã€ã³ã¹ã¿ã³ã¹ ã«å¯ŸããŠãªãã¬ãŒã·ã§ã³ãã§ããæ©èœã§ãã é©åãªããŒã«ãEC2ã«ã¢ã¿ãããããŠããŠãSSMãšãŒãžã§ã³ããã€ã³ã¹ããŒã«ãããŠãã ã€ã³ã¹ã¿ã³ã¹ ã以äžã®ããã«ãäžèЧã§è¡šç€ºãããŸãã ã€ã³ã¹ã¿ã³ã¹ ãéžæããŠãã»ãã·ã§ã³ãéå§ãã¯ãªãã¯ãããšãã»ãã·ã§ã³ãéå§ãã以äžã®ããã«ã·ã§ã«ã©ã€ã¯ãªç»é¢ããã©ãŠã¶ã§è¡šç€ºãããŸãã ä»»æã®æäœãå®äºããåŸãçµäºãã¯ãªãã¯ãããšã»ãã·ã§ã³ãçµäºããŸãã ãããŠãã»ãã·ã§ã³å±¥æŽã®ã¿ããã¯ãªãã¯ãããšãéå»ã«èµ·åããã»ãã·ã§ã³ã®å±¥æŽã確èªããããšãã§ããããã§ã¯ããã€ãã ãããã©ã® ã€ã³ã¹ã¿ã³ã¹ ã®ã»ãã·ã§ã³ãèµ·åããã®ãããããŠããã®ã»ãã·ã§ã³ã§ã©ã®ãããªãªãã¬ãŒã·ã§ã³ãå®è¡ããŠããã®ãã¯ãèšå®ç»é¢ã§ãS3 ãã±ãã ã¹ãã¬ãŒãžã®æå¹åããŸãã¯ããCloudWatch Logs ã¹ããªãŒã ã®æå¹åããONã«ããŠããã°ç¢ºèªããããšãã§ããŸããããŒã«ã«ã®ã¿ãŒããã«ã§ãªãã¬ãŒã·ã§ã³ãããŠããé ã¯ãåé¡ãèµ·ããæã«éå»ã®ãã°ãé¡ãããšãã§ããªãå Žåããããå°ã£ãããšããããŸãããããã§å®å¿ã§ãã ãŸããæ°ããã»ãã·ã§ã³ãå§ãŸã£ãæç¹ã§ SNS éç¥ãããããšãå¯èœã§ãã ã»ãã·ã§ã³ãããŒãžã£ãŒã®å°å
¥ã«ãããåéçºè
ããã® SSH ã¢ã¯ã»ã¹ãäžèŠã«ãªããèžã¿å°ãµãŒããŒã廿¢ããããšãã§ããŸãããã»ãã¥ã¢ã«ãªãã ãã§ãªããèžã¿å°ãµãŒã代ãç¯çŽã§ããŠè¯ãããšå°œããã§ãã ãªãŒãã¡ãŒã·ã§ã³ã§æ¥åžžã¿ã¹ã¯ã1ã¯ãªãã¯ã§èªåå ãªãŒãã¡ãŒã·ã§ã³ã®æ©èœã®èª¬æã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ããã確èªãã ããã Systems Manager ãªãŒãã¡ãŒã·ã§ã³ã䜿çšããŠãäžè¬çãªã¡ã³ããã³ã¹ãšãããã€ã®ã¿ã¹ã¯ãèªååããŸãããªãŒãã¡ãŒã·ã§ã³ã䜿çšãããšã Amazon Machine Image ã®äœæãšæŽæ°ããã©ã€ããŒãšãšãŒãžã§ã³ãã®æŽæ°ããã°ã©ã ã®é©çšã Windows ã€ã³ã¹ã¿ã³ã¹ ã§ã®ãã¹ã¯ãŒãã®ãªã»ããã Linux ã€ã³ã¹ã¿ã³ã¹ ã§ã® SSH ããŒã®ãªã»ãããããã³ OS ããããŸãã¯ã¢ããªã±ãŒã·ã§ã³æŽæ°ããã°ã©ã ã®é©çšãå¯èœã«ãªããŸãã ãªãŒãã¡ãŒã·ã§ã³ããã¥ã¡ã³ãã¯ãSystems Manager ããããŒãžã ã€ã³ã¹ã¿ã³ã¹ ããã³ AWS ãªãœãŒã¹ã§å®è¡ããã¢ã¯ã·ã§ã³ãå®çŸ©ããŸããããã¥ã¡ã³ã㯠JavaScript Object Notation ( JSON ) ã YAML ã䜿çšããããã«ã¯ãŠãŒã¶ãŒãæå®ãããã©ã¡ãŒã¿ããã³ã¹ããããå«ãŸããŸããã¹ãããã¯é çªã«å®è¡ãããŸãã AWS Systems Manager ãªãŒãã¡ãŒã·ã§ã³ - AWS Systems Manager Systems Managerã§å®è¡ããåçš®æäœã¯ããã¥ã¡ã³ããšããŠå®çŸ©ãããŠããŸãã AWS ãå®çŸ©æžã¿ã®ããã¥ã¡ã³ãããèªåã§äœæããã«ã¹ã¿ã ããã¥ã¡ã³ãããããŸãã ã¹ã¿ã¡ã³ã§ã®ãªãŒãã¡ãŒã·ã§ã³ã®æŽ»çšäŸã®äžã€ã«ãã¹ããŒãžã³ã°ç°å¢ãžã®ãããã€ããããŸãã以äžã¯ãç¹å® ã€ã³ã¹ã¿ã³ã¹ ã«å¯ŸããŠãããã〠ã¹ã¯ãªãã ãå®è¡ãããã«ã¹ã¿ã ããã¥ã¡ã³ãã§ãã --- description: "deploy staging" schemaVersion: "0.3" parameters: BranchName: type: "String" description: "(Required) Deploy target Branch" InstanceId: type: "String" description: "(Required) InstanceId to run command" default : "instance id" S3BucketName: type: "String" description: "(Required) S3BucketName" default : "tunag-systems-manager" S3KeyPrefix: type: "String" description: "(Required) S3KeyPrefix" default : "staging/deploy" mainSteps: - name: "deployStaging" action: "aws:runCommand" inputs: DocumentName: "AWS-RunShellScript" InstanceIds: ["{{ InstanceId }}"] OutputS3BucketName: "{{ S3BucketName }}" OutputS3KeyPrefix: "{{ S3KeyPrefix }}" Parameters: commands: - su operator -c "source ~/.zshrc; cd /home/app; ./bin/deploy_staging.sh {{ BranchName }}" ããã¯ãã¹ãããæ°ãïŒã€ã®ã·ã³ãã«ãªããã¥ã¡ã³ãã§ããparametersã§ããã©ã¡ãŒã¿åãããããŒã»ããªã¥ãŒãæå®ããŠããŸãã aws :runCommandã¯ãæå®ãããããã¥ã¡ã³ããå®è¡ããã¢ã¯ã·ã§ã³ã§ãã aws :runCommandã§ã¯ã1ã€ä»¥äžã®ç®¡ç察象 ã€ã³ã¹ã¿ã³ã¹ ã§ã³ãã³ããå®è¡ããããã®ãªãã·ã§ã³ãæå®å¯èœã§ããDocumentNameã§ãSystems Manager ãå®è¡ããããã¥ã¡ã³ã( AWS -RunShellScript)ãæå®ããŠããŸãã ãªããcommandsãªãã·ã§ã³ã§å®è¡ãããã³ãã³ããªã¹ãã¯ã1ã€1ã€ssm-userãšãããŠãŒã¶ãŒã§å®è¡ãããŸããåŒç€Ÿã®ãããã€ãããŒã§ã¯ãç¹å® Linux ãŠãŒã¶ãŒã«éå®ããŠãããã€ãããŠããã®ã§ãsuã³ãã³ãã®cãªãã·ã§ã³ãå©çšããããšã§ããŠãŒã¶ãŒãåãæ¿ããç¶æ
ã§ã³ãã³ãã®å®è¡ãããŠããŸãã ãŸãããªãŒãã¡ãŒã·ã§ã³ã®å®è¡ãªã³ã¯ããããŒã ã«å
±æããã°ç°¡åã«ç¹å®ã¿ã¹ã¯ã®å®è¡ç»é¢ã«ã¢ã¯ã»ã¹ããããšãã§ã䟿å©ã§ããäžå³ãäŸã«ãããšããããã€ããããã©ã³ãåãå
¥åãã©ã¡ãŒã¿ã«å
¥ããŠå®è¡ãã¯ãªãã¯ããã ãã§ãããã€ãéå§ããããšãã§ããŸãã ä»ãŸã§ã¢ããªã±ãŒã·ã§ã³ãšã³ãžãã¢ã¯ããããã€ãå®è¡ãããã ããªã®ã« SSH éµã®ç»é²ãå¿
èŠã«ãªã£ãŠããŸãããä»åããªãŒãã¡ãŒã·ã§ã³ã䜿ãããšã§ãã¢ããªã±ãŒã·ã§ã³ãšã³ãžãã¢ã«ç¹å®ãªãœãŒã¹ãžã® SSH ã¢ã¯ã»ã¹æš©éãä»äžããå¿
èŠãªããç¹å®ã®ãªãœãŒã¹ã®ç¹å®ã®ã¿ã¹ã¯ã«å¯Ÿããã¢ã¯ã»ã¹æš©éãIAMã«ãã£ãŠæäŸã§ããããã«ãªããŸãããããã¯ã»ãã¥ãªãã£çã«å€§ããªåé²ã§ããã ãŸããSystems Managerã«ããããã¥ã¡ã³ãåã«ãã£ãŠãããŒã å
ã§å±äººåããŠããéçšã®ãã¹ãã ã©ã¯ ãã£ã¹ã ãªãŒãžã§ã³ãIAMã°ã«ãŒãéã§ç°¡åã«å
±æãã§ããããã«ãªããŸããããŸããããã¥ã¡ã³ããåãå
¥ããèš±å¯ãã©ã¡ãŒã¿å€ã®ããªããŒã·ã§ã³ãè¡ãããšãã§ãããã¹ãªãã¬ãŒã·ã§ã³ãé²ãããšãã§ããŸãã ä»åã¯ãšãŠãã·ã³ãã«ãªã¿ã¹ã¯ãSystems Manageräžã§ç®¡çã§ããããã«ããŸããããSystems Managerã¯ã ã€ã³ã¹ã¿ã³ã¹ ã ãã§ãªãåšèŸºã®ãµãŒãã¹ãå«ããŠèªååã§ãã ãã¬ãŒã ã¯ãŒã¯ ãªã®ã§ãç©æ¥µçã«è€éãªã¿ã¹ã¯ãç°¡çŽ åã»èªååããŠãããããšæããŸãã ãªãŒãã¡ãŒã·ã§ã³ã«ã¯æ°å€ãã®ã¢ã¯ã·ã§ã³ãããã®ã§ãã²å
¬åŒããã¥ã¡ã³ããã確èªãã ããã Systems Manager Automation ã¢ã¯ã·ã§ã³ã®ãªãã¡ã¬ã³ã¹ - AWS Systems Manager ã€ãã³ããªã§ ã€ã³ã¹ã¿ã³ã¹ ã®ç¶æ
ãç°¡åã«ææ¡ ã€ãã³ããªã®æ©èœã®èª¬æã¯ä»¥äžã®å
¬åŒããã¥ã¡ã³ããã確èªãã ããã ã€ã³ãã³ããªã䜿çšããŠã Amazon EC2 ã€ã³ã¹ã¿ã³ã¹ ããã³ãªã³ãã¬ãã¹ãµãŒããŒããŸãã¯ãã€ããªããç°å¢ã® ä»®æ³ãã·ã³ ( VM ) ããã ãªãã¬ãŒãã£ã³ã°ã·ã¹ãã (OS)ãã¢ããªã±ãŒã·ã§ã³ã ã€ã³ã¹ã¿ã³ã¹ ã® ã¡ã¿ããŒã¿ ãåéã§ããŸãã ã¡ã¿ããŒã¿ ãç
§äŒãããšããœãããŠã§ã¢ããªã·ãŒã«åŸã£ãŠãœãããŠã§ã¢ãšèšå®ãå®è¡ããŠãã ã€ã³ã¹ã¿ã³ã¹ ãšãæŽæ°ãå¿
èŠãª ã€ã³ã¹ã¿ã³ã¹ ããã°ããææ¡ã§ããŸãã AWS Systems Manager ã€ã³ãã³ã㪠- AWS Systems Manager ã€ã³ãã³ããªæ
å ±ãååŸããããšã§ã ã€ã³ã¹ã¿ã³ã¹ ã®å
šãŠã®æ§æèŠçŽ (OS, ããã«ãŠã§ã¢ , ã©ã€ãã©ãª)ã®äžèЧãšãã®ããŒãžã§ã³ãç°¡åã«ç®¡çããããšãã§ããŸããOSã®ã»ãã¥ãªãã£èšå®ã«åé¡ã¯ãªãããäžèŠãª ããã«ãŠã§ã¢ ã¯ååšããªãããªã©ãå ã€ã³ã¹ã¿ã³ã¹ ã®ç¶æ³ãææ¡ããããšãã§ããŸãããŸããååŸããæ
å ±ã¯ã以äžã®ããã« ããã·ã¥ ããŒã圢åŒã§è¡šç€ºããããšãã§ããŸãã ãŸãšã ãŸã ãŸã Systems Managerã®äžéšæ©èœãã䜿ãããªããŠããŸãããããã®æç¹ã§æ¢ã«æããŠããã¡ãªããã¯ä»¥äžã®4ã€ã§ãã 1. æš©éã®å¯èŠæ§ãšå¶åŸ¡æ§ã®åäž IAMããŒã«ã§å¶åŸ¡å¯èœã«ãªãã®ã§ã誰ãã©ã®ãµãŒããŒã«ã©ã®ã¢ã¯ã»ã¹æš©éããã£ãŠããã®ãã AWS äžã§ç°¡åã«ç®¡çãå¯èœ 2. ã»ãã¥ãªãã£ãš ã³ã³ãã©ã€ã¢ã³ã¹ ã®åäž èžã¿å°ãµãŒããŒãäžèŠ SSH çšã®ã€ã³ããŠã³ãçšããŒãéæŸãäžèŠ SSH ããŒãã¢ã®ç®¡çäžèŠ ãµãŒããŒã®ã€ã³ãã³ããªæ
å ±ã®ç®¡çãç°¡åã« ã¢ã¯ã»ã¹å±¥æŽãS3ãCloudTrailã§å¯èœã«ãªãããµãŒããŒã¢ã¯ã»ã¹ãªã©ç£æ»å¯èœãªãã°ãå¿
èŠãšããäŒæ¥ããªã·ãŒã«æºæ ãå¯èœ 3. éçšæ¥åã®èªååãšå®åå ç¹°ãè¿ãäœæ¥ã®èªååãšå®ååãã§ããã³ã¹ãåæžãšãã¹ãªãã¬ãŒã·ã§ã³ã®åæžãå¯èœ 4. ç¡æã§å©çšå¯èœ AWS Systems Managerèªäœã¯ç¡æ Systems Managerã«ãã£ãŠç®¡çããã AWS ãµãŒãã¹ã䜿çšããŠããå Žåããã®ãµãŒãã¹ã®æéã¯çºç ãµãŒãã¹ãéå¶ããã«ããã£ãŠå¿
èŠãªã»ãã¥ãªãã£èŠä»¶ãã AWS ã®ãµãŒãã¹ãå©çšããããšã§ãã»ãšãã© å·¥æ° ãæããããµãŒãã¹å
šäœã®ã»ãã¥ãªãã£ãé«ããããšãã§ããŸããããããããé©å㪠AWS ãµãŒãã¹ãéžæããéçšã³ã¹ããäžããŠããããšæããŸãã ã¹ã¿ã¡ã³ã§ã¯äžç·ã«ã·ã¹ãã ã®ä¿¡é Œæ§ãåäžãããŠãã仲éãåéããŠããŸãããã² ãã¡ã ãããé£çµ¡ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã§ iOS / Android ã¢ããªã®ãšã³ãžãã¢ãããŠãã @temoki ã§ãã TUNAG ã® iOS ã¢ããªã¯ãã¹ãŠ Apple çºã® ããã°ã©ãã³ã°èšèª Swift ã§æžãããŠããŸããããã®ïŒæã«ãã® Swift ã®åœéã«ã³ãã¡ã¬ã³ã¹ try! Swift 2019 Tokyo ãéå¬ãããŸããã ã¢ããªéçº ã®æ
å ±åéãã¹ãã«åäžã®ããã«ãæšå¹Žããã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããã®ã«ã³ãã¡ã¬ã³ã¹ã«åå ããŠãããä»åã¯ç§ãåå ããŠããŸããã®ã§ããã®æ§åããäŒãããããšæããŸãã åœéè²è±ããªäŒå Ž éå¬å Žæã¯ ãã«ãµãŒã« æžè°·ãã¡ãŒã¹ãã§ããå°çãããšç·è²ã®Tã·ã£ããçãããããã®ã¹ã¿ãããšãtry! Swift ã㣠ã©ã¯ ã¿ãŒã® Riko ãåºè¿ããŠãããå°äžã®äŒå Žã®æ¹ã«æ¡å
ããŠããã ããŸããã ãã¡ããã»ãã·ã§ã³äŒå Žã§ãïŒïŒæ¥ç®æåŸã®åçæ®åœ±ã®æã®ãã®ã§ããïŒãåœå
å€ãããªããš900人ã®åå ããã£ãããã§ããæµ·å€ããã®åå è
ãéåžžã«å€ããåœå
ã§ããã ãåœéè²è±ã㪠ããã°ã©ãã³ã°èšèª ã«é¢ããã«ã³ãã¡ã¬ã³ã¹ã¯åžãªã®ã§ã¯ãªãããšæããŸãã ã¹ããŒã«ãŒã«ããçŽ æŽãããçºè¡šã®æ°ã
ãã®ã«ã³ãã¡ã¬ã³ã¹ã¯äž»ã«æåŸ
ã¹ããŒã«ãŒã«ããçºè¡šãšãCFP ã®åéããéžã°ããã¹ããŒã«ãŒã«ããã©ã€ããã³ã° ããŒã¯ ãäžå¿ãšãªããŸããåžäŒé²è¡ã¯æ¥æ¬èªãšè±èªäž¡æ¹ã§è¡ãããçºè¡šãåæéèš³ãå
¥ããªã©ãªãŒãã£ãšã³ã¹ãžã®é
æ
®ãäžå
šã§ãããç¹ã«åæéèš³ã®è³ªãé«ããããããã ãå°éçãªå
容ãããã«å€æã§ãããã®ã ãªãšé©ããŸããããããã§è±èªãåŸæã§ã¯ãªãç§ãçºè¡šå
容ã«å°å¿µããããšãã§ããŸããã å人çã«åºãã£ãçºè¡šã®ïŒã€ã¯ @1024jp ããã® native macOS applicationããŸãã¯AppKitã®äžç ã§ãã以åã«æž¡éæµå€ªããã® èãããã¶ã€ã³ ãšããæžç±ã«æéãåããã®ã§ããããã®å
容ã macOS ã®ã¢ããªã±ãŒã·ã§ã³ãšããäžçã§å
·äœåããããããªçŽ æŽãããçºè¡šã§ããã@1024jp ãããéçºãããŠãã CotEditor 㯠masOS ã«æé©åãããŠãšãŠã䜿ãããããä»ãæçšããŠããŸãã ãããŠããäžã€ãæãè峿·±ãã£ãã®ã @terhechte ããã® Introduction to Swift Keypaths ã§ããSwift ã® KeyPath æ©èœã«ã€ããŠã¯ç§ã®ç¥è㯠Swift 3 æä»£ã®ãã®ã§æ¢ãŸã£ãŠããŸã£ãŠããã®ã§ãåæ
å ±ãä¿æãããç¹ãªã© KeyPath ãéåžžã«åŒ·åãªãã®ã«ãªã£ãŠããããšã«é©ããŸããããããã ãžã§ããªã¯ã¹ ã ãããã³ã« ãšã¯ç°ãªãæœè±¡åã®ææ®µãšããŠçšããŠããŸã£ãç¹ã¯ ç®ããé± ã§ããã å人ã¹ãã³ãµãŒããšãŠããªã¹ã¹ã¡ã§ã ããã¯å人çãªæŽ»åãšãªããŸããããã®ãããªè¯ãã³ãã¥ããã£ãä»åŸãç¶ç¶ããŠããããšã«å°ãã§ãè²¢ç®ã§ããã°ãšæããå人ã¹ãã³ãµãŒãšããŠç»é²ããŠããŸããããããšããã®åçã®ããã«éäŒæã«ç޹ä»ããŠããã ããããå
¬åŒãŠã§ããµã€ããå
¬åŒã¢ããªããããŠäŒå Žã®å
¥ãå£ã®ã¹ãã³ãµãŒããŒããªã©æ§ã
ãªãšããã§ã¢ã€ã³ã³ãæ²èŒããŠããã ããŸãããã¹ãã³ãµãŒããŒãã«ã¯çãããµã€ã³ãããŠããã®ã§ç§ãæžããŠããŸãããããŸããããã«ãã£ãŠä»ã®åå è
ãšã®äŒè©±ã®ãã£ããã«ããªããŸããã®ã§ãtry! Swift ã®å人ã¹ãã³ãµãŒã¯ãªã¹ã¹ã¡ã§ãïŒ å€çš®å€æ§ãªã¹ãã³ãµãŒããŒã¹ try! Swift ã«ã¯ããããã®ã¹ãã³ãµãŒãã€ããŠãããå瀟工倫ãããããããŒã¹ãåºãããŠããŸããåç㯠Cyber Agent ããã®æç¥šã³ãŒããŒã§ããApplication Architecture ã®ãšããã§ç§ã¯ VIPER ã«æåã®äžç¥šãå
¥ããŠããŸãããTUNAG ã® iOS ã¢ããªã¯ VIPER ã¢ãŒããã¯ã㣠ãŒãæ¡çšããŠããŸãããã®ãããã¯ãŸãä»åºŠãã®ããã°ã§è©³çŽ°ãæžããããšæããŸãã ãã®ãããªã¹ãã³ãµãŒããŒã¹ã§å瀟ã®ãµãŒãã¹ã«ã€ããŠèãããããããæ¯ãããšã³ãžãã¢ãªã©ãšã亀æµããããšãã§ããŸããããã®äžã®ãã¬ã¿ããã®ããŒã¹ã§èª¬æãããŠããã仿ã«ãªãªãŒã¹ããã°ããã® ãã¬ã¿now ãšãã飲é£åºãåœæ¥äºçŽã§æçïŒïŒååŸã«å
¥ãããšãããµãŒãã¹ãçŽ æŽãããã£ãã§ãã飲é£åºçã®äºçŽç®¡çãããŠãããã¬ã¿ããã ããããã§ããããšã§ããããã®æ¥ã¯åå è
ïŒïŒäººãããã§å€é£ã«è¡ãããšã«ãªã£ãã®ã§ãããæ©éãã®ãã¬ã¿nowã§äºçŽããŠãæ¬åœã«ããã«ãåºã«å
¥ããŠããŸããŸãããäºæ¬¡äŒäŒå Žæ¢ããªã©ã«ããã圹ç«ã¡ããã§ãããåºèãšããŠã¯ç©ºããæ¥µåæžãããŠè¯ããµãŒãã¹ã ãªãšæããŸããã æåŸã« ç§ã¯éœåã«ããïŒæ¥ç®ã ãã®åå ãšãªããŸãããããã®ïŒæ¥ã ãã§éåžžã«å€ãã®åºæ¿ãåããŸããããããããã®çŽ æŽãããçºè¡šã«ãã£ãŠå€ãã®ç¥èŠãåŸãããšãã§ããŸãããã¹ã¿ã¡ã³ã¯ãŸã 嵿¥æã§ããã«ããããããããããã£ãã«ã³ãã¡ã¬ã³ã¹ã«ãšã³ãžãã¢ãç©æ¥µçã«éãåºããŠããã ããã®ã¯ãšãŠããããããã§ããã ã¹ã¿ã¡ã³ã§ã¯ãã®ãããªèªå·±ç éœã奚å±ããæåããããããããµããŒãããç°å¢ãæŽã£ãŠããããããšã³ãžãã¢ãæé·ããããããããã¯ãéçºã«æŽ»ãããããšããè¯ããµã€ã¯ã«ãçãŸããŠãããšæããŸãïŒãããªç°å¢ã®äžã§äžç·ã«åç£ç¢ç£šããŠã¿ãããšæã£ãŠããã ãããããã² ãã¡ã ãããé£çµ¡ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ã接ç°ã§ãã以åãåŒããã°ã§ãã TUNAGã®å
šææ€çŽ¢ãæ¯ãã Elasticsearch à Rails ããšããŠç޹ä»ãããŠããã ããããã«ãTUNAGã§ã¯æ€çŽ¢æ©èœã®å®è£
ã«Elasticsearchãå©çšããŠããŸããæ€çŽ¢ã¯ãšãªãšããŠã¯äž»ã«Multi Matchãå©çšããŠããã®ã§ãããRDBã«ç»é²ãããŠããã¬ã³ãŒããå©çšãããã圢ã§Elasticsearchã®ããã¥ã¡ã³ãåããæ¹æ³ã«ã€ããŠè©Šè¡é¯èª€ãããããå
±æãããŠããã ããŸãã TL;DR (æŠèŠ) Elasticseachã§ã¯ã Multi Match Query ã䜿ã£ãŠãè€æ°ã®ãã£ãŒã«ããæ€çŽ¢å¯Ÿè±¡ãšããã¯ãšãªãçºè¡ã§ããŸããæ€çŽ¢æã«å¯Ÿè±¡ãšãããã£ãŒã«ããæå®ããããšãã§ãããããäžã€ã®ã€ã³ããã¯ã¹ããæ€çŽ¢å¯Ÿè±¡ãã£ãŒã«ãã®ç°ãªãè€æ°ã®çšéã§å
±çšããããšãã§ããŸãããã ããäžã€ã®ã€ã³ããã¯ã¹ã«å«ãããããã£ãŒã«ãæ°ã«ã¯å¶éããããããæ³šæãå¿
èŠã§ãã å®çŸãããã£ãããš Elasticsearchãæ€çŽ¢çšéã§å°å
¥ããå ŽåãRDBã«ã¯1:Nã§æ ŒçŽãããŠããæ
å ±ããElasticsearchã«ã¯1ã€ã®ããã¥ã¡ã³ããšããŠç»é²ããã±ãŒã¹ããããŸããããšãã°ãã1ã€ã®åå : Nåã®ååæ
å ±ããããã1ã€ã®ååæ
å ±ãããã¥ã¡ã³ãã«ãŸãšããŠãããElasticsearchã«ç»é²ãããããªå Žåã§ãããNåã®ååæ
å ±ããã1ã€ã®ååæ
å ±ãã«æããããéã«ã¯ããæ€çŽ¢æã«ã©ã®ãããªæ
å ±ãæ€çŽ¢å¯Ÿè±¡ãšãããããèããå¿
èŠããããŸãã æ€çŽ¢ã®çšéãšããŠã åºèå©çšè
ãã賌å
¥æã«åç
§å¯èœãªååæ
å ±ã®ã¿ãæ€çŽ¢ãã åºèæ
åœè
ããåºèãåç
§å¯èœãªååæ
å ±ã®ã¿ãæ€çŽ¢ãã ã·ã¹ãã ã®ç®¡çè
ãããã¹ãŠã®ååæ
å ±ãæ€çŽ¢ãã ãšãã3ã€ã®ãŠãŒã¹ã±ãŒã¹ããã£ããšããŸããããæ¹ãšããŠãäºéãèããããŸãã ããã¥ã¡ã³ã"ç»é²æ"ã«å¿
èŠãªæ
å ±ãäºããŸãšããŠããŸã ããã¥ã¡ã³ã"æ€çŽ¢æ"ã«å¿
èŠãªãã£ãŒã«ãã®ã¿ãæ€çŽ¢ãã é ã«èª¬æããŸãã ããã¥ã¡ã³ãç»é²æã«å¿
èŠãªæ
å ±ãäºããŸãšããŠããŸã ãã®å Žåã¯ãããšãã°ã以äžã®ããã«3ã€ã®ãã£ãŒã«ãã«æ
å ±ããŸãšãããå
·äœçã«ã¯å¿
èŠãªæèšãçµåããŠç»é²ããããšã§ãæ
å ±ãæããããããšãå¯èœã§ãã ååæ
å ± åºèå©çšè
çšãã£ãŒã«ãïŒ ãåååãååã®ç¹åŸŽãä¿èšŒã«ã€ããŠãetc...ã ã·ã¹ãã 管çè
æ€çŽ¢çšãã£ãŒã«ã : åºèå©çšè
çšãã£ãŒã«ã +ã販売æã®æ³šæç¹ãåºèã§ã®ç®¡çæ¹æ³ãetc...ã åºèæ
åœè
çšãã£ãŒã«ã : ã·ã¹ãã 管çè
çšãã£ãŒã«ã + ãã·ã¹ãã 管çäžå¿
èŠãªæ
å ±ãetc...ã ãã®ããã«ããã¥ã¡ã³ããç»é²ããå Žåãæ€çŽ¢æã«ã¯1ã€ã®ãã£ãŒã«ãã察象ãšããã°ããã®ã§ã Match Query ã§æ€çŽ¢ããããšãã§ããŸãã ãã ãããã®æ¹æ³ã§ã¯ãæ€çŽ¢æã«ãã现ãã調æŽãããããšãã§ããŸãããããšãã°ãããã®ããŒãžã§ã®æ€çŽ¢ã¯ãåååãç¹åŸŽã®ã¿ããæ€çŽ¢ãããããšãªã£ãå Žåãå©çšã§ãããã£ãŒã«ãããªããããããã¥ã¡ã³ãã®åç»é²ãããªãéã察å¿ã§ããŸããã ããã¥ã¡ã³ãæ€çŽ¢æã«å¿
èŠãªãã£ãŒã«ãã®ã¿ãæ€çŽ¢ãã ããã§ã以äžã®ããã«ããã¥ã¡ã³ããç»é²ããŸãã ååæ
å ± ååå ååã®ç¹åŸŽ ä¿èšŒã«ã€ã㊠販売æã®æ³šæç¹ etc... ãã®ããã«æ§ç¯ããã€ã³ããã¯ã¹ã«å¯Ÿã㊠Multi Match Query ã䜿ãã°ãæ€çŽ¢æã«å¯Ÿè±¡ãšãããã£ãŒã«ãã倿ŽããŠå¯Ÿå¿ããããšãå¯èœã§ãã Multi Match Query ã§ã¯ãè€æ°ã®ãã£ãŒã«ãã«å¯ŸããŠMatchã¯ãšãªãå®è¡ãããããªã¯ãšãªã§ãã以äžã®ã¿ã€ãããããã¿ã€ãã«ãã£ãŠæåãç°ãªããŸãã best_fields most_fields cross_fields phrase phrase_prefix ä»åã®ãããªçšéã§ã¯ãcross_fiedsã䜿ããŸãã cross_fields ã¿ã€ãã®æ€çŽ¢ã§ã¯ããæå®ããããã£ãŒã«ãã倧ããªäžã€ã®ãã£ãŒã«ããšããŠæ±ããäžããããã¯ãšãªãšäžèŽããŠãããã®ãæ€çŽ¢ããããããªåããããŸãã ããšãã°ããéæ§ äžå€«ãã§æ€çŽ¢ããå Žåããããããæ
å ±ãæã€ã®ã¯ãåååããã£ãŒã«ããšããååã®ç¹åŸŽããã£ãŒã«ãã§ããcross_fieldsã¿ã€ãã®ã¯ãšãªã§ããã°ããã¯ããŸããããããŸãããããšãã°ãbest_fieldsãã§ã¯ãããããŸããããbest_fieldsããè€æ°ã®ãã£ãŒã«ãã察象ãšããŠæ€çŽ¢ããã®ã§ããããããããããã«ã¯ãåããã£ãŒã«ãã«ãéæ§ äžå€«ããå«ãŸããŠããå¿
èŠãããããã§ãã cross_fieldsãæå®ããmulti_matchã¯ãšãªã¯ã以äžã®ãããªåœ¢ã«ãªããŸãã { " query ": { " multi_match " : { " query ": " éæ§ äžå€« ", " type ": " cross_fields ", " fields ": [ " ååå ", " ååã®ç¹åŸŽ ", etc ... ] , " operator ": " and " } } } åé¡ç¹ ãã®ããã«äŸ¿å©ãªcross_fieldsã§ãããä»åã¯äžèšã®ããæ¹ã§ã¯äœ¿ããŸããã§ããã ä»åãç»é²ããããã¥ã¡ã³ãã¯ãã€ã³ããã¯ã¹åäœã§èŠããšããååã®ç¹åŸŽããNåããåçã«å¢ãããã®ã§ãããããã«è€æ°ã®ã¢ãã©ã€ã¶(ngramãškuromoji)ã§ãã£ãŒã«ããåããŠããããã ååæ
å ± ååç¹åŸŽ1_kuromoji ååç¹åŸŽ1_ngram ååç¹åŸŽ2_kuromoji ååç¹åŸŽ2_ngram ... ãšãªã£ãŠããããã£ãŒã«ãæ°ã®äžéãèšå®ããã®ãé£ããã£ãããã§ãã Elasticsearchã«ã¯ã1ã€ã®ãã£ãŒã«ãã«å«ãŸãããã£ãŒã«ãæ°ã®äžé(ããã©ã«ãã§ã¯1,000)ããååšããŠããŸããããã¯ãã€ã³ããã¯ã¹äœææã«ã index.mapping.total_fields.limit ãèšå®ããããšã§å€æŽå¯èœã§ã¯ãããŸãã ãããããããããã®å¶éã¯ã Add limit to total number of fields in mapping ã«ããããã«ãã¯ãšãªå®è¡æã« Mapping Explosion ãçºçããã®ãé¿ããããã®ãã®ã§ãã å®éã«ãåçã«ãã£ãŒã«ããå¢å ãã圢ã§ããã¥ã¡ã³ããç»é²ããå Žåã¯ãäºåã«äºæ³ããããã£ãŒã«ãæ°äžéãšãäžéã«ä»è¿ã«éããç¶æ
ã§ã®åäœæ€èšŒãå¿
èŠãšæãããŸãã ãŸãšã Elasticsearchã§ã®æ€çŽ¢ã¯éåžžã«é«éã§ãã¯ãšãªã髿©èœãªãã®ããããŸããããããæŽ»ããããã«ã¯INDEXã®èšèšãããã¡ããšè¡ã£ãŠãããªããã°ãããªããã®ã ãšæããŸããã ã¹ã¿ã¡ã³ã§ã¯ãšã³ãžãã¢ãåéããŠããŸããããèå³ããããŸããã ãã¡ã ãããé£çµ¡ãã ããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³CTOã®å°æã§ãã 2019幎3æ1æ¥ã«ãã¹ã¿ã¡ã³ã®ãšã³ãžãã¢å
šå¡ã§å宿ãè¡ããã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ãç®æããŠããå§¿(VISION) ãš åã
ã®ãšã³ãžãã¢ã®äŸ¡å€èгãè¡åæé(VALUE) ãå
šå¡ã§è©±ãåã£ãŠæ±ºããŸããã ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ãäœã£ãŠãããTUNAGãã®æè¡çãªè§£èª¬ ãšåããããšãã¹ã¿ã¡ã³éçºããŒã ã®çŸåšãšå°æ¥ããçè§£ããã ãããšæããŸãã ã¹ã¿ã¡ã³ã®éçºããŒã ã«èå³ããã£ãŠãã ãã£ããããã² @lifework_tech ã«æ°è»œã«ãé£çµ¡ãã ããããªãã£ã¹ç޹ä»ã詳现ããäŒããããŠãã ããã ãªãåå®¿ã§æ±ºããã®ã çŸåšãã¹ã¿ã¡ã³ã«ã¯ã10åã®ãšã³ãžãã¢ãåšç±ããŠããŸã(å
¥ç€Ÿäºå®è
å«ã)ããã®10åã¯ãããã°ã©ãã³ã°ã奜ãã§ãæé·ææ¬²ã«æº¢ããã¹ã¿ã¡ã³ã§å€§ããªä»äºãããããšæã£ãŠãããªã©ãå€ãã®å
±éç¹ããããŸãã ãšã¯ãããåèªã®ã¹ã¿ã¡ã³ã«å
¥ç€ŸãããŸã§ã®çµæŽã¯æ§ã
ã§ãããããããç°ãªã£ããã°ãããåæ§ããããŸããçŸåšã¹ã¿ã¡ã³ã§æ
åœããŠãã圹å²ã»é åãç°ãªããŸãã ä»åŸãTUNAGã®å€§èŠæš¡åã«ããæè¡çãªé£æåºŠã¯äžãããæ°èŠäºæ¥ã§ã¯äžå±€ã¹ããŒããæ±ãããããšã³ãžãã¢ã¡ã³ããŒãå¢å ããããšã§å€æ§æ§ãå¢ããŠããããªã©æ§ã
ãªèª²é¡ãåŸ
ã£ãŠããŸãã ãããã®èª²é¡ã«å¯Ÿå¿ããããã«ã¯ãåãšã³ãžãã¢ã¯äžå±€ã®æé·ãæ±ããããéçºããŒã ãšããŠããã«çµæããå¿
èŠããããŸããããã§ãéçºããŒã ãšããŠç®æãå§¿ãšãåã
ã®ã¡ã³ããŒã«æ±ããããèããæ¹éãåºããå£çµããããŒã ã§éé£ç¶ãªæé·ãããŠããããããããªèæ¯ã§ VISION ãš VALUE ãå®ããããšã«ããŸããã ãŸããäžèšã®çç±ã§ã嵿¥æããçŸåšã«è³ããŸã§ã¹ã¿ã¡ã³ãæ¯ããŠãããšã³ãžãã¢å
šå¡ãéãŸããå宿ãšãã圢ã§ãã£ããè°è«ããŠæ±ºããããšã«ããŸããã äžæäžéã®ããŒã ã§ã¯ãªããŠãåœäºè
æèã«æº¢ããããŒã ã§ããããã ããŒã ã®èŠæš¡çã«ãåã
ã®åŒ·ãã«å ããããŒã ã®åŒ·ãã§åè² ããææã«æ¥ãŠããã ã¢ã€ãã¢ãã¿ããªããåãããšã§ãå°ãã§ãè¯ãã¢ãŠããããã«ãããã è°è«ã«å ããããšã§ãä»åŸå ããã¡ã³ããŒã«èªåã®èšèã§äŒããŠãã£ãŠã»ããã åæãšãªãã¹ã¿ã¡ã³ã®äŒæ¥ç念 ãš Star Way æ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã®äŒæ¥ç念ã¯ã äžäººã§ãå€ãã®äººã«æåãå±ãã幞ããåºããã ãã§ãã ãŸãããã¹ãŠã®éšéã®å
šå¡ã倧åã«ããŠããäžèšã®5ã€ã®è¡åæéã Star Way ãããããŸãã Finish Promise ã»ã»ã» çŽæãå®ããæåŸãŸã§ãããã Work Bravely ã»ã»ã» 倧èã«æ»ããææŠã倱æãè®ãã Take Ownership ã»ã»ã» åœäºè
æèãæã£ãŠãèªããçå
ãã Say Niceplay ã»ã»ã» çŽ çŽã«èªãåãã匷ã¿ã§ããŒã ãåŒãäžãã Enjoy Together ã»ã»ã» ã¯ã¯ã¯ã¯ã倧äºã«ãäžäœæã楜ãã ä»åãåå®¿ã§æ±ºãã VISION ãš VALUE ã¯ãäžäœæŠå¿µãšã㊠ãã®äŒæ¥ç念 ãš Star Way ãååšãããšã³ãžãã¢ã«åãããŠããã«å
·äœçã«ãããã®ã«ãªããŸãã ã¹ã¿ã¡ã³éçºããŒã ã®VISION (ç®æããŠããå§¿) ãšã³ãžãã¢ãªã³ã°ã§äºæ¥ã®æé·ãçœåŒãã çµå¶çå¿µã«æ²ãããäžäººã§ãå€ãã®äººã«æåãå±ãã幞ããåºãããããå®çŸããããã«ãç®æããŠããéçºããŒã ã®å§¿ããã® VISION ã§ãã ãšã³ãžãã¢ãäºæ¥ãäœããäºæ¥ã«ãã£ãŠå€ãã®äººã«æåãå±ãã幞ããåºãããããŸããéçºããŒã ãæäŸããããã®ã¥ããåããäºæ¥ã®æé·ãçœåŒããååšã§ããããã ãšã³ãžãã¢ã®ã¿ããªã§è©±ããŸããããç®æããŠããå§¿ã«ããã¯ãªããã·ã³ãã«ã§åœããåãªå
容ã«ãªããŸããã ã¹ã¿ã¡ã³éçºããŒã ã®VALUE (䟡å€èгãšè¡åæé) VISION ãå®çŸããããã«ã¯ãé«ãæè¡åãšåŒ·ãããŒã ã¯ãŒã¯ãæã£ãéçºããŒã ãå¿
èŠã§ãã¡ã³ããŒã«æ±ãããã䟡å€èгãè¡åæéãå®ããå
容ã 以äžã® VALUE ã«ãªããŸãã ãŠãŒã¶ãŒç®ç·ã§èãã 倱æã«åãåã æ¬é³ã§äŒãã èªãã«æããªããªãååã§ãªã ãããããå¿ã« é ã«èª¬æãããŠãã ããã ãŠãŒã¶ãŒç®ç·ã§èãã å€ãã®äººã«æåãå±ãã幞ããåºããããã«ã¯ããŠãŒã¶ãŒãæ±ããããã¯ã(äºæ¥)ã«å¯ŸããŠã®æåŸ
å€ãè¶
ããå¿
èŠããããŸãã ãã®ããã«ã¯ããããã¯ãã«äœãæãšããŠã®ææã蟌ãã€ã€ããåžžã«ãŠãŒã¶ãŒç®ç·ã§èããå¿
èŠããããŸããå宿ã§è©±ããå
šå¡ãããŠãŒã¶ãŒãã¡ãŒã¹ãããªèããã䟡å€èгãšããŠæåªå
ããŠããŸããã 倱æã«åãåã ãšã³ãžãã¢ã®ä»äºã«ã¯ãäžå
·åãé害ãªã©å€±æã¯ã€ããã®ã§ããã§ãããã®å€±æããç®ãããããŠã¯ã ãã§ãæ£é¢ããåãåããªããŠã¯ãªããŸããã ã§ããéã倱æããªããããæ ¹æ§ã§ã¯ãªããšã³ãžãã¢ãšããŠåµæå·¥å€«ãããããã§ã倱æãããšãã«ã¯ãçæ¯ã«å€±æã«åãåã£ãŠå¯Ÿå¿ãã倱æããåŠã³ãåŸããããšã³ãžãã¢ã§ããããã æ¬é³ã§äŒãã ã©ãã ãåªç§ãªãšã³ãžãã¢ã§ãã£ãŠãäžäººã§ã¯éçããããŸãããšã³ãžãã¢ãªã³ã°ã§äºæ¥ã®æé·ãçœåŒããããã«ã¯ãããŒã ã§çµæããå¿
èŠããããã¹ã¿ã¡ã³ã®éçºããŒã ã§ã¯éåžžã«ããŒã ã¯ãŒã¯ãéãããŠããŸãã ããŒã ãšããŠã®çµæã匷ãããããã«äœãVALUEã«å ããã¹ãããè°è«ããçµæããæ¬é³ã§äŒãããã§ããã ã¬ãã¥ãŒãè°è«ãªã©æ§ã
ãªå Žé¢ã«ãããŠãé æ
®ããŠæ¬å¿ãäŒããªãããŒã ããããå³ããããšãæ¬é³ã§äŒãåããããŒã ã§ããããããã®ããã«ã¯ããäºãã«äžäººã®ãšã³ãžãã¢ãšããŠå¯Ÿçã«åãåãããªã¹ãã¯ããåãããšã倧åãšèããŠããŸãã èªãã«æããªããªãååã§ãªã ãšã³ãžãã¢ãšããŠããããã§ãã·ã§ãã«ãªä»äºããããããã®ããã«ããåžžã«é«ãã¬ãã«ã®ä»äºãç®æããäžã€äžã€ã®ææç©ã«å¯ŸããŠèªããæãŠãä»äºããããããèªãã«æãããããšåããããšãã«èºèºããããã§ããã°ãããã¯ååãªä»äºã§ã¯ãªããšèããŠããŸãã ãŸããææç©ã«éãããããŒã ããããã¯ããå®çžŸãªã©ãäœã«å¯ŸããŠãèªãã«æãããããã®ä»äºãããããšèããŠããŸãã ãããããå¿ã« ãšã³ãžãã¢ãªã³ã°ã¯æ¬åœã«æ¥œããä»äºã§ããæè¡ã§å°é£ãªåé¡ã解決ãããããããã¯ããå€ãã®ãŠãŒã¶ã«äœ¿ã£ãŠããããããšã¯ãæ¬åœã«ããããããŸãããããããåžžã«æãããšã³ãžãã¢ãšããä»äºã楜ãã¿ããã ãããããªãšã³ãžãã¢ãéãŸã£ãŠããããããªããŒã ã«ãªãããããªçŸå Žã§ããããã§ãã å宿ã®é¢šæ¯ èšå¿µã«åçãæ®ã£ãã®ã§ãå宿ã®é¢šæ¯ãã玹ä»ããŸãã åèªäºåã«èããŠãããVISION ãš VALUE ã®æ¡ãšãã®èããããããçºè¡šããå
šå¡ã§ã«ããŽã©ã€ãºãåªå
é äœãä»ããŠéçŽããŠãããŸããã ãã€ãã©ããåãããªé°å²æ°ã§å§ãŸããŸããããæãã®ããã£ãçºè¡šãšè°è«ã¯çå£ã§ããã å宿åŸã¯æèŠªäŒã§ãããæ¬é³ã§èªãåã£ãåŸã§ãã¿ãªããè¯ãç¬é¡ã§ãã æåŸã« ä»åã¯ãã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ã® VISION (ç®æããŠããå§¿) ãš VALUE (䟡å€èгãè¡åæé) ãã玹ä»ãããŠããã ããŸããã ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ãäœã£ãŠãããTUNAGãã®æè¡çãªè§£èª¬ ãšåãããŠãã¹ã¿ã¡ã³éçºããŒã ããå°ãã§ãç¥ã£ãŠããçè§£ããã ãããå¬ããã§ãã VISON ã VALUE ã¯ãæ²ããã ãã§ã¯æå³ããããŸãããçµå¶çå¿µã«æ²ãããäžäººã§ãå€ãã®äººã«æåãå±ãã幞ããåºãããããå®çŸããããã«ã¯ãåžžã« VISION ã VALUE ãæèããŠãæ¥ã
ã®ä»äºã®äžã§å®è·µããŠãããããšæããŸãã ã¹ã¿ã¡ã³éçºããŒã ã¯ãäžçäžã«èªããçŽ æŽãããããŒã ã§ããæ¬åœã«é Œããã仲éãã¡ãéã£ãŠãããŸãããã§ãããŸã 10åã§ãããã£ãšé Œããã仲éãéããŠããšã³ãžãã¢ãªã³ã°ã§äžã®äžã«æåãšå¹žããåºããããšæã£ãŠããŸãã ãã¡ã ãããåéäžã®è·çš®ã確èªã§ããŸãã ãããªç°å¢ã§ãã£ããã«åããããªãšæã£ãŠããã ããŸãããã @lifework_tech ã Wantedly ããããã²ãé£çµ¡ããã ããªãã§ããããã
ããã«ã¡ã¯ãã¹ã¿ã¡ã³CTOã®å°æã§ãã æè¿ã颿¥ãå匷äŒãªã©ã§ç€Ÿå€ã®ãšã³ãžãã¢ã®æ¹ãšè©±ããéã«ãã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ã®è©³çްã«ã€ããŠãæã£ãããé¢çœããããŸãšããããããããããããããšã®ææ³ãããã ãããšãç¶ããã¹ã¿ã¡ã³ã®äžã®äººãã¡ã®è©³çްãå€ã®äººãã¡ã«äŒãã£ãŠããªããšæããããšããããŸããã ã¡ããã©å
æã®1/29ãæ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã¯ãèšç«3åšå¹Žãè¿ããŠ4幎ç®ã«å
¥ã£ãããšããããïŒå¹Žç®ãè¿ããã¹ã¿ã¡ã³éçºããŒã ã®çŸåšã玹ä»ããèšäºãæžããŠã¿ãŸãã ãŸãã¯ã嵿¥ä»¥æ¥ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ãäœãç¶ããŠãã TUNAG ã«ã€ããŠãæè¡çãªåŽé¢ãã解説ããŸãã ã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ããŒã ã® VISION (ç®æããŠããå§¿) ãš VALUE (䟡å€èгãè¡åæé) ãšåãããŠèªãã§ããã ãããšãã¹ã¿ã¡ã³éçºããŒã ãããçè§£ããŠããã ãããšæã£ãŠããŸãã èå³ããã£ãŠãã ãã£ãæ¹ã¯ã ãã¡ã ãããé£çµ¡ããã ããæ°è»œã«ã¹ã¿ã¡ã³ã®ãªãã£ã¹ã«éã³ã«ããŠãã ããã詳现ããäŒããããŠããã ããŸãïŒ TUNAG ã«ã€ã㊠ã¹ã¿ã¡ã³ã¯ã TUNAGïŒããã°ïŒ ãšããããšã³ã²ãŒãžã¡ã³ãçµå¶ãæšé²ãã瀟å
SNSãéå¶ããŠãããç§ãå«ããŠãšã³ãžãã¢ãåšç±ããŠãããããã¯ãéšã¯ãTUNAG ã® äŒç»ãéçº ãæ
åœããŠããŸãã TUNAG ãçãŸããèæ¯ TUNAG ããã·ã§ã³ã¯ããšã³ã²ãŒãžã¡ã³ãã®åäžã«ãã£ãŠãæãã匷ãçµç¹äœãã®æ¯æŽãéããŠãã¯ã©ã€ã¢ã³ãã®æé·ãçœåŒããããšã§ãã ã¹ããŒãã«ãããŠã¡ã³ããŒãçžäºã«ä¿¡é Œããã£ãŠããããŒã ã¯åŒ·ãã®ãšåãããã«ãããžãã¹ã«ãããŠãçµå¶é£ãšåŸæ¥å¡ãåŸæ¥å¡éã§ä¿¡é Œé¢ä¿ãç¯ããŠããããŒã ã¯åŒ·ããæ§ã
ãªæ¥çã«ãããŠç«¶åä»ç€Ÿã«åããç¶ç¶çãªæé·ãéããŠããäŒæ¥ããããŸãã ãã®ãäŒæ¥ã«ãããŠã®çµå¶é£ãšåŸæ¥å¡ãåŸæ¥å¡éã§ä¿¡é Œé¢ä¿ã®åºŠåããããšã³ã²ãŒãžã¡ã³ãããšåŒã³ãã¹ã¿ã¡ã³ã¯ããšã³ã²ãŒãžã¡ã³ãããéèŠããçµå¶ãè¡ã£ãŠããäŒæ¥ã«å¯ŸããTUNAG ã«ãã瀟å
å¶åºŠã軞ãšããã³ãã¥ãã±ãŒã·ã§ã³ã«ãã£ãŠããšã³ã²ãŒãžã¡ã³ãã®éžæãæ¯æŽããŠããŸãã TUNAG ãçãŸããèæ¯ã®è©³çްãæãã«ã€ããŠã代衚ã®å è€ã ããžã§ã³ ã«æžããŠããŸãããŸãã å°å
¥äºäŸ ã«ãŠ TUNAG ãå°å
¥ããã ããŠããäŒæ¥æ§ã玹ä»ããŠããŸããã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ãã¡ããäžã®äžã«ã©ã®ããã«åœ±é¿ããŠããããç¥ãããšãã§ããŸãããã²èªãã§ããã ããªãã§ããããã TUNAG ã®äž»ãªæ©èœ TUNAG ã¯ãRuby on Rails ã§å®è£
ãããAmazon Web Services ã§éçšããã SaaSïŒãµãŒã¹ãSoftware as a ServiceïŒ ã§ãããŸããiOS/Android åã ã¢ã㪠㚠Chrome ãªã©ã® PC ã®ãã©ãŠã¶ã§å©çšã§ããå°å
¥é ããŠããåäŒæ¥ã®ç€Ÿå
ã³ãã¥ãã±ãŒã·ã§ã³ãæ
ã£ãŠããŸãã TUNAG ã¯ãèŠæš¡ãæ¥çã®ç°ãªãæ§ã
ãªäŒæ¥ã®ç€Ÿå
å¶åºŠã®éå¶ã«å¯Ÿå¿ã§ããããã«ãéåžžã«å€ãã®æ©èœãåããŠããŸãããåæ©èœã¯ïŒã€ã®ã°ã«ãŒãã«å€§å¥ãããŸãã SNS SNS ã«ã€ããŠã¯ãFacebook ã«ä»£è¡šããããããªãã¿ã€ã ã©ã€ã³ããããã£ãŒã«ã广çãªã³ãã¥ãã±ãŒã·ã§ã³ã«å¿
èŠãª çµµæåãã¹ã¿ã³ããç»åã»åç»ã®æçš¿ã«å ããããžãã¹ã§å©çšã§ãããã£ãããåãããã«æ©èœããã€ç€Ÿå
SNSãµãŒãã¹ã«ãªã£ãŠããŸãã 瀟å
å¶åºŠã®äœæãšå©çš 瀟å
å¶åºŠã®äœæãšéçšã«ã€ããŠã¯ãGoogle Form ã SurveyMonkey ã«ä»£è¡šããããã㪠任æã®ãã©ãŒã ãäœæã§ããŸããå¶åºŠã¯ãç¹å®ã®éšçœ²ã圹è·ã®ã¿ã«å©çšãå¶éãããå ŽåããããŸãããæžç±ã®è³Œå
¥è£å©ãªã©å©çšåæ°ã«å¶é(äŸ:æã«ïŒå)ãèšãããªã©ãå©çšã«éããŠæ§ã
ãªæ¡ä»¶ãèšå®ããããšãã§ããŸãã æžç±ã®è³Œå
¥è£å©ãªã©ç®¡çéšéã«ãã確èªãå¿
èŠãªå¶åºŠããããŸãããæçš¿åã«äžé·ã®ç¢ºèªãå¿
èŠãªå¶åºŠããããŸãããã£ãŠãTUNAGã§ã¯ãæ¿èªçµè·¯ã®èšå®ãæ¿èªç¶æ
ã®ç®¡çãšãã£ãã¯ãŒã¯ãããŒæ©èœãåããŠããŸãã ããã«ãã¯ãŒã¯ãããŒãæäŸããããã«ã¯åãŠãŒã¶ãŒã®æå±ãäžé·ãšãã£ãçµç¹åã³åœ¹è·ã®æ
å ±ãå¿
èŠãšãªããŸããTUNAG ã§ã¯ãäŒç€Ÿå
šäœã®åéšéã®çµç¹å³ãåãŠãŒã¶ã®æå±ãšåœ¹è·ãšãã£ãã人äºãã¹ã¿ãŒã管çããããšãã§ããå¶åºŠã®å©çšèšå®ããæçš¿ã®å
¬éç¯å²ããã£ããã®ã«ãŒã èšå®ãªã©ãçµç¹åäœã§èšå®ããããšãã§ããããã«ãªã£ãŠããŸãã å
±éåºç€ TUNAG ã®ç®çã¯ããšã³ã²ãŒãžã¡ã³ãçµå¶ã®æšé²ã§ãããã£ãŠãåäŒæ¥ã®éå¶æ
åœè
ã«ãšã£ãŠãããããã®ç€Ÿå
å¶åºŠã®å©çšç¶æ³ããåéšçœ²ããŠãŒã¶ã®ãã°ã€ã³ç¶æ³ãªã©ãTUNAG ã®å©çšç¶æ³ãææ¡ããããšã¯éåžžã«å€§åã§ã管çè
ç»é¢ã«ã¯ãã°ã©ãçã®ããã·ã¥ããŒãæ©èœããåçš®CSVãšã¯ã¹ããŒãæ©èœãæäŸãããŠããŸãã ãŸããTUNAG å
šäœã暪æçã«æ€çŽ¢ã§ãã å
šææ€çŽ¢æ©èœã Elasticsearch ãçšããŠæ§ç¯ããŠããŸãã å€éšé£æº ä»èŠçŽ èªèšŒãªã©ã®åŒ·åºãªã»ãã¥ãªã㣠ã ã¢ã«ãŠã³ãã®äžå
管çãªã©ãç®çã«ãGoogle G-Suite ã Microsoft Azure Active Directory ã®ã¢ã«ãŠã³ããçšãã Single Sign-on ãåžæãããã客æ§ãããŸãããã£ãŠãTUNAG ã§ã¯ãSAML 2.0 ã«å¯Ÿå¿ããŠããŸãã ãŸããPCã«ããŠã³ããŒãããããšãªããããŒã¿ããšã¯ã¹ããŒãã§ããããã« Google SpredSheet ãšé£æºããŠãããã ãã€ãã£ãã¢ããªãžPUSHéç¥ãéãããã«ãããã¯ã© mobile backend ãšé£æºããããšãæ§ã
ãªå€éšãµãŒãã¹ã飿ºããæ©èœãå®è£
ããŠããŸãã TUNAG ã® ãã¯ãããžãŒã¹ã¿ã㯠åè¿°ããããã«å€ãã®æ©èœãå®è£
ãã TUNAG ã¯ãã³ãŒãéãå€ãããŸããæ¥ã
ããŒã¿ãèç©ãããµã€ãèŠæš¡ãæ¡å€§ããŠãããããéçºå¹çãä¿å®æ§ãéçšæ§ãéèŠããŠæè¡éžå®ããŠããŸãã 嵿¥æãããCTOã®å°æããŸãšããŠæè¡éžå®ããŸããããããŒã ã倧ãããªã£ãçŸåšã§ã¯ãååéã§å°éçãªç¥èŠãæã£ããšã³ãžãã¢ãååšããåã
ãæ°ããæè¡ãã©ã€ãã©ãªãããŒã ã«ææ¡ããŠãTUNAG ã«åãå
¥ããŠããŸãã æè¡éžå®ã®èããšããŠã¯ãååé/æè¡ã®ãã¹ããã©ã¯ãã£ã¹ãç©æ¥µçã«å°å
¥ããããšã§éçº/éçšãå¹çåãã確ä¿ããæéã§TUNAGã®ã³ã¢éšåã®å®è£
ã«æ³šåããæ¹éããšã£ãŠããŸãã 2019幎2ææç¹ã®äž»ãªãã¯ãããžãŒã¹ã¿ãã¯ãšããŠã¯ãäžèšã®å³ã®ããã«ãªããŸããé ã«è§£èª¬ãããŠãã ããã èšèªã»ãã¬ãŒã ã¯ãŒã¯ TUNAGã® ãµãŒããŒãµã€ãã¯ãRuby on Rails ã§å®è£
ãããã¢ããªã·ãã¯ãªWebã¢ããªã±ãŒã·ã§ã³ãšãªã£ãŠããŸããTUNAG ã®ã³ãŒãéãããã«å€§ãããªããéçºããŒã ãè€æ°ã«ãªã£ãå Žåã¯ãæ©èœåäœã§ãã€ã¯ããµãŒãã¹åããããããããŸããããçŸæç¹ã§ã¯éçºå¹çãéèŠããŠã¢ããªã·ãã¯ã«äœã£ãŠããŸãã ãŸããã¿ã€ã ã©ã€ã³ ã ãã£ãã ãªã© è€éãªUI 㯠React ãçšããŠå®è£
ããŠããŸãããã€ãã£ãã¢ããªã¯ãiOS 㯠Swift ã§å®è£
ãããŠãããAndroid 㯠Java ãã Kotlin ãžç§»è¡äžã§ãã éçºæ¯æŽ ãšã³ãžãã¢ã®æ¥ã
ã¯ãã³ãŒãã£ã³ã°ããããã°ããã¹ãã倧éšåãå ããŸãããã£ãŠãæ¥ã
ã®éçºå¹çã¯éåžžã«éèŠã§ãããã®ãããã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ã¯ãå
šå¡ RubyMine ãçšããŠéçºããŠãããå¿«é©ã« Rails ã React ãæžããŠããŸãã ãŸããGitHub Flow ã§éçºãè¡ã£ãŠãããGitHub ã§ PullRequest ãäœæãããªãªãŒã¹ã«éããŠã¯ãããŒã ã¡ã³ããŒãã¬ãã¥ãŒãå¿
é ã«ããŠããŸãããªãªãŒã¹åã«å¿
ãã¬ãã¥ãŒãè¡ãããšã§ããã°ãæªç¶ã«é²ãããšã¯ãã¡ãããèšèšæ¹éãã³ãŒãã£ã³ã°ãã¯ããã¯ãªã©ã®ç¥èã®å
±æãªã©ãç©æ¥µçã«è¡ã£ãŠããŸãã èªååãç©æ¥µçã«è¡ã£ãŠããããã¹ã ã PullRequest ãããŒãžãããéã® Deploy ãªã©ã¯ãCircleCI ã«ããèªååãããŠããŸããSider ã¯ãrubocop ã«ããã³ãŒãã£ã³ã°èŠçŽã®ãã§ãã¯ã Rails Best Practices ã«ããèªåã¬ãã¥ãŒã«çšããŠããŸãã ãªããéçºã«çšããŠããããŒããŠã§ã¢ã¯ãMacBook Pro 15-inch, Apple Magic Keyboard, Magic Trackpad ããã㯠Magic Mouse ã«ã27ã€ã³ã 4Kã¢ãã¿ãŒãšãªã£ãŠããŸããã¹ã¿ã¡ã³ã®ãªãã£ã¹ã¯æºãåºããè©ãã察çã§ããŒããŒãã2å°äžŠã¹ãŠãããšã³ãžãã¢ãå€ãããããããŒããŒãããããããããªãã£ã¹ã«ãªã£ãŠããŸã(ç¬ ãããžã§ã¯ã管ç ãã¹ããã©ã¯ãã£ã¹ãç©æ¥µæ¡çšããã¹ã¿ã¡ã³ã§ããã瀟å
ã³ãã¥ãã±ãŒã·ã§ã³ã«ãããŠã¯å°ãéããŸãã ã¹ã¿ã¡ã³ã§ã¯ãå€ãã®ITãã³ãã£ãŒãå©çšããŠãã Slack ãå©çšããŠããããTUNAG ã® ãã£ããæ©èœã§æ¥ã
ã®ã³ãã¥ãã±ãŒã·ã§ã³ãè¡ã£ãŠããŸãã ãããžã§ã¯ã管çã¯ãTrello ã«ããã«ã³ãã³æ¹åŒã2é±éåäœã®ã¹ã¯ã©ã ã§è¡ã£ãŠããã DocBase ã«ä»æ§ããã€ã«ã¹ããŒã³ãªã©ãèšèŒããŠç€Ÿå
å
±æãšç¥èã®èç©ãè¡ã£ãŠããŸãã Amazon Web Services (AWS) AWS ã®äœ¿ãæ¹ã¯ãWebã¢ããªã±ãŒã·ã§ã³ã®ãã³ãã¬ãŒãçãªæ§æãšãªã£ãŠããŸããEC2 äžã® Amazon Linux ã§ nginx + puma ã®äžã§ rails ãåãããŠããŸãããããå¹ççãªéçšãç®æããŠå€ã«ãã㊠AWS ECS ãçšããæ§æã«å€æŽããäºå®ã§ãã æè¿ã¯ããã°ã®èç©ãšåæã®åºç€ãæºåäžã§ãKinesis FirehoseãGlueãAthena ãšãã£ããµãŒãã¹ãçšããŠæ§ç¯äžã§ãããããã®ããŒã¿ã¯ãBI Tool ã® Metabase ã§åæããããæ©æ¢°åŠç¿ãçšããã¿ã€ã ã©ã€ã³çã®æé©åãªã©ã«çšããäºå®ã§ãã ãã®ä»ã®ãµãŒãã¹ TUNAG ã§ã¯ãäž»èŠãªéšå㯠AWS ã§éå¶ãããŠããŸãããããã€ãã®åéã§ã¯ãã髿©èœãªã¯ã©ãŠããµãŒãã¹ãå©çšããŠããŸãã ã¢ããªã±ãŒã·ã§ã³ã®ç£èŠãšããã©ãŒãã³ã¹ã»ãã¥ãŒãã³ã°ã¯ CloudWatch ã«å ããŠãNewRelic ãçšããŠããŸãã Bugsnag ã倿§ããŠããŸããRails ã JavaScript ã§ äŸå€ãçºçããå Žåã¯ãBugsnag ã«ãã£ãŠéç¥ãããŸãããäŸå€çºçæ ã® ç°å¢å€æ° ã Stacktrace ãªã©ã®å®è¡ç°å¢ãä¿åãããããå¹ççãªãããã°ã«æ¬ ãããªãããŒã«ãšãªã£ãŠããŸãã ãŸããã¡ãŒã«ã®é
ä¿¡ã«ã¯ SendGridãç»åã®é
ä¿¡ã«ã¯ imgix ãçšããŠããŸãããããã®ãµãŒãã¹ã¯ãã¡ãŒã«ã®ããŠã³ã¹åŠçããç»åã®ãªã¢ã«ã¿ã€ã 倿ãªã©äŸ¿å©ãªæ©èœåããéçºãªãœãŒã¹ã®ç¯çŽã«ã€ãªããã ãã§ãªããè²»çšé¢ã§ãå¹çãè¯ãå©ãã£ãŠããŸãã æåŸã« Cloud Firestore ã§ãããTUNAG ã®ãã£ããæ©èœã®ãã€ãã£ãã¢ããªåãã®ããã¯ãšã³ããšããŠå©çšããŠããŸãããã£ããã¯ãå©çšé »åºŠãé«ãã¹ã±ãŒã©ããªãã£åã³ããã©ãŒãã³ã¹ãéåžžã«å€§åãšãªããŸããFireStore ãçšããããšã§ãAWS ã® S3 ã®ããã« ããã©ãŒãã³ã¹ãšã¹ã±ãŒã©ããªãã£ãããã«ç²åŸã§ãããŸããCloud Firestore ã®ãã€ãã£ãåãSDKã«å«ãŸããããŒã«ã«ãã£ãã·ã¥æ©èœã«ãããããŒã¿éä¿¡éã®åæžãã¬ã¹ãã³ã¹åäžã«ãå¯äžããŠãããä»åŸ Cloud Firestore ã®å©çšé »åºŠã¯åäžãããã§ãã ãŸãšã ãããŸã§èªãã§ãã ãã£ãŠããããšãããããŸãïŒ ä»åãã¹ã¿ã¡ã³ã®ãšã³ãžãã¢ãã¡ãäœã£ãŠãã TUNAG ã«ã€ããŠãæè¡çãªåŽé¢ããã玹ä»ãããŠããã ããŸããã TUNAG ã¯ãSNSããã£ãããã¯ãŒã¯ãããŒãå¶åºŠç®¡ç(ãã©ãŒã ãã«ããŒ) ãšãã£ãæ©èœãåãã å€§èŠæš¡ Webã¢ããªã±ãŒã·ã§ã³ã§ãRails ã®ä»ã«ããReact ãçšããããã³ããšã³ãéçºãSwift/Kotlinã§ã® åããã€ãã£ãã¢ããªã«å ããAWSãäžå¿ãšãã SRE ã«ãåãçµãã§ããŸãã UI/UXãããã©ãŒãã³ã¹ãå®å®æ§ãã¹ã±ãŒã©ããªã㣠㚠課é¡ã¯å±±çãã§ãåœé¢é£œãããã«ãããŸããã ãŸããTUNAG ã®å°å
¥ç€Ÿæ°ã¯å¢ãç¶ããŠãããå©çšããã ããŠãããŠãŒã¶äŒæ¥æ§ããã¯å€ãã®èŠæãšæåŸ
ãé ããŠããŸãããŠãŒã¶ãŒã®çããã®äŒç€Ÿãè¯ãããããšã«è²¢ç®ã§ããã®ã¯ãããããã瀟äŒçæçŸ©ãæããŸãã ãã®ããã«ãã¹ã¿ã¡ã³ã®éçºçŸå Žã¯ãé Œããã仲éãšãæ¯ãããã®ãã課é¡ã«åãåããå€ãã®ãŠãŒã¶ã«äœ¿ããããå€§èŠæš¡ãªã¢ããªã±ãŒã·ã§ã³éçºã«ã没é ã§ããç°å¢ã§ãã ããã ãã§ãããããªãã«å¹žããªããšãªã®ã§ããããã£ãšæè¡çãªãã£ã¬ã³ãžãããŠã¿ãããããã£ãšè¯ããµãŒãã¹ã«ããŠããã£ãšå€ãã®äººã«äœ¿ã£ãŠããã ããããšæã£ãŠãŸããäœããããããããã®é Œããã仲éãéããŠãããã«ãã°ãããéçºããŒã ãäœã£ãŠãããããšæã£ãŠããŸãã ã¡ãã£ãšã§ãèå³ããã£ãŠãã ãã£ããã æ ªåŒäŒç€Ÿã¹ã¿ã¡ã³ã®åéäžèЧ ã芧ããã ãããã²ãå¿åããã ããªãã§ããããã
1. ã¯ããã« æããŸããŠããã§ãšãããããŸãããšã³ãžãã¢ã®ããã¢ãã§ãã 幎æ«ã¯çããããããéããã§ããã§ããããïŒ å¹Žæ«ãç§ã¯å®¶ã§ãã£ããããªããããªãŒããã«ã³ãŒããèªãã§ããŸããã ãªãŒããã«ã³ãŒã ãã®æ¬ã¯ç§ãã¹ã¿ã¡ã³ã«å
¥ã£ãŠããæåã«å§ããããŠèªãã æ¬ã§ãã ã¹ã¿ã¡ã³ããªãŒããã«ã³ãŒããæšå¥šããŠããçç± ã¹ã¿ã¡ã³ã¯ããŒã ã§ã®éçºãåæã«ãªãããã誰ããèªã¿ãããããã«ã³ãŒãã£ã³ã°ããããšã倧äºã«ããŠããŸãããã®ããã人ã«ãã£ãŠèªã¿ãããã®åºæºã«å·®ãã§ãªãããããªãŒããã«ã³ãŒããåèã«ããŠããŸãã ããããŠRubocopãæ¡çšããã³ãŒãã£ã³ã°ã¹ã¿ã€ã«ãçµ±äžããŠããŸãã ãªãŒããã«ã³ãŒããèªã¿ãªãããWebã¢ããªã±ãŒã·ã§ã³ãšã³ãžãã¢ãšããŠã®1幎ç®ãæ¯ãè¿ã£ãã®ã§ãä»åã¯ããã®ïŒå¹Žã§ã³ãŒãã®æžãæ¹ãã©ãå€ãã£ããã玹ä»ããŸãã 2.ãèªã¿ãããã³ãŒããæžã 倿°ã»ã¡ãœããã® åœå ã«æ°ãä»ãã 倿°ã»ã¡ãœããåã§åŠçã®äžèº«ããããããããããããæ¬¡ã®ããšãããŠããŸãã åè©ã®æ±ããæ°ãã€ãã ã¡ãœããåã§äœãèµ·ããã®ã/ã§ããã®ãããããããããããã ä»ã§ã©ããã åœå ããããŠãã調ã¹ã ãããã¯ã/ããŒã å
šäœã§çšèªãçµ±äžãã Facebook ãªã©é¡äŒŒã®ãããã¯ãã®URLã CSS ã®å€æ°ãåèã«ãã æåã®é ã¯åè©ãæèããŠããªãã£ããããåè©ã»åœ¢å®¹è©ãå
¥ãæ··ãã£ãŠããŸããã ïŒæè¿ã§ãã©ã®åèªã«ãããè¿·ãïŒ æšå¹Žã瀟å
ã§è©±é¡ã«ãªã£ãã®ã¯ ghkw ãšãã ã³ãã³ãã©ã€ã³ ããŒã«ã§ãã github äžã®ããŒã¯ãŒããæ€çŽ¢ããå©çšä»¶æ°ãç¥ãããšãã§ããŸããè€æ°ã®ããŒã¯ãŒããåæã«æ€çŽ¢ã§ãããããæ¯èŒããã®ã«äŸ¿å©ã§ãã å®è£
ã®é·ãã¡ãœãããåãã ã¡ãœãããé·ããªããšãã³ãŒããèªã¿ã¥ãããªãããã¹ããè€éã«ãªããŸãã ãŠããããã¹ã ãæžããããç²åºŠã«ã³ãŒãéãä¿ã€ããšã§ãå¯èªæ§ãå¢ããäžå
·åãæžããŸãã ã¡ãœãããåãããããå
·äœçã«ã¯æ¬¡ã®ããšãããŠããŸãã ã©ãããåŠçãè¡ã£ãŠãããç®æ¡æžãã«ããã ç®æ¡æžãã«ããåŠçã®äžã§ãå¥ã¡ãœãããšããŠåãåºãããã®ã¯åããã å¥ã¡ãœããã«ããããšãé£ãããã°ãã¡ãœããã®äžã§æ®µèœã«åããã ããããããšã§ãïŒã€ã®ã¡ãœããã®èªã¿ããããåäžããŸãã ããã§ãåããã¥ããå Žåã¯ã³ã¡ã³ããã€ããŸããæ¬æ¥ã¯ãã³ãŒãã®ã¿ã§åŠçãããããç¶æ
ãäžçªè¯ãã§ãããä»ã®äººãæªæ¥ã®èªåãããããšããäžäœäœãããŠããã ïŒããšãªãåŸããã®ã¯ã³ã¡ã³ããä»ããããã«ããŠããŸãã ã³ã¡ã³ããä»ããéã¯ãèæ¯ãã¡ã³ããã³ã¹æã®æ³šæäºé
ãªã©ãããšã§èªã人ã«äŒãããããšãã·ã³ãã«ã«æžãããã«æ°ãã€ããŠããŸãã æ¡ä»¶åŒããã¹ãããŠãããæ©ãã« return ãã æ¡ä»¶åŒããã¹ãããŠãããšãã³ãŒããèªã¿ã¥ãããªããŸãã if post.present? if post.image? puts ' hoge1 ' else puts ' hoge2 ' end else puts ' hoge3 ' end éå±€ã®æ·±ãæ¡ä»¶åŒã¯ã¹ã¿ãã¯ãå€ããªãããæåã®æ¡ä»¶ã£ãŠäœã ã£ãïŒããšãªããŸãã ä»ã®äººã«ãšã£ãŠãèªã¿ã¥ãããªãã®ã§ãæ©ãã«returnããã®ã¯ãªã¹ã¹ã¡ã§ãã return puts ' hoge3 ' if post.blank? if post.image? puts ' hoge1 ' else puts ' hoge2 ' end 3. Rails ã§ã®èšèšã®èãæ¹ Model ã«å¯ŸããŠã® ããžãã¯ã View ãš Controller ã«æãããªã ã¹ã¿ã¡ã³ã¯ããã¯ãšã³ãã« Ruby on Rails ãæ¡çšããŠããã MVC ã® ã¢ãŒããã¯ã㣠ãããŒã¹ã«ãªããŸãã æã®ã³ãŒããèŠè¿ããš Model ã«ããã¹ãããžãã¯ã Controller ã View ã«åºãŠããŸããã Modelã«ããžãã¯ãå®çŸ©ãããŠããªããšã Controller ã View ã§ã³ãŒããéè€ããå¯èœæ§ããããDRY ( Don't repeat yourselfã®ç¥ãã³ãŒãã®éè€ãé²ãããã°ã©ãã³ã°ã®åå )ããé ããããŸãã View ã§ããããã®ã Model ã®ç¶æ
ã«é¢é£ããæ¡ä»¶åŒã§ãã if @post .present? && @post .image? && ( @post .image.width > 1024 || @post .image.height > 1024 ) # ãµã ãã€ã«è¡šç€ºãã end ããããå Žå㯠Model ã® ã€ã³ã¹ã¿ã³ã¹ ã¡ãœããã«ããŸããView 㯠ã¹ãã çãšPCçãã¡ãŒã«ãªã©ãåããããªå
容ã§è€æ°ã®ãã³ãã¬ãŒããå®è£
ããããšãå€ããModelã«ã¡ãœãããšããŠå®è£
ããããšã§DRYã«ãªããŸãã class Post def has_large_image? image.present? && (image.width > 1024 || image.height > 1024 ) end end Controller ã§ããããã®ãã scope ã䜿ã£ãŠããªããã¿ãŒã³ã§ãã @posts = Post .where( user : current_user, type : %i(image video) ) ãšãããã㪠where å¥ã æã¯ Controller ã«çŽæžãããŠããŸããã ããããæã¯ Model ã« scopeãå®çŸ©ããããšã§ãä»ã®Controller ã View ã§ãåŒã³åºãããšãã§ããŸãã View ãš Controller ã¯ã Model ã«å®çŸ©ããã¡ãœãããæäœéåŒã³åºãã ãã«ããŸããããïŒãã ããæè¿ FatModel ã«ãªãã€ã€ããã®ã§ã Model å
ã®æŽçãå¿
èŠïŒ private ã¡ãœããã§ ã«ãã»ã«å ãã æã¯æ·±ãèããã«å
šãŠ public ã¡ãœããã§å®çŸ©ããŠããã®ã§ãããå€éšããåç
§ããŠã»ãããªãã¡ãœãã㯠private ã¡ãœãããšããŠå®çŸ©ããããã«ãªããŸãããããã«ãã£ãŠå€éšããåŒã°ããªãããšãä¿èšŒããã ãªãã¡ã¯ã¿ãªã³ã° ããããããªããŸãã private ã¡ãœããã¯ããããçšãã public ã¡ãœããã®ãã¹ãã§åäœã確èªããããããprivate ã«ããããšã§ãã¹ããçãããšãã§ããŸãã validation ã§ãšã©ãŒãè¿ã ã€ã³ã¹ã¿ã³ã¹ ãšããŠç¡å¹ãªã®ã§ããã°ã ã€ã³ã¹ã¿ã³ã¹ ã¡ãœããå
ã§ãšã©ãŒãã³ããªã³ã°ããã®ã§ã¯ãªãã validation ãšããŠãšã©ãŒãè¿ããŸãããšã©ãŒã¡ãã»ãŒãžãæ±ãããããã validation ããããã¿ã€ãã³ã°ãèšå®ã§ããŸãã ActiveModel::Validator ãç¶æ¿ãã custom validator ã䜿ãã°ã è€æ°ã®ã¢ãã«ã«å
±éãã validation ã DRY ã«å®è£
ããããšãã§ããŸãã 4. ã¡ã³ããã³ã¹æ§ãé«ãã YAGNI ã ãã©ãæ¡åŒµãããã YAGNI ãšã¯ã"You ain't gonna need it"ã®ç¥ã§ããæ©èœã¯å®éã«å¿
èŠãšãªããŸã§ã¯è¿œå ããªãã®ãããããšãã ãšã¯ã¹ããªãŒã ã»ããã°ã©ãã³ã° ã«ãããååã§ãã æ°ããæ©èœãå®è£
ãããšãããããããæ©èœãä»å ããæ¹ãè¯ãã®ã§ã¯ïŒããšãã話ãåºãŸããèŠæã«æ²¿ã£ãŠä»å æ©èœãå®è£
ãããšããŠãã䜿ãããªããã°æå³ããããŸããããŸããgemãä»ãµãŒãã¹ã§ãããè£ããã®ãåºãŠããå¯èœæ§ããããŸããïŒå®éã« google drive ã® gem ãæ±ã£ããšããäœãéããªããŠããã£ãïŒãšããããšããããŸãããïŒ äœãéãã¯çŠç©ã§ãããäºèŠã§ããèŠæãåŸã§äœããããããããã«èšè𿮵éã«èæ
®ããŠããã®ã¯å€§äºã§ãã DB ã®ããŒãã«æ§æãèããæãªã©ãããšã§ä¿®æ£ããæã«ã³ã¹ããé«ãã€ããã®ã¯æ¡åŒµæ§ãæèããŸãã ã³ãŒãã®å
± éå æ¢åã®æ©èœãšäŒŒããã®ãäœããšãããã®ãŸãŸã³ããŒããŠéšåçã«å€æŽããã ãã§ãå®è£
ã§ãããã®ããããŸããã§ãããããããããšããžãã¯ã®ä¿®æ£ãå¿
èŠã«ãªã£ãéã«ä¿®æ£ç®æãå€ããªããã¡ã³ããã³ã¹æ§ã¯å€§ããäžãããŸãã ã¹ã¿ã¡ã³ã«å
¥ã£ãŠãããäœåºŠãããããå Žé¢ããããŸãããæåã®é ã¯ã³ããŒããŠå®è£
ããããšãå€ãã£ãã§ããïŒã¹ã¿ã¡ã³ã®çããã¿ãŸãã...ãïŒãã¬ãã¥ãŒçã§ææãåãæšå¹Žã®çµããé ããå
± éå ãæèããããã«ãªããŸããã Model ãš Controller ã¯å
±ééšåã Module ã«åãåºãããšã§ãæ¬¡ã«æ©èœã远å ããæã«æ¥œã«ãªããŸãã View ã«ã€ããŠã template ãå
± éå ããŸãã ãã®ç¬éã¯é¢åã ãšæããŸãããåŸã§å
šéšã€ããçŽãããä»ãã£ãæ¹ãè¯ãã§ãã 5. ãããã« ãšã³ãžãã¢ã«ãªã£ãã°ããã®äººã®åèã«ãªãã°ãšæããä»åã®èšäºãæžããŸããã ãããã£ãŠæžã䞊ã¹ããšããŸã ååã«ã§ããŠãªãéšåããããããä»åŸãããè¯ãã³ãŒããæžããããé 匵ãããšæããŸãã2019幎㯠Rails ã®åºç€ãåºãã€ã€ãããã³ããšã³ãã«ãå¹
ãåºããŠãããŸãã ã¹ã¿ã¡ã³ã§ã¯ãšã³ãžãã¢ãåéããŠããŸããããèå³ããããŸããã ãã¡ã ãããé£çµ¡ãã ããã ãªãã£ã¹ã§ã話ãã§ããã®ã楜ãã¿ã«ãåŸ
ã¡ããŠããŸãã