00README
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This is the talk that I just gave at the LA Ruby Meetup, minutes ago. | |
It was on the Watts not-framework. Watts can be found at | |
http://github.com/pete/watts , which has documentation and examples. | |
The "slide deck" is the text file below, which was run through a small | |
Ruby script that recognized "-- $header" as delimiters. | |
Enjoy. | |
Update 2013-01-28: There was a bug in Watts, and in this presentation, | |
specifically that unknown methods ought to be responded to with a "501 Not | |
Implemented" rather than a "400 Bad Request". I've left the talk as-is (with | |
the exception that I have put [[2013 edit! ...]] in a couple of places). | |
Watts was updated two years ago to remedy the bug: | |
https://github.com/pete/watts/commit/01af6ef1c7fb720024c4b032685261c0c6bba194 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#! /usr/bin/env ruby | |
# This is an ugly hacky script that I wrote an hour before I had to use for a | |
# different presentation, and was more recently used to run the slideshow for | |
# this LA Ruby Meetup talk. It is not very good Ruby; it's a script. It's | |
# probably buggy. But running it will show the slideshow from the plain-text | |
# 'talk' file. | |
require 'rubygems' | |
require 'colorize' | |
def cook! | |
system "stty sane" | |
end | |
def raw! | |
system "stty -icanon min 1" | |
end | |
def title str | |
" #{str} ".center((ENV['COLUMNS'] || 80).to_i, '='). | |
colorize(:color => :light_white, | |
:background => :blue, | |
:mode => :bold) | |
end | |
def show_slide slide | |
#if ENV['COLUMNS'] && ENV['COLUMNS'] != '80' | |
#puts slide.first | |
#IO.popen("fmt -s -w#{ENV['COLUMNS'] || 80}", 'w') { |i| | |
#i.puts *slide[1..-1] | |
#} | |
#else | |
puts *slide | |
#end | |
end | |
slides = [] | |
File.readlines('talk').each { |line| | |
if /^--(.*)/.match(line) | |
slides << [title($1)] | |
else | |
slides.last << line | |
end | |
} | |
slides << [title("It's over!"), "Have fun.", "Any (more) questions?"] | |
trap('INT') { cook!; exit } | |
raw! | |
i = 0 | |
loop { | |
system "clear" | |
show_slide slides[i] | |
case $stdin.read(1) | |
when ' ', 'n', 'j', "\n" | |
i += 1 if i < slides.length - 1 | |
when 'b', 'p', 'k' | |
i -= 1 if i > 0 | |
system "clear" | |
when 'q' | |
cook!; exit | |
end | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- I'm Pete. | |
Hi. | |
-- This is a talk on Watts. | |
# http://github.com/pete/watts | |
class Hi < Watts::App | |
class Res < Watts::Resource | |
get { "Hello, World!\n" } | |
end | |
end | |
-- It's also about simplicity and abstractions. | |
"Abstraction focuses on reducing complexity, generalizing the concrete, and | |
abridge or summarize. Indirection is a necessary evil used to create pluggable | |
systems." | |
-- Zed "I am a Jerk on the Internet But Frequently Correct" Shaw | |
[It's a good read, overall: | |
http://www.zedshaw.com/essays/indirection_is_not_abstraction.html] | |
-- ...Finally, it's also about HTTP and REST. | |
The boring stuff. But I won't go too in depth. Promise. I'll keep the talk, | |
like Watts itself, simple. If you want the gory details, have a look at | |
http://www.ietf.org/rfc/rfc2616.txt . | |
-- I don't have a choice but to make it simple. | |
These "slides" are just sections of a specially delimited text file that a hacky | |
Ruby script is spitting out onto the screen. I don't know how to operate | |
Powerpoint or Keynote or what-have-you. I'm writing this in a text editor. | |
So the slides have to be text-only and 80x24. Simple. | |
[All the links (and probably the whole deck) will be put into a gist or | |
something. Don't worry if you miss anything.] | |
-- Watts: NOT ANOTHER WEB FRAMEWORK | |
Don't let this get out, but it's not really a web framework in the strictest | |
sense. It just makes a sincere and simple attempt to represent resources (in | |
the HTTP sense) in a Ruby-friendly way, to provide some pattern-matching for | |
paths to these resources, and to...nope, that's it. Rack handles HTTP requests | |
and responses, and Watts handles HTTP resources. No ORM, no ERB, nothing. If | |
you want that stuff, you can require ActiveRecord or Sequel or ERB. | |
-- What are we trying to pull with this HTTP protocol? | |
"HTTP allows basic hypermedia access to resources available from diverse | |
applications." -- RFC "Please Friggin' Read Me if You Write Webapps" 2616 | |
-- Why's HTTP good for this? | |
HTTP's inherent niceness comes from a few properties: | |
* It's simple. | |
* It has a central abstraction ("the resources") and defines some means | |
for interacting with it. | |
* It is easy to implement a trivial client or server (although not a | |
performant one). | |
-- Central abstraction? | |
HTTP is all about resources: | |
A [resource is a] network data object or service that can be identified | |
by a URI, as defined in section 3.2. Resources may be available in | |
multiple representations (e.g. multiple languages, data formats, size, | |
and resolutions) or vary in other ways. | |
-- RFC "Surprisingly Pretty Readable" 2616, Section 1.3 | |
It defines what they are, how to identify them (URIs) and how to interact with | |
them. | |
-- They had a reason to make HTTP. | |
-- It's good to comply with HTTP. | |
People who write clients, people who write tools based on the clients written by | |
the previously mentioned people, people who write servers, all of those people | |
are happier when the software they use and the servers and clients they interact | |
with act as expected. | |
-- Sinatra has problems: | |
Sinatra: | |
$ cat > sinatra-test.rb | |
require 'sinatra' | |
get '/' do | |
"Hello World!\n" | |
end | |
^D | |
$ ruby sinatra-test.rb | |
== Sinatra/1.2.0 has taken the stage on 4567 for [ LONG FRIGGIN LINE ] | |
>> Thin web server (v1.2.7 codename No Hup) | |
>> Maximum connections set to 1024 | |
>> Listening on 0.0.0.0:4567, CTRL+C to stop | |
-- Let's try that out! | |
$ curl http://localhost:4567/ | |
# Prints the greeeting! Sinatra's log: | |
# 127.0.0.1 - - [10/Mar/2011 17:26:04] "GET / HTTP/1.1" 200 13 0.0012 | |
$ curl -XOPTIONS http://localhost:4567/ | |
# Error? What's the log say? | |
127.0.0.1 - - [10/Mar/2011 17:26:13] "OPTIONS / HTTP/1.1" 404 413 0.0003 | |
404? That resource exists! | |
$ curl -v -XWTFMAN http://localhost:9292/ | |
127.0.0.1 - - [10/Mar/2011 18:00:30] "WTFMAN / HTTP/1.1" 404 412 0.0004 | |
"WTFMAN" isn't even an HTTP method! | |
-- Okay, what about Rails? | |
[Making a basic Rails 3.0.5 app with a resource at / omitted. SO MUCH OUTPUT.] | |
$ grep hello config/routes.rb | |
root :to => "hello#index" | |
$ rackup config.ru | |
[2011-03-10 17:53:41] INFO WEBrick 1.3.1 | |
[2011-03-10 17:53:41] INFO ruby 1.9.2 (2010-08-18) [linux] | |
[2011-03-10 17:53:41] INFO WEBrick::HTTPServer#start: pid=4645 port=9292 | |
-- Hate to break it to you: | |
$ curl http://localhost:9292/ | |
127.0.0.1 - - [10/Mar/2011 17:54:44] "GET / HTTP/1.1" 200 - 0.8944 | |
$ curl -v -XOPTIONS http://localhost:9292/ 2>&1 | grep -i 'allow:' | |
# Nope, nothing, sorry, it acts exactly like a GET. | |
127.0.0.1 - - [10/Mar/2011 17:54:52] "OPTIONS / HTTP/1.1" 200 - 0.0109 | |
$ curl -XWTFMAN http://localhost:9292/ | |
127.0.0.1 - - [10/Mar/2011 17:55:25] "WTFMAN / HTTP/1.1" 500 54812 0.0272 | |
# ...Yes, that's 54k of HTML it gives you. | |
-- RFC2616: | |
You're supposed to respond with an "Allow:" header that contains a list of | |
allowed methods. The appropriate response to an invalid HTTP method is "405 | |
Method Not Allowed" and an "Allow:" header. And you're supposed to spit back a | |
400 instead of a 404 or a 500 when the server can't even understand the request. | |
[[2013 edit! It turns out you're supposed to return a 501 if your server | |
does not understand an HTTP method. This was fixed after this presentation.]] | |
-- Watts: | |
$ cat > config.ru | |
require 'watts' | |
class Hi < Watts::App | |
class Res < Watts::Resource | |
get { "Hello, World!\n" } | |
end | |
resource '/', Res | |
end | |
run Hi.new | |
$ rackup config.ru -p 8080 | |
... | |
-- I think we're doing it right. | |
$ curl http://localhost:8080/ | |
$ curl -v -XOPTIONS http://localhost:8080/ 2>&1 | grep Allow: | |
< Allow: GET | |
$ curl -v -XWTFMAN http://localhost:8080/ 2>&1 | grep 400 | |
< HTTP/1.1 400 Bad Request | |
400 Bad Request. | |
[[2013 edit! As I said above, this now gives a "501 Not Implemented" error in | |
Watts, per the RFC.]] | |
Well, that's not so bad at all. | |
-- Camping: | |
No, I didn't try out Camping. Sorry. | |
-- I love _why as much as the next guy. | |
Gonna call this a failure on my part. | |
-- How does Sinatra deal with resources? | |
It doesn't. It uses method names that match HTTP method names, and you give it | |
paths. Sinatra doesn't deal in resources, and so it doesn't keep track of them. | |
get '/' do ... end | |
put '/' do ... end | |
# etc. | |
It looks like the HTTP request, but it doesn't model resources the way they | |
could be in a nice OO language like Ruby. By design, Sinatra is method- and | |
path-centric, ignoring the central abstraction of HTTP and the niceness of Ruby. | |
-- How does Rails deal with resources? | |
Rails is heavily MVC-based; this is one of the good points of the framework. | |
You define "routes", and they map to controllers, which are roughly like | |
resources. This is a pretty good approach! | |
The only problem is the minor fumbling around Controller methods versus HTTP | |
methods, and how routes deal with them. I don't see a reason that "405 Method | |
Not Allowed" couldn't be handled properly, so it's a bug in Rails rather than an | |
architectural flaw, and can probably be fixed. | |
-- How does Watts deal with resources? | |
Watts provides a Resource class; you inherit from it and it will (mostly) handle | |
HTTP for you: | |
class SomeResource < Watts::Resource | |
get { "Hey, man. text/plain your style?" } | |
put { request.body.do_something_with_it_i_guess } | |
end | |
-- How does Watts deal with paths? | |
Paths are the part of the URI that your app definitely needs to be concerned | |
with for routing requests to the right resources. Watts handles it by providing | |
some pattern-matching, defined by your app: | |
class Asdf < Watts::Resource; end # No methods! Ouch. | |
class Jkl | |
get { |arg, number| "Arg was #{arg}, number was #{number}" } | |
put { |arg, number| ... } | |
end | |
class SomeApp < Watts::App | |
resource('/', SomeResource) { | |
# /asdf exists, but you can't do anything with it. | |
resource('asdf', Asdf) | |
# You can GET or PUT /jkl/anything-here/325: | |
resource(['jkl', :another_arg, /^0-9+$/], Jkl) | |
} | |
end | |
-- How does Watts deal with...? | |
It doesn't. It deals in apps and resources, and that's it. It's simple, I told | |
you. | |
$ find lib -name '*.rb' | xargs wc -l | |
323 lib/watts.rb | |
26 lib/watts/monkey_patching.rb | |
349 total | |
Most of that's documentation. | |
-- Why make Watts? | |
I wanted to be able to do web development in a framework that I could fit in my | |
head, but that I wouldn't need to, because it stays out of the way. It's | |
powering my (rarely updated) blog. I wanted something that would run under | |
1.8.7 and 1.9.x. I wanted something that wouldn't take up ALL COMPUTING | |
RESOURCES for apps. | |
So, I made it because I wanted to use it. | |
-- Simplicity! | |
Simple like the pyramids. It made the code easy to write, it keeps it easy to | |
read and maintain, and keeps the bug count down. There's less to think about. | |
"There are two ways of constructing a software design. One way is to make it so | |
simple that there are obviously no deficiencies. And the other way is to make it | |
so complicated that there are no obvious deficiencies." | |
-- Sir Charles Antony Richard "I Wrote Quicksort, Pay Attention" Hoare | |
-- Bringing back simple! | |
Sinatra is cool, but at 5k lines, it's getting less and less minimalist. If you | |
can find the right abstraction and remember what your software is supposed to | |
do, then it's pretty easy to get lots accomplished with very little code. | |
I don't think anyone's ever called Rails' codebase simple. | |
-- I'm almost done. | |
I don't know if I'm running under or over time at this point. I have trouble | |
sticking to the script. | |
-- TODO: | |
* I'm sure Watts has bugs. Oren found one! It works for me, though! | |
* I don't know of any features it's missing that it should have, but they may | |
exist. |