이전에 Ruby 메타프로그래밍에 대해 읽은 적이 있을 것입니다.
하지만…
몇 가지 구체적인 예가 없으면 약간 혼란스러울 수 있습니다.
그래서 이 기사에서 :
Ruby 메타프로그래밍을 사용하는 몇 가지 인기 있는 오픈 소스 프로젝트를 살펴보겠습니다.
다음과 같은 프로젝트 :
- 레일
- 시나트라
- 종이 클립 보석
그들 모두는 일종의 메타프로그래밍을 사용합니다. .
코드를 살펴보고 정확히 무엇을 하는지 알아봅시다!
레일 예
Rails는 메타프로그래밍을 많이 사용하므로 처음부터 살펴보는 것이 좋습니다.
예를 들어 :
Rails 앱에서 현재 환경을 확인하려면 다음과 같이 하면 됩니다.
Rails.env.production?
하지만 env는 무엇입니까? ? 어떻게 작동합니까?
답변은 StringInquirer에 있습니다. String의 하위 클래스인 클래스 . 이 클래스는 method_missing을 사용합니다. env.production?을 호출할 수 있도록 env == production 대신 .
코드는 다음과 같습니다. :
def method_missing(method_name, *arguments)
if method_name[-1] == '?'
self == method_name[0..-2]
else
super
end
end
이것은 :
"메소드 이름이 물음표로 끝나면 비교를 수행하고, 그렇지 않으면 상위 체인으로 계속 이동하십시오."
원본 코드는 여기에서 찾을 수 있습니다.
시나트라 대표단
이전에 Sinatra를 사용한 적이 있다면 경로를 정의하는 데 두 가지 방법이 있음을 알 수 있습니다.
get사용 /post클래스 외부에서 직접 메서드를 사용할 수 있습니다.Sinatra::Application에서 상속하는 클래스 정의 .
Sinatra DSL(Domain-Specific Language) 메서드는 Sinatra::Application 내부에 정의되어 있습니다. , 그러면 이 클래스 외부에서 어떻게 사용할 수 있습니까?
여기에는 두 가지 일이 있습니다. :
- 메타프로그래밍
- 모듈 확장
Sinatra는 Sinatra::Delegator를 정의합니다. target에 메소드 호출을 위임하는 데 사용되는 base.rb 내부의 모듈 .
대상은 Sinatra::Application으로 설정됩니다. 기본적으로.
이것은 delegate의 단순화된 버전입니다. Sinatra::Delegator에 정의된 클래스 메서드 :
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
Delegator.target.send(method_name, *args, &block)
end
end
end
delegate :get, :patch, :put, :post, :delete
이 코드는 메소드 세트를 생성합니다(define_method 사용). ) 메소드 호출을 Sinatra::Application으로 전달합니다. (Delegator.target의 기본값 ).
그런 다음 main 객체는 Sinatra::Delegator에 정의된 메소드로 확장됩니다. , Sinatra DSL을 Sinatra::Application 외부에서 사용할 수 있도록 합니다. 수업.
Ruby에는 필요한 경우를 대비하여 메서드 위임을 위한 두 가지 내장 클래스가 있습니다. Delegator 및 Forwardable입니다.
종이 클립의 보석
Paperclip은 애플리케이션이 파일 업로드를 처리할 수 있게 해주는 보석입니다. 파일은 ActiveRecord와 연결되어 있습니다. 모델.
has_attached_file을 사용하여 모델에 첨부 파일을 정의할 수 있습니다. 방법.
좋아요 :
class User
has_attached_file :avatar, :styles => { :normal => "100x100#" }
end
이 메서드는 paperclip.rb에 정의되어 있으며 메타프로그래밍을 사용하여 모델에 메서드를 정의합니다.
이 메소드는 첨부 파일에 액세스하는 데 사용할 수 있습니다(Paperclip::Attachment 수업).
예를 들어 첨부 파일이 :avatar인 경우 , Paperclip avatar를 정의합니다. 모델에 대한 방법입니다.
다음은 define_instance_getter입니다. 그 책임이 있는 메소드:
# @name => The name of the attachment
# @klass => The ActiveRecord model where this method is being defined
def define_instance_getter
name = @name
options = @options
@klass.send :define_method, @name do |*args|
ivar = "@attachment_#{name}"
attachment = instance_variable_get(ivar)
if attachment.nil?
attachment = Attachment.new(name, self, options)
instance_variable_set(ivar, attachment)
end
end
end
이 코드에서 첨부 개체가 @attachment_#{name} 아래에 저장되어 있음을 알 수 있습니다. 인스턴스 변수.
이 예에서는 @attachment_avatar가 됩니다. .
그런 다음 이 첨부 파일이 이미 있는지 확인하고 없으면 새 Attachment을 만듭니다. 개체 및 인스턴스 변수를 설정합니다.
다음은 원본 소스 코드입니다.
결론
이미 보았듯이 메타프로그래밍은 매우 강력하고 유연한 기술이지만, "큰 힘에는 큰 책임이 따른다"라는 문구를 기억하고 싶을 때마다 이를 기억하십시오.
이 게시물이 마음에 드셨다면 제 뉴스레터 구독을 잊지 마세요 🙂