2011-02-08 3 views

답변

34

나는 이것이 루비에 DSL 구축에 기사의 큰 일련의 생각 그것이 어떻게 작동 하는지를 이해하기 위해서. ,

당신은 DSL

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

를 통해 피자을 만들고 싶어 말 그리고 당신은 피자

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 

을 만들기 위해 빌더 패턴을 사용 그리고 당신은 DSL을 원하는 다음과 같이 말하십시오.

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

당신이해야 할 모든

require 'docile' 

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

그리고 강타로 피자 방법을 정의, 당신은 완료됩니다.

명시 적으로 yieldinstance_eval를 사용하는 매우 중요한 두 가지 패턴 커버로 나는, 아주 좋은 튜토리얼을 찾을
34

어느 보석을 사용하거나 the source code을 읽어 :

+5

-1 왜냐하면 Docile은 확실히 훌륭하지만 사용하는 방법을 설명하는 대신 일반 Ruby DSL을 작성하는 프로세스를 우회하기 때문입니다. – 00dani

+4

@ 00Davo 당신이 당신의 의견을 말할 자격이있는 동안 그것은 매우 비우호적 인 제스처입니다. 특히 Docile의 소스 코드가 루비에서 DSL을 만드는 법을 배우는 좋은 방법이라고 생각하면됩니다. –

+0

흠. 근원을 살펴 봤는데 네 말이 맞아. 그것은 참으로 실례입니다. 나는 투표를 변경할 것이지만 Docile 소스에 대한 참조를 포함하도록 답변을 편집 할 것을 권장합니다. (오, 방금 시도했는데 대답이 편집되지 않는 한 내 투표가 잠겨 있기 때문에 실제로 편집해야 할 필요가 있습니다.) – 00dani

0

DSL이 작성을위한 전제 조건은 항복 블록 같은 일부 고급 프로그래밍 기법을 이해된다 , Ruby의 메소드 검색 프로세스 및 method_missing() 등을 포함합니다.이 고급 Ruby 기술을 개발하는 가장 좋은 방법은 Metaprogramming Ruby입니다.이 책에는 내부 DSL 작성에 대한 섹션도 포함되어 있습니다.

약 20 행의 코드에 blog post on how to create a Ruby DSL to generate HTML markup을 썼습니다. Erector과 같은 프로덕션 급 애플리케이션으로 바로 넘어 가기보다는 작은 장난감 예제로 시작하는 것이 훨씬 낫습니다. ms-tg에서 제안한대로 Docile gem의 소스 코드를 배우는 것은 훌륭하지만 처음 DSL로는 여전히 압도적 인 편입니다. 몇 가지 고급 Ruby 프로그래밍 기술을 배우고 장난감 예제를 작성한 다음 Docile 소스 코드를 공부하십시오.

여기 처음부터 @ MS-TG에 의해 설명 된대로 유순 한 보석의 일부 기능을 얻을 방법은 다음과 같습니다

dsl() 방법의 Docile README 예처럼, 더 사소한 예제도 사용할 수 있습니다
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> 

+1

물론 네이티브'instance_eval'을 사용할 수는 있지만 인스턴스 변수 나 로컬 변수를 사용할 수 없기 때문에 컨텍스트를 연결할 수 없습니다 (일반적으로 멀티 레벨 DSL을 생각하십시오) . 말이 돼? 따라서 Docile이 추가 한 것은 이러한 문제에 대한 표준 솔루션이므로 프로젝트가이 문제에 대한 바퀴를 다시 발견하고 다시 발명 할 필요는 없습니다. –

관련 문제