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".
6. writing a
fucking
framework
It was the most appropriate name
for a simple fuckin framework!
Wednesday, August 12, 2009
7. 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
8. 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
9. A need to develop
similar types of web
applications...
Yep... pretty damn
simple.
Wednesday, August 12, 2009
10. ...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
11. An exercise in You’re tired or think MVC
just doesn’t cut it (it
challenging
doesn’t)
and think you can come
up with something that
solves the problem
better...
expectations
Wednesday, August 12, 2009
12. Or you’re just itching a
mental scratch....
An exercise mental masturbation
Wednesday, August 12, 2009
13. 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
14. We need to find out what
we’re abstracting
What are we abstracting?
Wednesday, August 12, 2009
15. 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
16. Non-application logic
Configuration
Templating
ORMs
assembling the working
pieces of a stack
request routing et al
Wednesday, August 12, 2009
18. 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
19. a fuckin resource:
responds with:
all
read one
accepts:
create new
update one
delete one
Wednesday, August 12, 2009
21. 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
22. So what are we aiming for
with the Fuck framework?
An overly simple
abstraction of a RESTful
CRUD interface
Wednesday, August 12, 2009
23. 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
24. Including:
Threats
Recipes Ask for some simple
objects we could easily
Posts
ser ve purely RESTfully
like WeeDB which stores
generic key-value pairs
Values (serialized JSON data)
People
Nouns
Verbs
Cities (called Atlanta)
Wednesday, August 12, 2009
25. 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
26. 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
27. Specialty Domain
You’re solving a special
problem that other
frameworks don’t, right?
Wednesday, August 12, 2009
28. Unfamiliar Expectations
Developers don’t know
what to expect from
your framework...
What content type does
it default to?
Does it support forms?
Wednesday, August 12, 2009
33. Why doesn’t work
anything
for our needs?
Wednesday, August 12, 2009
34. How?
So how do we do it?
This is after all the crux
of the talk
Wednesday, August 12, 2009
35. 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
36. Simplest possible
implementation of the
solution but no simpler
Wednesday, August 12, 2009
37. For Example:
Only resources These are essentially
what Fuck is designed
around
Only RESTfully
IO includes templating
and DB persistence
BUT specifically requests
and responses
No explicit routes
No explicit configuration
No assumptions about IO
Wednesday, August 12, 2009
38. 100 LOC
Keep 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
39. How do we reuse existing
tools? This is a talk about
building a framework on
Rack, after all
Wednesday, August 12, 2009
40. Rack abstracts server
integration into a
simple, consistent
interface This is why Rack was
written initially
Wednesday, August 12, 2009
41. 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
42. Rack has many great
libraries, middleware,
and guides freely
available already Rack::Cache
Rack::JSONP
Rack::Bug
CloudKit
Get audience to name some
middleware they’ve found
useful or interesting
Wednesday, August 12, 2009
43. Rack is constantly
improving and many
popular frameworks
use it internallyRails 2.3+ is built on top
of Rack and supports
Metal endpoints (Rack
apps)
Sinatra, Ramaze,
Halcyon, Mack, etc
Wednesday, August 12, 2009
44. How do we write it?
So how do we even begin
to know how to write a
framework?
Wednesday, August 12, 2009
45. YOU’RE DOING IT
WRONG
WRONG
WRONG
WRONG
WRONG
Stop, you retard...
WRONG
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
47. We write our tests
first. TATFT
Wednesday, August 12, 2009
48. It allows us to exercise
the API before Our opportunity to
design the API
implementing it...
Wednesday, August 12, 2009
49. ...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
50. 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
51. But lets wait until the
end of the talk to see...
...
Wednesday, August 12, 2009
52. How do we get it to run?
No need for JBoss
ser vers and all that
bullshit...
Essentially calls Mongrel
and then loads the
environment
Wednesday, August 12, 2009
54. config.ru
require 'fuck'
require 'posts'
run Fuck Fuck the class responds to the class
method #call to handle requests (it
figures out what resource to defer to)
Wednesday, August 12, 2009
55. How do we release it?
Wednesday, August 12, 2009
56. RubyGems Talk about the Gem spec
and simple Rakefile to make
this simple
Rip
Mention the GitHub
restriction
Rip, developed by Chris
Wanstrath of GitHub,
looks very promising;
specifically the branching
and sharing
Wednesday, August 12, 2009
60. class Posts < Fuck::Resource Simplistic CRUD with a
nonsensical
def all implementation
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}"
end
end
Wednesday, August 12, 2009
61. Dependencies for testing
require 'rubygems'
require 'fuck' # set load path first
require 'rack/mock'
require 'stringio'
require 'test/spec' # or whatever
Wednesday, August 12, 2009
62. A mock request is
essentially the
environment hash of the
request (PATH_INFO et al)
env = Rack::MockRequest.env_for(
"/posts",
"REQUEST_METHOD" => "GET")
This sets off the resource
Fuck.call(env) routing
Response is minimal
#=> [status, headers, body] required response for
Rack
Wednesday, August 12, 2009
63. Slightly specific to
test/spec
wrote this a few months
ago and would probably
pick context or shoulda
or rspec now...
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
Didn’t mention this but
wrote a helper method
for sending a mock
request for all of the
HTTP verbs
Wednesday, August 12, 2009
64. Let’s get to the actual
framework already!
Framework
But we’ll add tests for
each feature we start
to implement
Wednesday, August 12, 2009
66. find_handler finds the
resource class (Posts)
require 'rack' yay for minimal dependencies that will handle the
request
class Fuck stripped down but we
definitely respond with
class << self proper status codes and
PATH_INFO = %r{/?(w+)(/(w+))?} # matches /posts and /posts/1
log to rack.errors
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 Would probably just
require now instead of
end autoload because thread-
end safety concerns
autoload :Resource, "fuck/resource"
end
Wednesday, August 12, 2009
67. The actual request
handlers;
essentially a resource
controller, for instance
Resources
Wednesday, August 12, 2009
68. 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
find_method figures out what request
method is being used and whether an
def find_method(request_method) ID was set or not in order to determine
# determine the type of request (GET, PUT, POST, to call the :all, :create, :read, :update,
DELETE, etc)
or :delete methods...
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
end
end
Wednesday, August 12, 2009