typed_attrs 0.1.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 +7 -0
- data/CHANGELOG.md +22 -0
- data/LICENSE +21 -0
- data/README.md +355 -0
- data/lib/active_model/validations/typed_attrs_validator.rb +48 -0
- data/lib/tapioca/dsl/compilers/typed_attribute.rb +87 -0
- data/lib/typed_attrs/discriminated_union.rb +51 -0
- data/lib/typed_attrs/type_helpers.rb +45 -0
- data/lib/typed_attrs/type_processors/array.rb +88 -0
- data/lib/typed_attrs/type_processors/base.rb +45 -0
- data/lib/typed_attrs/type_processors/boolean.rb +39 -0
- data/lib/typed_attrs/type_processors/boolean_primitive.rb +39 -0
- data/lib/typed_attrs/type_processors/date.rb +47 -0
- data/lib/typed_attrs/type_processors/discriminated_union.rb +211 -0
- data/lib/typed_attrs/type_processors/float.rb +42 -0
- data/lib/typed_attrs/type_processors/hash.rb +116 -0
- data/lib/typed_attrs/type_processors/integer.rb +39 -0
- data/lib/typed_attrs/type_processors/nilable_type.rb +53 -0
- data/lib/typed_attrs/type_processors/registry.rb +35 -0
- data/lib/typed_attrs/type_processors/simple_type.rb +38 -0
- data/lib/typed_attrs/type_processors/string.rb +39 -0
- data/lib/typed_attrs/type_processors/struct.rb +70 -0
- data/lib/typed_attrs/type_processors/symbol.rb +41 -0
- data/lib/typed_attrs/type_processors/time.rb +47 -0
- data/lib/typed_attrs/type_processors.rb +36 -0
- data/lib/typed_attrs/types/type.rb +51 -0
- data/lib/typed_attrs/version.rb +6 -0
- data/lib/typed_attrs.rb +103 -0
- metadata +117 -0
@@ -0,0 +1,53 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class NilableType < Base
|
7
|
+
extend T::Sig
|
8
|
+
extend TypeHelpers
|
9
|
+
|
10
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
11
|
+
def self.handles?(type_spec)
|
12
|
+
nilable_type?(type_spec)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { params(type_spec: T.untyped, registry: Registry).void }
|
16
|
+
def initialize(type_spec, registry:)
|
17
|
+
super
|
18
|
+
|
19
|
+
@inner_type = T.let(self.class.extract_nilable_inner_type(type_spec), T.untyped)
|
20
|
+
@inner_serializer = T.let(@registry.serializer_for(@inner_type), Base)
|
21
|
+
end
|
22
|
+
|
23
|
+
sig { override.params(value: T.untyped).returns(T.untyped) }
|
24
|
+
def serialize(value)
|
25
|
+
return nil if value.nil?
|
26
|
+
|
27
|
+
@inner_serializer.serialize(value)
|
28
|
+
end
|
29
|
+
|
30
|
+
sig { override.params(value: T.untyped).returns(T.untyped) }
|
31
|
+
def deserialize(value)
|
32
|
+
return nil if value.nil?
|
33
|
+
|
34
|
+
@inner_serializer.deserialize(value)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig do
|
38
|
+
override.params(
|
39
|
+
value: T.untyped,
|
40
|
+
path: ::String,
|
41
|
+
blk: T.proc.params(type_spec: T.untyped, node: T.untyped, path: ::String).void
|
42
|
+
).void
|
43
|
+
end
|
44
|
+
def traverse(value, path: "", &blk)
|
45
|
+
super
|
46
|
+
|
47
|
+
return if value.nil?
|
48
|
+
|
49
|
+
@inner_serializer.traverse(value, path: path, &blk)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class UnknownTypeError < StandardError; end
|
7
|
+
|
8
|
+
class Registry
|
9
|
+
extend T::Sig
|
10
|
+
|
11
|
+
sig { void }
|
12
|
+
def initialize
|
13
|
+
@serializers = T.let([], T::Array[T.class_of(Base)])
|
14
|
+
end
|
15
|
+
|
16
|
+
sig { params(serializer_class: T.class_of(Base)).void }
|
17
|
+
def register(serializer_class)
|
18
|
+
@serializers << serializer_class
|
19
|
+
end
|
20
|
+
|
21
|
+
sig { params(type_spec: T.untyped).returns(T.class_of(Base)) }
|
22
|
+
def find_serializer(type_spec)
|
23
|
+
serializer_class = @serializers.find { |s| s.handles?(type_spec) }
|
24
|
+
raise UnknownTypeError, "No serializer for #{type_spec}" unless serializer_class
|
25
|
+
|
26
|
+
serializer_class
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(type_spec: T.untyped).returns(Base) }
|
30
|
+
def serializer_for(type_spec)
|
31
|
+
find_serializer(type_spec).new(type_spec, registry: self)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class SimpleType < Base
|
7
|
+
extend T::Sig
|
8
|
+
include TypeHelpers
|
9
|
+
|
10
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
11
|
+
def self.handles?(type_spec)
|
12
|
+
type_spec.is_a?(T::Types::Simple)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { params(type_spec: T.untyped, registry: Registry).void }
|
16
|
+
def initialize(type_spec, registry:)
|
17
|
+
super
|
18
|
+
unless type_spec.is_a?(T::Types::Simple)
|
19
|
+
raise ArgumentError, "Expected T::Types::Simple, got #{type_spec.class}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@raw_type = T.let(type_spec.raw_type, T.untyped)
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { override.params(value: T.untyped).returns(T.untyped) }
|
26
|
+
def serialize(value)
|
27
|
+
serializer = @registry.serializer_for(@raw_type)
|
28
|
+
serializer.serialize(value)
|
29
|
+
end
|
30
|
+
|
31
|
+
sig { override.params(value: T.untyped).returns(T.untyped) }
|
32
|
+
def deserialize(value)
|
33
|
+
serializer = @registry.serializer_for(@raw_type)
|
34
|
+
serializer.deserialize(value)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class String < Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
10
|
+
def self.handles?(type_spec)
|
11
|
+
type_spec == ::String
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { override.params(value: T.untyped).returns(::String) }
|
15
|
+
def serialize(value)
|
16
|
+
unless value.is_a?(::String)
|
17
|
+
raise TypedAttrs::TypeMismatchError.new(
|
18
|
+
"Expected String, got #{value.class}",
|
19
|
+
value: value
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
value
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(value: T.untyped).returns(::String) }
|
27
|
+
def deserialize(value)
|
28
|
+
unless value.is_a?(::String)
|
29
|
+
raise TypedAttrs::DeserializationError.new(
|
30
|
+
"Expected String, got #{value.class}",
|
31
|
+
value: value
|
32
|
+
)
|
33
|
+
end
|
34
|
+
|
35
|
+
value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class Struct < Base
|
7
|
+
extend T::Sig
|
8
|
+
extend TypeHelpers
|
9
|
+
|
10
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
11
|
+
def self.handles?(type_spec)
|
12
|
+
struct_class?(type_spec)
|
13
|
+
end
|
14
|
+
|
15
|
+
sig { params(type_spec: T.untyped, registry: Registry).void }
|
16
|
+
def initialize(type_spec, registry:)
|
17
|
+
super
|
18
|
+
unless type_spec.is_a?(Class) && type_spec < T::Struct
|
19
|
+
raise ArgumentError, "Expected T::Struct, got #{type_spec}"
|
20
|
+
end
|
21
|
+
|
22
|
+
@struct_class = T.let(type_spec, T.class_of(T::Struct))
|
23
|
+
end
|
24
|
+
|
25
|
+
sig { override.params(value: T.untyped).returns(T::Hash[::String, T.untyped]) }
|
26
|
+
def serialize(value)
|
27
|
+
unless value.is_a?(@struct_class)
|
28
|
+
raise TypedAttrs::TypeMismatchError.new(
|
29
|
+
"Expected #{@struct_class}, got #{value.class}",
|
30
|
+
value: value
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
@struct_class.props.each_with_object({}) do |(name, prop_def), hash|
|
35
|
+
prop_value = value.public_send(name)
|
36
|
+
|
37
|
+
prop_type = prop_def[:type_object]
|
38
|
+
serializer = @registry.serializer_for(prop_type)
|
39
|
+
hash[name.to_s] = serializer.serialize(prop_value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { override.params(value: T.untyped).returns(T::Struct) }
|
44
|
+
def deserialize(value)
|
45
|
+
return value if value.is_a?(@struct_class)
|
46
|
+
|
47
|
+
unless value.is_a?(::Hash)
|
48
|
+
raise TypedAttrs::DeserializationError.new(
|
49
|
+
"Expected Hash, got #{value.class}",
|
50
|
+
value: value
|
51
|
+
)
|
52
|
+
end
|
53
|
+
|
54
|
+
symbolized = value.transform_keys(&:to_sym)
|
55
|
+
|
56
|
+
deserialized = @struct_class.props.each_with_object({}) do |(name, prop_def), hash|
|
57
|
+
next unless symbolized.key?(name)
|
58
|
+
|
59
|
+
prop_value = symbolized[name]
|
60
|
+
|
61
|
+
prop_type = prop_def[:type_object]
|
62
|
+
serializer = @registry.serializer_for(prop_type)
|
63
|
+
hash[name] = serializer.deserialize(prop_value)
|
64
|
+
end
|
65
|
+
|
66
|
+
@struct_class.new(**deserialized)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class Symbol < Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
10
|
+
def self.handles?(type_spec)
|
11
|
+
type_spec == ::Symbol
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { override.params(value: T.untyped).returns(::String) }
|
15
|
+
def serialize(value)
|
16
|
+
unless value.is_a?(::Symbol)
|
17
|
+
raise TypedAttrs::TypeMismatchError.new(
|
18
|
+
"Expected Symbol, got #{value.class}",
|
19
|
+
value: value
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
value.to_s
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(value: T.untyped).returns(::Symbol) }
|
27
|
+
def deserialize(value)
|
28
|
+
return value if value.is_a?(::Symbol)
|
29
|
+
|
30
|
+
unless value.is_a?(::String)
|
31
|
+
raise TypedAttrs::DeserializationError.new(
|
32
|
+
"Expected String or Symbol, got #{value.class}",
|
33
|
+
value: value
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
value.to_sym
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
class Time < Base
|
7
|
+
extend T::Sig
|
8
|
+
|
9
|
+
sig { override.params(type_spec: T.untyped).returns(T::Boolean) }
|
10
|
+
def self.handles?(type_spec)
|
11
|
+
type_spec == ::Time
|
12
|
+
end
|
13
|
+
|
14
|
+
sig { override.params(value: T.untyped).returns(::String) }
|
15
|
+
def serialize(value)
|
16
|
+
unless value.is_a?(::Time)
|
17
|
+
raise TypedAttrs::TypeMismatchError.new(
|
18
|
+
"Expected Time, got #{value.class}",
|
19
|
+
value: value
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
value.iso8601
|
24
|
+
end
|
25
|
+
|
26
|
+
sig { override.params(value: T.untyped).returns(::Time) }
|
27
|
+
def deserialize(value)
|
28
|
+
case value
|
29
|
+
when ::Time
|
30
|
+
value
|
31
|
+
when ::String
|
32
|
+
::Time.iso8601(value)
|
33
|
+
else
|
34
|
+
raise TypedAttrs::DeserializationError.new(
|
35
|
+
"Expected Time or String, got #{value.class}",
|
36
|
+
value: value
|
37
|
+
)
|
38
|
+
end
|
39
|
+
rescue ArgumentError => e
|
40
|
+
raise TypedAttrs::DeserializationError.new(
|
41
|
+
"Failed to deserialize Time: #{e.message}",
|
42
|
+
value: value
|
43
|
+
)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module TypeProcessors
|
6
|
+
extend T::Sig
|
7
|
+
|
8
|
+
sig { returns(Registry) }
|
9
|
+
def self.default_registry
|
10
|
+
@default_registry ||= T.let(build_default_registry, T.nilable(Registry))
|
11
|
+
end
|
12
|
+
|
13
|
+
sig { returns(Registry) }
|
14
|
+
def self.build_default_registry
|
15
|
+
registry = Registry.new
|
16
|
+
|
17
|
+
# Register in search order (more specific types first)
|
18
|
+
registry.register(SimpleType) # Unwrap T::Types::Simple (first)
|
19
|
+
registry.register(NilableType) # Handle T.nilable(...)
|
20
|
+
registry.register(Array) # Handle T::Array[...]
|
21
|
+
registry.register(Hash) # Handle T::Hash[...]
|
22
|
+
registry.register(DiscriminatedUnion) # Handle DiscriminatedUnion
|
23
|
+
registry.register(Integer) # Handle Integer
|
24
|
+
registry.register(Float) # Handle Float
|
25
|
+
registry.register(Boolean) # Handle T::Boolean
|
26
|
+
registry.register(BooleanPrimitive) # Handle TrueClass/FalseClass
|
27
|
+
registry.register(String) # Handle String
|
28
|
+
registry.register(Symbol) # Handle Symbol
|
29
|
+
registry.register(Date) # Handle Date
|
30
|
+
registry.register(Time) # Handle Time
|
31
|
+
registry.register(Struct) # Handle T::Struct (last)
|
32
|
+
|
33
|
+
registry
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
module TypedAttrs
|
5
|
+
module Types
|
6
|
+
class Type < ActiveModel::Type::Value
|
7
|
+
include TypeHelpers
|
8
|
+
extend T::Sig
|
9
|
+
|
10
|
+
sig { params(type_spec: T.untyped).void }
|
11
|
+
def initialize(type_spec)
|
12
|
+
super()
|
13
|
+
@type_spec = type_spec
|
14
|
+
|
15
|
+
registry = T.let(TypeProcessors.default_registry, TypeProcessors::Registry)
|
16
|
+
@processor = T.let(registry.serializer_for(type_spec), TypeProcessors::Base)
|
17
|
+
end
|
18
|
+
|
19
|
+
sig { returns(TypeProcessors::Base) }
|
20
|
+
attr_reader :processor
|
21
|
+
|
22
|
+
sig { params(value: T.untyped).returns(T.untyped) }
|
23
|
+
def cast(value)
|
24
|
+
return nil if value.nil?
|
25
|
+
|
26
|
+
@processor.deserialize(value)
|
27
|
+
end
|
28
|
+
|
29
|
+
sig { params(value: T.untyped).returns(T.untyped) }
|
30
|
+
def serialize(value)
|
31
|
+
return nil if value.nil?
|
32
|
+
|
33
|
+
serialized = @processor.serialize(value)
|
34
|
+
ActiveSupport::JSON.encode(serialized)
|
35
|
+
end
|
36
|
+
|
37
|
+
sig { params(value: T.untyped).returns(T.untyped) }
|
38
|
+
def deserialize(value)
|
39
|
+
return nil if value.nil?
|
40
|
+
|
41
|
+
data = if value.is_a?(String)
|
42
|
+
ActiveSupport::JSON.decode(value)
|
43
|
+
else
|
44
|
+
value
|
45
|
+
end
|
46
|
+
|
47
|
+
@processor.deserialize(data)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/typed_attrs.rb
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
# typed: strict
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "sorbet-runtime"
|
5
|
+
require "active_model"
|
6
|
+
|
7
|
+
require_relative "typed_attrs/version"
|
8
|
+
require_relative "typed_attrs/type_helpers"
|
9
|
+
require_relative "typed_attrs/type_processors/registry"
|
10
|
+
require_relative "typed_attrs/type_processors"
|
11
|
+
require_relative "typed_attrs/type_processors/base"
|
12
|
+
require_relative "typed_attrs/type_processors/simple_type"
|
13
|
+
require_relative "typed_attrs/type_processors/nilable_type"
|
14
|
+
require_relative "typed_attrs/type_processors/integer"
|
15
|
+
require_relative "typed_attrs/type_processors/float"
|
16
|
+
require_relative "typed_attrs/type_processors/boolean"
|
17
|
+
require_relative "typed_attrs/type_processors/boolean_primitive"
|
18
|
+
require_relative "typed_attrs/type_processors/string"
|
19
|
+
require_relative "typed_attrs/type_processors/symbol"
|
20
|
+
require_relative "typed_attrs/type_processors/date"
|
21
|
+
require_relative "typed_attrs/type_processors/time"
|
22
|
+
require_relative "typed_attrs/type_processors/struct"
|
23
|
+
require_relative "typed_attrs/type_processors/array"
|
24
|
+
require_relative "typed_attrs/type_processors/hash"
|
25
|
+
require_relative "typed_attrs/type_processors/discriminated_union"
|
26
|
+
require_relative "typed_attrs/types/type"
|
27
|
+
require_relative "typed_attrs/discriminated_union"
|
28
|
+
require_relative "active_model/validations/typed_attrs_validator"
|
29
|
+
|
30
|
+
module TypedAttrs
|
31
|
+
extend T::Sig
|
32
|
+
|
33
|
+
# Exception class for type mismatch errors during serialization/deserialization/traversal
|
34
|
+
class TypeMismatchError < StandardError
|
35
|
+
extend T::Sig
|
36
|
+
|
37
|
+
sig { params(message: String, value: T.untyped).void }
|
38
|
+
def initialize(message, value: nil)
|
39
|
+
@value = value
|
40
|
+
super(message)
|
41
|
+
end
|
42
|
+
|
43
|
+
sig { returns(T.untyped) }
|
44
|
+
attr_reader :value
|
45
|
+
end
|
46
|
+
|
47
|
+
# Exception class for deserialization errors
|
48
|
+
class DeserializationError < StandardError
|
49
|
+
extend T::Sig
|
50
|
+
|
51
|
+
sig { params(message: String, value: T.untyped).void }
|
52
|
+
def initialize(message, value: nil)
|
53
|
+
@value = value
|
54
|
+
super(message)
|
55
|
+
end
|
56
|
+
|
57
|
+
sig { returns(T.untyped) }
|
58
|
+
attr_reader :value
|
59
|
+
end
|
60
|
+
|
61
|
+
# Exception class for missing union discriminator value
|
62
|
+
class MissingDiscriminatorValueError < StandardError
|
63
|
+
extend T::Sig
|
64
|
+
|
65
|
+
sig { params(struct_class: T.class_of(T::Struct)).void }
|
66
|
+
def initialize(struct_class)
|
67
|
+
super(
|
68
|
+
"Union discriminator value is not defined for #{struct_class}. " \
|
69
|
+
"Please add 'union_discriminator_value \"value\"' to the class definition."
|
70
|
+
)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class EmptyUnionModuleError < StandardError
|
75
|
+
extend T::Sig
|
76
|
+
|
77
|
+
sig { params(union_module: BasicObject).void }
|
78
|
+
def initialize(union_module)
|
79
|
+
super(
|
80
|
+
"Union module #{union_module} is empty. Please add 'sealed!', 'discriminator_key \"key\"', " \
|
81
|
+
"and at least one T::Struct subclass."
|
82
|
+
)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Exception class for missing union discriminator key
|
87
|
+
class MissingDiscriminatorKeyError < StandardError
|
88
|
+
extend T::Sig
|
89
|
+
|
90
|
+
sig { params(union_module: T.untyped).void }
|
91
|
+
def initialize(union_module)
|
92
|
+
super(
|
93
|
+
"Union discriminator key is not defined for #{union_module}. " \
|
94
|
+
"Please add 'discriminator_key \"key\"' to the module definition."
|
95
|
+
)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
sig { params(type_spec: T.untyped).returns(Types::Type) }
|
100
|
+
def self.to_type(type_spec)
|
101
|
+
Types::Type.new(type_spec)
|
102
|
+
end
|
103
|
+
end
|
metadata
ADDED
@@ -0,0 +1,117 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: typed_attrs
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Speria
|
8
|
+
bindir: bin
|
9
|
+
cert_chain: []
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
|
+
dependencies:
|
12
|
+
- !ruby/object:Gem::Dependency
|
13
|
+
name: activemodel
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - ">="
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '6.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '6.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: activerecord
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - ">="
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6.0'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '6.0'
|
40
|
+
- !ruby/object:Gem::Dependency
|
41
|
+
name: sorbet-runtime
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - "~>"
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0.5'
|
47
|
+
type: :runtime
|
48
|
+
prerelease: false
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - "~>"
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0.5'
|
54
|
+
description: TypedAttrs integrates Sorbet's T::Struct with ActiveRecord's JSON/JSONB
|
55
|
+
columns, providing type-safe structured data storage with runtime and static type
|
56
|
+
checking. Supports nested structs, arrays, hashes, union types, and automatic validation.
|
57
|
+
email:
|
58
|
+
- daichi.sakai@speria.jp
|
59
|
+
executables: []
|
60
|
+
extensions: []
|
61
|
+
extra_rdoc_files: []
|
62
|
+
files:
|
63
|
+
- CHANGELOG.md
|
64
|
+
- LICENSE
|
65
|
+
- README.md
|
66
|
+
- lib/active_model/validations/typed_attrs_validator.rb
|
67
|
+
- lib/tapioca/dsl/compilers/typed_attribute.rb
|
68
|
+
- lib/typed_attrs.rb
|
69
|
+
- lib/typed_attrs/discriminated_union.rb
|
70
|
+
- lib/typed_attrs/type_helpers.rb
|
71
|
+
- lib/typed_attrs/type_processors.rb
|
72
|
+
- lib/typed_attrs/type_processors/array.rb
|
73
|
+
- lib/typed_attrs/type_processors/base.rb
|
74
|
+
- lib/typed_attrs/type_processors/boolean.rb
|
75
|
+
- lib/typed_attrs/type_processors/boolean_primitive.rb
|
76
|
+
- lib/typed_attrs/type_processors/date.rb
|
77
|
+
- lib/typed_attrs/type_processors/discriminated_union.rb
|
78
|
+
- lib/typed_attrs/type_processors/float.rb
|
79
|
+
- lib/typed_attrs/type_processors/hash.rb
|
80
|
+
- lib/typed_attrs/type_processors/integer.rb
|
81
|
+
- lib/typed_attrs/type_processors/nilable_type.rb
|
82
|
+
- lib/typed_attrs/type_processors/registry.rb
|
83
|
+
- lib/typed_attrs/type_processors/simple_type.rb
|
84
|
+
- lib/typed_attrs/type_processors/string.rb
|
85
|
+
- lib/typed_attrs/type_processors/struct.rb
|
86
|
+
- lib/typed_attrs/type_processors/symbol.rb
|
87
|
+
- lib/typed_attrs/type_processors/time.rb
|
88
|
+
- lib/typed_attrs/types/type.rb
|
89
|
+
- lib/typed_attrs/version.rb
|
90
|
+
homepage: https://github.com/speria-jp/typed_attrs
|
91
|
+
licenses:
|
92
|
+
- MIT
|
93
|
+
metadata:
|
94
|
+
homepage_uri: https://github.com/speria-jp/typed_attrs
|
95
|
+
source_code_uri: https://github.com/speria-jp/typed_attrs.git
|
96
|
+
changelog_uri: https://github.com/speria-jp/typed_attrs/blob/master/CHANGELOG.md
|
97
|
+
bug_tracker_uri: https://github.com/speria-jp/typed_attrs/issues
|
98
|
+
documentation_uri: https://github.com/speria-jp/typed_attrs/blob/master/README.md
|
99
|
+
rubygems_mfa_required: 'true'
|
100
|
+
rdoc_options: []
|
101
|
+
require_paths:
|
102
|
+
- lib
|
103
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
104
|
+
requirements:
|
105
|
+
- - ">="
|
106
|
+
- !ruby/object:Gem::Version
|
107
|
+
version: 3.2.0
|
108
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
|
+
requirements:
|
110
|
+
- - ">="
|
111
|
+
- !ruby/object:Gem::Version
|
112
|
+
version: '0'
|
113
|
+
requirements: []
|
114
|
+
rubygems_version: 3.6.7
|
115
|
+
specification_version: 4
|
116
|
+
summary: Type-safe ActiveRecord attributes using Sorbet T::Struct
|
117
|
+
test_files: []
|