Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
読みやすいコード
Search
Takumi Shotoku
December 05, 2019
Technology
1
770
読みやすいコード
Omotesando.rb #53
https://omotesandorb.connpass.com/event/157355/
Takumi Shotoku
December 05, 2019
Tweet
Share
More Decks by Takumi Shotoku
See All by Takumi Shotoku
Automatically generating types by running tests
sinsoku
4
12k
滅・サービスクラス🔥 / Destruction Service Class
sinsoku
8
2.6k
テストを書かないためのテスト/ Tests for not writing tests
sinsoku
1
280
ドメインの本質を掴む / Get the essence of the domain
sinsoku
2
330
"型"のあるRailsアプリケーション開発 / Typed Rails application development
sinsoku
10
3k
Let's get started with Ruby && Rails Tips
sinsoku
0
470
LTの敷居を下げる / Lower the threshold for LT
sinsoku
2
410
CircleCIの高速化🚀 / CircleCI faster
sinsoku
3
1.5k
Railsアプリと型検査 / Rails app and type checking
sinsoku
5
1.6k
Other Decks in Technology
See All in Technology
業務の煩悩を祓うAI活用術108選 / AI 108 Usages
smartbank
9
19k
テストセンター受験、オンライン受験、どっちなんだい?
yama3133
0
200
Authlete で実装する MCP OAuth 認可サーバー #CIMD の実装を添えて
watahani
0
400
スクラムを一度諦めたチームにアジャイルコーチが入ってどう変化したか
kyamashiro73
0
160
BidiAgent と Nova 2 Sonic から考える音声 AI について
yama3133
2
140
ESXi のAIOps だ!2025冬
unnowataru
0
470
[Data & AI Summit '25 Fall] AIでデータ活用を進化させる!Google Cloudで作るデータ活用の未来
kirimaru
0
4.2k
All About Sansan – for New Global Engineers
sansan33
PRO
1
1.3k
Oracle Database@Azure:サービス概要のご紹介
oracle4engineer
PRO
3
260
Oracle Database@Google Cloud:サービス概要のご紹介
oracle4engineer
PRO
1
820
「アウトプット脳からユーザー価値脳へ」がそんなに簡単にできたら苦労しない #RSGT2026
aki_iinuma
7
3.7k
AIエージェントを5分で一気におさらい!AIエージェント「構築」元年に備えよう
yakumo
1
140
Featured
See All Featured
[Rails World 2023 - Day 1 Closing Keynote] - The Magic of Rails
eileencodes
38
2.7k
世界の人気アプリ100個を分析して見えたペイウォール設計の心得
akihiro_kokubo
PRO
65
35k
The Art of Programming - Codeland 2020
erikaheidi
56
14k
Testing 201, or: Great Expectations
jmmastey
46
7.8k
The #1 spot is gone: here's how to win anyway
tamaranovitovic
1
880
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Git: the NoSQL Database
bkeepers
PRO
432
66k
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.5k
Amusing Abliteration
ianozsvald
0
82
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.1k
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
200
Transcript
読みやすいコード 表参道.rb #53 2019/12/05(Thu) 1
自己紹介 名前: 神速 会社: メドピア株式会社 GitHub: @sinsoku (アイコン右上) Twitter: @sinsoku_listy
(アイコン右下) 2
コードを読みやすく書きたい 3
読みやすいコードとは何か 4
考えてみた • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 5
メソッド呼び出しのネストが深い例 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
浅くした例 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
話を戻して... 8
読みやすいコードの条件(主観) • 短いコード • 条件分岐の少ないコード • 適切な命名の変数、クラス、メソッド...etc • メソッド呼び出しのネストが浅い 9
RuboCopでだいたい検出できる • Metrics/AbcSize • Metrics/ClassLength: • Metrics/MethodLength 10
これで読みやすいコードになる! 11
しかし、現実は... # 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
Metrics/AbcSize を下げるテクニック 1. 変数を定義しない 2. Array(Enumerable)のメソッドを駆使する 3. クラスを抽出する 13
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
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
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
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
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
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
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
モジュールにするのは # 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
ここから追記 発表に間に合ったのはここまでだった... 22
Class vs Module • Classのprivateメソッドのスコープは狭い • リファクタリングしやすい • Moduleだと影響範囲の調査が少し面倒 •
Moduleは少ないメソッドに依存させるべき • Enumerableは each のみに依存してる • Moduleを上手く使うのは難しい 23
まとめ • RuboCopの指摘はリファクタリングの目安 • 安易に rubocop:disable しない • Arrayのメソッド一覧を読むのがおすすめ •
https://docs.ruby-lang.org/ja/latest/class/Array.html • ModuleよりClassの方がおすすめ 24