Building A Framework On Rack

Preview:

DESCRIPTION

Describes a high-level evaluation of why one would end up actually implementing a framework, how one should approach it, why one would do it, and then actually shows some examples of writing a Rack-based web application development framework called "Fuck".

Citation preview

Building aFramework

on RackWednesday, August 12, 2009

Wednesday, August 12, 2009

FornicationYou’ve probably heard about my fornication-themed framework called...

Wednesday, August 12, 2009

Wednesday, August 12, 2009

Fuck

Wednesday, August 12, 2009

writing afucking

frameworkIt was the most appropriate name for a simple fuckin framework!

Wednesday, August 12, 2009

What?I know what I want, but let’s establish what we’re after...

We need to ask some important questions before we get lost in glee and fascination

We’ll start with...

Wednesday, August 12, 2009

What is the problem we’re solving?

Clearly there’s a reason we need this framework... we’re solving a problem, but what is the problem?

Wednesday, August 12, 2009

A need to develop similar types of web applications...

Yep... pretty damn simple.

Wednesday, August 12, 2009

...that another framework doesn’t already do better

One option, though a plugin could probably be better used, or simply using Sinatra or Rack directly

Wednesday, August 12, 2009

An exercise in challenging expectations

You’re tired or think MVC just doesn’t cut it (it doesn’t)

and think you can come up with something that solves the problem better...

Wednesday, August 12, 2009

An exerciseOr you’re just itching a mental scratch....

mental masturbation

Wednesday, August 12, 2009

Abstracting out a solution’s pattern

Most successful frameworks

abstract a working solution

to a generic framework

because their solution solved both a specific and a general problem

Wednesday, August 12, 2009

What are we abstracting?

We need to find out what we’re abstracting

Wednesday, August 12, 2009

The hard parts in our solution’s pattern

like the distributed stuff in Mack

or handling uploads, testing, and RESTful routes and routes in general in Rails

Wednesday, August 12, 2009

Non-application logicConfigurationTemplatingORMs

assembling the working pieces of a stack

request routing et al

Wednesday, August 12, 2009

Reusable components, tools, and helpers

Wednesday, August 12, 2009

What should the API look like? if we could use the API

before writing it or testing it, what would it look like?

pseudo-code

Wednesday, August 12, 2009

a fuckin resource: responds with: all read one accepts: create new update one delete one

Wednesday, August 12, 2009

CRUD

Wednesday, August 12, 2009

What applications will it have? How will we use our

framework?

For example, the application the framework will be abstracted out of?

Aurora, JSON-based auth server

Wednesday, August 12, 2009

An overly simple abstraction of a RESTful CRUD interface

So what are we aiming for with the Fuck framework?

Wednesday, August 12, 2009

Any object we want to manage RESTfully

CloudKit does something like this, but it’s all automatic

We want to define the data IO (essentially)

Wednesday, August 12, 2009

Including:

ThreatsRecipesPostsValuesPeopleNounsVerbsCities (called Atlanta)

Ask for some simple objects we could easily serve purely RESTfully

like WeeDB which stores generic key-value pairs (serialized JSON data)

Wednesday, August 12, 2009

How about a Key-Value store web interface?

If we have time we can write an application that stores to Redis via a Fuck Resource

doubtful, though

Wednesday, August 12, 2009

What problems have we created?

There always comes some responsibility with writing and sharing a framework

ASSUMING we’re sharing it as OSS, right? RIGHT?!

Wednesday, August 12, 2009

Specialty DomainYou’re solving a special problem that other frameworks don’t, right?

Wednesday, August 12, 2009

Unfamiliar ExpectationsDevelopers don’t know what to expect from your framework...

What content type does it default to?

Does it support forms?

Wednesday, August 12, 2009

Maintenancecommunity & emailbug fixes & issue trackingheckling (j/k)speaking opportunitiesfeature improvementtesting

Wednesday, August 12, 2009

Why?One last sanity check

Wednesday, August 12, 2009

Why doesn’t Rails work for our needs?

Wednesday, August 12, 2009

Why doesn’t Sinatra work for our needs?

Wednesday, August 12, 2009

Why doesn’t work for our needs?

anything

Wednesday, August 12, 2009

How?So how do we do it?

This is after all the crux of the talk

Wednesday, August 12, 2009

How simple can we make it?

We still need to ask the right questions...

So how can we make the framework as simple as possible?

And why?

Wednesday, August 12, 2009

Simplest possible implementation of the solution but no simpler

Wednesday, August 12, 2009

For Example:

Only resourcesOnly RESTfullyNo explicit routesNo explicit configurationNo assumptions about IO

These are essentially what Fuck is designed around

IO includes templating and DB persistence

BUT specifically requests and responses

Wednesday, August 12, 2009

100 LOCKeep the code as small, lean, and clean as possible...

100 LOC is a little arbitrary and meaningless, but it’s small...

Can we meaningfully create a framework in 100 LOC? Yep.

Wednesday, August 12, 2009

How do we reuse existing tools? This is a talk about

building a framework on Rack, after all

Wednesday, August 12, 2009

Rack abstracts server integration into a simple, consistent interface This is why Rack was

written initially

Wednesday, August 12, 2009

Rack provides a simple DSL for injecting logic in the call cycle through middleware

Why we don’t worry about auth, caching, et al

Wednesday, August 12, 2009

Rack has many great libraries, middleware, and guides freely available already

Rack::CacheRack::JSONPRack::BugCloudKit

Get audience to name some middleware they’ve found useful or interesting

Wednesday, August 12, 2009

Rack is constantly improving and many popular frameworks use it internally

Rails 2.3+ is built on top of Rack and supports Metal endpoints (Rack apps)

Sinatra, Ramaze, Halcyon, Mack, etc

Wednesday, August 12, 2009

How do we write it?So how do we even begin to know how to write a framework?

Wednesday, August 12, 2009

WRONGYOU’RE DOING IT

WRONGWRONGWRONGWRONGWRONGWRONGWRONG

Stop, you retard...

you’re doing it wrong!

You write the tests first!

This will answer so many questions early on about how to write it!

Wednesday, August 12, 2009

How do we test it?

Wednesday, August 12, 2009

We write our tests first. TATFT

Wednesday, August 12, 2009

It allows us to exercise the API before implementing it...

Our opportunity to design the API

Wednesday, August 12, 2009

...and ensures our expectations are met as we implement. Failing tests are always a

good way to bring attention to problematic or incomplete portions of the library/framework

Wednesday, August 12, 2009

How do we write it?Now we can think about writing it once we’ve established or at least started our tests...

TDD/BDD works great!

Wednesday, August 12, 2009

...But lets wait until the end of the talk to see...

Wednesday, August 12, 2009

How do we get it to run?

No need for JBoss servers and all that bullshit...

Essentially calls Mongrel and then loads the environment

Wednesday, August 12, 2009

rackup -p 3000 config.ru

Wednesday, August 12, 2009

require 'fuck'require 'posts'

run Fuck

config.ru

Fuck the class responds to the class method #call to handle requests (it figures out what resource to defer to)

Wednesday, August 12, 2009

How do we release it?

Wednesday, August 12, 2009

RubyGemsRip

Talk about the Gem spec and simple Rakefile to make this simple

Mention the GitHub restriction

Rip, developed by Chris Wanstrath of GitHub, looks very promising ; specifically the branching and sharing

Wednesday, August 12, 2009

Wednesday, August 12, 2009

Implementingthe Framework

Wednesday, August 12, 2009

Tests TATFT

Wednesday, August 12, 2009

class Posts < Fuck::Resource def all respond "Fuckin A!" end def create respond params["a"] end def read(id) respond "You asked for #{id}?" end def update(id) respond params["a"] end def delete(id) respond "You asked me to delete #{id}" endend

Simplistic CRUD with a nonsensical implementation

Wednesday, August 12, 2009

require 'rubygems'

require 'fuck' # set load path first

require 'rack/mock'require 'stringio'

require 'test/spec' # or whatever

Dependencies for testing

Wednesday, August 12, 2009

env = Rack::MockRequest.env_for( "/posts", "REQUEST_METHOD" => "GET")

Fuck.call(env)#=> [status, headers, body]

A mock request is essentially the environment hash of the request (PATH_INFO et al)

This sets off the resource routing

Response is minimal required response for Rack

Wednesday, August 12, 2009

context "Fuck can route" do specify "to list all of the resources" do status, headers, body = get("/posts") status.should == 200 body.should =~ /Fuckin A!/ end end

Slightly specific totest/spec

wrote this a few months ago and would probably pick context or shoulda or rspec now...

Didn’t mention this but wrote a helper method for sending a mock request for all of the HTTP verbs

Wednesday, August 12, 2009

FrameworkLet’s get to the actual framework already!

But we’ll add tests for each feature we start to implement

Wednesday, August 12, 2009

BaseThe initial routing handler...

Wednesday, August 12, 2009

require 'rack'

class Fuck class << self PATH_INFO = %r{/?(\w+)(/(\w+))?} # matches /posts and /posts/1 def call(env) if handler = find_handler(env["PATH_INFO"], env["QUERY_STRING"]) handler.call(env) # dispatch the request else # not found end rescue Exception => e # internal server error end def find_handler(path_info, query_string) # determine the resource from the path info # /posts => Posts end end autoload :Resource, "fuck/resource"end

find_handler finds the resource class (Posts) that will handle the request

stripped down but we definitely respond with proper status codes and log to rack.errors

Would probably just require now instead of autoload because thread-safety concerns

yay for minimal dependencies

Wednesday, August 12, 2009

Resources

The actual request handlers;

essentially a resource controller, for instance

Wednesday, August 12, 2009

class Fuck class Resource DEFAULT_HEADERS = {"Content-Type" => "text/html"} attr_accessor :params def initialize(id, params); @id, @params = id, params; end def call(env) request_method = find_method(env["REQUEST_METHOD"]) || :not_implemented return not_implemented unless self.respond_to?(request_method) send(request_method, *[@id].compact) or not_found rescue Exception => e # internal server error end def find_method(request_method) # determine the type of request (GET, PUT, POST, DELETE, etc) end def respond(body = "OK", options = {}, headers = {}) # prepares a Rack-compatible response: [status, headers, body] end def not_found; respond("Not Found", :status => 404); end def not_implemented; respond("Not Implemented", :status => 501); end endend

find_method figures out what request method is being used and whether an ID was set or not in order to determine to call the :all, :create, :read, :update, or :delete methods...

Wednesday, August 12, 2009

Wednesday, August 12, 2009

Wednesday, August 12, 2009

Wednesday, August 12, 2009

Recommended