Universal settings in Sinatra
I have a class in Sinatra where I set some settings (from JSON, as it happens):
class Pavo < Sinatra::Base
configure :development do
set :config, JSON.parse(File.open(File.dirname(__FILE__) + "/pavo.configuration.development.json", "rb").read)
set :config_mtime, File.mtime(File.dirname(__FILE__) + "/pavo.configuration.development.json")
end
[...]
get '/' do
puts "whatever"
end
end
And this class has a model that is required to read these parameters.
class Resolver < Sinatra::Base
def get_data(workpid)
url_str = settings.config['public']['BOOKS_DATA_SERVICE_URL'].gsub('${WORKPID}', workpid)
return Resolver.get_json(url_str)
end
[...]
end
However, the Resolver class cannot do this: undefined `config 'method for Resolver: Class.
Perhaps I have the wrong scope, or should I be using Sinatra :: Application?
source to share
When you get a class that inherits from Sinatra::Base
, you make this Sinatra application. Each application gets its own object settings
. If you want to use general settings for applications, you have several options:
- Combine apps.
- Make settings more globally accessible.
- Inheritance (see below)
Merging them is easy (unless there is some specific reason that we don't know about), you basically put them in the same class.
To make the settings more global I would do the following:
a) Wrap the entire application in a module in a namespace.
b) Place the parameters you want to use in a class instance variable accessible with the "getter" method.
eg.
module MyNamespace
def self.global_settings
@gs ||= # load your settings
end
class App < Sinatra::Base
configure do
set :something_from_the_global, MyNamespace.global_settings.something
end
end
class SecondaryApp < Sinatra::Base
helpers do
def another_method
MyNamespace.global_settings.something_else # available anywhere
end
end
configure do # they're also available here, since you set them up before the app
set :something_from_the_global, MyNamespace.global_settings.something
end
end
end
It's fine if you have very small applications, but if you use multiple applications, you need to separate them a little. As I usually plan to organize my application, delete everything from the rackup file (usually config.ru
) that does anything other than require
and run
. I installed the middleware and app in a different file usually app/config.rb
, so I know this stuff from config.ru
. Then each application gets its own file (for example app/app.rb
, app/secondary.rb
)
# app/config.rb
require "app"
require "secondary"
module MyNamespace
# set up your getters… e.g.
def self.global_settings
@gs ||= # load your settings
end
def self.app
Rack::Builder.app do
# …and middleware here
use SecondaryApp
run App
end
end
end
# config.ru
require 'rubygems'
require 'bundler'
Bundler.require
root = File.expand_path File.dirname(__FILE__)
require File.join( root , "./app/config.rb" )
map "/" do
run MyNamespace.app
end
There are many advantages to this type of setup — it's easier to test; easier to organize; you can move apps easily. But YMMV as always.
I have to add as well, since this very much prevents me from not doing this, that inheritance can also be used, for example:
require 'sinatra/base'
module MyNamespace
class Controller < Sinatra::Base
configure :development do
set :config, "some JSON"
set :mtime, Time.now.to_s
end
end
class App1 < Controller
get "/app1" do
"in App1 config: #{settings.config} mtime: #{settings.mtime}"
end
end
class App2 < Controller
get "/app2" do
"in App2 with config: #{settings. config} mtime: #{settings.mtime}"
end
end
end
Settings, routes, helpers, filters are inherited, so if you customize something in the proposal, it will be available in the heirs. Sometimes it will be better to do it this way, perhaps when settings are just "global" for Sinatra applications or when you want to create reusable applications and controllers. In other cases, you will need settings that can be used in models, libraries, etc., and then the best global solution I gave at the beginning would be better.
source to share