dry-types 0.8.1 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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)