Permalink
Browse files

Witness the power of this fully functional HTTP toolkit.

  • Loading branch information...
1 parent 5f4da1b commit 11051dbee728e6b268c59446c5f735420c5e6f76 @seancribbs seancribbs committed Aug 25, 2011
Showing with 140 additions and 69 deletions.
  1. +26 −7 README.md
  2. +19 −0 examples/webrick.rb
  3. +5 −0 lib/webmachine.rb
  4. +15 −0 lib/webmachine/adapters.rb
  5. +74 −0 lib/webmachine/adapters/webrick.rb
  6. +1 −1 lib/webmachine/version.rb
  7. +0 −61 lib/webmachine/webrick.rb
View
@@ -16,12 +16,14 @@ intentional! Rack obscures HTTP in a way that makes it hard for
Webmachine to do its job properly, and encourages people to add
middleware that might break Webmachine's behavior. Rack is also built
on the tradition of CGI, which is nice for backwards compatibility but
-also an antiquated paradigm and should be scuttled (IMHO).
+also an antiquated paradigm and should be scuttled (IMHO). _Rack may
+be supported in the future, but only as a shim to support other web
+application servers._
## Getting Started
-Webmachine.rb is not fully-functional yet, but constructing an
-application for it will follow this general outline:
+Webmachine is very young, but it's still easy to construct an
+application for it!
```ruby
require 'webmachine'
@@ -31,7 +33,7 @@ require 'my_resource'
# Point all URIs at the MyResource class
Webmachine::Dispatcher.add_route(['*'], MyResource)
-# Start the server, binds to the default interface/port
+# Start the server, binds to port 3000 using WEBrick
Webmachine.run
```
@@ -47,7 +49,24 @@ end
Run the first file and your application is up. That's all there is to
it! If you want to customize your resource more, look at the available
-callbacks in lib/webmachine/resource/callbacks.rb.
+callbacks in lib/webmachine/resource/callbacks.rb. For example, you
+might want to enable "gzip" compression on your resource, for which
+you can simply add an `encodings_provided` callback method:
+
+```ruby
+class MyResource < Webmachine::Resource
+ def encodings_provided
+ {"gzip" => :encode_gzip, "identity" => :encode_identity}
+ end
+
+ def to_html
+ "<html><body>Hello, world!</body></html>"
+ end
+end
+```
+
+There are many other HTTP features exposed to your resource through
+callbacks. Give them a try!
## Features
@@ -57,11 +76,11 @@ callbacks in lib/webmachine/resource/callbacks.rb.
integer response code. You generally only want to do this when new
information comes to light, requiring a modification of the response.
* Currently supports WEBrick. Other host servers are planned.
+* Streaming/chunked response bodies are permitted as Enumerables or Procs.
## Problems/TODOs
-* Streaming and range responses will be supported as soon as API is
- decided on.
+* Support streamed responses as Fibers.
* Configuration, command-line tools, and general polish.
* An effort has been made to make the code feel as Ruby-ish as
possible, but there is still work to do.
View
@@ -0,0 +1,19 @@
+require 'webmachine'
+
+class HelloResource < Webmachine::Resource
+ def last_modified
+ File.mtime(__FILE__)
+ end
+
+ def encodings_provided
+ { "gzip" => :encode_gzip, "identity" => :encode_identity }
+ end
+
+ def to_html
+ "<html><head><title>Hello from Webmachine</title></head><body>Hello, world!</body></html>"
+ end
+end
+
+Webmachine::Dispatcher.add_route([], HelloResource)
+
+Webmachine.run
View
@@ -4,11 +4,16 @@
require 'webmachine/errors'
require 'webmachine/decision'
require 'webmachine/streaming'
+require 'webmachine/adapters'
require 'webmachine/dispatcher'
require 'webmachine/resource'
require 'webmachine/version'
# Webmachine is a toolkit for making well-behaved HTTP applications.
# It is based on the Erlang library of the same name.
module Webmachine
+ # Starts Webmachine serving requests
+ def self.run
+ Adapters.const_get(adapter).run
+ end
end
@@ -0,0 +1,15 @@
+require 'webmachine/adapters/webrick'
+
+module Webmachine
+ # Contains classes and modules that connect Webmachine to Ruby
+ # application servers.
+ module Adapters
+ end
+
+ class << self
+ # @return [Symbol] the current webserver adapter
+ attr_accessor :adapter
+ end
+
+ self.adapter = :WEBrick
+end
@@ -0,0 +1,74 @@
+require 'webrick'
+require 'webmachine/version'
+require 'webmachine/headers'
+require 'webmachine/request'
+require 'webmachine/response'
+require 'webmachine/dispatcher'
+
+module Webmachine
+ module Adapters
+ # Connects Webmachine to WEBrick.
+ module WEBrick
+ # Starts the WEBrick adapter
+ def self.run
+ server = Webmachine::Adapters::WEBrick::Server.new :Port => 3000
+ trap("INT"){ server.shutdown }
+ Thread.new { server.start }.join
+ end
+
+ class Server < ::WEBrick::HTTPServer
+ def service(wreq, wres)
+ header = Webmachine::Headers.new
+ wreq.each {|k,v| header[k] = v }
+ request = Webmachine::Request.new(wreq.request_method,
+ wreq.request_uri,
+ header,
+ RequestBody.new(wreq))
+ response = Webmachine::Response.new
+ Webmachine::Dispatcher.dispatch(request, response)
+ wres.status = response.code.to_i
+ response.headers.each do |k,v|
+ wres[k] = v
+ end
+ wres['Server'] = [Webmachine::SERVER_STRING, wres.config[:ServerSoftware]].join(" ")
+ case response.body
+ when String
+ wres.body << response.body
+ when Enumerable
+ response.body.each {|part| wres.body << part }
+ when response.body.respond_to?(:call)
+ wres.body << response.body.call
+ end
+ end
+ end
+
+ # Wraps the WEBrick request body so that it can be passed to
+ # {Request} while still lazily evaluating the body.
+ class RequestBody
+ def initialize(request)
+ @request = request
+ end
+
+ # Converts the body to a String so you can work with the entire
+ # thing.
+ def to_s
+ @value ? @value.join : @request.body
+ end
+
+ # Iterates over the body in chunks. If the body has previously
+ # been read, this method can be called again and get the same
+ # sequence of chunks.
+ # @yield [chunk]
+ # @yieldparam [String] chunk a chunk of the request body
+ def each
+ if @value
+ @value.each {|chunk| yield chunk }
+ else
+ @value = []
+ @request.body {|chunk| @value << chunk; yield chunk }
+ end
+ end
+ end
+ end
+ end
+end
@@ -1,4 +1,4 @@
module Webmachine
VERSION = "0.1.0"
- SERVER_STRING = "Webmachine.rb/#{VERSION}"
+ SERVER_STRING = "Webmachine-Ruby/#{VERSION}"
end
@@ -1,61 +0,0 @@
-require 'webrick'
-require 'webmachine/version'
-require 'webmachine/request'
-require 'webmachine/response'
-require 'webmachine/dispatcher'
-
-module Webmachine
- module WEBrick
- # A simple Webmachine handler for WEBrick.
- class Handler < ::WEBrick::HTTPServlet::AbstractServlet
- def service(wreq, wres)
- header = {}
- wreq.each {|k,v| header[k] = v }
- request = Webmachine::Request.new(wreq.request_method,
- wreq.request_uri,
- header,
- RequestBody.new(wreq))
- response = Webmachine::Response.new
- Webmachine::Dispatcher.dispatch(request, response)
- wres.status = response.code.to_i
- response.headers.each do |k,v|
- wres[k] = v
- end
- # wres['Server'] = [Webmachine::SERVER_STRING, wres['Server']].join(" ")
- if response.body.respond_to?(:each)
- response.body.each {|part| wres.body << part }
- else
- wres.body << response.body
- end
- end
- end
-
- # Wraps the WEBrick request body so that it can be passed to
- # {Request} while still lazily evaluating the body.
- class RequestBody
- def initialize(request)
- @request = request
- end
-
- # Converts the body to a String so you can work with the entire
- # thing.
- def to_s
- @value ? @value.join : @request.body
- end
-
- # Iterates over the body in chunks. If the body has previously
- # been read, this method can be called again and get the same
- # sequence of chunks.
- # @yield [chunk]
- # @yieldparam [String] chunk a chunk of the request body
- def each
- if @value
- @value.each {|chunk| yield chunk }
- else
- @value = []
- @request.body {|chunk| @value << chunk; yield chunk }
- end
- end
- end
- end
-end

0 comments on commit 11051db

Please sign in to comment.