• About me

    I'm a professional opensource web developer, interested in Ruby programming language and UNIX based operating systems.   read more

  • Pages

Simple Ruby DSL for parameter validation

This could be useful for those who wants to do a simple parameter validation in Sinatra.

Syntax is simple:

validation_configuration do
  rule_for :posts, :edit do
    param :id, :number,  :required
    param :language, :string,  :optional, [ 'sk', 'en' ]
  end
end

Now lets create some action in sinatra:

post "/posts/edit" do
   unless valid_params(:posts, :edit, params).empty?
     return "Bad parameters"
   end
end

Implementation in Ruby is little bit tricky ;-)

require 'singleton'
module Sinatra
  module Validation; end;
end;

class Sinatra::Validation::Suite
  include Singleton

  attr_accessor :rules

  def initialize
    self.rules = {}
    @current_action = nil
  end

  def rule_for(model, action, &params)
    @current_action = "#{model}:#{action}"
    rules[@current_action] ||= {}
    params.call
    @current_action = nil
  end

  def param(*args)
    rules[@current_action].merge!({ args.first => {
      :type => args[2],
      :class => args[1] || :string,
      :options => args[3] || []
      }})
  end

  def validate(model, action, params)
    @errors = []
    action_rules = rules["#{model}:#{action}"]
    validated_params = action_rules.collect { |name| name.first }
    validated_params.each do |param|
      check(param, action_rules[param], params[param])
    end
    return @errors
  end

private

  def check(name, rule, param)

    # Check if parameter is required
    if param.nil? and rule[:type].eql?(:required)
      @errors << Sinatra::Validation::Error.new(:required, name, nil, rule[:options])
      return false
    end

    # Check if paramer need some specific value
    if not rule[:options].empty? and not rule[:options].include?(param)
      @errors << Sinatra::Validation::Error.new(:invalid_value, name, param, rule[:options])
      return false
    end

    # Check parameter class (number, string...)
    case rule[:class]
      when :number
        unless param.to_s =~ /^([0-9]+)$/
          @errors << Sinatra::Validation::Error.new(:type_error, name, param, [rule[:class]])
        end
    end

  end

end

class Sinatra::Validation::Error
  attr_accessor :kind
  attr_accessor :parameter
  attr_accessor :given_value
  attr_accessor :valid_values

  def initialize(kind, parameter, given_value, valid_values=[])
    self.kind, self.parameter = kind, parameter
    self.given_value, self.valid_values = given_value, valid_values
  end

  def to_s
    case kind
      when :required
        "Required parameter #{parameter} not specified"
      when :invalid_value
        "Invalid value #{given_value} for parameter #{parameter}. Valid values: #{valid_values.join(',')}"
      when :type_error
        "Invalid type for parameter #{parameter}. Required type: #{valid_values.first.to_s.capitalize}"
    end
  end
end

def validation_configuration(&block)
  Sinatra::Validation::Suite.instance.instance_eval(&block)
end

def valid_params(model, action, params = {})
  Sinatra::Validation::Suite.instance.validate(model, action, params)
end

Silvester 2009 @ Brno – Desert

IMG_5343IMG_5344IMG_5346IMG_5349IMG_5350IMG_5353IMG_5357IMG_5358

HTTParty and Google

Recently i created a simple Ruby class for demonstrate a quick and easy way how to use a Google API and Google search inside Ruby. This class consist of two methods. First one use Google translation API. Usage is simple:

Google.translate('en', 'sk', 'Hello world') # - return 'Ahoj svet'

Second one is little bit complicated but i think very handy. It returns a google suggestion after you have misspelled search query or Google just offers you better combination.

Google.suggestion("ruby languag") # - return 'ruby language'
require 'httparty'
require 'nokogiri'
class Google
  include HTTParty

  def self.translate(from_lang, to_lang, text)
    base_uri 'http://ajax.googleapis.com/ajax/services/language'
    return get('/translate?', :query => {:v => '1.0',
      :langpair => "#{from_lang}|#{to_lang}",
      :q => text})['responseData']['translatedText']
  end

  def self.suggestion(q, lang='en')
    base_uri 'http://www.google.com'
    page=Nokogiri::HTML(self.get('/search', :query => {
      :client => 'firefox-a',
      :rls => 'org.mozilla',
      :hs => 'pdz',
      :hl => 'en',
      :q => q,
      :meta => ''
    }))
    page.search('a').each do |l|
      return l.text if l[:href]=~/^\/search.*spell=1$/
    end
    return false
  end

end

Mark exceptions as resolved in HopToad

Maybe this simple class will be useful for someone. Main idea is to extend HopToad service API, which unfortunately, doesn’t have support for marking exceptions as resolved. This stuff should be handy for people, who wants to manage exceptions in their administration interface. So here it comes:

require 'mechanize'

class HopToad

  def initialize(*args)
    @config=args.first
    @browser=WWW::Mechanize.new { |agent|
        agent.user_agent_alias = 'Mac Safari'
      }
  end

  def login
    @browser.get("http://#{@config[:project]}.hoptoadapp.com/login") do |login|
      page=login.form_with(:action => "http://#{@config[:project]}.hoptoadapp.com/session") do |login_form|
        login_form['session[email]']=@config[:email]
        login_form['session[password]']=@config[:password]
      end
      @session=page.submit
      return @session
    end
  end

  def exception(error_no)
    return 'Login first' unless @session
    error_page=@browser.get("http://#{@config[:project]}.hoptoadapp.com/errors/#{error_no}")
    return 'Exception not found' unless error_page
    return parse_exception(error_page)
  end

  def resolve(error_no)
    return 'Login first' unless @session
    error_page=@browser.get("http://#{@config[:project]}.hoptoadapp.com/errors/#{error_no}")
    return 'Exception not found' unless error_page
    error_page.form_with(:action => "/errors/#{error_no}") do |error_form|
      error_form['group[resolved]']=1
    end.submit
    self.exception(error_no)
  end

  private

  def parse_exception(page)
    error = {}
    error[:title] = page.search('div#summary table.summary td.error_message').text
    error[:backtrace] = page.search('div#backtrace pre').to_html
    error[:resolved] = page.search('span#resolved_status').text.eql?('Resolved') ? true : false
    return error
  end
end

Usage is simple:

h=HopToad.new(:project => "[PROJECTNAME]", :email => "[LOGIN]", :password => "[PASSWORD]")
h.login
h.show_exception([EXCEPTION_NUMBER])
h.resolve([EXCEPTION_NUMBER])

Memory allocation pain passenger

I known that RoR development has a big runtime. Big runtime means often a much more resources used for running applications. Passenger is a Rack deployment solution for nginx and apache. It can deploy any application which is configured as Rack. But… What we can do, if resources grows up abnormaly and memory consumption is fatal ? Nothing. There is no option for passenger to limit memory usage per worker. We should not use monit or other software, because there is no pid number for passenger instance.
But passenger comes with command ”passenger-memory-stats” to monitor resources. So let’s write a simple script:

kpid=$(/usr/bin/passenger-memory-stats | grep '[6789]..\.. MB.*Rails' | awk '{ print $1 }')
[ "$kpid" != "" ] && echo "Killing $kpid" && kill -9 $kpid

Just place this to crontab (every 3 minutes is good enough IMHO) and it will automatically kill all passenger instances which run out of limit (>500MB).

Howto setup nokogiri gem on Dreamhost

$ wget ftp://xmlsoft.org/libxml2/libxml2-2.7.6.tar.gz
$ wget ftp://xmlsoft.org/libxml2/libxslt-1.1.26.tar.gz
$ tar zxvf libxml2-2.7.6.tar.gz
$ cd libxml2-2.7.6
$ ./configure --prefix=$HOME/local/ \
--exec-prefix=$HOME/local
$ make && make install
$ cd ..
$ tar zxvf libxslt-1.1.26.tar.gz
$ cd libxslt-1.1.26
$ ./configure --prefix=$HOME/local/ \
--with-libxml-prefix=$HOME/local/
$ make && make install
$ export LD_LIBRARY_PATH=$HOME/local/lib
$ gem install nokogiri -- \
--with-xslt-dir=$HOME/local \
--with-xml2-include=$HOME/local/include/libxml2 \
--with-xml2-lib=$HOME/local/lib

Setting the LD_LIBRARY_PATH is important. You should probably modify your .bashrc to set that whenever you log in (if you haven’t already). Also, you might get a couple warnings on install, but those are just
from an older version of RDoc.

Make sure to run “nokogiri -v” after installing to make sure everything is set up correctly. The output should look like this:

$ nokogiri -v
---
warnings: []

libxml:
loaded: 2.7.6
binding: extension
compiled: 2.7.6
nokogiri: 1.4.0

Using mechanize to get movie data.

Just a quick note, that i have recently started new personal project based on mechanize. Goal will be to speed up searching for movie torrents and information about movies. Project is opensource of course and you should fork it on github. Also i setup working example at movies.mifo.sk
Idea is pretty simple. You just enter movie name and after few seconds (milliseconds ;) information about movie appear with a movie poster as well. Links to torrents will be available in short time.

Most interesting part of this project is Mechanize Ruby gem, which allows you to get content from other sites (IMDB in my case). Web scrapping is a common technique nowadays and my opinion is, that everything which will improve usability of web is good.

Zen GPS

zen gps

zen gps

Using Distributed Ruby example

Recently i had use very powerful thing which Ruby has built in and you should use it without installing any gems. I found that this Ruby feature was not well described yet. So i just made a quick sample for you:

Distributed Ruby

Distributed Ruby

First we prepare the ’server’. We call it ChatServer and it will contain only one method which just returns String passed as parameter. After class declaration we start DrB service. First parameter according to documentation consists from protocol (druby://), then comes hostname or IP address to bind server and last part is port for accepting connections from clients. I suggest to use higher ports to avoid port conflicts (>9000 is good enough). Second parameter is instance of class which we want to use from client.

require 'drb'
class ChatServer
  def msg(message)
    puts "[RECEIVED] #{message}"
  end
end

DRb.start_service('druby://localhost:9011', ChatServer.new)
DRb.thread.join

Now we’ll create simple client application:

require 'drb'
DRb.start_service()
chat = DRbObject.new(nil, 'druby://localhost:9011')
loop do
  text=gets.chomp
  break if text.eql?('exit')
  chat.msg(text)
end

We need to start a DrB service again (in client) and then assign a remote object to variable named chat using DrbObject.new. First parameter is the (local) object we want to create a stub for. Normally this is nil. Second is the URI of the remote object that this will be a stub for.

That’s it. Guess what will happen now. If you server is up and then you start a client application, you can enter some text.  Server will immediately show this text in console. Amazing isn’t it ?

But there is some bug, which i observed. Starting up a server takes usually a few seconds. I don’t known why, maybe DNS resolving…

DrB should be perfect if you need to distribute some large tasks or do a parallel computing of something. Nowadays in age of cloud computing this will greatly improve performance of your work…

Links:
http://www.humblelittlerubybook.com/book/html/chapter5.html
http://ruby-doc.org/../drb/rdoc/index.html

I’m alive again

Good news everyone. My blog is up again. Now i bed on Wordpress, because I’m just too old for programming own blogs. There are many things that happened until my last post. I managed a new apartment, so now i live with my girlfriend (which isn’t actually that good as it should be ;-). Also i recently have absolved a job interview in RedHat office in Brno.

RedHat

RedHat

So finally i’ll start making opensource software again and you should expect much more blog articles about Ruby, Rails and (new) cloud computing.