Allow mash error silencing (#488)

This commit is contained in:
Bobby McDonald 2019-10-14 14:14:47 -04:00 committed by Daniel Doubrovkine (dB.) @dblockdotorg
parent ca3604516b
commit 1a30427c9d
8 changed files with 131 additions and 77 deletions

View File

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2019-08-13 23:33:30 -0400 using RuboCop version 0.52.1.
# on 2019-10-10 00:07:29 -0400 using RuboCop version 0.52.1.
# The point is for the user to remove these configuration records
# one by one as the offenses are removed from the code base.
# Note that changes in the inspected code, or installation of new
@ -14,6 +14,12 @@ Metrics/AbcSize:
Metrics/CyclomaticComplexity:
Max: 11
# Offense count: 1
# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
# URISchemes: http, https
Metrics/LineLength:
Max: 111
# Offense count: 18
# Configuration parameters: CountComments.
Metrics/MethodLength:
@ -23,6 +29,6 @@ Metrics/MethodLength:
Metrics/PerceivedComplexity:
Max: 10
# Offense count: 39
# Offense count: 41
Style/Documentation:
Enabled: false

View File

@ -15,7 +15,8 @@ scheme are considered to be bugs.
* [#323](https://github.com/intridea/hashie/pull/323): Added `Hashie::Extensions::Mash::DefineAccessors` - [@marshall-lee](https://github.com/marshall-lee).
* [#474](https://github.com/intridea/hashie/pull/474): Expose `YAML#safe_load` options in `Mash#load` - [@riouruma](https://github.com/riouruma), [@dblock](https://github.com/dblock).
* [#478](https://github.com/intridea/hashie/pull/478): Added optional array parameter to `Hashie::Mash.disable_warnings` - [@bobbymcwho](https://github.com/bobbymcwho).
* [#481](https://github.com/intridea/hashie/pull/481): Ruby 2.6 - Support Hash#merge and #merge! called with multiple Hashes/Mashes - [@bobbymcwho](https://github.com/bobbymcwho).
* [#481](https://github.com/intridea/hashie/pull/481): Ruby 2.6 - Support `Hash#merge` and `#merge!` called with multiple Hashes/Mashes - [@bobbymcwho](https://github.com/bobbymcwho).
* [#488](https://github.com/intridea/hashie/pull/488): Added ability to create an anonymous `Hashie::Mash` subclass with key conflict errors silenced using `Hashie::Mash.quiet.new` - [@bobbymcwho](https://github.com/bobbymcwho).
* Your contribution here.
### Changed

View File

@ -557,6 +557,13 @@ end
Response.new(merge: 'true', compact: true, zip: '90210', zap: 'electric')
```
If you would like to create an anonymous subclass of a Hashie::Mash with key conflict warnings disabled:
```ruby
Hashie::Mash.quiet.new(zip: '90210', compact: true) # no errors logged
Hashie::Mash.quiet(:zip).new(zip: '90210', compact: true) # error logged for compact
```
### How does the wrapping of Mash sub-Hashes work?
Mash duplicates any sub-Hashes that you add to it and wraps them in a Mash. This allows for infinite chaining of nested Hashes within a Mash without modifying the object(s) that are passed into the Mash. When you subclass Mash, the subclass wraps any sub-Hashes in its own class. This preserves any extensions that you mixed into the Mash subclass and allows them to work within the sub-Hashes, in addition to the main containing Mash.

View File

@ -36,10 +36,14 @@ p cool_parents
This may make places where you had to re-make the Mash redundant, and may cause unintended side effects if your application was expecting a plain old ruby Hash.
### Ruby 2.6: Mash#merge and Mash#merge!
#### Ruby 2.6: Mash#merge and Mash#merge!
In Ruby > 2.6.0, Hashie now supports passing multiple hash and Mash objects to Mash#merge and Mash#merge!.
#### Hashie::Mash::CannotDisableMashWarnings error class is removed
There shouldn't really be a case that anyone was relying on catching this specific error, but if so, they should change it to rescue Hashie::Extensions::KeyConflictWarning::CannotDisableMashWarnings
### Upgrading to 3.7.0
#### Mash#load takes options

View File

@ -12,26 +12,26 @@ module Hashie
autoload :Utils, 'hashie/utils'
module Extensions
autoload :Coercion, 'hashie/extensions/coercion'
autoload :DeepMerge, 'hashie/extensions/deep_merge'
autoload :IgnoreUndeclared, 'hashie/extensions/ignore_undeclared'
autoload :IndifferentAccess, 'hashie/extensions/indifferent_access'
autoload :MergeInitializer, 'hashie/extensions/merge_initializer'
autoload :MethodAccess, 'hashie/extensions/method_access'
autoload :MethodQuery, 'hashie/extensions/method_access'
autoload :MethodReader, 'hashie/extensions/method_access'
autoload :MethodWriter, 'hashie/extensions/method_access'
autoload :StringifyKeys, 'hashie/extensions/stringify_keys'
autoload :SymbolizeKeys, 'hashie/extensions/symbolize_keys'
autoload :DeepFetch, 'hashie/extensions/deep_fetch'
autoload :DeepFind, 'hashie/extensions/deep_find'
autoload :DeepLocate, 'hashie/extensions/deep_locate'
autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
autoload :KeyConversion, 'hashie/extensions/key_conversion'
autoload :Coercion, 'hashie/extensions/coercion'
autoload :DeepMerge, 'hashie/extensions/deep_merge'
autoload :IgnoreUndeclared, 'hashie/extensions/ignore_undeclared'
autoload :IndifferentAccess, 'hashie/extensions/indifferent_access'
autoload :MergeInitializer, 'hashie/extensions/merge_initializer'
autoload :MethodAccess, 'hashie/extensions/method_access'
autoload :MethodQuery, 'hashie/extensions/method_access'
autoload :MethodReader, 'hashie/extensions/method_access'
autoload :MethodWriter, 'hashie/extensions/method_access'
autoload :StringifyKeys, 'hashie/extensions/stringify_keys'
autoload :SymbolizeKeys, 'hashie/extensions/symbolize_keys'
autoload :DeepFetch, 'hashie/extensions/deep_fetch'
autoload :DeepFind, 'hashie/extensions/deep_find'
autoload :DeepLocate, 'hashie/extensions/deep_locate'
autoload :PrettyInspect, 'hashie/extensions/pretty_inspect'
autoload :KeyConversion, 'hashie/extensions/key_conversion'
autoload :MethodAccessWithOverride, 'hashie/extensions/method_access'
autoload :StrictKeyAccess, 'hashie/extensions/strict_key_access'
autoload :RubyVersion, 'hashie/extensions/ruby_version'
autoload :RubyVersionCheck, 'hashie/extensions/ruby_version_check'
autoload :StrictKeyAccess, 'hashie/extensions/strict_key_access'
autoload :RubyVersion, 'hashie/extensions/ruby_version'
autoload :RubyVersionCheck, 'hashie/extensions/ruby_version_check'
module Parsers
autoload :YamlErbParser, 'hashie/extensions/parsers/yaml_erb_parser'

View File

@ -0,0 +1,55 @@
module Hashie
module Extensions
module KeyConflictWarning
class CannotDisableMashWarnings < StandardError
def initialize
super(
'You cannot disable warnings on the base Mash class. ' \
'Please subclass the Mash and disable it in the subclass.'
)
end
end
# Disable the logging of warnings based on keys conflicting keys/methods
#
# @api semipublic
# @return [void]
def disable_warnings(*method_keys)
raise CannotDisableMashWarnings if self == Hashie::Mash
if method_keys.any?
disabled_warnings.concat(method_keys).tap(&:flatten!).uniq!
else
disabled_warnings.clear
end
@disable_warnings = true
end
# Checks whether this class disables warnings for conflicting keys/methods
#
# @api semipublic
# @return [Boolean]
def disable_warnings?(method_key = nil)
return disabled_warnings.include?(method_key) if disabled_warnings.any? && method_key
@disable_warnings ||= false
end
# Returns an array of blacklisted methods that this class disables warnings for.
#
# @api semipublic
# @return [Boolean]
def disabled_warnings
@_disabled_warnings ||= []
end
# Inheritance hook that sets class configuration when inherited.
#
# @api semipublic
# @return [void]
def inherited(subclass)
super
subclass.disable_warnings(disabled_warnings) if disable_warnings?
end
end
end
end

View File

@ -2,6 +2,7 @@ require 'hashie/hash'
require 'hashie/array'
require 'hashie/utils'
require 'hashie/logger'
require 'hashie/extensions/key_conflict_warning'
module Hashie
# Mash allows you to create pseudo-objects that have method-like
@ -62,59 +63,10 @@ module Hashie
class Mash < Hash
include Hashie::Extensions::PrettyInspect
include Hashie::Extensions::RubyVersionCheck
extend Hashie::Extensions::KeyConflictWarning
ALLOWED_SUFFIXES = %w[? ! = _].freeze
class CannotDisableMashWarnings < StandardError
def initialize
super(
'You cannot disable warnings on the base Mash class. ' \
'Please subclass the Mash and disable it in the subclass.'
)
end
end
# Disable the logging of warnings based on keys conflicting keys/methods
#
# @api semipublic
# @return [void]
def self.disable_warnings(*method_keys)
raise CannotDisableMashWarnings if self == Hashie::Mash
if method_keys.any?
disable_warnings_blacklist.concat(method_keys).tap(&:flatten!).uniq!
else
disable_warnings_blacklist.clear
end
@disable_warnings = true
end
# Checks whether this class disables warnings for conflicting keys/methods
#
# @api semipublic
# @return [Boolean]
def self.disable_warnings?(method_key = nil)
return disable_warnings_blacklist.include?(method_key) if disable_warnings_blacklist.any? && method_key
@disable_warnings ||= false
end
# Returns an array of blacklisted methods that this class disables warnings for.
#
# @api semipublic
# @return [Boolean]
def self.disable_warnings_blacklist
@_disable_warnings_blacklist ||= []
end
# Inheritance hook that sets class configuration when inherited.
#
# @api semipublic
# @return [void]
def self.inherited(subclass)
super
subclass.disable_warnings(disable_warnings_blacklist) if disable_warnings?
end
def self.load(path, options = {})
@_mashes ||= new
@ -149,6 +101,20 @@ module Hashie
default ? super(default) : super(&blk)
end
# Creates a new anonymous subclass with key conflict
# warnings disabled. You may pass an array of method
# symbols to restrict the warnings blacklist to.
# Hashie::Mash.quiet.new(hash) all warnings disabled.
# Hashie::Mash.quiet(:zip).new(hash) only zip warning
# is disabled.
def self.quiet(*method_keys)
(@memoized_classes ||= {})[method_keys] ||
Class.new(self).tap do |k|
k.send(:disable_warnings, *method_keys)
@memoized_classes[method_keys] = k
end
end
class << self; alias [] new; end
alias regular_reader []

View File

@ -160,7 +160,9 @@ describe Hashie::Mash do
end
it 'cannot disable logging on the base Mash' do
expect { Hashie::Mash.disable_warnings }.to raise_error(Hashie::Mash::CannotDisableMashWarnings)
expected_error = Hashie::Extensions::KeyConflictWarning::CannotDisableMashWarnings
expect { Hashie::Mash.disable_warnings }.to raise_error(expected_error)
end
it 'carries over the disable for warnings on grandchild classes' do
@ -200,7 +202,7 @@ describe Hashie::Mash do
grandchild_class.new('address' => { 'zip' => '90210' }, 'merge' => true)
expect(grandchild_class.disable_warnings_blacklist).to eq(%i[zip merge])
expect(grandchild_class.disabled_warnings).to eq(%i[zip merge])
expect(logger_output).to be_blank
end
@ -213,7 +215,7 @@ describe Hashie::Mash do
disable_warnings :cycle
end
expect(child_class.disable_warnings_blacklist).to eq(%i[zip merge cycle])
expect(child_class.disabled_warnings).to eq(%i[zip merge cycle])
end
end
@ -226,7 +228,7 @@ describe Hashie::Mash do
child_class.new('address' => { 'zip' => '90210' }, 'merge' => true, 'cycle' => 'bi')
expect(child_class.disable_warnings_blacklist).to eq([])
expect(child_class.disabled_warnings).to eq([])
expect(logger_output).to be_blank
end
end
@ -962,6 +964,19 @@ describe Hashie::Mash do
end
end
describe '.quiet' do
it 'returns a subclass of the calling class' do
expect(Hashie::Mash.quiet.new).to be_a(Hashie::Mash)
end
it 'memoizes and returns classes' do
call_one = Hashie::Mash.quiet
call_two = Hashie::Mash.quiet
expect(Hashie::Mash.instance_variable_get('@memoized_classes').count).to eq(1)
expect(call_one).to eq(call_two)
end
end
with_minimum_ruby('2.3.0') do
describe '#dig' do
subject { described_class.new(a: { b: 1 }) }