iT邦幫忙

2021 iThome 鐵人賽

DAY 1
0
自我挑戰組

冒險村-30 Day Ruby on Rails Tips Challenge系列 第 1

冒險村01 - Begin from linter(1)

01 - Begin from linter : rails_best_practices

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

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

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

gem install

  # Gemfile
  gem "rails_best_practices"

Usage

  # 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 的例子來看:

  # 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,可能會這樣子寫:

  # 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 的部分

  /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 來做,相對於行數也比較短。

  # 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,沒問題啦!

Source Code: |=============================================================================================================================|

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

No warning found. Cool!

RubyGems

參考來源

My blog



下一篇
冒險村02 - Begin from linter(2)
系列文
冒險村-30 Day Ruby on Rails Tips Challenge30

1 則留言

0
小菜
iT邦新手 3 級 ‧ 2021-09-17 22:40:06

一起加油~

我要留言

立即登入留言