explicit 0.2.0 → 0.2.2
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.
- checksums.yaml +4 -4
- data/README.md +76 -68
- data/app/helpers/explicit/application_helper.rb +32 -0
- data/app/views/explicit/documentation/_attribute.html.erb +38 -0
- data/app/views/explicit/documentation/_page.html.erb +166 -0
- data/app/views/explicit/documentation/_request.html.erb +87 -0
- data/app/views/explicit/documentation/request/_examples.html.erb +50 -0
- data/app/views/explicit/documentation/type/_agreement.html.erb +7 -0
- data/app/views/explicit/documentation/type/_array.html.erb +3 -0
- data/app/views/explicit/documentation/type/_big_decimal.html.erb +4 -0
- data/app/views/explicit/documentation/type/_boolean.html.erb +7 -0
- data/app/views/explicit/documentation/type/_date_time_iso8601.html.erb +3 -0
- data/app/views/explicit/documentation/type/_date_time_posix.html.erb +3 -0
- data/app/views/explicit/documentation/type/_enum.html.erb +7 -0
- data/app/views/explicit/documentation/type/_file.html.erb +9 -0
- data/app/views/explicit/documentation/type/_hash.html.erb +4 -0
- data/app/views/explicit/documentation/type/_integer.html.erb +25 -0
- data/app/views/explicit/documentation/type/_one_of.html.erb +11 -0
- data/app/views/explicit/documentation/type/_record.html.erb +9 -0
- data/app/views/explicit/documentation/type/_string.html.erb +21 -0
- data/config/locales/en.yml +27 -11
- data/lib/explicit/configuration.rb +1 -1
- data/lib/explicit/documentation/builder.rb +80 -0
- data/lib/explicit/documentation/markdown.rb +2 -13
- data/lib/explicit/documentation/output/swagger.rb +176 -0
- data/lib/explicit/documentation/output/webpage.rb +31 -0
- data/lib/explicit/documentation/page/partial.rb +20 -0
- data/lib/explicit/documentation/page/request.rb +27 -0
- data/lib/explicit/documentation/section.rb +9 -0
- data/lib/explicit/documentation.rb +12 -145
- data/lib/explicit/request/example.rb +50 -1
- data/lib/explicit/request/invalid_params_error.rb +1 -3
- data/lib/explicit/request/invalid_response_error.rb +2 -15
- data/lib/explicit/request/route.rb +18 -0
- data/lib/explicit/request.rb +43 -24
- data/lib/explicit/test_helper/example_recorder.rb +7 -2
- data/lib/explicit/test_helper.rb +25 -7
- data/lib/explicit/type/agreement.rb +39 -0
- data/lib/explicit/type/array.rb +56 -0
- data/lib/explicit/type/big_decimal.rb +58 -0
- data/lib/explicit/type/boolean.rb +47 -0
- data/lib/explicit/type/date_time_iso8601.rb +41 -0
- data/lib/explicit/type/date_time_posix.rb +44 -0
- data/lib/explicit/type/enum.rb +41 -0
- data/lib/explicit/type/file.rb +60 -0
- data/lib/explicit/type/hash.rb +57 -0
- data/lib/explicit/type/integer.rb +79 -0
- data/lib/explicit/type/literal.rb +45 -0
- data/lib/explicit/type/modifiers/default.rb +24 -0
- data/lib/explicit/type/modifiers/description.rb +11 -0
- data/lib/explicit/type/modifiers/nilable.rb +19 -0
- data/lib/explicit/type/modifiers/param_location.rb +11 -0
- data/lib/explicit/type/one_of.rb +46 -0
- data/lib/explicit/type/record.rb +96 -0
- data/lib/explicit/type/string.rb +68 -0
- data/lib/explicit/type.rb +112 -0
- data/lib/explicit/version.rb +1 -1
- data/lib/explicit.rb +28 -18
- metadata +47 -25
- data/app/views/explicit/application/_documentation.html.erb +0 -136
- data/app/views/explicit/application/_request.html.erb +0 -37
- data/lib/explicit/documentation/property.rb +0 -19
- data/lib/explicit/spec/agreement.rb +0 -17
- data/lib/explicit/spec/array.rb +0 -28
- data/lib/explicit/spec/bigdecimal.rb +0 -27
- data/lib/explicit/spec/boolean.rb +0 -30
- data/lib/explicit/spec/date_time_iso8601.rb +0 -17
- data/lib/explicit/spec/date_time_posix.rb +0 -21
- data/lib/explicit/spec/default.rb +0 -20
- data/lib/explicit/spec/error.rb +0 -63
- data/lib/explicit/spec/hash.rb +0 -30
- data/lib/explicit/spec/inclusion.rb +0 -15
- data/lib/explicit/spec/integer.rb +0 -53
- data/lib/explicit/spec/literal.rb +0 -15
- data/lib/explicit/spec/nilable.rb +0 -15
- data/lib/explicit/spec/one_of.rb +0 -40
- data/lib/explicit/spec/record.rb +0 -33
- data/lib/explicit/spec/string.rb +0 -50
- data/lib/explicit/spec.rb +0 -72
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::BigDecimal < Explicit::Type
|
4
|
+
attr_reader :min, :max
|
5
|
+
|
6
|
+
def initialize(min: nil, max: nil)
|
7
|
+
@min = min
|
8
|
+
@max = max
|
9
|
+
end
|
10
|
+
|
11
|
+
def validate(value)
|
12
|
+
unless value.is_a?(::String) || value.is_a?(::Integer)
|
13
|
+
return [:error, error_i18n("bigdecimal")]
|
14
|
+
end
|
15
|
+
|
16
|
+
decimalvalue = BigDecimal(value)
|
17
|
+
|
18
|
+
if min && decimalvalue < min
|
19
|
+
return [:error, error_i18n("min", min:)]
|
20
|
+
end
|
21
|
+
|
22
|
+
if max && decimalvalue > max
|
23
|
+
return [:error, error_i18n("max", max:)]
|
24
|
+
end
|
25
|
+
|
26
|
+
[:ok, decimalvalue]
|
27
|
+
rescue ArgumentError
|
28
|
+
return [:error, error_i18n("bigdecimal")]
|
29
|
+
end
|
30
|
+
|
31
|
+
concerning :Webpage do
|
32
|
+
def summary
|
33
|
+
"string"
|
34
|
+
end
|
35
|
+
|
36
|
+
def partial
|
37
|
+
"explicit/documentation/type/big_decimal"
|
38
|
+
end
|
39
|
+
|
40
|
+
def has_details?
|
41
|
+
true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
concerning :Swagger do
|
46
|
+
def swagger_schema
|
47
|
+
{
|
48
|
+
type: "string",
|
49
|
+
pattern: /^\d*\.?\d*$/.inspect,
|
50
|
+
format: "decimal number",
|
51
|
+
description: swagger_description([
|
52
|
+
min&.then { swagger_i18n("big_decimal_min", min: _1) },
|
53
|
+
max&.then { swagger_i18n("big_decimal_max", max: _1) }
|
54
|
+
])
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Boolean < Explicit::Type
|
4
|
+
VALUES = {
|
5
|
+
true => true,
|
6
|
+
"true" => true,
|
7
|
+
"on" => true,
|
8
|
+
"1" => true,
|
9
|
+
1 => true,
|
10
|
+
false => false,
|
11
|
+
"false" => false,
|
12
|
+
"off" => false,
|
13
|
+
"0" => false,
|
14
|
+
0 => false
|
15
|
+
}.freeze
|
16
|
+
|
17
|
+
def validate(value)
|
18
|
+
value = VALUES[value]
|
19
|
+
|
20
|
+
return [:error, error_i18n("boolean")] if value.nil?
|
21
|
+
|
22
|
+
[:ok, value]
|
23
|
+
end
|
24
|
+
|
25
|
+
concerning :Webpage do
|
26
|
+
def summary
|
27
|
+
"boolean"
|
28
|
+
end
|
29
|
+
|
30
|
+
def partial
|
31
|
+
"explicit/documentation/type/boolean"
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_details?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
concerning :Swagger do
|
40
|
+
def swagger_schema
|
41
|
+
{
|
42
|
+
type: "boolean",
|
43
|
+
description: swagger_description([])
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
class Explicit::Type::DateTimeISO8601 < Explicit::Type
|
6
|
+
def validate(value)
|
7
|
+
return [:error, error_i18n("date_time_iso8601")] if !value.is_a?(::String)
|
8
|
+
|
9
|
+
timeval = Time.iso8601(value)
|
10
|
+
|
11
|
+
[:ok, timeval]
|
12
|
+
rescue ArgumentError
|
13
|
+
[:error, error_i18n("date_time_iso8601")]
|
14
|
+
end
|
15
|
+
|
16
|
+
concerning :Webpage do
|
17
|
+
def summary
|
18
|
+
"string"
|
19
|
+
end
|
20
|
+
|
21
|
+
def partial
|
22
|
+
"explicit/documentation/type/date_time_iso8601"
|
23
|
+
end
|
24
|
+
|
25
|
+
def has_details?
|
26
|
+
true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
concerning :Swagger do
|
31
|
+
def swagger_schema
|
32
|
+
{
|
33
|
+
type: "string",
|
34
|
+
format: "date-time",
|
35
|
+
description: swagger_description([
|
36
|
+
swagger_i18n("date_time_iso8601")
|
37
|
+
])
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
4
|
+
|
5
|
+
class Explicit::Type::DateTimePosix < Explicit::Type
|
6
|
+
def validate(value)
|
7
|
+
if !value.is_a?(::Integer) && !value.is_a?(::String)
|
8
|
+
return [:error, error_i18n("date_time_posix")]
|
9
|
+
end
|
10
|
+
|
11
|
+
datetimeval = DateTime.strptime(value.to_s, "%s")
|
12
|
+
|
13
|
+
[:ok, datetimeval]
|
14
|
+
rescue Date::Error
|
15
|
+
return [:error, error_i18n("date_time_posix")]
|
16
|
+
end
|
17
|
+
|
18
|
+
concerning :Webpage do
|
19
|
+
def summary
|
20
|
+
"integer"
|
21
|
+
end
|
22
|
+
|
23
|
+
def partial
|
24
|
+
"explicit/documentation/type/date_time_posix"
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_details?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
concerning :Swagger do
|
33
|
+
def swagger_schema
|
34
|
+
{
|
35
|
+
type: "integer",
|
36
|
+
minimum: 1,
|
37
|
+
format: "POSIX time",
|
38
|
+
description: swagger_description([
|
39
|
+
swagger_i18n("date_time_posix")
|
40
|
+
])
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Enum < Explicit::Type
|
4
|
+
attr_reader :allowed_values
|
5
|
+
|
6
|
+
def initialize(allowed_values)
|
7
|
+
@allowed_values = allowed_values
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate(value)
|
11
|
+
if allowed_values.include?(value)
|
12
|
+
[:ok, value]
|
13
|
+
else
|
14
|
+
[:error, error_i18n("enum", allowed_values: allowed_values.inspect)]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
concerning :Webpage do
|
19
|
+
def summary
|
20
|
+
"string"
|
21
|
+
end
|
22
|
+
|
23
|
+
def partial
|
24
|
+
"explicit/documentation/type/enum"
|
25
|
+
end
|
26
|
+
|
27
|
+
def has_details?
|
28
|
+
true
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
concerning :Swagger do
|
33
|
+
def swagger_schema
|
34
|
+
{
|
35
|
+
type: "string",
|
36
|
+
enum: allowed_values,
|
37
|
+
description: swagger_description([])
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::File < Explicit::Type
|
4
|
+
include ActionView::Helpers::NumberHelper
|
5
|
+
|
6
|
+
attr_reader :max_size, :content_types
|
7
|
+
|
8
|
+
FILE_CLASSES = [
|
9
|
+
ActionDispatch::Http::UploadedFile,
|
10
|
+
Rack::Test::UploadedFile
|
11
|
+
].freeze
|
12
|
+
|
13
|
+
def initialize(max_size: nil, content_types: nil)
|
14
|
+
@max_size = max_size
|
15
|
+
@content_types = Array(content_types)
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate(value)
|
19
|
+
if !FILE_CLASSES.any? { |klass| value.is_a?(klass) }
|
20
|
+
return [:error, error_i18n("file")]
|
21
|
+
end
|
22
|
+
|
23
|
+
if max_size && value.size > max_size
|
24
|
+
return [:error, error_i18n("file_max_size", max_size: number_to_human_size(max_size))]
|
25
|
+
end
|
26
|
+
|
27
|
+
if content_types.any? && !content_types.include?(value.content_type)
|
28
|
+
return [:error, error_i18n("file_content_type", allowed_content_types: content_types.inspect)]
|
29
|
+
end
|
30
|
+
|
31
|
+
[:ok, value]
|
32
|
+
end
|
33
|
+
|
34
|
+
concerning :Webpage do
|
35
|
+
def summary
|
36
|
+
"file"
|
37
|
+
end
|
38
|
+
|
39
|
+
def partial
|
40
|
+
"explicit/documentation/type/file"
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_details?
|
44
|
+
max_size.present? || content_types.any?
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
concerning :Swagger do
|
49
|
+
def swagger_schema
|
50
|
+
{
|
51
|
+
type: "string",
|
52
|
+
format: "binary",
|
53
|
+
description: swagger_description([
|
54
|
+
max_size&.then { swagger_i18n("file_max_size", max_size: number_to_human_size(_1)) },
|
55
|
+
content_types.any? ? swagger_i18n("file_content_types", content_types: content_types.join(', ')) : nil
|
56
|
+
])
|
57
|
+
}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Hash < Explicit::Type
|
4
|
+
attr_reader :keytype, :valuetype, :empty
|
5
|
+
|
6
|
+
def initialize(keytype:, valuetype:, empty: true)
|
7
|
+
@keytype = Explicit::Type.build(keytype)
|
8
|
+
@valuetype = Explicit::Type.build(valuetype)
|
9
|
+
@empty = empty
|
10
|
+
end
|
11
|
+
|
12
|
+
def validate(value)
|
13
|
+
return [:error, error_i18n("hash")] if !value.respond_to?(:[])
|
14
|
+
return [:error, error_i18n("empty")] if value.empty? && empty == false
|
15
|
+
|
16
|
+
validated_hash = {}
|
17
|
+
|
18
|
+
value.each do |key, value|
|
19
|
+
case [keytype.validate(key), valuetype.validate(value)]
|
20
|
+
in [[:ok, validated_key], [:ok, validated_value]]
|
21
|
+
validated_hash[validated_key] = validated_value
|
22
|
+
in [[:error, error], _]
|
23
|
+
return [:error, error_i18n("hash_key", key:, error:)]
|
24
|
+
in [_, [:error, error]]
|
25
|
+
return [:error, error_i18n("hash_value", key:, error:)]
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
[:ok, validated_hash]
|
30
|
+
end
|
31
|
+
|
32
|
+
concerning :Webpage do
|
33
|
+
def summary
|
34
|
+
"object"
|
35
|
+
end
|
36
|
+
|
37
|
+
def partial
|
38
|
+
"explicit/documentation/type/hash"
|
39
|
+
end
|
40
|
+
|
41
|
+
def has_details?
|
42
|
+
true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
concerning :Swagger do
|
47
|
+
def swagger_schema
|
48
|
+
{
|
49
|
+
type: "object",
|
50
|
+
additionalProperties: valuetype.swagger_schema,
|
51
|
+
description: swagger_description([
|
52
|
+
empty == false ? swagger_i18n("hash_not_empty") : nil
|
53
|
+
])
|
54
|
+
}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Integer < Explicit::Type
|
4
|
+
attr_reader :min, :max, :negative, :positive
|
5
|
+
|
6
|
+
def initialize(min: nil, max: nil, negative: nil, positive: nil)
|
7
|
+
@min = min
|
8
|
+
@max = max
|
9
|
+
@negative = negative
|
10
|
+
@positive = positive
|
11
|
+
end
|
12
|
+
|
13
|
+
ParseFromString = ->(value) do
|
14
|
+
Integer(value)
|
15
|
+
rescue ::ArgumentError
|
16
|
+
nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def validate(value)
|
20
|
+
value =
|
21
|
+
if value.is_a?(::Integer)
|
22
|
+
value
|
23
|
+
elsif value.is_a?(::String)
|
24
|
+
ParseFromString[value]
|
25
|
+
else
|
26
|
+
nil
|
27
|
+
end
|
28
|
+
|
29
|
+
return [:error, error_i18n("integer")] if value.nil?
|
30
|
+
|
31
|
+
if min && value < min
|
32
|
+
return [:error, error_i18n("min", min:)]
|
33
|
+
end
|
34
|
+
|
35
|
+
if max && value > max
|
36
|
+
return [:error, error_i18n("max", max:)]
|
37
|
+
end
|
38
|
+
|
39
|
+
if negative == false && value < 0
|
40
|
+
return [:error, error_i18n("not_negative")]
|
41
|
+
end
|
42
|
+
|
43
|
+
if positive == false && value > 0
|
44
|
+
return [:error, error_i18n("not_positive")]
|
45
|
+
end
|
46
|
+
|
47
|
+
[:ok, value]
|
48
|
+
end
|
49
|
+
|
50
|
+
concerning :Webpage do
|
51
|
+
def summary
|
52
|
+
"integer"
|
53
|
+
end
|
54
|
+
|
55
|
+
def partial
|
56
|
+
"explicit/documentation/type/integer"
|
57
|
+
end
|
58
|
+
|
59
|
+
def has_details?
|
60
|
+
min.present? || max.present? || !negative.nil? || !positive.nil?
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
concerning :Swagger do
|
65
|
+
def swagger_schema
|
66
|
+
{
|
67
|
+
type: "integer",
|
68
|
+
minimum: min,
|
69
|
+
maximum: max,
|
70
|
+
description: swagger_description([
|
71
|
+
positive == false ? swagger_i18n("integer_not_positive") : nil,
|
72
|
+
positive == true ? swagger_i18n("integer_only_positive") : nil,
|
73
|
+
negative == false ? swagger_i18n("integer_not_negative") : nil,
|
74
|
+
negative == true ? swagger_i18n("integer_only_negative") : nil
|
75
|
+
])
|
76
|
+
}.compact_blank
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Literal < Explicit::Type
|
4
|
+
attr_reader :value
|
5
|
+
|
6
|
+
def initialize(value:)
|
7
|
+
if !value.is_a?(::String) && !value.is_a?(::Integer)
|
8
|
+
raise ArgumentError("literal must be a string or integer")
|
9
|
+
end
|
10
|
+
|
11
|
+
@value = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(value)
|
15
|
+
if value == @value
|
16
|
+
[:ok, value]
|
17
|
+
else
|
18
|
+
[:error, error_i18n("literal", value: @value.inspect)]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
concerning :Webpage do
|
23
|
+
def summary
|
24
|
+
@value.inspect
|
25
|
+
end
|
26
|
+
|
27
|
+
def partial
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def has_details?
|
32
|
+
false
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
concerning :Swagger do
|
37
|
+
def swagger_schema
|
38
|
+
{
|
39
|
+
type: @value.is_a?(::String) ? "string" : "integer",
|
40
|
+
enum: [@value],
|
41
|
+
description: swagger_description([])
|
42
|
+
}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Explicit::Type::Modifiers::Default
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def apply(default, type)
|
7
|
+
Explicit::Type.build(type).tap do |type|
|
8
|
+
type.default = default if type.is_a?(Explicit::Type) # TODO: remove check
|
9
|
+
|
10
|
+
original_validate = type.method(:validate)
|
11
|
+
|
12
|
+
type.define_singleton_method(:validate, lambda do |value|
|
13
|
+
value =
|
14
|
+
if value.nil?
|
15
|
+
default.respond_to?(:call) ? default.call : default
|
16
|
+
else
|
17
|
+
value
|
18
|
+
end
|
19
|
+
|
20
|
+
original_validate.(value)
|
21
|
+
end)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Explicit::Type::Modifiers::Description
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def apply(description, type)
|
7
|
+
Explicit::Type.build(type).tap do |type|
|
8
|
+
type.description = description if type.is_a?(Explicit::Type) # TODO: remove check
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Explicit::Type::Modifiers::Nilable
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def apply(type)
|
7
|
+
Explicit::Type.build(type).tap do |type|
|
8
|
+
type.nilable = true if type.is_a?(Explicit::Type) # TODO: remove check
|
9
|
+
|
10
|
+
original_validate = type.method(:validate)
|
11
|
+
|
12
|
+
type.define_singleton_method(:validate, lambda do |value|
|
13
|
+
return [:ok, nil] if value.nil?
|
14
|
+
|
15
|
+
original_validate.(value)
|
16
|
+
end)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::OneOf < Explicit::Type
|
4
|
+
attr_reader :subtypes
|
5
|
+
|
6
|
+
def initialize(subtypes:)
|
7
|
+
@subtypes = subtypes.map { Explicit::Type.build(_1) }
|
8
|
+
end
|
9
|
+
|
10
|
+
def validate(value)
|
11
|
+
errors = []
|
12
|
+
|
13
|
+
@subtypes.each do |subtype|
|
14
|
+
case subtype.validate(value)
|
15
|
+
in [:ok, validated_value]
|
16
|
+
return [:ok, validated_value]
|
17
|
+
in [:error, err]
|
18
|
+
errors << err
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
[:error, errors.join(" OR ")]
|
23
|
+
end
|
24
|
+
|
25
|
+
concerning :Webpage do
|
26
|
+
def summary
|
27
|
+
@subtypes.all? { _1.is_a?(Explicit::Type::Record) } ? "object" : "any"
|
28
|
+
end
|
29
|
+
|
30
|
+
def partial
|
31
|
+
"explicit/documentation/type/one_of"
|
32
|
+
end
|
33
|
+
|
34
|
+
def has_details?
|
35
|
+
true
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
concerning :Swagger do
|
40
|
+
def swagger_schema
|
41
|
+
return subtypes.first.swagger_schema if subtypes.one?
|
42
|
+
|
43
|
+
{ oneOf: subtypes.map(&:swagger_schema) }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class Explicit::Type::Record < Explicit::Type
|
4
|
+
attr_reader :attributes
|
5
|
+
|
6
|
+
def initialize(attributes:)
|
7
|
+
@attributes = attributes.map do |attribute_name, type|
|
8
|
+
type = Explicit::Type.build(type) if !type.is_a?(Explicit::Type)
|
9
|
+
|
10
|
+
[attribute_name, type]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def validate(data)
|
15
|
+
return [:error, error_i18n("hash")] if !data.respond_to?(:[])
|
16
|
+
|
17
|
+
validated_data = {}
|
18
|
+
errors = {}
|
19
|
+
|
20
|
+
@attributes.each do |attribute_name, type|
|
21
|
+
value = data[attribute_name]
|
22
|
+
|
23
|
+
case type.validate(value)
|
24
|
+
in [:ok, validated_value]
|
25
|
+
validated_data[attribute_name] = validated_value
|
26
|
+
in [:error, err]
|
27
|
+
errors[attribute_name] = err
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
return [:error, errors] if errors.any?
|
32
|
+
|
33
|
+
[:ok, validated_data]
|
34
|
+
end
|
35
|
+
|
36
|
+
def path_params_type
|
37
|
+
path_params = @attributes.filter do |name, type|
|
38
|
+
type.param_location_path?
|
39
|
+
end
|
40
|
+
|
41
|
+
self.class.new(attributes: path_params)
|
42
|
+
end
|
43
|
+
|
44
|
+
def body_params_type
|
45
|
+
body_params = @attributes.filter do |name, type|
|
46
|
+
!type.param_location_path?
|
47
|
+
end
|
48
|
+
|
49
|
+
self.class.new(attributes: body_params)
|
50
|
+
end
|
51
|
+
|
52
|
+
concerning :Webpage do
|
53
|
+
def summary
|
54
|
+
"object"
|
55
|
+
end
|
56
|
+
|
57
|
+
def partial
|
58
|
+
"explicit/documentation/type/record"
|
59
|
+
end
|
60
|
+
|
61
|
+
def has_details?
|
62
|
+
true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
concerning :Swagger do
|
67
|
+
def swagger_parameters
|
68
|
+
attributes.map do |name, type|
|
69
|
+
{
|
70
|
+
name:,
|
71
|
+
in: type.param_location_path? ? "path" : "body",
|
72
|
+
description: type.description,
|
73
|
+
required: !type.nilable,
|
74
|
+
schema: type.swagger_schema
|
75
|
+
}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def swagger_schema
|
80
|
+
properties = attributes.to_h do |name, type|
|
81
|
+
[name, type.swagger_schema]
|
82
|
+
end
|
83
|
+
|
84
|
+
required = attributes.filter_map do |name, type|
|
85
|
+
type.required? ? name.to_s : nil
|
86
|
+
end
|
87
|
+
|
88
|
+
{
|
89
|
+
type: "object",
|
90
|
+
properties:,
|
91
|
+
required:,
|
92
|
+
description: swagger_description([])
|
93
|
+
}.compact_blank
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|