Skip to content

Reimplement CDO Configuration Object without OpenStruct - #51493

Merged
Hamms merged 7 commits into
stagingfrom
reimplement-cdo-config-without-openstruct
May 8, 2023
Merged

Reimplement CDO Configuration Object without OpenStruct#51493
Hamms merged 7 commits into
stagingfrom
reimplement-cdo-config-without-openstruct

Conversation

@Hamms

@Hamms Hamms commented Apr 24, 2023

Copy link
Copy Markdown
Contributor

OpenStruct has undergone some changes in Ruby 3.0 which break a few things with our CDO configuration option management object which inherits from it. We could probably patch up those incompatibilities, but OpenStruct is generally not recommended for use, so instead we just reimplement the specific functionality that we want on the class directly, and stop inheriting from it.

The specific OpenStruct functionality that we lose here is mostly a lot of logic to support redefining values that have already been defined (something we explicitly want to avoid anyway), as well as workarounds to prevent accidental overloading of various internal methods (something our specific use case of the structure renders irrelevant), and various miscellaneous YAGNI helpers that we aren't currently taking advantage of. I also chose not to carry over the manual protected accessor for the internal table object in favor of explicit @table references, and to define our own freeze_config method for applying our custom soft-freeze logic rather than overriding the builtin method just so we can prevent the default functionality.

Links

Testing story

Tweaked existing unit tests to reflect new slightly modified edge cases and method names. Otherwise relying on existing test coverage to verify that this does not result in any significant change in functionality.

Also tested on an adhoc

@Hamms Hamms added the Ruby Update Everything related to work to update the version of Ruby our codebase runs on label Apr 27, 2023
@Hamms
Hamms marked this pull request as ready for review April 27, 2023 23:31
@Hamms
Hamms requested a review from a team April 27, 2023 23:31
Comment thread lib/cdo/config.rb
# Inspired by OpenStruct; if unfrozen, allow assigning new configuration
# values with `=` and default undefined values to nil
# See https://github.com/ruby/ostruct/blob/e61b4464a033c38a65657eaf0467d12e2c7b9ec1/lib/ostruct.rb#L207-L229
def method_missing(mid, *args)

@sureshc sureshc Apr 28, 2023

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use ActiveSupport::OrderedOptions? Looks like it also overrides method_missing to provide the functionality we need, and it sub-classes Hash. https://www.akshaykhot.com/accessing-hash-values-like-methods/

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In theory I think it'd probably be possible, but would require much more extensive refactoring than this approach. In particular, our dynamic configuration rerendering logic as it's currently implemented requires us to be able to cache and restore the underlying data store between each interation, and I have no idea how we'd go about doing that with something that subclasses rather than wraps its data store. I'd also be worried about the couple of instances of full-table iteration that we currently do in the secrets config.

I'd prefer to stick with something that maintains existing functionality for now, rather than embark on a full rewrite of this class

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if a long-term port to OrderedOptions makes sense, I think its smart to go step-wise, I like the approach of extracting and reworking some bits of OpenStruct to make this a relatively minimal change

@Hamms
Hamms requested review from a team and sureshc May 2, 2023 19:32

@snickell snickell left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good to my relatively out-of-shape ruby eyes. Thanks for tackling moving away from a not-recommended structure.

Comment thread lib/cdo/config.rb
# Inspired by OpenStruct; if unfrozen, allow assigning new configuration
# values with `=` and default undefined values to nil
# See https://github.com/ruby/ostruct/blob/e61b4464a033c38a65657eaf0467d12e2c7b9ec1/lib/ostruct.rb#L207-L229
def method_missing(mid, *args)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if a long-term port to OrderedOptions makes sense, I think its smart to go step-wise, I like the approach of extracting and reworking some bits of OpenStruct to make this a relatively minimal change

Comment thread lib/cdo/secrets_config.rb
end

def freeze
def freeze_config

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My biggest question is "why create a new method rather than overloading freeze"? Config::freeze_config feels slightly redundant. But really not a big deal at all.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly because we don't want to actually freeze the Config object itself or to in any way take advantage of existing freeze functionality; we want to implement something analogous but distinct to native freeze. If the way we implemented this was to instead call @table.freeze and rely on the table being immutable for our table mutation methods to fail, then it'd make sense to me to handle this by overloading freeze on the Config object, but because we do something much more bespoke it feels right that it's all happening in a custom method.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe name the method ice instead? That implies the values will sometimes be thawed :)

@Hamms
Hamms merged commit e3aa570 into staging May 8, 2023
@Hamms
Hamms deleted the reimplement-cdo-config-without-openstruct branch May 8, 2023 19:09
@Hamms Hamms mentioned this pull request Jun 29, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Ruby Update Everything related to work to update the version of Ruby our codebase runs on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants