看完今天的文章,自己試著在專案寫動態寫法後,讀者們會發現今天講的東西很實用。我們可以透過動態的寫法,省去相當多程式碼。
define_method
是目前最常提到的用法,該用法可以動態的宣告方法
class Person
define_method :greeting, -> { puts 'Hello!' }
end
Person.new.greeting # => Hello!
上面的例子中,greeting
等同於以下寫法。
class Person
def greeting
puts 'Hello!'
end
end
⭐️ 接著我們來說明我在寄信服務上的動態方法
class NotificationMailer < ApplicationMailer
# 所有方法
MAIL_METHODS = %w[
order_init order_need_pay order_canceled_by_customer
order_canceled_by_user order_canceled_not_pay
order_shipped order_arrived order_ship_failed
invoice_issued return_applied return_failed return_partial_failed return_done
maintain_quoted register member_upgrade member_renewal expiring_accumulate
member_expiring cart acquire_rebate expiring_credit_point maintain_back
]
MAIL_METHODS.each do |key|
define_method(key) do |email, instance, title = nil|
@instance = instance
mail(to: email, subject: title || send("#{key}_title"))
end
end
end
上面為mailer
的方法,由於不同的寄信方式內容大致上相同,就可以使用define_method
。其中 define_method
的email, instance, title = nil
,都是被帶入的參數。上述的方式也可以用method_missing
實現,method_missing
在後面會介紹到,不過這裡用define_method
會比較直觀。
在我剛開始使用send
的時候,我問自己一個問題?這個問題是Ruby
的方法是Symbol
嗎?上網查了以後,發現很多人都有類似的問題。確實symbol
在Ruby
的世界中佔取的object_id
相同,所以也可以想像得到,我們可以使用send
搭配symbol
做動態的呼叫方法
# 意思相同
1 + 1 = 2
1.send(:+, 1)
# 意思相同
"We are the world".upcase
"We are the world".send(:.upcase)
# 使用客製化 send
class Klass
def hello(*args)
"Hello " + args.join(' ')
end
end
k = Klass.new
k.send(:hello, "gentle", "readers") #=> "Hello gentle readers"
send
的第一個參數是方法,第二個以後全部都是帶進來的參數。接著我們講send
如何搭配其他方法!
send
& respond_to?
的搭配簡直是絕妙。以下的寫法A,可以使用寫法B代替
print "Search for: "
request = gets.chomp
# 寫法A
if request == "writer"
puts book.writer
elsif request == "press"
puts book.press
elseif request == "date"
puts book.date
else
puts "Input error"
end
# 寫法b
if book.respond_to?(request)
puts book.send(request)
else
puts "Input error"
end
⭐️ 接著我們看respond_to?
的好處?使用respond_to?
被使用以後,就不必擔心會有undefined method
的錯誤。
obj = Object.new
if obj.respond_to?("talk")
obj.talk
else
puts "Sorry, object can't talk!"
end
# 若找不到talk,也不會跳出undefined method 'talk' for #<Object:0x12345678> (NoMethodError)的錯誤
⭐️ 學習rails
的讀者們千萬要記住, respond_to
是Rails 控制器的用法,和 respond_to?
完全是不一樣的概念喔!
def index
@people = Person.find(:all)
respond_to do |format|
format.html
format.json { render :json => @people.as_json }
end
end
⭐️ 以下為send
與 define_method
的搭配。
class A
def create_method(name, &block)
self.class.send(:define_method, name, &block)
end
end
a = A.new
a.create_method(:run) { "Running Man" }
a.run
#=> "Running Man"
send
和define_method
的搭配方法,確實可以玩很多不同的變化 ?,後面介紹的method_missing
,就會搭配send
和define_method
回傳self
在Ruby
是很常見的寫法,此外符合設計流程的Builder Pattern
class Salad
def initialize
@ingredients = []
end
def add_veg
@ingredients << :vegatable
self
end
end
salad = Salad.new #=> #<Salad:0x00007fc42151a3a0 @ingredients=[]>
salad.add_veg #=> #<Salad:0x00007fc42151a3a0 @ingredients=[:vegatable]>
salad.instance_eval {@ingredients} #=> [:vegatable]
salad.add_veg #=> #<Salad:0x00007fc42151a3a0 @ingredients=[:vegatable, :vegatable]>
salad.instance_eval {@ingredients} #=> [:vegatable, :vegatable]
class Stack
def initialize
@store = Array.new
end
def push(element)
@store.push(element)
self
end
def pop
@store.pop
self
end
end
stack = Stack.new
#=> #<Stack:0x00007fc42dd78ab8 @store=[]>
stack.push '1'
#=> #<Stack:0x00007fc42dd78ab8 @store=["1"]>
tack.push '1'
#=> #<Stack:0x00007fc42dd78ab8 @store=["1", "1"]>
stack.push '1'
#=> #<Stack:0x00007fc42dd78ab8 @store=["1", "1", "1"]>
stack.pop
#=> #<Stack:0x00007fc42dd78ab8 @store=["1", "1"]>
stack.pop
#=> #<Stack:0x00007fc42dd78ab8 @store=["1"]>
stack.pop
#=> #<Stack:0x00007fc42dd78ab8 @store=[]>
以上面的例子來講,我們做了多次push
, pop
,實體變數刪改元素。回傳self
,可以讓我們可以連鎖使用push
, pop
的動作,達到改動object
的目的
在Ruby當中,這不算是陌生的方法,有時候在報錯的時候都會看到。
除了報錯以外,我們還可以用method_missing做一些好玩的事情:
class Developer
def method_missing method, *args, &block
# 若 prefix 不為 it_day ,則繼承父層(預設)的 method_missing ➡️ 報錯
return super method, *args, &block unless method.to_s =~ /^it_\w+/
self.class.send(:define_method, method) do
"我要挑戰IT鐵人賽主題為 " + method.to_s.gsub(/^it_/, '').to_s + " 的文章"
end
self.send method, *args, &block
end
end
itday = Developer.new
itday.it_rails #=> "我要挑戰IT鐵人賽主題為 rails 的文章"
itday.it_javascript #=> "我要挑戰IT鐵人賽主題為 javascript 的文章"
當我們在寫條件句,或者case when
的時候,可以想想可以用什麼動態的寫法省去多餘的code。Rails
本身是個遵照慣例的框架,所以熟悉一段時間 Rails
之後,不難發現其實很多規律性的地方都可以使用動態生成的寫法來作替代。
接著我們來複習目前已經講了幾個設計流程
從Day18-22,我們會開始講解畫面。