= README for backnob

Backnob is a really simple background processor. Once started workers can be added. When created workers will fork off to do their processing, and return a hash of results for later inspection.

Server Usage: backnob start|stop|restart|run
[start:] Start the server
[stop:] Stop the server
[restart:] Restart the server
[run:] Run the server in the foreground

Client Usage: backnob create|results
[create:] Create and start a worker. Returns the worker key
[results:] Get the results for a worker key.

Options are:
[-h, --help] Show this help message.
[-c FILE, --configuration=FILE] Load a configuration file
[-l ADDRESS, --listen=ADDRESS] Set the listen address. Defaults to 127.0.0.1:6444
[-p DIR, --pid=DIR] Set the directory for storing the pid and log files. Defaults to /tmp
[-v, --version] Show the version

== Configuration File Options

[:workers] Array of worker files or directories
[:listen] Address to listen on. Default is 127.0.0.1:6444


== Using the library

A simple worker example (simple.rb):

class SimpleWorker < Backnob::Worker
def execute
contents = File.read(@options[:file])
File.open('simple.txt', 'a') do |file|
file.write contents
end
results(:size, File.size('simple.txt'))
end
end

Now start up a backnob server instance by running

backnob start

Using the worker from irb:

require 'rubygems'
require 'backnob/client'

client = Backnob::Client.new
client.add_worker('simple.rb')
key = client.create_worker('simple', :file => '/tmp/to_be_read.txt')
while !client.results(key, :finished)
sleep 1
end

unless client.results(key, :error)
puts "File length is now: #:size)"
end

= Using from Rails

Install the plugin:

svn co svn://rubyforge.org/var/svn/backnob/trunk/plugin vendor/plugins/backnob
rake backnob:setup

Edit config/backnob.yml as appropriate. Create workers in lib/backnob_workers. Workers must
specify "rails true" for the rails environment to be required in. It's not a great idea
to require the environment manually as this will load rails into the server as well as the
worker.

A simple example of a rails worker that imports a CSV file formatted "email, name" into a users table:

require 'csv'

class ImportWorker < Backnob:Worker
rails true

def execute
file = @options[:file]
return unless File.exists? file

records = CSV.parse(File.read(file))

records.each_with_index do |record, index|
results(:progress, (index.to_f / records.length) * 100)

unless User.find_by_email record[0]
User.create(:email => record[0],
:name => record[1],
:active => true)
end
end
end
end

Now in a controller:

class ImportController < ApplicationController
def import
return unless request.post?

file = Tempfile.new('import')
file.write params[:file].read
file.close

session[:import_key] = backnob.create_worker('import', :file => file.path)
render :action => 'wait'
end

def ajax_check_import_progress
results = backnob.results(session[:import_key])
unless results[:finished]
render :update do |page|
page.replace_html 'progress', results[:progress].to_i.to_s
end
else
render :update do |page|
page.redirect_to :action => 'success.rhtml'
end
end
end
end

Views, import.rhtml, wait.rhtml and success.rhtml:

# import.rhtml
<%- form_tag do %>
<%= file_field_tag :file %>
<%= submit_tag %>
<% end -%>


# wait.rhtml
<p> Importing <span id='progress'>0</span>% complete, please wait... <%= image_tag('indicator.gif') %> </p>
<%= update_page_tag do |page|
page << periodically_call_remote :url => => 'ajax_check_import_progress', :frequency => 5
end %>


# success.rhtml
<p> File imported successfully </p>

= Single Workers

A single worker will stay running after execution so you can queue as much work up as you want and it will
execute each bit of work sequentially. Each bit of work still has its own key and results.

An example worker:

class SingleWorker < Backnob::Worker
single true

def execute
@times_run ||= 0
@times_run += 1

work = @options[:work]
logger.info work

results(:run, @times_run)
end
end

Client:

client = Backnob::Client.new
client.add_worker('single_worker.rb')
key1 = client.create_worker('single', :work => "hello")
key2 = client.create_worker('single', :work => "world")

while !client.results(key1, :finished)
sleep 0.25
end

while !client.results(key2, :finished)
sleep 0.25
end

puts client.results(key1, :run) => 1
puts client.results(key2, :run) => 2