definition 0.3.0 → 0.4.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
  SHA256:
3
- metadata.gz: e3f3d334a06ce14e9f9f6eecccbc59206c11a293cd66b06cb8d20661698fb198
4
- data.tar.gz: 59df3ceb4ff7643eea3eac301ad735969fafe31e7cbd6de89227fa4383beb348
3
+ metadata.gz: 6b8b872d14fc97965062a65fcc1d75987cfd7b21c448fe4f61dfda1f11fd003b
4
+ data.tar.gz: 3195829dcc11b18abe5fa12826cefb2d16f70ce8d2bb127f715dc4ee6193b181
5
5
  SHA512:
6
- metadata.gz: 9e41d56a6e2eba1cc0e11adda3b69de22ce94d5de3d6ce01973c48548908745a8eb88329aa9b3f09d57f54231fc87a8eca0e614635902e1a9bb79809eed29816
7
- data.tar.gz: b475ec666afc58aa64ac440be548b9c475c55ec4598ba7de97d9d413d9d11f12492a96736c6cb639d6bf65fdb7cf04c9197c67b0d2ca36bf4cfac7794ad09eba
6
+ metadata.gz: 4861a2c4f8ee02c9706546b24dbf9040edc02e6ba8d05f6038529f7132d72d35403de1e079d46d40bb5b00e1d5e65fa2be93781cff4e920da58f5e835f84f242
7
+ data.tar.gz: 53c7365d464f2fed498ebb405dac60bffd5d960dd643485ff571c06c3bb76ca593bd7a2758295011e27f1c35cc38c7da687935f280fad03704791d5b412387ed
@@ -24,10 +24,6 @@ Metrics/MethodLength:
24
24
  Style/Documentation:
25
25
  Enabled: false
26
26
 
27
- Style/AlignHash:
28
- EnforcedColonStyle: table
29
- EnforcedHashRocketStyle: table
30
-
31
27
  Layout/AlignHash:
32
28
  EnforcedColonStyle: table
33
29
  EnforcedHashRocketStyle: table
@@ -35,10 +31,6 @@ Layout/AlignHash:
35
31
  Style/HashSyntax:
36
32
  EnforcedStyle: ruby19_no_mixed_keys
37
33
 
38
- Style/MultilineOperationIndentation:
39
- Description: Checks indentation of binary operations that span more than one line.
40
- EnforcedStyle: indented
41
-
42
34
  Layout/MultilineOperationIndentation:
43
35
  Description: Checks indentation of binary operations that span more than one line.
44
36
  EnforcedStyle: indented
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
4
4
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## [0.4.0] - 2019-03-22
8
+ ### Added
9
+ - Added support for default values to Keys Definition
10
+ ### Changed
11
+ - Errors returned from ConformResult were restructured for better debugging and usage
12
+
7
13
  ## [0.3.0] - 2019-02-03
8
14
  ### Added
9
15
  - Added I18n translation support for errors
data/README.md CHANGED
@@ -41,7 +41,9 @@ conform_result.error_message # => hash fails validation for key birthday: { Is o
41
41
  conform_result.error_hash # =>
42
42
  # {
43
43
  # :birthday => [
44
- # [0] "Value is of wrong type, needs to be a Date"
44
+ # [0] <Definition::ConformError
45
+ # desciption: "hash fails validation for key birthday: { Is of type String instead of Date }",
46
+ # json_pointer: "/birthday">
45
47
  # ]
46
48
  # }
47
49
  ```
@@ -107,6 +109,8 @@ array.first # => 1
107
109
  IntegerArray.new([1,2,"3"]) # => Definition::InvalidValueObjectError: Not all items conform with each: { Item "3" did not conform to each: { Is of type String instead of Integer } }
108
110
  ```
109
111
 
112
+ You can access the conform result object via `InvalidValueObjectError#conform_result`
113
+
110
114
  ### Conforming Hashes
111
115
 
112
116
  Hashes can be conformed by using the `Keys` definition. It allows you to configure
@@ -116,10 +120,13 @@ needs to have a Symbol key with that name, otherwise a string key.
116
120
 
117
121
  The key definition will also fail if the input value contains extra keys.
118
122
 
123
+ You can configure default values for optional keys, see the following example.
124
+
119
125
  ```ruby
120
126
  Definition.Keys do
121
- required :title, Definition.Type(String)
127
+ required :title, Definition.NonEmptyString
122
128
  optional :publication_date, Definition.Type(Date)
129
+ optional :is_draft, Definition.Boolean, default: true
123
130
  end
124
131
  ```
125
132
 
@@ -301,11 +308,9 @@ Every error object has a method `translated_error` that will give you a translat
301
308
  version of the error message. You can load the default English translations shipped
302
309
  with the gem by adding them to your I18n load path.
303
310
 
304
- If you use the `error_hash` method on a `ConformResult` you also get the translated
305
- error messages
306
311
 
307
312
  ```ruby
308
- chema = Definition.Keys do
313
+ schema = Definition.Keys do
309
314
  required :title, Definition.Type(String)
310
315
  required :body, Definition.Type(String)
311
316
  required(:author, Definition.Keys do
@@ -313,14 +318,7 @@ chema = Definition.Keys do
313
318
  required :email, Definition.Type(String)
314
319
  end)
315
320
  end
316
- result.error_hash # =>
317
- # {
318
- # :author => {
319
- # :email => [
320
- # [0] "Value is of wrong type, needs to be a String"
321
- # ]
322
- # }
323
- # }
321
+ schema.conform(input_hash).errors.first.translated_error # => Value is of wrong type, needs to be a String"
324
322
  ```
325
323
 
326
324
  ## Development
@@ -4,7 +4,9 @@ require "bundler/inline"
4
4
 
5
5
  gemfile do
6
6
  source "https://rubygems.org"
7
- gem "dry-validation"
7
+ # FIXME: benchmark needs to be updated to work with dry-validation 1.0.0"
8
+ gem "dry-validation", "< 1.0.0"
9
+ gem "dry-types", "< 0.15"
8
10
  gem "awesome_print"
9
11
  gem "benchmark-ips"
10
12
  gem "pry"
@@ -4,7 +4,9 @@ require "bundler/inline"
4
4
 
5
5
  gemfile do
6
6
  source "https://rubygems.org"
7
- gem "dry-validation"
7
+ # FIXME: benchmark needs to be updated to work with dry-validation 1.0.0"
8
+ gem "dry-validation", "< 1.0.0"
9
+ gem "dry-types", "< 0.15"
8
10
  gem "awesome_print"
9
11
  gem "benchmark-ips"
10
12
  gem "pry"
@@ -4,7 +4,9 @@ require "bundler/inline"
4
4
 
5
5
  gemfile do
6
6
  source "https://rubygems.org"
7
- gem "dry-validation"
7
+ # FIXME: benchmark needs to be updated to work with dry-validation 1.0.0"
8
+ gem "dry-validation", "< 1.0.0"
9
+ gem "dry-types", "< 0.15"
8
10
  gem "awesome_print"
9
11
  gem "benchmark-ips"
10
12
  gem "definition", path: File.expand_path("../.", __dir__)
@@ -34,8 +34,8 @@ Gem::Specification.new do |spec|
34
34
  spec.add_development_dependency "rake", "~> 10.0"
35
35
  spec.add_development_dependency "rspec", "~> 3.0"
36
36
  spec.add_development_dependency "rspec-its", "~> 1.2"
37
- spec.add_development_dependency "rubocop"
38
- spec.add_development_dependency "rubocop-rspec"
37
+ spec.add_development_dependency "rubocop", "0.66.0"
38
+ spec.add_development_dependency "rubocop-rspec", "1.32.0"
39
39
  spec.add_development_dependency "rubocop_runner"
40
40
  spec.add_development_dependency "timecop"
41
41
  end
@@ -23,6 +23,12 @@ module Definition
23
23
  end
24
24
  end
25
25
 
26
+ def to_s
27
+ "<Definition::ConformError \n\t desciption: \"#{message}\", \n\t json_pointer: \"#{json_pointer}\">"
28
+ end
29
+
30
+ alias inspect to_s
31
+
26
32
  def error_path
27
33
  current = self
28
34
  path = current.is_a?(KeyConformError) ? [key] : []
@@ -16,31 +16,27 @@ module Definition
16
16
  end
17
17
  alias conformed? passed?
18
18
 
19
- def descriptive_errors
20
- errors.map do |e|
21
- {
22
- description: e.message,
23
- pointer: e.json_pointer
24
- }
25
- end
26
- end
27
-
28
19
  def error_message
29
20
  error_tree.map(&:message).join(", ")
30
21
  end
31
22
 
32
- def errors
23
+ def leaf_errors
33
24
  conform_errors.map(&:leaf_errors).flatten
34
25
  end
35
26
 
36
- def error_hash(i18n_namespace: nil)
27
+ def errors
28
+ leaf_errors.map do |error|
29
+ find_next_parent_key_error(error) || error
30
+ end.compact.uniq
31
+ end
32
+
33
+ def error_hash
37
34
  {}.tap do |error_hash|
38
35
  errors.each do |error|
39
36
  next if error.error_path.empty?
40
37
 
41
- message = error.translated_error(i18n_namespace)
42
38
  path_hash = error.error_path.reverse
43
- .inject([message]) { |messages, key| { key => messages } }
39
+ .inject([error]) { |errors, key| { key => errors } }
44
40
 
45
41
  error_hash.deep_merge!(path_hash) do |_key, old, new|
46
42
  old + new if old.is_a?(Array) && new.is_a?(Array) # concat arrays during deep_merge
@@ -55,6 +51,17 @@ module Definition
55
51
 
56
52
  private
57
53
 
54
+ def find_next_parent_key_error(error)
55
+ current = error
56
+ loop do
57
+ return current if current.is_a?(KeyConformError)
58
+
59
+ current = current.parent
60
+ break unless current
61
+ end
62
+ nil
63
+ end
64
+
58
65
  attr_accessor :conform_errors
59
66
  end
60
67
  end
@@ -9,7 +9,7 @@ module Definition
9
9
  # required :name, Types::Type(String)
10
10
  # optional :age, Types::Type(Integer)
11
11
  # end
12
- def Keys(&block) # rubocop:disable Naming/MethodName
12
+ def Keys(&block) # rubocop:disable Style/MethodName
13
13
  Types::Keys.new(:hash).tap do |instance|
14
14
  instance.instance_exec(&block)
15
15
  end
@@ -17,25 +17,25 @@ module Definition
17
17
 
18
18
  # Example:
19
19
  # And(Types::Type(Float), Types::GreaterThen(10.0))
20
- def And(*definitions) # rubocop:disable Naming/MethodName
20
+ def And(*definitions) # rubocop:disable Style/MethodName
21
21
  Types::And.new(:and, *definitions)
22
22
  end
23
23
 
24
24
  # Example:
25
25
  # Or(Types::Type(Float), Types::Type(Integer))
26
- def Or(*definitions) # rubocop:disable Naming/MethodName
26
+ def Or(*definitions) # rubocop:disable Style/MethodName
27
27
  Types::Or.new(:or, *definitions)
28
28
  end
29
29
 
30
30
  # Example:
31
31
  # Type(Integer)
32
- def Type(klass) # rubocop:disable Naming/MethodName
32
+ def Type(klass) # rubocop:disable Style/MethodName
33
33
  Types::Type.new(:type, klass)
34
34
  end
35
35
 
36
36
  # Example:
37
37
  # CoercibleType(Integer)
38
- def CoercibleType(klass) # rubocop:disable Naming/MethodName
38
+ def CoercibleType(klass) # rubocop:disable Style/MethodName
39
39
  unless Kernel.respond_to?(klass.name)
40
40
  raise ArgumentError.new("#{klass} can't be used as CoercibleType because its not "\
41
41
  "a primitive that has a coercion function defined")
@@ -53,13 +53,13 @@ module Definition
53
53
  # Lambda(:even) do |value|
54
54
  # value.even?
55
55
  # end
56
- def Lambda(name, context: {}, &block) # rubocop:disable Naming/MethodName
56
+ def Lambda(name, context: {}, &block) # rubocop:disable Style/MethodName
57
57
  Types::Lambda.new(name, context: context, &block)
58
58
  end
59
59
 
60
60
  # Example:
61
61
  # Enum("allowed_value1", "allowed_value2")
62
- def Enum(*allowed_values) # rubocop:disable Naming/MethodName
62
+ def Enum(*allowed_values) # rubocop:disable Style/MethodName
63
63
  Lambda("enum", context: { allowed_values: allowed_values }) do |value|
64
64
  conform_with(value) if allowed_values.include?(value)
65
65
  end
@@ -67,8 +67,14 @@ module Definition
67
67
 
68
68
  # Example:
69
69
  # Each(Definition::Type(Integer))
70
- def Each(definition) # rubocop:disable Naming/MethodName
70
+ def Each(definition) # rubocop:disable Style/MethodName
71
71
  Types::Each.new(:each, definition: definition)
72
72
  end
73
+
74
+ # Example:
75
+ # Boolean
76
+ def Boolean # rubocop:disable Style/MethodName
77
+ Types::Or.new(:boolean, Type(TrueClass), Type(FalseClass))
78
+ end
73
79
  end
74
80
  end
@@ -5,7 +5,7 @@ module Definition
5
5
  module Comparators
6
6
  # Example:
7
7
  # MaxSize(5)
8
- def MaxSize(max_size) # rubocop:disable Naming/MethodName
8
+ def MaxSize(max_size) # rubocop:disable Style/MethodName
9
9
  Types::Lambda.new(:max_size, context: { max_size: max_size }) do |value|
10
10
  case value
11
11
  when String, Enumerable
@@ -18,7 +18,7 @@ module Definition
18
18
 
19
19
  # Example:
20
20
  # MinSize(5)
21
- def MinSize(min_size) # rubocop:disable Naming/MethodName
21
+ def MinSize(min_size) # rubocop:disable Style/MethodName
22
22
  Types::Lambda.new(:min_size, context: { min_size: min_size }) do |value|
23
23
  case value
24
24
  when String, Enumerable
@@ -31,13 +31,13 @@ module Definition
31
31
 
32
32
  # Example:
33
33
  # NonEmptyString
34
- def NonEmptyString # rubocop:disable Naming/MethodName
34
+ def NonEmptyString # rubocop:disable Style/MethodName
35
35
  Types::And.new(:non_empty_string, Type(String), MinSize(1))
36
36
  end
37
37
 
38
38
  # Example:
39
39
  # GreaterThen(5)
40
- def GreaterThen(min_value) # rubocop:disable Naming/MethodName
40
+ def GreaterThen(min_value) # rubocop:disable Style/MethodName
41
41
  Types::Lambda.new("greater_then", context: { min_value: min_value }) do |value|
42
42
  conform_with(value) if value.is_a?(Numeric) && value > min_value
43
43
  end
@@ -45,7 +45,7 @@ module Definition
45
45
 
46
46
  # Example:
47
47
  # GreaterThenEqual(5)
48
- def GreaterThenEqual(min_value) # rubocop:disable Naming/MethodName
48
+ def GreaterThenEqual(min_value) # rubocop:disable Style/MethodName
49
49
  Types::Lambda.new("greater_then_equal", context: { min_value: min_value }) do |value|
50
50
  conform_with(value) if value.is_a?(Numeric) && value >= min_value
51
51
  end
@@ -53,7 +53,7 @@ module Definition
53
53
 
54
54
  # Example:
55
55
  # LessThen(5)
56
- def LessThen(max_value) # rubocop:disable Naming/MethodName
56
+ def LessThen(max_value) # rubocop:disable Style/MethodName
57
57
  Types::Lambda.new("less_then", context: { max_value: max_value }) do |value|
58
58
  conform_with(value) if value.is_a?(Numeric) && value < max_value
59
59
  end
@@ -61,7 +61,7 @@ module Definition
61
61
 
62
62
  # Example:
63
63
  # LessThenEqual(5)
64
- def LessThenEqual(max_value) # rubocop:disable Naming/MethodName
64
+ def LessThenEqual(max_value) # rubocop:disable Style/MethodName
65
65
  Types::Lambda.new("less_then_equal", context: { max_value: max_value }) do |value|
66
66
  conform_with(value) if value.is_a?(Numeric) && value <= max_value
67
67
  end
@@ -69,7 +69,7 @@ module Definition
69
69
 
70
70
  # Example:
71
71
  # Equal("value")
72
- def Equal(expected_value) # rubocop:disable Naming/MethodName
72
+ def Equal(expected_value) # rubocop:disable Style/MethodName
73
73
  Types::Lambda.new(:equal, context: { expected_value: expected_value }) do |value|
74
74
  conform_with(value) if value == expected_value
75
75
  end
@@ -77,7 +77,7 @@ module Definition
77
77
 
78
78
  # Example:
79
79
  # Empty
80
- def Empty # rubocop:disable Naming/MethodName
80
+ def Empty # rubocop:disable Style/MethodName
81
81
  Types::Lambda.new(:empty) do |value|
82
82
  case value
83
83
  when String, Array, Hash
@@ -90,7 +90,7 @@ module Definition
90
90
 
91
91
  # Example:
92
92
  # NonEmpty
93
- def NonEmpty # rubocop:disable Naming/MethodName
93
+ def NonEmpty # rubocop:disable Style/MethodName
94
94
  Types::Lambda.new(:non_empty) do |value|
95
95
  case value
96
96
  when String, Array, Hash
@@ -103,7 +103,7 @@ module Definition
103
103
 
104
104
  # Example:
105
105
  # Regex
106
- def Regex(regex, name: :regex) # rubocop:disable Naming/MethodName
106
+ def Regex(regex, name: :regex) # rubocop:disable Style/MethodName
107
107
  Types::Lambda.new(name, context: { regex: regex.inspect }) do |value|
108
108
  case value
109
109
  when String
@@ -5,7 +5,7 @@ module Definition
5
5
  module Nil
6
6
  # Example:
7
7
  # Nil
8
- def Nil # rubocop:disable Naming/MethodName
8
+ def Nil # rubocop:disable Style/MethodName
9
9
  Types::Lambda.new(:nil) do |value|
10
10
  conform_with(value) if value.nil?
11
11
  end
@@ -35,7 +35,7 @@ module Definition
35
35
  ConformResult.new(results.last.value)
36
36
  else
37
37
  ConformResult.new(value, errors: [
38
- ConformError.new(definition, "Not all definitions are valid for #{definition.name}",
38
+ ConformError.new(definition, "Not all definitions are valid for '#{definition.name}'",
39
39
  sub_errors: results.map(&:error_tree).flatten)
40
40
  ])
41
41
  end
@@ -30,7 +30,7 @@ module Definition
30
30
  ConformResult.new(results.map(&:value))
31
31
  else
32
32
  ConformResult.new(value, errors: [ConformError.new(definition,
33
- "Not all items conform with #{definition.name}",
33
+ "Not all items conform with '#{definition.name}'",
34
34
  sub_errors: errors(results))])
35
35
  end
36
36
  end
@@ -14,18 +14,24 @@ module Definition
14
14
  required_definitions[key] = definition
15
15
  end
16
16
 
17
- def optional(key, definition)
17
+ def optional(key, definition, **opts)
18
18
  optional_definitions[key] = definition
19
+ default(key, opts[:default]) if opts.key?(:default)
20
+ end
21
+
22
+ def default(key, value)
23
+ defaults[key] = value
19
24
  end
20
25
  end
21
26
 
22
27
  include Dsl
23
- attr_accessor :required_definitions, :optional_definitions
28
+ attr_accessor :required_definitions, :optional_definitions, :defaults
24
29
 
25
- def initialize(name, req: {}, opt: {})
30
+ def initialize(name, req: {}, opt: {}, defaults: {})
26
31
  super(name)
27
32
  self.required_definitions = req
28
33
  self.optional_definitions = opt
34
+ self.defaults = defaults
29
35
  end
30
36
 
31
37
  def conform(value)
@@ -73,7 +79,7 @@ module Definition
73
79
  required_keys_values = conform_definitions(required_definitions)
74
80
  optional_keys_values = conform_definitions(optional_definitions)
75
81
 
76
- required_keys_values.merge!(optional_keys_values)
82
+ definition.defaults.merge(required_keys_values.merge!(optional_keys_values))
77
83
  end
78
84
 
79
85
  def all_keys
@@ -34,7 +34,7 @@ module Definition
34
34
  result
35
35
  else
36
36
  error = ConformError.new(definition,
37
- "None of the definitions are valid for #{definition.name}",
37
+ "None of the definitions are valid for '#{definition.name}'",
38
38
  sub_errors: result)
39
39
  ConformResult.new(value, errors: [error])
40
40
  end
@@ -1,13 +1,20 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Definition
4
- class InvalidValueObjectError < StandardError; end
4
+ class InvalidValueObjectError < StandardError
5
+ attr_accessor :conform_result
6
+
7
+ def initialize(conform_result)
8
+ super(conform_result.error_message)
9
+ self.conform_result = conform_result
10
+ end
11
+ end
5
12
  class NotConfiguredError < StandardError; end
6
13
 
7
14
  class ValueObject < SimpleDelegator
8
15
  def initialize(args = nil, **kwargs)
9
16
  result = self.class.conform(args || kwargs)
10
- raise InvalidValueObjectError.new(result.error_message) unless result.passed?
17
+ raise InvalidValueObjectError.new(result) unless result.passed?
11
18
 
12
19
  super(result.value.freeze)
13
20
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Definition
4
- VERSION = "0.3.0"
4
+ VERSION = "0.4.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: definition
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Goltermann
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-02-03 00:00:00.000000000 Z
11
+ date: 2019-03-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -182,30 +182,30 @@ dependencies:
182
182
  name: rubocop
183
183
  requirement: !ruby/object:Gem::Requirement
184
184
  requirements:
185
- - - ">="
185
+ - - '='
186
186
  - !ruby/object:Gem::Version
187
- version: '0'
187
+ version: 0.66.0
188
188
  type: :development
189
189
  prerelease: false
190
190
  version_requirements: !ruby/object:Gem::Requirement
191
191
  requirements:
192
- - - ">="
192
+ - - '='
193
193
  - !ruby/object:Gem::Version
194
- version: '0'
194
+ version: 0.66.0
195
195
  - !ruby/object:Gem::Dependency
196
196
  name: rubocop-rspec
197
197
  requirement: !ruby/object:Gem::Requirement
198
198
  requirements:
199
- - - ">="
199
+ - - '='
200
200
  - !ruby/object:Gem::Version
201
- version: '0'
201
+ version: 1.32.0
202
202
  type: :development
203
203
  prerelease: false
204
204
  version_requirements: !ruby/object:Gem::Requirement
205
205
  requirements:
206
- - - ">="
206
+ - - '='
207
207
  - !ruby/object:Gem::Version
208
- version: '0'
208
+ version: 1.32.0
209
209
  - !ruby/object:Gem::Dependency
210
210
  name: rubocop_runner
211
211
  requirement: !ruby/object:Gem::Requirement