camille 0.6.0 → 0.6.1

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