evil-client 0.3.3 → 1.0.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 (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
@@ -0,0 +1,93 @@
1
+ Use helpers `body` and `format` to define content of http message, and how it should be formatted.
2
+
3
+ We support several formats, namely: `:json` (default), `:text`, `:form`, `:yaml`, and `:multipart`.
4
+
5
+ ## Plain Formats
6
+
7
+ With default `:json` format, the body should be a hash:
8
+
9
+ ```ruby
10
+ class CatsAPI < Evil::Client
11
+ format { :json }
12
+ # ...
13
+ scope :cats do
14
+ # ...
15
+ operation :create do
16
+ option :species
17
+ option :name
18
+
19
+ body { { species: species, name: name } }
20
+ end
21
+ end
22
+ end
23
+ ```
24
+
25
+ Before sending to the stack of [middleware], it will be dumped:
26
+
27
+ ```ruby
28
+ CatsAPI.cats.create species: "Acinonyx jubatus", name: "Cheetah"
29
+ # sends a request with a body: '{"species":"Acinonyx jubatus","name":"Cheetah"}'
30
+ # and a header "Content-Type": "application/json"
31
+ ```
32
+
33
+ The same content, but formatted as `:yaml` will send another body and header:
34
+
35
+ ```ruby
36
+ class CatsAPI < Evil::Client
37
+ format { :yaml }
38
+ end
39
+
40
+ CatsAPI.cats.create species: "Acinonyx jubatus", name: "Cheetah"
41
+ # sends a request with a body: "---\n:species: Acinonyx jubatus\n:name: Cheetah\n'
42
+ # and a header "Content-Type": "application/yaml"
43
+ ```
44
+
45
+ The `:form` format will make body url encoded:
46
+
47
+ ```ruby
48
+ class CatsAPI < Evil::Client
49
+ format { :form }
50
+ end
51
+
52
+ CatsAPI.cats.create species: "Acinonyx jubatus", name: "Cheetah"
53
+ # sends a request with a body: "species=Acinonyx jubatus&name=Cheetah"
54
+ # and a header "Content-Type": "application/x-www-form-urlencoded"
55
+ ```
56
+
57
+ The `:text` just stringifies it as is:
58
+
59
+ ```ruby
60
+ class CatsAPI < Evil::Client
61
+ format { :text }
62
+ end
63
+
64
+ CatsAPI.cats.create species: "Acinonyx jubatus", name: "Cheetah"
65
+ # sends a request with a body: '{:species=>"Acionyx jubatus",:name=>"Cheetah"}'
66
+ # and a header "Content-Type": "text/plain"
67
+ ```
68
+
69
+ This format doesn't require the body to be hash. It can be anything.
70
+
71
+ ## Multipart Formats
72
+
73
+ When you need sending files you should select the `:multipart` format. This time the whole body is formatted as `form/multipart`.
74
+ Depending on its type (hash, file, StringIO), the content will be formatted correspondingly. Arrays are treated as several parts of the body. All other objects will be stringified.
75
+
76
+ ```ruby
77
+ class CatsAPI < Evil::Client
78
+ # ...
79
+ scope :cats do
80
+ # ...
81
+ operation :upload do
82
+ option :files, method(:Array)
83
+
84
+ format { :multipart }
85
+ body { files }
86
+ end
87
+ end
88
+ end
89
+
90
+ CatsAPI.cats.upload files: [StringIO.new("Cheetah"), StrinIO.new("Lion")]
91
+ ```
92
+
93
+ [middleware]:
@@ -0,0 +1,19 @@
1
+ By default, Evil Client uses [Net::HTTP][net-http]-based connection to send requests.
2
+
3
+ If you need another user agent, change it to you client via connection writer. The connection can be an object with method `#call` that takes [rack environment][rack-env] (from stack of middleware) and returns [rack response][rack-response] back.
4
+
5
+ ```ruby
6
+ conn = Object.new do
7
+ def self.call(env)
8
+ [200, {"Content-Type"=>"application/json"}, ['{"age":7}']]
9
+ end
10
+ end
11
+
12
+ class CatsClient < Evil::Client
13
+ connection = conn
14
+ end
15
+ ```
16
+
17
+ [net-http]: http://ruby-doc.org/stdlib-2.4.1/libdoc/net/http/rdoc/Net/HTTP.html
18
+ [rack-env]: http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Environment
19
+ [rack-response]: http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Response
@@ -0,0 +1,72 @@
1
+ Use method `headers` to add several headers to a request
2
+
3
+ ```ruby
4
+ class CatsClient < Evil::Client
5
+ operation :fetch do
6
+ option :id
7
+ option :language
8
+
9
+ headers { { "Accept-Language" => language } }
10
+ # ...
11
+ end
12
+ end
13
+ ```
14
+
15
+ Remember that you can define header values as a flat array instead of the string. Inside an array all the values are counted as srings.
16
+
17
+ ```ruby
18
+ headers { "Language" => ["ru_RU", "charset=utf-8"] }
19
+ ```
20
+
21
+ You can define headers bit-by-bit starting from a root scope:
22
+
23
+ ```ruby
24
+ class CatsClient < Evil::Client
25
+ option :charset, default: -> { "utf-8" }
26
+ headers { { "Accept-Charset" => charset } }
27
+
28
+ operation :fetch do
29
+ option :id
30
+ option :language
31
+
32
+ headers { { "Accept-Language" => language } }
33
+ # ...
34
+ end
35
+ end
36
+
37
+ CatsClient.new(charset: "ascii-1251").fetch(id: 3, language: "en_US")
38
+ # This would send a request with the headers:
39
+ # { "Accept-Charset" => "ascii-1251", "Accept-Language" => "en_US" }
40
+ ```
41
+
42
+ When you redefine some of root options, this redefinition can affects headers:
43
+
44
+ ```ruby
45
+ CatsClient.new(charset: "ascii-1251")
46
+ .fetch(id: 3, language: "en_US", charset: "utf-16")
47
+
48
+ # This would send a request with the headers:
49
+ # { "Accept-Charset" => "utf-16", "Accept-Language" => "en_US" }
50
+ ```
51
+
52
+ As a rule, you shouldn't define authorization headers in this way. Use [the security helper][security] instead.
53
+
54
+ **Remember** that eventual collection of request headers is also affected by [security][security] (sets `Authentication`), and [format][format] (sets `Content-Type`) helpers. You can add request headers via [middleware] as well. Finally, the [connection] adds some headers (like `User-Agent`) by its own.
55
+
56
+ When you define both headers and security settings at the same time, the priority will be given to security. This isn't depend on where (root scope or its sub-scopes) security and headers parts are defined. Security settings will always be written over the same headers.
57
+
58
+ ```ruby
59
+ class CatsAPI < Evil::Client
60
+ security { key_auth :Authentication, "Bar" }
61
+
62
+ scope :cats do
63
+ headers { { Authentication: "Foo" } }
64
+ # will set "Authentication" => "Bar" (not "Foo")
65
+ end
66
+ end
67
+ ```
68
+
69
+ [security]:
70
+ [format]:
71
+ [middleware]:
72
+ [connection]:
@@ -0,0 +1,39 @@
1
+ Define [http method] for sending a request using the `http_method` helper.
2
+
3
+ ```ruby
4
+ operation :fetch do
5
+ http_method :get
6
+ # ...
7
+ end
8
+ ```
9
+
10
+ As usual, you have access to current options. This can be useful to make the method dependent from either a version, or another variation of API.
11
+
12
+ ```ruby
13
+ operation :fetch do
14
+ # ...
15
+ option :version, proc(&:to_i)
16
+
17
+ http_method { version > 2 ? :post : :get }
18
+ # ...
19
+ end
20
+ ```
21
+
22
+ The definition can be reloaded at any level of scoping.
23
+
24
+ Following [RFC 7231], we support only valid methods (they could be set as case-insensitive stringified object):
25
+
26
+ - GET
27
+ - POST
28
+ - PUT
29
+ - PATCH
30
+ - DELETE
31
+ - OPTIONS
32
+ - HEAD
33
+ - TRACE
34
+ - CONNECT
35
+
36
+ Setting http method to another value, or missing it, will cause an exception.
37
+
38
+ [http method]:
39
+ [RFC 7231]: https://tools.ietf.org/html/rfc7231
@@ -0,0 +1,14 @@
1
+ Use `let` helper to add virtial memoized attributes to the scope/operation.
2
+
3
+ ```ruby
4
+ class CatsAPI < Evil::Client
5
+ option :version, default: proc { 0 }
6
+ option :release, default: proc { 1 }
7
+
8
+ let(:full_version) { [version, release].join(".") } # "0.1" by default
9
+ end
10
+ ```
11
+
12
+ These virtual attributes are available inside all block declarations, including [validations].
13
+
14
+ [validations]:
@@ -0,0 +1,24 @@
1
+ You can assign a logger to any instance of operation/scope.
2
+
3
+ As a rule you would do this assignment to the initialized client:
4
+
5
+ ```ruby
6
+ client = CatsClient.new(token: "foobar")
7
+ log = StringIO.new
8
+ logger = Logger.new log
9
+ ```
10
+
11
+ The client will publish debag messages to the log every time it sets instance of scope/operation schema, or initializes them with some options. Requests and responses are logged at the info level.
12
+
13
+ Later you can check the log:
14
+
15
+ ```ruby
16
+ client.cats.fetch id: 83
17
+
18
+ log.string
19
+ # D, [2017-07-30T22:23:50.262734 #23474] DEBUG -- CatsApi.cats: initializing with options {}...
20
+ # D, [2017-07-30T22:23:50.263240 #23474] DEBUG -- #<CatsApi.cats:0x000000034d2710 @token="foobar"> initialized
21
+ # D, [2017-07-30T22:23:50.262734 #23474] DEBUG -- CatsApi.cats.fetch: initializing with options {"token"=>"foobar", id"=>83}...
22
+ # D, [2017-07-30T22:23:50.263240 #23474] DEBUG -- #<CatsApi.cats.fetch:0x000000034d2840 @token="foobar", "id"=83> initialized
23
+ # ...
24
+ ```
@@ -0,0 +1,56 @@
1
+ Evil client supports stack of rack-compatible middleware which is specific for every scope and operation.
2
+
3
+ As usual, every middleware is just an object wrapped around some application. It should:
4
+
5
+ - initialize with the only parameter `app` for the rest of stack, wrapped around connection,
6
+ - define the only instance method `#call` which takes a [rack environment], and returns a [rack response].
7
+
8
+ The example of valid middleware which adds the tag "FOO" to the request header:
9
+
10
+ ```ruby
11
+ class Foo
12
+ def call(env)
13
+ env["HTTP_Variables"]["Tag"] = "FOO"
14
+ @app.call env
15
+ end
16
+
17
+ private
18
+
19
+ def initialize(app)
20
+ @app = app
21
+ end
22
+ end
23
+ ```
24
+
25
+ Use the `middleware` helper method to add a specific class to a stack. You can do this at any level of nesting:
26
+
27
+ ```ruby
28
+ class CatsAPI < Evil::Client
29
+ # ...
30
+ middleware Foo
31
+
32
+ scope :cats do
33
+ option :version, proc(&:to_i)
34
+ # ...
35
+ middleware { version < 2 ? Bar : [Bar, Baz] }
36
+
37
+ operation :fetch do
38
+ middleware { Qux }
39
+ # ...
40
+ end
41
+ end
42
+ end
43
+
44
+ # Depending on version, this will send rack request to either
45
+ # [Foo, Bar, Qux, Evil::Client::Connection] (versions 1-)
46
+ # [Foo, Bar, Baz, Qux, Evil::Client::Connection] (versions 2+)
47
+ #
48
+ # The response will be processed in the reverse order
49
+ ```
50
+
51
+ You can place the same class to the stack several times.
52
+
53
+ The order of definitions is important. The request is processed by middleware in the order from root to operation, and the response will be processed in reverse order -- from operation to the root.
54
+
55
+ [rack environment]: http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Environment
56
+ [rack response]: http://www.rubydoc.info/github/rack/rack/file/SPEC#The_Response
@@ -0,0 +1,103 @@
1
+ Operation defines a specific request to a remote API with some helpers to process its responses.
2
+
3
+ To specify an operation, you should define the following parts:
4
+
5
+ - [options] of the request sender
6
+ - http(s) [path] to the remote server
7
+ - [http method] for sending requests
8
+ - [security] definitions describing what credentials the request should carry
9
+ - http [headers] to include into the request (along with security-related ones)
10
+ - request [query] to be added to the request path (along with security-related)
11
+ - a [content][body] of the request and a [format][body] describing the content should be formatted
12
+ - a set of [middleware] that should be added to the remote [connection]
13
+ - a set of expected [responses] with corresponding handlers
14
+
15
+ An operation is specified with a unique name inside the corresponding [scope].
16
+
17
+ You can add it to the root of the client, or to any subscope:
18
+
19
+ ```ruby
20
+ class CatsClient < Evil::Client
21
+ # Returns current information about the client
22
+ operation :info do
23
+ # ...
24
+ end
25
+
26
+ scope :cats do
27
+ # ...
28
+
29
+ # Fetches information about a specific cat
30
+ operation :fetch do
31
+ # ...
32
+ end
33
+ end
34
+ end
35
+ ```
36
+
37
+ Operation-specific definitions should be made inside the block. They will affect only this operation:
38
+
39
+ ```ruby
40
+ class CatsClient < Evil::Client
41
+ # ...
42
+ scope :cats do
43
+ # ...
44
+ operation :fetch do
45
+ option :id
46
+
47
+ path { id }
48
+ http_method :get
49
+ response 200
50
+ response(400, 422) { |(status, *)| raise "#{status}: Wrong request" }
51
+ response(404) { raise "404: Not found" }
52
+ end
53
+ end
54
+ end
55
+ ```
56
+
57
+ Besides operation-specific settings, you can add same definitions for a scope. This definitions are shared by all operations of the scope and its sub-scopes on any level of nesting. Any sub-scope or operation can reload this shared definitions, or update it with those of its own.
58
+
59
+ ```ruby
60
+ class CatsClient < Evil::Client
61
+ # ...
62
+ scope :cats do
63
+ path { "cats" } # relative to root scope
64
+ http_method :get
65
+ response 200
66
+ response(400, 422) { |(status, *)| raise "#{status}: Wrong request" }
67
+ response(404) { raise "404: Not found" }
68
+
69
+ operation :fetch do
70
+ option :id
71
+ path { id } # relative to upper scope
72
+ end
73
+ end
74
+ end
75
+ ```
76
+
77
+ The user of custom client sends a request by invoking some operation by name on a corresponding scope.
78
+
79
+ ```ruby
80
+ client = CatsClient.new
81
+ cats = client.cats # scope for the `fetch` operaton
82
+
83
+ cats.fetch id: 44 # sends request and returns a processed response
84
+ ```
85
+
86
+ Alternatively you can initialize the operation first, and call it later:
87
+
88
+ ```ruby
89
+ operation = cats.operations[:fetch].new(id: 44)
90
+ operation.call
91
+ ```
92
+
93
+ [options]:
94
+ [path]:
95
+ [http method]:
96
+ [security]:
97
+ [headers]:
98
+ [query]:
99
+ [body]:
100
+ [middleware]:
101
+ [connection]:
102
+ [responses]:
103
+ [scope]:
@@ -0,0 +1,50 @@
1
+ Use the `option` helper to add options to some scope or operation.
2
+
3
+ ```ruby
4
+ class CatsClient < Evil::Client
5
+ # Define options for the client's initializer
6
+ option :domain, proc(&:to_s)
7
+ option :user, proc(&:to_s)
8
+ option :password, proc(&:to_s), optional: true
9
+ option :token, proc(&:to_s), optional: true
10
+ # ...
11
+
12
+ scope :cats do
13
+ # Scope-specific options
14
+ option :version, default: proc { 1 }
15
+
16
+ # Operation-specific options
17
+ operation :fetch do
18
+ option :id, proc(&:to_i)
19
+ end
20
+ end
21
+ end
22
+ ```
23
+
24
+ The helper is taken from [dry-initializer] gem, so you can see [the gem's documentation][dry-initializer-docs] for the details of its syntax. Notice, that evil-client doesn't support positional arguments (aka `param`-s).
25
+
26
+ All declarations made at a root scope, or any ancestor scope, are available at the nested levels.
27
+ Options assigned during client/scope/operation instantiation are accumulated:
28
+
29
+ ```ruby
30
+ client = CatsClient.new domain: "wild", user: "andy"
31
+ client.options # => { domain: "wild", user: "andy" }
32
+ client.domain # => "wild"
33
+ client.user # => "andy"
34
+
35
+ cats = client.cats(version: 3)
36
+ cats.options # => { domain: "wild", user: "andy", version: 3 }
37
+
38
+ fetch = cats.operations[:fetch].new(id: 7)
39
+ fetch.options # => { domain: "wild", user: "andy", version: 3, id: 7 }
40
+ ```
41
+
42
+ You can define any assigned option at any level of nesting:
43
+
44
+ ```ruby
45
+ fetch = cats.operations[:fetch].new(id: 7, domain: "domestic")
46
+ fetch.options # => { domain: "domestic", user: "andy", version: 3, id: 7 }
47
+ ```
48
+
49
+ [dry-initializer] https://github.com/dry-rb/dry-initializer
50
+ [dry-initializer-docs]: http://dry-rb.org/gems/dry-initializer/