From 3198c588ac04ae370eb45605fd50f0d72ba98d84 Mon Sep 17 00:00:00 2001 From: Filip Tepper Date: Fri, 1 Mar 2013 13:11:03 +0100 Subject: [PATCH] added Puma backend --- Gemfile | 1 + README.md | 22 +++++++ heroku-forward.gemspec | 5 ++ lib/heroku/forward/backends/base.rb | 27 +++++++++ lib/heroku/forward/backends/puma.rb | 50 ++++++++++++++++ lib/heroku/forward/backends/thin.rb | 28 +-------- lib/heroku/forward/backends/unicorn.rb | 25 ++------ spec/lib/heroku/forward/backends/puma_spec.rb | 57 +++++++++++++++++++ spec/lib/heroku/forward/proxy/server_spec.rb | 10 ++-- spec/support/puma.rb | 1 + 10 files changed, 176 insertions(+), 50 deletions(-) create mode 100644 lib/heroku/forward/backends/base.rb create mode 100644 lib/heroku/forward/backends/puma.rb create mode 100644 spec/lib/heroku/forward/backends/puma_spec.rb create mode 100644 spec/support/puma.rb diff --git a/Gemfile b/Gemfile index 14dfc4e..96f5cc1 100644 --- a/Gemfile +++ b/Gemfile @@ -12,5 +12,6 @@ group :development, :test do gem "jeweler", "~> 1.6" gem "thin", "~> 1.5" gem "unicorn", "~> 4.5" + gem "puma", "~> 1.6" gem "em-http-request", "~> 1.0" end diff --git a/README.md b/README.md index 9216575..5fc050e 100644 --- a/README.md +++ b/README.md @@ -159,6 +159,28 @@ proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port) proxy.forward! ``` +### Puma + +For more information about Puma see [http://puma.io/](http://puma.io/). + +The Puma back-end supports the following options. + +* **application**: application to load +* **env**: environment, eg. `:development` +* **socket**: socket, eg. `/tmp/puma.sock` +* **config_file**: Puma configuration file + +```ruby +require 'heroku-forward' +require 'heroku/forward/backends/puma' + +application = File.expand_path('../my_app.ru', __FILE__) +config_file = File.expand_path('../config/puma.rb', __FILE__) +backend = Heroku::Forward::Backends::Puma.new(application: application, env: env, config_file: config_file) +proxy = Heroku::Forward::Proxy::Server.new(backend, host: '0.0.0.0', port: port) +proxy.forward! +``` + Fail-Safe --------- diff --git a/heroku-forward.gemspec b/heroku-forward.gemspec index e1fd8f2..23cab45 100644 --- a/heroku-forward.gemspec +++ b/heroku-forward.gemspec @@ -19,8 +19,10 @@ Gem::Specification.new do |s| "lib/heroku-forward.rb", "lib/heroku/forward.rb", "lib/heroku/forward/backends.rb", + "lib/heroku/forward/backends/base.rb", "lib/heroku/forward/backends/thin.rb", "lib/heroku/forward/backends/unicorn.rb", + "lib/heroku/forward/backends/puma.rb", "lib/heroku/forward/config/locales/en.yml", "lib/heroku/forward/errors.rb", "lib/heroku/forward/errors/backend_failed_to_start_error.rb", @@ -52,6 +54,7 @@ Gem::Specification.new do |s| s.add_development_dependency(%q, ["~> 1.6"]) s.add_development_dependency(%q, ["~> 1.5"]) s.add_development_dependency(%q, ["~> 4.5"]) + s.add_development_dependency(%q, ["~> 1.6"]) s.add_development_dependency(%q, ["~> 1.0"]) else s.add_dependency(%q, [">= 0.1.8"]) @@ -64,6 +67,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.5"]) s.add_dependency(%q, ["~> 4.5"]) + s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.0"]) end else @@ -77,6 +81,7 @@ Gem::Specification.new do |s| s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.5"]) s.add_dependency(%q, ["~> 4.5"]) + s.add_dependency(%q, ["~> 1.6"]) s.add_dependency(%q, ["~> 1.0"]) end end diff --git a/lib/heroku/forward/backends/base.rb b/lib/heroku/forward/backends/base.rb new file mode 100644 index 0000000..2f2d2be --- /dev/null +++ b/lib/heroku/forward/backends/base.rb @@ -0,0 +1,27 @@ +module Heroku + module Forward + module Backends + class Base + attr_accessor :application, :socket, :environment, :pid + + def terminate! + return false unless spawned? + Process.kill 'QUIT', @pid + @spawned = false + true + end + + def spawned? + !!@spawned + end + + private + + def check! + raise Heroku::Forward::Errors::MissingBackendOptionError.new('application') unless @application && @application.length > 0 + raise Heroku::Forward::Errors::MissingBackendApplicationError.new(@application) unless File.exists?(@application) + end + end + end + end +end diff --git a/lib/heroku/forward/backends/puma.rb b/lib/heroku/forward/backends/puma.rb new file mode 100644 index 0000000..c1d8974 --- /dev/null +++ b/lib/heroku/forward/backends/puma.rb @@ -0,0 +1,50 @@ +require 'heroku/forward/backends/base' + +module Heroku + module Forward + module Backends + class Puma < Base + attr_accessor :config_file + + def initialize(options = {}) + @application = options[:application] + @socket = options[:socket] || Heroku::Forward::Utils::Dir.tmp_filename('puma-', '.sock') + @env = options[:env] || 'development' + @config_file = options[:config_file] + end + + def spawn! + return false if spawned? + check! + + args = ['puma'] + args.push '--environment', @env + args.push '--config', @config_file if @config_file + args.push '--bind', "unix://#{@socket}" + args.push @application + + @pid = Spoon.spawnp(*args) + @spawned = true + end + + def terminate! + return false unless spawned? + Process.kill 'QUIT', @pid + @spawned = false + true + end + + def spawned? + !!@spawned + end + + private + + def check! + raise Heroku::Forward::Errors::MissingBackendOptionError.new('application') unless @application && @application.length > 0 + raise Heroku::Forward::Errors::MissingBackendApplicationError.new(@application) unless File.exists?(@application) + end + end + end + end +end \ No newline at end of file diff --git a/lib/heroku/forward/backends/thin.rb b/lib/heroku/forward/backends/thin.rb index 34f99d9..7e06f66 100644 --- a/lib/heroku/forward/backends/thin.rb +++ b/lib/heroku/forward/backends/thin.rb @@ -1,12 +1,9 @@ +require 'heroku/forward/backends/base' + module Heroku module Forward module Backends - class Thin - attr_accessor :application - attr_accessor :socket - attr_accessor :environment - attr_accessor :pid - + class Thin < Base attr_accessor :ssl attr_accessor :ssl_key_file attr_accessor :ssl_cert_file @@ -48,25 +45,6 @@ def spawn! @pid = Spoon.spawnp(* spawn_with) @spawned = true end - - def terminate! - return false unless spawned? - Process.kill 'QUIT', @pid - @spawned = false - true - end - - def spawned? - !! @spawned - end - - private - - def check! - raise Heroku::Forward::Errors::MissingBackendOptionError.new('application') unless @application && @application.length > 0 - raise Heroku::Forward::Errors::MissingBackendApplicationError.new(@application) unless File.exists?(@application) - end - end end end diff --git a/lib/heroku/forward/backends/unicorn.rb b/lib/heroku/forward/backends/unicorn.rb index cd85aae..7629c62 100644 --- a/lib/heroku/forward/backends/unicorn.rb +++ b/lib/heroku/forward/backends/unicorn.rb @@ -1,8 +1,10 @@ +require 'heroku/forward/backends/base' + module Heroku module Forward module Backends - class Unicorn - attr_accessor :application, :socket, :environment, :pid, :config_file + class Unicorn < Base + attr_accessor :config_file def initialize(options = {}) @application = options[:application] @@ -24,25 +26,6 @@ def spawn! @pid = Spoon.spawnp(*args) @spawned = true end - - def terminate! - return false unless spawned? - Process.kill 'QUIT', @pid - @spawned = false - true - end - - def spawned? - !!@spawned - end - - private - - def check! - raise Heroku::Forward::Errors::MissingBackendOptionError.new('application') unless @application && @application.length > 0 - raise Heroku::Forward::Errors::MissingBackendApplicationError.new(@application) unless File.exists?(@application) - end - end end end diff --git a/spec/lib/heroku/forward/backends/puma_spec.rb b/spec/lib/heroku/forward/backends/puma_spec.rb new file mode 100644 index 0000000..714d8ae --- /dev/null +++ b/spec/lib/heroku/forward/backends/puma_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'heroku/forward/backends/puma' + +describe Heroku::Forward::Backends::Puma do + describe "with defaults" do + let(:backend) do + Heroku::Forward::Backends::Puma.new + end + + after do + backend.terminate! + end + + it "#spawned?" do + backend.spawned?.should be_false + end + + context "checks" do + it "checks for application" do + expect { + backend.spawn! + }.to raise_error Heroku::Forward::Errors::MissingBackendOptionError + end + + it "checks that the application file exists" do + expect { + backend.application = 'spec/foobar' + backend.spawn! + }.to raise_error Heroku::Forward::Errors::MissingBackendApplicationError + end + + end + + it "#spawn!" do + backend.application = "spec/support/app.ru" + backend.spawn!.should_not == 0 + sleep 2 + backend.terminate!.should be_true + end + end + + context "constructs command" do + let(:backend) do + Heroku::Forward::Backends::Puma.new( + :application => 'spec/support/app.ru', + :env => 'test', + :socket => '/tmp/puma.sock', + :config_file => 'spec/support/puma.rb' + ) + end + + it "forwards arguments to spawner" do + Spoon.should_receive(:spawnp).with(*%w{puma --environment test --config spec/support/puma.rb --bind unix:///tmp/puma.sock spec/support/app.ru}).and_return(0) + backend.spawn! + end + end +end \ No newline at end of file diff --git a/spec/lib/heroku/forward/proxy/server_spec.rb b/spec/lib/heroku/forward/proxy/server_spec.rb index 57835ef..19528c5 100644 --- a/spec/lib/heroku/forward/proxy/server_spec.rb +++ b/spec/lib/heroku/forward/proxy/server_spec.rb @@ -1,16 +1,18 @@ require 'spec_helper' require 'heroku/forward/backends/thin' require 'heroku/forward/backends/unicorn' +require 'heroku/forward/backends/puma' describe Heroku::Forward::Proxy::Server do [ Heroku::Forward::Backends::Thin, - Heroku::Forward::Backends::Unicorn + Heroku::Forward::Backends::Unicorn, + Heroku::Forward::Backends::Puma ].each do |backend_type| - + context "with #{backend_type.name} backend" do - + let(:backend) do backend_type.new(:application => 'spec/support/app.ru') end @@ -42,7 +44,7 @@ end end end - + end end diff --git a/spec/support/puma.rb b/spec/support/puma.rb new file mode 100644 index 0000000..7a3b2a0 --- /dev/null +++ b/spec/support/puma.rb @@ -0,0 +1 @@ +threads 0, 2 \ No newline at end of file