# webmachine-ruby turns HTTP response semantics into a decision tree

> webmachine-ruby is a Ruby port of the Erlang Webmachine, built for applications that want content negotiation, conditional requests and status codes decided by declared facts rather than hand-written control flow. It is a toolkit, not a web framework, and the visual debugger is the feature that makes its decision tree legible.

**webmachine/webmachine-ruby** — Webmachine, the HTTP toolkit (in Ruby)

- Repository: https://github.com/webmachine/webmachine-ruby
- Website: http://rdoc.info/github/seancribbs/webmachine-ruby/master/frames
- Stars: 850 · Forks: 49
- Language: Ruby
- License: NOASSERTION
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/webmachine-webmachine-ruby

## Declarative facts about a resource instead of a request handler

The idea webmachine-ruby inherits from the Erlang original is to move the interesting parts of HTTP out of your handler and into a set of declared facts. Instead of writing a method that inspects the request, negotiates a format, checks a validator header and picks a status code, you describe what is true about a resource and let a decision tree work out the response. The project documentation states the goal plainly: less concern with the procedures involved in handling requests, more with describing facts about the resources that make up the application.

That framing has consequences for how the library fits. It is not a web framework. It ships no templating engine and no persistence layer, and the documentation says outright that those choices are up to you. A resource is a class you subclass, and the class answers questions about itself.

The audience is Ruby developers building HTTP-facing services where the protocol details are the hard part: an API that serves several representations, supports conditional requests, or has to be correct about 304 and 412 responses. For a service that returns JSON from one table, this machinery is a cost with no matching benefit.

The Ruby port also diverges from Erlang where it matters. The feature list notes that, unlike the Erlang original, it does real Language negotiation, meaning the Accept-Language handling is not a placeholder.

## The decision tree, and what a resource actually implements

A resource answers a fixed set of questions, and the answers determine the outcome. The documentation calls this the decision tree, and the project documentation is emphatic that it is the thing that makes webmachine unique. A callback can alter the outcome of the tree, so behaviour is changed by overriding methods rather than by inserting branches into a request loop.

The examples show the shape clearly. A static resource needs only a representation method, and the shortest complete example is four lines of class body inside a subclass of Webmachine::Resource.

```ruby
require 'webmachine'

class MyResource < Webmachine::Resource
  def to_html
    "<html><body>Hello, world!</body></html>"
  end
end

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

The dynamic example answers more questions at once: which methods are allowed, which media types it can produce, and whether the underlying object exists.

```ruby
require 'webmachine'
require 'widget'

class MyResource < Webmachine::Resource

  # GET and HEAD are allowed by default, but are shown here for clarity.
  def allowed_methods
    ['GET','HEAD']
  end

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

  # Return a Truthy or Falsey value
  def resource_exists?
    widget
  end

  def widget
    @widget ||= Widget.find(request.path_info[:id])
  end

  def to_json
    widget.to_json
  end
end
```

Notice the structure of content_types_provided: a list of media type and method pairs, which is what makes negotiation declarative. The answer to a request is a function of the declared facts, not of a script you wrote. GET and HEAD are allowed by default, and resource_exists? returning a truthy or falsey value is what separates a 404 from a 200 in the tree.

## Routing paths with variables, and mounting resources on a real application

Resources are mounted onto paths through a router that lives on the application object. A flat mapping looks like this.

```ruby
Webmachine.application.routes do
  add ['myresource'], MyResource
end

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

Path components that should become variables are written as symbols, and the symbol name is the key you then read in the resource. The example uses request.path_info[:id] to look up a record, so the naming convention matters and is not free-form.

```ruby
Webmachine.application.routes do
  add ['myresource', :id], MyResource
end
```

Nesting is just a longer array, mixing literal segments and variables.

```ruby
Webmachine.application.routes do
  add ['myparentresource', :parent_id, 'myresource', :id], MyResource
end
```

The documentation notes that Webmachine::Resource.run exists for quick prototyping and development, and that in a real application you configure what path a resource is served from. That distinction is worth keeping in mind: the one-liner run is a development convenience, while routing plus an application object is how it goes into production.

Binding and server choice are handled by a configurator, which sets the IP address and port a web server should bind to as well as which web server serves the resource. The examples directory in the repository shows separate files for a WEBrick setup, a plain application, the debugger and logging, so these are separable concerns rather than one configuration blob.

## The visual debugger turns an opaque decision tree into a graph

The hardest part of a declarative HTTP toolkit is debugging it. When the tree returns a 403 and you expected a 200, reading the code tells you nothing, because the code is a set of facts and the behaviour comes from the order in which the tree consults them.

webmachine-ruby answers this with a visual debugger. The feature list describes it as a tool to look through the decision graph to determine how your resources are behaving, and the documentation index has a page dedicated to configuring it. The repository includes examples/debugger.rb, so the intended path is to enable it in a development configuration rather than to bolt it on afterwards.

For a team that is learning the semantics of the library, this is probably the single most valuable feature, because the alternative to the debugger is guessing which fact returned the wrong value. The project treats it as a headline item rather than a debugging afterthought, which tells you something about the intended workflow.

One caveat: the documentation is organised as a set of markdown files under /documentation, linked from the project readme, and the description of the debugger is short. The documentation does not explain what the debugger costs in performance, or whether it is safe to leave enabled in production, so treat it as a development tool until the documentation says otherwise.

## WEBrick and a Rack shim, with other servers still under investigation

Server support is the narrowest part of the design, and the documentation is unusually direct about it: webmachine supports WEBrick and a Rack shim, and other host servers are being investigated. That sentence dates the project, because WEBrick was removed from the Ruby standard library, and the Rack shim is the practical path to a modern server. The related libraries list even includes jruby-http-kit, which carries an adapter for the Clojure-based Ring library and server.

This matters for adoption. The library was designed to sit in front of whichever web server you like, and the related project list names adapters for other stacks, but the ones maintained inside this repository are WEBrick and Rack. If your production setup is a specific non-Rack server, check the adapters documentation before anything else.

Two other capabilities are easy to overlook. Streaming and chunked response bodies are permitted as Enumerables, Procs, or Fibers, which is a genuinely useful option for large or generated payloads. And the project ships a versioned API mechanism, documented under versioning APIs, so a resource can expose more than one representation version.

Around the core there is a small ecosystem of companion gems: webmachine-test for testing webmachine applications, webmachine-linking for linking between resources and Web Linking, webmachine-actionview for pulling Rails-style view conventions into webmachine, and newrelic-webmachine for New Relic instrumentation. If you adopt the core, these are the pieces that decide how much friction you inherit.

## Apache 2.0 in the docs, an unclassifiable licence file in the repository

The licence situation needs a direct look rather than a summary. The project states that webmachine-ruby is licensed under the Apache v2.0 license and links to the Apache licence text, saying to see LICENSE for details. The repository itself is flagged by GitHub as carrying a custom licence that the platform cannot classify, which usually means the LICENSE file is not a byte-for-byte copy of the standard Apache text, or includes additional terms.

Read the LICENSE file directly if this matters to you. The short version is that the project states Apache 2.0, and the authoritative wording is in the file rather than in the prose.

Maintenance is the other thing to check. The last push to the repository was on 2026-09-22, the project is not archived, and the repository has no GitHub releases, so there is no release page to check and no tag history to diff. A CHANGELOG.md file exists at the top level and a RELEASING.md file sits next to it, which is where the release cadence is described, and the gem is published on RubyGems under the name webmachine, with the gem version and build status carried as badges in the project description.

The testing setup is conventional Ruby: a spec directory, an .rspec file, a .rubocop.yml, a Rakefile and a Gemfile. There is also a memory_test.rb file at the top level, which suggests someone cared about the memory profile of the decision tree at some point.

## Where webmachine-ruby is the wrong tool

There are three clear cases. A small internal service that returns one representation of one resource will be shorter and clearer with a plain Rack application, and the decision tree adds a layer of indirection that buys nothing. A project that needs HTML views is in the same position, because webmachine deliberately ships no templating engine, and adopting webmachine-actionview to get view conventions back is more work than starting from a framework that has them.

The second case is a team that wants the HTTP details handled by a library with an active release trail. The last push was on 2026-09-22, but the repository publishes no GitHub releases, so upgrade planning means reading the CHANGELOG.md file rather than watching tags.

The third case is the production server question, and it is the one that most often decides the outcome. If your deployment target is not WEBrick, not Rack-compatible, or not covered by one of the community adapters, the integration cost lands on you. The tree also has a cost in learning: the semantics of conditional requests and content negotiation are subtle, and a wrong declaration in one resource produces a wrong status code that no amount of reading the source will explain. The visual debugger exists for exactly this, and if you are not willing to use it, the library works against you.

A fair comparison point is the plain Rack response: to_json, 200, done. Webmachine makes you declare the same knowledge in a different vocabulary, and returns a decision tree in exchange.

## Conclusion

Adopt webmachine-ruby when your API has negotiation and conditional request rules that keep growing and that keep producing wrong status codes, and when you can accept declaring resources instead of writing request handlers. Do not adopt it for a small CRUD service, a page that needs templates, or a codebase that expects Rails conventions, because it supplies neither a templating engine nor a persistence layer. Verify first that the callback list in Webmachine::Resource::Callbacks covers the rules you need, and that the adapter for your production web server exists, since the documented server support is WEBrick plus a Rack shim.

## FAQ

### How do I install webmachine-ruby and run a first resource?

The project is published as a gem named webmachine, and the project docs do not give a command line install. The shortest example subclasses Webmachine::Resource, defines a to_html method, and calls MyResource.run to serve requests through localhost.

### What is the difference between webmachine-ruby and the Erlang webmachine?

The Ruby project is a port of the Erlang project written by Basho, and the feature list notes one difference: unlike the Erlang original, it does real Language negotiation. The project docs do not document other divergences.

### Which web servers can webmachine-ruby run on?

The documentation states that webmachine supports WEBrick and a Rack shim, and that other host servers are being investigated. The adapters documentation page covers the available options.

### How do I debug a webmachine resource that returns the wrong status code?

The project includes a visual debugger that lets you look through the decision graph to see how a resource is behaving, and the repository ships examples/debugger.rb. The project docs do not state whether it is suitable for production use.

## Sources

- [Issues](https://github.com/webmachine/webmachine-ruby/issues)
- [Project website](http://rdoc.info/github/seancribbs/webmachine-ruby/master/frames)
- [README](https://github.com/webmachine/webmachine-ruby/blob/master/README.md)
- [webmachine/webmachine-ruby on GitHub](https://github.com/webmachine/webmachine-ruby)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/webmachine-webmachine-ruby
