63
Building web framework with Rack Marcin Kulik EuRuKo, 2010/05/30

Building web framework with Rack

  • Upload
    sickill

  • View
    3.983

  • Download
    8

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Building web framework with Rack

Building web frameworkwith Rack

Marcin Kulik

EuRuKo, 2010/05/30

Page 2: Building web framework with Rack

I am:Senior developer @ Lunar Logic Polska -agile Ruby on Rails development services

Working with web for 10 years

Using Ruby, Python, Java and others - inlove with Ruby since 2006

Page 3: Building web framework with Rack

Open Source contributor:CodeRack.org - Rack middlewarerepository

Open File Fast - Netbeans and JEditplugin

racksh - console for Rack apps

and many more (checkgithub.com/sickill)

Page 4: Building web framework with Rack

Why would you needanother framework?

"Because world needs yet another framework ;-)" - Tomash

Page 5: Building web framework with Rack

No, you probably don't needit actually :)

Page 6: Building web framework with Rack

Several mature frameworks

Tens of custom/experimental ones

"Don't reinvent the wheel", right?

But...

Page 7: Building web framework with Rack

But it's so easy that youshould at least try

Page 8: Building web framework with Rack

Rack provides everything you'll need, isextremely simple but extremelypowerful

It will help you to better understandHTTP

It will make you better developer

Your custom framework will be thefastest one *

It's fun! A lot of fun :)

Page 9: Building web framework with Rack

What is Rack?

Page 10: Building web framework with Rack

Ruby web applications interface

library

Page 11: Building web framework with Rack

Simplest Rack applicationrun lambda do |env| [200, { "Content-type" => "text/plain" }, ["Hello"]]end

Page 12: Building web framework with Rack

Simplest Rack middlewareclass EurukoMiddleware def initialize(app) @app = app end def call(env) env['euruko'] = 2010 @app.call endend

Page 13: Building web framework with Rack

Let's transform it intoframework!

Page 14: Building web framework with Rack

How does typical webframework look like?

Page 15: Building web framework with Rack

Rails

Merb

Pylons

Django

Rango

Page 16: Building web framework with Rack

Looks like MVC, more or less

Page 17: Building web framework with Rack

What features we'd like tohave?

Page 18: Building web framework with Rack

dependencies management

RESTful routing

controllers (session, flash messages)

views (layouts, templates, partials)

ORM

authentication

testing

console

Page 19: Building web framework with Rack

Available Rack middlewareand tools we can use

Page 20: Building web framework with Rack

(1/8) Gem dependencymanagement

Page 21: Building web framework with Rack

bundler"A gem to bundle gems"

github.com/carlhuda/bundler

Page 22: Building web framework with Rack

# Gemfilesource "http://gemcutter.org"gem "rack"# config.rurequire "bundler"Bundler.setupBundler.require

Page 23: Building web framework with Rack

(2/8) Routing

Page 24: Building web framework with Rack

Usher"Pure ruby general purpose router with interfaces for rails, rack,

email or choose your own adventure"

github.com/joshbuddy/usher

Page 25: Building web framework with Rack

# Gemfilegem "usher"# config.rurequire APP_ROOT / "config" / "router.rb"run Foobar::Router

Page 26: Building web framework with Rack

# config/router.rbmodule Foobar Router = Usher::Interface.for(:rack) do get('/').to(HomeController.action(:welcome)).name(:root) # root URL add('/login').to(SessionController.action(:login)).name(:login) # login get('/logout').to(SessionController.action(:logout)).name(:logout) # logout ... default ExceptionsController.action(:not_found) # 404 endend

Page 27: Building web framework with Rack

(3/8) Controller

Page 28: Building web framework with Rack

Let's build our basecontroller

every action is valid Rack endpoint

value returned from action becomesbody of the response

Page 29: Building web framework with Rack

# 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

Page 30: Building web framework with Rack

# config.rurequire APP_ROOT / "lib" / "base_controller.rb"Dir[APP_ROOT / "app" / "controllers" / "*.rb"].each do |f| require fend

Page 31: Building web framework with Rack

Now we can createUsersController

Page 32: Building web framework with Rack

# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController def index "Hello there!" endend

Page 33: Building web framework with Rack

Controllers also need following:session access

setting flash messages

setting HTTP headers

redirects

url generation

Page 34: Building web framework with Rack

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

Page 35: Building web framework with Rack

# Gemfilegem "rack-flash"gem "rack-contrib", :require => 'rack/contrib'# config.ruuse Rack::Flashuse Rack::Session::Cookieuse Rack::MethodOverrideuse Rack::NestedParams

Page 36: Building web framework with Rack

# 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 "You're being redirected" end endend

Page 37: Building web framework with Rack

Now we can use #session,#flash and #redirect_to

Page 38: Building web framework with Rack

# app/controllers/users_controller.rbclass UsersController < Foobar::BaseController def openid if session["openid.url"] flash[:notice] = "Cool!" redirect_to "/cool" else render end endend

Page 39: Building web framework with Rack

(4/8) Views

Page 40: Building web framework with Rack

Tilt"Generic interface to multiple Ruby template engines"

github.com/rtomayko/tilt

Page 41: Building web framework with Rack

# Gemfilegem "tilt"

Page 42: Building web framework with Rack

# 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

Page 43: Building web framework with Rack

(5/8) ORM

Page 44: Building web framework with Rack

DataMapper"DataMapper is a Object Relational Mapper written in Ruby. Thegoal is to create an ORM which is fast, thread-safe and feature

rich."

datamapper.org

Page 45: Building web framework with Rack

# 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

Page 46: Building web framework with Rack

(6/8) Authentication

Page 47: Building web framework with Rack

Warden"General Rack Authentication Framework"

github.com/hassox/warden

Page 48: Building web framework with Rack

# Gemfilegem "warden"# config.ruuse Warden::Manager do |manager| manager.default_strategies :password manager.failure_app = ExceptionsController.action(:unauthenticated)endrequire "#{APP_ROOT}/lib/warden.rb"

Page 49: Building web framework with Rack

# 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

Page 50: Building web framework with Rack

# 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

Page 51: Building web framework with Rack

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

Page 52: Building web framework with Rack

(7/8) Testing

Page 53: Building web framework with Rack

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 Webframeworks and testing libraries to build on."

github.com/brynary/rack-test

Page 54: Building web framework with Rack

# Gemfilegem "rack-test"

Page 55: Building web framework with Rack

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

Page 56: Building web framework with Rack

(8/8) Console

Page 57: Building web framework with Rack

racksh (aka Rack::Shell)"racksh is a console for Rack based ruby web applications. It's

like Rails script/console or Merb's merb -i, but for any app builton Rack"

github.com/sickill/racksh

Page 58: Building web framework with Rack

Installation

Page 59: Building web framework with Rack

gem install racksh

Page 60: Building web framework with Rack

Example racksh session

Page 61: Building web framework with Rack

$ 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

Page 62: Building web framework with Rack

Questions?

Page 63: Building web framework with Rack

That's it!Example code available at: github.com/sickill/example-rack-

framework

email: marcin.kulik at gmail.com / www: ku1ik.com / twitter:@sickill