Allow Mash subclasses to disable warnings

Since we are transitively used as a dependency in many projects, we
should have given the ability to toggle this behavior off. The logging
feature is more of a "help people get started with Mash" feature. If
you're using Hashie in a library, it's likely that you already know the
tradeoffs of attempting to override methods on the object.

To use this feature, you only have to subclass Mash and then call the
class method:

```ruby
class KeyStore < Hashie::Mash
  disable_warnings
end
```
This commit is contained in:
Michael Herold 2017-02-01 08:03:45 -06:00
parent 24caf3564e
commit b2f94a4866
7 changed files with 83 additions and 16 deletions

View File

@ -1,6 +1,6 @@
# This configuration was generated by
# `rubocop --auto-gen-config`
# on 2016-06-01 14:51:33 -0700 using RuboCop version 0.34.2.
# on 2017-02-01 08:14:39 -0600 using RuboCop version 0.34.2.
# 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
@ -11,30 +11,30 @@ Lint/NestedMethodDefinition:
Exclude:
- 'lib/hashie/extensions/indifferent_access.rb'
# Offense count: 8
# Offense count: 10
Metrics/AbcSize:
Max: 29
# Offense count: 2
# Configuration parameters: CountComments.
Metrics/ClassLength:
Max: 191
Max: 200
# Offense count: 6
# Offense count: 8
Metrics/CyclomaticComplexity:
Max: 11
# Offense count: 231
# Offense count: 239
# Configuration parameters: AllowURI, URISchemes.
Metrics/LineLength:
Max: 170
# Offense count: 16
# Offense count: 17
# Configuration parameters: CountComments.
Metrics/MethodLength:
Max: 28
# Offense count: 5
# Offense count: 6
Metrics/PerceivedComplexity:
Max: 10
@ -43,12 +43,12 @@ Style/CaseEquality:
Exclude:
- 'lib/hashie/hash.rb'
# Offense count: 32
# Offense count: 34
# Configuration parameters: Exclude.
Style/Documentation:
Enabled: false
# Offense count: 10
# Offense count: 11
Style/DoubleNegation:
Exclude:
- 'lib/hashie/dash.rb'

View File

@ -12,7 +12,7 @@ scheme are considered to be bugs.
### Added
* Your contribution here.
* [#395](https://github.com/intridea/hashie/pull/395): Add the ability to disable warnings in Mash subclasses - [@michaelherold](https://github.com/michaelherold).
### Changed

View File

@ -531,6 +531,20 @@ mash = Mash.load('data/user.csv', parser: MyCustomCsvParser)
mash[1] #=> { name: 'John', lastname: 'Doe' }
```
Since Mash gives you the ability to set arbitrary keys that then act as methods, Hashie logs when there is a conflict between a key and a pre-existing method. You can set the logger that this logs message to via the global Hashie logger:
```ruby
Hashie.logger = Rails.logger
```
You can also disable the logging in subclasses of Mash:
```ruby
class Response < Hashie::Mash
disable_warnings
end
```
### Mash Extension: SafeAssignment
This extension can be mixed into a Mash to guard the attempted overwriting of methods by property setters. When mixed in, the Mash will raise an `ArgumentError` if you attempt to write a property with the same name as an existing method.

View File

@ -1,6 +1,19 @@
Upgrading Hashie
================
### Upgrading to 3.5.2
#### Disable logging in Mash subclasses
If you subclass `Hashie::Mash`, you can now disable the logging we do about
overriding existing methods with keys. This looks like:
```ruby
class MyMash < Hashie::Mash
disable_warnings
end
```
### Upgrading to 3.4.7
#### Procs as default values for Dash
@ -11,7 +24,7 @@ class MyHash < Hashie::Dash
end
```
In versions < 3.4.7 `Time.now` will be evaluated when `time` property is accessed directly first time.
In versions < 3.4.7 `Time.now` will be evaluated when `time` property is accessed directly first time.
In version >= 3.4.7 `Time.now` is evaluated in time of object initialization.
### Upgrading to 3.4.4

View File

@ -63,6 +63,29 @@ module Hashie
ALLOWED_SUFFIXES = %w(? ! = _)
class CannotDisableMashWarnings < StandardError
def initialize(message = 'You cannot disable warnings on the base Mash class. Please subclass the Mash and disable it in the subclass.')
super(message)
end
end
# Disable the logging of warnings based on keys conflicting keys/methods
#
# @api semipublic
# @return [void]
def self.disable_warnings
fail CannotDisableMashWarnings if self == Hashie::Mash
@disable_warnings = true
end
# Checks whether this class disables warnings for conflicting keys/methods
#
# @api semipublic
# @return [Boolean]
def self.disable_warnings?
!!@disable_warnings
end
def self.load(path, options = {})
@_mashes ||= new
@ -303,6 +326,8 @@ module Hashie
private
def log_built_in_message(method_key)
return if self.class.disable_warnings?
method_information = Hashie::Utils.method_information(method(method_key))
Hashie.logger.warn(

View File

@ -1,6 +1,4 @@
require 'spec_helper'
require 'delegate'
require 'support/logger'
describe Hashie::Mash do
subject { Hashie::Mash.new }
@ -137,10 +135,26 @@ describe Hashie::Mash do
expect(subject.type).to eq 'Steve'
end
it 'logs a warning when overriding built-in methods' do
Hashie::Mash.new('trust' => { 'two' => 2 })
include_context 'with a logger' do
it 'logs a warning when overriding built-in methods' do
Hashie::Mash.new('trust' => { 'two' => 2 })
expect(logger_output).to match('Hashie::Mash#trust')
expect(logger_output).to match('Hashie::Mash#trust')
end
it 'does not write to the logger when warnings are disabled' do
mash_class = Class.new(Hashie::Mash) do
disable_warnings
end
mash_class.new('trust' => { 'two' => 2 })
expect(logger_output).to be_blank
end
it 'cannot disable logging on the base Mash' do
expect { Hashie::Mash.disable_warnings }.to raise_error(Hashie::Mash::CannotDisableMashWarnings)
end
end
context 'updating' do

View File

@ -9,6 +9,7 @@ require 'rspec'
require 'hashie'
require 'rspec/pending_for'
require './spec/support/ruby_version_check'
require './spec/support/logger'
require 'active_support'
require 'active_support/core_ext'