크리스탈 - 플러그인 패턴

5787 단어 crystal

영감



나는 최근에added Lucky to TechEmpower benchmarks 다른 프레임워크가 그렇게 잘 수행하기 위해 무엇을 하고 있는지에 대한 기사/비디오를 찾고 있었습니다. Optimization Techniques Used by the Benchmark Winners이라는 Jeremy Evans의 정말 좋은 강연을 보았습니다. Lucky를 변경하기 위한 목록을 가져오는 대신 그가 자신의 프레임워크를 디자인한 방식에 매료되었습니다Roda . 특히 플러그인을 완전히 사용하여 동작을 확장하고 추가한다는 아이디어가 마음에 들었습니다. 프레임워크의 소스 코드를 볼 기회가 있으면 클래스가 거의 비어 있음을 알 수 있습니다. 모든 동작은 플러그인을 통해 추가됩니다. 이것이 Crystal에서 가능한지 정말 보고 싶었습니다.

도전



Roda에서는 플러그인을 추가할 때 중첩된 모듈을 포함하고 확장합니다.

plugin CustomRender

# becomes...

include(CustomRender::InstanceMethods) if defined?(CustomRender::InstanceMethods)
extend(CustomRender::ClassMethods) if defined?(CustomRender::ClassMethods)
# and more...


또한 요청 및 응답 개체에 모듈을 추가하지만 Crystal로 변환할 때의 문제를 전달하기에 충분한 코드입니다.
  • 런타임에 모듈을 포함할 수 없음
  • defined? 방법이 없습니다

  • 해결책



    Crystal은 런타임에 모듈을 포함할 수 없지만 많은 동일한 작업을 수행할 수 있는 매크로가 있습니다. 원래 아이디어는 다음과 같은 매크로를 갖는 것이었습니다.

    macro plugin(type)
      include {{ type }}::InstanceMethods
      extend {{ type }}::ClassMethods
    end
    


    그것의 유일한 문제는 Roda가 가진 조건부 측면입니다. 플러그인이 클래스 메소드를 추가할 필요가 없다면 해당 모듈을 정의할 필요가 없고 프레임워크가 이를 처리할 수 있어야 합니다. 내가 찾은 유일한 해결책은 두 번째 매크로에 위임하는 것입니다.

    macro extend_self(instance_methods, class_methods)
      {% if ims = instance_methods.resolve? %}
        include {{ ims }}
      {% end %}
      {% if cms = class_methods.resolve? %}
        extend {{ cms }}
      {% end %}
    end
    
    macro plugin(type)
      extend_self({{ "#{type}::InstanceMethods".id }}, {{ "#{type}::ClassMethods".id }})
    end
    


    두 번째 매크로를 사용하는 이유는 모듈이 정의되었는지 확인하기 위해 Path#resolve? method이 필요하고 이것이 매크로에서 Path 개체를 동적으로 생성하는 유일한 방법이기 때문입니다. 더 좋은 방법이 생각나시면 알려주세요!

    plugin CustomRender
    
    # becomes...
    
    extend_self(CustomerRender::InstanceMethods, CustomRender::ClassMethods)
    
    # becomes...
    
    include CustomerRender::InstanceMethods
    extend CustomRender::ClassMethods
    


    아직 Rod를 Crystal로 실제로 변환하지 못했지만 희망합니다. 또한 그 과정에서 더 많은 게시물을 올릴 수 있기를 바랍니다.

    좋은 웹페이지 즐겨찾기