dry-types 0.9.0 → 0.15.0

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 (61) hide show
  1. checksums.yaml +5 -5
  2. data/.codeclimate.yml +15 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +43 -0
  5. data/.travis.yml +15 -14
  6. data/.yardopts +5 -0
  7. data/CHANGELOG.md +494 -88
  8. data/CONTRIBUTING.md +29 -0
  9. data/Gemfile +7 -6
  10. data/README.md +1 -3
  11. data/Rakefile +8 -3
  12. data/benchmarks/hash_schemas.rb +7 -7
  13. data/dry-types.gemspec +11 -9
  14. data/lib/dry/types/any.rb +36 -0
  15. data/lib/dry/types/array/member.rb +29 -4
  16. data/lib/dry/types/array.rb +6 -4
  17. data/lib/dry/types/builder.rb +48 -6
  18. data/lib/dry/types/builder_methods.rb +111 -0
  19. data/lib/dry/types/coercions/json.rb +3 -0
  20. data/lib/dry/types/coercions/{form.rb → params.rb} +23 -3
  21. data/lib/dry/types/coercions.rb +16 -3
  22. data/lib/dry/types/compat.rb +0 -0
  23. data/lib/dry/types/compiler.rb +66 -39
  24. data/lib/dry/types/constrained/coercible.rb +7 -1
  25. data/lib/dry/types/constrained.rb +42 -3
  26. data/lib/dry/types/constraints.rb +3 -0
  27. data/lib/dry/types/constructor.rb +98 -16
  28. data/lib/dry/types/container.rb +2 -0
  29. data/lib/dry/types/core.rb +30 -14
  30. data/lib/dry/types/decorator.rb +31 -5
  31. data/lib/dry/types/default.rb +34 -8
  32. data/lib/dry/types/enum.rb +71 -14
  33. data/lib/dry/types/errors.rb +23 -6
  34. data/lib/dry/types/extensions/maybe.rb +35 -16
  35. data/lib/dry/types/fn_container.rb +34 -0
  36. data/lib/dry/types/hash/constructor.rb +20 -0
  37. data/lib/dry/types/hash.rb +103 -23
  38. data/lib/dry/types/inflector.rb +7 -0
  39. data/lib/dry/types/json.rb +7 -7
  40. data/lib/dry/types/map.rb +98 -0
  41. data/lib/dry/types/module.rb +115 -0
  42. data/lib/dry/types/nominal.rb +119 -0
  43. data/lib/dry/types/options.rb +29 -7
  44. data/lib/dry/types/params.rb +53 -0
  45. data/lib/dry/types/printable.rb +12 -0
  46. data/lib/dry/types/printer.rb +309 -0
  47. data/lib/dry/types/result.rb +12 -2
  48. data/lib/dry/types/safe.rb +27 -1
  49. data/lib/dry/types/schema/key.rb +130 -0
  50. data/lib/dry/types/schema.rb +298 -0
  51. data/lib/dry/types/spec/types.rb +102 -0
  52. data/lib/dry/types/sum.rb +75 -13
  53. data/lib/dry/types/type.rb +6 -0
  54. data/lib/dry/types/version.rb +1 -1
  55. data/lib/dry/types.rb +104 -38
  56. data/log/.gitkeep +0 -0
  57. metadata +81 -50
  58. data/lib/dry/types/definition.rb +0 -79
  59. data/lib/dry/types/form.rb +0 -53
  60. data/lib/dry/types/hash/schema.rb +0 -156
  61. data/lib/spec/dry/types.rb +0 -56
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,29 @@
1
+ # Issue Guidelines
2
+
3
+ ## Reporting bugs
4
+
5
+ If you found a bug, report an issue and describe what's the expected behavior versus what actually happens. If the bug causes a crash, attach a full backtrace. If possible, a reproduction script showing the problem is highly appreciated.
6
+
7
+ ## Reporting feature requests
8
+
9
+ Report a feature request **only after discussing it first on [discourse.dry-rb.org](https://discourse.dry-rb.org)** where it was accepted. Please provide a concise description of the feature, don't link to a discussion thread, and instead summarize what was discussed.
10
+
11
+ ## Reporting questions, support requests, ideas, concerns etc.
12
+
13
+ **PLEASE DON'T** - use [discourse.dry-rb.org](https://discourse.dry-rb.org) instead.
14
+
15
+ # Pull Request Guidelines
16
+
17
+ A Pull Request will only be accepted if it addresses a specific issue that was reported previously, or fixes typos, mistakes in documentation etc.
18
+
19
+ Other requirements:
20
+
21
+ 1) Do not open a pull request if you can't provide tests along with it. If you have problems writing tests, ask for help in the related issue.
22
+ 2) Follow the style conventions of the surrounding code. In most cases, this is standard ruby style.
23
+ 3) Add API documentation if it's a new feature
24
+ 4) Update API documentation if it changes an existing feature
25
+ 5) Bonus points for sending a PR to [github.com/dry-rb/dry-rb.org](github.com/dry-rb/dry-rb.org) which updates user documentation and guides
26
+
27
+ # Asking for help
28
+
29
+ If these guidelines aren't helpful, and you're stuck, please post a message on [discourse.dry-rb.org](https://discourse.dry-rb.org).
data/Gemfile CHANGED
@@ -1,22 +1,23 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
+ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
4
+
3
5
  gemspec
4
6
 
5
7
  group :test do
6
- gem "codeclimate-test-reporter", platform: :rbx, require: false
8
+ platform :mri do
9
+ gem 'simplecov', require: false
10
+ end
7
11
  end
8
12
 
9
13
  group :tools do
10
- gem 'byebug', platform: :mri
11
- gem 'mutant'
12
- gem 'mutant-rspec'
14
+ gem 'pry-byebug', platform: :mri
13
15
  end
14
16
 
15
17
  group :benchmarks do
16
- gem 'sqlite3'
17
- gem 'activerecord', platform: %i(jruby mri)
18
18
  gem 'benchmark-ips'
19
19
  gem 'virtus'
20
20
  gem 'fast_attributes'
21
21
  gem 'attrio'
22
+ gem 'dry-struct'
22
23
  end
data/README.md CHANGED
@@ -1,6 +1,5 @@
1
1
  [gem]: https://rubygems.org/gems/dry-types
2
2
  [travis]: https://travis-ci.org/dry-rb/dry-types
3
- [gemnasium]: https://gemnasium.com/dry-rb/dry-types
4
3
  [codeclimate]: https://codeclimate.com/github/dry-rb/dry-types
5
4
  [coveralls]: https://coveralls.io/r/dry-rb/dry-types
6
5
  [inchpages]: http://inch-ci.org/github/dry-rb/dry-types
@@ -9,7 +8,6 @@
9
8
 
10
9
  [![Gem Version](https://badge.fury.io/rb/dry-types.svg)][gem]
11
10
  [![Build Status](https://travis-ci.org/dry-rb/dry-types.svg?branch=master)][travis]
12
- [![Dependency Status](https://gemnasium.com/dry-rb/dry-types.svg)][gemnasium]
13
11
  [![Code Climate](https://codeclimate.com/github/dry-rb/dry-types/badges/gpa.svg)][codeclimate]
14
12
  [![Test Coverage](https://codeclimate.com/github/dry-rb/dry-types/badges/coverage.svg)][codeclimate]
15
13
  [![Inline docs](http://inch-ci.org/github/dry-rb/dry-types.svg?branch=master)][inchpages]
@@ -20,7 +18,7 @@
20
18
 
21
19
  ## Development
22
20
 
23
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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.
24
22
 
25
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).
26
24
 
data/Rakefile CHANGED
@@ -4,12 +4,17 @@ require "rspec/core/rake_task"
4
4
  task :run_specs do
5
5
  require 'rspec/core'
6
6
 
7
- RSpec::Core::Runner.run(['spec/dry'])
7
+ types_result = RSpec::Core::Runner.run(['spec/dry'])
8
8
  RSpec.clear_examples
9
9
 
10
- load 'spec/dry/types.rb'
11
10
  Dry::Types.load_extensions(:maybe)
12
- RSpec::Core::Runner.run(['spec'])
11
+ ext_result = RSpec::Core::Runner.run(['spec'])
12
+
13
+ exit [types_result, ext_result].max
13
14
  end
14
15
 
15
16
  task default: :run_specs
17
+
18
+ require 'yard'
19
+ require 'yard/rake/yardoc_task'
20
+ YARD::Rake::YardocTask.new(:doc)
@@ -5,13 +5,13 @@ require 'dry-types'
5
5
 
6
6
  module SchemaBench
7
7
  def self.hash_schema(type)
8
- Dry::Types['hash'].public_send(type,
9
- email: Dry::Types['string'],
10
- age: Dry::Types['form.int'],
11
- admin: Dry::Types['form.bool'],
12
- address: Dry::Types['hash'].public_send(type,
13
- city: Dry::Types['string'],
14
- street: Dry::Types['string']
8
+ Dry::Types['nominal.hash'].public_send(type,
9
+ email: Dry::Types['nominal.string'],
10
+ age: Dry::Types['params.integer'],
11
+ admin: Dry::Types['params.bool'],
12
+ address: Dry::Types['nominal.hash'].public_send(type,
13
+ city: Dry::Types['nominal.string'],
14
+ street: Dry::Types['nominal.string']
15
15
  )
16
16
  )
17
17
  end
data/dry-types.gemspec CHANGED
@@ -1,4 +1,3 @@
1
- # coding: utf-8
2
1
  lib = File.expand_path('../lib', __FILE__)
3
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
3
  require 'dry/types/version'
@@ -12,12 +11,15 @@ Gem::Specification.new do |spec|
12
11
 
13
12
  spec.summary = 'Type system for Ruby supporting coercions, constraints and complex types like structs, value objects, enums etc.'
14
13
  spec.description = spec.summary
15
- spec.homepage = "https://github.com/dryrb/dry-types"
14
+ spec.homepage = "https://github.com/dry-rb/dry-types"
16
15
 
17
16
  # Prevent pushing this gem to RubyGems.org by setting 'allowed_push_host', or
18
17
  # delete this section to allow pushing this gem to any host.
19
18
  if spec.respond_to?(:metadata)
20
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"
21
23
  else
22
24
  raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
23
25
  end
@@ -26,18 +28,18 @@ Gem::Specification.new do |spec|
26
28
  spec.bindir = "exe"
27
29
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
30
  spec.require_paths = ["lib"]
29
- spec.required_ruby_version = ">= 2.1.0"
31
+ spec.required_ruby_version = ">= 2.3.0"
30
32
 
31
33
  spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
32
- spec.add_runtime_dependency 'dry-core', '~> 0.1'
34
+ spec.add_runtime_dependency 'dry-core', '~> 0.4', '>= 0.4.4'
35
+ spec.add_runtime_dependency 'dry-inflector', '~> 0.1', '>= 0.1.2'
33
36
  spec.add_runtime_dependency 'dry-container', '~> 0.3'
34
- spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
35
- spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
36
- spec.add_runtime_dependency 'dry-logic', '~> 0.4', '>= 0.4.0'
37
- spec.add_runtime_dependency 'inflecto', '~> 0.0.0', '>= 0.0.2'
37
+ spec.add_runtime_dependency 'dry-equalizer', '~> 0.2', '>= 0.2.2'
38
+ spec.add_runtime_dependency 'dry-logic', '~> 0.5', '>= 0.5'
38
39
 
39
- spec.add_development_dependency "bundler", "~> 1.6"
40
+ spec.add_development_dependency "bundler"
40
41
  spec.add_development_dependency "rake", "~> 11.0"
41
42
  spec.add_development_dependency "rspec", "~> 3.3"
42
43
  spec.add_development_dependency 'dry-monads', '~> 0.2'
44
+ spec.add_development_dependency 'yard', '~> 0.9.5'
43
45
  end
@@ -0,0 +1,36 @@
1
+ module Dry
2
+ module Types
3
+ Any = Class.new(Nominal) do
4
+ def self.name
5
+ 'Any'
6
+ end
7
+
8
+ def initialize(**options)
9
+ super(::Object, options)
10
+ end
11
+
12
+ # @return [String]
13
+ def name
14
+ 'Any'
15
+ end
16
+
17
+ # @param [Object] any input is valid
18
+ # @return [true]
19
+ def valid?(_)
20
+ true
21
+ end
22
+ alias_method :===, :valid?
23
+
24
+ # @param [Hash] new_options
25
+ # @return [Type]
26
+ def with(**new_options)
27
+ self.class.new(**options, meta: @meta, **new_options)
28
+ end
29
+
30
+ # @return [Array]
31
+ def to_ast(meta: true)
32
+ [:any, meta ? self.meta : EMPTY_HASH]
33
+ end
34
+ end.new
35
+ end
36
+ end
@@ -1,26 +1,40 @@
1
1
  module Dry
2
2
  module Types
3
- class Array < Definition
3
+ class Array < Nominal
4
4
  class Member < Array
5
+ # @return [Type]
5
6
  attr_reader :member
6
7
 
8
+ # @param [Class] primitive
9
+ # @param [Hash] options
10
+ # @option options [Type] :member
7
11
  def initialize(primitive, options = {})
8
12
  @member = options.fetch(:member)
9
13
  super
10
14
  end
11
15
 
16
+ # @param [Object] input
17
+ # @param [Symbol] meth
18
+ # @return [Array]
12
19
  def call(input, meth = :call)
13
20
  input.map { |el| member.__send__(meth, el) }
14
21
  end
15
22
  alias_method :[], :call
16
23
 
17
- def valid?(type)
18
- super && type.all? { |el| member.valid?(el) }
24
+ # @param [Array, #all?, Object] value
25
+ # @return [Boolean]
26
+ def valid?(value)
27
+ super && value.all? { |el| member.valid?(el) }
19
28
  end
20
29
 
30
+ # @param [Array, Object] input
31
+ # @param [#call,nil] block
32
+ # @yieldparam [Failure] failure
33
+ # @yieldreturn [Result]
34
+ # @return [Result,Logic::Result]
21
35
  def try(input, &block)
22
36
  if input.is_a?(::Array)
23
- result = call(input, :try)
37
+ result = call(input, :try).reject { |r| r.input.equal?(Undefined) }
24
38
  output = result.map(&:input)
25
39
 
26
40
  if result.all?(&:success?)
@@ -34,6 +48,17 @@ module Dry
34
48
  block ? yield(failure) : failure
35
49
  end
36
50
  end
51
+
52
+ # @api public
53
+ #
54
+ # @see Nominal#to_ast
55
+ def to_ast(meta: true)
56
+ if member.respond_to?(:to_ast)
57
+ [:array, [member.to_ast(meta: meta), meta ? self.meta : EMPTY_HASH]]
58
+ else
59
+ [:array, [member, meta ? self.meta : EMPTY_HASH]]
60
+ end
61
+ end
37
62
  end
38
63
  end
39
64
  end
@@ -2,15 +2,17 @@ require 'dry/types/array/member'
2
2
 
3
3
  module Dry
4
4
  module Types
5
- class Array < Definition
6
- def member(type)
5
+ class Array < Nominal
6
+ # @param [Type] type
7
+ # @return [Array::Member]
8
+ def of(type)
7
9
  member =
8
10
  case type
9
- when String, Class then Types[type]
11
+ when String then Types[type]
10
12
  else type
11
13
  end
12
14
 
13
- Array::Member.new(primitive, options.merge(member: member))
15
+ Array::Member.new(primitive, **options, member: member)
14
16
  end
15
17
  end
16
18
  end
@@ -1,47 +1,89 @@
1
- require 'dry/core/constants'
1
+ require 'dry/core/deprecations'
2
2
 
3
3
  module Dry
4
4
  module Types
5
5
  module Builder
6
6
  include Dry::Core::Constants
7
7
 
8
+ # @return [Class]
8
9
  def constrained_type
9
10
  Constrained
10
11
  end
11
12
 
13
+ # @return [Class]
14
+ def constructor_type
15
+ Constructor
16
+ end
17
+
18
+ # @param [Type] other
19
+ # @return [Sum, Sum::Constrained]
12
20
  def |(other)
13
21
  klass = constrained? && other.constrained? ? Sum::Constrained : Sum
14
22
  klass.new(self, other)
15
23
  end
16
24
 
25
+ # @return [Sum]
17
26
  def optional
18
27
  Types['strict.nil'] | self
19
28
  end
20
29
 
30
+ # @param [Hash] options constraining rule (see {Types.Rule})
31
+ # @return [Constrained]
21
32
  def constrained(options)
22
33
  constrained_type.new(self, rule: Types.Rule(options))
23
34
  end
24
35
 
25
- def default(input = Undefined, &block)
26
- value = input == Undefined ? block : input
36
+ # @param [Object] input
37
+ # @param [Hash] options
38
+ # @param [#call,nil] block
39
+ # @raise [ConstraintError]
40
+ # @return [Default]
41
+ def default(input = Undefined, options = EMPTY_HASH, &block)
42
+ unless input.frozen? || options[:shared]
43
+ where = Dry::Core::Deprecations::STACK.()
44
+ Dry::Core::Deprecations.warn(
45
+ "#{input.inspect} is mutable."\
46
+ ' Be careful: types will return the same instance of the default'\
47
+ ' value every time. Call `.freeze` when setting the default'\
48
+ ' or pass `shared: true` to discard this warning.'\
49
+ "\n#{ where }",
50
+ tag: :'dry-types'
51
+ )
52
+ end
27
53
 
28
- if value.is_a?(Proc) || valid?(value)
54
+ value = input.equal?(Undefined) ? block : input
55
+
56
+ if value.respond_to?(:call) || valid?(value)
29
57
  Default[value].new(self, value)
30
58
  else
31
59
  raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
32
60
  end
33
61
  end
34
62
 
63
+ # @param [Array] values
64
+ # @return [Enum]
35
65
  def enum(*values)
36
- Enum.new(constrained(included_in: values), values: values)
66
+ mapping =
67
+ if values.length == 1 && values[0].is_a?(::Hash)
68
+ values[0]
69
+ else
70
+ ::Hash[values.zip(values)]
71
+ end
72
+
73
+ Enum.new(constrained(included_in: mapping.keys), mapping: mapping)
37
74
  end
38
75
 
76
+ # @return [Safe]
39
77
  def safe
40
78
  Safe.new(self)
41
79
  end
42
80
 
81
+ # @param [#call,nil] constructor
82
+ # @param [Hash] options
83
+ # @param [#call,nil] block
84
+ # @return [Constructor]
43
85
  def constructor(constructor = nil, **options, &block)
44
- Constructor.new(with(options), fn: constructor || block)
86
+ constructor_type.new(with(options), fn: constructor || block)
45
87
  end
46
88
  end
47
89
  end
@@ -0,0 +1,111 @@
1
+ module Dry
2
+ module Types
3
+ module BuilderMethods
4
+ # @api private
5
+ def included(base)
6
+ super
7
+ base.extend(BuilderMethods)
8
+ end
9
+
10
+ # Build an array type.
11
+ # It is a shortcut for Array.of
12
+ #
13
+ # @example
14
+ # Types::Strings = Types.Array(Types::String)
15
+ #
16
+ # @param [Dry::Types::Type] type
17
+ #
18
+ # @return [Dry::Types::Array]
19
+ def Array(type)
20
+ self::Array.of(type)
21
+ end
22
+
23
+ # Build a hash schema
24
+ #
25
+ # @param [Hash{Symbol => Dry::Types::Type}] type_map
26
+ #
27
+ # @return [Dry::Types::Array]
28
+ # @api public
29
+ def Hash(type_map)
30
+ self::Hash.schema(type_map)
31
+ end
32
+
33
+ # Build a type which values are instances of a given class
34
+ # Values are checked using `is_a?` call
35
+ #
36
+ # @example
37
+ # Types::Error = Types.Instance(StandardError)
38
+ # Types::Error = Types.Strict(StandardError)
39
+ # Types.Strict(Integer) == Types::Strict::Int # => true
40
+ #
41
+ # @param [Class,Module] klass Class or module
42
+ #
43
+ # @return [Dry::Types::Type]
44
+ # @api public
45
+ def Instance(klass)
46
+ Nominal.new(klass).constrained(type: klass)
47
+ end
48
+ alias_method :Strict, :Instance
49
+
50
+ # Build a type with a single value
51
+ # The equality check done with `eql?`
52
+ #
53
+ # @param [Object] value
54
+ #
55
+ # @return [Dry::Types::Type]
56
+ # @api public
57
+ def Value(value)
58
+ Nominal.new(value.class).constrained(eql: value)
59
+ end
60
+
61
+ # Build a type with a single value
62
+ # The equality check done with `equal?`
63
+ #
64
+ # @param [Object] object
65
+ #
66
+ # @return [Dry::Types::Type]
67
+ # @api public
68
+ def Constant(object)
69
+ Nominal.new(object.class).constrained(is: object)
70
+ end
71
+
72
+ # Build a constructor type
73
+ # If no constructor block given it uses .new method
74
+ #
75
+ # @param [Class] klass
76
+ # @param [#call,nil] cons Value constructor
77
+ # @param [#call,nil] block Value constructor
78
+ #
79
+ # @return [Dry::Types::Type]
80
+ # @api public
81
+ def Constructor(klass, cons = nil, &block)
82
+ Nominal.new(klass).constructor(cons || block || klass.method(:new))
83
+ end
84
+
85
+ # Build a nominal type
86
+ #
87
+ # @param [Class] klass
88
+ #
89
+ # @return [Dry::Types::Type]
90
+ # @api public
91
+ def Nominal(klass)
92
+ Nominal.new(klass)
93
+ end
94
+
95
+ # Build a map type
96
+ #
97
+ # @example
98
+ # Types::IntMap = Types.Map(Types::Strict::Integer, 'any')
99
+ # Types::IntStringMap = Types.Map(Types::Strict::Integer, Types::Strict::String)
100
+ #
101
+ # @param [Type] key_type Key type
102
+ # @param [Type] value_type Value type
103
+ #
104
+ # @return [Dry::Types::Map]
105
+ # @api public
106
+ def Map(key_type, value_type)
107
+ Types['nominal.hash'].map(key_type, value_type)
108
+ end
109
+ end
110
+ end
111
+ end
@@ -9,7 +9,10 @@ module Dry
9
9
  module JSON
10
10
  extend Coercions
11
11
 
12
+ # @param [#to_d, Object] input
13
+ # @return [BigDecimal,nil]
12
14
  def self.to_decimal(input)
15
+ return if input.nil?
13
16
  input.to_d unless empty_str?(input)
14
17
  end
15
18
  end
@@ -4,24 +4,36 @@ require 'bigdecimal/util'
4
4
  module Dry
5
5
  module Types
6
6
  module Coercions
7
- module Form
8
- TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES].freeze
9
- FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO].freeze
7
+ module Params
8
+ TRUE_VALUES = %w[1 on On ON t true True TRUE T y yes Yes YES Y].freeze
9
+ FALSE_VALUES = %w[0 off Off OFF f false False FALSE F n no No NO N].freeze
10
10
  BOOLEAN_MAP = ::Hash[TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])].freeze
11
11
 
12
12
  extend Coercions
13
13
 
14
+ # @param [String, Object] input
15
+ # @return [Boolean,Object]
16
+ # @see TRUE_VALUES
17
+ # @see FALSE_VALUES
14
18
  def self.to_true(input)
15
19
  BOOLEAN_MAP.fetch(input.to_s, input)
16
20
  end
17
21
 
22
+ # @param [String, Object] input
23
+ # @return [Boolean,Object]
24
+ # @see TRUE_VALUES
25
+ # @see FALSE_VALUES
18
26
  def self.to_false(input)
19
27
  BOOLEAN_MAP.fetch(input.to_s, input)
20
28
  end
21
29
 
30
+ # @param [#to_int, #to_i, Object] input
31
+ # @return [Integer, nil, Object]
22
32
  def self.to_int(input)
23
33
  if empty_str?(input)
24
34
  nil
35
+ elsif input.is_a? String
36
+ Integer(input, 10)
25
37
  else
26
38
  Integer(input)
27
39
  end
@@ -29,6 +41,8 @@ module Dry
29
41
  input
30
42
  end
31
43
 
44
+ # @param [#to_f, Object] input
45
+ # @return [Float, nil, Object]
32
46
  def self.to_float(input)
33
47
  if empty_str?(input)
34
48
  nil
@@ -39,6 +53,8 @@ module Dry
39
53
  input
40
54
  end
41
55
 
56
+ # @param [#to_d, Object] input
57
+ # @return [BigDecimal, nil, Object]
42
58
  def self.to_decimal(input)
43
59
  result = to_float(input)
44
60
 
@@ -49,10 +65,14 @@ module Dry
49
65
  end
50
66
  end
51
67
 
68
+ # @param [Array, String, Object] input
69
+ # @return [Array, Object]
52
70
  def self.to_ary(input)
53
71
  empty_str?(input) ? [] : input
54
72
  end
55
73
 
74
+ # @param [Hash, String, Object] input
75
+ # @return [Hash, Object]
56
76
  def self.to_hash(input)
57
77
  empty_str?(input) ? {} : input
58
78
  end
@@ -1,21 +1,28 @@
1
- require 'dry/core/constants'
2
-
3
1
  module Dry
4
2
  module Types
5
3
  module Coercions
6
4
  include Dry::Core::Constants
7
5
 
6
+ # @param [String, Object] input
7
+ # @return [nil] if the input is an empty string
8
+ # @return [Object] otherwise the input object is returned
8
9
  def to_nil(input)
9
10
  input unless empty_str?(input)
10
11
  end
11
12
 
13
+ # @param [#to_str, Object] input
14
+ # @return [Date, Object]
15
+ # @see Date.parse
12
16
  def to_date(input)
13
17
  return input unless input.respond_to?(:to_str)
14
18
  Date.parse(input)
15
- rescue ArgumentError
19
+ rescue ArgumentError, RangeError
16
20
  input
17
21
  end
18
22
 
23
+ # @param [#to_str, Object] input
24
+ # @return [DateTime, Object]
25
+ # @see DateTime.parse
19
26
  def to_date_time(input)
20
27
  return input unless input.respond_to?(:to_str)
21
28
  DateTime.parse(input)
@@ -23,6 +30,9 @@ module Dry
23
30
  input
24
31
  end
25
32
 
33
+ # @param [#to_str, Object] input
34
+ # @return [Time, Object]
35
+ # @see Time.parse
26
36
  def to_time(input)
27
37
  return input unless input.respond_to?(:to_str)
28
38
  Time.parse(input)
@@ -32,6 +42,9 @@ module Dry
32
42
 
33
43
  private
34
44
 
45
+ # Checks whether String is empty
46
+ # @param [String, Object] value
47
+ # @return [Boolean]
35
48
  def empty_str?(value)
36
49
  EMPTY_STRING.eql?(value)
37
50
  end
File without changes