Nic Lin's Blog

喜歡在地上打滾的 Rails Developer

Rails 中的欄位及方法命名原則

Computer Science 中最難的兩件事情

  1. cache invalidation
  2. naming things

不過 Ruby / Rails 本身就有一些基本的慣例及約定在,只要熟悉這個規則通常都能夠寫出易懂易維護的程式碼。

就像我們呼叫 created_at 欄位時,腦袋會默認他就是 datetime,而不會是 string

以下是一些常用的慣例,分別是

  • Database column name
  • Method name
  • Library name

Column 欄位命名

使用過去分詞 + “at”

時間欄位請使用 Date / Datetime,嚴禁使用 String

被驗證的時間不應該使用 activated_time 而應該使用 activated_at。 因為 time 是表示時間(時間是指什麼時候還是花多久時間?),不是「什麼時候被驗證」。

example:

  • submitted_at
  • paid_at
  • done_at
  • created_at
  • updated_at

使用 “is” + 形容詞

欄位請使用 Boolean,這樣一來 Rails 可以直接使用帶有 ? 的 method

假設欄位名稱為 is_admin 屬性為 Boolean 時,可以直接呼叫 user.is_admin?

The #{attribute}? method is a method defined automatically by Active Record

example:

  • is_admin
  • is_activated
  • is_hidden
  • is_locked
  • is_enterprise

Method 方法命名

使用動詞 + 名詞表示要做的事情

把該做的事情放到對的地方,不要都在 controller 做

# Bad

class UsersController < ApplicationController
  def action
    user.token = SecureRandom.hex(5)
    user.save
  end
end

應該放在 model 裡面用 method 包起來,並敘述是做什麼事

# Grate

class User < ApplicationRecord
  def generate_token
    update_attribute(:token, SecureRandom.hex(5))
  end
end

請注意,名詞 method 不要有小動作,嚴禁偷動手腳,要操作資料務必另外寫

# Bad
class User < ApplicationRecord
  def full_name
    name = "#{first_name} #{last_name}"

    update_column(:fullname, name)

    name
  end
end

method 是名詞時,就讓他回傳單純的東西就好,例如 Array, String, Integer, Object

# Great

class User < ApplicationRecord
  def full_name
    "#{first_name} #{last_name}"
  end
end

使用 ! 結尾的 method 表示會做一些改變

  • Ruby method 會預期改變原有的 object 狀態
    • String 的 gsub! 與 gsub 是不同的結果與作用。
  • Rails 中的 ActiveRecord method (create!, save!) 失敗會 throw exception
class Cart < ApplicationRecord
  def clean!
    item.delete_all
  end
end

Class 命名

一律採用中性命名

Library

Library 是提供一個介面讓其他人可以呼叫

例如常用的第三方服務 OneSignal 推播通知

用 OneSignal(one_signal.rb) 是比較好的

不需要用

  • OneSignalSender(one_signal_sender.rb)
  • OneSignalWrapper(one_signal_wrapper.rb)

或是常用的 Telegram 機器人

用 TelegramBot(telegram_bot.rb) 是比較好的

不需要用

  • TelegramBotSender(telegram_bot_sender.rb)
  • TelegramBotWrapper(telegram_bot_wrapper.rb)

Wrapper

wrapper 應該是包裝了一些方法讓人呼叫

class OMDBWrapper
  include Singleton

  class << self
    delegate :get_movie_by_title, to: :instance
  end

  def initialize
    @omdb_client = OMDBClient.new
  end

  def get_movie_by_title(title)
    movie_attributes = omdb_client.
      get_movie(t: title).
      deep_transform_keys(&:underscore)
    Movie.new(movie_attributes)
  end

  private

  attr_reader :omdb_client
end

# OMDBWrapper.get_movie_by_title('The Rock') # =>
# <OMDBWrapper::Movie:0x00007fc6f5b7b290 @runtime="136 min", @title="The Rock", @year=1996>

Sender

sender 的職責是呼叫其他的 services wrapper,像是 Rails mailer 一樣,透過 mailer 去呼叫其他外部程式

例如

module NotificationSender
  def send_to_user
    # code
  end

  def send_to_vendor
    # code
  end
end

參考資源

comments powered by Disqus