All I Need to Know I Learned by Writing My Own Web Framework
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 6,475 views

Slides from session at Rubyconf 2008

Slides from session at Rubyconf 2008

Statistics

Views

Total Views
6,475
Views on SlideShare
6,368
Embed Views
107

Actions

Likes
8
Downloads
105
Comments
0

3 Embeds 107

http://www.culann.com 93
http://www.slideshare.net 9
http://archive.culann.com 5

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

    • All I Really Need to Know* I Learned by Writing My Own Web Framework Ben Scofield 7 November 2008 Rubyconf * about Ruby and the web
    • DSLs DSL Intimidate and Frighten flickr: cwsteeds
    • Rails
    • Rails your custom framework
    • Starter Projects
    • Hello World
    • main() { printf(quot;hello, worldnquot;); } -module(hello). -export([hello/0]). hello() -> io:format(quot;Hello World!~nquot;, []). PROGRAM HELLO PRINT*, 'Hello World!' END
    • main = putStrLn quot;Hello Worldquot; Imports System.Console Class HelloWorld Public Shared Sub Main() WriteLine(quot;Hello, world!quot;) End Sub End Class !greeting. +!greeting : true <- .print(quot;Hello Worldquot;).
    • Applications
    • To-do lists
    • DIY Blog
    • Frameworks?
    • PHP Framework (based on Rails)
    • NOT FOR PRODUCTION!
    • well, maybe for production
    • but really: NOT FOR PRODUCTION!
    • Frameworks
    • ActionMailer ActionPack ActiveRecord Ruby on Rails ActiveResource ActiveSupport Railties
    • request => response
    • Sequel ActiveRecord persistence layer DataMapper Og
    • ERB Liquid Amrita2 templating layer Erubis Markaby HAML
    • Ramaze Waves ActionPack the middle layer Merb Core Sinatra Camping
    • ActionMailer Merb Helpers utilities ActiveSupport Railties
    • Tools
    • Rack
    • Mack CGI Coset SCGI Camping LSWS Halcyon Mongrel Maveric WEBrick Sinatra FastCGI Vintage Fuzed Ramaze Thin Waves Ebb Merb
    • mod_rack
    • Rack::Lint Rack::URLMap Rack::Deflater Rack::CommonLogger middlewares Rack::ShowExceptions Rack::Reloader Rack::Static Rack::Cache
    • Rack::Request utility Rack::Response
    • Other Layers
    • Sequel ActiveRecord persistence layer DataMapper Og
    • ERB Liquid Amrita2 templating layer Erubis Markaby HAML
    • Start at the End
    • Vision
    • REST and Resources
    • fully-formed web applications built on resources
    • fully-formed web applications built on resources
    • Birth of Athena
    • Dionysus ask me after if you don’t get the joke
    • Project
    • Extraction flickr: 95579828@N00
    • 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} } }
    • Results
    • puts quot;Starting Athena applicationquot; require 'active_record' require File.expand_path(File.join('./vendor/athena/lib/athena')) puts quot;... Framework loadedquot; Athena.require_all_libs_relative_to('resources') puts quot;... Resources loadedquot; use Rack::Static, :urls => ['/images', '/stylesheets'], :root => 'public' run Athena::Application.new puts quot;... Application startednnquot; puts quot;^C to stop the applicationquot;
    • 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 end end
    • 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 # ... end end
    • 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 end end
    • 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) end end
    • 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 quot;set_table_name '#{self.name}'.tableizequot; eval quot;Persistent#{self.name} = pklassquot; @@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 # ...
    • Lessons About the Web
    • HTTP status codes http://thoughtpad.net/alan-dean/http-headers-status.gif
    • 207 Multi-Status
    • 207 sucks Multi-Status
    • rack::cache http://tomayko.com/src/rack-cache/
    • Lessons About Ruby
    • 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 # ... end Form Parameters end
    • 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 quot;set_table_name '#{self.name}'.tableizequot; eval quot;Persistent#{self.name} = pklassquot; @@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
    • Tangled classes flickr: randomurl
    • 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 end end Exception Propagation
    • this session has been a LIE
    • More to learn flickr: glynnis
    • ... always flickr: mointrigue
    • http://github.com/bscofield/athena (under construction)
    • Thank you! Questions? Ben Scofield Development Director, Viget Labs http://www.viget.com/extend | http://www.culann.com ben.scofield@viget.com | bscofield@gmail.com