Вызов Синатры из Синатры
У меня есть приложение службы REST на основе Sinatra, и я хотел бы назвать один из ресурсов на одном из маршрутов, эффективно составляя один ресурс из другого. Например.
get '/someresource' do
otherresource = get '/otherresource'
# do something with otherresource, return a new resource
end
get '/otherresource' do
# etc.
end
Переадресация не будет работать, так как мне нужно сделать некоторую обработку на втором ресурсе и создать новую. Очевидно, я мог бы a) использовать RestClient или какую-либо другую структуру клиента или b) структурировать свой код, поэтому вся логика для otherresource находится в методе и просто вызывает это, однако, похоже, что было бы намного чище, используйте мои ресурсы из Sinatra, используя их DSL.
Ответы
Ответ 1
Я смог что-то взломать, сделав быстрый и грязный запрос на стойку и напрямую призывая приложение Sinatra (приложение для стойки). Это не очень, но это работает. Обратите внимание, что было бы лучше извлечь код, который генерирует этот ресурс в вспомогательный метод вместо того, чтобы делать что-то подобное. Но это возможно, и могут быть лучшие, более чистые способы сделать это, чем это.
#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'
get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')
resource.upcase
end
get '/otherresource' do
"test"
end
Если вы хотите узнать больше о том, что происходит за кулисами, я написал несколько статей об основах стойки, которую вы можете прочитать. Существует Что такое Rack? и Использование стойки.
Ответ 2
Другая опция (я знаю, что это не отвечает на ваш реальный вопрос) заключается в том, чтобы поместить ваш общий код (даже рендеринг шаблона) в вспомогательный метод, например:
helpers do
def common_code( layout = true )
@title = 'common'
erb :common, :layout => layout
end
end
get '/foo' do
@subtitle = 'foo'
common_code
end
get '/bar' do
@subtitle = 'bar'
common_code
end
get '/baz' do
@subtitle = 'baz'
@common_snippet = common_code( false )
erb :large_page_with_common_snippet_injected
end
Ответ 3
Документация Sinatra охватывает это: по существу вы используете базовый rack
интерфейс call
:
http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route
Запуск другого маршрута
Иногда прохождение - это не то, что вы хотите, вместо этого вы хотите получить результат вызова другого маршрута. Просто используйте звоните, чтобы достичь этого:
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end
Ответ 4
Это может быть или не быть применимым в вашем случае, но когда Ive необходимо создать маршруты, подобные этому, я обычно пытаюсь что-то в этом направлении:
%w(main other).each do |uri|
get "/#{uri}" do
@res = "hello"
@res.upcase! if uri == "other"
@res
end
end
Ответ 5
Основываясь на AboutRuby answer, мне нужно было поддерживать выборку статических файлов в lib/public
, а также параметры запроса и файлы cookie (для поддержки аутентифицированных сеансов). Я также выбрал для получения исключений из ответов не-200 (и обрабатывать их в вызывающих функциях).
Если вы трассируете метод Sinatra self.call
в sinatra/base.rb
, он принимает параметр env
и создает Rack:: Request с ним, чтобы вы могли копаться там, чтобы увидеть, какие параметры поддерживаются.
Я не помню всех условий операторов return (я думаю, что были некоторые изменения Ruby 2), поэтому не стесняйтесь настраивать свои требования.
Здесь функция, которую я использую:
def get_route url
fn = File.join(File.dirname(__FILE__), 'public'+url)
return File.read(fn) if (File.exist?fn)
base_url, query = url.split('?')
begin
result = self.call('REQUEST_METHOD' => 'GET',
'PATH_INFO' => base_url,
'QUERY_STRING' => query,
'rack.input' => StringIO.new,
'HTTP_COOKIE' => @env['HTTP_COOKIE'] # Pass auth credentials
)
rescue Exception=>e
puts "Exception when fetching self route: #{url}"
raise e
end
raise "Error when fetching self route: #{url}" unless result[0]==200 # status
return File.read(result[2].path) if result[2].is_a? Rack::File
return result[2].join('') rescue result[2].to_json
end