Last active
March 1, 2023 14:12
-
-
Save kalsan/87826048ea0ade92ab1be93c0919b405 to your computer and use it in GitHub Desktop.
MethodAccessibleHash, a possible replacement for OpenStruct
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# OpenStruct has caveats and security problems and even leads to a rubocop offense: | |
# https://ruby-doc.org/stdlib-3.1.0/libdoc/ostruct/rdoc/OpenStruct.html#class-OpenStruct-label-Caveats | |
# https://msp-greg.github.io/rubocop/RuboCop/Cop/Style/OpenStructUse.html | |
# | |
# It's time to replace it. The feature I loved most about OpenStruct was being able to retrieve | |
# values by calling a method instead of having to use hash access syntax. | |
# The recommended way to go is Struct, but it is much more cumbersome than OpenStruct was. | |
# So here's MethodAccessibleHash that allows to do that, but without OpenStruct's security problems. | |
# | |
# This revision adds writers, direct creation from Hash, as well as enhanced merging. | |
# | |
# License: MIT | |
# Feel free to use this code anywhere you like. No warranty provided. | |
# | |
# Usage example: | |
# default_options = { foo: :bar } | |
# options = MethodAccessibleHash.new(default_options) | |
# options[:color] = :green | |
# options.foo # => :bar | |
# options.color # => green | |
class MethodAccessibleHash < ::Hash | |
# Takes an optional hash as argument and constructs a new | |
# MethodAccessibleHash. | |
def initialize(hash = {}) | |
super() | |
hash.each do |key, value| | |
self[key.to_sym] = value | |
end | |
end | |
# @private | |
def merge(hash) | |
super(hash.symbolize_keys) | |
end | |
# @private | |
def method_missing(method, *args, &_block) | |
if method.end_with?('=') | |
name = method.to_s.gsub(/=$/, '') | |
self[name.to_sym] = args.first | |
else | |
self[method.to_sym] | |
end | |
end | |
# @private | |
def respond_to_missing?(_method, _include_private = false) | |
true | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment