easy_talk_two 1.1.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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +36 -0
  3. data/CHANGELOG.md +127 -0
  4. data/CONSTRAINTS.md +70 -0
  5. data/LICENSE.txt +21 -0
  6. data/README.md +1060 -0
  7. data/Rakefile +11 -0
  8. data/docs/.gitignore +5 -0
  9. data/docs/404.html +25 -0
  10. data/docs/Gemfile +38 -0
  11. data/docs/_config.yml +53 -0
  12. data/docs/_posts/2024-05-07-welcome-to-jekyll.markdown +29 -0
  13. data/docs/about.markdown +18 -0
  14. data/docs/index.markdown +7 -0
  15. data/lib/easy_talk/active_record_schema_builder.rb +299 -0
  16. data/lib/easy_talk/builders/base_builder.rb +65 -0
  17. data/lib/easy_talk/builders/boolean_builder.rb +23 -0
  18. data/lib/easy_talk/builders/collection_helpers.rb +12 -0
  19. data/lib/easy_talk/builders/composition_builder.rb +77 -0
  20. data/lib/easy_talk/builders/integer_builder.rb +28 -0
  21. data/lib/easy_talk/builders/null_builder.rb +16 -0
  22. data/lib/easy_talk/builders/number_builder.rb +27 -0
  23. data/lib/easy_talk/builders/object_builder.rb +180 -0
  24. data/lib/easy_talk/builders/string_builder.rb +27 -0
  25. data/lib/easy_talk/builders/temporal_builder.rb +49 -0
  26. data/lib/easy_talk/builders/typed_array_builder.rb +56 -0
  27. data/lib/easy_talk/builders/union_builder.rb +37 -0
  28. data/lib/easy_talk/configuration.rb +29 -0
  29. data/lib/easy_talk/errors.rb +8 -0
  30. data/lib/easy_talk/errors_helper.rb +147 -0
  31. data/lib/easy_talk/keywords.rb +37 -0
  32. data/lib/easy_talk/model.rb +197 -0
  33. data/lib/easy_talk/property.rb +130 -0
  34. data/lib/easy_talk/schema_definition.rb +78 -0
  35. data/lib/easy_talk/sorbet_extension.rb +15 -0
  36. data/lib/easy_talk/tools/function_builder.rb +40 -0
  37. data/lib/easy_talk/types/base_composer.rb +23 -0
  38. data/lib/easy_talk/types/composer.rb +88 -0
  39. data/lib/easy_talk/version.rb +5 -0
  40. data/lib/easy_talk.rb +29 -0
  41. metadata +265 -0
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_builder'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # Builder class for integer properties.
8
+ class IntegerBuilder < BaseBuilder
9
+ extend T::Sig
10
+ VALID_OPTIONS = {
11
+ minimum: { type: Integer, key: :minimum },
12
+ maximum: { type: Integer, key: :maximum },
13
+ exclusive_minimum: { type: Integer, key: :exclusiveMinimum },
14
+ exclusive_maximum: { type: Integer, key: :exclusiveMaximum },
15
+ multiple_of: { type: Integer, key: :multipleOf },
16
+ enum: { type: T::Array[Integer], key: :enum },
17
+ const: { type: Integer, key: :const },
18
+ default: { type: Integer, key: :default }
19
+ }.freeze
20
+
21
+ # Initializes a new instance of the IntegerBuilder class.
22
+ sig { params(name: Symbol, constraints: Hash).void }
23
+ def initialize(name, constraints = {})
24
+ super(name, { type: 'integer' }, constraints, VALID_OPTIONS)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_builder'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # builder class for Null properties.
8
+ class NullBuilder < BaseBuilder
9
+ # Initializes a new instance of the NullBuilder class.
10
+ sig { params(name: Symbol, _constraints: Hash).void }
11
+ def initialize(name, _constraints = {})
12
+ super(name, { type: 'null' }, {}, {})
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_builder'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # Builder class for number properties.
8
+ class NumberBuilder < BaseBuilder
9
+ VALID_OPTIONS = {
10
+ multiple_of: { type: T.any(Integer, Float), key: :multipleOf },
11
+ minimum: { type: T.any(Integer, Float), key: :minimum },
12
+ maximum: { type: T.any(Integer, Float), key: :maximum },
13
+ exclusive_minimum: { type: T.any(Integer, Float), key: :exclusiveMinimum },
14
+ exclusive_maximum: { type: T.any(Integer, Float), key: :exclusiveMaximum },
15
+ enum: { type: T::Array[T.any(Integer, Float)], key: :enum },
16
+ const: { type: T.any(Integer, Float), key: :const },
17
+ default: { type: T.any(Integer, Float), key: :default }
18
+ }.freeze
19
+
20
+ # Initializes a new instance of the NumberBuilder class.
21
+ sig { params(name: Symbol, constraints: Hash).void }
22
+ def initialize(name, constraints = {})
23
+ super(name, { type: 'number' }, constraints, VALID_OPTIONS)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,180 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_builder'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ #
8
+ # ObjectBuilder is responsible for turning a SchemaDefinition of an "object" type
9
+ # into a validated JSON Schema hash. It:
10
+ #
11
+ # 1) Recursively processes the schema's :properties,
12
+ # 2) Determines which properties are required (unless optional),
13
+ # 3) Handles sub-schema composition (allOf, anyOf, oneOf, not),
14
+ # 4) Produces the final object-level schema hash.
15
+ #
16
+ class ObjectBuilder < BaseBuilder
17
+ extend T::Sig
18
+
19
+ # Required by BaseBuilder: recognized schema options for "object" types
20
+ VALID_OPTIONS = {
21
+ properties: { type: T::Hash[T.any(Symbol, String), T.untyped], key: :properties },
22
+ additional_properties: { type: T::Boolean, key: :additionalProperties },
23
+ subschemas: { type: T::Array[T.untyped], key: :subschemas },
24
+ required: { type: T::Array[T.any(Symbol, String)], key: :required },
25
+ defs: { type: T.untyped, key: :$defs },
26
+ allOf: { type: T.untyped, key: :allOf },
27
+ anyOf: { type: T.untyped, key: :anyOf },
28
+ oneOf: { type: T.untyped, key: :oneOf },
29
+ not: { type: T.untyped, key: :not }
30
+ }.freeze
31
+
32
+ sig { params(schema_definition: EasyTalk::SchemaDefinition).void }
33
+ def initialize(schema_definition)
34
+ # Keep a reference to the original schema definition
35
+ @schema_definition = schema_definition
36
+ # Duplicate the raw schema hash so we can mutate it safely
37
+ @original_schema = schema_definition.schema.dup
38
+
39
+ # We'll collect required property names in this Set
40
+ @required_properties = Set.new
41
+
42
+ # Usually the name is a string (class name). Fallback to :klass if nil.
43
+ name_for_builder = schema_definition.name ? schema_definition.name.to_sym : :klass
44
+
45
+ # Build the base structure: { type: 'object' } plus any top-level options
46
+ super(
47
+ name_for_builder,
48
+ { type: 'object' }, # minimal "object" structure
49
+ build_options_hash, # method below merges & cleans final top-level keys
50
+ VALID_OPTIONS
51
+ )
52
+ end
53
+
54
+ private
55
+
56
+ ##
57
+ # Main aggregator: merges the top-level schema keys (like :properties, :subschemas)
58
+ # into a single hash that we'll feed to BaseBuilder.
59
+ def build_options_hash
60
+ # Start with a copy of the raw schema
61
+ merged = @original_schema.dup
62
+
63
+ # Extract and build sub-schemas first (handles allOf/anyOf/oneOf references, etc.)
64
+ process_subschemas(merged)
65
+
66
+ # Build :properties into a final form (and find "required" props)
67
+ merged[:properties] = build_properties(merged.delete(:properties))
68
+
69
+ # Populate the final "required" array from @required_properties
70
+ merged[:required] = @required_properties.to_a if @required_properties.any?
71
+
72
+ # Add additionalProperties: false by default if not explicitly set
73
+ merged[:additional_properties] = false unless merged.key?(:additional_properties)
74
+
75
+ # Prune empty or nil values so we don't produce stuff like "properties": {} unnecessarily
76
+ merged.reject! { |_k, v| v.nil? || v == {} || v == [] }
77
+
78
+ merged
79
+ end
80
+
81
+ ##
82
+ # Given the property definitions hash, produce a new hash of
83
+ # { property_name => [Property or nested schema builder result] }.
84
+ #
85
+ def build_properties(properties_hash)
86
+ return {} unless properties_hash.is_a?(Hash)
87
+
88
+ # Cache with a key based on property name and its full configuration
89
+ @properties_cache ||= {}
90
+
91
+ properties_hash.each_with_object({}) do |(prop_name, prop_options), result|
92
+ cache_key = [prop_name, prop_options].hash
93
+
94
+ # Use cache if the exact property and configuration have been processed before
95
+ @properties_cache[cache_key] ||= begin
96
+ mark_required_unless_optional(prop_name, prop_options)
97
+ build_property(prop_name, prop_options)
98
+ end
99
+
100
+ result[prop_name] = @properties_cache[cache_key]
101
+ end
102
+ end
103
+
104
+ ##
105
+ # Decide if a property should be required. If it's optional or nilable,
106
+ # we won't include it in the "required" array.
107
+ #
108
+ def mark_required_unless_optional(prop_name, prop_options)
109
+ return if property_optional?(prop_options)
110
+
111
+ @required_properties.add(prop_name)
112
+ end
113
+
114
+ ##
115
+ # Returns true if the property is declared optional.
116
+ #
117
+ def property_optional?(prop_options)
118
+ # Check constraints[:optional]
119
+ return true if prop_options.dig(:constraints, :optional)
120
+
121
+ # Check for nil_optional config to determine if nilable should also mean optional
122
+ if prop_options[:type].respond_to?(:nilable?) && prop_options[:type].nilable?
123
+ return EasyTalk.configuration.nilable_is_optional
124
+ end
125
+
126
+ false
127
+ end
128
+
129
+ ##
130
+ # Builds a single property. Could be a nested schema if it has sub-properties,
131
+ # or a standard scalar property (String, Integer, etc.).
132
+ #
133
+ def build_property(prop_name, prop_options)
134
+ @property_cache ||= {}
135
+
136
+ # Memoize so we only build each property once
137
+ @property_cache[prop_name] ||= begin
138
+ # Remove optional constraints from the property
139
+ constraints = prop_options[:constraints].except(:optional)
140
+ # Normal property: e.g. { type: String, constraints: {...} }
141
+ Property.new(prop_name, prop_options[:type], constraints)
142
+ end
143
+ end
144
+
145
+ ##
146
+ # Process top-level composition keywords (e.g. allOf, anyOf, oneOf),
147
+ # converting them to definitions + references if appropriate.
148
+ #
149
+ def process_subschemas(schema_hash)
150
+ subschemas = schema_hash.delete(:subschemas) || []
151
+ subschemas.each do |subschema|
152
+ add_defs_from_subschema(schema_hash, subschema)
153
+ add_refs_from_subschema(schema_hash, subschema)
154
+ end
155
+ end
156
+
157
+ ##
158
+ # For each item in the composer, add it to :defs so that we can reference it later.
159
+ #
160
+ def add_defs_from_subschema(schema_hash, subschema)
161
+ # Build up a hash of class_name => schema for each sub-item
162
+ definitions = subschema.items.each_with_object({}) do |item, acc|
163
+ acc[item.name] = item.schema
164
+ end
165
+ # Merge or create :defs
166
+ existing_defs = schema_hash[:defs] || {}
167
+ schema_hash[:defs] = existing_defs.merge(definitions)
168
+ end
169
+
170
+ ##
171
+ # Add references to the schema for each sub-item in the composer
172
+ # e.g. { "$ref": "#/$defs/SomeClass" }
173
+ #
174
+ def add_refs_from_subschema(schema_hash, subschema)
175
+ references = subschema.items.map { |item| { '$ref': item.ref_template } }
176
+ schema_hash[subschema.name] = references
177
+ end
178
+ end
179
+ end
180
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'base_builder'
4
+ require 'sorbet-runtime' # Add the import statement for the T module
5
+
6
+ module EasyTalk
7
+ module Builders
8
+ # Builder class for string properties.
9
+ class StringBuilder < BaseBuilder
10
+ extend T::Sig
11
+ VALID_OPTIONS = {
12
+ format: { type: String, key: :format },
13
+ pattern: { type: String, key: :pattern },
14
+ min_length: { type: Integer, key: :minLength },
15
+ max_length: { type: Integer, key: :maxLength },
16
+ enum: { type: T::Array[String], key: :enum },
17
+ const: { type: String, key: :const },
18
+ default: { type: String, key: :default }
19
+ }.freeze
20
+
21
+ sig { params(name: Symbol, constraints: Hash).void }
22
+ def initialize(name, constraints = {})
23
+ super(name, { type: 'string' }, constraints, VALID_OPTIONS)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'string_builder'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # Builder class for temporal properties (date, datetime, time).
8
+ class TemporalBuilder < StringBuilder
9
+ # Initializes a new instance of the TemporalBuilder class.
10
+ #
11
+ # @param property_name [Symbol] The name of the property.
12
+ # @param options [Hash] The options for the builder.
13
+ # @param format [String] The format of the temporal property (date, date-time, time).
14
+ def initialize(property_name, options = {}, format = nil)
15
+ super(property_name, options)
16
+ @format = format
17
+ end
18
+
19
+ # Modifies the schema to include the format constraint for a temporal property.
20
+ sig { returns(T::Hash[Symbol, T.untyped]) }
21
+ def schema
22
+ super.tap do |schema|
23
+ schema[:format] = @format if @format
24
+ end
25
+ end
26
+
27
+ # Builder class for date properties.
28
+ class DateBuilder < TemporalBuilder
29
+ def initialize(property_name, options = {})
30
+ super(property_name, options, 'date')
31
+ end
32
+ end
33
+
34
+ # Builder class for datetime properties.
35
+ class DatetimeBuilder < TemporalBuilder
36
+ def initialize(property_name, options = {})
37
+ super(property_name, options, 'date-time')
38
+ end
39
+ end
40
+
41
+ # Builder class for time properties.
42
+ class TimeBuilder < TemporalBuilder
43
+ def initialize(property_name, options = {})
44
+ super(property_name, options, 'time')
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'collection_helpers'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # Builder class for array properties.
8
+ class TypedArrayBuilder < BaseBuilder
9
+ extend CollectionHelpers
10
+ extend T::Sig
11
+
12
+ VALID_OPTIONS = {
13
+ min_items: { type: Integer, key: :minItems },
14
+ max_items: { type: Integer, key: :maxItems },
15
+ unique_items: { type: T::Boolean, key: :uniqueItems },
16
+ enum: { type: T::Array[T.untyped], key: :enum },
17
+ const: { type: T::Array[T.untyped], key: :const }
18
+ }.freeze
19
+
20
+ attr_reader :type
21
+
22
+ sig { params(name: Symbol, type: T.untyped, constraints: Hash).void }
23
+ def initialize(name, type, constraints = {})
24
+ @name = name
25
+ @type = type
26
+ update_option_types
27
+ super(name, { type: 'array' }, constraints, VALID_OPTIONS)
28
+ end
29
+
30
+ private
31
+
32
+ # Modifies the schema to include the `items` property.
33
+ #
34
+ # @return [Hash] The built schema.
35
+ sig { returns(T::Hash[Symbol, T.untyped]) }
36
+ def schema
37
+ super.tap do |schema|
38
+ schema[:items] = Property.new(@name, inner_type, {}).build
39
+ end
40
+ end
41
+
42
+ def inner_type
43
+ return unless type.is_a?(T::Types::TypedArray)
44
+
45
+ type.type.raw_type
46
+ end
47
+
48
+ sig { void }
49
+ # Updates the option types for the array builder.
50
+ def update_option_types
51
+ VALID_OPTIONS[:enum][:type] = T::Array[inner_type]
52
+ VALID_OPTIONS[:const][:type] = T::Array[inner_type]
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'collection_helpers'
4
+
5
+ module EasyTalk
6
+ module Builders
7
+ # Base builder class for array-type properties.
8
+ class UnionBuilder
9
+ extend CollectionHelpers
10
+ extend T::Sig
11
+
12
+ sig { params(name: Symbol, type: T.untyped, constraints: T.untyped).void }
13
+ def initialize(name, type, constraints)
14
+ @name = name
15
+ @type = type
16
+ @constraints = constraints
17
+ @context = {}
18
+ end
19
+
20
+ def build
21
+ @context[@name] = {
22
+ 'anyOf' => schemas
23
+ }
24
+ end
25
+
26
+ def schemas
27
+ types.map do |type|
28
+ Property.new(@name, type, @constraints).build
29
+ end
30
+ end
31
+
32
+ def types
33
+ @type.types
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyTalk
4
+ class Configuration
5
+ attr_accessor :exclude_foreign_keys, :exclude_associations, :excluded_columns,
6
+ :exclude_primary_key, :exclude_timestamps, :default_additional_properties,
7
+ :nilable_is_optional
8
+
9
+ def initialize
10
+ @exclude_foreign_keys = true
11
+ @exclude_associations = true
12
+ @excluded_columns = []
13
+ @exclude_primary_key = true # New option, defaulting to true
14
+ @exclude_timestamps = true # New option, defaulting to true
15
+ @default_additional_properties = false
16
+ @nilable_is_optional = false
17
+ end
18
+ end
19
+
20
+ class << self
21
+ def configuration
22
+ @configuration ||= Configuration.new
23
+ end
24
+
25
+ def configure
26
+ yield(configuration)
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyTalk
4
+ class Error < StandardError; end
5
+ class ConstraintError < Error; end
6
+ class UnknownOptionError < Error; end
7
+ class InvalidPropertyNameError < Error; end
8
+ end
@@ -0,0 +1,147 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyTalk
4
+ module ErrorHelper
5
+ def self.raise_constraint_error(property_name:, constraint_name:, expected:, got:)
6
+ message = "Error in property '#{property_name}': Constraint '#{constraint_name}' expects #{expected}, " \
7
+ "but received #{got.inspect} (#{got.class})."
8
+ raise ConstraintError, message
9
+ end
10
+
11
+ def self.raise_array_constraint_error(property_name:, constraint_name:, index:, expected:, got:)
12
+ message = "Error in property '#{property_name}': Constraint '#{constraint_name}' at index #{index} " \
13
+ "expects #{expected}, but received #{got.inspect} (#{got.class})."
14
+ raise ConstraintError, message
15
+ end
16
+
17
+ def self.raise_unknown_option_error(property_name:, option:, valid_options:)
18
+ option = option.keys.first if option.is_a?(Hash)
19
+ message = "Unknown option '#{option}' for property '#{property_name}'. " \
20
+ "Valid options are: #{valid_options.join(', ')}."
21
+ raise UnknownOptionError, message
22
+ end
23
+
24
+ def self.extract_inner_type(type_info)
25
+ # No change needed here
26
+ if type_info.respond_to?(:type) && type_info.type.respond_to?(:raw_type)
27
+ type_info.type.raw_type
28
+ # special boolean handling
29
+ elsif type_info.try(:type).try(:name) == 'T::Boolean'
30
+ T::Boolean
31
+ elsif type_info.respond_to?(:type_parameter)
32
+ type_info.type_parameter
33
+ elsif type_info.respond_to?(:raw_a) && type_info.respond_to?(:raw_b)
34
+ # Handle SimplePairUnion types
35
+ [type_info.raw_a, type_info.raw_b]
36
+ elsif type_info.respond_to?(:types)
37
+ # Handle complex union types
38
+ type_info.types.map { |t| t.respond_to?(:raw_type) ? t.raw_type : t }
39
+ else
40
+ # Fallback to something sensible
41
+ Object
42
+ end
43
+ end
44
+
45
+ def self.validate_typed_array_values(property_name:, constraint_name:, type_info:, array_value:)
46
+ # Skip validation if it's not actually an array
47
+ return unless array_value.is_a?(Array)
48
+
49
+ # Extract the inner type from the array type definition
50
+ inner_type = extract_inner_type(type_info)
51
+
52
+ # Check each element of the array
53
+ array_value.each_with_index do |element, index|
54
+ if inner_type.is_a?(Array)
55
+ # For union types, check if the element matches any of the allowed types
56
+ unless inner_type.any? { |t| element.is_a?(t) }
57
+ expected = inner_type.join(' or ')
58
+ raise_array_constraint_error(
59
+ property_name: property_name,
60
+ constraint_name: constraint_name,
61
+ index: index,
62
+ expected: expected,
63
+ got: element
64
+ )
65
+ end
66
+ else
67
+ # For single types, just check against that type
68
+ next if [true, false].include?(element)
69
+
70
+ unless element.is_a?(inner_type)
71
+ raise_array_constraint_error(
72
+ property_name: property_name,
73
+ constraint_name: constraint_name,
74
+ index: index,
75
+ expected: inner_type,
76
+ got: element
77
+ )
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ def self.validate_constraint_value(property_name:, constraint_name:, value_type:, value:)
84
+ return if value.nil?
85
+
86
+ if value_type.to_s.include?('Boolean')
87
+ return if value.is_a?(Array) && value.all? { |v| [true, false].include?(v) }
88
+
89
+ unless [true, false].include?(value)
90
+ raise_constraint_error(
91
+ property_name: property_name,
92
+ constraint_name: constraint_name,
93
+ expected: 'Boolean (true or false)',
94
+ got: value
95
+ )
96
+ end
97
+ return
98
+ end
99
+
100
+ # Handle simple scalar types (String, Integer, etc.)
101
+ if value_type.is_a?(Class)
102
+ unless value.is_a?(value_type)
103
+ raise_constraint_error(
104
+ property_name: property_name,
105
+ constraint_name: constraint_name,
106
+ expected: value_type,
107
+ got: value
108
+ )
109
+ end
110
+ # Handle array types specifically
111
+ elsif value_type.class.name.include?('TypedArray') ||
112
+ (value_type.respond_to?(:to_s) && value_type.to_s.include?('T::Array'))
113
+ # This is an array type, validate it
114
+ validate_typed_array_values(
115
+ property_name: property_name,
116
+ constraint_name: constraint_name,
117
+ type_info: value_type,
118
+ array_value: value
119
+ )
120
+ # Handle Sorbet type objects
121
+ elsif value_type.class.ancestors.include?(T::Types::Base)
122
+ # Extract the inner type
123
+ inner_type = extract_inner_type(value_type)
124
+
125
+ if inner_type.is_a?(Array)
126
+ # For union types, check if the value matches any of the allowed types
127
+ unless inner_type.any? { |t| value.is_a?(t) }
128
+ expected = inner_type.join(' or ')
129
+ raise_constraint_error(
130
+ property_name: property_name,
131
+ constraint_name: constraint_name,
132
+ expected: expected,
133
+ got: value
134
+ )
135
+ end
136
+ elsif !value.is_a?(inner_type)
137
+ raise_constraint_error(
138
+ property_name: property_name,
139
+ constraint_name: constraint_name,
140
+ expected: inner_type,
141
+ got: value
142
+ )
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module EasyTalk
4
+ KEYWORDS = %i[
5
+ description
6
+ type
7
+ title
8
+ property
9
+ required
10
+ items
11
+ additional_items
12
+ pattern_properties
13
+ additional_properties
14
+ dependencies
15
+ dependent_required
16
+ format
17
+ content_media_type
18
+ content_encoding
19
+ enum
20
+ const
21
+ default
22
+ examples
23
+ max_length
24
+ min_length
25
+ pattern
26
+ maximum
27
+ exclusive_maximum
28
+ minimum
29
+ exclusive_minimum
30
+ multiple_of
31
+ max_items
32
+ min_items
33
+ unique_items
34
+ max_properties
35
+ min_properties
36
+ ].freeze
37
+ end