All I Need to Know I Learned by Writing My Own Web Framework

71
All I Really Need to Know* I Learned by Writing My Own Web Framework 7 November 2008 Rubyconf Ben Scofield * about Ruby and the web

description

Slides from session at Rubyconf 2008

Transcript of All I Need to Know I Learned by Writing My Own Web Framework

Page 1: All I Need to Know I Learned by Writing My Own Web Framework

All I Really Need to Know* I Learned by Writing My Own Web Framework

7 November 2008Rubyconf Ben Scofield

* about Ruby and the web

Page 2: All I Need to Know I Learned by Writing My Own Web Framework
Page 3: All I Need to Know I Learned by Writing My Own Web Framework
Page 5: All I Need to Know I Learned by Writing My Own Web Framework
Page 6: All I Need to Know I Learned by Writing My Own Web Framework

Rails

Page 7: All I Need to Know I Learned by Writing My Own Web Framework

your customframework

Rails

Page 8: All I Need to Know I Learned by Writing My Own Web Framework

Starter Projects

Page 9: All I Need to Know I Learned by Writing My Own Web Framework

Hello World

Page 10: All I Need to Know I Learned by Writing My Own Web Framework

main() { printf("hello, world\n");}

-module(hello).

-export([hello/0]).

hello() -> io:format("Hello World!~n", []).

PROGRAM HELLO PRINT*, 'Hello World!' END

Page 11: All I Need to Know I Learned by Writing My Own Web Framework

!greeting.+!greeting : true <- .print("Hello World").

Imports System.Console

Class HelloWorld

Public Shared Sub Main() WriteLine("Hello, world!") End Sub

End Class

main = putStrLn "Hello World"

Page 12: All I Need to Know I Learned by Writing My Own Web Framework

Applications

Page 13: All I Need to Know I Learned by Writing My Own Web Framework

To-do lists

Page 14: All I Need to Know I Learned by Writing My Own Web Framework

DIY Blog

Page 15: All I Need to Know I Learned by Writing My Own Web Framework

Frameworks?

Page 16: All I Need to Know I Learned by Writing My Own Web Framework

PHP Framework (based on Rails)

Page 17: All I Need to Know I Learned by Writing My Own Web Framework

NOT FOR PRODUCTION!

Page 18: All I Need to Know I Learned by Writing My Own Web Framework

well, maybe for production

Page 19: All I Need to Know I Learned by Writing My Own Web Framework

but really:

NOT FOR PRODUCTION!

Page 20: All I Need to Know I Learned by Writing My Own Web Framework

Frameworks

Page 21: All I Need to Know I Learned by Writing My Own Web Framework

ActionMailerActionPackActiveRecordActiveResourceActiveSupport

Railties

Ruby on Rails

Page 22: All I Need to Know I Learned by Writing My Own Web Framework

request => response

Page 23: All I Need to Know I Learned by Writing My Own Web Framework

SequelActiveRecordDataMapper

Og

persistence layer

Page 24: All I Need to Know I Learned by Writing My Own Web Framework

ERBLiquidAmrita2ErubisMarkabyHAML

templating layer

Page 25: All I Need to Know I Learned by Writing My Own Web Framework

RamazeWaves

ActionPackMerb CoreSinatraCamping

the middle layer

Page 26: All I Need to Know I Learned by Writing My Own Web Framework

ActionMailerMerb HelpersActiveSupport

Railties

utilities

Page 27: All I Need to Know I Learned by Writing My Own Web Framework

Tools

Page 28: All I Need to Know I Learned by Writing My Own Web Framework

Rack

Page 29: All I Need to Know I Learned by Writing My Own Web Framework

MackCosetCampingHalcyonMavericSinatraVintageRamazeWavesMerb

CGISCGILSWS

MongrelWEBrickFastCGIFuzedThinEbb

Page 30: All I Need to Know I Learned by Writing My Own Web Framework

mod_rack

Page 31: All I Need to Know I Learned by Writing My Own Web Framework

Rack::LintRack::URLMapRack::Deflater

Rack::CommonLoggerRack::ShowExceptions

Rack::ReloaderRack::StaticRack::Cache

middlewares

Page 32: All I Need to Know I Learned by Writing My Own Web Framework

Rack::RequestRack::Responseutility

Page 33: All I Need to Know I Learned by Writing My Own Web Framework

Other Layers

Page 34: All I Need to Know I Learned by Writing My Own Web Framework

SequelActiveRecordDataMapper

Og

persistence layer

Page 35: All I Need to Know I Learned by Writing My Own Web Framework

ERBLiquidAmrita2ErubisMarkabyHAML

templating layer

Page 36: All I Need to Know I Learned by Writing My Own Web Framework

Start at the End

Page 37: All I Need to Know I Learned by Writing My Own Web Framework

Vision

Page 38: All I Need to Know I Learned by Writing My Own Web Framework

REST and Resources

Page 39: All I Need to Know I Learned by Writing My Own Web Framework

fully-formed web applicationsbuilt on resources

Page 40: All I Need to Know I Learned by Writing My Own Web Framework

fully-formed web applications

built on resources

Page 41: All I Need to Know I Learned by Writing My Own Web Framework
Page 42: All I Need to Know I Learned by Writing My Own Web Framework

Birth of Athena

Page 43: All I Need to Know I Learned by Writing My Own Web Framework

Dionysusask me after if you don’t get the joke

Page 44: All I Need to Know I Learned by Writing My Own Web Framework

Project

Page 46: All I Need to Know I Learned by Writing My Own Web Framework
Page 47: All I Need to Know I Learned by Writing My Own Web Framework

class Habit < Athena::Resource def get @habit = Habit.find(@id) end # ...end

RouteMap = { :get => { /\/habit\/new/ => {:resource => :habit, :template => :new}, /\/habit\/(\d+)/ => {:resource => :habit}, /\/habit\/(\d+)\/edit/ => {:resource => :habit, :template => :edit} }, :put => { /\/habit\/(\d+)/ => {:resource => :habit} }, :delete => { /\/habit\/(\d+)/ => {:resource => :habit} }, :post => { /\/habit\// => {:resource => :habit} }}

Page 48: All I Need to Know I Learned by Writing My Own Web Framework

Results

Page 49: All I Need to Know I Learned by Writing My Own Web Framework
Page 50: All I Need to Know I Learned by Writing My Own Web Framework
Page 51: All I Need to Know I Learned by Writing My Own Web Framework

puts "Starting Athena application"

require 'active_record'require File.expand_path(File.join('./vendor/athena/lib/athena'))puts "... Framework loaded"

Athena.require_all_libs_relative_to('resources')puts "... Resources loaded"

use Rack::Static, :urls => ['/images', '/stylesheets'], :root => 'public'run Athena::Application.newputs "... Application started\n\n"

puts "^C to stop the application"

Page 52: All I Need to Know I Learned by Writing My Own Web Framework

module Athena class Application def self.root File.join(File.dirname(__FILE__), '..', '..', '..', '..') end def self.route_map @route_map ||= { :get => {}, :post => {}, :put => {}, :delete => {} } @route_map end def call(environment) request = Rack::Request.new(environment) request_method = request.params['_method'] ? # ... matching_route = Athena::Application.route_map # ...

if matching_route resource = matching_route.last[:class].new(request, request_method) resource.template = matching_route.last[:template] return resource.output else raise Athena::BadRequest end end endend

Page 53: All I Need to Know I Learned by Writing My Own Web Framework

module Athena class Resource extend Athena::Persistence attr_accessor :template def self.inherited(f) unless f == Athena::SingletonResource url_name = f.name.downcase routing = url_name + id_pattern url_pattern = /^\/#{routing}$/

Athena::Application.route_map[:get][/^\/#{routing}\/edit$/] = # ... Athena::Application.route_map[:post][/^\/#{url_name}$/] = # ... # ... end end def self.default_resource Athena::Application.route_map[:get][/^\/$/] = {:class => # ... end def self.id_pattern '\/(\d+)' end def get; raise Athena::MethodNotAllowed; end def put; raise Athena::MethodNotAllowed; end def post; raise Athena::MethodNotAllowed; end def delete; raise Athena::MethodNotAllowed; end

# ... endend

Page 54: All I Need to Know I Learned by Writing My Own Web Framework

class Habit < Athena::Resource persist(ActiveRecord::Base) do validates_presence_of :name end

def get @habit = Habit.find_by_id(@id) || Habit.new_record end def post @habit = Habit.new_record(@params['habit']) @habit.save! end def put @habit = Habit.find(@id) @habit.update_attributes!(@params['habit']) end def delete Habit.find(@id).destroy endend

Page 55: All I Need to Know I Learned by Writing My Own Web Framework

class Habits < Athena::SingletonResource default_resource def get @habits = Habit.all end def post @results = @params['habits'].map do |hash| Habit.new_record(hash).save end end def put @results = @params['habits'].map do |id, hash| Habit.find(id).update_attributes(hash) end end def delete Habit.find(:all, :conditions => ['id IN (?)', @params['habit_ids']] ).map(&:destroy) endend

Page 56: All I Need to Know I Learned by Writing My Own Web Framework

module Athena module Persistence def self.included(base) base.class_eval do @@persistence_class = nil end end

def persist(klass, &block) pklass = Class.new(klass)

pklass.class_eval(&block) pklass.class_eval "set_table_name '#{self.name}'.tableize" eval "Persistent#{self.name} = pklass" @@persistence_class = pklass @@persistence_class.establish_connection( YAML::load(IO.read(File.join(Athena::Application.root, # ... ) end def new_record(*args) self.persistence_class.new(*args) end def persistence_class @@persistence_class end

# ...

Page 57: All I Need to Know I Learned by Writing My Own Web Framework

Lessons About the Web

Page 58: All I Need to Know I Learned by Writing My Own Web Framework

HTTP status codeshttp://thoughtpad.net/alan-dean/http-headers-status.gif

Page 59: All I Need to Know I Learned by Writing My Own Web Framework

207Multi-Status

Page 60: All I Need to Know I Learned by Writing My Own Web Framework

207Multi-Status

sucks

Page 61: All I Need to Know I Learned by Writing My Own Web Framework

rack::cachehttp://tomayko.com/src/rack-cache/

Page 62: All I Need to Know I Learned by Writing My Own Web Framework

Lessons About Ruby

Page 63: All I Need to Know I Learned by Writing My Own Web Framework

module Athena class Application # ...

def process_params(params) nested_pattern = /^(.+?)\[(.*\])/ processed = {} params.each do |k, v| if k =~ nested_pattern scanned = k.scan(nested_pattern).flatten first = scanned.first last = scanned.last.sub(/\]/, '') if last == '' processed[first] ||= [] processed[first] << v else processed[first] ||= {} processed[first][last] = v processed[first] = process_params(processed[first]) end else processed[k] = v end end processed.delete('_method') processed end

# ... endend Form Parameters

Page 64: All I Need to Know I Learned by Writing My Own Web Framework

module Athena module Persistence def self.included(base) base.class_eval do @@persistence_class = nil end end

def persist(klass, &block) pklass = Class.new(klass)

pklass.class_eval(&block) pklass.class_eval "set_table_name '#{self.name}'.tableize" eval "Persistent#{self.name} = pklass" @@persistence_class = pklass @@persistence_class.establish_connection( YAML::load(IO.read(File.join(Athena::Application.root, # ... ) end def new_record(*args) self.persistence_class.new(*args) end def persistence_class @@persistence_class end

# ...

Dynamic class creation

Page 66: All I Need to Know I Learned by Writing My Own Web Framework

module Athena module Persistence # ... def method_missing(name, *args) return self.persistence_class.send(name, *args) rescue ActiveRecord::ActiveRecordError => e raise e rescue super end endend

Exception Propagation

Page 67: All I Need to Know I Learned by Writing My Own Web Framework

this session has been a LIE

Page 70: All I Need to Know I Learned by Writing My Own Web Framework

http://github.com/bscofield/athena(under construction)