What is Sinatra::Base responsible for? (i.e. class overview) - sinatra

I'm learning Sinatra and trying to wrap my brain around Sinatra::Base. The documentation doesn't readily answer the question:
What is Sinatra::Base responsible for?
Is there an easy way to think about it? (i.e. maybe there's a good diagram or something?)
Or is the answer just a long list of functionality? (For example: "Sinatra::Base is responsible for errors, filters, routes, templates, etc, etc.")
Or is it something simple like: "Sinatra::Base is Sinatra, minus the execution context, Sinatra::Application"?

Sinatra::Base is the Sinatra without delegation. Consider the following code with delegation:
# app.rb
require 'sinatra'
get '/' do
render :template
end
This style gives you option parser for free:
$ ruby app.rb -h
Usage: app [options]
-p port set the port (default is 4567)
-o addr set the host (default is localhost)
-e env set the environment (default is development)
-s server specify rack server/handler (default is thin)
-x turn on the mutex lock (default is off)
It also starts the server using appropriate Rack handler when you run the script with your application, so you don't have to write any related code.
It works only because Object is extended with Sinatra::Delegator in sinatra/main. See http://git.io/zWl7RA and http://git.io/NxgpBg. It delegates all Sinatra DSL methods to pre-configured instance of Sinatra::Base, namely Sinatra::Application.
When you write an application in modular style, nothing is delegated. You just inherit the base:
require 'sinatra/base'
class Application < Sinatra::Base
get '/'
render :template
end
end
Application.run!
So, Sinatra::Base is currently responsible for implementing all Sinatra DSL methods on top of Rack, Tilt and other dependencies.

Related

Sinatra - how to match production urls with the app's urls

Consider the following simple Sinatra application:
require 'sinatra'
post '/user/login' do
# login logic...
end
When deploying the application to a production environment, the url /user/login is usually changed to something else, i.e., /nitro/nutcracker/v1/user/login. And of course, the Sinatra app will not serve this url.
To cut the unwanted prefix, I've considered using a filter (i.e., before block), and routes with regex (i.e., get /*/user/login), but surely there are better solutions?
What say you?
You can mount it with rack. On the production server, create a config.ru file, and put this inside:
require_relative 'my_app.rb'
map('/nitro/nutcracker/v1/') { run Sinatra::Application } # Or your class, if it's modular
This will prefix the entire app with /nitro/nutcracker/v1/.
Then you run the server with rackup, or your application server might have a command line argument to pass a rack config file.

What is SINATRA_ENV?

Am I correct that within Sinatra there is no use or mention of SINATRA_ENV? For some reason I thought there was. Here's what I think is true now:
Sinatra bases its sense of environment on RACK_ENV. If RACK_ENV is not defined it defaults to development.
If you use ActiveRecord you will also need to set RAILS_ENV because Rails modules don't pay attention to RACK_ENV and certainly not to SINATRA_ENV
Can someone corroborate this analysis?
You are mostly correct. Some developers will build a SINATRA_ENV into their applications (and set RACK_ENV to it), but it is not built into the library. Sinatra 2.0 is introducing APP_ENV, which will be preferred over RACK_ENV.
ActiveRecord appears to check RACK_ENV if RAILS_ENV is not set, so you shouldn't need to set RAILS_ENV manually. This could potentially be a problem with Sinatra 2.0, so that's something to keep in mind. A simple workaround would be to set RACK_ENV to APP_ENV for backwards compatibility with other gems, or vice-versa.
Within Sinatra applications, classic and modular, you should be using environment (or self.environment in helpers and routes) and convenience methods such as development?, production?, etc. instead of inspecting the environment variables explicitly. I've taken to using Sinatra::Base.environment (and the aforementioned convenience methods) outside of my application(s) once Sinatra is loaded. For example:
# Gemfile
gem 'sinatra', require: 'sinatra/base' # in default group
group :development do
# ..
end
group :production do
# ..
end
And:
# config.ru
require 'bundler/setup'
Bundler.require :default # this loads Sinatra, which inspects e.g. RACK_ENV
Bundler.require Sinatra::Base.environment
It bears keeping in mind that Sinatra is a tiny, tiny library (last time I checked, about 2K LOC), and it really does leave a lot of these application concerns in the hands of the developer. There aren't many hard and fast rules!

How do I not start sinatra when Gemfile refers to it?

all, I have a client application which is a sinatra app, but that's only part of it, some other parts will do related work like download so need to spawn in a new process, this is mostly required the gem dependencies of the same client project, so I of course using some code to load in the bundler environment:
Dir.chdir(File.expand_path(File.dirname(__FILE__)))
ENV["BUNDLE_GEMFILE"] = File.expand_path("../Gemfile", __FILE__)
require 'bundler'
env = ENV['RACK_ENV'] || 'development'
Bundler.setup
Bundler.require :default, env.to_sym,
but unfornately even with no code,like
puts 3,the sinatra app will starts and listens on 4567 which is quite irritating,
I also tried modular approach to write in the file an empty class
class MyApp < Sinatra::Base;
end
but still sinatra starts on 4567, which is
quite irritating,
do I have a way not to start sinatra? Thanks.
Find out the problem,
because in Gemfile, is:
gem "sinatra",
better use
gem "sinatra", :require =>"sinatra/base"
instead.

Bind address in Sinatra Application

I'm running a classic style application in Sinatra and I would like to obtain a URL which the application is bound to. For example, if I start it in a development environment I would expect to get: http://localhost:4567/ while in production environment this would point to: http://example.com/
I know it is possible to retrieve it from the request. However, I need it in configuration block. How to do it?
Use the bind host and bind port method:
set :bind, 'example.com'
set :port, 80
should work.
taken from here. at the beginning of the page you can find how to implement them in you app (just copy it in front of your gets)

What's the best Perl module for hierarchical and inheritable configuration?

If I have a greenfield project, what is the best practice Perl based configuration module to use?
There will be a Catalyst app and some command line scripts. They should share the same configuration.
Some features I think I want ...
Hierarchical Configurations to cleanly maintain different development and live settings.
I'd like to define "global" configurations once (eg, results_per_page => 20), have those inherited but override-able by my dev/live configs.
Global:
results_per_page: 20
db_dsn: DBI:mysql;
db_name: my_app
Dev:
inherit_from: Global
db_user: dev
db_pass: dev
Dev_New_Feature_Branch:
inherit_from: Dev
db_name: my_app_new_feature
Live:
inherit_from: Global
db_user: live
db_pass: secure
When I deploy a project to a new server, or branch/fork/copy it somewhere new (eg, a new development instance), I want to (one time only) set which configuration set/file to use, and then all future updates are automatic.
I'd envisage this could be achieved with a symlink:
git clone example.com:/var/git/my_project . # or any equiv vcs
cd my_project/etc
ln -s live.config to_use.config
Then in the future
git pull # or any equiv vcs
I'd also like something that akin to FindBin, so that my configs can either use absolute paths, or relative to the current deployment. Given
/home/me/development/project/
bin
lib
etc/config
where /home/me/development/project/etc/config contains:
tmpl_dir: templates/
when my perl code looks up the tmpl_dir configuration it'll get:
/home/me/development/project/templates/
But on the live deployment:
/var/www/project/
bin
lib
etc/config
The same code would magically return
/var/www/project/templates/
Absolute values in the config should be honoured, so that:
apache_config: /etc/apache2/httpd.conf
would return "/etc/apache2/httpd.conf" in all cases.
Rather than a FindBin style approach, an alternative might be to allow configuration values to be defined in terms of other configuration values?
tmpl_dir: $base_dir/templates
I'd also like a pony ;)
Catalyst::Plugin::ConfigLoader supports multiple overriding config files. If your Catalyst app is called MyApp, then it has three levels of override: 1) MyApp.pm can have a __PACKAGE__->config(...) directive, 2) it next looks for MyApp.yml in the main directory of the app, 3) it looks for MyApp_local.yml. Each level may override settings in each other level.
In a Catalyst app I built, I put all of my immutable settings in MyApp.pm, my debug settings in MyApp.yml, and my production settings in MyApp_<servertype>.yml and then symlinked MyApp_local.yml to point at MyApp_<servertype>.yml on each deployed server (they were all a little different...).
That way, all of my config was in SVN and I just needed one ln -s step to manually config a server.
Perl Best Practices warns against exactly what you want. It states that config files should be simple and avoid the sort of baroque features you desire. It goes on to recommend three modules (none of which are Core Perl): Config::General, Config::Std, and Config::Tiny.
The general rational behind this is that the editing of config files tends to be done by non-programmers and the more complicated you make your config files, the more likely they will screw them up.
All of that said, you might take a look at YAML. It provides a full featured, human readable*, serialization format. I believe the currently recommend parser in Perl is YAML::XS. If you do go this route I would suggest writing a configuration tool for end users to use instead of having them edit the files directly.
ETA: Based on Chris Dolan's answer it sounds like YAML is the way to go for you since Catalyst is already using it (.yml is the de facto extension for YAML files).
* I have heard complaints that blind people may have difficulty with it
YAML is hateful for config - it's not non-programmer friendly partly because yaml in pod is by definition broken as they're both white-space dependent in different ways. This addresses the main problem with Config::General. I've written some quite complicated config files with C::G in the past and it really keeps out of your way in terms of syntax requirements etc. Other than that, Chris' advice seems on the money.