Foreman is an intra-app process manager used in a development environment. Although its main use case is that, I’ve started to use it to manage inter-app dependencies as well.

Pow is a simple Rack server that also contains a DNS server capable of port proxying.

These two tools combined allow for easy management of a SOA based local development environment.

Imagine a primary Ruby on Rails application that depends on a couple of other services (Rails, Rack, Sinatra, Faye, Node.js etc) to be fully functional in development. These other services/apps could have their own Gemfile and process dependencies (worker, clock etc) that are also managed locally by Foreman.

Service Oriented Foreman by Matthew Kocher describes an approach that uses Foreman to manage these inter-app processes. Here’s a more complete example of that approach:

# primary_app/Procfile
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb
worker: bundle exec sidekiq -C config/sidekiq.yml
service1: script/run_app service1_app


# primary_app/.foreman
port: 8000


#!/usr/bin/env ruby

# primary_app/script/run_app
require 'bundler'

app_dir = "../#{ARGV[0]}"
cmd = %Q{bash -lc "cd #{app_dir} && bundle exec foreman start"}
Bundler.with_clean_env do
  system({'NOEXEC' => 'skip'}, cmd)
end


# service1_app/Procfile
web: bundle exec unicorn -p $PORT -c ./config/unicorn.rb


# service1_app/.foreman
port: 8001

Assuming the Service1 App has the relevant .foreman file then typing foreman start in the Primary App terminal will also start all the associated services specified in the primary Procfile.

In order to easily keep the associated services up-to-date (which may be developed by other teams), it’s nifty to include a helper script in the Primary App that helps make this easy. Here’s an example of what that may look like:

#!/usr/bin/env ruby

# primary_app/script/update_apps
require 'yaml'

{ 'service1_app'      => {},
  'service2_app'      => {:bundle => true, :pow => true},
  'service3_app'      => {:bundle => true, :pow => true, :npm => true}
}.each do |app, opts|

  app_dir = File.expand_path("../#{app}")
  bundle = opts[:bundle]
  npm = opts[:npm]
  pow = opts[:pow]

  bundle_install = bundle ? '&& bundle install' : ''
  npm_install = npm ? '&& npm install' : ''

  puts "=== Updating App: #{app}"
  if File.directory?(app_dir)
    update_apps_cmd =
      %Q{bash -lc "cd #{app_dir} && git pull --rebase #{bundle_install} #{npm_install}"}
  else
    update_apps_cmd = %Q{bash -lc "cd #{File.expand_path('../')} &&
      git clone git@github.com:myorg/#{app}.git &&
      cd #{app_dir} #{bundle_install} #{npm_install}"}
  end
  system(update_apps_cmd)

  if File.exists?("#{app_dir}/script/copy_configs")
    puts "=== Copying example configs for #{app}"
    system(%Q{bash -lc "cd #{app_dir} && script/copy_configs"})
  end

  if pow
    pow_app_config = File.expand_path("~/.pow/#{app}")
    unless File.exists?(pow_app_config)
      puts "=== Creating Pow port proxy config for #{app}"

      port = YAML::load_file("#{app_dir}/.foreman")['port']
      File.open(pow_app_config, 'w') { |f| f.write("#{port}\n") }
    end
  end

end

This setup has been working well enough with just a few services in development and using Webmock to stub out requests in the test environment. Going forward, each service could also provide a private library/gem that mocks its interface out in the test environment.

There are other approaches to managing services in development that are worth evaluating like service stubs and more comprehensively using Vagrant as alluded to in this talk.