definition 0.5.2 → 0.7.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: db6d3a697d9641388ce9045cd7b9504d485e165480339e864dca6fe096289c6f
4
- data.tar.gz: ef5adc969fde0a0ca0a3e3b9a365cbce588379d2e0bd47be36567b2f4568e7e9
3
+ metadata.gz: 9de3bc4f221f0a67beb7c82f3d7bacfa86ab332cfc3ab7efe3f35bd6efeeeaca
4
+ data.tar.gz: a11b488ecb7fc5e3b2956dbb14c533631936b78d613f6f4a68c58e584cc25faa
5
5
  SHA512:
6
- metadata.gz: 800d13aa9d79318e639eaa969ab6d5d5e8737160f0852df873e38e18685ac58b6133f81e8327de2a937e6a9432e8e68ef26bf0b3758fad4a6c757db5b134572d
7
- data.tar.gz: 52082265fba3abe62846ccf4236888c9d5f6f2e04a9e265282da1526572b3e39b5c3bed3e6490fb5e98525165f74caed5064bdbc479053471e913175313ab353
6
+ metadata.gz: cc464d58c56ef306fdc83868be3f67a7151c64e17f9f28d652584c94f73d2ce083308c1fd1c152bc667d57989f0e7fcbaea0e4a89a9aa30e9fb6093bfab18787
7
+ data.tar.gz: cad0aa2a4165e5ea329751dc8ded8a61f943e110a30d930afbcfec3b2ba2ea7686dccbe41ef322906fb56c8ab23df65f83913d794933d29a3f7907ec0d3e23db
@@ -0,0 +1,53 @@
1
+ # Use the latest 2.1 version of CircleCI pipeline process engine.
2
+ # See: https://circleci.com/docs/2.0/configuration-reference
3
+ version: 2.1
4
+
5
+ # Orbs are reusable packages of CircleCI configuration that you may share across projects, enabling you to create encapsulated, parameterized commands, jobs, and executors that can be used across multiple projects.
6
+ # See: https://circleci.com/docs/2.0/orb-intro/
7
+ orbs:
8
+ ruby: circleci/ruby@1.4.0
9
+
10
+ # Define a job to be invoked later in a workflow.
11
+ # See: https://circleci.com/docs/2.0/configuration-reference/#jobs
12
+ jobs:
13
+ test:
14
+ parameters:
15
+ ruby_version:
16
+ type: string
17
+ docker:
18
+ - image: cimg/base:stable
19
+ steps:
20
+ - ruby/install:
21
+ version: << parameters.ruby_version >>
22
+ - checkout
23
+ - run: rm Gemfile.lock
24
+ - run: gem install bundler
25
+ - run: bundle install
26
+ - ruby/rspec-test
27
+ linting:
28
+ docker:
29
+ - image: 'cimg/base:stable'
30
+ steps:
31
+ - checkout
32
+ - ruby/install:
33
+ version: "3.0"
34
+ - ruby/install-deps
35
+ - ruby/rubocop-check:
36
+ format: progress
37
+ label: Inspecting with Rubocop
38
+
39
+ # Invoke jobs via workflows
40
+ # See: https://circleci.com/docs/2.0/configuration-reference/#workflows
41
+ workflows:
42
+ test: # This is the name of the workflow, feel free to change it to better match your workflow.
43
+ # Inside the workflow, you define the jobs you want to run.
44
+ jobs:
45
+ - test:
46
+ matrix:
47
+ parameters:
48
+ ruby_version:
49
+ - "2.6"
50
+ - "2.7"
51
+ - "3.0"
52
+ - "jruby-9.3.3.0"
53
+ - linting
data/.gitignore CHANGED
@@ -1,6 +1,5 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
- /Gemfile.lock
4
3
  /_yardoc/
5
4
  /coverage/
6
5
  /doc/
data/.rubocop.yml CHANGED
@@ -2,7 +2,7 @@ require:
2
2
  - rubocop-rspec
3
3
 
4
4
  AllCops:
5
- TargetRubyVersion: 2.3
5
+ TargetRubyVersion: 2.6
6
6
 
7
7
  Metrics/LineLength:
8
8
  Max: 119
data/Changelog.md CHANGED
@@ -4,6 +4,26 @@ 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
+ ## Unreleased
8
+
9
+ ## [0.7.0] - 2022-02-25
10
+ ### Added
11
+ - Lambda definitions can now be failed with custom error messages
12
+ - Compatibility with Ruby 3.0
13
+ ### Fixed
14
+ - In some cases errors from nested `Keys` definitions inside `Or` definitions got lost when listing the validation errors via the `error_hash` method on the conform result object.
15
+ ### Changed
16
+ - When no sub-definition of an `Or` conforms, then only the errors of the last definition of the `Or` are collected. Previously the errors of all sub-definitions were collected.
17
+ - Translated error messages have been improved to be more suitable to be used as end user error messages
18
+
19
+ ## [0.6.1] - 2021-12-14
20
+ ### Fixed
21
+ - The `Keys` definition crashed with an error if the input was not a Hash
22
+
23
+ ## [0.6.0] - 2020-03-21
24
+ ### Added
25
+ - Added include method to Keys Definition that allows to inline other `Keys` Definitions into each other
26
+
7
27
  ## [0.5.2] - 2019-06-03
8
28
  ### Fixed
9
29
  - added missing require for "pathname"
data/Gemfile.lock ADDED
@@ -0,0 +1,134 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ definition (0.7.0)
5
+ activesupport
6
+ i18n
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (7.0.2.2)
12
+ concurrent-ruby (~> 1.0, >= 1.0.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ approvals (0.0.25)
17
+ nokogiri (~> 1.8)
18
+ thor (~> 1.0)
19
+ ast (2.4.2)
20
+ awesome_print (1.9.2)
21
+ benchmark-ips (2.10.0)
22
+ coderay (1.1.3)
23
+ concurrent-ruby (1.1.9)
24
+ diff-lcs (1.5.0)
25
+ ffi (1.15.5)
26
+ formatador (1.1.0)
27
+ fuubar (2.5.1)
28
+ rspec-core (~> 3.0)
29
+ ruby-progressbar (~> 1.4)
30
+ guard (2.18.0)
31
+ formatador (>= 0.2.4)
32
+ listen (>= 2.7, < 4.0)
33
+ lumberjack (>= 1.0.12, < 2.0)
34
+ nenv (~> 0.1)
35
+ notiffany (~> 0.0)
36
+ pry (>= 0.13.0)
37
+ shellany (~> 0.0)
38
+ thor (>= 0.18.1)
39
+ guard-compat (1.2.1)
40
+ guard-rspec (4.7.3)
41
+ guard (~> 2.1)
42
+ guard-compat (~> 1.1)
43
+ rspec (>= 2.99.0, < 4.0)
44
+ i18n (1.10.0)
45
+ concurrent-ruby (~> 1.0)
46
+ jaro_winkler (1.5.4)
47
+ listen (3.7.1)
48
+ rb-fsevent (~> 0.10, >= 0.10.3)
49
+ rb-inotify (~> 0.9, >= 0.9.10)
50
+ lumberjack (1.2.8)
51
+ method_source (1.0.0)
52
+ minitest (5.15.0)
53
+ nenv (0.3.0)
54
+ nokogiri (1.13.1-x86_64-linux)
55
+ racc (~> 1.4)
56
+ notiffany (0.1.3)
57
+ nenv (~> 0.1)
58
+ shellany (~> 0.0)
59
+ parallel (1.21.0)
60
+ parser (3.1.0.0)
61
+ ast (~> 2.4.1)
62
+ pry (0.14.1)
63
+ coderay (~> 1.1)
64
+ method_source (~> 1.0)
65
+ psych (4.0.3)
66
+ stringio
67
+ racc (1.6.0)
68
+ rainbow (3.1.1)
69
+ rake (13.0.6)
70
+ rb-fsevent (0.11.1)
71
+ rb-inotify (0.10.1)
72
+ ffi (~> 1.0)
73
+ rspec (3.11.0)
74
+ rspec-core (~> 3.11.0)
75
+ rspec-expectations (~> 3.11.0)
76
+ rspec-mocks (~> 3.11.0)
77
+ rspec-core (3.11.0)
78
+ rspec-support (~> 3.11.0)
79
+ rspec-expectations (3.11.0)
80
+ diff-lcs (>= 1.2.0, < 2.0)
81
+ rspec-support (~> 3.11.0)
82
+ rspec-its (1.3.0)
83
+ rspec-core (>= 3.0.0)
84
+ rspec-expectations (>= 3.0.0)
85
+ rspec-mocks (3.11.0)
86
+ diff-lcs (>= 1.2.0, < 2.0)
87
+ rspec-support (~> 3.11.0)
88
+ rspec-support (3.11.0)
89
+ rspec_junit_formatter (0.5.1)
90
+ rspec-core (>= 2, < 4, != 2.12.0)
91
+ rubocop (0.66.0)
92
+ jaro_winkler (~> 1.5.1)
93
+ parallel (~> 1.10)
94
+ parser (>= 2.5, != 2.5.1.1)
95
+ psych (>= 3.1.0)
96
+ rainbow (>= 2.2.2, < 4.0)
97
+ ruby-progressbar (~> 1.7)
98
+ unicode-display_width (>= 1.4.0, < 1.6)
99
+ rubocop-rspec (1.32.0)
100
+ rubocop (>= 0.60.0)
101
+ rubocop_runner (2.2.0)
102
+ ruby-progressbar (1.11.0)
103
+ shellany (0.0.1)
104
+ stringio (3.0.1)
105
+ thor (1.2.1)
106
+ timecop (0.9.4)
107
+ tzinfo (2.0.4)
108
+ concurrent-ruby (~> 1.0)
109
+ unicode-display_width (1.5.0)
110
+
111
+ PLATFORMS
112
+ ruby
113
+ x86_64-linux
114
+
115
+ DEPENDENCIES
116
+ approvals (~> 0.0)
117
+ awesome_print
118
+ benchmark-ips
119
+ definition!
120
+ fuubar
121
+ guard
122
+ guard-rspec
123
+ pry
124
+ rake (~> 13.0)
125
+ rspec (~> 3.0)
126
+ rspec-its (~> 1.2)
127
+ rspec_junit_formatter
128
+ rubocop (= 0.66.0)
129
+ rubocop-rspec (= 1.32.0)
130
+ rubocop_runner
131
+ timecop
132
+
133
+ BUNDLED WITH
134
+ 2.3.7
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Definition
2
2
 
3
- [![Build Status](https://travis-ci.org/Goltergaul/definition.svg?branch=master)][travis]
3
+ [![Build Status](https://circleci.com/gh/Goltergaul/definition.svg?style=svg)][circleci]
4
4
  [![Gem Version](https://badge.fury.io/rb/definition.svg)][rubygems]
5
5
 
6
6
  Simple and composable validation and coercion of data structures. It also includes a ValueObject for convenience.
@@ -255,6 +255,24 @@ either be the original value or any transformed version of it. By not calling
255
255
  The first argument of `Definition.Lambda` is a name you can give this definition.
256
256
  It will only be used in the error message to make it more readable.
257
257
 
258
+ If you want to provide detailed custom error messages you can use `fail_with`:
259
+
260
+ ```ruby
261
+ Definition.Lambda(:password) do |value|
262
+ if !value.match(/[a-z]+/)
263
+ fail_with("must contain at least one lower case letter")
264
+ elsif !value.match(/[A-Z]+/)
265
+ fail_with("must contain at least one upper case letter")
266
+ elsif !value.match(/\d+/)
267
+ fail_with("must contain at least one digit")
268
+ elsif value.size < 6 || value.size > 50
269
+ fail_with("must be between 6 and 50 characters long")
270
+ else
271
+ conform_with(value)
272
+ end
273
+ end
274
+ ```
275
+
258
276
  ### Composing Definitions
259
277
 
260
278
  Definitions are reusable and can be easily composed:
@@ -279,6 +297,35 @@ order = Definition.Keys do
279
297
  end
280
298
  ```
281
299
 
300
+ ### Extending Key definitions with include
301
+
302
+ Besides composing Definitions, you can also include `Keys` Definitions in each
303
+ other. This will basically copy all required and optional keys as well as defaults into the other definition.
304
+
305
+ ```ruby
306
+ address_definition = Definition.Keys do
307
+ required :street, Definition.Type(String)
308
+ required :postal_code, Definition.Type(String)
309
+ required :country_code, Definition.Type(String)
310
+ end
311
+
312
+ user_definition = Definition.Keys do
313
+ required :user, user_definition
314
+
315
+ include address_definition
316
+ end
317
+ ```
318
+ Above Definition will equal the following:
319
+ ```ruby
320
+ user_definition = Definition.Keys do
321
+ required :user, user_definition
322
+
323
+ required :street, Definition.Type(String)
324
+ required :postal_code, Definition.Type(String)
325
+ required :country_code, Definition.Type(String)
326
+ end
327
+ ```
328
+
282
329
  ### Predefined Definitions
283
330
 
284
331
  #### Strings and Arrays
@@ -394,5 +441,5 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
394
441
 
395
442
  Bug reports and pull requests are welcome on GitHub at https://github.com/Goltergaul/definition.
396
443
 
397
- [travis]: https://travis-ci.org/Goltergaul/definition
444
+ [circleci]: https://circleci.com/gh/Goltergaul/definition
398
445
  [rubygems]: https://rubygems.org/gems/definition
@@ -1,22 +1,22 @@
1
1
  en:
2
2
  definition:
3
- max_size: "Value is bigger then %{max_size}"
4
- min_size: "Value is smaller then %{min_size}"
5
- greater_then: "Value must be greater then %{min_value}"
6
- greater_then_equal: "Value must be greater or eqaul to %{min_value}"
7
- less_then: "Value must be less then %{max_value}"
8
- less_then_equal: "Value must be less or eqaul to %{max_value}"
3
+ max_size: "Value is bigger than %{max_size}"
4
+ min_size: "Value is smaller than %{min_size}"
5
+ greater_then: "Value must be greater than %{min_value}"
6
+ greater_then_equal: "Value must be greater or equal to %{min_value}"
7
+ less_then: "Value must be less than %{max_value}"
8
+ less_then_equal: "Value must be less or equal to %{max_value}"
9
9
  equal: "Value must be equal to '%{expected_value}'"
10
10
  empty: "Value must be empty"
11
11
  non_empty: "Value must not be empty"
12
12
  nil: "Value must be nil"
13
+ each: "Is not an Array"
14
+ include: "Does not include %{value}"
13
15
  type: "Value is of wrong type, needs to be a %{class}"
14
16
  enum: "Value is not one of: %{allowed_values}"
15
- each: "Not all values are valid"
16
- and: "Not all definitions conform"
17
- or: "None of the definitions conform"
18
17
  non_empty_string: "Value must be a non empty string"
19
- regex: "Value dos not match regex %{regex}"
18
+ regex: "Value does not match regex %{regex}"
20
19
  keys:
21
- has_extra_key: "Hash has unexpected key %{key}"
22
- has_missing_key: "Hash is missing key %{key}"
20
+ has_extra_key: "Is unexpected"
21
+ has_missing_key: "Is missing"
22
+ not_a_hash: "Is not a Hash"
data/definition.gemspec CHANGED
@@ -31,9 +31,10 @@ Gem::Specification.new do |spec|
31
31
  spec.add_development_dependency "guard"
32
32
  spec.add_development_dependency "guard-rspec"
33
33
  spec.add_development_dependency "pry"
34
- spec.add_development_dependency "rake", "~> 10.0"
34
+ spec.add_development_dependency "rake", "~> 13.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 "rspec_junit_formatter"
37
38
  spec.add_development_dependency "rubocop", "0.66.0"
38
39
  spec.add_development_dependency "rubocop-rspec", "1.32.0"
39
40
  spec.add_development_dependency "rubocop_runner"
@@ -4,16 +4,18 @@ require "i18n"
4
4
 
5
5
  module Definition
6
6
  class ConformError
7
- def initialize(definition, message, sub_errors: [], i18n_key: definition.name)
7
+ def initialize(definition, message, sub_errors: [], **options)
8
8
  self.definition = definition
9
9
  self.message = message
10
10
  self.sub_errors = sub_errors
11
- self.i18n_key = i18n_key
11
+ self.i18n_key = options.fetch(:i18n_key, definition.name)
12
+ self.i18n_context = options.fetch(:i18n_context, {})
13
+ self.translated_error = options.fetch(:translated_message, nil)
12
14
  assign_parents
13
15
  end
14
16
 
15
- attr_accessor :definition, :sub_errors, :parent, :i18n_key
16
- attr_writer :message
17
+ attr_accessor :definition, :sub_errors, :parent, :i18n_key, :i18n_context
18
+ attr_writer :message, :translated_error
17
19
 
18
20
  def message
19
21
  if sub_errors.empty?
@@ -50,10 +52,8 @@ module Definition
50
52
  sub_errors.map(&:leaf_errors).flatten
51
53
  end
52
54
 
53
- def translated_error(namespace = "definition", vars: {})
54
- namespace ||= "definition"
55
- vars[:key] = key if respond_to?(:key)
56
- ::I18n.t("#{namespace}.#{i18n_key}", definition.context.merge!(vars))
55
+ def translated_error(namespace = "definition")
56
+ @translated_error ||= definition.error_renderer.new(self, i18n_namespace: namespace).translated_error
57
57
  end
58
58
 
59
59
  private
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "active_support/core_ext/hash"
3
+ require "active_support"
4
4
 
5
5
  module Definition
6
6
  class ConformResult
@@ -41,11 +41,9 @@ module Definition
41
41
  "a primitive that has a coercion function defined")
42
42
  end
43
43
  Types::Type.new(:type, klass) do |value|
44
- begin
45
- method(klass.name).call(value)
46
- rescue ArgumentError
47
- value
48
- end
44
+ method(klass.name).call(value)
45
+ rescue ArgumentError
46
+ value
49
47
  end
50
48
  end
51
49
 
@@ -4,9 +4,9 @@ require "definition/conform_error"
4
4
 
5
5
  module Definition
6
6
  class KeyConformError < ConformError
7
- def initialize(definition, message, key:, sub_errors: [], i18n_key: definition.name)
7
+ def initialize(definition, message, key:, sub_errors: [], **options)
8
8
  self.key = key
9
- super(definition, message, sub_errors: sub_errors, i18n_key: i18n_key)
9
+ super(definition, message, sub_errors: sub_errors, **options)
10
10
  end
11
11
 
12
12
  attr_accessor :key
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "definition/types/base"
4
+ require "definition/types/error_renderers/leaf"
4
5
 
5
6
  module Definition
6
7
  module Types
@@ -23,6 +24,10 @@ module Definition
23
24
  Conformer.new(self).conform(value)
24
25
  end
25
26
 
27
+ def error_renderer
28
+ ErrorRenderers::Leaf
29
+ end
30
+
26
31
  class Conformer
27
32
  def initialize(definition)
28
33
  self.definition = definition
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "definition/conform_result"
4
4
  require "definition/conform_error"
5
+ require "definition/types/error_renderers/standard"
5
6
 
6
7
  module Definition
7
8
  module Types
@@ -23,6 +24,10 @@ module Definition
23
24
  def conform(_value)
24
25
  raise NotImplementedError
25
26
  end
27
+
28
+ def error_renderer
29
+ ErrorRenderers::Standard
30
+ end
26
31
  end
27
32
  end
28
33
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "definition/types/base"
4
+ require "definition/types/error_renderers/leaf"
4
5
 
5
6
  module Definition
6
7
  module Types
@@ -16,6 +17,10 @@ module Definition
16
17
  Conformer.new(self).conform(value)
17
18
  end
18
19
 
20
+ def error_renderer
21
+ ErrorRenderers::Leaf
22
+ end
23
+
19
24
  class Conformer
20
25
  def initialize(definition)
21
26
  self.definition = definition
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "definition/types/error_renderers/standard"
4
+
5
+ module Definition
6
+ module Types
7
+ module ErrorRenderers
8
+ class Lambda < Standard
9
+ def default
10
+ "Did not pass test for '#{conform_error.definition.name}'"
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "definition/types/error_renderers/standard"
4
+
5
+ module Definition
6
+ module Types
7
+ module ErrorRenderers
8
+ class Leaf < Standard
9
+ def translated_error(_namespace = "definition")
10
+ # When there are no sub errors, proceeding gets us into an infinite loop.
11
+ return i18n_error if conform_error.sub_errors.empty?
12
+
13
+ conform_error.leaf_errors.map(&:translated_error).join(", ")
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Definition
4
+ module Types
5
+ module ErrorRenderers
6
+ class Standard
7
+ def initialize(conform_error, i18n_namespace:)
8
+ self.conform_error = conform_error
9
+ self.i18n_namespace = i18n_namespace
10
+ end
11
+
12
+ def translated_error
13
+ i18n_error
14
+ end
15
+
16
+ private
17
+
18
+ def i18n_error
19
+ ::I18n.t("#{i18n_namespace}.#{conform_error.i18n_key}",
20
+ **i18n_vars)
21
+ end
22
+
23
+ def i18n_vars
24
+ conform_error.definition.context.merge(
25
+ conform_error.i18n_context
26
+ ).tap do |vars|
27
+ vars[:default] = default if default
28
+ end
29
+ end
30
+
31
+ def default
32
+ nil
33
+ end
34
+
35
+ attr_accessor :conform_error, :i18n_namespace
36
+ end
37
+ end
38
+ end
39
+ end
@@ -38,7 +38,8 @@ module Definition
38
38
  definition.required_items.map do |item|
39
39
  next if value.include?(item)
40
40
 
41
- KeyConformError.new(definition, "#{definition.name} does not include #{item.inspect}", key: item)
41
+ KeyConformError.new(definition, "#{definition.name} does not include #{item.inspect}",
42
+ key: item, i18n_context: { value: item.inspect })
42
43
  end.compact
43
44
  end
44
45
 
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # frozen_string_literal: true
4
-
5
3
  require "definition/types/base"
6
4
  require "definition/types/include"
7
5
  require "definition/key_conform_error"
@@ -19,10 +17,6 @@ module Definition
19
17
  default(key, opts[:default]) if opts.key?(:default)
20
18
  end
21
19
 
22
- def default(key, value)
23
- defaults[key] = value
24
- end
25
-
26
20
  def option(option_name)
27
21
  case option_name
28
22
  when :ignore_extra_keys
@@ -31,6 +25,36 @@ module Definition
31
25
  raise "Option #{option_name} is not defined"
32
26
  end
33
27
  end
28
+
29
+ def include(other)
30
+ raise ArgumentError.new("Included Definition can only be a Keys Definition") unless other.is_a?(Types::Keys)
31
+
32
+ ensure_keys_do_not_interfere(other)
33
+ other.required_definitions.each do |key, definition|
34
+ required(key, definition)
35
+ end
36
+ other.optional_definitions.each do |key, definition|
37
+ optional(key, definition)
38
+ end
39
+ other.defaults.each do |key, default|
40
+ default(key, default)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def default(key, value)
47
+ defaults[key] = value
48
+ end
49
+
50
+ def ensure_keys_do_not_interfere(other)
51
+ overlapping_keys = keys & other.keys
52
+ return if overlapping_keys.empty?
53
+
54
+ raise ArgumentError.new(
55
+ "Included definition tries to redefine already defined fields: #{overlapping_keys.join(', ')}"
56
+ )
57
+ end
34
58
  end
35
59
 
36
60
  include Dsl
@@ -60,9 +84,15 @@ module Definition
60
84
  end
61
85
 
62
86
  def conform
63
- add_extra_key_errors unless definition.ignore_extra_keys
64
- add_missing_key_errors
65
- values = conform_all_keys
87
+ if valid_input_type?
88
+ add_extra_key_errors unless definition.ignore_extra_keys
89
+ add_missing_key_errors
90
+ values = conform_all_keys
91
+ else
92
+ errors.push(ConformError.new(definition,
93
+ "#{definition.name} is not a Hash",
94
+ i18n_key: "keys.not_a_hash"))
95
+ end
66
96
 
67
97
  ConformResult.new(values, errors: errors)
68
98
  end
@@ -71,6 +101,10 @@ module Definition
71
101
 
72
102
  attr_accessor :errors
73
103
 
104
+ def valid_input_type?
105
+ value.is_a?(Hash)
106
+ end
107
+
74
108
  def add_extra_key_errors
75
109
  extra_keys = value.keys - all_keys
76
110
  return if extra_keys.empty?
@@ -1,34 +1,65 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "definition/types/base"
4
+ require "definition/types/error_renderers/lambda"
4
5
 
5
6
  module Definition
6
7
  module Types
7
8
  class Lambda < Base
8
- module Dsl
9
- def conform_with(value)
10
- ConformResult.new(value)
11
- end
12
- end
13
- include Dsl
9
+ attr_accessor :conformity_test_lambda
14
10
 
15
- def initialize(name, context: {}, &test_lambda)
16
- self.test_lambda = test_lambda
11
+ def initialize(name, context: {}, &conformity_test_lambda)
12
+ self.conformity_test_lambda = conformity_test_lambda
17
13
  super(name, context: context)
18
14
  end
19
15
 
20
16
  def conform(value)
21
- lambda_result = instance_exec(value, &test_lambda)
22
- return lambda_result if lambda_result.is_a?(ConformResult)
17
+ Conformer.new(self).conform(value)
18
+ end
23
19
 
24
- ConformResult.new(value, errors: [
25
- ConformError.new(self, "Did not pass test for #{name}")
26
- ])
20
+ def error_renderer
21
+ ErrorRenderers::Lambda
27
22
  end
28
23
 
29
- private
24
+ class Conformer
25
+ module Dsl
26
+ def conform_with(value)
27
+ ConformResult.new(value)
28
+ end
29
+
30
+ def fail_with(error_message)
31
+ self.error_message = error_message
32
+ end
33
+ end
34
+ include Dsl
35
+
36
+ def initialize(definition)
37
+ self.definition = definition
38
+ end
39
+
40
+ def conform(value)
41
+ lambda_result = instance_exec(value, &definition.conformity_test_lambda)
42
+ return lambda_result if lambda_result.is_a?(ConformResult)
30
43
 
31
- attr_accessor :test, :test_lambda
44
+ failure_result_with(value, error_message)
45
+ end
46
+
47
+ private
48
+
49
+ attr_accessor :definition, :error_message
50
+
51
+ def standard_error_message
52
+ "Did not pass test for #{definition.name}"
53
+ end
54
+
55
+ def failure_result_with(value, error_message)
56
+ ConformResult.new(value, errors: [
57
+ ConformError.new(definition,
58
+ standard_error_message,
59
+ translated_message: error_message)
60
+ ])
61
+ end
62
+ end
32
63
  end
33
64
  end
34
65
  end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "definition/types/base"
4
+ require "definition/types/error_renderers/leaf"
4
5
 
5
6
  module Definition
6
7
  module Types
@@ -23,6 +24,10 @@ module Definition
23
24
  Conformer.new(self).conform(value)
24
25
  end
25
26
 
27
+ def error_renderer
28
+ ErrorRenderers::Leaf
29
+ end
30
+
26
31
  class Conformer
27
32
  def initialize(definition)
28
33
  self.definition = definition
@@ -34,7 +39,8 @@ module Definition
34
39
  result
35
40
  else
36
41
  error = ConformError.new(definition,
37
- "None of the definitions are valid for '#{definition.name}'",
42
+ "None of the definitions are valid for '#{definition.name}'."\
43
+ " Errors for last tested definition:",
38
44
  sub_errors: result)
39
45
  ConformResult.new(value, errors: [error])
40
46
  end
@@ -48,7 +54,7 @@ module Definition
48
54
  result = definition.conform(value)
49
55
  return result if result.passed?
50
56
 
51
- errors.push(result.error_tree)
57
+ errors = result.error_tree
52
58
  end
53
59
 
54
60
  errors.flatten
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Definition
4
- VERSION = "0.5.2"
4
+ VERSION = "0.7.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.5.2
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dominik Goltermann
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2019-06-12 00:00:00.000000000 Z
11
+ date: 2022-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -142,14 +142,14 @@ dependencies:
142
142
  requirements:
143
143
  - - "~>"
144
144
  - !ruby/object:Gem::Version
145
- version: '10.0'
145
+ version: '13.0'
146
146
  type: :development
147
147
  prerelease: false
148
148
  version_requirements: !ruby/object:Gem::Requirement
149
149
  requirements:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
- version: '10.0'
152
+ version: '13.0'
153
153
  - !ruby/object:Gem::Dependency
154
154
  name: rspec
155
155
  requirement: !ruby/object:Gem::Requirement
@@ -178,6 +178,20 @@ dependencies:
178
178
  - - "~>"
179
179
  - !ruby/object:Gem::Version
180
180
  version: '1.2'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rspec_junit_formatter
183
+ requirement: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ type: :development
189
+ prerelease: false
190
+ version_requirements: !ruby/object:Gem::Requirement
191
+ requirements:
192
+ - - ">="
193
+ - !ruby/object:Gem::Version
194
+ version: '0'
181
195
  - !ruby/object:Gem::Dependency
182
196
  name: rubocop
183
197
  requirement: !ruby/object:Gem::Requirement
@@ -234,7 +248,7 @@ dependencies:
234
248
  - - ">="
235
249
  - !ruby/object:Gem::Version
236
250
  version: '0'
237
- description:
251
+ description:
238
252
  email:
239
253
  - dominik@goltermann.cc
240
254
  executables: []
@@ -242,12 +256,13 @@ extensions: []
242
256
  extra_rdoc_files: []
243
257
  files:
244
258
  - ".approvals"
259
+ - ".circleci/config.yml"
245
260
  - ".gitignore"
246
261
  - ".rspec"
247
262
  - ".rubocop.yml"
248
- - ".travis.yml"
249
263
  - Changelog.md
250
264
  - Gemfile
265
+ - Gemfile.lock
251
266
  - Guardfile
252
267
  - LICENSE
253
268
  - README.md
@@ -271,6 +286,9 @@ files:
271
286
  - lib/definition/types/and.rb
272
287
  - lib/definition/types/base.rb
273
288
  - lib/definition/types/each.rb
289
+ - lib/definition/types/error_renderers/lambda.rb
290
+ - lib/definition/types/error_renderers/leaf.rb
291
+ - lib/definition/types/error_renderers/standard.rb
274
292
  - lib/definition/types/include.rb
275
293
  - lib/definition/types/keys.rb
276
294
  - lib/definition/types/lambda.rb
@@ -283,7 +301,7 @@ homepage: https://github.com/Goltergaul/definition
283
301
  licenses:
284
302
  - MIT
285
303
  metadata: {}
286
- post_install_message:
304
+ post_install_message:
287
305
  rdoc_options: []
288
306
  require_paths:
289
307
  - lib
@@ -298,9 +316,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
298
316
  - !ruby/object:Gem::Version
299
317
  version: '0'
300
318
  requirements: []
301
- rubyforge_project:
302
- rubygems_version: 2.7.6
303
- signing_key:
319
+ rubygems_version: 3.1.2
320
+ signing_key:
304
321
  specification_version: 4
305
322
  summary: Simple and composable validation and coercion of data structures inspired
306
323
  by clojure specs
data/.travis.yml DELETED
@@ -1,21 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - jruby-9.1.17.0 # ruby 2.3
4
- - jruby-9.2.0.0 # ruby 2.5
5
- - jruby-head
6
- - 2.3.0
7
- - 2.4.0
8
- - 2.5.0
9
- - ruby-head
10
- jobs:
11
- include:
12
- - stage: linting
13
- rvm: ruby-head
14
- script: bundle exec rake rubocop
15
- - stage: benchmark
16
- script: bundle exec ruby benchmark/complex_example.rb
17
- rvm: ruby-head
18
- - script: bundle exec ruby benchmark/coercion.rb
19
- rvm: ruby-head
20
- - script: bundle exec ruby benchmark/validation_only.rb
21
- rvm: ruby-head