Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Rails JSON Schema validation gem with ActiveRecord-like syntax, custom error messages, and performance optimization.

License

Notifications You must be signed in to change notification settings

zaid-4/json-guard

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

10 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

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: "[email protected]",
        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: "[email protected]",
        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: "[email protected]",
        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!

About

Rails JSON Schema validation gem with ActiveRecord-like syntax, custom error messages, and performance optimization.

Resources

License

Contributing

Stars

Watchers

Forks

Packages

No packages published

Languages