How mix in routes in Sinatra for a better structure
You don't do include with Sinatra. You use extensions together with register.
I.e. build your module in a separate file:
require 'sinatra/base'
module Sinatra
module OtherRoutes
def self.registered(app)
app.get "/route1" do
...
end
end
end
register OtherRoutes # for non modular apps, just include this file and it will register
end
And then register:
class Server < Sinatra::Base
register Sinatra::OtherRoutes
...
end
It's not really clear from the docs that this is the way to go for non-basic Sinatra apps. Hope it helps others.
Sinatra: Using routes from other files
You can use included
hook to implement what you want.
require 'sinatra'
require 'lib/web'
module NoCI
class Main < Sinatra::Base
include NoCI::Web
end
end
lib/web.rb
module NoCI
module Web
def self.included(base)
base.get '/' do
'Hello World!'
end
end
end
end
The code inside included
will execute when you include this module, with the NoCI::Base
as base
.
Writing a Sinatra Extension using options in routes
You're asking for dynamic routes, but Sinatra compiles the route information so it won't work the way you're looking for.
As a work around, you might consider defining a catch-all route, and checking the route information inside the catch-all, e.g.
get %r{^/(*)/bar$} do |capture|
if settings.url_prefix == capture # or perhaps check against request.path_info
# get file
else
status 404
end
end
Obviously, there are still many things to be done there, but you get the drift.
How do I config.ru properly in modular Sinatra application.?
With URLMap
you specify a base url where the app should be mounted. The path specified in the map is not used when determining which route to use within the app itself. In other words the app acts as if it's root is after the path used in URLMap
.
For example, your code will respond to the following paths:
/
: will be routed to the/
route inMyApp
/another
: will go to the/
route inAnotherRoute
. SinceAnotherRoute
extendsMyApp
this will be the same as/
inMyApp
(but in a different instance).URLMap
sees/another
and uses it to map toAnotherRoute
, stripping this part of the request from the path.AnotherRoute
then only sees/
./another/another
: will be routed to the two/another
routes inAnotherRoute
. Again, the firstanother
is used by the URLMap to route the request toAnotherRoute
.AnotherRoute
then only sees the secondanother
as the path.Note that this path will respond to both
GET
andPOST
requests, each being handled by the appropriate block.
It's not clear what you're trying to do, but I think you can achieve what you want by running an instance of AnotherRoute
, with a config.ru
that is just:
run AnotherRoute.new
Since AnotherRoute
extends MyApp
, the /
route will be defined for it.
If you're looking for a way to add routes to an existing Sinatra application, you could create a module with an included
method that adds the routes, rather than use inheritance.
Architecture for a modular, component-based Sinatra Application
This is similar to include's proposal, but it doesn't require access to the rackup file.
Write your various Handlers like:
class FoodHandler < Sinatra::Base
get '/chunky/:food' do
"Chunky #{params[:food]}!"
end
end
Then in your main application file:
require './lib/handlers/food_handler.rb'
class Main < Sinatra::Base
enable :sessions
... bla bla bla
use FoodHandler
end
I've used this kind of structure to build some fairly complex Sinatra apps. It scales just as well as Rails.
EDIT
To have your config file define the routes, you could do something like this:
class PlacesHandler < Sinatra::Base
# Given your example, this would define 'places/paris' and 'places/losangeles'
CONFIG['components'].select { |c| c['compontent_type'] == 'Mapper' }.each do |c|
get c['route'] do
@latitude = c['component_settings']['latitude']
@longitude = c['component_settings']['longitude']
end
end
end
View routes in Sinatra
I'm not sure if this is a security concern here (I'm not that into all the details of Sinatra) but I tend to be paranoid when using user specified data like for example params['page']
in your example. As said I'm not sure if Sinatra sanitizes the content and would make this example impossible, but imagine it said something like ../db_connection.yml
. So Sinatra would be told to load the haml file pages/../db_connection.yml
which might actually exist and display it to the user showing them your database configuration.
If you don't have any weird symlinks in your pages
directory it would probably be enough to replace all double occurences of a dot with something like .gsub(/\.+/, ".")
of the passed string (or replace all dots if you don't need them in the name to be even more paranoid). I'm not sure if there are any multi-byte insecurities where someone could do something ugly with encodings though and it might be useless to do the replacing at all because the exploit would work nevertheless.
Edit: Short reading into the Sinatra manual yielded that
By the way, unless you disable the path traversal attack protection (see below), the request path might be modified before matching against your routes.
So it seams that it should be secure to just use the params value without any special filtering, you may however like to look into the documentation some more (especially the security sections). However I don't think it's too much of a security issue if it's possible to figure out if a file in your pages
directory exists or not.
How to render archive-like structure in sinatra of old wordpress posts?
You have a few options for getting index.html to work in your public folders. The fastest option will be to have your load balancing server (e.g. Nginx or Apache) serve the static files. Nginx and Apache basically do this out of the box.
Another way is to manually read and send the files from a Sinatra route. You’ll get more control this way, but lose a little speed and use more memory. You might do that like this:
get "/:year/:month/:day/:title" do |*path|
File.read(File.join(settings.public, path, "index.html"))
end
You can get a list of your posts by globbing with the Dir class:
get "/archive" do
@posts = Dir["#{settings.public}/**/index.html"].map do |post|
File.dirname(post).sub(settings.public, "")
end
haml :archive
end
How does Sinatra define and invoke the get method?
It is spread out in a few places, but if you look in lib/sinatra/main.rb
, you can see this line at the bottom:include Sinatra::Delegator
If we go into lib/sinatra/base.rb
we see this chunk of code around like 1470.
# Sinatra delegation mixin. Mixing this module into an object causes all
# methods to be delegated to the Sinatra::Application class. Used primarily
# at the top-level.
module Delegator #:nodoc:
def self.delegate(*methods)
methods.each do |method_name|
define_method(method_name) do |*args, &block|
return super(*args, &block) if respond_to? method_name
Delegator.target.send(method_name, *args, &block)
end
private method_name
end
end
delegate :get, :patch, :put, :post, :delete, :head, :options, :template, :layout,
:before, :after, :error, :not_found, :configure, :set, :mime_type,
:enable, :disable, :use, :development?, :test?, :production?,
:helpers, :settings
class << self
attr_accessor :target
end
self.target = Application
end
This code does what the comment says: if it is included, it delegates all calls to the list of delegated methods to Sinatra::Application
class, which is a subclass of Sinatra::Base
, which is where the get
method is defined. When you write something like this:
require "sinatra"
get "foo" do
"Hello World"
end
Sinatra will end up calling the get
method on Sinatra::Base
due to the delegation it set up earlier.
Related Topics
How to Loop Over a Hash of Hashes
Is There a Less Intrusive Alternative to Rspec's 'Should_Receive'
Ruby: Sum Corresponding Members of Two or More Arrays
How to Convert PDF Files to Images Using Rmagick and Ruby
How to Catch Errno::Econnreset Class in "Case When"
Hash Ordering Preserved Between Iterations If Not Modified
Ruby Modules and Classes Same Name in Structure
How to Compile Ruby with Rvm on a Low Memory System
Fail VS. Raise in Ruby:Should We Really Believe the Style Guide
What Do the Different Brackets in Ruby Mean
Homebrew Install: Failed During: Git Fetch Origin Master:Refs/Remotes/Origin/Master -N --Depth=1
Ruby on Rails Error "Cannot Load Such File -- Less"
Is Inject the Same Thing as Reduce in Ruby
Ruby/Sinatra - Serving Up CSS, JavaScript, or Image Files
Convert an Array to Hash, Where Keys Are the Indices
In Ruby, How to Output JSON from Hash and Give It Line Breaks and Tabs