Computer >> 컴퓨터 >  >> 프로그램 작성 >> Ruby

Sinatra 작동 방식에 대한 빠른 분석

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단계로 구분된 요청 :

  1. 정적 파일이 먼저 확인됩니다. CSS, js 및 이미지와 같은 파일입니다. 이 설정은 "public"이라는 디렉토리가 있는 경우 기본적으로 활성화됩니다.
  2. 필터가 실행되기 전
  3. 경로 일치
  4. 애프터 필터 실행

이제 각 단계를 자세히 살펴보고 어떤 일이 일어나는지 자세히 알아볼 수 있습니다.

정적 파일 제공

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 개발자와도 함께 배울 수 있습니다. 🙂