rom-http 0.5.0 → 0.6.0.rc1

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: 940c73eee8219c8b80aa2ec5914549291be5e99d
4
- data.tar.gz: 0e852b96a95edb10991aefa9d5bcffa78e5211b4
3
+ metadata.gz: e61d4626a34ea84673dc4632b6f1bbdd668e0b8c
4
+ data.tar.gz: 859e9539a8127ad7c8b7cc2fd6d0904cb734e404
5
5
  SHA512:
6
- metadata.gz: 3db2d4a662ae4ae76138119946b71253e36e0be5653fda789a17083f6915f7630357b0680e297690c56fab85da363ffaee1cc03412d568b2c16bd61421f080db
7
- data.tar.gz: 8738e9b3524917c5e74535a2ae6b1ef9e23c82a98871fac7079f507321e40da86185a811f54f1b47468b9b6a56a485891c86f5377f15d13c7246f86703c41fbb
6
+ metadata.gz: fd492be4654b12a16194c74f2e0ae3db44f4a456b6d8edf85ead74a8f3581ac3bca6bd2d436a67370502c10d416f75692388832249a0e2e53dc4dc05b238cca6
7
+ data.tar.gz: 666263147a7e6f534d602d6aa904ae8ed28a35c2307799bd871b8312276419750457d5dce5d92218b0c578cf7c7e3f3bea06a4e0b85b034944d2345a28728131
@@ -1,24 +1,21 @@
1
1
  language: ruby
2
- sudo: false
2
+ dist: trusty
3
+ sudo: required
3
4
  cache: bundler
4
5
  bundler_args: --without sql benchmarks console tools
5
6
  script: "bundle exec rake ci"
6
7
  rvm:
7
- - 2.1
8
- - 2.2
9
- - 2.3.1
10
- - rbx-2
11
- - jruby-9.0.5.0
12
- - ruby-head
13
- - jruby-head
8
+ - 2.2.6
9
+ - 2.3.3
10
+ - 2.4.0
11
+ - rbx-3
12
+ - jruby-9.1.6.0
14
13
  env:
15
14
  global:
16
15
  - JRUBY_OPTS='--dev -J-Xmx1024M'
17
16
  matrix:
18
17
  allow_failures:
19
- - rvm: ruby-head
20
- - rvm: jruby-head
21
- - rvm: rbx-2
18
+ - rvm: rbx-3
22
19
  notifications:
23
20
  webhooks:
24
21
  urls:
@@ -1,3 +1,16 @@
1
+ # v0.6.0 to-be-released
2
+ # Changed
3
+ - Make schemas mandatory and use schema API from ROM core (AMHOL)
4
+ - Generate transformer using schema (AMHOL)
5
+ - Removed rbx-2 support (solnic)
6
+ - Add Dataset#base_path and Relation#primary_key (AMHOL)
7
+ - Updated to work with ROM 3.0.beta (maximderbin)
8
+ - Removed ruby 2.1 support (maximderbin)
9
+ - Fix Relation#primary_key when schema defines alias (AMHOL)
10
+ - Transform keys on insert and update (maximderbin)
11
+
12
+ [Compare v0.5.0...v0.6.0](https://github.com/rom-rb/rom-http/compare/v0.5.0...master)
13
+
1
14
  # v0.5.0 2016-08-08
2
15
  ### Changed
3
16
  - Removed ruby 2.0 support
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [rom]: https://github.com/rom-rb/rom
8
8
 
9
9
 
10
- # ROM-http [![Gitter chat](https://badges.gitter.im/rom-rb/chat.svg)][gitter]
10
+ # rom-http [![Gitter chat](https://badges.gitter.im/rom-rb/chat.svg)][gitter]
11
11
 
12
12
  [![Gem Version](https://badge.fury.io/rb/rom-http.svg)][gem]
13
13
  [![Build Status](https://travis-ci.org/rom-rb/rom-http.svg?branch=master)][travis]
@@ -45,6 +45,7 @@ See `LICENSE` file.
45
45
  ## Synopsis
46
46
 
47
47
  ```ruby
48
+ require 'inflecto'
48
49
  require 'json'
49
50
  require 'uri'
50
51
  require 'net/http'
@@ -52,11 +53,11 @@ require 'net/http'
52
53
  class RequestHandler
53
54
  def call(dataset)
54
55
  uri = URI(dataset.uri)
55
- uri.path = "/#{dataset.name}/#{dataset.path}"
56
+ uri.path = dataset.absolute_path
56
57
  uri.query = URI.encode_www_form(dataset.params)
57
58
 
58
59
  http = Net::HTTP.new(uri.host, uri.port)
59
- request_klass = Net::HTTP.const_get(ROM::Inflector.classify(dataset.request_method))
60
+ request_klass = Net::HTTP.const_get(Inflecto.classify(dataset.request_method))
60
61
 
61
62
  request = request_klass.new(uri.request_uri)
62
63
  dataset.headers.each_with_object(request) do |(header, value), request|
@@ -69,23 +70,22 @@ end
69
70
 
70
71
  class ResponseHandler
71
72
  def call(response, dataset)
72
- Array([JSON.parse(response.body)]).flatten
73
+ if %i(post put patch).include?(dataset.request_method)
74
+ JSON.parse(response.body, symbolize_names: true)
75
+ else
76
+ Array([JSON.parse(response.body, symbolize_names: true)]).flatten
77
+ end
73
78
  end
74
79
  end
75
80
 
76
81
  class Users < ROM::Relation[:http]
77
- dataset :users
78
-
79
- # You can also define a schema block
80
- # which will use dry-types' Dry::Types['hash']
81
- # coercion to pre-process your data
82
- schema do
83
- attribute 'id', 'strict.int'
84
- attribute 'name', 'strict.string'
85
- attribute 'username', 'strict.string'
86
- attribute 'email', 'strict.string'
87
- attribute 'phone', 'strict.string'
88
- attribute 'website', 'strict.string'
82
+ schema(:users) do
83
+ attribute :id, ROM::Types::Int
84
+ attribute :name, ROM::Types::String
85
+ attribute :username, ROM::Types::String
86
+ attribute :email, ROM::Types::String
87
+ attribute :phone, ROM::Types::String
88
+ attribute :website, ROM::Types::String
89
89
  end
90
90
 
91
91
  def by_id(id)
@@ -111,6 +111,7 @@ container.relation(:users).by_id(1).to_a
111
111
  ### Extending
112
112
 
113
113
  ```ruby
114
+ require 'inflecto'
114
115
  require 'json'
115
116
  require 'uri'
116
117
  require 'net/http'
@@ -120,11 +121,11 @@ module ROM
120
121
  class Dataset < ROM::HTTP::Dataset
121
122
  default_request_handler ->(dataset) do
122
123
  uri = URI(dataset.uri)
123
- uri.path = "/#{dataset.name}/#{dataset.path}"
124
+ uri.path = dataset.absolute_path
124
125
  uri.query = URI.encode_www_form(dataset.params)
125
126
 
126
127
  http = Net::HTTP.new(uri.host, uri.port)
127
- request_klass = Net::HTTP.const_get(ROM::Inflector.classify(dataset.request_method))
128
+ request_klass = Net::HTTP.const_get(Inflecto.classify(dataset.request_method))
128
129
 
129
130
  request = request_klass.new(uri.request_uri)
130
131
  dataset.headers.each_with_object(request) do |(header, value), request|
@@ -135,7 +136,11 @@ module ROM
135
136
  end
136
137
 
137
138
  default_response_handler ->(response, dataset) do
138
- Array([JSON.parse(response.body)]).flatten
139
+ if %i(post put patch).include?(dataset.request_method)
140
+ JSON.parse(response.body, symbolize_names: true)
141
+ else
142
+ Array([JSON.parse(response.body, symbolize_names: true)]).flatten
143
+ end
139
144
  end
140
145
  end
141
146
 
@@ -171,7 +176,14 @@ configuration = ROM::Configuration.new(:my_adapter, {
171
176
  })
172
177
 
173
178
  class Users < ROM::Relation[:my_adapter]
174
- dataset :users
179
+ schema(:users) do
180
+ attribute :id, ROM::Types::Int
181
+ attribute :name, ROM::Types::String
182
+ attribute :username, ROM::Types::String
183
+ attribute :email, ROM::Types::String
184
+ attribute :phone, ROM::Types::String
185
+ attribute :website, ROM::Types::String
186
+ end
175
187
 
176
188
  def by_id(id)
177
189
  with_path(id.to_s)
@@ -0,0 +1,154 @@
1
+ require 'inflecto'
2
+ require 'json'
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'rom-repository'
6
+
7
+ class RequestHandler
8
+ def call(dataset)
9
+ uri = URI(dataset.uri)
10
+ uri.path = dataset.absolute_path
11
+ uri.query = URI.encode_www_form(dataset.params)
12
+
13
+ http = Net::HTTP.new(uri.host, uri.port)
14
+ request_klass = Net::HTTP.const_get(Inflecto.classify(dataset.request_method))
15
+
16
+ request = request_klass.new(uri.request_uri)
17
+ dataset.headers.each_with_object(request) do |(header, value), request|
18
+ request[header.to_s] = value
19
+ end
20
+
21
+ response = http.request(request)
22
+ end
23
+ end
24
+
25
+ class ResponseHandler
26
+ def call(response, dataset)
27
+ if %i(post put patch).include?(dataset.request_method)
28
+ JSON.parse(response.body, symbolize_names: true)
29
+ else
30
+ Array([JSON.parse(response.body, symbolize_names: true)]).flatten
31
+ end
32
+ end
33
+ end
34
+
35
+ class Users < ROM::Relation[:http]
36
+ schema(:users) do
37
+ attribute :id, ROM::Types::Int.meta(primary_key: true)
38
+ attribute :name, ROM::Types::String
39
+ attribute :username, ROM::Types::String
40
+ attribute :email, ROM::Types::String
41
+ attribute :phone, ROM::Types::String
42
+ attribute :website, ROM::Types::String
43
+ end
44
+
45
+ def by_id(id)
46
+ with_path(id.to_s)
47
+ end
48
+ end
49
+
50
+ class Posts < ROM::Relation[:http]
51
+ schema(:posts) do
52
+ attribute :id, ROM::Types::Int.meta(primary_key: true)
53
+ attribute :userId, ROM::Types::Int.meta(alias: :user_id)
54
+ attribute :title, ROM::Types::String
55
+ attribute :body, ROM::Types::String
56
+ end
57
+
58
+ def by_id(id)
59
+ with_path(id.to_s)
60
+ end
61
+
62
+ def for_user(user)
63
+ with_options(
64
+ base_path: 'users',
65
+ path: "#{user.first[:id]}/posts"
66
+ )
67
+ end
68
+ end
69
+
70
+ class UserRepository < ROM::Repository[:users]
71
+ relations :posts
72
+
73
+ def find(id)
74
+ users.by_id(id).first
75
+ end
76
+
77
+ def find_with_posts(user_id)
78
+ users.by_id(user_id).combine_children(many: posts.for_user).first
79
+ end
80
+ end
81
+
82
+ configuration = ROM::Configuration.new(:http, {
83
+ uri: 'http://jsonplaceholder.typicode.com',
84
+ headers: {
85
+ Accept: 'application/json'
86
+ },
87
+ request_handler: RequestHandler.new,
88
+ response_handler: ResponseHandler.new
89
+ })
90
+ configuration.register_relation(Users)
91
+ configuration.register_relation(Posts)
92
+ container = ROM.container(configuration)
93
+
94
+ UserRepository.new(container).find_with_posts(1)
95
+ # =>
96
+ # #<ROM::Struct[User]
97
+ # id=1
98
+ # name="Leanne Graham"
99
+ # username="Bret"
100
+ # email="Sincere@april.biz"
101
+ # phone="1-770-736-8031 x56442"
102
+ # website="hildegard.org"
103
+ # posts=[
104
+ # #<ROM::Struct[Post]
105
+ # id=1
106
+ # user_id=1
107
+ # title="sunt aut facere repellat provident occaecati excepturi optio reprehenderit"
108
+ # body="quia et suscipit\nsuscipit recusandae consequuntur expedita et cum\nreprehenderit molestiae ut ut quas totam\nnostrum rerum est autem sunt rem eveniet architecto">,
109
+ # #<ROM::Struct[Post]
110
+ # id=2
111
+ # user_id=1
112
+ # title="qui est esse"
113
+ # body="est rerum tempore vitae\nsequi sint nihil reprehenderit dolor beatae ea dolores neque\nfugiat blanditiis voluptate porro vel nihil molestiae ut reiciendis\nqui aperiam non debitis possimus qui neque nisi nulla">,
114
+ # #<ROM::Struct[Post]
115
+ # id=3
116
+ # user_id=1
117
+ # title="ea molestias quasi exercitationem repellat qui ipsa sit aut"
118
+ # body="et iusto sed quo iure\nvoluptatem occaecati omnis eligendi aut ad\nvoluptatem doloribus vel accusantium quis pariatur\nmolestiae porro eius odio et labore et velit aut">,
119
+ # #<ROM::Struct[Post]
120
+ # id=4
121
+ # user_id=1
122
+ # title="eum et est occaecati"
123
+ # body="ullam et saepe reiciendis voluptatem adipisci\nsit amet autem assumenda provident rerum culpa\nquis hic commodi nesciunt rem tenetur doloremque ipsam iure\nquis sunt voluptatem rerum illo velit">,
124
+ # #<ROM::Struct[Post]
125
+ # id=5
126
+ # user_id=1
127
+ # title="nesciunt quas odio"
128
+ # body="repudiandae veniam quaerat sunt sed\nalias aut fugiat sit autem sed est\nvoluptatem omnis possimus esse voluptatibus quis\nest aut tenetur dolor neque">,
129
+ # #<ROM::Struct[Post]
130
+ # id=6
131
+ # user_id=1
132
+ # title="dolorem eum magni eos aperiam quia"
133
+ # body="ut aspernatur corporis harum nihil quis provident sequi\nmollitia nobis aliquid molestiae\nperspiciatis et ea nemo ab reprehenderit accusantium quas\nvoluptate dolores velit et doloremque molestiae">,
134
+ # #<ROM::Struct[Post]
135
+ # id=7
136
+ # user_id=1
137
+ # title="magnam facilis autem"
138
+ # body="dolore placeat quibusdam ea quo vitae\nmagni quis enim qui quis quo nemo aut saepe\nquidem repellat excepturi ut quia\nsunt ut sequi eos ea sed quas">,
139
+ # #<ROM::Struct[Post]
140
+ # id=8
141
+ # user_id=1
142
+ # title="dolorem dolore est ipsam"
143
+ # body="dignissimos aperiam dolorem qui eum\nfacilis quibusdam animi sint suscipit qui sint possimus cum\nquaerat magni maiores excepturi\nipsam ut commodi dolor voluptatum modi aut vitae">,
144
+ # #<ROM::Struct[Post]
145
+ # id=9
146
+ # user_id=1
147
+ # title="nesciunt iure omnis dolorem tempora et accusantium"
148
+ # body="consectetur animi nesciunt iure dolore\nenim quia ad\nveniam autem ut quam aut nobis\net est aut quod aut provident voluptas autem voluptas">,
149
+ # #<ROM::Struct[Post]
150
+ # id=10
151
+ # user_id=1
152
+ # title="optio molestias id quia eum"
153
+ # body="quo et expedita modi cum officia vel magni\ndoloribus qui repudiandae\nvero nisi sit\nquos veniam quod sed accusamus veritatis error">
154
+ # ]>
@@ -13,7 +13,6 @@ module ROM
13
13
  def execute(tuples)
14
14
  Array([tuples]).flatten.map do |tuple|
15
15
  attributes = input[tuple]
16
- validator.call(attributes)
17
16
  relation.insert(attributes.to_h)
18
17
  end.to_a
19
18
  end
@@ -13,7 +13,6 @@ module ROM
13
13
  def execute(tuples)
14
14
  Array([tuples]).flatten.map do |tuple|
15
15
  attributes = input[tuple]
16
- validator.call(attributes)
17
16
  relation.update(attributes.to_h)
18
17
  end.to_a
19
18
  end
@@ -1,5 +1,4 @@
1
- require 'rom/http/dataset/response_transformers/schemad'
2
- require 'rom/http/dataset/response_transformers/schemaless'
1
+ require 'rom/initializer'
3
2
 
4
3
  module ROM
5
4
  module HTTP
@@ -9,17 +8,20 @@ module ROM
9
8
  #
10
9
  # @api public
11
10
  class Dataset
11
+ PATH_SEPARATOR = '/'.freeze
12
+ STRIP_PATH = ->(path) { path.sub(%r{\A/}, '') }.freeze
13
+
12
14
  include Enumerable
13
15
  include Dry::Equalizer(:config, :options)
14
- include ROM::Options
16
+ extend ::ROM::Initializer
15
17
 
16
- attr_reader :config
18
+ param :config
17
19
 
18
- option :projections, type: ::Array, default: [], reader: true
19
- option :request_method, type: ::Symbol, default: :get, reader: true
20
- option :path, type: ::String, default: ''
21
- option :params, type: ::Hash, default: {}, reader: true
22
- option :headers, type: ::Hash, default: {}
20
+ option :request_method, type: Types::Symbol, default: proc { :get }, reader: true
21
+ option :base_path, type: Types::String, default: proc { name }
22
+ option :path, type: Types::String, default: proc { '' }
23
+ option :params, type: Types::Hash, default: proc { {} }, reader: true
24
+ option :headers, type: Types::Hash, default: proc { {} }
23
25
 
24
26
  class << self
25
27
  def default_request_handler(handler = Undefined)
@@ -33,39 +35,6 @@ module ROM
33
35
  end
34
36
  end
35
37
 
36
-
37
- # HTTP Dataset interface
38
- #
39
- # @param [Hash] config the Gateway's HTTP server configuration
40
- #
41
- # @param [Hash] options dataset configuration options
42
- # @option options [Symbol] :request_method (:get) The HTTP verb to use
43
- # @option options [Array] :projections ([]) Keys to select from response tuple
44
- # @option options [String] :path ('') URI request path
45
- # @option options [Hash] :headers ({}) Additional request headers
46
- # @option options [Hash] :params ({}) HTTP request parameters
47
- #
48
- # @api public
49
- def initialize(config, options = {})
50
- @config = config
51
- @response_transformer = ResponseTransformers::Schemaless.new
52
- super(options)
53
- end
54
-
55
- # @overload response_transformer
56
- # Return the current response transformer
57
- #
58
- # @overload response_transformer(transformer)
59
- # Set a new response transformer
60
- #
61
- # @param transformer [#call] The new response transformer
62
- #
63
- # @api private
64
- def response_transformer(transformer = Undefined)
65
- return @response_transformer if Undefined === transformer
66
- @response_transformer = transformer
67
- end
68
-
69
38
  # Return the gateway's URI
70
39
  #
71
40
  # @return [String]
@@ -104,6 +73,19 @@ module ROM
104
73
  config[:name].to_s
105
74
  end
106
75
 
76
+ # Return the base path
77
+ #
78
+ # @example
79
+ # Dataset.new(config, base_path: '/users').base_path
80
+ # # => 'users'
81
+ #
82
+ # @return [String] the dataset path, without a leading slash
83
+ #
84
+ # @api public
85
+ def base_path
86
+ STRIP_PATH.call(super)
87
+ end
88
+
107
89
  # Return the dataset path
108
90
  #
109
91
  # @example
@@ -114,7 +96,7 @@ module ROM
114
96
  #
115
97
  # @api public
116
98
  def path
117
- options[:path].to_s.sub(%r{\A/}, '')
99
+ STRIP_PATH.call(join_path(base_path, super))
118
100
  end
119
101
 
120
102
  # Return the dataset path
@@ -127,7 +109,7 @@ module ROM
127
109
  #
128
110
  # @api public
129
111
  def absolute_path
130
- '/' + path
112
+ PATH_SEPARATOR + path
131
113
  end
132
114
 
133
115
  # Return a new dataset with given headers
@@ -177,24 +159,19 @@ module ROM
177
159
  __new__(config, options.merge(opts))
178
160
  end
179
161
 
180
- # Add a new set projection to the result set
162
+ # Return a new dataset with a different base path
181
163
  #
182
- # @param [Array, *args] projections the keys to add to the projections
164
+ # @param base_path [String] the new base request path
183
165
  #
184
166
  # @example
185
- # users = Dataset.new(config, projections: [:login])
186
- # users.project(:email, :name).projections
187
- # # => [:login, :email, :name]
167
+ # users.with_base_path('/profiles').base_path
168
+ # # => 'profiles'
188
169
  #
189
170
  # @return [Dataset]
190
171
  #
191
172
  # @api public
192
- def project(*args)
193
- projections = args.first.is_a?(::Array) ? args.first : args
194
-
195
- with_options(
196
- projections: (self.projections + projections)
197
- )
173
+ def with_base_path(base_path)
174
+ with_options(base_path: base_path)
198
175
  end
199
176
 
200
177
  # Return a new dataset with a different path
@@ -223,8 +200,8 @@ module ROM
223
200
  # @return [Dataset]
224
201
  #
225
202
  # @api public
226
- def append_path(path)
227
- with_options(path: options[:path] + '/' + path)
203
+ def append_path(append_path)
204
+ with_options(path: join_path(path, append_path))
228
205
  end
229
206
 
230
207
  # Return a new dataset with a different request method
@@ -316,10 +293,7 @@ module ROM
316
293
  #
317
294
  # @api public
318
295
  def response
319
- response_transformer.call(
320
- response_handler.call(request_handler.call(self), self),
321
- self
322
- )
296
+ response_handler.call(request_handler.call(self), self)
323
297
  end
324
298
 
325
299
  private
@@ -339,6 +313,10 @@ module ROM
339
313
  def __new__(*args, &block)
340
314
  self.class.new(*args, &block)
341
315
  end
316
+
317
+ def join_path(*paths)
318
+ paths.reject(&:empty?).join(PATH_SEPARATOR)
319
+ end
342
320
  end
343
321
  end
344
322
  end