summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBobby McDonald <BobbyMcWho@users.noreply.github.com>2019-10-14 14:14:47 -0400
committerDaniel Doubrovkine (dB.) @dblockdotorg <dblock@dblock.org>2019-10-14 14:14:47 -0400
commit1a30427c9db1bdf974530aeddf90b305a3a621a5 (patch)
tree7dde72202b0fbfdd250113aa119239c322bac107
parentca3604516b5725b30a290482c219ca55fca5de49 (diff)
downloadhashie-1a30427c9db1bdf974530aeddf90b305a3a621a5.tar.gz
Allow mash error silencing (#488)
-rw-r--r--.rubocop_todo.yml10
-rw-r--r--CHANGELOG.md3
-rw-r--r--README.md7
-rw-r--r--UPGRADING.md6
-rw-r--r--lib/hashie.rb38
-rw-r--r--lib/hashie/extensions/key_conflict_warning.rb55
-rw-r--r--lib/hashie/mash.rb66
-rw-r--r--spec/hashie/mash_spec.rb23
8 files changed, 131 insertions, 77 deletions
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index b3fa0d3..0590878 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -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
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 56ac655..a2b2c37 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -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
diff --git a/README.md b/README.md
index 0a088dd..be94965 100644
--- a/README.md
+++ b/README.md
@@ -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.
diff --git a/UPGRADING.md b/UPGRADING.md
index ce01eca..1374e10 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -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
diff --git a/lib/hashie.rb b/lib/hashie.rb
index fd93e05..a3fc11c 100644
--- a/lib/hashie.rb
+++ b/lib/hashie.rb
@@ -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'
diff --git a/lib/hashie/extensions/key_conflict_warning.rb b/lib/hashie/extensions/key_conflict_warning.rb
new file mode 100644
index 0000000..7ce56a1
--- /dev/null
+++ b/lib/hashie/extensions/key_conflict_warning.rb
@@ -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
diff --git a/lib/hashie/mash.rb b/lib/hashie/mash.rb
index 888b4f6..77a852f 100644
--- a/lib/hashie/mash.rb
+++ b/lib/hashie/mash.rb
@@ -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 []
diff --git a/spec/hashie/mash_spec.rb b/spec/hashie/mash_spec.rb
index e2a178c..77a1c6b 100644
--- a/spec/hashie/mash_spec.rb
+++ b/spec/hashie/mash_spec.rb
@@ -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 }) }