json-schema-diff 0.1.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1aea7e4fe80d247f0391631e587a149d622839d7b78b99443e334eea7c422df1
4
- data.tar.gz: 4a9aeceeb2c7d356306cb237c03f8710eb4bd8291d010f583e2d1bb433110046
3
+ metadata.gz: 114ad725bbe219e05ad01ecbb7f419cf47a9d6e0e0861a3f65259ffe5ba08954
4
+ data.tar.gz: 34aa2c647ac5e9b6443788328af81dc8b26143641891e34d53b7cd732e711eb2
5
5
  SHA512:
6
- metadata.gz: 86c942503b94f7ab01c896fabefa07e9def0701daabff6bf17725dbf9e148c49282293a95e52ebb06a9ee941905c767124134a38243533591bab35b1a59a06c0
7
- data.tar.gz: 5ef4bbfcf3fc8957ec04800a8ea3ea3c3696553dec7f746050194ef6e241c8b55170cfbc87b0505c6238f0af4c5bccef205fe571b451d22cf9f27dcae0727e79
6
+ metadata.gz: a26c6bd182618f38904058a803235b897257263fb012074fb609ba5c11b00835809cdf13fe1e0f963a00a5fb555c9a3cbd5eaebe8d41a43cb976deea839eefdf
7
+ data.tar.gz: c657e645bcbe47ad82a46c527b09a0013838569f807674b66af106c5b94ffebdb4b100b3573c4727354240e2b1b1b430601d77de8308bfec5266cfe69b70c7ca
data/CHANGELOG.md CHANGED
@@ -7,6 +7,35 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.1] - 2025-01-26
11
+
12
+ ### Added
13
+
14
+ - Comprehensive RDoc documentation for all classes and methods
15
+ - API documentation with @param and @return annotations
16
+ - Documentation URI in gemspec pointing to rubydoc.info
17
+ - Documentation badge in README
18
+
19
+ ## [0.2.0] - 2025-01-26
20
+
21
+ ### Added
22
+
23
+ - JSON Schema validation capabilities with `json-schema` gem dependency
24
+ - New CLI options for validation control:
25
+ - `--[no-]validate` for JSON Schema format validation (enabled by default)
26
+ - `--[no-]validate-json` for JSON content validation against schema (disabled by default)
27
+ - Comprehensive error handling for invalid schemas and JSON validation failures
28
+ - Schema structure validation with clear error messages for malformed schemas
29
+ - Type checking validation ensuring JSON data types match schema expectations
30
+ - Required field validation for object schemas
31
+ - Fallback validation approach for complex schemas with external references
32
+
33
+ ### Changed
34
+
35
+ - Updated `json-schema` dependency to version ~> 5.0 for improved performance and compatibility
36
+ - Removed `bigdecimal` dependency (no longer needed with json-schema v5)
37
+ - Enhanced error messages with specific validation failure details
38
+
10
39
  ## [0.1.0] - 2025-01-26
11
40
 
12
41
  ### Added
data/README.md CHANGED
@@ -4,6 +4,7 @@ A Ruby gem that performs semantic diffs between JSON files, using JSON Schema to
4
4
 
5
5
  [![Ruby](https://img.shields.io/badge/ruby-%3E%3D%203.2-red.svg)](https://www.ruby-lang.org/)
6
6
  [![Gem Version](https://badge.fury.io/rb/json-schema-diff.svg)](https://rubygems.org/gems/json-schema-diff)
7
+ [![Documentation](https://img.shields.io/badge/docs-rubydoc.info-blue.svg)](https://rubydoc.info/gems/json-schema-diff)
7
8
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
8
9
 
9
10
  Perfect for comparing structured CLI output from security tools like zizmor and capslock across versions to highlight when security issues have been introduced or resolved.
@@ -60,6 +61,19 @@ json-schema-diff --format json schema.json old.json new.json
60
61
  json-schema-diff --no-color schema.json old.json new.json
61
62
  ```
62
63
 
64
+ ### Validation Options
65
+
66
+ ```bash
67
+ # Enable JSON validation against schema (helps catch data format errors)
68
+ json-schema-diff --validate-json schema.json old.json new.json
69
+
70
+ # Disable schema format validation (for malformed schemas)
71
+ json-schema-diff --no-validate schema.json old.json new.json
72
+
73
+ # Both validation options
74
+ json-schema-diff --validate-json --validate schema.json old.json new.json
75
+ ```
76
+
63
77
  ### Ignoring Fields
64
78
 
65
79
  ```bash
@@ -83,6 +97,8 @@ Options:
83
97
  -f, --format FORMAT Output format (pretty, json)
84
98
  -i, --ignore-fields FIELDS Comma-separated list of field paths to ignore
85
99
  --[no-]color Enable/disable colored output (default: enabled)
100
+ --[no-]validate Enable/disable JSON Schema format validation (default: enabled)
101
+ --[no-]validate-json Enable/disable JSON validation against schema (default: disabled)
86
102
  -h, --help Show help message
87
103
  -v, --version Show version
88
104
  ```
@@ -3,11 +3,24 @@
3
3
  module Json
4
4
  module Schema
5
5
  module Diff
6
+ # Command-line interface for json-schema-diff
7
+ #
8
+ # Provides a comprehensive CLI for comparing JSON files using JSON Schema guidance.
9
+ # Supports multiple output formats, validation options, and field filtering.
6
10
  class CLI
11
+ # Entry point for the CLI application
12
+ #
13
+ # @param args [Array<String>] Command-line arguments
14
+ # @return [void]
7
15
  def self.start(args)
8
16
  new.run(args)
9
17
  end
10
18
 
19
+ # Runs the CLI with the provided arguments
20
+ #
21
+ # @param args [Array<String>] Command-line arguments including schema, old JSON, and new JSON files
22
+ # @return [void]
23
+ # @raise [SystemExit] Exits with status 1 on errors
11
24
  def run(args)
12
25
  options = parse_options(args)
13
26
 
@@ -20,12 +33,18 @@ module Json
20
33
  schema_file, old_file, new_file = args
21
34
 
22
35
  begin
23
- schema = SchemaParser.new(schema_file)
36
+ schema = SchemaParser.new(schema_file, validate_schema: options[:validate])
24
37
  comparer = Comparer.new(schema, options[:ignore_fields] || [])
25
38
 
26
39
  old_json = JSON.parse(File.read(old_file))
27
40
  new_json = JSON.parse(File.read(new_file))
28
41
 
42
+ # Validate JSON files against schema if requested
43
+ if options[:validate_json]
44
+ schema.validate_json(old_json)
45
+ schema.validate_json(new_json)
46
+ end
47
+
29
48
  diff_result = comparer.compare(old_json, new_json)
30
49
 
31
50
  formatter = Formatter.new(options[:format], options[:color])
@@ -44,11 +63,18 @@ module Json
44
63
 
45
64
  private
46
65
 
66
+ # Parses command-line options and arguments
67
+ #
68
+ # @param args [Array<String>] Command-line arguments to parse
69
+ # @return [Hash] Parsed options hash
70
+ # @raise [SystemExit] Exits on invalid options or help/version requests
47
71
  def parse_options(args)
48
72
  options = {
49
73
  format: "pretty",
50
74
  color: true,
51
- ignore_fields: []
75
+ ignore_fields: [],
76
+ validate: true,
77
+ validate_json: false
52
78
  }
53
79
 
54
80
  parser = OptionParser.new do |opts|
@@ -77,6 +103,14 @@ module Json
77
103
  options[:color] = color
78
104
  end
79
105
 
106
+ opts.on("--[no-]validate", "Enable/disable JSON Schema format validation (default: enabled)") do |validate|
107
+ options[:validate] = validate
108
+ end
109
+
110
+ opts.on("--[no-]validate-json", "Enable/disable JSON validation against schema (default: disabled)") do |validate_json|
111
+ options[:validate_json] = validate_json
112
+ end
113
+
80
114
  opts.on("-h", "--help", "Show this help message") do
81
115
  puts opts
82
116
  exit 0
@@ -3,12 +3,25 @@
3
3
  module Json
4
4
  module Schema
5
5
  module Diff
6
+ # Compares two JSON objects using schema guidance to detect changes
7
+ #
8
+ # This class performs recursive comparison of JSON structures, using schema
9
+ # information to provide context and handle special cases like read-only fields.
6
10
  class Comparer
11
+ # Initialize a new Comparer
12
+ #
13
+ # @param schema_parser [SchemaParser] Parser for the JSON Schema
14
+ # @param ignore_fields [Array<String>] List of field paths to ignore during comparison
7
15
  def initialize(schema_parser, ignore_fields = [])
8
16
  @schema_parser = schema_parser
9
17
  @ignore_fields = ignore_fields.map(&:to_s)
10
18
  end
11
19
 
20
+ # Compares two JSON objects and returns a list of changes
21
+ #
22
+ # @param old_json [Object] The original JSON data
23
+ # @param new_json [Object] The new JSON data to compare against
24
+ # @return [Array<Hash>] Array of change objects with metadata
12
25
  def compare(old_json, new_json)
13
26
  changes = []
14
27
  compare_recursive(old_json, new_json, "", changes)
@@ -3,7 +3,12 @@
3
3
  module Json
4
4
  module Schema
5
5
  module Diff
6
+ # Formats diff results into human-readable or machine-readable output
7
+ #
8
+ # Supports both pretty-printed colorized output for humans and JSON output for machines.
9
+ # Handles ANSI color codes and provides structured formatting of change information.
6
10
  class Formatter
11
+ # ANSI color codes for terminal output
7
12
  COLORS = {
8
13
  red: "\e[31m",
9
14
  green: "\e[32m",
@@ -14,11 +19,19 @@ module Json
14
19
  reset: "\e[0m"
15
20
  }.freeze
16
21
 
22
+ # Initialize a new Formatter
23
+ #
24
+ # @param format [String] Output format - "pretty" or "json" (default: "pretty")
25
+ # @param use_color [Boolean] Whether to use ANSI color codes (default: true)
17
26
  def initialize(format = "pretty", use_color = true)
18
27
  @format = format
19
28
  @use_color = use_color
20
29
  end
21
30
 
31
+ # Formats an array of changes into the specified output format
32
+ #
33
+ # @param changes [Array<Hash>] Array of change objects from Comparer
34
+ # @return [String] Formatted output string
22
35
  def format(changes)
23
36
  case @format
24
37
  when "json"
@@ -3,17 +3,67 @@
3
3
  module Json
4
4
  module Schema
5
5
  module Diff
6
+ # Parses and validates JSON Schema files, extracting field metadata for diff guidance
7
+ #
8
+ # This class handles loading JSON Schema files, validating their structure,
9
+ # and providing methods to extract field information and detect noisy fields.
6
10
  class SchemaParser
11
+ # @return [Hash] The parsed JSON Schema
7
12
  attr_reader :schema
8
13
 
9
- def initialize(schema_file)
14
+ # Initialize a new SchemaParser with a JSON Schema file
15
+ #
16
+ # @param schema_file [String] Path to the JSON Schema file
17
+ # @param validate_schema [Boolean] Whether to validate the schema structure (default: true)
18
+ # @raise [Error] If the schema file is invalid or not found
19
+ def initialize(schema_file, validate_schema: true)
10
20
  @schema = JSON.parse(File.read(schema_file))
21
+
22
+ if validate_schema
23
+ validate_basic_schema_structure!
24
+ end
11
25
  rescue JSON::ParserError => e
12
26
  raise Error, "Invalid JSON schema: #{e.message}"
13
27
  rescue Errno::ENOENT => e
14
28
  raise Error, "Schema file not found: #{e.message}"
15
29
  end
16
30
 
31
+ # Validates JSON data against the schema
32
+ #
33
+ # Performs basic structural validation to ensure JSON data types match schema expectations
34
+ # and required fields are present.
35
+ #
36
+ # @param json_data [Object] The JSON data to validate
37
+ # @return [Boolean] True if validation passes
38
+ # @raise [Error] If validation fails
39
+ def validate_json(json_data)
40
+ # Simple structural validation - check if JSON structure roughly matches schema expectations
41
+ begin
42
+ # Basic check - if schema has type "array", JSON should be array, etc.
43
+ if @schema["type"] == "array" && !json_data.is_a?(Array)
44
+ raise Error, "JSON validation failed: Expected array but got #{json_data.class.name.downcase}"
45
+ elsif @schema["type"] == "object" && !json_data.is_a?(Hash)
46
+ raise Error, "JSON validation failed: Expected object but got #{json_data.class.name.downcase}"
47
+ end
48
+
49
+ # If we have required fields in schema, check they exist in JSON
50
+ if @schema["required"] && json_data.is_a?(Hash)
51
+ missing_fields = @schema["required"] - json_data.keys
52
+ unless missing_fields.empty?
53
+ raise Error, "JSON validation failed: Missing required fields: #{missing_fields.join(', ')}"
54
+ end
55
+ end
56
+
57
+ true
58
+ rescue StandardError => e
59
+ raise Error, "JSON validation error: #{e.message}"
60
+ end
61
+ end
62
+
63
+ # Gets field information from the schema for a given path
64
+ #
65
+ # @param path [String] Dot-separated path to the field (e.g., "user.profile.name")
66
+ # @return [Hash] Field information including type, title, description, format, enum, and read_only
17
67
  def get_field_info(path)
18
68
  field_schema = traverse_schema(path.split('.'))
19
69
  return {} unless field_schema
@@ -28,6 +78,14 @@ module Json
28
78
  }
29
79
  end
30
80
 
81
+ # Determines if a field is considered "noisy" and should potentially be ignored
82
+ #
83
+ # Noisy fields are those that change frequently but aren't meaningful for comparison,
84
+ # such as timestamps, UUIDs, or fields marked as readOnly in the schema.
85
+ #
86
+ # @param path [String] Dot-separated path to the field
87
+ # @param value [Object] The field value
88
+ # @return [Boolean] True if the field is considered noisy
31
89
  def is_noisy_field?(path, value)
32
90
  field_info = get_field_info(path)
33
91
  format = field_info[:format]
@@ -49,6 +107,26 @@ module Json
49
107
 
50
108
  private
51
109
 
110
+ # Validates that the schema has basic JSON Schema structure
111
+ #
112
+ # @return [void]
113
+ # @raise [Error] If the schema is missing basic structure
114
+ def validate_basic_schema_structure!
115
+ # Basic structural validation for schema
116
+ unless @schema.is_a?(Hash)
117
+ raise Error, "Schema must be a JSON object"
118
+ end
119
+
120
+ # Check for basic schema structure
121
+ if @schema["type"].nil? && @schema["properties"].nil? && @schema["items"].nil?
122
+ raise Error, "Schema appears to be missing basic JSON Schema structure (no type, properties, or items)"
123
+ end
124
+ end
125
+
126
+ # Traverses the schema following a path to find field-specific schema information
127
+ #
128
+ # @param path_parts [Array<String>] Array of path components
129
+ # @return [Hash, nil] Schema for the field at the path, or nil if not found
52
130
  def traverse_schema(path_parts)
53
131
  current = @schema
54
132
 
@@ -3,7 +3,7 @@
3
3
  module Json
4
4
  module Schema
5
5
  module Diff
6
- VERSION = "0.1.0"
6
+ VERSION = "0.2.1"
7
7
  end
8
8
  end
9
9
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "json"
4
+ require "json-schema"
4
5
  require "optparse"
5
6
  require_relative "diff/version"
6
7
  require_relative "diff/cli"
@@ -8,9 +9,25 @@ require_relative "diff/schema_parser"
8
9
  require_relative "diff/comparer"
9
10
  require_relative "diff/formatter"
10
11
 
12
+ # JSON Schema Diff provides semantic diffing capabilities for JSON files using JSON Schema metadata.
13
+ #
14
+ # This gem allows you to compare two JSON files and get a rich diff output that is guided by
15
+ # a JSON Schema, providing type information, field metadata, and structured change detection.
16
+ #
17
+ # @example Basic usage
18
+ # Json::Schema::Diff::CLI.start(['schema.json', 'old.json', 'new.json'])
19
+ #
20
+ # @example Programmatic usage
21
+ # schema = Json::Schema::Diff::SchemaParser.new('schema.json')
22
+ # comparer = Json::Schema::Diff::Comparer.new(schema)
23
+ # diff = comparer.compare(old_data, new_data)
24
+ # formatter = Json::Schema::Diff::Formatter.new('pretty')
25
+ # puts formatter.format(diff)
11
26
  module Json
12
27
  module Schema
28
+ # The Diff module contains all functionality for JSON Schema-guided diffing
13
29
  module Diff
30
+ # Base error class for all JSON Schema Diff errors
14
31
  class Error < StandardError; end
15
32
  end
16
33
  end
metadata CHANGED
@@ -1,14 +1,28 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: json-schema-diff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrew Nesbitt
8
8
  bindir: exe
9
9
  cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
- dependencies: []
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: json-schema
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '5.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '5.0'
12
26
  description: A Ruby gem that performs semantic diffs between JSON files, using JSON
13
27
  Schema to guide and annotate the diff output with type information, field metadata,
14
28
  and structured change detection.
@@ -50,6 +64,7 @@ metadata:
50
64
  homepage_uri: https://github.com/andrew/json-schema-diff
51
65
  source_code_uri: https://github.com/andrew/json-schema-diff
52
66
  changelog_uri: https://github.com/andrew/json-schema-diff/blob/main/CHANGELOG.md
67
+ documentation_uri: https://rubydoc.info/gems/json-schema-diff
53
68
  rdoc_options: []
54
69
  require_paths:
55
70
  - lib