Sinatra는 Ruby 웹 프레임워크입니다.
Rails의 남동생 같아요...
Sinatra가 어떻게 작동하는지 살펴보겠습니다. :
- 프로젝트에 Sinatra가 필요하면 어떻게 됩니까?
- 경로 일치는 어떻게 작동합니까?
- 요청 및 응답은 어떻게 처리되나요?
질문은 많지만 시간은 너무 짧습니다...
문제 없습니다!
나는 당신을 위해 열심히 일했고 당신이 더 빨리 배울 수 있도록 이 질문에 답하는 이 기사를 준비했습니다!
시나트라 초기화
모든 것이 하나의 파일로 시작됩니다:sinatra.rb
.
이 파일이 하는 모든 일은 main.rb
를 요구하는 것입니다. , 별로 흥미롭지 않습니까?
더 재미있어집니다!
main.rb
내부 base.rb
에 대한 요구 사항을 찾을 수 있습니다. &옵션 구문 분석을 위한 코드(포트, 환경, 자동 모드 등)도 찾을 수 있습니다.
Sinatra는 optparse
를 사용합니다. , Ruby의 표준 라이브러리에서.
여기서 또 무엇을 찾을 수 있습니까?
이 at_exit
를 살펴보세요. 차단:
at_exit { Application.run! if $!.nil? && Application.run? }
이것은 프로그램이 종료될 때 실행될 약간의 코드입니다.
모든 코드가 Ruby에서 읽히고 루프, 절전 또는 이와 유사한 것이 없기 때문에 프로그램이 자연스럽게 종료됩니다.
...하지만 at_exit
가 끝나기 직전 차단이 발동됩니다!
그런 다음 Sinatra가 요청을 처리할 수 있도록 웹 서버를 인수하고 시작합니다.
이를 수행하는 코드는 다음과 같습니다. :
begin start_server(handler, server_settings, handler_name, &block) rescue Errno::EADDRINUSE $stderr.puts "== Someone is already performing on port #{port}!" raise end # Part of base.rb `run!` method
아 그리고 여기서 또 다른 중요한 일이 발생합니다:
extend Sinatra::Delegator
Sinatra::Delegator
get
과 같은 Sinatra DSL 메소드를 정의하는 모듈입니다. , post
&set
.
이것이 바로 당신이 할 수 있는 이유입니다:
get '/' do puts "Hello World!" end
Sinatra는 전역 main
을 확장합니다. 이 모듈이 있는 개체입니다.
요청 및 응답 처리
자, 이제 새 연결을 수락할 준비가 된 서버가 실행 중입니다.
하지만 새로운 연결이 수신되면 어떻게 됩니까?
Sinatra는 Rails 및 기타 Ruby 웹 프레임워크와 마찬가지로 Rack을 사용하여 모든 하위 수준 항목을 처리합니다.
랙은 call
을 기대합니다. 응용 프로그램에서 사용할 수 있습니다. 이는 Rack을 초기화할 때 Rack에 제공하는 객체일 뿐입니다.
Sinatra의 경우 이 객체는 Sinatra::Base
입니다. 수업.
방법은 다음과 같습니다. :
# Rack call interface. def call!(env) @env = env @request = Request.new(env) @response = Response.new invoke { dispatch! } invoke { error_block!(response.status) } unless @env['sinatra.error'] @response.finish end # Modified version of Sinatra's call method (for clarity)
dispatch!
를 조사해야 할 것 같습니다. 요청이 처리되는 방법을 보여주기 위해 옆에 있는 메소드
방법은 다음과 같습니다. :
def dispatch! invoke do static! if settings.static? && (request.get? || request.head?) filter! :before route! end rescue ::Exception => boom invoke { handle_exception!(boom) } ensure filter! :after unless env['sinatra.static_file'] end # Edited down to the important parts
4단계로 구분된 요청 :
- 정적 파일이 먼저 확인됩니다. CSS, js 및 이미지와 같은 파일입니다. 이 설정은 "public"이라는 디렉토리가 있는 경우 기본적으로 활성화됩니다.
- 필터가 실행되기 전
- 경로 일치
- 애프터 필터 실행
이제 각 단계를 자세히 살펴보고 어떤 일이 일어나는지 자세히 알아볼 수 있습니다.
정적 파일 제공
static!
방법은 매우 간단합니다.
def static!(options = {}) return if (public_dir = settings.public_folder).nil? path = File.expand_path("#{public_dir}#{URI_INSTANCE.unescape(request.path_info)}" ) return unless File.file?(path) cache_control(*settings.static_cache_control) if settings.static_cache_control? send_file(path, options) end
이 코드는 요청된 파일이 있는지 확인한 다음 "Cache Control" HTTP 헤더를 설정합니다.
마지막 줄에서 send_file
을 호출합니다. &이름 그대로 🙂
필터링 전
이전 필터를 사용하면 일치하는 경로를 찾기 전에 코드를 실행할 수 있습니다.
필터를 추가하는 방법은 다음과 같습니다.
# Define a before filter. # Runs before all requests within the same context as route handlers # and may access/modify the request and response. @filters = {:before => [], :after => []} def before(path = /.*/, **options, &block) add_filter(:before, path, options, &block) end def after(path = /.*/, **options, &block) add_filter(:after, path, options, &block) end def add_filter(type, path = /.*/, **options, &block) filters[type] << compile!(type, path, block, options) end
filters
에서 볼 수 있듯이 각 필터 유형에 대해 하나씩, 두 개의 키가 있는 해시입니다.
하지만 compile!
이란 무엇입니까? ?
이 메서드는 패턴, 조건 배열 및 래퍼의 3가지 요소가 있는 배열을 반환합니다.
동일한 방법이 경로 생성에 사용됩니다(get
또는 post
차단):
def get(path, opts = {}, &block) route('GET', path, opts, &block) end def route(verb, path, options = {}, &block) signature = compile!(verb, path, block, options) (@routes[verb] ||= []) << signature signature end # Methods edited for clarity
이를 통해 Sinatra 필터가 경로와 동일한 방식으로 작동하고 작동한다는 것을 알 수 있습니다.
경로 일치
요청 처리 주기의 다음 단계는 경로 일치입니다.
def route!(base = settings, pass_block = nil) routes = base.routes[@request.request_method] routes.each do |pattern, conditions, block| process_route(pattern, conditions) route_eval end route_missing end # Edited method
이 코드는 요청 메소드(get
, post
등).
경로 일치는 process_route
내에서 발생합니다. 방법:
def process_route(pattern, keys, conditions, block = nil, values = []) route = @request.path_info route = '/' if route.empty? and not settings.empty_path_info? return unless match = pattern.match(route) end
pattern
정규식입니다.
경로가 경로와 조건 모두와 일치하면 route_eval
블록을 평가하는 호출됩니다(get
/ post
route) &경로 일치 프로세스를 종료합니다.
# Run a route block and throw :halt with the result. def route_eval throw :halt, yield end
이것은 비정상적인 catch
를 사용합니다. / throw
흐름 제어를 위한 메커니즘입니다.
코드 흐름을 따라가는 것이 매우 혼란스러울 수 있기 때문에 사용하지 않는 것이 좋습니다. 그러나 이 기능이 실제로 사용되는 예를 보는 것은 흥미롭습니다.
응답 구축
요청 주기의 마지막 단계는 응답을 준비하는 것입니다.
그러면 응답은 어디로 가나요?
call
메소드는 다음과 같이 응답을 수집합니다.
res = catch(:halt) { yield }
이 결과는 body
를 사용하여 응답 본문에 할당됩니다. 방법:
body(res)
이제 우리가 시작한 곳을 되돌아보면 call
메소드에서 다음 코드 라인을 찾을 수 있습니다.
@response.finish
이것은 finish
을 호출합니다. @response
의 메소드 , 이는 Rack::Response
입니다. 개체.
즉, 이것은 실제로 응답을 클라이언트로 보내도록 트리거합니다.
보너스:설정 방법 작동 방식
set 메소드는 Sinatra의 DSL(Domain-Specific Language)의 일부이며 Sinatra 애플리케이션의 어디에서나 구성 옵션을 설정할 수 있습니다.
예 :
set :public_folder, '/var/www'
set
를 사용할 때마다 Sinatra는 3가지 방법을 만듭니다(메타프로그래밍을 통해):
define_singleton("#{option}=", setter) if setter define_singleton(option, getter) if getter define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
3가지 방법은 (public_folder
포함) 예):
- public_folder
- public_folder=
- public_folder?
이 메소드는 setter 메소드(public_folder=
) 이미 존재하는 경우:
if respond_to?("#{option}=") && !ignore_setter return __send__("#{option}=", value) end
메타프로그래밍은 무료가 아니므로 options
만 사용하겠습니다. 해시시. 그런 멋진 방법은 필요하지 않습니다.
요약
Sinatra가 초기화되는 방법, 요청을 처리하는 방법 및 응답이 생성될 때까지 걸리는 여러 단계를 배웠습니다. 이것은 몇 가지 Ruby 트릭을 배우고 Sinatra를 더 잘 이해하는 데 도움이 될 것입니다!
이 게시물을 공유하는 것을 잊지 마세요. 다른 Ruby 개발자와도 함께 배울 수 있습니다. 🙂