uinit-type 0.1.3 → 0.1.5

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: 24e0c467351a77c8c49cbe9bd8012902d553c02b2a068d78f4267876b18d0673
4
- data.tar.gz: cadc2bb0c1226c212d8cdefdc26090a6a2180d8a5603bf9792fc23dfb2ee6896
3
+ metadata.gz: 5bd4a4dd91cfd1b86c516b8a94c0449f01e410222900c73f352a6cba5c9d701f
4
+ data.tar.gz: 050b3c0341ec1abdb28bfb876487ca56a1572c2cdc8e7b2eb9455c59c88d857c
5
5
  SHA512:
6
- metadata.gz: 98665f640c4d146830cc2fe4dd3b2af809af031d9eb56b57628a00d9edc45e31177d221c13cc5d203bd5dcdc83dc207d0e995a08b869ef94286fd64bc84c66ff
7
- data.tar.gz: b7db5aee9085482bf5babd0f30ee6952eedface4657548edec4d7927fe5d1b4f8165a4548205fcdbc663236405b15a2a9d54ed8903aa427541f4ebaa2ae9931c
6
+ metadata.gz: eec7569793abefa92343963566c67dddc2486bd1576973f4dcb8e2aa2ffa49699a26726cad42e00c6b9f38cd4017624705eb22cb1aa6c029a100a3a848d0e390
7
+ data.tar.gz: 1f0ce366d5488199b402e5fa4d291291e5543b1847892f600a405e194cb5b405976da6ee83dec1d22f1d993cf1381a16113dcb3b4c20feb976b30ecc70b4db47
data/README.md CHANGED
@@ -2,6 +2,3 @@
2
2
 
3
3
  Dynamic type checker
4
4
 
5
-
6
- ## TODO
7
- - type Map, an generic hash (key, value types)
@@ -9,7 +9,7 @@ module Uinit
9
9
  def initialize(operand, compositions = [])
10
10
  super()
11
11
 
12
- @operand = Type.from(operand)
12
+ @operand = ::Uinit::Type.from(operand)
13
13
  @compositions = compositions
14
14
  end
15
15
 
@@ -44,8 +44,6 @@ module Uinit
44
44
  is
45
45
  end
46
46
 
47
- # rubocop:disable Metrics/AbcSize
48
- # rubocop:disable Metrics/CyclomaticComplexity
49
47
  # rubocop:disable Metrics/PerceivedComplexity
50
48
  def check!(value, depth)
51
49
  errors = []
@@ -86,7 +84,7 @@ module Uinit
86
84
  private
87
85
 
88
86
  def add_composition(operator, operand)
89
- compositions << [operator, Type.from(operand)]
87
+ compositions << [operator, ::Uinit::Type.from(operand)]
90
88
 
91
89
  self
92
90
  end
@@ -5,12 +5,12 @@ module Uinit
5
5
  module Context
6
6
  Nil = Const[nil].freeze
7
7
  Boolean = (Const[true] | Const[false]).freeze
8
- Str = Instance[String].freeze
9
- Int = Instance[Integer].freeze
10
- Float = Instance[Float].freeze
11
- Hsh = Instance[Hash].freeze
12
- Arr = Instance[Array].freeze
13
- Sym = Instance[Symbol].freeze
8
+ Str = String.freeze
9
+ Sym = TypeOf[Symbol].freeze
10
+ Int = Integer.freeze
11
+ Float = Float.freeze
12
+ Hsh = TypeOf[Hash].freeze
13
+ Arr = TypeOf[Array].freeze
14
14
 
15
15
  def array_of(type)
16
16
  ArrayOf.new(type)
@@ -32,20 +32,20 @@ module Uinit
32
32
  Fn.new(arity)
33
33
  end
34
34
 
35
- def hash_of(schema, strict: false)
36
- HashOf.new(schema, strict:)
35
+ def schema(schema, strict: false)
36
+ Schema.new(schema, strict:)
37
37
  end
38
38
 
39
39
  def impl(*, **)
40
40
  Impl.new(*, **)
41
41
  end
42
42
 
43
- def instance(type)
44
- Instance.new(type)
43
+ def set_of(type)
44
+ SetOf.new(type)
45
45
  end
46
46
 
47
- def set_of(type) # rubocop:disable Naming/AccessorMethodName
48
- SetOf.new(type)
47
+ def type(type = BasicObject)
48
+ Type.new(type)
49
49
  end
50
50
 
51
51
  def type_of(type)
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uinit
4
+ module Type
5
+ class Float < TypeOf
6
+ # rubocop:disable Metrics/ParameterLists
7
+ def initialize(min: nil, max: nil, positive: nil, negative: nil, zero: nil, precision: nil, finite: true)
8
+ super(::Float)
9
+
10
+ @min = min
11
+ @max = max
12
+ @positive = positive
13
+ @negative = negative
14
+ @zero = zero
15
+ @precision = precision
16
+ @finite = finite
17
+ end
18
+ # rubocop:enable Metrics/ParameterLists
19
+
20
+ attr_reader :min, :max, :positive, :negative, :zero, :precision, :finite
21
+
22
+ # rubocop:disable Metrics/PerceivedComplexity
23
+ def is?(value)
24
+ return false unless super
25
+
26
+ return false if finite && !value.finite?
27
+ return false if min && value < min
28
+ return false if max && value > max
29
+ return false if positive == false && value.positive?
30
+ return false if positive == true && value <= 0
31
+ return false if negative == false && value.negative?
32
+ return false if negative == true && value >= 0
33
+ return false if zero == false && value.zero?
34
+ return false if zero == true && !value.zero?
35
+ return false if precision && !valid_precision?(value)
36
+
37
+ true
38
+ end
39
+
40
+ def check!(value, depth)
41
+ type_error!('Float must be finite (not Infinity or NaN)', depth) if finite && !value.finite?
42
+ type_error!("Float too small (minimum #{min})", depth) if min && value < min
43
+ type_error!("Float too large (maximum #{max})", depth) if max && value > max
44
+ type_error!('Float cannot be positive', depth) if positive == false && value.positive?
45
+ type_error!('Float must be positive', depth) if positive == true && value <= 0
46
+ type_error!('Float cannot be negative', depth) if negative == false && value.negative?
47
+ type_error!('Float must be negative', depth) if negative == true && value >= 0
48
+ type_error!('Float cannot be zero', depth) if zero == false && value.zero?
49
+ type_error!('Float must be zero', depth) if zero == true && !value.zero?
50
+
51
+ if precision && !valid_precision?(value)
52
+ type_error!("Float exceeds maximum precision of #{precision} decimal places", depth)
53
+ end
54
+
55
+ value
56
+ end
57
+ # rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
58
+
59
+ private
60
+
61
+ def valid_precision?(value)
62
+ return true if precision.nil?
63
+
64
+ # Compter les décimales
65
+ decimal_places = value.to_s.split('.').last&.length || 0
66
+ decimal_places <= precision
67
+ end
68
+ end
69
+ end
70
+ end
data/lib/uinit/type/fn.rb CHANGED
@@ -7,7 +7,7 @@ module Uinit
7
7
  def initialize(arity = -1)
8
8
  super()
9
9
 
10
- raise ArgumentError, 'arity must be an Integer' unless arity.is_a?(Integer)
10
+ raise ArgumentError, 'arity must be an Integer' unless arity.is_a?(::Integer)
11
11
 
12
12
  @arity = arity
13
13
  end
@@ -6,7 +6,7 @@ module Uinit
6
6
  def initialize(type)
7
7
  super()
8
8
 
9
- @type = Type.from(type)
9
+ @type = ::Uinit::Type.from(type)
10
10
  end
11
11
 
12
12
  attr_reader :type
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uinit
4
+ module Type
5
+ class Integer < TypeOf
6
+ def initialize(min: nil, max: nil, positive: nil, negative: nil, zero: nil)
7
+ super(::Integer)
8
+
9
+ @min = min
10
+ @max = max
11
+ @positive = positive
12
+ @negative = negative
13
+ @zero = zero
14
+ end
15
+
16
+ attr_reader :min, :max, :positive, :negative, :zero
17
+
18
+ # rubocop:disable Metrics/PerceivedComplexity
19
+ def is?(value)
20
+ return false unless super
21
+
22
+ return false if min && value < min
23
+ return false if max && value > max
24
+ return false if positive == false && value.positive?
25
+ return false if positive == true && value <= 0
26
+ return false if negative == false && value.negative?
27
+ return false if negative == true && value >= 0
28
+ return false if zero == false && value.zero?
29
+ return false if zero == true && !value.zero?
30
+
31
+ true
32
+ end
33
+
34
+ def check!(value, depth)
35
+ type_error!("Integer too small (minimum #{min})", depth) if min && value < min
36
+ type_error!("Integer too large (maximum #{max})", depth) if max && value > max
37
+ type_error!('Integer cannot be positive', depth) if positive == false && value.positive?
38
+ type_error!('Integer must be positive', depth) if positive == true && value <= 0
39
+ type_error!('Integer cannot be negative', depth) if negative == false && value.negative?
40
+ type_error!('Integer must be negative', depth) if negative == true && value >= 0
41
+ type_error!('Integer cannot be zero', depth) if zero == false && value.zero?
42
+ type_error!('Integer must be zero', depth) if zero == true && !value.zero?
43
+
44
+ value
45
+ end
46
+ # rubocop:enable Metrics/PerceivedComplexity
47
+ end
48
+ end
49
+ end
@@ -2,7 +2,7 @@
2
2
 
3
3
  module Uinit
4
4
  module Type
5
- class HashOf < Base
5
+ class Schema < Base
6
6
  def self.from?(schema)
7
7
  schema.is_a?(Hash)
8
8
  end
@@ -18,7 +18,7 @@ module Uinit
18
18
 
19
19
  @schema =
20
20
  schema.transform_values do |type|
21
- Type.from(type)
21
+ ::Uinit::Type.from(type)
22
22
  end
23
23
 
24
24
  @strict = !!strict
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uinit
4
+ module Type
5
+ class String < TypeOf
6
+ def initialize(empty: true, min_length: nil, max_length: nil, format: nil)
7
+ super(::String)
8
+
9
+ @empty = empty
10
+ @min_length = min_length
11
+ @max_length = max_length
12
+ @format = format
13
+ end
14
+
15
+ attr_reader :empty, :min_length, :max_length, :format
16
+
17
+ # rubocop:disable Metrics/PerceivedComplexity
18
+ def is?(value)
19
+ return false unless super
20
+
21
+ return false if !empty && (value.empty? || value.match?(/^\s+$/))
22
+ return false if min_length && value.length < min_length
23
+ return false if max_length && value.length > max_length
24
+ return false if format && !value.match?(format)
25
+
26
+ true
27
+ end
28
+
29
+ def check!(value, depth)
30
+ if !empty && (value.empty? || value.match?(/^\s+$/))
31
+ type_error!('String cannot be empty or contain only whitespace', depth)
32
+ end
33
+
34
+ if min_length && value.length < min_length
35
+ type_error!("String too short (minimum #{min_length} characters)", depth)
36
+ end
37
+
38
+ if max_length && value.length > max_length
39
+ type_error!("String too long (maximum #{max_length} characters)", depth)
40
+ end
41
+
42
+ type_error!('String doesn\'t match required pattern', depth) if format && !value.match?(format)
43
+
44
+ value
45
+ end
46
+ # rubocop:enable Metrics/PerceivedComplexity
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Uinit
4
+ module Type
5
+ class Type < Base
6
+ def self.from?(value)
7
+ value.is_a?(Class) || value.is_a?(Module)
8
+ end
9
+
10
+ def self.from(value)
11
+ new(value) if from?(value)
12
+ end
13
+
14
+ # TODO: Allow to pass Fn
15
+ def initialize(class_module)
16
+ super()
17
+
18
+ @class_module = class_module
19
+ end
20
+
21
+ attr_reader :class_module
22
+
23
+ def is?(value)
24
+ return false unless value.is_a?(Class) || value.is_a?(Module)
25
+
26
+ value.ancestors.include?(class_module)
27
+ end
28
+
29
+ def check!(value, depth)
30
+ unless value.is_a?(Class) || value.is_a?(Module)
31
+ type_error!("#{value.inspect} is not a Class or a Module", depth)
32
+ end
33
+
34
+ return value if value.ancestors.include?(class_module)
35
+
36
+ type_error!("#{value.inspect} does not extend or include or preprend #{class_module}", depth)
37
+ end
38
+
39
+ def inspect
40
+ "#{super}[#{class_module.inspect}]"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -3,37 +3,32 @@
3
3
  module Uinit
4
4
  module Type
5
5
  class TypeOf < Base
6
- def self.from?(value)
7
- value.is_a?(Class) || value.is_a?(Module)
6
+ def self.from?(class_module)
7
+ class_module.is_a?(::Class) || class_module.is_a?(::Module)
8
8
  end
9
9
 
10
- def self.from(value)
11
- new(value) if from?(value)
10
+ def self.from(class_module)
11
+ new(class_module) if from?(class_module)
12
12
  end
13
13
 
14
- # TODO: Allow to pass Fn
15
14
  def initialize(class_module)
16
15
  super()
17
16
 
17
+ raise ArgumentError, 'class_module must be a Class or a Module' unless self.class.from?(class_module)
18
+
18
19
  @class_module = class_module
19
20
  end
20
21
 
21
22
  attr_reader :class_module
22
23
 
23
24
  def is?(value)
24
- return false unless value.is_a?(Class) || value.is_a?(Module)
25
-
26
- value.ancestors.include?(class_module)
25
+ value.is_a?(class_module)
27
26
  end
28
27
 
29
28
  def check!(value, depth)
30
- unless value.is_a?(Class) || value.is_a?(Module)
31
- type_error!("#{value.inspect} is not a Class or a Module", depth)
32
- end
33
-
34
- return value if value.ancestors.include?(class_module)
29
+ return value if is?(value)
35
30
 
36
- type_error!("#{value.inspect} does not extend or include or preprend #{class_module}", depth)
31
+ type_error!("#{value.inspect} must be an instance of #{class_module}", depth)
37
32
  end
38
33
 
39
34
  def inspect
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Uinit
4
4
  module Type
5
- VERSION = '0.1.3'
5
+ VERSION = '0.1.5'
6
6
  end
7
7
  end
data/lib/uinit/type.rb CHANGED
@@ -21,7 +21,7 @@ module Uinit
21
21
 
22
22
  def self.from(arg)
23
23
  klass =
24
- [Base, Instance, ArrayOf, SetOf, HashOf, Check, TypeOf, Const].find do |type_class|
24
+ [Base, TypeOf, ArrayOf, SetOf, Schema, Check, Type, Const].find do |type_class|
25
25
  type_class.from?(arg)
26
26
  end
27
27
 
data/uinit-type.gemspec CHANGED
@@ -28,10 +28,5 @@ Gem::Specification.new do |spec|
28
28
 
29
29
  spec.add_runtime_dependency 'zeitwerk', '~> 2.6'
30
30
 
31
- spec.add_development_dependency 'bundler'
32
- spec.add_development_dependency 'pry'
33
- spec.add_development_dependency 'rspec'
34
- spec.add_development_dependency 'rubocop'
35
- spec.add_development_dependency 'rake'
36
31
  spec.metadata['rubygems_mfa_required'] = 'true'
37
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uinit-type
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kimoja
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-09-18 00:00:00.000000000 Z
11
+ date: 2025-08-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: zeitwerk
@@ -24,76 +24,6 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: '2.6'
27
- - !ruby/object:Gem::Dependency
28
- name: bundler
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: pry
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: rspec
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: rubocop
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - ">="
74
- - !ruby/object:Gem::Version
75
- version: '0'
76
- type: :development
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - ">="
81
- - !ruby/object:Gem::Version
82
- version: '0'
83
- - !ruby/object:Gem::Dependency
84
- name: rake
85
- requirement: !ruby/object:Gem::Requirement
86
- requirements:
87
- - - ">="
88
- - !ruby/object:Gem::Version
89
- version: '0'
90
- type: :development
91
- prerelease: false
92
- version_requirements: !ruby/object:Gem::Requirement
93
- requirements:
94
- - - ">="
95
- - !ruby/object:Gem::Version
96
- version: '0'
97
27
  description: Dynamic type checker
98
28
  email:
99
29
  - joakim.carrilho@cheerz.com
@@ -113,13 +43,16 @@ files:
113
43
  - lib/uinit/type/context.rb
114
44
  - lib/uinit/type/error.rb
115
45
  - lib/uinit/type/extensions.rb
46
+ - lib/uinit/type/float.rb
116
47
  - lib/uinit/type/fn.rb
117
48
  - lib/uinit/type/generic.rb
118
- - lib/uinit/type/hash_of.rb
119
49
  - lib/uinit/type/impl.rb
120
- - lib/uinit/type/instance.rb
50
+ - lib/uinit/type/integer.rb
121
51
  - lib/uinit/type/operators.rb
52
+ - lib/uinit/type/schema.rb
122
53
  - lib/uinit/type/set_of.rb
54
+ - lib/uinit/type/string.rb
55
+ - lib/uinit/type/type.rb
123
56
  - lib/uinit/type/type_of.rb
124
57
  - lib/uinit/type/version.rb
125
58
  - uinit-type.gemspec
@@ -148,7 +81,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
148
81
  - !ruby/object:Gem::Version
149
82
  version: '0'
150
83
  requirements: []
151
- rubygems_version: 3.4.6
84
+ rubygems_version: 3.5.3
152
85
  signing_key:
153
86
  specification_version: 4
154
87
  summary: Dynamic type checker
@@ -1,39 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Uinit
4
- module Type
5
- class Instance < Base
6
- def self.from?(class_module)
7
- class_module.is_a?(::Class) || class_module.is_a?(::Module)
8
- end
9
-
10
- def self.from(class_module)
11
- new(class_module) if from?(class_module)
12
- end
13
-
14
- def initialize(class_module)
15
- super()
16
-
17
- raise ArgumentError, 'class_module must be a Class or a Module' unless self.class.from?(class_module)
18
-
19
- @class_module = class_module
20
- end
21
-
22
- attr_reader :class_module
23
-
24
- def is?(value)
25
- value.is_a?(class_module)
26
- end
27
-
28
- def check!(value, depth)
29
- return value if is?(value)
30
-
31
- type_error!("#{value.inspect} must be an instance of #{class_module}", depth)
32
- end
33
-
34
- def inspect
35
- "#{super}[#{class_module.inspect}]"
36
- end
37
- end
38
- end
39
- end