ActiveRecord::JSONValidator

repository·master·Indexed 19 days ago

https://github.com/mirego/activerecord_json_validator

A Ruby gem that provides a way to validate JSON attributes in ActiveRecord models against a JSON schema using the json_schemer gem. It supports static schemas (files, hashes, strings) and dynamic schemas (Symbols or Procs), custom error messages, and provides helper methods to retrieve raw invalid JSON strings when parsing fails.

Tokens
2.3K
Snippets
8
Records
11
Agent score
63%

What's inside activerecord_json_validator

  1. Use JSON schema validation in ActiveRecord

    master

    Use the json: validator in your ActiveRecord models to validate JSON attributes against a schema. The schema can be a path to a JSON file, a Ruby Hash, or a JSON string.

    If the JSON data itself is malformed (invalid JSON), the validator provides a helper method named {attribute}_invalid_json to retrieve the raw invalid string.

    class User < ActiveRecord::Base
      PROFILE_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json')
    
      validates :profile, presence: true, json: { schema: PROFILE_JSON_SCHEMA }
    end
    
    user = User.new(name: 'Samuel Garneau', profile: { city: 'Quebec City' })
    user.valid? # => false
    
    user = User.new(name: 'Samuel Garneau', profile: '{invalid JSON":}')
    user.valid? # => false
    user.profile_invalid_json # => '{invalid JSON":}'
  2. Handle JSON parsing errors with #{attribute}_invalid_json

    master

    The JsonValidator injects a custom setter for attributes defined in the attributes option. This setter attempts to decode strings using ActiveSupport::JSON.decode.

    If parsing fails (e.g., due to malformed JSON), the validator:

    1. Sets an instance variable #{attribute}_invalid_json to the raw, unparseable string.
    2. Calls super({}) to ensure the attribute is set to an empty hash rather than crashing.

    You can check this variable on your model instance to determine if the input was invalid JSON before schema validation even occurs.

  3. Use dynamic schemas with Symbols or Procs

    master

    You can provide a Symbol or a Proc to the :schema option. These are executed in the context of the validated record. This allows you to switch schemas based on the record's state (e.g., user roles).

    class User < ActiveRecord::Base
      PROFILE_REGULAR_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile.json_schema')
      PROFILE_ADMIN_JSON_SCHEMA = Rails.root.join('config', 'schemas', 'profile_admin.json_schema')
    
      # Using a Proc
      validates :profile, presence: true, json: { schema: lambda { dynamic_profile_schema } }
    
      # Or using a Symbol
      # validates :profile, presence: true, json: { schema: :dynamic_profile_schema }
    
      def dynamic_profile_schema
        admin? ? PROFILE_ADMIN_JSON_SCHEMA : PROFILE_REGULAR_JSON_SCHEMA
      end
    end
  4. Customize validation error messages

    master

    You can customize the error message using the :message option. If you provide a Proc, it will receive an array of errors returned by the JSON schema validator. You can use this to flatten schema errors directly into the ActiveRecord errors object.

    class User < ActiveRecord::Base
      # This will add each schema error as a first-level error on the record
      validates :profile, presence: true, json: { 
        message: ->(errors) { errors }, 
        schema: 'foo.json_schema' 
      }
    end
    
    user = User.new.tap(&:valid?)
    user.errors.full_messages
    # => ['The property "#/email" of type Fixnum did not match...']
  5. Customize the value being validated

    master

    By default, the validator calls the attribute's getter method. If your getter returns something other than raw JSON data (a Hash), use the :value option with a Proc to specify how to fetch the raw data, or implement a "raw getter" method.

    # Option 1: Use the :value Proc to access the raw database value
    validates :foo, json: { schema: SCHEMA, value: ->(record, _, _) { record[:foo] } }
    
    # Option 2: Implement a custom 'raw' getter
    validates :raw_foo, json: { schema: SCHEMA }
    
    def raw_foo
      self[:foo]
    end
  6. Configure JsonValidator options

    master

    When using validates :attribute, json: { ... }, you can pass the following options:

    • schema: The JSON schema to validate against. Can be a Hash, a JSON String, a Symbol (method name on the record), or a Proc.
    • options: A hash of options passed directly to the underlying JSONSchemer validator.
    • message: The error message to use. Can be a symbol, a string, or a Proc that receives the errors array from the schema validation.
    • value: A Proc used to extract the actual value to be validated. It receives (record, attribute, value) as arguments. This is useful if you need to validate a value derived from a getter rather than the raw attribute.
    • attributes: A list of attributes to which the validator should inject custom setter methods to catch parsing errors.
  7. Configure the PostgreSQL service for testing

    master

    The docker-compose.yml file defines a postgres service used for the project's testing environment. It uses the postgres:10 image and exposes port 5432.

    Key environment variables for the test database include:

    • POSTGRES_DB: Set to activerecord_json_validator_test.
    • POSTGRES_HOST_AUTH_METHOD: Set to trust to allow connections without a password during testing.
    services:
      postgres:
        image: postgres:10
        ports:
          - 5432:5432
        restart: on-failure
        environment:
          POSTGRES_DB: activerecord_json_validator_test
          POSTGRES_HOST_AUTH_METHOD: trust
  8. Configure JSON validator options

    master

    The json: validator accepts an options hash to customize validation behavior:

    OptionDescription
    :schemaThe JSON schema to validate against. Supports file paths, Symbol (method name), Proc, Hash, or JSON string.
    :valueA Proc used to determine the actual value to validate. Useful if the attribute getter doesn't return a raw Hash.
    :messageThe error message added to the record. Can be a Symbol, String, or a Proc that returns an array of errors.
    :optionsA Hash of options supported by the underlying json_schemer gem.
  9. Use JsonValidator in ActiveRecord models

    master

    To validate a JSON attribute against a schema, use the json: validator in your ActiveRecord model. The validator supports providing a schema as a static object (Hash or String), a Symbol (calling a method on the record), or a Proc (executing logic on the record).

    By default, the validator uses the :invalid_json error message. It also automatically injects a setter method for the specified attributes to catch JSON parsing errors during assignment. If parsing fails, the raw string is stored in an #{attribute}_invalid_json instance variable, which prevents validation errors from being swallowed silently.

    class User < ApplicationRecord
      # Using a static schema
      validates :settings, json: { schema: { 'type' => 'object' } }
    
      # Using a method name (Symbol)
      validates :metadata, json: { schema: :metadata_schema }
    
      def metadata_schema
        { 'type' => 'object', 'properties' => { 'role' => { 'type' => 'string' } } }
      end
    end
  10. Use JSONValidator as an alias for JsonValidator

    master

    Due to how ActiveSupport::Inflector handles acronyms like JSON, the gem provides JSONValidator as a constant alias to JsonValidator. You can use either name when referencing the validator in your ActiveRecord models, but JSONValidator is recommended if your application treats JSON as an acronym.

    # Both are available, but JSONValidator is provided for acronym compatibility
    validates :column_name, JSONValidator => { schema: my_schema }