Skip to content

Commit 0b9a79e

Browse files
committed
Fix bug to overwrite all subsections definition by subclass
* overwriting config_section options like required/multi/alias breaks behavior of `super` * so subclass must not overwrite these options * subsection definitions of subsections (Hash) is simply merged * it is bug, because same section definition like <a> is overwritten by subclass * under this bug, subclass MUST repeat superclass's definition, and add original definition * but this is impossible in the real world * subsection definitions should be merged correctly
1 parent 223fd0e commit 0b9a79e

File tree

2 files changed

+85
-8
lines changed

2 files changed

+85
-8
lines changed

lib/fluent/config/configure_proxy.rb

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,16 +60,29 @@ def multi?
6060

6161
def merge(other) # self is base class, other is subclass
6262
options = {
63-
param_name: other.param_name,
64-
required: (other.required.nil? ? self.required : other.required),
65-
multi: (other.multi.nil? ? self.multi : other.multi)
63+
param_name: other.param_name, # param_name is used not to ovewrite plugin's instance varible, so this should be able to be overwritten
64+
required: (@required.nil? ? other.required : self.required), # subclass cannot overwrite base class's definition
65+
multi: (@multi.nil? ? other.multi : self.multi),
66+
alias: (@alias.nil? ? other.alias : self.alias),
6667
}
6768
merged = self.class.new(other.name, options)
6869

6970
merged.argument = other.argument || self.argument
70-
merged.params = self.params.merge(other.params)
71+
merged.params = other.params.merge(self.params)
7172
merged.defaults = self.defaults.merge(other.defaults)
72-
merged.sections = self.sections.merge(other.sections)
73+
merged.sections = {}
74+
(self.sections.keys + other.sections.keys).uniq.each do |section_key|
75+
self_section = self.sections[section_key]
76+
other_section = other.sections[section_key]
77+
merged_section = if self_section && other_section
78+
self_section.merge(other_section)
79+
elsif self_section || other_section
80+
self_section || other_section
81+
else
82+
raise "BUG: both of self and other section are nil"
83+
end
84+
merged.sections[section_key] = merged_section
85+
end
7386

7487
merged
7588
end
@@ -148,11 +161,16 @@ def config_section(name, *args, &block)
148161
raise ArgumentError, "#{self.name}: unknown config_section arguments: #{args.inspect}"
149162
end
150163

151-
sub_proxy = ConfigureProxy.new(name, *args)
152-
sub_proxy.instance_exec(&block)
164+
# if @sections[name]
165+
# @sections[name].instance_exec(&block)
166+
# else
167+
sub_proxy = ConfigureProxy.new(name, *args)
168+
sub_proxy.instance_exec(&block)
169+
170+
@sections[name] = sub_proxy
171+
# end
153172

154173
@params.delete(name)
155-
@sections[name] = sub_proxy
156174

157175
name
158176
end

test/config/test_configurable.rb

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,12 @@ def get_all
108108
[@name, @detail]
109109
end
110110
end
111+
112+
class Example2 < Example1
113+
config_section :detail, required: false, multi: false, alias: "information2" do
114+
config_param :phone_no, :string
115+
end
116+
end
111117
end
112118

113119
module Fluent::Config
@@ -493,6 +499,59 @@ def e(name, arg = '', attrs = {}, elements = [])
493499
assert_false(ex5.boolvalue)
494500
end
495501
end
502+
503+
sub_test_case '.config_section' do
504+
def e(name, arg = '', attrs = {}, elements = [])
505+
attrs_str_keys = {}
506+
attrs.each{|key, value| attrs_str_keys[key.to_s] = value }
507+
Fluent::Config::Element.new(name, arg, attrs_str_keys, elements)
508+
end
509+
510+
test 'adds only config_param definitions into configuration without overwriting existing configuration elements' do
511+
# conf0 = e('ROOT', '', {}, [])
512+
513+
conf1 = e('ROOT', '', {
514+
'name' => 'tagomoris',
515+
'bool' => true,
516+
},
517+
)
518+
# <detail> section is required by Example1 config_section spec
519+
assert_raise(Fluent::ConfigError.new("'<detail>' sections are required")) { ConfigurableSpec::Example1.new.configure(conf1) }
520+
assert_raise(Fluent::ConfigError.new("'<detail>' sections are required")) { ConfigurableSpec::Example2.new.configure(conf1) }
521+
522+
conf2 = e('ROOT', '', {
523+
'name' => 'tagomoris',
524+
'bool' => true,
525+
},
526+
[e('detail', '', { 'phone_no' => "+81-00-0000-0000" }, [])],
527+
)
528+
# <detail> address </detail> is required by Example1 config_param spec
529+
assert_raise(Fluent::ConfigError.new("'address' parameter is required, in section detail")) { ConfigurableSpec::Example2.new.configure(conf2) }
530+
531+
conf3 = e('ROOT', '', {
532+
'name' => 'tagomoris',
533+
'bool' => true,
534+
},
535+
[e('detail', '', { 'address' => "Chiyoda Tokyo Japan" }, [])],
536+
)
537+
# <detail> phone_no </detail is required by Example2 config_param spec
538+
assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf3) }
539+
assert_raise(Fluent::ConfigError.new("'phone_no' parameter is required, in section detail")) { ConfigurableSpec::Example2.new.configure(conf3) }
540+
541+
conf4 = e('ROOT', '', {
542+
'name' => 'tagomoris',
543+
'bool' => true,
544+
},
545+
[e('detail', '', { 'address' => "Chiyoda Tokyo Japan", 'phone_no' => '+81-00-0000-0000' }, [])],
546+
)
547+
assert_nothing_raised { ConfigurableSpec::Example1.new.configure(conf4) } # phone_no is not used
548+
assert_nothing_raised { ConfigurableSpec::Example2.new.configure(conf4) }
549+
550+
ex2 = ConfigurableSpec::Example2.new.configure(conf4)
551+
assert_equal "Chiyoda Tokyo Japan", ex2.detail.address
552+
assert_equal "+81-00-0000-0000", ex2.detail.phone_no
553+
end
554+
end
496555
end
497556

498557
sub_test_case 'class defined with config_param/config_section having :alias' do

0 commit comments

Comments
 (0)