I'm trying to work on a DSL to manage different locales within the same route, like get "/test"
. The
This is an exercise to learn how to extend Sinatra, therefore Rack::Locale
or a similar tool is not a valid answer.
Based on the body of the request JSON body, assuming I receive JSON as POST or PUT, I want to respond with the specific locale.
I currently have a barebones script, of what I think I need:
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
I am missing the capability to respond based on the locale in the request.body
JSON I receive as input, and the class here has something else I do not yet see that is needed or is missing.
An example of how this would get used would be:
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
Therefore to try to clarify even more clearly I will try with a TDD approach:
As User when I send a JSON that contains: "locale": "cs-CS"
the result is Czech.
Have you read Extending The DSL and the Conditions section of the README?
Right now, you're not really extending the DSL. I'd redesign it slightly, because it looks like you'd want to match on a case statement but that would mean creating lots of classes or an ugly matching statement. But, Sinatra already has some really nice ways to match on routes and conditions. So, something like this would be more idiomatic:
post '/', :locale => "Czech" do
"Czech"
end
post '/', :locale => "British English" do
"British"
end
or
post '/', :locale => "en-GB" do
"cs-CS"
end
post '/', :locale => "cs-CS" do
"cs-CS"
end
How to do this? First, you'll need a filter to transform the JSON coming in:
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
and then you'll need a condition to check against:
set(:locale) {|value|
condition {
!!@locale && (@locale == value || @json["locale"] == value)
}
}
All together (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
That works but it won't work as an extension. So, relying the docs I posted at the top:
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
Now it will extend the DSL. For example:
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
Hopefully that helps clarify a few things for you.