Module: SchemaTest

Defined in:
lib/schema_test.rb,
lib/schema_test/version.rb,
lib/schema_test/minitest.rb,
lib/schema_test/property.rb,
lib/schema_test/rewriter.rb,
lib/schema_test/collapser.rb,
lib/schema_test/validator.rb,
lib/schema_test/collection.rb,
lib/schema_test/definition.rb,
lib/schema_test/configuration.rb,
lib/schema_test/fingerprint_rewriter.rb

Defined Under Namespace

Modules: Minitest Classes: Collapser, Collection, Configuration, Definition, Error, FingerprintRewriter, Property, Rewriter, Validator

Constant Summary collapse

SCHEMA_VERSION =
"http://json-schema.org/draft-07/schema#"
FINGERPRINT_LENGTH =

The number of leading hex characters of the schema digest kept as a fingerprint. The fingerprint only needs to change when the schema changes (it is compared against a single expected value, never searched for collisions), so a short prefix keeps the assertion lines readable while remaining collision-free in practice.

12
VERSION =
"0.2.0"
OPENING_COMMENT =
'# EXPANDED'.freeze
CLOSING_COMMENT =
'# END EXPANDED'.freeze
DISABLE_RUBOCOP_COMMENT =
'# rubocop:disable all'.freeze
ENABLE_RUBOCOP_COMMENT =
'# rubocop:enable all'.freeze

Class Method Summary collapse

Class Method Details

.collapse!(*paths) ⇒ Object

Collapse expanded schema assertions in test files back to simple one-line calls. Pass file paths or directory paths. Directories are globbed for */.rb files.



104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/schema_test.rb', line 104

def collapse!(*paths)
  files = paths.flat_map do |path|
    if File.directory?(path)
      Dir[File.join(path, '**', '*.rb')]
    else
      [path]
    end
  end
  files.each do |file|
    contents = File.read(file)
    next unless contents.include?(OPENING_COMMENT)
    collapser = SchemaTest::Collapser.new(contents)
    File.write(file, collapser.output)
  end
end

.collection(name, of:, **attributes) ⇒ Object

Explicitly define a new schema collection (an array of other schema objects)



56
57
58
# File 'lib/schema_test.rb', line 56

def collection(name, of:, **attributes)
  SchemaTest::Collection.new(name, of, location: definition_location(caller[1]), **attributes)
end

.compile!Object

Compile all definitions to JSON Schema files in a ‘compiled` directory within each definition path. The compiled files mirror the layout of the original definition files, so a definition declared in `api/v3/film.rb` is written to `compiled/api/v3/film.json`.



65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/schema_test.rb', line 65

def compile!
  load_definitions
  SchemaTest::Definition.all.each do |definition|
    begin
      definition_path = owning_definition_path(definition)
      next unless definition_path
      path = Pathname.new(definition_path).join('compiled', compiled_relative_path(definition))
      path.dirname.mkpath
      File.write(path, JSON.pretty_generate(definition.as_json_schema) + "\n")
    rescue => e
      warn "SchemaTest: failed to compile #{definition.name} (version: #{definition.version}): #{e.message}"
    end
  end
end

.configurationObject



36
37
38
# File 'lib/schema_test.rb', line 36

def configuration
  @configuration ||= SchemaTest::Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields a configuration object, which can be used to set up various aspects of the library

Yields:



32
33
34
# File 'lib/schema_test.rb', line 32

def configure
  yield configuration
end

.define(name, collection: nil, **attributes, &block) ⇒ Object

Define a new schema



46
47
48
49
50
51
52
# File 'lib/schema_test.rb', line 46

def define(name, collection: nil, **attributes, &block)
  definition = SchemaTest::Definition.new(name, location: definition_location(caller[0]), **attributes, &block)
  if collection
    collection(collection, of: name, version: attributes[:version])
  end
  definition
end

.load!Object

Recursively loads all files under the ‘definition_paths` directories



41
42
43
# File 'lib/schema_test.rb', line 41

def load!
  load_definitions
end

.load_compiled_schema(name, version: nil) ⇒ Object

Load a pre-compiled JSON schema from the compiled directory. Because compiled files mirror the original definition layout, the file may live in a nested subdirectory, so the compiled tree is searched recursively for the expected filename.

Raises:



84
85
86
87
88
89
90
91
92
# File 'lib/schema_test.rb', line 84

def load_compiled_schema(name, version: nil)
  filename = compiled_filename(name, version)
  configuration.definition_paths.each do |definition_path|
    compiled_root = Pathname.new(definition_path).join('compiled')
    match = Pathname.glob(compiled_root.join('**', filename)).first
    return JSON.parse(match.read) if match
  end
  raise SchemaTest::Error, "Could not find compiled schema for #{name.inspect} (version: #{version.inspect})"
end

.reset!Object



25
26
27
28
# File 'lib/schema_test.rb', line 25

def reset!
  @configuration = nil
  SchemaTest::Definition.reset!
end

.schema_fingerprint(schema) ⇒ Object

A stable fingerprint of a compiled schema. The fingerprint is derived from the schema’s semantic content, so it changes whenever the schema changes but is unaffected by pretty-print formatting.



97
98
99
# File 'lib/schema_test.rb', line 97

def schema_fingerprint(schema)
  Digest::SHA256.hexdigest(JSON.generate(schema))[0, FINGERPRINT_LENGTH]
end

.validate_json(json, definition_or_schema) ⇒ Object

Validate some JSON data against a schema or schema definition



121
122
123
124
125
126
127
128
# File 'lib/schema_test.rb', line 121

def validate_json(json, definition_or_schema)
  validator = SchemaTest::Validator.new(json)
  if definition_or_schema.is_a?(SchemaTest::Property::Object)
    validator.validate_using_definition(definition_or_schema)
  else
    validator.validate_using_json_schema(definition_or_schema)
  end
end