冒險村01 - Begin from linter(1)

01 - Begin from linter : rails_best_practices

好的開始,是成功的一半。

不管是前端、後端,當一個團隊每個成員撰寫程式的風格不同,對於維護、開發都相對會困難些。如果有個統一的規定,會一定程度提高專案開發效率。這類型的 tools 在每個程式都有相對應的 linter 可以使用。

這次開頭第一篇就從名字很淺顯易懂的 rails_best_practices 開始吧!一看就知道這個 gem 在做什麼(雖然說 官方網站 已經有點久沒更新 & Github Commit 沒什麼新的東西,但至少還是一個不錯使用的工具,也在上個專案使用了約一年多左右)

gem install

1
2
# Gemfile
gem "rails_best_practices"

Usage

1
2
3
4
5
# At the root directory of a Rails app
bundle exec rails_best_practices

# for HTML output
bundle exec rails_best_practices -f html

以 User has_many post 的例子來看:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# Schema
create_table "posts", force: :cascade do |t|
t.string "title"
t.text "content"
t.boolean "is_available"
t.integer "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id"], name: "index_posts_on_user_id"
end

create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.string "tel"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end

# model
# user.rb
class User < ApplicationRecord
has_many :posts
end

# post.rb
class Post < ApplicationRecord
belongs_to :user
end

在 post index 頁面中,假如想顯示 Post 所有欄位 & 來自於哪個 User Name,可能會這樣子寫:

1
2
3
4
5
6
7
8
9
10
# app/views/posts/index.html.erb
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.content %></td>
<td><%= post.is_available %></td>
# User Name
<td><%= post.user.name %></td>
</tr>
<% end %>

執行 bundle exec rails_best_practices 會發現出現 law of demeter 的錯誤訊息,原因其實就是出在 post.user.name 的部分

1
2
3
4
5
/blog/app/views/posts/index.html.erb:22 - law of demeter

Please go to https://rails-bestpractices.com to see more useful Rails Best Practices.

Found 1 warnings.

可以寫個 user_name 方法來呼叫關聯 user 的 name,不過如果要顯示 user 更多的其他欄位(e.g. email),等於要一直寫更多的方法,這時候其實可以用 delegate 來做,相對於行數也比較短。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
# model
# user.rb
class User < ApplicationRecord
has_many :posts
end

# post.rb
class Post < ApplicationRecord
belongs_to :user

# (1)
def user_name
user.name
end

def user_email
user.email
end

# (2)
delegate :name, :email, to: :user, prefix: true
end

# app/views/posts/index.html.erb
<% @posts.each do |post| %>
<tr>
<td><%= post.title %></td>
<td><%= post.content %></td>
<td><%= post.is_available %></td>
# User Name & Email
<td><%= post.user_name %></td>
<td><%= post.user_email %></td>
</tr>
<% end %>

透過 Delegate 來把 attributes 給關聯的 model 來使用讓邏輯更容易理解,更改後再跑一次就 No warning found,沒問題啦!

1
2
3
4
5
Source Code: |=============================================================================================================================|

Please go to https://rails-bestpractices.com to see more useful Rails Best Practices.

No warning found. Cool!

RubyGems

參考來源