Skip to content

Commit

Permalink
Refactoring all the docs. Aiming to make README more concise, and pro…
Browse files Browse the repository at this point in the history
…vide links to further documentation.
  • Loading branch information
bethesque committed Sep 16, 2014
1 parent b051861 commit eb7a6d1
Show file tree
Hide file tree
Showing 6 changed files with 286 additions and 92 deletions.
111 changes: 19 additions & 92 deletions README.md
Expand Up @@ -4,9 +4,10 @@ webmachine-ruby is a port of
[Webmachine](https://github.com/basho/webmachine), which is written in
Erlang. The goal of both projects is to expose interesting parts of
the HTTP protocol to your application in a declarative way. This
means that you are less concerned with handling requests directly and
more with describing the behavior of the resources that make up your
application. Webmachine is not a web framework _per se_, but more of a
means that you are less concerned with the procedures involved in handling
requests directly and more with describing facts about the resources
that make up your application.
Webmachine is not a web framework _per se_, but more of a
toolkit for building HTTP-friendly applications. For example, it does
not provide a templating engine or a persistence layer; those choices
are up to you.
Expand All @@ -15,46 +16,27 @@ are up to you.

* Handles the hard parts of content negotiation, conditional
requests, and response codes for you.
* Most callbacks can interrupt the decision flow by returning an
integer response code. You generally only want to do this when new
information comes to light, requiring a modification of the response.
* Provides a base resource with points of extension to let you
describe what is relevant about your particular resource.
* Supports WEBrick, Reel, HTTPkit, and a Rack shim. Other host
servers are being investigated.
* Streaming/chunked response bodies are permitted as Enumerables,
Procs, or Fibers!
* Unlike the Erlang original, it does real Language negotiation.
* Includes the visual debugger so you can look through the decision
* Includes a visual debugger so you can look through the decision
graph to determine how your resources are behaving.

## Documentation & Finding Help

* [Example](/documentation/examples.md) resources showing how to implement each HTTP method.
* [Principles](/documentation/principles.md) to understand when implementing a resource.
* The best way to get an understanding of how Webmachine works under under the covers is to study the [Callbacks][callbacks] class, and then see how the [Flow][flow] class relates to the [Decision Flow Diagram][diagram].
* [API documentation](http://rubydoc.info/gems/webmachine/frames/file/README.md)
* [Mailing list](mailto:webmachine.rb@librelist.com)
* IRC channel #webmachine on freenode

## A Note about Rack

In order to be compatible with popular deployment stacks,
Webmachine has a [Rack](https://github.com/rack/rack) adapter (thanks to Jamis Buck).
**n.b.:** We recommend that NO middleware is used. The
behaviors that are encapsulated in Webmachine assume that no modifications
are done to requests or response outside of Webmachine.

## A Note about MRI 1.9

The [Reel][reel] and [HTTPkit][httpkit]
adapters might crash with a `SystemStackError` on MRI 1.9 due to its
limited fiber stack size. If your application is affected by this, the
only known solution is to switch to JRuby, Rubinius or MRI 2.0.


## Getting Started

[GiddyUp](https://github.com/basho/giddyup) is an actively
developed webmachine-ruby app that is in production. You
can look there for an example of how to write and structure a
webmachine-ruby app (although it is hacky in places).

Below we go through some examples of how to do basic things
with webmachine-ruby.

Expand All @@ -71,6 +53,8 @@ There are many other HTTP features exposed to a resource through
of the decision tree Webmachine implements, and the decision tree
is what makes Webmachine unique and powerful.

### A Simple Resource

```ruby
require 'webmachine'
class MyResource < Webmachine::Resource
Expand Down Expand Up @@ -101,78 +85,18 @@ Webmachine.application.run

There's a configurator that allows you to set what IP address and port
a web server should bind to as well as what web server should serve a
webmachine resource.

A call to `Webmachine::Application#configure` returns a `Webmachine::Application` instance,
so you could chain other method calls if you like. If you don't want to create your own separate
application object `Webmachine.application` will return a global one.
webmachine resource. Learn how to configure your application [here](/documentation/configurator.md)

```ruby
require 'webmachine'
require 'my_resource'

Webmachine.application.configure do |config|
config.ip = '127.0.0.1'
config.port = 3000
config.adapter = :WEBrick
end

# Start a web server to serve requests via localhost
Webmachine.application.run
```

Webmachine includes adapters for [WEBrick][webrick], [Reel][reel], and
[HTTPkit][httpkit]. Additionally, the [Rack][rack] adapter lets it
run on any webserver that provides a Rack interface. It also lets it run on
[Shotgun][shotgun] ([example][shotgun_example]).
### Adapters

[webrick]: http://rubydoc.info/stdlib/webrick
[reel]: https://github.com/celluloid/reel
[httpkit]: https://github.com/lgierth/httpkit
[rack]: https://github.com/rack/rack
[shotgun]: https://github.com/rtomayko/shotgun
[shotgun_example]: https://gist.github.com/4389220
Webmachine provides adapters for many popular webservers. Learn more [here](/documentation/adapters.md)

### Visual debugger

It can be hard to understand all of the decisions that Webmachine
makes when servicing a request to your resource, which is why we have
the "visual debugger". In development, you can turn on tracing of the
decision graph for a resource by implementing the `#trace?` callback
so that it returns true:

```ruby
class MyTracedResource < Webmachine::Resource
def trace?
true
end

# The rest of your callbacks...
end
```

Then enable the visual debugger resource by adding a route to your
configuration:

```ruby
Webmachine.application.routes do
# This can be any path as long as it ends with '*'
add ['trace', '*'], Webmachine::Trace::TraceResource
# The rest of your routes...
end
```

Now when you visit your traced resource, a trace of the request
process will be recorded in memory. Open your browser to `/trace` to
list the recorded traces and inspect the result. The response from your
traced resource will also include the `X-Webmachine-Trace-Id` that you
can use to lookup the trace. It might look something like this:

![preview calls at decision](http://seancribbs-skitch.s3.amazonaws.com/Webmachine_Trace_2156885920-20120625-100153.png)

Refer to
[examples/debugger.rb](/examples/debugger.rb)
for an example of how to enable the debugger.
the "visual debugger". Learn how to configure it [here](/documentation/visual-debugger.md)

## Related libraries

Expand All @@ -190,3 +114,6 @@ webmachine-ruby is licensed under the
[Apache v2.0 license](http://www.apache.org/licenses/LICENSE-2.0). See
LICENSE for details.

[callbacks]: https://github.com/seancribbs/webmachine-ruby/blob/master/lib/webmachine/resource/callbacks.rb
[diagram]: http://benoitc.github.io/dj-webmachine/images/http-headers-status-v3.png
[flow]: https://github.com/seancribbs/webmachine-ruby/blob/master/lib/webmachine/decision/flow.rb
28 changes: 28 additions & 0 deletions documentation/adapters.md
@@ -0,0 +1,28 @@
### Adapters

Webmachine includes adapters for [WEBrick][webrick], [Reel][reel], and
[HTTPkit][httpkit]. Additionally, the [Rack][rack] adapter lets it
run on any webserver that provides a Rack interface. It also lets it run on
[Shotgun][shotgun] ([example][shotgun_example]).

#### A Note about Rack

In order to be compatible with popular deployment stacks,
Webmachine has a [Rack](https://github.com/rack/rack) adapter (thanks to Jamis Buck).
**n.b.:** We recommend that NO middleware is used. The
behaviors that are encapsulated in Webmachine assume that no modifications
are done to requests or response outside of Webmachine.

## A Note about MRI 1.9

The [Reel][reel] and [HTTPkit][httpkit]
adapters might crash with a `SystemStackError` on MRI 1.9 due to its
limited fiber stack size. If your application is affected by this, the
only known solution is to switch to JRuby, Rubinius or MRI 2.0.

[webrick]: http://rubydoc.info/stdlib/webrick
[reel]: https://github.com/celluloid/reel
[httpkit]: https://github.com/lgierth/httpkit
[rack]: https://github.com/rack/rack
[shotgun]: https://github.com/rtomayko/shotgun
[shotgun_example]: https://gist.github.com/4389220
19 changes: 19 additions & 0 deletions documentation/configurator.md
@@ -0,0 +1,19 @@
### Application/Configurator

A call to `Webmachine::Application#configure` returns a `Webmachine::Application` instance,
so you could chain other method calls if you like. If you don't want to create your own separate
application object `Webmachine.application` will return a global one.

```ruby
require 'webmachine'
require 'my_resource'

Webmachine.application.configure do |config|
config.ip = '127.0.0.1'
config.port = 3000
config.adapter = :WEBrick
end

# Start a web server to serve requests via localhost
Webmachine.application.run
```
171 changes: 171 additions & 0 deletions documentation/examples.md
@@ -0,0 +1,171 @@
# GET
* Override `resource_exists?`, `content_types_provided`, `allowed_methods`, and implement the method to render the resource.

```ruby
class OrderResource < Webmachine::Resource
def allowed_methods
["GET"]
end

def content_types_provided
[["application/json", :to_json]]
end

def resource_exists?
@order = Order.find(id)
end

def to_json
@order.to_json
end

private

def id
request.path_info[:id]
end
end

```

# POST to create a new resource in a collection
* Override `post_is_create?` to return true
* Override `create_path` to return the relative path to the new resource. Note that this will be called _before_ the resource is actually created, which means that you need to know the ID before the object has been inserted into the database. This might seem a hassle, but it stops you from exposing your database column IDs to the world, which is a naughty and lazy habit we've all picked up from Rails.
* The response Content-Type and status will be set for you.

```ruby
class OrdersResource < Webmachine::Resource

def allowed_methods
["POST"]
end

def content_types_accepted
[["application/json", :from_json]]
end

def post_is_create?
true
end

def create_path
@id = Order.next_id
"/orders/#@id"
end

private

def from_json
order = Order.new(params).save(@id)
response.body = order.to_json
end

def params
JSON.parse(request.body.to_s)
end
end
```

# POST to perform a task
* Override `allowed_methods` and `process_post`. Put all the code to be executed in `process_post`.
* `process_post` must return true, or the HTTP response code
* Response headers like Content-Type will need to be set manually.

```ruby
class DispatchOrderResource < Webmachine::Resource

def allowed_methods
["POST"]
end

def resource_exists?
@order = Order.find(id)
end

def process_post
@order.dispatch
response.headers['Content-Type'] = 'text/plain'
response.body = "Successfully dispatched order #{id}"
true
end

private

def id
request.path_info[:id]
end
end

```

# PUT
* Override `resource_exists?`, `content_types_accepted`, `allowed_methods`, and implement the method to create/replace the resource.

```ruby
class OrderResource < Webmachine::Resource

def allowed_methods
["PUT"]
end

def content_types_accepted
[["application/json", :from_json]]
end

def resource_exists?
@order = Order.find(id)
end

def from_json
# Remember PUT should replace the entire resource, not merge the attributes! That's what PATCH is for.
# It's also why you should not expose your database IDs as your API IDs.
@order.destroy if @order
new_order = Order.new(params)
new_order.save(id)
response.body = new_order.to_json
end

private

def params
JSON.parse(request.body.to_s)
end

def id
request.path_info[:id]
end
end
```

# PATCH
* Webmachine does not currently support PATCH requests. See https://github.com/seancribbs/webmachine-ruby/issues/109 for more information and https://github.com/bethesque/pact_broker/blob/2918814e70bbda14df68598a6a41502a5eac4308/lib/pact_broker/api/resources/pacticipant.rb for a dirty hack to make it work if you need to.

# DELETE
* Override `resource_exists?` and `delete_resource`
* `delete_resource` must return true
* See callbacks.rb for documentation on asynchronous deletes.

```ruby
class OrderResource < Webmachine::Resource

def allowed_methods
["DELETE"]
end

def resource_exists?
@order = Order.find(id)
end

def delete_resource
Order.find(id).destroy
true
end

private

def id
request.path_info[:id]
end

end
```

0 comments on commit eb7a6d1

Please sign in to comment.