如何在Sinatra中使用DSL元编程

问题描述 投票:1回答:1

我正在尝试使用DSL来管理同一路由内的不同语言环境,例如get "/test"

这是学习如何扩展Sinatra的练习,因此Rack::Locale或类似工具不是有效的答案。

基于请求JSON主体的主体,假设我以POST或PUT形式接收JSON,我想以特定的语言环境进行响应。

我目前有一个我认为自己需要的准系统脚本:

class Locale
  attr_reader :locale_id
  attr_reader :described_class

  alias :current_locale :locale_id

  def initialize(locale_id, &block)
    @locale_id = locale_id
    instance_eval &block
  end

end

def locale(locale_id, &block)
  Locale.new(locale_id, &block)
end

我缺少基于输入的request.body JSON中的语言环境做出响应的能力,并且这里的类还有其他我不知道的必要或缺失的东西。

如何使用它的示例是:

get '/' do 
   locale 'cs-CS' do 
     "Czech"
     #or db query or string
   end 
   locale 'en-UK' do 
     "British english"
     #or db query or string
   end
end

因此,为了更清楚地说明,我将尝试使用TDD方法:

作为用户,当我发送包含:"locale": "cs-CS"的JSON时,结果是捷克语。

ruby sinatra metaprogramming
1个回答
2
投票

您是否已阅读Extending The DSLREADME的条件部分?

现在,您并没有真正扩展DSL。我会稍微重新设计它,因为它看起来像您要在case语句上进行匹配,但这意味着要创建许多类或一个丑陋的匹配语句。但是,Sinatra已经有了一些非常好的方法来匹配路线和条件。因此,像这样的东西会更惯用:

post '/', :locale => "Czech" do
  "Czech"
end

post '/', :locale => "British English" do
  "British"
end

post '/', :locale => "en-GB" do
  "cs-CS"
end

post '/', :locale => "cs-CS" do
  "cs-CS"
end

如何执行此操作?首先,您需要一个过滤器来转换传入的JSON:

before do
  if request.media_type == "application/json"
    request.body.rewind
    @json = JSON.parse request.body.read
    @locale = @json["locale"] && Locales[@json["locale"]]
  end
end

然后您将需要一个条件来检查:

set(:locale) {|value|
  condition {
    !!@locale && (@locale == value || @json["locale"] == value)
  }
}

全部(app.rb):

require 'sinatra'

Locales = {
  'cs-CS' => "Czech",
  'en-GB' => "British English"
}

before do
  if request.media_type == "application/json"
    request.body.rewind
    @json = JSON.parse request.body.read
    @locale = @json["locale"] && Locales[@json["locale"]]
  end
end


set(:locale) {|value|
  condition {
    !!@locale && (@locale == value || @json["locale"] == value)
  }
}


post '/', :locale => "en-GB" do
  "cs-CS"
end


post '/', :locale => "cs-CS" do
  "cs-CS"
end

可以,但是不能作为扩展。因此,依靠我在顶部发布的文档:

require 'sinatra/base'

module Sinatra
  module Localiser

    Locales = {
      'cs-CS' => "Czech",
      'en-GB' => "British English"
    }

    def localise!(locales=Locales)
      before do
        if request.media_type == "application/json"
          request.body.rewind
          @json = JSON.parse request.body.read
          @locale = @json["locale"] && locales[@json["locale"]]
        end
      end

      set(:locale) {|value|
        condition {
          !!@locale && (@locale == value || @json["locale"] == value)
        }
      }
    end
  end
  register Localiser
end

现在它将扩展DSL。例如:

require "sinatra/localiser"
class Localised < Sinatra::Base
  register Sinatra::Localiser

  localise!


  post '/', :locale => "Czech" do
    "Czech"
  end


  post '/', :locale => "British English" do
    "British"
  end


  ["get","post"].each{|verb|
    send verb, "/*" do
      "ELSE"
    end
  }

  run! if app_file == $0
end

希望能为您澄清一些事情。

© www.soinside.com 2019 - 2024. All rights reserved.