From 4e791d669a32002f597808e96ed7ec66973341c3 Mon Sep 17 00:00:00 2001 From: Brooke Kuhlmann Date: Wed, 10 Dec 2025 15:52:52 -0700 Subject: [PATCH] Added custom types Necessary to validate and cast custom types in Dry Schema objects. RuboCop was updated to ignore IP addresses in the spec since we need to confirm the IP addresses are properly cast. Milestone: minor --- .config/rubocop/config.yml | 3 ++ lib/trmnl/api/types.rb | 19 +++++++++++ spec/lib/trmnl/api/types_spec.rb | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 lib/trmnl/api/types.rb create mode 100644 spec/lib/trmnl/api/types_spec.rb diff --git a/.config/rubocop/config.yml b/.config/rubocop/config.yml index 39b33dd..77af493 100644 --- a/.config/rubocop/config.yml +++ b/.config/rubocop/config.yml @@ -4,3 +4,6 @@ inherit_gem: Metrics/ParameterLists: Exclude: - lib/trmnl/api/requester.rb +Style/IpAddresses: + Exclude: + - spec/lib/trmnl/api/types_spec.rb diff --git a/lib/trmnl/api/types.rb b/lib/trmnl/api/types.rb new file mode 100644 index 0000000..1c926ff --- /dev/null +++ b/lib/trmnl/api/types.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +require "dry/types" +require "ipaddr" + +module TRMNL + module API + # A collection of custom types. + module Types + include Dry.Types(default: :strict) + + CSSVariables = Dry::Types["array"].of( + Dry::Types["array"].constrained(filled: true, size: 2).of(Dry::Types["coercible.string"]) + ) + + IPAddress = Constructor IPAddr + end + end +end diff --git a/spec/lib/trmnl/api/types_spec.rb b/spec/lib/trmnl/api/types_spec.rb new file mode 100644 index 0000000..768621f --- /dev/null +++ b/spec/lib/trmnl/api/types_spec.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +require "spec_helper" + +RSpec.describe TRMNL::API::Types do + describe "CSSVariables" do + subject(:type) { described_class::CSSVariables } + + it "answers primitive" do + expect(type.primitive).to eq(Array) + end + + it "answers array of CSS variables when valid" do + variables = [%w[--screen-w 1040px], %w[--screen-h 780px]] + expect(type.call(variables)).to eq(variables) + end + + it "answers array of coerced strings when valid" do + expect(type.call([[1, 2]])).to eq([%w[1 2]]) + end + + it "fails with empty constraint error" do + expectation = proc { type.call [[]] } + + expect(&expectation).to raise_error( + Dry::Types::ConstraintError, + "[] violates constraints (filled?([]) AND size?(2, []) failed)" + ) + end + + it "fails with size constraint error" do + expectation = proc { type.call [[1, 2, 3]] } + + expect(&expectation).to raise_error( + Dry::Types::ConstraintError, + "[1, 2, 3] violates constraints (size?(2, [1, 2, 3]) failed)" + ) + end + end + + describe "IPAddress" do + subject(:type) { described_class::IPAddress } + + it "answers primitive" do + expect(type.primitive).to eq(IPAddr) + end + + it "answers Version 4.0.0 address" do + expect(type.call("10.10.1.5")).to eq(IPAddr.new("10.10.1.5")) + end + + it "answers Version 6.0.0 address" do + expect(type.call("111:a111:111:d1:0:1:111e:1111")).to eq( + IPAddr.new("111:a111:111:d1:0:1:111e:1111") + ) + end + end +end