Ruby面試精選30題 - Day25 初探 Rails: .present? 和 .exists?比較

[昨天]的鐵人賽裡,我們提到了.present?方法,第一次把腳跨進Rails的領域,這是一種從Ruby幼幼班畢業的架勢!

今天再來研究另一個Rails方法:.exists?,從英文字面上來說這兩個方法名字挺像的(.present?現在?在場?;.exists?存在?生存?),讓我們來繼續看下


重點摘要:


Ruby經典面試題目 #25

  • present?.exists?比較?
    What is the difference between .present? and .exists?`

根據我們在這趟鐵人賽journey所養成的好習慣,當學到新的方法時,第一步就是去:翻手冊!

.present?()

.present()所在的API手冊網址:https://api.rubyonrails.org/classes/Object.html#method-i-present-3F 我們可以發現.present?()方法是屬於Object之下的方法。如果物件Object不是blank(nil, 空值, 或空白鍵),就是代表現在;在場,回傳true。

.present?(): An object is present if it’s not blank.

@return [true, false]

# File activesupport/lib/active_support/core_ext/object/blank.rb, line 26
def present?
  !blank?
end

.present.presence

present是動詞,presence是名詞,我們從Rails API發現 這兩者方法可以互相轉換,

# File activesupport/lib/active_support/core_ext/object/blank.rb, line 46
def presence
  self if present?
end

present?可以和presence互相改寫:

params[:something] if params[:something].present?

變成

params[:something].presence

程式碼從兩行變成一行了呢~~

如果某物件不在場(present),就回傳nil(returns nil if object is ‘empty’)

params[:something] == ''

[].presence
# => nil

等同於

object.present? ? object : nil

.exists?

接著來看看.exists()所在的API手冊網址: https://api.rubyonrails.org/v3.1/classes/ActiveResource/Base.html#method-c-exists-3F 可以發現.exists()方法是屬於ActiveResource::Base下的方法。

當我們架設一個包含多種功能的動態網站,為了防止效能變差,確保系統資源能被有效利用,我們會先檢查資源是否存在

exists?(id, options = {})Asserts the existence of a resource, returning true if the resource is found.

這篇文章提到Existence checks in Rails是非常重要的觀念!就好像出門前要先檢查鑰匙錢包手機是否隨身攜帶;出國去機場坐飛機前記得檢查是否攜帶護照,不然就要再回家拿、浪費大把時間與資源、又搞得自己很狼狽喔!


IronmanDairy.create(:title => 'Day 24', :body => 'The importance of Existence checks')

IronmanDairy.exists?(1) # => true
IronmanDairy.exists(9527) # => false

來看看rails文件的.exists方法是怎麼被刻出來的:

# File activeresource/lib/active_resource/base.rb, line 869
def exists?(id, options = {})
  if id
    prefix_options, query_options = split_options(options[:params])
    path = element_path(id, prefix_options, query_options)
    response = connection.head(path, headers)
    response.code.to_i == 200
  end
  # id && !find_single(id, options).nil? id存在且不為空
rescue ActiveResource::ResourceNotFound, ActiveResource::ResourceGone
  false
end

至於什麼是ActiveResource::Base呢?

It is the main class for mapping RESTful resources as models in a Rails application.出處

這邊的RESTfulMVC框架model兩個博大精深的概念都可以另外再寫兩本書了,因此限於篇幅,這邊就先暫不提起。(想研究的新手同好可參考:RESTful 與 MVC 的旅程 之 那些年我不懂的 Rails)等到小妹未來Ruby功力加深後,會續寫30篇Rails面試精選題深入研究的:)

.present?.exists?比較

由於.present?來自於ActiveRecord.exists來自於ActiveResource,它們本質上的意義其實很不同:

.present?

Ironmen.where(name: 'Ting Ting')

Ironmen Load (8.1ms) SELECT "ironmen".* FROM "ironmen" WHERE "ironmen"."name" = $1 ORDER BY users.id ASC  [["name", 'Ting Ting']]

You have initialized an object!
You have initialized an object!

我們看到了初始化initialized an object的提示。

.exists?

Ironmen.exists?(name: 'Ting Ting')

Ironmen Exists (2.4ms)  SELECT 1 AS one FROM "ironmen" WHERE "ironmen"."name" = $1 ORDER BY users.id ASC LIMIT 1  [["name", 'Ting Ting']]

我們發現.present?.exists?所代表的SQL語法也有所差異:

.present?

Ironman.where(name: "Ting Ting").present?

# => SELECT COUNT(*) FROM ironman WHERE ironman.name = "Ting Ting";

.exists?

Ironman.exists?(name: "Ting Ting")

# => SELECT 1 AS one from ironman WHERE name ="Ting Ting" limit 1;

之前鐵人賽文章曾提到的Benchmark標竿測試,我們常比較不同的method在不同數量級的資料下,哪一個效能較佳。相信你也可以像我一樣猜到.exist?()來自於確認請求網路資源是否存在的ActiveResource::Base,速度當然要飛快才行!這篇文章的案例標竿測試.present?花了900ms(毫秒),而.exists?僅花了1ms!!!

最後,不免俗地來個超級比一比:)

方法 .present?() .exists?()
路徑 active_support/core_ext/object/blank.rb activeresource/lib/active_resource/base.rb
用途 如果物件Object不為blank(nil, 空值, 或空白鍵),回傳true 若資源存在,回傳true
特點 利用ActiveRecord初始化物件,效能差 ActiveResource下的方法,效能佳

Ref: