request_handler 1.3.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -0
  3. data/Gemfile +2 -0
  4. data/README.md +167 -29
  5. data/lib/request_handler/base.rb +27 -33
  6. data/lib/request_handler/base_parser.rb +9 -0
  7. data/lib/request_handler/builder/base.rb +23 -0
  8. data/lib/request_handler/builder/body_builder.rb +27 -0
  9. data/lib/request_handler/builder/fieldsets_builder.rb +28 -0
  10. data/lib/request_handler/builder/fieldsets_resource_builder.rb +17 -0
  11. data/lib/request_handler/builder/filter_builder.rb +31 -0
  12. data/lib/request_handler/builder/include_options_builder.rb +23 -0
  13. data/lib/request_handler/builder/multipart_builder.rb +22 -0
  14. data/lib/request_handler/builder/multipart_resource_builder.rb +35 -0
  15. data/lib/request_handler/builder/options_builder.rb +88 -0
  16. data/lib/request_handler/builder/page_builder.rb +30 -0
  17. data/lib/request_handler/builder/page_resource_builder.rb +23 -0
  18. data/lib/request_handler/builder/query_builder.rb +23 -0
  19. data/lib/request_handler/builder/sort_options_builder.rb +23 -0
  20. data/lib/request_handler/concerns/config_helper.rb +25 -0
  21. data/lib/request_handler/config.rb +33 -0
  22. data/lib/request_handler/fieldsets_parser.rb +5 -4
  23. data/lib/request_handler/include_option_parser.rb +2 -2
  24. data/lib/request_handler/multipart_parser.rb +8 -11
  25. data/lib/request_handler/option_parser.rb +1 -1
  26. data/lib/request_handler/page_parser.rb +19 -10
  27. data/lib/request_handler/schema_parser.rb +8 -4
  28. data/lib/request_handler/sort_option_parser.rb +2 -2
  29. data/lib/request_handler/validation/definition_engine.rb +2 -1
  30. data/lib/request_handler/validation/dry_engine.rb +24 -16
  31. data/lib/request_handler/version.rb +1 -1
  32. data/lib/request_handler.rb +7 -22
  33. data/request_handler.gemspec +2 -4
  34. metadata +33 -44
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbf5f683f749e9a5431fe1d07a250c43e72446ddc4fdbb472acd7b06d737fad6
4
- data.tar.gz: e12067caf9c5ec19b306a00d89666fac1a862449f8a68d17cb3ccda42e4a0cde
3
+ metadata.gz: c6f03be51da1bccf44ba08a55724d6cd68c8dd43817e5b1e74f427b9ae1164f7
4
+ data.tar.gz: 942131e22d64ee316f7e7c690c9c1a6af0d5c589459d2bc77786e86a940fe342
5
5
  SHA512:
6
- metadata.gz: df12c3277ca4cb9d73bd8a1661427db90b257bc263e10f40344775a4e3f6f7c8f558a5873cfacab95d888cd7d3ea6e835b72ebc2aa9c176117196530e5a585a2
7
- data.tar.gz: a53b49ccdfde6d9fb929263729972bc5732f01e2997848299ce1dbec187be23a565b5a745ca00dc6cd5765b20bff62ccc2b729abd80bb8ad3965f1ce457e4808
6
+ metadata.gz: f50ec83b0dc218d07d9f9703f929696f761dfd812e2a1dd5230d2e901f20ac1c4141d8674a332d08ecf3297dd7e3a3af4514e7330e37d341db92efc1dc660714
7
+ data.tar.gz: 7e0b0d58b1f0fd697432c9e3f0fb4447b983510bc643acde7713db1c3bc8de23a4209bd45e80ce9f255458ba0c83c5dbd9f22eca672280c98363e85a6cd3c429
data/CHANGELOG.md CHANGED
@@ -4,6 +4,20 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
5
5
  and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [2.0.0] - 2019-11-19
8
+ ### Changed
9
+ - BREAKING: Required configuration of validation engine
10
+ - BREAKING: Dynamic resource keys in config DSL are not supported anymore and
11
+ have to be defined via the `resource` key
12
+
13
+ ### Added
14
+ - Support for dry-* 1.x
15
+
16
+ ### Removed
17
+ - BREAKING: Dry dependencies were removed and should manually be added
18
+ - BREAKING: Support for config inheritance
19
+ - BREAKING: Support for dry-* 0.x
20
+
7
21
  ## [1.3.0] - 2019-05-21
8
22
  ### Added
9
23
  - Serializable JSONAPI error objects out of validation failures
data/Gemfile CHANGED
@@ -9,5 +9,7 @@ group :development, :test do
9
9
  gem 'danger'
10
10
  gem 'danger-commit_lint'
11
11
  gem 'danger-rubocop'
12
+ gem 'dry-types', '~> 1.0'
13
+ gem 'dry-validation', '~> 1.0'
12
14
  gem 'rspec_junit_formatter'
13
15
  end
data/README.md CHANGED
@@ -26,34 +26,51 @@ Or install it yourself as:
26
26
 
27
27
  ## Configuration
28
28
 
29
+ You have to chose a validation engine and configure it globally:
30
+ ```ruby
31
+ RequestHandler.configure do |config|
32
+ config.validation_engine = RequestHandler::Validation::DryEngine
33
+ end
34
+ ```
35
+
36
+ If you want to use the included dry engine you also have to add the dry gems to
37
+ your Gemfile:
38
+ ```ruby
39
+ gem 'dry-validation', '~> 1.0'
40
+ gem 'dry-types', '~> 1.0'
41
+ ```
42
+ Note that only dry >= 1.0 is supported.
43
+
29
44
  The default logger and separator can be changed globally:
30
45
 
31
46
  ```ruby
32
- RequestHandler.configure do
33
- logger Logger.new(STDERR)
34
- separator '____'
35
- validation_engine RequestHandler::Validation::DryEngine
47
+ RequestHandler.configure do |config|
48
+ config.logger = Logger.new(STDERR)
49
+ config.separator = '____'
36
50
  end
37
51
  ```
38
52
 
39
53
  JSON:API-style error data can be included in validation errors raised by `RequestHandler`.
40
54
 
41
55
  ```ruby
42
- RequestHandler.configure do
43
- raise_jsonapi_errors = true # default: false
56
+ RequestHandler.configure do |config|
57
+ config.raise_jsonapi_errors = true # default: false
44
58
  end
45
59
  ```
46
60
 
47
61
 
48
62
  ### Validation Engine
49
- Per default this gem uses the `DryEngine` which relies on dry-validation. All
50
- examples in this Readme assume you are using this default engine. However
51
- you can also use the builtin `DefinitionEngine`, which uses [Definition](https://github.com/Goltergaul/definition) as validation library:
63
+ You have to chose a validation engine and configure it globally (see
64
+ configuration section above).
65
+ All examples in this Readme assume you are using the `DryEngine` which relies on
66
+ dry-validation. However you can also use the builtin `DefinitionEngine`, which
67
+ uses [Definition](https://github.com/Goltergaul/definition) as validation
68
+ library:
52
69
 
53
70
  ```ruby
54
- RequestHandler.configure do
71
+ RequestHandler.configure do |config|
55
72
  require 'request_handler/validation/definition_engine'
56
- validation_engine RequestHandler::Validation::DefinitionEngine
73
+ config.validation_engine = RequestHandler::Validation::DefinitionEngine
57
74
  end
58
75
  ```
59
76
 
@@ -85,14 +102,13 @@ defines if the JsonApiDocumentParser or JsonParser is used.
85
102
  If nothing is defined, JsonApiDocumentParser will be used by default.
86
103
 
87
104
  ```ruby
88
- require "dry-validation"
89
105
  require "request_handler"
90
106
  class DemoHandler < RequestHandler::Base
91
107
  options do
92
108
  page do
93
109
  default_size 10
94
110
  max_size 20
95
- comments do
111
+ resource :comments do
96
112
  default_size 20
97
113
  max_size 100
98
114
  end
@@ -108,11 +124,11 @@ class DemoHandler < RequestHandler::Base
108
124
 
109
125
  filter do
110
126
  schema(
111
- Dry::Validation.Params do
112
- configure do
113
- option :foo
127
+ Class.new(Dry::Validation::Contract) do
128
+ option :foo
129
+ params do
130
+ required(:name).filled(:string)
114
131
  end
115
- required(:name).filled(:str?)
116
132
  end
117
133
  )
118
134
  additional_url_filter %i(user_id id)
@@ -122,8 +138,8 @@ class DemoHandler < RequestHandler::Base
122
138
 
123
139
  query do
124
140
  schema(
125
- Dry::Validation.Params do
126
- optional(:name).filled(:str?)
141
+ Dry::Schema.Params do
142
+ optional(:name).filled(:string)
127
143
  end
128
144
  )
129
145
  end
@@ -131,11 +147,11 @@ class DemoHandler < RequestHandler::Base
131
147
  body do
132
148
  type :jsonapi
133
149
  schema(
134
- Dry::Validation.JSON do
135
- configure do
136
- option :foo
150
+ Class.new(Dry::Validation::Contract) do
151
+ option :foo
152
+ json do
153
+ required(:id).filled(:string)
137
154
  end
138
- required(:id).filled(:str?)
139
155
  end
140
156
  )
141
157
  options(->(_handler, _request) { { foo: "bar" } })
@@ -208,19 +224,19 @@ file related to the question
208
224
  class CreateQuestionHandler < RequestHandler::Base
209
225
  options do
210
226
  multipart do
211
- question do
227
+ resource :question do
212
228
  required true
213
229
  type "json"
214
230
  schema(
215
- Dry::Validation.JSON do
216
- required(:id).filled(:str?)
217
- required(:type).filled(:str?)
218
- required(:content).filled(:str?)
231
+ Dry::Schema.JSON do
232
+ required(:id).filled(:string)
233
+ required(:type).filled(:string)
234
+ required(:content).filled(:string)
219
235
  end
220
236
  )
221
237
  end
222
238
 
223
- file do
239
+ resource :file do
224
240
  # no validation necessary
225
241
  end
226
242
  end
@@ -317,6 +333,128 @@ get "/users/:user_id/posts" do
317
333
  end
318
334
  ```
319
335
 
336
+ ## v1 to v2 migration guide
337
+ Multiple breaking changes were introduced with request_handler 2.0. This section
338
+ describes which steps have to be taken in order to migrate from 1.x to 2.0.
339
+
340
+ ### Configure validation engine
341
+ By default the DryEngine was used in 1.0. You now have to explicitly configure
342
+ a validation engine:
343
+
344
+ ```ruby
345
+ RequestHandler.configure do |config|
346
+ config.validation_engine = RequestHandler::Validation::DryEngine
347
+ end
348
+ ```
349
+
350
+ ### Add dry dependency if you use the DryEngine
351
+ Since the DryEngine is not configured by default anymore, the dependency to the
352
+ dry gems could be removed from request_handler. If you use the DryEngine
353
+ simply add the dry-gems to your Gemfile:
354
+
355
+ ```ruby
356
+ gem 'dry-validation', '~> 1.0'
357
+ gem 'dry-types', '~> 1.0'
358
+ ```
359
+ Note that only dry >= 1.0 is supported.
360
+
361
+ ### Define custom resources via the `resource` key
362
+ In request_handler 1.x it was possible to define custom resource names like this:
363
+
364
+ ```ruby
365
+ options do
366
+ fieldsets do
367
+ allowed do
368
+ posts schema
369
+ end
370
+ end
371
+ end
372
+ ```
373
+
374
+ This was possible in multiple places (`page`, `multipart`, `fieldsets.allowed`).
375
+ Starting with version 2.0 you will have to define those custom resources via the
376
+ `resource` key:
377
+
378
+ ```ruby
379
+ options do
380
+ fieldsets do
381
+ allowed do
382
+ resource :posts, schema
383
+ end
384
+ end
385
+ end
386
+ ```
387
+
388
+ ### Use dry-* 1.x instead of dry-* 0.x if you use the DryEngine
389
+ Some of the most common required changes are listed here:
390
+
391
+ * Use `Dry::Schema.Params` instead of `Dry::Validation.Schema`
392
+ * Use `Dry::Schema.JSON` instead of `Dry::Validation.JSON`
393
+ * If you use some more complex validation rules with options like this:
394
+
395
+ ```
396
+ Dry::Validation.Params do
397
+ configure do
398
+ option :query_id
399
+ end
400
+ required(:id).value(eql?: query_id)
401
+ end
402
+
403
+ options(->(_parser, request) { { query_id: request.params['id'] } })
404
+ ```
405
+
406
+ please rewrite it using `Dry::Validation::Contract` like this:
407
+
408
+ ```
409
+ Class.new(Dry::Validation::Contract) do
410
+ option :query_id
411
+ params do
412
+ required(:id).value(:string)
413
+ end
414
+ rule(:id) do
415
+ key.failure('invalid id') unless values[:id] == query_id
416
+ end
417
+ end)
418
+ options(->(_parser, request) { { query_id: request.params['id'] } })
419
+ ```
420
+
421
+ A useful guide for upgrading to dry 1 types, validations and schemas can be
422
+ found [here](https://www.morozov.is/2019/05/31/upgrading-dry-gems.html).
423
+
424
+ Also please refer to the official docs of
425
+ [dry-schema](https://dry-rb.org/gems/dry-schema) and
426
+ [dry-validation](https://dry-rb.org/gems/dry-validation).
427
+
428
+ ### Remove config inheritance
429
+ It was possible to (partially) overwrite configs defined in a request-handler
430
+ super-class:
431
+ ```
432
+ class Parent < RequestHandler::Base
433
+ options do
434
+ page do
435
+ comments do
436
+ default_size 20
437
+ end
438
+ end
439
+ end
440
+ end
441
+ ```
442
+
443
+ ```ruby
444
+ class Child < Parent
445
+ options do
446
+ page do
447
+ comments do
448
+ default_size 10
449
+ end
450
+ end
451
+ end
452
+ end
453
+ ```
454
+
455
+ Support for this has been fully removed. If you overwrite configs in subclasses
456
+ please remove the inheritance and define the two request-handlers separately.
457
+
320
458
  ## Development
321
459
 
322
460
  After checking out the repo, run `bin/setup` to install dependencies. You can
@@ -10,22 +10,22 @@ require 'request_handler/multipart_parser'
10
10
  require 'request_handler/fieldsets_parser'
11
11
  require 'request_handler/query_parser'
12
12
  require 'request_handler/helper'
13
- require 'confstruct'
13
+ require 'request_handler/builder/options_builder'
14
+ require 'request_handler/concerns/config_helper'
15
+ require 'request_handler/config'
16
+
14
17
  module RequestHandler
15
18
  class Base
19
+ include RequestHandler::Concerns::ConfigHelper
20
+
16
21
  class << self
17
22
  def options(&block)
18
- @config ||= ::Confstruct::Configuration.new
19
- @config.configure(&block)
20
- end
21
-
22
- def inherited(subclass)
23
- return if @config.nil?
24
- subclass.config = @config.deep_copy
23
+ @config = Config.new(&block)
25
24
  end
26
25
 
27
26
  attr_accessor :config
28
27
  end
28
+
29
29
  def initialize(request:)
30
30
  raise MissingArgumentError, request: 'is missing' if request.nil?
31
31
  @request = request
@@ -38,7 +38,7 @@ module RequestHandler
38
38
  def page_params
39
39
  @page_params ||= PageParser.new(
40
40
  params: params,
41
- page_config: lookup!('page')
41
+ page_config: config.lookup!('page')
42
42
  ).run
43
43
  end
44
44
 
@@ -82,9 +82,9 @@ module RequestHandler
82
82
  defaults = fetch_defaults('filter.defaults', {})
83
83
  defaults.merge(FilterParser.new(
84
84
  params: params,
85
- schema: lookup!('filter.schema'),
86
- additional_url_filter: lookup('filter.additional_url_filter'),
87
- schema_options: execute_options(lookup('filter.options'))
85
+ schema: config.lookup!('filter.schema'),
86
+ additional_url_filter: config.lookup('filter.additional_url_filter'),
87
+ schema_options: execute_options(config.lookup('filter.options'))
88
88
  ).run)
89
89
  end
90
90
 
@@ -100,7 +100,7 @@ module RequestHandler
100
100
  defaults = fetch_defaults("#{type}.defaults", [])
101
101
  result = parser.new(
102
102
  params: params,
103
- allowed_options_type: lookup!("#{type}.allowed")
103
+ allowed_options_type: config.lookup!("#{type}.allowed")
104
104
  ).run
105
105
  result.empty? ? defaults : result
106
106
  end
@@ -108,35 +108,35 @@ module RequestHandler
108
108
  def parse_body_params
109
109
  BodyParser.new(
110
110
  request: request,
111
- schema: lookup!('body.schema'),
112
- schema_options: execute_options(lookup('body.options')),
113
- type: lookup('body.type')
111
+ schema: config.lookup!('body.schema'),
112
+ schema_options: execute_options(config.lookup('body.options')),
113
+ type: config.lookup('body.type')
114
114
  ).run
115
115
  end
116
116
 
117
117
  def parse_multipart_params
118
118
  MultipartsParser.new(
119
119
  request: request,
120
- multipart_config: lookup!('multipart')
120
+ multipart_config: config.lookup!('multipart').to_h
121
121
  ).run
122
122
  end
123
123
 
124
124
  def parse_fieldsets_params
125
125
  FieldsetsParser.new(params: params,
126
- allowed: lookup!('fieldsets.allowed'),
127
- required: lookup('fieldsets.required') || []).run
126
+ allowed: config.lookup!('fieldsets.allowed'),
127
+ required: config.lookup('fieldsets.required') || []).run
128
128
  end
129
129
 
130
130
  def parse_query_params
131
131
  QueryParser.new(
132
132
  params: params,
133
- schema: lookup!('query.schema'),
134
- schema_options: execute_options(lookup('query.options'))
133
+ schema: config.lookup!('query.schema'),
134
+ schema_options: execute_options(config.lookup('query.options'))
135
135
  ).run
136
136
  end
137
137
 
138
138
  def fetch_defaults(key, default)
139
- value = lookup(key)
139
+ value = config.lookup(key)
140
140
  return default if value.nil?
141
141
  return value unless value.respond_to?(:call)
142
142
  value.call(request)
@@ -148,26 +148,20 @@ module RequestHandler
148
148
  options.call(self, request)
149
149
  end
150
150
 
151
- def lookup!(key)
152
- config.lookup!(key).tap do |data|
153
- raise NoConfigAvailableError, key.to_sym => 'is not configured' if data.nil?
154
- end
155
- end
156
-
157
- def lookup(key)
158
- config.lookup!(key)
159
- end
160
-
161
151
  def params
162
152
  raise MissingArgumentError, params: 'is missing' if request.params.nil?
163
153
  raise ExternalArgumentError, [] unless request.params.is_a?(Hash)
164
154
  @params ||= Helper.deep_transform_keys_in_object(request.params) do |k|
165
- k.to_s.gsub('.', ::RequestHandler.separator)
155
+ k.to_s.gsub('.', separator)
166
156
  end
167
157
  end
168
158
 
169
159
  def config
170
160
  self.class.instance_variable_get('@config')
171
161
  end
162
+
163
+ def separator
164
+ ::RequestHandler.configuration.separator
165
+ end
172
166
  end
173
167
  end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/concerns/config_helper'
4
+
5
+ module RequestHandler
6
+ class BaseParser
7
+ include RequestHandler::Concerns::ConfigHelper
8
+ end
9
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ostruct'
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class Base
8
+ attr_accessor :result
9
+
10
+ def initialize
11
+ create_klass_struct
12
+ end
13
+
14
+ def create_klass_struct
15
+ raise NotImplementedError
16
+ end
17
+
18
+ def build
19
+ result
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class BodyBuilder < Base
8
+ Body = Struct.new(:type, :schema, :options)
9
+
10
+ def create_klass_struct
11
+ @result = Body.new
12
+ end
13
+
14
+ def type(value)
15
+ @result.type = value
16
+ end
17
+
18
+ def schema(value)
19
+ @result.schema = value
20
+ end
21
+
22
+ def options(value)
23
+ @result.options = value
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+ require 'request_handler/builder/fieldsets_resource_builder'
5
+
6
+ module RequestHandler
7
+ module Builder
8
+ class FieldsetsBuilder < Base
9
+ Fieldsets = Struct.new(:allowed, :required)
10
+
11
+ def create_klass_struct
12
+ @result = Fieldsets.new
13
+ end
14
+
15
+ def allowed(&block)
16
+ @result.allowed = build_fieldsets_resource(&block)
17
+ end
18
+
19
+ def required(value)
20
+ @result.required = value
21
+ end
22
+
23
+ def build_fieldsets_resource(&block)
24
+ Docile.dsl_eval(FieldsetsResourceBuilder.new, &block).build
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "request_handler/builder/base"
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class FieldsetsResourceBuilder < Base
8
+ def create_klass_struct
9
+ @result = OpenStruct.new
10
+ end
11
+
12
+ def resource(name, value)
13
+ @result[name.to_sym] = value
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class FilterBuilder < Base
8
+ Filter = Struct.new(:schema, :additional_url_filter, :options, :defaults)
9
+
10
+ def create_klass_struct
11
+ @result = Filter.new
12
+ end
13
+
14
+ def schema(value)
15
+ @result.schema = value
16
+ end
17
+
18
+ def additional_url_filter(value)
19
+ @result.additional_url_filter = value
20
+ end
21
+
22
+ def options(value)
23
+ @result.options = value
24
+ end
25
+
26
+ def defaults(value)
27
+ @result.defaults = value
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class IncludeOptionsBuilder < Base
8
+ IncludeOptions = Struct.new(:allowed, :defaults)
9
+
10
+ def create_klass_struct
11
+ @result = IncludeOptions.new
12
+ end
13
+
14
+ def allowed(value)
15
+ @result.allowed = value
16
+ end
17
+
18
+ def defaults(value)
19
+ @result.defaults = value
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+ require 'request_handler/builder/multipart_resource_builder'
5
+
6
+ module RequestHandler
7
+ module Builder
8
+ class MultipartBuilder < Base
9
+ def create_klass_struct
10
+ @result = OpenStruct.new
11
+ end
12
+
13
+ def resource(name, &block)
14
+ @result[name.to_sym] = build_multipart_resource(&block)
15
+ end
16
+
17
+ def build_multipart_resource(&block)
18
+ Docile.dsl_eval(MultipartResourceBuilder.new, &block).build
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'request_handler/builder/base'
4
+
5
+ module RequestHandler
6
+ module Builder
7
+ class MultipartResourceBuilder < Base
8
+ MultipartResource = Struct.new(:required, :schema, :type, :options)
9
+
10
+ def create_klass_struct
11
+ @result = MultipartResource.new
12
+ end
13
+
14
+ def type(value)
15
+ @result.type = value
16
+ end
17
+
18
+ def required(value)
19
+ @result.required = value
20
+ end
21
+
22
+ def resource(name, &block)
23
+ @result[name.to_sym] = build_multipart_resource(&block)
24
+ end
25
+
26
+ def schema(value)
27
+ @result.schema = value
28
+ end
29
+
30
+ def options(value)
31
+ @result.options = value
32
+ end
33
+ end
34
+ end
35
+ end