Git Product home page Git Product logo

Comments (7)

nepalez avatar nepalez commented on June 27, 2024 2

@flash-gordon v1.3.0 has been released

from dry-initializer.

flash-gordon avatar flash-gordon commented on June 27, 2024

note that atm we're using v0.11 that doesn't have UNDEFINEDs.

from dry-initializer.

nepalez avatar nepalez commented on June 27, 2024

@flash-gordon thank you! I think adding that config is a good idea.

I think it could work like:

  # current behaviour with UNDEFINED assigned and checked in readers
  extend Dry::Initializer

  # don't use UNDEFINED (assign `nil` instead)
  extend Dry::Initializer[undefined: false]

I should think a bit more about edge cases (like setting default values in case of nil was assigned). I thihk I could add in a week or so.

from dry-initializer.

nepalez avatar nepalez commented on June 27, 2024

Concerning v0.11... actually, it does use UNDEFINED:

required? ? name.to_s : "#{name} = Dry::Initializer::UNDEFINED"

from dry-initializer.

flash-gordon avatar flash-gordon commented on June 27, 2024

ahh, yes, I meant undefined in readers :)

from dry-initializer.

nepalez avatar nepalez commented on June 27, 2024

The only edge case I found is following

By design, type constraint/coercion provides a sort of a "hard" contract.

It gives garancy that an attribute is either left UNDEFINED or satisfies the constraint. For this reason a constraint is applied to both the explicitly assigned value, and result of default.

class Foo
  extend Dry::Initializer

  option :bar, proc(&:to_i), default:  proc { "1" }
  option :baz, proc(&:to_i), optional: true
end

foo = Foo.new(bar: 3, baz: nil)
foo.instance_variable_get(:@bar) # => 3
foo.instance_variable_get(:@baz) # => 0

foo = Foo.new
foo.instance_variable_get(:@bar) # => 1
foo.instance_variable_get(:@baz) # => UNDEFINED

The latest example is special in a sense it only marks missing value. If you try to assign UNDEFINED either explicitly, or implicitly (via defaults), it will be sent to coercer and (hopefully) raise an exception.

class Foo
  extend Dry::Initializer

  option :baz, proc(&:to_i), optional: true
end

foo = Foo.new(baz: Dry::Initializer::UNDEFINED)
# => BOOM! 'cause UNDEFINED has no #to_i

foo = Foo.new
foo.instance_variable_get(:@baz) # => nil 

TBH, it is not that exceptional due to #to_s etc., but the core idea is here.

The code for the initializer is the following

def __initialize__(__options__ = {})
  # ... instantiating @__options__

  @bar = __coercers__[:'option_bar'].call(__options__.fetch(:'bar') { instance_exec(&__defaults__[:'option_bar']) })
  @baz = __options__.key?(:'baz') ? __coercers__[:'option_baz'].call(__options__.fetch(:'baz')) : Dry::Initializer::UNDEFINED

  # ... assigning @__options__
end

In case when UNDEFINED dropped in favour of nil, the behaviour changes so that a constraint becomes "softer" and less consistent.

class Foo
  extend Dry::Initializer

  option :bar,               optional: true
  option :baz, proc(&:to_i), optional: true
end

foo = Foo.new(bar: nil, baz: nil)
foo.instance_variable_get(:@bar) # => nil
foo.instance_variable_get(:@baz) # => 0

foo = Foo.new
foo.instance_variable_get(:@bar) # => nil
foo.instance_variable_get(:@baz) # => nil

This becomes a bit confusing to me. For baz the nil marks a case no assignment was made. But it is not universal -- for bar nil is... just a nil.

This time I'm wondering whether this simplification worth it. Maybe we should left UNDEFINED assigned (just like in v0.11), but not hidden in a reader.

class Foo
  extend Dry::Initializer
  option :bar, optional: true
end

foo = Foo.new
foo.instance_variable_get(:@bar) # => Dry::Initializer::UNDEFINED
foo.bar # => nil

class Qux
  extend Dry::Initializer[hide_undefined: false] # no check for UNDEFINED
  option :bar, optional: true
end

qux = Qux.new
qux.instance_variable_get(:@bar) # => Dry::Initializer::UNDEFINED
qux.bar # => Dry::Initializer::UNDEFINED

from dry-initializer.

flash-gordon avatar flash-gordon commented on June 27, 2024

Well, I dunno, from the rom POV we don't need undefined because it doesn't help us, it requires extra checks for undefined where nil? is enough. Hence we're using default: proc { nil } for having nils everywhere and the need of wrapping nil with a proc is also annoying a bit.

from dry-initializer.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.