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
data/docs/base_url.md DELETED
@@ -1,38 +0,0 @@
1
- Use `base_url` to define a base url, [operation paths][path] are relative to.
2
-
3
- You can use [`settings`][settings] as the only argument of the declaration block.
4
-
5
- ```ruby
6
- require "evil-client"
7
- require "dry-types"
8
-
9
- class CatsClient < Evil::Client
10
- settings do
11
- option :version, type: Dry::Types["coercible.int"], default: proc { 1 }
12
- end
13
-
14
- base_url do |settings|
15
- "https://cats.example.com/v#{settings.version}"
16
- end
17
-
18
- operation :find_cats do |_settings|
19
- http_method :get
20
- path { "cats" }
21
- end
22
- end
23
- ```
24
-
25
- After a client's instantiation...
26
-
27
- ```ruby
28
- client = CatsClient.new version: 3
29
- ```
30
-
31
- ...the following call will send a request `GET https://example.com/v3/cats`.
32
-
33
- ```ruby
34
- client.operations[:find_cat].call
35
- ```
36
-
37
- [settings]:
38
- [path]:
@@ -1,9 +0,0 @@
1
- Use the `documentation` to provide reference to online docs for the current operation.
2
-
3
- ```ruby
4
- operation :find_cat do |settings| # remember that you have access to settings
5
- documentation "https://cats.example.com/v#{settings.version}/docs/find_cats"
6
- end
7
- ```
8
-
9
- The link will be shown in exceptions risen when either request or response mismatches type constraints.
data/docs/headers.md DELETED
@@ -1,59 +0,0 @@
1
- Use method `headers` to add several headers to a request. The declaration should have a block with several `attributes` describing corresponding headers.
2
-
3
- ```ruby
4
- operation :find_cat do |settings|
5
- headers do
6
- attribute :token if settings.version > 1
7
- attribute :id
8
- end
9
- end
10
- ```
11
-
12
- The syntax of the attribute declaration is exactly the same as of [Evil::Struct][model]. Type constraints and default values are available.
13
-
14
- All values for the headers will be taken from a request options:
15
-
16
- ```ruby
17
- # Sends a request with headers { "id" => 43 }
18
- client.options[:find_cat].call id: 43
19
- ```
20
-
21
- As a rule, you shouldn't define authorization headers in this way. Use [the security method][security] instead.
22
-
23
- Default headers can be declared for every request via anonymous operation. **Notice** that a default headers can be reloaded for specific operation as a whole. New declaration will overwrite all the default set headers instead of merging to them.
24
-
25
- ```ruby
26
- operation do
27
- headers do
28
- attribute :id
29
- end
30
- end
31
-
32
- operation :find_cat do
33
- headers do
34
- attribute :cat_id
35
- end
36
- end
37
-
38
- # later at the runtime the following call
39
- # will send request with { "cat_id" => 4 } header only
40
- client.operations[:find_cat].call id: 1, cat_id: 4
41
- ```
42
-
43
- According to [RFC-2616][rfc-2616], headers are case-insensitive. Inside [middleware][middleware] they are forced to lower case.
44
-
45
- For example, while the following declaration is valid by itself, only value from `Foo` option will be sent to remote server:
46
-
47
- ```ruby
48
- operation do
49
- headers do
50
- attribute :foo
51
- attribute :Foo
52
- end
53
- end
54
- ```
55
-
56
- [rfc-2616]: https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
57
- [security]:
58
- [model]:
59
- [middleware]:
data/docs/http_method.md DELETED
@@ -1,31 +0,0 @@
1
- Use `http_method` to define it for the current operation.
2
-
3
- ```ruby
4
- operation :find_cat do
5
- http_method :get
6
- end
7
- ```
8
-
9
- As usual, you have access to current settings. This can be useful to make the method dependent from either a version, or other variation of the api.
10
-
11
- ```ruby
12
- operation :find_cat do |settings|
13
- http_method settings.version > 2 ? :post : :get
14
- end
15
- ```
16
-
17
- You can also set a default method for all operations. It can be reloaded later:
18
-
19
- ```ruby
20
- operation do
21
- http_method :get
22
- end
23
-
24
- operation :find_cat do
25
- # sends requests via get
26
- end
27
-
28
- operation :update_cat do
29
- http_method :patch
30
- end
31
- ```
data/docs/model.md DELETED
@@ -1,173 +0,0 @@
1
- Models are simple nested structures, based on [dry-initializer][dry-initializer] and [dry-types][dry-types].
2
-
3
- They are needed to prepare and validate nested bodies and queries, as well as wrap and validate responses.
4
-
5
- # Model Definition
6
-
7
- To define a model create a subclass of `Evil::Struct` and define its attributes.
8
-
9
- ```ruby
10
- class Cat < Evil::Struct
11
- attribute :name, type: Dry::Types["strict.string"], optional: true
12
- attribute :age, type: Dry::Types["coercible.int"], default: proc { 0 }
13
- attribute :color, type: Dry::Types["strict.string"]
14
- end
15
- ```
16
-
17
- The method `attribute` is just an alias of [dry-initializer `option`][dry-initializer]. Because model's constructor takes options only, not params, `param` is reloaded as another alias of `option`. You can use any method you like.
18
-
19
- To initialize an instance send a hash of options to the constructor:
20
-
21
- ```ruby
22
- cat = Cat.new(name: "Navuxodonosor II", age: "15", color: "black")
23
- cat.name # => "Navuxodonosor II"
24
- cat.age # => 15
25
- cat.color # => "black"
26
- ```
27
-
28
- You can build a model from another one (it just returns the object back):
29
-
30
- ```ruby
31
- Cat.new Cat.new(name: "Navuxodonosor II", age: 15, color: "black")
32
- ```
33
-
34
- or from a hash with string keys:
35
-
36
- ```ruby
37
- Cat.new("name" => "Navuxodonosor II", "age" => 15, "color" => "black")
38
- ```
39
-
40
- You can use other (nested) models in type definitions:
41
-
42
- ```ruby
43
- class CatPack < Evil::Struct
44
- attribute :cats, type: Dry::Types["array"].member(Cat)
45
- end
46
-
47
- CatPack.new cats: [{ name: "Navuxodonosor II", age: 15, color: "black" }]
48
- ```
49
-
50
- Models can be converted back to hashes with **symbolic** keys:
51
-
52
- ```ruby
53
- cat = Cat.new(name: "Navuxodonosor II", age: "15", color: "black")
54
- cat.to_h # => { name: "Navuxodonosor II", age: "15", color: "black" }
55
- ```
56
-
57
- The model ignores all options it doesn't know about, and applies constraints to known ones only.
58
-
59
- ```ruby
60
- # Cats don't care about your expectations
61
- cat = Cat.new(name: "Navuxodonosor II", age: "15", color: "black", expectation: "hunting")
62
- cat.to_h # => { name: "Navuxodonosor II", age: "15", color: "black" }
63
- ```
64
-
65
- If all you need is data filtering, just use a shortcut `.[]`:
66
-
67
- ```ruby
68
- Cat[name: "Navuxodonosor II", age: "15", color: "black", expectation: "hunting"]
69
- # => { name: "Navuxodonosor II", age: "15", color: "black" }
70
- ```
71
-
72
- # Model Usage
73
-
74
- You can use models in definitions of request [body][body], [query][query], and [headers][headers]...
75
-
76
- ```ruby
77
- operation :create_cat do
78
- method :post
79
- path { "cats" }
80
- body model: Cat
81
- end
82
- ```
83
-
84
- ...and in [response][response] processing:
85
-
86
- ```ruby
87
- operation :create_cat do
88
- # ...
89
- response 201 do |body:, **|
90
- Cat[JSON.parse(body)]
91
- end
92
- end
93
- ```
94
-
95
- # Distinction of Models from Dry::Struct
96
-
97
- Models are like ~~onions~~ structures, defined in [`dry-struct`][dry-struct]. Both models and structures support hash arguments, type constraints, nested data, and backward hashification via `to_h`. You can check [dry-struct documentation] to make an impression of how it works.
98
-
99
- Nethertheless, there is an important difference between the implementations of nested structures.
100
-
101
- ## Undefined Values vs nils
102
-
103
- The main reason to define gem-specific model is the following. In `Dry::Struct` both the `optional` and `default` properties belong to value type constraint. The gem does not draw the line between attributes that are not set, and those that are set to `nil`.
104
-
105
- To the contrary, in [dry-initializer][dry-initializer] and `Evil::Struct` both `optional` and `default` properties describe not a value type by itself, but its relation to the model. An attribute value can be set to `nil`, or been kept in undefined state.
106
-
107
- Let's see the difference on the example of `StructCat` and `ModelCat`:
108
-
109
- ```ruby
110
- class StructCat < Dry::Struct
111
- attribute :name, type: Dry::Types["strict.string"].optional
112
- attribute :age, type: Dry::Types["coercible.int"].default(0)
113
- end
114
-
115
- class ModelCat < Evil::Struct
116
- attribute :name, type: Dry::Types["strict.string"], optional: true
117
- attribute :age, type: Dry::Types["coercible.int"], default: proc { 0 }
118
- end
119
-
120
- struct_cat = StructCat.new
121
- struct_cat.name # => nil
122
- struct_cat.age # => 0
123
- struct_cat.to_h # => { name: nil, age: 0 }
124
-
125
- model_cat = ModelCat.new
126
- model_cat.name # => #<Dry::Initializer::UNDEFINED>
127
- model_cat.age # => 0
128
- model_cat.to_h # => { age: 0 }
129
-
130
- model_cat = ModelCat.new name: nil
131
- model_cat.name # => nil
132
- model_cat.age # => 0
133
- model_cat.to_h # => { name: nil, age: 0 }
134
- ```
135
-
136
- Notice that in a model hashification ignores undefined attributes. This is important to filter arguments of a request. In PUT/PATCH requests (update server-side data) there is a difference between values not changed, and those that are explicitly reset to `nil`.
137
-
138
- ## Tolerance to Unknown Options
139
-
140
- A model's constructor ignores unknown options, so you can safely sent any ones:
141
-
142
- ```ruby
143
- # Oh, no, this is a cat, not a bat!
144
- model_cat = ModelCat.new name: "Abraham", flying_distance: "5 miles"
145
-
146
- # so we simply ignore flying_distance
147
- model_cat.to_h # => { name: "Abraham", age: 0 }
148
- ```
149
-
150
- This behaviour allows us to slice only necessary arguments for [body][body], [query][query], and [headers][headers] of a request.
151
-
152
- ## Stringified Keys in Constructor
153
-
154
- Ahother difference between structs and models is that models can take hashes with both symbolic and string keys.
155
-
156
- This addition is useful when processing [responses][response]:
157
-
158
- ```ruby
159
- # This works even though JSON#parse returns a hash with string keys
160
- ModelCat.new JSON.parse('{"age":4}')
161
- ```
162
-
163
- ## Equality
164
-
165
- Models, whose methods `to_h` returns equal hashes, are counted as equal.
166
-
167
- [dry-initializer]: http://dry-rb.org/gems/dry-initializer
168
- [dry-struct]: http://dry-rb.org/gems/dry-struct
169
- [dry-types]: http://dry-rb.org/gems/dry-types
170
- [body]:
171
- [headers]:
172
- [query]:
173
- [response]:
data/docs/operation.md DELETED
File without changes
data/docs/overview.md DELETED
File without changes
data/docs/path.md DELETED
@@ -1,48 +0,0 @@
1
- Use `path` to define operation's path that is relative to the [base url][base_url].
2
-
3
- ```ruby
4
- operation :find_cats do
5
- path { "cats" }
6
- end
7
- ```
8
-
9
- Notice that a value should be wrapped into the block. This is necessary to build paths dependent on arguments of the request. The following definition inserts a mandatory id from options:
10
-
11
- ```ruby
12
- operation :find_cat do
13
- path { |id:, **| "cats/#{id}" }
14
- end
15
-
16
- # later at a runtime
17
- client.operations[:find_cat].call id: 98 # sends to "/cats/98"
18
- ```
19
-
20
- As usual, you have access to current settings. This can be useful to add client tokens to paths when necessary:
21
-
22
- ```ruby
23
- operation :find_cats do |settings|
24
- path { "cats/#{settings.token}" }
25
- end
26
- ```
27
-
28
- ## Default Path
29
-
30
- You can set a default path for all operations. Use it to DRY clients whose operations differs not by endpoints, but, for example, by parameters ([query][query], [body][body]) of various requests:
31
-
32
- ```ruby
33
- operation do
34
- path { "cats" }
35
- end
36
-
37
- operation :find_cats do
38
- # sends requests to "/cats"
39
- end
40
-
41
- operation :find_details do
42
- path { "cats/details" } # reloads default setting
43
- end
44
- ```
45
-
46
- [base_url]:
47
- [query]:
48
- [body]:
data/docs/query.md DELETED
@@ -1,99 +0,0 @@
1
- Use `query` to add some data to the request query. The syntax is pretty the same as for [body][body] and [headers][headers].
2
-
3
- ```ruby
4
- operation :find_cat do |settings|
5
- # ...
6
- path { "cats" }
7
- query do
8
- attribute :token, default: proc { settings.token }
9
- attribute :id
10
- end
11
- end
12
-
13
- # Later at the runtime it will send a request to "../cats?id=4&token=foo"
14
- client.operations[:find_cat].call id: 4, token: "foo"
15
- ```
16
-
17
- ## Nested Data Representation
18
-
19
- Nested data are represented in a query following Rails convention:
20
-
21
- ```ruby
22
- client.operations[:find_cat].call id: [{ key: 4 }], token: ["foo"]
23
- # "/cats?id[][key]=4&token[]=foo"
24
- ```
25
-
26
- Non-unicode symbols are encoded as defined in [RFC-3986][rfc-3986]
27
-
28
- ## Model-Based Queries
29
-
30
- Use [models][model] to provide validation of query data:
31
-
32
- ```ruby
33
- class Cat < Evil::Struct
34
- attribute :name, type: Dry::Types["strict.string"], optional: true
35
- attribute :age, type: Dry::Types["strict.int"], default: proc { 0 }
36
- attribute :color, type: Dry::Types["strict.string"]
37
- end
38
- ```
39
-
40
- You can either restrict `type` of an attribute:
41
-
42
- ```ruby
43
- operation :create_cat do
44
- query do
45
- attribute :cat, type: Cat
46
- end
47
- end
48
-
49
- # Later at runtime will send "...?cat[color]=tabby&cat[age]=0"
50
- client.operations[:create_cat].call cat: { color: "tabby" }
51
- ```
52
-
53
- ...or use in for the query as a whole under the `model` key:
54
-
55
- ```ruby
56
- operation :create_cat do
57
- query model: Cat
58
- end
59
-
60
- # Later at runtime will send "...?color=tabby&age=0"
61
- client.operations[:create_cat].call color: "tabby"
62
- ```
63
-
64
- In the last case you can define additional attributes (this redefinition is local, it don't affect a model by itself):
65
-
66
- ```ruby
67
- operation :create_cat do
68
- query model: Cat do
69
- attribute :mood, default: proc { "sleeping" }
70
- end
71
- end
72
-
73
- # Later at runtime will send "...?color=tabby&age=0&mood=sleeping"
74
- client.operations[:create_cat].call color: "tabby"
75
- ```
76
-
77
- **Be careful!** You cannot reload existing attributes (this will cause an exception).
78
-
79
- In operations that update remote data you can skip some attributes (mark them `optional`). If you need to check responses strictly (to require all the necessary attributes), you should provide different models.
80
-
81
- ```ruby
82
- # Requires remote server to return consistent beasts
83
- class Cat
84
- attribute :id, type: Dry::Types["strict.int"].constrained(gt: 0)
85
- attribute :age, type: Dry::Types["strict.int"]
86
- attribute :name, type: Dry::Types["strict.string"]
87
- end
88
-
89
- # Allows updating attributes when necessary
90
- class CatUpdate
91
- attribute :age, type: Dry::Types["coercible.int"], optional: true
92
- attribute :name, type: Dry::Types["coercible.string"], optional: true
93
- end
94
- ```
95
-
96
- [rfc-3986]: https://tools.ietf.org/html/rfc3986
97
- [body]:
98
- [headers]:
99
- [model]: