dry-schema 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -8,14 +8,7 @@ module Dry
8
8
  class MessageSet
9
9
  include Enumerable
10
10
 
11
- HINT_EXCLUSION = %i(
12
- key? filled? nil? bool?
13
- str? int? float? decimal?
14
- date? date_time? time? hash?
15
- array? format?
16
- ).freeze
17
-
18
- attr_reader :messages, :failures, :hints, :paths, :placeholders, :options
11
+ attr_reader :messages, :placeholders, :options
19
12
 
20
13
  # @api private
21
14
  def self.[](messages, options = EMPTY_HASH)
@@ -25,12 +18,7 @@ module Dry
25
18
  # @api private
26
19
  def initialize(messages, options = EMPTY_HASH)
27
20
  @messages = messages
28
- @hints = messages.select(&:hint?)
29
- @failures = messages - hints
30
- @paths = failures.map(&:path).uniq
31
21
  @options = options
32
-
33
- initialize_hints!
34
22
  initialize_placeholders!
35
23
  end
36
24
 
@@ -42,16 +30,11 @@ module Dry
42
30
 
43
31
  # @api public
44
32
  def to_h
45
- failures? ? messages_map : hints_map
33
+ messages_map
46
34
  end
47
35
  alias_method :to_hash, :to_h
48
36
  alias_method :dump, :to_h
49
37
 
50
- # @api private
51
- def failures?
52
- options[:failures].equal?(true)
53
- end
54
-
55
38
  # @api private
56
39
  def empty?
57
40
  messages.empty?
@@ -60,26 +43,8 @@ module Dry
60
43
  private
61
44
 
62
45
  # @api private
63
- def messages_map
64
- failures.group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
65
- node = path.reduce(hash) { |a, e| a[e] }
66
-
67
- msgs.each do |msg|
68
- node << msg
69
- end
70
-
71
- msg_hints = hint_groups[path]
72
- node.concat(msg_hints) if msg_hints
73
-
74
- node.map!(&:to_s)
75
-
76
- hash
77
- end
78
- end
79
-
80
- # @api private
81
- def hints_map
82
- hints.group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
46
+ def messages_map(messages = self.messages)
47
+ messages.group_by(&:path).reduce(placeholders) do |hash, (path, msgs)|
83
48
  node = path.reduce(hash) { |a, e| a[e] }
84
49
 
85
50
  msgs.each do |msg|
@@ -93,18 +58,13 @@ module Dry
93
58
  end
94
59
 
95
60
  # @api private
96
- def hint_groups
97
- @hint_groups ||= hints.group_by(&:path)
98
- end
99
-
100
- # @api private
101
- def initialize_hints!
102
- hints.reject! { |hint| HINT_EXCLUSION.include?(hint.predicate) }
61
+ def paths
62
+ @paths ||= messages.map(&:path).uniq
103
63
  end
104
64
 
105
65
  # @api private
106
66
  def initialize_placeholders!
107
- @placeholders = paths.reduce({}) do |hash, path|
67
+ @placeholders = messages.map(&:path).uniq.reduce({}) do |hash, path|
108
68
  curr_idx = 0
109
69
  last_idx = path.size - 1
110
70
  node = hash
@@ -20,17 +20,19 @@ module Dry
20
20
 
21
21
  # @api private
22
22
  def self.build(config)
23
- case config.messages
23
+ klass = case config.messages
24
24
  when :yaml then default
25
- when :i18n then Messages::I18n.new
25
+ when :i18n then Messages::I18n
26
26
  else
27
27
  raise "+#{config.messages}+ is not a valid messages identifier"
28
28
  end
29
+
30
+ klass.build
29
31
  end
30
32
 
31
33
  # @api private
32
34
  def self.default
33
- Messages::YAML.load
35
+ Messages::YAML
34
36
  end
35
37
  end
36
38
  end
@@ -34,6 +34,10 @@ module Dry
34
34
  %{root}.%{predicate}
35
35
  ).freeze
36
36
 
37
+ setting :rule_lookup_paths, %w(
38
+ rules.%{name}
39
+ ).freeze
40
+
37
41
  setting :arg_type_default, 'default'.freeze
38
42
  setting :val_type_default, 'default'.freeze
39
43
 
@@ -66,8 +70,10 @@ module Dry
66
70
 
67
71
  # @api private
68
72
  def rule(name, options = {})
69
- path = "%{locale}.rules.#{name}"
70
- get(path, options) if key?(path, options)
73
+ tokens = { name: name, locale: options.fetch(:locale, default_locale) }
74
+ path = rule_lookup_paths(tokens).detect { |key| key?(key, options) }
75
+
76
+ get(path, options) if path
71
77
  end
72
78
 
73
79
  # Retrieve a message template
@@ -95,7 +101,7 @@ module Dry
95
101
  message_type: options[:message_type] || :failure
96
102
  )
97
103
 
98
- tokens[:path] = options[:rule] || Array(options[:path]).join(DOT)
104
+ tokens[:path] = options[:rule] || Array(options[:path]).last
99
105
 
100
106
  opts = options.select { |k, _| !config.lookup_options.include?(k) }
101
107
 
@@ -111,6 +117,11 @@ module Dry
111
117
  config.lookup_paths.map { |path| path % tokens }
112
118
  end
113
119
 
120
+ # @api private
121
+ def rule_lookup_paths(tokens)
122
+ config.rule_lookup_paths.map { |key| key % tokens }
123
+ end
124
+
114
125
  # Return a new message backend that will look for messages under provided namespace
115
126
  #
116
127
  # @param [Symbol,String] namespace
@@ -9,7 +9,21 @@ module Dry
9
9
  class Messages::I18n < Messages::Abstract
10
10
  attr_reader :t
11
11
 
12
- ::I18n.load_path.concat(config.paths)
12
+ configure do |config|
13
+ config.root = 'dry_schema.errors'.freeze
14
+ config.rule_lookup_paths = config.rule_lookup_paths.map { |path| "dry_schema.#{path}" }
15
+ end
16
+
17
+ # @api private
18
+ def self.build(paths = config.paths)
19
+ set_load_paths(paths)
20
+ new
21
+ end
22
+
23
+ # @api private
24
+ def self.set_load_paths(paths)
25
+ ::I18n.load_path.concat(paths)
26
+ end
13
27
 
14
28
  # @api private
15
29
  def initialize
@@ -14,12 +14,16 @@ module Dry
14
14
  # @api private
15
15
  attr_reader :root
16
16
 
17
+ # @api private
18
+ attr_reader :call_opts
19
+
17
20
  # @api private
18
21
  def initialize(namespace, messages)
19
22
  super()
20
23
  @namespace = namespace
21
24
  @messages = messages
22
25
  @root = messages.root
26
+ @call_opts = { namespace: namespace }.freeze
23
27
  end
24
28
 
25
29
  # Get a message for the given key and its options
@@ -34,6 +38,12 @@ module Dry
34
38
  messages.get(key, options)
35
39
  end
36
40
 
41
+ # @api public
42
+ def call(key, options = {})
43
+ super(key, options.empty? ? call_opts : options.merge(call_opts))
44
+ end
45
+ alias_method :[], :call
46
+
37
47
  # Check if given key is defined
38
48
  #
39
49
  # @return [Boolean]
@@ -45,7 +55,12 @@ module Dry
45
55
 
46
56
  # @api private
47
57
  def lookup_paths(tokens)
48
- super(tokens.merge(root: "#{root}.rules.#{namespace}")) + super
58
+ super(tokens.merge(root: "#{tokens[:root]}.#{namespace}")) + super
59
+ end
60
+
61
+ def rule_lookup_paths(tokens)
62
+ base_paths = messages.rule_lookup_paths(tokens)
63
+ base_paths.map { |key| key.gsub("dry_schema", "dry_schema.#{namespace}") } + base_paths
49
64
  end
50
65
  end
51
66
  end
@@ -16,11 +16,12 @@ module Dry
16
16
 
17
17
  # @api private
18
18
  configure do |config|
19
- config.root = '%{locale}.errors'.freeze
19
+ config.root = '%{locale}.dry_schema.errors'.freeze
20
+ config.rule_lookup_paths = config.rule_lookup_paths.map { |path| "%{locale}.dry_schema.#{path}" }
20
21
  end
21
22
 
22
23
  # @api private
23
- def self.load(paths = config.paths)
24
+ def self.build(paths = config.paths)
24
25
  new(paths.map { |path| load_file(path) }.reduce(:merge))
25
26
  end
26
27
 
@@ -51,7 +52,11 @@ module Dry
51
52
  #
52
53
  # @api public
53
54
  def get(key, options = {})
54
- data[key % { locale: options.fetch(:locale, default_locale) }]
55
+ evaluated_key = key.include?('%{locale}') ?
56
+ key % { locale: options.fetch(:locale, default_locale) } :
57
+ key
58
+
59
+ data[evaluated_key]
55
60
  end
56
61
 
57
62
  # Check if given key is defined
@@ -60,7 +65,11 @@ module Dry
60
65
  #
61
66
  # @api public
62
67
  def key?(key, options = {})
63
- data.key?(key % { locale: options.fetch(:locale, default_locale) })
68
+ evaluated_key = key.include?('%{locale}') ?
69
+ key % { locale: options.fetch(:locale, default_locale) } :
70
+ key
71
+
72
+ data.key?(evaluated_key)
64
73
  end
65
74
 
66
75
  # Merge messages from an additional path
@@ -0,0 +1,23 @@
1
+ module Dry
2
+ module Schema
3
+ class NamespacedRule
4
+ attr_reader :rule
5
+
6
+ attr_reader :namespace
7
+
8
+ def initialize(namespace, rule)
9
+ @namespace = namespace
10
+ @rule = rule
11
+ end
12
+
13
+ def call(input)
14
+ result = rule.call(input)
15
+ Logic::Result.new(result.success?) { [:namespace, [namespace, result.to_ast]] }
16
+ end
17
+
18
+ def ast(input=Undefined)
19
+ [:namespace, [namespace, rule.ast(input)]]
20
+ end
21
+ end
22
+ end
23
+ end
@@ -105,29 +105,7 @@ module Dry
105
105
  #
106
106
  # @api public
107
107
  def errors(options = EMPTY_HASH)
108
- message_set(options.merge(hints: false)).dump
109
- end
110
-
111
- # Get all messages including hints
112
- #
113
- # @see #message_set
114
- #
115
- # @return [Hash<Symbol=>Array>]
116
- #
117
- # @api public
118
- def messages(options = EMPTY_HASH)
119
- message_set(options.merge(hints: true)).dump
120
- end
121
-
122
- # Get hints exclusively without errors
123
- #
124
- # @see #message_set
125
- #
126
- # @return [Hash<Symbol=>Array>]
127
- #
128
- # @api public
129
- def hints(options = EMPTY_HASH)
130
- message_set(options.merge(failures: false)).dump
108
+ message_set(options).dump
131
109
  end
132
110
 
133
111
  # Return the message set
@@ -38,7 +38,11 @@ module Dry
38
38
 
39
39
  # @api private
40
40
  def to_ast
41
- [:set, rules.values.map(&:to_ast)]
41
+ if config.namespace
42
+ [:namespace, [config.namespace, [:set, rules.values.map(&:to_ast)]]]
43
+ else
44
+ [:set, rules.values.map(&:to_ast)]
45
+ end
42
46
  end
43
47
  end
44
48
  end
@@ -28,6 +28,9 @@ module Dry
28
28
 
29
29
  # @api private
30
30
  def evaluate(*predicates, **opts, &block)
31
+ pred_opts = opts.dup
32
+ pred_opts.delete(:type_spec)
33
+
31
34
  predicates.each do |predicate|
32
35
  if predicate.respond_to?(:call)
33
36
  append(predicate)
@@ -38,7 +41,7 @@ module Dry
38
41
  end
39
42
  end
40
43
 
41
- opts.each do |predicate, *args|
44
+ pred_opts.each do |predicate, *args|
42
45
  append(__send__(predicate, *args))
43
46
  end
44
47
 
@@ -1,5 +1,5 @@
1
1
  module Dry
2
2
  module Schema
3
- VERSION = '0.1.1'.freeze
3
+ VERSION = '0.2.0'.freeze
4
4
  end
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dry-schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Piotr Solnica
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-17 00:00:00.000000000 Z
11
+ date: 2019-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -192,6 +192,10 @@ files:
192
192
  - lib/dry/schema/constants.rb
193
193
  - lib/dry/schema/dsl.rb
194
194
  - lib/dry/schema/extensions.rb
195
+ - lib/dry/schema/extensions/hints.rb
196
+ - lib/dry/schema/extensions/hints/message_compiler_methods.rb
197
+ - lib/dry/schema/extensions/hints/message_set_methods.rb
198
+ - lib/dry/schema/extensions/hints/result_methods.rb
195
199
  - lib/dry/schema/extensions/monads.rb
196
200
  - lib/dry/schema/json.rb
197
201
  - lib/dry/schema/key.rb
@@ -207,6 +211,7 @@ files:
207
211
  - lib/dry/schema/macros/maybe.rb
208
212
  - lib/dry/schema/macros/optional.rb
209
213
  - lib/dry/schema/macros/required.rb
214
+ - lib/dry/schema/macros/schema.rb
210
215
  - lib/dry/schema/macros/value.rb
211
216
  - lib/dry/schema/message.rb
212
217
  - lib/dry/schema/message_compiler.rb
@@ -218,6 +223,7 @@ files:
218
223
  - lib/dry/schema/messages/namespaced.rb
219
224
  - lib/dry/schema/messages/template.rb
220
225
  - lib/dry/schema/messages/yaml.rb
226
+ - lib/dry/schema/namespaced_rule.rb
221
227
  - lib/dry/schema/params.rb
222
228
  - lib/dry/schema/path.rb
223
229
  - lib/dry/schema/predicate.rb
@@ -243,7 +249,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
243
249
  requirements:
244
250
  - - ">="
245
251
  - !ruby/object:Gem::Version
246
- version: '0'
252
+ version: '2.4'
247
253
  required_rubygems_version: !ruby/object:Gem::Requirement
248
254
  requirements:
249
255
  - - ">="