Вызов Синатры из Синатры

У меня есть приложение службы 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