### Basic dry-types and dry-struct Usage Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Demonstrates basic setup with dry-types and dry-struct for defining a User object with string and integer attributes. ```ruby require 'dry-types' require 'dry-struct' module Types include Dry.Types() end User = Dry.Struct(name: Types::String, age: Types::Integer) User.new(name: 'Bob', age: 35) # => # ``` -------------------------------- ### Load Maybe Extension for dry-types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/maybe.html.md Include Dry::Monads[:maybe] and load the :maybe extension for dry-types. This setup is required before using .maybe on types. ```ruby require 'dry-monads' include Dry::Monads[:maybe] # This should be inside your class require 'dry-types' Dry::Types.load_extensions(:maybe) Types = Dry.Types() ``` -------------------------------- ### Define Instance Type with Types.Instance Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Instance` to create a type that verifies if a value is an instance of a given class. Example shows checking for `Range` instances. ```ruby range_type = Types.Instance(Range) range_type[1..2] # => 1..2 ``` -------------------------------- ### Define and Use Intersection Types with Hashes Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/combining-types/intersection.html.md Demonstrates creating a Hash type that requires an 'id' key while also allowing other arbitrary keys. Shows examples of valid and invalid inputs. ```ruby Id = Types::Hash.schema(id: Types::Integer) HashWithId = Id & Types::Hash Id[{id: 1}] # => {:id=>1} Id[{id: 1, message: 'foo'}] # => {:id=>1} Id[{message: 'foo'}] # => Dry::Types::MissingKeyError: :id is missing in Hash input HashWithId[{ message: 'hello' }] # => Dry::Types::MissingKeyError: :id is missing in Hash input HashWithId[{ id: 1, message: 'hello' }] # => {:id=>1, :message=>"hello"} ``` -------------------------------- ### Define and use a homogeneous Integer-to-Float Map Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/map.html.md Use `Types::Hash.map` to define a map with specific key and value types. This example shows a map expecting Integer keys and Float values. It will raise a `Dry::Types::MapError` if the input does not conform to the defined types. ```ruby int_float_hash = Types::Hash.map(Types::Integer, Types::Float) int_float_hash[100 => 300.0, 42 => 70.0] # => {100=>300.0, 42=>70.0} # Only accepts mappings of integers to floats int_float_hash[name: 'Jane'] # => Dry::Types::MapError: input key :name is invalid: type?(Integer, :name) ``` -------------------------------- ### Define Nil or String Type Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/combining-types/sum.html.md Use the `|` operator to create a sum type that accepts either `nil` or a `String`. This example demonstrates successful type checks for valid inputs and raises a `ConstraintError` for invalid ones. ```ruby nil_or_string = Types::Nil | Types::String nil_or_string[nil] # => nil nil_or_string["hello"] # => "hello" nil_or_string[123] # raises Dry::Types::ConstraintError ``` -------------------------------- ### Define Array Member Type Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/array-with-member.html.md Use `Types::Array.of` to specify the type for all members within an array. This example defines an array that accepts only strings. ```ruby PostStatuses = Types::Array.of(Types::Coercible::String) PostStatuses[[:foo, :bar]] # ["foo", "bar"] ``` -------------------------------- ### Define Interface Type with Types.Interface Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Interface` to create a type that checks if a value responds to a specified set of methods. Examples show checking for a single method or multiple methods. ```ruby Callable = Types.Interface(:call) Contact = Types.Interface(:name, :phone) ``` -------------------------------- ### Hash Schema Type Transformation with Conditional Logic Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Apply conditional type transformations within a hash schema using `with_type_transform`. This example converts string values ending in '_at' to Time objects. ```ruby Types::Hash.with_type_transform do |key| if key.name.to_s.end_with?('_at') key.constructor { |v| Time.iso8601(v) } else key end end ``` -------------------------------- ### Transform Types within a Hash Schema Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Apply type transformations to all keys within a schema using `.with_type_transform`. For example, this can make all keys optional by default. ```ruby user_hash = Types::Hash.with_type_transform { |type| type.required(false) }.schema( name: Types::String, age: Types::Integer ) user_hash[name: 'Jane'] # => { name: 'Jane' } user_hash[{}] ``` -------------------------------- ### Include Dry::Types in a Module Source: https://context7.com/dry-rb/dry-types/llms.txt Include Dry.Types() to make all built-in type categories available as constants within a module. This is a common setup for defining custom types. ```ruby require 'dry-types' require 'dry-struct' module Types include Dry.Types() end # Basic struct using nominal types User = Dry.Struct(name: Types::String, age: Types::Integer) User.new(name: 'Bob', age: 35) # => # ``` -------------------------------- ### Strict Integer Type Check Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/built-in-types.html.md Strict types raise an error if the input is not an instance of the primitive type. This example shows a successful type check and a failed one. ```ruby Types::Strict::Integer[1] # => 1 Types::Strict::Integer['1'] # => raises Dry::Types::ConstraintError ``` -------------------------------- ### Use .maybe on Types to return Maybe objects Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/maybe.html.md Append .maybe to a type to get a Maybe object. This returns Some(value) if the type coerces successfully, or None if the input is nil. It raises ConstraintError for invalid inputs. ```ruby Types::Strict::Integer.maybe[nil] # => None Types::Strict::Integer.maybe[123] # => Some(123) Types::Coercible::String.maybe[nil] # => None Types::Coercible::String.maybe[123] # => Some("123") Types::Coercible::Float.maybe[nil] # => None Types::Coercible::Float.maybe['12.3'] # => Some(12.3) Types::Strict::String.maybe[123] # => raises Dry::Types::ConstraintError Types::Strict::Integer.maybe["foo"] # => raises Dry::Types::ConstraintError ``` -------------------------------- ### Adding Metadata with .meta Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Illustrates how to attach arbitrary metadata to a type using the `.meta` method, which can be accessed later via the schema. ```ruby class User < Dry::Struct attribute :name, Types::String attribute :age, Types::Integer.meta(info: 'extra info about age') end User.schema.key(:age).meta # => {:info=>"extra info about age"} ``` -------------------------------- ### Optional Values with .optional Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Demonstrates how to define attributes that can accept `nil` using the `.optional` method. It also shows that non-optional keys must still be present. ```ruby class User < Dry::Struct attribute :name, Types::String attribute :age, Types::Integer.optional end User.new(name: 'Bob', age: nil) # => # # name is not optional: User.new(name: nil, age: 18) # => Dry::Struct::Error: [User.new] nil (NilClass) has invalid type for :name # keys must still be present: User.new(name: 'Bob') # => Dry::Struct::Error: [User.new] :age is missing in Hash input ``` -------------------------------- ### Constructing and Constraining Types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-type-builders.html.md Shows how to create a new type based on an existing one and apply constraints. Useful for defining specific data formats. ```ruby source_type = Dry::Types['integer'] constructor_type = source_type.constructor(Kernel.method(:Integer)) constrained_type = constructor_type.constrained(gteq: 18) ``` -------------------------------- ### Load Monads Extension Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/monads.html.md Load the :monads extension to enable the #to_monad method. This requires loading dry/types and then the specific extension. ```ruby require 'dry/types' Dry::Types.load_extensions(:monads) Types = Dry.Types() ``` -------------------------------- ### Custom Constraints with .constrained Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Shows how to add custom validation rules to types using the `.constrained` method, such as ensuring an integer is greater than or equal to a specific value. ```ruby class User < Dry::Struct attribute :name, Types::Strict::String attribute :age, Types::Strict::Integer.constrained(gteq: 18) end User.new(name: 'Bob', age: 17) # => Dry::Struct::Error: [User.new] 17 (Fixnum) has invalid type for :age ``` -------------------------------- ### Type Metadata with `.meta` Source: https://context7.com/dry-rb/dry-types/llms.txt Attach arbitrary metadata to types using the `.meta` method. This metadata can be used for documentation, serialization hints, or tooling. ```ruby class User < Dry::Struct attribute :name, Types::String attribute :age, Types::Integer.meta(info: 'must be 18+', source: :database) end User.schema.key(:age).meta # => { info: "must be 18+", source: :database } ``` -------------------------------- ### Maybe Extension: Functional Pipeline Source: https://context7.com/dry-rb/dry-types/llms.txt Demonstrates using `.maybe` with `fmap` and `value_or` for functional programming patterns. This allows chaining operations on potentially absent values. ```ruby require 'dry-monads' include Dry::Monads[:maybe] require 'dry-types' Dry::Types.load_extensions(:maybe) Types = Dry.Types() # Functional pipeline maybe_string = Types::Strict::String.maybe maybe_string[nil].fmap(&:upcase) # => None maybe_string['hello'].fmap(&:upcase).value_or('NOTHING') # => "HELLO" ``` -------------------------------- ### Basic Try and To Monad Conversion Source: https://context7.com/dry-rb/dry-types/llms.txt Use `try` to attempt type coercion and `to_monad` to convert the result into a `Success` or `Failure` monad. This is useful for validating and transforming input in a functional manner. ```ruby result = Types::String.try('Jane') result.class # => Dry::Types::Result::Success monad = result.to_monad # => Success("Jane") monad.value! # => "Jane" ``` ```ruby result = Types::String.try(nil) result.class # => Dry::Types::Result::Failure monad = result.to_monad # => Failure([...]) monad.failure # => [#, nil] monad .fmap { |v| puts "passed: #{v.inspect}" } .or { |err, i| puts "input #{i.inspect} failed: #{err}" } ``` -------------------------------- ### Defining a Custom Type Builder Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-type-builders.html.md Demonstrates how to extend Dry::Types with a new builder method, `:or`, for defining fallback values. This allows for more flexible type definitions. ```ruby Dry::Types.define_builder(:or) { |type, value| type.fallback(value) } ``` ```ruby source_type = Dry::Types['integer'] type = source_type.or(0) type.(10) # => 10 type.(:invalid) # => 0 ``` -------------------------------- ### Default Values with .default Source: https://context7.com/dry-rb/dry-types/llms.txt Assign a default value to a type using `.default`. This fallback is applied when the input is `Dry::Types::Undefined` (i.e., the key is missing). Defaults can be static values or callable blocks. ```ruby # Static default PostStatus = Types::String.default('draft') PostStatus[] # => "draft" PostStatus['published'] # => "published" PostStatus[true] # raises ConstraintError ``` -------------------------------- ### Define Hash Schema with Types.Hash Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Hash` to build a new hash schema. It can be used in its full form with `schema` or more concisely with `Types.Hash()`. ```ruby # In the full form Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer) # Using Types.Hash() Types.Hash(name: Types::String, age: Types::Coercible::Integer) ``` -------------------------------- ### Maybe Extension: Basic Usage Source: https://context7.com/dry-rb/dry-types/llms.txt Use the `.maybe` extension to handle `nil` values gracefully, returning `None` (from `dry-monads`) instead of raising errors. Supports functional methods like `fmap` and `value_or`. ```ruby require 'dry-monads' include Dry::Monads[:maybe] require 'dry-types' Dry::Types.load_extensions(:maybe) Types = Dry.Types() Types::Strict::Integer.maybe[nil] # => None Types::Strict::Integer.maybe[123] # => Some(123) Types::Coercible::String.maybe[nil] # => None Types::Coercible::String.maybe[123] # => Some("123") Types::Strict::String.maybe[123] # => raises Dry::Types::ConstraintError ``` -------------------------------- ### Hash Schema with Default Values Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Define default values for keys using `.default(value)`. Defaults are only evaluated if the key is missing. To handle `nil` inputs and apply defaults, wrap the type in a constructor and map `nil` to `Dry::Types::Undefined`. ```ruby user_hash = Types::Hash.schema( name: Types::String, age: Types::Integer.default(18) ) user_hash[name: 'Jane'] # => { name: 'Jane', age: 18 } ``` ```ruby # nil violates the constraint user_hash[name: 'Jane', age: nil] # => Dry::Types::SchemaError: nil (NilClass) has invalid type # for :age violates constraints (type?(Integer, nil) failed) ``` ```ruby user_hash = Types::Hash.schema( name: Types::String, age: Types::Integer. default(18). constructor { |value| value.nil? ? Dry::Types::Undefined : value } ) user_hash[name: 'Jane', age: nil] # => { name: 'Jane', age: 18 } ``` -------------------------------- ### Using Do Notation with Monads Extension Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/monads.html.md Integrate dry-types coercions with dry-monads do notation. Load both dry-types and dry-monads, then use yield with Types.Coercible types. ```ruby require 'dry/types' require 'dry/monads' Types = Dry.Types() Dry::Types.load_extensions(:monads) class AddTen include Dry::Monads[:result, :do] def call(input) integer = yield Types::Coercible::Integer.try(input) Success(integer + 10) end end add_ten = AddTen.new add_ten.call(10) # => Success(20) add_ten.call('integer') # => Failure([#, "integer"]) ``` -------------------------------- ### Define Constructor Type with Types.Constructor Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Constructor` to build a new constructor type for a given class, defaulting to the `new` method. It can also accept a custom constructor method or a block. ```ruby class User def initialize(attributes) @attributes = attributes end def name = @attributes.fetch(:name) end user_type = Types.Constructor(User) # It is equivalent to User.new(name: 'John') user_type[name: 'John'] # Using a method User.build user_type = Types.Constructor(User, User.method(:build)) # Using a block user_type = Types.Constructor(User) { |values| User.new(values) } ``` -------------------------------- ### Custom Type: Constructor Source: https://context7.com/dry-rb/dry-types/llms.txt Use `Types.Constructor` to create a type that instantiates a given class, passing the input value to its initializer. This allows for creating objects from raw data. ```ruby module Types include Dry.Types() end class User def initialize(attrs) = @attrs = attrs def name = @attrs[:name] end user_type = Types.Constructor(User) user_type[name: 'John'] # => # ``` -------------------------------- ### Define a String type with a default value Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md Use `.default` to set a static default value for a type. This value is returned when the input is undefined. ```ruby PostStatus = Types::String.default('draft') PostStatus[] # "draft" PostStatus["published"] # "published" PostStatus[true] # raises ConstraintError ``` -------------------------------- ### Map methods on Maybe objects with fmap and value_or Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/maybe.html.md Apply methods to the value within a Maybe object using fmap. Use value_or to extract the value from Some or provide a default for None. This allows for functional chaining of operations on optional values. ```ruby maybe_string = Types::Strict::String.maybe maybe_string[nil] # => None maybe_string[nil].fmap(&:upcase) # => None maybe_string['something'] # => Some('something') maybe_string['something'].fmap(&:upcase) # => Some('SOMETHING') maybe_string['something'].fmap(&:upcase).value_or('NOTHING') # => "SOMETHING" ``` -------------------------------- ### Define Optional Keys in Hash Schema Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Mark a key as optional by appending `?` to its name in the schema definition. This allows the key to be absent from the input hash. ```ruby user_hash = Types::Hash.schema(name: Types::String, age?: Types::Integer) user_hash[name: 'Jane'] # => { name: 'Jane' } ``` -------------------------------- ### Transform Input Keys in Hash Schema Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Use `.with_key_transform` to apply a transformation to input keys, such as converting string keys to symbols. This is useful when input data uses a different key format than expected. ```ruby user_hash = Types::Hash.schema(name: Types::String).with_key_transform(&:to_sym) user_hash['name' => 'Jane'] # => { name: 'Jane' } ``` -------------------------------- ### Custom Type Builders: Registering a Custom Builder Source: https://context7.com/dry-rb/dry-types/llms.txt Register custom type builders using `Dry::Types.define_builder`. This allows you to create reusable methods for constructing specific types, like adding a fallback value. ```ruby # Register a custom builder Dry::Types.define_builder(:or) { |type, value| type.fallback(value) } type = Dry::Types['integer'].or(0) type.(10) # => 10 type.(:invalid) # => 0 ``` -------------------------------- ### Include Dry::Types in a Namespace Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/getting-started.html.md Include Dry::Types in a module to make its type definitions available. This is the first step to using Dry::Types in your application. ```ruby module Types include Dry.Types() end ``` -------------------------------- ### Combine Fallback and Default Values Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/fallbacks.html.md Fallbacks and default values can be combined in schemas. The default value is applied when input is missing, while the fallback is applied when the provided input is invalid. ```ruby schema = Dry::Types['hash'].schema( size: Dry::Types['integer'].fallback(50).default(100) ) schema.({}) # => { size: 100 } schema.({ size: 'invalid' }) # => { size: 50 } ``` -------------------------------- ### Define Hash Schema with Key and Type Transformations Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Use `with_key_transform` to symbolize keys and `with_type_transform` to make attributes optional. This is useful for defining flexible input schemas. ```ruby SymbolizeAndOptionalSchema = Types::Hash .schema({}) .with_key_transform(&:to_sym) .with_type_transform { |type| type.required(false) } user_hash = SymbolizeAndOptionalSchema.schema( name: Types::String, age: Types::Integer ) user_hash['name' => 'Jane'] ``` -------------------------------- ### Passing Values Directly to Dry::Types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Demonstrates how to use `Dry::Types` directly with the `[]` operator for type coercion and constraint checking without creating a struct object. ```ruby Types::Strict::String["foo"] # => "foo" Types::Strict::String["10000"] # => "10000" Types::Coercible::String[10000] # => "10000" Types::Strict::String[10000] # Dry::Types::ConstraintError: 1000 violates constraints ``` -------------------------------- ### Define a Hash Schema with String and Coercible Integer Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Use `Types::Hash.schema` to define a hash with specific keys and their corresponding types. Values that do not conform to the type will raise an error. Extra keys are omitted by default. ```ruby user_hash = Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer) user_hash[name: 'Jane', age: '21'] # => { name: 'Jane', age: 21 } # :name left untouched and :age was coerced to Integer ``` ```ruby user_hash[name: :Jane, age: '21'] # => Dry::Types::SchemaError: :Jane (Symbol) has invalid type # for :name violates constraints (type?(String, :Jane) failed) ``` ```ruby user_hash[name: 'Jane'] # => Dry::Types::MissingKeyError: :age is missing in Hash input ``` ```ruby user_hash[name: 'Jane', age: '21', city: 'London'] # => { name: 'Jane', age: 21 } ``` -------------------------------- ### Define a DateTime type with a callable default value Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md Provide a block to `.default` to execute code and generate the default value. This is useful for dynamic defaults like the current time. ```ruby CallableDateTime = Types::DateTime.default { DateTime.now } CallableDateTime[] # => # CallableDateTime[] # => # ``` -------------------------------- ### Understanding .optional as Union Type Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/optional-values.html.md `Types::String.optional` is equivalent to a union of `Types::Strict::Nil` and `Types::Strict::String`. ```ruby Types::String.optional # is syntactic sugar for Types::Strict::Nil | Types::Strict::String ``` -------------------------------- ### Warning: Mutating default values Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md Be cautious when mutating default values, as types return the same instance every time. This can lead to unexpected behavior if the default value is mutated. ```ruby default_0 = PostStatus.() # => "draft" default_1 = PostStatus.() # => "draft" # Both variables point to the same string: default_0.object_id == default_1.object_id # => true # Mutating the string will change the default value of type: default_0 << '_mutated' PostStatus.(nil) # => "draft_mutated" # not "draft" ``` -------------------------------- ### Define Integer Fallback Block Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/fallbacks.html.md Use fallback with a block to dynamically generate a fallback value. The block is executed each time an invalid input is encountered, allowing for stateful fallbacks. ```ruby cnt = 0 type = Dry::Types['integer'].fallback { cnt += 1 } type.(99) # => 99 type.('99') # => 1 type.(:invalid) # => 2 ``` -------------------------------- ### Inherit and Merge Hash Schemas Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Hash schemas support inheritance and merging. New schemas can be defined based on existing ones, merging declared keys and preserving transformations. The `strict` option is also inherited. ```ruby # Building an empty base schema StrictSymbolizingHash = Types::Hash.schema({}).strict.with_key_transform(&:to_sym) user_hash = StrictSymbolizingHash.schema( name: Types::String ) user_hash['name' => 'Jane'] # => { name: 'Jane' } user_hash['name' => 'Jane', 'city' => 'London'] # => Dry::Types::UnknownKeysError: unexpected keys [:city] in Hash input ``` ```ruby user_hash = Types::Hash.schema( name: Types::String ) address_hash = Types::Hash.schema( address: Types::String ) user_with_address_schema = user_hash.merge(address_hash) user_with_address_schema['name' => 'Jane', 'address' => 'C/ Foo'] # => { name: 'Jane', address: 'C/ Foo' } ``` -------------------------------- ### Do Notation Integration with Dry-Monads Source: https://context7.com/dry-rb/dry-types/llms.txt Integrate `dry-types` with `dry-monads` using `do` notation for sequential operations. This pattern allows chaining type coercions and transformations, handling errors functionally. ```ruby class AddTen include Dry::Monads[:result, :do] def call(input) integer = yield Types::Coercible::Integer.try(input) Success(integer + 10) end end add_ten = AddTen.new add_ten.call(10) # => Success(20) add_ten.call('integer') # => Failure([#, "integer"]) ``` -------------------------------- ### Strict Hash Schema to Disallow Extra Keys Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/hash-schemas.html.md Use `.strict` on a hash schema to raise an `UnknownKeysError` if any keys not defined in the schema are present in the input. By default, extra keys are omitted. ```ruby user_hash = Types::Hash.schema(name: Types::String).strict user_hash[name: 'Jane', age: 21] # => Dry::Types::UnknownKeysError: unexpected keys [:age] in Hash input ``` -------------------------------- ### Warning: Default values with constrained types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md When using a default value with a constrained type, if the default value does not match the constraints, it will be used as-is without raising an exception because it bypasses the constructor. ```ruby CallableDateTime = Types::DateTime.constructor(&:to_datetime).default { Time.now } CallableDateTime[Time.now] # => # CallableDateTime[Date.today] # => # CallableDateTime[] # => 2017-05-06 00:50:15 +0300 ``` -------------------------------- ### Verify Dry::Types Availability Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/getting-started.html.md After including Dry::Types, you can verify its availability by calling a type constructor in the Ruby console. ```ruby Types::Coercible::String # => #> ``` -------------------------------- ### Allowing mutation with dup Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md If mutation is intended, call `dup` on the default value before mutating it. This creates a copy that can be safely modified. ```ruby # If you really want to mutate it, call `dup` on it first: default = default.dup default << "this time it'll work" ``` -------------------------------- ### Define Array Type with Types.Array Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Array` as a shortcut for `Types::Array.of` to define an array type, specifying the type of its elements. ```ruby ListOfStrings = Types.Array(Types::String) ``` -------------------------------- ### Maybe Extension: Namespaced Form Source: https://context7.com/dry-rb/dry-types/llms.txt Shows the namespaced form of the `.maybe` extension, providing identical behavior to the method-based approach but accessed via `Types::Maybe::`. ```ruby require 'dry-monads' include Dry::Monads[:maybe] require 'dry-types' Dry::Types.load_extensions(:maybe) Types = Dry.Types() # Namespaced form (identical behavior) Types::Maybe::Strict::Integer[nil] # => None Types::Maybe::Coercible::Float['12.3'] # => Some(12.3) ``` -------------------------------- ### Shortcut Helpers for Common Types Source: https://context7.com/dry-rb/dry-types/llms.txt Dry-Types provides convenient shortcuts for common complex types like arrays and hash schemas. These simplify type definitions. ```ruby module Types include Dry.Types() end # Shortcut helpers ListOfStrings = Types.Array(Types::String) UserSchema = Types.Hash(name: Types::String, age: Types::Coercible::Integer) ``` -------------------------------- ### Monads Extension: `.try` and `.to_monad` Source: https://context7.com/dry-rb/dry-types/llms.txt Utilize the `.try` method for type application that returns a `Dry::Types::Result` instead of raising exceptions. Convert this `Result` to a `dry-monads`-compatible `Result` using `.to_monad` for use with `do` notation. ```ruby require 'dry/types' require 'dry/monads' Types = Dry.Types() Dry::Types.load_extensions(:monads) ``` -------------------------------- ### Defining Hash Schemas with Dry Types Source: https://context7.com/dry-rb/dry-types/llms.txt Define typed schemas for hashes with known keys, supporting optional keys, strict mode, key transformations, inheritance, and merging. ```ruby # Basic schema with coercion user_hash = Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer) user_hash[name: 'Jane', age: '21'] # => { name: 'Jane', age: 21 } user_hash[name: :Jane, age: '21'] # => Dry::Types::SchemaError: :Jane (Symbol) has invalid type for :name ``` ```ruby # Optional key with `?` suffix user_hash = Types::Hash.schema(name: Types::String, age?: Types::Integer) user_hash[name: 'Jane'] # => { name: 'Jane' } ``` ```ruby # Strict mode — rejects unknown keys strict_hash = Types::Hash.schema(name: Types::String).strict strict_hash[name: 'Jane', age: 21] # => Dry::Types::UnknownKeysError: unexpected keys [:age] in Hash input ``` ```ruby # Key transformation (strings to symbols) user_hash = Types::Hash.schema(name: Types::String).with_key_transform(&:to_sym) user_hash['name' => 'Jane'] # => { name: 'Jane' } ``` ```ruby # Merging two schemas user_schema = Types::Hash.schema(name: Types::String) address_schema = Types::Hash.schema(address: Types::String) combined = user_schema.merge(address_schema) combined[name: 'Jane', address: '1 Main St'] # => { name: 'Jane', address: '1 Main St' } ``` ```ruby # Type transformation — make all keys optional user_hash = Types::Hash.with_type_transform { |t| t.required(false) }.schema( name: Types::String, age: Types::Integer ) user_hash[name: 'Jane'] # => { name: 'Jane' } user_hash[{}] # => {} ``` -------------------------------- ### Optional Values with .optional Source: https://context7.com/dry-rb/dry-types/llms.txt Mark a type as optional by appending `.optional`. This allows the type to accept `nil` in addition to its base type. When used in a struct, the key must still be present in the input hash, even if its value is `nil`. ```ruby optional_string = Types::Strict::String.optional optional_string[nil] # => nil optional_string['something'] # => "something" optional_string[123] # raises Dry::Types::ConstraintError # In a struct: key must still be present in the hash class User < Dry::Struct attribute :name, Types::String attribute :age, Types::Integer.optional end User.new(name: 'Bob', age: nil) # => # User.new(name: 'Bob') # => Dry::Struct::Error: [User.new] :age is missing in Hash input ``` -------------------------------- ### Homogeneous Hashmaps with `Types::Hash.map` Source: https://context7.com/dry-rb/dry-types/llms.txt Describe a homogeneous hashmap where only the types of keys and values are constrained, not specific keys. Raises `Dry::Types::MapError` for invalid key types. ```ruby int_float_hash = Types::Hash.map(Types::Integer, Types::Float) int_float_hash[100 => 300.0, 42 => 70.0] # => { 100 => 300.0, 42 => 70.0 } int_float_hash[name: 'Jane'] # => Dry::Types::MapError: input key :name is invalid: type?(Integer, :name) ``` -------------------------------- ### Fallback Value for Invalid Input Source: https://context7.com/dry-rb/dry-types/llms.txt The `.fallback` method provides a value when input is invalid, unlike `.default` which triggers on missing input. It can be combined with `.default`. ```ruby type = Dry::Types['integer'].fallback(100) type.(99) # => 99 type.('99') # => 100 (invalid, not coerced) type.(:invalid) # => 100 ``` ```ruby # Callable block fallback cnt = 0 type = Dry::Types['integer'].fallback { cnt += 1 } type.(99) # => 99 type.('bad') # => 1 type.(:nope) # => 2 ``` ```ruby # Combining fallback + default in a hash schema schema = Dry::Types['hash'].schema( size: Dry::Types['integer'].fallback(50).default(100) ) schema.({}) # => { size: 100 } # missing → default schema.({ size: 'invalid' }) # => { size: 50 } # invalid → fallback ``` -------------------------------- ### Preventing mutation with freeze Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md Call `freeze` on the default value when setting it to prevent accidental mutation. This ensures the default value remains unchanged. ```ruby PostStatus = Types::Params::String.default('draft'.freeze) default = PostStatus.() default << 'attempt to mutate default' # => RuntimeError: can't modify frozen string ``` -------------------------------- ### Constrain String Format with Regex Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/constraints.html.md Create a string type that validates input against a regular expression for format compliance. Invalid formats will result in a ConstraintError. ```ruby email = Types::String.constrained( format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i ) email["jane@doe.org"] # => "jane@doe.org" email["jane"] # => Dry::Types::ConstraintError: "jane" violates constraints ``` -------------------------------- ### Use Maybe:: Namespaced Types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/maybe.html.md Alternatively, use the Maybe:: namespaced types to achieve the same result as appending .maybe. This provides a clear way to indicate that a type operation will return a Maybe object. ```ruby Types::Maybe::Strict::Integer[nil] # => None Types::Maybe::Strict::Integer[123] # => Some(123) Types::Maybe::Coercible::String[nil] # => None Types::Maybe::Coercible::String[123] # => Some("123") Types::Maybe::Coercible::Float[nil] # => None Types::Maybe::Coercible::Float['12.3'] # => Some(12.3) Types::Maybe::Strict::String[123] # => raises Dry::Types::ConstraintError Types::Maybe::Strict::Integer["foo"] # => raises Dry::Types::ConstraintError ``` -------------------------------- ### Dry-Types Built-in Categories Source: https://context7.com/dry-rb/dry-types/llms.txt Demonstrates the usage of various built-in dry-types categories including Nominal, Strict, Coercible, Params, and JSON. Note that Strict types raise errors on type mismatches, while Coercible and Params attempt conversions. ```ruby module Types include Dry.Types() end # --- Nominal (no checks, documentation only) --- Types::Nominal::String['anything goes'] # => "anything goes" Types::Nominal::Integer[42] # => 42 # --- Strict (raises on wrong type) --- Types::Strict::Integer[1] # => 1 Types::Strict::Integer['1'] # => raises Dry::Types::ConstraintError # --- Coercible (kernel coercions) --- Types::Coercible::Integer['42'] # => 42 Types::Coercible::String[10_000] # => "10000" Types::Coercible::Float['3.14'] # => 3.14 # --- Params (HTTP param coercions) --- Types::Params::Integer['42'] # => 42 Types::Params::Bool['true'] # => true Types::Params::Bool['false'] # => false Types::Params::Date['2024-01-15'] # => # # --- JSON (JSON-specific coercions) --- Types::JSON::Date['2024-01-15'] # => # Types::JSON::DateTime['2024-01-15T10:00:00+00:00'] # => # # --- Direct call operator --- Types::Strict::String["foo"] # => "foo" Types::Coercible::String[10_000] # => "10000" Types::Strict::String[10_000] # => Dry::Types::ConstraintError: 10000 violates constraints ``` -------------------------------- ### Correct Enum Default Usage Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/enum.html.md When defining an enum with a default value, call `.default` before `.enum`. Calling `.default` after `.enum` will raise an error. ```ruby # this is the correct usage: Dry::Types::String.default('red').enum('blue', 'green', 'red') # this will raise an error: Dry::Types::String.enum('blue', 'green', 'red').default('red') ``` -------------------------------- ### Allowing Nil Values with .optional Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/optional-values.html.md Append .optional to a type to allow nil values. This creates a new type that accepts either the original type or nil. ```ruby optional_string = Types::Strict::String.optional optional_string[nil] # => nil optional_string['something'] # => "something" optional_string[123] # raises Dry::Types::ConstraintError ``` -------------------------------- ### Define Integer Fallback Value Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/fallbacks.html.md Use fallback with a static value to specify what should be returned when the input is invalid. This is useful for ensuring a valid type even with incorrect data. ```ruby type = Dry::Types['integer'].fallback(100) type.(99) # => 99 type.('99') # => 100 type.(:invalid) # => 100 ``` -------------------------------- ### Enum with Default Value Source: https://context7.com/dry-rb/dry-types/llms.txt Define an enum with a default value by calling `.default` before `.enum`. This ensures a fallback value if none is provided. ```ruby DefaultedStatus = Types::String.default('draft').enum('draft', 'published', 'archived') ``` -------------------------------- ### Define Value Type with Types.Value Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Value` to create a type that checks for exact value equality using `==`. It raises a `ConstraintError` if the value does not match. ```ruby valid = Types.Value('valid') valid['valid'] # => 'valid' valid['invalid'] # => Dry::Types::ConstraintError: "invalid" violates constraints (eql?("valid", "invalid") failed) ``` -------------------------------- ### Sum Types with '|' Operator Source: https://context7.com/dry-rb/dry-types/llms.txt Combine multiple types using the `|` operator to create a sum type that accepts values matching any of the constituent types. Errors from the rightmost type are raised if all branches fail. ```ruby nil_or_string = Types::Nil | Types::String nil_or_string[nil] # => nil nil_or_string['hello'] # => "hello" nil_or_string[123] # => raises Dry::Types::ConstraintError # Bool is defined as a sum type internally Types::Bool # equivalent to Types::True | Types::False ``` -------------------------------- ### Define Constant Type with Types.Constant Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Constant` to create a type that checks for value identity using `equal?`. It raises a `ConstraintError` if the values are not identical. ```ruby valid = Types.Constant(:valid) valid[:valid] # => :valid valid[:invalid] # => Dry::Types::ConstraintError: :invalid violates constraints (is?(:valid, :invalid) failed) ``` -------------------------------- ### Constrain String Minimum Size Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/constraints.html.md Define a string type that enforces a minimum size. Input violating this constraint will raise a ConstraintError. ```ruby string = Types::String.constrained(min_size: 3) string['foo'] # => "foo" string['fo'] # => Dry::Types::ConstraintError: "fo" violates constraints ``` -------------------------------- ### Define Nominal Type with Types.Nominal Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/custom-types.html.md Use `Types.Nominal` to wrap a class with a simple definition without attaching any specific behavior. It does not perform type checks beyond the initial wrapping. ```ruby int = Types.Nominal(Integer) int[1] # => 1 # The type doesn't have any checks int['one'] # => 'one' ``` -------------------------------- ### Custom Type Builders: Chaining Built-in Builders Source: https://context7.com/dry-rb/dry-types/llms.txt Extend existing types by chaining built-in builders like `constructor` and `constrained`. This allows for creating complex types with specific coercions and validations. ```ruby source_type = Dry::Types['integer'] constructor_type = source_type.constructor(Kernel.method(:Integer)) constrained_type = constructor_type.constrained(gteq: 18) constrained_type[25] # => 25 constrained_type['5'] # => raises (coercion fails on string for strict integer) ``` -------------------------------- ### Default value receives type constructor Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/default-values.html.md The default block can receive the type constructor as an argument, allowing it to call the constructor itself. ```ruby CallableDateTime = Types::DateTime.constructor(&:to_datetime).default { |type| type[Time.now] } CallableDateTime[Time.now] # => # CallableDateTime[Date.today] # => # CallableDateTime[] # => # ``` -------------------------------- ### Custom Type: Interface (Duck Typing) Source: https://context7.com/dry-rb/dry-types/llms.txt Use `Types.Interface` to define types based on required methods (duck typing). This checks if an object responds to specific methods, rather than its class. ```ruby module Types include Dry.Types() end Callable = Types.Interface(:call) Contact = Types.Interface(:name, :phone) ``` -------------------------------- ### Strict Types in dry-types Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/index.html.md Illustrates the use of `Types::Strict` which raises an error if an attribute is of the wrong type. This ensures type safety at runtime. ```ruby class User < Dry::Struct attribute :name, Types::Strict::String attribute :age, Types::Strict::Integer end User.new(name: 'Bob', age: '18') # => Dry::Struct::Error: [User.new] "18" (String) has invalid type for :age ``` -------------------------------- ### Callable Default Value Source: https://context7.com/dry-rb/dry-types/llms.txt Use a callable to set a default value that is evaluated each time it's called. Frozen static defaults prevent mutation bugs. ```ruby CallableDateTime = Types::DateTime.default { DateTime.now } CallableDateTime[] # => # ``` ```ruby SafeStatus = Types::Params::String.default('draft'.freeze) default = SafeStatus.() default << 'mutate attempt' # => RuntimeError: can't modify frozen string ``` ```ruby PostStatus[Dry::Types::Undefined] # => "draft" ``` -------------------------------- ### Custom Type: Value Check Source: https://context7.com/dry-rb/dry-types/llms.txt Use `Types.Value` to create a type that checks for exact equality with a given value. This is useful for enforcing specific literal values. ```ruby module Types include Dry.Types() end valid = Types.Value('valid') valid['valid'] # => 'valid' valid['invalid'] # => Dry::Types::ConstraintError ``` -------------------------------- ### Convert dry-types Result to dry-monads Result Source: https://github.com/dry-rb/dry-types/blob/main/docsite/source/extensions/monads.html.md Use the #to_monad method on a dry-types Result object to convert it into a dry-monads Result. This allows for chaining dry-monads methods. ```ruby result = Types::String.try('Jane') result.class # => Dry::Types::Result::Success monad = result.to_monad # => Success("Jane") monad.class # => Dry::Monads::Result::Success monad.value! # => 'Jane' result = Types::String.try(nil) result.class # => Dry::Types::Result::Failure monad = result.to_monad # => Failure([...]) monad.class # => Dry::Monads::Result::Failure monad .fmap { |result| puts "passed: #{result.inspect}" } .or { |error, input| puts "input '#{input.inspect}' failed with error: #{error.to_s}" } ``` -------------------------------- ### Constraining Types with Validation Predicates Source: https://context7.com/dry-rb/dry-types/llms.txt Use `.constrained` with `dry-logic` predicates to add validation rules to types. Violations raise `Dry::Types::ConstraintError`. ```ruby string = Types::String.constrained(min_size: 3) string['foo'] # => "foo" string['fo'] # => Dry::Types::ConstraintError: "fo" violates constraints ``` ```ruby email = Types::String.constrained( format: /\A[\w+\-.]+@[a-z\d\-]+(\.[a-z]+)*\.[a-z]+\z/i ) email['jane@doe.org'] # => "jane@doe.org" email['jane'] # => Dry::Types::ConstraintError: "jane" violates constraints ``` ```ruby # Constrained types in a struct class User < Dry::Struct attribute :name, Types::Strict::String attribute :age, Types::Strict::Integer.constrained(gteq: 18) end User.new(name: 'Bob', age: 17) # => Dry::Struct::Error: [User.new] 17 (Integer) has invalid type for :age ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.