rom-http 0.7.0 → 0.8.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.
Files changed (46) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +26 -2
  3. data/LICENSE.txt +1 -1
  4. data/README.md +12 -15
  5. data/lib/rom-http.rb +2 -0
  6. data/lib/rom/http.rb +2 -0
  7. data/lib/rom/http/attribute.rb +10 -0
  8. data/lib/rom/http/commands.rb +2 -0
  9. data/lib/rom/http/commands/create.rb +2 -0
  10. data/lib/rom/http/commands/delete.rb +2 -0
  11. data/lib/rom/http/commands/update.rb +2 -0
  12. data/lib/rom/http/dataset.rb +152 -101
  13. data/lib/rom/http/error.rb +2 -0
  14. data/lib/rom/http/gateway.rb +44 -3
  15. data/lib/rom/http/handlers.rb +14 -0
  16. data/lib/rom/http/handlers/json.rb +65 -0
  17. data/lib/rom/http/mapper_compiler.rb +11 -0
  18. data/lib/rom/http/relation.rb +19 -64
  19. data/lib/rom/http/schema.rb +20 -0
  20. data/lib/rom/http/schema/dsl.rb +12 -0
  21. data/lib/rom/http/transformer.rb +2 -0
  22. data/lib/rom/http/types.rb +13 -0
  23. data/lib/rom/http/version.rb +3 -1
  24. metadata +32 -59
  25. data/.gitignore +0 -16
  26. data/.rspec +0 -3
  27. data/.rubocop.yml +0 -22
  28. data/.rubocop_todo.yml +0 -12
  29. data/.travis.yml +0 -20
  30. data/Gemfile +0 -24
  31. data/Rakefile +0 -24
  32. data/examples/repository_with_combine.rb +0 -154
  33. data/lib/rom/http/dataset/class_interface.rb +0 -33
  34. data/rakelib/rubocop.rake +0 -18
  35. data/rom-http.gemspec +0 -32
  36. data/spec/integration/abstract/commands/create_spec.rb +0 -119
  37. data/spec/integration/abstract/commands/delete_spec.rb +0 -52
  38. data/spec/integration/abstract/commands/update_spec.rb +0 -119
  39. data/spec/integration/abstract/relation_spec.rb +0 -78
  40. data/spec/shared/setup.rb +0 -18
  41. data/spec/shared/users_and_tasks.rb +0 -30
  42. data/spec/spec_helper.rb +0 -19
  43. data/spec/support/mutant.rb +0 -10
  44. data/spec/unit/rom/http/dataset_spec.rb +0 -824
  45. data/spec/unit/rom/http/gateway_spec.rb +0 -69
  46. data/spec/unit/rom/http/relation_spec.rb +0 -268
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5fd07f83166373c0e0f2bfe8c83f8f68578818dc
4
- data.tar.gz: e643299868045661adadfd6ea6881cf59feda4fe
2
+ SHA256:
3
+ metadata.gz: d88086baa2e0dccb6c7917f2e0a5ccf3ec268fa722f1648283b66ea9feb839e2
4
+ data.tar.gz: 3c4385179232ad49adc12214a00ecd690663828eaf1defb05d7af042a3e85d8e
5
5
  SHA512:
6
- metadata.gz: 2a2de5a39a3cf600c2464250809a2e292c7abef3a8e1b2e79232ac035b14992a160a56035c5c7a65414ab60b643093ac375e234ffab952c6a262f3517cd146fd
7
- data.tar.gz: 44315f4744aa86c60fb92c801bd1521e2834dbada29fd53a6cfc4a6f35f27fb09cb335719d9bb4b52be5f1bda9c549539f4e98c96fca2344640c2e7b0a096062
6
+ metadata.gz: 2a9ff4446983f2b2a996e8ea488b2f8212a16aaaeac1165244169c6d914df3bb8decdc8eb7ecef4c5afec98c0f29162d923dd288146770de8f10b4ff5335ba32
7
+ data.tar.gz: e0078237b342b0578adad5a8741fa80ad2798d9d5038d30eb4eef329e440486e028f98084ffac936948e1bb94d27552c81ecca56a83b98ec67980598e8a2761f
@@ -1,3 +1,29 @@
1
+ # v0.8.0 2019-04-29
2
+
3
+ This is a major overhaul of the gem which brings it closer to 1.0.0. Custom data mapping was replaced by core APIs that leverage schemas and their attributes. Custom `MapperCompiler` was added that rejects keys that are not specified in the schemas, and can be extended further to meet any future requirements.
4
+
5
+ As a consequence of these changes, with this release you can easily use `rom-http` along with repositories and changesets.
6
+
7
+ ### Added
8
+
9
+ - Support for relation schemas (solnic)
10
+ - Support for auto-struct mapping (solnic)
11
+ - Support for registering your own request/response handlers for all datasets from a specific gateway (solnic)
12
+ - Built-in `JSON` handlers that you can set via `handlers: :json` gateway option (solnic)
13
+ - Convenient request method predicates `Dataset#{get?,post?,delete?,put?}` (solnic)
14
+
15
+ ### Changed
16
+
17
+ - Updated to work with `rom ~> 5.0` (parndt)
18
+ - Input/output data are now handled by core functionality using schema's `input_schema` and `output_schema` (solnic)
19
+ - `Dataset#name` was removed in favor of `Dataset#base_path`
20
+
21
+ ### Fixed
22
+
23
+ - `Relation#append_path` no longer duplicates `base_path` (solnic)
24
+
25
+ [Compare v0.7.0...v0.8.0](https://github.com/rom-rb/rom-http/compare/v0.7.0...v0.8.0)
26
+
1
27
  # v0.7.0 2018-01-11
2
28
 
3
29
  ### Added
@@ -9,8 +35,6 @@
9
35
  - Removed ruby 2.1 support (maximderbin)
10
36
  - Removed rbx-3 support (maximderbin)
11
37
 
12
-
13
-
14
38
  [Compare v0.5.0...v0.6.0](https://github.com/rom-rb/rom-http/compare/v0.6.0...v0.7.0)
15
39
 
16
40
  # v0.6.0 2017-02-06
@@ -1,4 +1,4 @@
1
- Copyright (c) 2015 Ruby Object Mapper Team
1
+ Copyright (c) 2015-2019 rom-rb team
2
2
 
3
3
  MIT License
4
4
 
data/README.md CHANGED
@@ -1,21 +1,23 @@
1
1
  [gem]: https://rubygems.org/gems/rom-http
2
2
  [travis]: https://travis-ci.org/rom-rb/rom-http
3
- [gemnasium]: https://gemnasium.com/rom-rb/rom-http
4
3
  [codeclimate]: https://codeclimate.com/github/rom-rb/rom-http
5
4
  [inchpages]: http://inch-ci.org/github/rom-rb/rom-http
6
- [gitter]: https://gitter.im/rom-rb/chat
7
- [rom]: https://github.com/rom-rb/rom
5
+ [chat]: https://rom-rb.zulipchat.com
8
6
 
9
-
10
- # rom-http [![Gitter chat](https://badges.gitter.im/rom-rb/chat.svg)][gitter]
7
+ # rom-http [![Join the chat at https://rom-rb.zulipchat.com](https://img.shields.io/badge/rom--rb-join%20chat-942283.svg)][chat]
11
8
 
12
9
  [![Gem Version](https://badge.fury.io/rb/rom-http.svg)][gem]
13
10
  [![Build Status](https://travis-ci.org/rom-rb/rom-http.svg?branch=master)][travis]
14
- [![Dependency Status](https://gemnasium.com/rom-rb/rom-http.svg)][gemnasium]
15
11
  [![Code Climate](https://codeclimate.com/github/rom-rb/rom-http/badges/gpa.svg)][codeclimate]
16
- [![Documentation Status](http://inch-ci.org/github/rom-rb/rom-http.svg?branch=master&style=flat)][inchpages]
12
+ [![Test Coverage](https://codeclimate.com/github/rom-rb/rom-http/badges/coverage.svg)][codeclimate]
13
+ [![Inline docs](http://inch-ci.org/github/rom-rb/rom-http.svg?branch=master)][inchpages]
14
+
15
+ HTTP adapter for [rom-rb](https://github.com/rom-rb/rom).
16
+
17
+ Resources:
17
18
 
18
- HTTP adapter for [Ruby Object Mapper][rom]
19
+ - [User Documentation](http://rom-rb.org/learn/http/)
20
+ - [API Documentation](http://rubydoc.info/gems/rom-http)
19
21
 
20
22
  ## Installation
21
23
 
@@ -33,11 +35,6 @@ Or install it yourself as:
33
35
 
34
36
  $ gem install rom-http
35
37
 
36
- ## ROADMAP
37
-
38
- For details please refer to [issues](https://github.com/rom-rb/rom-http/issues).
39
-
40
-
41
38
  ## License
42
39
 
43
40
  See `LICENSE` file.
@@ -77,7 +74,7 @@ end
77
74
 
78
75
  class Users < ROM::Relation[:http]
79
76
  schema(:users) do
80
- attribute :id, ROM::Types::Int
77
+ attribute :id, ROM::Types::Integer
81
78
  attribute :name, ROM::Types::String
82
79
  attribute :username, ROM::Types::String
83
80
  attribute :email, ROM::Types::String
@@ -174,7 +171,7 @@ configuration = ROM::Configuration.new(:my_adapter, {
174
171
 
175
172
  class Users < ROM::Relation[:my_adapter]
176
173
  schema(:users) do
177
- attribute :id, ROM::Types::Int
174
+ attribute :id, ROM::Types::Integer
178
175
  attribute :name, ROM::Types::String
179
176
  attribute :username, ROM::Types::String
180
177
  attribute :email, ROM::Types::String
@@ -1 +1,3 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/http'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom'
2
4
  require 'rom/http/error'
3
5
  require 'rom/http/commands'
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rom/attribute'
4
+
5
+ module ROM
6
+ module HTTP
7
+ class Attribute < ROM::Attribute
8
+ end
9
+ end
10
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'rom/commands'
2
4
  require 'rom/http/commands/create'
3
5
  require 'rom/http/commands/update'
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module HTTP
3
5
  module Commands
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module HTTP
3
5
  module Commands
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ROM
2
4
  module HTTP
3
5
  module Commands
@@ -1,114 +1,194 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'uri'
2
- require 'dry-configurable'
4
+
5
+ require 'dry/configurable'
3
6
  require 'dry/core/deprecations'
7
+
8
+ require 'rom/support/memoizable'
9
+ require 'rom/constants'
4
10
  require 'rom/initializer'
5
- require 'rom/http/dataset/class_interface'
11
+ require 'rom/http/types'
12
+ require 'rom/http/transformer'
6
13
 
7
14
  module ROM
8
15
  module HTTP
9
16
  # HTTP Dataset
10
17
  #
11
- # Represents a specific HTTP collection resource
18
+ # Represents a specific HTTP collection resource. This class can be
19
+ # subclassed in a specialized HTTP adapter to provide its own
20
+ # response/request handlers or any other configuration that should
21
+ # differ from the defaults.
12
22
  #
13
23
  # @api public
14
24
  class Dataset
15
25
  PATH_SEPARATOR = '/'.freeze
16
26
 
17
- extend ::ROM::Initializer
18
- extend ::Dry::Configurable
19
- extend ::ROM::HTTP::Dataset::ClassInterface
20
- include ::Enumerable
21
- include ::Dry::Equalizer(:config, :options)
22
-
23
- setting :default_request_handler
24
- setting :default_response_handler
25
- setting :param_encoder, ->(params) { URI.encode_www_form(params) }
26
-
27
- param :config
28
-
29
- option :request_method, type: Types::Symbol, default: proc { :get }, reader: true
30
- option :base_path, type: Types::String, default: proc { name }
31
- option :path, type: Types::String, default: proc { '' }, reader: false
32
- option :params, type: Types::Hash, default: proc { {} }, reader: true
33
- option :headers, type: Types::Hash, default: proc { {} }
34
-
35
- # Return the gateway's URI
36
- #
37
- # @return [String]
38
- #
39
- # @raise [Error] if the configuration does not contain a URI
27
+ extend Dry::Configurable
28
+ extend ROM::Initializer
29
+
30
+ include ROM::Memoizable
31
+ include Enumerable
32
+ include Dry::Equalizer(:options)
33
+
34
+ # @!method self.default_request_handler
35
+ # Return configured default request handler
36
+ #
37
+ # @example
38
+ # class MyDataset < ROM::HTTP::Dataset
39
+ # configure do |config|
40
+ # config.default_request_handler = MyRequestHandler
41
+ # end
42
+ # end
43
+ #
44
+ # MyDataset.default_request_handler # MyRequestHandler
45
+ # MyDataset.new(uri: "http://localhost").request_handler # MyRequestHandler
46
+ setting :default_request_handler, reader: true
47
+
48
+ # @!method self.default_response_handler
49
+ # Return configured default response handler
50
+ #
51
+ # @example
52
+ # class MyDataset < ROM::HTTP::Dataset
53
+ # configure do |config|
54
+ # config.default_response_handler = MyResponseHandler
55
+ # end
56
+ # end
57
+ #
58
+ # MyDataset.default_response_handler # MyResponseHandler
59
+ # MyDataset.new(uri: "http://localhost").response_handler # MyResponseHandler
60
+ setting :default_response_handler, reader: true
61
+
62
+ # @!method self.param_encoder
63
+ # Return configured param encoder
64
+ #
65
+ # @example
66
+ # class MyDataset < ROM::HTTP::Dataset
67
+ # configure do |config|
68
+ # config.param_encoder = MyParamEncoder
69
+ # end
70
+ # end
71
+ #
72
+ # MyDataset.param_encoder # MyParamEncoder
73
+ # MyDataset.new(uri: "http://localhost").param_encoder # MyParamEncoder
74
+ setting :param_encoder, URI.method(:encode_www_form), reader: true
75
+
76
+ # @!attribute [r] request_handler
77
+ # @return [Object]
78
+ # @api public
79
+ option :request_handler, default: proc { self.class.default_request_handler }
80
+
81
+ # @!attribute [r] response_handler
82
+ # @return [Object]
83
+ # @api public
84
+ option :response_handler, default: proc { self.class.default_response_handler }
85
+
86
+ # @!attribute [r] request_method
87
+ # @return [Symbol]
88
+ # @api public
89
+ option :request_method, type: Types::Symbol, default: proc { :get }
90
+
91
+ # @!attribute [r] base_path
92
+ # @return [String]
93
+ # @api public
94
+ option :base_path, type: Types::Path, default: proc { EMPTY_STRING }
95
+
96
+ # @!attribute [r] path
97
+ # @return [String]
98
+ # @api public
99
+ option :path, type: Types::Path, default: proc { EMPTY_STRING }
100
+
101
+ # @!attribute [r] params
102
+ # @return [Hash]
103
+ # @api public
104
+ option :params, type: Types::Hash, default: proc { EMPTY_HASH }
105
+
106
+ # @!attribute [r] headers
107
+ # @return [Hash]
108
+ # @api public
109
+ option :headers, type: Types::Hash, default: proc { EMPTY_HASH }
110
+
111
+ # @!attribute [r] headers
112
+ # @return [Hash]
113
+ # @api public
114
+ option :param_encoder, default: proc { self.class.param_encoder }
115
+
116
+ # @!attribute [r] uri
117
+ # @return [String]
118
+ # @api public
119
+ option :uri, type: Types::String
120
+
121
+ # Return the dataset's URI
122
+ #
123
+ # @return [URI::HTTP]
40
124
  #
41
125
  # @api public
42
126
  def uri
43
- uri = config.fetch(:uri) { fail Error, '+uri+ configuration missing' }
44
- uri = URI(join_path(uri, path))
45
- if request_method == :get && params.any?
46
- uri.query = self.class.config.param_encoder.call(params)
127
+ uri = URI(join_path(super, path))
128
+
129
+ if get? && params.any?
130
+ uri.query = param_encoder.call(params)
47
131
  end
48
132
 
49
133
  uri
50
134
  end
51
135
 
52
- # Return request headers
136
+ # Return true if request method is set to :get
53
137
  #
54
- # Merges default headers from the Gateway configuration and the
55
- # current Dataset
138
+ # @return [Boolean]
56
139
  #
57
- # @example
58
- # config = { Accepts: 'application/json' }
59
- # users = Dataset.new(config, headers: { 'Cache-Control': 'no-cache' }
60
- # users.headers
61
- # # => {:Accepts => "application/json", :'Cache-Control' => 'no-cache'}
140
+ # @api public
141
+ def get?
142
+ request_method.equal?(:get)
143
+ end
144
+
145
+ # Return true if request method is set to :post
62
146
  #
63
- # @return [Hash]
147
+ # @return [Boolean]
64
148
  #
65
149
  # @api public
66
- def headers
67
- config.fetch(:headers, {}).merge(options.fetch(:headers, {}))
150
+ def post?
151
+ request_method.equal?(:post)
68
152
  end
69
153
 
70
- # Return the dataset name
154
+ # Return true if request method is set to :put
71
155
  #
72
- # @return [String]
156
+ # @return [Boolean]
73
157
  #
74
158
  # @api public
75
- def name
76
- config[:name].to_s
159
+ def put?
160
+ request_method.equal?(:put)
77
161
  end
78
162
 
79
- # Return the base path
80
- #
81
- # @example
82
- # Dataset.new(config, base_path: '/users').base_path
83
- # # => 'users'
163
+ # Return true if request method is set to :delete
84
164
  #
85
- # @return [String] the dataset path, without a leading slash
165
+ # @return [Boolean]
86
166
  #
87
167
  # @api public
88
- def base_path
89
- strip_path(super)
168
+ def delete?
169
+ request_method.equal?(:delete)
90
170
  end
91
171
 
92
172
  # Return the dataset path
93
173
  #
94
174
  # @example
95
- # Dataset.new(config, path: '/users').path
175
+ # Dataset.new(path: '/users').path
96
176
  # # => 'users'
97
177
  #
98
178
  # @return [String] the dataset path, without a leading slash
99
179
  #
100
180
  # @api public
101
181
  def path
102
- join_path(base_path, strip_path(options[:path].to_s))
182
+ join_path(base_path, super)
103
183
  end
104
184
 
105
185
  # Return the dataset path
106
186
  #
107
187
  # @example
108
- # Dataset.new(config, path: '/users').path
188
+ # Dataset.new(path: '/users').path
109
189
  # # => '/users'
110
190
  #
111
- # @return [string] the dataset path, with leading slash
191
+ # @return [String] the dataset path, with leading slash
112
192
  #
113
193
  # @api public
114
194
  def absolute_path
@@ -123,7 +203,7 @@ module ROM
123
203
  # To non-destructively add a new header, use `#add_header`
124
204
  #
125
205
  # @example
126
- # users = Dataset.new(config, headers: { Accept: 'application/json' })
206
+ # users = Dataset.new(headers: { Accept: 'application/json' })
127
207
  # users.with_headers(:'X-Api-Key' => '1234').headers
128
208
  # # => { :'X-Api-Key' => '1234' }
129
209
  #
@@ -131,7 +211,7 @@ module ROM
131
211
  #
132
212
  # @api public
133
213
  def with_headers(headers)
134
- __new__(config, options.merge(headers: headers))
214
+ with_options(headers: headers)
135
215
  end
136
216
 
137
217
  # Return a new dataset with additional header
@@ -140,7 +220,7 @@ module ROM
140
220
  # @param value [String] the header value
141
221
  #
142
222
  # @example
143
- # users = Dataset.new(config, headers: { Accept: 'application/json' })
223
+ # users = Dataset.new(headers: { Accept: 'application/json' })
144
224
  # users.add_header(:'X-Api-Key', '1234').headers
145
225
  # # => { :Accept => 'application/json', :'X-Api-Key' => '1234' }
146
226
  #
@@ -159,7 +239,7 @@ module ROM
159
239
  #
160
240
  # @api public
161
241
  def with_options(opts)
162
- __new__(config, options.merge(opts))
242
+ __new__(options.merge(opts))
163
243
  end
164
244
 
165
245
  # Return a new dataset with a different base path
@@ -204,7 +284,7 @@ module ROM
204
284
  #
205
285
  # @api public
206
286
  def append_path(append_path)
207
- with_options(path: join_path(path, append_path))
287
+ with_path(join_path(options[:path], append_path))
208
288
  end
209
289
 
210
290
  # Return a new dataset with a different request method
@@ -226,7 +306,7 @@ module ROM
226
306
  # @param [Hash] params the new request parameters
227
307
  #
228
308
  # @example
229
- # users = Dataset.new(config, params: { uid: 33 })
309
+ # users = Dataset.new(params: { uid: 33 })
230
310
  # users.with_params(login: 'jdoe').params
231
311
  # # => { :login => 'jdoe' }
232
312
  #
@@ -242,7 +322,7 @@ module ROM
242
322
  # @param [Hash] params the new request parameters to add
243
323
  #
244
324
  # @example
245
- # users = Dataset.new(config, params: { uid: 33 })
325
+ # users = Dataset.new(params: { uid: 33 })
246
326
  # users.add_params(login: 'jdoe').params
247
327
  # # => { uid: 33, :login => 'jdoe' }
248
328
  #
@@ -250,10 +330,7 @@ module ROM
250
330
  #
251
331
  # @api public
252
332
  def add_params(new_params)
253
- # TODO: Should we merge arrays?
254
- with_options(
255
- params: ::ROM::HTTP::Transformer[:deep_merge][params, new_params]
256
- )
333
+ with_options(params: ::ROM::HTTP::Transformer[:deep_merge][params, new_params])
257
334
  end
258
335
 
259
336
  # Iterate over each response value
@@ -277,10 +354,7 @@ module ROM
277
354
  #
278
355
  # @api public
279
356
  def insert(params)
280
- with_options(
281
- request_method: :post,
282
- params: params
283
- ).response
357
+ with_options(request_method: :post, params: params).response
284
358
  end
285
359
 
286
360
  # Perform an update over HTTP Put
@@ -291,10 +365,7 @@ module ROM
291
365
  #
292
366
  # @api public
293
367
  def update(params)
294
- with_options(
295
- request_method: :put,
296
- params: params
297
- ).response
368
+ with_options(request_method: :put, params: params).response
298
369
  end
299
370
 
300
371
  # Perform an delete over HTTP Delete
@@ -304,9 +375,7 @@ module ROM
304
375
  #
305
376
  # @api public
306
377
  def delete
307
- with_options(
308
- request_method: :delete
309
- ).response
378
+ with_options(request_method: :delete).response
310
379
  end
311
380
 
312
381
  # Execute the current dataset
@@ -318,37 +387,19 @@ module ROM
318
387
  response_handler.call(request_handler.call(self), self)
319
388
  end
320
389
 
321
- private
322
-
323
- def response_handler
324
- response_handler = config.fetch(
325
- :response_handler,
326
- self.class.config.default_response_handler
327
- )
328
- fail Error, '+default_response_handler+ configuration missing' if response_handler.nil?
329
- response_handler
330
- end
390
+ memoize :uri, :absolute_path
331
391
 
332
- def request_handler
333
- request_handler = config.fetch(
334
- :request_handler,
335
- self.class.config.default_request_handler
336
- )
337
- fail Error, '+default_response_handler+ configuration missing' if request_handler.nil?
338
- request_handler
339
- end
392
+ private
340
393
 
394
+ # @api private
341
395
  def __new__(*args, &block)
342
396
  self.class.new(*args, &block)
343
397
  end
344
398
 
399
+ # @api private
345
400
  def join_path(*paths)
346
401
  paths.reject(&:empty?).join(PATH_SEPARATOR)
347
402
  end
348
-
349
- def strip_path(path)
350
- path.sub(%r{\A/}, '')
351
- end
352
403
  end
353
404
  end
354
405
  end