"歴史的経緯の説明 as code" であるところの querly の紹介です。
Code Review Meetup #4 (#codereview4) の発表資料です。
ྺ࢙తܦҢͷઆ໌as codeCode Review Meetup by Sider #42018/09/26Presentation by FUJI Goro (@__gfx__)
View Slide
ࣗݾհ• id:gfx• Bit JouruneyͰKibelaΛ։ൃ͍ͯ͠Δ• Kibela: ίϥϘϨʔγϣϯπʔϧ• ࣾϒϩά & wiki ͱ͍͏SaaS• ࠷ۙͷؔ৺ࣄ DX: Developer Experience
ίʔυϨϏϡʔͱྺ࢙తܦҢ• ίʔυϨϏϡʔΔͱͯ͠ʮྺ࢙తͷઆ໌ʯͬͯ͋Γ·͢ΑͶ• ʮͦͷϝιου·ͩੜ͖ͯ·͕͢ݹ͍ͷͰΘͳ͍Ͱ͍ͩ͘͞ʯ• ʮͦͷgemґଘؔͷ߹্͑ͪΌ͍·͕͢ɺ͕͋ΔͷͰΘͳ͍Ͱ͍ͩ͘͞ʯ• ʮͦͷgemͷ͍ํͰ͕͢ɺϕετϓϥΫςΟε˓˓ͳͷͰै͍ͬͯͩ͘͞ʯ
ྺ࢙తܦҢͷઆ໌ as code ͱ• ྺ࢙తܦҢͷઆ໌͍͍ͩͨύλʔϯ͕͋Δ• ϓϩδΣΫτݻ༗ͷϕετϓϥΫςΟε• ৽چ༷͕ࠞࡏ͍ͯ͠Δաظͷա͝͠ํ• ύλʔϯ͕͋ΔͳΒࣗಈԽ͢Ε͍͍͡Όͳ͍ʂ⇢ “ྺ࢙త(ry as code”
ຊͷςʔϚ: Querlyʢ͑͘Γʙʣ• QuerlyίʔυϨϏϡʔͷࣗಈԽπʔϧͳ͕Βɺ͍ํ͕গ͠Ή͔͍ͣ͠• ͱ͍͏ΑΓɺ “Querly͕ఏڙ͢ΔՁ” ͕Θ͔Γʹ͍͘• ͦ͜ͰʮίʔυϨϏϡʔͷࣗಈԽͷͨΊʹQuerlyΛ͏ʯͱ͍͏͜ͱΛओʹ͢
KibelaͱQuerlyͷ͖߹͍• querly.ymlͷinitial commit 20169݄• ࠷ॳ͍ॴ͕͍·͍ͪཧղͰ͖ͳ͔ͬͨ• ·͡Ίʹӡ༻͠͡Ίͨͷ 20176݄• Querly meetup ʹग़ͯΑ͏͘ཧղ͠͡ΊΔ• 2018ʹͳ͔ͬͯΒසൟʹϧʔϧΛߋ৽͢ΔΑ͏ʹ• 2ோΊͯ “ྺ࢙త(ry as code” ͱ͍͏ཧղʹ౸ୡͨ͠
Querly: ྺ࢙త(ry as code• ύλʔϯԽ͍ͯ͠ΔͷࣗಈԽͰ͖Δ• ύλʔϯϚον⇢ܯࠂϝοηʔδͷग़ྗ ͱ͍͏λεΫΛ࣮ߦ͢Δͷ͕ querly(1)• QuerlyΛpull-requestͷมߋൣғʹݶఆ࣮ͯ͠ߦͯ͘͠ΕΔSaaS͕SiderͰ͢
QuerlyͷϫʔΫϑϩʔ• `querly console [path…]` ͰconsoleΛ։͘• `find $pattern` ͰύλʔϯϚον• querly.yml ʹύλʔϯͱܯࠂจΛՃ• bad pattern͕ͳ͍߹ɺ࣮ࡍʹιʔεʹॻ͍ͯ֬ೝ͢Δ
Demo
Querly DSL• Querlyจࣈྻʹର͢ΔύλʔϯϚονͰͳ͘ɺRuby AST ʹରͯ͠ύλʔϯϚονΛߦ͏• ͜ͷύλʔϯϚονDSLͳͷͰֶश͕ඞཁ• ͦ͜Ͱquerly consoleͰࢼߦࡨޡ͢Δ
Querly DSL example (1)• foo• “foo” ͱ͍͏ϝιουݺͼग़͠• Ҿҙ• มจࣈྻɺγϯϘϧؚ·ͳ͍
Querly DSL example (2)• _• ҙͷࣜʹϚον• foo(_) ͩͱʮҙͷҾΛ1͚ͭͩ༩͑Δϝιουݺͼग़͠ʯͱ͍͏ҙຯ
Querly DSL example (3)• …• ҙͷҾϦετʹϚον• foo(…) foo ͱಉ͡ɺͨͩ͠ෳࡶͳύλʔϯΛॻ͘ͱ͖…ͷ໌͕ࣔඞཁͳ͜ͱ͕͋Δ
Querly DSL example (4)• foo(…){}• blockΛͱΔfooͷݺͼग़͠ʹϚον• …ඞਢ• foo(…)!{}• blockΛͱΒͳ͍fooͷݺͼग़͠ʹϚον• …ඞਢ
Querly DSL examples (5)• [conditional], [!conditional]• `save [conditiona]` Ͱʮ#save Λ݅ࣜͰධՁ͍ͯ͠Δͱ͖ʯʹϚον• `save [!conditional]` Ͱʮ#save Λ݅ࣜͰධՁ͍ͯ͠ͳ͍ͱ͖ʯʹϚον
Querly DSL syntax• ৄࡉϚχϡΞϧࢀরͷ͜ͱ• https://github.com/soutaro/querly/blob/master/manual/patterns.md
ࣄྫ from kibela/querly.yml
ϝλϓϩͷ੍- id: sample.metaprogramming_abusepattern:- classify- constantize- eval- instance_values- safe_constantizemessage: "本当にメタプログラミングが必要か3回考えてください。"※ 3ճߟ͑ͯҊ͕ͳ͍ͳΒͬͯΑ͍ɻͦ͏͍͑send ܥ͜͜ʹՃ͍͑ͨɻ
migration ࣌ͷΧϥϜͷআ- id: kibela.remove_columnpattern:- "remove_column"- "remove_reference"message: |カラムを削除する前に、該当カラムを使わないようにした上で `ignore_colums` で無視するようにしてください。参考文献: Rails アプリでオンラインでカラムの削除やリネームを行うには - eagletmt's blog http://eagletmt.hateblo.jp/entry/2017/09/24/004709
ARͷenumͷ͍ํ- id: kibela.user_rolespattern: "User.roles[:symbol:]"message: "User.roles[:member] は多くの場合で必要ありません。たとえばupdateやwhereではenum symbolを使えます。"※ ੲARͷenumͷ͍ํ͕Θ͔ͬͯͳ͔ͬͨͷͰɻ༻Օॴ͕ଟ͍͏͑ʹࣗಈͰஔ͖͑ΒΕΔ΄ͲͰͳ͍ͷͰաظҙשى͚ͩʹ͢Δ
localeͷࢀরͷ͔ͨ͠- id: kibela.current_user_localepattern: "current_user.locale"message: "current_userはログインしていない場合nilになります。 I18n.localeを使ってください。"
Raw SQLͷҙשى- id: kibela.order_by_stringpattern:- "order(:dstr:)"- "where(:dstr:)"- "find(:dstr:)"- "exists?(:dstr:)"message: "文字列によるSQL構築は本当に必要ですか? SQL Injection を引き起こさないように気をつけてください。"
block.callΛ͔͍ͭ·͠ΐ͏- id: kibela.block_callpattern:- "yield"message: "yieldではなくblock.callを使いましょう。そのほうが渡す引数が明確になります。"
developmentͰͷΈଘࡏ͢Δϝιουͷҙשى- id: kibela.yard_class_namepattern: "class_name()"message: "Class#class_name は yard gem による拡張なのでproductionでは使えません。必要なのはビルトインメソッドの Class#name ではないですか。"
graphql-ruby ͷϕετϓϥΫςΟε- id: kibela.connection_type_without_resolverpattern: "field(:symbol:,_.connection_type, ..., !resolve: _, ...)"message: "Relay connection に resolver が設定されていません。 AR::Relation に対するconnectionはresolver でソートを指定すべきです。”※ʮ field(:notes, Note.connection_type) Ͱ resolver option ͕ͳ͍ύλʔϯʯͱಡΉɻසൃ͢Δϛεͳ͕Βͱͱsortͯ͋͠Δ͜ͱ͋ΔͷͰgraphql-rubyຊମͰαϙʔτ͖͢Ͱͳ͍
چػೳΛͭͭ͠ҠߦΛଅ͢- id: kibela.accessible_forpattern: "accessible_for(_)"message: "accessible_for(user) は古いメソッドです。 readable_by(user) または manageable_by(user)を使ってください"justification: “互換性の確認のためtestでは一部残っています。余裕があれば新しいメソッドに書き換えてください"※ Kibela ACL v1 ͔Β ACL v2 ʹҠߦ͢Δʹ͋ͨͬͯΞΫηεݖݶܥΛΨόͬͱม͑Δʹ͋ͨΓɺʮچϝιουҰԠ͕͢৽نίʔυͰ༻ېࢭʯͱ͍͏͜ͱʹ͔ͨͬͨ͠
FAQ
Linter ͱԿ͕ҧ͏ʁ• linter ʮҰൠతͳϧʔϧʯͷνΣοΫ• ͲͷϓϩδΣΫτɾͲͷϨϏϡΞʔͰಉ͡ࢦఠΛ͢ΔͳΒlinterͷruleΛͭ͘Δ͖• QuerlyʮϓϩδΣΫτݻ༗ͷϧʔϧʯͷνΣοΫ• ଐਓԽ͕ͪ͠ͳʮྺ࢙తܦҢʯΛίʔυԽ͢Δͷ
ࢥͬͨͱ͓ΓʹϚον͠ͳ͍Αʁ• TwitterͰ࡞ऀʹฉ͘ͷ͕ૣ͍Ͱ͢• ⇢ @soutaro• লུͰ͖ͦ͏ͳύλʔϯΛলུͰ͖ͳ͍͜ͱ͕͋Γ·͢ (e.g. ○: `foo(…){}` , ×: `foo{}`)
ޡݕ͕ଟ͗͢ΔΜͰ͕͢ʁ• Rubyʹܕ͕ͳ͍ͷͰ͠ΐ͏͕ͳ͍ΜͰ͢• ޡݕ͕ଟ͍ͱ͍͏͜ͱϝιουͷ໋໊نଇʹҰ؏ੑ͕ͳ͍ͷ͔͠Ε·ͤΜɻ໋໊نଇΛݟ͢ͳͲͯ͠Έ·͠ΐ͏• e.g. ಠࣗʹఆٛ͢Δ update / update! ͳͲARͱৼΔ͍ΛҰகͤ͞Δ
ଞࣾͷࣄྫΛͬͱΓ͍ͨ• Θ͔Δ• ઃఆϓϩδΣΫτݻ༗ͳͷͰҰൠతʹ͢ΔͷͰͳ͍• ͱ͍͑ൺֱతҰൠతͳϧʔϧͯ͠Αͦ͞͏• `querly init` ͱ͔ͰͦΕͳΓʹ͑ΔϨϕϧͷquerly.yml ͕ੜ͞Εͯ΄͍͠ؾ͢Δ
That’s it.