从Sinatra内部召唤Sinatra

我有一个基于Sinatra的REST服务应用程序,我想从其中一个路由中调用其中一个资源,从而有效地组合一个资源。 例如

get '/someresource' do otherresource = get '/otherresource' # do something with otherresource, return a new resource end get '/otherresource' do # etc. end 

重定向不起作用,因为我需要对第二个资源进行一些处理并从中创建一个新的。 显然我可以a)使用RestClient或其他一些客户端框架或者b)构造我的代码所以其他资源的所有逻辑都在一个方法中然后调用它,但是,感觉如果我可以重新使用它会更清洁使用他们的DSL从Sinatra内部使用我的资源。

通过快速而脏的机架请求并直接调用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基础知识的文章。 什么是机架? 和使用Rack 。

另一个选项(我知道这不是回答你的实际问题)是将你的公共代码(甚至是模板渲染)放在一个辅助方法中,例如:

 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 

Sinatra的文档涵盖了这一点 – 基本上您使用底层rack接口的call方法:

http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route

触发另一条路线

有时传球不是你想要的,而是你希望获得调用另一条路线的结果。 只需使用call即可实现此目的:

 get '/foo' do status, headers, body = call env.merge("PATH_INFO" => '/bar') [status, headers, body.map(&:upcase)] end get '/bar' do "bar" end 

这可能适用于您的情况,也可能不适用,但是当我需要创建这样的路线时,我通常会沿着以下方向尝试:

 %w(main other).each do |uri| get "/#{uri}" do @res = "hello" @res.upcase! if uri == "other" @res end end 

基于AboutRuby的答案 ,我需要支持在lib/public获取静态文件以及查询参数和cookie(用于维护经过身份validation的会话。)我还选择在非200响应上引发exception(并在调用函数中处理它们) 。

如果你在sinatra/base.rb跟踪Sinatra的self.call方法,它需要一个env参数并用它构建一个Rack :: Request ,所以你可以在那里挖掘以查看支持的参数。

我不记得返回语句的所有条件(我认为有一些Ruby 2的更改),所以请随意调整您的要求。

这是我正在使用的function:

  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