dry-types 0.8.1 → 0.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c98574b6a658385b9a5edd1ecee455ee8f7eb1c8
4
- data.tar.gz: 4eb98a36d945929df0dd0dbf017eae91fe8c860a
3
+ metadata.gz: f08f7240bc96724af80fa080ce0086e3274927e1
4
+ data.tar.gz: 61952ddec432653f82f5d1f8edf37dd0dd8f6c55
5
5
  SHA512:
6
- metadata.gz: ea546931be6f39b663a2ce48eef8bc8e5d728072dc4d0ef744c0df4c37e9082b82d91803a44c3ec746494ef0ec7c16eb4f44767d0ef60d6a89304fb7fd0d9e79
7
- data.tar.gz: d4f3c4b12abecfc27932cb0c5295b45ee88af7006b730e8642c0b5d35d8a4a989d08ae1e302e3a8529c3823e27506da1e03ad5f9fe80671dd93ad04feda4353a
6
+ metadata.gz: 2e6fdc5887495ff175ea35397c0f912b97264485d4796c43ab85d13ff99a06e5c81ab8d8cacea3875764142a2408d348a21d980e2965002fdc9d52499dbca910
7
+ data.tar.gz: 758ed4045287102f4475a2b12228ae41da0c8f3f88c780589e4a2d8be945941ebd46e079b307bcf3844d788b4c706fec1ef0c7cb74708937592495a79ec7ba8a
data/.travis.yml CHANGED
@@ -3,14 +3,13 @@ sudo: false
3
3
  cache: bundler
4
4
  bundler_args: --without benchmarks tools
5
5
  script:
6
- - bundle exec rake spec
6
+ - bundle exec rake
7
7
  rvm:
8
- - 2.0
9
- - 2.1
10
- - 2.2
11
- - 2.3.0
8
+ - 2.1.10
9
+ - 2.2.5
10
+ - 2.3.1
12
11
  - rbx-2
13
- - jruby-9000
12
+ - jruby-9.1.1.0
14
13
  - ruby-head
15
14
  env:
16
15
  global:
@@ -18,10 +17,6 @@ env:
18
17
  matrix:
19
18
  allow_failures:
20
19
  - rvm: ruby-head
21
- - rvm: jruby-head
22
- include:
23
- - rvm: jruby-head
24
- before_install: gem install bundler --no-ri --no-rdoc
25
20
  notifications:
26
21
  email: false
27
22
  webhooks:
data/CHANGELOG.md CHANGED
@@ -1,3 +1,27 @@
1
+ # v0.9.0 2016-09-21
2
+
3
+ ## Added
4
+
5
+ * `Hash#strict_with_defaults` which validates presence of all required keys and respects default types for missing *values* (backus)
6
+ * `Type#constrained?` method (flash-gordon)
7
+
8
+ ## Fixed
9
+
10
+ * Summing two constrained types works correctly (flash-gordon)
11
+ * `Types::Array::Member#valid?` in cases where member type is a constraint (solnic)
12
+ * `Hash::Schema#try` handles exceptions properly and returns a failure object (solnic)
13
+
14
+ ## Changed
15
+
16
+ * [BREAKING] Renamed `Hash##{schema=>permissive}` (backus)
17
+ * [BREAKING] `dry-monads` dependency was made optional, Maybe types are available after `Dry::Types.load_extension(:maybe)` (flash-gordon)
18
+ * [BREAKING] `Dry::Types::Struct` and `Dry::Types::Value` have been extracted to [`dry-struct`](https://github.com/dry-rb/dry-struct) (backus)
19
+ * `Types::Form::Bool` supports upcased true/false values (kirs)
20
+ * `Types::Form::{Date,DateTime,Time}` fail gracefully for invalid input (padde)
21
+ * ice_nine dependency has been dropped as it was required by Struct only (flash-gordon)
22
+
23
+ [Compare v0.8.1...v0.9.0](https://github.com/dryrb/dry-types/compare/v0.8.1...v0.9.0)
24
+
1
25
  # v0.8.1 2016-07-13
2
26
 
3
27
  ## Fixed
@@ -34,7 +58,7 @@
34
58
  - `Bool#default` gladly accepts `false` as its value (solnic)
35
59
  - Creating an empty schema with input processor no longer fails (lasseebert)
36
60
 
37
- ## Changes
61
+ ## Changed
38
62
 
39
63
  - Allow multiple calls to meta (solnic)
40
64
  - Allow capitalised versions of true and false values for boolean coercions (nil0bject)
data/Gemfile CHANGED
@@ -14,11 +14,9 @@ end
14
14
 
15
15
  group :benchmarks do
16
16
  gem 'sqlite3'
17
- gem 'activerecord'
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
22
  end
23
-
24
- gem "dry-logic", git: 'https://github.com/dry-rb/dry-logic.git', branch: "master"
data/README.md CHANGED
@@ -20,7 +20,7 @@
20
20
 
21
21
  ## Development
22
22
 
23
- After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
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.
24
24
 
25
25
  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
26
 
data/Rakefile CHANGED
@@ -1,6 +1,15 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
3
 
4
- RSpec::Core::RakeTask.new(:spec)
4
+ task :run_specs do
5
+ require 'rspec/core'
5
6
 
6
- task :default => :spec
7
+ RSpec::Core::Runner.run(['spec/dry'])
8
+ RSpec.clear_examples
9
+
10
+ load 'spec/dry/types.rb'
11
+ Dry::Types.load_extensions(:maybe)
12
+ RSpec::Core::Runner.run(['spec'])
13
+ end
14
+
15
+ task default: :run_specs
@@ -0,0 +1,51 @@
1
+ $LOAD_PATH.unshift('lib')
2
+
3
+ require 'bundler/setup'
4
+ require 'dry-types'
5
+
6
+ module SchemaBench
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']
15
+ )
16
+ )
17
+ end
18
+
19
+ private_class_method(:hash_schema)
20
+
21
+ SCHEMAS =
22
+ Dry::Types::Hash
23
+ .public_instance_methods(false)
24
+ .map { |schema_type| [schema_type, hash_schema(schema_type)] }
25
+ .to_h
26
+
27
+ INPUT = {
28
+ email: 'jane@doe.org',
29
+ age: '20',
30
+ admin: '1',
31
+ address: { city: 'NYC', street: 'Street 1/2' }
32
+ }
33
+ end
34
+
35
+ require 'benchmark/ips'
36
+
37
+ Benchmark.ips do |x|
38
+ SchemaBench::SCHEMAS.each do |schema_type, schema|
39
+ x.report("#{schema_type}#call") do
40
+ schema.call(SchemaBench::INPUT)
41
+ end
42
+ end
43
+
44
+ SchemaBench::SCHEMAS.each do |schema_type, schema|
45
+ x.report("#{schema_type}#try") do
46
+ schema.try(SchemaBench::INPUT)
47
+ end
48
+ end
49
+
50
+ x.compare!
51
+ end
data/dry-types.gemspec CHANGED
@@ -26,17 +26,18 @@ Gem::Specification.new do |spec|
26
26
  spec.bindir = "exe"
27
27
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
28
  spec.require_paths = ["lib"]
29
+ spec.required_ruby_version = ">= 2.1.0"
29
30
 
30
31
  spec.add_runtime_dependency 'concurrent-ruby', '~> 1.0'
32
+ spec.add_runtime_dependency 'dry-core', '~> 0.1'
31
33
  spec.add_runtime_dependency 'dry-container', '~> 0.3'
32
34
  spec.add_runtime_dependency 'dry-equalizer', '~> 0.2'
33
35
  spec.add_runtime_dependency 'dry-configurable', '~> 0.1'
34
- spec.add_runtime_dependency 'dry-logic', '~> 0.3', '>= 0.3.0'
36
+ spec.add_runtime_dependency 'dry-logic', '~> 0.4', '>= 0.4.0'
35
37
  spec.add_runtime_dependency 'inflecto', '~> 0.0.0', '>= 0.0.2'
36
- spec.add_runtime_dependency 'dry-monads', '>= 0.0.1'
37
- spec.add_runtime_dependency 'ice_nine', '~> 0.11'
38
38
 
39
39
  spec.add_development_dependency "bundler", "~> 1.6"
40
40
  spec.add_development_dependency "rake", "~> 11.0"
41
41
  spec.add_development_dependency "rspec", "~> 3.3"
42
+ spec.add_development_dependency 'dry-monads', '~> 0.2'
42
43
  end
data/lib/dry/types.rb CHANGED
@@ -7,21 +7,19 @@ require 'concurrent'
7
7
 
8
8
  require 'dry-container'
9
9
  require 'dry-equalizer'
10
+ require 'dry/core/extensions'
10
11
 
11
12
  require 'dry/types/version'
12
13
  require 'dry/types/container'
13
14
  require 'dry/types/definition'
14
15
  require 'dry/types/constructor'
15
- require 'dry/types/struct'
16
- require 'dry/types/value'
17
16
 
18
17
  require 'dry/types/errors'
19
18
 
20
19
  module Dry
21
20
  module Types
22
21
  extend Dry::Configurable
23
-
24
- Undefined = Object.new.freeze
22
+ extend Dry::Core::Extensions
25
23
 
26
24
  setting :namespace, self
27
25
 
@@ -67,7 +65,13 @@ module Dry
67
65
  container[name]
68
66
  end
69
67
  when Class
70
- self[identifier(name)]
68
+ type_name = identifier(name)
69
+
70
+ if container.key?(type_name)
71
+ self[type_name]
72
+ else
73
+ name
74
+ end
71
75
  end
72
76
  end
73
77
  end
@@ -102,3 +106,4 @@ module Dry
102
106
  end
103
107
 
104
108
  require 'dry/types/core' # load built-in types
109
+ require 'dry/types/extensions'
@@ -14,6 +14,10 @@ module Dry
14
14
  end
15
15
  alias_method :[], :call
16
16
 
17
+ def valid?(type)
18
+ super && type.all? { |el| member.valid?(el) }
19
+ end
20
+
17
21
  def try(input, &block)
18
22
  if input.is_a?(::Array)
19
23
  result = call(input, :try)
@@ -1,12 +1,16 @@
1
+ require 'dry/core/constants'
2
+
1
3
  module Dry
2
4
  module Types
3
5
  module Builder
6
+ include Dry::Core::Constants
7
+
4
8
  def constrained_type
5
9
  Constrained
6
10
  end
7
11
 
8
12
  def |(other)
9
- klass = is_a?(Constrained) && other.is_a?(Constrained) ? Sum::Constrained : Sum
13
+ klass = constrained? && other.constrained? ? Sum::Constrained : Sum
10
14
  klass.new(self, other)
11
15
  end
12
16
 
@@ -14,10 +18,6 @@ module Dry
14
18
  Types['strict.nil'] | self
15
19
  end
16
20
 
17
- def maybe
18
- Maybe.new(Types['strict.nil'] | self)
19
- end
20
-
21
21
  def constrained(options)
22
22
  constrained_type.new(self, rule: Types.Rule(options))
23
23
  end
@@ -28,7 +28,7 @@ module Dry
28
28
  if value.is_a?(Proc) || valid?(value)
29
29
  Default[value].new(self, value)
30
30
  else
31
- raise ConstraintError, "default value #{value.inspect} violates constraints"
31
+ raise ConstraintError.new("default value #{value.inspect} violates constraints", value)
32
32
  end
33
33
  end
34
34
 
@@ -50,6 +50,5 @@ end
50
50
  require 'dry/types/default'
51
51
  require 'dry/types/constrained'
52
52
  require 'dry/types/enum'
53
- require 'dry/types/maybe'
54
53
  require 'dry/types/safe'
55
54
  require 'dry/types/sum'
@@ -1,25 +1,30 @@
1
+ require 'dry/core/constants'
2
+
1
3
  module Dry
2
4
  module Types
3
5
  module Coercions
4
- EMPTY_STRING = ''.freeze
6
+ include Dry::Core::Constants
5
7
 
6
8
  def to_nil(input)
7
9
  input unless empty_str?(input)
8
10
  end
9
11
 
10
12
  def to_date(input)
13
+ return input unless input.respond_to?(:to_str)
11
14
  Date.parse(input)
12
15
  rescue ArgumentError
13
16
  input
14
17
  end
15
18
 
16
19
  def to_date_time(input)
20
+ return input unless input.respond_to?(:to_str)
17
21
  DateTime.parse(input)
18
22
  rescue ArgumentError
19
23
  input
20
24
  end
21
25
 
22
26
  def to_time(input)
27
+ return input unless input.respond_to?(:to_str)
23
28
  Time.parse(input)
24
29
  rescue ArgumentError
25
30
  input
@@ -5,18 +5,18 @@ module Dry
5
5
  module Types
6
6
  module Coercions
7
7
  module Form
8
- TRUE_VALUES = %w[1 on On ON t true True TRUE y yes Yes YES].freeze
9
- FALSE_VALUES = %w[0 off Off OFF f false False FALSE n no No NO].freeze
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
10
10
  BOOLEAN_MAP = ::Hash[TRUE_VALUES.product([true]) + FALSE_VALUES.product([false])].freeze
11
11
 
12
12
  extend Coercions
13
13
 
14
14
  def self.to_true(input)
15
- BOOLEAN_MAP.fetch(input, input)
15
+ BOOLEAN_MAP.fetch(input.to_s, input)
16
16
  end
17
17
 
18
18
  def self.to_false(input)
19
- BOOLEAN_MAP.fetch(input, input)
19
+ BOOLEAN_MAP.fetch(input.to_s, input)
20
20
  end
21
21
 
22
22
  def self.to_int(input)
@@ -18,29 +18,34 @@ module Dry
18
18
 
19
19
  def call(input)
20
20
  try(input) do |result|
21
- raise ConstraintError, result
21
+ raise ConstraintError.new(result, input)
22
22
  end.input
23
23
  end
24
24
  alias_method :[], :call
25
25
 
26
26
  def try(input, &block)
27
- validation = rule.(input)
27
+ result = rule.(input)
28
28
 
29
- if validation.success?
29
+ if result.success?
30
30
  type.try(input, &block)
31
31
  else
32
- block ? yield(validation) : validation
32
+ failure = failure(input, result)
33
+ block ? yield(failure) : failure
33
34
  end
34
35
  end
35
36
 
36
37
  def valid?(value)
37
- rule.(value).success?
38
+ rule.(value).success? && type.valid?(value)
38
39
  end
39
40
 
40
41
  def constrained(options)
41
42
  with(rule: rule & Types.Rule(options))
42
43
  end
43
44
 
45
+ def constrained?
46
+ true
47
+ end
48
+
44
49
  private
45
50
 
46
51
  def decorate?(response)
@@ -1,11 +1,12 @@
1
1
  require 'dry/logic/rule_compiler'
2
2
  require 'dry/logic/predicates'
3
+ require 'dry/logic/rule/predicate'
3
4
 
4
5
  module Dry
5
6
  module Types
6
7
  def self.Rule(options)
7
8
  rule_compiler.(
8
- options.map { |key, val| [:val, Logic::Predicates[:"#{key}?"].curry(val).to_ast] }
9
+ options.map { |key, val| Logic::Rule::Predicate.new(Logic::Predicates[:"#{key}?"]).curry(val).to_ast }
9
10
  ).reduce(:and)
10
11
  end
11
12
 
@@ -22,6 +22,8 @@ module Dry
22
22
 
23
23
  ALL_PRIMITIVES = COERCIBLE.merge(NON_COERCIBLE).freeze
24
24
 
25
+ NON_NIL = ALL_PRIMITIVES.reject { |name, _| name == :nil }.freeze
26
+
25
27
  # Register built-in types that are non-coercible through kernel methods
26
28
  ALL_PRIMITIVES.each do |name, primitive|
27
29
  register(name.to_s, Definition[primitive].new(primitive))
@@ -37,20 +39,8 @@ module Dry
37
39
  register("coercible.#{name}", self[name.to_s].constructor(Kernel.method(primitive.name)))
38
40
  end
39
41
 
40
- # Register non-coercible maybe types
41
- ALL_PRIMITIVES.each_key do |name|
42
- next if name == :nil
43
- register("maybe.strict.#{name}", self["strict.#{name}"].maybe)
44
- end
45
-
46
- # Register coercible maybe types
47
- COERCIBLE.each_key do |name|
48
- register("maybe.coercible.#{name}", self["coercible.#{name}"].maybe)
49
- end
50
-
51
42
  # Register non-coercible optional types
52
- ALL_PRIMITIVES.each_key do |name|
53
- next if name == :nil
43
+ NON_NIL.each_key do |name|
54
44
  register("optional.strict.#{name}", self["strict.#{name}"].optional)
55
45
  end
56
46
 
@@ -28,8 +28,8 @@ module Dry
28
28
  type.default?
29
29
  end
30
30
 
31
- def maybe?
32
- type.maybe?
31
+ def constrained?
32
+ type.constrained?
33
33
  end
34
34
 
35
35
  def respond_to_missing?(meth, include_private = false)