/
autoboxing_fields.rb
109 lines (94 loc) · 3.41 KB
/
autoboxing_fields.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
require 'delegate'
require 'google/ads/google_ads/autoboxing_mappings'
module Google
module Ads
module GoogleAds
module AutoboxingFields
def self.patch_class(klass)
return if klass.instance_variable_get(:@_patched_for_autoboxing_fields)
klass.instance_variable_set(:@_patched_for_autoboxing_fields, true)
klass.instance_eval do
fields = []
repeated_fields = []
descriptor.each do |field|
if field.type == :message && AutoboxingFields.is_value_field?(field.subtype.msgclass)
repeated = field.label == :repeated
if repeated
repeated_fields << field
else
fields << field
end
AutoboxingFields.patch_field_for_autoboxing(field, repeated, self)
end
end
AutoboxingFields.patch_constructor_for_autoboxing(fields, repeated_fields, self)
end
end
def self.is_value_field?(class_name)
AutoboxingMappings.has_type?(class_name)
end
def self.patch_constructor_for_autoboxing(fields, repeated_fields, klass_to_patch)
orig_initialize = klass_to_patch.instance_method(:initialize)
klass_to_patch.instance_eval do
define_method(:initialize) do |**kwargs|
new_kwargs = kwargs.dup
fields.select { |x| new_kwargs.include?(x.name.to_sym) }.each do |field|
value = new_kwargs.fetch(field.name.to_sym)
new_kwargs[field.name.to_sym] = AutoboxingMappings.wrapped_mapping(field.subtype.msgclass).call(value)
end
repeated_fields.select { |x| new_kwargs.include?(x.name.to_sym) }.each do |field|
value = new_kwargs.fetch(field.name.to_sym)
mapping = AutoboxingMappings.wrapped_mapping(field.subtype.msgclass)
new_kwargs[field.name.to_sym] = value.map { |x| mapping.call(x) }
end
orig_initialize.bind(self).call(**new_kwargs)
end
end
end
def self.patch_field_for_autoboxing(field, repeated, klass_to_patch)
name = field.name
mapping = AutoboxingMappings.wrapped_mapping(field.subtype.msgclass)
if repeated
klass_to_patch.instance_eval do
define_method(name.to_sym) do
@patched_repeated_fields ||= {}
@patched_repeated_fields[name] ||= RepeatedFieldProxy.new(
send(:method_missing, name.to_sym),
mapping,
)
@patched_repeated_fields[name]
end
end
else
klass_to_patch.instance_eval do
define_method("#{name}=".to_sym) do |value|
send(:method_missing, :"#{name}=", mapping.call(value))
end
end
end
end
end
end
end
end
class RepeatedFieldProxy < SimpleDelegator
def initialize(collection, mapping)
super(collection)
@mapping = mapping
end
def <<(value)
super(@mapping.call(value))
end
end
module Google::Protobuf
module MessageExts
module ClassMethods
def new(*args, &blk)
if self.name.start_with?("Google::Ads::GoogleAds")
Google::Ads::GoogleAds::AutoboxingFields.patch_class(self)
end
super
end
end
end
end