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

Rack 'em, Stack 'em, Web Apps

Rack 'em, Stack 'em, Web Apps

While Rails is the most common Ruby web framework, it’s not the only option. Rack is a simple, elegant HTTP library, ideal for microservices and high performance applications.

In this talk, you’ll see Rack from top to bottom. Starting from the simplest app, we’ll grow our code into a RESTful HTTP API. We’ll test our code, write reusable middleware, and dig through what Rack provides out of the box. Throughout, we’ll balance when Rack is a good fit, and when larger tools are needed.

If you’ve heard of Rack but wondered where it fits in the Ruby web stack, here’s your chance!

Jason R Clark

April 25, 2017
Tweet

More Decks by Jason R Clark

Other Decks in Programming

Transcript

  1. 4

  2. 16 $ terminal $ rackup [2017-04-17] INFO WEBrick 1.3.1 [2017-04-17]

    INFO ruby 2.4.1 [2017-04-17] INFO WEBrick::HTTPServer#start: pid=32555 port=9292
  3. 17

  4. 25 # app_test.rb class ApplicationTest < Minitest::Test def test_its_good_enough get

    "/" assert last_response.ok? assert_equal "Good enough", last_response.body end end
  5. 26 # app_test.rb class ApplicationTest < Minitest::Test def test_its_good_enough get

    "/" assert last_response.ok? assert_equal "Good enough", last_response.body end end
  6. 27 # app_test.rb class ApplicationTest < Minitest::Test def test_its_good_enough get

    "/" assert last_response.ok? assert_equal "Good enough", last_response.body end end
  7. 35 # config.ru class Application def call(env) if env["PATH_INFO"] ==

    "/bot" [403, {}, ["Beep beep intruder"]] else [200, {}, ["Good enough"]] end end end
  8. 36 # config.ru class Application def call(env) if env["PATH_INFO"] ==

    "/bot" [403, {}, ["Beep beep intruder"]] else [200, {}, ["Good enough"]] end end end
  9. 39 # config.ru class Status def call(env) # ... end

    end map "/status" do run Status.new end map "/" do run Application.new end
  10. 40 # config.ru class Status def call(env) # ... end

    end map "/status" do run Status.new end map "/" do run Application.new end
  11. 41 # config.ru class Status def call(env) # ... end

    end run Rack::URLMap.new( "/status" => Status.new, "/" => Application.new )
  12. 43 # app.rb class Application def call(env) request = Rack::Request.new(env)

    if request.path_info == "/bot" [403, {}, ["Beep intruder"]] else [200, {}, ["Good enough"]] end end end
  13. 44 # app.rb class Application def call(env) request = Rack::Request.new(env)

    if request.path_info.match %r{/bot/(\d+)} [403, {}, ["Beep intruder on #{$1}"]] else [200, {}, ["Good enough"]] end end end
  14. 47 # app.rb class Application def call(env) request = Rack::Request.new(env)

    if request.path_info.match %r{/bot/(\d+)} bot = Database.find($1) [200, {}, [bot.to_s]] else # ... end end end
  15. 51 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  16. 52 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  17. 53 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  18. 54 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  19. 55 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  20. 56 # app.rb if request.path_info.match %r{/bot/(\d+)} if request.get? bot =

    Database.find($1) [200, {}, [bot.to_s]] elsif request.post? bot = request.body.read Database.save($1, bot) [200, {}, ["Wrote bot #{$1}"]] end end
  21. 58 $ terminal $ curl -X POST http://localhost:9292/bot/2 --data "\[*]/"

    Wrote bot 2 $ curl http://localhost:9292/bot/2 \[*]/
  22. 60 # app.rb def call(env) response = Rack::Response.new if #...

    bot = Database.find($1) response.write(bot.to_s) end response.finish end
  23. 61 # app.rb def call(env) response = Rack::Response.new if #...

    bot = Database.find($1) response.write(bot.to_s) end response.finish end
  24. 62 # app.rb def call(env) response = Rack::Response.new if #...

    bot = Database.find($1) response.write(bot.to_s) end response.finish end
  25. 63 # app.rb def call(env) response = Rack::Response.new if #...

    bot = Database.find($1) response.write(bot.to_s) end response.finish end
  26. 69 middleware middleware ----- ------ --- ---- app ----- ------

    --- ---- ----- ------ --- ---- call() call() call()
  27. 73 # middleware.rb def call(env) req = Rack::Request.new(env) key =

    req.get_header("HTTP_X_API_KEY") if key == "beep" @app.call(env) else [403, {}, ["FORBIDDEN! BEEP"]] end end
  28. 74 # middleware.rb def call(env) req = Rack::Request.new(env) key =

    req.get_header("HTTP_X_API_KEY") if key == "beep" @app.call(env) else [403, {}, ["FORBIDDEN! BEEP"]] end end
  29. 75 # middleware.rb def call(env) req = Rack::Request.new(env) key =

    req.get_header("HTTP_X_API_KEY") if key == "beep" @app.call(env) else [403, {}, ["FORBIDDEN! BEEP"]] end end
  30. 76 # middleware.rb def call(env) req = Rack::Request.new(env) key =

    req.get_header("HTTP_X_API_KEY") if key == "beep" @app.call(env) else [403, {}, ["FORBIDDEN! BEEP"]] end end
  31. 79 $ terminal $ curl http://localhost:9292/bot/1 FORBIDDEN! BEEP $ curl

    -H "X-Api-Key: beep" http://localhost:9292/bot/1 <--[1]-->