typelizer 0.6.0 → 0.7.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 +4 -4
- data/CHANGELOG.md +13 -1
- data/lib/typelizer/config.rb +34 -0
- data/lib/typelizer/dsl/hooks/alba.rb +17 -0
- data/lib/typelizer/dsl/hooks/ams.rb +15 -0
- data/lib/typelizer/dsl/hooks/oj_serializers.rb +15 -0
- data/lib/typelizer/dsl/hooks/panko.rb +16 -0
- data/lib/typelizer/dsl/hooks.rb +64 -0
- data/lib/typelizer/dsl.rb +20 -0
- data/lib/typelizer/generator.rb +7 -53
- data/lib/typelizer/interface.rb +43 -17
- data/lib/typelizer/property.rb +48 -9
- data/lib/typelizer/serializer_plugins/alba/trait_interface.rb +1 -1
- data/lib/typelizer/serializer_plugins/alba.rb +0 -23
- data/lib/typelizer/serializer_plugins/ams.rb +0 -13
- data/lib/typelizer/serializer_plugins/base.rb +0 -8
- data/lib/typelizer/serializer_plugins/oj_serializers.rb +0 -7
- data/lib/typelizer/serializer_plugins/panko.rb +0 -12
- data/lib/typelizer/templates/enums.ts.erb +1 -1
- data/lib/typelizer/templates/index.ts.erb +5 -3
- data/lib/typelizer/templates/inheritance.ts.erb +5 -1
- data/lib/typelizer/templates/inline_type.ts.erb +1 -1
- data/lib/typelizer/templates/interface.ts.erb +7 -6
- data/lib/typelizer/union_type_sorter.rb +148 -0
- data/lib/typelizer/version.rb +1 -1
- data/lib/typelizer/writer.rb +10 -4
- data/lib/typelizer.rb +1 -1
- metadata +7 -2
- data/lib/typelizer/contexts/scan_context.rb +0 -20
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 819c25a9bfc5dd74c3b7c77045bb5f5ef968f518f8a8d228502badb6936ea50d
|
|
4
|
+
data.tar.gz: c8d8d9affec2d6eb07cf1e58942fe46009fad2782c57689218b8357a85c227f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b4b39192ff398bb25c6b537fcb5c73893de40d82f14a09a254e4ef8317a08232819c3285b7e6806938f1c7058f895d0100b03884574158bbaa2e329982518683
|
|
7
|
+
data.tar.gz: 8971c5a2cc78992fd38dc101e7b2fc1eaefe82ad756f828bca4416a4a6ed54d1ff25eace61fd80cd293cca3dc66b6b6c24706a0fa45156fb275eb9bfe4976c46
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,17 @@ and this project adheres to [Semantic Versioning].
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.7.0] - 2026-01-15
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- Use DSL hooks instead of TracePoint for `typelize` method. ([@skryukov])
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Apply sorting and quote style configs consistently to all generated files. ([@jonmarkgo], [@skryukov])
|
|
19
|
+
- Fix fingerprint calculations to include all config options. ([@skryukov])
|
|
20
|
+
|
|
10
21
|
## [0.6.0] - 2026-01-14
|
|
11
22
|
|
|
12
23
|
### Added
|
|
@@ -347,7 +358,8 @@ and this project adheres to [Semantic Versioning].
|
|
|
347
358
|
[@prog-supdex]: https://github.com/prog-supdex
|
|
348
359
|
[@ventsislaf]: https://github.com/ventsislaf
|
|
349
360
|
|
|
350
|
-
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.
|
|
361
|
+
[Unreleased]: https://github.com/skryukov/typelizer/compare/v0.7.0...HEAD
|
|
362
|
+
[0.7.0]: https://github.com/skryukov/typelizer/compare/v0.6.0...v0.7.0
|
|
351
363
|
[0.6.0]: https://github.com/skryukov/typelizer/compare/v0.5.6...v0.6.0
|
|
352
364
|
[0.5.6]: https://github.com/skryukov/typelizer/compare/v0.5.5...v0.5.6
|
|
353
365
|
[0.5.5]: https://github.com/skryukov/typelizer/compare/v0.5.4...v0.5.5
|
data/lib/typelizer/config.rb
CHANGED
|
@@ -19,6 +19,40 @@ module Typelizer
|
|
|
19
19
|
|
|
20
20
|
DEFAULT_TYPES_GLOBAL = %w[Array Date Record File FileList].freeze
|
|
21
21
|
|
|
22
|
+
# Config keys that affect generated file content and must be included in fingerprints.
|
|
23
|
+
# When adding a new config, add it here if it affects output, or to CONFIGS_NOT_AFFECTING_OUTPUT.
|
|
24
|
+
CONFIGS_AFFECTING_OUTPUT = %i[
|
|
25
|
+
types_import_path
|
|
26
|
+
types_global
|
|
27
|
+
prefer_double_quotes
|
|
28
|
+
comments
|
|
29
|
+
verbatim_module_syntax
|
|
30
|
+
imports_sort_order
|
|
31
|
+
properties_sort_order
|
|
32
|
+
].freeze
|
|
33
|
+
|
|
34
|
+
# Subset of CONFIGS_AFFECTING_OUTPUT that specifically affect index.ts output.
|
|
35
|
+
CONFIGS_AFFECTING_INDEX_OUTPUT = %i[
|
|
36
|
+
verbatim_module_syntax
|
|
37
|
+
prefer_double_quotes
|
|
38
|
+
imports_sort_order
|
|
39
|
+
].freeze
|
|
40
|
+
|
|
41
|
+
# Config keys that don't affect file content (runtime behavior, or effects captured via properties).
|
|
42
|
+
CONFIGS_NOT_AFFECTING_OUTPUT = %i[
|
|
43
|
+
serializer_name_mapper
|
|
44
|
+
serializer_model_mapper
|
|
45
|
+
properties_transformer
|
|
46
|
+
model_plugin
|
|
47
|
+
serializer_plugin
|
|
48
|
+
plugin_configs
|
|
49
|
+
type_mapping
|
|
50
|
+
null_strategy
|
|
51
|
+
output_dir
|
|
52
|
+
inheritance_strategy
|
|
53
|
+
associations_strategy
|
|
54
|
+
].freeze
|
|
55
|
+
|
|
22
56
|
Config = Struct.new(
|
|
23
57
|
:serializer_name_mapper,
|
|
24
58
|
:serializer_model_mapper,
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Typelizer
|
|
4
|
+
module DSL
|
|
5
|
+
module Hooks
|
|
6
|
+
module Alba
|
|
7
|
+
include Methods
|
|
8
|
+
extend Builder
|
|
9
|
+
|
|
10
|
+
hook :attribute, :association, :one, :has_one
|
|
11
|
+
hook :nested_attribute, :nested, :meta
|
|
12
|
+
hook :many, :has_many, multi: true
|
|
13
|
+
hook_method_added
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Typelizer
|
|
4
|
+
module DSL
|
|
5
|
+
module Hooks
|
|
6
|
+
def self.install(base)
|
|
7
|
+
# Always install hooks to capture multi associations.
|
|
8
|
+
# The hooks only record data to a Set, so overhead is minimal.
|
|
9
|
+
if defined?(::Alba::Resource) && base.ancestors.include?(::Alba::Resource)
|
|
10
|
+
require_relative "hooks/alba"
|
|
11
|
+
base.singleton_class.prepend(Alba)
|
|
12
|
+
elsif defined?(::ActiveModel::Serializer) && base.ancestors.include?(::ActiveModel::Serializer)
|
|
13
|
+
require_relative "hooks/ams"
|
|
14
|
+
base.singleton_class.prepend(AMS)
|
|
15
|
+
elsif defined?(::Oj::Serializer) && base.ancestors.include?(::Oj::Serializer)
|
|
16
|
+
require_relative "hooks/oj_serializers"
|
|
17
|
+
base.singleton_class.prepend(OjSerializers)
|
|
18
|
+
elsif defined?(::Panko::Serializer) && base.ancestors.include?(::Panko::Serializer)
|
|
19
|
+
require_relative "hooks/panko"
|
|
20
|
+
base.singleton_class.prepend(Panko)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Shared methods available to all hook modules
|
|
25
|
+
module Methods
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def consume_keyless_type(name)
|
|
29
|
+
return unless keyless_type
|
|
30
|
+
|
|
31
|
+
type, attrs = keyless_type
|
|
32
|
+
typelize(name => [type, attrs])
|
|
33
|
+
self.keyless_type = nil
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def record_multi(name)
|
|
37
|
+
_own_typelizer_multi_attributes << name.to_sym
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# DSL for defining hooks with less boilerplate
|
|
42
|
+
module Builder
|
|
43
|
+
def hook(*methods, multi: false)
|
|
44
|
+
methods.each do |method|
|
|
45
|
+
define_method(method) do |name = nil, *args, **kwargs, &block|
|
|
46
|
+
if name
|
|
47
|
+
record_multi(name) if multi
|
|
48
|
+
consume_keyless_type(name)
|
|
49
|
+
end
|
|
50
|
+
super(name, *args, **kwargs, &block)
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def hook_method_added
|
|
56
|
+
define_method(:method_added) do |method_name|
|
|
57
|
+
consume_keyless_type(method_name)
|
|
58
|
+
super(method_name)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
data/lib/typelizer/dsl.rb
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
require "set"
|
|
2
|
+
require_relative "dsl/hooks"
|
|
3
|
+
|
|
1
4
|
module Typelizer
|
|
2
5
|
module DSL
|
|
3
6
|
# typelize_from Model
|
|
@@ -6,11 +9,13 @@ module Typelizer
|
|
|
6
9
|
def self.included(base)
|
|
7
10
|
Typelizer.base_classes << base.to_s if base.name
|
|
8
11
|
base.extend(ClassMethods)
|
|
12
|
+
Hooks.install(base)
|
|
9
13
|
end
|
|
10
14
|
|
|
11
15
|
def self.extended(base)
|
|
12
16
|
Typelizer.base_classes << base.to_s if base.name
|
|
13
17
|
base.extend(ClassMethods)
|
|
18
|
+
Hooks.install(base)
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
module ClassMethods
|
|
@@ -49,6 +54,21 @@ module Typelizer
|
|
|
49
54
|
|
|
50
55
|
attr_accessor :keyless_type
|
|
51
56
|
|
|
57
|
+
# Returns union of own + ancestors' multi attributes
|
|
58
|
+
def _typelizer_multi_attributes
|
|
59
|
+
result = @_typelizer_multi_attributes || Set.new
|
|
60
|
+
if superclass.respond_to?(:_typelizer_multi_attributes)
|
|
61
|
+
superclass._typelizer_multi_attributes | result
|
|
62
|
+
else
|
|
63
|
+
result
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns own Set (initializing if needed) for writing
|
|
68
|
+
def _own_typelizer_multi_attributes
|
|
69
|
+
@_typelizer_multi_attributes ||= Set.new
|
|
70
|
+
end
|
|
71
|
+
|
|
52
72
|
def typelize_meta(**attributes)
|
|
53
73
|
assign_type_information(:_typelizer_meta_attributes, attributes)
|
|
54
74
|
end
|
data/lib/typelizer/generator.rb
CHANGED
|
@@ -9,15 +9,9 @@ module Typelizer
|
|
|
9
9
|
def call(force: false)
|
|
10
10
|
return [] unless Typelizer.enabled?
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
@scan_plugin_cache = {}
|
|
14
|
-
|
|
15
|
-
read_serializers
|
|
12
|
+
load_serializers
|
|
16
13
|
serializers = target_serializers
|
|
17
14
|
|
|
18
|
-
# For each writer, build a dedicated WriterContext. The context holds that writer's
|
|
19
|
-
# configuration and resolves the effective Config for every Interface (per serializer)
|
|
20
|
-
# by merging global, writer, and per-serializer (DSL) overrides
|
|
21
15
|
Typelizer.configuration.writers.each do |writer_name, writer_config|
|
|
22
16
|
context = WriterContext.new(writer_name: writer_name)
|
|
23
17
|
interfaces = serializers.map { |klass| context.interface_for(klass) }
|
|
@@ -30,6 +24,12 @@ module Typelizer
|
|
|
30
24
|
|
|
31
25
|
private
|
|
32
26
|
|
|
27
|
+
def load_serializers
|
|
28
|
+
Typelizer.dirs.flat_map { |dir| Dir["#{dir}/**/*.rb"] }.each do |file|
|
|
29
|
+
require file
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
33
|
def target_serializers
|
|
34
34
|
base_classes = Typelizer.base_classes.filter_map do |base_class|
|
|
35
35
|
Object.const_get(base_class) if Object.const_defined?(base_class)
|
|
@@ -41,51 +41,5 @@ module Typelizer
|
|
|
41
41
|
.reject { |serializer| Typelizer.reject_class.call(serializer: serializer) }
|
|
42
42
|
.sort_by(&:name)
|
|
43
43
|
end
|
|
44
|
-
|
|
45
|
-
def read_serializers(files = nil)
|
|
46
|
-
files ||= Typelizer.dirs.flat_map { |dir| Dir["#{dir}/**/*.rb"] }
|
|
47
|
-
files.each do |file|
|
|
48
|
-
trace = TracePoint.new(:call) do |tp|
|
|
49
|
-
next unless typelized_class?(tp.self)
|
|
50
|
-
|
|
51
|
-
serializer_plugin = build_scan_plugin_for(tp.self)
|
|
52
|
-
next unless serializer_plugin
|
|
53
|
-
|
|
54
|
-
if tp.callee_id.in?(serializer_plugin.methods_to_typelize)
|
|
55
|
-
type, attrs = tp.self.keyless_type
|
|
56
|
-
name = tp.binding.local_variable_get(:name) if tp.binding.local_variable_defined?(:name)
|
|
57
|
-
tp.self.typelize(**serializer_plugin.typelize_method_transform(method: tp.callee_id, binding: tp.binding, name: name, type: type, attrs: attrs || {}))
|
|
58
|
-
tp.self.keyless_type = nil
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
trace.enable
|
|
63
|
-
require file
|
|
64
|
-
trace.disable
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def typelized_class?(klass)
|
|
69
|
-
klass.is_a?(Class) && klass.respond_to?(:typelizer_config)
|
|
70
|
-
rescue
|
|
71
|
-
false
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
# Builds a minimal plugin instance used only during scan time for TracePoint
|
|
75
|
-
def build_scan_plugin_for(serializer_klass)
|
|
76
|
-
return @scan_plugin_cache[serializer_klass] if @scan_plugin_cache&.key?(serializer_klass)
|
|
77
|
-
|
|
78
|
-
base = Typelizer.configuration.writer_config(:default)
|
|
79
|
-
local_configuration = serializer_klass.typelizer_config.to_h.slice(:serializer_plugin, :plugin_configs)
|
|
80
|
-
cfg = base.with_overrides(**local_configuration)
|
|
81
|
-
|
|
82
|
-
@scan_plugin_cache[serializer_klass] = cfg.serializer_plugin.new(
|
|
83
|
-
serializer: serializer_klass,
|
|
84
|
-
config: cfg,
|
|
85
|
-
context: Typelizer::ScanContext
|
|
86
|
-
)
|
|
87
|
-
rescue NameError
|
|
88
|
-
nil
|
|
89
|
-
end
|
|
90
44
|
end
|
|
91
45
|
end
|
data/lib/typelizer/interface.rb
CHANGED
|
@@ -25,7 +25,7 @@ module Typelizer
|
|
|
25
25
|
|
|
26
26
|
def name
|
|
27
27
|
if inline?
|
|
28
|
-
Renderer.call("inline_type.ts.erb", properties: properties).strip
|
|
28
|
+
Renderer.call("inline_type.ts.erb", properties: properties, sort_order: config.properties_sort_order).strip
|
|
29
29
|
else
|
|
30
30
|
config.serializer_name_mapper.call(serializer).tr_s(":", "")
|
|
31
31
|
end
|
|
@@ -141,12 +141,15 @@ module Typelizer
|
|
|
141
141
|
end
|
|
142
142
|
|
|
143
143
|
def fingerprint
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
144
|
+
[
|
|
145
|
+
name,
|
|
146
|
+
properties_to_print.map(&:fingerprint),
|
|
147
|
+
parent_interface&.name,
|
|
148
|
+
root_key,
|
|
149
|
+
meta_fields.map(&:fingerprint),
|
|
150
|
+
trait_interfaces.map { |t| [t.name, t.properties.map(&:fingerprint)] },
|
|
151
|
+
CONFIGS_AFFECTING_OUTPUT.map { |key| config.public_send(key) }
|
|
152
|
+
].inspect
|
|
150
153
|
end
|
|
151
154
|
|
|
152
155
|
def quote(str)
|
|
@@ -168,18 +171,41 @@ module Typelizer
|
|
|
168
171
|
end
|
|
169
172
|
|
|
170
173
|
def infer_types(props, hash_name = :_typelizer_attributes)
|
|
174
|
+
dsl_attrs = serializer.respond_to?(hash_name) ? serializer.public_send(hash_name) : {}
|
|
175
|
+
multi_attrs = serializer.respond_to?(:_typelizer_multi_attributes) ? serializer._typelizer_multi_attributes : Set.new
|
|
176
|
+
|
|
171
177
|
props.map do |prop|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
178
|
+
has_dsl = dsl_attrs[prop.column_name.to_sym]&.any?
|
|
179
|
+
|
|
180
|
+
prop
|
|
181
|
+
.then { |p| apply_dsl_type(p, dsl_attrs) }
|
|
182
|
+
.then { |p| has_dsl ? p : apply_model_inference(p) }
|
|
183
|
+
.then { |p| apply_multi_flag(p, multi_attrs) }
|
|
184
|
+
.then { |p| apply_metadata(p) }
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
def apply_dsl_type(prop, dsl_attrs)
|
|
189
|
+
dsl_type = dsl_attrs[prop.column_name.to_sym]
|
|
190
|
+
return prop unless dsl_type&.any?
|
|
191
|
+
|
|
192
|
+
prop.with(**dsl_type)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def apply_model_inference(prop)
|
|
196
|
+
model_plugin.infer_types(prop)
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def apply_multi_flag(prop, multi_attrs)
|
|
200
|
+
return prop unless multi_attrs.include?(prop.column_name.to_sym)
|
|
201
|
+
|
|
202
|
+
prop.with(multi: true)
|
|
203
|
+
end
|
|
181
204
|
|
|
182
|
-
|
|
205
|
+
def apply_metadata(prop)
|
|
206
|
+
prop.tap do |p|
|
|
207
|
+
p.comment ||= model_plugin.comment_for(p) if config.comments && p.comment != false
|
|
208
|
+
p.enum ||= model_plugin.enum_for(p) if p.enum != false
|
|
183
209
|
end
|
|
184
210
|
end
|
|
185
211
|
|
data/lib/typelizer/property.rb
CHANGED
|
@@ -5,6 +5,10 @@ module Typelizer
|
|
|
5
5
|
:with_traits,
|
|
6
6
|
keyword_init: true
|
|
7
7
|
) do
|
|
8
|
+
def with(**attrs)
|
|
9
|
+
dup.tap { |p| attrs.each { |k, v| p[k] = v } }
|
|
10
|
+
end
|
|
11
|
+
|
|
8
12
|
def inspect
|
|
9
13
|
props = to_h.merge(type: type_name).map { |k, v| "#{k}=#{v.inspect}" }.join(" ")
|
|
10
14
|
"<#{self.class.name} #{props}>"
|
|
@@ -16,8 +20,17 @@ module Typelizer
|
|
|
16
20
|
fingerprint == other.fingerprint
|
|
17
21
|
end
|
|
18
22
|
|
|
23
|
+
# Default to_s for backward compatibility (no sorting)
|
|
19
24
|
def to_s
|
|
20
|
-
|
|
25
|
+
render(sort_order: :none)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Renders the property as a TypeScript property string
|
|
29
|
+
# @param sort_order [Symbol, Proc, nil] Sort order for union types (:none, :alphabetical, or Proc)
|
|
30
|
+
# @param prefer_double_quotes [Boolean] Whether to use double quotes for string values
|
|
31
|
+
# @return [String] The property string like "name?: Type1 | Type2"
|
|
32
|
+
def render(sort_order: :none, prefer_double_quotes: false)
|
|
33
|
+
type_str = type_name(sort_order: sort_order, prefer_double_quotes: prefer_double_quotes)
|
|
21
34
|
|
|
22
35
|
# Handle intersection types for traits
|
|
23
36
|
if with_traits&.any? && type.respond_to?(:name)
|
|
@@ -26,30 +39,56 @@ module Typelizer
|
|
|
26
39
|
end
|
|
27
40
|
|
|
28
41
|
type_str = "Array<#{type_str}>" if multi
|
|
42
|
+
|
|
43
|
+
# Apply union sorting to the final type string (handles Array<...> unions too)
|
|
44
|
+
type_str = UnionTypeSorter.sort(type_str, sort_order)
|
|
45
|
+
|
|
46
|
+
# Add nullable at the end (null should always be last in sorted output)
|
|
29
47
|
type_str = "#{type_str} | null" if nullable
|
|
30
48
|
|
|
31
49
|
"#{name}#{"?" if optional}: #{type_str}"
|
|
32
50
|
end
|
|
33
51
|
|
|
34
52
|
def fingerprint
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
end << ">"
|
|
53
|
+
# Use array format for consistent output across Ruby versions
|
|
54
|
+
# (Hash#inspect format changed in Ruby 3.4)
|
|
55
|
+
to_h.merge(type: UnionTypeSorter.sort(type_name(sort_order: :alphabetical), :alphabetical))
|
|
56
|
+
.to_a.inspect
|
|
40
57
|
end
|
|
41
58
|
|
|
42
|
-
|
|
59
|
+
# Generates a TypeScript type definition for named enums
|
|
60
|
+
# @param sort_order [Symbol, Proc, nil] Sort order for enum values (:none, :alphabetical, or Proc)
|
|
61
|
+
# @param prefer_double_quotes [Boolean] Whether to use double quotes for string values
|
|
62
|
+
# @return [String, nil] The type definition like "type UserRole = 'admin' | 'user'"
|
|
63
|
+
def enum_definition(sort_order: :none, prefer_double_quotes: false)
|
|
43
64
|
return unless enum && enum_type_name
|
|
44
65
|
|
|
45
|
-
|
|
66
|
+
values = enum.map { |v| quote_string(v.to_s, prefer_double_quotes) }
|
|
67
|
+
values = values.sort_by(&:downcase) if sort_order == :alphabetical
|
|
68
|
+
"type #{enum_type_name} = #{values.join(" | ")}"
|
|
46
69
|
end
|
|
47
70
|
|
|
48
71
|
private
|
|
49
72
|
|
|
50
|
-
def
|
|
73
|
+
def quote_string(str, prefer_double_quotes)
|
|
74
|
+
prefer_double_quotes ? "\"#{str}\"" : "'#{str}'"
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Returns the type name, optionally sorting union members
|
|
78
|
+
# @param sort_order [Symbol, Proc, nil] Sort order for union types
|
|
79
|
+
# @param prefer_double_quotes [Boolean] Whether to use double quotes for string values
|
|
80
|
+
# @return [String] The type name
|
|
81
|
+
def type_name(sort_order: :none, prefer_double_quotes: false)
|
|
82
|
+
# If enum_type_name is set, use it (named enum type)
|
|
51
83
|
return enum_type_name if enum_type_name
|
|
52
84
|
|
|
85
|
+
if enum
|
|
86
|
+
# Sort enum values if alphabetical sorting is requested
|
|
87
|
+
enum_values = enum.map { |v| quote_string(v.to_s, prefer_double_quotes) }
|
|
88
|
+
enum_values = enum_values.sort_by(&:downcase) if sort_order == :alphabetical
|
|
89
|
+
return enum_values.join(" | ")
|
|
90
|
+
end
|
|
91
|
+
|
|
53
92
|
type.respond_to?(:name) ? type.name : type || "unknown"
|
|
54
93
|
end
|
|
55
94
|
end
|
|
@@ -36,7 +36,7 @@ module Typelizer
|
|
|
36
36
|
# First check for typelize DSL in the trait
|
|
37
37
|
dsl_type = typelizes[prop.column_name.to_sym]
|
|
38
38
|
if dsl_type&.any?
|
|
39
|
-
next
|
|
39
|
+
next prop.with(**dsl_type).tap do |property|
|
|
40
40
|
property.comment ||= model_plugin.comment_for(property) if config.comments && property.comment != false
|
|
41
41
|
property.enum ||= model_plugin.enum_for(property) if property.enum != false
|
|
42
42
|
end
|
|
@@ -17,29 +17,6 @@ module Typelizer
|
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def methods_to_typelize
|
|
21
|
-
[
|
|
22
|
-
:association, :one, :has_one,
|
|
23
|
-
:many, :has_many,
|
|
24
|
-
:attributes, :attribute,
|
|
25
|
-
:method_added,
|
|
26
|
-
:nested_attribute, :nested,
|
|
27
|
-
:meta
|
|
28
|
-
]
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
def typelize_method_transform(method:, name:, binding:, type:, attrs:)
|
|
32
|
-
if method == :method_added && binding.local_variable_defined?(:method_name)
|
|
33
|
-
name = binding.local_variable_get(:method_name)
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
if [:many, :has_many].include?(method)
|
|
37
|
-
return {name => [type, attrs.merge(multi: true)]}
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
super
|
|
41
|
-
end
|
|
42
|
-
|
|
43
20
|
def root_key
|
|
44
21
|
root = serializer.new({}).send(:_key)
|
|
45
22
|
if !root.nil? && has_transform_key?(serializer) && should_transform_root_key?(serializer)
|
|
@@ -3,19 +3,6 @@ require_relative "base"
|
|
|
3
3
|
module Typelizer
|
|
4
4
|
module SerializerPlugins
|
|
5
5
|
class AMS < Base
|
|
6
|
-
def methods_to_typelize
|
|
7
|
-
[
|
|
8
|
-
:has_many, :has_one, :belongs_to,
|
|
9
|
-
:attribute, :attributes
|
|
10
|
-
]
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def typelize_method_transform(method:, name:, binding:, type:, attrs:)
|
|
14
|
-
return {binding.local_variable_get(:attr) => [type, attrs]} if method == :attribute
|
|
15
|
-
|
|
16
|
-
super
|
|
17
|
-
end
|
|
18
|
-
|
|
19
6
|
def properties
|
|
20
7
|
serializer._attributes_data.merge(serializer._reflections).flat_map do |key, association|
|
|
21
8
|
type = association.options[:serializer] ? context.interface_for(association.options[:serializer]) : nil
|
|
@@ -3,13 +3,6 @@ require_relative "base"
|
|
|
3
3
|
module Typelizer
|
|
4
4
|
module SerializerPlugins
|
|
5
5
|
class OjSerializers < Base
|
|
6
|
-
def methods_to_typelize
|
|
7
|
-
[
|
|
8
|
-
:has_many, :has_one, :belongs_to,
|
|
9
|
-
:flat_one, :attribute, :attributes
|
|
10
|
-
]
|
|
11
|
-
end
|
|
12
|
-
|
|
13
6
|
def properties
|
|
14
7
|
transform_keys = serializer.try(:_transform_keys)
|
|
15
8
|
attributes = serializer._attributes
|
|
@@ -3,10 +3,6 @@ require_relative "base"
|
|
|
3
3
|
module Typelizer
|
|
4
4
|
module SerializerPlugins
|
|
5
5
|
class Panko < Base
|
|
6
|
-
def methods_to_typelize
|
|
7
|
-
[:has_many, :has_one, :attributes, :method_added]
|
|
8
|
-
end
|
|
9
|
-
|
|
10
6
|
def properties
|
|
11
7
|
descriptor = serializer.new.instance_variable_get(:@descriptor)
|
|
12
8
|
attributes = descriptor.attributes
|
|
@@ -25,14 +21,6 @@ module Typelizer
|
|
|
25
21
|
end
|
|
26
22
|
end
|
|
27
23
|
|
|
28
|
-
def typelize_method_transform(method:, name:, binding:, type:, attrs:)
|
|
29
|
-
if method == :method_added && binding.local_variable_defined?(:method)
|
|
30
|
-
name = binding.local_variable_get(:method)
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
super
|
|
34
|
-
end
|
|
35
|
-
|
|
36
24
|
private
|
|
37
25
|
|
|
38
26
|
def attribute_property(att)
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
<%- if enums.any? -%>
|
|
2
|
-
|
|
2
|
+
<%- enums_path = prefer_double_quotes ? '"./Enums"' : "'./Enums'" -%>
|
|
3
|
+
export type { <%= ImportSorter.sort(enums.map(&:enum_type_name), imports_sort_order).join(", ") %> } from <%= enums_path %>
|
|
3
4
|
<%- end -%>
|
|
4
5
|
<%- interfaces.each do |interface| -%>
|
|
6
|
+
<%- sorted_traits = ImportSorter.sort(interface.trait_interfaces.map(&:name), interface.config.imports_sort_order) -%>
|
|
5
7
|
<%- if interface.config.verbatim_module_syntax -%>
|
|
6
|
-
export type { <%= interface.name %><%= ", " +
|
|
8
|
+
export type { <%= interface.name %><%= ", " + sorted_traits.join(", ") if sorted_traits.any? %> } from <%= interface.quote('./' + interface.filename) %>
|
|
7
9
|
<%- else -%>
|
|
8
|
-
export type { default as <%= interface.name %><%= ", " +
|
|
10
|
+
export type { default as <%= interface.name %><%= ", " + sorted_traits.join(", ") if sorted_traits.any? %> } from <%= interface.quote('./' + interface.filename) %>
|
|
9
11
|
<%- end -%>
|
|
10
12
|
<%- end -%>
|
|
@@ -1 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
<%
|
|
2
|
+
omit_props = interface.overwritten_properties.map { |pr| interface.quote(pr.name) }
|
|
3
|
+
omit_props = omit_props.sort_by(&:downcase) if interface.config.properties_sort_order == :alphabetical
|
|
4
|
+
-%>
|
|
5
|
+
<%= interface.overwritten_properties.any? ? "Omit<" : "" %><%= interface.parent_interface.name %><%= "[" + interface.quote(interface.parent_interface.root_key) + "]" if interface.parent_interface.root_key %><%= interface.overwritten_properties.any? ? ", " + omit_props.join(' | ') + ">" : ""%>
|
|
@@ -9,14 +9,14 @@ render("inheritance.ts.erb", interface: interface).strip if interface.parent_int
|
|
|
9
9
|
<%= " & " if interface.parent_interface %>{
|
|
10
10
|
<% interface.properties_to_print.each do |property| -%>
|
|
11
11
|
<%= render("comment.ts.erb", interface: interface, property: property) -%>
|
|
12
|
-
<%= indent(property) %>;
|
|
12
|
+
<%= indent(property.render(sort_order: interface.config.properties_sort_order, prefer_double_quotes: interface.config.prefer_double_quotes)) %>;
|
|
13
13
|
<% end -%>
|
|
14
14
|
}
|
|
15
15
|
<% end %><% if interface.root_key %>
|
|
16
16
|
type <%= interface.name %> = {
|
|
17
17
|
<%= indent(interface.root_key) %>: <%= interface.name %>Data;
|
|
18
18
|
<% interface.meta_fields&.each do |property| -%>
|
|
19
|
-
<%= indent(property) %>;
|
|
19
|
+
<%= indent(property.render(sort_order: interface.config.properties_sort_order, prefer_double_quotes: interface.config.prefer_double_quotes)) %>;
|
|
20
20
|
<% end -%>
|
|
21
21
|
}
|
|
22
22
|
<% end -%>
|
|
@@ -24,16 +24,17 @@ type <%= interface.name %> = {
|
|
|
24
24
|
|
|
25
25
|
type <%= trait.name %> = {
|
|
26
26
|
<% trait.properties.each do |property| -%>
|
|
27
|
-
<%= indent(property) %>;
|
|
27
|
+
<%= indent(property.render(sort_order: interface.config.properties_sort_order, prefer_double_quotes: interface.config.prefer_double_quotes)) %>;
|
|
28
28
|
<% end -%>
|
|
29
29
|
}
|
|
30
30
|
<% end -%>
|
|
31
31
|
|
|
32
|
+
<% sorted_trait_names = ImportSorter.sort(interface.trait_interfaces.map(&:name), interface.config.imports_sort_order) -%>
|
|
32
33
|
<% if interface.config.verbatim_module_syntax -%>
|
|
33
|
-
export type { <%= interface.name %><%= ", " +
|
|
34
|
+
export type { <%= interface.name %><%= ", " + sorted_trait_names.join(", ") if sorted_trait_names.any? %> };
|
|
34
35
|
<% else -%>
|
|
35
36
|
export default <%= interface.name %>;
|
|
36
|
-
<% if
|
|
37
|
-
export type { <%=
|
|
37
|
+
<% if sorted_trait_names.any? -%>
|
|
38
|
+
export type { <%= sorted_trait_names.join(", ") %> };
|
|
38
39
|
<% end -%>
|
|
39
40
|
<% end -%>
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Typelizer
|
|
4
|
+
# Sorts union type members within TypeScript type strings.
|
|
5
|
+
# Handles types like "Type3 | Type1 | Type2" -> "Type1 | Type2 | Type3"
|
|
6
|
+
# Also handles complex nested types like "Array<Type3 | Type1>" -> "Array<Type1 | Type3>"
|
|
7
|
+
module UnionTypeSorter
|
|
8
|
+
# Sorts union type members in a type string
|
|
9
|
+
# @param type_str [String] The type string potentially containing unions
|
|
10
|
+
# @param sort_order [Symbol, Proc, nil] The sort order (:none, :alphabetical, or Proc)
|
|
11
|
+
# @return [String] The type string with sorted union members
|
|
12
|
+
def self.sort(type_str, sort_order)
|
|
13
|
+
return type_str if type_str.nil? || type_str.empty?
|
|
14
|
+
|
|
15
|
+
case sort_order
|
|
16
|
+
when :none, nil
|
|
17
|
+
type_str
|
|
18
|
+
when :alphabetical
|
|
19
|
+
sort_unions_alphabetically(type_str)
|
|
20
|
+
when Proc
|
|
21
|
+
result = sort_order.call(type_str)
|
|
22
|
+
result.is_a?(String) ? result : type_str
|
|
23
|
+
else
|
|
24
|
+
type_str
|
|
25
|
+
end
|
|
26
|
+
rescue => e
|
|
27
|
+
Typelizer.logger.warn("UnionTypeSorter error: #{e.message}, preserving original order")
|
|
28
|
+
type_str
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Sorts union members alphabetically while preserving structure
|
|
32
|
+
# @param type_str [String] The type string to sort
|
|
33
|
+
# @return [String] The sorted type string
|
|
34
|
+
def self.sort_unions_alphabetically(type_str)
|
|
35
|
+
# Handle the string by sorting unions at each level
|
|
36
|
+
# We need to be careful with nested generics like Array<A | B | C>
|
|
37
|
+
|
|
38
|
+
result = type_str.dup
|
|
39
|
+
|
|
40
|
+
# First, handle unions inside angle brackets (generics)
|
|
41
|
+
# Match content inside < > and sort unions within
|
|
42
|
+
result = result.gsub(/<([^<>]+)>/) do |match|
|
|
43
|
+
inner = Regexp.last_match(1)
|
|
44
|
+
sorted_inner = sort_simple_union(inner)
|
|
45
|
+
"<#{sorted_inner}>"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Then handle any remaining top-level unions
|
|
49
|
+
# But avoid sorting if the string has unbalanced brackets
|
|
50
|
+
if balanced_brackets?(result)
|
|
51
|
+
result = sort_top_level_union(result)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
result
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Sorts a simple union string (no nested generics)
|
|
58
|
+
# @param union_str [String] String like "Type3 | Type1 | Type2"
|
|
59
|
+
# @return [String] Sorted string like "Type1 | Type2 | Type3"
|
|
60
|
+
def self.sort_simple_union(union_str)
|
|
61
|
+
return union_str unless union_str.include?("|")
|
|
62
|
+
|
|
63
|
+
parts = split_union_members(union_str)
|
|
64
|
+
return union_str if parts.size <= 1
|
|
65
|
+
|
|
66
|
+
# Sort while preserving special cases:
|
|
67
|
+
# - 'null' should typically stay at the end
|
|
68
|
+
# - Keep the relative order of complex nested types
|
|
69
|
+
regular_parts, null_parts = parts.partition { |p| p.strip.downcase != "null" }
|
|
70
|
+
|
|
71
|
+
sorted_regular = regular_parts.sort_by { |p| p.strip.downcase }
|
|
72
|
+
(sorted_regular + null_parts).join(" | ")
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Sorts top-level union (handles cases where unions aren't inside generics)
|
|
76
|
+
# @param type_str [String] The type string
|
|
77
|
+
# @return [String] The sorted type string
|
|
78
|
+
def self.sort_top_level_union(type_str)
|
|
79
|
+
return type_str unless type_str.include?("|")
|
|
80
|
+
|
|
81
|
+
parts = split_union_members(type_str)
|
|
82
|
+
return type_str if parts.size <= 1
|
|
83
|
+
|
|
84
|
+
# Separate null from other types
|
|
85
|
+
regular_parts, null_parts = parts.partition { |p| p.strip.downcase != "null" }
|
|
86
|
+
|
|
87
|
+
sorted_regular = regular_parts.sort_by { |p| p.strip.downcase }
|
|
88
|
+
(sorted_regular + null_parts).join(" | ")
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Splits union members while respecting nested brackets
|
|
92
|
+
# @param str [String] The string to split
|
|
93
|
+
# @return [Array<String>] Array of union members
|
|
94
|
+
def self.split_union_members(str)
|
|
95
|
+
members = []
|
|
96
|
+
current = +""
|
|
97
|
+
depth = 0
|
|
98
|
+
|
|
99
|
+
str.each_char do |char|
|
|
100
|
+
case char
|
|
101
|
+
when "<", "("
|
|
102
|
+
depth += 1
|
|
103
|
+
current << char
|
|
104
|
+
when ">", ")"
|
|
105
|
+
depth -= 1
|
|
106
|
+
current << char
|
|
107
|
+
when "|"
|
|
108
|
+
if depth == 0
|
|
109
|
+
members << current.strip unless current.strip.empty?
|
|
110
|
+
current = +""
|
|
111
|
+
else
|
|
112
|
+
current << char
|
|
113
|
+
end
|
|
114
|
+
else
|
|
115
|
+
current << char
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
members << current.strip unless current.strip.empty?
|
|
120
|
+
members
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
# Checks if brackets are balanced in the string
|
|
124
|
+
# @param str [String] The string to check
|
|
125
|
+
# @return [Boolean] True if brackets are balanced
|
|
126
|
+
def self.balanced_brackets?(str)
|
|
127
|
+
angle_depth = 0
|
|
128
|
+
paren_depth = 0
|
|
129
|
+
|
|
130
|
+
str.each_char do |char|
|
|
131
|
+
case char
|
|
132
|
+
when "<"
|
|
133
|
+
angle_depth += 1
|
|
134
|
+
when ">"
|
|
135
|
+
angle_depth -= 1
|
|
136
|
+
return false if angle_depth < 0
|
|
137
|
+
when "("
|
|
138
|
+
paren_depth += 1
|
|
139
|
+
when ")"
|
|
140
|
+
paren_depth -= 1
|
|
141
|
+
return false if paren_depth < 0
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
angle_depth == 0 && paren_depth == 0
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
data/lib/typelizer/version.rb
CHANGED
data/lib/typelizer/writer.rb
CHANGED
|
@@ -60,15 +60,21 @@ module Typelizer
|
|
|
60
60
|
end
|
|
61
61
|
|
|
62
62
|
def write_enums(enums)
|
|
63
|
-
|
|
64
|
-
|
|
63
|
+
fingerprint = [enums.map(&:fingerprint), config.properties_sort_order, config.prefer_double_quotes].inspect
|
|
64
|
+
write_file("Enums.ts", fingerprint) do
|
|
65
|
+
render_template("enums.ts.erb", enums: enums, sort_order: config.properties_sort_order, prefer_double_quotes: config.prefer_double_quotes)
|
|
65
66
|
end
|
|
66
67
|
end
|
|
67
68
|
|
|
68
69
|
def write_index(interfaces, enums: [])
|
|
69
|
-
fingerprint =
|
|
70
|
+
fingerprint = [
|
|
71
|
+
enums.map(&:enum_type_name),
|
|
72
|
+
interfaces.map { |i|
|
|
73
|
+
[i.name, i.filename, i.trait_interfaces.map(&:name), CONFIGS_AFFECTING_INDEX_OUTPUT.map { |key| i.config.public_send(key) }]
|
|
74
|
+
}
|
|
75
|
+
].inspect
|
|
70
76
|
write_file("index.ts", fingerprint) do
|
|
71
|
-
render_template("index.ts.erb", interfaces: interfaces, enums: enums)
|
|
77
|
+
render_template("index.ts.erb", interfaces: interfaces, enums: enums, imports_sort_order: config.imports_sort_order, prefer_double_quotes: config.prefer_double_quotes)
|
|
72
78
|
end
|
|
73
79
|
end
|
|
74
80
|
|
data/lib/typelizer.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
require_relative "typelizer/version"
|
|
4
|
+
require_relative "typelizer/union_type_sorter"
|
|
4
5
|
require_relative "typelizer/property"
|
|
5
6
|
require_relative "typelizer/model_plugins/auto"
|
|
6
7
|
require_relative "typelizer/serializer_plugins/auto"
|
|
@@ -10,7 +11,6 @@ require_relative "typelizer/configuration"
|
|
|
10
11
|
require_relative "typelizer/serializer_config_layer"
|
|
11
12
|
|
|
12
13
|
require_relative "typelizer/contexts/writer_context"
|
|
13
|
-
require_relative "typelizer/contexts/scan_context"
|
|
14
14
|
require_relative "typelizer/property_sorter"
|
|
15
15
|
require_relative "typelizer/import_sorter"
|
|
16
16
|
require_relative "typelizer/interface"
|
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.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Svyatoslav Kryukov
|
|
@@ -50,9 +50,13 @@ files:
|
|
|
50
50
|
- lib/typelizer.rb
|
|
51
51
|
- lib/typelizer/config.rb
|
|
52
52
|
- lib/typelizer/configuration.rb
|
|
53
|
-
- lib/typelizer/contexts/scan_context.rb
|
|
54
53
|
- lib/typelizer/contexts/writer_context.rb
|
|
55
54
|
- lib/typelizer/dsl.rb
|
|
55
|
+
- lib/typelizer/dsl/hooks.rb
|
|
56
|
+
- lib/typelizer/dsl/hooks/alba.rb
|
|
57
|
+
- lib/typelizer/dsl/hooks/ams.rb
|
|
58
|
+
- lib/typelizer/dsl/hooks/oj_serializers.rb
|
|
59
|
+
- lib/typelizer/dsl/hooks/panko.rb
|
|
56
60
|
- lib/typelizer/generator.rb
|
|
57
61
|
- lib/typelizer/import_sorter.rb
|
|
58
62
|
- lib/typelizer/interface.rb
|
|
@@ -81,6 +85,7 @@ files:
|
|
|
81
85
|
- lib/typelizer/templates/inline_type.ts.erb
|
|
82
86
|
- lib/typelizer/templates/interface.ts.erb
|
|
83
87
|
- lib/typelizer/type_parser.rb
|
|
88
|
+
- lib/typelizer/union_type_sorter.rb
|
|
84
89
|
- lib/typelizer/version.rb
|
|
85
90
|
- lib/typelizer/writer.rb
|
|
86
91
|
homepage: https://github.com/skryukov/typelizer
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Typelizer
|
|
4
|
-
# Builds a minimal plugin used during scan time
|
|
5
|
-
class ScanContext
|
|
6
|
-
class InvalidOperationError < StandardError; end
|
|
7
|
-
|
|
8
|
-
# Interface creation is not available during DSL scanning phase (TracePoint)
|
|
9
|
-
def self.interface_for(serializer_class)
|
|
10
|
-
class_name = serializer_class&.name || "unknown class"
|
|
11
|
-
raise InvalidOperationError,
|
|
12
|
-
"Interface creation is not allowed during DSL scan (#{class_name})"
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
# just in case, if we call ScanContext like an object
|
|
16
|
-
def interface_for(serializer_class)
|
|
17
|
-
self.class.interface_for(serializer_class)
|
|
18
|
-
end
|
|
19
|
-
end
|
|
20
|
-
end
|