dry-validation 0.13.2 → 0.13.3

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
  SHA256:
3
- metadata.gz: 1a31ac4653e7f148d6ee09543b38c81f9670bc40c51c8644414aacc000e66357
4
- data.tar.gz: 2c84a8c6b29bb290f3e24505a9f52c1abcc31c777b54ed4140bc38225e5410d2
3
+ metadata.gz: d569600e476b1c0830184bea7e9393f333693e11c1a9d8b074e2b2329deeba7a
4
+ data.tar.gz: 4811db9bc4e1cc3f398df8717333683355779116a72314532b10bc2d49a87333
5
5
  SHA512:
6
- metadata.gz: afb6b1de3fea9028ec2d4ffd481b9a49a79f2a9192c0101122a00c4b567d99e5d53083c130cc1789d643b9c80a5b7c98384e776d36c746a3ca1ff83ea80b1caf
7
- data.tar.gz: d287f0d13f12c65a476de805e6fd95175eea35da74b679dd0fcb98e8ac616e000cf465db769b2e53e5db2e082173c30a3c8c16cffe0537517e96dbd499e8d004
6
+ metadata.gz: c497db70c2bf78e3355c685176361b06357ee9d9f17a2c867127d4d575865f0e6cb1f5bafae4b595e1db3af8748fc6122e6522177a152e17e8e6119006591b3c
7
+ data.tar.gz: 2b93c44b453e47f6a68ec05bedaec9d2f48d001a6dc9d0f62f2f2d7b70de5f8c1a7130bfc33c558fff1129fceb69d37f33f341d38c96549570a684d7279d24b0
@@ -1,3 +1,16 @@
1
+ # v0.13.3 2019-05-21
2
+
3
+ ### Fixed
4
+
5
+ * Fixed regression with caching templates that rely on predicate arguments (solnic)
6
+ * Removed ugly workaround for template evaluation with extra tokens (solnic)
7
+
8
+ ### Changed
9
+
10
+ * [internal] `Messages::Abstract#call` returns a `Template` object now, this was backported from dry-schema (solnic)
11
+
12
+ [Compare v0.13.2...v0.13.3](https://github.com/dry-rb/dry-validation/compare/v0.13.2...v0.13.3)
13
+
1
14
  # v0.13.2 2019-05-12
2
15
 
3
16
  ### Fixed
@@ -32,7 +32,7 @@ module Dry
32
32
  end
33
33
 
34
34
  def to_s
35
- [left, right].uniq.join(" #{messages[:or]} ")
35
+ [left, right].uniq.join(" #{messages[:or].()} ")
36
36
  end
37
37
  end
38
38
 
@@ -160,9 +160,7 @@ module Dry
160
160
  end
161
161
 
162
162
  def message_text(rule, template, tokens, opts)
163
- original_verbosity = $VERBOSE
164
- $VERBOSE = nil
165
- text = template % tokens
163
+ text = template[template.data(tokens)]
166
164
 
167
165
  if full?
168
166
  rule_name = messages.rule(rule, opts) || rule
@@ -170,8 +168,6 @@ module Dry
170
168
  else
171
169
  text
172
170
  end
173
- ensure
174
- $VERBOSE = original_verbosity
175
171
  end
176
172
 
177
173
  def message_tokens(args)
@@ -1,8 +1,11 @@
1
1
  require 'pathname'
2
2
  require 'concurrent/map'
3
+
3
4
  require 'dry/equalizer'
4
5
  require 'dry/configurable'
5
6
 
7
+ require 'dry/validation/template'
8
+
6
9
  module Dry
7
10
  module Validation
8
11
  module Messages
@@ -62,23 +65,15 @@ module Dry
62
65
  end
63
66
 
64
67
  def call(predicate, options = EMPTY_HASH)
65
- cache.fetch_or_store(cache_key(predicate, options)) do
68
+ cache.fetch_or_store([predicate, options.reject { |k,| k.equal?(:input) }]) do
66
69
  path, opts = lookup(predicate, options)
67
- get(path, opts) if path
70
+ return unless path
71
+ text = yield(path, opts)
72
+ Template[text]
68
73
  end
69
74
  end
70
75
  alias_method :[], :call
71
76
 
72
- if ::Hash.instance_methods.include?(:slice)
73
- def cache_key(predicate, options)
74
- [predicate, options.slice(*CACHE_KEYS)]
75
- end
76
- else
77
- def cache_key(predicate, options)
78
- [predicate, options.select { |key,| CACHE_KEYS.include?(key) }]
79
- end
80
- end
81
-
82
77
  def lookup(predicate, options = {})
83
78
  tokens = options.merge(
84
79
  root: root,
@@ -112,7 +107,7 @@ module Dry
112
107
  end
113
108
 
114
109
  def cache
115
- @cache ||= self.class.cache[hash]
110
+ @cache ||= self.class.cache[self]
116
111
  end
117
112
 
118
113
  def default_locale
@@ -13,8 +13,15 @@ module Dry
13
13
  @t = I18n.method(:t)
14
14
  end
15
15
 
16
+ def call(predicate, options = EMPTY_HASH)
17
+ super do |path, opts|
18
+ get(path, opts)
19
+ end
20
+ end
21
+ alias_method :[], :call
22
+
16
23
  def get(key, options = {})
17
- t.(key, options) if key
24
+ t.(key, locale: options.fetch(:locale, default_locale)) if key
18
25
  end
19
26
 
20
27
  def rule(name, options = {})
@@ -11,6 +11,13 @@ module Dry
11
11
  @root = messages.root
12
12
  end
13
13
 
14
+ def call(predicate, options = EMPTY_HASH)
15
+ super do |path, opts|
16
+ messages.get(path, opts)
17
+ end
18
+ end
19
+ alias_method :[], :call
20
+
14
21
  def key?(key, *args)
15
22
  messages.key?(key, *args)
16
23
  end
@@ -34,6 +34,13 @@ module Dry
34
34
  @data = data
35
35
  end
36
36
 
37
+ def call(predicate, options = EMPTY_HASH)
38
+ super do |path, opts|
39
+ get(path, opts)
40
+ end
41
+ end
42
+ alias_method :[], :call
43
+
37
44
  def get(key, options = {})
38
45
  data[key % { locale: options.fetch(:locale, default_locale) }]
39
46
  end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'dry/equalizer'
4
+
5
+ module Dry
6
+ module Validation
7
+ module Messages
8
+ # Template wraps a string with interpolation tokens and defines evaluator function
9
+ # dynamically
10
+ #
11
+ # @api private
12
+ class Template
13
+ include Dry::Equalizer(:text)
14
+
15
+ TOKEN_REGEXP = /%{(\w*)}/
16
+
17
+ # !@attribute [r] text
18
+ # @return [String]
19
+ attr_reader :text
20
+
21
+ # !@attribute [r] tokens
22
+ # @return [Hash]
23
+ attr_reader :tokens
24
+
25
+ # !@attribute [r] evaluator
26
+ # @return [Proc]
27
+ attr_reader :evaluator
28
+
29
+ # @api private
30
+ def self.[](input)
31
+ new(*parse(input))
32
+ end
33
+
34
+ # @api private
35
+ def self.parse(input)
36
+ tokens = input.scan(TOKEN_REGEXP).flatten(1).map(&:to_sym)
37
+ text = input.gsub('%', '#')
38
+
39
+ evaluator = <<-RUBY.strip
40
+ -> (#{tokens.map { |token| "#{token}:" }.join(", ")}) { "#{text}" }
41
+ RUBY
42
+
43
+ [text, tokens, eval(evaluator, binding, __FILE__, __LINE__ - 3)]
44
+ end
45
+
46
+ # @api private
47
+ def initialize(text, tokens, evaluator)
48
+ @text = text
49
+ @tokens = tokens
50
+ @evaluator = evaluator
51
+ end
52
+
53
+ # @api private
54
+ def data(input)
55
+ tokens.each_with_object({}) { |k, h| h[k] = input[k] }
56
+ end
57
+
58
+ # @api private
59
+ def call(data = EMPTY_HASH)
60
+ data.empty? ? evaluator.() : evaluator.(data)
61
+ end
62
+ alias_method :[], :call
63
+ end
64
+ end
65
+ end
66
+ end
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Validation
3
- VERSION = '0.13.2'.freeze
3
+ VERSION = '0.13.3'.freeze
4
4
  end
5
5
  end
@@ -3,4 +3,6 @@ en:
3
3
  rules:
4
4
  email:
5
5
  filled?: "Please provide your email"
6
+ age:
7
+ gt?: "%{input} must be greater than %{num}"
6
8
  email?: "must be an email"
@@ -4,8 +4,12 @@ RSpec.describe Dry::Validation do
4
4
  shared_context 'schema with customized messages' do
5
5
  describe '#messages' do
6
6
  it 'returns compiled error messages' do
7
- expect(schema.(email: '').messages).to eql(
8
- email: ['Please provide your email']
7
+ expect(schema.(email: '', age: 12).messages).to eql(
8
+ email: ['Please provide your email'], age: ['12 must be greater than 18']
9
+ )
10
+
11
+ expect(schema.(email: '', age: 14).messages).to eql(
12
+ email: ['Please provide your email'], age: ['14 must be greater than 18']
9
13
  )
10
14
  end
11
15
  end
@@ -18,7 +22,8 @@ RSpec.describe Dry::Validation do
18
22
  config.messages_file = SPEC_ROOT.join('fixtures/locales/en.yml')
19
23
  end
20
24
 
21
- required(:email, &:filled?)
25
+ required(:email).filled
26
+ required(:age).value(gt?: 18)
22
27
  end
23
28
  end
24
29
 
@@ -38,7 +43,8 @@ RSpec.describe Dry::Validation do
38
43
  config.messages = :i18n
39
44
  end
40
45
 
41
- required(:email, &:filled?)
46
+ required(:email).filled
47
+ required(:age).value(gt?: 18)
42
48
  end
43
49
  end
44
50
 
@@ -17,33 +17,33 @@ RSpec.describe Messages::I18n do
17
17
  end
18
18
 
19
19
  it 'returns a message for a predicate' do
20
- message = messages[:filled?, rule: :name]
20
+ message = messages[:filled?, rule: :name].text
21
21
 
22
22
  expect(message).to eql("nie może być pusty")
23
23
  end
24
24
 
25
25
  it 'returns a message for a specific rule' do
26
- message = messages[:filled?, rule: :email]
26
+ message = messages[:filled?, rule: :email].text
27
27
 
28
28
  expect(message).to eql("Proszę podać adres email")
29
29
  end
30
30
 
31
31
  it 'returns a message for a specific val type' do
32
- message = messages[:size?, rule: :pages, val_type: String]
32
+ message = messages[:size?, rule: :pages, val_type: String].text
33
33
 
34
- expect(message).to eql("wielkość musi być równa %{size}")
34
+ expect(message).to eql("wielkość musi być równa \#{size}")
35
35
  end
36
36
 
37
37
  it 'returns a message for a specific rule and its default arg type' do
38
- message = messages[:size?, rule: :pages]
38
+ message = messages[:size?, rule: :pages].text
39
39
 
40
- expect(message).to eql("wielkość musi być równa %{size}")
40
+ expect(message).to eql("wielkość musi być równa \#{size}")
41
41
  end
42
42
 
43
43
  it 'returns a message for a specific rule and its arg type' do
44
- message = messages[:size?, rule: :pages, arg_type: Range]
44
+ message = messages[:size?, rule: :pages, arg_type: Range].text
45
45
 
46
- expect(message).to eql("wielkość musi być między %{size_left} a %{size_right}")
46
+ expect(message).to eql("wielkość musi być między \#{size_left} a \#{size_right}")
47
47
  end
48
48
  end
49
49
 
@@ -57,27 +57,27 @@ RSpec.describe Messages::I18n do
57
57
 
58
58
  context 'with a different locale' do
59
59
  it 'returns a message for a predicate' do
60
- message = messages[:filled?, rule: :name, locale: :en]
60
+ message = messages[:filled?, rule: :name, locale: :en].text
61
61
 
62
62
  expect(message).to eql("must be filled")
63
63
  end
64
64
 
65
65
  it 'returns a message for a specific rule' do
66
- message = messages[:filled?, rule: :email, locale: :en]
66
+ message = messages[:filled?, rule: :email, locale: :en].text
67
67
 
68
68
  expect(message).to eql("Please provide your email")
69
69
  end
70
70
 
71
71
  it 'returns a message for a specific rule and its default arg type' do
72
- message = messages[:size?, rule: :pages, locale: :en]
72
+ message = messages[:size?, rule: :pages, locale: :en].text
73
73
 
74
- expect(message).to eql("size must be %{size}")
74
+ expect(message).to eql("size must be \#{size}")
75
75
  end
76
76
 
77
77
  it 'returns a message for a specific rule and its arg type' do
78
- message = messages[:size?, rule: :pages, arg_type: Range, locale: :en]
78
+ message = messages[:size?, rule: :pages, arg_type: Range, locale: :en].text
79
79
 
80
- expect(message).to eql("size must be within %{size_left} - %{size_right}")
80
+ expect(message).to eql("size must be within \#{size_left} - \#{size_right}")
81
81
  end
82
82
  end
83
83
 
@@ -90,7 +90,7 @@ RSpec.describe Messages::I18n do
90
90
  it 'returns a message for a predicate in the default_locale' do
91
91
  pending 'FIXME: this got broken for some reason, probably an I18n issue'
92
92
 
93
- message = messages[:even?, rule: :some_number]
93
+ message = messages[:even?, rule: :some_number].text
94
94
 
95
95
  expect(I18n.locale).to eql(:pl)
96
96
  expect(message).to eql("must be even")
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-validation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.2
4
+ version: 0.13.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andy Holland
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2019-05-12 00:00:00.000000000 Z
12
+ date: 2019-05-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: concurrent-ruby
@@ -226,6 +226,7 @@ files:
226
226
  - lib/dry/validation/schema/rule.rb
227
227
  - lib/dry/validation/schema/value.rb
228
228
  - lib/dry/validation/schema_compiler.rb
229
+ - lib/dry/validation/template.rb
229
230
  - lib/dry/validation/type_specs.rb
230
231
  - lib/dry/validation/version.rb
231
232
  - log/.gitkeep