pragma-operation 1.1.1 → 1.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3721ff3095c7adde870e9d879340f12d1aed3a90
4
- data.tar.gz: 5a10bdd0eaeb67e0a3457fc2881eaef1b01eedcc
3
+ metadata.gz: 4af59fe5624f0ad6a25a16d2f3a34e6b4b24f7cc
4
+ data.tar.gz: 385279725b2ebbc9210535c274de95060025a4cd
5
5
  SHA512:
6
- metadata.gz: dfc63fb63cebc9737babfcdf3ee1bb831911c44460407432d23d3d173fb2f58743bd8cd6f15cf469c1d6b4c746c218a5cdf6e9a14205f1888f5582a01029c87f
7
- data.tar.gz: 42b1fca36ece4067a9e31674edacdbf3b0c99560c4088961c990ef82e81f860bfe9f91f7087d47e3edc435dd7b0e5fee8bd6ab3540c284ce8cc002af6671f83a
6
+ metadata.gz: 11d83a8c30d72bca7e25e86239d6170f6687bdc5113e0a0775feabc979ca9e8e743dbd30af3dc68607a37eb7eaaeb586302b33e641d48ad8359700e23fa614aa
7
+ data.tar.gz: 3900e69be07c8310c89955a858d100bef14132d47e87cddcd1eab83dc6e70176d152c4a05a568dbf3ccdb178f93b60c2045cddb4956d7ddc526aa93e2d99ed29
@@ -12,10 +12,7 @@ module API
12
12
  # The `status` parameter is optional (the default is `:ok`).
13
13
  respond_with(
14
14
  status: :ok,
15
- resource: { pong: params[:pong] },
16
- headers: {
17
- 'X-Ping-Time' => Time.now.to_i
18
- }
15
+ resource: { pong: params[:pong] }
19
16
  )
20
17
  end
21
18
  end
@@ -32,7 +29,6 @@ result = API::V1::Ping::Operation::Create.call(params: { pong: 'HELLO' })
32
29
 
33
30
  result.status # => :ok
34
31
  result.resource # => { pong: 'HELLO' }
35
- result.headers # => { 'X-Ping-Time' => 1482927872 }
36
32
  ```
37
33
 
38
34
  As you can see, an operation takes parameters as input and responds with:
@@ -62,6 +58,121 @@ Since Pragma::Operation is built on top of [Interactor](https://github.com/colle
62
58
  you should consult its documentation for the basic usage of operations; the rest of this section
63
59
  only covers the features provided specifically by Pragma::Operation.
64
60
 
61
+ ## Headers
62
+
63
+ You can attach headers to your response by manipulating the `headers` hash:
64
+
65
+ ```ruby
66
+ module API
67
+ module V1
68
+ module Post
69
+ module Operation
70
+ class Create < Pragma::Operation::Base
71
+ def call
72
+ post = ::Post.new(params)
73
+ post.save!
74
+
75
+ headers['X-Post-Id'] = post.id
76
+
77
+ respond_with status: :created, resource: post
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
83
+ end
84
+ ```
85
+
86
+ You can also set headers when calling `#respond_with`:
87
+
88
+ ```ruby
89
+ module API
90
+ module V1
91
+ module Post
92
+ module Operation
93
+ class Create < Pragma::Operation::Base
94
+ def call
95
+ post = ::Post.new(params)
96
+ post.save!
97
+
98
+ respond_with status: :created, resource: post, headers: {
99
+ 'X-Post-Id' => post.id
100
+ }
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end
106
+ end
107
+ ```
108
+
109
+ ## HATEOAS
110
+
111
+ Pragma::Operation supports HATEOAS by allowing you to specify a list of links to use for building
112
+ the `Link` header. You can set the links by manipulating the `links` hash.
113
+
114
+ For instance, here's how you could link to a post's comments and author:
115
+
116
+ ```ruby
117
+ module API
118
+ module V1
119
+ module Post
120
+ module Operation
121
+ class Show < Pragma::Operation::Base
122
+ def call
123
+ post = ::Post.find(params[:id])
124
+
125
+ links['comments'] = "/posts/#{post.id}/comments"
126
+ links['author'] = "/users/#{post.author.id}"
127
+
128
+ respond_with resource: post
129
+ end
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
135
+ ```
136
+
137
+ You can also set the links when calling `#respond_with`:
138
+
139
+ ```ruby
140
+ module API
141
+ module V1
142
+ module Post
143
+ module Operation
144
+ class Show < Pragma::Operation::Base
145
+ def call
146
+ post = ::Post.find(params[:id])
147
+
148
+ respond_with resource: post, links: {
149
+ comments: "/posts/#{post.id}/comments",
150
+ author: "/users/#{post.author.id}"
151
+ }
152
+ end
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
158
+ ```
159
+
160
+ This will build the `Link` header accordingly:
161
+
162
+ ```ruby
163
+ result = API::V1::Post::Operation::Show.call(params: { id: 1 })
164
+
165
+ result.status # => :ok
166
+ result.headers
167
+ # => {
168
+ # 'Link' => '</posts/1/comments>; rel="comments",
169
+ # </users/49>; rel="author"'
170
+ # }
171
+ ```
172
+
173
+ **Note: Do not set the `Link` header manually, as it will be replaced when building links from the
174
+ `links` hash.**
175
+
65
176
  ## Handling errors
66
177
 
67
178
  You can use the `#success?` and `#failure?` method to check whether an operation was successful. An
@@ -77,6 +77,7 @@ module Pragma
77
77
  before :setup_context
78
78
  around :handle_halt
79
79
  after :mark_result, :consolidate_status, :validate_status, :set_default_status
80
+ after :build_links
80
81
  end
81
82
  end
82
83
 
@@ -121,19 +122,21 @@ module Pragma
121
122
  # method can be called multiple times, overriding the previous context.
122
123
  #
123
124
  # @param status [Integer|Symbol] an HTTP status code
124
- # @param headers [Hash] HTTP headers
125
125
  # @param resource [Object] an object responding to +#to_json+
126
- def respond_with(status: nil, headers: {}, resource: nil)
127
- context.status = status
128
- context.headers = headers.to_h
129
- context.resource = resource
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
130
133
  end
131
134
 
132
135
  # Same as {#respond_with}, but also halts the execution of the operation.
133
136
  #
134
137
  # @see #respond_with
135
138
  def respond_with!(*args)
136
- respond_with *args
139
+ respond_with(*args)
137
140
  fail Halt
138
141
  end
139
142
 
@@ -168,6 +171,24 @@ module Pragma
168
171
  context.current_user
169
172
  end
170
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
+
171
192
  private
172
193
 
173
194
  def with_hooks
@@ -187,6 +208,7 @@ module Pragma
187
208
  def setup_context
188
209
  context.params ||= {}
189
210
  context.headers = {}
211
+ context.links = {}
190
212
  end
191
213
 
192
214
  def handle_halt(interactor)
@@ -219,6 +241,18 @@ module Pragma
219
241
  return if /\A(2|3)\d{2}\z/ =~ STATUSES.invert[context.status].to_s
220
242
  context.fail!
221
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
222
256
  end
223
257
 
224
258
  Halt = Class.new(StandardError)
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
  module Pragma
3
3
  module Operation
4
- VERSION = '1.1.1'
4
+ VERSION = '1.2.0'
5
5
  end
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pragma-operation
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alessandro Desantis