View
1.921
Download
2
Category
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