Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support sum types of hashes in arrays #458

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions lib/dry/schema/key_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ module Schema
class KeyValidator
extend Dry::Initializer

INDEX_REGEX = /\[\d+\]/.freeze
DIGIT_REGEX = /\A\d+\z/.freeze
INDEX_REGEX = /\[\d+\]/
DIGIT_REGEX = /\A\d+\z/
BRACKETS = "[]"

# @api private
Expand Down
4 changes: 2 additions & 2 deletions lib/dry/schema/macros/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,8 @@ class Core
option :schema_dsl, optional: true

# @api private
def new(**options)
self.class.new(name: name, compiler: compiler, schema_dsl: schema_dsl, **options)
def new(klass: self.class, **options)
klass.new(name: name, compiler: compiler, schema_dsl: schema_dsl, **options)
end

# @api private
Expand Down
12 changes: 3 additions & 9 deletions lib/dry/schema/macros/dsl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,6 @@ def append_macro(macro_type)

# @api private
# rubocop: disable Metrics/AbcSize
# rubocop: disable Metrics/CyclomaticComplexity
# rubocop: disable Metrics/PerceivedComplexity
def extract_type_spec(args, nullable: false, set_type: true)
type_spec = args[0] unless schema_or_predicate?(args[0])
Expand All @@ -230,9 +229,9 @@ def extract_type_spec(args, nullable: false, set_type: true)

if type_spec.is_a?(::Array)
type_rule = type_spec.map { |ts| new(chain: false).value(ts) }.reduce(:|)
elsif type_spec.is_a?(Dry::Types::Sum) && set_type
elsif type_spec.is_a?(Dry::Types::Sum)
type_rule = [type_spec.left, type_spec.right].map { |ts|
new(klass: Core, chain: false).value(ts)
new(klass: DSL, chain: false).value(ts)
}.reduce(:|)
else
type_predicates = predicate_inferrer[resolved_type]
Expand All @@ -245,14 +244,9 @@ def extract_type_spec(args, nullable: false, set_type: true)

type(resolved_type) if set_type && resolved_type

if type_rule
yield(*predicates, type_spec: nil, type_rule: type_rule)
else
yield(*predicates, type_spec: type_spec, type_rule: nil)
end
yield(*predicates, type_spec: type_spec, type_rule: type_rule)
end
# rubocop: enable Metrics/AbcSize
# rubocop: enable Metrics/CyclomaticComplexity
# rubocop: enable Metrics/PerceivedComplexity

# @api private
Expand Down
6 changes: 3 additions & 3 deletions lib/dry/schema/macros/filled.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ def call(*predicates, **opts, &block)
ensure_valid_predicates(predicates)

append_macro(Macros::Value) do |macro|
if opts[:type_spec] && !filter_empty_string?
if opts[:type_rule]
macro.call(:filled?).value(*predicates, **opts, &block)
elsif opts[:type_spec] && !filter_empty_string?
macro.call(predicates[0], :filled?, *predicates[1..predicates.size - 1], **opts,
&block)
elsif opts[:type_rule]
macro.call(:filled?).value(*predicates, **opts, &block)
else
macro.call(:filled?, *predicates, **opts, &block)
end
Expand Down
22 changes: 22 additions & 0 deletions spec/integration/params/macros/array_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,5 +76,27 @@
expect(result).to be_failure
expect(result.errors.to_h).to eql(nums: {1 => ["must be an integer or cannot be defined"]})
end

it "applies coercion and rules to hashes" do
schema = Dry::Schema.Params {
required(:hashes).array(
Types::Hash.schema(name: "string") | Types::Hash.schema(other_name: "string")
)
}

result = schema.(hashes: [{name: "string"}, {name: "string", other_name: "string"}, {other_name: "string"}])

expect(result).to be_success
expect(result.output).to eql(hashes: [
{name: "string"},
{name: "string"},
{other_name: "string"}
])

result = schema.(hashes: [{name: "string"}, {other_key: 1}, {name: 0}, {other_name: "string"}])

expect(result).to be_failure
expect(result.errors.to_h).to eq(hashes: {1 => {or: [{name: ["is missing"]}, {other_name: ["is missing"]}]}, 2 => {or: [{name: ["must be a string"]}, {other_name: ["is missing"]}]}})
end
end
end