Ruby 如何在Sinatra中使用DSL元编程

Ruby 如何在Sinatra中使用DSL元编程,ruby,sinatra,metaprogramming,Ruby,Sinatra,Metaprogramming,我正在尝试使用DSL来管理同一路线中的不同地区,如get”/test“。 这是一个学习如何扩展Sinatra的练习,因此Rack::Locale或类似工具不是有效的答案 基于请求JSON主体的主体,假设我以POST或PUT的形式接收JSON,我希望使用特定的语言环境进行响应 我目前有一个我认为我需要的赤裸裸的脚本: class Locale attr_reader :locale_id attr_reader :described_class alias :current_loca

我正在尝试使用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
区域设置“cs”do
“捷克语”
#或数据库查询或字符串
结束
地点“en-UK”do
“英国英语”
#或数据库查询或字符串
结束
结束
因此,为了更清楚地澄清,我将尝试采用TDD方法:

作为用户,当我发送一个包含以下内容的JSON时:
“locale”:“cs cs”
结果是捷克语。

您阅读了吗

现在,您并没有真正扩展DSL。我会稍微重新设计它,因为它看起来像是要匹配case语句,但这意味着要创建大量的类或丑陋的匹配语句。但是,西纳特拉已经有了一些非常好的方式来匹配路线和条件。因此,类似这样的内容更为惯用:

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

post '/', :locale => "British English" do
  "British"
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/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
需要“sinatra/定位器”
类本地化“捷克”do
“捷克语”
结束
post'/',:locale=>“英国英语”do
“英国人”
结束
[“get”,“post”]。每个{动词|
发送动词“/*”do
“其他”
结束
}
跑如果应用程序文件==$0
结束

希望这能帮助你澄清一些事情。

你能从路线处理程序那里展示你使用它的尝试吗?当然<代码>获取“/”do locale“cs cs”do“捷克”end locale“en UK”do“British english”end很抱歉,我无法在注释中正确设置格式。请勿使用注释添加代码或输入数据或所需结果。评论是针对要求澄清的问题和疑问。编辑您的问题,并将信息放入最初包含的位置。不要使用“已编辑”或“已更新”标记来标记更改,只需添加它,这样问题才有意义。如果必要的话,我们可以看到事情在何时何地发生了变化。非常感谢大家以正确的方式帮助我解决这个问题!哇@iain,非常感谢!这并不能完全满足我的需求,但从总体来看,这是一个很好的开始。问题是,我试图应用于区域设置的行为在Sinatra中,但它是为了扩展我正在研究的另一个DSL。我将更新我的问题以进一步澄清,但您的回答非常详细且具有分析性。@M.Litalek谢谢,我认为您发布了一个有趣的问题。我会继续关注这个问题进行编辑,如果我想到其他有用的东西,我会更新这个。不管怎样,我希望你能找到一个好的解决办法。
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
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