Last active
April 9, 2025 14:23
-
-
Save jtzero/8c05d31566bfdf3b0785199f14b9c6b5 to your computer and use it in GitHub Desktop.
easier Structual typing with dry-types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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') |
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
need to add not nil
attribute :default_role, Types::Interface(:to_s).constrained(not_eql: nil)