Project automation for internal affairs

023b04c98f39cc041293d780352432ff?s=47 Koichi ITO
November 03, 2019

Project automation for internal affairs

023b04c98f39cc041293d780352432ff?s=128

Koichi ITO

November 03, 2019
Tweet

Transcript

  1.  4VO ۀ຿ͰʂRubyΛʂ
 ΩϝΔʂ ෋ࢁ3VCZձٞ ˏ෋ࢁࠃࡍձٞ৔େखொϑΥʔϥϜ Active Record, CSV, and

    RuboCop ҏ౻ߒҰ&4. *OD
  2. !LPJD w 3VCP$PQίϛολʔ w "30SBDMFFOIBODFE BEBQUFSίϛολʔ w Ӭ࿨γεςϜϚωδϝϯτ
 ίϛϡχςΟϚωʔδϟ

  3. None
  4. None
  5. #VOEMFS 1SZ 34QFD 3VCP$PQ CZFCVH 3VCZ,BJHJ %3&$0.͞Μϒʔεʹͯ

  6. None
  7. IUUQTIFJTFJSCHJUIVCJPLBJHJ

  8. IUUQTIFJTFJSCHJUIVCJPLBJHJ

  9. None
  10. 8&"3&)*3*/( ͷ஥ؒͨͪ ӡӦ -5 -5 ࢀՃऀ ࢀՃऀ ෋ࢁ3VCZձٞʹू·ͬͨ ߨԋ

  11. 044ίϛϡχςΟͱͷڞੜΛࢧԉ Support coexistence with OSS community

  12. ࠓ೔ͷ࿩

  13. ୡਓϓϩάϥϚʔ΁ͷجૅٕज़ ςεςΟϯά %FWFMPQFS5FTUJOH ࣗಈԽ 1SPKFDU"VUPNBUJPO όʔδϣϯ؅ཧ 7FSTJPO$POUSPM

  14. ୡਓϓϩάϥϚʔ΁ͷجૅٕज़ ςεςΟϯά %FWFMPQFS5FTUJOH ࣗಈԽ 1SPKFDU"VUPNBUJPO όʔδϣϯ؅ཧ 7FSTJPO$POUSPM

  15. ͜ͷߨԋͰಘΒΕΔ͔΋͠Εͳ͍͜ͱ w 伱͋Β͹3VCZΛ࢖͏ߟ͑ํͱ͍͏ ͔ࣥ೦Έ͍ͨͳ΋ͷ w ;ͩΜͷΞϓϦέʔγϣϯ։ൃͰ͸ ͋·Γ࢖ͬͯͳ͍͔΋͠Εͳ͍"1* ͷ஌ࣝ w 3VCP$PQͷअಓͳ࢖͍ํ

  16.  4VO ۀ຿ͰʂRubyΛʂ
 ΩϝΔʂ ෋ࢁ3VCZձٞ ˏ෋ࢁࠃࡍձٞ৔େखொϑΥʔϥϜ Active Record, CSV, and

    RuboCop ,PJDIJ*50&4. *OD
  17. ͝ଘ஌ʜͰ͔͢ʁ ۀ຿ͱ&YDFM͸ ͣͬͱ༑ୡͩΑʜ

  18. ػೳ࣮૷Ͱͳ͍ۀ຿͓୊ྫ w σʔλϕʔεεΩʔϚͷҰཡͷ &YDFM͕ཉ͍͠ w ಛఆͷҙຯ߹͍ͷ͋ΔΧϥϜʹ͸
 ϚʔΩϯά͕ཉ͍͠ w TDIFNBSC΍%%-ͷϑΥʔϚοτ Ͱ͸ͳ͘ॴఆͷ&YDFMϑΥʔϚοτ

    Ͱཉ͍͠ w ࠓिதʹཉ͍͠
  19. ਅ࣮ɿ༏Εͨιϑτ΢ΤΞɾ ΤϯδχΞϦϯάʹԊͬͯϓϩ άϥϜΛ։ൃ͢Δͱɺอक͸ݮ Βͣɺ͔͑ͬͯ૿͑Δɻ ιϑτ΢ΣΞΤϯδχΞϦϯά ιϑτ΢ΣΞ։ൃͷਅ࣮ͱͷ΢ι ϩόʔτɾ-ɾάϥε

  20. ੵΈॏͳΔσʔλϕʔεεΩʔϚ w ੵΈॏͳΔεΩʔϚఆٛʹΑͬͯɺ
 ςʔϒϧҎ্ ΧϥϜ Ҏ্ w ͜ΕΛॴఆͷϑΥʔϚοτʹͯ͠ &YDFMఏग़

  21. ৬ਓͷஆ͔ͳख࡞ۀ͸٧Ή w ॻ͘ͷ΋ϨϏϡʔ͢Δͷ΋͠ΜͲ͍ w Ծʹ෼Ͱ࡞੒ͯ͠ɺਓ͕෼͔͚ͯ
 ϨϏϡʔ͢Δͱ෼ͷूதྗ͕ඞཁ w ϚϯύϫʔۀͰർΕͯͦͷ೔͕ऴΘΔ

  22. None
  23. "XBZPG&4. *OD

  24. Ͳ͏͢Ε͹ಘҙྖҬʹͳΔ͔ߟ͑Δ w ΫϥΠΞϯτ͕ཉ͍͠΋ͷ͸ɺ7JFX ࠐΈͷ&YDFMγʔτͳͷ͔ʁ&YDFM γʔτʹ૊ΈࠐΉσʔλͳͷ͔ʁ w ϚΫϩ͕૊·Ε֦ͨுࢠYMTNͰ͋Ε ͹ͱ΋͔͘ɺϚΫϩͳ͠Ͱ͋Ε͹߲໨ ॱʹྻڍͨ͠$47Ͱௐ੔ΛࢼΈͯΈΔ w

    ຊฤʹొ৔͠ͳ͍͕ཁ݅࣍ୈͰ SVCZ9-΍TQSFBETIFFUͱ͍ͬͨ HFN͕ొ৔
  25. &YDFM͸$47ͷϥούʔ ͷͱ͖͕͋Δ

  26. ୡਓϓϩάϥϚʔ΁ͷجૅٕज़ ςεςΟϯά %FWFMPQFS5FTUJOH ࣗಈԽ 1SPKFDU"VUPNBUJPO όʔδϣϯ؅ཧ 7FSTJPO$POUSPM

  27. $47ੜ੒εΫϦϓτΛ࡞Δ w 3FWJFXBCMF ϨϏϡʔՄೳ  w 3VOOBCMF ࣮ߦՄೳ  w

    3FQFBUBCMF ࠶࣮ߦՄೳ  w 3FQSPEVDJCMF ࠶ݱՄೳ  w 'SPN(VUTUP'VO
  28. ࿩ͷத֩

  29. ֯୩ޠ࿥

  30. None
  31. ίʔυΛॻ͘લʹු͔ΜͩΞΠσΞ w "DUJWF3FDPSEΛ࢖ͬͯ
 σʔλϕʔεεΩʔϚ৘ใΛݩʹ $47Λ࡞੒͢Δ w 3VCP$PQΛ࢖ͬͯ
 ECTDIFNBSCΛݩʹ
 $47Λ࡞੒͢Δ w

    ͙͢ු͔Μͩͷ͸͜ͷೋ୒
  32. ݁࿦Λઌʹ w "DUJWF3FDPSEͰ͸ͳ͘ 3VCP$PQΛ࢖࣮ͬͨ૷Λͨ͠ w ECTDIFNBSC͸σʔλϕʔεͱ ಉظͯ͠ϦϙδτϦʹೖΕ͍ͯΔ w σʔλϕʔεʹܨ͛ͣʹ࣮ߦͰ͖Δ w

    ΫϦςΟΧϧͳ΋ͷͰ͸ͳ͍ͷͰɺ "45ϓϩάϥϛϯάͷՄೳੑΛࢼ͠ ͯΈΑ͏ͱࢥͬͨ
  33. ͋ͱ͔Βࢥ͑͹TFEίϚϯυ Λ࢖͑͹ྑ͔ͬͨΑ͏ͳؾ͕ ͢ΔͷͰ͕͢ɺͦΕͩͱ࿩͕ ऴΘͬͯ͠·͏ͷͰࠓ೔ͷͱ ͜Ζ͸3VCZͰͻͱͭ ݁࿦Λઌʹ

  34. "DUJWF3FDPSEͰͷ࣮૷Ҋ w ίʔυʹ͠ͳ͔ͬͨҊ͕ͩͲ͏͍ͬͨ͜ͱΛߟ ͍͔͑ͯͨ౰࣌ͷࢥߟΛμϯϓ͢Δ w ࠷ॳͷൃ૝Ͱɺաڈʹ࡞ͬͨ(FNͷ࣮૷Λݩ ʹߟ͑ͯΈ࣮ͨ૷Ҋ

  35. None
  36. 4DSFBNFSTHFN w ೥͘Β͍લʹ࡞ͬͨΧϥϜܕҠߦͷ HFN w 3BJMTͰͷ0SBDMFͰ%"5&ܕ ͔Β5*.&45".1ܕʹมߋ͞Εͨͱ ͖ʹ࡞ͬͨ w 3BJMTͰJEΧϥϜ͕JOUFHFSܕ͔Β


    CJHJOUܕʹͳͬͨέʔεʹ΋࢖͑Δ͔΋ w 0SBDMF͸/6.#&3ܕͳͷͰෆ࢖༻
  37. શ෦Λ஌Δඞཁ͸ͳͯ͘ $47Λߏ੒͢Δςʔϒϧ ໊ͱΧϥϜ໊ΛಘΔ෦෼ ΛݟͯΈΔ ໨త͸ςʔϒϧ໊ͱΧϥϜ໊ΛಘΔ͜ͱ

  38. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC $PSF

  39. ղઆ

  40. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC AR::Base.connection.tables Ͱςʔϒϧ໊ͷҰཡΛऔಘ͢Δ

  41. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC ςʔϒϧ໊Λeach_with_object ͢Δ

  42. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC "table_names".classifyͰ
 Ϋϥε໊ͷจࣈྻΛऔಘ͢Δ

  43. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC Module.const_get(class_name) ͰΫϥε໊ͷఆ਺Λऔಘ͢Δ

  44. IUUQTHJUIVCDPNLPJDTDSFBNFSTCMPCWMJCTDSFBNFSTDPMVNO@DPMMFDUPSSC Model.columns.map(&:name)
 ͰΧϥϜ໊ͷҰཡΛऔಘ͢Δ

  45. $47Λ૊ΈཱͯΒΕΔ
 ؾ͕͠·͢ΑͶʁ

  46. ͋ͱ͸खΛಈ͔͚ͩ͢ ͳͷͰɺ͜ͷ͋ͨΓͰ ଞͷҊΛߟ͑ͯΈΔ

  47. 3VCP$PQͰͷ࣮૷Ҋ w ೔ࠒϝϯςφϯε͍ͯ͠Δ044ͷ஌ݟΛݩʹ ߟ͑ͯΈ࣮ͨ૷Ҋ w 3VCP$PQͷ"45΁ͷΠϕϯτυϦϒϯΛ
 ར༻ͨ͠3VCP$PQͷࣼΊ্ͷ࢖͍ํ
 अಓฤ

  48. ਖ਼ಓͱअಓ w ਖ਼ಓͱͳΔ3VCP$PQͷ࢖͍ํ͸"45ΛḷΓ ͭͭɺॴఆͷϧʔϧʹԊͬͨίʔσΟϯά͕͞ Ε͍ͯͳ͚Ε͹ܯࠂΛొ࿥͢Δ"1*Λݺͼग़͢ w ࠓճͷअಓฤͰ͸"45ΛḷΓͭͭॴఆͷϊʔ υͱͦͷ஋Λݕग़ͨ͠ΒɺϑΝΠϧγεςϜ΁ ͷॻ͖ࠐΈͱ͍͏෭࡞༻Λ࢖ͬͨࣼΊ্ͷ׆༻

  49. $47Λߏ੒͢Δ ςʔϒϧ໊ͱΧϥϜ໊ ΛಘΑ͏ ॳ৺๨Δ΂͔Βͣ

  50. %BUB 4USVDUVSFT "MHPSJUINT

  51. %BUB 4USVDUVSFT

  52. σʔλߏ଄ΛಘΔ w σʔλߏ଄ʹର͢ΔΞϧΰϦζϜΛ ߟ͑Δॳखͱͯ͠ɺ
 ECTDIFNBSCͷ"45ΛಘΔ w "45ͷσʔλߏ଄Λݟͯɺςʔϒϧ ໊ͱΧϥϜ໊͕Ͳͷϊʔυʹଘࡏ͠ ͍ͯΔ͔֬ೝ͢Δ

  53. ·ͣECTDIFNBSC Λύʔε͠·͢ % ruby-parse db/schema.rb

  54. %&.0

  55. ECTDIFNBSC ActiveRecord::Schema.define( version: 2018_11_23_153926 ) do create_table "articles", force: :cascade

    do |t| t.integer "author_id", null: false t.string "title", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end end
  56. ECTDIFNBSCͷ"45 s(:block, s(:send, s(:const, s(:const, nil, :ActiveRecord), :Schema), :define, s(:hash,

    s(:pair, s(:sym, :version), s(:int, 20181123153926)))), s(:args), s(:block, s(:send, nil, :create_table, s(:str, "articles"), s(:hash, s(:pair, s(:sym, :force), s(:sym, :cascade)))), s(:args, s(:arg, :t)), s(:begin, s(:send, s(:lvar, :t), :integer, s(:str, "author_id"), s(:hash, s(:pair, s(:sym, :null), s(:false)))), s(:send, s(:lvar, :t), :string, s(:str, "title"), s(:hash, s(:pair, s(:sym, :null), s(:false)))), s(:send, s(:lvar, :t), :datetime, s(:str, "created_at"), s(:hash, s(:pair, s(:sym, :null), s(:false)))), s(:send, s(:lvar, :t), :datetime, s(:str, "updated_at"), s(:hash, s(:pair, s(:sym, :null), s(:false)))))))
  57. ղઆ

  58. "DUJWF3FDPSE4DIFNBEFpOF WFSTJPO@@@ s(:block, s(:send, s(:const, s(:const, nil, :ActiveRecord), :Schema ),

    :define, s(:hash, s(:pair, s(:sym, :version), s(:int, 20181123153926)))), s(:args),
  59. DSFBUF@UBCMFBSUJDMFT  GPSDFDBTDBEFEPcUc s(:block, s(:send, nil, :create_table, s(:str, "articles"), s(:hash,

    s(:pair, s(:sym, :force), s(:sym, :cascade)))), s(:args, s(:arg, :t)), CMPDLΛϒϩοΫҾ਺Uͱ Ұॹʹड͚औΔ DSFBUF@UBCMFϝιουΛ ఆٛͨ͠CMPDLϊʔυ )FSF
  60. UJOUFHFSBVUIPS@JE OVMM GBMTF s(:begin, s(:send, s(:lvar, :t), :integer, s(:str, "author_id"),

    s(:hash, s(:pair, s(:sym, :null), s(:false)))), UJOUFHFS UTUSJOH ͱ͍ͬͨΧϥϜఆٛͷTFOE ϊʔυΛଋͶͨCFHJOϊʔυ )FSF
  61. UTUSJOHUJUMF OVMMGBMTF s(:send, s(:lvar, :t), :string, s(:str, "title"), s(:hash, s(:pair,

    s(:sym, :null), s(:false)))), ͜ΕΒϩʔΧϧม਺U΁ͷ ϝιουݺͼग़͠Λද͢ TFOEϊʔυ͕ଓ͘
  62. ECTDIFNBSC ࠶ܝ ActiveRecord::Schema.define( version: 2018_11_23_153926 ) do create_table "articles", force:

    :cascade do |t| t.integer "author_id", null: false t.string "title", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false end end
  63. % ruby_ast_visualizer db/schema.rb

  64. "MHPSJUINT

  65. SEQBSUZDPQΛ࣮૷

  66. w 3VCP$PQʹ͸3VCP$PQࣗମͷ ಺෦ϧʔϧΛऔΓక·Δ෦ॺ͕͋Δ JOUFSOBMBGGBJSTʜ಺੓ɺࠃ಺ࣄ৘ ͲͷσΟϨΫτϦʹ഑ஔ͢Δʁ )FSF

  67. ͲͷσΟϨΫτϦʹ഑ஔ͢Δʁ w 3VCP$PQࣗମͷ಺෦ϧʔϧʹ͍ͭ ͯऔΓక·Δ෦ॺ໊Λഈआͯ͠Έͨ w 3BJMTΞϓϦͰ΋ಉ༷ʹ3BJMTSPPU ௚ԼͷMJCσΟϨΫτϦʹ഑ஔ͍ͯ͠ Δ w ڞ༗ϥΠϒϥϦͱͯ͠நग़ͯ͠ଞϓϩ

    δΣΫτͰͷར༻΋ݕ౼Ͱ͖Δ͜ͱ͔ ΒMJC͸ѱ͘ͳ͍બ୒ͩͱࢥ͍ͬͯΔ
  68. $PQΫϥεΛܧঝ͢Δ module RuboCop module Cop module InternalAffairs class SchemaToCsv <

    Cop end end end end HFNSVCPDPQ
 ͳͲͰ3VCP$PQΠϯεί ͞ΕͯΕ͹࢖͑Δ"1*
  69. PO@YYYͰର৅ϊʔυΛϋϯυϦϯά module RuboCop module Cop module InternalAffairs class SchemaToCsv <

    Cop def on_block(node) end end end end end
  70. DSFBUF@UBCMFϝιουͷΈର৅ def on_block(node) table = node.children.first # Get table node

    return if table.method_name == :create_table
  71. DSFBUF@UBCMFϝιουͷΈର৅ def on_block(node) table = node.children.first return unless table.method_name ==

    :create_table Ұக
  72. DSFBUF@UBCMFϝιουͷΈର৅ def on_block(node) table = node.children.first return unless table.method_name ==

    :create_table Ұக ෆҰக
  73. ςʔϒϧ໊Λऔಘ͢Δ def on_block(node) table = node.children.first return unless table.method_name ==

    :create_table table_name = table.first_argument.str_content
  74. ΧϥϜϊʔυΛऔಘ͢Δ def on_block(node) table = node.children.first return unless table.method_name ==

    :create_table table_name = table.first_argument.str_content columns = node.children.detect(&:begin_type?)
  75. ΧϥϜϊʔυΛऔಘ͢Δ def on_block(node) table = node.children.first return unless table.method_name ==

    :create_table table_name = table.first_argument.str_content columns = node.children.detect(&:begin_type?) UJOUFHFS UTUSJOH ͱ͍ͬͨΧϥϜఆٛͷTFOE ϊʔυΛଋͶͨCFHJOϊʔυ )FSF
  76. $47Λ։͘ columns = node.children.detect(&:begin_type?) CSV.open('tmp/schema.csv', 'a') do |csv| end

  77. ΧϥϜϊʔυΛFBDI͢Δ columns = node.children.detect(&:begin_type?) CSV.open('tmp/schema.csv', 'a') do |csv| columns.child_nodes.each do

    |column| end end
  78. JOEFYͩͬͨΒεΩοϓ͢Δ columns = node.children.detect(&:begin_type?) CSV.open('tmp/schema.csv', 'a') do |csv| columns.child_nodes.each do

    |column| next if column.method_name == :index end end
  79. ΧϥϜ໊ΛಘΔ columns = node.children.detect(&:begin_type?) CSV.open('tmp/schema.csv', 'a') do |csv| columns.child_nodes.each do

    |column| next if column.method_name == :index column_name = column.first_argument.str_content end end
  80. ͋ͱ͸$47ʹ͍͍ײ͡ʹग़ྗ͢Δ columns = node.children.detect(&:begin_type?) CSV.open('tmp/schema.csv', 'a') do |csv| columns.child_nodes.each do

    |column| next if column.method_name == :index column_name = column.first_argument.str_content write_csv_row(csv, table_name, column_name) end end
  81. $47Λ૊ΈཱͯΒΕΔ
 ؾ͕͠·͢ΑͶʁ

  82. w SFRVJSFͱPOMZΦϓγϣϯ % bundle exec rubocop \ --cache false --require

    ./lib/rubocop/cop/ internal_affairs \ --only InternalAffairs/ SchemaToCsv \ db/schema.rb > /dev/null ͓·͚ ࣮ߦͷ౾஌ࣝ
  83. w ීஈ͸ࢦఆෆཁͳDBDIFΦϓγϣϯ % bundle exec rubocop \ --cache false --require

    ./lib/rubocop/cop/ internal_affairs \ --only InternalAffairs/ SchemaToCsv \ db/schema.rb > /dev/null ͓·͚ ࣮ߦͷ౾஌ࣝ --cache falseʹ͠ͳ͍ͱҰ౓ ղੳͨ͠db/schema.rbʹ͍ͭͯ ͸Ωϟογϡ͞Εͯॲཧ͞Εͳ͍ (΍͍ͬͯΔ͜ͱ͕अಓͳূ͔΋)
  84. ෣୆ཪͱͦͷޙ w JEΧϥϜ͸ग़ྗ͞Ε͍ͯͳ͍ͷͰɺ DSFBUF@UBCMFUBCMF@OBNF  JEGBMTFҎ֎͸ରԠ͓ͯ͘͠ඞཁ ͕͋Δ w ࣗಈԽੜ੒ͨ͠ϑΝΠϧʹɺखಈม ߋΛೖΕΔͱࣗಈԽෆೳʹͳΔͷͰ

    ͦͷ͋ͨΓ͸ؔ܎ऀͱཁௐ੔
  85. ۀ຿Ͱʂ3VCZΛʂΩϝΔʂ w 伱͋Β͹3VCZΛΩϝΔਫ਼ਆ w ࣗ෼ͷ࢓ࣄΛ଀Ήʹ͸ਓੜ͸༨Γʹ ΋୹͍ w ΦϨͬͯ͹͛͢ʔײΛಘΔ w 3VCZΛΩϝΔͱؾ͍͍࣋ͪ

    w 伱͋Β͹3VCZΛΩϝΔਫ਼ਆ
  86. ࣗΒͷٕज़ʹؔ৺Λ࣋ͭ͜ͱ ͋ͳͨͷ࢓ࣄʹ͍ͭͯߟ͑Δ͜ͱʂ ܅͸ֶͿ͜ͱ͕৺͔Β޷͖ͩ ܅͸ιϑτ΢ΣΞͷ͜ͱΛେ੾ʹࢥ͍ͬͯΔ ΞδϟΠϧαϜϥΠ "HJMF4BNVSBJ +POBUIBO3BTNVTTPO ୡਓϓϩάϥϚʔ 5IF1SBHNBUJD1SPHSBNNFS %BWJE5IPNBT

    "OESFX)VOU ࠜݯͷӔ Stairway to the pragmatic programmer
  87. 4UBZIVOHSZ  4UBZGPPMJTI ʵ4UFWF+PCT