Slide 1

Slide 1 text

読みやすいコード 表参道.rb #53 2019/12/05(Thu) 1

Slide 2

Slide 2 text

自己紹介 名前: 神速 会社: メドピア株式会社 GitHub: @sinsoku (アイコン右上) Twitter: @sinsoku_listy (アイコン右下) 2

Slide 3

Slide 3 text

コードを読みやすく書きたい 3

Slide 4

Slide 4 text

読みやすいコードとは何か 4

Slide 5

Slide 5 text

考えてみた • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 5

Slide 6

Slide 6 text

メソッド呼び出しのネストが深い例 def index set_user end private def set_user user_name = get_user_name_from_api @user = User.find_or_initialize(user_name: user_name) end def get_user_name_from_api api = Sugoi::API.new(token: ENV['SUGOI_TOKEN']) user_name = api.get_user_name convert(user_name) end def convert(user_name) user_name.gsub('-', '_') end 6

Slide 7

Slide 7 text

浅くした例 def index user_name = api.get_user_name converted_user_name = user_name.gsub('-', '_') @user = User.find_or_initialize(user_name: converted_user_name) end private def api @api ||= Sugoi::API.new(token: ENV['SUGOI_TOKEN']) end 7

Slide 8

Slide 8 text

話を戻して... 8

Slide 9

Slide 9 text

読みやすいコードの条件(主観) • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 9

Slide 10

Slide 10 text

RuboCopでだいたい検出できる • Metrics/AbcSize • Metrics/ClassLength: • Metrics/MethodLength 10

Slide 11

Slide 11 text

これで読みやすいコードになる! 11

Slide 12

Slide 12 text

しかし、現実は... # rubocop:disable Metrics/AbcSize Metrics/MethodLength def index # rubocop:enable Metrics/AbcSize Metrics/MethodLength set_user set_blogs if params[:new] @users = User.where(foo: params[:foo]) .foo .bar .buz elsif params[:create] @users = User.where(foo: params[:foo]).piyo else # ർΕͨͷͰུ end end 12

Slide 13

Slide 13 text

Metrics/AbcSize を下げるテクニック 1. 変数を定義しない 2. Array(Enumerable)のメソッドを駆使する 3. クラスを抽出する 13

Slide 14

Slide 14 text

1. 変数を定義しない # bad def count_blogs(users) result = {} users.each do |user| next unless user.active? result[user.id] = uesr.blogs.size end result end 14

Slide 15

Slide 15 text

1. 変数を定義しない # good def count_blogs(users) {}.tap do |result| users.each do |user| next unless user.active? result[user.id] = uesr.blogs.size end end end 15

Slide 16

Slide 16 text

2. Array(Enumerable)のメソッドを駆使する • if/unlessよりselect/reject # good def count_blogs(users) # active_users = users.select { |user| user.active? } ͱಉ͡ active_users = users.select(&:active?) {}.tap do |result| active_users.each do |user| result[user.id] = uesr.blogs.size end end end 16

Slide 17

Slide 17 text

2. Array(Enumerable)のメソッドを駆使する • このケースは to_h で良い # good def count_blogs(users) users.select(&:active).to_h { |user| [user.id, user.blogs.size] } # ruby 2.5 ͩͱ͜Ε # users.select(&:active).map { |user| [user.id, user.blogs.size] }.to_h end 17

Slide 18

Slide 18 text

2. Array(Enumerable)のメソッドを駆使する • eachで範囲を-1/+1する必要はない (1..10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] (1...10).to_a #=> [1, 2, 3, 4, 5, 6, 7, 8, 9] [:a, :b, :c].each.with_index(1).to_a #=> [[:a, 1], [:b, 2], [:c, 3]] 18

Slide 19

Slide 19 text

2. Array(Enumerable)のメソッドを駆使する • group_by と transform_values users_per_status = User.find_each.group_by(&:status) #=> { "pending" => [, ...], "active" => [, ... } users_per_status.transform_values { |users| users.map(&:user_name) } #=> { "pending" => ["sinsoku", ...], "active" => ["yuki3738", ... } 19

Slide 20

Slide 20 text

3. クラスを抽出する # app/models/user/ranking.rb class User class Ranking def initialize(user) @user = user end end def rank score = calculate_score # ུ end private def calculate_score # ུ end end 20

Slide 21

Slide 21 text

モジュールにするのは # app/models/concerns/rankingable.rb module Rankingable def rank # ུ end private def calculate_score # ུ end end class User include Rankingable end User.new.private_methods.include?(:calculate_score) #=> true 21

Slide 22

Slide 22 text

ここから追記 発表に間に合ったのはここまでだった... 22

Slide 23

Slide 23 text

Class vs Module • Classのprivateメソッドのスコープは狭い • リファクタリングしやすい • Moduleだと影響範囲の調査が少し面倒 • Moduleは少ないメソッドに依存させるべき • Enumerableは each のみに依存してる • Moduleを上手く使うのは難しい 23

Slide 24

Slide 24 text

まとめ • RuboCopの指摘はリファクタリングの目安 • 安易に rubocop:disable しない • Arrayのメソッド一覧を読むのがおすすめ • https://docs.ruby-lang.org/ja/latest/class/Array.html • ModuleよりClassの方がおすすめ 24