webmachine 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +12 -10
- data/Guardfile +1 -1
- data/README.md +1 -1
- data/examples/application.rb +35 -0
- data/lib/webmachine.rb +4 -3
- data/lib/webmachine/adapter.rb +36 -0
- data/lib/webmachine/adapters/mongrel.rb +18 -12
- data/lib/webmachine/adapters/rack.rb +26 -7
- data/lib/webmachine/adapters/webrick.rb +20 -16
- data/lib/webmachine/application.rb +108 -0
- data/lib/webmachine/chunked_body.rb +2 -2
- data/lib/webmachine/configuration.rb +24 -14
- data/lib/webmachine/decision/conneg.rb +9 -10
- data/lib/webmachine/decision/flow.rb +25 -28
- data/lib/webmachine/decision/fsm.rb +21 -22
- data/lib/webmachine/decision/helpers.rb +3 -3
- data/lib/webmachine/dispatcher.rb +18 -10
- data/lib/webmachine/dispatcher/route.rb +54 -17
- data/lib/webmachine/errors.rb +1 -1
- data/lib/webmachine/headers.rb +2 -2
- data/lib/webmachine/media_type.rb +2 -2
- data/lib/webmachine/request.rb +78 -3
- data/lib/webmachine/resource.rb +3 -2
- data/lib/webmachine/resource/authentication.rb +4 -3
- data/lib/webmachine/resource/callbacks.rb +4 -3
- data/lib/webmachine/resource/encodings.rb +4 -3
- data/lib/webmachine/response.rb +3 -2
- data/lib/webmachine/streaming.rb +4 -4
- data/lib/webmachine/version.rb +1 -1
- data/spec/webmachine/adapter_spec.rb +40 -0
- data/spec/webmachine/adapters/mongrel_spec.rb +22 -0
- data/spec/webmachine/adapters/rack_spec.rb +34 -8
- data/spec/webmachine/adapters/webrick_spec.rb +18 -0
- data/spec/webmachine/application_spec.rb +73 -0
- data/spec/webmachine/dispatcher/route_spec.rb +59 -2
- data/spec/webmachine/dispatcher_spec.rb +17 -5
- data/spec/webmachine/request_spec.rb +158 -1
- data/webmachine.gemspec +6 -27
- metadata +304 -112
- data/spec/tests.org +0 -80
data/Gemfile
CHANGED
@@ -6,20 +6,22 @@ gemspec
|
|
6
6
|
|
7
7
|
gem 'bundler'
|
8
8
|
|
9
|
-
|
9
|
+
group :guard do
|
10
10
|
gem 'guard-rspec'
|
11
|
+
case RbConfig::CONFIG['host_os']
|
12
|
+
when /darwin/
|
13
|
+
gem 'rb-fsevent'
|
14
|
+
# gem 'growl_notify'
|
15
|
+
gem 'growl'
|
16
|
+
when /linux/
|
17
|
+
gem 'rb-inotify'
|
18
|
+
gem 'libnotify'
|
19
|
+
end
|
20
|
+
end
|
11
21
|
|
22
|
+
group :docs do
|
12
23
|
platform :mri do
|
13
24
|
gem 'redcarpet'
|
14
|
-
|
15
|
-
case RbConfig::CONFIG['host_os']
|
16
|
-
when /darwin/
|
17
|
-
gem 'rb-fsevent'
|
18
|
-
gem 'growl_notify'
|
19
|
-
when /linux/
|
20
|
-
gem 'rb-inotify'
|
21
|
-
gem 'libnotify'
|
22
|
-
end
|
23
25
|
end
|
24
26
|
end
|
25
27
|
|
data/Guardfile
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
gemset = ENV['RVM_GEMSET'] || 'webmachine'
|
2
2
|
gemset = "@#{gemset}" unless gemset.to_s == ''
|
3
3
|
|
4
|
-
rvms = %W[ 1.8.7 1.9.2 jruby rbx ].map {|v| "#{v}#{gemset}" }
|
4
|
+
rvms = %W[ 1.8.7 1.9.2 1.9.3 jruby rbx ].map {|v| "#{v}#{gemset}" }
|
5
5
|
|
6
6
|
guard 'rspec', :cli => "--color --profile", :growl => true, :rvm => rvms do
|
7
7
|
watch(%r{^lib/webmachine/locale/.+$}) { "spec" }
|
data/README.md
CHANGED
@@ -17,7 +17,7 @@ Webmachine has a Rack adapter -- thanks to Jamis Buck -- but when
|
|
17
17
|
using it, we recommend you ensure that NO middleware is used. The
|
18
18
|
behaviors that are encapsulated in Webmachine could be broken by
|
19
19
|
middlewares that sit above it, and there is no way to detect them at
|
20
|
-
runtime. _Caveat
|
20
|
+
runtime. _Caveat implementor_. That said, Webmachine should behave properly
|
21
21
|
when given a clear stack.
|
22
22
|
|
23
23
|
## Getting Started
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'webmachine'
|
2
|
+
|
3
|
+
class RouteDebugResource < Webmachine::Resource
|
4
|
+
def to_html
|
5
|
+
<<-HTML
|
6
|
+
<html>
|
7
|
+
<head><title>Test from Webmachine</title></head>
|
8
|
+
<body>
|
9
|
+
<h5>request.disp_path</h5>
|
10
|
+
<pre>#{request.disp_path}</pre>
|
11
|
+
<h5>request.path_info</h5>
|
12
|
+
<pre>#{request.path_info}</pre>
|
13
|
+
<h5>request.path_tokens</h5>
|
14
|
+
<pre>#{request.path_tokens}</pre>
|
15
|
+
</body>
|
16
|
+
</html>
|
17
|
+
HTML
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
MyApp = Webmachine::Application.new do |app|
|
22
|
+
# Configure your app like this:
|
23
|
+
app.configure do |config|
|
24
|
+
config.port = 8888
|
25
|
+
config.adapter = :WEBrick
|
26
|
+
end
|
27
|
+
# And add routes like this:
|
28
|
+
app.add_route ['fizz', :buzz, '*'], RouteDebugResource
|
29
|
+
# OR add routes this way:
|
30
|
+
app.routes do
|
31
|
+
add [:test, :foo, '*'], RouteDebugResource
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
MyApp.run
|
data/lib/webmachine.rb
CHANGED
@@ -5,17 +5,18 @@ require 'webmachine/response'
|
|
5
5
|
require 'webmachine/errors'
|
6
6
|
require 'webmachine/decision'
|
7
7
|
require 'webmachine/streaming'
|
8
|
+
require 'webmachine/adapter'
|
8
9
|
require 'webmachine/adapters'
|
9
10
|
require 'webmachine/dispatcher'
|
11
|
+
require 'webmachine/application'
|
10
12
|
require 'webmachine/resource'
|
11
13
|
require 'webmachine/version'
|
12
14
|
|
13
15
|
# Webmachine is a toolkit for making well-behaved HTTP applications.
|
14
16
|
# It is based on the Erlang library of the same name.
|
15
17
|
module Webmachine
|
16
|
-
# Starts Webmachine serving requests
|
18
|
+
# Starts Webmachine's default global Application serving requests
|
17
19
|
def self.run
|
18
|
-
|
19
|
-
Adapters.const_get(configuration.adapter).run
|
20
|
+
application.run
|
20
21
|
end
|
21
22
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module Webmachine
|
2
|
+
|
3
|
+
# The abstract class for definining a Webmachine adapter.
|
4
|
+
#
|
5
|
+
# @abstract Subclass and override {#run} to implement a custom adapter.
|
6
|
+
class Adapter
|
7
|
+
|
8
|
+
# @return [Webmachine::Configuration] the application's configuration.
|
9
|
+
attr_reader :configuration
|
10
|
+
|
11
|
+
# @return [Webmachine::Dispatcher] the application's dispatcher.
|
12
|
+
attr_reader :dispatcher
|
13
|
+
|
14
|
+
# @param [Webmachine::Configuration] configuration the application's
|
15
|
+
# configuration.
|
16
|
+
# @param [Webmachine::Dispatcher] dispatcher the application's dispatcher.
|
17
|
+
def initialize(configuration, dispatcher)
|
18
|
+
@configuration = configuration
|
19
|
+
@dispatcher = dispatcher
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new adapter and run it.
|
23
|
+
def self.run(configuration, dispatcher)
|
24
|
+
new(configuration, dispatcher).run
|
25
|
+
end
|
26
|
+
|
27
|
+
# Start the adapter.
|
28
|
+
#
|
29
|
+
# @abstract Subclass and override {#run} to implement a custom adapter.
|
30
|
+
# @raise [NotImplementedError]
|
31
|
+
def run
|
32
|
+
raise NotImplementedError
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -9,17 +9,17 @@ require 'webmachine/chunked_body'
|
|
9
9
|
module Webmachine
|
10
10
|
module Adapters
|
11
11
|
# Connects Webmachine to Mongrel.
|
12
|
-
|
12
|
+
class Mongrel < Adapter
|
13
|
+
|
13
14
|
# Starts the Mongrel adapter
|
14
|
-
def
|
15
|
-
c = Webmachine.configuration
|
15
|
+
def run
|
16
16
|
options = {
|
17
|
-
:port =>
|
18
|
-
:host =>
|
19
|
-
}.merge(
|
17
|
+
:port => configuration.port,
|
18
|
+
:host => configuration.ip
|
19
|
+
}.merge(configuration.adapter_options)
|
20
20
|
config = ::Mongrel::Configurator.new(options) do
|
21
21
|
listener do
|
22
|
-
uri '/', :handler => Webmachine::Adapters::Mongrel::Handler.new
|
22
|
+
uri '/', :handler => Webmachine::Adapters::Mongrel::Handler.new(dispatcher)
|
23
23
|
end
|
24
24
|
trap("INT") { stop }
|
25
25
|
run
|
@@ -29,6 +29,11 @@ module Webmachine
|
|
29
29
|
|
30
30
|
# A Mongrel handler for Webmachine
|
31
31
|
class Handler < ::Mongrel::HttpHandler
|
32
|
+
def initialize(dispatcher)
|
33
|
+
@dispatcher = dispatcher
|
34
|
+
super
|
35
|
+
end
|
36
|
+
|
32
37
|
# Processes an individual request from Mongrel through Webmachine.
|
33
38
|
def process(wreq, wres)
|
34
39
|
header = Webmachine::Headers.from_cgi(wreq.params)
|
@@ -39,7 +44,7 @@ module Webmachine
|
|
39
44
|
wreq.body || StringIO.new(''))
|
40
45
|
|
41
46
|
response = Webmachine::Response.new
|
42
|
-
|
47
|
+
@dispatcher.dispatch(request, response)
|
43
48
|
|
44
49
|
begin
|
45
50
|
wres.status = response.code.to_i
|
@@ -74,7 +79,8 @@ module Webmachine
|
|
74
79
|
response.body.close if response.body.respond_to? :close
|
75
80
|
end
|
76
81
|
end
|
77
|
-
end
|
78
|
-
|
79
|
-
|
80
|
-
end
|
82
|
+
end # class Handler
|
83
|
+
|
84
|
+
end # module Mongrel
|
85
|
+
end # module Adapters
|
86
|
+
end # module Webmachine
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require 'rack
|
1
|
+
require 'rack'
|
2
2
|
require 'webmachine/version'
|
3
3
|
require 'webmachine/headers'
|
4
4
|
require 'webmachine/request'
|
@@ -19,11 +19,29 @@ module Webmachine
|
|
19
19
|
# # put your own Webmachine resources in another file:
|
20
20
|
# require 'my/resources'
|
21
21
|
#
|
22
|
-
# run
|
22
|
+
# run MyApplication.adapter
|
23
23
|
#
|
24
24
|
# Servers like pow and unicorn will read config.ru by default and it should
|
25
25
|
# all "just work".
|
26
|
-
|
26
|
+
#
|
27
|
+
# And for development or testing your application can be run with Rack's
|
28
|
+
# builtin Server identically to the Mongrel and WEBrick adapters with:
|
29
|
+
#
|
30
|
+
# MyApplication.run
|
31
|
+
#
|
32
|
+
class Rack < Adapter
|
33
|
+
|
34
|
+
# Start the Rack adapter
|
35
|
+
def run
|
36
|
+
options = {
|
37
|
+
:app => self,
|
38
|
+
:Port => configuration.port,
|
39
|
+
:Host => configuration.ip
|
40
|
+
}.merge(configuration.adapter_options)
|
41
|
+
|
42
|
+
::Rack::Server.start(options)
|
43
|
+
end
|
44
|
+
|
27
45
|
# Handles a Rack-based request.
|
28
46
|
# @param [Hash] env the Rack environment
|
29
47
|
def call(env)
|
@@ -36,7 +54,7 @@ module Webmachine
|
|
36
54
|
rack_req.body)
|
37
55
|
|
38
56
|
response = Webmachine::Response.new
|
39
|
-
|
57
|
+
@dispatcher.dispatch(request, response)
|
40
58
|
|
41
59
|
response.headers['Server'] = [Webmachine::SERVER_STRING, "Rack/#{::Rack.version}"].join(" ")
|
42
60
|
|
@@ -45,6 +63,7 @@ module Webmachine
|
|
45
63
|
|
46
64
|
[response.code.to_i, response.headers, body || []]
|
47
65
|
end
|
48
|
-
end
|
49
|
-
|
50
|
-
end
|
66
|
+
end # class Rack
|
67
|
+
|
68
|
+
end # module Adapters
|
69
|
+
end # module Webmachine
|
@@ -8,21 +8,26 @@ require 'webmachine/dispatcher'
|
|
8
8
|
module Webmachine
|
9
9
|
module Adapters
|
10
10
|
# Connects Webmachine to WEBrick.
|
11
|
-
|
11
|
+
class WEBrick < Adapter
|
12
|
+
|
12
13
|
# Starts the WEBrick adapter
|
13
|
-
def
|
14
|
-
c = Webmachine.configuration
|
14
|
+
def run
|
15
15
|
options = {
|
16
|
-
:Port =>
|
17
|
-
:BindAddress =>
|
18
|
-
}.merge(
|
19
|
-
server = Webmachine::Adapters::WEBrick::Server.new options
|
16
|
+
:Port => configuration.port,
|
17
|
+
:BindAddress => configuration.ip
|
18
|
+
}.merge(configuration.adapter_options)
|
19
|
+
server = Webmachine::Adapters::WEBrick::Server.new(dispatcher, options)
|
20
20
|
trap("INT"){ server.shutdown }
|
21
21
|
Thread.new { server.start }.join
|
22
22
|
end
|
23
23
|
|
24
24
|
# WEBRick::HTTPServer that is run by the WEBrick adapter.
|
25
25
|
class Server < ::WEBrick::HTTPServer
|
26
|
+
def initialize(dispatcher, options)
|
27
|
+
@dispatcher = dispatcher
|
28
|
+
super(options)
|
29
|
+
end
|
30
|
+
|
26
31
|
# Handles a request
|
27
32
|
def service(wreq, wres)
|
28
33
|
header = Webmachine::Headers.new
|
@@ -32,11 +37,9 @@ module Webmachine
|
|
32
37
|
header,
|
33
38
|
RequestBody.new(wreq))
|
34
39
|
response = Webmachine::Response.new
|
35
|
-
|
40
|
+
@dispatcher.dispatch(request, response)
|
36
41
|
wres.status = response.code.to_i
|
37
|
-
response.headers.each
|
38
|
-
wres[k] = v
|
39
|
-
end
|
42
|
+
response.headers.each { |k,v| wres[k] = v }
|
40
43
|
wres['Server'] = [Webmachine::SERVER_STRING, wres.config[:ServerSoftware]].join(" ")
|
41
44
|
case response.body
|
42
45
|
when String
|
@@ -51,7 +54,7 @@ module Webmachine
|
|
51
54
|
end
|
52
55
|
end
|
53
56
|
end
|
54
|
-
end
|
57
|
+
end # class Server
|
55
58
|
|
56
59
|
# Wraps the WEBrick request body so that it can be passed to
|
57
60
|
# {Request} while still lazily evaluating the body.
|
@@ -79,7 +82,8 @@ module Webmachine
|
|
79
82
|
@request.body {|chunk| @value << chunk; yield chunk }
|
80
83
|
end
|
81
84
|
end
|
82
|
-
end
|
83
|
-
|
84
|
-
|
85
|
-
end
|
85
|
+
end # class RequestBody
|
86
|
+
|
87
|
+
end # module WEBrick
|
88
|
+
end # module Adapters
|
89
|
+
end # module Webmachine
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'webmachine/configuration'
|
3
|
+
require 'webmachine/dispatcher'
|
4
|
+
|
5
|
+
module Webmachine
|
6
|
+
# How to get your Webmachine app running:
|
7
|
+
#
|
8
|
+
# MyApp = Webmachine::Application.new do |app|
|
9
|
+
# app.routes do
|
10
|
+
# add ['*'], AssetResource
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
# app.configure do |config|
|
14
|
+
# config.port = 8888
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# MyApp.run
|
19
|
+
#
|
20
|
+
class Application
|
21
|
+
extend Forwardable
|
22
|
+
|
23
|
+
def_delegators :dispatcher, :add_route
|
24
|
+
|
25
|
+
# @return [Configuration] the current configuration
|
26
|
+
attr_accessor :configuration
|
27
|
+
|
28
|
+
# @return [Dispatcher] the current dispatcher
|
29
|
+
attr_reader :dispatcher
|
30
|
+
|
31
|
+
# Create an Application instance
|
32
|
+
#
|
33
|
+
# An instance of application contains Adapter configuration and
|
34
|
+
# a Dispatcher instance which can be configured with Routes.
|
35
|
+
#
|
36
|
+
# @param [Webmachine::Configuration] configuration
|
37
|
+
# a Webmachine::Configuration
|
38
|
+
#
|
39
|
+
# @yield [app]
|
40
|
+
# a block in which to configure this Application
|
41
|
+
# @yieldparam [Application]
|
42
|
+
# the Application instance being initialized
|
43
|
+
def initialize(configuration = Configuration.default)
|
44
|
+
@configuration = configuration
|
45
|
+
@dispatcher = Dispatcher.new
|
46
|
+
|
47
|
+
yield self if block_given?
|
48
|
+
end
|
49
|
+
|
50
|
+
# Starts this Application serving requests
|
51
|
+
def run
|
52
|
+
adapter.run
|
53
|
+
end
|
54
|
+
|
55
|
+
# @return an instance of the configured web-server adapter
|
56
|
+
# @see Adapters
|
57
|
+
def adapter
|
58
|
+
@adapter ||= adapter_class.new(configuration, dispatcher)
|
59
|
+
end
|
60
|
+
|
61
|
+
# @return an instance of the configured web-server adapter
|
62
|
+
# @see Adapters
|
63
|
+
def adapter_class
|
64
|
+
Adapters.const_get(configuration.adapter)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Evaluates the passed block in the context of {Webmachine::Dispatcher}
|
68
|
+
# for use in adding a number of routes at once.
|
69
|
+
#
|
70
|
+
# @return [Application, Array<Route>]
|
71
|
+
# self if configuring, or an Array of Routes otherwise
|
72
|
+
#
|
73
|
+
# @see Webmachine::Dispatcher#add_route
|
74
|
+
def routes(&block)
|
75
|
+
if block_given?
|
76
|
+
dispatcher.instance_eval(&block)
|
77
|
+
self
|
78
|
+
else
|
79
|
+
dispatcher.routes
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Configure the web server adapter via the passed block
|
84
|
+
#
|
85
|
+
# Returns the receiver so you can chain it with Application#run
|
86
|
+
#
|
87
|
+
# @yield [config]
|
88
|
+
# a block in which to set configuration values
|
89
|
+
# @yieldparam [Configuration]
|
90
|
+
# config the Configuration instance
|
91
|
+
#
|
92
|
+
# @return [Application] self
|
93
|
+
def configure
|
94
|
+
yield configuration if block_given?
|
95
|
+
self
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [Configuration] the current configuration
|
99
|
+
def configuration
|
100
|
+
@configuration ||= Configuration.default
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Application] the default global Application
|
105
|
+
def self.application
|
106
|
+
@application ||= Application.new
|
107
|
+
end
|
108
|
+
end
|