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 that class has a model, that is required to read those settings.
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 can't do it: undefined method `config' for Resolver:Class.
Maybe I have the wrong scope, or I should be using Sinatra::Application?
When you get a class to inherit from Sinatra::Base
you are making that a Sinatra application. Each application gets its own settings
object. If you want to share settings across applications you have some options:
- Merge the applications.
- Make the settings more globally accessible.
- Inheritance (see edit below)
Merging them is easy (unless there's some particular reason we're not aware of), you basically put them in the same class.
To make the settings more globally accessible, I'd do the following:
a) Wrap the entire application in a module to namespace it.
b) Put the settings you want to use in a class instance variable accessible via a "getter" method.
e.g.
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
That's fine if you've got some very small apps, but if you're using multiple apps then you'll want to separate things out a bit. The way I tend to organise an app is remove everything from the rackup file (usually config.ru
) that does anything, aside from require
s and run
. I put the middleware and app setup in another file, usually app/config.rb
so I know it's the stuff from the config.ru
. Then each application gets its own file (e.g. 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 a lot of benefits to this kind of set up - it's easier to test; it's easier to organise; you can move apps around easier. But YMMV as always.
I should also add, as it's very remiss of me not to, that it's also possible to use inheritance, 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 all inherited, so if you configure something in the ancestor app it will be available in the inheritors. Sometimes it will be better to do it this way, probably when settings are just "global" to the Sinatra apps or when you want to create reusable apps and controllers. Other times, you'll need settings that can be used in models, libraries etc, and then the more global solution I've given first would be best.