easy_talk 3.1.0 → 3.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -0
- data/.yardopts +13 -0
- data/CHANGELOG.md +75 -0
- data/README.md +616 -35
- data/Rakefile +27 -0
- data/docs/.gitignore +1 -0
- data/docs/about.markdown +28 -8
- data/docs/getting-started.markdown +102 -0
- data/docs/index.markdown +51 -4
- data/docs/json_schema_compliance.md +55 -0
- data/docs/nested-models.markdown +216 -0
- data/docs/property-types.markdown +212 -0
- data/docs/schema-definition.markdown +180 -0
- data/lib/easy_talk/builders/base_builder.rb +4 -2
- data/lib/easy_talk/builders/composition_builder.rb +10 -12
- data/lib/easy_talk/builders/object_builder.rb +45 -30
- data/lib/easy_talk/builders/registry.rb +168 -0
- data/lib/easy_talk/builders/typed_array_builder.rb +15 -4
- data/lib/easy_talk/configuration.rb +31 -1
- data/lib/easy_talk/error_formatter/base.rb +100 -0
- data/lib/easy_talk/error_formatter/error_code_mapper.rb +82 -0
- data/lib/easy_talk/error_formatter/flat.rb +38 -0
- data/lib/easy_talk/error_formatter/json_pointer.rb +38 -0
- data/lib/easy_talk/error_formatter/jsonapi.rb +64 -0
- data/lib/easy_talk/error_formatter/path_converter.rb +53 -0
- data/lib/easy_talk/error_formatter/rfc7807.rb +69 -0
- data/lib/easy_talk/error_formatter.rb +143 -0
- data/lib/easy_talk/errors.rb +2 -0
- data/lib/easy_talk/errors_helper.rb +63 -34
- data/lib/easy_talk/model.rb +123 -90
- data/lib/easy_talk/model_helper.rb +13 -0
- data/lib/easy_talk/naming_strategies.rb +20 -0
- data/lib/easy_talk/property.rb +16 -94
- data/lib/easy_talk/ref_helper.rb +27 -0
- data/lib/easy_talk/schema.rb +198 -0
- data/lib/easy_talk/schema_definition.rb +7 -1
- data/lib/easy_talk/schema_methods.rb +80 -0
- data/lib/easy_talk/tools/function_builder.rb +1 -1
- data/lib/easy_talk/type_introspection.rb +178 -0
- data/lib/easy_talk/types/base_composer.rb +2 -1
- data/lib/easy_talk/types/composer.rb +4 -0
- data/lib/easy_talk/validation_adapters/active_model_adapter.rb +329 -0
- data/lib/easy_talk/validation_adapters/base.rb +144 -0
- data/lib/easy_talk/validation_adapters/none_adapter.rb +36 -0
- data/lib/easy_talk/validation_adapters/registry.rb +87 -0
- data/lib/easy_talk/validation_builder.rb +28 -309
- data/lib/easy_talk/version.rb +1 -1
- data/lib/easy_talk.rb +41 -0
- metadata +26 -4
- data/docs/404.html +0 -25
- data/docs/_posts/2024-05-07-welcome-to-jekyll.markdown +0 -29
- data/easy_talk.gemspec +0 -39
|
@@ -1,327 +1,46 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'uri'
|
|
4
|
-
|
|
5
3
|
module EasyTalk
|
|
6
|
-
#
|
|
4
|
+
# @deprecated Use EasyTalk::ValidationAdapters::ActiveModelAdapter instead.
|
|
5
|
+
#
|
|
6
|
+
# The ValidationBuilder class is kept for backward compatibility.
|
|
7
|
+
# It delegates to the ActiveModelAdapter and shows a deprecation warning.
|
|
8
|
+
#
|
|
9
|
+
# This class will be removed in a future major version.
|
|
10
|
+
#
|
|
11
|
+
# @example Migration
|
|
12
|
+
# # Before (deprecated)
|
|
13
|
+
# EasyTalk::ValidationBuilder.build_validations(klass, :name, String, {})
|
|
14
|
+
#
|
|
15
|
+
# # After (recommended)
|
|
16
|
+
# EasyTalk::ValidationAdapters::ActiveModelAdapter.build_validations(klass, :name, String, {})
|
|
17
|
+
#
|
|
7
18
|
class ValidationBuilder
|
|
8
|
-
|
|
19
|
+
@deprecation_warned = false
|
|
20
|
+
|
|
21
|
+
# Build validations for a property and apply them to the model class.
|
|
9
22
|
#
|
|
23
|
+
# @deprecated Use EasyTalk::ValidationAdapters::ActiveModelAdapter.build_validations instead.
|
|
10
24
|
# @param klass [Class] The model class to apply validations to
|
|
11
25
|
# @param property_name [Symbol, String] The name of the property
|
|
12
26
|
# @param type [Class, Object] The type of the property
|
|
13
27
|
# @param constraints [Hash] The JSON Schema constraints for the property
|
|
14
28
|
# @return [void]
|
|
15
29
|
def self.build_validations(klass, property_name, type, constraints)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# Initialize a new ValidationBuilder
|
|
21
|
-
#
|
|
22
|
-
# @param klass [Class] The model class to apply validations to
|
|
23
|
-
# @param property_name [Symbol, String] The name of the property
|
|
24
|
-
# @param type [Class, Object] The type of the property
|
|
25
|
-
# @param constraints [Hash] The JSON Schema constraints for the property
|
|
26
|
-
attr_reader :klass, :property_name, :type, :constraints
|
|
27
|
-
|
|
28
|
-
def initialize(klass, property_name, type, constraints)
|
|
29
|
-
@klass = klass
|
|
30
|
-
@property_name = property_name.to_sym
|
|
31
|
-
@type = type
|
|
32
|
-
@constraints = constraints || {}
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Apply validations based on property type and constraints
|
|
36
|
-
def apply_validations
|
|
37
|
-
# Determine if the type is boolean
|
|
38
|
-
type_class = get_type_class(@type)
|
|
39
|
-
is_boolean = type_class == [TrueClass, FalseClass] ||
|
|
40
|
-
type_class == TrueClass ||
|
|
41
|
-
type_class == FalseClass ||
|
|
42
|
-
@type.to_s.include?('T::Boolean')
|
|
43
|
-
|
|
44
|
-
# Skip presence validation for booleans and nilable types
|
|
45
|
-
apply_presence_validation unless optional? || is_boolean || nilable_type?
|
|
46
|
-
if nilable_type?
|
|
47
|
-
# For nilable types, get the inner type and apply validations to it
|
|
48
|
-
inner_type = extract_inner_type(@type)
|
|
49
|
-
apply_type_validations(inner_type)
|
|
50
|
-
else
|
|
51
|
-
apply_type_validations(@type)
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Common validations for most types
|
|
55
|
-
apply_enum_validation if @constraints[:enum]
|
|
56
|
-
apply_const_validation if @constraints[:const]
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
private
|
|
60
|
-
|
|
61
|
-
# Determine if a property is optional based on constraints and configuration
|
|
62
|
-
def optional?
|
|
63
|
-
@constraints[:optional] == true ||
|
|
64
|
-
(@type.respond_to?(:nilable?) && @type.nilable? && EasyTalk.configuration.nilable_is_optional)
|
|
65
|
-
end
|
|
66
|
-
|
|
67
|
-
# Check if the type is nilable (e.g., T.nilable(String))
|
|
68
|
-
def nilable_type?(type = @type)
|
|
69
|
-
type.respond_to?(:nilable?) && type.nilable?
|
|
70
|
-
end
|
|
71
|
-
|
|
72
|
-
# Extract the inner type from a complex type like T.nilable(String)
|
|
73
|
-
def extract_inner_type(type)
|
|
74
|
-
if type.respond_to?(:unwrap_nilable) && type.unwrap_nilable.respond_to?(:raw_type)
|
|
75
|
-
type.unwrap_nilable.raw_type
|
|
76
|
-
elsif type.respond_to?(:types)
|
|
77
|
-
# For union types like T.nilable(String), extract the non-nil type
|
|
78
|
-
type.types.find { |t| t.respond_to?(:raw_type) && t.raw_type != NilClass }
|
|
79
|
-
else
|
|
80
|
-
type
|
|
81
|
-
end
|
|
82
|
-
end
|
|
83
|
-
|
|
84
|
-
# Apply validations based on the type of the property
|
|
85
|
-
def apply_type_validations(type)
|
|
86
|
-
type_class = get_type_class(type)
|
|
87
|
-
|
|
88
|
-
if type_class == String
|
|
89
|
-
apply_string_validations
|
|
90
|
-
elsif type_class == Integer
|
|
91
|
-
apply_integer_validations
|
|
92
|
-
elsif [Float, BigDecimal].include?(type_class)
|
|
93
|
-
apply_number_validations
|
|
94
|
-
elsif type_class == Array
|
|
95
|
-
apply_array_validations(type)
|
|
96
|
-
elsif type_class == [TrueClass,
|
|
97
|
-
FalseClass] || [TrueClass,
|
|
98
|
-
FalseClass].include?(type_class) || type.to_s.include?('T::Boolean')
|
|
99
|
-
apply_boolean_validations
|
|
100
|
-
elsif type_class.is_a?(Object) && type_class.include?(EasyTalk::Model)
|
|
101
|
-
apply_object_validations
|
|
102
|
-
end
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Determine the actual class for a type, handling Sorbet types
|
|
106
|
-
def get_type_class(type)
|
|
107
|
-
if type.is_a?(Class)
|
|
108
|
-
type
|
|
109
|
-
elsif type.respond_to?(:raw_type)
|
|
110
|
-
type.raw_type
|
|
111
|
-
elsif type.is_a?(T::Types::TypedArray)
|
|
112
|
-
Array
|
|
113
|
-
elsif type.is_a?(Symbol) || type.is_a?(String)
|
|
114
|
-
begin
|
|
115
|
-
type.to_s.classify.constantize
|
|
116
|
-
rescue StandardError
|
|
117
|
-
String
|
|
118
|
-
end
|
|
119
|
-
elsif type.to_s.include?('T::Boolean')
|
|
120
|
-
[TrueClass, FalseClass] # Return both boolean classes
|
|
121
|
-
elsif nilable_type?(type)
|
|
122
|
-
extract_inner_type(type)
|
|
123
|
-
else
|
|
124
|
-
String # Default fallback
|
|
125
|
-
end
|
|
126
|
-
end
|
|
127
|
-
|
|
128
|
-
# Add presence validation for the property
|
|
129
|
-
def apply_presence_validation
|
|
130
|
-
@klass.validates @property_name, presence: true
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
# Validate string-specific constraints
|
|
134
|
-
def apply_string_validations
|
|
135
|
-
# Handle format constraints
|
|
136
|
-
apply_format_validation(@constraints[:format]) if @constraints[:format]
|
|
137
|
-
|
|
138
|
-
# Handle pattern (regex) constraints
|
|
139
|
-
@klass.validates @property_name, format: { with: Regexp.new(@constraints[:pattern]) } if @constraints[:pattern]
|
|
140
|
-
|
|
141
|
-
# Handle length constraints
|
|
142
|
-
begin
|
|
143
|
-
length_options = {}
|
|
144
|
-
length_options[:minimum] = @constraints[:min_length] if @constraints[:min_length].is_a?(Numeric) && @constraints[:min_length] >= 0
|
|
145
|
-
length_options[:maximum] = @constraints[:max_length] if @constraints[:max_length].is_a?(Numeric) && @constraints[:max_length] >= 0
|
|
146
|
-
@klass.validates @property_name, length: length_options if length_options.any?
|
|
147
|
-
rescue ArgumentError
|
|
148
|
-
# Silently ignore invalid length constraints
|
|
149
|
-
end
|
|
150
|
-
end
|
|
151
|
-
|
|
152
|
-
# Apply format-specific validations (email, url, etc.)
|
|
153
|
-
def apply_format_validation(format)
|
|
154
|
-
format_configs = {
|
|
155
|
-
'email' => { with: URI::MailTo::EMAIL_REGEXP, message: 'must be a valid email address' },
|
|
156
|
-
'uri' => { with: URI::DEFAULT_PARSER.make_regexp, message: 'must be a valid URL' },
|
|
157
|
-
'url' => { with: URI::DEFAULT_PARSER.make_regexp, message: 'must be a valid URL' },
|
|
158
|
-
'uuid' => { with: /\A[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\z/i, message: 'must be a valid UUID' },
|
|
159
|
-
'date' => { with: /\A\d{4}-\d{2}-\d{2}\z/, message: 'must be a valid date in YYYY-MM-DD format' },
|
|
160
|
-
'date-time' => { with: /\A\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})?\z/, message: 'must be a valid ISO 8601 date-time' },
|
|
161
|
-
'time' => { with: /\A\d{2}:\d{2}:\d{2}(?:\.\d+)?\z/, message: 'must be a valid time in HH:MM:SS format' }
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
config = format_configs[format.to_s]
|
|
165
|
-
@klass.validates @property_name, format: config if config
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# Validate integer-specific constraints
|
|
169
|
-
def apply_integer_validations
|
|
170
|
-
apply_numeric_validations(only_integer: true)
|
|
171
|
-
end
|
|
172
|
-
|
|
173
|
-
# Validate number-specific constraints
|
|
174
|
-
def apply_number_validations
|
|
175
|
-
apply_numeric_validations(only_integer: false)
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
# Apply numeric validations for integers and floats
|
|
179
|
-
def apply_numeric_validations(only_integer: false)
|
|
180
|
-
begin
|
|
181
|
-
options = { only_integer: only_integer }
|
|
182
|
-
|
|
183
|
-
# Add range constraints - only if they are numeric
|
|
184
|
-
options[:greater_than_or_equal_to] = @constraints[:minimum] if @constraints[:minimum].is_a?(Numeric)
|
|
185
|
-
options[:less_than_or_equal_to] = @constraints[:maximum] if @constraints[:maximum].is_a?(Numeric)
|
|
186
|
-
options[:greater_than] = @constraints[:exclusive_minimum] if @constraints[:exclusive_minimum].is_a?(Numeric)
|
|
187
|
-
options[:less_than] = @constraints[:exclusive_maximum] if @constraints[:exclusive_maximum].is_a?(Numeric)
|
|
188
|
-
|
|
189
|
-
@klass.validates @property_name, numericality: options
|
|
190
|
-
rescue ArgumentError
|
|
191
|
-
# Silently ignore invalid numeric constraints
|
|
30
|
+
unless @deprecation_warned
|
|
31
|
+
warn "[DEPRECATION] EasyTalk::ValidationBuilder is deprecated and will be removed in a future version. " \
|
|
32
|
+
"Use EasyTalk::ValidationAdapters::ActiveModelAdapter instead."
|
|
33
|
+
@deprecation_warned = true
|
|
192
34
|
end
|
|
193
35
|
|
|
194
|
-
|
|
195
|
-
return unless @constraints[:multiple_of]
|
|
196
|
-
|
|
197
|
-
prop_name = @property_name
|
|
198
|
-
multiple_of_value = @constraints[:multiple_of]
|
|
199
|
-
@klass.validate do |record|
|
|
200
|
-
value = record.public_send(prop_name)
|
|
201
|
-
record.errors.add(prop_name, "must be a multiple of #{multiple_of_value}") if value && (value % multiple_of_value != 0)
|
|
202
|
-
end
|
|
36
|
+
ValidationAdapters::ActiveModelAdapter.build_validations(klass, property_name, type, constraints)
|
|
203
37
|
end
|
|
204
38
|
|
|
205
|
-
#
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
length_options[:minimum] = @constraints[:min_items] if @constraints[:min_items]
|
|
211
|
-
length_options[:maximum] = @constraints[:max_items] if @constraints[:max_items]
|
|
212
|
-
|
|
213
|
-
@klass.validates @property_name, length: length_options
|
|
214
|
-
end
|
|
215
|
-
|
|
216
|
-
# Validate uniqueness within the array
|
|
217
|
-
if @constraints[:unique_items]
|
|
218
|
-
prop_name = @property_name
|
|
219
|
-
@klass.validate do |record|
|
|
220
|
-
value = record.public_send(prop_name)
|
|
221
|
-
record.errors.add(prop_name, 'must contain unique items') if value && value.uniq.length != value.length
|
|
222
|
-
end
|
|
223
|
-
end
|
|
224
|
-
|
|
225
|
-
# Validate array item types if using T::Array[SomeType]
|
|
226
|
-
return unless type.respond_to?(:type_parameter)
|
|
227
|
-
|
|
228
|
-
inner_type = type.type_parameter
|
|
229
|
-
prop_name = @property_name
|
|
230
|
-
@klass.validate do |record|
|
|
231
|
-
value = record.public_send(prop_name)
|
|
232
|
-
if value.is_a?(Array)
|
|
233
|
-
value.each_with_index do |item, index|
|
|
234
|
-
record.errors.add(prop_name, "item at index #{index} must be a #{inner_type}") unless item.is_a?(inner_type)
|
|
235
|
-
end
|
|
236
|
-
end
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
|
|
240
|
-
# Validate boolean-specific constraints
|
|
241
|
-
def apply_boolean_validations
|
|
242
|
-
# For boolean values, validate inclusion in [true, false]
|
|
243
|
-
# If not optional, don't allow nil (equivalent to presence validation for booleans)
|
|
244
|
-
if optional?
|
|
245
|
-
@klass.validates @property_name, inclusion: { in: [true, false] }, allow_nil: true
|
|
246
|
-
else
|
|
247
|
-
@klass.validates @property_name, inclusion: { in: [true, false] }
|
|
248
|
-
# Add custom validation for nil values that provides the "can't be blank" message
|
|
249
|
-
prop_name = @property_name
|
|
250
|
-
@klass.validate do |record|
|
|
251
|
-
value = record.public_send(prop_name)
|
|
252
|
-
record.errors.add(prop_name, "can't be blank") if value.nil?
|
|
253
|
-
end
|
|
254
|
-
end
|
|
255
|
-
|
|
256
|
-
# Add type validation to ensure the value is actually a boolean
|
|
257
|
-
prop_name = @property_name
|
|
258
|
-
@klass.validate do |record|
|
|
259
|
-
value = record.public_send(prop_name)
|
|
260
|
-
record.errors.add(prop_name, 'must be a boolean') if value && ![true, false].include?(value)
|
|
261
|
-
end
|
|
262
|
-
end
|
|
263
|
-
|
|
264
|
-
# Validate object/hash-specific constraints
|
|
265
|
-
def apply_object_validations
|
|
266
|
-
# Capture necessary variables outside the validation block's scope
|
|
267
|
-
prop_name = @property_name
|
|
268
|
-
expected_type = get_type_class(@type) # Get the raw model class
|
|
269
|
-
|
|
270
|
-
@klass.validate do |record|
|
|
271
|
-
nested_object = record.public_send(prop_name)
|
|
272
|
-
|
|
273
|
-
# Only validate if the nested object is present
|
|
274
|
-
if nested_object
|
|
275
|
-
# Check if the object is of the expected type (e.g., an actual Email instance)
|
|
276
|
-
if nested_object.is_a?(expected_type)
|
|
277
|
-
# Check if this object appears to be empty (created from an empty hash)
|
|
278
|
-
# by checking if all defined properties are nil/blank
|
|
279
|
-
properties = expected_type.schema_definition.schema[:properties] || {}
|
|
280
|
-
all_properties_blank = properties.keys.all? do |property|
|
|
281
|
-
value = nested_object.public_send(property)
|
|
282
|
-
value.nil? || (value.respond_to?(:empty?) && value.empty?)
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
if all_properties_blank
|
|
286
|
-
# Treat as blank and add a presence error to the parent field
|
|
287
|
-
record.errors.add(prop_name, "can't be blank")
|
|
288
|
-
else
|
|
289
|
-
# If it's the correct type and not empty, validate it
|
|
290
|
-
unless nested_object.valid?
|
|
291
|
-
# Merge errors from the nested object into the parent
|
|
292
|
-
nested_object.errors.each do |error|
|
|
293
|
-
# Prefix the attribute name (e.g., 'email.address')
|
|
294
|
-
nested_key = "#{prop_name}.#{error.attribute}"
|
|
295
|
-
record.errors.add(nested_key.to_sym, error.message)
|
|
296
|
-
end
|
|
297
|
-
end
|
|
298
|
-
end
|
|
299
|
-
else
|
|
300
|
-
# If present but not the correct type, add a type error
|
|
301
|
-
record.errors.add(prop_name, "must be a valid #{expected_type.name}")
|
|
302
|
-
end
|
|
303
|
-
end
|
|
304
|
-
# NOTE: Presence validation (if nested_object is nil) is handled
|
|
305
|
-
# by apply_presence_validation based on the property definition.
|
|
306
|
-
end
|
|
307
|
-
end
|
|
308
|
-
|
|
309
|
-
# Apply enum validation for inclusion in a specific list
|
|
310
|
-
def apply_enum_validation
|
|
311
|
-
@klass.validates @property_name, inclusion: {
|
|
312
|
-
in: @constraints[:enum],
|
|
313
|
-
message: "must be one of: #{@constraints[:enum].join(', ')}"
|
|
314
|
-
}
|
|
315
|
-
end
|
|
316
|
-
|
|
317
|
-
# Apply const validation for equality with a specific value
|
|
318
|
-
def apply_const_validation
|
|
319
|
-
const_value = @constraints[:const]
|
|
320
|
-
prop_name = @property_name
|
|
321
|
-
@klass.validate do |record|
|
|
322
|
-
value = record.public_send(prop_name)
|
|
323
|
-
record.errors.add(prop_name, "must be equal to #{const_value}") if !value.nil? && value != const_value
|
|
324
|
-
end
|
|
39
|
+
# Reset the deprecation warning flag (useful for testing).
|
|
40
|
+
#
|
|
41
|
+
# @api private
|
|
42
|
+
def self.reset_deprecation_warning!
|
|
43
|
+
@deprecation_warned = false
|
|
325
44
|
end
|
|
326
45
|
end
|
|
327
46
|
end
|
data/lib/easy_talk/version.rb
CHANGED
data/lib/easy_talk.rb
CHANGED
|
@@ -5,15 +5,56 @@ module EasyTalk
|
|
|
5
5
|
require 'sorbet-runtime'
|
|
6
6
|
require 'easy_talk/sorbet_extension'
|
|
7
7
|
require 'easy_talk/errors'
|
|
8
|
+
require 'easy_talk/type_introspection'
|
|
8
9
|
require 'easy_talk/errors_helper'
|
|
9
10
|
require 'easy_talk/configuration'
|
|
11
|
+
require 'easy_talk/schema_methods'
|
|
10
12
|
require 'easy_talk/types/composer'
|
|
13
|
+
|
|
14
|
+
# Validation adapters
|
|
15
|
+
require 'easy_talk/validation_adapters/base'
|
|
16
|
+
require 'easy_talk/validation_adapters/registry'
|
|
17
|
+
require 'easy_talk/validation_adapters/active_model_adapter'
|
|
18
|
+
require 'easy_talk/validation_adapters/none_adapter'
|
|
19
|
+
|
|
20
|
+
# Builder registry for pluggable type support
|
|
21
|
+
require 'easy_talk/builders/registry'
|
|
22
|
+
|
|
11
23
|
require 'easy_talk/model'
|
|
24
|
+
require 'easy_talk/schema'
|
|
12
25
|
require 'easy_talk/property'
|
|
13
26
|
require 'easy_talk/schema_definition'
|
|
27
|
+
require 'easy_talk/validation_builder'
|
|
28
|
+
require 'easy_talk/error_formatter'
|
|
14
29
|
require 'easy_talk/tools/function_builder'
|
|
15
30
|
require 'easy_talk/version'
|
|
16
31
|
|
|
32
|
+
# Register default validation adapters
|
|
33
|
+
ValidationAdapters::Registry.register_default_adapters
|
|
34
|
+
|
|
35
|
+
# Register built-in type builders
|
|
36
|
+
Builders::Registry.register_built_in_types
|
|
37
|
+
|
|
38
|
+
# Register a custom type with its corresponding schema builder.
|
|
39
|
+
#
|
|
40
|
+
# This allows extending EasyTalk with domain-specific types without
|
|
41
|
+
# modifying the gem's source code.
|
|
42
|
+
#
|
|
43
|
+
# @param type_key [Class, String, Symbol] The type to register
|
|
44
|
+
# @param builder_class [Class] The builder class that generates JSON Schema
|
|
45
|
+
# @param collection [Boolean] Whether this is a collection type builder
|
|
46
|
+
# Collection builders receive (name, type, constraints) instead of (name, constraints)
|
|
47
|
+
# @return [void]
|
|
48
|
+
#
|
|
49
|
+
# @example Register a custom Money type
|
|
50
|
+
# EasyTalk.register_type(Money, MoneySchemaBuilder)
|
|
51
|
+
#
|
|
52
|
+
# @example Register a collection type
|
|
53
|
+
# EasyTalk.register_type(CustomArray, CustomArrayBuilder, collection: true)
|
|
54
|
+
def self.register_type(type_key, builder_class, collection: false)
|
|
55
|
+
Builders::Registry.register(type_key, builder_class, collection: collection)
|
|
56
|
+
end
|
|
57
|
+
|
|
17
58
|
def self.assert_valid_property_options(property_name, options, *valid_keys)
|
|
18
59
|
valid_keys.flatten!
|
|
19
60
|
options.each_key do |k|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: easy_talk
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sergio Bayona
|
|
@@ -86,19 +86,22 @@ extensions: []
|
|
|
86
86
|
extra_rdoc_files: []
|
|
87
87
|
files:
|
|
88
88
|
- ".rubocop.yml"
|
|
89
|
+
- ".yardopts"
|
|
89
90
|
- CHANGELOG.md
|
|
90
91
|
- CONSTRAINTS.md
|
|
91
92
|
- LICENSE.txt
|
|
92
93
|
- README.md
|
|
93
94
|
- Rakefile
|
|
94
95
|
- docs/.gitignore
|
|
95
|
-
- docs/404.html
|
|
96
96
|
- docs/Gemfile
|
|
97
97
|
- docs/_config.yml
|
|
98
|
-
- docs/_posts/2024-05-07-welcome-to-jekyll.markdown
|
|
99
98
|
- docs/about.markdown
|
|
99
|
+
- docs/getting-started.markdown
|
|
100
100
|
- docs/index.markdown
|
|
101
|
-
-
|
|
101
|
+
- docs/json_schema_compliance.md
|
|
102
|
+
- docs/nested-models.markdown
|
|
103
|
+
- docs/property-types.markdown
|
|
104
|
+
- docs/schema-definition.markdown
|
|
102
105
|
- lib/easy_talk.rb
|
|
103
106
|
- lib/easy_talk/builders/base_builder.rb
|
|
104
107
|
- lib/easy_talk/builders/boolean_builder.rb
|
|
@@ -108,21 +111,40 @@ files:
|
|
|
108
111
|
- lib/easy_talk/builders/null_builder.rb
|
|
109
112
|
- lib/easy_talk/builders/number_builder.rb
|
|
110
113
|
- lib/easy_talk/builders/object_builder.rb
|
|
114
|
+
- lib/easy_talk/builders/registry.rb
|
|
111
115
|
- lib/easy_talk/builders/string_builder.rb
|
|
112
116
|
- lib/easy_talk/builders/temporal_builder.rb
|
|
113
117
|
- lib/easy_talk/builders/typed_array_builder.rb
|
|
114
118
|
- lib/easy_talk/builders/union_builder.rb
|
|
115
119
|
- lib/easy_talk/configuration.rb
|
|
120
|
+
- lib/easy_talk/error_formatter.rb
|
|
121
|
+
- lib/easy_talk/error_formatter/base.rb
|
|
122
|
+
- lib/easy_talk/error_formatter/error_code_mapper.rb
|
|
123
|
+
- lib/easy_talk/error_formatter/flat.rb
|
|
124
|
+
- lib/easy_talk/error_formatter/json_pointer.rb
|
|
125
|
+
- lib/easy_talk/error_formatter/jsonapi.rb
|
|
126
|
+
- lib/easy_talk/error_formatter/path_converter.rb
|
|
127
|
+
- lib/easy_talk/error_formatter/rfc7807.rb
|
|
116
128
|
- lib/easy_talk/errors.rb
|
|
117
129
|
- lib/easy_talk/errors_helper.rb
|
|
118
130
|
- lib/easy_talk/keywords.rb
|
|
119
131
|
- lib/easy_talk/model.rb
|
|
132
|
+
- lib/easy_talk/model_helper.rb
|
|
133
|
+
- lib/easy_talk/naming_strategies.rb
|
|
120
134
|
- lib/easy_talk/property.rb
|
|
135
|
+
- lib/easy_talk/ref_helper.rb
|
|
136
|
+
- lib/easy_talk/schema.rb
|
|
121
137
|
- lib/easy_talk/schema_definition.rb
|
|
138
|
+
- lib/easy_talk/schema_methods.rb
|
|
122
139
|
- lib/easy_talk/sorbet_extension.rb
|
|
123
140
|
- lib/easy_talk/tools/function_builder.rb
|
|
141
|
+
- lib/easy_talk/type_introspection.rb
|
|
124
142
|
- lib/easy_talk/types/base_composer.rb
|
|
125
143
|
- lib/easy_talk/types/composer.rb
|
|
144
|
+
- lib/easy_talk/validation_adapters/active_model_adapter.rb
|
|
145
|
+
- lib/easy_talk/validation_adapters/base.rb
|
|
146
|
+
- lib/easy_talk/validation_adapters/none_adapter.rb
|
|
147
|
+
- lib/easy_talk/validation_adapters/registry.rb
|
|
126
148
|
- lib/easy_talk/validation_builder.rb
|
|
127
149
|
- lib/easy_talk/version.rb
|
|
128
150
|
homepage: https://github.com/sergiobayona/easy_talk
|
data/docs/404.html
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
permalink: /404.html
|
|
3
|
-
layout: default
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
<style type="text/css" media="screen">
|
|
7
|
-
.container {
|
|
8
|
-
margin: 10px auto;
|
|
9
|
-
max-width: 600px;
|
|
10
|
-
text-align: center;
|
|
11
|
-
}
|
|
12
|
-
h1 {
|
|
13
|
-
margin: 30px 0;
|
|
14
|
-
font-size: 4em;
|
|
15
|
-
line-height: 1;
|
|
16
|
-
letter-spacing: -1px;
|
|
17
|
-
}
|
|
18
|
-
</style>
|
|
19
|
-
|
|
20
|
-
<div class="container">
|
|
21
|
-
<h1>404</h1>
|
|
22
|
-
|
|
23
|
-
<p><strong>Page not found :(</strong></p>
|
|
24
|
-
<p>The requested page could not be found.</p>
|
|
25
|
-
</div>
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
layout: post
|
|
3
|
-
title: "Welcome to Jekyll!"
|
|
4
|
-
date: 2024-05-07 18:39:19 -0500
|
|
5
|
-
categories: jekyll update
|
|
6
|
-
---
|
|
7
|
-
You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated.
|
|
8
|
-
|
|
9
|
-
Jekyll requires blog post files to be named according to the following format:
|
|
10
|
-
|
|
11
|
-
`YEAR-MONTH-DAY-title.MARKUP`
|
|
12
|
-
|
|
13
|
-
Where `YEAR` is a four-digit number, `MONTH` and `DAY` are both two-digit numbers, and `MARKUP` is the file extension representing the format used in the file. After that, include the necessary front matter. Take a look at the source for this post to get an idea about how it works.
|
|
14
|
-
|
|
15
|
-
Jekyll also offers powerful support for code snippets:
|
|
16
|
-
|
|
17
|
-
{% highlight ruby %}
|
|
18
|
-
def print_hi(name)
|
|
19
|
-
puts "Hi, #{name}"
|
|
20
|
-
end
|
|
21
|
-
print_hi('Tom')
|
|
22
|
-
#=> prints 'Hi, Tom' to STDOUT.
|
|
23
|
-
{% endhighlight %}
|
|
24
|
-
|
|
25
|
-
Check out the [Jekyll docs][jekyll-docs] for more info on how to get the most out of Jekyll. File all bugs/feature requests at [Jekyll’s GitHub repo][jekyll-gh]. If you have questions, you can ask them on [Jekyll Talk][jekyll-talk].
|
|
26
|
-
|
|
27
|
-
[jekyll-docs]: https://jekyllrb.com/docs/home
|
|
28
|
-
[jekyll-gh]: https://github.com/jekyll/jekyll
|
|
29
|
-
[jekyll-talk]: https://talk.jekyllrb.com/
|
data/easy_talk.gemspec
DELETED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'lib/easy_talk/version'
|
|
4
|
-
|
|
5
|
-
Gem::Specification.new do |spec|
|
|
6
|
-
spec.name = 'easy_talk'
|
|
7
|
-
spec.version = EasyTalk::VERSION
|
|
8
|
-
spec.authors = ['Sergio Bayona']
|
|
9
|
-
spec.email = ['bayona.sergio@gmail.com']
|
|
10
|
-
|
|
11
|
-
spec.summary = 'Generate json-schema from Ruby classes with ActiveModel integration.'
|
|
12
|
-
spec.description = 'Generate json-schema from plain Ruby classes with ActiveModel integration for validations and serialization.'
|
|
13
|
-
spec.homepage = 'https://github.com/sergiobayona/easy_talk'
|
|
14
|
-
spec.license = 'MIT'
|
|
15
|
-
spec.required_ruby_version = '>= 3.2'
|
|
16
|
-
|
|
17
|
-
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
|
18
|
-
|
|
19
|
-
spec.metadata['homepage_uri'] = spec.homepage
|
|
20
|
-
spec.metadata['changelog_uri'] = 'https://github.com/sergiobayona/easy_talk/blob/main/CHANGELOG.md'
|
|
21
|
-
|
|
22
|
-
# Specify which files should be added to the gem when it is released.
|
|
23
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
24
|
-
spec.files = Dir.chdir(__dir__) do
|
|
25
|
-
`git ls-files -z`.split("\x0").reject do |f|
|
|
26
|
-
(File.expand_path(f) == __FILE__) ||
|
|
27
|
-
f.start_with?(*%w[bin/ spec/ .git .github Gemfile])
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
spec.require_paths = ['lib']
|
|
32
|
-
|
|
33
|
-
spec.add_dependency 'activemodel', ['>= 7.0', '< 9.0']
|
|
34
|
-
spec.add_dependency 'activesupport', ['>= 7.0', '< 9.0']
|
|
35
|
-
spec.add_dependency 'js_regex', '~> 3.0'
|
|
36
|
-
spec.add_dependency 'sorbet-runtime', '~> 0.5'
|
|
37
|
-
|
|
38
|
-
spec.metadata['rubygems_mfa_required'] = 'true'
|
|
39
|
-
end
|