json_schema 0.0.10 → 0.0.11
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.
@@ -80,7 +80,13 @@ module Commands
|
|
80
80
|
|
81
81
|
# Builds a JSON Reference + message like "/path/to/file#/path/to/data".
|
82
82
|
def map_schema_errors(file, errors)
|
83
|
-
errors.map { |e|
|
83
|
+
errors.map { |e|
|
84
|
+
if e.is_a?(JsonSchema::ValidationError)
|
85
|
+
"#{file}#{e.pointer}: failed #{e.schema.pointer}: #{e.message}"
|
86
|
+
else
|
87
|
+
"#{file}#{e.schema.pointer}: #{e.message}"
|
88
|
+
end
|
89
|
+
}
|
84
90
|
end
|
85
91
|
|
86
92
|
def parse(file)
|
@@ -10,7 +10,7 @@ module JsonSchema
|
|
10
10
|
# for now.
|
11
11
|
errors.map { |e|
|
12
12
|
if e.schema
|
13
|
-
%{
|
13
|
+
%{#{e.schema.pointer}: #{e.message}}
|
14
14
|
else
|
15
15
|
e.message
|
16
16
|
end
|
@@ -22,4 +22,17 @@ module JsonSchema
|
|
22
22
|
@message = message
|
23
23
|
end
|
24
24
|
end
|
25
|
+
|
26
|
+
class ValidationError < SchemaError
|
27
|
+
attr_accessor :path
|
28
|
+
|
29
|
+
def initialize(schema, path, message)
|
30
|
+
super(schema, message)
|
31
|
+
@path = path
|
32
|
+
end
|
33
|
+
|
34
|
+
def pointer
|
35
|
+
path.join("/")
|
36
|
+
end
|
37
|
+
end
|
25
38
|
end
|
@@ -20,7 +20,7 @@ module JsonSchema
|
|
20
20
|
|
21
21
|
def validate(data)
|
22
22
|
@errors = []
|
23
|
-
validate_data(@schema, data, @errors)
|
23
|
+
validate_data(@schema, data, @errors, ['#'])
|
24
24
|
@errors.size == 0
|
25
25
|
end
|
26
26
|
|
@@ -37,55 +37,55 @@ module JsonSchema
|
|
37
37
|
valid_old && valid_new
|
38
38
|
end
|
39
39
|
|
40
|
-
def validate_data(schema, data, errors)
|
40
|
+
def validate_data(schema, data, errors, path)
|
41
41
|
valid = true
|
42
42
|
|
43
43
|
# validation: any
|
44
|
-
valid = strict_and valid, validate_all_of(schema, data, errors)
|
45
|
-
valid = strict_and valid, validate_any_of(schema, data, errors)
|
46
|
-
valid = strict_and valid, validate_enum(schema, data, errors)
|
47
|
-
valid = strict_and valid, validate_one_of(schema, data, errors)
|
48
|
-
valid = strict_and valid, validate_not(schema, data, errors)
|
49
|
-
valid = strict_and valid, validate_type(schema, data, errors)
|
44
|
+
valid = strict_and valid, validate_all_of(schema, data, errors, path)
|
45
|
+
valid = strict_and valid, validate_any_of(schema, data, errors, path)
|
46
|
+
valid = strict_and valid, validate_enum(schema, data, errors, path)
|
47
|
+
valid = strict_and valid, validate_one_of(schema, data, errors, path)
|
48
|
+
valid = strict_and valid, validate_not(schema, data, errors, path)
|
49
|
+
valid = strict_and valid, validate_type(schema, data, errors, path)
|
50
50
|
|
51
51
|
# validation: array
|
52
52
|
if data.is_a?(Array)
|
53
|
-
valid = strict_and valid, validate_items(schema, data, errors)
|
54
|
-
valid = strict_and valid, validate_max_items(schema, data, errors)
|
55
|
-
valid = strict_and valid, validate_min_items(schema, data, errors)
|
56
|
-
valid = strict_and valid, validate_unique_items(schema, data, errors)
|
53
|
+
valid = strict_and valid, validate_items(schema, data, errors, path)
|
54
|
+
valid = strict_and valid, validate_max_items(schema, data, errors, path)
|
55
|
+
valid = strict_and valid, validate_min_items(schema, data, errors, path)
|
56
|
+
valid = strict_and valid, validate_unique_items(schema, data, errors, path)
|
57
57
|
end
|
58
58
|
|
59
59
|
# validation: integer/number
|
60
60
|
if data.is_a?(Float) || data.is_a?(Integer)
|
61
|
-
valid = strict_and valid, validate_max(schema, data, errors)
|
62
|
-
valid = strict_and valid, validate_min(schema, data, errors)
|
63
|
-
valid = strict_and valid, validate_multiple_of(schema, data, errors)
|
61
|
+
valid = strict_and valid, validate_max(schema, data, errors, path)
|
62
|
+
valid = strict_and valid, validate_min(schema, data, errors, path)
|
63
|
+
valid = strict_and valid, validate_multiple_of(schema, data, errors, path)
|
64
64
|
end
|
65
65
|
|
66
66
|
# validation: object
|
67
67
|
if data.is_a?(Hash)
|
68
|
-
valid = strict_and valid, validate_additional_properties(schema, data, errors)
|
69
|
-
valid = strict_and valid, validate_dependencies(schema, data, errors)
|
70
|
-
valid = strict_and valid, validate_max_properties(schema, data, errors)
|
71
|
-
valid = strict_and valid, validate_min_properties(schema, data, errors)
|
72
|
-
valid = strict_and valid, validate_pattern_properties(schema, data, errors)
|
73
|
-
valid = strict_and valid, validate_properties(schema, data, errors)
|
74
|
-
valid = strict_and valid, validate_required(schema, data, errors, schema.required)
|
68
|
+
valid = strict_and valid, validate_additional_properties(schema, data, errors, path)
|
69
|
+
valid = strict_and valid, validate_dependencies(schema, data, errors, path)
|
70
|
+
valid = strict_and valid, validate_max_properties(schema, data, errors, path)
|
71
|
+
valid = strict_and valid, validate_min_properties(schema, data, errors, path)
|
72
|
+
valid = strict_and valid, validate_pattern_properties(schema, data, errors, path)
|
73
|
+
valid = strict_and valid, validate_properties(schema, data, errors, path)
|
74
|
+
valid = strict_and valid, validate_required(schema, data, errors, path, schema.required)
|
75
75
|
end
|
76
76
|
|
77
77
|
# validation: string
|
78
78
|
if data.is_a?(String)
|
79
|
-
valid = strict_and valid, validate_format(schema, data, errors)
|
80
|
-
valid = strict_and valid, validate_max_length(schema, data, errors)
|
81
|
-
valid = strict_and valid, validate_min_length(schema, data, errors)
|
82
|
-
valid = strict_and valid, validate_pattern(schema, data, errors)
|
79
|
+
valid = strict_and valid, validate_format(schema, data, errors, path)
|
80
|
+
valid = strict_and valid, validate_max_length(schema, data, errors, path)
|
81
|
+
valid = strict_and valid, validate_min_length(schema, data, errors, path)
|
82
|
+
valid = strict_and valid, validate_pattern(schema, data, errors, path)
|
83
83
|
end
|
84
84
|
|
85
85
|
valid
|
86
86
|
end
|
87
87
|
|
88
|
-
def validate_additional_properties(schema, data, errors)
|
88
|
+
def validate_additional_properties(schema, data, errors, path)
|
89
89
|
return true if schema.additional_properties == true
|
90
90
|
|
91
91
|
extra = data.keys - schema.properties.keys
|
@@ -94,7 +94,7 @@ module JsonSchema
|
|
94
94
|
# validated according to subschema
|
95
95
|
if schema.additional_properties.is_a?(Schema)
|
96
96
|
extra.each do |key|
|
97
|
-
validate_data(schema.additional_properties, data[key], errors)
|
97
|
+
validate_data(schema.additional_properties, data[key], errors, path + [key])
|
98
98
|
end
|
99
99
|
# boolean indicates whether additional properties are allowed
|
100
100
|
else
|
@@ -102,49 +102,49 @@ module JsonSchema
|
|
102
102
|
true
|
103
103
|
else
|
104
104
|
message = %{Extra keys in object: #{extra.sort.join(", ")}.}
|
105
|
-
errors <<
|
105
|
+
errors << ValidationError.new(schema, path, message)
|
106
106
|
false
|
107
107
|
end
|
108
108
|
end
|
109
109
|
end
|
110
110
|
|
111
|
-
def validate_all_of(schema, data, errors)
|
111
|
+
def validate_all_of(schema, data, errors, path)
|
112
112
|
return true if schema.all_of.empty?
|
113
113
|
valid = schema.all_of.all? do |subschema|
|
114
|
-
validate_data(subschema, data, errors)
|
114
|
+
validate_data(subschema, data, errors, path)
|
115
115
|
end
|
116
116
|
message = %{Data did not match all subschemas of "allOf" condition.}
|
117
|
-
errors <<
|
117
|
+
errors << ValidationError.new(schema, path, message) if !valid
|
118
118
|
valid
|
119
119
|
end
|
120
120
|
|
121
|
-
def validate_any_of(schema, data, errors)
|
121
|
+
def validate_any_of(schema, data, errors, path)
|
122
122
|
return true if schema.any_of.empty?
|
123
123
|
valid = schema.any_of.any? do |subschema|
|
124
|
-
validate_data(subschema, data, [])
|
124
|
+
validate_data(subschema, data, [], path)
|
125
125
|
end
|
126
126
|
if !valid
|
127
127
|
message = %{Data did not match any subschema of "anyOf" condition.}
|
128
|
-
errors <<
|
128
|
+
errors << ValidationError.new(schema, path, message)
|
129
129
|
end
|
130
130
|
valid
|
131
131
|
end
|
132
132
|
|
133
|
-
def validate_dependencies(schema, data, errors)
|
133
|
+
def validate_dependencies(schema, data, errors, path)
|
134
134
|
return true if schema.dependencies.empty?
|
135
135
|
schema.dependencies.each do |key, obj|
|
136
136
|
# if the key is not present, the dependency is fulfilled by definition
|
137
137
|
next unless data[key]
|
138
138
|
if obj.is_a?(Schema)
|
139
|
-
validate_data(obj, data, errors)
|
139
|
+
validate_data(obj, data, errors, path)
|
140
140
|
else
|
141
141
|
# if not a schema, value is an array of required fields
|
142
|
-
validate_required(schema, data, errors, obj)
|
142
|
+
validate_required(schema, data, errors, path, obj)
|
143
143
|
end
|
144
144
|
end
|
145
145
|
end
|
146
146
|
|
147
|
-
def validate_format(schema, data, errors)
|
147
|
+
def validate_format(schema, data, errors, path)
|
148
148
|
return true unless schema.format
|
149
149
|
valid = case schema.format
|
150
150
|
when "date-time"
|
@@ -168,51 +168,52 @@ module JsonSchema
|
|
168
168
|
true
|
169
169
|
else
|
170
170
|
message = %{Expected data to match "#{schema.format}" format, value was: #{data}.}
|
171
|
-
errors <<
|
171
|
+
errors << ValidationError.new(schema, path, message)
|
172
172
|
false
|
173
173
|
end
|
174
174
|
end
|
175
175
|
|
176
|
-
def validate_enum(schema, data, errors)
|
176
|
+
def validate_enum(schema, data, errors, path)
|
177
177
|
return true unless schema.enum
|
178
178
|
if schema.enum.include?(data)
|
179
179
|
true
|
180
180
|
else
|
181
181
|
message = %{Expected data to be a member of enum #{schema.enum}, value was: #{data}.}
|
182
|
-
errors <<
|
182
|
+
errors << ValidationError.new(schema, path, message)
|
183
183
|
false
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
|
-
def validate_items(schema, data,
|
187
|
+
def validate_items(schema, data, errors, path)
|
188
188
|
return true unless schema.items
|
189
189
|
if schema.items.is_a?(Array)
|
190
190
|
if data.size < schema.items.count
|
191
191
|
message = %{Expected array to have at least #{schema.items.count} item(s), had #{data.size} item(s).}
|
192
|
-
errors <<
|
192
|
+
errors << ValidationError.new(schema, path, message)
|
193
193
|
false
|
194
194
|
elsif data.size > schema.items.count && !schema.additional_items?
|
195
195
|
message = %{Expected array to have no more than #{schema.items.count} item(s), had #{data.size} item(s).}
|
196
|
-
errors <<
|
196
|
+
errors << ValidationError.new(schema, path, message)
|
197
197
|
false
|
198
198
|
else
|
199
199
|
valid = true
|
200
200
|
schema.items.each_with_index do |subschema, i|
|
201
201
|
valid = strict_and valid,
|
202
|
-
validate_data(subschema, data[i], errors)
|
202
|
+
validate_data(subschema, data[i], errors, path + [i])
|
203
203
|
end
|
204
204
|
valid
|
205
205
|
end
|
206
206
|
else
|
207
207
|
valid = true
|
208
|
-
data.
|
209
|
-
valid = strict_and valid,
|
208
|
+
data.each_with_index do |value, i|
|
209
|
+
valid = strict_and valid,
|
210
|
+
validate_data(schema.items, value, errors, path + [i])
|
210
211
|
end
|
211
212
|
valid
|
212
213
|
end
|
213
214
|
end
|
214
215
|
|
215
|
-
def validate_max(schema, data,
|
216
|
+
def validate_max(schema, data, errors, path)
|
216
217
|
return true unless schema.max
|
217
218
|
if schema.max_exclusive? && data < schema.max
|
218
219
|
true
|
@@ -220,45 +221,45 @@ module JsonSchema
|
|
220
221
|
true
|
221
222
|
else
|
222
223
|
message = %{Expected data to be smaller than maximum #{schema.max} (exclusive: #{schema.max_exclusive?}), value was: #{data}.}
|
223
|
-
errors <<
|
224
|
+
errors << ValidationError.new(schema, path, message)
|
224
225
|
false
|
225
226
|
end
|
226
227
|
end
|
227
228
|
|
228
|
-
def validate_max_items(schema, data,
|
229
|
+
def validate_max_items(schema, data, errors, path)
|
229
230
|
return true unless schema.max_items
|
230
231
|
if data.size <= schema.max_items
|
231
232
|
true
|
232
233
|
else
|
233
234
|
message = %{Expected array to have no more than #{schema.max_items} item(s), had #{data.size} item(s).}
|
234
|
-
errors <<
|
235
|
+
errors << ValidationError.new(schema, path, message)
|
235
236
|
false
|
236
237
|
end
|
237
238
|
end
|
238
239
|
|
239
|
-
def validate_max_length(schema, data,
|
240
|
+
def validate_max_length(schema, data, errors, path)
|
240
241
|
return true unless schema.max_length
|
241
242
|
if data.length <= schema.max_length
|
242
243
|
true
|
243
244
|
else
|
244
245
|
message = %{Expected string to have a maximum length of #{schema.max_length}, was #{data.length} character(s) long.}
|
245
|
-
errors <<
|
246
|
+
errors << ValidationError.new(schema, path, message)
|
246
247
|
false
|
247
248
|
end
|
248
249
|
end
|
249
250
|
|
250
|
-
def validate_max_properties(schema, data,
|
251
|
+
def validate_max_properties(schema, data, errors, path)
|
251
252
|
return true unless schema.max_properties
|
252
253
|
if data.keys.size <= schema.max_properties
|
253
254
|
true
|
254
255
|
else
|
255
256
|
message = %{Expected object to have a maximum of #{schema.max_properties} property/ies; it had #{data.keys.size}.}
|
256
|
-
errors <<
|
257
|
+
errors << ValidationError.new(schema, path, message)
|
257
258
|
false
|
258
259
|
end
|
259
260
|
end
|
260
261
|
|
261
|
-
def validate_min(schema, data,
|
262
|
+
def validate_min(schema, data, errors, path)
|
262
263
|
return true unless schema.min
|
263
264
|
if schema.min_exclusive? && data > schema.min
|
264
265
|
true
|
@@ -266,140 +267,146 @@ module JsonSchema
|
|
266
267
|
true
|
267
268
|
else
|
268
269
|
message = %{Expected data to be larger than minimum #{schema.min} (exclusive: #{schema.min_exclusive?}), value was: #{data}.}
|
269
|
-
errors <<
|
270
|
+
errors << ValidationError.new(schema, path, message)
|
270
271
|
false
|
271
272
|
end
|
272
273
|
end
|
273
274
|
|
274
|
-
def validate_min_items(schema, data,
|
275
|
+
def validate_min_items(schema, data, errors, path)
|
275
276
|
return true unless schema.min_items
|
276
277
|
if data.size >= schema.min_items
|
277
278
|
true
|
278
279
|
else
|
279
280
|
message = %{Expected array to have at least #{schema.min_items} item(s), had #{data.size} item(s).}
|
280
|
-
errors <<
|
281
|
+
errors << ValidationError.new(schema, path, message)
|
281
282
|
false
|
282
283
|
end
|
283
284
|
end
|
284
285
|
|
285
|
-
def validate_min_length(schema, data,
|
286
|
+
def validate_min_length(schema, data, errors, path)
|
286
287
|
return true unless schema.min_length
|
287
288
|
if data.length >= schema.min_length
|
288
289
|
true
|
289
290
|
else
|
290
291
|
message = %{Expected string to have a minimum length of #{schema.min_length}, was #{data.length} character(s) long.}
|
291
|
-
errors <<
|
292
|
+
errors << ValidationError.new(schema, path, message)
|
292
293
|
false
|
293
294
|
end
|
294
295
|
end
|
295
296
|
|
296
|
-
def validate_min_properties(schema, data,
|
297
|
+
def validate_min_properties(schema, data, errors, path)
|
297
298
|
return true unless schema.min_properties
|
298
299
|
if data.keys.size >= schema.min_properties
|
299
300
|
true
|
300
301
|
else
|
301
302
|
message = %{Expected object to have a minimum of #{schema.min_properties} property/ies; it had #{data.keys.size}.}
|
302
|
-
errors <<
|
303
|
+
errors << ValidationError.new(schema, path, message)
|
303
304
|
false
|
304
305
|
end
|
305
306
|
end
|
306
307
|
|
307
|
-
def validate_multiple_of(schema, data, errors)
|
308
|
+
def validate_multiple_of(schema, data, errors, path)
|
308
309
|
return true unless schema.multiple_of
|
309
310
|
if data % schema.multiple_of == 0
|
310
311
|
true
|
311
312
|
else
|
312
313
|
message = %{Expected data to be a multiple of #{schema.multiple_of}, value was: #{data}.}
|
313
|
-
errors <<
|
314
|
+
errors << ValidationError.new(schema, path, message)
|
314
315
|
false
|
315
316
|
end
|
316
317
|
end
|
317
318
|
|
318
|
-
def validate_one_of(schema, data, errors)
|
319
|
+
def validate_one_of(schema, data, errors, path)
|
319
320
|
return true if schema.one_of.empty?
|
320
321
|
num_valid = schema.one_of.count do |subschema|
|
321
|
-
validate_data(subschema, data, [])
|
322
|
+
validate_data(subschema, data, [], path)
|
323
|
+
end
|
324
|
+
if num_valid != 1
|
325
|
+
message = %{Data did not match exactly one subschema of "oneOf" condition.}
|
326
|
+
errors << ValidationError.new(schema, path, message)
|
322
327
|
end
|
323
|
-
message = %{Data did not match exactly one subschema of "oneOf" condition.}
|
324
|
-
errors << SchemaError.new(schema, message) if num_valid != 1
|
325
328
|
num_valid == 1
|
326
329
|
end
|
327
330
|
|
328
|
-
def validate_not(schema, data, errors)
|
331
|
+
def validate_not(schema, data, errors, path)
|
329
332
|
return true unless schema.not
|
330
333
|
# don't bother accumulating these errors, they'll all be worded
|
331
334
|
# incorrectly for the inverse condition
|
332
|
-
valid = !validate_data(schema.not, data, [])
|
333
|
-
|
334
|
-
|
335
|
+
valid = !validate_data(schema.not, data, [], path)
|
336
|
+
if !valid
|
337
|
+
message = %{Data matched subschema of "not" condition.}
|
338
|
+
errors << ValidationError.new(schema, path, message)
|
339
|
+
end
|
335
340
|
valid
|
336
341
|
end
|
337
342
|
|
338
|
-
def validate_pattern(schema, data,
|
343
|
+
def validate_pattern(schema, data, errors, path)
|
339
344
|
return true unless schema.pattern
|
340
345
|
if data =~ schema.pattern
|
341
346
|
true
|
342
347
|
else
|
343
348
|
message = %{Expected string to match pattern "#{schema.pattern.inspect}", value was: #{data}.}
|
344
|
-
errors <<
|
349
|
+
errors << ValidationError.new(schema, path, message)
|
345
350
|
false
|
346
351
|
end
|
347
352
|
end
|
348
353
|
|
349
|
-
def validate_pattern_properties(schema, data, errors)
|
354
|
+
def validate_pattern_properties(schema, data, errors, path)
|
350
355
|
return true if schema.pattern_properties.empty?
|
351
356
|
valid = true
|
352
357
|
schema.pattern_properties.each do |pattern, subschema|
|
353
358
|
data.each do |key, value|
|
354
359
|
if key =~ pattern
|
355
|
-
valid = strict_and valid,
|
360
|
+
valid = strict_and valid,
|
361
|
+
validate_data(subschema, value, errors, path + [key])
|
356
362
|
end
|
357
363
|
end
|
358
364
|
end
|
359
365
|
valid
|
360
366
|
end
|
361
367
|
|
362
|
-
def validate_properties(schema, data, errors)
|
368
|
+
def validate_properties(schema, data, errors, path)
|
363
369
|
return true if schema.properties.empty?
|
364
370
|
valid = true
|
365
371
|
schema.properties.each do |key, subschema|
|
366
372
|
if value = data[key]
|
367
|
-
valid = strict_and valid,
|
373
|
+
valid = strict_and valid,
|
374
|
+
validate_data(subschema, value, errors, path + [key])
|
368
375
|
end
|
369
376
|
end
|
370
377
|
valid
|
371
378
|
end
|
372
379
|
|
373
|
-
def validate_required(schema, data, errors, required)
|
380
|
+
def validate_required(schema, data, errors, path, required)
|
374
381
|
return true if !required || required.empty?
|
375
382
|
if (missing = required - data.keys).empty?
|
376
383
|
true
|
377
384
|
else
|
378
385
|
message = %{Missing required keys "#{missing.sort.join(", ")}" in object; keys are "#{data.keys.sort.join(", ")}".}
|
379
|
-
errors <<
|
386
|
+
errors << ValidationError.new(schema, path, message)
|
380
387
|
false
|
381
388
|
end
|
382
389
|
end
|
383
390
|
|
384
|
-
def validate_type(schema, data, errors)
|
391
|
+
def validate_type(schema, data, errors, path)
|
385
392
|
return true if schema.type.empty?
|
386
393
|
valid_types = schema.type.map { |t| TYPE_MAP[t] }.flatten.compact
|
387
394
|
if valid_types.any? { |t| data.is_a?(t) }
|
388
395
|
true
|
389
396
|
else
|
390
397
|
message = %{Expected data to be of type "#{schema.type.join("/")}"; value was: #{data.inspect}.}
|
391
|
-
errors <<
|
398
|
+
errors << ValidationError.new(schema, path, message)
|
392
399
|
false
|
393
400
|
end
|
394
401
|
end
|
395
402
|
|
396
|
-
def validate_unique_items(schema, data,
|
403
|
+
def validate_unique_items(schema, data, errors, path)
|
397
404
|
return true unless schema.unique_items?
|
398
405
|
if data.size == data.uniq.size
|
399
406
|
true
|
400
407
|
else
|
401
408
|
message = %{Expected array items to be unique, but duplicate items were found.}
|
402
|
-
errors <<
|
409
|
+
errors << ValidationError.new(schema, path, message)
|
403
410
|
false
|
404
411
|
end
|
405
412
|
end
|
@@ -640,6 +640,15 @@ describe JsonSchema::Validator do
|
|
640
640
|
%{Expected string to match pattern "/^[a-z][a-z0-9-]{3,30}$/", value was: ab.}
|
641
641
|
end
|
642
642
|
|
643
|
+
it "builds appropriate JSON Pointers to bad data" do
|
644
|
+
pointer("#/definitions/app/definitions/visibility").merge!(
|
645
|
+
"enum" => ["private", "public"]
|
646
|
+
)
|
647
|
+
data_sample["visibility"] = "personal"
|
648
|
+
refute validate
|
649
|
+
assert_equal "#/visibility", @validator.errors[0].pointer
|
650
|
+
end
|
651
|
+
|
643
652
|
def data_sample
|
644
653
|
@data_sample ||= DataScaffold.data_sample
|
645
654
|
end
|