Sinatra 1.2 新功能

作者: Konstantin Haase,发布于 2011 年 3 月 3 日,星期四

正如在 邮件列表 中宣布的那样,我们刚刚发布了 Sinatra 1.2.0。让我们仔细看看新功能。

Slim 支持

Sinatra 现在支持 Slim 模板引擎

require 'sinatra'
require 'slim'

get('/') { slim :index }

__END__

@@ index
! doctype html
html
  head
    title Sinatra With Slim
  body
    h1 Slim Is Fun!
    a href="https://haml-lang.com/" A bit like Haml, don't you think?

内联 Markaby

与 Builder 和 Nokogiri 模板一样,Markaby 现在可以直接在内联中使用

require 'sinatra'
require 'markaby'

get '/' do
  markaby do
    html do
      head { title "Sinatra With Markaby" }
      body { h1 "Markaby Is Fun!" }
    end
  end
end

布局引擎

每当你渲染一个模板,比如 some_page.haml,Sinatra 会为你使用一个相应的布局,比如 layout.haml。使用 :layout 选项,一直以来都可以使用不同的布局,但它仍然必须用相同的模板语言编写,在我们的例子中是 Haml。在 1.1 中,我们引入了 markdowntextilerdoc 模板。这些模板不能用于布局。因此,我们添加了 :layout_engine 选项,它可以轻松地将两种不同的模板引擎组合在一起

require 'sinatra'
require 'rdiscount'

# for all markdown files, use post.haml as layout
set :markdown, :layout_engine => :haml, :layout => :post

get '/' do
  # use index.haml for readme
  markdown :README, :layout => :index
end

get '/:post' do
  markdown params[:post].to_sym
end

当从一种模板语言迁移到另一种模板语言时,此功能也很方便,因为它允许你将 Erb 与 Haml 组合起来,例如。

条件过滤器

我们在 1.1 中引入了模式匹配过滤器。现在它们也支持条件

before :agent => /Song Bird/ do
  # ...
end

当然,这些也可以与模式组合起来

after '/api/*', :provides => :json do
  # ...
end

URL 助手

通常,Sinatra 不会提供任何视图助手方法。这些由扩展提供,不适合 Sinatra 的小而强大的核心方法。但是,构建 URL 是大多数人迟早会遇到的用例。正确构建 URL 有点复杂。考虑这个例子

get('/foo') { "<a href='/bar'>Will you make it?</a>" }
get('/bar') { "You made it!" }

随意运行它。有效,不是吗?那么,它有什么问题呢?

假设你的应用程序被另一个 Rack 应用程序“挂载”,例如在像这样的 config.ru

map('/there') { run Sinatra::Application }
map('/') { run MyRailsApp::Application }

现在,指向 /bar 的链接将最终发送到 MyRailsApp,而不是 Sinatra。注入 request.script_name 可以解决这个问题,但说实话,你有多常这样做呢?

现在,想象一下这些链接是在没有上下文的情况下呈现的,例如在 RSS 订阅源中或嵌入在另一个主机上。在这种情况下,你可能想要构建绝对 URL。这更加繁琐,因为你很可能会忘记处理反向代理、备用端口/协议,或者最终在代码中到处都是与 URL 相关的代码,而你应该做的是使用 url 助手

get('/foo') { "<a href='#{url '/bar'}'>You will make it!</a>" }
get('/bar') { "You made it!" }

由于你很可能将它与重定向一起使用,因此我们还将助手别名为 to

get('/foo') { redirect to('/bar') }
get('/bar') { "You made it!" }

1.9 上的命名捕获

Ruby 1.9 引入了 命名捕获 用于正则表达式。Sinatra 接受正则表达式来匹配路径。现在,命名捕获将自动填充 params

get %r{/(?<year>\d{4})/(?<month>\d{2})/(?<day>\d{2})/?} do
  date = Date.new params[:year].to_i, params[:month].to_i, params[:day].to_i
  @posts = Post.pubished_on date
  erb :posts
end

具有不同范围的模板

所有渲染方法现在都接受 :scope 选项

get '/:id' do |id|
  @post = Post.find id

  # without scope
  erb "<%= @post.name %>"

  # with scope
  erb "<%= name %>", :scope => @post
end

请注意,所有 Sinatra 助手方法和实例变量将 *不会* 可用。

可配置的重定向

在 1.1 中,我们确保所有重定向都是绝对 URI,以符合 RFC 2616 (HTTP 1.1)。如果你的反向代理配置存在问题,这将导致问题。如果是这样,你应该真正修复你的配置。如果你无法做到这一点,一个简单的 disable :absolute_redirects 现在将为你提供 1.0 行为。如上所示,你现在可以使用 to 助手与重定向一起使用。如果你的所有重定向都是应用程序本地,你现在可以 enable :prefixed_redirects 并完全跳过 to

enable :prefixed_redirects
get('/foo') { redirect '/bar' }
get('/bar') { "You made it!" }

我们没有默认启用它,以避免破坏兼容性,并允许你将重定向到其他 Rack 端点。

覆盖模板查找

一个受欢迎的功能请求是支持多个视图文件夹。但每个人都想要不同的语义。因此,我们没有选择一种方法,而是为你提供了实现自己的查找逻辑的方法

helpers do
  def find_template(*)
    puts "looking for index.txt"
    yield "views/index.txt"
    puts "apparently, index.txt doesn't exist, let's try index.html"
    yield "views/index.html"
  end
end

get "/" do
  haml :foo
end

Sinatra 将调用 find_template 来发现模板文件。在上面的示例中,我们不关心要使用哪个模板引擎或模板的名称。它将使用 views/index.txtviews/index.html 作为每个模板。让我们看一下标准实现

def find_template(views, name, engine)
  Tilt.mappings.each do |ext, klass|
    next unless klass == engine
    yield ::File.join(views, "#{name}.#{ext}")
  end
end

如你所见,它将在 views 文件夹中查找一个与模板同名的文件,该文件具有为模板引擎注册的任何文件扩展名。

如果你只想更改文件夹,你可能应该只调用 super

def find_template(views, *a, &b)
  super("#{views}/a", *a, &b)
  super("#{views}/b", *a, &b)
end

更多示例可以在 readme 中找到。

其他更改

  • send_file 现在接受 :last_modified 选项
  • 改进的错误处理