typelizer 0.9.2 → 0.10.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: 505210c62639d70e95336cfb02bb0ab952089ac8433d5816774977853af264a1
4
- data.tar.gz: 2878868d86acf4da05caaf5e139e3ccacf58025b8bf5fc2bc56c04b0a91049b4
3
+ metadata.gz: bc23d62a95119b1ff871bcde1eeb0c3180cc0fe1e38fa1252edad30a454661bc
4
+ data.tar.gz: 2658b01eebb6f9317ff4905f6c482cbdfa55afcadd4ba6508e1cd9b5469072da
5
5
  SHA512:
6
- metadata.gz: b1522f196e8372d77e253bba7f9515043f925aa0f4d8ac6c3b1a11b837424743bba3225774b9dbd65232e46befdabbcb440ef6cf3370edc61e9dece9bf204e2e
7
- data.tar.gz: 6e8acdb494ec3f5167bbfe9f53422ebd26d4e9d8dab89ec832518b1acda47aae821a002dd954daa7605f273f323092e3622699dffad88805120dbf3d3ad2fa4e
6
+ metadata.gz: 237f41ebfc6999a73c30e97a89dab3435638c49bac38ff8072493b62833fd7a6c7d5cd7655868bf07c958b879c906c4c38bd809331cad1827b37529183c1aae3
7
+ data.tar.gz: b882bab4aea8a599b69e39c296a8794cf1fa357ec20f092649d5fa7e7d80bf2fb05ffff69b4362f7e0aa0be9b669724933a4167bfe403f9ce094e37254dfc9e8
data/CHANGELOG.md CHANGED
@@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning].
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.10.0] - 2026-03-02
11
+
12
+ ### Changed
13
+
14
+ - **Breaking:** Arrays of strings in `typelize` now produce string literal unions (`'active' | 'inactive'`) instead of type reference unions. Use symbols for type references: `typelize status: [:string, :number]`. ([@skryukov])
15
+
16
+ ## [0.9.3] - 2026-02-27
17
+
18
+ ### Fixed
19
+
20
+ - Fix keyless `typelize` DSL without name. ([@skryukov])
21
+ - Support arrays for keyless `typelize` calls. ([@skryukov])
22
+
10
23
  ## [0.9.2] - 2026-02-26
11
24
 
12
25
  ### Fixed
@@ -426,7 +439,9 @@ and this project adheres to [Semantic Versioning].
426
439
  [@skryukov]: https://github.com/skryukov
427
440
  [@ventsislaf]: https://github.com/ventsislaf
428
441
 
429
- [Unreleased]: https://github.com/skryukov/typelizer/compare/v0.9.2...HEAD
442
+ [Unreleased]: https://github.com/skryukov/typelizer/compare/v0.10.0...HEAD
443
+ [0.10.0]: https://github.com/skryukov/typelizer/compare/v0.9.3...v0.10.0
444
+ [0.9.3]: https://github.com/skryukov/typelizer/compare/v0.9.2...v0.9.3
430
445
  [0.9.2]: https://github.com/skryukov/typelizer/compare/v0.9.1...v0.9.2
431
446
  [0.9.1]: https://github.com/skryukov/typelizer/compare/v0.9.0...v0.9.1
432
447
  [0.9.0]: https://github.com/skryukov/typelizer/compare/v0.8.0...v0.9.0
data/README.md CHANGED
@@ -119,10 +119,10 @@ class PostResource < ApplicationResource
119
119
  typelize categories: "string?[]" # optional array of strings (categories?: Array<string>)
120
120
 
121
121
  # Shortcuts can be combined with explicit options
122
- typelize status: ["string?", nullable: true] # optional and nullable
122
+ typelize status: [:string?, nullable: true] # optional and nullable
123
123
 
124
124
  # Also works with keyless typelize
125
- typelize "string?"
125
+ typelize :string?
126
126
  attribute :nickname do |user|
127
127
  user.nickname
128
128
  end
@@ -167,8 +167,8 @@ class PostResource < ApplicationResource
167
167
  typelize target: "UserResource | CommentResource"
168
168
  attribute :target
169
169
 
170
- # String and class constant can be mixed
171
- typelize item: ["Namespace::UserResource", CommentResource]
170
+ # Pipe-delimited string with namespaced serializer
171
+ typelize item: "Namespace::UserResource | CommentResource"
172
172
  attribute :item
173
173
  end
174
174
  ```
@@ -183,8 +183,8 @@ class PostResource < ApplicationResource
183
183
  typelize content: "TextBlock | ImageBlock"
184
184
  attribute :content
185
185
 
186
- # Works with arrays too
187
- typelize sections: ["TextBlock", "ImageBlock"]
186
+ # Works with arrays of symbols too
187
+ typelize sections: [:TextBlock, :ImageBlock]
188
188
  attribute :sections
189
189
  end
190
190
  ```
@@ -200,10 +200,39 @@ type Post = {
200
200
  }
201
201
  ```
202
202
 
203
+ String arrays are treated as string literal unions — useful for enums and state machines:
204
+
205
+ ```ruby
206
+ class PostResource < ApplicationResource
207
+ attributes :id, :title
208
+
209
+ # Array of strings — generates string literal union type
210
+ typelize status: ["draft", "published", "archived"]
211
+ attribute :status
212
+
213
+ # Works with Rails enums and state machines
214
+ typelize review_state: ReviewStateMachine.states.keys
215
+ attribute :review_state
216
+ end
217
+ ```
218
+
219
+ This generates:
220
+
221
+ ```typescript
222
+ type Post = {
223
+ id: number;
224
+ title: string;
225
+ status: 'draft' | 'published' | 'archived';
226
+ review_state: 'pending' | 'approved' | 'rejected';
227
+ }
228
+ ```
229
+
230
+ > **Note:** In arrays, **strings** become string literal types (`'a'`), while **symbols** and **class constants** become type references (`A`). You can mix them: `[:number, "auto"]` produces `number | 'auto'`.
231
+
203
232
  For more complex type definitions, use the full API:
204
233
 
205
234
  ```ruby
206
- typelize attribute_name: ["string", "Date", optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description", deprecated: "Use `another_attribute` instead"]
235
+ typelize attribute_name: [:string, :Date, optional: true, nullable: true, multi: true, enum: %w[foo bar], comment: "Attribute description", deprecated: "Use `another_attribute` instead"]
207
236
  ```
208
237
 
209
238
  ### Alba Traits
@@ -29,7 +29,7 @@ module Typelizer
29
29
  return unless keyless_type
30
30
 
31
31
  type, attrs = keyless_type
32
- typelize(name => [type, attrs])
32
+ store_type(:_typelizer_attributes, name, attrs.merge(type: type))
33
33
  self.keyless_type = nil
34
34
  end
35
35
 
data/lib/typelizer/dsl.rb CHANGED
@@ -73,11 +73,25 @@ module Typelizer
73
73
  assign_type_information(:_typelizer_meta_attributes, attributes)
74
74
  end
75
75
 
76
+ def store_type(attribute_name, name, options)
77
+ ensure_type_store(attribute_name)
78
+ instance_variable_get("@#{attribute_name}")[name.to_sym] ||= {}
79
+ instance_variable_get("@#{attribute_name}")[name.to_sym].merge!(options)
80
+ end
81
+
76
82
  private
77
83
 
78
84
  def assign_type_information(attribute_name, attributes)
79
85
  return unless Typelizer.enabled?
80
86
 
87
+ attributes.each do |name, attrs|
88
+ next unless name
89
+
90
+ store_type(attribute_name, name, TypeParser.parse_declaration(attrs))
91
+ end
92
+ end
93
+
94
+ def ensure_type_store(attribute_name)
81
95
  instance_variable = "@#{attribute_name}"
82
96
 
83
97
  unless instance_variable_get(instance_variable)
@@ -96,29 +110,6 @@ module Typelizer
96
110
  end
97
111
  end
98
112
  end
99
-
100
- attributes.each do |name, attrs|
101
- next unless name
102
-
103
- attrs = [attrs] if attrs && !attrs.is_a?(Array)
104
- options = attrs.last.is_a?(Hash) ? attrs.pop : {}
105
-
106
- if attrs.any?
107
- parsed_types = attrs.map { |t| TypeParser.parse(t) }
108
- all_types = parsed_types.flat_map { |p| Array(p[:type]) }
109
- parsed_types.each do |parsed|
110
- options[:optional] = true if parsed[:optional]
111
- options[:multi] = true if parsed[:multi]
112
- options[:nullable] = true if parsed[:nullable]
113
- end
114
- options[:nullable] = true if all_types.delete(:null)
115
- # Unwrap single-element arrays: typelize field: ["string"] behaves like typelize field: "string"
116
- options[:type] = (all_types.size == 1) ? all_types.first : all_types
117
- end
118
-
119
- instance_variable_get(instance_variable)[name.to_sym] ||= {}
120
- instance_variable_get(instance_variable)[name.to_sym].merge!(options)
121
- end
122
113
  end
123
114
  end
124
115
  end
@@ -111,17 +111,7 @@ module Typelizer
111
111
  private
112
112
 
113
113
  def normalize_typelize(type_def, **options)
114
- case type_def
115
- when Array
116
- # [:string, nullable: true] or ['string?', nullable: true]
117
- type, *rest = type_def
118
- opts = rest.first || {}
119
- TypeParser.parse(type, **opts)
120
- when Symbol, String
121
- TypeParser.parse(type_def, **options)
122
- else
123
- options
124
- end
114
+ TypeParser.parse_declaration(type_def, **options)
125
115
  end
126
116
  end
127
117
  end
@@ -10,8 +10,20 @@ module Typelizer
10
10
  TYPE_PATTERN = /\A(.+?)(\?)?(\[\])?(\?)?\z/
11
11
 
12
12
  class << self
13
+ def parse_declaration(attrs, **options)
14
+ return options.merge(attrs) if attrs.is_a?(Hash)
15
+ return parse(attrs, **options) unless attrs.is_a?(Array)
16
+
17
+ options = attrs.last.merge(options) if attrs.last.is_a?(Hash)
18
+ types = attrs.reject { |t| t.is_a?(Hash) }
19
+ return options if types.empty?
20
+
21
+ parse((types.size == 1) ? types.first : types, **options)
22
+ end
23
+
13
24
  def parse(type_def, **options)
14
25
  return options if type_def.nil?
26
+ return parse_array(type_def, **options) if type_def.is_a?(Array)
15
27
 
16
28
  type_str = type_def.to_s
17
29
  return parse_union(type_str, **options) if type_str.include?("|")
@@ -39,6 +51,31 @@ module Typelizer
39
51
 
40
52
  private
41
53
 
54
+ def parse_array(type_defs, **options)
55
+ raise ArgumentError, "Empty array passed to typelize" if type_defs.empty?
56
+
57
+ types = []
58
+ type_defs.each do |t|
59
+ if t.is_a?(String)
60
+ types << :"'#{t}'"
61
+ else
62
+ parsed = parse(t)
63
+ types.concat(Array(parsed[:type]))
64
+ options[:optional] = true if parsed[:optional]
65
+ options[:multi] = true if parsed[:multi]
66
+ options[:nullable] = true if parsed[:nullable]
67
+ end
68
+ end
69
+
70
+ options[:nullable] = true if types.delete(:null)
71
+ wrap_type(types, **options)
72
+ end
73
+
74
+ def wrap_type(types, **options)
75
+ type = (types.size == 1) ? types.first : types
76
+ {type: type}.merge(options)
77
+ end
78
+
42
79
  def parse_union(type_str, **options)
43
80
  parts = UnionTypeSorter.split_union_members(type_str)
44
81
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Typelizer
4
- VERSION = "0.9.2"
4
+ VERSION = "0.10.0"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: typelizer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.2
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Svyatoslav Kryukov