webmachine 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/CHANGELOG.md +4 -0
  4. data/Gemfile +13 -11
  5. data/README.md +85 -89
  6. data/Rakefile +0 -1
  7. data/documentation/adapters.md +39 -0
  8. data/documentation/authentication-and-authorization.md +37 -0
  9. data/documentation/configurator.md +19 -0
  10. data/documentation/error-handling.md +86 -0
  11. data/documentation/examples.md +215 -0
  12. data/documentation/how-it-works.md +76 -0
  13. data/documentation/routes.md +97 -0
  14. data/documentation/validation.md +159 -0
  15. data/documentation/versioning-apis.md +74 -0
  16. data/documentation/visual-debugger.md +38 -0
  17. data/examples/application.rb +2 -2
  18. data/examples/debugger.rb +1 -1
  19. data/lib/webmachine.rb +3 -1
  20. data/lib/webmachine/adapter.rb +7 -13
  21. data/lib/webmachine/adapters.rb +1 -2
  22. data/lib/webmachine/adapters/httpkit.rb +74 -0
  23. data/lib/webmachine/adapters/lazy_request_body.rb +1 -2
  24. data/lib/webmachine/adapters/rack.rb +37 -21
  25. data/lib/webmachine/adapters/reel.rb +21 -23
  26. data/lib/webmachine/adapters/webrick.rb +16 -16
  27. data/lib/webmachine/application.rb +2 -2
  28. data/lib/webmachine/chunked_body.rb +3 -4
  29. data/lib/webmachine/constants.rb +75 -0
  30. data/lib/webmachine/decision/conneg.rb +12 -10
  31. data/lib/webmachine/decision/flow.rb +31 -21
  32. data/lib/webmachine/decision/fsm.rb +10 -18
  33. data/lib/webmachine/decision/helpers.rb +9 -37
  34. data/lib/webmachine/dispatcher.rb +13 -10
  35. data/lib/webmachine/dispatcher/route.rb +18 -8
  36. data/lib/webmachine/errors.rb +7 -1
  37. data/lib/webmachine/header_negotiation.rb +25 -0
  38. data/lib/webmachine/headers.rb +7 -2
  39. data/lib/webmachine/locale/en.yml +7 -5
  40. data/lib/webmachine/media_type.rb +10 -8
  41. data/lib/webmachine/request.rb +44 -15
  42. data/lib/webmachine/resource.rb +1 -1
  43. data/lib/webmachine/resource/callbacks.rb +6 -4
  44. data/lib/webmachine/spec/IO_response.body +1 -0
  45. data/lib/webmachine/spec/adapter_lint.rb +70 -36
  46. data/lib/webmachine/spec/test_resource.rb +10 -4
  47. data/lib/webmachine/streaming/fiber_encoder.rb +1 -5
  48. data/lib/webmachine/streaming/io_encoder.rb +6 -0
  49. data/lib/webmachine/trace.rb +1 -0
  50. data/lib/webmachine/trace/fsm.rb +20 -10
  51. data/lib/webmachine/trace/resource_proxy.rb +2 -0
  52. data/lib/webmachine/translation.rb +2 -1
  53. data/lib/webmachine/version.rb +3 -3
  54. data/memory_test.rb +37 -0
  55. data/spec/spec_helper.rb +9 -9
  56. data/spec/webmachine/adapter_spec.rb +14 -15
  57. data/spec/webmachine/adapters/httpkit_spec.rb +10 -0
  58. data/spec/webmachine/adapters/rack_spec.rb +6 -6
  59. data/spec/webmachine/adapters/reel_spec.rb +15 -11
  60. data/spec/webmachine/adapters/webrick_spec.rb +2 -2
  61. data/spec/webmachine/application_spec.rb +18 -17
  62. data/spec/webmachine/chunked_body_spec.rb +3 -3
  63. data/spec/webmachine/configuration_spec.rb +5 -5
  64. data/spec/webmachine/cookie_spec.rb +13 -13
  65. data/spec/webmachine/decision/conneg_spec.rb +48 -42
  66. data/spec/webmachine/decision/falsey_spec.rb +4 -4
  67. data/spec/webmachine/decision/flow_spec.rb +194 -144
  68. data/spec/webmachine/decision/fsm_spec.rb +17 -17
  69. data/spec/webmachine/decision/helpers_spec.rb +20 -20
  70. data/spec/webmachine/dispatcher/route_spec.rb +73 -27
  71. data/spec/webmachine/dispatcher_spec.rb +34 -24
  72. data/spec/webmachine/errors_spec.rb +1 -1
  73. data/spec/webmachine/etags_spec.rb +19 -19
  74. data/spec/webmachine/events_spec.rb +6 -6
  75. data/spec/webmachine/headers_spec.rb +14 -14
  76. data/spec/webmachine/media_type_spec.rb +36 -36
  77. data/spec/webmachine/request_spec.rb +33 -33
  78. data/spec/webmachine/resource/authentication_spec.rb +6 -6
  79. data/spec/webmachine/response_spec.rb +12 -12
  80. data/spec/webmachine/trace/fsm_spec.rb +8 -8
  81. data/spec/webmachine/trace/resource_proxy_spec.rb +9 -9
  82. data/spec/webmachine/trace/trace_store_spec.rb +5 -5
  83. data/spec/webmachine/trace_spec.rb +3 -3
  84. data/webmachine.gemspec +2 -6
  85. metadata +48 -206
  86. data/lib/webmachine/adapters/hatetepe.rb +0 -108
  87. data/lib/webmachine/adapters/mongrel.rb +0 -127
  88. data/lib/webmachine/dispatcher/not_found_resource.rb +0 -5
  89. data/lib/webmachine/fiber18.rb +0 -88
  90. data/spec/webmachine/adapters/hatetepe_spec.rb +0 -60
  91. data/spec/webmachine/adapters/mongrel_spec.rb +0 -16
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9c535ba9701c69baab3387693d7060214271a615
4
+ data.tar.gz: 2701314eb2dd27ba6af46013532b6e653f63ac95
5
+ SHA512:
6
+ metadata.gz: 5e24dfb767ed02c92a69c8d8962dad39abb1652baeb0f7852a692cf61f5baa6fbd36ca297dfca9b10a3262579c4047b83ea6fe1861927177fa50f4e6522803d2
7
+ data.tar.gz: a2cabce4143030fbec07b5ebdc65c38c1018df32fe6844a23c0ced95c1dc88e434be17c260dadb0205d6279a6c915c67c321f62665a4a4b247f1da9145d42775
data/.gitignore CHANGED
@@ -26,3 +26,7 @@ Gemfile.lock
26
26
  **/bin
27
27
  *.rbc
28
28
  .rvmrc
29
+ .SyncID
30
+ .SyncIgnore
31
+ vendor
32
+
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ### HEAD
2
+
3
+ * decode the value of the header 'Content-MD5' as base64-encoded string.
4
+
1
5
  ### 1.2.2 January 8, 2014
2
6
 
3
7
  1.2.2 is a bugfix/patch release that expands functionality with some edge
data/Gemfile CHANGED
@@ -1,15 +1,22 @@
1
1
  require 'rbconfig'
2
-
3
2
  source 'https://rubygems.org'
4
-
5
3
  gemspec
6
4
 
7
- gem 'bundler'
5
+ group :development do
6
+ gem "yard"
7
+ gem "rake"
8
+ end
9
+
10
+ group :test do
11
+ gem "rspec", '~> 3.0.0'
12
+ gem "rspec-its"
13
+ gem "rack"
14
+ end
8
15
 
9
16
  group :webservers do
10
- gem 'mongrel', '~> 1.2.beta', :platform => [:mri, :rbx]
11
- gem 'reel', '~> 0.4.0.pre5'
12
- gem 'hatetepe', '~> 0.5.2'
17
+ gem 'reel', '~> 0.5.0'
18
+ gem 'http', '~> 0.6.0'
19
+ gem 'httpkit', :platform => [:mri, :rbx]
13
20
  end
14
21
 
15
22
  group :guard do
@@ -34,8 +41,3 @@ end
34
41
  platforms :jruby do
35
42
  gem 'jruby-openssl'
36
43
  end
37
-
38
- platform :rbx do
39
- gem 'rubysl'
40
- gem 'racc'
41
- end
data/README.md CHANGED
@@ -4,9 +4,10 @@ webmachine-ruby is a port of
4
4
  [Webmachine](https://github.com/basho/webmachine), which is written in
5
5
  Erlang. The goal of both projects is to expose interesting parts of
6
6
  the HTTP protocol to your application in a declarative way. This
7
- means that you are less concerned with handling requests directly and
8
- more with describing the behavior of the resources that make up your
9
- application. Webmachine is not a web framework _per se_, but more of a
7
+ means that you are less concerned with the procedures involved in handling
8
+ requests directly and more with describing facts about the resources
9
+ that make up your application.
10
+ Webmachine is not a web framework _per se_, but more of a
10
11
  toolkit for building HTTP-friendly applications. For example, it does
11
12
  not provide a templating engine or a persistence layer; those choices
12
13
  are up to you.
@@ -15,46 +16,34 @@ are up to you.
15
16
 
16
17
  * Handles the hard parts of content negotiation, conditional
17
18
  requests, and response codes for you.
18
- * Most callbacks can interrupt the decision flow by returning an
19
- integer response code. You generally only want to do this when new
20
- information comes to light, requiring a modification of the response.
21
- * Supports WEBrick and Mongrel (1.2pre+), and a Rack shim. Other host
19
+ * Provides a base resource with points of extension to let you
20
+ describe what is relevant about your particular resource.
21
+ * Supports WEBrick, Reel, HTTPkit, and a Rack shim. Other host
22
22
  servers are being investigated.
23
23
  * Streaming/chunked response bodies are permitted as Enumerables,
24
24
  Procs, or Fibers!
25
25
  * Unlike the Erlang original, it does real Language negotiation.
26
- * Includes the visual debugger so you can look through the decision
26
+ * Includes a visual debugger so you can look through the decision
27
27
  graph to determine how your resources are behaving.
28
28
 
29
29
  ## Documentation & Finding Help
30
30
 
31
+ * [How it works](/documentation/how-it-works.md) - understand how Webmachine works and the basics of creating a resource.
32
+ * [Example resources][example-resources] showing how to implement each HTTP method.
33
+ * [Routes][routes]
34
+ * [Authentication and authorization][authentication-and-authorization]
35
+ * [Validation][validation]
36
+ * [Error handling][error-handling]
37
+ * [Visual debugger][visual-debugger]
38
+ * [Configurator][configurator]
39
+ * [Webserver adapters][adapters]
40
+ * [Versioning APIs][versioning-apis]
31
41
  * [API documentation](http://rubydoc.info/gems/webmachine/frames/file/README.md)
32
42
  * [Mailing list](mailto:webmachine.rb@librelist.com)
33
43
  * IRC channel #webmachine on freenode
34
44
 
35
- ## A Note about Rack
36
-
37
- In order to be compatible with popular deployment stacks,
38
- Webmachine has a [Rack](https://github.com/rack/rack) adapter (thanks to Jamis Buck).
39
- **n.b.:** We recommend that NO middleware is used. The
40
- behaviors that are encapsulated in Webmachine assume that no modifications
41
- are done to requests or response outside of Webmachine.
42
-
43
- ## A Note about MRI 1.9
44
-
45
- The [Reel](https://github.com/celluloid/reel) and [Hatetepe](https://github.com/lgierth/hatetepe)
46
- adapters might crash with a `SystemStackError` on MRI 1.9 due to its
47
- limited fiber stack size. If your application is affected by this, the
48
- only known solution is to switch to JRuby, Rubinius or MRI 2.0.
49
-
50
-
51
45
  ## Getting Started
52
46
 
53
- [GiddyUp](https://github.com/basho/giddyup) is an actively
54
- developed webmachine-ruby app that is in production. You
55
- can look there for an example of how to write and structure a
56
- webmachine-ruby app (although it is hacky in places).
57
-
58
47
  Below we go through some examples of how to do basic things
59
48
  with webmachine-ruby.
60
49
 
@@ -71,8 +60,11 @@ There are many other HTTP features exposed to a resource through
71
60
  of the decision tree Webmachine implements, and the decision tree
72
61
  is what makes Webmachine unique and powerful.
73
62
 
63
+ ### A simple static HTML resource
64
+
74
65
  ```ruby
75
66
  require 'webmachine'
67
+
76
68
  class MyResource < Webmachine::Resource
77
69
  def to_html
78
70
  "<html><body>Hello, world!</body></html>"
@@ -83,6 +75,39 @@ end
83
75
  MyResource.run
84
76
  ```
85
77
 
78
+ ### A simple dynamic JSON Resource
79
+
80
+ ```ruby
81
+ require 'webmachine'
82
+ require 'widget'
83
+
84
+ class MyResource < Webmachine::Resource
85
+
86
+ # GET and HEAD are allowed by default, but are shown here for clarity.
87
+ def allowed_methods
88
+ ['GET','HEAD']
89
+ end
90
+
91
+ def content_types_provided
92
+ [['application/json', :to_json]]
93
+ end
94
+
95
+ # Return a Truthy or Falsey value
96
+ def resource_exists?
97
+ widget
98
+ end
99
+
100
+ def widget
101
+ @widget ||= Widget.find(request.path_info[:id])
102
+ end
103
+
104
+ def to_json
105
+ widget.to_json
106
+ end
107
+ end
108
+
109
+ ```
110
+
86
111
  ### Router
87
112
 
88
113
  The router is used to map a resource to a given path. To map the class `MyResource` to
@@ -97,83 +122,44 @@ end
97
122
  Webmachine.application.run
98
123
  ```
99
124
 
100
- ### Application/Configurator
101
-
102
- There's a configurator that allows you to set what IP address and port
103
- a web server should bind to as well as what web server should serve a
104
- webmachine resource.
105
-
106
- A call to `Webmachine::Application#configure` returns a `Webmachine::Application` instance,
107
- so you could chain other method calls if you like. If you don't want to create your own separate
108
- application object `Webmachine.application` will return a global one.
125
+ When the resource needs to be mapped with variables that will be passed into the resource, use symbols to identify which path components are variables.
109
126
 
110
127
  ```ruby
111
- require 'webmachine'
112
- require 'my_resource'
113
128
 
114
- Webmachine.application.configure do |config|
115
- config.ip = '127.0.0.1'
116
- config.port = 3000
117
- config.adapter = :Mongrel
129
+ Webmachine.application.routes do
130
+ add ['myresource', :id], MyResource
118
131
  end
119
132
 
120
- # Start a web server to serve requests via localhost
121
- Webmachine.application.run
122
133
  ```
123
134
 
124
- Webmachine includes adapters for [Webrick][webrick], [Mongrel][mongrel],
125
- [Reel][reel], and [Hatetepe][hatetepe]. Additionally, the [Rack][rack] adapter lets it
126
- run on any webserver that provides a Rack interface. It also lets it run on
127
- [Shotgun][shotgun] ([example][shotgun_example]).
128
-
129
- [webrick]: http://rubydoc.info/stdlib/webrick
130
- [mongrel]: https://github.com/evan/mongrel
131
- [reel]: https://github.com/celluloid/reel
132
- [hatetepe]: https://github.com/lgierth/hatetepe
133
- [rack]: https://github.com/rack/rack
134
- [shotgun]: https://github.com/rtomayko/shotgun
135
- [shotgun_example]: https://gist.github.com/4389220
136
-
137
- ### Visual debugger
138
-
139
- It can be hard to understand all of the decisions that Webmachine
140
- makes when servicing a request to your resource, which is why we have
141
- the "visual debugger". In development, you can turn on tracing of the
142
- decision graph for a resource by implementing the `#trace?` callback
143
- so that it returns true:
135
+ To add more components to the URL mapping, simply add them to the array.
144
136
 
145
137
  ```ruby
146
- class MyTracedResource < Webmachine::Resource
147
- def trace?
148
- true
149
- end
150
138
 
151
- # The rest of your callbacks...
139
+ Webmachine.application.routes do
140
+ add ['myparentresource', :parent_id, 'myresource', :id], MyResource
152
141
  end
142
+
153
143
  ```
154
144
 
155
- Then enable the visual debugger resource by adding a route to your
156
- configuration:
145
+ Read more about routing [here][routes].
157
146
 
158
- ```ruby
159
- Webmachine.application.routes do
160
- # This can be any path as long as it ends with '*'
161
- add ['trace', '*'], Webmachine::Trace::TraceResource
162
- # The rest of your routes...
163
- end
164
- ```
147
+ ### Application/Configurator
148
+
149
+ There is a configurator that allows you to set what IP address and port
150
+ a web server should bind to as well as what web server should serve a
151
+ webmachine resource. Learn how to configure your application [here][configurator].
165
152
 
166
- Now when you visit your traced resource, a trace of the request
167
- process will be recorded in memory. Open your browser to `/trace` to
168
- list the recorded traces and inspect the result. The response from your
169
- traced resource will also include the `X-Webmachine-Trace-Id` that you
170
- can use to lookup the trace. It might look something like this:
171
153
 
172
- ![preview calls at decision](http://seancribbs-skitch.s3.amazonaws.com/Webmachine_Trace_2156885920-20120625-100153.png)
154
+ ### Adapters
173
155
 
174
- Refer to
175
- [examples/debugger.rb](/examples/debugger.rb)
176
- for an example of how to enable the debugger.
156
+ Webmachine provides adapters for many popular webservers. Learn more [here][adapters].
157
+
158
+ ### Visual debugger
159
+
160
+ It can be hard to understand all of the decisions that Webmachine
161
+ makes when servicing a request to your resource, which is why we have
162
+ the "visual debugger". Learn how to configure it [here][visual-debugger].
177
163
 
178
164
  ## Related libraries
179
165
 
@@ -183,6 +169,7 @@ for an example of how to enable the debugger.
183
169
  * [webmachine-sprockets](https://github.com/lgierth/webmachine-sprockets) - Integration with Sprockets assets packaging system
184
170
  * [webmachine-actionview](https://github.com/rgarner/webmachine-actionview) - Integration of some Rails-style view conventions into Webmachine
185
171
  * [jruby-http-kit](https://github.com/nLight/jruby-http-kit) - Includes an adapter for the Clojure-based Ring library/server
172
+ * [newrelic-webmachine](https://github.com/mdub/newrelic-webmachine) - NewRelic instrumentation
186
173
 
187
174
  ## LICENSE
188
175
 
@@ -190,3 +177,12 @@ webmachine-ruby is licensed under the
190
177
  [Apache v2.0 license](http://www.apache.org/licenses/LICENSE-2.0). See
191
178
  LICENSE for details.
192
179
 
180
+ [example-resources]: /documentation/examples.md
181
+ [versioning-apis]: /documentation/versioning-apis.md
182
+ [routes]: /documentation/routes.md
183
+ [error-handling]: /documentation/error-handling.md
184
+ [authentication-and-authorization]: /documentation/authentication-and-authorization.md
185
+ [adapters]: /documentation/adapters.md
186
+ [visual-debugger]: /documentation/visual-debugger.md
187
+ [configurator]: /documentation/configurator.md
188
+ [validation]: /documentation/validation.md
data/Rakefile CHANGED
@@ -51,7 +51,6 @@ task :clean_whitespace do
51
51
  end
52
52
  end
53
53
 
54
- require 'rspec/core'
55
54
  require 'rspec/core/rake_task'
56
55
 
57
56
  desc "Run specs"
@@ -0,0 +1,39 @@
1
+ ### Adapters
2
+
3
+ Webmachine includes adapters for [WEBrick][webrick], [Reel][reel], and
4
+ [HTTPkit][httpkit]. Additionally, the [Rack][rack] adapter lets it
5
+ run on any webserver that provides a Rack interface. It also lets it run on
6
+ [Shotgun][shotgun] ([example][shotgun_example]).
7
+
8
+ #### A Note about Rack
9
+
10
+ In order to be compatible with popular deployment stacks,
11
+ Webmachine has a [Rack](https://github.com/rack/rack) adapter (thanks to Jamis Buck).
12
+
13
+ Webmachine can be used with Rack middlware features such as Rack::Map and Rack::Cascade as long as:
14
+
15
+ 1. The Webmachine app is mounted at the root directory.
16
+ 2. Any requests/responses that are handled by the Webmachine app are not modified by the middleware. The behaviours that are encapsulated in Webmachine assume that no modifications
17
+ are done to requests or response outside of Webmachine.
18
+
19
+ Keep in mind that Webmachine already supports many things that Rack middleware is used for with other HTTP frameworks (eg. etags, specifying supported/preferred Accept and Content-Types).
20
+
21
+ For an example of using Webmachine with Rack middleware, see the [Pact Broker][middleware-example].
22
+
23
+ See the [Rack Adapter API docs][rack-adapter-api-docs] for more information.
24
+
25
+ #### A Note about MRI 1.9
26
+
27
+ The [Reel][reel] and [HTTPkit][httpkit]
28
+ adapters might crash with a `SystemStackError` on MRI 1.9 due to its
29
+ limited fiber stack size. If your application is affected by this, the
30
+ only known solution is to switch to JRuby, Rubinius or MRI 2.0.
31
+
32
+ [webrick]: http://rubydoc.info/stdlib/webrick
33
+ [reel]: https://github.com/celluloid/reel
34
+ [httpkit]: https://github.com/lgierth/httpkit
35
+ [rack]: https://github.com/rack/rack
36
+ [shotgun]: https://github.com/rtomayko/shotgun
37
+ [shotgun_example]: https://gist.github.com/4389220
38
+ [rack-adapter-api-docs]: http://rubydoc.info/gems/webmachine/Webmachine/Adapters/Rack
39
+ [middleware-example]: https://github.com/bethesque/pact_broker/blob/6dfa71d98e38be94f0776d30bf66cfca58f97d61/lib/pact_broker/app.rb
@@ -0,0 +1,37 @@
1
+ # Authentication
2
+
3
+ To secure a resource, override the `is_authorized?` method to return a boolean indicating whether or not the client is authenticated (ie. your application believes they are who they say they are). Confusingly, the HTTP "401 Unauthorized" response code actually relates to authentication, not authorization (see the [Authorization](#authorization) section below).
4
+
5
+ ## HTTP Basic Auth
6
+
7
+ ```ruby
8
+
9
+ class MySecureResource < Webmachine::Resource
10
+
11
+ include Webmachine::Resource::Authentication
12
+
13
+ def is_authorized?(authorization_header)
14
+ basic_auth(authorization_header, "My Application") do |username, password|
15
+ @user = User.find_by_username(username)
16
+ !@user.nil? && @user.auth?(password)
17
+ end
18
+ end
19
+
20
+ end
21
+
22
+ ```
23
+
24
+ # Authorization
25
+
26
+ Once the client is authenticated (that is, you believe they are who they say they are), override `forbidden?` to return true if the client does not have permission to perform the given method this resource.
27
+
28
+ ```ruby
29
+
30
+ class MySecureResource < Webmachine::Resource
31
+
32
+ def forbidden?
33
+ MySecureResourcePolicy.new(@user, my_secure_domain_model).forbidden?(request.method)
34
+ end
35
+
36
+ end
37
+ ```
@@ -0,0 +1,19 @@
1
+ ### Application/Configurator
2
+
3
+ A call to `Webmachine::Application#configure` returns a `Webmachine::Application` instance,
4
+ so you could chain other method calls if you like. If you don't want to create your own separate
5
+ application object `Webmachine.application` will return a global one.
6
+
7
+ ```ruby
8
+ require 'webmachine'
9
+ require 'my_resource'
10
+
11
+ Webmachine.application.configure do |config|
12
+ config.ip = '127.0.0.1'
13
+ config.port = 3000
14
+ config.adapter = :WEBrick
15
+ end
16
+
17
+ # Start a web server to serve requests via localhost
18
+ Webmachine.application.run
19
+ ```
@@ -0,0 +1,86 @@
1
+ # Error handling
2
+
3
+ ## Handling runtime errors
4
+
5
+ Runtime errors should happen infrequently when using Webmachine, as many of the potential causes of "error" will have already been checked in the appropriate callback, and handled with a meaningful HTTP response code (eg. `resource_exists?` or `is_authorized?`).
6
+
7
+ To return a custom error response, override `handle_exception` and modify the response body and headers as desired.
8
+
9
+ ```ruby
10
+
11
+ class MyResource < Webmachine::Resource
12
+
13
+ def handle_exception e
14
+ response.headers['Content-Type'] = 'application/json'
15
+ response.body = {:message => e.message, :backtrace => e.backtrace }.to_json
16
+ end
17
+
18
+ end
19
+
20
+ ```
21
+
22
+ Given that this should be a genuine "Server Error", the response code is set to 500, and cannot be overridden in `handle_exception`. If you must set a custom error response code, but were unable to use one of the previous callbacks to set it, use `finish_request` to set the response code as desired.
23
+
24
+ ## Customising the error response
25
+
26
+ You can modify the response headers and body in any callback.
27
+
28
+ ```ruby
29
+
30
+ class MyResource < Webmachine::Resource
31
+
32
+ def resource_exists?
33
+ @droid = Droid.find(request.path_info[:droid_id]).tap do | droid |
34
+ unless droid
35
+ response.headers['Content-Type'] = "text/plain"
36
+ response.body = "These aren't the droids you're looking for."
37
+ end
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ ```
44
+
45
+ ## Returning a custom error code
46
+
47
+ If your response code cannot be determined in an appropriate callback, returning an integer response code from most of the callbacks will cause the response to be returned immediately. You generally only want to do this when new information comes to light, requiring a modification of the response.
48
+
49
+ ```ruby
50
+
51
+ class MyResource < Webmachine::Resource
52
+
53
+ def content_types_accepted
54
+ [
55
+ ["application/json", :from_json],
56
+ ["application/xml", :from_xml]
57
+ ]
58
+ end
59
+
60
+ def malformed_request?
61
+ # Is this JSON or XML? Don't know without a messy if statement.
62
+ # Maybe cleaner to decide in the response handler for the appropriate Content-Type?
63
+ false
64
+ end
65
+
66
+ def from_json
67
+ return 400 if invalid_json?
68
+ ...
69
+ end
70
+
71
+ def from_xml
72
+ return 400 if invalid_xml?
73
+ ...
74
+ end
75
+
76
+ def invalid_json?
77
+ ...
78
+ end
79
+
80
+ def invalid_xml?
81
+ ...
82
+ end
83
+
84
+ end
85
+
86
+ ```