Skip to content

Instantly share code, notes, and snippets.

@jtzero
Last active April 9, 2025 14:23
Show Gist options
  • Save jtzero/8c05d31566bfdf3b0785199f14b9c6b5 to your computer and use it in GitHub Desktop.
Save jtzero/8c05d31566bfdf3b0785199f14b9c6b5 to your computer and use it in GitHub Desktop.
easier Structual typing with dry-types
#Types.Interface
#Types.Interface builds a type that checks a value responds to given methods.
#
#Contact = Types.Interface(:name, :phone)
# https://dry-rb.org/gems/dry-types/1.7/custom-types/#code-types-interface-code
require 'dry-types'
require 'dry-struct'
module DryStructalBuilder
def self.define!(**attrs)
Class.new(Dry::Struct) do
class << self
private
def flatten_rule(rule)
if rule.respond_to?(:rules)
rule.rules
else
[rule]
end
end
def all_are_interface?(messages)
messages.all? do |msg|
flatten_rule(msg.rule).any? {|rule| rule.predicate.name == :respond_to? }
end
end
end
attrs.each do |name, message_s|
ary_messages = [*message_s]
if ary_messages.all? { _1.is_a?(Symbol) }
attribute name, Types.Interface(*ary_messages)
elsif all_are_interface?(ary_messages)
attribute? name, message_s
else
raise Dry::Struct::Error, "Invalid types in messages list #{ary_messages}"
end
end
def matches?(other)
attributes.keys.all? { other.respond_to?(_1) }
end
end
end
end
module Types
include Dry.Types()
end
Car = DryStructalBuilder.define!(model: Types.Interface(:length))
Car.new(model: 'ford')
CarOne = DryStructalBuilder.define!(model: Types.Interface(:length).optional)
CarOne.new(model: nil)
CarOne.new
CarTwo = DryStructalBuilder.define!(model: :length)
CarTwo.new(model: 'ford')
CarThree = DryStructalBuilder.define!(model: [:length, :hash])
CarThree.new(model: 'ford')
@jtzero
Copy link
Author

jtzero commented Jan 29, 2025

need to add not nil attribute :default_role, Types::Interface(:to_s).constrained(not_eql: nil)

@jtzero
Copy link
Author

jtzero commented Apr 9, 2025

only accepts Types.Interface however an interface is just a constrained type with nothing but respond_to? rules
#<Dry::Types[Constrained<Any rule=[respond_to?(:id) AND respond_to?(:name)]>]>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment