types 0.3.0 → 0.4.0

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: d9009b2973c8d9b30fbc68da3eb4088ef28672339e593e63ed92f7f1685daf35
4
- data.tar.gz: 45cbaae7e30b4603f59e7b95e8d85768edd8c8ded3df216c2c7087fdae839216
3
+ metadata.gz: 21a7be2b61d63c443516b9dcd51088fd9febe5b4a75361d99ccf0c3348e15261
4
+ data.tar.gz: 76ad7f162e268407e20a17cb2cf89701675a9672ed352ba27c4746d9bdabd3a2
5
5
  SHA512:
6
- metadata.gz: 1cbda8d764a40a9adb42358620e21a5091dd98dee66904d14e170a96f58fdfc744ad4c613beccb5ec0f52ec17ca2b61261f4a58c664e7eb5bbe41b7788f95f88
7
- data.tar.gz: 34341cc300faea4cfb3053388d928d84b32432e128c17ba3c7641ea757d65fbaab706683352731e6b13c67620e0e7f9d32bc84a9957f390346e69b70fc9defc9
6
+ metadata.gz: 5172ce882b220e0555aea69d66568b53968a3f07139596a00864c72cadfc9a729ccabb4fa34bad26ff8b356e4b2eb52e2596540777fd80d2b9c3fcc4a49b297f
7
+ data.tar.gz: dad9326a9a932c28d413dfe30c6b19451d2f6c5e8e2f302ec8b4d4b2a469a9b9e7a52bad8571bb809e8d0e39f934f97e0635aae7c09b6e845020b2d7a47a570b
checksums.yaml.gz.sig CHANGED
Binary file
data/lib/types/any.rb CHANGED
@@ -4,7 +4,7 @@
4
4
  # Copyright, 2022-2025, by Samuel Williams.
5
5
 
6
6
  module Types
7
- # Represents a union of multiple types. The first type to match the input is used.
7
+ # Represents a union of multiple types. The first type to match the input is used. If no types are specified, this matches any type or value.
8
8
  #
9
9
  # ```ruby
10
10
  # type = Types::Any(Types::String, Types::Integer)
@@ -15,23 +15,29 @@ module Types
15
15
  def initialize(types)
16
16
  @types = types
17
17
  end
18
-
18
+
19
19
  # @returns [Any] a new {Any} with the other type appended.
20
20
  # @parameter other [Type] The type instance to append.
21
21
  def | other
22
22
  self.class.new([*@types, other])
23
23
  end
24
-
24
+
25
25
  # @returns [Boolean] true if any of the listed types is composite.
26
26
  def composite?
27
- @types.any? {|type| type.composite?}
27
+ @types.any?(&:composite?)
28
28
  end
29
-
29
+
30
30
  # Parses the input using the listed types in order, returning the first one that succeeds.
31
31
  # @parameter input [String] the input to parse.
32
32
  # @returns [Object] the parsed value.
33
33
  # @raises [ArgumentError] if no type can parse the input.
34
34
  def parse(input)
35
+ # If there are no types, we can just return the input.
36
+ return input if @types.empty?
37
+
38
+ # We need to track the last error, because we want to raise the last error that occurred, if any:
39
+ last_error = nil
40
+
35
41
  @types.each do |type|
36
42
  return type.parse(input)
37
43
  rescue => error
@@ -59,9 +65,14 @@ module Types
59
65
  end
60
66
  end
61
67
 
68
+ # Returns the RBS type string for the union of the listed types. If there are no types, it returns `untyped`.
62
69
  # @returns [String] the RBS type string, e.g. `String | Integer`.
63
70
  def to_rbs
64
- @types.map(&:to_rbs).join(" | ")
71
+ if @types.empty?
72
+ "untyped"
73
+ else
74
+ @types.map(&:to_rbs).join(" | ")
75
+ end
65
76
  end
66
77
  end
67
78
 
data/lib/types/array.rb CHANGED
@@ -57,6 +57,12 @@ module Types
57
57
  "Array[#{@item_type.to_rbs}]"
58
58
  end
59
59
 
60
+ # Resolves to the actual Ruby Array class.
61
+ # @returns [Class] The Array class.
62
+ def resolve
63
+ ::Array
64
+ end
65
+
60
66
  private
61
67
 
62
68
  def parse_string(input)
data/lib/types/boolean.rb CHANGED
@@ -29,10 +29,16 @@ module Types
29
29
  raise ArgumentError, "Cannot coerce #{input.inspect} into Boolean!"
30
30
  end
31
31
  end
32
-
32
+
33
33
  # @returns [String] the RBS type string, e.g. `bool`.
34
34
  def self.to_rbs
35
35
  "bool"
36
36
  end
37
+
38
+ # Resolves to nil since Ruby doesn't have a single Boolean class.
39
+ # @returns [nil] Returns nil as there's no single Ruby Boolean class.
40
+ def self.resolve
41
+ nil
42
+ end
37
43
  end
38
44
  end
data/lib/types/class.rb CHANGED
@@ -29,15 +29,24 @@ module Types
29
29
  true
30
30
  end
31
31
 
32
+ # Resolve the base class if possible.
33
+ # @returns [Class] the resolved base class.
34
+ def resolve
35
+ Object.const_get(@base.to_s)
36
+ rescue NameError
37
+ nil
38
+ end
39
+
32
40
  # Parses the input as a class, optionally checking the base class constraint.
33
41
  # @parameter input [String] The class name to parse.
34
42
  # @returns [Class] the parsed class.
35
43
  # @raises [ArgumentError] if the class is not a subclass of the base.
36
44
  def parse(input)
37
45
  klass = Object.const_get(input)
46
+ base = self.resolve
38
47
 
39
- if @base and !klass.ancestors.include?(@base)
40
- raise ArgumentError, "Class #{klass} is not a subclass of #{@base}!"
48
+ if base and !klass.ancestors.include?(base)
49
+ raise ArgumentError, "Class #{klass} is not a subclass of #{base}!"
41
50
  end
42
51
 
43
52
  return klass
@@ -66,6 +75,12 @@ module Types
66
75
 
67
76
  return klass
68
77
  end
78
+
79
+ # Resolves to the actual Ruby Class class.
80
+ # @returns [Class] The Class class.
81
+ def self.resolve
82
+ ::Class
83
+ end
69
84
  end
70
85
 
71
86
  # Constructs a {Class} type with an optional base class constraint.
data/lib/types/decimal.rb CHANGED
@@ -23,10 +23,19 @@ module Types
23
23
  def self.parse(input)
24
24
  case input
25
25
  when ::Float
26
- BigDecimal(input, ::Float::DIG+1)
26
+ Kernel.BigDecimal(input, ::Float::DIG+1)
27
27
  else
28
- BigDecimal(input)
28
+ Kernel.BigDecimal(input)
29
29
  end
30
30
  end
31
+
32
+ # Resolves to the actual Ruby BigDecimal class.
33
+ # @returns [Class] The BigDecimal class.
34
+ def self.resolve
35
+ ::BigDecimal
36
+ end
31
37
  end
38
+
39
+ # Alias for the Decimal type.
40
+ BigDecimal = Decimal
32
41
  end
data/lib/types/float.rb CHANGED
@@ -27,5 +27,11 @@ module Types
27
27
  def self.to_rbs
28
28
  "Float"
29
29
  end
30
+
31
+ # Resolves to the actual Ruby Float class.
32
+ # @returns [Class] The Float class.
33
+ def self.resolve
34
+ ::Float
35
+ end
30
36
  end
31
37
  end
data/lib/types/hash.rb CHANGED
@@ -52,6 +52,12 @@ module Types
52
52
  "{ #{@key_type.to_rbs} => #{@value_type.to_rbs} }"
53
53
  end
54
54
 
55
+ # Resolves to the actual Ruby Hash class.
56
+ # @returns [Class] The Hash class.
57
+ def resolve
58
+ ::Hash
59
+ end
60
+
55
61
  private
56
62
 
57
63
  def parse_string(input)
data/lib/types/integer.rb CHANGED
@@ -27,5 +27,11 @@ module Types
27
27
  def self.to_rbs
28
28
  "Integer"
29
29
  end
30
+
31
+ # Resolves to the actual Ruby Integer class.
32
+ # @returns [Class] The Integer class.
33
+ def self.resolve
34
+ ::Integer
35
+ end
30
36
  end
31
37
  end
data/lib/types/method.rb CHANGED
@@ -57,7 +57,7 @@ module Types
57
57
 
58
58
  # @returns [String] the RBS type string, e.g. `Method[Receiver, (Args) -> Return]`.
59
59
  def to_rbs
60
- argument_types = @argument_types.map {|t| t.to_rbs}.join(", ")
60
+ argument_types = @argument_types.map(&:to_rbs).join(", ")
61
61
  return_type = @return_type ? @return_type.to_rbs : "void"
62
62
 
63
63
  return "Method[#{@receiver_type}, (#{argument_types}) -> #{return_type}]"
@@ -0,0 +1,106 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Released under the MIT License.
4
+ # Copyright, 2025, by Samuel Williams.
5
+
6
+ require_relative "generic"
7
+
8
+ module Types
9
+ # Represents a named type that may not be defined yet.
10
+ #
11
+ # ```ruby
12
+ # type = Types::Named("CustomType")
13
+ # type.parse(value) # => value (pass-through)
14
+ # ```
15
+ class Named < Module
16
+ include Generic
17
+
18
+ # Initialize with a type name.
19
+ # @parameter name [String] The name of the type.
20
+ def initialize(name)
21
+ @name = name
22
+ end
23
+
24
+ # @returns [String] The name of the type.
25
+ attr :name
26
+
27
+ # @returns [Boolean] whether the type is absolute.
28
+ def absolute?
29
+ @name.start_with?("::")
30
+ end
31
+
32
+ # @returns [Boolean] whether the type is relative.
33
+ def relative?
34
+ !absolute?
35
+ end
36
+
37
+ # Parses the input by passing it through unchanged.
38
+ # @parameter input [Object] The value to parse.
39
+ # @returns [Object] The input value unchanged.
40
+ def parse(input)
41
+ if resolved = self.resolve
42
+ if resolved.respond_to?(:load)
43
+ return resolved.load(input)
44
+ elsif resolved.respond_to?(:parse)
45
+ return resolved.parse(input)
46
+ else
47
+ raise ArgumentError, "Type #{@name} does not implement .load or .parse!"
48
+ end
49
+ else
50
+ raise ArgumentError, "Unknown type: #{@name}"
51
+ end
52
+ end
53
+
54
+ # Resolves the named type to the actual Ruby type if it exists.
55
+ # @returns [Class | Module | Nil] The resolved Ruby type or nil if not found.
56
+ def resolve
57
+ Object.const_get(@name)
58
+ rescue NameError
59
+ nil
60
+ end
61
+
62
+ # @returns [String] the RBS type string using the name.
63
+ def to_rbs
64
+ @name
65
+ end
66
+
67
+ # @returns [String] the string representation of the named type.
68
+ def to_s
69
+ @name
70
+ end
71
+
72
+ def inspect
73
+ "<#{self.class} #{@name}>"
74
+ end
75
+
76
+ # @returns [Boolean] true if other is a Named type with the same name.
77
+ def == other
78
+ other.is_a?(Named) && @name == other.name
79
+ end
80
+
81
+ # @returns [Integer] hash code based on the name.
82
+ def hash
83
+ @name.hash
84
+ end
85
+
86
+ # @returns [Boolean] whether this type is composite.
87
+ def composite?
88
+ false
89
+ end
90
+
91
+ # Handles missing constants by creating nested Named types.
92
+ # This allows parsing of nested type signatures like Foo::Bar.
93
+ # @parameter name [Symbol] The name of the missing constant.
94
+ # @returns [Named] A Named type representing the nested unknown type.
95
+ def const_missing(name)
96
+ Named.new("#{@name}::#{name}")
97
+ end
98
+ end
99
+
100
+ # Constructs a {Named} type with the given name.
101
+ # @parameter name [String] The name of the type.
102
+ # @returns [Named] a new {Named} type.
103
+ def self.Named(name)
104
+ Named.new(name)
105
+ end
106
+ end
data/lib/types/nil.rb CHANGED
@@ -31,5 +31,11 @@ module Types
31
31
  def self.to_rbs
32
32
  "nil"
33
33
  end
34
+
35
+ # Resolves to the actual Ruby NilClass.
36
+ # @returns [Class] The NilClass.
37
+ def self.resolve
38
+ ::NilClass
39
+ end
34
40
  end
35
41
  end
data/lib/types/string.rb CHANGED
@@ -26,5 +26,11 @@ module Types
26
26
  def self.to_rbs
27
27
  "String"
28
28
  end
29
+
30
+ # Resolves to the actual Ruby String class.
31
+ # @returns [Class] The String class.
32
+ def self.resolve
33
+ ::String
34
+ end
29
35
  end
30
36
  end
data/lib/types/symbol.rb CHANGED
@@ -26,5 +26,11 @@ module Types
26
26
  def self.to_rbs
27
27
  "Symbol"
28
28
  end
29
+
30
+ # Resolves to the actual Ruby Symbol class.
31
+ # @returns [Class] The Symbol class.
32
+ def self.resolve
33
+ ::Symbol
34
+ end
29
35
  end
30
36
  end
data/lib/types/tuple.rb CHANGED
@@ -44,6 +44,12 @@ module Types
44
44
  end
45
45
  end
46
46
 
47
+ # Resolves to the actual Ruby Array class, since there is no direct support for tuples in Ruby.
48
+ # @returns [Class] The Array class.
49
+ def resolve
50
+ ::Array
51
+ end
52
+
47
53
  # @returns [String] the string representation of the tuple type.
48
54
  def to_s
49
55
  "Tuple(#{@item_types.join(', ')})"
@@ -51,7 +57,7 @@ module Types
51
57
 
52
58
  # @returns [String] the RBS type string, e.g. `[String, Integer]`.
53
59
  def to_rbs
54
- "[#{@item_types.map {|t| t.to_rbs}.join(', ')}]"
60
+ "[#{@item_types.map(&:to_rbs).join(', ')}]"
55
61
  end
56
62
 
57
63
  private
data/lib/types/version.rb CHANGED
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  # Released under the MIT License.
4
- # Copyright, 2022-2024, by Samuel Williams.
4
+ # Copyright, 2022-2025, by Samuel Williams.
5
5
 
6
6
  module Types
7
- VERSION = "0.3.0"
7
+ VERSION = "0.4.0"
8
8
  end
data/lib/types.rb CHANGED
@@ -17,6 +17,7 @@ require_relative "types/integer"
17
17
  require_relative "types/interface"
18
18
  require_relative "types/lambda"
19
19
  require_relative "types/method"
20
+ require_relative "types/named"
20
21
  require_relative "types/nil"
21
22
  require_relative "types/numeric"
22
23
  require_relative "types/string"
@@ -32,17 +33,34 @@ module Types
32
33
  # ```ruby
33
34
  # Types.parse("Array(String)") # => Types::Array(Types::String)
34
35
  # ```
35
- VALID_SIGNATURE = /\A[a-zA-Z\(\):,_|\s]+\z/
36
-
36
+ VALID_SIGNATURE = /\A[a-zA-Z0-9\(\):,_|\s]+\z/
37
+
37
38
  # Parses a type signature string and returns the corresponding type instance.
38
39
  # @parameter signature [String] The type signature to parse.
39
40
  # @returns [Object] The type instance.
40
41
  # @raises [ArgumentError] if the signature is invalid.
41
42
  def self.parse(signature)
42
43
  if signature =~ VALID_SIGNATURE
43
- eval(signature, binding)
44
+ # Replace leading :: with Top:: to handle absolute type paths
45
+ normalized_signature = signature.gsub(/(?<=\A|\W)::/, "TOP::")
46
+ eval(normalized_signature, binding)
44
47
  else
45
48
  raise ArgumentError, "Invalid type signature: #{signature.inspect}!"
46
49
  end
47
50
  end
51
+
52
+ # Handles absolute type paths by creating absolute Named types for unknown types.
53
+ module TOP
54
+ def self.const_missing(name)
55
+ Named.new("::#{name}")
56
+ end
57
+ end
58
+
59
+ # Handles missing constants by creating Named types for unknown types.
60
+ # This allows parsing of type signatures with unknown types.
61
+ # @parameter name [Symbol] The name of the missing constant.
62
+ # @returns [Named] A Named type representing the unknown type.
63
+ def self.const_missing(name)
64
+ Named(name.to_s)
65
+ end
48
66
  end
data.tar.gz.sig CHANGED
Binary file
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: types
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
@@ -58,6 +58,7 @@ files:
58
58
  - lib/types/interface.rb
59
59
  - lib/types/lambda.rb
60
60
  - lib/types/method.rb
61
+ - lib/types/named.rb
61
62
  - lib/types/nil.rb
62
63
  - lib/types/numeric.rb
63
64
  - lib/types/string.rb
metadata.gz.sig CHANGED
Binary file