lluminary 0.1.4 → 0.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/lib/lluminary/models/base.rb +166 -66
- data/lib/lluminary/schema.rb +26 -0
- data/lib/lluminary/schema_model.rb +134 -64
- data/spec/examples/character_profiler_spec.rb +85 -0
- data/spec/lluminary/models/base_spec.rb +901 -100
- data/spec/lluminary/schema_spec.rb +255 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 20a5892da2184571fc6e1a681fc5ed81143c87a5abafc745249b0a28d5ef4f3a
|
4
|
+
data.tar.gz: 984b6467b9ac1a82af4107efbd72a4ae14ab05cae8e3838120dd0252ea0a5dc0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85ad7a6f048fc3bad3ca587a638da18e5026dce9a7f5068e551170c5531f26a1b5da83b6fe193339dbeae1eaf087d50283d96742e5f0ace04905a9eb8abb73b1
|
7
|
+
data.tar.gz: 0c501c6277d1b58114998b005b02d0a6ab10aec14ed8f21026d8c682a20c9d09692ee818cb24252c2befd88f99aa578fa4302b880d6f8ed67b05ae8b56b94b87
|
@@ -25,11 +25,11 @@ module Lluminary
|
|
25
25
|
|
26
26
|
#{output_preamble}
|
27
27
|
|
28
|
-
#{
|
28
|
+
#{format_fields_descriptions(task.class.output_fields)}
|
29
29
|
|
30
30
|
#{json_preamble}
|
31
31
|
|
32
|
-
#{
|
32
|
+
#{generate_example_json_object(task.class.output_fields)}
|
33
33
|
PROMPT
|
34
34
|
end
|
35
35
|
|
@@ -46,30 +46,142 @@ module Lluminary
|
|
46
46
|
"Your response must be ONLY this JSON object:"
|
47
47
|
end
|
48
48
|
|
49
|
-
def
|
49
|
+
def format_fields_descriptions(fields)
|
50
50
|
fields
|
51
|
-
.map
|
52
|
-
|
53
|
-
|
51
|
+
.map { |name, field| format_field_description(name, field) }
|
52
|
+
.compact # Remove nil entries from skipped types
|
53
|
+
.join("\n\n")
|
54
|
+
end
|
54
55
|
|
55
|
-
|
56
|
-
|
57
|
-
|
56
|
+
def format_field_description(name, field, name_for_example = nil)
|
57
|
+
case field[:type]
|
58
|
+
when :hash
|
59
|
+
format_hash_description(name, field, name_for_example)
|
60
|
+
when :array
|
61
|
+
format_array_description(name, field, name_for_example)
|
62
|
+
else
|
63
|
+
format_simple_field_description(name, field, name_for_example)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def format_hash_description(name, field, name_for_example = nil)
|
68
|
+
return nil unless field[:fields]
|
69
|
+
|
70
|
+
lines = build_field_description_lines(name, field, name_for_example)
|
71
|
+
|
72
|
+
# Add descriptions for each field in the hash
|
73
|
+
field[:fields].each do |subname, subfield|
|
74
|
+
lines << "\n#{format_field_description("#{name}.#{subname}", subfield, subname)}"
|
75
|
+
end
|
58
76
|
|
59
|
-
|
60
|
-
|
77
|
+
lines.join("\n")
|
78
|
+
end
|
79
|
+
|
80
|
+
# Helper to ensure consistent JSON formatting for examples
|
81
|
+
def format_json_for_examples(value)
|
82
|
+
case value
|
83
|
+
when Hash, Array
|
84
|
+
JSON.generate(value)
|
85
|
+
else
|
86
|
+
value.inspect
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def format_simple_field_description(name, field, name_for_example = nil)
|
91
|
+
build_field_description_lines(name, field, name_for_example).join("\n")
|
92
|
+
end
|
93
|
+
|
94
|
+
def format_array_description(name, field, name_for_example = nil)
|
95
|
+
lines = build_field_description_lines(name, field, name_for_example)
|
96
|
+
|
97
|
+
if field[:element_type]
|
98
|
+
if field[:element_type][:type] == :array
|
99
|
+
# Create a nested array field by adding [] to the name
|
100
|
+
# and recursively call format_array_description
|
101
|
+
element_field = field[:element_type].dup
|
102
|
+
nested_description =
|
103
|
+
format_array_description("#{name}[]", element_field, "item")
|
104
|
+
lines << "\n#{nested_description}"
|
105
|
+
elsif field[:element_type][:type] == :hash &&
|
106
|
+
field[:element_type][:fields]
|
107
|
+
field[:element_type][:fields].each do |subname, subfield|
|
108
|
+
inner_field = {
|
109
|
+
type: subfield[:type],
|
110
|
+
description: subfield[:description]
|
111
|
+
}
|
112
|
+
inner_lines =
|
113
|
+
build_field_description_lines(
|
114
|
+
"#{name}[].#{subname}",
|
115
|
+
inner_field,
|
116
|
+
subname
|
117
|
+
)
|
118
|
+
lines << "\n#{inner_lines.join("\n")}"
|
61
119
|
end
|
120
|
+
else
|
121
|
+
inner_field = {
|
122
|
+
type: field[:element_type][:type],
|
123
|
+
description: field[:element_type][:description]
|
124
|
+
}
|
125
|
+
inner_lines =
|
126
|
+
build_field_description_lines("#{name}[]", inner_field, "item")
|
127
|
+
lines << "\n#{inner_lines.join("\n")}"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
lines.join("\n")
|
132
|
+
end
|
133
|
+
|
134
|
+
# Common method for building field description lines
|
135
|
+
def build_field_description_lines(name, field, name_for_example = nil)
|
136
|
+
lines = []
|
137
|
+
# Add field description
|
138
|
+
lines << "# #{name}"
|
139
|
+
lines << "Description: #{field[:description]}" if field[:description]
|
140
|
+
lines << "Type: #{format_type(field)}"
|
141
|
+
|
142
|
+
# Add validation info
|
143
|
+
if (validations = describe_validations(field))
|
144
|
+
lines << "Validations: #{validations}"
|
145
|
+
end
|
146
|
+
|
147
|
+
# Generate and add example
|
148
|
+
example_value =
|
149
|
+
generate_example_value(
|
150
|
+
name_for_example || name.to_s.split(".").last,
|
151
|
+
field
|
152
|
+
)
|
153
|
+
lines << "Example: #{format_json_for_examples(example_value)}"
|
154
|
+
|
155
|
+
lines
|
156
|
+
end
|
62
157
|
|
63
|
-
|
64
|
-
|
158
|
+
def format_type(field)
|
159
|
+
case field[:type]
|
160
|
+
when :datetime
|
161
|
+
"datetime in ISO8601 format"
|
162
|
+
when :array
|
163
|
+
if field[:element_type].nil?
|
164
|
+
"array"
|
165
|
+
elsif field[:element_type][:type] == :array
|
166
|
+
"array of arrays"
|
167
|
+
elsif field[:element_type][:type] == :datetime
|
168
|
+
"array of datetimes in ISO8601 format"
|
169
|
+
elsif field[:element_type][:type] == :hash
|
170
|
+
"array of objects"
|
171
|
+
else
|
172
|
+
"array of #{field[:element_type][:type]}s"
|
65
173
|
end
|
66
|
-
|
174
|
+
when :hash
|
175
|
+
"object"
|
176
|
+
else
|
177
|
+
field[:type].to_s
|
178
|
+
end
|
67
179
|
end
|
68
180
|
|
69
|
-
def describe_validations(
|
70
|
-
return unless validations&.any?
|
181
|
+
def describe_validations(field)
|
182
|
+
return unless field[:validations]&.any?
|
71
183
|
|
72
|
-
validations
|
184
|
+
field[:validations]
|
73
185
|
.map do |options|
|
74
186
|
case options.keys.first
|
75
187
|
when :presence
|
@@ -81,7 +193,7 @@ module Lluminary
|
|
81
193
|
when :format
|
82
194
|
"must match format: #{options[:format][:with]}"
|
83
195
|
when :length
|
84
|
-
describe_length_validation(options[:length])
|
196
|
+
describe_length_validation(options[:length], field[:type])
|
85
197
|
when :numericality
|
86
198
|
describe_numericality_validation(options[:numericality])
|
87
199
|
when :comparison
|
@@ -94,43 +206,29 @@ module Lluminary
|
|
94
206
|
.join(", ")
|
95
207
|
end
|
96
208
|
|
97
|
-
def describe_length_validation(options)
|
209
|
+
def describe_length_validation(options, field_type = nil)
|
98
210
|
descriptions = []
|
211
|
+
units = field_type == :array ? "elements" : "characters"
|
212
|
+
|
99
213
|
if options[:minimum]
|
100
|
-
descriptions << "must
|
214
|
+
descriptions << "must have at least #{options[:minimum]} #{units}"
|
101
215
|
end
|
102
216
|
if options[:maximum]
|
103
|
-
descriptions << "must
|
217
|
+
descriptions << "must have at most #{options[:maximum]} #{units}"
|
104
218
|
end
|
105
219
|
if options[:is]
|
106
|
-
descriptions << "must
|
220
|
+
descriptions << "must have exactly #{options[:is]} #{units}"
|
107
221
|
end
|
108
222
|
if options[:in]
|
109
|
-
descriptions << "must
|
223
|
+
descriptions << "must have between #{options[:in].min} and #{options[:in].max} #{units}"
|
110
224
|
end
|
111
225
|
descriptions.join(", ")
|
112
226
|
end
|
113
227
|
|
114
228
|
def describe_numericality_validation(options)
|
115
229
|
descriptions = []
|
116
|
-
|
117
|
-
|
118
|
-
end
|
119
|
-
if options[:greater_than_or_equal_to]
|
120
|
-
descriptions << "must be greater than or equal to #{options[:greater_than_or_equal_to]}"
|
121
|
-
end
|
122
|
-
if options[:equal_to]
|
123
|
-
descriptions << "must be equal to #{options[:equal_to]}"
|
124
|
-
end
|
125
|
-
if options[:less_than]
|
126
|
-
descriptions << "must be less than #{options[:less_than]}"
|
127
|
-
end
|
128
|
-
if options[:less_than_or_equal_to]
|
129
|
-
descriptions << "must be less than or equal to #{options[:less_than_or_equal_to]}"
|
130
|
-
end
|
131
|
-
if options[:other_than]
|
132
|
-
descriptions << "must be other than #{options[:other_than]}"
|
133
|
-
end
|
230
|
+
descriptions.concat(describe_common_comparisons(options))
|
231
|
+
|
134
232
|
if options[:in]
|
135
233
|
descriptions << "must be in: #{options[:in].to_a.join(", ")}"
|
136
234
|
end
|
@@ -140,6 +238,10 @@ module Lluminary
|
|
140
238
|
end
|
141
239
|
|
142
240
|
def describe_comparison_validation(options)
|
241
|
+
describe_common_comparisons(options).join(", ")
|
242
|
+
end
|
243
|
+
|
244
|
+
def describe_common_comparisons(options)
|
143
245
|
descriptions = []
|
144
246
|
if options[:greater_than]
|
145
247
|
descriptions << "must be greater than #{options[:greater_than]}"
|
@@ -159,10 +261,10 @@ module Lluminary
|
|
159
261
|
if options[:other_than]
|
160
262
|
descriptions << "must be other than #{options[:other_than]}"
|
161
263
|
end
|
162
|
-
descriptions
|
264
|
+
descriptions
|
163
265
|
end
|
164
266
|
|
165
|
-
def
|
267
|
+
def generate_example_json_object(fields)
|
166
268
|
example =
|
167
269
|
fields.each_with_object({}) do |(name, field), hash|
|
168
270
|
hash[name] = generate_example_value(name, field)
|
@@ -170,26 +272,14 @@ module Lluminary
|
|
170
272
|
JSON.pretty_generate(example)
|
171
273
|
end
|
172
274
|
|
173
|
-
def format_type(field)
|
174
|
-
type = field[:type]
|
175
|
-
case type
|
176
|
-
when :datetime
|
177
|
-
"datetime in ISO8601 format"
|
178
|
-
when :array
|
179
|
-
if field[:element_type]
|
180
|
-
"array of #{format_type(field[:element_type])}"
|
181
|
-
else
|
182
|
-
"array"
|
183
|
-
end
|
184
|
-
else
|
185
|
-
type.to_s
|
186
|
-
end
|
187
|
-
end
|
188
|
-
|
189
275
|
def generate_example_value(name, field)
|
190
276
|
case field[:type]
|
191
277
|
when :string
|
192
|
-
"
|
278
|
+
if name == "item" # For items in arrays
|
279
|
+
"first #{name}"
|
280
|
+
else
|
281
|
+
"your #{name} here"
|
282
|
+
end
|
193
283
|
when :integer
|
194
284
|
0
|
195
285
|
when :datetime
|
@@ -200,6 +290,8 @@ module Lluminary
|
|
200
290
|
0.0
|
201
291
|
when :array
|
202
292
|
generate_array_example(name, field)
|
293
|
+
when :hash
|
294
|
+
generate_hash_example(name, field)
|
203
295
|
end
|
204
296
|
end
|
205
297
|
|
@@ -208,11 +300,7 @@ module Lluminary
|
|
208
300
|
|
209
301
|
case field[:element_type][:type]
|
210
302
|
when :string
|
211
|
-
[
|
212
|
-
"first #{name.to_s.singularize}",
|
213
|
-
"second #{name.to_s.singularize}",
|
214
|
-
"..."
|
215
|
-
]
|
303
|
+
["first #{name.to_s.singularize}", "second #{name.to_s.singularize}"]
|
216
304
|
when :integer
|
217
305
|
[1, 2, 3]
|
218
306
|
when :float
|
@@ -226,8 +314,20 @@ module Lluminary
|
|
226
314
|
inner_example = generate_array_example("item", field[:element_type])
|
227
315
|
[inner_example, inner_example]
|
228
316
|
else
|
229
|
-
[[
|
317
|
+
[[], []]
|
230
318
|
end
|
319
|
+
when :hash
|
320
|
+
example =
|
321
|
+
generate_hash_example(name.to_s.singularize, field[:element_type])
|
322
|
+
[example, example]
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def generate_hash_example(name, field)
|
327
|
+
return {} unless field[:fields]
|
328
|
+
|
329
|
+
field[:fields].each_with_object({}) do |(subname, subfield), hash|
|
330
|
+
hash[subname] = generate_example_value(subname, subfield)
|
231
331
|
end
|
232
332
|
end
|
233
333
|
end
|
data/lib/lluminary/schema.rb
CHANGED
@@ -42,6 +42,21 @@ module Lluminary
|
|
42
42
|
@fields[name] = field
|
43
43
|
end
|
44
44
|
|
45
|
+
def hash(name, description: nil, &block)
|
46
|
+
unless block
|
47
|
+
raise ArgumentError, "Hash fields must be defined with a block"
|
48
|
+
end
|
49
|
+
|
50
|
+
nested_schema = Schema.new
|
51
|
+
nested_schema.instance_eval(&block)
|
52
|
+
|
53
|
+
@fields[name] = {
|
54
|
+
type: :hash,
|
55
|
+
description: description,
|
56
|
+
fields: nested_schema.fields
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
45
60
|
attr_reader :fields
|
46
61
|
|
47
62
|
def validates(*args, **options)
|
@@ -100,6 +115,17 @@ module Lluminary
|
|
100
115
|
) if block
|
101
116
|
field
|
102
117
|
end
|
118
|
+
|
119
|
+
def hash(description: nil, &block)
|
120
|
+
unless block
|
121
|
+
raise ArgumentError, "Hash fields must be defined with a block"
|
122
|
+
end
|
123
|
+
|
124
|
+
nested_schema = Schema.new
|
125
|
+
nested_schema.instance_eval(&block)
|
126
|
+
|
127
|
+
{ type: :hash, description: description, fields: nested_schema.fields }
|
128
|
+
end
|
103
129
|
end
|
104
130
|
end
|
105
131
|
end
|
@@ -21,6 +21,12 @@ module Lluminary
|
|
21
21
|
|
22
22
|
def self.build(fields:, validations:)
|
23
23
|
Class.new(self) do
|
24
|
+
class << self
|
25
|
+
attr_accessor :schema_fields
|
26
|
+
end
|
27
|
+
|
28
|
+
self.schema_fields = fields
|
29
|
+
|
24
30
|
# Add accessors for each field
|
25
31
|
fields.each_key do |name|
|
26
32
|
define_method(name) { @attributes[name.to_s] }
|
@@ -29,7 +35,7 @@ module Lluminary
|
|
29
35
|
|
30
36
|
# Add raw_response field and validation
|
31
37
|
define_method(:raw_response) { @attributes["raw_response"] }
|
32
|
-
define_method(
|
38
|
+
define_method("raw_response=") do |value|
|
33
39
|
@attributes["raw_response"] = value
|
34
40
|
end
|
35
41
|
|
@@ -41,92 +47,44 @@ module Lluminary
|
|
41
47
|
record.errors.add(:raw_response, "must be valid JSON")
|
42
48
|
end
|
43
49
|
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# Add type validations
|
47
|
-
validate do |record|
|
48
|
-
def validate_array_field(
|
49
|
-
record,
|
50
|
-
name,
|
51
|
-
value,
|
52
|
-
element_type,
|
53
|
-
path = nil
|
54
|
-
)
|
55
|
-
field_name = path || name
|
56
|
-
|
57
|
-
unless value.is_a?(Array)
|
58
|
-
record.errors.add(field_name, "must be an Array")
|
59
|
-
return
|
60
|
-
end
|
61
|
-
|
62
|
-
return unless element_type # untyped array
|
63
|
-
|
64
|
-
value.each_with_index do |element, index|
|
65
|
-
current_path = "#{field_name}[#{index}]"
|
66
|
-
|
67
|
-
case element_type[:type]
|
68
|
-
when :array
|
69
|
-
validate_array_field(
|
70
|
-
record,
|
71
|
-
name,
|
72
|
-
element,
|
73
|
-
element_type[:element_type],
|
74
|
-
current_path
|
75
|
-
)
|
76
|
-
when :string
|
77
|
-
unless element.is_a?(String)
|
78
|
-
record.errors.add(current_path, "must be a String")
|
79
|
-
end
|
80
|
-
when :integer
|
81
|
-
unless element.is_a?(Integer)
|
82
|
-
record.errors.add(current_path, "must be an Integer")
|
83
|
-
end
|
84
|
-
when :boolean
|
85
|
-
unless [true, false].include?(element)
|
86
|
-
record.errors.add(current_path, "must be true or false")
|
87
|
-
end
|
88
|
-
when :float
|
89
|
-
unless element.is_a?(Float)
|
90
|
-
record.errors.add(current_path, "must be a float")
|
91
|
-
end
|
92
|
-
when :datetime
|
93
|
-
unless element.is_a?(DateTime)
|
94
|
-
record.errors.add(current_path, "must be a DateTime")
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
50
|
|
100
51
|
record.attributes.each do |name, value|
|
101
52
|
next if name == "raw_response"
|
102
53
|
next if value.nil?
|
103
54
|
|
104
|
-
field =
|
55
|
+
field = self.class.schema_fields[name.to_sym]
|
105
56
|
next unless field
|
106
57
|
|
107
58
|
case field[:type]
|
59
|
+
when :hash
|
60
|
+
validate_hash_field(record, name.to_s.capitalize, value, field)
|
61
|
+
when :array
|
62
|
+
validate_array_field(
|
63
|
+
record,
|
64
|
+
name.to_s.capitalize,
|
65
|
+
value,
|
66
|
+
field[:element_type]
|
67
|
+
)
|
108
68
|
when :string
|
109
69
|
unless value.is_a?(String)
|
110
|
-
record.errors.add(name, "must be a String")
|
70
|
+
record.errors.add(name.to_s.capitalize, "must be a String")
|
111
71
|
end
|
112
72
|
when :integer
|
113
73
|
unless value.is_a?(Integer)
|
114
|
-
record.errors.add(name, "must be an Integer")
|
74
|
+
record.errors.add(name.to_s.capitalize, "must be an Integer")
|
115
75
|
end
|
116
76
|
when :boolean
|
117
77
|
unless [true, false].include?(value)
|
118
|
-
record.errors.add(name, "must be true or false")
|
78
|
+
record.errors.add(name.to_s.capitalize, "must be true or false")
|
119
79
|
end
|
120
80
|
when :float
|
121
81
|
unless value.is_a?(Float)
|
122
|
-
record.errors.add(name, "must be a float")
|
82
|
+
record.errors.add(name.to_s.capitalize, "must be a float")
|
123
83
|
end
|
124
84
|
when :datetime
|
125
85
|
unless value.is_a?(DateTime)
|
126
|
-
record.errors.add(name, "must be a DateTime")
|
86
|
+
record.errors.add(name.to_s.capitalize, "must be a DateTime")
|
127
87
|
end
|
128
|
-
when :array
|
129
|
-
validate_array_field(record, name, value, field[:element_type])
|
130
88
|
end
|
131
89
|
end
|
132
90
|
end
|
@@ -138,6 +96,118 @@ module Lluminary
|
|
138
96
|
define_singleton_method(:model_name) do
|
139
97
|
ActiveModel::Name.new(self, nil, "SchemaModel")
|
140
98
|
end
|
99
|
+
|
100
|
+
private
|
101
|
+
|
102
|
+
def validate_hash_field(
|
103
|
+
record,
|
104
|
+
name,
|
105
|
+
value,
|
106
|
+
field_definition,
|
107
|
+
path = nil
|
108
|
+
)
|
109
|
+
field_name = path || name
|
110
|
+
|
111
|
+
unless value.is_a?(Hash)
|
112
|
+
record.errors.add(field_name, "must be a Hash")
|
113
|
+
return
|
114
|
+
end
|
115
|
+
|
116
|
+
field_definition[:fields].each do |key, field|
|
117
|
+
current_path = path ? "#{path}[#{key}]" : "#{field_name}[#{key}]"
|
118
|
+
# Try both string and symbol keys
|
119
|
+
field_value = value[key.to_s] || value[key.to_sym]
|
120
|
+
|
121
|
+
next if field_value.nil?
|
122
|
+
|
123
|
+
case field[:type]
|
124
|
+
when :hash
|
125
|
+
validate_hash_field(record, key, field_value, field, current_path)
|
126
|
+
when :array
|
127
|
+
validate_array_field(
|
128
|
+
record,
|
129
|
+
key,
|
130
|
+
field_value,
|
131
|
+
field[:element_type],
|
132
|
+
current_path
|
133
|
+
)
|
134
|
+
when :string
|
135
|
+
unless field_value.is_a?(String)
|
136
|
+
record.errors.add(current_path, "must be a String")
|
137
|
+
end
|
138
|
+
when :integer
|
139
|
+
unless field_value.is_a?(Integer)
|
140
|
+
record.errors.add(current_path, "must be an Integer")
|
141
|
+
end
|
142
|
+
when :boolean
|
143
|
+
unless [true, false].include?(field_value)
|
144
|
+
record.errors.add(current_path, "must be true or false")
|
145
|
+
end
|
146
|
+
when :float
|
147
|
+
unless field_value.is_a?(Float)
|
148
|
+
record.errors.add(current_path, "must be a float")
|
149
|
+
end
|
150
|
+
when :datetime
|
151
|
+
unless field_value.is_a?(DateTime)
|
152
|
+
record.errors.add(current_path, "must be a DateTime")
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
def validate_array_field(record, name, value, element_type, path = nil)
|
159
|
+
field_name = path || name
|
160
|
+
|
161
|
+
unless value.is_a?(Array)
|
162
|
+
record.errors.add(field_name, "must be an Array")
|
163
|
+
return
|
164
|
+
end
|
165
|
+
|
166
|
+
return unless element_type # untyped array
|
167
|
+
|
168
|
+
value.each_with_index do |element, index|
|
169
|
+
current_path = "#{field_name}[#{index}]"
|
170
|
+
|
171
|
+
case element_type[:type]
|
172
|
+
when :hash
|
173
|
+
validate_hash_field(
|
174
|
+
record,
|
175
|
+
name,
|
176
|
+
element,
|
177
|
+
element_type,
|
178
|
+
current_path
|
179
|
+
)
|
180
|
+
when :array
|
181
|
+
validate_array_field(
|
182
|
+
record,
|
183
|
+
name,
|
184
|
+
element,
|
185
|
+
element_type[:element_type],
|
186
|
+
current_path
|
187
|
+
)
|
188
|
+
when :string
|
189
|
+
unless element.is_a?(String)
|
190
|
+
record.errors.add(current_path, "must be a String")
|
191
|
+
end
|
192
|
+
when :integer
|
193
|
+
unless element.is_a?(Integer)
|
194
|
+
record.errors.add(current_path, "must be an Integer")
|
195
|
+
end
|
196
|
+
when :boolean
|
197
|
+
unless [true, false].include?(element)
|
198
|
+
record.errors.add(current_path, "must be true or false")
|
199
|
+
end
|
200
|
+
when :float
|
201
|
+
unless element.is_a?(Float)
|
202
|
+
record.errors.add(current_path, "must be a float")
|
203
|
+
end
|
204
|
+
when :datetime
|
205
|
+
unless element.is_a?(DateTime)
|
206
|
+
record.errors.add(current_path, "must be a DateTime")
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
141
211
|
end
|
142
212
|
end
|
143
213
|
end
|