Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Shut Up and Pipe! Unix-style Object Collaboration in Rack and Vagrant

Paul Hinze
January 29, 2014

Shut Up and Pipe! Unix-style Object Collaboration in Rack and Vagrant

Paul Hinze

January 29, 2014
Tweet

More Decks by Paul Hinze

Other Decks in Technology

Transcript

  1. paulh Paul Hinze jbelser Jeff Belser liz Liz Abinante nomitch

    Mike Nomitch jstern Jennifer Stern z Mike Ziwisky illstructure
  2. A B

  3. A B

  4. an Object that responds to #call #call accepts one Hash

    argument #call returns an Array with [status, headers, body] where and
  5. app = ->(env) { [200, {}, 'Hello, World!'] } !

    Rack::Handler::Thin.run(app) It's Simple!
  6. app = ->(env) { [200, {}, 'Hello, World!'] } Rack::Handler.const_get(

    %i[ Thin WEBrick Unicorn Puma ].sample ).run(app) Any Server
  7. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  8. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  9. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  10. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  11. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  12. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  13. class YellIt def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) [status, headers, body.upcase] end end ! app = ->(env) {[200, {}, 'Hello, World!']} ! Rack::Handler::Thin.run(YellIt.new(app))
  14. class Rack::Logger def initialize(app, logger=Logger.new(STDOUT)) @app = app @logger =

    logger end ! def call(env) status, headers, body = @app.call(env) @logger.info("[#{Time.now}] #{status}") [status, headers, body] end end A logger
  15. class Rack::ContentLength def initialize(app) @app = app end ! def

    call(env) status, headers, body = @app.call(env) ! if !headers['Content-Length'] headers['Content-Length'] = body.length end ! [status, headers, body] end end A header
  16. class Rack::Runtime def initialize(app) @app = app end ! def

    call(env) start_time = Time.now status, headers, body = @app.call(env) request_time = Time.now - start_time ! headers['X-Runtime'] = "%0.6f" % request_time ! [status, headers, body] end end A timer
  17. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth) A auth!
  18. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth)
  19. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth)
  20. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth)
  21. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth)
  22. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth)
  23. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth) Our app doesn't need to care about Authentication
  24. class Rack::Auth::Basic def initialize(app, user, pass) @app, @user, @pass =

    app, user, pass end ! def call(env) if authenticated?(env['HTTP_AUTHORIZATION']) @app.call(env) else [403, {}, "Go Away!"] end end ! def authenticated?(auth) return false unless auth _, token = auth.split(' ') user, pass = token.unpack('m*').first.split(':') (user == @user && pass == @pass) end end ! app = ->(env) {[200, {}, 'Hello, World!']} with_auth = Rack::Auth::Basic.new(app, 'paulh', 'hello') Rack::Handler::Thin.run(with_auth) Amazing!
  25. class Rack::Builder def initialize(app, &block) @use = [] @app =

    app instance_eval(&block) end ! def use(middleware) @use.push ->(app) { middleware.new(app) } end ! def call(env) @use.reverse.inject(@app) { |app, middleware_builder| middleware_builder.call(app) }.call(env) end end ! built = Rack::Builder.new(app) do use Rack::Logger use Rack::ContentLength use Rack::Runtime end ! Rack::Handler::Thin.run(built)
  26. def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use

    SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end
  27. def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use

    SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end Extensibility!
  28. def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use

    SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end 1. 2. 3. 4. 5. 6. wait...
  29. def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use

    SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end Complexity escalate quickly can
  30. B def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder

    b.use SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end A