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

Configure how nullable values are treated by the struct compiler #519

Closed
waiting-for-dev opened this issue Feb 9, 2019 · 6 comments
Closed
Labels
Milestone

Comments

@waiting-for-dev
Copy link
Collaborator

Be able to change the way that the struct compiler treats nullable values from the datastore. Right now, they end up being whether nil or an actual value. It would be nice to have it being configurable, for example, to translate them into the Dry::Monads::Maybe type.

For that, I would:

  • Make configurable the struct compiler to use. The current one would be the default.
  • Change current struct compiler so that it can be easily extended to treat nullable values in different ways. For example, accepting two proc's as arguments to do something with nullable values and optional associations. Current behaviour as default.
  • Optional: ship with a extension to use dry-monads's Maybe.

Examples

class Users < ROM::Relation[:sql]
  auto_struct true
  struct_compiler My::Struct::Compiler

  schema do
    attribute :id, Types::Integer
    attribute :name, Types::String
    attribute :age, Types::Integer.optional

    associations do
      belongs_to :team
    end
  end
end

# ...

user = user_relation.where(id: 1).one.age # => Some(18)
user = user_relation.where(id: 2).one.age # => None
user = user_relation.combine(:team).where(id: 2).one.team # => None

Resources

@solnic solnic added this to the 5.1.0 milestone Apr 27, 2019
@solnic
Copy link
Member

solnic commented Apr 27, 2019

I reported #528 which covers the part of configuring a custom struct compiler class. Could you update the description here and provide an example of the API where you define how nullable values should be treated?

@waiting-for-dev
Copy link
Collaborator Author

waiting-for-dev commented May 5, 2019

I reported #528 which covers the part of configuring a custom struct compiler class.

@solnic thanks, #528 looks great!

Could you update the description here and provide an example of the API where you define how nullable values should be treated?

Actually, I'm not 100% sure about how we should tackle it. What I want is to be able to process how optional types should be treated, no mind if they are an optional attribute or a belongs_to or has_one relation. If this needs to be done in rom's struct compiler, maybe something like this would be fine:

class MyStructCompiler < StructCompiler
  do_with_optional -> (type) { type.maybe }
end

But I wonder if this could be done at dry-struct or even dry-types level...

@solnic
Copy link
Member

solnic commented Jun 16, 2019

@waiting-for-dev I believe this is something that could be done via transform_types in Dry::Struct. See dry-struct recipes.

@waiting-for-dev
Copy link
Collaborator Author

Thanks for your tip @solnic . However, I think it doesn't fulfill the feature:

require 'dry/types'
require 'dry/struct'
require 'dry/monads/maybe'

class Foo < Dry::Struct
  transform_types do |type|
    if type.optional?
      type.constructor do |value|
        Dry::Monads::Maybe.coerce(value)
      end
    else
      type
    end
  end

  attribute :foo1, Dry::Types['strict.string'].optional
end

Foo.new(foo1: nil)
# => [Foo.new] nil (NilClass) has invalid type for :foo1 violates constraints (type?(String, None) failed) (Dry::Struct::Error)

The type remains being a Dry::Types['strict.string'].optional, so it crashes when the value is transformed to Dry::Monads::Maybe::None (and same for Dry::Monads::Maybe::Some).

I first tried transforming the type itself, which seems the appropriate thing to do, but then I always get nil not sure why:

class Foo < Dry::Struct
  transform_types do |type|
    type.optional? ? type.right.maybe : type
  end

  attribute :foo1, Dry::Types['strict.string'].optional
end

puts Foo.new(foo1: nil).foo1.inspect
# => nil
puts Foo.new(foo1: "a").foo1.inspect
# => nil

@flash-gordon
Copy link
Member

This is fixed by dry-rb/dry-types#329

@waiting-for-dev
Copy link
Collaborator Author

Thanks @flash-gordon for your quick action.

This is super neat! I don't need to configure anything in the struct compiler. I just have to create a parent struct with the transform_types stuff and inherit all my entities from it. @solnic, I guess #528 can still be useful for other scenarios, but this issue can be closed.

Thanks for all your help.

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