第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

首頁 慕課教程 Ruby 入門教程 Ruby 入門教程 44 使用 Ruby 編寫 DSL 語言

使用 Ruby 編寫 DSL 語言

領域特定語言(英語:domain-specific language、DSL)指的是專注于某個應用程序領域的計算機語言。又譯作領域專用語言。同名著作是 DSL 領域的豐碑之作,由世界級軟件開發(fā)大師和軟件開發(fā)“教父” Martin Fowler 歷時多年寫作。

Ruby 中很多的框架都采用了 DSL 語言的風格,比如:Grape 和 Rspec。今天讓我們學習使用 Ruby 的語言來寫一下 DSL。

1. 編寫第一個 DSL 語言

現(xiàn)在經理給我們提了一個需求:讓我們監(jiān)聽幾個數(shù)據(jù):用戶成功創(chuàng)建訂單數(shù)、用戶成功付款訂單數(shù)、商家及時放貨的訂單數(shù)、等等幾十個事件,每天更新一次,后臺管理員可以從后臺看到這些數(shù)據(jù)。

我們理想中的 DSL 代碼格式應該是這樣的:

listen "用戶成功創(chuàng)建訂單數(shù):" do
  # 從數(shù)據(jù)庫獲取用戶今天創(chuàng)建的訂單數(shù)
  # order = ...
  # order.count
end

代碼塊(Block)中返回需要顯示的數(shù)量。

由此我們可以寫出這個代碼。

實例:

def listen description
  puts "#{description}#{yield}" if yield
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  300
end

listen "用戶成功付款訂單數(shù):" do
  150
end

listen "商家及時放貨的訂單數(shù):" do
  130
end

# ---- 輸出結果 ----
用戶成功創(chuàng)建訂單數(shù):300
用戶成功付款訂單數(shù):150
商家及時放貨的訂單數(shù):130

2. DSL中使用變量

2.1 定義常規(guī)變量

如果我們現(xiàn)在要在 DSL 中插入變量應該怎們辦呢,比如增加一個必須大于 150 才通知的限制。在上一章節(jié)的作用域中我們可以學到,變量是可以在閉包外定義作用于閉包內的,所以我們可以這樣的改動代碼。

實例:

limit = 150

listen "用戶成功創(chuàng)建訂單數(shù):" do
  order_count = 300
  order_count > limit ? order_count: nil
end

listen "用戶成功付款訂單數(shù):" do
  order_count = 150
  order_count > limit ? order_count: nil
end

listen "商家及時放貨的訂單數(shù):" do
  order_count = 130
  order_count > limit ? order_count: nil
end

# ---- 輸出結果 ----
用戶成功創(chuàng)建訂單數(shù):300

2.2 變量集中初始化

但是如果我們需要增加很多種變量,這樣定義變量的方式會讓人感覺散亂又無序,一般DSL的語法會讓我們定義變量在一個專門定義變量的塊中,下面就是一個例子。

實例:

define do
  @limit = 150
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  order_count = 300
  order_count > @limit ? order_count: nil
end

listen "用戶成功付款訂單數(shù):" do
  order_count = 150
  order_count > @limit ? order_count: nil
end

listen "商家及時放貨的訂單數(shù):" do
  order_count = 130
  order_count > @limit ? order_count: nil
end

我們將上述代碼制成一個 event.rb,然后在實現(xiàn)代碼中進行引用:

實例:

@defines = []
@listens = []

def define &block
  @defines << block
end

def listen description, &block
  @listens << {description: description, condition: block}
end

load 'event.rb'

@listens.each do |listen|
  @defines.each do |define|
    define.call
  end 
  condition = listen[:condition].call
  puts "#{listen[:description]}#{condition}" if condition
end

# ---- 輸出結果 ----
用戶成功創(chuàng)建訂單數(shù):300

Tips:load 方法會加載 event.rb 并執(zhí)行文件中的代碼。

解釋:

在實例中我們將塊yield換成了&proc的形式,我們定義了兩個處于頂級作用域中的變量:@defines@listens,我們將每次從define中定義的proc對象都保存在了@defines中,將所有監(jiān)聽的事件也都保存到了@listens中,在后面的代碼里面,每一次我們處理listen事件的時候都會運行define,這樣就完成了變量集中初始化。

2.3 消除事件之間共享頂級作用域變量

2.3.1 潔凈室

上述處理方法中,不同事件頂級實例變量會存在一個共享的問題,處理這個問題之前,首先讓我了解一下潔凈室(Clean Room)的概念。

潔凈室是一個用來執(zhí)行塊的環(huán)境。理想的潔凈室是不應該有任何的方法以及實例變量的,所以不會產生任何方法名或者變量名的沖突。因此BasicObjectObject往往被用來充當潔凈室。

實例:

obj = Object.new
obj.instance_eval do
  @a = 1
  @b = 2
  @c = 3
end

obj.instance_eval do 
  puts "@a == #{@a}"
  puts "@b == #{@b}"
  puts "@c == #{@c}"
  puts "sum == #{@a + @b + @c}"
end

# ---- 輸出結果 ----
@a == 1
@b == 2
@c == 3
sum == 6

解釋:

讓我們創(chuàng)建一個Object的實例作為潔凈室,使用instance_eval在潔凈室第一個塊中里面定義三個變量,在第二個塊中定義4個方法。因為他們的作用域是這個潔凈室的實例,所以會得到最后的輸出結果。

2.3.2 用潔凈室來處理上述問題

讓我們使用潔凈室處理剛剛頂級作用域的問題。

先修改一下event.rb。

define do
  @limit = 150 
end

listen "用戶成功創(chuàng)建訂單數(shù):" do
  @num = 1 
  puts "@num1 == #{@num}"
  order_count = 300 
  order_count > @limit ? order_count: nil 
end

listen "用戶成功付款訂單數(shù):" do
  @num = @num.to_i + 1
  puts "@num2 == #{@num}"
  order_count = 150 
  order_count > @limit ? order_count: nil 
end

listen "商家及時放貨的訂單數(shù):" do
  @num = @num.to_i + 1
  puts "@num3 == #{@num}"
  order_count = 130 
  order_count > @limit ? order_count: nil 
end

重新運行一下腳本,得到結果:

@num1 == 1
用戶成功創(chuàng)建訂單數(shù):300
@num2 == 2
@num3 == 3

每一個事件之間應該是獨立的,不應該共享不必要的變量,為此,我們使用潔凈室修改一下實現(xiàn)代碼。

實例:

@defines = []
@listens = []

def define &block
  @defines << block
end

def listen description, &block
  @listens << {description: description, condition: block}
end

load 'event.rb'

@listens.each do |listen|
  env = Object.new
  @defines.each do |define|
    env.instance_eval &define
  end 
  condition = env.instance_eval &listen[:condition]
  puts "#{listen[:description]}#{condition}" if condition
end

# ---- 輸出結果 ----
@num1 == 1
用戶成功創(chuàng)建訂單數(shù):300
@num2 == 1
@num3 == 1

解釋:

我們在之前的基礎上,每一次定義事件的時候創(chuàng)建了一個潔凈室,這樣實例變量的作用范圍就從頂級作用域變?yōu)榱藵崈羰覍ο笾?,事件之間就不會存在共享變量的情況了。

3. 小結

本章節(jié)中我們學習到了如何使用 Ruby 去寫一個 DSL 語言,了解了 DSL 語言中使用不同變量的方法,學習了潔凈室的使用方法。