dry-types 0.14.1 → 1.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +631 -134
  3. data/LICENSE +17 -17
  4. data/README.md +15 -13
  5. data/dry-types.gemspec +27 -30
  6. data/lib/dry/types/any.rb +32 -12
  7. data/lib/dry/types/array/constructor.rb +32 -0
  8. data/lib/dry/types/array/member.rb +75 -16
  9. data/lib/dry/types/array.rb +19 -6
  10. data/lib/dry/types/builder.rb +131 -15
  11. data/lib/dry/types/builder_methods.rb +49 -20
  12. data/lib/dry/types/coercions/json.rb +43 -7
  13. data/lib/dry/types/coercions/params.rb +118 -31
  14. data/lib/dry/types/coercions.rb +76 -22
  15. data/lib/dry/types/compat.rb +0 -2
  16. data/lib/dry/types/compiler.rb +56 -41
  17. data/lib/dry/types/constrained/coercible.rb +36 -6
  18. data/lib/dry/types/constrained.rb +81 -32
  19. data/lib/dry/types/constraints.rb +18 -4
  20. data/lib/dry/types/constructor/function.rb +216 -0
  21. data/lib/dry/types/constructor/wrapper.rb +94 -0
  22. data/lib/dry/types/constructor.rb +126 -56
  23. data/lib/dry/types/container.rb +7 -0
  24. data/lib/dry/types/core.rb +54 -21
  25. data/lib/dry/types/decorator.rb +38 -17
  26. data/lib/dry/types/default.rb +61 -16
  27. data/lib/dry/types/enum.rb +43 -20
  28. data/lib/dry/types/errors.rb +75 -9
  29. data/lib/dry/types/extensions/maybe.rb +74 -16
  30. data/lib/dry/types/extensions/monads.rb +29 -0
  31. data/lib/dry/types/extensions.rb +7 -1
  32. data/lib/dry/types/fn_container.rb +6 -1
  33. data/lib/dry/types/hash/constructor.rb +33 -0
  34. data/lib/dry/types/hash.rb +86 -67
  35. data/lib/dry/types/inflector.rb +3 -1
  36. data/lib/dry/types/json.rb +18 -16
  37. data/lib/dry/types/lax.rb +75 -0
  38. data/lib/dry/types/map.rb +76 -33
  39. data/lib/dry/types/meta.rb +51 -0
  40. data/lib/dry/types/module.rb +120 -0
  41. data/lib/dry/types/nominal.rb +210 -0
  42. data/lib/dry/types/options.rb +13 -26
  43. data/lib/dry/types/params.rb +39 -25
  44. data/lib/dry/types/predicate_inferrer.rb +238 -0
  45. data/lib/dry/types/predicate_registry.rb +34 -0
  46. data/lib/dry/types/primitive_inferrer.rb +97 -0
  47. data/lib/dry/types/printable.rb +16 -0
  48. data/lib/dry/types/printer.rb +315 -0
  49. data/lib/dry/types/result.rb +29 -3
  50. data/lib/dry/types/schema/key.rb +156 -0
  51. data/lib/dry/types/schema.rb +408 -0
  52. data/lib/dry/types/spec/types.rb +103 -33
  53. data/lib/dry/types/sum.rb +84 -35
  54. data/lib/dry/types/type.rb +49 -0
  55. data/lib/dry/types/version.rb +3 -1
  56. data/lib/dry/types.rb +156 -76
  57. data/lib/dry-types.rb +3 -1
  58. metadata +65 -78
  59. data/.gitignore +0 -10
  60. data/.rspec +0 -2
  61. data/.travis.yml +0 -27
  62. data/.yardopts +0 -5
  63. data/CONTRIBUTING.md +0 -29
  64. data/Gemfile +0 -24
  65. data/Rakefile +0 -20
  66. data/benchmarks/hash_schemas.rb +0 -51
  67. data/lib/dry/types/compat/form_types.rb +0 -27
  68. data/lib/dry/types/compat/int.rb +0 -14
  69. data/lib/dry/types/definition.rb +0 -113
  70. data/lib/dry/types/hash/schema.rb +0 -199
  71. data/lib/dry/types/hash/schema_builder.rb +0 -75
  72. data/lib/dry/types/safe.rb +0 -59
  73. data/log/.gitkeep +0 -0
data/LICENSE CHANGED
@@ -1,20 +1,20 @@
1
- Copyright (c) 2013-2014 Piotr Solnica
1
+ The MIT License (MIT)
2
2
 
3
- Permission is hereby granted, free of charge, to any person obtaining
4
- a copy of this software and associated documentation files (the
5
- "Software"), to deal in the Software without restriction, including
6
- without limitation the rights to use, copy, modify, merge, publish,
7
- distribute, sublicense, and/or sell copies of the Software, and to
8
- permit persons to whom the Software is furnished to do so, subject to
9
- the following conditions:
3
+ Copyright (c) 2015-2021 dry-rb team
10
4
 
11
- The above copyright notice and this permission notice shall be
12
- included in all copies or substantial portions of the Software.
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
6
+ this software and associated documentation files (the "Software"), to deal in
7
+ the Software without restriction, including without limitation the rights to
8
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software is furnished to do so,
10
+ subject to the following conditions:
13
11
 
14
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,27 +1,29 @@
1
1
  [gem]: https://rubygems.org/gems/dry-types
2
- [travis]: https://travis-ci.org/dry-rb/dry-types
3
- [codeclimate]: https://codeclimate.com/github/dry-rb/dry-types
4
- [coveralls]: https://coveralls.io/r/dry-rb/dry-types
2
+ [actions]: https://github.com/dry-rb/dry-types/actions
3
+ [codacy]: https://www.codacy.com/gh/dry-rb/dry-types
4
+ [chat]: https://dry-rb.zulipchat.com
5
5
  [inchpages]: http://inch-ci.org/github/dry-rb/dry-types
6
6
 
7
- # dry-types [![Join the chat at https://gitter.im/dry-rb/chat](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/dry-rb/chat)
7
+ # dry-types [![Join the chat at https://dry-rb.zulipchat.com](https://img.shields.io/badge/dry--rb-join%20chat-%23346b7a.svg)][chat]
8
8
 
9
9
  [![Gem Version](https://badge.fury.io/rb/dry-types.svg)][gem]
10
- [![Build Status](https://travis-ci.org/dry-rb/dry-types.svg?branch=master)][travis]
11
- [![Code Climate](https://codeclimate.com/github/dry-rb/dry-types/badges/gpa.svg)][codeclimate]
12
- [![Test Coverage](https://codeclimate.com/github/dry-rb/dry-types/badges/coverage.svg)][codeclimate]
10
+ [![CI Status](https://github.com/dry-rb/dry-types/workflows/ci/badge.svg)][actions]
11
+ [![Codacy Badge](https://api.codacy.com/project/badge/Grade/f2d71613195f4da993acb9ac9d6ea336)][codacy]
12
+ [![Codacy Badge](https://api.codacy.com/project/badge/Coverage/f2d71613195f4da993acb9ac9d6ea336)][codacy]
13
13
  [![Inline docs](http://inch-ci.org/github/dry-rb/dry-types.svg?branch=master)][inchpages]
14
14
 
15
15
  ## Links
16
16
 
17
- * [Documentation](http://dry-rb.org/gems/dry-types)
17
+ * [User documentation](http://dry-rb.org/gems/dry-types)
18
+ * [API documentation](http://rubydoc.info/gems/dry-types)
18
19
 
19
- ## Development
20
+ ## Supported Ruby versions
20
21
 
21
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake run_specs` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
22
+ This library officially supports the following Ruby versions:
22
23
 
23
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
24
+ * MRI >= `2.5`
25
+ * jruby >= `9.2`
24
26
 
25
- ## Contributing
27
+ ## License
26
28
 
27
- Bug reports and pull requests are welcome on GitHub at https://github.com/dry-rb/dry-types.
29
+ See `LICENSE` file.
data/dry-types.gemspec CHANGED
@@ -1,45 +1,42 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ # frozen_string_literal: true
2
+ # this file is managed by dry-rb/devtools project
3
+
4
+ lib = File.expand_path('lib', __dir__)
2
5
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
6
  require 'dry/types/version'
4
7
 
5
8
  Gem::Specification.new do |spec|
6
- spec.name = "dry-types"
7
- spec.version = Dry::Types::VERSION.dup
9
+ spec.name = 'dry-types'
8
10
  spec.authors = ["Piotr Solnica"]
9
11
  spec.email = ["piotr.solnica@gmail.com"]
10
12
  spec.license = 'MIT'
13
+ spec.version = Dry::Types::VERSION.dup
11
14
 
12
- spec.summary = 'Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc.'
15
+ spec.summary = "Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc"
13
16
  spec.description = spec.summary
14
- spec.homepage = "https://github.com/dry-rb/dry-types"
17
+ spec.homepage = 'https://dry-rb.org/gems/dry-types'
18
+ spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-types.gemspec", "lib/**/*"]
19
+ spec.bindir = 'bin'
20
+ spec.executables = []
21
+ spec.require_paths = ['lib']
15
22
 
16
- # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
17
- # delete this section to allow pushing this gem to any host.
18
- if spec.respond_to?(:metadata)
19
- spec.metadata['allowed_push_host'] = "https://rubygems.org"
20
- spec.metadata['changelog_uri'] = "https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md"
21
- spec.metadata['source_code_uri'] = "https://github.com/dry-rb/dry-types"
22
- spec.metadata['bug_tracker_uri'] = "https://github.com/dry-rb/dry-types/issues"
23
- else
24
- raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
25
- end
23
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
24
+ spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-types/blob/master/CHANGELOG.md'
25
+ spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-types'
26
+ spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-types/issues'
26
27
 
27
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } - ['bin/console', 'bin/setup']
28
- spec.bindir = "exe"
29
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
30
- spec.require_paths = ["lib"]
31
- spec.required_ruby_version = ">= 2.3.0"
28
+ spec.required_ruby_version = ">= 2.5.0"
32
29
 
33
- spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
34
- spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.4'
35
- spec.add_runtime_dependency 'dry-inflector', '~> 0.1', '>= 0.1.2'
36
- spec.add_runtime_dependency 'dry-container', '~> 0.3'
37
- spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
38
- spec.add_runtime_dependency 'dry-logic', '~> 0.5', '>= 0.5'
30
+ # to update dependencies edit project.yml
31
+ spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
32
+ spec.add_runtime_dependency "dry-container", "~> 0.3"
33
+ spec.add_runtime_dependency "dry-core", "~> 0.5", ">= 0.5"
34
+ spec.add_runtime_dependency "dry-inflector", "~> 0.1", ">= 0.1.2"
35
+ spec.add_runtime_dependency "dry-logic", "~> 1.0", ">= 1.0.2"
39
36
 
40
37
  spec.add_development_dependency "bundler"
41
- spec.add_development_dependency "rake", "~> 11.0"
42
- spec.add_development_dependency "rspec", "~> 3.3"
43
- spec.add_development_dependency 'dry-monads', '~> 0.2'
44
- spec.add_development_dependency 'yard', '~> 0.9.5'
38
+ spec.add_development_dependency "dry-monads", "~> 1.0"
39
+ spec.add_development_dependency "rake"
40
+ spec.add_development_dependency "rspec"
41
+ spec.add_development_dependency "yard"
45
42
  end
data/lib/dry/types/any.rb CHANGED
@@ -1,27 +1,47 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Dry
2
4
  module Types
3
- Any = Class.new(Definition) do
5
+ # Any is a nominal type that defines Object as the primitive class
6
+ #
7
+ # This type is useful in places where you can't be specific about the type
8
+ # and anything is acceptable.
9
+ #
10
+ # @api public
11
+ class AnyClass < Nominal
12
+ def self.name
13
+ "Any"
14
+ end
15
+
16
+ # @api private
4
17
  def initialize(**options)
5
- super(::Object, options)
18
+ super(::Object, **options)
6
19
  end
7
20
 
8
21
  # @return [String]
22
+ #
23
+ # @api public
9
24
  def name
10
- 'Any'
11
- end
12
-
13
- # @param [Object] any input is valid
14
- # @return [true]
15
- def valid?(_)
16
- true
25
+ "Any"
17
26
  end
18
- alias_method :===, :valid?
19
27
 
20
28
  # @param [Hash] new_options
29
+ #
21
30
  # @return [Type]
22
- def with(new_options)
31
+ #
32
+ # @api public
33
+ def with(**new_options)
23
34
  self.class.new(**options, meta: @meta, **new_options)
24
35
  end
25
- end.new
36
+
37
+ # @return [Array]
38
+ #
39
+ # @api public
40
+ def to_ast(meta: true)
41
+ [:any, meta ? self.meta : EMPTY_HASH]
42
+ end
43
+ end
44
+
45
+ Any = AnyClass.new
26
46
  end
27
47
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/constructor"
4
+
5
+ module Dry
6
+ module Types
7
+ # @api public
8
+ class Array < Nominal
9
+ # @api private
10
+ class Constructor < ::Dry::Types::Constructor
11
+ # @api private
12
+ def constructor_type
13
+ ::Dry::Types::Array::Constructor
14
+ end
15
+
16
+ # @return [Lax]
17
+ #
18
+ # @api public
19
+ def lax
20
+ Lax.new(type.lax.constructor(fn, meta: meta))
21
+ end
22
+
23
+ # @see Dry::Types::Array#of
24
+ #
25
+ # @api public
26
+ def of(member)
27
+ type.of(member).constructor(fn, meta: meta)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -1,57 +1,111 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/array/constructor"
4
+
1
5
  module Dry
2
6
  module Types
3
- class Array < Definition
7
+ class Array < Nominal
8
+ # Member arrays define their member type that is applied to each element
9
+ #
10
+ # @api public
4
11
  class Member < Array
5
12
  # @return [Type]
6
13
  attr_reader :member
7
14
 
8
15
  # @param [Class] primitive
9
16
  # @param [Hash] options
17
+ #
10
18
  # @option options [Type] :member
11
- def initialize(primitive, options = {})
19
+ #
20
+ # @api private
21
+ def initialize(primitive, **options)
12
22
  @member = options.fetch(:member)
13
23
  super
14
24
  end
15
25
 
16
26
  # @param [Object] input
17
- # @param [Symbol] meth
27
+ #
18
28
  # @return [Array]
19
- def call(input, meth = :call)
20
- input.map { |el| member.__send__(meth, el) }
29
+ #
30
+ # @api private
31
+ def call_unsafe(input)
32
+ if primitive?(input)
33
+ input.each_with_object([]) do |el, output|
34
+ coerced = member.call_unsafe(el)
35
+
36
+ output << coerced unless Undefined.equal?(coerced)
37
+ end
38
+ else
39
+ super
40
+ end
21
41
  end
22
- alias_method :[], :call
23
42
 
24
- # @param [Array, #all?, Object] value
25
- # @return [Boolean]
26
- def valid?(value)
27
- super && value.all? { |el| member.valid?(el) }
43
+ # @param [Object] input
44
+ # @return [Array]
45
+ #
46
+ # @api private
47
+ def call_safe(input)
48
+ if primitive?(input)
49
+ failed = false
50
+
51
+ result = input.each_with_object([]) do |el, output|
52
+ coerced = member.call_safe(el) { |out = el|
53
+ failed = true
54
+ out
55
+ }
56
+
57
+ output << coerced unless Undefined.equal?(coerced)
58
+ end
59
+
60
+ failed ? yield(result) : result
61
+ else
62
+ yield
63
+ end
28
64
  end
29
65
 
30
66
  # @param [Array, Object] input
31
67
  # @param [#call,nil] block
68
+ #
32
69
  # @yieldparam [Failure] failure
33
70
  # @yieldreturn [Result]
71
+ #
34
72
  # @return [Result,Logic::Result]
73
+ #
74
+ # @api public
35
75
  def try(input, &block)
36
- if input.is_a?(::Array)
37
- result = call(input, :try).reject { |r| r.input.equal?(Undefined) }
38
- output = result.map(&:input)
76
+ if primitive?(input)
77
+ output = []
78
+
79
+ result = input.map { |el| member.try(el) }
80
+ result.each do |r|
81
+ output << r.input unless Undefined.equal?(r.input)
82
+ end
39
83
 
40
84
  if result.all?(&:success?)
41
85
  success(output)
42
86
  else
43
- failure = failure(output, result.select(&:failure?))
87
+ error = result.find(&:failure?).error
88
+ failure = failure(output, error)
44
89
  block ? yield(failure) : failure
45
90
  end
46
91
  else
47
- failure = failure(input, "#{input} is not an array")
92
+ failure = failure(input, CoercionError.new("#{input} is not an array"))
48
93
  block ? yield(failure) : failure
49
94
  end
50
95
  end
51
96
 
97
+ # Build a lax type
98
+ #
99
+ # @return [Lax]
100
+ #
52
101
  # @api public
102
+ def lax
103
+ Lax.new(Member.new(primitive, **options, member: member.lax, meta: meta))
104
+ end
105
+
106
+ # @see Nominal#to_ast
53
107
  #
54
- # @see Definition#to_ast
108
+ # @api public
55
109
  def to_ast(meta: true)
56
110
  if member.respond_to?(:to_ast)
57
111
  [:array, [member.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
@@ -59,6 +113,11 @@ module Dry
59
113
  [:array, [member, meta ? self.meta : EMPTY_HASH]]
60
114
  end
61
115
  end
116
+
117
+ # @api private
118
+ def constructor_type
119
+ ::Dry::Types::Array::Constructor
120
+ end
62
121
  end
63
122
  end
64
123
  end
@@ -1,13 +1,21 @@
1
- require 'dry/types/array/member'
2
- require 'dry/core/deprecations'
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/types/array/member"
4
+ require "dry/types/array/constructor"
3
5
 
4
6
  module Dry
5
7
  module Types
6
- class Array < Definition
7
- extend Dry::Core::Deprecations[:'dry-types']
8
-
9
- # @param [Type] type
8
+ # Array type can be used to define an array with optional member type
9
+ #
10
+ # @api public
11
+ class Array < Nominal
12
+ # Build an array type with a member type
13
+ #
14
+ # @param [Type,#call] type
15
+ #
10
16
  # @return [Array::Member]
17
+ #
18
+ # @api public
11
19
  def of(type)
12
20
  member =
13
21
  case type
@@ -17,6 +25,11 @@ module Dry
17
25
 
18
26
  Array::Member.new(primitive, **options, member: member)
19
27
  end
28
+
29
+ # @api private
30
+ def constructor_type
31
+ ::Dry::Types::Array::Constructor
32
+ end
20
33
  end
21
34
  end
22
35
  end
@@ -1,47 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "dry/core/deprecations"
4
+
1
5
  module Dry
2
6
  module Types
7
+ # Common API for building types and composition
8
+ #
9
+ # @api public
3
10
  module Builder
4
11
  include Dry::Core::Constants
5
12
 
6
13
  # @return [Class]
14
+ #
15
+ # @api private
7
16
  def constrained_type
8
17
  Constrained
9
18
  end
10
19
 
20
+ # @return [Class]
21
+ #
22
+ # @api private
23
+ def constructor_type
24
+ Constructor
25
+ end
26
+
27
+ # Compose two types into a Sum type
28
+ #
11
29
  # @param [Type] other
30
+ #
12
31
  # @return [Sum, Sum::Constrained]
32
+ #
33
+ # @api private
13
34
  def |(other)
14
35
  klass = constrained? && other.constrained? ? Sum::Constrained : Sum
15
36
  klass.new(self, other)
16
37
  end
17
38
 
39
+ # Turn a type into an optional type
40
+ #
18
41
  # @return [Sum]
42
+ #
43
+ # @api public
19
44
  def optional
20
- Types['strict.nil'] | self
45
+ Types["nil"] | self
21
46
  end
22
47
 
48
+ # Turn a type into a constrained type
49
+ #
23
50
  # @param [Hash] options constraining rule (see {Types.Rule})
51
+ #
24
52
  # @return [Constrained]
53
+ #
54
+ # @api public
25
55
  def constrained(options)
26
56
  constrained_type.new(self, rule: Types.Rule(options))
27
57
  end
28
58
 
59
+ # Turn a type into a type with a default value
60
+ #
29
61
  # @param [Object] input
62
+ # @option [Boolean] shared Whether it's safe to share the value across type applications
30
63
  # @param [#call,nil] block
64
+ #
31
65
  # @raise [ConstraintError]
66
+ #
32
67
  # @return [Default]
33
- def default(input = Undefined, &block)
34
- value = input.equal?(Undefined) ? block : input
68
+ #
69
+ # @api public
70
+ def default(input = Undefined, options = EMPTY_HASH, &block)
71
+ unless input.frozen? || options[:shared]
72
+ where = Core::Deprecations::STACK.()
73
+ Core::Deprecations.warn(
74
+ "#{input.inspect} is mutable."\
75
+ " Be careful: types will return the same instance of the default"\
76
+ " value every time. Call `.freeze` when setting the default"\
77
+ " or pass `shared: true` to discard this warning."\
78
+ "\n#{where}",
79
+ tag: :'dry-types'
80
+ )
81
+ end
82
+
83
+ value = Undefined.default(input, block)
84
+ type = Default[value].new(self, value)
35
85
 
36
- if value.is_a?(Proc) || valid?(value)
37
- Default[value].new(self, value)
86
+ if !type.callable? && !valid?(value)
87
+ raise ConstraintError.new(
88
+ "default value #{value.inspect} violates constraints",
89
+ value
90
+ )
38
91
  else
39
- raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
92
+ type
40
93
  end
41
94
  end
42
95
 
96
+ # Define an enum on top of the existing type
97
+ #
43
98
  # @param [Array] values
99
+ #
44
100
  # @return [Enum]
101
+ #
102
+ # @api public
45
103
  def enum(*values)
46
104
  mapping =
47
105
  if values.length == 1 && values[0].is_a?(::Hash)
@@ -53,24 +111,82 @@ module Dry
53
111
  Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
54
112
  end
55
113
 
56
- # @return [Safe]
57
- def safe
58
- Safe.new(self)
114
+ # Turn a type into a lax type that will rescue from type-errors and
115
+ # return the original input
116
+ #
117
+ # @return [Lax]
118
+ #
119
+ # @api public
120
+ def lax
121
+ Lax.new(self)
59
122
  end
60
123
 
124
+ # Define a constructor for the type
125
+ #
61
126
  # @param [#call,nil] constructor
62
127
  # @param [Hash] options
63
128
  # @param [#call,nil] block
129
+ #
64
130
  # @return [Constructor]
131
+ #
132
+ # @api public
65
133
  def constructor(constructor = nil, **options, &block)
66
- Constructor.new(with(options), fn: constructor || block)
134
+ constructor_type[with(**options), fn: constructor || block]
135
+ end
136
+ alias_method :append, :constructor
137
+ alias_method :prepend, :constructor
138
+ alias_method :>>, :constructor
139
+ alias_method :<<, :constructor
140
+
141
+ # Use the given value on type mismatch
142
+ #
143
+ # @param [Object] value
144
+ # @option [Boolean] shared Whether it's safe to share the value across type applications
145
+ # @param [#call,nil] fallback
146
+ #
147
+ # @return [Constructor]
148
+ #
149
+ # @api public
150
+ def fallback(value = Undefined, shared: false, &_fallback)
151
+ if Undefined.equal?(value) && !block_given?
152
+ raise ::ArgumentError, "fallback value or a block must be given"
153
+ end
154
+
155
+ if !block_given? && !valid?(value)
156
+ raise ConstraintError.new(
157
+ "fallback value #{value.inspect} violates constraints",
158
+ value
159
+ )
160
+ end
161
+
162
+ unless value.frozen? || shared
163
+ where = Core::Deprecations::STACK.()
164
+ Core::Deprecations.warn(
165
+ "#{value.inspect} is mutable."\
166
+ " Be careful: types will return the same instance of the fallback"\
167
+ " value every time. Call `.freeze` when setting the fallback"\
168
+ " or pass `shared: true` to discard this warning."\
169
+ "\n#{where}",
170
+ tag: :'dry-types'
171
+ )
172
+ end
173
+
174
+ constructor do |input, type, &_block|
175
+ type.(input) do |output = input|
176
+ if block_given?
177
+ yield(output)
178
+ else
179
+ value
180
+ end
181
+ end
182
+ end
67
183
  end
68
184
  end
69
185
  end
70
186
  end
71
187
 
72
- require 'dry/types/default'
73
- require 'dry/types/constrained'
74
- require 'dry/types/enum'
75
- require 'dry/types/safe'
76
- require 'dry/types/sum'
188
+ require "dry/types/default"
189
+ require "dry/types/constrained"
190
+ require "dry/types/enum"
191
+ require "dry/types/lax"
192
+ require "dry/types/sum"