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

Like this? Share it with your network

Share

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

  • 6,567 views
Uploaded on

Slides from session at Rubyconf 2008

Slides from session at Rubyconf 2008

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
6,567
On Slideshare
6,460
From Embeds
107
Number of Embeds
3

Actions

Shares
Downloads
106
Comments
0
Likes
8

Embeds 107

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

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 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