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

5,523 views
5,223 views

Published on

Slides from session at Rubyconf 2008

Published in: Technology
0 Comments
8 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
5,523
On SlideShare
0
From Embeds
0
Number of Embeds
113
Actions
Shares
0
Downloads
107
Comments
0
Likes
8
Embeds 0
No embeds

No notes for slide

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

  1. 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
  2. DSLs DSL Intimidate and Frighten flickr: cwsteeds
  3. Rails
  4. Rails your custom framework
  5. Starter Projects
  6. Hello World
  7. main() { printf(quot;hello, worldnquot;); } -module(hello). -export([hello/0]). hello() -> io:format(quot;Hello World!~nquot;, []). PROGRAM HELLO PRINT*, 'Hello World!' END
  8. 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;).
  9. Applications
  10. To-do lists
  11. DIY Blog
  12. Frameworks?
  13. PHP Framework (based on Rails)
  14. NOT FOR PRODUCTION!
  15. well, maybe for production
  16. but really: NOT FOR PRODUCTION!
  17. Frameworks
  18. ActionMailer ActionPack ActiveRecord Ruby on Rails ActiveResource ActiveSupport Railties
  19. request => response
  20. Sequel ActiveRecord persistence layer DataMapper Og
  21. ERB Liquid Amrita2 templating layer Erubis Markaby HAML
  22. Ramaze Waves ActionPack the middle layer Merb Core Sinatra Camping
  23. ActionMailer Merb Helpers utilities ActiveSupport Railties
  24. Tools
  25. Rack
  26. Mack CGI Coset SCGI Camping LSWS Halcyon Mongrel Maveric WEBrick Sinatra FastCGI Vintage Fuzed Ramaze Thin Waves Ebb Merb
  27. mod_rack
  28. Rack::Lint Rack::URLMap Rack::Deflater Rack::CommonLogger middlewares Rack::ShowExceptions Rack::Reloader Rack::Static Rack::Cache
  29. Rack::Request utility Rack::Response
  30. Other Layers
  31. Sequel ActiveRecord persistence layer DataMapper Og
  32. ERB Liquid Amrita2 templating layer Erubis Markaby HAML
  33. Start at the End
  34. Vision
  35. REST and Resources
  36. fully-formed web applications built on resources
  37. fully-formed web applications built on resources
  38. Birth of Athena
  39. Dionysus ask me after if you don’t get the joke
  40. Project
  41. Extraction flickr: 95579828@N00
  42. 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} } }
  43. Results
  44. 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;
  45. 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
  46. 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
  47. 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
  48. 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
  49. 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 # ...
  50. Lessons About the Web
  51. HTTP status codes http://thoughtpad.net/alan-dean/http-headers-status.gif
  52. 207 Multi-Status
  53. 207 sucks Multi-Status
  54. rack::cache http://tomayko.com/src/rack-cache/
  55. Lessons About Ruby
  56. 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
  57. 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
  58. Tangled classes flickr: randomurl
  59. 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
  60. this session has been a LIE
  61. More to learn flickr: glynnis
  62. ... always flickr: mointrigue
  63. http://github.com/bscofield/athena (under construction)
  64. 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

×