Dynamic WEBRick Servers in Ruby
Sometimes it's nice to have a self contained click-'n-run web-server - this way you are not dependent on presence of Apache, Mongrel, or any other 3rd party package. One way to go about this, is to do some socket programming. However, sockets may be too low-level for comfort, instead we can leverage yet another great Ruby library: WEBrick. Most Rails developers will be very familiar with WEBRick, but most don't realize how powerful it can be for quick mock-ups and one-off proofs of concept.
In my case, I wanted to run a quick, dynamic survey where the questions were pulled from the database, and the results would be persisted to disk. Specifically, I wanted to ask users to rank about a hundred different news stories for affect and impact. Thus, I needed to dynamically serve GET requests from the database, and also be able to process POST request (from the survey form) to persist my data. With the help of WEBRick I could do exactly this in about 60 lines of code.
Initializing WEBRick servers
A quick browse through the servlet examples, along with the RDoc gets us well on the way. We will start our server on port 8000, and mount two 'paths' - one for serving the survey, and second for processing the POST request (saving the form):
require 'rubygems' require 'webrick' require 'dbi' # Initialize our WEBrick server if $0 == __FILE__ then server = WEBrick::HTTPServer.new(:Port => 8000) server.mount "/questions", WebForm server.mount "/save", PersistAnswers trap "INT" do server.shutdown end server.start end
Serving GET requests
From the initialization code, you will notice that we specified WebForm and PersistAnswers as the two classes that will process each request. Specifically, the user will first request to see the questions (issue a GET request), thus we need to define a WebForm class that will build and return to the user the HTML for our survey. To do this, we simply inherit from AbstractServlet, and define the do_GET method:
class WebForm < WEBrick::HTTPServlet::AbstractServlet # Process the request, return response def do_GET(request, response) status, content_type, body = print_questions(request) response.status = status response['Content-Type'] = content_type response.body = body end # Construct the return HTML page def print_questions(request) html = "<html><body><form method='POST' action='/save'>" html += "Name: <input type='textbox' name='first_name' /><br /><br />"; dbh = DBI.connect("DBI:Mysql:webarchive:localhost", "root", "pass") sth = dbh.execute("SELECT headline, story, id FROM yahoo_news where date >= '2004-12-01' and date <= '2005-01-01'") # iterate over every returned news-story from the database while row = sth.fetch_hash do html += "<b>#{row['headline']}</b><br />\\n" html += "#{row['story']}<br />\\n" html += "<input type='textbox' name='#{row['id']}' /><br /><br />\\n" end sth.finish html += "<input type='submit'></form></body></html>" # Return OK (200), content-type: text/html, followed by the HTML itself return 200, "text/html", html end end
Processing POST requests
In similar fashion, to process a POST request, we simply have to define the do_POST method:
class PersistAnswers < WEBrick::HTTPServlet::AbstractServlet def do_POST(request, response) status, content_type, body = save_answers(request) response.status = status response['Content-Type'] = content_type response.body = body end # Save POST request into a text file def save_answers(request) # Check if the user provided a name if (filename = request.query['first_name'] ) f = File.open("save-#{filename}.#{Time.now.strftime('%H%M%S')}.txt", 'w') # Iterate over every POST'ed value and persist it to file request.query.collect { | key, value | f.write("#{key}: #{value}\\n") } f.close end # Return OK (200), content-type: text/plain, and a plain-text "Saved! Thank you." notice return 200, "text/plain", "Saved! Thank you." end end
Click 'n Run
Believe it or not, that's it! You can extend this example to serve a file, an entire directory, or even run a full-blown Rails application. But with our basic setup, pointing your browser to localhost:8000 should produce:
[2007-02-13 15:15:21] INFO WEBrick 1.3.1
[2007-02-13 15:15:21] INFO ruby 1.8.5 (2006-08-25) [i386-mswin32]
[2007-02-13 15:15:21] INFO WEBrick::HTTPServer#start: pid=1224 port=8000
localhost - - [13/Feb/2007:15:15:31 Pacific Standard Time] "GET /questions HTTP/1.1" 200 56679
- -> /questions
localhost - - [13/Feb/2007:15:15:36 Pacific Standard Time] "POST /save HTTP/1.1" 200 17
http://localhost:8000/questions -> /save
About this entry
- Published:
- 13.02.07 / 6pm
- Category:
- Ruby
Related Posts
- 31.07 Reconstructing Request URIs in Rails
- 11.02 Nginx and Memcached, a 400% boost!
- 05.01 Dynamic Stat Graphs in Rails
- 21.08 Keith Rarick: Building Causes.com
- 07.03 Client HTTP Caching in Rails









Entries RSS
1 Comment
comments rss | trackback uri