Multiple Sinatra apps using rack-mount - ruby

I have a question regarding using rack-mount with Sinatra. I've got two classic-style Sinatra apps. Let's call one App defined in app.rb and the other API defined in api.rb.
I would like it so that api.rb handles all routes beginning with '/api' and app.rb handles all other requests including the root ('/').
How would I set this up with rack-mount? Or is there a better solution than that?

I think you'll prefer Rack::URLMap - it will probably look something like this:
run Rack::URLMap.new("/" => App.new,
"/api" => Api.new)
That should go in your config.ru file.

I had a similar issue and I am not very familiar with Rack. I could not figure out what to do based on the answers above. My final solution was to have the following in config.ru.
This works perfectly for me.
# Main Ramaze site
map "/" do
Encoding.default_external = Encoding::UTF_8
Encoding.default_internal = Encoding::UTF_8
require ::File.expand_path('../app', __FILE__)
Ramaze.start(:root => __DIR__, :started => true)
run Ramaze
end
# Sinatra & Grape API
map "/api" do
use Rack::Static, :urls => ["/stylesheets", "/images", "/javascripts"], :root => "public"
use Rack::Session::Cookie
run Rack::Cascade.new([
MySinatraApp::Application,
MySinatraApp::API])
end

In config.ru you could also take advantage of Sinatra's middleware feature. If you have several Sinatra apps, each with its own routes, and want to run them simultaneously, you can arrange them in the order you want them found, e.g.
# config.ru
...
use MyAppA
use MyAppB
use MyAppC
run MyAppD

I had the same problem once and so I came up with this template: sinatra-rspec-bundler-template which is layed out for multiple apps.
It may have more features than you need but it should help you when you need something "a bit more" complex.

Related

Configuring Rack::Static

I'm building a Rack application which serves static and dynamic resources, and I'm trying to use Rack::Static middleware to do this.
The idea is serving all requests for static files and pass the requests that won't match this catogory, the dynamic requests, to my application. But I junt don't know how to do this and the documentation for Rack::Static is not very clear about how to do this.
This is my basic configuration file by now:
config.ru
#\ -w -p 8888
require_relative 'be/emeraldfw/emerald_app'
# use Rack::Reloader, 0
# use Rack::ContentLength
use Rack::Static, :urls => [""], :root => 'public', :index => 'pages/index.html'
run EmeraldApp.new
which just serves requests for / with public/pages/index.html, since the declared root is public.
This is far from being enough.
I need to serve all standard image files from public/resources/images, all CSS files from public/resources/css and so on.
Could someone give a hint about how to do this? I've been fighting this problem for two days now.

What does this do in my routes.rb file?

I am looking at a gist showing me how to handle CORS request in a Rails app. My routes.rb needs to have this bit of code in it.
Rails.application.routes.draw do
controller :whatever, path: '/whatever' do
match 'post_action', via: [ :post, :options]
end
end
If I was looking at a post resource, could someone show me how to set this up? I mainly do not understand the match function.
It will match the /whatever/post_action with the Controller/Action whatever#post_action. One useful tool you could use to see what your routes.rb are doing is to run the command:
rake routes
That will display all the routing table of your Rails app.
You can find more info in the Rails guide http://guides.rubyonrails.org/routing.html
Take care!
Actually match is not the good way to handle routes.
In rails 4 also it removed.
You can use the following code to use it:-
resource :whatever do
post 'post_action'
end
To test your routes
rake routes

Rack apps mounted in different subdomains

I'm building a Grape API alongside Sinatra. So far I've been mounting them in separate routes like this:
run Rack::URLMap.new("/" => Frontend::Server.new,
"/api" => API::Server.new)
Where the "/api" is served by a Grape app and "/" by a Sinatra app. But I wanted to use subdomains to separate those concerns instead of the actual "sub-URL". Any clues on how to do this?
Thanks in advance for the help.
There is a rack-subdomain gem, but it only handles redirection to paths, not rack apps. You could fork it and make it redirect to rack apps instead.
You could also just roll your own :
class SubdomainDispatcher
def initialize
#frontend = Frontend::Server.new
#api = API::Server.new
end
def call(env)
if subdomain == 'api'
return #api.call(env)
else
return #frontend.call(env)
end
end
private
# If may be more robust to use a 3rd party plugin to extract the subdomain
# e.g ActionDispatch::Http::URL.extract_subdomain(#env['HTTP_HOST'])
def subdomain
#env['HTTP_HOST'].split('.').first
end
end
run SubdomainDispatcher.new

Strange issue in Sinatra

ok so this is very strange (well is to me), everything in my master branch works fine, I then created a new branch called twitter to conduct some twitter feed implementation. I have done this and was working yesterday on my linux machine.. I have pulled the branch today in a windows environment but when i load the app i now get the regular Sinatra 404 Sinatra doesn’t know this ditty.
This is my profile.rb file
require 'bundler/setup'
Bundler.require(:default)
require 'rubygems'
require 'sinatra'
require './config/config.rb' if File.exists?('./config/config.rb')
require 'sinatra/jsonp'
require 'twitter'
require 'sinatra/static_assets'
class Profile < Sinatra::Base
helpers Sinatra::Jsonp
enable :json_pretty
register Sinatra::StaticAssets
##twitter_client = Twitter::Client.new(
:consumer_key => ENV["CONSUMER_KEY"],
:consumer_secret => ENV["CONSUMER_SECRET"],
:oauth_token => ENV["OAUTH_TOKEN"],
:oauth_token_secret => ENV["OAUTH_SECRET"],
)
get '/' do
erb :index
end
get '/feed' do
jsonp ##twitter_client.user_timeline('richl14').map(&:attrs)
end
end
Config.ru
require './profile'
run Profile
Does anyone have any ideas of what i need to be looking at to solve this? Can anyone speak from experience with this?
Thanks
When you use the classic Sinatra style you use require 'sinatra' and then add routes to the top level. These routes get added to the Sinatra::Application. When you directly run this file, e.g. with ruby my_app.rb, Sinatra runs a built in web server, which will serve the Sinatra::Application app.
When you use the modular style, you use require 'sinatra/base', and then add routes to your Sinatra::Base subclass. In this case directly executing the file doesn’t start the built in server.
In your case you are using the modular style, but have used require 'sinatra'. You create your Profile app, but when you run the file directly Sinatra launches the built in server and serves the Sinatra::Application app. Since you haven’t added any routes to this (they’ve all been added to Profile) it runs but all requests return 404.
One way to get your app to launch you is to use rackup. This will launch the Profile app that you have explicitly set in your config.ru. (Explicitly starting your webserver will also work, e.g. using thin start).
Another possibility would be to add a line like this to the end of your Profile class:
run! if app_file == $0
This tells Sinatra to start the build in server running the Profile app if the file is the same as the Ruby file being executed, in a similar way to how the classic style app is launched. If you use this method you should change require 'sinatra' to require 'sinatra/base' otherwise you will get two servers launched, one after the other (in fact you should probably make that change anyway).
See the Sinatra docs for more info about the difference between classic and modular style.

Devise with Sinatra

Does anyone had installed Devise gem with Sinatra?
Devise is based on Warden and so it should work on Sinatra, I couldn't find any related info about how to implement it.
Devise is really just a Rails-centric wrapper with nice helpers for warden, which is the underlying Rack authentication framework. So if you're using Sinatra in conjunction with Rails, you can use Devise in your Rails app, and use warden directly in your Sinatra app, and they will see the same user session data.
So no, you can't use Devise directly within your Sinatra app, but if you're building a modular app with some pieces in Rails, and other pieces in Sinatra, you can use Devise/Warden among the components.
Devise is designed for Rails only. You can't use it with Sinatra.
You can check out:
https://github.com/maxjustus/sinatra-authentication
http://www.gittr.com/index.php/archive/sinatra-basic-authentication-selectively-applied/
https://gist.github.com/243611
There is also https://github.com/jsmestad/sinatra_warden available.
I was able to get it working. There were a few main aspects:
Get Devise working with Rails (Devise is a Rails app, won't work without it)
Setup the mapping (route) on Rack level to support both Rails and Sinatra
Share the sessions between Rails and Sinatra
Setup Warden and make it available to Sinatra
Here is most relevant part of code from /config.ru:
#
# ...
# Rest with Rails
map "/" do
run MyApp::Application
end
# Anything urls starting with /slim will go to Sinatra
map "/slim" do
# make sure :key and :secret be in-sync with initializers/secret_store.rb initializers/secret_token.rb
use Rack::Session::Cookie, :key => '<< see, initializers/secret_store.rb >>', :secret => '<< copy from initializers/secret_token.rb >>'
# Point Warden to the Sinatra App
use Warden::Manager do |manager|
manager.failure_app = AppMain
manager.default_scope = Devise.default_scope
end
# Borrowed from https://gist.github.com/217362
Warden::Manager.before_failure do |env, opts|
env['REQUEST_METHOD'] = "POST"
end
run AppMain
end
See, http://labnote.beedesk.com/sinatra-warden-rails-devise for a complete solution.

Resources