adaptiveconfiguration 1.0.0.beta01 → 1.0.0.beta02

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: 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: