dry-schema
repository·main·Indexed 19 days ago
https://github.com/dry-rb/dry-schemaA Ruby library for defining data schemas and validating input data. Part of the dry-rb ecosystem, it allows developers to specify expected data types, structures, and constraints, providing detailed error messages and hints when validation fails. It includes specialized schemas for HTTP params and JSON data, as well as a DSL for defining predicates, nested hashes, and arrays.
What's inside dry-schema
- dry-schema is a library for defining data schemas and validating input data in Ruby. It allows you to define the expected structure, types, and constraints of your data, providing detailed error messages when validation fails. It is part of the dry-rb ecosystem.
Install and require dry-schema
mainTo use
dry-schemain your Ruby project, ensure the gem is installed and require the main entrypoint. This makes theDry::Schemanamespace available for defining data schemas and validation rules.require 'dry/schema'Use the dry-schema interactive console
mainThe
bin/consolescript provides an interactive Pry-based REPL for exploring thedry-schemaAPI. When running the console, you have access to aContextobject that provides shorthand methods for defining different types of schemas.Available shorthand methods in the console:
schema { ... }: CallsDry::Schema.define.params { ... }: CallsDry::Schema.Params(optimized for HTTP params, handling string keys/coercion).json { ... }: CallsDry::Schema.JSON(optimized for JSON data, handling symbol keys).
Additionally,
Dry::Typesare available via theTypesmodule.# Start the console from your terminal ./bin/console # Inside the console, you can use shorthand methods: dry-schema> my_schema = params { required(:name).filled(:string) } dry-schema*> my_schema.call(name: 'Alice') => {:name=>"Alice"}Configure `Dry::Schema::Processor` settings
mainThe
Processorclass supports several configuration settings viaDry::Configurable:key_map_type: Configures how keys are mapped.type_registry_namespace: Sets the namespace for the type registry (defaults to:strict).filter_empty_string: A boolean setting to determine if empty strings should be filtered (defaults tofalse).
Negate a predicate using the `!` operator
mainIn
dry-schema, you can negate a predicate within a schema block using the!operator. This is useful when you want to ensure a value does not satisfy a specific condition (e.g., ensuring a string is not empty).Example usage:
required(:name).value(:string) { !empty? }Create a coercible KeyMap
mainThe
#coerciblemethod returns a newKeyMapthat is configured to use a provided coercer function when processing keys. This allows the schema to transform input values during the key mapping process.coercible_map = key_map.coercible { |value| value.to_s }Define value constraints with `value()`
mainThe
value()macro sets predicates for a key. It can be used with simple predicates, predicates with arguments, or by passing a block for complex logic. You can also pass a type specification.# With a predicate required(:name).value(:filled?) # With a predicate with arguments required(:name).value(min_size?: 2) # With a predicate and arguments required(:name).value(:filled?, min_size?: 2) # With a block required(:name).value { filled? & min_size?(2) }Merge KeyMaps using the + operator
mainYou can merge two
KeyMapinstances or aKeyMapwith an array of key specs using the+operator. This returns a newKeyMapcontaining the combined keys.new_map = key_map1 + key_map2 # or new_map = key_map1 + [:new_key]Check if a MessageSet is empty
mainUse the
empty?method to determine if the validation result contains any error messages.if result.errors.empty? puts "Validation passed!" endRebuild a target hash from a source hash using write
mainThe
#writemethod allows you to take asourcehash and rebuild its structure into atargethash based on the keys defined in theKeyMap. This is a core step in schema processing for extracting or restructuring data.# source is the input hash, target is the output hash being built key_map.write(source, target)Configure custom message backends via Dry::Schema::Messages::Abstract
mainWhen implementing a custom message backend for
dry-schema(e.g., for custom translation logic or error formatting), you should inherit fromDry::Schema::Messages::Abstract. This class provides a configuration DSL and a standardized lookup mechanism for error messages.Configuration Options
You can configure the following settings within the
buildblock or via the configuration object:Setting Default Value Description default_locale(not specified) The fallback locale for translations load_paths[DEFAULT_MESSAGES_PATH]Paths to load message files top_namespaceDEFAULT_MESSAGES_ROOTThe top-level namespace for message lookups root"errors"The root key for messages lookup_options[:root, :predicate, :path, :val_type, :arg_type]Keys to exclude from interpolation options lookup_paths(array of templates) Templates used to resolve message keys based on predicate and options rule_lookup_paths["rules.%<name>s"]Templates used to resolve rule-specific messages arg_typesHash(withRange => "range")Mapping of argument classes to type strings val_typesHash(withRange => "range",String => "string")Mapping of value classes to type strings Implementation Requirements
To create a functional backend, your subclass must implement:
key?(key, options): Returnstrueif the message key exists.interpolatable_data(key, options, **data): (Private) Logic to prepare data for interpolation.interpolate(key, options, **data): (Private) Logic to perform the actual string interpolation.
class MyCustomMessages < Dry::Schema::Messages::Abstract def key?(key, options = {}) # implementation end # ... other required methods end messages = MyCustomMessages.build do |config| config.default_locale = :en config.root = "my_errors" endRetrieve error texts for a specific key
mainIf you need to find all error messages associated with a specific attribute, you can use the
[]method or thefetchmethod on theMessageSet.message_set[key]: Returns anArray<String>of error texts for the given key. Returnsnilif the key does not exist.message_set.fetch(key): Returns anArray<String>of error texts for the given key. Raises aKeyErrorif the key is not found.
# Returns an array of strings, e.g., ["must be filled", "must be a string"] errors = result.errors[:email] # Raises KeyError if :email has no errors errors = result.errors.fetch(:email)