pragma-operation 1.6.3 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +9 -8
- data/README.md +164 -3
- data/lib/pragma/operation.rb +14 -4
- data/lib/pragma/operation/base.rb +2 -247
- data/lib/pragma/operation/error.rb +27 -0
- data/lib/pragma/operation/response.rb +102 -0
- data/lib/pragma/operation/response/bad_request.rb +16 -0
- data/lib/pragma/operation/response/created.rb +13 -0
- data/lib/pragma/operation/response/forbidden.rb +19 -0
- data/lib/pragma/operation/response/no_content.rb +13 -0
- data/lib/pragma/operation/response/not_found.rb +19 -0
- data/lib/pragma/operation/response/ok.rb +13 -0
- data/lib/pragma/operation/response/unprocessable_entity.rb +23 -0
- data/lib/pragma/operation/version.rb +2 -1
- data/pragma-operation.gemspec +16 -19
- metadata +15 -55
- data/doc/01-basic-usage.md +0 -264
- data/doc/02-contracts.md +0 -154
- data/doc/03-policies.md +0 -179
- data/doc/04-decorators.md +0 -50
- data/lib/pragma/operation/authorization.rb +0 -130
- data/lib/pragma/operation/decoration.rb +0 -76
- data/lib/pragma/operation/validation.rb +0 -146
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b48bd839081e93bf86ae68587a35c6f3ad07fc4d
|
4
|
+
data.tar.gz: a0ce83bc5177c74503d82b744126f0f02931093c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cde98287cc82bc74923286973dc18cf83c04847943bc5bbc86652ac59b7391272b2b41a336cfbc47f2da9f3cc2dbf3c0f381a0dde76040a5a00d36d87848ca5d
|
7
|
+
data.tar.gz: 9d4179add815dcb6c5732c8c8501ba407298d97a1ac78c749e1499e85dede8a6b2d146c6e48e6db890df171f426f3a78e85469f2eed9aafe22f52b085245b015
|
data/.rubocop.yml
CHANGED
@@ -15,6 +15,7 @@ AllCops:
|
|
15
15
|
- 'config/**/*'
|
16
16
|
- '**/Rakefile'
|
17
17
|
- '**/Gemfile'
|
18
|
+
- 'pragma-operation.gemspec'
|
18
19
|
|
19
20
|
RSpec/DescribeClass:
|
20
21
|
Exclude:
|
@@ -24,26 +25,26 @@ Style/BlockDelimiters:
|
|
24
25
|
Exclude:
|
25
26
|
- 'spec/**/*'
|
26
27
|
|
27
|
-
|
28
|
+
Layout/AlignParameters:
|
28
29
|
EnforcedStyle: with_fixed_indentation
|
29
30
|
|
30
|
-
|
31
|
+
Layout/ClosingParenthesisIndentation:
|
31
32
|
Enabled: false
|
32
33
|
|
33
34
|
Metrics/LineLength:
|
34
35
|
Max: 100
|
35
36
|
AllowURI: true
|
36
37
|
|
37
|
-
|
38
|
+
Layout/FirstParameterIndentation:
|
38
39
|
Enabled: false
|
39
40
|
|
40
|
-
|
41
|
+
Layout/MultilineMethodCallIndentation:
|
41
42
|
EnforcedStyle: indented
|
42
43
|
|
43
|
-
|
44
|
+
Layout/IndentArray:
|
44
45
|
EnforcedStyle: consistent
|
45
46
|
|
46
|
-
|
47
|
+
Layout/IndentHash:
|
47
48
|
EnforcedStyle: consistent
|
48
49
|
|
49
50
|
Style/SignalException:
|
@@ -68,7 +69,7 @@ RSpec/NamedSubject:
|
|
68
69
|
RSpec/ExampleLength:
|
69
70
|
Enabled: false
|
70
71
|
|
71
|
-
|
72
|
+
Layout/MultilineMethodCallBraceLayout:
|
72
73
|
Enabled: false
|
73
74
|
|
74
75
|
Metrics/MethodLength:
|
@@ -86,7 +87,7 @@ Metrics/CyclomaticComplexity:
|
|
86
87
|
Metrics/ClassLength:
|
87
88
|
Enabled: false
|
88
89
|
|
89
|
-
|
90
|
+
Layout/CaseIndentation:
|
90
91
|
EnforcedStyle: end
|
91
92
|
IndentOneStep: false
|
92
93
|
|
data/README.md
CHANGED
@@ -7,7 +7,7 @@
|
|
7
7
|
|
8
8
|
Operations encapsulate the business logic of your JSON API.
|
9
9
|
|
10
|
-
They are built on top of the
|
10
|
+
They are built on top of the [Trailblazer::Operation](https://github.com/trailblazer/trailblazer-operation) gem.
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -31,8 +31,169 @@ $ gem install pragma-operation
|
|
31
31
|
|
32
32
|
## Usage
|
33
33
|
|
34
|
-
|
35
|
-
|
34
|
+
Let's build your first operation!
|
35
|
+
|
36
|
+
```ruby
|
37
|
+
module API
|
38
|
+
module V1
|
39
|
+
module Article
|
40
|
+
class Show < Pragma::Operation::Base
|
41
|
+
step :find!
|
42
|
+
failure :handle_not_found!, fail_fast: true
|
43
|
+
step :authorize!
|
44
|
+
failure :handle_unauthorized!
|
45
|
+
step :respond!
|
46
|
+
|
47
|
+
def find!(params:, **options)
|
48
|
+
options['model'] = ::Article.find_by(id: params[:id])
|
49
|
+
end
|
50
|
+
|
51
|
+
def handle_not_found!(options)
|
52
|
+
options['result.response'] = Pragma::Operation::Response::NotFound.new
|
53
|
+
false
|
54
|
+
end
|
55
|
+
|
56
|
+
def authorize!(options)
|
57
|
+
options['result.authorization'] = options['model'].published? ||
|
58
|
+
options['model'].author == options['current_user']
|
59
|
+
end
|
60
|
+
|
61
|
+
def handle_unauthorized!(options)
|
62
|
+
options['result.response'] = Pragma::Operation::Response::Forbidden.new(
|
63
|
+
entity: Error.new(
|
64
|
+
error_type: :forbidden,
|
65
|
+
error_message: 'You can only access an article if published or authored by you.'
|
66
|
+
)
|
67
|
+
)
|
68
|
+
end
|
69
|
+
|
70
|
+
def respond!(options)
|
71
|
+
options['result.response'] = Pragma::Operation::Response::Ok.new(
|
72
|
+
entity: options['model'].as_json
|
73
|
+
)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
```
|
80
|
+
|
81
|
+
Yes, I know. This does not make any sense yet. Before continuing, I encourage you to read (and
|
82
|
+
understand!) the documentation of [Trailblazer::Operation](http://trailblazer.to/gems/operation/2.0/index.html).
|
83
|
+
Pragma::Operation is simply an extension of its TRB counterpart. For the rest of this guide, we will
|
84
|
+
assume you have a good understanding of TRB concepts like flow control and macros.
|
85
|
+
|
86
|
+
### Response basics
|
87
|
+
|
88
|
+
The only requirement for a Pragma operation is that it sets a `result.response` key in the options
|
89
|
+
hash by the end of its execution. This is a `Pragma::Operation::Response` object that will be used
|
90
|
+
by [pragma-rails](https://github.com/pragmarb/pragma-rails) or another integration to respond with
|
91
|
+
the proper HTTP information.
|
92
|
+
|
93
|
+
Responses have, just as you'd expect, a status, headers and body. You can manipulate them by using
|
94
|
+
the `status`, `headers` and `entity` parameters of the initializer:
|
95
|
+
|
96
|
+
```ruby
|
97
|
+
response = Pragma::Operation::Response.new(
|
98
|
+
status: 201,
|
99
|
+
headers: {
|
100
|
+
'X-Api-Custom' => 'Value'
|
101
|
+
},
|
102
|
+
entity: my_model
|
103
|
+
)
|
104
|
+
```
|
105
|
+
|
106
|
+
You can also set these properties through their accessors after instantiating the response:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
# You can set the status as a symbol:
|
110
|
+
response.status = :created
|
111
|
+
|
112
|
+
# You can set it as an HTTP status code:
|
113
|
+
response.status = 201
|
114
|
+
|
115
|
+
# You can manipulate headers:
|
116
|
+
response.headers['X-Api-Custom'] = 'Value'
|
117
|
+
|
118
|
+
# You can manipulate the entity:
|
119
|
+
response.entity = my_model
|
120
|
+
|
121
|
+
# The entity can be any object responding to #to_json:
|
122
|
+
response.entity = {
|
123
|
+
foo: :bar
|
124
|
+
}
|
125
|
+
```
|
126
|
+
|
127
|
+
### Decorating entities
|
128
|
+
|
129
|
+
The response class also has support for Pragma [decorators](https://github.com/pragmarb/pragma-decorator).
|
130
|
+
|
131
|
+
If you use decorators, you can set a decorator as the entity or you can use the `#decorate_with`
|
132
|
+
convenience method to decorate the existing entity:
|
133
|
+
|
134
|
+
```ruby
|
135
|
+
response.entity = ArticleDecorator.new(article)
|
136
|
+
|
137
|
+
# This is equivalent to the above:
|
138
|
+
response.entity = article
|
139
|
+
response.decorate_with(ArticleDecorator) # returns the response itself for chaining
|
140
|
+
```
|
141
|
+
|
142
|
+
### Errors
|
143
|
+
|
144
|
+
Pragma::Operation ships with an `Error` data structure that's simply the recommended way to present
|
145
|
+
your errors. You can build your custom error by creating a new instance of it and specify a
|
146
|
+
machine-readable error type and a human-readable error message:
|
147
|
+
|
148
|
+
```ruby
|
149
|
+
error = Pragma::Operation::Error.new(
|
150
|
+
error_type: :invalid_date,
|
151
|
+
error_message: 'You have specified an invalid date in your request.'
|
152
|
+
)
|
153
|
+
|
154
|
+
error.as_json # => {:error_type=>:invalid_date, :error_message=>"You have specified an invalid date in your request.", :meta=>{}}
|
155
|
+
error.to_json # => {"error_type":"invalid_date","error_message":"You have specified an invalid date in your request.","meta":{}}
|
156
|
+
```
|
157
|
+
|
158
|
+
Do you see that `meta` property in the JSON representation of the error? You can use it to include
|
159
|
+
additional metadata about the error. This is especially useful, for instance, with validation errors
|
160
|
+
as you can include the exact fields and validation messages (which is exactly what Pragma does by
|
161
|
+
default, by the way):
|
162
|
+
|
163
|
+
```ruby
|
164
|
+
error = Pragma::Operation::Error.new(
|
165
|
+
error_type: :invalid_date,
|
166
|
+
error_message: 'You have specified an invalid date in your request.',
|
167
|
+
meta: {
|
168
|
+
expected_format: 'YYYY-MM-DD'
|
169
|
+
}
|
170
|
+
)
|
171
|
+
|
172
|
+
error.as_json # => {:error_type=>:invalid_date, :error_message=>"You have specified an invalid date in your request.", :meta=>{:expected_format=>"YYYY-MM-DD"}}
|
173
|
+
error.to_json # => {"error_type":"invalid_date","error_message":"You have specified an invalid date in your request.","meta":{"expected_format":"YYYY-MM-DD"}}
|
174
|
+
```
|
175
|
+
|
176
|
+
If you don't want to go with this format, you are free to implement your own error class, but it is
|
177
|
+
not recommended, as the [built-in macros](https://github.com/pragmarb/pragma/tree/master/lib/pragma/operation/macro)
|
178
|
+
will use `Pragma::Operation::Error`.
|
179
|
+
|
180
|
+
### Built-in responses
|
181
|
+
|
182
|
+
Last but not least, as you have seen in the example operation, Pragma provides some
|
183
|
+
[built-in responses](https://github.com/pragmarb/pragma-operation/tree/master/lib/pragma/operation/response)
|
184
|
+
for common status codes and bodies. Some of these only have a status code while others (the error
|
185
|
+
responses) also have a default entity attached to them. For instance, you can use `Pragma::Operation::Response::Forbidden`
|
186
|
+
without specifying your own error type and message:
|
187
|
+
|
188
|
+
```ruby
|
189
|
+
response = Pragma::Operation::Response::Forbidden.new
|
190
|
+
|
191
|
+
response.status # => 403
|
192
|
+
response.entity.to_json # => {"error_type":"forbidden","error_message":"You are not authorized to access the requested resource.","meta":{}}
|
193
|
+
```
|
194
|
+
|
195
|
+
The built-in responses are not meant to be comprehensive and you will most likely have to implement
|
196
|
+
your own. If you write some that you think could be useful, feel free to open a PR!
|
36
197
|
|
37
198
|
## Contributing
|
38
199
|
|
data/lib/pragma/operation.rb
CHANGED
@@ -1,11 +1,21 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
-
|
2
|
+
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
require 'trailblazer/operation'
|
3
6
|
|
4
7
|
require 'pragma/operation/version'
|
5
|
-
require 'pragma/operation/authorization'
|
6
|
-
require 'pragma/operation/validation'
|
7
|
-
require 'pragma/operation/decoration'
|
8
8
|
require 'pragma/operation/base'
|
9
|
+
require 'pragma/operation/error'
|
10
|
+
|
11
|
+
require 'pragma/operation/response'
|
12
|
+
require 'pragma/operation/response/bad_request'
|
13
|
+
require 'pragma/operation/response/not_found'
|
14
|
+
require 'pragma/operation/response/forbidden'
|
15
|
+
require 'pragma/operation/response/unprocessable_entity'
|
16
|
+
require 'pragma/operation/response/created'
|
17
|
+
require 'pragma/operation/response/ok'
|
18
|
+
require 'pragma/operation/response/no_content'
|
9
19
|
|
10
20
|
module Pragma
|
11
21
|
# Operations provide business logic encapsulation for your JSON API.
|
@@ -1,86 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
|
+
|
2
3
|
module Pragma
|
3
4
|
module Operation
|
4
5
|
# This is the base class all your operations should extend.
|
5
6
|
#
|
6
7
|
# @author Alessandro Desantis
|
7
|
-
|
8
|
-
# @abstract Subclass and override {#call} to implement an operation.
|
9
|
-
class Base
|
10
|
-
include Interactor
|
11
|
-
|
12
|
-
include Authorization
|
13
|
-
include Validation
|
14
|
-
include Decoration
|
15
|
-
|
16
|
-
STATUSES = {
|
17
|
-
200 => :ok,
|
18
|
-
201 => :created,
|
19
|
-
202 => :accepted,
|
20
|
-
203 => :non_authoritative_information,
|
21
|
-
204 => :no_content,
|
22
|
-
205 => :reset_content,
|
23
|
-
206 => :partial_content,
|
24
|
-
207 => :multi_status,
|
25
|
-
208 => :already_reported,
|
26
|
-
300 => :multiple_choices,
|
27
|
-
301 => :moved_permanently,
|
28
|
-
302 => :found,
|
29
|
-
303 => :see_other,
|
30
|
-
304 => :not_modified,
|
31
|
-
305 => :use_proxy,
|
32
|
-
307 => :temporary_redirect,
|
33
|
-
400 => :bad_request,
|
34
|
-
401 => :unauthorized,
|
35
|
-
402 => :payment_required,
|
36
|
-
403 => :forbidden,
|
37
|
-
404 => :not_found,
|
38
|
-
405 => :method_not_allowed,
|
39
|
-
406 => :not_acceptable,
|
40
|
-
407 => :proxy_authentication_required,
|
41
|
-
408 => :request_timeout,
|
42
|
-
409 => :conflict,
|
43
|
-
410 => :gone,
|
44
|
-
411 => :length_required,
|
45
|
-
412 => :precondition_failed,
|
46
|
-
413 => :request_entity_too_large,
|
47
|
-
414 => :request_uri_too_large,
|
48
|
-
415 => :unsupported_media_type,
|
49
|
-
416 => :request_range_not_satisfiable,
|
50
|
-
417 => :expectation_failed,
|
51
|
-
418 => :im_a_teapot,
|
52
|
-
422 => :unprocessable_entity,
|
53
|
-
423 => :locked,
|
54
|
-
424 => :failed_dependency,
|
55
|
-
425 => :unordered_collection,
|
56
|
-
426 => :upgrade_required,
|
57
|
-
428 => :precondition_required,
|
58
|
-
429 => :too_many_requests,
|
59
|
-
431 => :request_header_fields_too_large,
|
60
|
-
449 => :retry_with,
|
61
|
-
500 => :internal_server_error,
|
62
|
-
501 => :not_implemented,
|
63
|
-
502 => :bad_gateway,
|
64
|
-
503 => :service_unavailable,
|
65
|
-
504 => :gateway_timeout,
|
66
|
-
505 => :http_version_not_supported,
|
67
|
-
506 => :variant_also_negotiates,
|
68
|
-
507 => :insufficient_storage,
|
69
|
-
509 => :bandwidth_limit_exceeded,
|
70
|
-
510 => :not_extended,
|
71
|
-
511 => :network_authentication_required
|
72
|
-
}.freeze
|
73
|
-
|
8
|
+
class Base < Trailblazer::Operation
|
74
9
|
class << self
|
75
|
-
def inherited(child)
|
76
|
-
child.class_eval do
|
77
|
-
before :setup_context
|
78
|
-
around :handle_halt
|
79
|
-
after :mark_result, :consolidate_status, :validate_status, :set_default_status
|
80
|
-
after :build_links
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
10
|
# Returns the name of this operation.
|
85
11
|
#
|
86
12
|
# For instance, if the operation is called +API::V1::Post::Operation::Create+, returns
|
@@ -96,177 +22,6 @@ module Pragma
|
|
96
22
|
.to_sym
|
97
23
|
end
|
98
24
|
end
|
99
|
-
|
100
|
-
# Runs the operation.
|
101
|
-
def call
|
102
|
-
fail NotImplementedError
|
103
|
-
end
|
104
|
-
|
105
|
-
protected
|
106
|
-
|
107
|
-
# Returns the params this operation is being run with.
|
108
|
-
#
|
109
|
-
# This is just a shortcut for +context.params+.
|
110
|
-
#
|
111
|
-
# @return [Hash]
|
112
|
-
def params
|
113
|
-
context.params
|
114
|
-
end
|
115
|
-
|
116
|
-
# Sets the status and resource to respond with.
|
117
|
-
#
|
118
|
-
# You can achieve the same result by setting +context.status+, +context.headers+ and
|
119
|
-
# +context.resource+ wherever you want in {#call}.
|
120
|
-
#
|
121
|
-
# Note that calling this method doesn't halt the execution of the operation and that this
|
122
|
-
# method can be called multiple times, overriding the previous context.
|
123
|
-
#
|
124
|
-
# @param status [Integer|Symbol] an HTTP status code
|
125
|
-
# @param resource [Object] an object responding to +#to_json+
|
126
|
-
# @param headers [Hash] HTTP headers
|
127
|
-
# @param links [Hash] links to use for building the +Link+ header
|
128
|
-
def respond_with(status: nil, resource: nil, headers: nil, links: nil)
|
129
|
-
context.status = status if status
|
130
|
-
context.resource = resource if resource
|
131
|
-
context.headers = headers if headers
|
132
|
-
context.links = links if links
|
133
|
-
end
|
134
|
-
|
135
|
-
# Same as {#respond_with}, but also halts the execution of the operation.
|
136
|
-
#
|
137
|
-
# @see #respond_with
|
138
|
-
def respond_with!(*args)
|
139
|
-
respond_with(*args)
|
140
|
-
fail Halt
|
141
|
-
end
|
142
|
-
|
143
|
-
# Sets the status to respond with.
|
144
|
-
#
|
145
|
-
# You can achieve the same result by setting +context.status+ wherever you want in {#call}.
|
146
|
-
#
|
147
|
-
# Note that calling this method doesn't halt the execution of the operation and that this
|
148
|
-
# method can be called multiple times, overriding the previous context.
|
149
|
-
#
|
150
|
-
# @param status [Integer|Symbol] an HTTP status code
|
151
|
-
def head(status)
|
152
|
-
context.status = status
|
153
|
-
end
|
154
|
-
|
155
|
-
# Same as {#head}, but also halts the execution of the operation.
|
156
|
-
#
|
157
|
-
# @param status [Integer|Symbol] an HTTP status code
|
158
|
-
#
|
159
|
-
# @see #head
|
160
|
-
def head!(status)
|
161
|
-
head status
|
162
|
-
fail Halt
|
163
|
-
end
|
164
|
-
|
165
|
-
# Returns the current user.
|
166
|
-
#
|
167
|
-
# This is just a shortcut for +context.current_user+.
|
168
|
-
#
|
169
|
-
# @return [Object]
|
170
|
-
def current_user
|
171
|
-
context.current_user
|
172
|
-
end
|
173
|
-
|
174
|
-
# Returns the headers we are responding with.
|
175
|
-
#
|
176
|
-
# This is just a shortcut for +context.headers+.
|
177
|
-
#
|
178
|
-
# @return [Hash]
|
179
|
-
def headers
|
180
|
-
context.headers
|
181
|
-
end
|
182
|
-
|
183
|
-
# Returns the links we are responding with.
|
184
|
-
#
|
185
|
-
# This is just a shotcut for +context.links+.
|
186
|
-
#
|
187
|
-
# @return [Hash]
|
188
|
-
def links
|
189
|
-
context.links
|
190
|
-
end
|
191
|
-
|
192
|
-
private
|
193
|
-
|
194
|
-
def with_hooks
|
195
|
-
# This overrides the default behavior, which is not to run after hooks if an exception is
|
196
|
-
# raised either in +#call+ or one of the before hooks. See:
|
197
|
-
# https://github.com/collectiveidea/interactor/blob/master/lib/interactor/hooks.rb#L210)
|
198
|
-
run_around_hooks do
|
199
|
-
begin
|
200
|
-
run_before_hooks
|
201
|
-
yield
|
202
|
-
ensure
|
203
|
-
run_after_hooks
|
204
|
-
end
|
205
|
-
end
|
206
|
-
end
|
207
|
-
|
208
|
-
def setup_context
|
209
|
-
context.params ||= {}
|
210
|
-
context.headers = {}
|
211
|
-
context.links = {}
|
212
|
-
end
|
213
|
-
|
214
|
-
def handle_halt(interactor)
|
215
|
-
interactor.call
|
216
|
-
rescue Halt # rubocop:disable Lint/HandleExceptions
|
217
|
-
end
|
218
|
-
|
219
|
-
def set_default_status
|
220
|
-
return if context.status
|
221
|
-
context.status = context.resource ? :ok : :no_content
|
222
|
-
end
|
223
|
-
|
224
|
-
def validate_status
|
225
|
-
if context.status.is_a?(Integer)
|
226
|
-
fail InvalidStatusError, context.status unless STATUSES.key?(context.status)
|
227
|
-
else
|
228
|
-
fail InvalidStatusError, context.status unless STATUSES.invert.key?(context.status.to_sym)
|
229
|
-
end
|
230
|
-
end
|
231
|
-
|
232
|
-
def consolidate_status
|
233
|
-
context.status = if context.status.is_a?(Integer)
|
234
|
-
STATUSES[context.status]
|
235
|
-
else
|
236
|
-
context.status.to_sym
|
237
|
-
end
|
238
|
-
end
|
239
|
-
|
240
|
-
def mark_result
|
241
|
-
return if /\A(2|3)\d{2}\z/ =~ STATUSES.invert[context.status].to_s
|
242
|
-
context.fail! pragma_operation_failure: true
|
243
|
-
end
|
244
|
-
|
245
|
-
def build_links
|
246
|
-
headers.delete('Link')
|
247
|
-
|
248
|
-
link_header = context.links.each_pair.select do |_relation, url|
|
249
|
-
url && !url.empty?
|
250
|
-
end.map do |relation, url|
|
251
|
-
%(<#{url}>; rel="#{relation}")
|
252
|
-
end.join(",\n ")
|
253
|
-
|
254
|
-
headers['Link'] = link_header unless link_header.empty?
|
255
|
-
end
|
256
|
-
end
|
257
|
-
|
258
|
-
Halt = Class.new(StandardError)
|
259
|
-
|
260
|
-
# This error is raised when an invalid status is set for an operation.
|
261
|
-
#
|
262
|
-
# @author Alessandro Desantis
|
263
|
-
class InvalidStatusError < StandardError
|
264
|
-
# Initializes the error.
|
265
|
-
#
|
266
|
-
# @param [Integer|Symbol] an invalid HTTP status code
|
267
|
-
def initialize(status)
|
268
|
-
super "'#{status}' is not a valid HTTP status code."
|
269
|
-
end
|
270
25
|
end
|
271
26
|
end
|
272
27
|
end
|