evil-client 0.3.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (180) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +0 -11
  3. data/.gitignore +1 -0
  4. data/.rspec +0 -1
  5. data/.rubocop.yml +22 -19
  6. data/.travis.yml +1 -0
  7. data/CHANGELOG.md +251 -6
  8. data/LICENSE.txt +3 -1
  9. data/README.md +47 -81
  10. data/docs/helpers/body.md +93 -0
  11. data/docs/helpers/connection.md +19 -0
  12. data/docs/helpers/headers.md +72 -0
  13. data/docs/helpers/http_method.md +39 -0
  14. data/docs/helpers/let.md +14 -0
  15. data/docs/helpers/logger.md +24 -0
  16. data/docs/helpers/middleware.md +56 -0
  17. data/docs/helpers/operation.md +103 -0
  18. data/docs/helpers/option.md +50 -0
  19. data/docs/helpers/path.md +37 -0
  20. data/docs/helpers/query.md +59 -0
  21. data/docs/helpers/response.md +40 -0
  22. data/docs/helpers/scope.md +121 -0
  23. data/docs/helpers/security.md +102 -0
  24. data/docs/helpers/validate.md +68 -0
  25. data/docs/index.md +70 -78
  26. data/docs/license.md +5 -1
  27. data/docs/rspec.md +96 -0
  28. data/evil-client.gemspec +10 -8
  29. data/lib/evil/client.rb +126 -72
  30. data/lib/evil/client/builder.rb +47 -0
  31. data/lib/evil/client/builder/operation.rb +40 -0
  32. data/lib/evil/client/builder/scope.rb +31 -0
  33. data/lib/evil/client/chaining.rb +17 -0
  34. data/lib/evil/client/connection.rb +60 -20
  35. data/lib/evil/client/container.rb +66 -0
  36. data/lib/evil/client/container/operation.rb +23 -0
  37. data/lib/evil/client/container/scope.rb +28 -0
  38. data/lib/evil/client/exceptions/definition_error.rb +15 -0
  39. data/lib/evil/client/exceptions/name_error.rb +32 -0
  40. data/lib/evil/client/exceptions/response_error.rb +42 -0
  41. data/lib/evil/client/exceptions/type_error.rb +29 -0
  42. data/lib/evil/client/exceptions/validation_error.rb +27 -0
  43. data/lib/evil/client/formatter.rb +49 -0
  44. data/lib/evil/client/formatter/form.rb +45 -0
  45. data/lib/evil/client/formatter/multipart.rb +33 -0
  46. data/lib/evil/client/formatter/part.rb +66 -0
  47. data/lib/evil/client/formatter/text.rb +21 -0
  48. data/lib/evil/client/resolver.rb +84 -0
  49. data/lib/evil/client/resolver/body.rb +22 -0
  50. data/lib/evil/client/resolver/format.rb +30 -0
  51. data/lib/evil/client/resolver/headers.rb +46 -0
  52. data/lib/evil/client/resolver/http_method.rb +34 -0
  53. data/lib/evil/client/resolver/middleware.rb +36 -0
  54. data/lib/evil/client/resolver/query.rb +39 -0
  55. data/lib/evil/client/resolver/request.rb +96 -0
  56. data/lib/evil/client/resolver/response.rb +26 -0
  57. data/lib/evil/client/resolver/security.rb +113 -0
  58. data/lib/evil/client/resolver/uri.rb +35 -0
  59. data/lib/evil/client/rspec.rb +127 -0
  60. data/lib/evil/client/schema.rb +105 -0
  61. data/lib/evil/client/schema/operation.rb +177 -0
  62. data/lib/evil/client/schema/scope.rb +73 -0
  63. data/lib/evil/client/settings.rb +172 -0
  64. data/lib/evil/client/settings/validator.rb +64 -0
  65. data/mkdocs.yml +21 -15
  66. data/spec/features/custom_connection_spec.rb +17 -0
  67. data/spec/features/operation/middleware_spec.rb +50 -0
  68. data/spec/features/operation/options_spec.rb +71 -0
  69. data/spec/features/operation/request_spec.rb +94 -0
  70. data/spec/features/operation/response_spec.rb +48 -0
  71. data/spec/features/scope/options_spec.rb +52 -0
  72. data/spec/fixtures/locales/en.yml +16 -0
  73. data/spec/fixtures/test_client.rb +76 -0
  74. data/spec/spec_helper.rb +18 -6
  75. data/spec/support/fixtures_helper.rb +7 -0
  76. data/spec/unit/builder/operation_spec.rb +90 -0
  77. data/spec/unit/builder/scope_spec.rb +84 -0
  78. data/spec/unit/client_spec.rb +137 -0
  79. data/spec/unit/connection_spec.rb +78 -0
  80. data/spec/unit/container/operation_spec.rb +81 -0
  81. data/spec/unit/container/scope_spec.rb +61 -0
  82. data/spec/unit/container_spec.rb +107 -0
  83. data/spec/unit/exceptions/definition_error_spec.rb +15 -0
  84. data/spec/unit/exceptions/name_error_spec.rb +77 -0
  85. data/spec/unit/exceptions/response_error_spec.rb +22 -0
  86. data/spec/unit/exceptions/type_error_spec.rb +71 -0
  87. data/spec/unit/exceptions/validation_error_spec.rb +13 -0
  88. data/spec/unit/formatter/form_spec.rb +27 -0
  89. data/spec/unit/formatter/multipart_spec.rb +23 -0
  90. data/spec/unit/formatter/part_spec.rb +49 -0
  91. data/spec/unit/formatter/text_spec.rb +37 -0
  92. data/spec/unit/formatter_spec.rb +46 -0
  93. data/spec/unit/resolver/body_spec.rb +65 -0
  94. data/spec/unit/resolver/format_spec.rb +66 -0
  95. data/spec/unit/resolver/headers_spec.rb +93 -0
  96. data/spec/unit/resolver/http_method_spec.rb +67 -0
  97. data/spec/unit/resolver/middleware_spec.rb +83 -0
  98. data/spec/unit/resolver/query_spec.rb +85 -0
  99. data/spec/unit/resolver/request_spec.rb +121 -0
  100. data/spec/unit/resolver/response_spec.rb +64 -0
  101. data/spec/unit/resolver/security_spec.rb +156 -0
  102. data/spec/unit/resolver/uri_spec.rb +117 -0
  103. data/spec/unit/rspec_spec.rb +342 -0
  104. data/spec/unit/schema/operation_spec.rb +309 -0
  105. data/spec/unit/schema/scope_spec.rb +110 -0
  106. data/spec/unit/schema_spec.rb +157 -0
  107. data/spec/unit/settings/validator_spec.rb +128 -0
  108. data/spec/unit/settings_spec.rb +248 -0
  109. metadata +192 -135
  110. data/docs/base_url.md +0 -38
  111. data/docs/documentation.md +0 -9
  112. data/docs/headers.md +0 -59
  113. data/docs/http_method.md +0 -31
  114. data/docs/model.md +0 -173
  115. data/docs/operation.md +0 -0
  116. data/docs/overview.md +0 -0
  117. data/docs/path.md +0 -48
  118. data/docs/query.md +0 -99
  119. data/docs/responses.md +0 -66
  120. data/docs/security.md +0 -102
  121. data/docs/settings.md +0 -32
  122. data/lib/evil/client/connection/net_http.rb +0 -57
  123. data/lib/evil/client/dsl.rb +0 -127
  124. data/lib/evil/client/dsl/base.rb +0 -26
  125. data/lib/evil/client/dsl/files.rb +0 -37
  126. data/lib/evil/client/dsl/headers.rb +0 -16
  127. data/lib/evil/client/dsl/http_method.rb +0 -24
  128. data/lib/evil/client/dsl/operation.rb +0 -91
  129. data/lib/evil/client/dsl/operations.rb +0 -41
  130. data/lib/evil/client/dsl/path.rb +0 -25
  131. data/lib/evil/client/dsl/query.rb +0 -16
  132. data/lib/evil/client/dsl/response.rb +0 -61
  133. data/lib/evil/client/dsl/responses.rb +0 -29
  134. data/lib/evil/client/dsl/scope.rb +0 -27
  135. data/lib/evil/client/dsl/security.rb +0 -57
  136. data/lib/evil/client/dsl/verifier.rb +0 -35
  137. data/lib/evil/client/middleware.rb +0 -81
  138. data/lib/evil/client/middleware/base.rb +0 -11
  139. data/lib/evil/client/middleware/merge_security.rb +0 -20
  140. data/lib/evil/client/middleware/normalize_headers.rb +0 -17
  141. data/lib/evil/client/middleware/stringify_form.rb +0 -40
  142. data/lib/evil/client/middleware/stringify_json.rb +0 -19
  143. data/lib/evil/client/middleware/stringify_multipart.rb +0 -36
  144. data/lib/evil/client/middleware/stringify_multipart/part.rb +0 -36
  145. data/lib/evil/client/middleware/stringify_query.rb +0 -35
  146. data/lib/evil/client/operation.rb +0 -34
  147. data/lib/evil/client/operation/request.rb +0 -26
  148. data/lib/evil/client/operation/response.rb +0 -39
  149. data/lib/evil/client/operation/response_error.rb +0 -13
  150. data/lib/evil/client/operation/unexpected_response_error.rb +0 -19
  151. data/spec/features/instantiation_spec.rb +0 -68
  152. data/spec/features/middleware_spec.rb +0 -79
  153. data/spec/features/operation_with_documentation_spec.rb +0 -41
  154. data/spec/features/operation_with_files_spec.rb +0 -40
  155. data/spec/features/operation_with_form_body_spec.rb +0 -158
  156. data/spec/features/operation_with_headers_spec.rb +0 -99
  157. data/spec/features/operation_with_http_method_spec.rb +0 -45
  158. data/spec/features/operation_with_json_body_spec.rb +0 -156
  159. data/spec/features/operation_with_nested_responses_spec.rb +0 -95
  160. data/spec/features/operation_with_path_spec.rb +0 -47
  161. data/spec/features/operation_with_query_spec.rb +0 -84
  162. data/spec/features/operation_with_security_spec.rb +0 -228
  163. data/spec/features/scoping_spec.rb +0 -48
  164. data/spec/support/test_client.rb +0 -15
  165. data/spec/unit/evil/client/connection/net_http_spec.rb +0 -38
  166. data/spec/unit/evil/client/dsl/files_spec.rb +0 -37
  167. data/spec/unit/evil/client/dsl/operation_spec.rb +0 -374
  168. data/spec/unit/evil/client/dsl/operations_spec.rb +0 -29
  169. data/spec/unit/evil/client/dsl/scope_spec.rb +0 -32
  170. data/spec/unit/evil/client/dsl/security_spec.rb +0 -135
  171. data/spec/unit/evil/client/middleware/merge_security_spec.rb +0 -32
  172. data/spec/unit/evil/client/middleware/normalize_headers_spec.rb +0 -17
  173. data/spec/unit/evil/client/middleware/stringify_form_spec.rb +0 -63
  174. data/spec/unit/evil/client/middleware/stringify_json_spec.rb +0 -61
  175. data/spec/unit/evil/client/middleware/stringify_multipart/part_spec.rb +0 -59
  176. data/spec/unit/evil/client/middleware/stringify_multipart_spec.rb +0 -62
  177. data/spec/unit/evil/client/middleware/stringify_query_spec.rb +0 -40
  178. data/spec/unit/evil/client/middleware_spec.rb +0 -46
  179. data/spec/unit/evil/client/operation/request_spec.rb +0 -49
  180. data/spec/unit/evil/client/operation/response_spec.rb +0 -63
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d05eb1654246a3c8ebda837f8e292fa356bca35e
4
- data.tar.gz: 9685ee8ea9bf347a54c794ca7c83b612eb206912
3
+ metadata.gz: 1a0f6860291b235077bd6d9063eb9e1ec47cb9ef
4
+ data.tar.gz: cbb9aca48a0283cf8e6ec84bec9444ea74bb440c
5
5
  SHA512:
6
- metadata.gz: '09241951f999f8909d4000e5b7ec81bab248729125bdd040f23d2162ab2f8739eb55d9c4f736fc72fc63750e7ebe48a62b0b13fecc82deaa01ccbdc839c76813'
7
- data.tar.gz: eade03b9334d111284a7c08d81a30f0bf5457a24b5ae7bec0a5461fe8f162a6e41fa9ff6833b1fc89db83a5583ab0c3999696e9cdf7cb1f9fb70d539103e93c7
6
+ metadata.gz: c1d2c2e407c90ef12db1d58c4aed5fef1a4c851934f90e25602ad5461c14278e30dcd3a1dbc7534dc17218797d46dedaa8f023389be46625f66bb046a06791a5
7
+ data.tar.gz: 433099e8dcebb8e48e0f22f057db11c68eb78556a1660508bdd22a261a40ad2c59407971459716c027d7c5d4b7cf122b2c03fa63d3308b06762e5658f3e7d763
data/.codeclimate.yml CHANGED
@@ -2,17 +2,6 @@
2
2
  engines:
3
3
  rubocop:
4
4
  enabled: true
5
- checks:
6
- Rubocop/Style/FrozenStringLiteralComment:
7
- enabled: false
8
- Rubocop/Style/Documentation:
9
- enabled: false
10
- Rubocop/Style/ClassAndModuleChildren:
11
- enabled: false
12
- Rubocop/Style/SpaceInLambdaLiteral:
13
- enabled: false
14
- Style/SingleLineBlockParams:
15
- enabled: false
16
5
  ratings:
17
6
  paths:
18
7
  - "lib/**.rb"
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /spec/reports/
9
9
  /tmp/
10
10
  /.rubocop.todo.yml
11
+ /.rspec_status
data/.rspec CHANGED
@@ -1,3 +1,2 @@
1
1
  --require spec_helper
2
- --format documentation
3
2
  --color
data/.rubocop.yml CHANGED
@@ -1,41 +1,44 @@
1
1
  ---
2
2
  AllCops:
3
- DisplayCopNames: true
4
- DisplayStyleGuide: true
5
- StyleGuideCopsOnly: true
6
- TargetRubyVersion: 2.3
3
+ DisplayCopNames: true
4
+ TargetRubyVersion: 2.3
7
5
 
8
- Style/CaseEquality:
6
+ Lint/AmbiguousBlockAssociation:
9
7
  Enabled: false
10
8
 
11
- Style/Documentation:
9
+ Metrics/BlockLength:
12
10
  Enabled: false
13
11
 
14
- Style/FileName:
15
- Exclude:
16
- - lib/evil-client.rb
17
-
18
- Style/Lambda:
12
+ Style/Alias:
19
13
  Enabled: false
20
14
 
21
- Style/LambdaCall:
15
+ Style/ClassAndModuleChildren:
22
16
  Enabled: false
23
17
 
24
- Style/MethodMissing:
25
- Enabled: false
26
-
27
- Style/MultilineMethodCallIndentation:
18
+ Style/FileName:
28
19
  Exclude:
29
- - spec/**.rb
20
+ - lib/evil-client.rb
30
21
 
31
- Style/NumericPredicate:
22
+ Style/FrozenStringLiteralComment:
23
+ Enabled: false
24
+
25
+ Style/ModuleFunction:
32
26
  Enabled: false
33
27
 
34
28
  Style/RaiseArgs:
35
29
  Enabled: false
36
30
 
37
31
  Style/RescueModifier:
38
- Enabled: false
32
+ Exclude:
33
+ - '**/*_spec.rb'
34
+
35
+ Style/Semicolon:
36
+ Exclude:
37
+ - '**/*_spec.rb'
39
38
 
40
39
  Style/StringLiterals:
41
40
  EnforcedStyle: double_quotes
41
+
42
+ Style/SingleLineMethods:
43
+ Exclude:
44
+ - '**/*_spec.rb'
data/.travis.yml CHANGED
@@ -7,6 +7,7 @@ script:
7
7
  - bundle exec rubocop
8
8
  rvm:
9
9
  - '2.3.0'
10
+ - '2.4.0'
10
11
  - ruby-head
11
12
  - jruby-9.1.0.0
12
13
  - jruby-head
data/CHANGELOG.md CHANGED
@@ -4,9 +4,254 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog], and this project adheres
5
5
  to [Semantic Versioning].
6
6
 
7
- ## [0.3.3] - [2017-07-14]
7
+ ## [1.0.0] [2017-08-06]
8
+
9
+ This is a total new reincarnation of the gem. I've changed its
10
+ architecture for consistency and unification reasons. This version is
11
+ backward-incompatible to all the previous ones up to the [0.3.3].
12
+
13
+ Its DSL was re-written and regularized as well. While the idea of the gem
14
+ remains about the same as before, its implementation has been changed
15
+ drastically in many important details.
16
+
17
+ ### [BREAKING] Changed
18
+
19
+ - There is no more differencies between "root" DSL, DSL of scope and
20
+ operation. All scopes use exactly the same methods. All operations
21
+ uses just the same methods except for `#operation` and `#scope` that
22
+ provide further nesting.
23
+
24
+ Any customization (inside a sub-scope or operation of some scope)
25
+ re-loads previous definitions in the following order:
26
+
27
+ ```text
28
+ root operation(anonymous)
29
+ subscope operation(anonymous)
30
+ # ...
31
+ custom(named) operation
32
+ ```
33
+
34
+ - Unlike previous versions every named operation belongs to some scope
35
+ (possibly nested into parents ones). Operations/subscopes should
36
+ have unique names inside its scope only (no more global namespace
37
+ for all operations).
38
+
39
+ Every scope knows the list of its subscopes and operations like:
40
+
41
+ ```ruby
42
+ MyClient.new.scopes[:users].operations[:fetch]
43
+ ```
44
+
45
+ - You can describe operations step-by-step. For example, you have
46
+ to describe `path` of root anonymous operation. Later you can add subpath
47
+ in the corresponding operation or scope:
48
+
49
+ ```ruby
50
+ class MyClient
51
+ option :subdomain
52
+ path { "https://#{subdomain}.foobar.com" } # same as base_path
53
+
54
+ scope :users do
55
+ option :version
56
+
57
+ # makes full path "https://{subdomain}.foobar.com/v{version}/users"
58
+ path { "v#{version}/users" }
59
+
60
+ operation :fetch do
61
+ option :id
62
+
63
+ # the final path: "https://{subdomain}.foobar.com/v{version}/users/{id}"
64
+ path { id }
65
+ end
66
+ end
67
+ end
68
+ ```
69
+
70
+ - As a syntax sugar all undefined methods are delegated to subscopes
71
+ and operations.
72
+
73
+ Instead of full syntax:
74
+
75
+ ```ruby
76
+ MyClient.new
77
+ .scopes[:users][version: "1.1"]
78
+ .operations[:fetch][id: 7]
79
+ .call
80
+ ```
81
+
82
+ You can use more natural one:
83
+
84
+ ```ruby
85
+ MyClient.new.users(version: "1.1").fetch(id: 7)
86
+ ```
87
+
88
+ - Every scope or operation takes some options.
89
+ Defined options are inherited and collected from the very root of the client:
90
+
91
+ ```ruby
92
+ client = MyClient.new token: "foo", bar: :baz
93
+ client.options # => { token: "foo" }
94
+
95
+ users = client.users(version: 3)
96
+ users.options # => { token: "foo", version: 3 }
97
+
98
+ fetch = users.operations[:fetch][id: 7]
99
+ fetch.options # => { token: "foo", version: 3, id: 7 }
100
+ ```
101
+
102
+ - You can reload assigned options at any level of nesting
103
+
104
+ ```ruby
105
+ users.options # => { token: "foo", version: 3 }
106
+
107
+ fetch = users.operations[:fetch][id: 7, token: "baz"]
108
+ fetch.options # => { token: "baz", version: 3, id: 7 }
109
+ ```
110
+
111
+ - When adding an option you can define the necessary coercers, default values
112
+ and requirements using the [dry-initializer] gem API.
113
+
114
+ ```ruby
115
+ class MyClient
116
+ option :token, proc(&:to_s) # required
117
+ option :subdomain, proc(&:to_s), default: { "europe" }
118
+ end
119
+ ```
120
+
121
+ - In addition you can define validator to check whether options
122
+ correspond to each other:
123
+
124
+ ```ruby
125
+ class MyClient
126
+ option :token, optional: true
127
+ option :password, optional: true
128
+
129
+ validate(:valid_credentials) { token.nil? ^ password.nil? }
130
+ end
131
+ ```
132
+
133
+ You have to add i18n localization for that errors:
134
+
135
+ ```yaml
136
+ # config/locales/evil-client.en.yml
137
+ en:
138
+ evil:
139
+ client:
140
+ errors:
141
+ my_client:
142
+ valid_credentials: "You should set either token or password"
143
+ ```
144
+
145
+ All validations from root will be applied at every single instance
146
+ (of subscope or operation). Every time we validate current (reloaded) options.
147
+
148
+ - The method `let` allows to define custom memoizers:
149
+
150
+ ```ruby
151
+ class MyClient
152
+ option :first_name, proc(&:to_s)
153
+ option :last_name, proc(&:to_s)
154
+
155
+ let(:full_name) { [first_name, last_name].join(" ") }
156
+ end
157
+ ```
158
+
159
+ Such memoizers are available in validators and request/response declarations.
160
+
161
+ - Definitions of request/response parts can be made ether as a plain values
162
+
163
+ ```ruby
164
+ class MyClient
165
+ path "https://api.example.com"
166
+ end
167
+ ```
8
168
 
9
- On the road to v0.3.3
169
+ or via blocks:
170
+
171
+ ```ruby
172
+ class MyClient
173
+ option :subdomain
174
+
175
+ path { "https://#{subdomain}.example.com" }
176
+ end
177
+ ```
178
+
179
+ Inside the block you can access all the current options.
180
+
181
+ - Some upper-level operation definitions (path, query, and headers)
182
+ are updated by nested definitions, while the others (http_method, format,
183
+ security settings, request body, and response handler) will be reloaded.
184
+
185
+ For example, you can define some shared headers at the client (root) level,
186
+ then add some scope/operation-specific headers later. Or you can define
187
+ security schema by default, and reload it for a specific operation.
188
+
189
+ - I found customization of underlying client an overkill. That's why
190
+ all clients will be based on the same old Net::HTTP(S) connection.
191
+
192
+ But the client connection is just the object with the only required method
193
+ `#call` taking rack-compatible env, and returning rack-compatible response.
194
+
195
+ You can define your own connection for a client:
196
+
197
+ ```ruby
198
+ my_connection = double call: [200, {}, []]
199
+
200
+ class MyClient
201
+ connection = my_connection
202
+ end
203
+ ```
204
+
205
+ - You can define a middleware for every single operation -- exactly
206
+ in the same way as other parts of operation specification.
207
+
208
+ All middleware will be used in the order of their definition:
209
+
210
+ ```ruby
211
+ class MyClient
212
+ middleware Foo
213
+
214
+ scope :users do
215
+ middleware { [Bar, Baz] }
216
+ end
217
+ end
218
+ ```
219
+
220
+ In the above example rack env will be sent to Foo -> Bar -> Baz -> Connection,
221
+ and rack response will be processed by Connection -> Baz -> Bar -> Foo.
222
+
223
+ - I've simplified some definitions in the OperationDSL.
224
+
225
+ Now you should define `headers` and `query` as a simple hashes (no helpers).
226
+ That hashes will be merged to upper-level ones (that's how we customize them).
227
+
228
+ You can also define body as either hash, or IO (for files), depending
229
+ on request format.
230
+
231
+ You have to define a format for operation via special `format` helper
232
+ (:json by default, :form, :text, :multipart are available as well):
233
+
234
+ ```
235
+ operation :upload do
236
+ option :multipart, default: proc { File.new "Hi!" }
237
+
238
+ format { :files } # :json (default), :text, and :form are supported as well
239
+ body { [StringIO.new, "Hi!"] }
240
+ end
241
+ ```
242
+
243
+ Because `format` takes a block, you can customize its value depending
244
+ on any option(s).
245
+
246
+ - Response handlers take a block with rack request: [status, headers, [body]]
247
+
248
+ There is no dsl for processing that responses (because it can be anything).
249
+ You don't need to provide models (but you can do it on your own), or
250
+ validate responses in any special way. Do your best!
251
+
252
+ - No more dependencies from both the `dry-types` and `evil-struct`.
253
+
254
+ ## [0.3.3] - [2017-07-14]
10
255
 
11
256
  ### Fixed
12
257
  - dependency from 'securerandom' standard library (nepalez)
@@ -16,8 +261,6 @@ On the road to v0.3.3
16
261
 
17
262
  # [0.3.2] - [2016-11-29]
18
263
 
19
- On the road to v0.4.0
20
-
21
264
  ### Fixed
22
265
  - Query and body encoding (nepalez)
23
266
 
@@ -36,7 +279,7 @@ On the road to v0.4.0
36
279
 
37
280
  This version changes the way of processing responses. Instead of dealing
38
281
  with raw rake responses, we add opinionated methods to gracefully process
39
- responses from JSON or plain text.
282
+ responses from JSON or form text.
40
283
 
41
284
  In the next minor versions processors for both "form" and "file" (multipart)
42
285
  formats will be added.
@@ -59,7 +302,7 @@ formats will be added.
59
302
 
60
303
  This time response handler will try processing a response using various
61
304
  definitions (in order of their declaration) until some suits. The hanlder
62
- returns `UnexpectedResponseError` in case no definition proves suitable.
305
+ returns `ResponseError` in case no definition proves suitable.
63
306
 
64
307
  Names (the first param) are unique. When several definitions use the same name,
65
308
  only the last one will be applicable.
@@ -83,8 +326,10 @@ formats will be added.
83
326
  response :not_found, 404, format: "json", raise: true
84
327
  ```
85
328
 
329
+ [1.0.0]: https://github.com/evilmartians/evil-client/compare/v0.3.3...v1.0.0
86
330
  [0.3.3]: https://github.com/evilmartians/evil-client/compare/v0.3.2...v0.3.3
87
331
  [0.3.2]: https://github.com/evilmartians/evil-client/compare/v0.3.1...v0.3.2
88
332
  [0.3.1]: https://github.com/evilmartians/evil-client/compare/v0.3.0...v0.3.1
89
333
  [Keep a Changelog]: http://keepachangelog.com/
90
334
  [Semantic Versioning]: http://semver.org/
335
+ [dry-initializer]: http://github.com/dry-rb/dry-initalizer
data/LICENSE.txt CHANGED
@@ -1,6 +1,8 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2016-2017 Andrew Kozin (nepalez), andrew.kozin@gmail.com
3
+ Copyright (c) 2015-2017 Andrew Kozin (nepalez),
4
+ Ravil Bairamgalin (brainopia),
5
+ Evil Martians (evilmartians)
4
6
 
5
7
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
8
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -13,15 +13,7 @@ Human-friendly DSL for writing HTTP(s) clients in Ruby
13
13
 
14
14
  ## Intro
15
15
 
16
- The gem allows writing http(s) clients in a way close to [Swagger][swagger] specifications. Like in Swagger, you need to specify models and operations in domain-specific terms. In addition, the gem supports settings and scopes for instantiating clients and sending requests in idiomatic Ruby.
17
-
18
- The gem stands away from mutable states and monkey patching when possible. To support multithreading all instances are immutable (though not frozen to avoid performance loss). Its DSL is backed on top of [dry-initializer][dry-initializer] gem, and supposes heavy usage of [dry-types][dry-types] system of contracts.
19
-
20
- For now the DSL supports clients to **json** and **form data** APIs out of the box. Because of high variance of XML-based APIs, building corresponding clients require more efforts on a middleware level.
21
-
22
- [swagger]: http://swagger.io
23
- [dry-initializer]: http://dry-rb.org/gems/dry-initializer
24
- [dry-types]: http://dry-rb.org/gems/dry-types
16
+ The gem allows writing http(s) clients in a way inspired by [Swagger][swagger] specifications. It stands away from mutable states and monkey patching when possible. To support multithreading all instances are immutable (though not frozen to avoid performance loss).
25
17
 
26
18
  ## Installation
27
19
 
@@ -45,97 +37,69 @@ $ gem install evil-client
45
37
 
46
38
  ## Synopsis
47
39
 
48
- The following example gives an idea of how a client to remote API looks like when written on top of `Evil::Client` using [dry-types][dry-types]-based contracts.
40
+ The following example gives an idea of how a client to remote API looks like when written on top of `Evil::Client`.
49
41
 
50
42
  ```ruby
51
43
  require "evil-client"
52
- require "dry-types"
53
44
 
54
45
  class CatsClient < Evil::Client
55
- # Prepare client-specific model of cat (the furry pinnacle of evolution)
56
- class Cat < Evil::Struct
57
- attribute :name, Dry::Types["strict.string"], optional: true
58
- attribute :color, Dry::Types["strict.string"]
59
- attribute :age, Dry::Types["coercible.int"], default: proc { 0 }
60
- end
61
-
62
- # Define settings the client initialized with
63
- # The settings parameterizes operations when necessary
64
- settings do
65
- param :domain, Dry::Types["strict.string"] # required!
66
- option :version, Dry::Types["coercible.int"], default: proc { 0 }
67
- option :user, Dry::Types["strict.string"] # required!
68
- option :password, Dry::Types["strict.string"] # required!
69
- end
70
-
71
- # Define a base url using
72
- base_url do |settings|
73
- "https://#{settings.domain}.example.com/api/v#{settings.version}/"
74
- end
46
+ # Define options for the client's initializer
47
+ option :domain, proc(&:to_s)
48
+ option :user, proc(&:to_s)
49
+ option :password, proc(&:to_s)
75
50
 
76
51
  # Definitions shared by all operations
77
- operation do |settings|
78
- security { basic_auth settings.user, settings.password }
79
- end
80
-
81
- # Operation-specific definition to update a cat by id
82
- # This provides low-level DSL `operations[:update_cat].call`
83
- operation :update_cat do |settings|
84
- http_method :patch
85
- path { |id:, **| "cats/#{id}" } # id will be taken from request parameters
86
-
87
- body format: "json" do
88
- attributes optional: true do
89
- attribute :name
90
- attribute :color
91
- attribute :age
92
- end
93
- end
94
-
95
- # Parses json response and wraps it into Cat instance with additional
96
- # parameter
97
- response 200, format: :json, model: Cat do
98
- attribute :success # add response-specific attribute to the cat
99
- end
100
-
101
- # Parses json response, wraps it into model with [#error] and raises
102
- # an exception where [ResponseError#response] contains the model istance
103
- response 422, format: :json, raise: true do
104
- attribute :error
105
- end
52
+ path { "https://#{domain}.example.com/api" }
53
+ security { basic_auth settings.user, settings.password }
106
54
 
107
- # Takes raw body and converts it into the hashie
108
- response 404, raise: true do |body|
109
- Hashie::Mash.new error: body
110
- end
111
- end
112
-
113
- # Add top-level DSL
114
55
  scope :cats do
115
- scope do |id|
116
- def find(**data)
117
- operations[:update_cat].call(id: id, **data)
56
+ # Scope-specific definitions
57
+ option :version, default: proc { 1 }
58
+ path { "v#{version}" } # subpath added to root path
59
+
60
+ # Operation-specific definitions to update a cat by id
61
+ operation :update do
62
+ option :id, proc(&:to_i)
63
+ option :name, optional: true
64
+ option :color, optional: true
65
+ option :age, optional: true
66
+
67
+ let(:data) { options.select { |key, _| %i(name color age).include? key } }
68
+ validate(:data_present) { !data.empty? }
69
+
70
+ path { "cats/#{id}" } # added to root path
71
+ http_method :patch # you can use plain syntax instead of a block
72
+ format "json"
73
+ body { options.reject { |key, _val| key == :id } }
74
+
75
+ # Parses json response and wraps it into Cat instance with additional
76
+ # parameter
77
+ response 200 do |(status, headers, body)|
78
+ # Suppose you define a model for cats
79
+ Cat.new JSON.parse(body)
118
80
  end
81
+
82
+ # Parses json response, wraps it into model with [#error] and raises
83
+ # an exception where [ResponseError#response] contains the model istance
84
+ response(400, 422) { |(status, *)| raise "#{status}: Record invalid" }
119
85
  end
120
86
  end
121
87
  end
122
88
 
123
- # Instantiate a client with concrete settings
124
- cat_client = CatClient.new "awesome-cats", # domain
125
- version: 1,
126
- user: "cat_lover",
89
+ # Instantiate a client with a concrete settings
90
+ cat_client = CatClient.new domain: "awesome-cats",
91
+ user: "cat_lover",
127
92
  password: "purr"
128
93
 
129
- # Use low-level DSL to send requests
130
- cat_client.operations[:update_cat].call id: 4,
131
- age: 10,
132
- name: "Agamemnon",
133
- color: "tabby"
94
+ # Use verbose low-level DSL to send requests
95
+ cat_client.scopes[:cats].new(version: 2)
96
+ .operations[:update].new(id: 4, age: 10, color: "tabby")
97
+ .call # sends request
134
98
 
135
99
  # Use top-level DSL for the same request
136
- cat_client.cats[4].call(age: 10, name: "Agamemnon", color: "tabby")
100
+ cat_client.cats(version: 2).update(id: 4, age: 10, color: "tabby")
137
101
 
138
- # Both the methods send `PATCH https://awesom-cats.example.com/api/v1/cats/7`
102
+ # Both the methods send `PATCH https://awesome-cats.example.com/api/v2/cats/4`
139
103
  # with a specified body and headers (authorization via basic_auth)
140
104
  ```
141
105
 
@@ -145,11 +109,13 @@ The gem is available as open source under the terms of the [MIT License](http://
145
109
 
146
110
  [codeclimate-badger]: https://img.shields.io/codeclimate/github/evilmartians/evil-client.svg?style=flat
147
111
  [codeclimate]: https://codeclimate.com/github/evilmartians/evil-client
112
+ [dry-initializer]: http://dry-rb.org/gems/dry-initializer
148
113
  [gem-badger]: https://img.shields.io/gem/v/evil-client.svg?style=flat
149
114
  [gem]: https://rubygems.org/gems/evil-client
150
115
  [gemnasium-badger]: https://img.shields.io/gemnasium/evilmartians/evil-client.svg?style=flat
151
116
  [gemnasium]: https://gemnasium.com/evilmartians/evil-client
152
117
  [inch-badger]: http://inch-ci.org/github/evilmartians/evil-client.svg
153
118
  [inch]: https://inch-ci.org/github/evilmartians/evil-client
119
+ [swagger]: http://swagger.io
154
120
  [travis-badger]: https://img.shields.io/travis/evilmartians/evil-client/master.svg?style=flat
155
121
  [travis]: https://travis-ci.org/evilmartians/evil-client