adaptiveconfiguration 1.0.0.beta01 → 1.0.0.beta02

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: 40a0689cda0cd8ceb25859afd3c06379667fb296d3a2a90dbf027f00d8fb6454
4
- data.tar.gz: cf253ace46d9f2d634a9b578653c16a12e6909c517c53cd19d0b584e17111819
3
+ metadata.gz: 1c9efb51c751d9e2d5ed9f13cde645e6ad67eb33e657bb5a9942d8bc32ec09da
4
+ data.tar.gz: 4bb48b03dcecf0ccc32a4a5c107ea149cf006d1d7bf7d9d10fec7e585eb00d6d
5
5
  SHA512:
6
- metadata.gz: f2af0a82072944057dca43e2c4a294c42cdb140e1433e2fdedd7d353e92df0d97e6e9736b45e00d7e391d7f7bb40336b6b631626e972f7d1e4d10888b3abf0b9
7
- data.tar.gz: 04a3bee03bffff23ab8b13157b2e5c7fd589d435f18870f709b4c176342d402fdf6f9bf16ed4734fc7172e5d07385808fda56fcf36287fd170bf5799d8af6a1a
6
+ metadata.gz: dadebc70156e871f0986e1858080de3c67e411b3a39dae3328962642d39157be221615b9811d7170e6f3c8fbc2f198d4037a14f109015b8cfc17cd6e139c69bf
7
+ data.tar.gz: 501f930713ec9f0ce6396d5bcb308065c3448b34f845225c20c57e6152306ba7a930a7a67868616394fd0f620d36e988e454aa4d7a89fce2ac88c1e383bebe70
@@ -1,7 +1,7 @@
1
1
  Gem::Specification.new do | spec |
2
2
 
3
3
  spec.name = 'adaptiveconfiguration'
4
- spec.version = '1.0.0.beta01'
4
+ spec.version = '1.0.0.beta02'
5
5
  spec.authors = [ 'Kristoph Cichocki-Romanov' ]
6
6
  spec.email = [ 'rubygems.org@kristoph.net' ]
7
7
 
@@ -11,14 +11,14 @@ module AdaptiveConfiguration
11
11
 
12
12
  DEFAULT_CONVERTERS = {
13
13
 
14
+ Array => ->( v ) { Array( v ) },
14
15
  Date => ->( v ) { v.respond_to?( :to_date ) ? v.to_date : Date.parse( v.to_s ) },
15
16
  Time => ->( v ) { v.respond_to?( :to_time ) ? v.to_time : Time.parse( v.to_s ) },
16
17
  URI => ->( v ) { URI.parse( v.to_s ) },
17
18
  String => ->( v ) { String( v ) },
18
- Integer => ->( v ) { Integer( v ) },
19
- Float => ->( v ) { Float( v ) },
20
19
  Rational => ->( v ) { Rational( v ) },
21
- Array => ->( v ) { Array( v ) },
20
+ Float => ->( v ) { Float( v ) },
21
+ Integer => ->( v ) { Integer( v ) },
22
22
  TrueClass => ->( v ) {
23
23
  case v
24
24
  when Numeric
@@ -47,7 +47,7 @@ module AdaptiveConfiguration
47
47
  @converters[ klass ] = block
48
48
  end
49
49
 
50
- def build!( values = nil, &block )
50
+ def build( values = nil, &block )
51
51
  context = AdaptiveConfiguration::Context.new(
52
52
  values,
53
53
  converters: @converters,
@@ -57,5 +57,11 @@ module AdaptiveConfiguration
57
57
  context
58
58
  end
59
59
 
60
+ def build!( values = nil, &block )
61
+ context = self.build( values, &block )
62
+ context.validate!
63
+ context
64
+ end
65
+
60
66
  end
61
67
  end
@@ -1,6 +1,8 @@
1
1
  module AdaptiveConfiguration
2
2
  class Context < BasicObject
3
3
 
4
+ attr_reader :errors
5
+
4
6
  def initialize( values = nil, definitions:, converters: )
5
7
 
6
8
  values = values ? values.transform_keys( &:to_sym ) : {}
@@ -8,6 +10,7 @@ module AdaptiveConfiguration
8
10
  @converters = converters&.dup
9
11
  @definitions = definitions&.dup
10
12
  @values = {}
13
+ @errors = []
11
14
 
12
15
  @definitions.each do | key, definition |
13
16
  name = definition[ :as ] || key
@@ -30,6 +33,14 @@ module AdaptiveConfiguration
30
33
 
31
34
  end
32
35
 
36
+ def nil?
37
+ false
38
+ end
39
+
40
+ def empty?
41
+ @values.empty?
42
+ end
43
+
33
44
  def []( key )
34
45
  @values[ key ]
35
46
  end
@@ -42,12 +53,18 @@ module AdaptiveConfiguration
42
53
  @values.each( &block )
43
54
  end
44
55
 
56
+
45
57
  def merge( hash )
46
58
  self.to_h.merge( hash )
47
59
  end
48
60
 
49
- def empty?
50
- @values.empty?
61
+ def valid?
62
+ __validate_values
63
+ @errors.empty?
64
+ end
65
+
66
+ def validate!
67
+ __validate_values { | error | ::Kernel.raise error }
51
68
  end
52
69
 
53
70
  def to_h
@@ -103,10 +120,10 @@ module AdaptiveConfiguration
103
120
  Context.new( converters: @converters, definitions: definition[ :definitions ] )
104
121
  context.instance_eval( &block ) if block
105
122
  @values[ name ] = context
106
- else
123
+ else
107
124
  value = args.first
108
- value = _convert_value!( definition[ :type ], method, value ) if definition[ :type ]
109
- @values[ name ] = value
125
+ new_value = definition[ :type ] ? __coerce_value( definition[ :type ], value ) : value
126
+ @values[ name ] = new_value.nil? ? value : new_value
110
127
  end
111
128
  else
112
129
  @values[ name ] = definition[ :default_assigned ] ?
@@ -119,7 +136,10 @@ module AdaptiveConfiguration
119
136
  else
120
137
  values = ::Kernel.method( :Array ).call( args.first )
121
138
  if type = definition[ :type ]
122
- values = values.map { | v | _convert_value!( type, method, v ) }
139
+ values = values.map do | v |
140
+ new_value = __coerce_value( type, v )
141
+ new_value.nil? ? v : new_value
142
+ end
123
143
  end
124
144
  @values[ name ].concat( values )
125
145
  end
@@ -142,29 +162,103 @@ module AdaptiveConfiguration
142
162
  @definitions.key?( method ) || self.class.instance_methods.include?( method )
143
163
  end
144
164
 
145
- private; def _convert_value!( klass, key, value )
146
- return value unless klass && value
165
+ protected
166
+
167
+ def __coerce_value( types, value )
168
+
169
+ return value unless types && !value.nil?
147
170
 
148
- types =::Kernel.method( :Array ).call( klass )
171
+ types = ::Kernel.method( :Array ).call( types )
149
172
  result = nil
150
173
 
151
- types.each do | type |
152
- result =
153
- ( ( value.respond_to?( :is_a? ) && value.is_a?( type ) ) ? value : nil ) ||
154
- @converters[ klass ].call( value ) rescue nil
155
- break if result
174
+ if value.respond_to?( :is_a? )
175
+ types.each do | type |
176
+ result = value.is_a?( type ) ? value : nil
177
+ break unless result.nil?
178
+ end
156
179
  end
157
180
 
158
181
  if result.nil?
159
- types_names = types.map( &:name ).join( ', ' )
160
- ::Kernel.raise ::TypeError, <<~TEXT.gsub( /\s+/, ' ' ).strip
161
- The key #{key} expects a value of type #{types_names} but received an
162
- incompatible #{value.class.name}.
163
- TEXT
182
+ types.each do | type |
183
+ result = @converters[ type ].call( value ) rescue nil
184
+ break unless result.nil?
185
+ end
164
186
  end
165
187
 
166
188
  result
167
189
  end
168
190
 
191
+ def __validate_values( path = nil, &block )
192
+
193
+ path.chomp( '/' ) if path
194
+ @errors = []
195
+
196
+ is_of_matching_types = ::Proc.new do | value, types |
197
+ type_match = false
198
+ ::Kernel.method( :Array ).call( types ).each do | type |
199
+ type_match = value.is_a?( type )
200
+ break if type_match
201
+ end
202
+ type_match
203
+ end
204
+
205
+ @definitions.each do | key, definition |
206
+
207
+ name = definition[ :as ] || key
208
+ value = @values[ name ]
209
+
210
+ if definition[ :required ] &&
211
+ ( !value || ( value.respond_to?( :empty ) && value.empty? ) )
212
+
213
+ error = RequirementUnmetError.new( path: path, key: key )
214
+ block.call( error ) if block
215
+ @errors << error
216
+
217
+ elsif !definition[ :default_assigned ] && !value.nil?
218
+
219
+ unless definition[ :array ]
220
+
221
+ if definition[ :type ] == :group
222
+ value.__validate_values( "#{ ( path || '' ) + ( path ? '/' : '' ) + key.to_s }", &block )
223
+ @errors.concat( value.errors )
224
+ else
225
+ if definition[ :type ] && value && !definition[ :default_assigned ]
226
+ unless is_of_matching_types.call( value, definition[ :type ] )
227
+ error = IncompatibleTypeError.new(
228
+ path: path, key: key, type: definition[ :type ], value: value
229
+ )
230
+ block.call( error ) if block
231
+ @errors << error
232
+ end
233
+ end
234
+ end
235
+
236
+ else
237
+
238
+ if definition[ :type ] == :group
239
+ groups.each do | group |
240
+ group.__validate_values( "#{ ( path || '' ) + ( path ? '/' : '' ) + key.to_s }", &block )
241
+ @errors.concat( group.errors )
242
+ end
243
+ else
244
+ if definition[ :type ] && !definition[ :default_assigned ]
245
+ values = ::Kernel.method( :Array ).call( value )
246
+ values.each do | v |
247
+ unless is_of_matching_types.call( v, definition[ :type ] )
248
+ error = IncompatibleTypeError.new(
249
+ path: path, key: key, type: definition[ :type ], value: v
250
+ )
251
+ block.call( error ) if block
252
+ @errors << error
253
+ end
254
+ end
255
+ end
256
+ end
257
+
258
+ end
259
+ end
260
+ end
261
+ end
262
+
169
263
  end
170
264
  end
@@ -0,0 +1,41 @@
1
+ require 'debug'
2
+
3
+ module AdaptiveConfiguration
4
+
5
+ class Error < StandardError; end
6
+
7
+
8
+ class IncompatibleTypeError < Error
9
+
10
+ attr_reader :keypath
11
+ attr_reader :key
12
+ attr_reader :type
13
+
14
+ def initialize( path: nil, key:, type:, value: )
15
+
16
+ path = path ? path.to_s.chomp( '/' ) : nil
17
+ @key = key
18
+ @keypath = path ? ( path + '/' + @key.to_s ) : @key.to_s
19
+ @type = type
20
+ type_text = @type.respond_to?( :join ) ? type.join( ', ' ) : type
21
+
22
+ super( "The parameter '#{@keypath}' expects #{type_text} but received incompatible #{value.class.name}." )
23
+ end
24
+
25
+ end
26
+
27
+ class RequirementUnmetError < Error
28
+
29
+ attr_reader :keypath
30
+ attr_reader :key
31
+
32
+ def initialize( path: nil, key: )
33
+ path = path ? path.chomp( '/' ) : nil
34
+ @key = key
35
+ @keypath = path ? ( path + '/' + @key.to_s ) : key.to_s
36
+ super( "The parameter #{@keypath} is required." )
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -1,2 +1,3 @@
1
+ require_relative 'adaptive_configuration/errors'
1
2
  require_relative 'adaptive_configuration/builder'
2
3
  require_relative 'adaptive_configuration/configurable'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: adaptiveconfiguration
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta01
4
+ version: 1.0.0.beta02
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kristoph Cichocki-Romanov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-29 00:00:00.000000000 Z
11
+ date: 2024-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -58,6 +58,7 @@ files:
58
58
  - lib/adaptive_configuration/builder.rb
59
59
  - lib/adaptive_configuration/configurable.rb
60
60
  - lib/adaptive_configuration/context.rb
61
+ - lib/adaptive_configuration/errors.rb
61
62
  - lib/adaptive_configuration/group_builder.rb
62
63
  homepage: https://github.com/EndlessInternational/adaptive_configuration
63
64
  licenses: