Upgrade to Pro — share decks privately, control downloads, hide ads and more …

読みやすいコード

Sponsored · SiteGround - Reliable hosting with speed, security, and support you can count on.

 読みやすいコード

Avatar for Takumi Shotoku

Takumi Shotoku

December 05, 2019
Tweet

More Decks by Takumi Shotoku

Other Decks in Technology

Transcript

  1. メソッド呼び出しのネストが深い例 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
  2. 浅くした例 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
  3. しかし、現実は... # 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 2. Array(Enumerable)のメソッドを駆使する • group_by と transform_values users_per_status = User.find_each.group_by(&:status) #=>

    { "pending" => [<User>, ...], "active" => [<User>, ... } users_per_status.transform_values { |users| users.map(&:user_name) } #=> { "pending" => ["sinsoku", ...], "active" => ["yuki3738", ... } 19
  10. 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
  11. モジュールにするのは # 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
  12. Class vs Module • Classのprivateメソッドのスコープは狭い • リファクタリングしやすい • Moduleだと影響範囲の調査が少し面倒 •

    Moduleは少ないメソッドに依存させるべき • Enumerableは each のみに依存してる • Moduleを上手く使うのは難しい 23