my_api_client 0.2.0 → 0.3.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
  SHA256:
3
- metadata.gz: eb13e4cf8d1a3d9b3a1bbb5154b695493384c1d94377d8260f0ababa59d42c55
4
- data.tar.gz: 52df7eb8caa4ad533ce6130078da94da6ff59924efbb5f52c441225114c03fcb
3
+ metadata.gz: 8f79acd9dda7576ad4ef44c65c31d5c6fd4c3ebe64dc6ce8106bd81097931b84
4
+ data.tar.gz: ff17d603eabd348ff39e7c209e3fa601bcbaeee1a97beeda73e46b9c6d16eda4
5
5
  SHA512:
6
- metadata.gz: b073a7464c97c5d2b9181cb0ed5e1de4296a808a0668d3bb9bf1357f4bad591a24c30ff7935e88383a934fe6867bca86878b673ca51898678ec057e36404413d
7
- data.tar.gz: 7bba09a981e3e71a5de194a84cae6fb19cafa3631b283e1f778072837a354db67b555d80f13329b8895496de886393494155427ad0a8050d97b9a36b61171015
6
+ metadata.gz: e3258a69b3e289f8277d307759aeca39ea14dd8f0573ef686cb5fb24625b7afe30d9be4e65fe896c7ca9ebdd25612c2fe07b980e72bcb8dfe63598c0297fe46d
7
+ data.tar.gz: 6cf8c1ee680e128f4ecfda474a528dc61323dba9ca592b7f044e5a9024a27eb6e8926cb4423909c7299198abca88d48aa127cb11d0b0f72d74e2e95c7310550d
data/.rubocop.yml CHANGED
@@ -38,6 +38,7 @@ RSpec/ExampleLength:
38
38
  RSpec/FilePath:
39
39
  Exclude:
40
40
  - 'spec/lib/my_api_client/errors/**/*'
41
+ - 'spec/lib/my_api_client/rspec/**/*'
41
42
 
42
43
  RSpec/NestedGroups:
43
44
  Max: 4
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- my_api_client (0.2.0)
4
+ my_api_client (0.3.0)
5
5
  activesupport (>= 4.2.0)
6
6
  jsonpath
7
7
  sawyer
data/README.jp.md CHANGED
@@ -6,6 +6,11 @@ MyApiClient は API リクエストクラスを作成するための汎用的な
6
6
 
7
7
  また、 Ruby on Rails で利用することを想定してますが、それ以外の環境でも動作するように作っているつもりです。不具合などあれば Issue ページからご報告下さい。
8
8
 
9
+ ## Supported Versions
10
+
11
+ * Ruby 2.4, 2.5, 2.6
12
+ * Rails 4.2, 5.0, 5.1, 5.2
13
+
9
14
  ## Installation
10
15
 
11
16
  この gem は macOS と Linux で作動します。まずは、my_api_client を Gemfile に追加します:
@@ -31,7 +36,7 @@ create spec/api_clients/path/to/resource_api_client_spec.rb
31
36
 
32
37
  最もシンプルな利用例を以下に示します。
33
38
 
34
- ```rb
39
+ ```ruby
35
40
  class ExampleApiClient < MyApiClient::Base
36
41
  endpoint 'https://example.com'
37
42
 
@@ -78,7 +83,7 @@ api_clinet.get_users #=> #<Sawyer::Response>
78
83
 
79
84
  上記のコードにエラーハンドリングを追加してみます。
80
85
 
81
- ```rb
86
+ ```ruby
82
87
  class ExampleApiClient < MyApiClient::Base
83
88
  endpoint 'https://example.com'
84
89
 
@@ -106,7 +111,7 @@ end
106
111
 
107
112
  一つずつ解説していきます。まず、以下のように `status_code` を指定するものについて。
108
113
 
109
- ```rb
114
+ ```ruby
110
115
  error_handling status_code: 400..499, raise: MyApiClient::ClientError
111
116
  ```
112
117
 
@@ -118,7 +123,7 @@ https://github.com/ryz310/my_api_client/blob/master/lib/my_api_client/errors.rb
118
123
 
119
124
  次に、 `raise` の代わりに Block を指定する場合について。
120
125
 
121
- ```rb
126
+ ```ruby
122
127
  error_handling status_code: 500..599 do |params, logger|
123
128
  logger.warn 'Server error occurred.'
124
129
  raise MyApiClient::ServerError, params
@@ -135,7 +140,7 @@ API request `GET https://example.com/path/to/resouce`: "Server error occurred."
135
140
 
136
141
  最後に `json` と `with` を利用する場合について。
137
142
 
138
- ```rb
143
+ ```ruby
139
144
  error_handling json: { '$.errors.code': 10..19 }, with: :my_error_handling
140
145
  ```
141
146
 
@@ -152,7 +157,7 @@ error_handling json: { '$.errors.code': 10..19 }, with: :my_error_handling
152
157
 
153
158
  `with` にはインスタンスメソッド名を指定することで、エラーを検出した際に任意のメソッドを実行させることができます。メソッドに渡される引数は Block 定義の場合と同じく `params` と `logger` です。
154
159
 
155
- ```rb
160
+ ```ruby
156
161
  # @param params [MyApiClient::Params::Params] HTTP req and res params
157
162
  # @param logger [MyApiClient::Logger] Logger for a request processing
158
163
  def my_error_handling(params, logger)
@@ -173,7 +178,7 @@ WIP
173
178
 
174
179
  多くの場合、同一ホストの API は リクエストヘッダーやエラー情報が同じ構造になっているため、上記のように一つのクラス内に複数の API を定義する設計が理にかなっていますが、 API 毎に個別に定義したい場合は、以下のように 1 つのクラスに 1 の API という構造で設計することも可能です。
175
180
 
176
- ```rb
181
+ ```ruby
177
182
  class ExampleApiClient < MyApiClient::Base
178
183
  endpoint 'https://example.com'
179
184
 
@@ -227,6 +232,70 @@ WIP
227
232
 
228
233
  WIP
229
234
 
235
+ ### RSpec
236
+
237
+ RSpec を使ったテストをサポートしています。
238
+ 以下のコードを `spec/spec_helper.rb` (または `spec/rails_helper.rb`) に追記して下さい。
239
+
240
+ ```ruby
241
+ require 'my_api_client/rspec'
242
+ ```
243
+
244
+ 例えば以下のような `ApiClient` を定義しているとします。
245
+
246
+ ```ruby
247
+ class ExampleApiClient < MyApiClient::Base
248
+ endpoint 'https://example.com'
249
+
250
+ def request(user_id:)
251
+ get "users/#{user_id}"
252
+ end
253
+ end
254
+ ```
255
+
256
+ `my_api_client_stub` を使うことで、 `ExampleApiClient#request` をスタブ化することができます。これで `#request` を実行してもリアルな HTTP リクエストが実行されなくなります。
257
+
258
+ ```ruby
259
+ my_api_client_stub(ExampleApiClient, :request, response: { id: 12345 })
260
+
261
+ response = ExampleApiClient.new.request(user_id: 1)
262
+ response.id # => 12345
263
+ ```
264
+
265
+ リクスエストパラメータを使ったレスポンスを返すようにスタブ化したい場合は、 block を利用することで実現できます。
266
+
267
+ ```ruby
268
+ my_api_client_stub(ExampleApiClient, :request) do |params|
269
+ { id: params[:user_id] }
270
+ end
271
+
272
+ response = ExampleApiClient.new.request(user_id: 1)
273
+ response.id # => 1
274
+ ```
275
+
276
+ `receive` や `have_received` を使ったテストを書きたい場合は、 `my_api_client_stub` の戻り値を利用すると良いでしょう。
277
+
278
+ ```ruby
279
+ def execute_api_request
280
+ ExampleApiClient.new.request(user_id: 1)
281
+ end
282
+
283
+ api_clinet = my_api_client_stub(ExampleApiClient, :request)
284
+ execute_api_request
285
+ expect(api_client).to have_received(:request).with(user_id: 1)
286
+ ```
287
+
288
+ また、例外が発生する場合のテストを書きたい場合は、 `raise` オプションを利用することができます。
289
+
290
+ ```ruby
291
+ def execute_api_request
292
+ ExampleApiClient.new.request(user_id: 1)
293
+ end
294
+
295
+ my_api_client_stub(ExampleApiClient, :request, raise: MyApiClient::Error)
296
+ expect { execute_api_request }.to raise_error(MyApiClient::Error)
297
+ ```
298
+
230
299
  ## Contributing
231
300
 
232
301
  不具合の報告や Pull Request を歓迎しています。OSS という事で自分はなるべく頑張って英語を使うようにしていますが、日本語での報告でも大丈夫です :+1:
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'my_api_client'
4
+ require 'my_api_client/rspec/stub'
5
+
6
+ RSpec.configure do |config|
7
+ config.include MyApiClient::Stub
8
+ end
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MyApiClient
4
+ # Test helper module for RSpec
5
+ module Stub
6
+ # Provides stubbing feature for `MyApiClient`.
7
+ #
8
+ # @param klass [Class]
9
+ # Stubbing target class.
10
+ # @param action [Symbol]
11
+ # Stubbing target method name.
12
+ # @param response [Object, nil]
13
+ # Stubbed ApiClient will return parameters set by `response`.
14
+ # default: nil
15
+ # @param raise [Class, MyApiClient::Error, nil]
16
+ # Stubbed ApiClient will raise exception set by `raise`.
17
+ # You can set either exception class or exception instance.
18
+ # default: nil
19
+ # @return [InstanceDouble]
20
+ # Returns a spy object for the ApiClient.
21
+ # rubocop:disable Metrics/AbcSize
22
+ def my_api_client_stub(klass, action, response: nil, raise: nil)
23
+ instance = instance_double(klass)
24
+ allow(klass).to receive(:new).and_return(instance)
25
+ if raise.present?
26
+ allow(instance).to receive(action).and_raise(process_raise_option(raise))
27
+ elsif block_given?
28
+ allow(instance).to receive(action) { |*request| stub_as_sawyer(yield(*request)) }
29
+ else
30
+ allow(instance).to receive(action).and_return(stub_as_sawyer(response))
31
+ end
32
+ instance
33
+ end
34
+ # rubocop:enable Metrics/AbcSize
35
+
36
+ private
37
+
38
+ # Provides a shorthand for `raise` option.
39
+ # `MyApiClient::Error` requires `MyApiClient::Params::Params` instance on
40
+ # initialize, but it makes trubolesome. `MyApiClient::NetworkError` is more.
41
+ # If given a error instance, it will return raw value without processing.
42
+ #
43
+ # @param exception [Clsas, MyApiClient::Error] Processing target.
44
+ # @return [MyApiClient::Error] Processed exception.
45
+ # @raise [RuntimeError] Unsupported error class was set.
46
+ def process_raise_option(exception)
47
+ case exception
48
+ when Class
49
+ params = instance_double(MyApiClient::Params::Params, to_bugsnag: {})
50
+ if exception == MyApiClient::NetworkError
51
+ exception.new(params, Net::OpenTimeout.new)
52
+ else
53
+ exception.new(params)
54
+ end
55
+ when MyApiClient::Error
56
+ exception
57
+ else
58
+ raise "Unsupported error class was set: #{exception.inspect}"
59
+ end
60
+ end
61
+
62
+ def stub_as_sawyer(params)
63
+ case params
64
+ when Hash then Sawyer::Resource.new(agent, params)
65
+ when Array then params.map { |hash| sawyer_resource(hash) }
66
+ when nil then nil
67
+ else params
68
+ end
69
+ end
70
+
71
+ def agent
72
+ instance_double('Sawyer::Agent').tap do |agent|
73
+ allow(agent).to receive(:parse_links) do |data|
74
+ data ||= {}
75
+ links = data.delete(:_links)
76
+ [data, links]
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MyApiClient
4
- VERSION = '0.2.0'
4
+ VERSION = '0.3.0'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: my_api_client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ryz310
@@ -264,6 +264,8 @@ files:
264
264
  - lib/my_api_client/params/params.rb
265
265
  - lib/my_api_client/params/request.rb
266
266
  - lib/my_api_client/request.rb
267
+ - lib/my_api_client/rspec.rb
268
+ - lib/my_api_client/rspec/stub.rb
267
269
  - lib/my_api_client/version.rb
268
270
  - my_api_client.gemspec
269
271
  homepage: https://github.com/ryz310/my_api_client