How to designate a Rack handler - ruby

Rackup is successfully running any Rack app via Rack's default handler. e.g.:
class RackApp
def call(environment)
[
'200',
{'Content-Type' => 'text/html'},
["Hello world"]
]
end
end
run RackApp.new
But rackup is giving "NoMethodError at / undefined method `call' for nil:NilClass" when the last line is changed to instead use Rack's built-in CGI handler:
Rack::Handler::CGI.run RackApp.new
The same objection is raised for Rack's other built-in handlers. e.g. Rack::Handler::Thin, Rack::Handler::FastCGI, even Rack::Handler::WEBrick (which is the handler Rack selects above in default mode).
What's the correct syntax here?

The rackup command reads the config file and starts a server. The Rack::Handler::XXX.run methods also start a server, independently of the rackup command (CGI is slightly different as it isn't actually a server as such).
What happens when you change the line
run RackApp.new
to
Rack::Handler::CGI.run RackApp.new
and run rackup is as follows. The server starts and parses the config file. When the Rack::Handler::CGI.run RackApp.new line is reached it is executed as any other Ruby code would be. In the case of the CGI handler this calls the app and writes the output to the standard output as it would if running as a CGI script (have a look at your terminal when you run rackup). Afterwards the 'rackup' server is started as normal, but without an app to run. When you try to access the page you'll get the NoMethodError, since the app is nil.
Using Rack::Handler::Thin is similar, but in this case, as Thin actually is a web server, it is started and will serve RackApp, but listens on Thin's default port of 8080 (not the rack default of 9292). After stopping Thin (e.g. with Ctrl-C) the default rackup server (Mongrel or Webrick) will start listening on port 9292, again with no app specified so you'll get the NoMethodError.
If you run your modified 'config.ru' as a plain Ruby script rather than using rackup you'll see the same behaviour, but without the rackup server being started. (You'll need to require rack first, so use ruby -rrack config.ru). In the CGI case the output of a single call to your app will be printed to the console, in the Thin case Thin will be started serving your app.
In order to specify the server to use with rackup, you can use the -s option, e.g. rackup -s thin will start the app using Thin (this time on the rackup default port of 9292). You can also do rackup -s cgi but this won't really work in any useful way - it just prints out the html of an error page to the console.
CGI
If you're trying to run your app as a CGI there are a couple of options. You need to create a CGI script that calls your app using the CGI handler. This could itself be a ruby script that calls Rack::Handler::CGI.run directly, in fact you could use your modified config.ru directly (you might want to rename it first and add an explicit require 'rack' line).
Alternatively you can use a shell script which then calls rackup config.ru. In this situation rackup detects that it's running as CGI and automatically uses the correct handler

http://guides.rubyonrails.org/rails_on_rack.html (see point 3.5)
also interesting:
http://railscasts.com/episodes/151-rack-middleware
http://railscasts.com/episodes/222-rack-in-rails-3

Related

Headless operations don't work inside Sinatra route

I am using the headless and selenium-webdriver gems to launch a headless Firefox browser:
headless = Headless.new(
video: {
frame_rate: 12,
codec: 'libx264'
}
)
headless.start
driver = Selenium::WebDriver.for(:firefox)
With this code I can write the following:
driver.navigate.to("http://google.com")
Yet the following raises an error after I visit '/' in the browser:
get '/' do
driver.navigate.to("http://google.com")
erb :root
end
The error is as follows:
*** Errno::ECONNREFUSED Exception: Failed to open TCP connection to 127.0.0.1:7055 (Connection refused - connect(2) for "127.0.0.1" port 7055)
I'm pretty sure this is because the driver.navigate is not being called in the headless scope, therefore it can't connect to the Firefox instance.
I have also tried using the modular sinatra style, but the same error occurred.
workaround
What I ended up doing is separating the headless server in a separate script. This script has loop and gets input, printing the output of running the command in the headless scope. Then from the sinatra server, i use PTY.spawn to instantiate the server and pass around its stdin and stdout so I can use it in my Sinatra app. This way the headless script is only run once (therefore multiple headless servers aren't started) and I can connect to it from my Sinatra routes. I can't interact with the headless script's variables or methods directly - I need to just use i/o.
I am hoping for an answer which hows how to make the original code work, though (when the sinatra app's routes are called in headless scope)
One possible trick that can help:
this = self
get '/' do
this.driver.navigate.to("http://google.com")
erb :root
end
I ended up getting this working.
At first I thought that what fixed it was doing the headless environment initialization in the scope of a sinatra route, i.e.
get '/' { do_initialization_here }
The real fix may have been in the way I was calling Headless.new (I originally had a bunch of options tacked on and I removed all them).

OCRA missing submodules of libraries if rack/grape api endpoints aren't called

I'm trying to pack my REST app into into an executable with OCRA. I have a few gems required in my script:
require 'rack'
require 'rack/server'
require 'grape'
require 'grape-entity'
require 'rubygems'
I skip starting the server with this:
if not defined?(Ocra)
Rack::Server.start options
end
When I try to run my server.exe:
Temp/../server.rb:221:in `default_middleware_by_environment':
cannot load such file -- rack/content_length (LoadError)
Which means that it doesn't detect submodules of rack that exist, but aren't used and therefore not included. If I add a require 'rack/content_length' it continues with cannot load such file -- rack/chunkedEven` and so on.
When I interrupted my server by hand before I also had to call a few api endpoints to have everything included.
I think my options are either:
Tell OCRA to include all the submodules of rack and grape, but compiling that list is a bit time consuming and would increase the file size
I already tried ocra server.rb --gem-full=rack --gem-full=grape, which get my server started, but when calling the API 'rack/mount/strexp' is missing again..
Calling the API within my script, but I couldn't figure out how to do that. I can't add a block to Rack::Server.start options and it does only continue when I interrupt the server.
Any ideas to implement either option, or is there another solution?
If we run the rack app with a rack handler (webrick / thin / else), we can shutdown the server in another thread so that ocra can finish packing (not sure how to do same thing with Rack::Server).
app = Rack::Directory.new ENV['HOME'] # a sample app
handler = Rack::Handler.pick %w/ thin webrick /
handler.run app do |server|
# handler.run yields a server object,
# which we shutdown when ocra is packing
if ocra_is_packing # replace with proper condition
Thread.new { sleep 10; server.shutdown }
end
end
You may have to do something else (access the server etc.) to have ocra pack appropriate dependencies.

Disable Rack::CommonLogger without monkey patching

So, I want to have completely custom logging for my sinatra application, but I can't seem to disable the Rack::CommonLogger.
As per the sinatra docs all I should need to do is add the following line (tried setting it to false as well):
set :logging, nil
to my configuration. This does not work however, and I still receive the Apache-like log messages in my terminal. So the only solution I've found so far is to monkey patch the damn thing.
module Rack
class CommonLogger
def call(env)
# do nothing
#app.call(env)
end
end
end
Anyone got any ideas if it's possible to disable this without restorting to such matters?
I monkey patched the log(env, status, header, began_at) function, which is what gets called by rack to produce the apache style logs. We use jruby with logback so we have no use for all the custom logging that seems to pervade the ruby ecosystem. I suspect fishwife is initalizing the CommonLogger, which might explain why all my attempts to disable it or to configure it with a custom logger fail. Probably puma does something similar. I actually had two instances at one point. One logging with my custom logger (yay) and another one still doing its silly puts statements on stderr. I must say, I'm pretty appalled with the logging hacks in the rack ecosystem. Seems somebody needs a big cluebat to their heads.
Anyway, putting this in our config.ru works for us:
module Rack
class CommonLogger
def log(env, status, header, began_at)
# make rack STFU; our logging middleware takes care of this
end
end
end
In addition to that, I wrote my own logging middleware that uses slf4j with a proper MDC so we get more meaningful request logging.
Puma adds logging middleware to your app if you are in development mode and haven’t set the --quiet option.
To stop Puma logging in development, pass the -q or --quiet option on the command line:
puma -p 3001 -q
or if you are using a Puma config file, add quiet to it.
Rack includes a few middlewares by default when you rackup your application. It is defined in this file.
By default, as you mention, the logging middleware is enabled.
To disable it, just pass the option --quiet to rackup:
rackup --quiet
And the default loggin middleware will not be enabled.
Then, you can enable your own logging middleware without pollution by the default logger (named CommonLogger).
You can also add #\ --quiet to the top of your config.ru file to avoir always typing --quiet, but this behaviour is now deprecated.
It's probably not Sinatra what is writing to STDOUT or STDERR, but your webserver. Puma can be started with -q (quiet) option as noted by #matt. When using webrick make sure the AccessLog configuration variable is an empty array, otherwise it will also be logged on your standard output.
Disabling echo from webrick
This is one of the top results. So this probably more of a message to my future self the next time I'm annoyed to death about sinatra/puma not shutting up. But to actually get a silent start up:
class MyApp < Sinatra::Base
configure do
set :server, :puma
set :quiet, true
set :server_settings, Silent: true
end
end

How to make Thin run on a different port?

I've a very basic test app. When I execute this command the server ignores the port I specify and runs Thin on port 4567. Why is the port I specify ignored?
$ruby xxx.rb start -p 8000
== Sinatra/1.3.3 has taken the stage on 4567 for production with backup from Thin
>> Thin web server (v1.4.1 codename Chromeo)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:4567, CTRL+C to stop
xxx.rb file
require 'Thin'
rackup_file = "config.ru"
argv = ARGV
argv << ["-R", rackup_file ] unless ARGV.include?("-R")
argv << ["-e", "production"] unless ARGV.include?("-e")
puts argv.flatten
Thin::Runner.new(argv.flatten).run!
config.ru file
require 'sinatra'
require 'sinatra/base'
class SingingRain < Sinatra::Base
get '/' do
return 'hello'
end
end
SingingRain.run!
#\ -p 8000
put this at the top of the config.ru
Your problem is with the line:
SingingRain.run!
This is Sinatra’s run method, which tells Sinatra to start its own web server which runs on port 4567 by default. This is in your config.ru file, but config.ru is just Ruby, so this line is run as if it was in any other .rb file. This is why you see Sinatra start up on that port.
When you stop this server with CTRL-C, Thin will try to continue loading the config.ru file to determine what app to run. You don’t actually specify an app in your config.ru, so you’ll see something like:
^C>> Stopping ...
== Sinatra has ended his set (crowd applauds)
/Users/matt/.rvm/gems/ruby-1.9.3-p194/gems/rack-1.4.1/lib/rack/builder.rb:129:in `to_app': missing run or map statement (RuntimeError)
from config.ru:1:in `<main>'
...
This error is simply telling you that you didn’t actually specify an app to run in your config file.
Instead of SingingRain.run!, use:
run SingingRain
run is a Rack method that specifies which app to run. You could also do run SingingRain.new – Sinatra takes steps to enable you to use just the class itself here, or an instance.
The output to this should now just be:
>> Thin web server (v1.4.1 codename Chromeo)
>> Maximum connections set to 1024
>> Listening on 0.0.0.0:8000, CTRL+C to stop
You don’t get the == Sinatra/1.3.2 has taken the stage on 4567 for production with backup from Thin message because Sinatra isn’t running its built in server, it’s just your Thin server as you configured it.
in your config.ru add
set :port=> 8000
Also i would highly suggest using Sinatra with something like passenger+nginx which makes deploying to production a breeze. But You need not worry about this if you are going to deploy to heroku.

Passing options to rackup via a Sinatra application

I'm new to ruby, learning Sinatra. While creating a Sinatra site by requiring 'sinatra' and setting up the routes directly under is pretty easy and rather well documented, creating an application by requiring 'sinatra/base' and writing a class that inherits from 'Sinatra::Base', while still relatively easy, is very poorly documented (maybe because it's a pretty recent feature of Sinatra).
And that's exactly what I am doing. I am not having too much trouble on the Sinatra part, however I am having a bit of trouble on the rackup/thin/server part. Apparently there are two ways to deploy the application: using Sinatra itself (using the run! method) and using a rackup file (typically config.ru).
Using Sinatra's run! method is extremely intuitive and works like a charm, but apparently it doesn't work if I want to deploy my app on heroku. As a matter of fact, almost all the Sinatra apps that I have encountered on GitHub use a config.ru file.
Using a rackup file might be equally intuitive, but I can't manage to understand how to pass options from the Sinatra app to the server (ir: the port). I tried to merge options to rackup's default options array:
MyApp::App.default_options.merge!(
:run => false,
:env => :production,
:port => 4567
)
run MyApp::App
by adding options directly to the app:
MyApp::App.set :port, 4567
MyApp::App.set :run, false
MyApp::App.set :env, :production
run MyApp::App
by setting options from within the application class:
module MyApp
class App < Sinatra::Base
set :port, 4567
set :run, false
set :env, :production
# ...
# config.ru
require 'app'
run MyApp::App
All the methods above failed, either by showing error messages or by just not taking any of the options into consideration. So is there any way to pass options to rackup/thin/the sever via a Sinatra app when using a rackup file? Or the options in questions should be passed directly to rackup/thin/the sever via command-line options?
As a reference to the problem, here is the little Sinatra application I am building: https://github.com/AzizLight/Wiki/
You're actully going to pass options to thin on the command line directly or via a configuration file. See all options:
$ thin -h
For production, use a configuration file:
$ thin -C thin-production.yml -R config.ru start
Here is an example thin-production.yml file:
---
address: localhost
port: 3020
servers: 4
max_conns: 1024
max_persistent_conns: 512
timeout: 30
environment: production
pid: tmp/pids/thin-production.pid
log: log/thin-production.log
daemonize: true
I know I'm resurrecting an ancient question here, but I came across another useful solution not yet mentioned. As stated in this rack wiki tutorial:
the first line starting with #\ is treated as if it was options, allowing rackup arguments to be specified in the config file.
So if you wanted to set your host to 0.0.0.0 and port to 5656, you would add the following line to the beginning of your config.ru file:
#\ -o 0.0.0.0 -p 5656

Resources