Ruby Doable Framework

About

This is a gem largely targeted and making the lives of DevOps / SysAdmin types easier. Whether it is installing complicated sets of software, writing maintenance scripts, or automating administrative tasks, Doable aims to simplify these common tasks, as well as make them more reliable and robust.

The core set of values for this framework are:

  • Extensibility
  • Modularity
  • Simplicity
  • Concurrency
  • Flexibility
  • Reliability
  • Remaining lightweight

These goals are largely achieved through a small set of core features, with nearly all functionality broken out into plugins that can be enabled as required.

Building

While it is recommended to simply install via a normal rubygems search via gem install doable (or adding gem "doable" to your Gemfile), you can build and install the gem yourself. This can be done like so:

#!bash
# need Mercurial to clone the repo... or download it from https://bitbucket.org/jgnagy/doable/get/tip.zip
hg clone https://bitbucket.org/jgnagy/doable
cd doable
# highly recommend using RVM here, and Ruby 2.x or above
gem build doable.gemspec
# install what you just built
gem install ./doable-*.gem

Sometimes you might get lucky and there's a semi-recent version of the pre-built gem available on bitbucket here.

Usage

Require the right gem(s):

#!ruby
require 'doable'

To create a simple job, define a plan:

#!ruby
job = Doable::Job.plan do |j|
  j.before  { log "Starting my awesome job" }
  j.step    { # do some stuff here }
  j.attempt { # try to do some other stuff here }
  j.after   { log "Looks like we're all set" }
end

job.run # executes our job

This job is obviously pretty boring, but it shows some of the basics. Now let's continue and include some additional helpers in a new, more complex job:

#!ruby
require 'doable/helpers/password'

class JobWithPasswords < Doable::Job
  include Doable::Helpers::Password
end

job2 = JobWithPasswords.plan do |j|
  j.before  { log "Here's a password for you..." }
  j.step    { puts "Password: " + generate_password(16) }
end

job2.run

After this job runs, you should see output that looks something like:

[2015/01/29 00:10:36] Here's a password for you...
Password: ZwyIVH8LjHGYYzDX
[2015/01/29 00:10:36] All Job steps completed successfully!

Let's try something more realistic. Say the goal is to:

  • check to make sure we have enough space to do things
  • add a user (unless they already exist)
  • unpack some software
  • run it as our new user

Here is an example job:

#!ruby
require 'doable/helpers/linux'

class InstallCoolSoftware < Doable::Job
  include Doable::Helpers::Linux
end

job3 = InstallCoolSoftware.plan do |j|
  # make sure we have 500MB of space
  j.before  {
    @app_dir = "/opt/coolsoft/server"
    check_disk_space @app_dir, (500 * 1024 * 1024)
  }

  j.step    { 
    @user  = "cooluser"
    find_or_create_user @user, File.dirname(@app_dir)
  }

  j.step    {
    find_or_create_directory @app_dir

    FileUtils.cd(@app_dir) do
      tee "tar -xf /opt/media/files/coolsoft-server-1.2.3.tgz --strip=1"
    end

    chown @user, @user, @app_dir, :recursive => true
  }.rollback {
    remove(@app_dir)
  }

  j.step     {
    run_as_user @user, File.join(@app_dir, "/bin", "start.sh")
  }
end

job3.run

Hopefully this demonstrates how to get access to additional helper methods. Also, you may have noticed the rollback method added to the step for uncompressing the tarball. If things go wrong, rollbacks can be run to revert changes. To do so, just do this:

job3.rollback!

The Job instance also has many other methods available for accessing its internals.

RubyDoc

The latest, published documentation can be seen on RubyDoc

License

Doable is distributed under the MIT License.

Contributing

I welcome pull-requests. I may or may not use your code, but I encourage the growth of others too. If this project inspires you to contribute, feel free to fork my code and submit a pull request. In most cases, I'll probably recommend you package additions in an extension gem rather than including new files, etc, directly into the core codebase.