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
data/docs/responses.md DELETED
@@ -1,66 +0,0 @@
1
- For every operation you have to describe all expected responses and how they should be processed.
2
-
3
- Use the `response` method with anexpected http response status(es):
4
-
5
- ```ruby
6
- operation :find_cat do
7
- # ...
8
- response 200, 201
9
- end
10
- ```
11
-
12
- This definition tells a client to accept responses with given statuses, and to return an instance of `Rack::Response`.
13
-
14
- ```ruby
15
- client.operations[:find_cat].call
16
- # => #<Rack::Response @code=200 ...>
17
- ```
18
-
19
- ## Data Coersion
20
-
21
- Instead of returning a raw rack response, you can coerce it using a block. The block will take 3 options, namely the response, its body and headers:
22
-
23
- ```ruby
24
- operation :find_cat do |settings| # remember that you have access to settings
25
- # ...
26
- response 200 do |response:, body:, headers:|
27
- JSON.parse(body) if settings.format == "json"
28
- end
29
- end
30
-
31
- # later at a runtime
32
- client.operations[:find_cat].call
33
- # => { name: "Bastet", age: 10 }
34
- ```
35
-
36
-
37
-
38
- ## Raising Exceptions
39
-
40
- When processing responces with error statuses you may need to raise an exception instead of returning values. Do this using option `raise: true`
41
-
42
- ```ruby
43
- operation :find_cat do
44
- # ...
45
- response 422, raise: true
46
- end
47
- ```
48
-
49
- This time the operation will raise a `Evil::Client::ResponseError` (inherited from the `RuntimeError`). The exception carries a rack response:
50
-
51
- ```ruby
52
- begin
53
- client.operations[:find_cat].call
54
- rescue Evil::Client::ResponseError => error
55
- error.response
56
- # => #<Rack::Response @code=422 ...>
57
- end
58
- ```
59
-
60
- Like before, you can add a block to handle the response. In this case an exception will carry a result of the block.
61
-
62
- ## Unexpected Responses
63
-
64
- In case the server responded with undefined status, the operation raises `Evil::Client::UnexpectedResponseError` (inherited from the `RuntimeError`) that carries a rack response just like the `Evil::Client::ResponseError` before.
65
-
66
- Notice that you can declare default responses using anonymous `operation {}` syntax. Only those responces that are declared neither by default, nor for a specific operation, will cause unexpected response behaviour.
data/docs/security.md DELETED
@@ -1,102 +0,0 @@
1
- Use `security` declaration for the authorization schema. Inside the block you have access to 3 methods:
2
- * `basic_auth`
3
- * `token_auth`
4
- * `key_auth`
5
-
6
- ## Basic Authentication
7
-
8
- Use `basic_auth(login, password)` to define [basic authentication following RFC-7617][basic_auth]:
9
-
10
- ```ruby
11
- operation :find_cat do |settings|
12
- security do
13
- basic_auth settings.login, settings.password
14
- end
15
- end
16
- ```
17
-
18
- This declaration with add a header `"Authentication" => "Basic {encoded token}"` to every request. The header is added independenlty of declaration for other [headers][headers].
19
-
20
- ## Token Authentication
21
-
22
- The command `token_auth(token, **options)` allows you to insert a customizable token to any part of the request. Unlike `basic_auth`, you need to provide the token (build, encrypt etc.) by hand.
23
-
24
- ```ruby
25
- operation :find_cat do |settings|
26
- security do
27
- token_auth settings.token
28
- end
29
- end
30
- ```
31
-
32
- By default the token is added to `"Authentication" => {token}` header of the request. You can prepend it with a necessary prefix. For example, you can define a [Bearer token authentication following RFC-6750][bearer]:
33
-
34
- ```ruby
35
- operation :find_cat do |settings|
36
- security do
37
- token_auth settings.token, prefix: "Bearer"
38
- end
39
- end
40
- ```
41
-
42
- Instead of headers, you can send a token in either request body, or a query. In this case the token will be sent under `access_key` ignoring a prefix:
43
-
44
- ```ruby
45
- operation :find_cat do |settings|
46
- path { "/cats" }
47
- security do
48
- token_auth settings.token, using: :query
49
- end
50
- end
51
-
52
- # will send a request to "../cats?access_key={token}"
53
- ```
54
-
55
- ## Authentication Using Arbitrary Key
56
-
57
- The most customizeable option is to authenticate requests with an arbitrary key. This time key-value pair will be added to the selected part (`headers`, `body`, or `query`) of the request:
58
-
59
- ```ruby
60
- operation :find_cat do |settings|
61
- path { "/cats" }
62
- security do
63
- key_auth :accss_key, settings.token, using: :query
64
- end
65
- end
66
- ```
67
-
68
- ## Authentication Using Several Schemes
69
-
70
- You can define several schemes for the same request. All of them will be applied at once:
71
-
72
- ```ruby
73
- operation :find_cat do |settings|
74
- security do
75
- basic_auth settings.login, settings.password
76
- token_auth settings.token, using: :query
77
- end
78
- end
79
- ```
80
-
81
- Moreover, you can declare shared authentication by default, and either update, or reload it for a specific operation:
82
-
83
- ```ruby
84
- operation do |settings|
85
- security { basic_auth settings.login, settings.password }
86
- end
87
-
88
- operation :find_cat do |settings|
89
- security { token_auth settings.token, using: :query } # added to default security
90
- end
91
-
92
- operation :find_cats do |settings|
93
- security { token_auth settings.token } # reloads default "Authentication" header
94
- end
95
- ```
96
-
97
-
98
- [basic_auth]: https://tools.ietf.org/html/rfc7617
99
- [bearer]: https://tools.ietf.org/html/rfc6750
100
- [headers]:
101
- [body]:
102
- [query]:
data/docs/settings.md DELETED
@@ -1,32 +0,0 @@
1
- Use `settings` to parameterize an instance of the client.
2
-
3
- Inside the block you can define both `param`s and `option`s for a client constructor. See [dry-initializer docs][dry-initializer] for detailed description of the methods' syntax.
4
-
5
- ```ruby
6
- require "evil-client"
7
- require "dry-types"
8
-
9
- class CatsClient < Evil::Client
10
- settings do
11
- param :roor_url
12
- option :version, type: Dry::Types["coercible.int"], default: proc { 1 }
13
- option :login, type: Dry::Types["strict.string"] # required
14
- option :password, type: Dry::Types["strict.string"] # required
15
- end
16
- end
17
- ```
18
-
19
- Now you can initialize a client:
20
-
21
- ```ruby
22
- client = CatsClient.new "https://cats.example.com",
23
- login: "cats_lover",
24
- password: "purr"
25
- ```
26
-
27
- A container with assigned settings will be passed to blocks declaring [base_url][base_url], [connection][connection], and [operations][operation].
28
-
29
- [base_url]:
30
- [connection]:
31
- [operation]:
32
- [dry-initializer]: http://dry-rb.org/gems/dry-initializer
@@ -1,57 +0,0 @@
1
- require "net/http"
2
- require "net/https"
3
-
4
- class Evil::Client
5
- class Connection
6
- # Net::HTTP based implementation of [Evil::Client::Connection]
7
- class NetHTTP < Connection
8
- # Sends a request to the remote uri,
9
- # and returns rack-compatible response
10
- #
11
- # @param [Hash] env Middleware environment with keys:
12
- # :http_method, :path, :query_string, :body_string, :headers
13
- # @return [Array] Rack-compatible response [status, body, headers]
14
- #
15
- def call(env, *)
16
- request = build_request(env)
17
- Net::HTTP.start base_uri.host, base_uri.port, opts do |http|
18
- handle http.request(request)
19
- end
20
- end
21
-
22
- private
23
-
24
- def opts
25
- @opts ||= {}.tap { |hash| hash[:use_ssl] = base_uri.scheme == "https" }
26
- end
27
-
28
- def build_request(env)
29
- type, path, query, body, headers = parse_env(env)
30
-
31
- sender = build_sender(type)
32
- uri = build_uri(path, query)
33
-
34
- sender.new(uri).tap do |request|
35
- request.body = body
36
- headers.each { |key, value| request[key] = value }
37
- end
38
- end
39
-
40
- def parse_env(env)
41
- env.values_at :http_method, :path, :query_string, :body_string, :headers
42
- end
43
-
44
- def build_sender(type)
45
- Net::HTTP.const_get type.capitalize
46
- end
47
-
48
- def build_uri(path, query)
49
- base_uri.merge(URI.encode(path)).tap { |uri| uri.query = query }
50
- end
51
-
52
- def handle(response)
53
- [response.code.to_i, Hash(response.header), Array(response.body)]
54
- end
55
- end
56
- end
57
- end
@@ -1,127 +0,0 @@
1
- class Evil::Client
2
- # Defines a DSL to customize class-level settings of the specific client
3
- module DSL
4
- require_relative "dsl/base"
5
- require_relative "dsl/files"
6
- require_relative "dsl/headers"
7
- require_relative "dsl/http_method"
8
- require_relative "dsl/path"
9
- require_relative "dsl/query"
10
- require_relative "dsl/response"
11
- require_relative "dsl/responses"
12
- require_relative "dsl/security"
13
- require_relative "dsl/verifier"
14
- require_relative "dsl/operation"
15
- require_relative "dsl/operations"
16
- require_relative "dsl/scope"
17
-
18
- # Adds [#operations] to a specific client's instances
19
- def self.extended(klass)
20
- klass.include Dry::Initializer.define -> do
21
- param :settings
22
- param :base_url
23
- param :operations
24
- end
25
- end
26
-
27
- # Helper to define params and options a for a client's constructor
28
- #
29
- # @example
30
- # class MyClient < Evil::Client
31
- # end
32
- #
33
- # MyClient.new "https://foo.com", user: "bar", token: "baz"
34
- #
35
- # @param [Proc] block
36
- # @return [self]
37
- #
38
- def settings(&block)
39
- return self unless block
40
- schema[:settings] = Class.new { include Dry::Initializer.define(&block) }
41
- self
42
- end
43
-
44
- # Helper to define base url of the server
45
- #
46
- # @param [#to_s] value
47
- # @return [self]
48
- #
49
- def base_url(&block)
50
- return self unless block
51
- schema[:base_url] = block
52
- self
53
- end
54
-
55
- # Helper specify a connection to be used by a client
56
- #
57
- # @param [#to_sym] type (nil)
58
- # The specific type of connection. Uses NetHTTP by default.
59
- # @return [self]
60
- #
61
- def connection(type = nil, &block)
62
- schema[:connection] = Connection[type]
63
- schema[:middleware] = Middleware.new(&block)
64
- self
65
- end
66
-
67
- # Helper to declare operation, either default or specific
68
- #
69
- # @param [#to_sym] name (nil)
70
- # @param [Proc] block
71
- # @return [self]
72
- #
73
- def operation(name = nil, &block)
74
- schema[:operations].register(name, &block)
75
- self
76
- end
77
-
78
- # Helper to define scopes of the client's top-level DSL
79
- #
80
- # @param [#to_sym] name (:[])
81
- # @param [Proc] block
82
- # @return [self]
83
- #
84
- def scope(name = :[], &block)
85
- klass = Class.new(Scope, &block)
86
- define_method(name) do |*args, **options|
87
- klass.new(*args, __scope__: self, **options)
88
- end
89
- self
90
- end
91
-
92
- # Takes constructor arguments and builds a final schema for an instance
93
- # (All the instantiation magics goes here)
94
- #
95
- # @param [Object] *args
96
- # @return [Hash<Symbol, Object>]
97
- #
98
- def new(*args)
99
- settings = schema[:settings].new(*args)
100
- base_url = schema[:base_url].call(settings)
101
- middleware = schema[:middleware].finalize(settings)
102
- operations = schema[:operations].finalize(settings)
103
- client = schema[:connection].new URI(base_url)
104
- connection = Middleware.prepend.(middleware.(Middleware.append.(client)))
105
-
106
- data = operations.each_with_object({}) do |(key, schema), hash|
107
- hash[key] = Evil::Client::Operation.new schema, connection
108
- end
109
-
110
- super(settings, base_url, data)
111
- end
112
-
113
- private
114
-
115
- BASE_URL = proc { raise NotImplementedError.new "Base url is not defined" }
116
-
117
- def schema
118
- @schema ||= {
119
- settings: Class.new,
120
- base_url: BASE_URL,
121
- connection: Connection[nil],
122
- middleware: Middleware.new,
123
- operations: Operations.new
124
- }
125
- end
126
- end
127
- end
@@ -1,26 +0,0 @@
1
- module Evil::Client::DSL
2
- class Base
3
- def self.[](schema, *args, &block)
4
- new(*args, &block).call(schema)
5
- end
6
-
7
- def call(schema)
8
- schema
9
- end
10
-
11
- private
12
-
13
- def merge(source, target)
14
- target.inject(source) do |obj, (key, val)|
15
- obj.merge key => (Hash === val ? merge(obj.fetch(key, {}), val) : val)
16
- end
17
- end
18
-
19
- def coercer
20
- @coercer ||= if @model && @block then Class.new(@model, &@block)
21
- elsif @block then Class.new(Evil::Struct, &@block)
22
- elsif @model then @model
23
- end
24
- end
25
- end
26
- end
@@ -1,37 +0,0 @@
1
- module Evil::Client::DSL
2
- # Nested definition for attached files
3
- class Files
4
- # Builds a final upload schema from request options
5
- #
6
- # @param [Hash<Symbol, Object>] options
7
- # @return [Hash<Symbol, Object>]
8
- #
9
- def call(**options)
10
- @mutex.synchronize do
11
- @schema = []
12
- instance_exec(options, &@block)
13
- @schema
14
- end
15
- end
16
-
17
- private
18
-
19
- def initialize(&block)
20
- @mutex = Mutex.new
21
- @block = block
22
- end
23
-
24
- # ==========================================================================
25
- # Helper methods that mutate files @schema
26
- # ==========================================================================
27
-
28
- def add(data, type: "text/plain", charset: "utf-8", filename: nil, **)
29
- @schema << {
30
- file: data.respond_to?(:read) ? data : StringIO.new(data),
31
- type: MIME::Types[type].first,
32
- charset: charset,
33
- filename: filename
34
- }
35
- end
36
- end
37
- end