Skip to content


Subversion checkout URL

You can clone with
Download ZIP
Fetching contributors…
Cannot retrieve contributors at this time
198 lines (130 sloc) 6.92 KB

Shared Workforce Client

Build Status

Shared Workforce is a platform for managing and completing repetitive tasks that require human intelligence. For example, tagging and cropping photos, approving text and answering simple questions.

The Shared Workforce client lets you add behaviour to your models so that when an action occurs (for example, a photo is added), details of that action and model are sent to human workers, who can asses the data and return a response, which can be used to trigger further actions in the model (for example, marking it as spam).

You can view a live demo sandbox app at

The service is currently in private beta and is available as a Heroku add-on. You can apply for an invitation at

Getting started

Note to Heroku users: Getting started with Heroku takes less than 5 minutes. The best way to get started is to follow the README on the demo app source code.

Step 1 - get an API key

Register for a beta invitation at Once you are invited, you can create your account and retrieve your API key - which will look something like this:


Step 2 - add the gem

Add shared_workforce to your Gemfile:

gem "shared_workforce"

Create config/initializers/shared_workforce.rb

SharedWorkforce.configure do |config|
  config.api_key = "your-api-key"
  config.callback_host = "http://your-website-host"

If you're not using Rails, simply require the gem or include it in your Gemfile, set the client configuration settings as above.

Step 3 - define tasks

A class defines the content (for example, an image url) and instructions that the human worker will see when they work on your task, and the methods to be run once the task has been completed.

If, for example, you would like to approve a photo on upload, create a task class file in an app/tasks directory (or anywhere in the load path).


class TagPhotoTask
  include SharedWorkforce::Task

  title 'Please tag our photo'

  instruction 'Please look at the photo and tick all that apply.'

  answer_type :tags
  answer_options ['Offensive', 'Contains Nudity', 'Blurry or low quality', 'Upside down or sideways']

  responses_required 1

  on_success :moderate_photo

  def setup(photo)
    self.image_url = photo.url

  def moderate_photo(photo, responses)
    # responses => [{:answer=>['Offensive', 'Contains Nudity']}]
    if {|r| r[:answer]}.flatten.include?('Offensive')
      photo.user.quarantine("Uploded offensive photo")

Note: the task definition includes a setup method which is called automatically whenever the task is initialized. In the example, the task's image_url (the image shown to the worker) is set from the photo model's url attribute. Any of the task's attributes can be set this way.

Once you have created a task definition, you can request real human responses for a model instance by calling its create method. This can be done in an after_create callback in one of your Active Record models. This will be covered in more detail in the next section.

Step 4 - request tasks

When you publish a task it will be queued for a human worker to complete. You can publish a task in an after_create callback on a typical Active Record model:

class Photo < ActiveRecord::Base

  after_create :request_tags

  def request_tags

Note: In the example, the photo model instance (self) is used an argument to the TagPhotoTask.create method. This argument will be available in the setup method and the callback method as shown in the example of a Task Definition in step 3.

When the response(s) from the human workers are collected, the method specified in the on_success attribute in your task definition will be called. Typically this will take about 15 minutes. You can check the Shared Workforce web site for an up to date status on the current response time.

Unit testing

You can test your task definition by calling its methods directly.

it "should quarantine the user" do
  photo = Factory(:photo)
  task =
  task.moderate_photo(photo, [{:answer=>['Offensive']}])
  photo.user.should be_quarantined

Advanced definition options

Task types

SharedWorkforce currently supports 5 types of task. Possible options are:

  • "choice": choose one option from a list
  • "tags": choose any number of options from a list
  • "edit": edit the text in the 'text' attribute
  • "crop": crop the photo (see image_crop_ratio)
  • "rotate": rotate the photo

Multiple responses

SharedWorkforce supports multiple responses for each task. The callback method provides you with an array of responses from multiple workers. You can create your own logic to decide what to do. This is useful if you want to prevent destructive action unless a number of workers agree.

class ApprovePhotoTask
  include SharedWorkforce::Task

  title 'Approve Photo'

  instruction 'Look at this photo. Please tick all that apply.'
  responses_required 3

  answer_options ['Offensive', 'Contains Nudity', 'Blurry or low quality', 'Upside down or sideways']
  answer_type :tags

  on_complete :moderate_photo

  def moderate_photo(photo, responses)
    photo.hide! if { |r| r[:answer] }.all? { |a| a.include?('Contains Nudity') }


Replacing tasks

The "replace" option allows you to overwrite or update any existing tasks with the same name and callback params. This could be useful in the example to handle the situation where a user re-uploads their photo - you may only care about the latest one.

class ApprovePhotoTask
  include SharedWorkforce::Task
  replace true

Cancelling tasks

You can cancel tasks when they are no longer relevant.

class Photo
  after_destroy :cancel_tagging_request

  def cancel_tagging_request

Disabling requests during development

You can black-hole requests to Shared Workforce for testing and development by adding the following configuration option in your initializer:

SharedWorkforce.configure do |config|
  config.request_class = SharedWorkforce::TaskRequest::BlackHole


The SharedWorkforce Client is (c) Pigment and released under the MIT license

Jump to Line
Something went wrong with that request. Please try again.