diff --git a/gems/aws-sdk-core/CHANGELOG.md b/gems/aws-sdk-core/CHANGELOG.md index f815aff5271..39e30cc8ce2 100644 --- a/gems/aws-sdk-core/CHANGELOG.md +++ b/gems/aws-sdk-core/CHANGELOG.md @@ -1,6 +1,8 @@ Unreleased Changes ------------------ +* Feature - Seahorse::Client::Configuration no longer generate real Ruby Struct at runtime. + 3.265.0 (2026-11-06) ------------------ diff --git a/gems/aws-sdk-core/lib/seahorse/client/configuration.rb b/gems/aws-sdk-core/lib/seahorse/client/configuration.rb index 683a053d26d..7b22e250b57 100644 --- a/gems/aws-sdk-core/lib/seahorse/client/configuration.rb +++ b/gems/aws-sdk-core/lib/seahorse/client/configuration.rb @@ -73,6 +73,58 @@ def call(*args) end end + # @api private + class DynamicStruct + attr_reader :members + + def initialize(members) + @members = members.map(&:to_sym).to_set + @attributes = {} + end + + def [](name) + name = name.to_sym + unless @members.include?(name) + raise NameError, "No member #{name} in Struct" + end + + @attributes[name] + end + + def []=(name, value) + name = name.to_sym + unless @members.include?(name) + raise NameError, "No member #{name} in Struct" + end + + @attributes[name] = value + end + + private + + alias_method :write_attribute, :[]= + alias_method :read_attribute, :[] + + def method_missing(name, *args, **kwargs, &block) + if name.end_with?('=') + attr_name = name[0..-2].to_sym + if @members.include?(attr_name) + write_attribute(attr_name, *args, **kwargs, &block) + else + super + end + elsif @members.include?(name) + read_attribute(name, *args, **kwargs, &block) + else + super + end + end + + def respond_to_missing?(name, include_private = false) + @members.include?(name) || (name.end_with?('=') && @members.include?(name[0..-2].to_sym)) || super + end + end + # @api private def initialize @defaults = Hash.new { |h,k| h[k] = Defaults.new } @@ -156,7 +208,7 @@ def build!(options = {}) private def empty_struct - Struct.new(*@defaults.keys.sort).new + DynamicStruct.new(@defaults.keys.sort) end def apply_options(struct, options) diff --git a/gems/aws-sdk-core/spec/seahorse/client/base_spec.rb b/gems/aws-sdk-core/spec/seahorse/client/base_spec.rb index f500163c256..3bab868e6c2 100644 --- a/gems/aws-sdk-core/spec/seahorse/client/base_spec.rb +++ b/gems/aws-sdk-core/spec/seahorse/client/base_spec.rb @@ -24,7 +24,7 @@ module Client describe '#config' do it 'returns a Configuration struct' do - expect(client.config).to be_kind_of(Struct) + expect(client.config).to be_kind_of(Configuration::DynamicStruct) end it 'contains the client api' do diff --git a/gems/aws-sdk-core/spec/seahorse/client/configuration_spec.rb b/gems/aws-sdk-core/spec/seahorse/client/configuration_spec.rb index 37842a70436..ab4cae5f999 100644 --- a/gems/aws-sdk-core/spec/seahorse/client/configuration_spec.rb +++ b/gems/aws-sdk-core/spec/seahorse/client/configuration_spec.rb @@ -66,7 +66,7 @@ module Client it 'returns self so it can be chained' do c = config.add_option(:name).add_option(:color) expect(c).to be(config) - expect(c.build!.members).to eq([:color, :name]) + expect(c.build!.members).to eq(Set[:color, :name]) end end @@ -74,7 +74,7 @@ module Client describe '#build!' do it 'returns a Struct' do - expect(config.add_option(:opt).build!).to be_kind_of(Struct) + expect(config.add_option(:opt).build!).to be_kind_of(Configuration::DynamicStruct) end it 'accepts a hash of options' do