diff options
author | Michael Herold <opensource@michaeljherold.com> | 2019-11-17 11:16:10 -0600 |
---|---|---|
committer | Michael Herold <opensource@michaeljherold.com> | 2019-11-17 11:36:31 -0600 |
commit | 15ea67ef0667546627f9a3cd4fef4457512f5880 (patch) | |
tree | 2cf6345d16a063cd0dca51eb6a0dfc8c90ea2d80 /lib/hashie/extensions/mash/permissive_respond_to.rb | |
parent | 2846ea63a90a594ed67e3eb8ba7c5fd125909089 (diff) | |
download | hashie-15ea67ef0667546627f9a3cd4fef4457512f5880.tar.gz |
Add a PermissiveRespondTo extension for Mashes
By default, Mashes don't state that they respond to unset keys. This
causes unexpected behavior when you try to use a Mash with a
SimpleDelegator.
This new extension allows you create a permissive subclass of Mash that
will be fully compatible with SimpleDelegator and allow you to fully do
thunk-oriented programming with Mashes.
This comes with the trade-off of a ~19KB cache for each of these
subclasses and a ~20% performance penalty on any of those subclasses.
Diffstat (limited to 'lib/hashie/extensions/mash/permissive_respond_to.rb')
-rw-r--r-- | lib/hashie/extensions/mash/permissive_respond_to.rb | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/lib/hashie/extensions/mash/permissive_respond_to.rb b/lib/hashie/extensions/mash/permissive_respond_to.rb new file mode 100644 index 0000000..5f8c231 --- /dev/null +++ b/lib/hashie/extensions/mash/permissive_respond_to.rb @@ -0,0 +1,61 @@ +module Hashie + module Extensions + module Mash + # Allow a Mash to properly respond to everything + # + # By default, Mashes only say they respond to methods for keys that exist + # in their key set or any of the affix methods (e.g. setter, underbang, + # etc.). This causes issues when you try to use them within a + # SimpleDelegator or bind to a method for a key that is unset. + # + # This extension allows a Mash to properly respond to `respond_to?` and + # `method` for keys that have not yet been set. This enables full + # compatibility with SimpleDelegator and thunk-oriented programming. + # + # There is a trade-off with this extension: it will run slower than a + # regular Mash; insertions and initializations with keys run approximately + # 20% slower and cost approximately 19KB of memory per class that you + # make permissive. + # + # @api public + # @example Make a new, permissively responding Mash subclass + # class PermissiveMash < Hashie::Mash + # include Hashie::Extensions::Mash::PermissiveRespondTo + # end + # + # mash = PermissiveMash.new(a: 1) + # mash.respond_to? :b #=> true + module PermissiveRespondTo + # The Ruby hook for behavior when including the module + # + # @api private + # @private + # @return void + def self.included(base) + base.instance_variable_set :@_method_cache, base.instance_methods + base.define_singleton_method(:method_cache) { @_method_cache } + end + + # The Ruby hook for determining what messages a class might respond to + # + # @api private + # @private + def respond_to_missing?(_method_name, _include_private = false) + true + end + + private + + # Override the Mash logging behavior to account for permissiveness + # + # @api private + # @private + def log_collision?(method_key) + self.class.method_cache.include?(method_key) && + !self.class.disable_warnings?(method_key) && + !(regular_key?(method_key) || regular_key?(method_key.to_s)) + end + end + end + end +end |