Showing posts with label Rails. Show all posts
Showing posts with label Rails. Show all posts

Friday, June 28, 2013

Ongoing open-source Rails secret_token vulnerability

Today I was re-alerted to a somewhat dangerous insecurity for open-source Rails apps if their Application.config.secret_token is kept under version control. The relevant file:

config/initializers/secret_token.rb

Rails itself generates this file in this way (without an entry in .gitignore) but it's still dangerous. There's lots of discussion here.

Presumably easily an environment variable could be set in config/application.yml (often kept out of version control) and retrieved at run time into config/initializers/secret_token.rb.

Remembering this would seem the only way to solve the problem and protect one's apps into the future from this known security vulnerability.

Here are Ruby on Rails Security Guide's recommendations:

'[P]lease secure your database configuration, e.g. config/database.yml, and your server-side secret, e.g. stored in config/initializers/secret_token.rb. You may want to further restrict access, using environment-specific versions of these files and any others that may contain sensitive information.' — per its Environmental Security section.

'If you have received an application where the secret was exposed (e.g. an application whose source was shared), strongly consider changing the secret.' — per its Session Storage section.

BTW, newly, Rails 4 has added Application.config.secret_key_base (described in the previous link and Guide for Upgrading Ruby on Rails' section, Action Pack).

'If you happen to share your code publicly, make sure your secret_key_base value is kept private.' — per this blog post.

Presumably, however, this new variable name setting does not remove the current security vulnerability either in Rails 3 or 4 since the Rails secret_token still is under version control.

Copyright (c) 2013 Mark D. Blackwell.

Thursday, April 25, 2013

Meetup authentication & email addresses

As part of its OAuth authentication process with other apps, Meetup doesn't provide email addresses of its users. (I refer to this official Meetup forum question, and to this page in the Meetup API docs—search the page for 'email'.)

Twitter doesn't provide email addresses either. However, Meetup seems nicer than Twitter.

People use multiple Twitter accounts (I know some who do). But people don't use multiple Meetup accounts (at least supposedly not).

When registering new users through this difficult class of OAuth authentication providers (those which don't supply an email address) one might ask each new user directly for some email address, or might not. Requesting this is normally recommended.

If an app uses Meetup authentication (and it doesn't request and confirm an email address during user registration), and uses another form of authentication also (even added later) then there's no way to identify the same user, if or when they sign on by a different way.

So Meetup authentication (without email) is only good if the app is forever limited to using Meetup authentication alone. With that permanent limitation, in such an app, nobody (mysteriously) will run into the problem of having more than one account.

Of course, having Meetup as the single method of authentication is useful, reasonably, only to apps which are already limited to Meetup users.

Keeping the UI simple (by not requesting an email address when people register) means the app might never have email addresses. But that might be okay if an app uses Meetup authentication alone, forever.

Then one need not bother people with asking for their email address when they first use an app. The ease of that emotional UX moment when new customers are forming their first impression of an app (and making their initial commitment to it), from the standpoint of building a customer base—depending on the app—could be considered more important than ever knowing their email addresses.

BTW, omniauth-meetup is a good gem for doing Meetup authentication in Rails.

Copyright (c) 2013 Mark D. Blackwell.

Saturday, April 20, 2013

Flat UI and Twitter Bootstrap "Mobile First" for Rails

Designmodo's announcement for their free Flat UI (which uses Sass) mentions plans (in the discussion) to release a pay version containing LESS code. And an issue on their Flat UI gem floats a suggestion of possibly porting it to LESS.

Darthdeus' gem flat-ui-rails contains Flat UI precompiled to CSS with no LESS code.

Whether Flat UI will continue to be maintained and useful for Rails doesn't relate to Darthdeus' trivially easy to maintain gem but therefore instead to Designmodo's free Flat UI product itself. So far, BTW, their Pro version seems to be vaporware.

There is a rumor Twitter Bootstrap version 3 "Mobile first" will have a flatter UI, but that's only temporary (for development purposes) according to this article.

Web searching revealed no sign how Designmodo's Flat UI might be affected by Twitter's new "Mobile first" initiative otherwise.

Copyright (c) 2013 Mark D. Blackwell.

Friday, September 28, 2012

Frontend experience

Recently, I acquired some practical website frontend experience—which took quite a bit of learning!

For an initial demo for a startup, I analyzed, selected and set up all the infrastructure (Rails, Heroku & Amazon). I wrote all the CSS frontend. I also wrote all the working database backend.

See the demo! See how its layout is fluid?

(Click here, if you missed the above links.)

It doesn't have multiple user capability yet; it's just a demo, at this time.

I made this in the pursuit of becoming a does-everything website developer.

Copyright (c) 2012 Mark D. Blackwell.

Monday, September 17, 2012

Website page layouts, proofs of concept

A big part of frontend website development is implementing webpage layouts using CSS stylesheets (of course).

Recently, I've been experiencing a great deal more of business in the area of layouts (specifically for Rails websites) and especially the work of implementing these layouts through developing CSS stylesheets—whether or not this is really programming! (Well, I think it is.)

I find it much less efficient to run the Rails server, and much more efficient to 'web-browse' the local filesystem. The work progresses much more quickly, in other words, when it is isolated from any complicating factors arising from our misunderstanding of the Rails server, jQuery, ERB/HAML, and perhaps even Sass. The weightiest reason for this improvement (by far) is the troubleshooting principle: 'divide and conquer'. Less important is that the filesystem also is relatively quicker.

It is much more doable (dare I say, even feasible) to get isolated layouts working using pure CSS and HTML (while keeping class names simple). And the same is true while paring down a stylesheet to be as simple and clean as possible.

Of course, further simplifying cross-browser development is the use of a CSS-reset stylesheet. Also it is essential, for HTML5's semantic tags: header, footer and nav (etc.), to include a (JavaScript) HTML5 shim (or 'shiv') script. So I include both of these best practices.

I have prepared a repository of my CSS (layout) proofs of concept on GitHub—including nine(!) useful proofs (as of now, September, 2012).

These layout proofs contain stylesheet code the way I write for Rails projects as much as possible (without actually including Rails).

Copyright (c) 2012 Mark D. Blackwell.

Saturday, August 11, 2012

Simple webserver for troubleshooting, howto

Here is an extremely simple web server to use in troubleshooting your code, derived from Yohanes Santoso's wonderful Gnome's Guide to WEBrick.

It serves any directory tree you're working on (including HTML properly) without any complexity arising from Rails, Sinatra, or any other web frameworks. (Isolation is a good thing when troubleshooting.)

Just place this in your tools directory (e.g., ~/t/serve-files — making sure it's executable):

#!/usr/bin/env ruby
require 'webrick'

program_name = $0
puts "#{program_name} #{ARGV.join ' '}"

puts "Running Ruby #{RUBY_VERSION}"

include WEBrick

options = {
  :BindAddress => '0.0.0.0',
  :Port => 3000,
  :DocumentRoot => Dir.pwd,
}
server = WEBrick::HTTPServer.new options

%w[INT TERM].each{|e| trap(e){server.shutdown}}

server.start

Copyright (c) 2012 Mark D. Blackwell.

Tuesday, July 24, 2012

Getting started with Ajax live updating, howto

Complexity is the enemy of troubleshooting. Remove as many dimensions as possible—it helps in a big way!

To create (with Ajax) your first live-updated web page, this no less applies. (Of course, these kinds of web pages don't require full page refreshes to change their appearance—familiar, right?)

The chosen web stack also makes its own recommendations. For getting Ajax to work, the elements of complexity include:
  • Understanding normally what happens in Ajax on the server and getting that to work, especially in an evolving web stack like Rails. (Googling gives obsolete information.)
  • Learning the details of the recommended language for the browser (naturally it gets compiled into Javascript). This is CoffeeScript for Rails now.
  • Learning the details of the particular Javascript library; Rails now recommends jQuery.
  • Getting from an external server the chosen Javascript library into the browser (which might not be working, giving no sign).
  • Learning in the chosen Javascript library (jQuery, e.g.) how to do Ajax.
  • Learning in (raw) Javascript how to do Ajax in a combination of browsers (using try-and-catch).
  • Learning in (raw) Javascript in a chosen, single browser how to do Ajax (e.g., doing XMLHttpRequest).
  • Learning to troubleshoot Javascript in a browser.
  • Learning the (browser) Document Object Model.
  • Learning Javascript.
For getting first-time Ajax working, all but the last four are unnecessarily complicated dimensions for troubleshooting.

Obviously if Ajax isn't working and nothing's happening, one had better remove all possible dimensions and get one thing working at a time!

So I have written a simple Ajax testbed server for the cloud:
  • It is always running.
  • It is extremely simple.
  • It doesn't have to be installed or configured by you.
  • It responds absolutely identically to:
    • GET and POST requests.
    • Header-specified HTML, JSON, XML and JS requests.
    • URL extension-specified HTML, JSON, XML and JS requests.
  • It simply returns the same JSON no matter what you do.
The testbed is running. Fork it on GitHub! Or tell me how it's not working for you.

Here's some browser client code to match (it runs on Mozilla's browsers):

<!DOCTYPE html>
<html> <head>
<title>bare Ajax</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf8">
<script type="text/javascript">
<!--

//Browser support code:

function getAjaxObject(){
  var result;
  try{ //Gecko: Mozilla, Firefox; Webkit: Chrome, Safari; ?: Opera; (etc.?) browsers:
    result = new XMLHttpRequest();
  } catch (e){ //Internet Explorer browsers:
    try{
      result = new ActiveXObject('Msxml2.XMLHTTP');
    } catch (e) {
      try{
        result = new ActiveXObject('Microsoft.XMLHTTP');
      } catch (e){ //Something went wrong:
        alert('Unable to obtain an Ajax object.');
        return false;
      }
    }
  }
  return result;
}

//Non-browser support code:

function targetId() {
  return 'replaceable';
}

function Target(id) {
  this.id = id;
}

function alterTarget(s) {
  var target = new Target(targetId());
  var elem = document.getElementById(target.id);
  elem.innerHTML = s;
}

function requestAjaxUpdate() {
  var req = getAjaxObject();
  //req.open('GET', 'http://localhost:5000/ajax',false);
  req.open('GET', 'http://ajax-testbed-simple.herokuapp.com/ajax',false); //Synchronous.
  req.send();
  //alert(req.status);
  var myJSON = JSON.parse(req.responseText);
  var s = myJSON.message;
  alert(s);
  alterTarget(s);
}

// When the DOM is fully loaded:
window.document.addEventListener('DOMContentLoaded', function() {
  //alert('window document ready');
}, false);

// When images, etc. are fully loaded:
window.onload = function() {
  //alert('window load');
  requestAjaxUpdate();
};

//-->
</script> </head> <body>

<div id="replaceable"  >replaceable-content</div>

</body> </html>

References:

http://ajaxpatterns.org/XMLHttpRequest_Call
https://developer.mozilla.org/en/AJAX/Getting_Started
https://developer.mozilla.org/en/DOM/
https://developer.mozilla.org/en/DOM/About_the_Document_Object_Model
https://developer.mozilla.org/en/DOM/DOM_event_reference/DOMContentLoaded
https://developer.mozilla.org/en/DOM/XMLHttpRequest
https://developer.mozilla.org/en/DOM/XMLHttpRequest/Using_XMLHttpRequest
https://developer.mozilla.org/en/DOM_Client_Object_Cross-Reference/DOM_Events
https://developer.mozilla.org/en/JavaScript/
https://developer.mozilla.org/en/JavaScript/A_re-introduction_to_JavaScript
https://developer.mozilla.org/en/JavaScript/Guide
https://developer.mozilla.org/en/JavaScript/Reference/
https://developer.mozilla.org/en/JavaScript_technologies_overview
https://developer.mozilla.org/en/Server-Side_Access_Control
http://en.wikipedia.org/wiki/Ajax_(programming)
http://en.wikipedia.org/wiki/JSON
http://en.wikipedia.org/wiki/XMLHttpRequest
http://linuxgazette.net/123/smith.html
http://molily.de/weblog/domcontentloaded
http://stackoverflow.com/questions/1457/modify-address-bar-url-in-ajax-app-to-match-current-state
http://stackoverflow.com/questions/6410951/how-to-simplify-render-to-string-in-rails-3
http://www.hunlock.com/blogs/Mastering_JSON_(_JavaScript_Object_Notation_)
http://www.json.org/js.html
http://www.mousewhisperer.co.uk/ajax_page.html
Ajax Tutorial – tizag.com/
http://www.w3.org/TR/XMLHttpRequest/
http://www.w3schools.com/json/default.asp
http://www.w3schools.com/xml/xml_http.asp
http://www.xml.com/pub/a/2005/02/09/xml-http-request.html

Copyright (c) 2012 Mark D. Blackwell.

Saturday, July 7, 2012

Manage long-running external webservice requests from Rails apps (on cloud servers), howto

Case: (as long as Rails is synchronous) requests to external webservices take the use of server resources to impossible levels, even when webservices behave normally—let alone when they are long delayed.

Plan: two web apps (one Rails, the other async Sinatra) can fairly easily manage the problem of external web service requests by minimizing use of server resources—without abandoning normal, threaded, synchronous Rails. The async Sinatra web app can be a separate business, even a moneymaking one.

This solution uses RabbitMQ, Memcache and PusherApp.

The async Sinatra web dynos (on the one hand) comprise external webservice request brokers. Also they have browser-facing functionality for signing up webmasters.

The Rails web dynos don't wait (on the other hand) for external webservices and they aren't short-polled by browsers.

This attempts to be efficient and robust. It should speed up heavily loaded servers while remaining within the mainstream of the Rails Way as much as possible.

E.g. it tries hard not to Pusherize browsers more than once for the case that a cached response to an external webservice was missed, but relies on browser short-polling after perhaps a 10-second timeout to cover these and other unusual cases.

But in the normal case browser short-polling will be avoided so Rails server response time should be peppy.

It tries to delete its temporary work from memcache but even if something is missed, memcache times out its data eventually so too much garbage won't pile up there.

Note: this is for web services without terribly large responses (thus appropriate for memcaching). Very large responses and non-idempotent services should be handled another way such as supplying them directly to the browser.

Method: the Rails web app dynos immediately use memcached external webservice responses if the URL's match.

Otherwise they push the URL of each external webservice request and an associated PusherApp channel ID (for eventually informing the browser) to a RabbitMQ Exchange.

For security purposes, minimal information is passed through PusherApp to the browser (only suggesting a short-poll now, not where).

The Rails web dynos (if necessary) return an incomplete page to the browser as usual (for completion with AJAX).

To cover cases where something got dropped the browser should short-poll the Rails app after a longish timeout—its length should be set by an environment variable and may be shortened to half a second when the Rails website is not terribly active, or when the async Sinatra web dynos are scaled down to off.

Each async Sinatra web dyno attaches a queue to the Rails app's RabbitMQ exchange for accepting messages without confirmation.

With each queued message, an async Sinatra web dyno:
  1. Checks the memcache for the external webservice request (with response)—if present, it:
    • Drops the message. (Some may slip through and be multiply-processed, but that's okay.)
    • Frees memcache of the request (without response) if it still exists (see below).
    Otherwise it checks the memcache for the external webservice request—without response. If recently memcached (perhaps within 10 seconds) it drops the message. (Some may slip through and be multiply-processed, but that's okay.)
    Otherwise it makes the request to the external webservice, setting a generous response timeout (maybe 60 seconds).
  2. Memcaches the external webservice request (without response) with the current time (not in the key).
  3. If the request times out, drops it in favor of letting the browser handle the problem, but leaves the memcached external webservice request (without response) for later viewing by async Sinatra web dynos.
  4. (Usually) receives a response from the external webservice request.
  5. Again checks memcache for the external webservice request (combined with the same response). If it's not there:
    • Pusherizes the appropriate browser. (Some requests may be multiply-processed, but that's okay.)
    • Memcaches the external webservice request (with response).
    • Clears from memcache the external webservice request without response.
The browser then asks the Rails web dyno to supply all available AJAX updates.

The Rails web dyno returns (usually incomplete: whatever is memcached—some may have been dropped, but that's okay) a set of still-needed AJAX responses to the browser (for further completion with AJAX).

Or (if all were memcached) the Rails web dynos return the complete set of outstanding AJAX responses to the browser.

I'm starting to implement this here, now.
Copyright (c) 2012 Mark D. Blackwell.

Monday, July 2, 2012

Scale Rails in the cloud by handling external webservice delays, howto

Background:

With Rails, each simultaneous browser request maintains its use of server memory till Rails responds—for unthreaded Rails, this is several tens of Mb. For threaded or JRuby, it's still substantial.

Webservers often (or usually) queue requests for a relatively small number of Rails instances, perhaps a dozen or two.

Freezing (in I/O wait) thousands of Rails instances for long-delayed webservices (located elsewhere on the Internet) is impracticable.

A naive Rails website design (without AJAX) which obtained all needed results from external services first (before responding to the browser) would provide little server throughput.

Usually for website scalability, developers offload (to other worker programs) what, in a naive or low-traffic design, Rails instances (themselves) might do.

In more scalable and sophisticated designs, Rails responds rapidly to initial requests (thus freeing its instance). Then, AJAX finishes the webpage (i.e., the browser polls the server).

Case:

It is a generally established software principle that events are better than polling.

Webpage content delivered by AJAX can be short-polled from the browser. However, short-polling gives unnecessarily slow response. From a user experience (UX) perspective it is either too slow, especially at peak times, or slower than it could be. Also short-polling loads up servers with extra, running Rails instances (maybe queued up) yet ultimately, most such requests determine (regrettably) there is nothing new.

Furthermore, during each polling request Rails reacquires all relevant information from its database cache (due to the stateless nature of the web). Therefore, each Rails short-polling responses takes a terrible amount of server resources—yet only calculates the time for the next poll.

Plan:

Long-polling (or websockets) should be used by Rails websites which access external services.

This can be accomplished efficiently if the browser doesn't long-polls Rails, but instead an asynchronous webserver such as (Ruby) EventMachine, configured for something other than Rails: for instance asynchronous Sinatra::Synchrony.

A RabbitMQ exchange (in a cloud environment such as Heroku) then can provide a queue to (all) front-facing asynchronous website server instances (dynos) containing information desired about (all) worker programs performing tasks which Rails instances offload.

Because the notifications don't need to be stored permanently, it's better to use a message system than a database.

Probably the information (simply) would be notification that each (single) task was complete. The exchange would connect all worker instances to all server instances in an instance-agnostic design typical of the cloud. A notification would include the user's session ID; then each asynchronous webserver could filter the notifications down to (presently active) long-polling connections they (themselves) own. Browsers can provide the session ID's (perhaps in a header) while setting up the long-poll.

Normally a webserver will keep long-poll HTTP connections open for quite a long time; however, if for any reason a connection has been broken, it doesn't matter much; the RabbitMQ queue (configurably) doesn't keep a message once the webserver has received it (so they won't pile up anywhere). Also if the webserver is restarted, the old queue automatically will be deleted.

This is because, in RabbitMQ, (named) exchanges actually manage the flow of messages; each receiver creates a queue of its own (from that exchange) which receives all the messages (applying to all server instances on the website).

Receipt confirmation also is unnecessary. If some messages might be dropped when the server is busy, so what? Nothing much bad will happen further; in that case the user may refresh the webpage—so the scheme is quite tolerant of individual failures.

After getting the new messages, the asynchronous webservers merely return from those long polls. After return, AJAX in the browser knows it can make a short-poll Rails request and be guaranteed of receiving more information for the webpage. Even if the connection merely is broken accidentally, the overall process is still safe, and will result in valid short-poll AJAX results. In other words (for simplicity), the normal way Rails responds to AJAX short-polling should not change.

This cycle of long-poll, short-poll should automatically repeat till Rails effectively tells the AJAX code (in the browser) all the work is done for the page—i.e., till no worker jobs are queued.

Perhaps (the default schedule of) AJAX short-polling can most easily be put off by increasing (to some large value) the time delay on the first short-poll. Presumably this is configurable in Rails. Long-polling of the other (asynchronous) webserver should be added to Rails page view layouts.

Thin (and Ruby EventMachine) are asynchronous and non-blocking just like Node.js. They can accept thousands of simultaneous HTTP connections (to browsers) each consuming only a few Kb of memory. Thin being based partly on Ruby EventMachine demonstrates the latter's quality.

The job queue for Rails worker programs also probably should be a RabbitMQ exchange, since we're using it.

Some various other asynchronous Ruby servers are: cramp, goliath, rainbows! and puma.

Actually, probably the best asynchronous webserver for this purpose is the paid service Pusher (or the open-source Slanger equivalent, to keep it in-house).

References:

blog.headius.com/2008/08/qa-what-thread-safe-rails-means.html
confreaks.com/videos/727-rockymtnruby2011-real-time-rack
github.com/igrigorik/async-rails/
github.com/igrigorik/em-synchrony
github.com/jjb/threaded-rails-example
jordanhollinger.com/2011/04/22/how-to-use-thin-effectivly [sic]
www.igvita.com/2009/05/13/fibers-cooperative-scheduling-in-ruby/
thechangelog.com/post/927103350/episode-0-3-1-websockets
www.igvita.com/2010/03/22/untangling-evented-code-with-ruby-fibers/
www.igvita.com/2010/06/07/rails-performance-needs-an-overhaul/
www.igvita.com/2011/03/08/goliath-non-blocking-ruby-19-web-server/
www.tumblr.com/tagged/pusher?before=1323905509
yehudakatz.com/2010/08/14/threads-in-ruby-enough-already/

Copyright (c) 2012 Mark D. Blackwell.

Thursday, June 28, 2012

Install Postgres on Debian squeeze for Rails, howto

Intro


Here's how to install Postgres, the popular, open-source database server for Rails 3 development on Debian squeeze (I used Rails 3.2.6). I switched to Postgres for maximum compatibility with Heroku (it's one of their '12-factor app' development principles).

This installation procedure keeps safety particularly in mind for anyone (like me) who has never before used Postgres.

Install


Debian squeeze's normal Postgres version (8.4) is unnecessary, unlike (apparently) the case on Mac OS.

Don't install the latest Postgres from source though postgresql.org recommends it—because its setup for Debian is difficult. Likely you will get mysterious errors such as:

$ bundle exec rake db:create:all --trace
  rake aborted!
  libpq.so.5: cannot open shared object file: No such file or directory - /home/mark/.rvm/gems/ruby-1.9.2-p320@global/gems/pg-0.14.0/lib/pg_ext.so
  /home/mark/.rvm/gems/ruby-1.9.2-p320@global/gems/pg-0.14.0/lib/pg.rb:4:in `require'

Instead use the most recent Postgres, backported to squeeze (currently 9.1.4). Here's how:

Remove old versions of Postgres software (e.g. 8.4) with:
$ apt-get purge libpq-dev libpq5 postgresql postgresql-client postgresql-common

Pay attention to messages, especially those warning about directory names containing 'postgres'. Take appropriate action to remove those directories.

Then clean up if you want to:
$ apt-get clean

Note that libpq is part of Postgres. Edit where Debian gets packages:
$ nano /etc/apt/sources.list

Include backports by adding:
  deb http://backports.debian.org/debian-backports squeeze-backports main

Ruby needs package 'libpq-dev' to connect to Postgres. Get the latest backported Postgres packages:
$ apt-get update
$ apt-get upgrade
$ apt-get -t squeeze-backports install postgresql libpq-dev

Expect to see the message, 'Configuring postgresql.conf to use port 5432' (which is the proper port for PostgreSQL *not* port 5433, which can come about if Debian gets confused).

Automatically, installation should start the Postgres server—look at what's actually running to confirm the port:
$ ls -a /var/run/postgresql

Instead of 5433 you should see:
  .s.PGSQL.5432

Minimally alter one of Postgres's configuration files to accept (app-specific) connections from Rails...:
$ nano /etc/postgresql/9.1/main/pg_hba.conf

by changing 'peer' to 'md5' where it says:

  # "local" is for Unix domain socket connections only
  #local   all         all                               peer
  local   all         all                               md5

Restart the Postgres server:
$ sudo /etc/init.d/postgresql stop
$ sudo /etc/init.d/postgresql start

New app


Now that you have Postgres installed, you can use it to create a new Rails app (which I'll call, 'APP'; replace this with something in lower case):
$ rails new APP -d postgresql; cd APP

Tell Rails you'll be using a database password from environment variables:

$ nano config/database.yml

Do this by changing the relevant lines (without moving them) to:

database:   <%=   ENV['DATABASE_USERNAME']   %>_development
database:   <%=   ENV['DATABASE_USERNAME']   %>_test
database:   <%=   ENV['DATABASE_USERNAME']   %>_production

username:   <%=   ENV['DATABASE_USERNAME']   %>
password:   <%=   ENV['DATABASE_PASSWORD']   %>

Repeat the username and password lines three times, once for each Rails environment.

Decide upon (or generate) a new database password for your app. Create the two environment variables above and set them somehow. If you're using foreman, you can set these in your .env file.

Unfortunately, Rails wants to drop the whole test database, not just its tables. Because it seems difficult to change this, we'll let Rails handle database creation:

Create the app's safe Postgres user. This asks you to enter your new app's password twice:
$ sudo -u postgres createuser --echo --encrypted --pwprompt --no-superuser --no-inherit --createdb --no-createrole APP

(If you made a mistake):
$ sudo -u postgres dropuser APP

Confirm your new app is included in the list of existing databases and users (called 'roles'):
$ sudo -u postgres psql
=> \dg
=> \l
=> \q

Rails should now be working. Test it by something like:
$ foreman run bundle exec rake db:create:all

If Rails merely complains that the three databases already exist, then this setup is working fine.

If Rails didn't work and the final error message started 'Couldn't create database' then scroll up: if you see...:

  could not connect to server: No such file or directory
  Is the server running locally and accepting connections on Unix domain socket "/var/run/postgresql/.s.PGSQL.5432"?

then go back and redo the above part regarding installing for the correct port 5432 (not 5433).

If after scrolling you see:

  password authentication failed for user {APP}

then go back and redo the above part regarding creating the 'role' (user) for the new app.

Rake


To run tests, with foreman setting up your local environment, remember that you should be running rake by:

$ foreman run bundle exec bin/rake ...

E.g., to run database migrations, do:

$ foreman run bundle exec bin/rake db:migrate

(And similarly for some other commands.)

Heroku


Heroku (e.g.) rewrites your database.yml to use only the single value in DATABASE_URL, which makes it effectively your production database, so beware. Don't set the DATABASE_URL environment variable (locally) in your .env file.

If you want to troubleshoot Heroku's access to your database with a local setup closer to Heroku's method, make a script (call it: my-foreman) containing this:

environment=$1
shift 1
export DATABASE_URL=\
postgres://$DATABASE_USERNAME:$DATABASE_PASSWORD@\
localhost:5432/$DATABASE_USERNAME'_'$environment
foreman start $@

Then you can run foreman in your desired environment with (e.g.)

./my-foreman production -p 5001

Alternative


Alternatively, you could take this approach:
* Make your own Unix username be a Postgres superuser;
* Keep the local authentication line in pg_hba.conf as,  'peer'; and
* Change the database username in new Rails apps to your Unix username.

But that approach is less safe, especially across apps. If you make a mistake in a new app, you don't want it to overwrite the database of another of your apps.

References

http://backports-master.debian.org/Instructions/
http://railscasts.com/episodes/342-migrating-to-postgresql?view=asciicast
http://wiki.debian.org/Backports
http://wiki.debian.org/PostgreSql#Installation
http://www.jquantlib.org/index.php/Upgrading_PostgreSQL_8.2_to_8.3_on_Debian
http://www.postgresql.org/docs/9.1/interactive/install-short.html
http://www.postgresql.org/docs/9.1/static/auth-methods.html
http://www.thegeekstuff.com/2009/04/linux-postgresql-install-and-configure-from-source/
http://xtremekforever.blogspot.com/2011/05/setup-rails-project-with-postgresql-on.html

Copyright (c) 2012 Mark D. Blackwell.

Tuesday, January 10, 2012

Lisp & Ruby metaprogramming

I just reread Paul Graham's article, Beating the Averages, on Lisp being the most powerful computer programming language because of Lisp macros (which, BTW, are not like assembly language macros). It led me to the obvious perception that because Ruby lacks Lisp macros, metaprogramming in it is weaker than in Lisp.

He explains the essence of Lisp macros: '[I]n general, for application software, you want to be using the most powerful ...language you can get, and using anything else is a mistake. ...Lisp code, after it's read by the parser, is made of data structures that you can traverse. If you understand how compilers work, [in Lisp you] write programs in the parse trees that get generated within the compiler when other languages are parsed. But these parse trees are fully accessible to your programs. You can write programs that manipulate them. In Lisp, these programs are called macros. [P]ower ...refers to features you could only get in [a] less powerful language by writing an interpreter for [a] more powerful language in it.'

So, for someone who wants to know where to go next after Ruby, and thinks that, in a Ruby code base, having a high proportion of metaprogramming code, like some say Rails 3 has these days, results in awkwardness, the next step is Lisp. Lisp apparently is perhaps a better metaprogramming language.

Copyright (c) 2012 Mark D. Blackwell.

Friday, December 2, 2011

Webmaster gallery

I made a picture gallery application for webmasters who use plain-vanilla web-hosting services (particularly those with cPanel).

The webmaster uploads pictures, thumbnails and CSS to a directory, then runs the app in order to create and modify the picture descriptions. It handles later uploaded pictures just as well.

It has been installed and run successfully (using cPanel).

An art gallery (of the app's first client) is visible (actually the static pages generated).

It is written in Rails 3.

Copyright (c) 2011 Mark D. Blackwell.

Wednesday, March 23, 2011

Install Rails 3 application to cPanel in sub-URI, howto

Recently, I successfully installed a Rails 3.0.3 application (using Ruby 1.8.7) on a plain-vanilla webhost with cPanel (which normally is restricted to Rails 2).

It can be managed in the normal way (started and stopped) on cPanel's webpage, 'Manage Ruby on Rails Applications', because of additional code I wrote in the Rails app.

Here's how I did it:

o See the webhost's current Ruby version and gem environment (for your_user_name) with:

cron> gem env

(If shell access is unavailable, command lines can be run as one-time cron jobs by specifying (year) month, day, hour and minute.)

o Ask the webhost to update if they don't have the latest RubyGems system software:

root> gem update --system

o Create a Ruby on Rails application (on the cPanel Ruby on Rails page) specifying application path, 'rails_apps/your_app_name'.

o In cPanel's File Manager (with the option to show hidden files) navigate to $HOME/rails_apps/your_app_name and delete the contents just made. Upload your Rails 3 application (there, compressing with 'zip' gives the relative pathnames required by cPanel) and extract it.

o Correct your cPanel user's gem environment by uploading to $HOME/.gemrc the following:

---
gem: --no-rdoc --no-ri

# Note: a non-default value adds another gempath.
# Is default for rubygems 1.5.2:
gemhome: /home/your_user_name/.gem/ruby/1.8

gempath:
# Is default for rubygems 1.5.2:
- /home/your_user_name/.gem/ruby/1.8
# Your app's vendor bundle:
- /home/your_user_name/rails_apps/your_app_name/vendor/bundle/ruby/1.8
# Change this to the webhost's gem repository location:
- /usr/lib/ruby/gems/1.8

o The webhost's cPanel Software/Services RubyGems page might be incorrect:

oo It might say the location of your ruby gems is '/home/your_user_name/ruby/gems'. This is wrong; Rubygems software currently uses, '/home/your_user_name/.gem/ruby/1.8'.

oo It might recommend adding a gem repository to the include path. This is unnecessary; don't add either of the following:

$:.push("/home/your_user_name/ruby/gems")
$:.push("/home/your_user_name/.gem/ruby/1.8")

o Install the bundler gem:

cron> gem install bundler

o Generate Gemfile.lock with:

cron> cd $HOME/rails_apps/your_app_name; bundle install --path vendor/bundle

o Ask the webhost to install (in the system gem repository) any gems Bundler failed to install, for instance, when Bundler tries to compile native extensions, which fails for cPanel users. For me, this was:

root> gem install mysql2

o Replace the Bundler code in config/boot.rb with a fixed list of gems:

=begin
# Set up gems listed in the Gemfile.
gemfile = File.expand_path('../../Gemfile', __FILE__)
begin
ENV['BUNDLE_GEMFILE'] = gemfile
require 'bundler'
Bundler.setup
rescue Bundler::GemNotFound => e
STDERR.puts e.message
STDERR.puts "Try running `bundle install`."
exit!
end if File.exist?(gemfile)
=end

o To make the list, sort Gemfile.lock and keep only its highest version of each gem. My fixed list was:

require 'rubygems'

require 'mysql2'

# Moved arel up, because otherwise got error:
## /usr/lib/ruby/site_ruby/1.8/rubygems.rb:274:in `activate': can't activate arel (= 2.0.7, runtime) for [], already activated arel-2.0.8 for ["activerecord-3.0.3"] (Gem::LoadError)

gem 'arel', '=2.0.7'

gem 'abstract', '=1.0.0'
gem 'actionmailer', '=3.0.3'
gem 'actionpack', '=3.0.3'
gem 'activemodel', '=3.0.3'
gem 'activerecord', '=3.0.3'
gem 'activeresource', '=3.0.3'
gem 'activesupport', '=3.0.3'
gem 'builder', '=2.1.2'
gem 'erubis', '=2.6.6'
gem 'i18n', '=0.5.0'
gem 'mail', '=2.2.15'
gem 'mime-types', '=1.16'
gem 'polyglot', '=0.3.1'
gem 'rack', '=1.2.1'
gem 'rack-mount', '=0.6.13'
gem 'rack-test', '=0.5.7'
gem 'rails', '=3.0.3'
gem 'railties', '=3.0.3'
gem 'rake', '=0.8.7'
gem 'thor', '=0.14.6'
gem 'treetop', '=1.4.9'
gem 'tzinfo', '=0.3.24'

o Seamonkey's caching causes problems in testing. The setting, 'Menu-Edit-Preferences-Advanced-Cache-Compare the page in the cache to the page on the network', should be 'Every time I view the page'. This still did not prevent all old, cached pages even with page reloads. I recommend using another browser, such as Opera to test your application.

o For my Rails 3 application, I used port 12009, one higher than cPanel's Mongel port for Rails (12008).

o Besides the cPanel button, you can start the application yourself in the cPanel way by:

cron> cd $HOME/rails_apps/your_app_name; /usr/bin/ruby /usr/bin/mongrel_rails start -p cPanel_Mongel_port; -d -e development -P log/mongrel.pid

o When troubleshooting, you can also start the application yourself by:

cron> cd $HOME/rails_apps/your_app_name; $HOME/.gem/ruby/1.8/bin/rails server webrick --port=Rails_3_port

o In config/environment.rb, add this line at the top:

unless (STARTED_BY_CPANEL='script/rails' != $PROGRAM_NAME)

o and these lines at the bottom:

else
load File.expand_path '../../monkey_patch_mongrel_1.1.5/start_webrick.rb', __FILE__
end

o Make a directory at the app root called, 'monkey_patch_mongrel_1.1.5'. Into it, place a file called 'dispatcher.rb' containing:

## Work around Mongrel source file: mongrel-1.1.5/lib/mongrel/rails.rb: 148
## require 'dispatcher'

module ActionController
class AbstractRequest
def relative_url_root=
end
end
end

o Also place a file there called 'start_webrick.rb' containing:

p Time.now, 'in '+__FILE__

# Versions tested on:
# Apache: 2.2.15
# Architecture: x86_64
# cPanel: 11.28.86
# cPanel Pro: 1.0 (RC1)
# Hosting package: Starter
# Kernel: 2.6.9-89.31.1.ELsmp (Linux)
# Mongrel: 1.1.5 (cPanel's)
# MySQL: 5.1.45
# Rails: 3.0.3
# Ruby: 1.8.7 patchlevel 330

module MyStartup
require 'pathname'

MY_RAILS_ENV=ENV['RAILS_ENV'] # Set by cPanel's Mongrel.

PROGRAM_FILE=Pathname(__FILE__).realpath
pfd=[]; PROGRAM_FILE.descend{|e| pfd << e}
USER_HOME=pfd.at 1
APP_ROOT= pfd.at -3
SERVER='webrick'
PORT='12009'

ARGUMENTS="--environment=#{MY_RAILS_ENV} --port=#{PORT}"
# For debugging, change to '':
REDIRECT_OUTPUT='> /dev/null'

class GemPathEntry
SYSTEM = Pathname('/').join *%w[ usr lib ruby gems 1.8 ]
USER = USER_HOME .join *%w[ .gem ruby 1.8 ]
APP_BUNDLE = APP_ROOT .join *%w[ vendor bundle ruby 1.8 ]
end
MY_GEM_HOME= GemPathEntry::USER
MY_GEM_PATH=[GemPathEntry::APP_BUNDLE, GemPathEntry::USER, GemPathEntry::SYSTEM].join ':'

# We might need Bundler in system gems. Or, as working now, in user (.gem) gems; I don't remember.

# The following line doesn't work because Bundler (1.0.10) "bundle install --binstubs" rewrites gem executables to invoke Bundler:
## r = APP_ROOT.
r = GemPathEntry::APP_BUNDLE
RAILS_COMMAND=['exec',r.join(*%w[bin rails]),'server',SERVER,ARGUMENTS,REDIRECT_OUTPUT].join ' '

REQUIRED_ENVIRONMENT_VARIABLES=[
"HOME=#{ USER_HOME }",
"RAILS_ENV=#{ MY_RAILS_ENV }",
"GEM_HOME=#{ MY_GEM_HOME }",
"GEM_PATH=#{ MY_GEM_PATH }",
].join ' '

def self.stop_process(name,pid,signal)
begin
p "I Stopping #{name} pid #{pid} at #{Time.now}"
Process.kill signal, pid
p "I #{Process.waitall.inspect}, #{name} finished at #{Time.now}"
rescue Errno::EINVAL, Errno::ESRCH
p "I No #{name} process #{pid}"
end
# Got error, undefined local variable or method `pwa' with:
## p pwa, "All child processes finished at #{Time.now}." unless (pwa=Process.waitall).empty?
# Sometimes there are other processes, I don't know why; so wait for them.
pwa=Process.waitall
p "I #{pwa.inspect}, all child processes finished at #{Time.now}" unless pwa.empty?
end

# Rack (1.2.1) fails with Rails 3.0.3 and any Mongrel, although Webrick works, per:
# https://github.com/rack/rack/issues/35

MONGREL_PID_FILE=APP_ROOT.join *%w[ log mongrel.pid ]

p Time.now
p "I Program name ($0) is #{$PROGRAM_NAME}"
p "I In #{PROGRAM_FILE}"
$LOAD_PATH.unshift APP_ROOT.join 'monkey_patch_mongrel_1.1.5'

unless WEBRICK_MONITOR_PID=Process.fork # Mongrel doesn't stop all threads, so we use a process.
p "I Starting Webrick monitor pid #{WEBRICK_MONITOR_PID} at #{Time.now} using command:"
p (c="export #{REQUIRED_ENVIRONMENT_VARIABLES}; cd #{APP_ROOT}; #{RAILS_COMMAND}")
Process.exec c unless WEBRICK_PID=Process.fork # Fork and replace another process.
p "I Starting Webrick pid #{WEBRICK_PID} at #{Time.now}"
Signal.trap 'TERM' do
stop_process 'Webrick', WEBRICK_PID, 'INT'
end
p "I #{Process.waitall.inspect}, Webrick finished (itself); stopping Mongrel at #{Time.now}"
unless File.exist? MONGREL_PID_FILE.to_s
p "I File '#{MONGREL_PID_FILE}' not found"
else
`#{s='mongrel_rails stop'}`
p "I '#{s}' finished at #{Time.now}"
end
Process.exit
end

Kernel.at_exit do # Handle being stopped by Mongrel.
stop_process 'Webrick monitor', WEBRICK_MONITOR_PID, 'TERM'
end

end

o The following rewrite rules require you to make this symlink:
/home/your_user_name/public_html/your_app_name ->
/home/your_user_name/rails_apps/your_app_name/public

ln -s $HOME/rails_apps/your_app_name/public $HOME/public_html/your_app_name

o At least in Rails 3.0.3, the following config/application.rb statement, which allows your application to generate the proper sub-URI URL's for its static assets such as images, requires an extra '/your_app_name' in the RewriteCond directive for most cached pages:

config.action_controller.asset_path=proc{|p| "/your_app_name#{p}"}

o Note that cPanel users cannot change Apache's system-level configuration file (/usr/local/apache/conf/httpd.conf), for instance to add Alias directives, so the following applies:

'The most common situation in which mod_rewrite is the right tool is when the very best solution requires access to the server configuration files, and you don't have that access. Some configuration directives are only available in the server configuration file. So if you are in a hosting situation where you only have .htaccess files to work with, you may need to resort to mod_rewrite.' --from http://httpd.apache.org/docs/current/rewrite/avoid.html

o The Pattern argument to RewriteRule must be without leading slash (it is stripped by Apache) or trailing slash. Its Substitution argument must have the leading slash.

o Apache's directive, DirectorySlash redirects bare sub-URI requests to '/your_app_name/', since 'your_app_name' is a symlink to your application's directory, 'public'. Similarly, Apache normally redirects bare HTTP_HOST requests to '/' because 'public_html' is a directory.

o Per: httpd-docs-2.2.14.en/mod/mod_rewrite.html#rewriterule
Per-directory Rewrites: When using the rewrite engine in .htaccess files the per-directory prefix (which always is the same for a specific directory) is automatically removed for the pattern matching and automatically added after the substitution has been done.

o My file, 'public_html/.htaccess' has these directives regarding cache expiration:

# Requires mod_expires to be enabled.
<IfModule mod_expires.c>
# Enable expirations.
ExpiresActive On
# Cache all files for 2 weeks after access (A).
ExpiresDefault A1209600
# Do not cache dynamically generated pages.
ExpiresByType text/html A1
</IfModule>

o Apache's <if> directive is unavailable until version 2.3, per:
http://serverfault.com/questions/238832/how-should-i-use-the-if-directive-in-htaccess

o Apache's FallbackResource directive is unavailable until version 2.2.16, per:
http://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource

o Place this .htaccess file in your application's public directory:

#
# Rails 3.0.3 cPanel Apache settings.
#
# Don't show directory listings for URLs which map to a directory.
Options -Indexes
# Set the default handler to none.
DirectoryIndex
# Don't follow symbolic links in this directory or below.
Options -FollowSymLinks

# Rewrite module.
<IfModule mod_rewrite.c>
RewriteEngine on
# Per http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html#rewriteoptions :
RewriteOptions inherit
RewriteBase /your_app_name

# 1. Simplify the rules for your sub-URI by redirecting a missing subdomain to 'www.':

RewriteCond %{HTTP_HOST} ^your_user_name\.com$ [NC]
RewriteRule ^(.*)$ http://www.your_user_name.com/your_app_name/$1 [L,R=301]

# 2. Rewrite your Rails application URI's to various files if they exist.

# 2.1. Rewrite its page-cached URI's:

# 2.1.1. Try to rewrite the sub-URI root URI to a file ('public/your_app_name.html') with path '/home/your_user_name/public_html/your_app_name/your_app_name.html':

RewriteCond %{HTTP_HOST} ^www\.your_user_name\.com$ [NC]
RewriteCond %{REQUEST_FILENAME}your_app_name.html -f
#SHOW RewriteRule ^$ /your_app_name/your_app_name.html?dr=%{DOCUMENT_ROOT}&rf=%{REQUEST_FILENAME} [L,R=301]
RewriteRule ^$ /your_app_name/your_app_name.html [L]

# 2.1.2. Try to rewrite a sub-URI, non-root URI to a file with extension '.html' (under tree, 'public/your_app_name/') by prefixing '/home/your_user_name/public_html/your_app_name' and suffixing '.html':

RewriteCond %{HTTP_HOST} ^www\.your_user_name\.com$ [NC]
RewriteCond %{DOCUMENT_ROOT}/your_app_name%{REQUEST_URI}.html -f
#SHOW RewriteRule ^(.+)$ /your_app_name/$1.html?dr=%{DOCUMENT_ROOT}&ru=%{REQUEST_URI}&rf=%{REQUEST_FILENAME} [L,R=301]
RewriteRule ^(.+)$ /your_app_name/your_app_name/$1.html [L]

# 3. Rewrite all other requests (except existing files) to the Rails port:

RewriteCond %{HTTP_HOST} ^www\.your_user_name\.com$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
#SHOW RewriteRule ^(.*)$ /your_app_name/your_app_name.html?rf=%{REQUEST_FILENAME} [L,R=301]
RewriteRule ^(.*)$ http://127.0.0.1:12009/your_app_name/$1 [L,P]

</IfModule>

o References:
http://httpd.apache.org/docs/2.2/
http://httpd.apache.org/docs/2.2/mod/quickreference.html
http://httpd.apache.org/docs/2.2/rewrite/
http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html
http://httpd.apache.org/docs/1.3/mod/mod_rewrite.html (simpler)
http://httpd.apache.org/docs/2.2/rewrite/vhosts.html
http://wiki.apache.org/httpd/WhenNotToUseRewrite
http://borkweb.com/story/apache-rewrite-cheatsheet
http://www.askapache.com/htaccess/crazy-advanced-mod_rewrite-tutorial.html

o Then as a result, if you start your application, its cPanel Mongrel log in log/mongrel.log should look like this:

** Daemonized, any open files are closed. Look at log/mongrel.pid and log/mongrel.log for info.
** Starting Mongrel listening at 0.0.0.0:12008
** Starting Rails with development environment...
Wed Mar 23 12:29:15 -0700 2011
"in /home/your_user_name/rails_apps/your_app_name/config/environment.rb"
Wed Mar 23 12:29:15 -0700 2011
"in /home/your_user_name/rails_apps/your_app_name/monkey_patch_mongrel_1.1.5/start_webrick.rb"
Wed Mar 23 12:29:15 -0700 2011
"I Program name ($0) is /usr/bin/mongrel_rails"
"I In /home/your_user_name/rails_apps/your_app_name/monkey_patch_mongrel_1.1.5/start_webrick.rb"
"I Starting Webrick monitor pid at Wed Mar 23 12:29:15 -0700 2011 using command:"
"export HOME=/home RAILS_ENV=development GEM_HOME=/home/.gem/ruby/1.8 GEM_PATH=/home/your_user_name/rails_apps/your_app_name/vendor/bundle/ruby/1.8:/home/.gem/ruby/1.8:/usr/lib/ruby/gems/1.8; cd /home/your_user_name/rails_apps/your_app_name; exec /home/your_user_name/rails_apps/your_app_name/vendor/bundle/ruby/1.8/bin/rails server webrick --environment=development --port=12009 > /dev/null"
"I Starting Webrick pid 15175 at Wed Mar 23 12:29:15 -0700 2011"
** Rails loaded.
** Loading any Rails specific GemPlugins
** Signals ready. TERM => stop. USR2 => restart. INT => stop (no restart).
** Rails signals registered. HUP => reload (without restart). It might not work well.
** Mongrel 1.1.5 available at 0.0.0.0:12008
** Writing PID file to log/mongrel.pid
[2011-03-23 12:29:16] INFO WEBrick 1.3.1
[2011-03-23 12:29:16] INFO ruby 1.8.7 (2009-06-12) [x86_64-linux]
[2011-03-23 12:29:16] INFO WEBrick::HTTPServer#start: pid=15175 port=12009

o If you use the cPanel button to stop your application, it should look like this:

** TERM signal received.
"I Stopping Webrick monitor pid 22436 at Wed Mar 23 12:47:43 -0700 2011"
"I Stopping Webrick pid 22437 at Wed Mar 23 12:47:43 -0700 2011"
[2011-03-23 12:47:43] INFO going to shutdown ...
[2011-03-23 12:47:43] INFO WEBrick::HTTPServer#start done.
"I [[22437, #<Process::Status: pid=22437,exited(0)>]], Webrick finished at Wed Mar 23 12:47:43 -0700 2011"
"I [], Webrick finished (itself); stopping Mongrel at Wed Mar 23 12:47:43 -0700 2011"
"I File '/home/your_user_name/rails_apps/your_app_name/log/mongrel.pid' not found"

o If, from inside your application, you also want to stop cPanel's Mongrel server and Webrick (I did, because my application makes static pages), add this to one of your controllers:

WEBRICK_PID_PATH=App.root.join *%w[ tmp pids server.pid ]
WEBRICK_PID = begin
f=File.new WEBRICK_PID_PATH.to_s, 'r'
s=f.gets("\n").chomp "\n"
f.close
s.to_i if s.present?
rescue Errno::ENOENT
nil
end
application_PID=Process.pid

def stop_application(s)
logger.info "I #{s}; sending INT to application PID #{application_PID}"
Process.kill 'INT', application_PID
end

def stop_server
# Attempt to stop Webrick server gracefully by sending it SIGINT:
webrick_killed=false
if WEBRICK_PID.present? && WEBRICK_PID > 0
begin
logger.info "I sending INT to Webrick PID #{WEBRICK_PID}; application PID is #{application_PID}"
Process.kill 'INT', WEBRICK_PID
webrick_killed=true
rescue Errno::EINVAL, Errno::ESRCH
end
end
# Handle various unusual conditions:
s=case
when WEBRICK_PID.blank?
'No Webrick server.pid file found'
when (WEBRICK_PID <= 0)
"Bad value in Webrick server.pid file: #{WEBRICK_PID}"
when (WEBRICK_PID != application_PID)
"Server PID #{WEBRICK_PID} differs from application's: not Webrick?"
when (!webrick_killed)
"No process #{WEBRICK_PID}"
# TODO: Handle hung Webrick server?
end
stop_application s if s
end

o If you stop Mongrel from within your application, it should look like this:

[2011-03-23 12:30:46] INFO going to shutdown ...
[2011-03-23 12:30:47] INFO WEBrick::HTTPServer#start done.
"I [[15175, #<Process::Status: pid=15175,exited(0)>]], Webrick finished (itself); stopping Mongrel at Wed Mar 23 12:30:47 -0700 2011"
** TERM signal received.
"I Stopping Webrick monitor pid 15174 at Wed Mar 23 12:30:47 -0700 2011"
"I Stopping Webrick pid 15175 at Wed Mar 23 12:30:47 -0700 2011"
"I No Webrick process 15175"
"I [[16266, #<Process::Status: pid=16266,exited(0)>]], all child processes finished at Wed Mar 23 12:30:47 -0700 2011"
"I 'mongrel_rails stop' finished at Wed Mar 23 12:30:47 -0700 2011"
"I [[15174, #<Process::Status: pid=15174,exited(0)>]], Webrick monitor finished at Wed Mar 23 12:30:47 -0700 2011"

References:
Webmaster gallery - cPanel Rails 3 app - Mark D. Blackwell

Copyright (c) 2011 Mark D. Blackwell.