modern 0.4.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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +8 -0
  3. data/.gitignore +10 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +173 -0
  6. data/CODE_OF_CONDUCT.md +74 -0
  7. data/Gemfile +10 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +36 -0
  10. data/Rakefile +10 -0
  11. data/TODOS.md +4 -0
  12. data/bin/console +9 -0
  13. data/bin/setup +8 -0
  14. data/example/Gemfile +9 -0
  15. data/example/Gemfile.lock +102 -0
  16. data/example/config.ru +11 -0
  17. data/example/example.rb +19 -0
  18. data/lib/modern/app/error_handling.rb +45 -0
  19. data/lib/modern/app/request_handling/input_handling.rb +65 -0
  20. data/lib/modern/app/request_handling/output_handling.rb +54 -0
  21. data/lib/modern/app/request_handling/request_container.rb +55 -0
  22. data/lib/modern/app/request_handling.rb +70 -0
  23. data/lib/modern/app/router.rb +27 -0
  24. data/lib/modern/app/trie_router.rb +37 -0
  25. data/lib/modern/app.rb +82 -0
  26. data/lib/modern/capsule.rb +17 -0
  27. data/lib/modern/configuration.rb +16 -0
  28. data/lib/modern/core_ext/array.rb +23 -0
  29. data/lib/modern/core_ext/hash.rb +17 -0
  30. data/lib/modern/descriptor/content.rb +14 -0
  31. data/lib/modern/descriptor/converters/input/base.rb +29 -0
  32. data/lib/modern/descriptor/converters/input/json.rb +21 -0
  33. data/lib/modern/descriptor/converters/output/base.rb +25 -0
  34. data/lib/modern/descriptor/converters/output/json.rb +48 -0
  35. data/lib/modern/descriptor/converters/output/yaml.rb +21 -0
  36. data/lib/modern/descriptor/converters.rb +4 -0
  37. data/lib/modern/descriptor/core.rb +63 -0
  38. data/lib/modern/descriptor/info.rb +27 -0
  39. data/lib/modern/descriptor/parameters.rb +149 -0
  40. data/lib/modern/descriptor/request_body.rb +13 -0
  41. data/lib/modern/descriptor/response.rb +26 -0
  42. data/lib/modern/descriptor/route.rb +93 -0
  43. data/lib/modern/descriptor/security.rb +104 -0
  44. data/lib/modern/descriptor/server.rb +12 -0
  45. data/lib/modern/descriptor.rb +15 -0
  46. data/lib/modern/doc_generator/open_api3/operations.rb +114 -0
  47. data/lib/modern/doc_generator/open_api3/paths.rb +24 -0
  48. data/lib/modern/doc_generator/open_api3/schema_default_types.rb +50 -0
  49. data/lib/modern/doc_generator/open_api3/schemas.rb +171 -0
  50. data/lib/modern/doc_generator/open_api3/security_schemes.rb +15 -0
  51. data/lib/modern/doc_generator/open_api3.rb +141 -0
  52. data/lib/modern/dsl/info.rb +39 -0
  53. data/lib/modern/dsl/response_builder.rb +41 -0
  54. data/lib/modern/dsl/root.rb +38 -0
  55. data/lib/modern/dsl/route_builder.rb +130 -0
  56. data/lib/modern/dsl/scope.rb +144 -0
  57. data/lib/modern/dsl/scope_settings.rb +39 -0
  58. data/lib/modern/dsl.rb +14 -0
  59. data/lib/modern/errors/error.rb +7 -0
  60. data/lib/modern/errors/setup_errors.rb +11 -0
  61. data/lib/modern/errors/web_errors.rb +83 -0
  62. data/lib/modern/errors.rb +3 -0
  63. data/lib/modern/redirect.rb +30 -0
  64. data/lib/modern/request.rb +34 -0
  65. data/lib/modern/response.rb +39 -0
  66. data/lib/modern/services.rb +17 -0
  67. data/lib/modern/struct.rb +25 -0
  68. data/lib/modern/types.rb +41 -0
  69. data/lib/modern/util/header_parsing.rb +27 -0
  70. data/lib/modern/util/trie_node.rb +53 -0
  71. data/lib/modern/version.rb +6 -0
  72. data/lib/modern.rb +8 -0
  73. data/manual/01-why_modern.md +115 -0
  74. data/modern.gemspec +54 -0
  75. metadata +439 -0
@@ -0,0 +1,149 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "modern/struct"
4
+ require "modern/errors/web_errors"
5
+
6
+ module Modern
7
+ module Descriptor
8
+ module Parameters
9
+ class Base < Modern::Struct
10
+ # TODO: maybe do something behind-the-scenes where `required` is
11
+ # expressed as part of dry-types (| Types::Nil); we need
12
+ # the field for easily generating a doc later, but we could parse
13
+ # it out of the type if we had to.
14
+ attribute :name, Types::Strict::String
15
+ attribute :type, Types::Type
16
+ attribute :description, Types::Strict::String.optional.default(nil)
17
+ attribute :deprecated, Types::Strict::Bool.default(false)
18
+
19
+ def friendly_name
20
+ name
21
+ end
22
+
23
+ def retrieve(request, route_captures)
24
+ ret = do_retrieve(request, route_captures)
25
+
26
+ raise Errors::BadRequestError, "Invalid/missing parameter '#{friendly_name}'." \
27
+ if required && ret.nil?
28
+
29
+ begin
30
+ ret.nil? ? nil : type[ret]
31
+ rescue StandardError => _err
32
+ raise Errors::InternalServiceError,
33
+ "Couldn't interpret the value provided for parameter '#{friendly_name}': #{ret}."
34
+ end
35
+ end
36
+
37
+ def openapi3_in
38
+ self.class.name.split("::").last.downcase
39
+ end
40
+
41
+ def do_retrieve(_request, _route_captures)
42
+ raise "#{self.class.name}#do_retrieve(request, route_captures) must be implemented."
43
+ end
44
+
45
+ def to_openapi3(is_api_key = false)
46
+ {
47
+ name: friendly_name,
48
+ in: openapi3_in,
49
+ required: !is_api_key ? required : nil,
50
+ description: !is_api_key ? description : nil,
51
+ deprecated: !is_api_key ? deprecated : nil
52
+ }.compact
53
+ end
54
+ end
55
+
56
+ class Path < Base
57
+ # TODO: add 'matrix' and 'label'
58
+ attribute :style, Types::Coercible::String.default("simple").enum("simple")
59
+
60
+ def required
61
+ true
62
+ end
63
+
64
+ def do_retrieve(_request, route_captures)
65
+ route_captures[name]
66
+ end
67
+ end
68
+
69
+ class Cookie < Base
70
+ attribute :cookie_name, Types::Coercible::String
71
+ attribute :style, Types::Coercible::String.default("form").enum("form")
72
+ attribute :required, Types::Strict::Bool.default(false)
73
+
74
+ def friendly_name
75
+ cookie_name
76
+ end
77
+
78
+ def do_retrieve(request, _route_captures = nil)
79
+ request.cookies[cookie_name]
80
+ end
81
+ end
82
+
83
+ class Header < Base
84
+ attribute :header_name, Types::Coercible::String
85
+ attribute :style, Types::Coercible::String.default("simple").enum("simple")
86
+ attribute :required, Types::Strict::Bool.default(false)
87
+
88
+ attr_reader :rack_env_key
89
+
90
+ def initialize(fields)
91
+ super(fields)
92
+
93
+ @rack_env_key = "HTTP_" + header_name.upcase.tr("-", "_")
94
+ end
95
+
96
+ def friendly_name
97
+ header_name
98
+ end
99
+
100
+ def do_retrieve(request, _route_captures = nil)
101
+ request.env[@rack_env_key]
102
+ end
103
+ end
104
+
105
+ class Query < Base
106
+ # TODO: add 'space_delimited', 'pipe_delimited', 'deep_object'
107
+ attribute :style, Types::Coercible::String.default("form").enum("form")
108
+ attribute :required, Types::Strict::Bool.default(false)
109
+
110
+ attribute :allow_empty_value, Types::Strict::Bool.default(false)
111
+
112
+ attr_reader :parser
113
+
114
+ def initialize(fields)
115
+ super(fields)
116
+
117
+ @query_parser = Rack::QueryParser.make_default(99, 99)
118
+ end
119
+
120
+ def do_retrieve(request, _route_captures = nil)
121
+ @query_parser.parse_query(request.query_string)[name]
122
+ end
123
+
124
+ def to_openapi3(is_api_key = false)
125
+ super.merge(
126
+ allowEmptyValue: !is_api_key ? description : nil,
127
+ ).compact
128
+ end
129
+ end
130
+
131
+ def self.from_inputs(name, parameter_type, opts)
132
+ opts = opts.merge(name: name.to_s)
133
+
134
+ case parameter_type.to_sym
135
+ when :path
136
+ Modern::Descriptor::Parameters::Path.new(opts)
137
+ when :cookie
138
+ Modern::Descriptor::Parameters::Cookie.new(opts)
139
+ when :header
140
+ Modern::Descriptor::Parameters::Header.new(opts)
141
+ when :query
142
+ Modern::Descriptor::Parameters::Query.new(opts)
143
+ else
144
+ raise "Unrecognized parameter type '#{parameter_type}'.'"
145
+ end
146
+ end
147
+ end
148
+ end
149
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "modern/struct"
4
+
5
+ module Modern
6
+ module Descriptor
7
+ class RequestBody < Modern::Struct
8
+ attribute :type, (Types::Type | Types::Struct).optional.default(nil)
9
+ attribute :required, Types::Strict::Bool.default(false)
10
+ attribute :description, Types::Strict::String.optional.default(nil)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "modern/struct"
4
+ require "modern/descriptor/content"
5
+ require "modern/descriptor/parameters"
6
+
7
+ module Modern
8
+ module Descriptor
9
+ class Response < Modern::Struct
10
+ # TODO: figure out a good way to handle header responses
11
+ # This is in part an API response type of thing; how do we ensure that
12
+ # an action defines the header that it says it's defining?
13
+ attribute :http_code, Types::Strict::Int | Types.Value(:default)
14
+ attribute :description, Types::Strict::String.default("No description provided.")
15
+ attribute :content, Types.array_of(Content)
16
+
17
+ attr_reader :content_by_type
18
+
19
+ def initialize(fields)
20
+ super
21
+
22
+ @content_by_type = content.map { |c| [c.media_type, c] }.to_h.freeze
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+
5
+ require "modern/struct"
6
+
7
+ require "modern/descriptor/converters"
8
+ require "modern/descriptor/response"
9
+ require "modern/descriptor/parameters"
10
+ require "modern/descriptor/request_body"
11
+ require "modern/descriptor/security"
12
+
13
+ module Modern
14
+ module Descriptor
15
+ class Route < Modern::Struct
16
+ # TODO: define OpenAPI-style callbacks
17
+ TEMPLATE_TOKEN = %r|\{.+\}|
18
+ OPENAPI_CAPTURE = %r|/\{(?<name>.+?)\}|
19
+
20
+ attribute :id, Types::String
21
+
22
+ attribute :http_method, Types::HttpMethod
23
+ attribute :path, Types::HttpPath
24
+
25
+ attribute :summary, Types::Strict::String.optional.default(nil)
26
+ attribute :description, Types::Strict::String.optional.default(nil)
27
+ attribute :deprecated, Types::Strict::Bool.default(false)
28
+ attribute :tags, Types.array_of(Types::Coercible::String)
29
+
30
+ attribute :parameters, Types.array_of(Parameters::Base)
31
+ attribute :request_body, RequestBody.optional.default(nil)
32
+ attribute :responses, Types.array_of(Response)
33
+
34
+ attribute :input_converters, Types.array_of(Modern::Descriptor::Converters::Input::Base)
35
+ attribute :output_converters, Types.array_of(Modern::Descriptor::Converters::Output::Base)
36
+
37
+ attribute :security, Types.array_of(Security::Base)
38
+ attribute :helpers, Types.array_of(Types.Instance(Module))
39
+ attribute :action, Types::RouteAction
40
+
41
+ attr_reader :path_matcher
42
+ attr_reader :route_tokens
43
+
44
+ attr_reader :content_types
45
+ attr_reader :responses_by_code
46
+
47
+ attr_reader :input_converters_by_type
48
+ attr_reader :output_converters_by_type
49
+
50
+ attr_reader :request_container_class
51
+
52
+ def initialize(fields)
53
+ super
54
+
55
+ @path_matcher = Regexp.new("^" + path.gsub(OPENAPI_CAPTURE, "/(?<\\k<name>>[^/]+)") + "$")
56
+ @route_tokens =
57
+ path.sub(%r|^/|, "").split("/").map do |token|
58
+ TEMPLATE_TOKEN =~ token ? :templated : token
59
+ end
60
+
61
+ @content_types = responses.map { |r| r.content.map(&:media_type) }.flatten.to_set.freeze
62
+ @responses_by_code = responses.map { |r| [r.http_code, r] }.to_h.freeze
63
+
64
+ raise "Cannot create a Route without a Response where http_code = :default." \
65
+ unless @responses_by_code.key?(:default)
66
+
67
+ _nondefault_content = @content_types - @responses_by_code[:default].content.map(&:media_type).to_set
68
+ # TODO: figure out how to better validate these
69
+ # This might be a larger-scale problem. The DSL creates a route with this, and you can end
70
+ # up in a case where you try to add a new content type to another response type. This causes
71
+ # the commented test below to fail unless you defined the :default response, with that content
72
+ # type, higher in the DSL. We might need some sort of intermediate builder class, or a way to
73
+ # squelch this error somehow...
74
+ # require 'pry'; binding.pry
75
+ # raise "Missing content types in default HTTP response for #{id}: #{nondefault_content.to_a.join(', ')}" \
76
+ # unless nondefault_content.empty?
77
+
78
+ @input_converters_by_type = input_converters.map { |c| [c.media_type.downcase.strip, c] }.to_h.freeze
79
+ @output_converters_by_type = output_converters.map { |c| [c.media_type.downcase.strip, c] }.to_h.freeze
80
+
81
+ @request_container_class =
82
+ if helpers.empty?
83
+ Modern::App::RequestHandling::FullRequestContainer
84
+ else
85
+ rcc = Class.new(Modern::App::RequestHandling::FullRequestContainer)
86
+ helpers.each { |h| rcc.send(:include, h) }
87
+
88
+ rcc
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "modern/descriptor/parameters"
4
+ require "modern/errors/web_errors"
5
+ require "modern/struct"
6
+
7
+ module Modern
8
+ module Descriptor
9
+ module Security
10
+ # TODO: implement OAuth2 security, with flow objects
11
+ # TODO: implement OpenID Connect security
12
+
13
+ # Securities in Modern allow for specifying the "plumbing" bits of the
14
+ # security in a predictable, unsurprising way. If you specify that you use
15
+ # HTTP authorization with the `Foobar` scheme--great. Modern finds the
16
+ # authorization header, checks to see if it's Foobar type, and retrieves
17
+ # the value that signifies its authentication. This value will be passed
18
+ # to the validator, which can determine whether or not its a valid bit of
19
+ # authentication.
20
+ #
21
+ # The idea is that, given that the validation gets access to a
22
+ # `PartialRequestContainer` that includes both the request and the
23
+ # application service set, it can connect to the auth server/user
24
+ # database/whatever and make sure it's atually a legitimate user. Since
25
+ # {Modern::Request} has a mutable store in {Modern::Request#local_store},
26
+ # the validator can then store a User object (or whatever) into it for use
27
+ # in the actual application.
28
+ class Base < Modern::Struct
29
+ attribute :name, Types::Strict::String
30
+ attribute :description, Types::Strict::String.optional.default(nil)
31
+ attribute :validation, Types::SecurityAction
32
+
33
+ def validate(container)
34
+ value = do_credential_fetch(container.request)
35
+
36
+ if value.nil?
37
+ false
38
+ else
39
+ !!container.instance_exec(value, &validation)
40
+ end
41
+ end
42
+
43
+ def do_credential_fetch(_request)
44
+ raise "#{self.class.name}#do_credential_fetch(request) must be implemented."
45
+ end
46
+
47
+ def to_openapi3
48
+ {
49
+ description: description
50
+ }
51
+ end
52
+ end
53
+
54
+ class ApiKey < Base
55
+ attribute :parameter, Parameters::Query | Parameters::Header | Parameters::Cookie
56
+
57
+ def initialize(fields)
58
+ super
59
+
60
+ # I didn't want to rewrite all my parameter logic.
61
+ raise Modern::Errors::SetupError, "Parameter must not be 'required' (internal limitation)." \
62
+ if parameter.required
63
+ end
64
+
65
+ def do_credential_fetch(request)
66
+ parameter.do_retrieve(request)
67
+ end
68
+
69
+ def to_openapi3
70
+ parameter.to_openapi3(true).merge(type: "apiKey")
71
+ end
72
+ end
73
+
74
+ class Http < Base
75
+ # aside: some people think that the Authorization field can support multiple sets of credentials,
76
+ # as RFC 7230 suggests that headers can be sent "multiple" times by using a comma to split them.
77
+ # however, this is for headers like Accept-Encoding. We don't need to split Authorization.
78
+ SPLITTER = %r,([^\s]+?)\s+(.*+),
79
+
80
+ attribute :scheme, Types::Strict::String
81
+
82
+ def do_credential_fetch(request)
83
+ header = request.env["HTTP_AUTHORIZATION"]
84
+
85
+ if header.nil?
86
+ nil
87
+ else
88
+ match = SPLITTER.match(header)
89
+ # yields #<MatchData "Bearer foo" 1:"Bearer" 2:"foo">
90
+
91
+ match[2].strip if !match.nil? && match[1].casecmp(scheme).zero?
92
+ end
93
+ end
94
+
95
+ def to_openapi3
96
+ super.merge(
97
+ type: "http",
98
+ scheme: scheme
99
+ )
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "modern/struct"
4
+
5
+ module Modern
6
+ module Descriptor
7
+ class Server < Modern::Struct
8
+ attribute :url, Modern::Types::Strict::String
9
+ attribute :description, Modern::Types::Strict::String.optional.default(nil)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'modern/struct'
4
+
5
+ module Modern
6
+ module Descriptor
7
+ def self.define(title, version, &block)
8
+ require 'modern/dsl'
9
+
10
+ Modern::DSL::Root.build(title, version, &block)
11
+ end
12
+ end
13
+ end
14
+
15
+ Dir["#{__dir__}/descriptor/**/*.rb"].each { |f| require_relative f }
@@ -0,0 +1,114 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Modern
4
+ module DocGenerator
5
+ class OpenAPI3
6
+ module Operations
7
+ def _operation(route)
8
+ {
9
+ operationId: route.id,
10
+ summary: route.summary,
11
+ description: route.description,
12
+ deprecated: route.deprecated,
13
+ tags: route.tags.uniq,
14
+
15
+ security: route.security.map { |s| _security_requirement(s) },
16
+
17
+ parameters: route.parameters.map { |p| _parameter(p) },
18
+ requestBody: _request_body(route),
19
+ responses:
20
+ route.responses_by_code.map do |code, response|
21
+ [code, _response(response)]
22
+ end.to_h,
23
+ # TODO: implement callbacks
24
+ callbacks: nil
25
+ }.compact
26
+ end
27
+
28
+ def _security_requirement(security)
29
+ # TODO: OAuth2
30
+ ret = {}
31
+ ret[security.name] = []
32
+
33
+ ret
34
+ end
35
+
36
+ def _parameter(parameter)
37
+ # TODO: this should be in the parameter class, or that logic should be here.
38
+
39
+ parameter.to_openapi3.merge(
40
+ schema: _build_schema_value({}, {}, parameter.type)
41
+ )
42
+ end
43
+
44
+ def _request_body(route)
45
+ body = route.request_body
46
+
47
+ if body.nil?
48
+ nil
49
+ else
50
+ schema =
51
+ if body.type.nil?
52
+ nil
53
+ elsif body.type.is_a?(Class) && body.type.ancestors.include?(Dry::Struct)
54
+ _struct_ref(body.type)
55
+ else
56
+ # TODO: make this less wasteful (see _response)
57
+ _build_schema_value({}, {}, body.type)
58
+ end
59
+
60
+ {
61
+ required: body.required,
62
+ content:
63
+ route.input_converters.map(&:media_type).map do |content_type|
64
+ [
65
+ content_type,
66
+ {
67
+ schema: schema
68
+ }.compact
69
+ ]
70
+ end.to_h.compact
71
+ }
72
+ end
73
+ end
74
+
75
+ def _response(response)
76
+ {
77
+ description: response.description,
78
+ # headers:
79
+ # response.headers.map do |header|
80
+ # [
81
+ # header.name,
82
+ # {
83
+ # description: header.description
84
+ # }.compact
85
+ # ]
86
+ # end.to_h,
87
+ content:
88
+ response.content_by_type.map do |content_type, content|
89
+ [
90
+ content_type,
91
+ {
92
+ schema:
93
+ if content.type.nil?
94
+ nil
95
+ elsif content.type.is_a?(Class) && content.type.ancestors.include?(Dry::Struct)
96
+ _struct_ref(content.type)
97
+ else
98
+ # TODO: make this less wasteful
99
+ # Right now, this reuses the schema walking stuff. It totally will re-walk
100
+ # existing schemas as far as `content.type` will reach. Which is slower
101
+ # during startup than we'd like, but it's fixable later by converting
102
+ # the methods to using state (that's why `OpenAPI3` is an object in the
103
+ # first place, but I wrote myself into a corner here).
104
+ _build_schema_value({}, {}, content.type)
105
+ end
106
+ }.compact
107
+ ]
108
+ end.to_h
109
+ }.compact
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative './operations'
4
+
5
+ module Modern
6
+ module DocGenerator
7
+ class OpenAPI3
8
+ module Paths
9
+ include Operations
10
+
11
+ def _paths(descriptor)
12
+ descriptor.routes_by_path.map do |path, routes_by_method|
13
+ [
14
+ path,
15
+ routes_by_method.map do |method, route|
16
+ [method.downcase, _operation(route)]
17
+ end.to_h
18
+ ]
19
+ end.to_h
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Modern
4
+ module DocGenerator
5
+ class OpenAPI3
6
+ module SchemaDefaultTypes
7
+ def _register_default_types!
8
+ # TODO: handle all default types
9
+ # This misses a few types, mostly because I don't yet know how to
10
+ # handle them:
11
+ # - I don't understand the Types::Form set of types well enough.
12
+ # - I have not provided a DateTime mapping because you shouldn't
13
+ # use DateTime and I have no idea how to do so sanely.
14
+ # - We can't coerce Symbols, so those are out.
15
+ [Types::String, Types::Strict::String, Types::Coercible::String].each do |t|
16
+ register_literal_type(t, type: "string")
17
+ end
18
+
19
+ [Types::Int, Types::Strict::Int, Types::Coercible::Int].each do |t|
20
+ register_literal_type(t, type: "integer", format: "int64")
21
+ end
22
+
23
+ [
24
+ Types::Bool, Types::Strict::Bool,
25
+ Types::True, Types::Strict::True,
26
+ Types::False, Types::Strict::False
27
+ ].each do |t|
28
+ register_literal_type(t, type: "boolean")
29
+ end
30
+
31
+ [Types::Float, Types::Strict::Float, Types::Coercible::Float].each do |t|
32
+ register_literal_type(t, type: "number", format: "double")
33
+ end
34
+
35
+ [Types::Date, Types::Strict::Date, Types::Json::Date].each do |t|
36
+ register_literal_type(t, type: "string", format: "date")
37
+ end
38
+
39
+ [Types::Time, Types::Strict::Time, Types::Json::Time].each do |t|
40
+ register_literal_type(t, type: "string", format: "date")
41
+ end
42
+
43
+ [Types::Decimal, Types::Strict::Decimal, Types::Coercible::Decimal].each do |t|
44
+ register_literal_type(t, type: "number")
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end