高見龍

iOS app/Ruby/Rails Developer & Instructor, 喜愛非主流的新玩具 :)

Ruby 語法放大鏡之「常在 controller 裡看到 before_action,它是一個方法嗎? 跟一般用 def 定義的有何不同?」

Ruby 語法放大鏡」系列短文主要是針對在大家學習 Ruby 或 Rails 時看到一些神奇但不知道用途的語法介紹,希望可以藉由這一系列的短文幫大家更容易的了解到底 Ruby 或 Rails 是怎麼回事。

在 Rails 專案中,常會在 Controller 裡看到這樣的寫法:

1
2
3
4
5
6
7
8
9
10
class ProductController < ApplicationController
  before_action :find_product, only: [:show, :edit, :update, :destroy]

  # .. 中略

  private
  def find_product
    @product = Product.find_by(id: params[:id])
  end
end

這是指在執行特定 Action 之前,先去執行 find_product 方法。

在 Model 裡也常看到類似的寫法:

1
2
3
4
5
6
7
8
9
class Article < ApplicationRecord
  before_save :encrypt_password
  validates :title, presence: true

  private
  def encrypt_password
    # ....
  end
end

這是說在資料儲存之前,先執行 encrypt_password 方法對密碼進行加密。

在一般的 Ruby 專案裡也常會看到這樣的寫法:

1
2
3
4
5
6
7
8
9
10
class Cat
  attr_accessor :age, :name
end

kitty = Cat.new
kitty.name = "nancy"
kitty.age = 10

puts kitty.name   # => nancy
puts kitty.age    # => 10

所以,在 Controller 裡的 before_action 或是 Model 裡 before_savevalidates,或是那個 attr_accessor 到底是什麼來歷? 感覺好像是什麼設定還是屬性,或是關鍵字之類的東西?

類別方法

其實,這些看起來像設定或是屬性的東西,它們不過就是類別方法罷了。

1
2
3
4
5
6
7
class Cat
  def self.my_attr_accessor(*field_name)
    # ... 實作
  end

  my_attr_accessor :age, :name
end

跟寫一般的方法沒什麼不同,一樣是使用 def 這個關鍵字來定義,只是在定義類別方法的時候,需要在方法名字前面加個 self ,這樣就可以定義出類別方法(事實上是 Singleton Method),然後就可以直接在類別裡面使用。

至於這些類別方法裡面的實作稍微有些複雜,有的需要知道怎麼操作 Block,有的還需要知道怎麼動態的定義方法(例如使用 define_method),我們另外再開一篇來介紹。

關鍵字?

有些人以為 privateprotected 這些語法是關鍵字,事實上這些都只是方法罷了(Ruby 什麼東西沒有,方法最多了)。

Ruby 裡的關鍵字不算太多,有興趣可翻 Ruby 的原始碼出來看看,Ruby 大概定義了 40 個左右的關鍵字,大概就是 ifelsedefclass 之類的。參考連結

但即使是關鍵字也不表示全部不能用,你要拿來定義方法也是可以的,例如:

1
2
3
def if
  puts "this is a if"
end

在定義的時候不會出錯,但使用的時候就沒辦法了:

1
if()

會得到錯誤訊息如下:

1
2
-:51: syntax error, unexpected end-of-input
shell returned 1

因為 Ruby 在判讀語法的時候,期待 if 後面還有一些東西,所以 Ruby 認為你還沒寫完然後被判定成語法錯誤。但如果是包在類別裡的話:

1
2
3
4
5
6
7
class Joke
  def if
    puts "this is a if"
  end
end

Joke.new.if

這樣就能正常執行了 :)

Comments