Day3 要來介紹Ruby
字串、數字,還有Ruby
的 ===
數字比起其他類別,還要來的單純。不過身為Rails的工程師,必須知道Ruby
提供的語法
num = 10
num.even? # 是否為偶數
num.odd? # 是否為奇數
num.positive? # 是否為正數
num.negative? # 是否為負數
num.zero? # 是否為零
num.integer? # 是否為正數
⭐️ Ruby的目的為寫得出讓人愉悅的語法,所以除了上述的口語化語法以外,數字本身也有小巧思。
1000 #=> 1000
1_000 #=> 1000
10_00 #=> 1000
在Ruby
語言中,我們可以把_
當成是逗點, 若數字的位數太多,則可以加註底下下去。不過Ruby
底線的位置不會限制,因此寫在哪裡都可以。
⭐️ Ruby
使用隨機數字的語法為rand
,而我們也可以透過+/-某個數字來做平移的效果。
rand(10) #=> 數字1-10
rand(10) + 10 #=> 數字10-20
⭐️ 處理除法方面,Ruby
必須小心整數相除的狀況。在Ruby
程式語言中,所有皆為物件,而Integer
, Float
為不同物件,表現的行為也會不一樣要注意。
1/2 #=> 0
1.0/2.0 #=> 0.5
1.to_f/2.to_f #=> 0.5
1.class #=> Integer
1.0.class #=> Float
⭐️ 如果想要了解線性代數,或者公職/研究所有用到線性代數的話,也可以使用下列的matrix
玩玩看。
require 'matrix'
⭐️ 如果要取概數,可以使用round
2.3465.round(2) #=> 2.35
⭐️ 可以使用format
,但型別就為字串。小心不要用字串來做數字的運算
format("%.2f", 2.3465) #=> "2.35"
format("%.2f", 2.3465).class #=> String
⭐️ 和Rails
數字有關的 helper
也有一些值得介紹的,像是如果要顯示金額的話,可以使用(可參考這篇):
helper.number_to_currency(42.50) #=> "NT$42.50"
helper.number_to_currency(nil) #=> nil
helper.number_to_currency(42.50, unit: "Richard$") #=> "Richard$42.50"
Ruby
的字串有很多實用的寫法,以下跟大家介紹一些目前自己常用的!
"admin/sub_order".camelize #=> "Admin::SubOrder"
"Admin::SubOrder".underscore #=> "admin/sub_order"
"admin/sub_order".dasherize #=> "admin/sub-order"
"book".pluralize #=> "books"
"person".pluralize #=> "people"
"fish".pluralize #=> "fish"
"books".singularize #=> "book"
"people".singularize #=> "person"
"book_and_person".pluralize #=> "book_and_people"
"book and person".pluralize #=> "book and people"
"BookAndPerson".pluralize #=> "BookAndPeople"
"books_and_people".singularize #=> "books_and_person"
"books and people".singularize #=> "books and person"
"BooksAndPeople".singularize #=> "BooksAndPerson"
"admin/sub_orders".humanize #=> "Admin/sub orders"
"admin/sub_orders".titleize #=> "Admin/Sub Orders"
"admin/sub_order".classify #=> "Admin::SubOrder"
"admin/sub_orders".classify #=> "Admin::SubOrder"
"admin/sub_order".tableize #=> "admin/sub_orders"
"Admin::SubOrder".tableize #=> "admin/sub_orders"
"Module".constantize #=> Module
"A::b::C".deconstantize #=> "A::b"
"A::b::C".demodulize #=> "C"
上述的這些用法如果搭配Ruby on Rails
的慣例,可以寫出非常漂亮的語法。我們在對controllers
, models
命名的時候基本上都會按慣例,而只要命名符合規則,就可以用動態的方式寫。以下是自己專案中的程式片段,依照慣例後就可以使用動態寫法,因此可以精簡化程式碼。
下列為自己寫過的某一支讀取controller
的程式碼。我們可以看到,以下使用了camelize
, singularize
, constantize
, classify
來對被included
的controller
做操作,達到程式碼精簡化的效果。
# controller.camelize.singularize.constantize
define_method("pre_query_#{controller}") do
# descent_exposure
return send(controller_name.to_sym) if respond_to?(controller_name.to_sym)
# instance_variable
instance_variable_set("@#{controller}", controller.camelize.singularize.constantize)
end
# Admin::#{controller.classify}Serializer
define_method(:default_viewable_lists) do |filtered_sub_orders, keys|
data_lists(viewable(filtered_sub_orders), "Admin::#{controller.classify}Serializer".constantize, keys)
end
我們不用懂上面的程式碼在做什麼,我們把焦點放在兩處:
controller.camelize.singularize.constantize
"Admin::#{controller.classify}Serializer".constantize
這裡使用動態的寫法,讓被included
的檔案都可以被讀取到。至於#included
的話,我們會在Day14講到
in?
與 include?
的用法差別在以下的例子!兩者用法的差別就是倒過來放而已。
'a'.in?(['Cat', 'Dog', 'Bird']) # => false
['Cat', 'Dog', 'Bird'].include?('a') # => false
include?
為陣列的方法,照理說應該要放在Day4,但由於放在這裡剛剛好,提前先提到。
有了start_with?
, end_with?
,可以少寫很多正則表達式!
"popping_hoan".start_with? "popping" #=> true
"jquery".end_with? "query" #=> true
strip
為去除頭尾的扣白,可以應用在填寫表單。當使用者前後不小心加了空白,我們就可以使用strip
" I have leading and trailing white space ".strip
#=> "I have leading and trailing white space"
正規表達式是大部分的工程師,只要每用一次就要查一次的頭痛地雷,正因為這樣,對於一些常用的正則,我們可以把它放在心上。Ruby
, Rails
對一些常用的正則已經囊括進來,像是email
的驗證便不用從頭寫過,ruby
的函式庫也有很多正則表達的替代寫法,例如上面提到的start_with?
, end_with?
。
我們來講一下一些關於字串的用法
⭐️ 在字串中判斷數字
def number?(obj)
obj = obj.to_s unless obj.is_a? String
/\A[+-]?\d+(\.[\d]+)?\z/.match(obj)
end
# / start of the regex
# \A start of the string to be matched
# [+-]? zero or one of '+' or'-'
# \d+ one or more of digit
# (\.\d+)? zero or one of 'one dot and 'one or more of digit''
# \z end of the string to be matched
# / end of the regex
⭐️ 字串判斷A-Z(不是疫苗,是A到Z)
/\A[A-Z]+?\z/.match("ABC") # => #<MatchData "AZ">/\A[A-Z]+?\z/.match("A123") # => nil
⭐️ 字串判斷A-Z和.-+
/\A[A-Z+-\.]+?\z/.match("AZZR.-+")
⭐️ 字串判斷手機載具:第一個字元/
,後7個字元包含 A-Z
, .-+
/\A[\/][A-Z+-\.]{7}\z/.match("/AAA++++")
⭐️ 字串判斷自然人憑證:2位大寫+14位數字
/\A[A-Z]{2}[0-9]{14}\z/.match("AA22222222222222")
/\A[A-Z]{2}[0-9]{14}\z/ =~ ("AA22222222222222")
⭐️ 字串的取代也是很重要的語法,在Ruby
我們可以使用gsub
來取代字串,下列為一些取代的例子。
"chenhanting".gsub(/[aeiou]/, '*') #=> "ch*nh*nt*ng"
"chenhanting".gsub(/([aeiou])/, '<\1>') #=> "ch<e>nh<a>nt<i>ng"
"chenhanting".gsub(/./) {|s| s.ord.to_s + ' '} #=> "99 104 101 110 104 97 110 116 105 110 103 "
"chenhanting".gsub(/(?<foo>[aeiou])/, '{\k<foo>}') #=> "ch{e}nh{a}nt{i}ng"
'chenhanting'.gsub(/[eo]/, 'e' => 3, 'o' => '*') #=> "ch3nhanting"
"Me & You".gsub(/[&]/, 'and') #=> "Me and You"
ActiveSupport
是Rails
的寶庫,很多東西都可以在 ActiveSupport
找,ActiveSupport
在rails
的地位就相當於 Javascript
的 lodash
,只不過不一樣的地方是 lodash
為外部庫、ActiveSupport
則為Rails
的內建庫。
vehicle = ActiveSupport::StringInquirer.new('car')
vehicle.car? # => true
vehicle.bike? # => false
variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
variants.phone? # => true
variants.tablet? # => true
variants.desktop? # => false
variants = ActiveSupport::ArrayInquirer.new([:phone, :tablet])
variants.any? # => true
variants.any?(:phone, :tablet) # => true
variants.any?('phone', 'desktop') # => true
variants.any?(:desktop, :watch) # => false
Ruby的等於一共有這幾種:equal?
, ==
, eql?
。等於的方法,常用的為後面兩種,eql?
較==
還嚴格,不過基本上沒太多嚴格定義,兩者可以混用。
5 == 5.0 # true
5.eql? 5.0 # false
但跟===
的概念完全不一樣,===
在Ruby
的概念為case when
,跟等於一點關係都沒有。我們實際舉一些範例,跟附上說明:
# 1 是不是符合在1到10裡面
(1..10) === 1
# 'abcdef'字串是不是符合 /abc/ 的正則表達
/abc/ === 'abcdef'
# 'abcdef' 是不是 string
String === 'abcdef'
然而===
的寫法實在不好看懂,Ruby
有很多寫法可以語法糖可以替代 ===
,以下是推薦的好寫法
# Bad
(1..10) === 1
/abc/ === 'abcdef'
String === 'abcdef'
# Good, uses synonym method
(1..10).include?(1)
/abc/ =~ 'abcdef'
'abcdef'.is_a?(String)
接著,我們來比較case when
vs ===
。以下為使用case when
,與使用===
的比對
def display_installment(v)
case v
when 0 then '一次付清'
when 3 then '3期'
when 6 then '6期'
end
end
# 被比較的放在右側
def display_installment(v)
if 0 === v
'一次付清'
elsif 3 === v
'3期'
elsif 6 === v
'6期'
end
end
display_installment(6)
會拿case when
與 ===
當例子,是因為case when
使用 ===
實作
%q
, %Q
都可以包字串,但 %Q
代表雙引號,%q
代表單引號。雙引號的字串裡面可以放變數、放特殊字元。因此若要使用,就使用%Q
,而不要使用%q
。
%Q(han react
ruby
vue it2021)
#=> "han react\nruby\nvue it2021"
str = 'apple'
%Q[#{str} is "DELICIOUS"]
#=> "apple is \"DELICIOUS\"
實際上,我們可以在helper
裡面,可以使用%Q
回傳html
。
def get_form(action, options = {})
# ......
%Q(
<form name='xxxx' method='post' action='#{url}'>
#{params.map do |k,v|
"<input name='#{k}' type='hidden' value='#{v}'/>"
end.join("")
}
<input type='submit'/>
</form>
)
end
我們在helper
使用%Q
,來引入 recaptcha
的樣式。
module ApplicationHelper
RECAPTCHA_SCORE_SITE_KEY = ENV['RECAPTCHA_SCORE_SITE_KEY']
RECAPTCHA_CHECKBOX_SITE_KEY = ENV['RECAPTCHA_CHECKBOX_SITE_KEY']
def include_recaptcha_js
raw %Q{<script src="https://www.google.com/recaptcha/enterprise.js?render=#{RECAPTCHA_SCORE_SITE_KEY}"></script>}
end
def recaptcha_execute(action)
raw %Q{
<input name="recaptcha_token" type="hidden" id="__recaptcha_token__"/>
<script>
grecaptcha.enterprise.ready(function() {
grecaptcha.enterprise.execute('#{RECAPTCHA_SCORE_SITE_KEY}', {action: '#{action}'})
.then((token) => {
console.log('reCAPTCHA score action', '#{action}');
console.log('reCAPTCHA score token:', token);
document.getElementById("__recaptcha_token__").value = token;
})
.catch((error) => {
console.log(`The unknown error has occurred: ${error}`);
});
});
</script>
}
end
end
chomp
:移除結尾字元chop
concat
range
'kitty'[1..2] #=>it
ljust
reverse
⭐️ 顯示子訂單的出貨來源
# 有廠牌 & 店櫃資訊: iphone-新莊店
# 只有廠牌資訊: iphone
#
[sub_order.brand.title, sub_order.store.title_zh].compact.join('-')
⭐️ 將We have lunch at 12 o'clock
句子的英文字轉成數字,其餘特殊符號如空白、引號剔除,並用底線連接。以下為對應表跟實際內容!
A => 1
B => 2
C => 3
...
Z => 26
str = "We have lunch at 12 o'clock"
str.downcase.chars.select { |s| /\A[A-Z|a-z]+?\z/.match(s) }.map{|n| n.ord - 'a'.ord}.join('_')
#=> "22_4_7_0_21_4_11_20_13_2_7_0_19_14_2_11_14_2_10"