Strange issue in Sinatra - ruby

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.

Related

serving static files on sinatra

I'm working on a Sinatra app for the first time and am stumbling into an issue with serving a javascript asset.
I am following the Sinatra convention of putting the static files in “public” folder and it works locally but when we create a Docker image, we get a 404 while this works with localhost.
I see where I can set the public_folder which I have tried like this:
http://sinatrarb.com/configuration.html
but still 404'ing. Is there a way to get the top-level object and ask it where it expects the public_folder to be (like Rails.env)?
You can check the settings object.
irb(main):001:0> require "sinatra"
=> true
irb(main):002:0> settings.public_folder
=> "/usr/lib/ruby/2.5.0/irb/public"
This allows you to create a route which returns the path, something like this
require 'sinatra'
get '/' do
settings.public_folder
end
Without more information I would guess that your public folder points to a wrong directory inside your docker because the project :root points to a different directory that what you expect.

Passing headers (or other settings) from Sinatra through Puma

I am trying to learn how to develop a simple web app using pure Ruby. I have a simple backend service which I created as a Sinatra app. While developing it, I noticed the frontend (a simple HTML/JS static site) would not communicate with it because of CORS policies. So I looked into how to pass headers from Sinatra.
I came across sinatra-cors. I set it up as instructed and my app looks like this (abbreviated):
require 'sinatra'
require 'sinatra/cors'
set :allow_origin, '*'
set :allow_methods, 'GET,HEAD,POST'
set :allow_headers, 'content-type,if-modified-since,access-control-allow-methods,access-control-allow-origin'
set :expose_headers, 'content-disposition'
set :allow_credentials, true
post '/' do
[...]
end
When I run it with ruby app.rb, it works perfectly. Frontend can communicate and CORS policies are observed.
Now, I want to set up the service for a production environment. For that, I want to use Puma. So with Puma, I have a config.ru which looks like this:
require File.expand_path('app', File.dirname(__FILE__))
run WebApp
and I modified my app.rb to look like this (again abbreviated):
require 'sinatra'
require 'sinatra/cors'
class WebApp < Sinatra::Application
set :allow_origin, '*'
set :allow_methods, 'GET,HEAD,POST'
set :allow_headers, 'content-type,if-modified-since,access-control-allow-methods,access-control-allow-origin'
set :expose_headers, 'content-disposition'
set :allow_credentials, true
post '/' do
[...]
end
end
basically, wrapped the app in a class, and call it from the config.ru. When I run this by running puma in the directory, the service comes up, but headers are no longer passed back. Whenever I try to hit the backend, I get:
Access to XMLHttpRequest at 'http://localhost:4567/' from origin 'null' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This was the error I was getting before I originally set up the headers. So it seems pretty clear to me that the set parameters are being ignored.
So, this seems like a simple matter, but I have not been able to find a proper answer: How do I make Puma respect the 'set' parameters? Or alternatively, how do I achieve the same desired result?
It seems clear to me that I am missing a very simple thing, but I cannot figure out what exactly it is.
Thanks in advance!
It looks like you are just missing register Sinatra::Cors in your class.
class WebApp < Sinatra::Application
register Sinatra::Cors # Add this line.
set :allow_origin, '*'
# etc.

Webrick does not start when starting a Sinatra app

Any idea why Webrick refuses to start?
require 'sinatra/base'
require 'slim'
class Blog < Sinatra::Base
get '/' do
slim :home
end
end
Running ruby blog.rb does nothing. No error is raised.
The built in web server isn’t started when using the modular style of Sinatra apps. See the docs for the differences between modular and classic styles.
To get it to run like a classic style app, add this line to the bottom of your Blog class:
run! if app_file == $0

Two Sinatra Apps, shared datamapper DB

I'm trying to think of a good way of using Sinatra and Datamapper to create a solid web app that shares a database. I started using just one app, but I'm thinking about splitting the admin aspect to it's own app with a different mapping in a config.ru file.
So, config.ru will map the root route to the main app, and '/admin' to the Admin app. The main app will be responsible for showing data from the db to any user, and the Admin app is responsible for allowing admins to add content to the db. My reasoning behind this is to allow me more flexibility and reusability with the admin app.
The main app has the Datamapper classes responsible for defining my model, though I am sure I could move that to the admin app without a problem. Is there a way/reason to move it to it's own separate .rb file?
Obviously new to this platform, but I'm loving it. Any help is greatly appreciated.
You can have one project with as many sinatra apps as you like. for your project, you can set it up like this:
# site.rb
require 'config/boot'
class Site < Sinatra::Base
...
end
# admin.rb
require 'config/boot'
class Admin < Sinatra::Base
...
end
# config/boot.rb
require 'model/user.rb'
require 'model/post.rb'
# config.ru
require 'admin'
require 'site'
run Rack::URLMap.new("/" => Site.new, "/admin" => Admin.new)
The key is to put both apps in the same project / version control.

Multiple Sinatra apps using rack-mount

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.

Resources