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.
- checksums.yaml +5 -5
- data/.codeclimate.yml +15 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +43 -0
- data/.travis.yml +15 -14
- data/.yardopts +5 -0
- data/CHANGELOG.md +494 -88
- data/CONTRIBUTING.md +29 -0
- data/Gemfile +7 -6
- data/README.md +1 -3
- data/Rakefile +8 -3
- data/benchmarks/hash_schemas.rb +7 -7
- data/dry-types.gemspec +11 -9
- data/lib/dry/types/any.rb +36 -0
- data/lib/dry/types/array/member.rb +29 -4
- data/lib/dry/types/array.rb +6 -4
- data/lib/dry/types/builder.rb +48 -6
- data/lib/dry/types/builder_methods.rb +111 -0
- data/lib/dry/types/coercions/json.rb +3 -0
- data/lib/dry/types/coercions/{form.rb → params.rb} +23 -3
- data/lib/dry/types/coercions.rb +16 -3
- data/lib/dry/types/compat.rb +0 -0
- data/lib/dry/types/compiler.rb +66 -39
- data/lib/dry/types/constrained/coercible.rb +7 -1
- data/lib/dry/types/constrained.rb +42 -3
- data/lib/dry/types/constraints.rb +3 -0
- data/lib/dry/types/constructor.rb +98 -16
- data/lib/dry/types/container.rb +2 -0
- data/lib/dry/types/core.rb +30 -14
- data/lib/dry/types/decorator.rb +31 -5
- data/lib/dry/types/default.rb +34 -8
- data/lib/dry/types/enum.rb +71 -14
- data/lib/dry/types/errors.rb +23 -6
- data/lib/dry/types/extensions/maybe.rb +35 -16
- data/lib/dry/types/fn_container.rb +34 -0
- data/lib/dry/types/hash/constructor.rb +20 -0
- data/lib/dry/types/hash.rb +103 -23
- data/lib/dry/types/inflector.rb +7 -0
- data/lib/dry/types/json.rb +7 -7
- data/lib/dry/types/map.rb +98 -0
- data/lib/dry/types/module.rb +115 -0
- data/lib/dry/types/nominal.rb +119 -0
- data/lib/dry/types/options.rb +29 -7
- data/lib/dry/types/params.rb +53 -0
- data/lib/dry/types/printable.rb +12 -0
- data/lib/dry/types/printer.rb +309 -0
- data/lib/dry/types/result.rb +12 -2
- data/lib/dry/types/safe.rb +27 -1
- data/lib/dry/types/schema/key.rb +130 -0
- data/lib/dry/types/schema.rb +298 -0
- data/lib/dry/types/spec/types.rb +102 -0
- data/lib/dry/types/sum.rb +75 -13
- data/lib/dry/types/type.rb +6 -0
- data/lib/dry/types/version.rb +1 -1
- data/lib/dry/types.rb +104 -38
- data/log/.gitkeep +0 -0
- metadata +81 -50
- data/lib/dry/types/definition.rb +0 -79
- data/lib/dry/types/form.rb +0 -53
- data/lib/dry/types/hash/schema.rb +0 -156
- 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
|
-
|
|
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]
|
|
11
10
|
[][travis]
|
|
12
|
-
[][gemnasium]
|
|
13
11
|
[][codeclimate]
|
|
14
12
|
[][codeclimate]
|
|
15
13
|
[][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
|
|
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)
|
data/benchmarks/hash_schemas.rb
CHANGED
|
@@ -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['
|
|
11
|
-
admin: Dry::Types['
|
|
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/
|
|
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.
|
|
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.
|
|
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-
|
|
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"
|
|
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 <
|
|
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
|
-
|
|
18
|
-
|
|
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
|
data/lib/dry/types/array.rb
CHANGED
|
@@ -2,15 +2,17 @@ require 'dry/types/array/member'
|
|
|
2
2
|
|
|
3
3
|
module Dry
|
|
4
4
|
module Types
|
|
5
|
-
class Array <
|
|
6
|
-
|
|
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
|
|
11
|
+
when String then Types[type]
|
|
10
12
|
else type
|
|
11
13
|
end
|
|
12
14
|
|
|
13
|
-
Array::Member.new(primitive, options
|
|
15
|
+
Array::Member.new(primitive, **options, member: member)
|
|
14
16
|
end
|
|
15
17
|
end
|
|
16
18
|
end
|
data/lib/dry/types/builder.rb
CHANGED
|
@@ -1,47 +1,89 @@
|
|
|
1
|
-
require 'dry/core/
|
|
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
|
-
|
|
26
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
@@ -4,24 +4,36 @@ require 'bigdecimal/util'
|
|
|
4
4
|
module Dry
|
|
5
5
|
module Types
|
|
6
6
|
module Coercions
|
|
7
|
-
module
|
|
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
|
data/lib/dry/types/coercions.rb
CHANGED
|
@@ -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
|