Building web framework       with Rack          Marcin Kulik       EuRuKo, 2010/05/30
I am:Senior developer @ Lunar Logic Polska -agile Ruby on Rails development servicesWorking with web for 10 yearsUsing Rub...
Open Source contributor: CodeRack.org - Rack middleware repository Open File Fast - Netbeans and JEdit plugin racksh - con...
Why would you need   another framework?"Because world needs yet another framework ;-)" - Tomash
No, you probably dont need       it actually :)
Several mature frameworksTens of custom/experimental ones"Dont reinvent the wheel", right?But...
But its so easy that you  should at least try
Rack provides everything youll need, isextremely simple but extremelypowerfulIt will help you to better understandHTTPIt w...
What is Rack?
Ruby web applications interfacelibrary
Simplest Rack applicationrun lambda do |env|  [200, { "Content-type" => "text/plain" }, ["Hello"]]end
Simplest Rack middlewareclass EurukoMiddleware  def initialize(app)    @app = app  end  def call(env)    env[euruko] = 201...
Lets transform it into      framework!
How does typical webframework look like?
RailsMerbPylonsDjangoRango
Looks like MVC, more or less
What features wed like to         have?
dependencies managementRESTful routingcontrollers (session, flash messages)views (layouts, templates, partials)ORMauthenti...
Available Rack middleware   and tools we can use
(1/8) Gem dependency     management
bundler  "A gem to bundle gems"github.com/carlhuda/bundler
# Gemfilesource "http://gemcutter.org"gem "rack"# config.rurequire "bundler"Bundler.setupBundler.require
(2/8) Routing
Usher"Pure ruby general purpose router with interfaces for rails, rack,              email or choose your own adventure"  ...
# Gemfilegem "usher"# config.rurequire APP_ROOT / "config" / "router.rb"run Foobar::Router
# config/router.rbmodule Foobar  Router = Usher::Interface.for(:rack) do    get(/).to(HomeController.action(:welcome)).nam...
(3/8) Controller
Lets build our base       controllerevery action is valid Rack endpointvalue returned from action becomesbody of the respo...
# lib/base_controller.rbmodule Foobar  class BaseController    def call(env)      @request = Rack::Request.new(env)      @...
# config.rurequire APP_ROOT / "lib" / "base_controller.rb"Dir[APP_ROOT / "app" / "controllers" / "*.rb"].each do |f|  requ...
Now we can create UsersController
# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController  def index    "Hello there!"  endend
Controllers also need following: session access setting flash messages setting HTTP headers redirects url generation
rack-contrib  "Contributed Rack Middleware and Utilities"         github.com/rack/rack-contrib            rack-flash"Simpl...
# Gemfilegem "rack-flash"gem "rack-contrib", :require => rack/contrib# config.ruuse   Rack::Flashuse   Rack::Session::Cook...
# lib/base_controller.rbmodule Foobar  class BaseController    def status=(code); @response.status = code; end   def heade...
Now we can use #session, #flash and #redirect_to
# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController  def openid    if session["openid.url"...
(4/8) Views
Tilt"Generic interface to multiple Ruby template engines"             github.com/rtomayko/tilt
# Gemfilegem "tilt"
# lib/base_controller.rbmodule Foobar  class BaseController    def render(template=nil)      template ||= @request.env[x-r...
(5/8) ORM
DataMapper"DataMapper is a Object Relational Mapper written in Ruby. The goal is to create an ORM which is fast, thread-sa...
# Gemfilegem "dm-core"gem "dm-..."# app/models/user.rbclass User  include DataMapper::Resource  property :id, Serial  prop...
(6/8) Authentication
Warden"General Rack Authentication Framework"      github.com/hassox/warden
# Gemfilegem "warden"# config.ruuse Warden::Manager do |manager|  manager.default_strategies :password  manager.failure_ap...
# lib/warden.rbWarden::Manager.serialize_into_session do |user|  user.idendWarden::Manager.serialize_from_session do |key|...
# lib/base_controller.rbmodule Foobar  class BaseController    def authenticate!      @request.env[warden].authenticate!  ...
Now we can guard our action:# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController  def index...
(7/8) Testing
rack-test"Rack::Test is a small, simple testing API for Rack apps. It can be     used on its own or as a reusable starting...
# Gemfilegem "rack-test"
require "rack/test"class UsersControllerTest < Test::Unit::TestCase  include Rack::Test::Methods def app   Foobar::Router....
(8/8) Console
racksh (aka Rack::Shell) "racksh is a console for Rack based ruby web applications. Itslike Rails script/console or Merbs ...
Installation
gem install racksh
Example racksh session
$ rackshRack::Shell v0.9.7 started in development environment.>> $rack.get "/"=> #<Rack::MockResponse:0xb68fa7bc @body="<h...
Questions?
Thats it!Example code available at: github.com/sickill/example-rack-                        frameworkemail: marcin.kulik a...
Upcoming SlideShare
Loading in...5
×

Building web framework with Rack

4,232

Published on

My EuRuKo 2010 talk about how to build your own web framework using existing Rack components.

Published in: Technology, Education
1 Comment
6 Likes
Statistics
Notes
  • page 12, an error, it should be `@app.call(env)`
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total Views
4,232
On Slideshare
0
From Embeds
0
Number of Embeds
1
Actions
Shares
0
Downloads
46
Comments
1
Likes
6
Embeds 0
No embeds

No notes for slide

Building web framework with Rack

  1. 1. Building web framework with Rack Marcin Kulik EuRuKo, 2010/05/30
  2. 2. I am:Senior developer @ Lunar Logic Polska -agile Ruby on Rails development servicesWorking with web for 10 yearsUsing Ruby, Python, Java and others - inlove with Ruby since 2006
  3. 3. Open Source contributor: CodeRack.org - Rack middleware repository Open File Fast - Netbeans and JEdit plugin racksh - console for Rack apps and many more (check github.com/sickill)
  4. 4. Why would you need another framework?"Because world needs yet another framework ;-)" - Tomash
  5. 5. No, you probably dont need it actually :)
  6. 6. Several mature frameworksTens of custom/experimental ones"Dont reinvent the wheel", right?But...
  7. 7. But its so easy that you should at least try
  8. 8. Rack provides everything youll need, isextremely simple but extremelypowerfulIt will help you to better understandHTTPIt will make you better developerYour custom framework will be thefastest one *Its fun! A lot of fun :)
  9. 9. What is Rack?
  10. 10. Ruby web applications interfacelibrary
  11. 11. Simplest Rack applicationrun lambda do |env| [200, { "Content-type" => "text/plain" }, ["Hello"]]end
  12. 12. Simplest Rack middlewareclass EurukoMiddleware def initialize(app) @app = app end def call(env) env[euruko] = 2010 @app.call endend
  13. 13. Lets transform it into framework!
  14. 14. How does typical webframework look like?
  15. 15. RailsMerbPylonsDjangoRango
  16. 16. Looks like MVC, more or less
  17. 17. What features wed like to have?
  18. 18. dependencies managementRESTful routingcontrollers (session, flash messages)views (layouts, templates, partials)ORMauthenticationtestingconsole
  19. 19. Available Rack middleware and tools we can use
  20. 20. (1/8) Gem dependency management
  21. 21. bundler "A gem to bundle gems"github.com/carlhuda/bundler
  22. 22. # Gemfilesource "http://gemcutter.org"gem "rack"# config.rurequire "bundler"Bundler.setupBundler.require
  23. 23. (2/8) Routing
  24. 24. Usher"Pure ruby general purpose router with interfaces for rails, rack, email or choose your own adventure" github.com/joshbuddy/usher
  25. 25. # Gemfilegem "usher"# config.rurequire APP_ROOT / "config" / "router.rb"run Foobar::Router
  26. 26. # config/router.rbmodule Foobar Router = Usher::Interface.for(:rack) do get(/).to(HomeController.action(:welcome)).name(:root) # root add(/login).to(SessionController.action(:login)).name(:login) get(/logout).to(SessionController.action(:logout)).name(:logo ... default ExceptionsController.action(:not_found) # 404 endend
  27. 27. (3/8) Controller
  28. 28. Lets build our base controllerevery action is valid Rack endpointvalue returned from action becomesbody of the response
  29. 29. # lib/base_controller.rbmodule Foobar class BaseController def call(env) @request = Rack::Request.new(env) @response = Rack::Response.new resp_text = self.send(env[x-rack.action-name]) @response.write(resp_text) @response.finish end def self.action(name) lambda do |env| env[x-rack.action-name] = name self.new.call(env) end end endend
  30. 30. # config.rurequire APP_ROOT / "lib" / "base_controller.rb"Dir[APP_ROOT / "app" / "controllers" / "*.rb"].each do |f| require fend
  31. 31. Now we can create UsersController
  32. 32. # app/controllers/users_controller.rbclass UsersController < Foobar::BaseController def index "Hello there!" endend
  33. 33. Controllers also need following: session access setting flash messages setting HTTP headers redirects url generation
  34. 34. rack-contrib "Contributed Rack Middleware and Utilities" github.com/rack/rack-contrib rack-flash"Simple flash hash implementation for Rack apps" nakajima.github.com/rack-flash
  35. 35. # Gemfilegem "rack-flash"gem "rack-contrib", :require => rack/contrib# config.ruuse Rack::Flashuse Rack::Session::Cookieuse Rack::MethodOverrideuse Rack::NestedParams
  36. 36. # lib/base_controller.rbmodule Foobar class BaseController def status=(code); @response.status = code; end def headers; @response.header; end def session; @request.env[rack.session]; end def flash; @request.env[x-rack.flash]; end def url(name, opts={}); Router.generate(name, opts); end def redirect_to(url) self.status = 302 headers["Location"] = url "Youre being redirected" end endend
  37. 37. Now we can use #session, #flash and #redirect_to
  38. 38. # app/controllers/users_controller.rbclass UsersController < Foobar::BaseController def openid if session["openid.url"] flash[:notice] = "Cool!" redirect_to "/cool" else render end endend
  39. 39. (4/8) Views
  40. 40. Tilt"Generic interface to multiple Ruby template engines" github.com/rtomayko/tilt
  41. 41. # Gemfilegem "tilt"
  42. 42. # lib/base_controller.rbmodule Foobar class BaseController def render(template=nil) template ||= @request.env[x-rack.action-name] views_path = "#{APP_ROOT}/app/views" template_path = "#{views_path}/#{self.class.to_s.underscore}/" + "#{template}.html.erb" layout_path = "#{views_path}/layouts/application.html.erb" Tilt.new(layout_path).render(self) do Tilt.new(template_path).render(self) end end endend
  43. 43. (5/8) ORM
  44. 44. DataMapper"DataMapper is a Object Relational Mapper written in Ruby. The goal is to create an ORM which is fast, thread-safe and feature rich." datamapper.org
  45. 45. # Gemfilegem "dm-core"gem "dm-..."# app/models/user.rbclass User include DataMapper::Resource property :id, Serial property :login, String, :required => true property :password, String, :required => trueend# config.ruDir[APP_ROOT / "app" / "models" / "*.rb"].each do |f| require fend
  46. 46. (6/8) Authentication
  47. 47. Warden"General Rack Authentication Framework" github.com/hassox/warden
  48. 48. # Gemfilegem "warden"# config.ruuse Warden::Manager do |manager| manager.default_strategies :password manager.failure_app = ExceptionsController.action(:unauthenticated)endrequire "#{APP_ROOT}/lib/warden.rb"
  49. 49. # lib/warden.rbWarden::Manager.serialize_into_session do |user| user.idendWarden::Manager.serialize_from_session do |key| User.get(key)endWarden::Strategies.add(:password) do def authenticate! u = User.authenticate( params["username"], params["password"] ) u.nil? ? fail!("Could not log in") : success!(u) endend
  50. 50. # lib/base_controller.rbmodule Foobar class BaseController def authenticate! @request.env[warden].authenticate! end def logout!(scope=nil) @request.env[warden].logout(scope) end def current_user @request.env[warden].user end endend
  51. 51. Now we can guard our action:# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController def index authenticate! @users = User.all(:id.not => current_user.id) render endend
  52. 52. (7/8) Testing
  53. 53. rack-test"Rack::Test is a small, simple testing API for Rack apps. It can be used on its own or as a reusable starting point for Web frameworks and testing libraries to build on." github.com/brynary/rack-test
  54. 54. # Gemfilegem "rack-test"
  55. 55. require "rack/test"class UsersControllerTest < Test::Unit::TestCase include Rack::Test::Methods def app Foobar::Router.new end def test_redirect_from_old_dashboard get "/old_dashboard" follow_redirect! assert_equal "http://example.org/new_dashboard", last_request.url assert last_response.ok? endend
  56. 56. (8/8) Console
  57. 57. racksh (aka Rack::Shell) "racksh is a console for Rack based ruby web applications. Itslike Rails script/console or Merbs merb -i, but for any app built on Rack" github.com/sickill/racksh
  58. 58. Installation
  59. 59. gem install racksh
  60. 60. Example racksh session
  61. 61. $ rackshRack::Shell v0.9.7 started in development environment.>> $rack.get "/"=> #<Rack::MockResponse:0xb68fa7bc @body="<html>...", @headers={"Content-Type"=>"text/html", "Content-Length"=>"1812"} @status=200, ...>> User.count=> 123
  62. 62. Questions?
  63. 63. Thats it!Example code available at: github.com/sickill/example-rack- frameworkemail: marcin.kulik at gmail.com / www: ku1ik.com / twitter: @sickill
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×