2011-02-08 28 views

Trả lời

34

Bạn có thể làm điều này rất dễ dàng với các Docilegem, hoặc bằng đá quý, hoặc bằng cách đọc the source code để hiểu cách hoạt động của nó.

Giả sử bạn muốn thực hiện một Pizza qua một DSL

Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce) 

Và bạn sử dụng một mô hình builder để làm cho Pizza

class PizzaBuilder 
    def cheese(v=true); @cheese = v; end 
    def pepperoni(v=true); @pepperoni = v; end 
    def bacon(v=true); @bacon = v; end 
    def sauce(v=nil); @sauce = v; end 
    def build 
    Pizza.new([email protected], [email protected], [email protected], @sauce) 
    end 
end 

Và bạn muốn có một DSL, nói điều gì đó như

@sauce = :extra 
pizza do 
    bacon 
    cheese 
    sauce @sauce 
end 
# => should return Pizza.new(true, false, true, :extra) 

Tất cả bạn phải làm là xác định bánh pizza phương pháp như

require 'docile' 

def pizza(&block) 
    Docile.dsl_eval(PizzaBuilder.new, &block).build 
end 

Và bang, bạn đã hoàn tất.

+5

-1 vì, trong khi Docile chắc chắn rất đẹp, sử dụng nó * bỏ qua * quá trình viết một DSL thông thường thay vì giải thích cách thực hiện. – 00dani

+4

@ 00Davo trong khi bạn được hưởng ý kiến ​​của mình, đó là một cử chỉ rất không thân thiện. Đặc biệt là xem xét mã nguồn cho Docile là một cách tuyệt vời để tìm hiểu cách tạo một DSL trong ruby. –

+0

Hmm. Tôi đã xem qua nguồn, và bạn nói đúng; nó thực sự khá minh họa. Tôi sẽ thay đổi phiếu bầu của mình nhưng cũng khuyên bạn nên chỉnh sửa câu trả lời để bao gồm một tham chiếu đến nguồn Docile. (Oh. Tôi vừa thử và dường như lá phiếu của tôi bị khóa trong khi câu trả lời không được chỉnh sửa, vì vậy bạn sẽ thực sự * cần * để chỉnh sửa, có vẻ như?) – 00dani

4

tôi thấy hướng dẫn này rất tốt, vì nó bao gồm một cách rõ ràng hai mô hình rất quan trọng của việc sử dụng yieldinstance_eval:

How do I build DSLs with yield and instance_eval?

+0

Tôi đang tìm kiếm ví dụ này mà tôi đã đọc khi tôi còn là người mới bắt đầu. Đây là một trong những ví dụ tốt nhất mà tôi đã đọc về việc xây dựng một DSL. –

0

Một điều kiện tiên quyết để viết một DSL là sự hiểu biết một số kỹ thuật lập trình tiên tiến như khối năng suất , Phương thức tra cứu phương thức của Ruby và method_missing(), vv Đọc Metaprogramming Ruby là cách tốt nhất để phát triển các kỹ năng Ruby tiên tiến này (cuốn sách cũng chứa một phần viết các DSL nội bộ).

Tôi đã viết blog post on how to create a Ruby DSL to generate HTML markup trong khoảng 20 dòng mã. Tốt hơn hết là bắt đầu với một số ví dụ đồ chơi nhỏ, hơn là nhảy ngay vào một ứng dụng cấp sản xuất như Erector. Nghiên cứu mã nguồn của Docile gem theo gợi ý của ms-tg là tuyệt vời, nhưng nó vẫn có thể hơi lấn át như DSL đầu tiên của bạn. Tìm hiểu một số kỹ thuật lập trình Ruby nâng cao, xây dựng một số ví dụ về đồ chơi và sau đó nghiên cứu mã nguồn Docile.

Dưới đây là làm thế nào để có được một số chức năng của đá quý ngoan ngoãn như được giải thích bởi @ ms-tg từ đầu:

def dsl(obj, &block) 
    obj.instance_eval(&block) 
end 

Pizza = Struct.new(:cheese, :pepperoni, :bacon, :sauce) 
obj = Pizza.new 

dsl(obj) do |pizza| 
    pizza.cheese = true 
    pizza.pepperoni = true 
    pizza.sauce = :extra 
end 

p obj 
# => #<struct Pizza cheese=true, pepperoni=true, bacon=nil, sauce=:extra> 

Phương pháp dsl() cũng có thể được sử dụng cho các ví dụ tầm thường hơn, như Docile README ví dụ về xây dựng một mảng:

arr = [] 

dsl(arr) do 
    push(1) 
    push(2) 
    pop 
    push(3) 
end 

p arr 
+1

Chắc chắn, bạn có thể sử dụng 'instance_eval' trần truồng, nhưng sau đó bạn sẽ không thể sử dụng biến mẫu hoặc biến cục bộ và bạn sẽ không thể chuỗi ngữ cảnh (nghĩ DSL đa cấp, phổ biến) . Điều đó có ý nghĩa? Vì vậy, những gì Docile bổ sung là các giải pháp tiêu chuẩn cho những vấn đề này, để các dự án không phải tiếp tục khám phá và phát minh lại bánh xe về điều này. –

Các vấn đề liên quan