How to deploy Sinatra / Merb applications with nginx

Posted by Dan Sosedoff on July 04, 2009

Developing with Merb or Sinatra ? Like web server nginx ? Ok, lets go.

Basically, Merb/Sinatra uses thin (based on eventmachine) server module, so it is working as stand-alone application. So, there is a way to connect nginx with these applications. Very simple – all you need to proxy requests to backend server. And all static files will be served by nginx.

upstream your_app {
  server 127.0.0.1:YOUR_PORT_NUMBER;
}

server {
  server_name YOUR_SERVERNAME;
  listen 80;
  charset utf-8;
  client_max_body_size 64M; # maximum of proxied filesize (for uploads)

  location / {
    root /path/to/app/root;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header Host $http_host;
    proxy_redirect false;
    if (-f $request_filename) {
      break;
    }

    if (!-f $request_filename) {
      proxy_pass http://your_app;
      break;
    }
  }

  # static content (images, flash, styles, etc.)
  location ~* ^.+\.(jpg|jpeg|gif|png|ico|css|js|swf)$ {
    root /path/to/your/public/dir;
    access_log off;
  }
}

Then, all you need – start your application in daemon mode. Dont forget to bind application server to listen only on local address (127.0.0.1).

Simple php url routing controller 1

Posted by Dan Sosedoff on July 04, 2009

!!! CONSIDER THIS AS AN UPDATE:Rails-like PHP Url Router

Here is simple url parser, that produces output parameters lile: controller, action, parameters. Also, it support simple virtual key rules, “rails” style.

For example, url like “http://foo.bar/book/view/12″ will produce parameters: controller = book, action = view, id = 12.

Here is php code:

<?
 
/* ------------------------------------------------------------- */
/* URL Router class */
/* ------------------------------------------------------------- */
 
class Router {
  static protected $instance;
  static protected $controller;
  static protected $action;
  static protected $params;
  static protected $rules;
 
  public static function getInstance() {
    if (isset(self::$instance) and (self::$instance instanceof self)) {
      return self::$instance;
    } else {
      self::$instance = new self();
      return self::$instance;
    }
  }
 
  private static function arrayClean($array) {
    foreach($array as $key => $value) {
      if (strlen($value) == 0) unset($array[$key]);
    }  
  }
 
  private static function ruleMatch($rule, $data) {    
    $ruleItems = explode('/',$rule); self::arrayClean(&$ruleItems);
    $dataItems = explode('/',$data); self::arrayClean(&$dataItems);
 
    if (count($ruleItems) == count($dataItems)) {
      $result = array();
 
      foreach($ruleItems as $ruleKey => $ruleValue) {
        if (preg_match('/^:[\w]{1,}$/',$ruleValue)) {
          $ruleValue = substr($ruleValue,1);
          $result[$ruleValue] = $dataItems[$ruleKey];
        }
        else {
          if (strcmp($ruleValue,$dataItems[$ruleKey]) != 0) {
            return false;
          }
        }
      }
 
      if (count($result) > 0) return $result;
      unset($result);
    }
    return false;
  }
 
  private static function defaultRoutes($url) {
    // process default routes
    $items = explode('/',$url);
 
    // remove empty blocks
    foreach($items as $key => $value) {
      if (strlen($value) == 0) unset($items[$key]);
    }
 
    // extract data
    if (count($items)) {
      self::$controller = array_shift($items);
      self::$action = array_shift($items);
      self::$params = $items;
    }
  }
 
  protected function __construct() {
    self::$rules = array();
  }
 
  public static function init() {
    $url = $_SERVER['REQUEST_URI'];
    $isCustom = false;
 
    if (count(self::$rules)) {
      foreach(self::$rules as $ruleKey => $ruleData) {
        $params = self::ruleMatch($ruleKey,$url);
        if ($params) {          
          self::$controller = $ruleData['controller'];
          self::$action = $ruleData['action'];
          self::$params = $params;
          $isCustom = true;
          break;
        }
      }
    }
 
    if (!$isCustom) self::defaultRoutes($url);
 
    if (!strlen(self::$controller)) self::$controller = 'home';
    if (!strlen(self::$action)) self::$action = 'index';
  }
 
  public static function addRule($rule, $target) {
    self::$rules[$rule] = $target;
  }
 
 
  public static function getController() { return self::$controller; }
  public static function getAction() { return self::$action; }
  public static function getParams() { return self::$params; }
  public static function getParam($id) { return self::$params[$id]; }
}
?>

Basic usage:

$router = Router::getInstance(); // init router
$router->addRule('/books/:id/:keyname',array('controller' => 'books', 'action' => 'view')); // add simple rule
// add some more rules
$router->init(); // execute router

So, as you can see, the router class can be accessible from anywhere in your code. Just because the class has beed designed as one-instance object, you cannot call for new instance. To get instance of router you need to call Router::getInstance() instead.
We have added simple rule for url`s like this ‘/books/12345/this-is-a-book-keyname’. Default routing system will identify this url incorrectly (controller => books, action => 12345). So the action 12345 not exists. That`s why we need to add custom rule. It will parse this url correctly: controller => books, action => view. And the rest of parameters will be automatically added to parameteres array. To access this array you need to call $router->getParams(); If you need to get just one parameter, call directly: $var = $router->getParam(0).

This router is really simple and can be used in small web sites that not require complete framework. Also it is fast.

Download file Router.php
View paste