Project

json-guard

0.0
No release in over 3 years
Beautiful JSON Schema validation with Rails-native syntax, context-aware rules, and production-ready monitoring. Features include ActiveRecord-like schema definitions, multiple error message types, performance optimization with caching, internationalization support, and extensive Rails integration for APIs and web applications.
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

~> 2.0
~> 0.14
~> 13.0
~> 3.0
~> 1.50
~> 0.22
~> 1.4

Runtime

 Project Readme

JsonGuard

Gem Version Downloads GitHub stars GitHub issues License: MIT Ruby Rails

Enterprise-grade JSON Schema validation for Rails applications with beautiful error messages, context-aware rules, and production-ready monitoring.

πŸš€ Features

  • 🎯 Rails-Native Syntax - Clean, ActiveRecord-like schema definitions
  • πŸ’Ž Multiple Message Types - Simple, dynamic, i18n, and template-based error messages
  • ⚑ Performance Optimized - Built-in caching and lazy loading
  • 🎨 Context-Aware - Different validation rules per context (create/update/api)
  • πŸ“Š Production Ready - Monitoring, analytics, and error tracking
  • πŸ”§ Developer Tools - CLI generators and migration helpers
  • 🌍 Internationalization - Full i18n support for error messages
  • πŸ”— Extensible - Custom validators and formatters

πŸ“¦ Installation

Add this line to your application's Gemfile:

gem 'json-guard'

And then execute:

bundle install

Or install it yourself as:

gem install json-guard

Requirements

  • Ruby 2.7.0 or higher
  • Rails 6.0 or higher
  • ActiveSupport 6.0 or higher

⚑ Quick Start

1. Define Your Schema

class UserProfileSchema < JsonGuard::Schema
  name :string, required: true, min_length: 2
  email :string, required: true, format: :email
  age :integer, minimum: 18, maximum: 120

  preferences do
    theme :string, enum: %w[light dark], message: "Theme must be light or dark"
    notifications :boolean, required: true
    language :string, required: true, enum: %w[en es fr de]
  end

  settings do
    timezone :string, format: :timezone
    currency :string, enum: %w[USD EUR GBP]
  end
end

2. Add to Your Model

class User < ApplicationRecord
  validates_json_schema :profile, schema: UserProfileSchema
end

3. Validate Your Data

user = User.new(profile: { preferences: { theme: "purple" } })
user.valid? # => false
user.errors.full_messages
# => ["Theme must be light or dark"]

πŸ”§ Configuration

Global Configuration

Create an initializer file config/initializers/json_guard.rb:

JsonGuard.configure do |config|
  # Performance settings
  config.cache_schemas = true
  config.cache_validators = true
  config.max_cache_size = 1000

  # Error handling
  config.raise_on_validation_error = false
  config.detailed_error_messages = true
  config.include_error_paths = true

  # Internationalization
  config.i18n_scope = 'json_guard.errors'
  config.default_locale = :en

  # Custom formats
  config.custom_formats = {
    phone: /^\+?[\d\s\-\(\)]+$/,
    slug: /^[a-z0-9]+(?:-[a-z0-9]+)*$/,
    hex_color: /^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/,
    version: /^\d+\.\d+\.\d+$/
  }

  # Performance monitoring
  config.performance_threshold = 100 # milliseconds
  config.log_validation_errors = Rails.env.development?
  config.log_performance_metrics = Rails.env.production?
end

Environment-Specific Configuration

Development

# config/environments/development.rb
JsonGuard.configure do |config|
  config.include_suggestions = true
  config.detailed_errors = true
  config.cache_schemas = false  # Reload schemas in development
end

Production

# config/environments/production.rb
JsonGuard.configure do |config|
  config.include_suggestions = false
  config.cache_schemas = true
  config.performance_monitoring = true
end

Test

# config/environments/test.rb
JsonGuard.configure do |config|
  config.detailed_errors = true
  config.cache_schemas = false
  config.raise_on_validation_failure = true
end

πŸ“š Usage Guide

Basic Schema Types

class BasicSchema < JsonGuard::Schema
  # String validations
  title :string, required: true, min_length: 3, max_length: 100
  slug :string, format: :slug

  # Number validations
  price :number, minimum: 0, maximum: 1000000
  quantity :integer, minimum: 1

  # Boolean validations
  active :boolean, required: true

  # Array validations
  tags :array, items: { type: :string }, max_items: 10

  # Enum validations
  status :string, enum: %w[draft published archived]
end

Nested Objects

class OrderSchema < JsonGuard::Schema
  order_id :string, required: true

  # Nested object
  customer do
    name :string, required: true
    email :string, required: true, format: :email

    # Deeply nested
    address do
      street :string, required: true
      city :string, required: true
      postal_code :string, required: true
    end
  end

  # Array of objects
  items :array, items: {
    type: :object,
    properties: {
      product_id: { type: :string, required: true },
      quantity: { type: :integer, minimum: 1 },
      price: { type: :number, minimum: 0 }
    }
  }
end

Context-Aware Validation

class UserSchema < JsonGuard::Schema
  email :string, required: true, format: :email

  # Different validation rules based on context
  case_when context: :create do
    password :string, required: true, min_length: 8
  end

  case_when context: :update do
    password :string, min_length: 8  # Optional for updates
  end

  # Role-based validation
  case_when "user.role": "admin" do
    permissions :array, required: true
  end
end

Custom Error Messages

class ProductSchema < JsonGuard::Schema
  name :string, required: true,
       message: "Product name is required"

  price :number, minimum: 0,
        messages: {
          required: "Price is required",
          invalid_type: "Price must be a number",
          too_small: "Price must be at least $0"
        }

  # Dynamic messages with interpolation
  discount :number, minimum: 0, maximum: 100,
           message: "Discount must be between 0% and 100%"
end

Rails Integration

ActiveRecord Models

class User < ApplicationRecord
  validates_json_schema :profile, schema: UserProfileSchema
  validates_json_schema :preferences,
    schema: UserPreferencesSchema,
    context: :user_preferences
end

API Controllers

class Api::V1::UsersController < ApplicationController
  before_action :validate_request, only: [:create, :update]

  def create
    @user = User.new(user_params)

    if @user.save
      render json: { success: true, user: @user }, status: :created
    else
      render json: {
        success: false,
        errors: @user.errors.full_messages
      }, status: :unprocessable_entity
    end
  end

  private

  def validate_request
    validator = JsonGuard::Validator.new(CreateUserSchema)

    unless validator.validate(request_json)
      render json: {
        success: false,
        errors: validator.errors.full_messages,
        details: validator.errors.detailed_messages
      }, status: :bad_request
    end
  end

  def request_json
    @request_json ||= JSON.parse(request.body.read)
  rescue JSON::ParserError
    {}
  end
end

Direct Validation

# Validate data directly
validator = JsonGuard::Validator.new(UserSchema)
result = validator.validate(user_data)

if result.valid?
  puts "Data is valid!"
else
  puts "Errors: #{result.errors.full_messages}"
end

πŸ§ͺ Testing

RSpec Integration

# spec/schemas/user_profile_schema_spec.rb
RSpec.describe UserProfileSchema do
  describe "validation" do
    it "validates valid data" do
      valid_data = {
        name: "John Doe",
        email: "john@example.com",
        age: 30,
        preferences: {
          theme: "dark",
          notifications: true,
          language: "en"
        }
      }

      validator = JsonGuard::Validator.new(UserProfileSchema)
      expect(validator.validate(valid_data)).to be_truthy
    end

    it "rejects invalid data" do
      invalid_data = {
        name: "",
        email: "invalid-email",
        age: 15,
        preferences: {
          theme: "rainbow"
        }
      }

      validator = JsonGuard::Validator.new(UserProfileSchema)
      expect(validator.validate(invalid_data)).to be_falsy
      expect(validator.errors.full_messages).to include("Theme must be light or dark")
    end
  end
end

Model Testing

# spec/models/user_spec.rb
RSpec.describe User do
  describe "profile validation" do
    it "validates correct profile data" do
      user = User.new(
        email: "test@example.com",
        profile: {
          name: "Test User",
          preferences: {
            theme: "light",
            notifications: true,
            language: "en"
          }
        }
      )

      expect(user).to be_valid
    end

    it "rejects invalid profile data" do
      user = User.new(
        email: "test@example.com",
        profile: {
          preferences: {
            theme: "invalid"
          }
        }
      )

      expect(user).not_to be_valid
      expect(user.errors[:profile]).to be_present
    end
  end
end

Test Helpers

# spec/support/json_guard_helpers.rb
module JsonGuardHelpers
  def expect_schema_validation(schema, data, to_be_valid: true)
    validator = JsonGuard::Validator.new(schema)
    result = validator.validate(data)

    if to_be_valid
      expect(result).to be_truthy,
        "Expected data to be valid, but got errors: #{validator.errors.full_messages}"
    else
      expect(result).to be_falsy,
        "Expected data to be invalid"
    end
  end
end

RSpec.configure do |config|
  config.include JsonGuardHelpers
end

🌍 Internationalization

Setting up I18n

# config/locales/json_guard.en.yml
en:
  json_guard:
    errors:
      required: "is required"
      invalid_type: "must be a %{expected_type}"
      too_short: "must be at least %{minimum} characters"
      too_long: "must be at most %{maximum} characters"
      invalid_format: "has an invalid format"
      invalid_enum: "must be one of: %{allowed_values}"
      too_small: "must be greater than or equal to %{minimum}"
      too_large: "must be less than or equal to %{maximum}"
# config/locales/json_guard.es.yml
es:
  json_guard:
    errors:
      required: "es requerido"
      invalid_type: "debe ser un %{expected_type}"
      too_short: "debe tener al menos %{minimum} caracteres"
      too_long: "debe tener como mΓ‘ximo %{maximum} caracteres"
      invalid_format: "tiene un formato invΓ‘lido"
      invalid_enum: "debe ser uno de: %{allowed_values}"
      too_small: "debe ser mayor o igual a %{minimum}"
      too_large: "debe ser menor o igual a %{maximum}"

Using I18n in Schemas

class UserSchema < JsonGuard::Schema
  name :string, required: true,
       message: I18n.t('json_guard.errors.name_required')

  email :string, required: true, format: :email,
        messages: {
          required: I18n.t('json_guard.errors.email_required'),
          invalid_format: I18n.t('json_guard.errors.email_invalid')
        }
end

πŸ“Š Performance Optimization

Schema Caching

# Enable caching for frequently used schemas
class CachedSchema < JsonGuard::Schema
  cache_schema true
  compile_validations true

  # Your schema definition
end

Batch Validation

# Validate multiple records efficiently
validator = JsonGuard::BatchValidator.new(UserSchema)
results = validator.validate_batch([user1_data, user2_data, user3_data])

results.each_with_index do |result, index|
  if result[:valid]
    puts "Record #{index} is valid"
  else
    puts "Record #{index} has errors: #{result[:errors]}"
  end
end

Performance Monitoring

# Monitor validation performance
JsonGuard.configure do |config|
  config.performance_threshold = 50 # milliseconds
  config.slow_validation_callback = proc do |schema, data, duration|
    Rails.logger.warn "Slow validation: #{schema.name} took #{duration}ms"
    # Send to monitoring service
    StatsD.increment('json_guard.slow_validation')
  end
end

πŸ” Debugging

Verbose Error Messages

JsonGuard.configure do |config|
  config.detailed_error_messages = true
  config.include_error_paths = true
end

Logging

# Enable logging in development
JsonGuard.configure do |config|
  config.log_validation_errors = true
  config.logger = Rails.logger
end

Error Inspection

validator = JsonGuard::Validator.new(UserSchema)
validator.validate(invalid_data)

# Get detailed error information
validator.errors.each do |error|
  puts "Field: #{error.field}"
  puts "Message: #{error.message}"
  puts "Code: #{error.code}"
  puts "Path: #{error.path}"
  puts "Value: #{error.value}"
end

πŸ“– Examples

Comprehensive examples are available in the examples directory:

Each example is fully documented with explanations and test cases.

🀝 Contributing

Welcome contributions! Please see Contributing Guide for details.

Development Setup

git clone https://github.com/zaid-4/json-guard.git
cd json-guard
bundle install

Running Tests

# Run all tests
bundle exec rspec

# Run specific test file
bundle exec rspec spec/json_guard/schema_spec.rb

# Run with coverage
bundle exec rspec --format documentation

Code Style

RuboCop is being used for code style enforcement:

# Check code style
bundle exec rubocop

# Auto-fix issues
bundle exec rubocop -a

πŸ“ Changelog

See CHANGELOG.md for details about changes in each version.

πŸ“„ License

The gem is available as open source under the terms of the MIT License.

πŸ‘₯ Credits

Created and maintained by Zaid Saeed.

πŸ†˜ Support


If you find this gem useful, please consider giving it a ⭐️ on GitHub!