camille 0.6.0 → 0.6.1

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: 5a179a530118d866cdd0d5baa3b90a454a3d4c2a5c77b47d43b3be4132587b13
4
- data.tar.gz: 82341acb5d71fa1209c32c9c2fa09fdde41bbe71890fa13cfe8469b43842489b
3
+ metadata.gz: 7812549c03ae51c6ea3883e982770259b1757ce13b9014e63f21bf87a6522e02
4
+ data.tar.gz: b562c295c761474368defa4aaa5ba544a5c82d54ad013eafcd1fee5305a68b82
5
5
  SHA512:
6
- metadata.gz: 744ac81f6d267007676d123e4b9111a462989adde5f060e58bde32b749c6204f6912bd2b17fcba298b2aca0ee34f91fc534356ce6d3d3acb01d450e24e839168
7
- data.tar.gz: 6bcd20cd62ff6de5c7ffd15666f1f6bf0a4c2d453acc682c109cad901b9cf8e77c488685b97ad8353efbff98ef810e30e4f4286a6e03f9428d11077319989622
6
+ metadata.gz: 924f63558e9540098f5073ed9b806b3e73169324ad5dea5da1967f275922396f81e21db4968ba3298baa3443d65ae09561c8612a87da15dfa1549ea1404cfafb
7
+ data.tar.gz: 60fe1b8a0baf1df20417518f74e977431f7f9df30b1d5a6087bdef96a78b40d6782f2677f9bbdcac44e674cd676456b50c719ec0e194b069edaa8960afc7e7c5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.1
4
+
5
+ ### Fixed
6
+
7
+ * Tuple type checking now checks length.
8
+
3
9
  ## 0.6.0
4
10
 
5
11
  ### Added
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- camille (0.5.16)
4
+ camille (0.6.0)
5
5
  rails (>= 6.1, < 8.1)
6
6
 
7
7
  GEM
@@ -1,6 +1,9 @@
1
1
  require "benchmark/ips"
2
2
 
3
3
  class A
4
+ def is_A?
5
+ true
6
+ end
4
7
  end
5
8
 
6
9
  a = A.new
@@ -22,5 +25,9 @@ Benchmark.ips do |x|
22
25
  a.class.equal?(A)
23
26
  end
24
27
 
28
+ x.report("is_A?") do
29
+ a.is_A?
30
+ end
31
+
25
32
  x.compare!
26
33
  end
@@ -1,8 +1,16 @@
1
+ require 'digest/md5'
2
+
1
3
  module Camille
2
4
  # This class specifies the methods available for all types includeing built-in and custom ones.
3
5
  class BasicType
4
6
  class InvalidTypeError < ::ArgumentError; end
5
7
 
8
+ attr_reader :fingerprint
9
+
10
+ def initialize
11
+ @fingerprint = Digest::MD5.hexdigest self.class.name
12
+ end
13
+
6
14
  def | other
7
15
  Camille::Types::Union.new(self, other)
8
16
  end
@@ -15,11 +23,6 @@ module Camille
15
23
  Camille::Types::Array.new(self)
16
24
  end
17
25
 
18
- def transform_and_check value
19
- transformed = transform value
20
- [check(value), transformed]
21
- end
22
-
23
26
  def transform value
24
27
  value
25
28
  end
@@ -0,0 +1,18 @@
1
+ module Camille
2
+ class Checked
3
+ attr_reader :fingerprint, :value
4
+
5
+ def initialize fingerprint, value
6
+ @fingerprint = fingerprint
7
+ @value = value
8
+ end
9
+
10
+ def checked?
11
+ true
12
+ end
13
+
14
+ def type_error?
15
+ false
16
+ end
17
+ end
18
+ end
@@ -19,13 +19,13 @@ module Camille
19
19
  if intended_status == 200 || intended_status == :ok
20
20
  if render_options.has_key? :json
21
21
  value = render_options[:json]
22
- error, transformed = endpoint.response_type.transform_and_check(value)
23
- if error
22
+ result = endpoint.response_type.check(value)
23
+ if result.type_error?
24
24
  string_io = StringIO.new
25
- Camille::TypeErrorPrinter.new(error).print(string_io)
25
+ Camille::TypeErrorPrinter.new(result).print(string_io)
26
26
  raise TypeError.new("\nType check failed for response.\n#{string_io.string}")
27
27
  else
28
- super(json: transformed)
28
+ super(json: result.value)
29
29
  end
30
30
  else
31
31
  raise ArgumentError.new("Expected key :json for `render` call.")
@@ -0,0 +1,61 @@
1
+ module Camille
2
+ class IntersectionPreprocessor
3
+ class TypeNotCompatibleError < ArgumentError; end
4
+
5
+ class << self
6
+ def process left, right
7
+ if left.instance_of?(Camille::Types::Object) && right.instance_of?(Camille::Types::Object)
8
+ overlapping_keys = left.fields.keys & right.fields.keys
9
+
10
+ new_left_fields = []
11
+ new_left_optional_keys = []
12
+ new_right_fields = []
13
+ new_right_optional_keys = []
14
+
15
+ (left.fields.keys - overlapping_keys).each do |key|
16
+ new_left_fields << [key, left.fields[key]]
17
+ new_left_optional_keys << key if left.optional_keys.include?(key)
18
+ end
19
+
20
+ (right.fields.keys - overlapping_keys).each do |key|
21
+ new_right_fields << [key, right.fields[key]]
22
+ new_right_optional_keys << key if right.optional_keys.include?(key)
23
+ end
24
+
25
+ overlapping_keys.map do |key|
26
+ processed_left, processed_right = IntersectionPreprocessor.process(left.fields[key], right.fields[key])
27
+ new_left_fields << [key, processed_left]
28
+ new_right_fields << [key, processed_right]
29
+
30
+ if left.optional_keys.include?(key) && right.optional_keys.include?(key)
31
+ new_left_optional_keys << key
32
+ new_right_optional_keys << key
33
+ end
34
+ end
35
+
36
+ [
37
+ Camille::Types::Object.new(**(apply_optional_to_fields new_left_fields, new_left_optional_keys).to_h),
38
+ Camille::Types::Object.new(**(apply_optional_to_fields new_right_fields, new_right_optional_keys).to_h)
39
+ ]
40
+ else
41
+ if left.literal == right.literal
42
+ [left, Camille::Types::Any.new]
43
+ else
44
+ raise TypeNotCompatibleError.new "Cannot reconcile type #{left.literal} and type #{right.literal}."
45
+ end
46
+ end
47
+ end
48
+
49
+ private
50
+ def apply_optional_to_fields fields, optional_keys
51
+ fields.map do |key, type|
52
+ if optional_keys.include?(key)
53
+ ["#{key.to_s}?".to_sym, type]
54
+ else
55
+ [key, type]
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,50 @@
1
+ module Camille
2
+ class IntersectionSolver
3
+ class TypeNotCompatibleError < ArgumentError; end
4
+
5
+ class << self
6
+ def solve a, b
7
+ if a.instance_of?(Camille::Types::Object) && b.instance_of?(Camille::Types::Object)
8
+ overlapping_keys = a.fields.keys & b.fields.keys
9
+
10
+ new_fields = []
11
+ new_optional_keys = []
12
+
13
+ [a, b].each do |x|
14
+ (x.fields.keys - overlapping_keys).each do |key|
15
+ new_fields << [key, x.fields[key]]
16
+ new_optional_keys << key if x.optional_keys.include?(key)
17
+ end
18
+ end
19
+
20
+ overlapping_keys.map do |key|
21
+ solved = IntersectionSolver.solve(a.fields[key], b.fields[key])
22
+ new_fields << [key, solved]
23
+ if a.optional_keys.include?(key) && b.optional_keys.include?(key)
24
+ new_optional_keys << key
25
+ end
26
+ end
27
+
28
+ Camille::Types::Object.new(**(apply_optional_to_fields new_fields, new_optional_keys).to_h)
29
+ else
30
+ if a.literal == b.literal
31
+ a
32
+ else
33
+ raise TypeNotCompatibleError.new "Cannot reconcile type #{a.literal} and type #{b.literal}."
34
+ end
35
+ end
36
+ end
37
+
38
+ private
39
+ def apply_optional_to_fields fields, optional_keys
40
+ fields.map do |key, type|
41
+ if optional_keys.include?(key)
42
+ ["#{key.to_s}?".to_sym, type]
43
+ else
44
+ [key, type]
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -18,6 +18,11 @@ module Camille
18
18
  raise ArgumentError.new("The second argument of #{klass_name} has to be an array of symbols. Got #{keys.inspect}.")
19
19
  end
20
20
  @keys = keys
21
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@type.fingerprint}#{@keys.sort}"
22
+ end
23
+
24
+ def check value
25
+ processed_object.check(value)
21
26
  end
22
27
 
23
28
  def literal
data/lib/camille/type.rb CHANGED
@@ -11,24 +11,22 @@ module Camille
11
11
 
12
12
  def self.alias_of type
13
13
  underlying = Camille::Type.instance(type)
14
+ fingerprint = underlying.fingerprint
14
15
 
15
16
  define_method(:initialize) do
16
17
  @underlying = underlying
18
+ @fingerprint = fingerprint
17
19
  end
18
20
  end
19
21
 
20
22
  def check value
21
- @underlying.check value
22
- end
23
-
24
- def transform_and_check value
25
- transformed = transform value
26
- @underlying.transform_and_check transformed
23
+ normalized = transform value
24
+ @underlying.check normalized
27
25
  end
28
26
 
29
27
  def test value
30
- error, _ = transform_and_check value
31
- error
28
+ result = check value
29
+ result.type_error? ? result : nil
32
30
  end
33
31
 
34
32
  def self.test value
@@ -27,5 +27,13 @@ module Camille
27
27
  def to_s
28
28
  inspect
29
29
  end
30
+
31
+ def checked?
32
+ false
33
+ end
34
+
35
+ def type_error?
36
+ true
37
+ end
30
38
  end
31
39
  end
@@ -2,6 +2,7 @@ module Camille
2
2
  module Types
3
3
  class Any < Camille::BasicType
4
4
  def check value
5
+ Camille::Checked.new(fingerprint, value)
5
6
  end
6
7
 
7
8
  def literal
@@ -5,25 +5,30 @@ module Camille
5
5
 
6
6
  def initialize content
7
7
  @content = Camille::Type.instance content
8
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@content.fingerprint}"
8
9
  end
9
10
 
10
- def transform_and_check value
11
+ def check value
11
12
  if value.is_a? ::Array
12
- transform_and_check_results = value.map.with_index do |element, index|
13
- [index, @content.transform_and_check(element)]
13
+ results = value.map.with_index do |element, index|
14
+ [index, @content.check(element)]
14
15
  end
15
- errors = transform_and_check_results.map do |index, (error, transformed)|
16
- ["array[#{index}]", error]
17
- end.select{|x| x[1]}
16
+
17
+ errors = results.map do |index, result|
18
+ if result.type_error?
19
+ ["array[#{index}]", result]
20
+ else
21
+ nil
22
+ end
23
+ end.compact
18
24
 
19
25
  if errors.empty?
20
- transformed = transform_and_check_results.map{|_, (_, transformed)| transformed}
21
- [nil, transformed]
26
+ Camille::Checked.new(fingerprint, results.map{|_, checked| checked.value})
22
27
  else
23
- [Camille::TypeError.new(**errors.to_h), nil]
28
+ Camille::TypeError.new(**errors.to_h)
24
29
  end
25
30
  else
26
- [Camille::TypeError.new("Expected array, got #{value.inspect}."), nil]
31
+ Camille::TypeError.new("Expected array, got #{value.inspect}.")
27
32
  end
28
33
  end
29
34
 
@@ -2,7 +2,9 @@ module Camille
2
2
  module Types
3
3
  class Boolean < Camille::BasicType
4
4
  def check value
5
- unless value == false || value == true
5
+ if value == false || value == true
6
+ Camille::Checked.new(fingerprint, value)
7
+ else
6
8
  Camille::TypeError.new("Expected boolean, got #{value.inspect}.")
7
9
  end
8
10
  end
@@ -8,13 +8,16 @@ module Camille
8
8
  def initialize value
9
9
  if value == true || value == false
10
10
  @value = value
11
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@value}"
11
12
  else
12
13
  raise ArgumentError.new("Expecting true or false, got #{value.inspect}")
13
14
  end
14
15
  end
15
16
 
16
17
  def check value
17
- unless value == @value
18
+ if value == @value
19
+ Camille::Checked.new(fingerprint, value)
20
+ else
18
21
  Camille::TypeError.new("Expected boolean literal #{@value.inspect}, got #{value.inspect}.")
19
22
  end
20
23
  end
@@ -8,24 +8,23 @@ module Camille
8
8
  def initialize left, right
9
9
  @left = Camille::Type.instance left
10
10
  @right = Camille::Type.instance right
11
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{[@left.fingerprint, @right.fingerprint].sort}"
11
12
  end
12
13
 
13
- def transform_and_check value
14
- left_error, left_transformed = @left.transform_and_check value
15
- if left_error
16
- error = Camille::TypeError.new(
17
- 'intersection.left' => left_error
14
+ def check value
15
+ left_result = @left.check value
16
+ if left_result.type_error?
17
+ Camille::TypeError.new(
18
+ 'intersection.left' => left_result
18
19
  )
19
- [error, nil]
20
20
  else
21
- right_error, right_transformed = @right.transform_and_check left_transformed
22
- if right_error
23
- error = Camille::TypeError.new(
24
- 'intersection.right' => right_error
21
+ right_result = @right.check left_result.value
22
+ if right_result.type_error?
23
+ Camille::TypeError.new(
24
+ 'intersection.right' => right_result
25
25
  )
26
- [error, nil]
27
26
  else
28
- [nil, right_transformed]
27
+ Camille::Checked.new(fingerprint, right_result.value)
29
28
  end
30
29
  end
31
30
  end
@@ -2,7 +2,9 @@ module Camille
2
2
  module Types
3
3
  class Null < Camille::BasicType
4
4
  def check value
5
- unless value == nil
5
+ if value == nil
6
+ Camille::Checked.new(fingerprint, value)
7
+ else
6
8
  Camille::TypeError.new("Expected nil, got #{value.inspect}.")
7
9
  end
8
10
  end
@@ -1,9 +1,10 @@
1
1
  module Camille
2
2
  module Types
3
3
  class Number < Camille::BasicType
4
-
5
4
  def check value
6
- unless value.is_a?(Integer) || value.is_a?(Float)
5
+ if value.is_a?(Integer) || value.is_a?(Float)
6
+ Camille::Checked.new(fingerprint, value)
7
+ else
7
8
  Camille::TypeError.new("Expected an integer or a float, got #{value.inspect}.")
8
9
  end
9
10
  end
@@ -8,13 +8,16 @@ module Camille
8
8
  def initialize value
9
9
  if value.is_a?(Integer) || value.is_a?(Float)
10
10
  @value = value
11
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@value}"
11
12
  else
12
13
  raise ArgumentError.new("Expecting an integer or a float, got #{value.inspect}")
13
14
  end
14
15
  end
15
16
 
16
17
  def check value
17
- unless value == @value
18
+ if value == @value
19
+ Camille::Checked.new(fingerprint, value)
20
+ else
18
21
  Camille::TypeError.new("Expected number literal #{@value.inspect}, got #{value.inspect}.")
19
22
  end
20
23
  end
@@ -7,37 +7,43 @@ module Camille
7
7
  def initialize fields
8
8
  @optional_keys = []
9
9
  @fields = normalize_fields fields
10
+ @fingerprint = generate_fingerprint
10
11
  end
11
12
 
12
- def transform_and_check value
13
+ def check value
13
14
  if value.is_a? Hash
14
15
  keys = (@fields.keys + value.keys).uniq
15
- transform_and_check_results = keys.map do |key|
16
- if type = @fields[key]
17
- if @optional_keys.include?(key) && value[key].nil?
18
- nil
19
- else
20
- [key, type.transform_and_check(value[key])]
21
- end
16
+ keys_to_check, keys_to_skip = keys.partition{|key| @fields[key]}
17
+
18
+ results = keys_to_check.map do |key|
19
+ type = @fields[key]
20
+ if @optional_keys.include?(key) && value[key].nil?
21
+ nil
22
+ else
23
+ [key, type.check(value[key])]
24
+ end
25
+ end.compact
26
+
27
+ errors = results.map do |key, result|
28
+ if result.type_error?
29
+ [key.to_s, result]
22
30
  else
23
- [key, [nil, value[key]]]
31
+ nil
24
32
  end
25
33
  end.compact
26
34
 
27
- errors = transform_and_check_results.map do |key, (error, transformed)|
28
- [key.to_s, error]
29
- end.select{|x| x[1]}
35
+ skipped_pairs = keys_to_skip.map do |key|
36
+ [key, value[key]]
37
+ end
30
38
 
31
39
  if errors.empty?
32
- transformed = transform_and_check_results.map do |key, (error, transformed)|
33
- [key, transformed]
34
- end.to_h
35
- [nil, Camille::ObjectHash[transformed]]
40
+ object = Camille::ObjectHash[results.map{|key, checked| [key, checked.value]}.concat(skipped_pairs).to_h]
41
+ Camille::Checked.new(fingerprint, object)
36
42
  else
37
- [Camille::TypeError.new(**errors.to_h), nil]
43
+ Camille::TypeError.new(**errors.to_h)
38
44
  end
39
45
  else
40
- [Camille::TypeError.new("Expected hash, got #{value.inspect}."), nil]
46
+ Camille::TypeError.new("Expected hash, got #{value.inspect}.")
41
47
  end
42
48
  end
43
49
 
@@ -74,6 +80,12 @@ module Camille
74
80
  raise ArgumentError.new("Only keys satisfying `key == key.to_s.camelize.underscore` can be used.")
75
81
  end
76
82
  end
83
+
84
+ def generate_fingerprint
85
+ sorted_fields = @fields.sort_by{|k, v| k}.map{|k, v| [k, v.fingerprint]}
86
+ sorted_optional_keys = @optional_keys.sort
87
+ Digest::MD5.hexdigest "#{self.class.name}#{sorted_fields}#{sorted_optional_keys}"
88
+ end
77
89
  end
78
90
  end
79
91
  end
@@ -1,9 +1,6 @@
1
1
  module Camille
2
2
  module Types
3
3
  class Omit < PickAndOmit
4
- def transform_and_check value
5
- processed_object.transform_and_check(value)
6
- end
7
4
 
8
5
  private
9
6
  def klass_name
@@ -1,9 +1,6 @@
1
1
  module Camille
2
2
  module Types
3
3
  class Pick < PickAndOmit
4
- def transform_and_check value
5
- processed_object.transform_and_check(value)
6
- end
7
4
 
8
5
  private
9
6
  def klass_name
@@ -7,27 +7,31 @@ module Camille
7
7
  def initialize key, value
8
8
  @key = Camille::Type.instance key
9
9
  @value = Camille::Type.instance value
10
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@key.fingerprint}#{@value.fingerprint}"
10
11
  end
11
12
 
12
- def transform_and_check value
13
+ def check value
13
14
  if value.is_a? ::Hash
14
15
 
15
- transform_and_check_results = value.map.with_index do |(k, v), index|
16
- [index, transform_and_check_pair(k, v)]
16
+ results = value.map.with_index do |(k, v), index|
17
+ [index, check_pair(k, v)]
17
18
  end
18
19
 
19
- errors = transform_and_check_results.map do |index, (error, transformed)|
20
- ["record[#{index}]", error]
21
- end.select{|x| x[1]}
20
+ errors = results.map do |index, result|
21
+ if result.instance_of?(Camille::TypeError)
22
+ ["record[#{index}]", result]
23
+ else
24
+ nil
25
+ end
26
+ end.compact
22
27
 
23
28
  if errors.empty?
24
- transformed = transform_and_check_results.map{|_, (_, transformed)| transformed}.to_h
25
- [nil, transformed]
29
+ Camille::Checked.new(fingerprint, results.map{|_, result| [result[0].value, result[1].value]}.to_h)
26
30
  else
27
- [Camille::TypeError.new(**errors.to_h), nil]
31
+ Camille::TypeError.new(**errors.to_h)
28
32
  end
29
33
  else
30
- [Camille::TypeError.new("Expected hash, got #{value.inspect}."), nil]
34
+ Camille::TypeError.new("Expected hash, got #{value.inspect}.")
31
35
  end
32
36
  end
33
37
 
@@ -40,17 +44,16 @@ module Camille
40
44
  end
41
45
 
42
46
  private
43
- def transform_and_check_pair key, value
44
- key_error, key_transformed = @key.transform_and_check key
45
- value_error, value_transformed = @value.transform_and_check value
47
+ def check_pair key, value
48
+ key_result = @key.check key
49
+ value_result = @value.check value
46
50
 
47
- if key_error.nil? && value_error.nil?
48
- [nil, [key_transformed, value_transformed]]
51
+ if key_result.checked? && value_result.checked?
52
+ [key_result, value_result]
49
53
  else
50
- errors = [['record.key', key_error], ['record.value', value_error]].select{|x| x[1]}.to_h
54
+ errors = [['record.key', key_result], ['record.value', value_result]].select{|x| x[1].type_error?}.to_h
51
55
 
52
- error = Camille::TypeError.new(**errors)
53
- [error, nil]
56
+ Camille::TypeError.new(**errors)
54
57
  end
55
58
  end
56
59
  end
@@ -2,7 +2,9 @@ module Camille
2
2
  module Types
3
3
  class String < Camille::BasicType
4
4
  def check value
5
- unless value.is_a?(::String) || value.is_a?(Symbol)
5
+ if value.is_a?(::String) || value.is_a?(Symbol)
6
+ Camille::Checked.new(fingerprint, value)
7
+ else
6
8
  Camille::TypeError.new("Expected string, got #{value.inspect}.")
7
9
  end
8
10
  end
@@ -8,6 +8,7 @@ module Camille
8
8
  def initialize value
9
9
  if value.is_a?(::String)
10
10
  @value = value
11
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@value}"
11
12
  else
12
13
  raise ArgumentError.new("Expecting a string, got #{value.inspect}")
13
14
  end
@@ -15,7 +16,9 @@ module Camille
15
16
 
16
17
  def check value
17
18
  transformed = value.is_a?(Symbol) ? value.to_s : value
18
- unless transformed == @value
19
+ if transformed == @value
20
+ Camille::Checked.new(fingerprint, value)
21
+ else
19
22
  Camille::TypeError.new("Expected string literal #{@value.inspect}, got #{value.inspect}.")
20
23
  end
21
24
  end
@@ -5,26 +5,30 @@ module Camille
5
5
 
6
6
  def initialize elements
7
7
  @elements = elements.map{|e| Camille::Type.instance e}
8
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{@elements.map(&:fingerprint)}"
8
9
  end
9
10
 
10
- def transform_and_check value
11
- if value.is_a? ::Array
12
- transform_and_check_results = @elements.map.with_index do |type, index|
13
- [index, type.transform_and_check(value[index])]
11
+ def check value
12
+ if value.is_a?(::Array) && value.size == @elements.size
13
+ results = @elements.map.with_index do |type, index|
14
+ [index, type.check(value[index])]
14
15
  end
15
16
 
16
- errors = transform_and_check_results.map do |index, (error, transformed)|
17
- ["tuple[#{index}]", error]
18
- end.select{|x| x[1]}
17
+ errors = results.map do |index, result|
18
+ if result.type_error?
19
+ ["tuple[#{index}]", result]
20
+ else
21
+ nil
22
+ end
23
+ end.compact
19
24
 
20
25
  if errors.empty?
21
- transformed = transform_and_check_results.map{|index, (error, transformed)| transformed}
22
- [nil, transformed]
26
+ Camille::Checked.new(fingerprint, results.map{|_, checked| checked.value})
23
27
  else
24
- [Camille::TypeError.new(**errors.to_h), nil]
28
+ Camille::TypeError.new(**errors.to_h)
25
29
  end
26
30
  else
27
- [Camille::TypeError.new("Expected array, got #{value.inspect}."), nil]
31
+ Camille::TypeError.new("Expected array of size #{@elements.size}, got #{value.inspect}.")
28
32
  end
29
33
  end
30
34
 
@@ -2,7 +2,9 @@ module Camille
2
2
  module Types
3
3
  class Undefined < Camille::BasicType
4
4
  def check value
5
- unless value == nil
5
+ if value == nil
6
+ Camille::Checked.new(fingerprint, value)
7
+ else
6
8
  Camille::TypeError.new("Expected nil, got #{value.inspect}.")
7
9
  end
8
10
  end
@@ -6,23 +6,23 @@ module Camille
6
6
  def initialize left, right
7
7
  @left = Camille::Type.instance left
8
8
  @right = Camille::Type.instance right
9
+ @fingerprint = Digest::MD5.hexdigest "#{self.class.name}#{[@left.fingerprint, @right.fingerprint].sort}"
9
10
  end
10
11
 
11
- def transform_and_check value
12
- left_error, left_transformed = @left.transform_and_check value
13
- if left_error
14
- right_error, right_transformed = @right.transform_and_check value
15
- if right_error
16
- error = Camille::TypeError.new(
17
- 'union.left' => left_error,
18
- 'union.right' => right_error
12
+ def check value
13
+ left_result = @left.check value
14
+ if left_result.type_error?
15
+ right_result = @right.check value
16
+ if right_result.type_error?
17
+ Camille::TypeError.new(
18
+ 'union.left' => left_result,
19
+ 'union.right' => right_result
19
20
  )
20
- [error, nil]
21
21
  else
22
- [nil, right_transformed]
22
+ Camille::Checked.new(fingerprint, right_result.value)
23
23
  end
24
24
  else
25
- [nil, left_transformed]
25
+ Camille::Checked.new(fingerprint, left_result.value)
26
26
  end
27
27
  end
28
28
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Camille
4
- VERSION = "0.6.0"
4
+ VERSION = "0.6.1"
5
5
  end
data/lib/camille.rb CHANGED
@@ -4,6 +4,7 @@ require "active_support"
4
4
 
5
5
  require_relative "camille/version"
6
6
  require_relative "camille/object_hash"
7
+ require_relative "camille/checked"
7
8
  require_relative "camille/basic_type"
8
9
  require_relative "camille/types"
9
10
  require_relative "camille/types/number"
@@ -14,6 +15,8 @@ require_relative "camille/types/object"
14
15
  require_relative "camille/types/null"
15
16
  require_relative "camille/types/undefined"
16
17
  require_relative "camille/types/union"
18
+ require_relative "camille/intersection_solver"
19
+ require_relative "camille/intersection_preprocessor"
17
20
  require_relative "camille/types/intersection"
18
21
  require_relative "camille/types/tuple"
19
22
  require_relative "camille/types/any"
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: camille
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - merely
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-12-13 00:00:00.000000000 Z
11
+ date: 2024-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -52,6 +52,7 @@ files:
52
52
  - gemfiles/rails-8.0
53
53
  - lib/camille.rb
54
54
  - lib/camille/basic_type.rb
55
+ - lib/camille/checked.rb
55
56
  - lib/camille/code_generator.rb
56
57
  - lib/camille/configuration.rb
57
58
  - lib/camille/controller.rb
@@ -65,6 +66,8 @@ files:
65
66
  - lib/camille/generators/templates/schema_template.erb
66
67
  - lib/camille/generators/templates/type_template.erb
67
68
  - lib/camille/generators/type_generator.rb
69
+ - lib/camille/intersection_preprocessor.rb
70
+ - lib/camille/intersection_solver.rb
68
71
  - lib/camille/key_converter.rb
69
72
  - lib/camille/line.rb
70
73
  - lib/camille/loader.rb