Introducing Invoker - Manage processes in development envionment

Written by Hemant on June 8, 2013; tagged under , ,

If you are working on a large application with several dependencies such as - more than one application server, background job, it can get tedious to start and stop and check their logs in bunch of different places.

Invoker is a small utility we wrote to manage all such processes from one place. Invoker owes much to Foreman for inspiration, however unlike Foreman it has been exclusively written to be used in development environment. Some major differences are:

To get started we need to install invoker gem which will give us command line utility called invoker, we can do that via:

gem install invoker

Currently it only works with Ruby 1.9.3 and 2.0.

After that we need to start by creating a ini file which will define processes we want to manage using invoker. For example:

[rails]
directory = /home/gnufied/god_particle
command = zsh -c 'bundle exec rails s -p 5000'

[dj]
directory = /home/gnufied/god_particle
command = zsh -c 'bundle exec ruby script/delayed_job'

[events]
directory = /home/gnufied/god_particle
command = zsh -c 'bundle exec ruby script/event_server'

After that we can start process manager via:

~> invoker start invoker.ini

Above command will start all your processes in one terminal with their stdout/stderr merged and labelled.

Now additionally we can control individual process by,

# Will try to stop running delayed job by sending SIGINT to the process
~> invoker remove dj

# If Process can't be killed by SIGINT send a custom signal
~> invoker remove dj -s 9

# add and start running
~> invoker add dj

One can also enable OSX notifications for crashed processes by installing terminal-notification gem. It is not a dependency, but can be useful if something crashed and you weren’t paying attention.

Using with rbenv or rvm

The way rbenv and rvm work sometimes creates problems when you are trying to use a process supervisor like invoker. There are couple of things to keep in mind, If you are running invoker with Ruby version x, but your application requires Ruby version Y:

I hope you find this gem useful and do not forget to follow us on twitter @codemancershq if you want to keep up with latest utility we are making to make our lives wee bit easier.

Source Code

The kitchen sink libraries

Written by Hemant on May 8, 2012; tagged under , ,

There was a time when Ruby libraries where like small Unix utilities, they did one thing and they did it well. If you go read code of some of the older plugins, it will be quite simple and straightforward affair.

Today however trend is, to write kitchen sink libraries that do a lot. You want file upload, they work at Rack level and support not only Rails but all the rack complaint frameworks under the sun, not only relational databases but Mongo as well, not just local file storage but S3/Cloudfiles as well, not just file uploads but some kind of caching via Rack::Cache as well.

Now at its core, handling file uploads is very simple and straightforward affair. Uploading to S3 or Cloudfiles is even simpler. I daresay, getting started with the kitchen sink library will be easy and simple as well. But if one thing Law of leaky Abstractions has taught us that is - eventually you will have look under the hood of these kitchen sink libraries and suddently MysqlAdapter which subclasses AbstractPersistenceAdapter won’t appear very attractive, because your application doesn’t gives two hoots about Mongo support. Jumping through hooves to grab that uploaded file via Rack will appear waste of time as well because you are probably never going to need this working on framework other than Rails. And while support for Rack::Cache was nice, you can’t use it in production anyways.

I don’t know about you, but I prefer gems and plugins that I include in my code easily grokkable. Below is a time bomb taken from a popular file upload gem and good luck - if you will have to grok this code and fix something yourself.

autoload_files_in_dir = proc do |path, namespace|
  # Define the module
  eval("module #{namespace}; end")
  # Autoload modules/classes in that module
  Dir.glob("#{path}/*.rb").each do |file|
    file = File.expand_path(file)
    sub_const_name = camelize[ File.basename(file, '.rb') ]
    eval("#{namespace}.autoload('#{sub_const_name}', '#{file}')")
  end
  # Recurse on subdirectories
  Dir.glob("#{path}/*/").each do |dir|
    sub_namespace = camelize[ File.basename(dir) ]
    autoload_files_in_dir[dir, "#{namespace}::#{sub_namespace}"]
  end
end