rom-http 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
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