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

Sum operator not working when default values are defined #158

Closed
npearson72 opened this issue May 20, 2020 · 4 comments
Closed

Sum operator not working when default values are defined #158

npearson72 opened this issue May 20, 2020 · 4 comments
Labels

Comments

@npearson72
Copy link

npearson72 commented May 20, 2020

Describe the bug

Given this setup:

class A < Dry::Struct
  attribute :foo_a, Types::Strict::String.default("foo_a")
  attribute :foo_b, Types::Strict::String.default("foo_b")
end

class B < Dry::Struct
  attribute :bar_a, Types::Strict::String.default("bar_a")
  attribute :bar_b, Types::Strict::String.default("bar_b")
end

class C < Dry::Struct
  attribute :baz, A | B
end

And this data:

data = { baz: { bar_a: "example", bar_b: "example" }}

I would expect:

C.new(data)

#=> #<C baz=#<B bar_a="example" bar_b="example">>

What I get is:

C.new(data)

#=> #<C baz=#<A foo_a="foo_a" foo_b="foo_b">>

I would expect the values I pass in to take precedence over the default values and for the sum operator to evaluate the keys and values to choose which struct to use.

To Reproduce

Please see above

Expected behavior

Please see above

Your environment

  • Affects my production application: Yes
  • Ruby version: ruby 2.6.3p62 (2019-04-16 revision 67580) [x86_64-darwin19]
  • OS: MacOS version: 10.15.4 (19E287)
@npearson72 npearson72 added the bug label May 20, 2020
@npearson72 npearson72 changed the title Bitwise operator not working when default values are defined Sum operator not working when default values are defined May 21, 2020
@flash-gordon
Copy link
Member

This is expected, in some way. dry-struct has to have a way to pick the right variant. There are two possible solutions for you

  1. Add a tag field:
class A < Dry::Struct
  attribute :tag, Types::String.constrained(eql: 'a')
  attribute :foo_a, Types::String.default("foo_a".freeze)
  attribute :foo_b, Types::String.default("foo_b".freeze)
end

class B < Dry::Struct
  attribute :tag, Types::String.constrained(eql: 'b')
  attribute :bar_a, Types::String.default("bar_a".freeze)
  attribute :bar_b, Types::String.default("bar_b".freeze)
end

# then pass the tag to pick the right struct
(A | B).(bar_a: "example", bar_b: "example", tag: 'b')
# => #<B tag="b" bar_a="example" bar_b="example"> 
  1. Another way is prohibit unexpected keys:
class A < Dry::Struct
  schema schema.strict
  attribute :foo_a, Types::String.default("foo_a".freeze)
  attribute :foo_b, Types::String.default("foo_b".freeze)
end

class B < Dry::Struct
  schema schema.strict
  attribute :bar_a, Types::String.default("bar_a".freeze)
  attribute :bar_b, Types::String.default("bar_b".freeze)
end

# now A will reject unexpected keys and dry-struct will try B
(A | B).(bar_a: "example", bar_b: "example")
 => #<B bar_a="example" bar_b="example"> 

You can always use strict structs by creating a base class:

class StrictStruct < Dry::Struct
  schema schema.strict
end

class A < StrictStruct
  # ...
end

class B < StrictStruct
  # ...
end

Both ways are OK.

@solnic
Copy link
Member

solnic commented May 21, 2020

@flash-gordon is this documented?

@flash-gordon
Copy link
Member

@solnic I don't know what exactly to document, it's not a common issue tbh. Regardless, dry-struct is barely documented

@solnic
Copy link
Member

solnic commented May 22, 2020

@flash-gordon IIRC people trip on how sum types work on a regular basis (the fact that you need one variant to fail in order for the other to be used isn't that obvious from the API point of view).

I reported #159 about this

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants