typed_attrs 0.1.0 → 0.2.2

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: 11ede186057fb2f00a926b578dfa7ea647842bf76a1f3f3904b71b3b7f8cbff1
4
- data.tar.gz: 7a1e677adbc23fa563fc600bd842fbe1ba82531c1b5f743f804f32582c2b450e
3
+ metadata.gz: c4ec7589abe8bedcc01f25d42c9419ae5e1525bbce0ef18d8b1dd119add2d4cb
4
+ data.tar.gz: a93cc3758a8b571394d285818853131d756fe2a9373d2def2654119e8418ccbd
5
5
  SHA512:
6
- metadata.gz: 1b1c68d5519e6b4ed951264bbcc60e276c3378e853f448f4cf92d903af0aaa2d967d48954ec392a94aed5b6ad102479cedff76baab530c3eeb1a77cc8fa7a2bf
7
- data.tar.gz: 5062ae18a4d829f802b1593f21be45fb98da5258563ecdbfc75641c7de7ab331806360f597a0bd23a73c68cff4e0b49cddde24d766c16bc1c18c32fb68313fb4
6
+ metadata.gz: efd329de2ce4ae3e5cd7798328030c2ddb48543c37a74469d34fe5cb1cfe77f8f45b267707ec7aa4cba7239a63d6f1c604b23281bbe2aec28f85c3b5f0d23ac7
7
+ data.tar.gz: 45e46415a3e7aa5145c5442fca8fe2ba4668812f634c2f8b5cc452c18e8e2f5fbf9bfed48cd1be5233f8ca1827094a38388cd223706884b13de65216a45e76aa
data/CHANGELOG.md CHANGED
@@ -5,9 +5,20 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [Unreleased]
8
+ ## [0.2.0] - 2025-10-11
9
9
 
10
- ## [0.1.0] - 2025-01-05
10
+ ### Changed
11
+ - **BREAKING**: Changed public API from `TypedAttrs.to_type(Product)` to `T::Attr[Product]`
12
+ - Users must update attribute definitions: `attribute :field, TypedAttrs.to_type(MyStruct)` → `attribute :field, T::Attr[MyStruct]`
13
+ - Public API is now under `T::Attr` module while implementation remains in `TypedAttrs` namespace
14
+
15
+ ### Refactored
16
+ - Simplified type class from `TypedAttrs::Types::Type` to `TypedAttrs::Type`
17
+ - Renamed Tapioca DSL compiler from `TypedAttribute` to `TypedAttrs`
18
+ - File: `lib/tapioca/dsl/compilers/typed_attribute.rb` → `typed_attrs.rb`
19
+ - Class: `Tapioca::Dsl::Compilers::TypedAttribute` → `TypedAttrs`
20
+
21
+ ## [0.1.0] - 2025-10-10
11
22
 
12
23
  ### Added
13
24
  - Initial release
data/README.md CHANGED
@@ -37,7 +37,7 @@ end
37
37
 
38
38
  # Use it as a typed attribute
39
39
  class Order < ActiveRecord::Base
40
- attribute :product, TypedAttrs.to_type(Product)
40
+ attribute :product, T::Attr[Product]
41
41
  end
42
42
 
43
43
  # Assign with Hash
@@ -64,7 +64,7 @@ class Color < T::Struct
64
64
  end
65
65
 
66
66
  class Palette < ActiveRecord::Base
67
- attribute :colors, TypedAttrs.to_type(T::Array[Color])
67
+ attribute :colors, T::Attr[T::Array[Color]]
68
68
  end
69
69
 
70
70
  palette = Palette.new
@@ -85,7 +85,7 @@ class BoxSize < T::Struct
85
85
  end
86
86
 
87
87
  class Layout < ActiveRecord::Base
88
- attribute :box_sizes, TypedAttrs.to_type(T::Hash[String, BoxSize])
88
+ attribute :box_sizes, T::Attr[T::Hash[String, BoxSize]]
89
89
  end
90
90
 
91
91
  layout = Layout.new
@@ -127,7 +127,7 @@ class Cat < T::Struct
127
127
  end
128
128
 
129
129
  class PetOwner < ActiveRecord::Base
130
- attribute :pet, TypedAttrs.to_type(PetType)
130
+ attribute :pet, T::Attr[PetType]
131
131
  end
132
132
 
133
133
  owner = PetOwner.new
@@ -152,7 +152,7 @@ class Company < T::Struct
152
152
  end
153
153
 
154
154
  class Employee < ActiveRecord::Base
155
- attribute :company, TypedAttrs.to_type(Company)
155
+ attribute :company, T::Attr[Company]
156
156
  end
157
157
 
158
158
  employee = Employee.new
@@ -174,7 +174,7 @@ class Settings < T::Struct
174
174
  end
175
175
 
176
176
  class User < ActiveRecord::Base
177
- attribute :settings, TypedAttrs.to_type(Settings)
177
+ attribute :settings, T::Attr[Settings]
178
178
  end
179
179
 
180
180
  user = User.new
@@ -200,7 +200,7 @@ class Product < T::Struct
200
200
  end
201
201
 
202
202
  class Order < ActiveRecord::Base
203
- attribute :product, TypedAttrs.to_type(Product)
203
+ attribute :product, T::Attr[Product]
204
204
  validates :product, typed_attrs: true
205
205
  end
206
206
 
@@ -227,7 +227,7 @@ class Article < T::Struct
227
227
  end
228
228
 
229
229
  class Post < ActiveRecord::Base
230
- attribute :article, TypedAttrs.to_type(Article)
230
+ attribute :article, T::Attr[Article]
231
231
  validates :article, typed_attrs: true
232
232
  end
233
233
 
@@ -305,12 +305,12 @@ end
305
305
  # Ruby: Company.new(name: "Acme", address: Address.new(...))
306
306
 
307
307
  # Array type - stored as JSON array
308
- attribute :colors, TypedAttrs.to_type(T::Array[Color])
308
+ attribute :colors, T::Attr[T::Array[Color]]
309
309
  # Database: [{ "name": "Red", "hex": "#FF0000" }, ...]
310
- # Ruby: [Color.new(name: "Red", hex: "#FF0000"), ...]
310
+ # Ruby: [Color.new(name: "Red", hex: "#FF0000"], ...]
311
311
 
312
312
  # Hash type - stored as JSON object
313
- attribute :sizes, TypedAttrs.to_type(T::Hash[String, BoxSize])
313
+ attribute :sizes, T::Attr[T::Hash[String, BoxSize]]
314
314
  # Database: { "small": { "width": 100, "height": 50 }, ... }
315
315
  # Ruby: { "small" => BoxSize.new(width: 100, height: 50), ... }
316
316
  ```
@@ -36,12 +36,12 @@ module ActiveModel
36
36
  end
37
37
  end
38
38
 
39
- sig { params(record: T.untyped, attribute: T.any(String, Symbol)).returns(T.nilable(TypedAttrs::Types::Type)) }
39
+ sig { params(record: T.untyped, attribute: T.any(String, Symbol)).returns(T.nilable(TypedAttrs::Type)) }
40
40
  def get_typed_attribute_type(record, attribute)
41
41
  return nil unless record.class.respond_to?(:attribute_types)
42
42
 
43
43
  attribute_type = record.class.attribute_types[attribute.to_s]
44
- attribute_type.is_a?(TypedAttrs::Types::Type) ? attribute_type : nil
44
+ attribute_type.is_a?(TypedAttrs::Type) ? attribute_type : nil
45
45
  end
46
46
  end
47
47
  end
@@ -9,7 +9,7 @@ require "tapioca/dsl"
9
9
  module Tapioca
10
10
  module Dsl
11
11
  module Compilers
12
- class TypedAttribute < Tapioca::Dsl::Compiler
12
+ class TypedAttrs < Tapioca::Dsl::Compiler
13
13
  extend T::Sig
14
14
 
15
15
  ConstantType = type_member { { fixed: T.class_of(ActiveRecord::Base) } }
@@ -46,7 +46,7 @@ module Tapioca
46
46
 
47
47
  class << self
48
48
  def typed_attribute_type?(type)
49
- type.is_a?(TypedAttrs::Types::Type)
49
+ type.is_a?(::TypedAttrs::Type)
50
50
  end
51
51
  end
52
52
 
@@ -60,12 +60,12 @@ module Tapioca
60
60
  sig { params(klass: RBI::Scope, attr_name: String, type: ActiveModel::Type::Value).void }
61
61
  def create_attribute_methods(klass, attr_name, type)
62
62
  case type
63
- when TypedAttrs::Types::Type
63
+ when ::TypedAttrs::Type
64
64
  create_type_attribute_methods(klass, attr_name, type)
65
65
  end
66
66
  end
67
67
 
68
- sig { params(klass: RBI::Scope, attr_name: String, type: TypedAttrs::Types::Type).void }
68
+ sig { params(klass: RBI::Scope, attr_name: String, type: ::TypedAttrs::Type).void }
69
69
  def create_type_attribute_methods(klass, attr_name, type)
70
70
  type_spec = type.instance_variable_get(:@type_spec)
71
71
  type_string = type_spec.name
@@ -0,0 +1,49 @@
1
+ # typed: strict
2
+ # frozen_string_literal: true
3
+
4
+ module TypedAttrs
5
+ class Type < ActiveModel::Type::Value
6
+ include TypeHelpers
7
+ extend T::Sig
8
+
9
+ sig { params(type_spec: T.untyped).void }
10
+ def initialize(type_spec)
11
+ super()
12
+ @type_spec = type_spec
13
+
14
+ registry = T.let(TypeProcessors.default_registry, TypeProcessors::Registry)
15
+ @processor = T.let(registry.serializer_for(type_spec), TypeProcessors::Base)
16
+ end
17
+
18
+ sig { returns(TypeProcessors::Base) }
19
+ attr_reader :processor
20
+
21
+ sig { params(value: T.untyped).returns(T.untyped) }
22
+ def cast(value)
23
+ return nil if value.nil?
24
+
25
+ @processor.deserialize(value)
26
+ end
27
+
28
+ sig { params(value: T.untyped).returns(T.untyped) }
29
+ def serialize(value)
30
+ return nil if value.nil?
31
+
32
+ serialized = @processor.serialize(value)
33
+ ActiveSupport::JSON.encode(serialized)
34
+ end
35
+
36
+ sig { params(value: T.untyped).returns(T.untyped) }
37
+ def deserialize(value)
38
+ return nil if value.nil?
39
+
40
+ data = if value.is_a?(String)
41
+ ActiveSupport::JSON.decode(value)
42
+ else
43
+ value
44
+ end
45
+
46
+ @processor.deserialize(data)
47
+ end
48
+ end
49
+ end
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module TypedAttrs
5
- VERSION = "0.1.0"
5
+ VERSION = "0.2.2"
6
6
  end
data/lib/typed_attrs.rb CHANGED
@@ -23,7 +23,7 @@ require_relative "typed_attrs/type_processors/struct"
23
23
  require_relative "typed_attrs/type_processors/array"
24
24
  require_relative "typed_attrs/type_processors/hash"
25
25
  require_relative "typed_attrs/type_processors/discriminated_union"
26
- require_relative "typed_attrs/types/type"
26
+ require_relative "typed_attrs/type"
27
27
  require_relative "typed_attrs/discriminated_union"
28
28
  require_relative "active_model/validations/typed_attrs_validator"
29
29
 
@@ -95,9 +95,16 @@ module TypedAttrs
95
95
  )
96
96
  end
97
97
  end
98
+ end
99
+
100
+ # Add T::Attr as the public API for typed attributes
101
+ module T
102
+ module Attr
103
+ extend T::Sig
98
104
 
99
- sig { params(type_spec: T.untyped).returns(Types::Type) }
100
- def self.to_type(type_spec)
101
- Types::Type.new(type_spec)
105
+ sig { params(type_spec: T.untyped).returns(TypedAttrs::Type) }
106
+ def self.[](type_spec)
107
+ TypedAttrs::Type.new(type_spec)
108
+ end
102
109
  end
103
110
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typed_attrs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Speria
@@ -23,20 +23,6 @@ dependencies:
23
23
  - - ">="
24
24
  - !ruby/object:Gem::Version
25
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
26
  - !ruby/object:Gem::Dependency
41
27
  name: sorbet-runtime
42
28
  requirement: !ruby/object:Gem::Requirement
@@ -64,9 +50,10 @@ files:
64
50
  - LICENSE
65
51
  - README.md
66
52
  - lib/active_model/validations/typed_attrs_validator.rb
67
- - lib/tapioca/dsl/compilers/typed_attribute.rb
53
+ - lib/tapioca/dsl/compilers/typed_attrs.rb
68
54
  - lib/typed_attrs.rb
69
55
  - lib/typed_attrs/discriminated_union.rb
56
+ - lib/typed_attrs/type.rb
70
57
  - lib/typed_attrs/type_helpers.rb
71
58
  - lib/typed_attrs/type_processors.rb
72
59
  - lib/typed_attrs/type_processors/array.rb
@@ -85,17 +72,16 @@ files:
85
72
  - lib/typed_attrs/type_processors/struct.rb
86
73
  - lib/typed_attrs/type_processors/symbol.rb
87
74
  - lib/typed_attrs/type_processors/time.rb
88
- - lib/typed_attrs/types/type.rb
89
75
  - lib/typed_attrs/version.rb
90
76
  homepage: https://github.com/speria-jp/typed_attrs
91
77
  licenses:
92
78
  - MIT
93
79
  metadata:
94
80
  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
81
+ source_code_uri: https://github.com/speria-jp/typed_attrs
82
+ changelog_uri: https://github.com/speria-jp/typed_attrs/blob/main/CHANGELOG.md
97
83
  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
84
+ documentation_uri: https://github.com/speria-jp/typed_attrs/blob/main/README.md
99
85
  rubygems_mfa_required: 'true'
100
86
  rdoc_options: []
101
87
  require_paths:
@@ -111,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
111
97
  - !ruby/object:Gem::Version
112
98
  version: '0'
113
99
  requirements: []
114
- rubygems_version: 3.6.7
100
+ rubygems_version: 3.6.9
115
101
  specification_version: 4
116
102
  summary: Type-safe ActiveRecord attributes using Sorbet T::Struct
117
103
  test_files: []
@@ -1,51 +0,0 @@
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