panko_serializer 1.0.0 → 1.0.1
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/lib/panko/code_gen/active_record/access_classifier.rb +1 -1
- data/lib/panko/code_gen/compiler.rb +22 -17
- data/lib/panko/code_gen/config.rb +0 -4
- data/lib/panko/code_gen/descriptor.rb +4 -2
- data/lib/panko/code_gen/descriptor_builder.rb +1 -1
- data/lib/panko/code_gen/dump.rb +3 -6
- data/lib/panko/code_gen/generator.rb +3 -3
- data/lib/panko/code_gen/generators/class_emitter.rb +4 -4
- data/lib/panko/code_gen/generators/field_index.rb +2 -4
- data/lib/panko/code_gen/generators/field_walk.rb +4 -4
- data/lib/panko/code_gen/generators/generated_names.rb +28 -8
- data/lib/panko/code_gen/generators/hash_sink.rb +18 -18
- data/lib/panko/code_gen/generators/json_sink.rb +37 -41
- data/lib/panko/code_gen/generators/record_access/generic.rb +4 -4
- data/lib/panko/code_gen/generators/record_access/specialized.rb +9 -7
- data/lib/panko/code_gen/generators/release.rb +5 -5
- data/lib/panko/code_gen/generators/sink.rb +9 -9
- data/lib/panko/code_gen/validators/source_resolution.rb +1 -1
- data/lib/panko/code_gen/validators/validator.rb +4 -8
- data/lib/panko/code_gen.rb +0 -2
- data/lib/panko/serializer.rb +2 -1
- data/lib/panko/version.rb +1 -1
- metadata +1 -3
- data/lib/panko/code_gen/active_record/define_attribute_methods.rb +0 -13
- data/lib/panko/code_gen/compile_cache.rb +0 -28
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d979b96c2fb100900b8ee11656f8a9d8d78eecb28b58d3d6c26e1db63c34c829
|
|
4
|
+
data.tar.gz: 43f9c3badbeff01f514923de689c6fb300b72db18e3d0a751684009afcf559db
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6ec1e557bc67c0bcb2743582fd93a18410b05161c3ea760943cc33e65f1c571f7a3d0d99bdb25870a8a87177e19bdc652cea15a4d5da56e6b087df56bf80640f
|
|
7
|
+
data.tar.gz: 792a57264e47d36207515fe6c4d2bdd9d4fab7ba0e34fe8b6b558e997ae691897b78e36a07306075438fe680f667b6d67c71f3d701301c7c5abc353893388be4
|
|
@@ -4,7 +4,7 @@ module Panko::CodeGen
|
|
|
4
4
|
module ActiveRecord
|
|
5
5
|
# Columns read through AR's own generated reader classify as +:column+.
|
|
6
6
|
# A user override of a column reader classifies as +:method+, so the
|
|
7
|
-
# Specialized path never bypasses it. Call +
|
|
7
|
+
# Specialized path never bypasses it. Call +define_attribute_methods+
|
|
8
8
|
# first, or non-column readers (+attribute+, +alias_attribute+) raise +UnknownSourceError+.
|
|
9
9
|
module AccessClassifier
|
|
10
10
|
def self.classify(klass, source)
|
|
@@ -5,28 +5,34 @@ module Panko::CodeGen
|
|
|
5
5
|
# A table, not +to_s.upcase+: the +:hash+ suffix is +Hash+, not +HASH+.
|
|
6
6
|
OUTPUT_SUFFIXES = {json: "JSON", hash: "Hash"}.freeze
|
|
7
7
|
|
|
8
|
-
def initialize(descriptor, output:, config
|
|
9
|
-
generator: Generator.new)
|
|
8
|
+
def initialize(descriptor, output:, config:)
|
|
10
9
|
@descriptor = descriptor
|
|
11
10
|
@output = output
|
|
12
11
|
@config = config
|
|
13
|
-
|
|
14
|
-
@
|
|
15
|
-
@cache = CompileCache.new
|
|
12
|
+
# Identity, not equality: equal but distinct Descriptors compile to separate classes.
|
|
13
|
+
@compiled = {}.compare_by_identity
|
|
16
14
|
end
|
|
17
15
|
|
|
18
16
|
def compile
|
|
19
|
-
|
|
20
|
-
source =
|
|
17
|
+
Validators::Validator.validate(@descriptor, output: @output, config: @config)
|
|
18
|
+
source = Generator.emit(@descriptor, output: @output, config: @config)
|
|
21
19
|
namespace = Class.new
|
|
22
20
|
bind_anonymous_parents(namespace)
|
|
23
|
-
namespace
|
|
24
|
-
|
|
25
|
-
@
|
|
21
|
+
evaluate(namespace, source)
|
|
22
|
+
collect_classes(@descriptor, namespace)
|
|
23
|
+
@compiled.fetch(@descriptor)
|
|
26
24
|
end
|
|
27
25
|
|
|
28
26
|
private
|
|
29
27
|
|
|
28
|
+
# SyntaxError is a ScriptError, which a bare +rescue+ (Panko.compile_all, the
|
|
29
|
+
# auto-specialization fallback) does not catch.
|
|
30
|
+
def evaluate(namespace, source)
|
|
31
|
+
namespace.module_eval(source, synthetic_path, 1)
|
|
32
|
+
rescue SyntaxError => error
|
|
33
|
+
raise CompileError, "#{@descriptor.name}: generated #{@output} source does not parse: #{error.message}"
|
|
34
|
+
end
|
|
35
|
+
|
|
30
36
|
# A Hash, not one constant per class: +const_set+ gives an anonymous class a name,
|
|
31
37
|
# which would change +parent_class.name+ for the next compile.
|
|
32
38
|
def bind_anonymous_parents(namespace)
|
|
@@ -43,13 +49,12 @@ module Panko::CodeGen
|
|
|
43
49
|
"(#{GENERATOR_NAME}: #{@descriptor.name}/#{@output})"
|
|
44
50
|
end
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
52
|
+
# Records the class before descending, so a cycle back to this Descriptor stops here.
|
|
53
|
+
def collect_classes(descriptor, namespace)
|
|
54
|
+
return if @compiled.key?(descriptor)
|
|
55
|
+
@compiled[descriptor] = namespace.const_get(:"#{descriptor.name}_#{OUTPUT_SUFFIXES.fetch(@output)}")
|
|
56
|
+
descriptor.associations.each do |assoc|
|
|
57
|
+
assoc.descriptors.each { |child| collect_classes(child, namespace) }
|
|
53
58
|
end
|
|
54
59
|
end
|
|
55
60
|
end
|
|
@@ -8,7 +8,6 @@ module Panko::CodeGen
|
|
|
8
8
|
# - +supports_root_key+: when +false+, passing +root_key:+ raises +ArgumentError+.
|
|
9
9
|
# - +json_column_emit+ (JSON mode, Specialized path only): +:wire_format+ writes the
|
|
10
10
|
# stored bytes after a well-formedness check, +:html_safe+ the typed value, escaped.
|
|
11
|
-
# - +pool_writer+: no effect in Hash output mode.
|
|
12
11
|
# - +guarded_model+: a record of another class goes to the generic body.
|
|
13
12
|
# Needs a named +descriptor.model+.
|
|
14
13
|
Config = Data.define(
|
|
@@ -17,7 +16,6 @@ module Panko::CodeGen
|
|
|
17
16
|
:hash_record_key_type,
|
|
18
17
|
:hash_output_key_type,
|
|
19
18
|
:json_column_emit,
|
|
20
|
-
:pool_writer,
|
|
21
19
|
:guarded_model
|
|
22
20
|
)
|
|
23
21
|
|
|
@@ -27,7 +25,6 @@ module Panko::CodeGen
|
|
|
27
25
|
hash_record_key_type: :string,
|
|
28
26
|
hash_output_key_type: :string,
|
|
29
27
|
json_column_emit: :wire_format,
|
|
30
|
-
pool_writer: true,
|
|
31
28
|
guarded_model: false
|
|
32
29
|
}.freeze
|
|
33
30
|
|
|
@@ -50,7 +47,6 @@ module Panko::CodeGen
|
|
|
50
47
|
validate_enum!(:hash_record_key_type, values[:hash_record_key_type])
|
|
51
48
|
validate_enum!(:hash_output_key_type, values[:hash_output_key_type])
|
|
52
49
|
validate_json_column_emit!(values[:json_column_emit])
|
|
53
|
-
validate_boolean!(:pool_writer, values[:pool_writer])
|
|
54
50
|
validate_boolean!(:guarded_model, values[:guarded_model])
|
|
55
51
|
end
|
|
56
52
|
|
|
@@ -94,8 +94,10 @@ module Panko::CodeGen
|
|
|
94
94
|
StructuralValidation.validate_kind!("Association#kind", kind)
|
|
95
95
|
StructuralValidation.validate_descriptor!("Association#descriptor", descriptor)
|
|
96
96
|
StructuralValidation.validate_symbol!("Association#source", source)
|
|
97
|
-
#
|
|
98
|
-
|
|
97
|
+
# A method name is not supported here (unlike a MethodAttribute body), so a Symbol is rejected up front.
|
|
98
|
+
if if_callable.is_a?(Symbol)
|
|
99
|
+
raise DescriptorError, "Association#if: must be a Callable, not a Symbol; got #{if_callable.inspect}"
|
|
100
|
+
end
|
|
99
101
|
StructuralValidation.validate_callable!("Association#if", if_callable) unless if_callable.nil?
|
|
100
102
|
StructuralValidation.validate_array_of!("Association#variants", variants, Descriptor, "Descriptor")
|
|
101
103
|
validate_variants!(variants)
|
|
@@ -87,7 +87,7 @@ module Panko
|
|
|
87
87
|
# A declared model missing one of the sources would stop the whole parent from compiling,
|
|
88
88
|
# so it is left out and its records get the unspecialized child.
|
|
89
89
|
def serves?(klass, descriptor)
|
|
90
|
-
|
|
90
|
+
klass.define_attribute_methods
|
|
91
91
|
sources = descriptor.attributes.map(&:source) + descriptor.associations.map(&:source)
|
|
92
92
|
sources.all? do |source|
|
|
93
93
|
ActiveRecord::AccessClassifier.classify(klass, source)
|
data/lib/panko/code_gen/dump.rb
CHANGED
|
@@ -2,19 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
module Panko::CodeGen
|
|
4
4
|
class Dump
|
|
5
|
-
def initialize(descriptor, output:, config:, path
|
|
6
|
-
validator: Validators::Validator.new, generator: Generator.new)
|
|
5
|
+
def initialize(descriptor, output:, config:, path:)
|
|
7
6
|
@descriptor = descriptor
|
|
8
7
|
@output = output
|
|
9
8
|
@config = config
|
|
10
9
|
@path = path
|
|
11
|
-
@validator = validator
|
|
12
|
-
@generator = generator
|
|
13
10
|
end
|
|
14
11
|
|
|
15
12
|
def dump
|
|
16
13
|
validate_path!
|
|
17
|
-
|
|
14
|
+
Validators::Validator.validate(@descriptor, output: @output, config: @config)
|
|
18
15
|
if @descriptor.associations.empty?
|
|
19
16
|
write_flat
|
|
20
17
|
else
|
|
@@ -34,7 +31,7 @@ module Panko::CodeGen
|
|
|
34
31
|
end
|
|
35
32
|
|
|
36
33
|
def write_flat
|
|
37
|
-
source =
|
|
34
|
+
source = Generator.emit(@descriptor, output: @output, config: @config)
|
|
38
35
|
File.write(@path, source)
|
|
39
36
|
end
|
|
40
37
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Panko::CodeGen
|
|
4
|
-
|
|
4
|
+
module Generator
|
|
5
5
|
OUTPUT_MODES = %i[json hash].freeze
|
|
6
6
|
|
|
7
7
|
# Sinks are stateless, so one frozen instance per mode serves every emit.
|
|
@@ -16,8 +16,8 @@ module Panko::CodeGen
|
|
|
16
16
|
end
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def emit(descriptor, output:, config:)
|
|
20
|
-
Generators::ClassEmitter.new(
|
|
19
|
+
def self.emit(descriptor, output:, config:)
|
|
20
|
+
Generators::ClassEmitter.new(sink_for(output)).emit(descriptor, config)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
23
23
|
end
|
|
@@ -76,16 +76,16 @@ module Panko::CodeGen
|
|
|
76
76
|
builder.line "_construct_cache[descriptor.__id__] = self" if cyclic_self
|
|
77
77
|
descriptor.method_attributes.each_with_index do |method_attribute, index|
|
|
78
78
|
next if method_attribute.body.is_a?(Symbol)
|
|
79
|
-
ivar = GeneratedNames.callable_ivar(
|
|
79
|
+
ivar = GeneratedNames.callable_ivar(index)
|
|
80
80
|
builder.line "#{ivar} = descriptor.method_attributes[#{index}].body"
|
|
81
81
|
end
|
|
82
82
|
descriptor.associations.each_with_index do |assoc, i|
|
|
83
83
|
if assoc.if
|
|
84
|
-
builder.line "#{GeneratedNames.if_guard_ivar(
|
|
84
|
+
builder.line "#{GeneratedNames.if_guard_ivar(i)} = descriptor.associations[#{i}].if"
|
|
85
85
|
end
|
|
86
86
|
builder.line emit_serializer_assignment(descriptor, assoc, i, cyclic_ids)
|
|
87
87
|
assoc.variants.each_with_index do |variant, v|
|
|
88
|
-
builder.line "#{GeneratedNames.variant_serializer_ivar(
|
|
88
|
+
builder.line "#{GeneratedNames.variant_serializer_ivar(i, v)} = " \
|
|
89
89
|
"#{GeneratedNames.class_name(variant, @sink.suffix)}.new(descriptor: descriptor.associations[#{i}].variants[#{v}])"
|
|
90
90
|
end
|
|
91
91
|
end
|
|
@@ -96,7 +96,7 @@ module Panko::CodeGen
|
|
|
96
96
|
# The cache is passed only when both ends are cyclic: a child outside
|
|
97
97
|
# the cycle has a constructor without the +_construct_cache:+ kwarg.
|
|
98
98
|
def emit_serializer_assignment(descriptor, assoc, i, cyclic_ids)
|
|
99
|
-
ivar = GeneratedNames.serializer_ivar(
|
|
99
|
+
ivar = GeneratedNames.serializer_ivar(i)
|
|
100
100
|
if assoc.descriptor.equal?(descriptor)
|
|
101
101
|
"#{ivar} = self"
|
|
102
102
|
elsif cyclic_ids[descriptor.__id__] && cyclic_ids[assoc.descriptor.__id__]
|
|
@@ -20,11 +20,9 @@ module Panko::CodeGen
|
|
|
20
20
|
index
|
|
21
21
|
end
|
|
22
22
|
|
|
23
|
-
#
|
|
23
|
+
# Ruby 3.4+ +Hash#inspect+ quotes a Symbol key that is not an identifier (+"2fa": 0+).
|
|
24
24
|
def to_hash_literal(field_index)
|
|
25
|
-
|
|
26
|
-
pairs = field_index.map { |name, idx| "#{name}: #{idx}" }
|
|
27
|
-
"{#{pairs.join(", ")}}"
|
|
25
|
+
field_index.inspect
|
|
28
26
|
end
|
|
29
27
|
end
|
|
30
28
|
end
|
|
@@ -19,15 +19,15 @@ module Panko::CodeGen
|
|
|
19
19
|
sink.attribute(attribute, read_expr.call(attribute.source), config, index, builder)
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
|
-
descriptor.associations.
|
|
22
|
+
descriptor.associations.each_with_index do |association, position|
|
|
23
23
|
sink.association(
|
|
24
|
-
association, read_expr.call(association.source), config,
|
|
24
|
+
association, position, read_expr.call(association.source), config,
|
|
25
25
|
field_index.fetch(GeneratedNames.filter_key(association)), builder
|
|
26
26
|
)
|
|
27
27
|
end
|
|
28
|
-
descriptor.method_attributes.
|
|
28
|
+
descriptor.method_attributes.each_with_index do |method_attribute, position|
|
|
29
29
|
sink.method_attribute(
|
|
30
|
-
method_attribute, config,
|
|
30
|
+
method_attribute, position, config,
|
|
31
31
|
field_index.fetch(GeneratedNames.filter_key(method_attribute)), builder
|
|
32
32
|
)
|
|
33
33
|
end
|
|
@@ -11,20 +11,40 @@ module Panko::CodeGen
|
|
|
11
11
|
"#{descriptor.name}_#{suffix}"
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
# Field names can hold any character, so instance variables are named by
|
|
15
|
+
# the Field's position in its Descriptor list, never by its name.
|
|
16
|
+
def serializer_ivar(position)
|
|
17
|
+
"@child_#{position}"
|
|
16
18
|
end
|
|
17
19
|
|
|
18
|
-
def variant_serializer_ivar(
|
|
19
|
-
"#{serializer_ivar(
|
|
20
|
+
def variant_serializer_ivar(position, index)
|
|
21
|
+
"#{serializer_ivar(position)}_#{index}"
|
|
20
22
|
end
|
|
21
23
|
|
|
22
|
-
def callable_ivar(
|
|
23
|
-
"@cb_#{
|
|
24
|
+
def callable_ivar(position)
|
|
25
|
+
"@cb_#{position}"
|
|
24
26
|
end
|
|
25
27
|
|
|
26
|
-
def if_guard_ivar(
|
|
27
|
-
"@cb_if_#{
|
|
28
|
+
def if_guard_ivar(position)
|
|
29
|
+
"@cb_if_#{position}"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Names reach generated source only as escaped literals, so a quote or an
|
|
33
|
+
# interpolation in a name cannot change the code.
|
|
34
|
+
def string_literal(name)
|
|
35
|
+
name.to_s.inspect
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def symbol_literal(name)
|
|
39
|
+
name.to_sym.inspect
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
PLAIN_METHOD_NAME = /\A[A-Za-z_][A-Za-z0-9_]*[?!]?\z/
|
|
43
|
+
|
|
44
|
+
# A name dot syntax cannot parse (+2fa+, +a-b+) is called through +__send__+.
|
|
45
|
+
def method_call(receiver, name)
|
|
46
|
+
return "#{receiver}.#{name}" if PLAIN_METHOD_NAME.match?(name.to_s)
|
|
47
|
+
"#{receiver}.__send__(#{symbol_literal(name)})"
|
|
28
48
|
end
|
|
29
49
|
|
|
30
50
|
def write_one
|
|
@@ -107,10 +107,10 @@ module Panko::CodeGen
|
|
|
107
107
|
end
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
def method_attribute(method_attribute, config, index, builder)
|
|
110
|
+
def method_attribute(method_attribute, position, config, index, builder)
|
|
111
111
|
builder.line "unless filters.drops?(#{index})"
|
|
112
112
|
builder.indent do
|
|
113
|
-
builder.line "value = #{method_attribute_call_expression(method_attribute)}"
|
|
113
|
+
builder.line "value = #{method_attribute_call_expression(method_attribute, position)}"
|
|
114
114
|
builder.line "unless value.equal?(Panko::CodeGen::SKIP)"
|
|
115
115
|
builder.indent do
|
|
116
116
|
builder.line "result[#{key_literal(method_attribute.name, config)}] = Panko::CodeGen.cast_datetime(value)"
|
|
@@ -120,21 +120,21 @@ module Panko::CodeGen
|
|
|
120
120
|
builder.line "end"
|
|
121
121
|
end
|
|
122
122
|
|
|
123
|
-
def association(association, source_read_expr, config, index, builder)
|
|
123
|
+
def association(association, position, source_read_expr, config, index, builder)
|
|
124
124
|
key_lit = key_literal(association.name, config)
|
|
125
125
|
builder.line "unless filters.drops?(#{index})"
|
|
126
126
|
builder.indent do
|
|
127
|
-
with_if_guard(association, builder) do
|
|
127
|
+
with_if_guard(association, position, builder) do
|
|
128
128
|
case association.kind
|
|
129
129
|
when :has_one
|
|
130
130
|
builder.line "value = #{source_read_expr}"
|
|
131
131
|
if config.null_for_missing_has_one
|
|
132
|
-
has_one_default(association, key_lit, builder)
|
|
132
|
+
has_one_default(association, position, key_lit, builder)
|
|
133
133
|
else
|
|
134
|
-
has_one_omit(association, key_lit, builder)
|
|
134
|
+
has_one_omit(association, position, key_lit, builder)
|
|
135
135
|
end
|
|
136
136
|
when :has_many
|
|
137
|
-
has_many(association, source_read_expr, key_lit, builder)
|
|
137
|
+
has_many(association, position, source_read_expr, key_lit, builder)
|
|
138
138
|
end
|
|
139
139
|
end
|
|
140
140
|
end
|
|
@@ -145,33 +145,33 @@ module Panko::CodeGen
|
|
|
145
145
|
|
|
146
146
|
def key_literal(name, config)
|
|
147
147
|
case config.hash_output_key_type
|
|
148
|
-
when :symbol then
|
|
149
|
-
else
|
|
148
|
+
when :symbol then GeneratedNames.symbol_literal(name)
|
|
149
|
+
else GeneratedNames.string_literal(name)
|
|
150
150
|
end
|
|
151
151
|
end
|
|
152
152
|
|
|
153
|
-
def has_one_default(association, key_lit, builder)
|
|
153
|
+
def has_one_default(association, position, key_lit, builder)
|
|
154
154
|
builder.line "result[#{key_lit}] = if value.nil?"
|
|
155
155
|
builder.indent { builder.line "nil" }
|
|
156
156
|
builder.line "else"
|
|
157
157
|
builder.indent do
|
|
158
|
-
child_write(association, "value", builder) do |ivar|
|
|
158
|
+
child_write(association, position, "value", builder) do |ivar|
|
|
159
159
|
"#{ivar}.#{GeneratedNames.to_hash}(value, context, scope, #{child_filter_expr(association)})"
|
|
160
160
|
end
|
|
161
161
|
end
|
|
162
162
|
builder.line "end"
|
|
163
163
|
end
|
|
164
164
|
|
|
165
|
-
def has_one_omit(association, key_lit, builder)
|
|
165
|
+
def has_one_omit(association, position, key_lit, builder)
|
|
166
166
|
builder.line "unless value.nil?"
|
|
167
167
|
builder.indent do
|
|
168
168
|
if association.variants.empty?
|
|
169
|
-
builder.line "result[#{key_lit}] = #{GeneratedNames.serializer_ivar(
|
|
169
|
+
builder.line "result[#{key_lit}] = #{GeneratedNames.serializer_ivar(position)}." \
|
|
170
170
|
"#{GeneratedNames.to_hash}(value, context, scope, #{child_filter_expr(association)})"
|
|
171
171
|
else
|
|
172
172
|
builder.line "result[#{key_lit}] = begin"
|
|
173
173
|
builder.indent do
|
|
174
|
-
child_write(association, "value", builder) do |ivar|
|
|
174
|
+
child_write(association, position, "value", builder) do |ivar|
|
|
175
175
|
"#{ivar}.#{GeneratedNames.to_hash}(value, context, scope, #{child_filter_expr(association)})"
|
|
176
176
|
end
|
|
177
177
|
end
|
|
@@ -182,18 +182,18 @@ module Panko::CodeGen
|
|
|
182
182
|
end
|
|
183
183
|
|
|
184
184
|
# +child_filter+ is looked up once per record, not once per element.
|
|
185
|
-
def has_many(association, source_read_expr, key_lit, builder)
|
|
185
|
+
def has_many(association, position, source_read_expr, key_lit, builder)
|
|
186
186
|
builder.line "child_filter = #{child_filter_expr(association)}"
|
|
187
187
|
if association.variants.empty?
|
|
188
188
|
builder.line(
|
|
189
189
|
"result[#{key_lit}] = #{source_read_expr}.map { |element| " \
|
|
190
|
-
"#{GeneratedNames.serializer_ivar(
|
|
190
|
+
"#{GeneratedNames.serializer_ivar(position)}.#{GeneratedNames.to_hash}(element, context, scope, child_filter) }"
|
|
191
191
|
)
|
|
192
192
|
return
|
|
193
193
|
end
|
|
194
194
|
builder.line "result[#{key_lit}] = #{source_read_expr}.map do |element|"
|
|
195
195
|
builder.indent do
|
|
196
|
-
child_write(association, "element", builder) do |ivar|
|
|
196
|
+
child_write(association, position, "element", builder) do |ivar|
|
|
197
197
|
"#{ivar}.#{GeneratedNames.to_hash}(element, context, scope, child_filter)"
|
|
198
198
|
end
|
|
199
199
|
end
|
|
@@ -203,7 +203,7 @@ module Panko::CodeGen
|
|
|
203
203
|
# +format_raw+ returns nil for raw values it cannot format; the typed-read
|
|
204
204
|
# fallback then goes through +cast_datetime+ so a Time still becomes a String.
|
|
205
205
|
def datetime_column_attribute(attribute, config, index, builder)
|
|
206
|
-
source_lit =
|
|
206
|
+
source_lit = GeneratedNames.string_literal(attribute.source)
|
|
207
207
|
builder.line "unless filters.drops?(#{index})"
|
|
208
208
|
builder.indent do
|
|
209
209
|
builder.line "value = Panko::CodeGen::DateTimeFormat.format_raw(record.read_attribute_before_type_cast(#{source_lit}))"
|
|
@@ -32,7 +32,6 @@ module Panko::CodeGen
|
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
def emit_class_constants(descriptor, config, builder)
|
|
35
|
-
return unless config.pool_writer
|
|
36
35
|
subclass = defined?(ActiveSupport::IsolatedExecutionState) ? "IsolatedExecutionState" : "ThreadLocal"
|
|
37
36
|
builder.line "POOL = Panko::CodeGen::WritersPool::#{subclass}.new"
|
|
38
37
|
end
|
|
@@ -47,7 +46,7 @@ module Panko::CodeGen
|
|
|
47
46
|
if config.supports_root_key
|
|
48
47
|
builder.line "validate_root_key!(root_key)"
|
|
49
48
|
end
|
|
50
|
-
with_writer(
|
|
49
|
+
with_writer(builder) { emit_serialize_one_body(config, builder) }
|
|
51
50
|
end
|
|
52
51
|
builder.line "end"
|
|
53
52
|
end
|
|
@@ -62,7 +61,7 @@ module Panko::CodeGen
|
|
|
62
61
|
if config.supports_root_key
|
|
63
62
|
builder.line "validate_root_key!(root_key)"
|
|
64
63
|
end
|
|
65
|
-
with_writer(
|
|
64
|
+
with_writer(builder) { emit_serialize_many_body(config, builder) }
|
|
66
65
|
end
|
|
67
66
|
builder.line "end"
|
|
68
67
|
end
|
|
@@ -103,7 +102,7 @@ module Panko::CodeGen
|
|
|
103
102
|
def attribute(attribute, read_expr, config, index, builder, cast: true)
|
|
104
103
|
builder.line "unless filters.drops?(#{index})"
|
|
105
104
|
builder.indent do
|
|
106
|
-
builder.line
|
|
105
|
+
builder.line "writer.push_value(#{read_expr}, #{GeneratedNames.string_literal(attribute.name)})"
|
|
107
106
|
end
|
|
108
107
|
builder.line "end"
|
|
109
108
|
end
|
|
@@ -119,33 +118,34 @@ module Panko::CodeGen
|
|
|
119
118
|
end
|
|
120
119
|
|
|
121
120
|
# +equal?+, not +==+, so an object that overrides +==+ cannot match +SKIP+.
|
|
122
|
-
def method_attribute(method_attribute, config, index, builder)
|
|
121
|
+
def method_attribute(method_attribute, position, config, index, builder)
|
|
123
122
|
builder.line "unless filters.drops?(#{index})"
|
|
124
123
|
builder.indent do
|
|
125
|
-
builder.line "value = #{method_attribute_call_expression(method_attribute)}"
|
|
124
|
+
builder.line "value = #{method_attribute_call_expression(method_attribute, position)}"
|
|
126
125
|
builder.line "unless value.equal?(Panko::CodeGen::SKIP)"
|
|
127
126
|
builder.indent do
|
|
128
|
-
builder.line
|
|
127
|
+
builder.line "writer.push_value(value, #{GeneratedNames.string_literal(method_attribute.name)})"
|
|
129
128
|
end
|
|
130
129
|
builder.line "end"
|
|
131
130
|
end
|
|
132
131
|
builder.line "end"
|
|
133
132
|
end
|
|
134
133
|
|
|
135
|
-
def association(association, source_read_expr, config, index, builder)
|
|
134
|
+
def association(association, position, source_read_expr, config, index, builder)
|
|
135
|
+
key_lit = GeneratedNames.string_literal(association.name)
|
|
136
136
|
builder.line "unless filters.drops?(#{index})"
|
|
137
137
|
builder.indent do
|
|
138
|
-
with_if_guard(association, builder) do
|
|
138
|
+
with_if_guard(association, position, builder) do
|
|
139
139
|
case association.kind
|
|
140
140
|
when :has_one
|
|
141
141
|
builder.line "value = #{source_read_expr}"
|
|
142
142
|
if config.null_for_missing_has_one
|
|
143
|
-
has_one_default(association, builder)
|
|
143
|
+
has_one_default(association, position, key_lit, builder)
|
|
144
144
|
else
|
|
145
|
-
has_one_omit(association, builder)
|
|
145
|
+
has_one_omit(association, position, key_lit, builder)
|
|
146
146
|
end
|
|
147
147
|
when :has_many
|
|
148
|
-
has_many(association, source_read_expr, builder)
|
|
148
|
+
has_many(association, position, source_read_expr, key_lit, builder)
|
|
149
149
|
end
|
|
150
150
|
end
|
|
151
151
|
end
|
|
@@ -155,21 +155,16 @@ module Panko::CodeGen
|
|
|
155
155
|
private
|
|
156
156
|
|
|
157
157
|
# +result+ starts nil so a body that raises hands checkin nil, and the writer is dropped.
|
|
158
|
-
def with_writer(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
builder.
|
|
166
|
-
builder.line "POOL.checkin(writer, result)"
|
|
167
|
-
end
|
|
168
|
-
builder.line "end"
|
|
169
|
-
else
|
|
170
|
-
builder.line "writer = Oj::StringWriter.new(mode: :rails)"
|
|
171
|
-
yield
|
|
158
|
+
def with_writer(builder, &block)
|
|
159
|
+
builder.line "writer = POOL.checkout"
|
|
160
|
+
builder.line "result = nil"
|
|
161
|
+
builder.line "begin"
|
|
162
|
+
builder.indent(&block)
|
|
163
|
+
builder.line "ensure"
|
|
164
|
+
builder.indent do
|
|
165
|
+
builder.line "POOL.checkin(writer, result)"
|
|
172
166
|
end
|
|
167
|
+
builder.line "end"
|
|
173
168
|
end
|
|
174
169
|
|
|
175
170
|
def emit_serialize_one_body(config, builder)
|
|
@@ -205,36 +200,36 @@ module Panko::CodeGen
|
|
|
205
200
|
|
|
206
201
|
# A non-nil +has_one+ pushes its key separately instead of +push_object("name")+: the
|
|
207
202
|
# child's +_write_one+ opens its own +push_object+ frame.
|
|
208
|
-
def has_one_default(association, builder)
|
|
203
|
+
def has_one_default(association, position, key_lit, builder)
|
|
209
204
|
builder.line "if value.nil?"
|
|
210
|
-
builder.indent { builder.line
|
|
205
|
+
builder.indent { builder.line "writer.push_value(nil, #{key_lit})" }
|
|
211
206
|
builder.line "else"
|
|
212
207
|
builder.indent do
|
|
213
|
-
builder.line
|
|
214
|
-
child_write(association, "value", builder) do |ivar|
|
|
208
|
+
builder.line "writer.push_key(#{key_lit})"
|
|
209
|
+
child_write(association, position, "value", builder) do |ivar|
|
|
215
210
|
"#{ivar}.#{GeneratedNames.write_one}(value, writer, context, scope, #{child_filter_expr(association)})"
|
|
216
211
|
end
|
|
217
212
|
end
|
|
218
213
|
builder.line "end"
|
|
219
214
|
end
|
|
220
215
|
|
|
221
|
-
def has_one_omit(association, builder)
|
|
216
|
+
def has_one_omit(association, position, key_lit, builder)
|
|
222
217
|
builder.line "unless value.nil?"
|
|
223
218
|
builder.indent do
|
|
224
|
-
builder.line
|
|
225
|
-
child_write(association, "value", builder) do |ivar|
|
|
219
|
+
builder.line "writer.push_key(#{key_lit})"
|
|
220
|
+
child_write(association, position, "value", builder) do |ivar|
|
|
226
221
|
"#{ivar}.#{GeneratedNames.write_one}(value, writer, context, scope, #{child_filter_expr(association)})"
|
|
227
222
|
end
|
|
228
223
|
end
|
|
229
224
|
builder.line "end"
|
|
230
225
|
end
|
|
231
226
|
|
|
232
|
-
def has_many(association, source_read_expr, builder)
|
|
227
|
+
def has_many(association, position, source_read_expr, key_lit, builder)
|
|
233
228
|
builder.line "child_filter = #{child_filter_expr(association)}"
|
|
234
|
-
builder.line
|
|
229
|
+
builder.line "writer.push_array(#{key_lit})"
|
|
235
230
|
builder.line "#{source_read_expr}.each do |element|"
|
|
236
231
|
builder.indent do
|
|
237
|
-
child_write(association, "element", builder) do |ivar|
|
|
232
|
+
child_write(association, position, "element", builder) do |ivar|
|
|
238
233
|
"#{ivar}.#{GeneratedNames.write_one}(element, writer, context, scope, child_filter)"
|
|
239
234
|
end
|
|
240
235
|
end
|
|
@@ -246,11 +241,11 @@ module Panko::CodeGen
|
|
|
246
241
|
# (non-String, empty, malformed) falls back to the typed read.
|
|
247
242
|
def json_column_attribute(attribute, config, index, builder)
|
|
248
243
|
if config.json_column_emit == :html_safe
|
|
249
|
-
attribute(attribute,
|
|
244
|
+
attribute(attribute, "record._read_attribute(#{GeneratedNames.string_literal(attribute.source)})", config, index, builder)
|
|
250
245
|
return
|
|
251
246
|
end
|
|
252
|
-
source_lit =
|
|
253
|
-
key_lit =
|
|
247
|
+
source_lit = GeneratedNames.string_literal(attribute.source)
|
|
248
|
+
key_lit = GeneratedNames.string_literal(attribute.name)
|
|
254
249
|
builder.line "unless filters.drops?(#{index})"
|
|
255
250
|
builder.indent do
|
|
256
251
|
builder.line %(raw = record.read_attribute_before_type_cast(#{source_lit}))
|
|
@@ -277,11 +272,12 @@ module Panko::CodeGen
|
|
|
277
272
|
end
|
|
278
273
|
|
|
279
274
|
def datetime_column_attribute(attribute, index, builder)
|
|
280
|
-
source_lit =
|
|
275
|
+
source_lit = GeneratedNames.string_literal(attribute.source)
|
|
281
276
|
builder.line "unless filters.drops?(#{index})"
|
|
282
277
|
builder.indent do
|
|
283
278
|
builder.line "value = Panko::CodeGen::DateTimeFormat.format_raw(record.read_attribute_before_type_cast(#{source_lit}))"
|
|
284
|
-
builder.line
|
|
279
|
+
builder.line "writer.push_value(value || record._read_attribute(#{source_lit}), " \
|
|
280
|
+
"#{GeneratedNames.string_literal(attribute.name)})"
|
|
285
281
|
end
|
|
286
282
|
builder.line "end"
|
|
287
283
|
end
|
|
@@ -27,7 +27,7 @@ module Panko::CodeGen
|
|
|
27
27
|
builder.indent do
|
|
28
28
|
FieldWalk.emit_fields(
|
|
29
29
|
descriptor, config, field_index, builder, sink,
|
|
30
|
-
read_expr: ->(source) { "record
|
|
30
|
+
read_expr: ->(source) { GeneratedNames.method_call("record", source) }
|
|
31
31
|
)
|
|
32
32
|
end
|
|
33
33
|
builder.line "end"
|
|
@@ -60,7 +60,7 @@ module Panko::CodeGen
|
|
|
60
60
|
builder.indent do
|
|
61
61
|
FieldWalk.emit_fields(
|
|
62
62
|
descriptor, config, field_index, builder, sink,
|
|
63
|
-
read_expr: ->(source) { "record
|
|
63
|
+
read_expr: ->(source) { GeneratedNames.method_call("record", source) }
|
|
64
64
|
)
|
|
65
65
|
end
|
|
66
66
|
builder.line "end"
|
|
@@ -82,8 +82,8 @@ module Panko::CodeGen
|
|
|
82
82
|
|
|
83
83
|
def self.hash_read_expr(source_name, config)
|
|
84
84
|
case config.hash_record_key_type
|
|
85
|
-
when :symbol then "record[
|
|
86
|
-
else
|
|
85
|
+
when :symbol then "record[#{GeneratedNames.symbol_literal(source_name)}]"
|
|
86
|
+
else "record[#{GeneratedNames.string_literal(source_name)}]"
|
|
87
87
|
end
|
|
88
88
|
end
|
|
89
89
|
end
|
|
@@ -13,7 +13,7 @@ module Panko::CodeGen
|
|
|
13
13
|
Generic.emit_parent_class_ivar_writes(descriptor, builder)
|
|
14
14
|
FieldWalk.emit_fields(
|
|
15
15
|
descriptor, config, field_index, builder, sink,
|
|
16
|
-
read_expr: ->(source) { "record
|
|
16
|
+
read_expr: ->(source) { GeneratedNames.method_call("record", source) },
|
|
17
17
|
attribute_emit: ->(attribute, index) { sink.specialized_attribute(attribute, ar_model, config, index, builder) }
|
|
18
18
|
)
|
|
19
19
|
end
|
|
@@ -45,21 +45,23 @@ module Panko::CodeGen
|
|
|
45
45
|
# SELECT alias named like a method (+thing_ids+ next to +has_many :things+) returns the selected value.
|
|
46
46
|
def self.attribute_read_expr(attribute, ar_model)
|
|
47
47
|
source = attribute.source
|
|
48
|
-
|
|
49
|
-
return
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
method_read = GeneratedNames.method_call("record", source)
|
|
49
|
+
return method_read if ar_model.nil?
|
|
50
|
+
source_lit = GeneratedNames.string_literal(source)
|
|
51
|
+
return "record._read_attribute(#{source_lit})" if ActiveRecord::AccessClassifier.classify(ar_model, source) == :column
|
|
52
|
+
return method_read if declared_attribute?(ar_model, source)
|
|
53
|
+
"(record._has_attribute?(#{source_lit}) ? record._read_attribute(#{source_lit}) : #{method_read})"
|
|
52
54
|
end
|
|
53
55
|
|
|
54
56
|
def self.declared_attribute?(ar_model, source)
|
|
55
57
|
ar_model.attribute_types.key?(source.to_s) || ar_model.attribute_aliases.key?(source.to_s)
|
|
56
58
|
end
|
|
57
59
|
|
|
58
|
-
# Called here too so the Generator works without the validators
|
|
60
|
+
# Called here too so the Generator works without the validators.
|
|
59
61
|
def self.ar_model(descriptor)
|
|
60
62
|
model = descriptor.model
|
|
61
63
|
return nil unless model.respond_to?(:columns_hash) && model.respond_to?(:attribute_methods_generated?)
|
|
62
|
-
|
|
64
|
+
model.define_attribute_methods
|
|
63
65
|
model
|
|
64
66
|
end
|
|
65
67
|
|
|
@@ -25,11 +25,11 @@ module Panko::CodeGen
|
|
|
25
25
|
end
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
-
def child_ivars(assoc)
|
|
28
|
+
def child_ivars(assoc, position)
|
|
29
29
|
variants = assoc.variants.each_with_index.map do |variant, index|
|
|
30
|
-
[GeneratedNames.variant_serializer_ivar(
|
|
30
|
+
[GeneratedNames.variant_serializer_ivar(position, index), variant]
|
|
31
31
|
end
|
|
32
|
-
[[GeneratedNames.serializer_ivar(
|
|
32
|
+
[[GeneratedNames.serializer_ivar(position), assoc.descriptor], *variants]
|
|
33
33
|
end
|
|
34
34
|
|
|
35
35
|
# Every class defines +_release+, even an empty one: the serializers call it
|
|
@@ -42,8 +42,8 @@ module Panko::CodeGen
|
|
|
42
42
|
builder.line "@context = nil"
|
|
43
43
|
builder.line "@scope = nil"
|
|
44
44
|
end
|
|
45
|
-
descriptor.associations.
|
|
46
|
-
child_ivars(assoc).each do |ivar, child|
|
|
45
|
+
descriptor.associations.each_with_index do |assoc, position|
|
|
46
|
+
child_ivars(assoc, position).each do |ivar, child|
|
|
47
47
|
next if child.equal?(descriptor)
|
|
48
48
|
# Ends the chain without per-call visited state; such serializers keep their last record until the next call.
|
|
49
49
|
next if cyclic_ids[descriptor.__id__] && cyclic_ids[child.__id__]
|
|
@@ -6,14 +6,14 @@ module Panko::CodeGen
|
|
|
6
6
|
# The child's +FIELD_INDEX+ is read through its class name when the method runs, so
|
|
7
7
|
# self-recursive Descriptors resolve it.
|
|
8
8
|
def child_filter_expr(association)
|
|
9
|
-
"filters.child(
|
|
9
|
+
"filters.child(#{GeneratedNames.symbol_literal(GeneratedNames.filter_key(association))}, " \
|
|
10
10
|
"#{GeneratedNames.class_name(association.descriptor, suffix)}::#{GeneratedNames.field_index_const})"
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
# +case+/+when+, not an +if+/+elsif+ chain of +instance_of?+: under YJIT the chain gets
|
|
14
14
|
# slower once the call site has warmed on one class. HashSink uses the +case+ as an expression.
|
|
15
|
-
def child_write(association, record_expr, builder, &call_for)
|
|
16
|
-
base_call = call_for.call(GeneratedNames.serializer_ivar(
|
|
15
|
+
def child_write(association, position, record_expr, builder, &call_for)
|
|
16
|
+
base_call = call_for.call(GeneratedNames.serializer_ivar(position))
|
|
17
17
|
if association.variants.empty?
|
|
18
18
|
builder.line base_call
|
|
19
19
|
return
|
|
@@ -24,7 +24,7 @@ module Panko::CodeGen
|
|
|
24
24
|
builder.line "when #{model}"
|
|
25
25
|
builder.indent do
|
|
26
26
|
builder.line "if #{record_expr}.instance_of?(#{model})"
|
|
27
|
-
builder.indent { builder.line call_for.call(GeneratedNames.variant_serializer_ivar(
|
|
27
|
+
builder.indent { builder.line call_for.call(GeneratedNames.variant_serializer_ivar(position, index)) }
|
|
28
28
|
builder.line "else"
|
|
29
29
|
builder.indent { builder.line base_call }
|
|
30
30
|
builder.line "end"
|
|
@@ -42,9 +42,9 @@ module Panko::CodeGen
|
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
# The guard wraps the Source read too, so a falsy +if:+ skips the read.
|
|
45
|
-
def with_if_guard(association, builder)
|
|
45
|
+
def with_if_guard(association, position, builder)
|
|
46
46
|
if association.if
|
|
47
|
-
builder.line "if #{if_guard_call_expression(GeneratedNames.if_guard_ivar(
|
|
47
|
+
builder.line "if #{if_guard_call_expression(GeneratedNames.if_guard_ivar(position), association.if.arity)}"
|
|
48
48
|
builder.indent { yield }
|
|
49
49
|
builder.line "end"
|
|
50
50
|
else
|
|
@@ -64,10 +64,10 @@ module Panko::CodeGen
|
|
|
64
64
|
|
|
65
65
|
# Symbol bodies need the explicit +self.+: a bare name such as +record+ or +value+ would
|
|
66
66
|
# resolve to a local of the generated method.
|
|
67
|
-
def method_attribute_call_expression(method_attribute)
|
|
67
|
+
def method_attribute_call_expression(method_attribute, position)
|
|
68
68
|
body = method_attribute.body
|
|
69
|
-
return "self
|
|
70
|
-
ivar = GeneratedNames.callable_ivar(
|
|
69
|
+
return GeneratedNames.method_call("self", body) if body.is_a?(Symbol)
|
|
70
|
+
ivar = GeneratedNames.callable_ivar(position)
|
|
71
71
|
case body.arity
|
|
72
72
|
when 0 then "#{ivar}.call"
|
|
73
73
|
when 1 then "#{ivar}.call(record)"
|
|
@@ -23,7 +23,7 @@ module Panko::CodeGen
|
|
|
23
23
|
def classify_attributes!(descriptor)
|
|
24
24
|
model = descriptor.model
|
|
25
25
|
return unless ar_class?(model)
|
|
26
|
-
|
|
26
|
+
model.define_attribute_methods
|
|
27
27
|
descriptor.attributes.each do |attribute|
|
|
28
28
|
classify_or_raise!(model, attribute, descriptor.name)
|
|
29
29
|
end
|
|
@@ -2,16 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
module Panko::CodeGen
|
|
4
4
|
module Validators
|
|
5
|
-
|
|
5
|
+
module Validator
|
|
6
6
|
# Order matters: an arity error is reported before a source or name error.
|
|
7
|
-
|
|
7
|
+
RULES = [CallableArity, SourceResolution, NameUniqueness].freeze
|
|
8
8
|
|
|
9
|
-
def
|
|
10
|
-
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
def validate(descriptor, output:, config:)
|
|
14
|
-
@rules.each { |rule| rule.validate(descriptor, output: output, config: config) }
|
|
9
|
+
def self.validate(descriptor, output:, config:)
|
|
10
|
+
RULES.each { |rule| rule.validate(descriptor, output: output, config: config) }
|
|
15
11
|
nil
|
|
16
12
|
end
|
|
17
13
|
end
|
data/lib/panko/code_gen.rb
CHANGED
|
@@ -16,7 +16,6 @@ require_relative "code_gen/code_builder"
|
|
|
16
16
|
require_relative "code_gen/filter"
|
|
17
17
|
require_relative "code_gen/datetime_format"
|
|
18
18
|
require_relative "code_gen/active_record/access_classifier"
|
|
19
|
-
require_relative "code_gen/active_record/define_attribute_methods"
|
|
20
19
|
require_relative "code_gen/validators/callable_arity"
|
|
21
20
|
require_relative "code_gen/validators/source_resolution"
|
|
22
21
|
require_relative "code_gen/validators/name_uniqueness"
|
|
@@ -36,7 +35,6 @@ require_relative "code_gen/generators/banner"
|
|
|
36
35
|
require_relative "code_gen/generators/class_emitter"
|
|
37
36
|
require_relative "code_gen/generators/fanout"
|
|
38
37
|
require_relative "code_gen/generator"
|
|
39
|
-
require_relative "code_gen/compile_cache"
|
|
40
38
|
require_relative "code_gen/compiler"
|
|
41
39
|
require_relative "code_gen/dump"
|
|
42
40
|
require_relative "code_gen/writers_pool"
|
data/lib/panko/serializer.rb
CHANGED
data/lib/panko/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: panko_serializer
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.0.
|
|
4
|
+
version: 1.0.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yosi Attias
|
|
@@ -68,9 +68,7 @@ files:
|
|
|
68
68
|
- lib/panko/array_serializer.rb
|
|
69
69
|
- lib/panko/code_gen.rb
|
|
70
70
|
- lib/panko/code_gen/active_record/access_classifier.rb
|
|
71
|
-
- lib/panko/code_gen/active_record/define_attribute_methods.rb
|
|
72
71
|
- lib/panko/code_gen/code_builder.rb
|
|
73
|
-
- lib/panko/code_gen/compile_cache.rb
|
|
74
72
|
- lib/panko/code_gen/compiler.rb
|
|
75
73
|
- lib/panko/code_gen/config.rb
|
|
76
74
|
- lib/panko/code_gen/datetime_format.rb
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Panko::CodeGen
|
|
4
|
-
# Descriptor -> Generated Class, one entry per unique Descriptor in a tree.
|
|
5
|
-
class CompileCache
|
|
6
|
-
# Keyed by +__id__+, so equal but distinct Descriptors get separate entries.
|
|
7
|
-
def initialize
|
|
8
|
-
@store = {}
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def get(descriptor)
|
|
12
|
-
@store[descriptor.__id__]
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def set(descriptor, generated_class)
|
|
16
|
-
@store[descriptor.__id__] = generated_class
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# The block must call +#set+ before it descends into children, so a recursive
|
|
20
|
-
# call on the same Descriptor hits the entry and the cycle ends.
|
|
21
|
-
def lookup_or_compile(descriptor)
|
|
22
|
-
cached = @store[descriptor.__id__]
|
|
23
|
-
return cached if cached
|
|
24
|
-
yield
|
|
25
|
-
@store[descriptor.__id__]
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
end
|