Posted by Dan Sosedoff
on July 03, 2010
Most of Ruby web frameworks have terminal logging in development environments. It makes application debugging process much easier than using file logging. Especially for AJAX requests. Of course there is simple solution – use Firebug and javascript console. Unfortunately it is not that convenient.
So, one day i came up with idea to make the same ruby-like style of application logging, even with colorized output.
I decided to use EventMachine library as a server platform. And API is based on JSON packets, via json/pure. Simple, isnt it?
And i called it DebugServer. Pretty understandable
Installation
To install just type
$ sudo gem install debugserver
And for information type:
Usage: debugserver [options]
-h, --host HOSTNAME Server hostname
-p, --port PORT Server port
-i, --info Get usage information
By default it will start on localhost:9000.
Usage in PHP
First, download library from GitHub repository: http://github.com/sosedoff/debugclient-php
And then:
// place it into app initialization file
$debug = DebugClient::instance();
$debug->connect();
// .... and write it to terminal output
$obj = array(
'id' => rand(0, 0xFFFF),
'name' => 'Sample name',
'time' => strftime('%m-%d-%Y', time())
);
// these functions are globally defined
debug_clear(); // clear terminal
debug('This is a plain text message.');
debug_info('This is an informational message.');
debug_warning('This is a warning message.');
debug_error('This is an error message.');
debug_dump($obj);
// optional
$debug->close();
Results:

Sources
DebugServer: http://github.com/sosedoff/debugserver
PHP Client: http://github.com/sosedoff/debugclient-php
Posted by Dan Sosedoff
on June 18, 2010
Made a simple gem for website sitemap generation. Could be used in any Ruby/Rails/Merb/Sinatra application. It does not have any caching in that case if you want to use framework built-in cache methods.
Installation:
$ sudo gem install xml-sitemap
Example
pages = Page.all(:order => [:updated_at.desc] # DM model
map = XmlSitemap::Map.new('somedomain.com') do |m|
m.add(:url => '/', :period => :daily, :priority => 1.0)
m.add(:url => '/contractors', :period => :daily, :priority => 1.0)
pages.each do |p|
m.add(
:url => url_for_page(p),
:updated => p.updated_at,
:priority => 0.5,
:period => :never
)
end
end
# render the sitemap
puts map.render
Sinatra Example
# ... your code
get '/sitemap.xml' do
map = XmlSitemap::Map.new('domain.com') do |m|
m.add(:url => '/')
m.add(:url => '/posts', :period => :weekly)
end
headers['Content-Type'] = 'text/xml'
map.render
end
# ... more code
Options
:url – page path, relative to domain (ex.: /test), String.
:period – freqchange attribute, Symbol, :none, :never, :always, :hourly, :daily, :weekly, :monthly, :yearly
:priority – priority attribute, Float class,(0.0..1.0)
:updated – (optional) last_update attribute, Time class
Source Code
http://github.com/sosedoff/xml-sitemap
Posted by Dan Sosedoff
on June 09, 2010
At some point you will find that you have reached requests per IP limit while using some API or crawling resources. And if you`re doing it via standard Net::HTTP you`ll face the problem that you cannot assign request class to specified network interface (or IP). Bummer? No. Even if you cant do it with core class you might take a look on Curb – libcurl ruby binding. It has everything that you need to make regular get/post/etc requests. And of course – easy.
A simple example (real ip`s are changed):
require 'rubygems'
require 'curb'
ip_addresses = [
'1.1.1.1',
'2.2.2.2',
'3.3.3.3',
'4.4.4.4',
'5.5.5.5'
]
ip_addresses.each do |ip|
req = Curl::Easy.new('http://www.ip-adress.com/')
req.interface = ip
req.perform
result_ip = req.body_str.scan(/<h2>My IP address is: ([\d\.]{1,})<\/h2>/).first
puts("for #{ip} got response: #{result_ip}")
end
Output (ip`s are changed):
for 1.1.1.1 got response: 1.1.1.1
for 2.2.2.2 got response: 2.2.2.2
for 3.3.3.3 got response: 3.3.3.3
for 4.4.4.4 got response: 4.4.4.4
for 5.5.5.5 got response: 5.5.5.5
At least its working. Havent done any performance tests.
Sample on pastie: http://pastie.org/private/afxlcuk1npwjov3wer5hw
Posted by Dan Sosedoff
on October 02, 2009
Some time ago i wrote a simple regex patterns to determine whether my client crawler bot, mobile client or just regular one. Easy to expand and to use.
function is_mobile($agent) {
$pattern = '/(blackberry|motorokr|motorola|sony|windows ce|240x320|176x220|palm|mobile|iphone|ipod|symbian|nokia|samsung|midp)/i';
return (bool)preg_match($pattern, $agent);
}
function is_crawler($agent) {
pattern = '/(google|yahoo|baidu|bot|webalta|ia_archiver)/';
return (bool)preg_match($pattern, $agent);
}
Posted by Dan Sosedoff
on September 20, 2009
My previous version of php url router was not good enough, so i`ve done some core modification to the class. Its more flexible now. The whole idea is very similar to Rails` and Merb`s url router (merb is a rails-fork). In fact, previous version doesn`t even support GET variables in the request uri. New version just merges variables from request string into the same parameters array where other variables are stored. Also there is a command “default_routes” that maps default routes like this “/:controller/:action/:id”. So, basically you have two options – use defaults & custom routes or use only custom, which means that all other requests will be ignored. Function default_routes must be declared last.
Here is the sources:
define('ROUTER_DEFAULT_CONTROLLER', 'home');
define('ROUTER_DEFAULT_ACTION', 'index');
class Router {
public $request_uri;
public $routes;
public $controller, $controller_name;
public $action, $id;
public $params;
public $route_found = false;
public function __construct() {
$request = $_SERVER['REQUEST_URI'];
$pos = strpos($request, '?');
if ($pos) $request = substr($request, 0, $pos);
$this->request_uri = $request;
$this->routes = array();
}
public function map($rule, $target=array(), $conditions=array()) {
$this->routes[$rule] = new Route($rule, $this->request_uri, $target, $conditions);
}
public function default_routes() {
$this->map('/:controller');
$this->map('/:controller/:action');
$this->map('/:controller/:action/:id');
}
private function set_route($route) {
$this->route_found = true;
$params = $route->params;
$this->controller = $params['controller']; unset($params['controller']);
$this->action = $params['action']; unset($params['action']);
$this->id = $params['id'];
$this->params = array_merge($params, $_GET);
if (empty($this->controller)) $this->controller = ROUTER_DEFAULT_CONTROLLER;
if (empty($this->action)) $this->action = ROUTER_DEFAULT_ACTION;
if (empty($this->id)) $this->id = null;
$w = explode('_', $this->controller);
foreach($w as $k => $v) $w[$k] = ucfirst($v);
$this->controller_name = implode('', $w);
}
public function execute() {
foreach($this->routes as $route) {
if ($route->is_matched) {
$this->set_route($route);
break;
}
}
}
}
class Route {
public $is_matched = false;
public $params;
public $url;
private $conditions;
function __construct($url, $request_uri, $target, $conditions) {
$this->url = $url;
$this->params = array();
$this->conditions = $conditions;
$p_names = array(); $p_values = array();
preg_match_all('@:([\w]+)@', $url, $p_names, PREG_PATTERN_ORDER);
$p_names = $p_names[0];
$url_regex = preg_replace_callback('@:[\w]+@', array($this, 'regex_url'), $url);
$url_regex .= '/?';
if (preg_match('@^' . $url_regex . '$@', $request_uri, $p_values)) {
array_shift($p_values);
foreach($p_names as $index => $value) $this->params[substr($value,1)] = urldecode($p_values[$index]);
foreach($target as $key => $value) $this->params[$key] = $value;
$this->is_matched = true;
}
unset($p_names); unset($p_values);
}
function regex_url($matches) {
$key = str_replace(':', '', $matches[0]);
if (array_key_exists($key, $this->conditions)) {
return '('.$this->conditions[$key].')';
}
else {
return '([a-zA-Z0-9_\+\-%]+)';
}
}
}
Setup example:
$r = new Router(); // create router instance
$r->map('/', array('controller' => 'home')); // main page will call controller "Home" with method "index()"
$r->map('/login', array('controller' => 'auth', 'action' => 'login'));
$r->map('/logout', array('controller' => 'auth', 'action' => 'logout'));
$r->map('/signup', array('controller' => 'auth', 'action' => 'signup'));
$r->map('/profile/:action', array('controller' => 'profile')); // will call controller "Profile" with dynamic method ":action()"
$r->map('/users/:id', array('controller' => 'users'), array('id' => '[\d]{1,8}')); // define filters for the url parameters
$r->default_routes();
$r->execute();
Usage example:
$router = new Router();
// ... some configs ...
$controller = $router->controller; // will return name as it appears in url, ex: 'user_images'
$controller = $router->controller_name; // will return processed name of controller
// for example, if class name in url is 'user_images', then 'controller_name' var will be UserImages
$router->action;
$router->id; // if parameter :id presents
$router->params; // array(...)
$router->route_matched; // true - if route found, false - if not
Small and useful class.
Posted by Dan Sosedoff
on March 12, 2009
Need to identify web crawlers and mobile devices with your web-app ? Here is the list of couple regular expressions you can use (most common values):
Mobile Devices:
/(blackberry|motorokr|motorola|sony|windows ce|240x320|176x220|palm|mobile|iphone|ipod|symbian|nokia|samsung|midp)/i
Web Spyders:
/(google|yahoo|baidu|bot|webalta|archiver|crawler|spyder)/i