diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 0000000000000000000000000000000000000000..55a3b9bbc1354718edf71e803749d212ce2add03
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,99 @@
+name: CI
+
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:
+      - master
+
+jobs:
+  danger:
+    runs-on: ubuntu-latest
+    if: ${{ github.event_name == 'pull_request' }}
+    steps:
+    - uses: actions/checkout@v2
+      with:
+        fetch-depth: 0
+    - uses: ruby/setup-ruby@v1
+      with:
+        ruby-version: 2.7
+        bundler-cache: true
+    - uses: MeilCli/danger-action@v5
+      with:
+        danger_file: Dangerfile
+        danger_id: danger-pr
+        install_path: vendor/bundle
+        plugins_file: Gemfile
+      env:
+        DANGER_GITHUB_API_TOKEN: ${{ secrets.github_token }}
+
+  integration-test:
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Ruby
+      uses: ruby/setup-ruby@v1
+      with:
+        bundler-cache: true
+        ruby-version: 2.7
+    - name: Run integration tests
+      run: |
+        for dir in spec/integration/*; do
+          echo "testing $dir integration"
+          BUNDLE_GEMFILE=$dir/Gemfile bundle install --jobs 4 --retry 3
+          BUNDLE_GEMFILE=$dir/Gemfile bundle exec rspec $dir
+        done
+
+  test:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        ruby:
+          - 3.0
+          - 2.7
+          - 2.6
+          - 2.5
+          - 2.4
+          - 2.3
+          - 2.2
+          - 2.1
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Ruby
+      uses: ruby/setup-ruby@v1
+      with:
+        bundler-cache: true
+        ruby-version: ${{ matrix.ruby }}
+    - name: Install dependencies
+      run: bundle install --jobs 4 --retry 3
+    - name: Run tests
+      run: bundle exec rake
+
+  test-jruby:
+    runs-on: ubuntu-latest
+    strategy:
+      fail-fast: false
+      matrix:
+        os:
+          - macos
+          - ubuntu
+        jruby:
+          - jruby
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Ruby
+      uses: ruby/setup-ruby@v1
+      with:
+        bundler-cache: true
+        ruby-version: ${{ matrix.jruby }}
+    - name: Install dependencies
+      env:
+        JRUBY_OPTS: --debug
+      run: bundle install --jobs 4 --retry 3
+    - name: Run tests
+      env:
+        JRUBY_OPTS: --debug
+      run: bundle exec rake
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000000000000000000000000000000000000..3563ce055ab17d1b9f083e8aaf50cae27a957a90
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+*.sw?
+.DS_Store
+coverage
+rdoc
+pkg
+*.gem
+*.log
+.bundle
+.rvmrc
+Gemfile.lock
+log/
diff --git a/.rspec b/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..4b942a1905881a8c624e9ecfdd0c4fa5f9e718e4
--- /dev/null
+++ b/.rspec
@@ -0,0 +1,3 @@
+--colour
+--format=documentation
+--exclude-pattern='spec/integration/**/*_spec.rb'
diff --git a/.rubocop.yml b/.rubocop.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d35e18e89b077a2719c6e26f47154570fd1b5789
--- /dev/null
+++ b/.rubocop.yml
@@ -0,0 +1,45 @@
+AllCops:
+  Include:
+    - Guardfile
+    - Rakefile
+  Exclude:
+    - .bundle/**/*
+    - vendor/**/*
+
+inherit_from: .rubocop_todo.yml
+
+# Disabled until we can use the squiggly heredoc (after deprecating Ruby <2.3)
+Layout/IndentHeredoc:
+  Enabled: false
+
+Metrics/ClassLength:
+  Enabled: false
+
+Metrics/ModuleLength:
+  Enabled: false
+
+Metrics/BlockLength:
+  Exclude:
+    - 'spec/**/*.rb'
+
+Metrics/LineLength:
+  Exclude:
+    - 'Guardfile'
+  Max: 100
+
+Lint/UnifiedInteger:
+  Exclude:
+    - 'lib/hashie/extensions/coercion.rb'
+    - 'spec/hashie/extensions/coercion_spec.rb'
+
+Naming/FileName:
+  Exclude:
+    - 'Dangerfile'
+    - '**/Gemfile'
+    - '**/Rakefile'
+
+# Disabled because of the very generic nature of Hashie. In the cases where we
+# use double negation, there isn't a means that as succinct of doing the type of
+# boolean casting that we do.
+Style/DoubleNegation:
+  Enabled: false
diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f2cc4f76160204344a18ac3e709696527bfd43d2
--- /dev/null
+++ b/.rubocop_todo.yml
@@ -0,0 +1,28 @@
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2020-10-02 23:12:27 -0300 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
+# versions of RuboCop, may require this file to be generated again.
+
+# Offense count: 9
+Metrics/AbcSize:
+  Max: 23
+
+# Offense count: 8
+Metrics/CyclomaticComplexity:
+  Max: 11
+
+# Offense count: 18
+# Configuration parameters: CountComments.
+Metrics/MethodLength:
+  Max: 28
+
+# Offense count: 6
+Metrics/PerceivedComplexity:
+  Max: 10
+
+# Offense count: 44
+Style/Documentation:
+  Enabled: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0d764635df483136739e21e181339b1d01cff255..31acfaa9df17fc20ef704f2e2057808642d6dbf1 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,435 +1,541 @@
-# Change Log
+# Changelog
 
-All notable changes to this project will be documented in this file. This
-project adheres to [Semantic Versioning 2.0.0][semver]. Any violations of this
-scheme are considered to be bugs.
+All notable changes to this project will be documented in this file.
 
-[semver]: http://semver.org/spec/v2.0.0.html
+The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
+and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
+Any violations of this scheme are considered to be bugs.
+
+## [5.0.0] - 2021-11-08
+
+[5.0.0]: https://github.com/hashie/hashie/compare/v4.1.0...v5.0.0
+
+### Added
+
+* [#523](https://github.com/hashie/hashie/pull/523): Added TOC, ensure a keep-a-changelog formatted CHANGELOG - [@dblock](https://github.com/dblock).
+* [#522](https://github.com/hashie/hashie/pull/522): Added eierlegende Wollmilchsau mascot graphic - [@carolineartz](https://github.com/carolineartz).
+* [#530](https://github.com/hashie/hashie/pull/530): Added Hashie::Extensions::Dash::PredefinedValues - [@caalberts](https://github.com/caalberts).
+* [#536](https://github.com/hashie/hashie/pull/536): Added exporting a normal Hash from an indifferent one through the `#to_hash` method - [@michaelherold](https://github.com/michaelherold).
+* [#539](https://github.com/hashie/hashie/pull/539): Run 2.7 tests once - [@anakinj](https://github.com/anakinj).
+
+### Changed
+
+* [#521](https://github.com/hashie/hashie/pull/499): Do not convert keys that cannot be represented as symbols to `String` in `Mash` initialization - [@carolineartz](https://github.com/carolineartz).
+* [#524](https://github.com/hashie/hashie/pull/524): Test with Ruby 2.7 - [@aried3r](https://github.com/aried3r).
+* [#525](https://github.com/hashie/hashie/pull/525): Use `indifferent_writer` in `IndifferentAccess#convert!` - [@yogeshjain999](https://github.com/yogeshjain999).
+* [#527](https://github.com/hashie/hashie/pull/527): Updated Copyright to (c) 2009-2020 Intridea, Inc., and Contributors - [@dblock](https://github.com/dblock).
+* [#555](https://github.com/hashie/hashie/pull/555): Test with Ruby 3.0 - [@dblock](https://github.com/dblock).
+
+### Removed
+
+* [#538](https://github.com/hashie/hashie/pull/538): Dropped testing for JRuby 9.0, though not support - [@michaelherold](https://github.com/michaelherold).
+
+### Fixed
+
+* [#516](https://github.com/hashie/hashie/issues/516): Fixed `NoMethodError` raised when including `Hashie::Extensions::Mash::SymbolizeKeys` and `Hashie::Extensions::SymbolizeKeys` in mashes/hashes with non string or symbol keys - [@carolineartz](https://github.com/carolineartz).
+* [#531](https://github.com/hashie/hashie/pull/531): Fixed [slice doesn't work using symbols](https://github.com/hashie/hashie/issues/529) using hash with `IndifferentAccess` extension - [@gnomex](https://github.com/gnomex).
+* [#533](https://github.com/hashie/hashie/pull/533): Fixed `NoMethodError: undefined method 'to_json'` at `hashie/dash_spec` - [@gnomex](https://github.com/gnomex).
+* [#535](https://github.com/hashie/hashie/pull/535): Restored the exporting of all properties as part of `Dash#to_h` and `Dash#to_hash` - [@michaelherold](https://github.com/michaelherold).
+* [#537](https://github.com/hashie/hashie/pull/537): Fixed inconsistencies with handling defaults in `Dash` with and without `IgnoreUnclared` mixed in - [@michaelherold](https://github.com/michaelherold).
+* [#547](https://github.com/hashie/hashie/pull/547): Fixed issue where a source hash key can be used in translating multiple properties - [@danwa5](https://github.com/danwa5).
+
+## [4.1.0] - 2020-02-01
+
+[4.1.0]: https://github.com/hashie/hashie/compare/v4.0.0...v4.1.0
+
+### Added
+
+* [#545](https://github.com/hashie/hashie/pull/545): Add `Hashie::Mash#except` and `Hashie::Extensions::IndifferentAccess#except` when running under Ruby 3 to match newly added Ruby stdlib method - [@jackjennings](https://github.com/jackjennings).
+* [#499](https://github.com/hashie/hashie/pull/499): Add `Hashie::Extensions::Mash::PermissiveRespondTo` to make specific subclasses of Mash fully respond to messages for use with `SimpleDelegator` - [@michaelherold](https://github.com/michaelherold).
+
+### Changed
+
+* [#498](https://github.com/hashie/hashie/pull/498): Exclude tests from the gem release to reduce installation size and improve installation speed - [@michaelherold](https://github.com/michaelherold).
+
+### Fixed
+
+* [#467](https://github.com/intridea/hashie/pull/467): Fixed `DeepMerge#deep_merge` mutating nested values within the receiver - [@michaelherold](https://github.com/michaelherold).
+* [#505](https://github.com/hashie/hashie/pull/505): Ensure that `Hashie::Array`s are not deconverted within `Hashie::Mash`es to make `Mash#dig` work properly - [@michaelherold](https://github.com/michaelherold).
+* [#507](https://github.com/hashie/hashie/pull/507): Suppress `Psych.safe_load` arg warn when using Psych 3.1.0+ - [@koic](https://github.com/koic).
+* [#508](https://github.com/hashie/hashie/pull/508): Fixed `Mash.load` no longer uses Rails-only `#except` - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#508](https://github.com/hashie/hashie/pull/508): Fixed `Hashie::Extensions::DeepMerge` `#deep_merge` not correctly dup'ing sub-hashes if active_support hash extensions were not present - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#500](https://github.com/hashie/hashie/pull/500): Do not warn when setting Mash keys that look like underbang, bang, and query methods - [@michaelherold](https://github.com/michaelherold).
+* [#510](https://github.com/hashie/hashie/pull/510): Ensure that `Hashie::Mash#compact` is only defined on Ruby version >= 2.4.0 - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#511](https://github.com/hashie/hashie/pull/511): Suppress keyword arguments warning for Ruby 2.7.0 - [@koic](https://github.com/koic).
+* [#512](https://github.com/hashie/hashie/pull/512): Suppress an integer unification warning for using Ruby 2.4.0+ - [@koic](https://github.com/koic).
+* [#513](https://github.com/hashie/hashie/pull/513): Suppress a Ruby's warning when using Ruby 2.6.0+ - [@koic](https://github.com/koic).
+
+## [4.0.0] - 2019-10-30
+
+[4.0.0]: https://github.com/hashie/hashie/compare/v3.6.0...v4.0.0
+
+### Added
+
+* [#323](https://github.com/hashie/hashie/pull/323): Added `Hashie::Extensions::Mash::DefineAccessors` - [@marshall-lee](https://github.com/marshall-lee).
+* [#474](https://github.com/hashie/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/hashie/hashie/pull/478): Added optional array parameter to `Hashie::Mash.disable_warnings` - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#481](https://github.com/hashie/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/hashie/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).
+
+### Changed
+
+* [#481](https://github.com/hashie/hashie/pull/481): Implement non-destructive standard Hash methods - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#482](https://github.com/hashie/hashie/pull/482): Update Travis configs to make jruby builds run on trusty dist - [@BobbyMcWho](https://github.com/BobbyMcWho).
+
+### Fixed
+
+* [#459](https://github.com/hashie/hashie/pull/459): Fixed a regression in `Mash.load` that disallowed aliases - [@arekt](https://github.com/arekt) and [@michaelherold](https://github.com/michaelherold).
+* [#465](https://github.com/hashie/hashie/pull/465): Fixed `deep_update` to call any readers when a key exists - [@laertispappas](https://github.com/laertispappas).
+* [#479](https://github.com/hashie/hashie/pull/479): Fixed an issue with `Hash#except` not returning a `Mash` in Rails 6 - [@bobbymcwho](https://github.com/bobbymcwho).
+* [#489](https://github.com/hashie/hashie/pull/489): Updated the documentation to exlain the behavior of `Mash` and keyword arguments - [@Bhacaz](https://github.com/Bhacaz).
+* [#465](https://github.com/hashie/hashie/pull/465): Clean up our RuboCop configuration and fix the outstanding line length violations. This involved some minor refactoring on `Hashie::Extensions::Coercion`, `Hashie::Extensions::Dash::IndifferentAccess`, `Hashie::Extensions::DeepLocate`, `Hashie::Extensions::Mash::SafeAssignment`, and `Hashie::Hash`, but none that were detectable via the test suite - [@michaelherold](https://github.com/michaelherold).
+
+## [3.6.0] - 2018-08-13
+
+[3.6.0]: https://github.com/hashie/hashie/compare/v3.5.7...v3.6.0
+
+### Added
+
+* [#455](https://github.com/hashie/hashie/pull/455): Allow overriding methods when passing in a hash - [@lnestor](https://github.com/lnestor).
+* [#434](https://github.com/hashie/hashie/pull/434): Add documentation around Mash sub-Hashes - [@michaelherold](https://github.com/michaelherold).
+* [#439](https://github.com/hashie/hashie/pull/439): Add an integration spec for Elasticsearch - [@michaelherold](https://github.com/michaelherold).
+
+### Changed
+
+* [#433](https://github.com/hashie/hashie/pull/433): Update Rubocop to the most recent version - [@michaelherold](https://github.com/michaelherold).
+
+### Fixed
+
+* [#435](https://github.com/hashie/hashie/pull/435): Mash `default_proc`s are now propagated down to nested sub-Hashes - [@michaelherold](https://github.com/michaelherold).
+* [#436](https://github.com/hashie/hashie/pull/436): Ensure that `Hashie::Extensions::IndifferentAccess` injects itself after a non-destructive merge - [@michaelherold](https://github.com/michaelherold).
+* [#437](https://github.com/hashie/hashie/pull/437): Allow codependent properties to be set on Dash - [@michaelherold](https://github.com/michaelherold).
+* [#438](https://github.com/hashie/hashie/pull/438): Fix: `NameError (uninitialized constant Hashie::Extensions::Parsers::YamlErbParser::Pathname)` in `Hashie::Mash.load` - [@onk](https://github.com/onk).
+* [#457](https://github.com/hashie/hashie/pull/457): Fix `Trash` to allow it to copy properties from other properties - [@michaelherold](https://github.com/michaelherold).
+
+## [3.5.7] - 2017-12-19
+
+[3.5.7]: https://github.com/hashie/hashie/compare/v3.5.6...v3.5.7
+
+### Fixed
+
+* [#430](https://github.com/hashie/hashie/pull/430): Fix Hashie::Rash randomly losing items - [@Antti](https://github.com/Antti).
+
+### Changed
+
+* [#425](https://github.com/hashie/hashie/pull/425): Update rubies in CI - [@kachick](https://github.com/kachick).
+
+## [3.5.6] - 2017-07-12
+
+[3.5.6]: https://github.com/hashie/hashie/compare/v3.5.5...v3.5.6
+
+### Fixed
+
+* [#416](https://github.com/hashie/hashie/pull/416): Fix `warning: instance variable @disable_warnings not initialized` - [@axfcampos](https://github.com/axfcampos).
 
 ## [3.5.5] - 2017-02-24
 
-[3.5.5]: https://github.com/intridea/hashie/compare/v3.5.4...v3.5.5
+[3.5.5]: https://github.com/hashie/hashie/compare/v3.5.4...v3.5.5
 
 ### Added
 
-* [#326](https://github.com/intridea/hashie/pull/326): Added `Hashie::Extensions::Mash::KeepOriginalKeys` to give Mashes the ability to keep the original structure given to it - [@michaelherold](https://github.com/michaelherold).
+* [#326](https://github.com/hashie/hashie/pull/326): Added `Hashie::Extensions::Mash::KeepOriginalKeys` to give Mashes the ability to keep the original structure given to it - [@michaelherold](https://github.com/michaelherold).
 
 ### Fixed
 
-* [#415](https://github.com/intridea/hashie/pull/415): Fixed Mash logging keys multiple times which lead to a bad user experience or, in some cases, errors - [@michaelherold](https://github.com/michaelherold).
+* [#415](https://github.com/hashie/hashie/pull/415): Fixed Mash logging keys multiple times which lead to a bad user experience or, in some cases, errors - [@michaelherold](https://github.com/michaelherold).
 
 ## [3.5.4] - 2017-02-22
 
-[3.5.4]: https://github.com/intridea/hashie/compare/v3.5.3...v3.5.4
+[3.5.4]: https://github.com/hashie/hashie/compare/v3.5.3...v3.5.4
 
 ### Added
 
-* [#412](https://github.com/intridea/hashie/pull/412): Added a Hashie::Extensions::Mash::SymbolizeKeys extension that overrides the default stringification behavior for keys - [@michaelherold](https://github.com/michaelherold).
+* [#412](https://github.com/hashie/hashie/pull/412): Added a Hashie::Extensions::Mash::SymbolizeKeys extension that overrides the default stringification behavior for keys - [@michaelherold](https://github.com/michaelherold).
 
 ### Fixed
 
-* [#409](https://github.com/intridea/hashie/pull/409): Fixed Railtie detection for projects where Rails is defined but Railties are not availble - [@CallumD](https://github.com/callumd).
-* [#411](https://github.com/intridea/hashie/pull/411): Fixed a performance regression from 3.4.3 that caused a 10x slowdown in OmniAuth - [@michaelherold](https://github.com/michaelherold).
+* [#409](https://github.com/hashie/hashie/pull/409): Fixed Railtie detection for projects where Rails is defined but Railties are not availble - [@CallumD](https://github.com/callumd).
+* [#411](https://github.com/hashie/hashie/pull/411): Fixed a performance regression from 3.4.3 that caused a 10x slowdown in OmniAuth - [@michaelherold](https://github.com/michaelherold).
 
 ## [3.5.3] - 2017-02-11
 
-[3.5.3]: https://github.com/intridea/hashie/compare/v3.5.2...v3.5.3
+[3.5.3]: https://github.com/hashie/hashie/compare/v3.5.2...v3.5.3
 
 ### Fixed
 
-* [#402](https://github.com/intridea/hashie/pull/402): Use a Railtie to set Hashie.logger on rails boot - [@matthewrudy](https://github.com/matthewrudy).
-* [#406](https://github.com/intridea/hashie/pull/406): Ensure that subclasses that disable warnings propagate that setting to grandchild classes - [@michaelherold](https://github.com/michaelherold).
+* [#402](https://github.com/hashie/hashie/pull/402): Use a Railtie to set Hashie.logger on rails boot - [@matthewrudy](https://github.com/matthewrudy).
+* [#406](https://github.com/hashie/hashie/pull/406): Ensure that subclasses that disable warnings propagate that setting to grandchild classes - [@michaelherold](https://github.com/michaelherold).
 * Your contribution here.
 
 ## [3.5.2] - 2017-02-10
 
-[3.5.2]: https://github.com/intridea/hashie/compare/v3.5.1...v3.5.2
+[3.5.2]: https://github.com/hashie/hashie/compare/v3.5.1...v3.5.2
 
 ### Added
 
-* [#395](https://github.com/intridea/hashie/pull/395): Add the ability to disable warnings in Mash subclasses - [@michaelherold](https://github.com/michaelherold).
-* [#400](https://github.com/intridea/hashie/pull/400): Fix Hashie.logger load and set the Hashie logger to the Rails logger in a Rails environment - [@michaelherold](https://github.com/michaelherold).
+* [#395](https://github.com/hashie/hashie/pull/395): Add the ability to disable warnings in Mash subclasses - [@michaelherold](https://github.com/michaelherold).
+* [#400](https://github.com/hashie/hashie/pull/400): Fix Hashie.logger load and set the Hashie logger to the Rails logger in a Rails environment - [@michaelherold](https://github.com/michaelherold).
+* [#397](https://github.com/hashie/hashie/pull/397): Add the integration specs harness into the main test tasks - [@michaelherold](https://github.com/michaelherold).
 
 ### Fixed
 
-* [#396](https://github.com/intridea/hashie/pull/396): Fix for specs in #381: Incorrect use of shared context meant example was not being run - [@biinari](https://github.com/biinari).
-* [#399](https://github.com/intridea/hashie/pull/399): Fix passing Pathname object to Hashie::Mesh.load() - [@albb0920](https://github.com/albb0920).
-
-### Miscellanous
-
-* [#397](https://github.com/intridea/hashie/pull/397): Add the integration specs harness into the main test tasks - [@michaelherold](https://github.com/michaelherold).
+* [#396](https://github.com/hashie/hashie/pull/396): Fix for specs in #381: Incorrect use of shared context meant example was not being run - [@biinari](https://github.com/biinari).
+* [#399](https://github.com/hashie/hashie/pull/399): Fix passing Pathname object to Hashie::Mesh.load() - [@albb0920](https://github.com/albb0920).
 
 ## [3.5.1] - 2017-01-31
 
-* [#392](https://github.com/intridea/hashie/pull/392): Fix for #391: Require all dependencies of Hashie::Mash - [@dblock](https://github.com/dblock).
+* [#392](https://github.com/hashie/hashie/pull/392): Fix for #391: Require all dependencies of Hashie::Mash - [@dblock](https://github.com/dblock).
 
-[3.5.1]: https://github.com/intridea/hashie/compare/v3.5.0...v3.5.1
+[3.5.1]: https://github.com/hashie/hashie/compare/v3.5.0...v3.5.1
 
 ## [3.5.0] - 2017-01-31
 
-* [#386](https://github.com/intridea/hashie/pull/386): Fix for #385: Make `deep_merge` always `deep_dup` nested hashes before merging them in so that there are no shared references between the two hashes being merged. - [@mltsy](https://github.com/mltsy).
-* [#389](https://github.com/intridea/hashie/pull/389): Support Ruby 2.4.0 - [@camelmasa](https://github.com/camelmasa).
+* [#386](https://github.com/hashie/hashie/pull/386): Fix for #385: Make `deep_merge` always `deep_dup` nested hashes before merging them in so that there are no shared references between the two hashes being merged. - [@mltsy](https://github.com/mltsy).
+* [#389](https://github.com/hashie/hashie/pull/389): Support Ruby 2.4.0 - [@camelmasa](https://github.com/camelmasa).
 
-[3.5.0]: https://github.com/intridea/hashie/compare/v3.4.6...v3.5.0
+[3.5.0]: https://github.com/hashie/hashie/compare/v3.4.6...v3.5.0
 
 ### Added
 
-* [#381](https://github.com/intridea/hashie/pull/381): Add a logging layer that lets us report potential issues to our users. As the first logged issue, report when a `Hashie::Mash` is attempting to overwrite a built-in method, since that is one of our number one questions - [@michaelherold](https://github.com/michaelherold).
+* [#381](https://github.com/hashie/hashie/pull/381): Add a logging layer that lets us report potential issues to our users. As the first logged issue, report when a `Hashie::Mash` is attempting to overwrite a built-in method, since that is one of our number one questions - [@michaelherold](https://github.com/michaelherold).
 
 ### Changed
 
-* [#384](https://github.com/intridea/hashie/pull/384): Updated to CodeClimate 1.x - [@boffbowsh](https://github.com/boffbowsh).
+* [#384](https://github.com/hashie/hashie/pull/384): Updated to CodeClimate 1.x - [@boffbowsh](https://github.com/boffbowsh).
 
 ### Fixed
 
-* [#369](https://github.com/intridea/hashie/pull/369): If a translation for a property exists when using IndifferentAccess and IgnoreUndeclared, use the translation to find the property - [@whitethunder](https://github.com/whitethunder).
-* [#376](https://github.com/intridea/hashie/pull/376): Leave string index unchanged if it can't be converted to integer for Array#dig - [@sazor](https://github.com/sazor).
-* [#377](https://github.com/intridea/hashie/pull/377): Dont use Rubygems to check ruby version - [@sazor](https://github.com/sazor).
-* [#378](https://github.com/intridea/hashie/pull/378): Deep find all searches inside all nested hashes - [@sazor](https://github.com/sazor).
-* [#380](https://github.com/intridea/hashie/pull/380): Evaluate procs default values of Dash in object initialization - [@sazor](https://github.com/sazor).
-
-### Miscellanous
-
-* [#387](https://github.com/intridea/hashie/pull/387): Fix builds failing due to Rake 11 having a breaking change - [@michaelherold](https://github.com/michaelherold).
+* [#369](https://github.com/hashie/hashie/pull/369): If a translation for a property exists when using IndifferentAccess and IgnoreUndeclared, use the translation to find the property - [@whitethunder](https://github.com/whitethunder).
+* [#376](https://github.com/hashie/hashie/pull/376): Leave string index unchanged if it can't be converted to integer for Array#dig - [@sazor](https://github.com/sazor).
+* [#377](https://github.com/hashie/hashie/pull/377): Dont use Rubygems to check ruby version - [@sazor](https://github.com/sazor).
+* [#378](https://github.com/hashie/hashie/pull/378): Deep find all searches inside all nested hashes - [@sazor](https://github.com/sazor).
+* [#380](https://github.com/hashie/hashie/pull/380): Evaluate procs default values of Dash in object initialization - [@sazor](https://github.com/sazor).
+* [#387](https://github.com/hashie/hashie/pull/387): Fixed builds failing due to Rake 11 having a breaking change - [@michaelherold](https://github.com/michaelherold).
 
 ## [3.4.6] - 2016-09-16
 
-[3.4.6]: https://github.com/intridea/hashie/compare/v3.4.5...v3.4.6
+[3.4.6]: https://github.com/hashie/hashie/compare/v3.4.5...v3.4.6
 
 ### Fixed
 
-* [#368](https://github.com/intridea/hashie/pull/368): Since `hashie/mash` can be required alone, require its dependencies - [@jrafanie](https://github.com/jrafanie).
+* [#368](https://github.com/hashie/hashie/pull/368): Since `hashie/mash` can be required alone, require its dependencies - [@jrafanie](https://github.com/jrafanie).
 
 ## [3.4.5] - 2016-09-16
 
-[3.4.5]: https://github.com/intridea/hashie/compare/v3.4.4...v3.4.5
+[3.4.5]: https://github.com/hashie/hashie/compare/v3.4.4...v3.4.5
 
 ### Added
 
-* [#337](https://github.com/intridea/hashie/pull/337), [#331](https://github.com/intridea/hashie/issues/331): `Hashie::Mash#load` accepts a `Pathname` object - [@gipcompany](https://github.com/gipcompany).
+* [#337](https://github.com/hashie/hashie/pull/337), [#331](https://github.com/hashie/hashie/issues/331): `Hashie::Mash#load` accepts a `Pathname` object - [@gipcompany](https://github.com/gipcompany).
+* [#366](https://github.com/hashie/hashie/pull/366): Added Danger, PR linter - [@dblock](https://github.com/dblock).
 
 ### Deprecated
 
-* [#366](https://github.com/intridea/hashie/pull/366): Hashie is no longer tested on Ruby < 2 - [@dblock](https://github.com/dblock).
+* [#366](https://github.com/hashie/hashie/pull/366): Hashie is no longer tested on Ruby < 2 - [@dblock](https://github.com/dblock).
 
 ### Fixed
 
-* [#358](https://github.com/intridea/hashie/pull/358): Fixed support for Array#dig - [@modosc](https://github.com/modosc).
-* [#365](https://github.com/intridea/hashie/pull/365): Ensured ActiveSupport::HashWithIndifferentAccess is defined before use in #deep_locate  - [@mikejarema](https://github.com/mikejarema).
-
-### Miscellanous
-
-* [#366](https://github.com/intridea/hashie/pull/366): Added Danger, PR linter - [@dblock](https://github.com/dblock).
+* [#358](https://github.com/hashie/hashie/pull/358): Fixed support for Array#dig - [@modosc](https://github.com/modosc).
+* [#365](https://github.com/hashie/hashie/pull/365): Ensured ActiveSupport::HashWithIndifferentAccess is defined before use in #deep_locate  - [@mikejarema](https://github.com/mikejarema).
 
 ## [3.4.4] - 2016-04-29
 
-[3.4.4]: https://github.com/intridea/hashie/compare/v3.4.3...v3.4.4
+[3.4.4]: https://github.com/hashie/hashie/compare/v3.4.3...v3.4.4
 
 ### Added
 
-* [#349](https://github.com/intridea/hashie/pull/349): Convert `Hashie::Mash#dig` arguments for Ruby 2.3.0 - [@k0kubun](https://github.com/k0kubun).
+* [#349](https://github.com/hashie/hashie/pull/349): Convert `Hashie::Mash#dig` arguments for Ruby 2.3.0 - [@k0kubun](https://github.com/k0kubun).
 
 ### Fixed
 
-* [#240](https://github.com/intridea/hashie/pull/240): Fixed nesting twice with Clash keys - [@bartoszkopinski](https://github.com/bartoszkopinski).
-* [#317](https://github.com/intridea/hashie/pull/317): Ensured `Hashie::Extensions::MethodQuery` methods return boolean values - [@michaelherold](https://github.com/michaelherold).
-* [#319](https://github.com/intridea/hashie/pull/319): Fixed a regression from 3.4.1 where `Hashie::Extensions::DeepFind` is no longer indifference-aware - [@michaelherold](https://github.com/michaelherold).
-* [#322](https://github.com/intridea/hashie/pull/322): Fixed `reverse_merge` issue with `Mash` subclasses - [@marshall-lee](https://github.com/marshall-lee).
-* [#346](https://github.com/intridea/hashie/pull/346): Fixed `merge` breaking indifferent access - [@docwhat](https://github.com/docwhat), [@michaelherold](https://github.com/michaelherold).
-* [#350](https://github.com/intridea/hashie/pull/350): Fixed from string translations used with `IgnoreUndeclared` - [@marshall-lee](https://github.com/marshall-lee).
+* [#240](https://github.com/hashie/hashie/pull/240): Fixed nesting twice with Clash keys - [@bartoszkopinski](https://github.com/bartoszkopinski).
+* [#317](https://github.com/hashie/hashie/pull/317): Ensured `Hashie::Extensions::MethodQuery` methods return boolean values - [@michaelherold](https://github.com/michaelherold).
+* [#319](https://github.com/hashie/hashie/pull/319): Fixed a regression from 3.4.1 where `Hashie::Extensions::DeepFind` is no longer indifference-aware - [@michaelherold](https://github.com/michaelherold).
+* [#322](https://github.com/hashie/hashie/pull/322): Fixed `reverse_merge` issue with `Mash` subclasses - [@marshall-lee](https://github.com/marshall-lee).
+* [#346](https://github.com/hashie/hashie/pull/346): Fixed `merge` breaking indifferent access - [@docwhat](https://github.com/docwhat), [@michaelherold](https://github.com/michaelherold).
+* [#350](https://github.com/hashie/hashie/pull/350): Fixed from string translations used with `IgnoreUndeclared` - [@marshall-lee](https://github.com/marshall-lee).
 
 ## [3.4.3] - 2015-10-25
 
-[3.4.3]: https://github.com/intridea/hashie/compare/v3.4.2...v3.4.3
+[3.4.3]: https://github.com/hashie/hashie/compare/v3.4.2...v3.4.3
 
 ### Added
 
-* [#306](https://github.com/intridea/hashie/pull/306): Added `Hashie::Extensions::Dash::Coercion` - [@marshall-lee](https://github.com/marshall-lee).
-* [#314](https://github.com/intridea/hashie/pull/314): Added a `StrictKeyAccess` extension that will raise an error whenever a key is accessed that does not exist in the hash - [@pboling](https://github.com/pboling).
+* [#306](https://github.com/hashie/hashie/pull/306): Added `Hashie::Extensions::Dash::Coercion` - [@marshall-lee](https://github.com/marshall-lee).
+* [#314](https://github.com/hashie/hashie/pull/314): Added a `StrictKeyAccess` extension that will raise an error whenever a key is accessed that does not exist in the hash - [@pboling](https://github.com/pboling).
 
 ### Fixed
 
-* [#304](https://github.com/intridea/hashie/pull/304): Ensured compatibility of `Hash` extensions with singleton objects - [@regexident](https://github.com/regexident).
-* [#310](https://github.com/intridea/hashie/pull/310): Fixed `Hashie::Extensions::SafeAssignment` bug with private methods - [@marshall-lee](https://github.com/marshall-lee).
+* [#304](https://github.com/hashie/hashie/pull/304): Ensured compatibility of `Hash` extensions with singleton objects - [@regexident](https://github.com/regexident).
+* [#310](https://github.com/hashie/hashie/pull/310): Fixed `Hashie::Extensions::SafeAssignment` bug with private methods - [@marshall-lee](https://github.com/marshall-lee).
 
-### Miscellaneous
+### Changed
 
-* [#313](https://github.com/intridea/hashie/pull/313): Restrict pending spec to only Ruby versions 2.2.0-2.2.2 - [@pboling](https://github.com/pboling).
-* [#315](https://github.com/intridea/hashie/pull/315): Default `bin/` scripts: `console` and `setup` - [@pboling](https://github.com/pboling).
+* [#313](https://github.com/hashie/hashie/pull/313): Restrict pending spec to only Ruby versions 2.2.0-2.2.2 - [@pboling](https://github.com/pboling).
+* [#315](https://github.com/hashie/hashie/pull/315): Default `bin/` scripts: `console` and `setup` - [@pboling](https://github.com/pboling).
 
 ## [3.4.2] - 2015-06-02
 
-[3.4.2]: https://github.com/intridea/hashie/compare/v3.4.1...v3.4.2
+[3.4.2]: https://github.com/hashie/hashie/compare/v3.4.1...v3.4.2
 
 ### Added
 
-* [#297](https://github.com/intridea/hashie/pull/297): Extracted `Trash`'s behavior into a new `Dash::PropertyTranslation` extension - [@michaelherold](https://github.com/michaelherold).
+* [#297](https://github.com/hashie/hashie/pull/297): Extracted `Trash`'s behavior into a new `Dash::PropertyTranslation` extension - [@michaelherold](https://github.com/michaelherold).
 
 ### Removed
 
-* [#292](https://github.com/intridea/hashie/pull/292): Removed `Mash#id` and `Mash#type` - [@jrochkind](https://github.com/jrochkind).
+* [#292](https://github.com/hashie/hashie/pull/292): Removed `Mash#id` and `Mash#type` - [@jrochkind](https://github.com/jrochkind).
 
 ## [3.4.1] - 2015-03-31
 
-[3.4.1]: https://github.com/intridea/hashie/compare/v3.4.0...v3.4.1
+[3.4.1]: https://github.com/hashie/hashie/compare/v3.4.0...v3.4.1
 
 ### Added
 
-* [#269](https://github.com/intridea/hashie/pull/272): Added Hashie::Extensions::DeepLocate - [@msievers](https://github.com/msievers).
-* [#281](https://github.com/intridea/hashie/pull/281): Added #reverse_merge to Mash to override ActiveSupport's version - [@mgold](https://github.com/mgold).
+* [#269](https://github.com/hashie/hashie/pull/272): Added Hashie::Extensions::DeepLocate - [@msievers](https://github.com/msievers).
+* [#281](https://github.com/hashie/hashie/pull/281): Added #reverse_merge to Mash to override ActiveSupport's version - [@mgold](https://github.com/mgold).
 
 ### Fixed
 
-* [#270](https://github.com/intridea/hashie/pull/277): Fixed ArgumentError raised when using IndifferentAccess and HashWithIndifferentAccess - [@gardenofwine](https://github.com/gardenofwine).
-* [#282](https://github.com/intridea/hashie/pull/282): Fixed coercions in a subclass accumulating in the superclass - [@maxlinc](https://github.com/maxlinc), [@martinstreicher](https://github.com/martinstreicher).
+* [#270](https://github.com/hashie/hashie/pull/277): Fixed ArgumentError raised when using IndifferentAccess and HashWithIndifferentAccess - [@gardenofwine](https://github.com/gardenofwine).
+* [#282](https://github.com/hashie/hashie/pull/282): Fixed coercions in a subclass accumulating in the superclass - [@maxlinc](https://github.com/maxlinc), [@martinstreicher](https://github.com/martinstreicher).
 
 ## [3.4.0] - 2015-02-02
 
-[3.4.0]: https://github.com/intridea/hashie/compare/v3.3.2...v3.4.0
+[3.4.0]: https://github.com/hashie/hashie/compare/v3.3.2...v3.4.0
 
 ### Added
 
-* [#251](https://github.com/intridea/hashie/pull/251): Added block support to indifferent access #fetch - [@jgraichen](https://github.com/jgraichen).
-* [#252](https://github.com/intridea/hashie/pull/252): Added support for conditionally required Hashie::Dash attributes - [@ccashwell](https://github.com/ccashwell).
-* [#254](https://github.com/intridea/hashie/pull/254): Added public utility methods for stringify and symbolize keys - [@maxlinc](https://github.com/maxlinc).
-* [#260](https://github.com/intridea/hashie/pull/260): Added block support to Extensions::DeepMerge - [@galathius](https://github.com/galathius).
-* [#271](https://github.com/intridea/hashie/pull/271): Added ability to define defaults based on current hash - [@gregory](https://github.com/gregory).
+* [#251](https://github.com/hashie/hashie/pull/251): Added block support to indifferent access #fetch - [@jgraichen](https://github.com/jgraichen).
+* [#252](https://github.com/hashie/hashie/pull/252): Added support for conditionally required Hashie::Dash attributes - [@ccashwell](https://github.com/ccashwell).
+* [#254](https://github.com/hashie/hashie/pull/254): Added public utility methods for stringify and symbolize keys - [@maxlinc](https://github.com/maxlinc).
+* [#260](https://github.com/hashie/hashie/pull/260): Added block support to Extensions::DeepMerge - [@galathius](https://github.com/galathius).
+* [#271](https://github.com/hashie/hashie/pull/271): Added ability to define defaults based on current hash - [@gregory](https://github.com/gregory).
 
 ### Changed
 
-* [#249](https://github.com/intridea/hashie/pull/249): SafeAssignment will now also protect hash-style assignments - [@jrochkind](https://github.com/jrochkind).
-* [#264](https://github.com/intridea/hashie/pull/264): Methods such as abc? return true/false with Hashie::Extensions::MethodReader - [@Zloy](https://github.com/Zloy).
+* [#249](https://github.com/hashie/hashie/pull/249): SafeAssignment will now also protect hash-style assignments - [@jrochkind](https://github.com/jrochkind).
+* [#264](https://github.com/hashie/hashie/pull/264): Methods such as abc? return true/false with Hashie::Extensions::MethodReader - [@Zloy](https://github.com/Zloy).
 
 ### Fixed
 
-* [#247](https://github.com/intridea/hashie/pull/247): Fixed #stringify_keys and #symbolize_keys collision with ActiveSupport - [@bartoszkopinski](https://github.com/bartoszkopinski).
-* [#256](https://github.com/intridea/hashie/pull/256): Inherited key coercions - [@Erol](https://github.com/Erol).
-* [#259](https://github.com/intridea/hashie/pull/259): Fixed handling of default proc values in Mash - [@Erol](https://github.com/Erol).
-* [#261](https://github.com/intridea/hashie/pull/261): Fixed bug where Dash.property modifies argument object - [@d-tw](https://github.com/d-tw).
-* [#269](https://github.com/intridea/hashie/pull/269): Added #extractable_options? so ActiveSupport Array#extract_options! can extract it - [@ridiculous](https://github.com/ridiculous).
+* [#247](https://github.com/hashie/hashie/pull/247): Fixed #stringify_keys and #symbolize_keys collision with ActiveSupport - [@bartoszkopinski](https://github.com/bartoszkopinski).
+* [#256](https://github.com/hashie/hashie/pull/256): Inherited key coercions - [@Erol](https://github.com/Erol).
+* [#259](https://github.com/hashie/hashie/pull/259): Fixed handling of default proc values in Mash - [@Erol](https://github.com/Erol).
+* [#261](https://github.com/hashie/hashie/pull/261): Fixed bug where Dash.property modifies argument object - [@d-tw](https://github.com/d-tw).
+* [#269](https://github.com/hashie/hashie/pull/269): Added #extractable_options? so ActiveSupport Array#extract_options! can extract it - [@ridiculous](https://github.com/ridiculous).
 
 ## [3.3.2] - 2014-11-26
 
-[3.3.2]: https://github.com/intridea/hashie/compare/v3.3.1...v3.3.2
+[3.3.2]: https://github.com/hashie/hashie/compare/v3.3.1...v3.3.2
 
 ### Added
 
-* [#231](https://github.com/intridea/hashie/pull/231): Added support for coercion on class type that inherit from Hash - [@gregory](https://github.com/gregory).
-* [#233](https://github.com/intridea/hashie/pull/233): Custom error messages for required properties in Hashie::Dash subclasses - [@joss](https://github.com/joss).
-* [#245](https://github.com/intridea/hashie/pull/245): Added Hashie::Extensions::MethodAccessWithOverride to autoloads - [@Fritzinger](https://github.com/Fritzinger).
+* [#231](https://github.com/hashie/hashie/pull/231): Added support for coercion on class type that inherit from Hash - [@gregory](https://github.com/gregory).
+* [#233](https://github.com/hashie/hashie/pull/233): Custom error messages for required properties in Hashie::Dash subclasses - [@joss](https://github.com/joss).
+* [#245](https://github.com/hashie/hashie/pull/245): Added Hashie::Extensions::MethodAccessWithOverride to autoloads - [@Fritzinger](https://github.com/Fritzinger).
 
 ### Fixed
 
-* [#221](https://github.com/intridea/hashie/pull/221): Reduced amount of allocated objects on calls with suffixes in Hashie::Mash - [@kubum](https://github.com/kubum).
-* [#224](https://github.com/intridea/hashie/pull/224): Merging Hashie::Mash now correctly only calls the block on duplicate values - [@amysutedja](https://github.com/amysutedja).
-* [#228](https://github.com/intridea/hashie/pull/228): Made Hashie::Extensions::Parsers::YamlErbParser pass template filename to ERB - [@jperville](https://github.com/jperville).
+* [#221](https://github.com/hashie/hashie/pull/221): Reduced amount of allocated objects on calls with suffixes in Hashie::Mash - [@kubum](https://github.com/kubum).
+* [#224](https://github.com/hashie/hashie/pull/224): Merging Hashie::Mash now correctly only calls the block on duplicate values - [@amysutedja](https://github.com/amysutedja).
+* [#228](https://github.com/hashie/hashie/pull/228): Made Hashie::Extensions::Parsers::YamlErbParser pass template filename to ERB - [@jperville](https://github.com/jperville).
 
 ## [3.3.1] - 2014-08-26
 
-[3.3.1]: https://github.com/intridea/hashie/compare/v3.3.0...v3.3.1
+[3.3.1]: https://github.com/hashie/hashie/compare/v3.3.0...v3.3.1
 
 ### Added
 
-* [#183](https://github.com/intridea/hashie/pull/183): Added Mash#load with YAML file support - [@gregory](https://github.com/gregory).
-* [#189](https://github.com/intridea/hashie/pull/189): Added Rash#fetch - [@medcat](https://github.com/medcat).
-* [#204](https://github.com/intridea/hashie/pull/204): Added Hashie::Extensions::MethodOverridingWriter and MethodAccessWithOverride - [@michaelherold](https://github.com/michaelherold).
-* [#205](https://github.com/intridea/hashie/pull/205): Added Hashie::Extensions::Mash::SafeAssignment - [@michaelherold](https://github.com/michaelherold).
-* [#209](https://github.com/intridea/hashie/pull/209): Added Hashie::Extensions::DeepFind - [@michaelherold](https://github.com/michaelherold).
+* [#183](https://github.com/hashie/hashie/pull/183): Added Mash#load with YAML file support - [@gregory](https://github.com/gregory).
+* [#189](https://github.com/hashie/hashie/pull/189): Added Rash#fetch - [@medcat](https://github.com/medcat).
+* [#204](https://github.com/hashie/hashie/pull/204): Added Hashie::Extensions::MethodOverridingWriter and MethodAccessWithOverride - [@michaelherold](https://github.com/michaelherold).
+* [#205](https://github.com/hashie/hashie/pull/205): Added Hashie::Extensions::Mash::SafeAssignment - [@michaelherold](https://github.com/michaelherold).
+* [#209](https://github.com/hashie/hashie/pull/209): Added Hashie::Extensions::DeepFind - [@michaelherold](https://github.com/michaelherold).
 
 ### Fixed
 
-* [#69](https://github.com/intridea/hashie/pull/69): Fixed regression in assigning multiple properties in Hashie::Trash - [@michaelherold](https://github.com/michaelherold), [@einzige](https://github.com/einzige), [@dblock](https://github.com/dblock).
-* [#195](https://github.com/intridea/hashie/pull/195): Ensured that the same object is returned after injecting IndifferentAccess - [@michaelherold](https://github.com/michaelherold).
-* [#201](https://github.com/intridea/hashie/pull/201): Hashie::Trash transforms can be inherited - [@fobocaster](https://github.com/fobocaster).
-* [#200](https://github.com/intridea/hashie/pull/200): Improved coercion: primitives and error handling - [@maxlinc](https://github.com/maxlinc).
-* [#206](https://github.com/intridea/hashie/pull/206): Fixed stack overflow from repetitively including coercion in subclasses - [@michaelherold](https://github.com/michaelherold).
-* [#207](https://github.com/intridea/hashie/pull/207): Fixed inheritance of transformations in Trash - [@fobocaster](https://github.com/fobocaster).
+* [#69](https://github.com/hashie/hashie/pull/69): Fixed regression in assigning multiple properties in Hashie::Trash - [@michaelherold](https://github.com/michaelherold), [@einzige](https://github.com/einzige), [@dblock](https://github.com/dblock).
+* [#195](https://github.com/hashie/hashie/pull/195): Ensured that the same object is returned after injecting IndifferentAccess - [@michaelherold](https://github.com/michaelherold).
+* [#201](https://github.com/hashie/hashie/pull/201): Hashie::Trash transforms can be inherited - [@fobocaster](https://github.com/fobocaster).
+* [#200](https://github.com/hashie/hashie/pull/200): Improved coercion: primitives and error handling - [@maxlinc](https://github.com/maxlinc).
+* [#206](https://github.com/hashie/hashie/pull/206): Fixed stack overflow from repetitively including coercion in subclasses - [@michaelherold](https://github.com/michaelherold).
+* [#207](https://github.com/hashie/hashie/pull/207): Fixed inheritance of transformations in Trash - [@fobocaster](https://github.com/fobocaster).
 
 ## [3.2.0] - 2014-07-10
 
-[3.2.0]: https://github.com/intridea/hashie/compare/v3.1.0...v3.2.0
+[3.2.0]: https://github.com/hashie/hashie/compare/v3.1.0...v3.2.0
 
 ### Added
 
-* [#177](https://github.com/intridea/hashie/pull/177): Added support for coercing enumerables and collections - [@gregory](https://github.com/gregory).
+* [#177](https://github.com/hashie/hashie/pull/177): Added support for coercing enumerables and collections - [@gregory](https://github.com/gregory).
 
 ### Changed
 
-* [#179](https://github.com/intridea/hashie/pull/179): Mash#values_at will convert each key before doing the lookup - [@nahiluhmot](https://github.com/nahiluhmot).
-* [#184](https://github.com/intridea/hashie/pull/184): Allow ranges on Rash to match all Numeric types - [@medcat](https://github.com/medcat).
+* [#179](https://github.com/hashie/hashie/pull/179): Mash#values_at will convert each key before doing the lookup - [@nahiluhmot](https://github.com/nahiluhmot).
+* [#184](https://github.com/hashie/hashie/pull/184): Allow ranges on Rash to match all Numeric types - [@medcat](https://github.com/medcat).
 
 ### Fixed
 
-* [#164](https://github.com/intridea/hashie/pull/164), [#165](https://github.com/intridea/hashie/pull/165), [#166](https://github.com/intridea/hashie/pull/166): Fixed stack overflow when coercing mashes that contain ActiveSupport::HashWithIndifferentAccess values - [@numinit](https://github.com/numinit), [@kgrz](https://github.com/kgrz).
-* [#187](https://github.com/intridea/hashie/pull/187): Automatically require version - [@medcat](https://github.com/medcat).
-* [#190](https://github.com/intridea/hashie/issues/190): Fixed `coerce_key` with `from` Trash feature and Coercion extension - [@gregory](https://github.com/gregory).
-* [#192](https://github.com/intridea/hashie/pull/192): Fixed StringifyKeys#stringify_keys! to recursively stringify keys of embedded ::Hash types - [@dblock](https://github.com/dblock).
+* [#164](https://github.com/hashie/hashie/pull/164), [#165](https://github.com/hashie/hashie/pull/165), [#166](https://github.com/hashie/hashie/pull/166): Fixed stack overflow when coercing mashes that contain ActiveSupport::HashWithIndifferentAccess values - [@numinit](https://github.com/numinit), [@kgrz](https://github.com/kgrz).
+* [#187](https://github.com/hashie/hashie/pull/187): Automatically require version - [@medcat](https://github.com/medcat).
+* [#190](https://github.com/hashie/hashie/issues/190): Fixed `coerce_key` with `from` Trash feature and Coercion extension - [@gregory](https://github.com/gregory).
+* [#192](https://github.com/hashie/hashie/pull/192): Fixed StringifyKeys#stringify_keys! to recursively stringify keys of embedded ::Hash types - [@dblock](https://github.com/dblock).
 
 ## [3.1.0] - 2014-06-25
 
-[3.1.0]: https://github.com/intridea/hashie/compare/v3.0.0...v3.1.0
+[3.1.0]: https://github.com/hashie/hashie/compare/v3.0.0...v3.1.0
 
 ### Added
 
-* [#172](https://github.com/intridea/hashie/pull/172): Added Dash and Trash#update_attributes! - [@gregory](https://github.com/gregory).
+* [#172](https://github.com/hashie/hashie/pull/172): Added Dash and Trash#update_attributes! - [@gregory](https://github.com/gregory).
 
 ### Changed
 
-* [#169](https://github.com/intridea/hashie/pull/169): Hash#to_hash will also convert nested objects that implement to_hash - [@gregory](https://github.com/gregory).
-* [#173](https://github.com/intridea/hashie/pull/173): Auto include Dash::IndifferentAccess when IndifferentAccess is included in Dash - [@gregory](https://github.com/gregory).
+* [#169](https://github.com/hashie/hashie/pull/169): Hash#to_hash will also convert nested objects that implement to_hash - [@gregory](https://github.com/gregory).
+* [#173](https://github.com/hashie/hashie/pull/173): Auto include Dash::IndifferentAccess when IndifferentAccess is included in Dash - [@gregory](https://github.com/gregory).
 
 ### Fixed
 
-* [#171](https://github.com/intridea/hashie/pull/171): Include Trash and Dash class name when raising `NoMethodError` - [@gregory](https://github.com/gregory).
-* [#174](https://github.com/intridea/hashie/pull/174): Fixed `from` and `transform_with` Trash features when IndifferentAccess is included - [@gregory](https://github.com/gregory).
+* [#171](https://github.com/hashie/hashie/pull/171): Include Trash and Dash class name when raising `NoMethodError` - [@gregory](https://github.com/gregory).
+* [#174](https://github.com/hashie/hashie/pull/174): Fixed `from` and `transform_with` Trash features when IndifferentAccess is included - [@gregory](https://github.com/gregory).
 
 ## [3.0.0] - 2014-06-03
 
-[3.0.0]: https://github.com/intridea/hashie/compare/v2.1.2...v3.0.0
-
-Note: This version introduces several backward incompatible API changes. See [UPGRADING](UPGRADING.md) for details.
+[3.0.0]: https://github.com/hashie/hashie/compare/v2.1.2...v3.0.0
 
 ### Added
 
-* [#149](https://github.com/intridea/hashie/issues/149): Allow IgnoreUndeclared and DeepMerge to be used with undeclared properties - [@jhaesus](https://github.com/jhaesus).
+* [#149](https://github.com/hashie/hashie/issues/149): Allow IgnoreUndeclared and DeepMerge to be used with undeclared properties - [@jhaesus](https://github.com/jhaesus).
 
 ### Changed
 
-* [#152](https://github.com/intridea/hashie/pull/152): Do not convert keys to String in Hashie::Dash and Hashie::Trash, use Hashie::Extensions::Dash::IndifferentAccess to achieve backward compatible behavior - [@dblock](https://github.com/dblock).
-* [#152](https://github.com/intridea/hashie/pull/152): Do not automatically stringify keys in Hashie::Hash#to_hash, pass `:stringify_keys` to achieve backward compatible behavior - [@dblock](https://github.com/dblock).
+* [#152](https://github.com/hashie/hashie/pull/152): Do not convert keys to String in Hashie::Dash and Hashie::Trash, use Hashie::Extensions::Dash::IndifferentAccess to achieve backward compatible behavior - [@dblock](https://github.com/dblock).
+* [#152](https://github.com/hashie/hashie/pull/152): Do not automatically stringify keys in Hashie::Hash#to_hash, pass `:stringify_keys` to achieve backward compatible behavior - [@dblock](https://github.com/dblock).
 
 ### Fixed
 
-* [#146](https://github.com/intridea/hashie/issues/146): Mash#respond_to? inconsistent with #method_missing and does not respond to #permitted? - [@dblock](https://github.com/dblock).
-* [#148](https://github.com/intridea/hashie/pull/148): Consolidated Hashie::Hash#stringify_keys implementation - [@dblock](https://github.com/dblock).
-* [#159](https://github.com/intridea/hashie/pull/159): Handle nil intermediate object on deep fetch - [@stephenaument](https://github.com/stephenaument).
+* [#146](https://github.com/hashie/hashie/issues/146): Mash#respond_to? inconsistent with #method_missing and does not respond to #permitted? - [@dblock](https://github.com/dblock).
+* [#148](https://github.com/hashie/hashie/pull/148): Consolidated Hashie::Hash#stringify_keys implementation - [@dblock](https://github.com/dblock).
+* [#159](https://github.com/hashie/hashie/pull/159): Handle nil intermediate object on deep fetch - [@stephenaument](https://github.com/stephenaument).
 
 ## [2.1.2] - 2014-05-12
 
-[2.1.2]: https://github.com/intridea/hashie/compare/v2.1.1...v2.1.2
+[2.1.2]: https://github.com/hashie/hashie/compare/v2.1.1...v2.1.2
 
 ### Changed
 
-* [#169](https://github.com/intridea/hashie/pull/169): Hash#to_hash will also convert nested objects that implement `to_hash` - [@gregory](https://github.com/gregory).
+* [#169](https://github.com/hashie/hashie/pull/169): Hash#to_hash will also convert nested objects that implement `to_hash` - [@gregory](https://github.com/gregory).
 
 ## [2.1.1] - 2014-04-12
 
-[2.1.1]: https://github.com/intridea/hashie/compare/v2.1.0...v2.1.1
+[2.1.1]: https://github.com/hashie/hashie/compare/v2.1.0...v2.1.1
 
 ### Fixed
 
-* [#131](https://github.com/intridea/hashie/pull/131): Added IgnoreUndeclared, an extension to silently ignore undeclared properties at intialization - [@righi](https://github.com/righi).
-* [#138](https://github.com/intridea/hashie/pull/138): Added Hashie::Rash, a hash whose keys can be regular expressions or ranges - [@epitron](https://github.com/epitron).
-* [#144](https://github.com/intridea/hashie/issues/144): Fixed regression invoking `to_hash` with no parameters - [@mbleigh](https://github.com/mbleigh).
+* [#131](https://github.com/hashie/hashie/pull/131): Added IgnoreUndeclared, an extension to silently ignore undeclared properties at intialization - [@righi](https://github.com/righi).
+* [#138](https://github.com/hashie/hashie/pull/138): Added Hashie::Rash, a hash whose keys can be regular expressions or ranges - [@epitron](https://github.com/epitron).
+* [#144](https://github.com/hashie/hashie/issues/144): Fixed regression invoking `to_hash` with no parameters - [@mbleigh](https://github.com/mbleigh).
 
 ## [2.1.0] - 2014-04-06
 
-[2.1.0]: https://github.com/intridea/hashie/compare/v2.0.5...v2.1.0
+[2.1.0]: https://github.com/hashie/hashie/compare/v2.0.5...v2.1.0
 
 ### Added
 
-* [#134](https://github.com/intridea/hashie/pull/134): Added deep_fetch extension for nested access - [@tylerdooling](https://github.com/tylerdooling).
+* [#134](https://github.com/hashie/hashie/pull/134): Added deep_fetch extension for nested access - [@tylerdooling](https://github.com/tylerdooling).
 
 ### Changed
 
-* [#89](https://github.com/intridea/hashie/issues/89): Do not respond to every method with suffix in Hashie::Mash, fixes Rails strong_parameters - [@Maxim-Filimonov](https://github.com/Maxim-Filimonov).
+* [#89](https://github.com/hashie/hashie/issues/89): Do not respond to every method with suffix in Hashie::Mash, fixes Rails strong_parameters - [@Maxim-Filimonov](https://github.com/Maxim-Filimonov).
+* Ruby style now enforced with Rubocop - [@dblock](https://github.com/dblock).
 
 ### Removed
 
 * Removed support for Ruby 1.8.7 - [@dblock](https://github.com/dblock).
-* [#136](https://github.com/intridea/hashie/issues/136): Removed Hashie::Extensions::Structure - [@markiz](https://github.com/markiz).
+* [#136](https://github.com/hashie/hashie/issues/136): Removed Hashie::Extensions::Structure - [@markiz](https://github.com/markiz).
 
 ### Fixed
 
-* [#69](https://github.com/intridea/hashie/issues/69): Fixed assigning multiple properties in Hashie::Trash - [@einzige](https://github.com/einzige).
-* [#99](https://github.com/intridea/hashie/issues/99): Hash#deep_merge raises errors when it encounters integers - [@defsprite](https://github.com/defsprite).
-* [#100](https://github.com/intridea/hashie/pull/100): IndifferentAccess#store will respect indifference - [@jrochkind](https://github.com/jrochkind).
-* [#103](https://github.com/intridea/hashie/pull/103): Fixed support for Hashie::Dash properties that end in bang - [@thedavemarshall](https://github.com/thedavemarshall).
-* [#107](https://github.com/intridea/hashie/pull/107): Fixed excessive value conversions, poor performance of deep merge in Hashie::Mash - [@davemitchell](https://github.com/dblock), [@dblock](https://github.com/dblock).
-* [#110](https://github.com/intridea/hashie/pull/110): Correctly use Hash#default from Mash#method_missing - [@ryansouza](https://github.com/ryansouza).
-* [#111](https://github.com/intridea/hashie/issues/111): Trash#translations correctly maps original to translated names - [@artm](https://github.com/artm).
-* [#113](https://github.com/intridea/hashie/issues/113): Fixed Hash#merge with Hashie::Dash - [@spencer1248](https://github.com/spencer1248).
-* [#120](https://github.com/intridea/hashie/pull/120): Pass options to recursive to_hash calls - [@pwillett](https://github.com/pwillett).
-* [#129](https://github.com/intridea/hashie/pull/129): Added Trash#permitted_input_keys and inverse_translations - [@artm](https://github.com/artm).
-* [#130](https://github.com/intridea/hashie/pull/130): IndifferentAccess now works without MergeInitializer - [@npj](https://github.com/npj).
-* [#133](https://github.com/intridea/hashie/pull/133): Fixed Hash##to_hash with symbolize_keys - [@mhuggins](https://github.com/mhuggins).
-
-### Miscellaneous
-
-* Ruby style now enforced with Rubocop - [@dblock](https://github.com/dblock).
+* [#69](https://github.com/hashie/hashie/issues/69): Fixed assigning multiple properties in Hashie::Trash - [@einzige](https://github.com/einzige).
+* [#99](https://github.com/hashie/hashie/issues/99): Hash#deep_merge raises errors when it encounters integers - [@defsprite](https://github.com/defsprite).
+* [#100](https://github.com/hashie/hashie/pull/100): IndifferentAccess#store will respect indifference - [@jrochkind](https://github.com/jrochkind).
+* [#103](https://github.com/hashie/hashie/pull/103): Fixed support for Hashie::Dash properties that end in bang - [@thedavemarshall](https://github.com/thedavemarshall).
+* [#107](https://github.com/hashie/hashie/pull/107): Fixed excessive value conversions, poor performance of deep merge in Hashie::Mash - [@davemitchell](https://github.com/dblock), [@dblock](https://github.com/dblock).
+* [#110](https://github.com/hashie/hashie/pull/110): Correctly use Hash#default from Mash#method_missing - [@ryansouza](https://github.com/ryansouza).
+* [#111](https://github.com/hashie/hashie/issues/111): Trash#translations correctly maps original to translated names - [@artm](https://github.com/artm).
+* [#113](https://github.com/hashie/hashie/issues/113): Fixed Hash#merge with Hashie::Dash - [@spencer1248](https://github.com/spencer1248).
+* [#120](https://github.com/hashie/hashie/pull/120): Pass options to recursive to_hash calls - [@pwillett](https://github.com/pwillett).
+* [#129](https://github.com/hashie/hashie/pull/129): Added Trash#permitted_input_keys and inverse_translations - [@artm](https://github.com/artm).
+* [#130](https://github.com/hashie/hashie/pull/130): IndifferentAccess now works without MergeInitializer - [@npj](https://github.com/npj).
+* [#133](https://github.com/hashie/hashie/pull/133): Fixed Hash##to_hash with symbolize_keys - [@mhuggins](https://github.com/mhuggins).
 
 ## [2.0.5] - 2013-05-10
 
-[2.0.5]: https://github.com/intridea/hashie/compare/v2.0.4...v2.0.5
+[2.0.5]: https://github.com/hashie/hashie/compare/v2.0.4...v2.0.5
 
 ### Fixed
 
-* [#96](https://github.com/intridea/hashie/pull/96): Made coercion work better with non-symbol keys in Hashie::Mash - [@wapcaplet](https://github.com/wapcaplet).
+* [#96](https://github.com/hashie/hashie/pull/96): Made coercion work better with non-symbol keys in Hashie::Mash - [@wapcaplet](https://github.com/wapcaplet).
 
 ## [2.0.4] - 2013-04-24
 
-[2.0.4]: https://github.com/intridea/hashie/compare/v2.0.3...v2.0.4
+[2.0.4]: https://github.com/hashie/hashie/compare/v2.0.3...v2.0.4
 
 ### Fixed
 
-* [#94](https://github.com/intridea/hashie/pull/94): Made #fetch method consistent with normal Hash - [@markiz](https://github.com/markiz).
+* [#94](https://github.com/hashie/hashie/pull/94): Made #fetch method consistent with normal Hash - [@markiz](https://github.com/markiz).
 
-### Miscellaneous
+### Changed
 
-* [#90](https://github.com/intridea/hashie/pull/90): Various doc tweaks - [@craiglittle](https://github.com/craiglittle).
+* [#90](https://github.com/hashie/hashie/pull/90): Various doc tweaks - [@craiglittle](https://github.com/craiglittle).
 
 ## [2.0.3] - 2013-03-18
 
-[2.0.3]: https://github.com/intridea/hashie/compare/v2.0.2...v2.0.3
+[2.0.3]: https://github.com/hashie/hashie/compare/v2.0.2...v2.0.3
 
 ### Fixed
 
-* [#68](https://github.com/intridea/hashie/pull/68): Fixed #replace - [@jimeh](https://github.com/jimeh).
-* [#88](https://github.com/intridea/hashie/pull/88): Hashie::Mash.new(abc: true).respond_to?(:abc?) works - [@7even](https://github.com/7even).
+* [#68](https://github.com/hashie/hashie/pull/68): Fixed #replace - [@jimeh](https://github.com/jimeh).
+* [#88](https://github.com/hashie/hashie/pull/88): Hashie::Mash.new(abc: true).respond_to?(:abc?) works - [@7even](https://github.com/7even).
 
 ## [2.0.2] - 2013-02-26
 
-[2.0.2]: https://github.com/intridea/hashie/compare/v2.0.1...v2.0.2
+[2.0.2]: https://github.com/hashie/hashie/compare/v2.0.1...v2.0.2
 
 ### Fixed
 
-* [#85](https://github.com/intridea/hashie/pull/85): Added symbolize_keys back to to_hash - [@cromulus](https://github.com/cromulus).
+* [#85](https://github.com/hashie/hashie/pull/85): Added symbolize_keys back to to_hash - [@cromulus](https://github.com/cromulus).
 
 ## [2.0.1] - 2013-02-26
 
-[2.0.1]: https://github.com/intridea/hashie/compare/v2.0.0...v2.0.1
+[2.0.1]: https://github.com/hashie/hashie/compare/v2.0.0...v2.0.1
 
 ### Removed
 
-* [#81](https://github.com/intridea/hashie/pull/81): Removed Mash#object_id override - [@matschaffer](https://github.com/matschaffer).
-
-### Miscellaneous
-
-* Gem cleanup: removed VERSION, Gemfile.lock - [@jch](https://github.com/jch), [@mbleigh](https://github.com/mbleigh).
+* [#81](https://github.com/hashie/hashie/pull/81): Removed Mash#object_id override - [@matschaffer](https://github.com/matschaffer).
+* Removed VERSION and Gemfile.lock - [@jch](https://github.com/jch), [@mbleigh](https://github.com/mbleigh).
 
 ## [2.0.0] - 2013-02-16
 
-[2.0.0]: https://github.com/intridea/hashie/compare/v1.2.0...v2.0.0
+[2.0.0]: https://github.com/hashie/hashie/compare/v1.2.0...v2.0.0
 
 ### Added
 
-* [#41](https://github.com/intridea/hashie/pull/41): DeepMerge extension - [@nashby](https://github.com/nashby).
-* [#78](https://github.com/intridea/hashie/pull/78): Merge and update accepts a block - [@jch](https://github.com/jch).
+* [#41](https://github.com/hashie/hashie/pull/41): DeepMerge extension - [@nashby](https://github.com/nashby).
+* [#78](https://github.com/hashie/hashie/pull/78): Merge and update accepts a block - [@jch](https://github.com/jch).
+* [#72](https://github.com/hashie/hashie/pull/72): Updated gemspec with license info - [@jordimassaguerpla](https://github.com/jordimassaguerpla).
 
 ### Changed
 
-* [#28](https://github.com/intridea/hashie/pull/28): Hashie::Extensions::Coercion coerce_keys takes arguments - [@mattfawcett](https://github.com/mattfawcett).
-* [#77](https://github.com/intridea/hashie/pull/77): Removed id, type, and object_id as special allowable keys - [@jch](https://github.com/jch).
+* [#28](https://github.com/hashie/hashie/pull/28): Hashie::Extensions::Coercion coerce_keys takes arguments - [@mattfawcett](https://github.com/mattfawcett).
+* [#77](https://github.com/hashie/hashie/pull/77): Removed id, type, and object_id as special allowable keys - [@jch](https://github.com/jch).
 
 ### Fixed
 
-* [#27](https://github.com/intridea/hashie/pull/27): Initialize with merge coerces values - [@mattfawcett](https://github.com/mattfawcett).
-* [#39](https://github.com/intridea/hashie/pull/39): Trash removes translated values on initialization - [@sleverbor](https://github.com/sleverbor).
-* [#49](https://github.com/intridea/hashie/pull/49): Hashie::Hash inherits from ::Hash to avoid ambiguity - [@meh](https://github.com/meh), [@orend](https://github.com/orend).
-* [#62](https://github.com/intridea/hashie/pull/62): Updated respond_to? method signature to match ruby core definition - [@dlupu](https://github.com/dlupu).
-* [#63](https://github.com/intridea/hashie/pull/63): Dash defaults are dup'ed before assigned - [@ohrite](https://github.com/ohrite).
-* [#66](https://github.com/intridea/hashie/pull/66): Mash#fetch works with symbol or string keys - [@arthwood](https://github.com/arthwood).
-
-### Miscellaneous
-
-* [#72](https://github.com/intridea/hashie/pull/72): Updated gemspec with license info - [@jordimassaguerpla](https://github.com/jordimassaguerpla).
+* [#27](https://github.com/hashie/hashie/pull/27): Initialize with merge coerces values - [@mattfawcett](https://github.com/mattfawcett).
+* [#39](https://github.com/hashie/hashie/pull/39): Trash removes translated values on initialization - [@sleverbor](https://github.com/sleverbor).
+* [#49](https://github.com/hashie/hashie/pull/49): Hashie::Hash inherits from ::Hash to avoid ambiguity - [@meh](https://github.com/meh), [@orend](https://github.com/orend).
+* [#62](https://github.com/hashie/hashie/pull/62): Updated respond_to? method signature to match ruby core definition - [@dlupu](https://github.com/dlupu).
+* [#63](https://github.com/hashie/hashie/pull/63): Dash defaults are dup'ed before assigned - [@ohrite](https://github.com/ohrite).
+* [#66](https://github.com/hashie/hashie/pull/66): Mash#fetch works with symbol or string keys - [@arthwood](https://github.com/arthwood).
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 060d5b78aa1d37ed324f5d7a4048fb67fde81357..7eb5cc02e9250efc7d06b38d96f8399ef5067293 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -1,16 +1,16 @@
 Contributing to Hashie
 ======================
 
-Hashie is work of [many contributors](https://github.com/intridea/hashie/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/intridea/hashie/pulls), [propose features and discuss issues](https://github.com/intridea/hashie/issues).
+Hashie is work of [many contributors](https://github.com/hashie/hashie/graphs/contributors). You're encouraged to submit [pull requests](https://github.com/hashie/hashie/pulls), [propose features and discuss issues](https://github.com/hashie/hashie/issues).
 
 #### Fork the Project
 
-Fork the [project on Github](https://github.com/intridea/hashie) and check out your copy.
+Fork the [project on Github](https://github.com/hashie/hashie) and check out your copy.
 
 ```
 git clone https://github.com/contributor/hashie.git
 cd hashie
-git remote add upstream https://github.com/intridea/hashie.git
+git remote add upstream https://github.com/hashie/hashie.git
 ```
 
 #### Create a Topic Branch
@@ -23,12 +23,19 @@ git pull upstream master
 git checkout -b my-feature-branch
 ```
 
-#### Bundle Install and Test
+#### Install dependencies
+
+You can use the setup script to install dependencies for the gem and its integration tests.
+
+```
+bin/setup
+```
+
+#### Test
 
 Ensure that you can build the project and run tests.
 
 ```
-bundle install
 bundle exec rake
 ```
 
@@ -105,7 +112,7 @@ git push origin my-feature-branch -f
 Update the [CHANGELOG](CHANGELOG.md) with the pull request number. A typical entry looks as follows.
 
 ```
-* [#123](https://github.com/intridea/hashie/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
+* [#123](https://github.com/hashie/hashie/pull/123): Reticulated splines - [@contributor](https://github.com/contributor).
 ```
 
 Amend your previous commit and force push the changes.
diff --git a/Dangerfile b/Dangerfile
new file mode 100644
index 0000000000000000000000000000000000000000..229e8f70319ac2fd9679baabb5efec990983f002
--- /dev/null
+++ b/Dangerfile
@@ -0,0 +1,6 @@
+# see http://danger.systems
+
+changelog.format = :keep_a_changelog
+changelog.check!
+
+toc.check!
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..7edd0a4f04bc0eba59a5bd8ee1a2ae9f6c7f8b0b
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,36 @@
+source 'http://rubygems.org'
+
+gemspec
+
+group :development do
+  gem 'benchmark-ips'
+  gem 'benchmark-memory'
+  gem 'guard', '~> 2.6.1'
+  gem 'guard-rspec', '~> 4.3.1', require: false
+  gem 'guard-yield', '~> 0.1.0', require: false
+  gem 'pry'
+  gem 'pry-stack_explorer', platforms: %i[ruby_19 ruby_20 ruby_21]
+  gem 'rubocop', '0.52.1'
+
+  group :test do
+    # ActiveSupport required to test compatibility with ActiveSupport Core Extensions.
+    # rubocop:disable Bundler/DuplicatedGem
+    require File.expand_path('../lib/hashie/extensions/ruby_version', __FILE__)
+    if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >=
+       Hashie::Extensions::RubyVersion.new('2.4.0')
+      gem 'activesupport', '~> 5.x', require: false
+    else
+      gem 'activesupport', '~> 4.x', require: false
+    end
+    # rubocop:enable Bundler/DuplicatedGem
+    gem 'rake'
+    gem 'rspec', '~> 3'
+    gem 'rspec-pending_for', '~> 0.1'
+  end
+end
+
+group :test do
+  gem 'danger-changelog', '~> 0.6.1', require: false
+  gem 'danger-toc', '~> 0.2.0', require: false
+  gem 'simplecov'
+end
diff --git a/Guardfile b/Guardfile
new file mode 100644
index 0000000000000000000000000000000000000000..4dfef8cfaed26e93755fc7de8ba550f1ba924246
--- /dev/null
+++ b/Guardfile
@@ -0,0 +1,21 @@
+require_relative 'spec/support/integration_specs'
+
+run_all = lambda do |*|
+  Compat::UI.info('Running all integration tests', reset: true)
+  run_all_integration_specs(logger: ->(msg) { Compat::UI.info(msg) })
+end
+
+guard 'rspec', all_on_start: false, cmd: 'bundle exec rspec', run_all: { cmd: 'bundle exec rspec --exclude-pattern "spec/integration/**/*_spec.rb"' } do
+  watch(%r{^spec(?!/integration)/.+_spec\.rb})
+  watch(%r{^lib/(.+)\.rb}) { |m| "spec/#{m[1]}_spec.rb" }
+  watch('spec/spec_helper.rb') { 'spec' }
+end
+
+guard :yield, run_all: run_all do
+  watch(%r{^lib/(.+)\.rb}) { run_all.call }
+  watch(%r{^spec/integration/(?<integration>.*)/.+_spec\.rb}) do |file, integration|
+    Compat::UI.info(%(Running "#{integration}" integration test), reset: true)
+    system(integration_command(integration, 'bundle --quiet'))
+    system(integration_command(integration, "bundle exec rspec #{file}"))
+  end
+end
diff --git a/LICENSE b/LICENSE
index 2db77ec42ac1cba20b2904734423ca463f588658..3bedaa12bacb9243d710961354d8157eb658926a 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-Copyright (c) 2009 Intridea, Inc., and Contributors
+Copyright (c) 2009-2020 Intridea, Inc., and Contributors.
 
 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
diff --git a/README.md b/README.md
index 1413bb720d2181801119c77f215a300f55c02762..721bdc5ce1fe743dd225aa62b873930280db0400 100644
--- a/README.md
+++ b/README.md
@@ -1,14 +1,56 @@
 # Hashie
 
-[![Join the chat at https://gitter.im/intridea/hashie](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/intridea/hashie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
-
+[![Join the chat at https://gitter.im/hashie/hashie](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/hashie/hashie?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![Gem Version](http://img.shields.io/gem/v/hashie.svg)](http://badge.fury.io/rb/hashie)
-[![Build Status](http://img.shields.io/travis/intridea/hashie.svg)](https://travis-ci.org/intridea/hashie)
-[![Dependency Status](https://gemnasium.com/intridea/hashie.svg)](https://gemnasium.com/intridea/hashie)
-[![Code Climate](https://codeclimate.com/github/intridea/hashie.svg)](https://codeclimate.com/github/intridea/hashie)
-[![Coverage Status](https://codeclimate.com/github/intridea/hashie/badges/coverage.svg)](https://codeclimate.com/github/intridea/hashie)
-
-Hashie is a growing collection of tools that extend Hashes and make them more useful.
+[![Build Status](https://github.com/hashie/hashie/actions/workflows/main.yml/badge.svg)](https://github.com/hashie/hashie/actions/workflows/main.yml)
+
+[![eierlegende Wollmilchsau](./mascot.svg)](#mascot) Hashie is a growing collection of tools that extend Hashes and make them more useful.
+
+# Table of Contents
+
+- [Hashie](#hashie)
+- [Table of Contents](#table-of-contents)
+  - [Installation](#installation)
+  - [Stable Release](#stable-release)
+  - [Hash Extensions](#hash-extensions)
+  - [Logging](#logging)
+    - [Coercion](#coercion)
+    - [Coercing Collections](#coercing-collections)
+    - [Coercing Hashes](#coercing-hashes)
+    - [Coercing Core Types](#coercing-core-types)
+    - [Coercion Proc](#coercion-proc)
+      - [A note on circular coercion](#a-note-on-circular-coercion)
+    - [KeyConversion](#keyconversion)
+    - [MergeInitializer](#mergeinitializer)
+    - [MethodAccess](#methodaccess)
+    - [MethodAccessWithOverride](#methodaccesswithoverride)
+    - [MethodOverridingInitializer](#methodoverridinginitializer)
+    - [IndifferentAccess](#indifferentaccess)
+    - [IgnoreUndeclared](#ignoreundeclared)
+    - [DeepMerge](#deepmerge)
+    - [DeepFetch](#deepfetch)
+    - [DeepFind](#deepfind)
+    - [DeepLocate](#deeplocate)
+  - [StrictKeyAccess](#strictkeyaccess)
+  - [Mash](#mash)
+    - [KeepOriginalKeys](#keeporiginalkeys)
+    - [PermissiveRespondTo](#permissiverespondto)
+    - [SafeAssignment](#safeassignment)
+    - [SymbolizeKeys](#symbolizekeys)
+    - [DefineAccessors](#defineaccessors)
+  - [Dash](#dash)
+    - [Potential Gotchas](#potential-gotchas)
+    - [PropertyTranslation](#propertytranslation)
+    - [Mash and Rails 4 Strong Parameters](#mash-and-rails-4-strong-parameters)
+    - [Coercion](#coercion-1)
+    - [PredefinedValues](#predefinedvalues)
+  - [Trash](#trash)
+  - [Clash](#clash)
+  - [Rash](#rash)
+    - [Auto-Optimized](#auto-optimized)
+  - [Mascot](#mascot)
+  - [Contributing](#contributing)
+  - [Copyright](#copyright)
 
 ## Installation
 
@@ -18,9 +60,9 @@ Hashie is available as a RubyGem:
 $ gem install hashie
 ```
 
-## Upgrading
+## Stable Release
 
-You're reading the documentation for the stable release of Hashie, 3.5.5. Please read [UPGRADING](UPGRADING.md) when upgrading from a previous version.
+You're reading the documentation for the stable release of Hashie, v5.0.0.
 
 ## Hash Extensions
 
@@ -133,7 +175,7 @@ You can also use coerce from the following supertypes with `coerce_value`:
 - Integer
 - Numeric
 
-Hashie does not have built-in support for coercion boolean values, since Ruby does not have a built-in boolean type or standard method for to a boolean. You can coerce to booleans using a custom proc.
+Hashie does not have built-in support for coercing boolean values, since Ruby does not have a built-in boolean type or standard method for coercing to a boolean. You can coerce to booleans using a custom proc.
 
 ### Coercion Proc
 
@@ -197,8 +239,8 @@ The KeyConversion extension gives you the convenience methods of `symbolize_keys
 Hashie also has a utility method for converting keys on a Hash without a mixin:
 
 ```ruby
-Hashie.symbolize_keys! hash # => Symbolizes keys of hash.
-Hashie.symbolize_keys hash # => Returns a copy of hash with keys symbolized.
+Hashie.symbolize_keys! hash # => Symbolizes all string keys of hash.
+Hashie.symbolize_keys hash # => Returns a copy of hash with string keys symbolized.
 Hashie.stringify_keys! hash # => Stringifies keys of hash.
 Hashie.stringify_keys hash # => Returns a copy of hash with keys stringified.
 ```
@@ -245,14 +287,32 @@ overriding.zip   #=> 'a-dee-doo-dah'
 overriding.__zip #=> [[['zip', 'a-dee-doo-dah']]]
 ```
 
+### MethodOverridingInitializer
+
+The MethodOverridingInitializer extension will override hash methods if you pass in a normal hash to the constructor. It aliases any overridden method with two leading underscores. To include only this initializing functionality, you can include the single module `Hashie::Extensions::MethodOverridingInitializer`.
+
+```ruby
+class MyHash < Hash
+end
+
+class MyOverridingHash < Hash
+  include Hashie::Extensions::MethodOverridingInitializer
+end
+
+non_overriding = MyHash.new(zip: 'a-dee-doo-dah')
+non_overriding.zip #=> []
+
+overriding = MyOverridingHash.new(zip: 'a-dee-doo-dah')
+overriding.zip   #=> 'a-dee-doo-dah'
+overriding.__zip #=> [[['zip', 'a-dee-doo-dah']]]
+```
+
 ### IndifferentAccess
 
 This extension can be mixed in to your Hash subclass to allow you to use Strings or Symbols interchangeably as keys; similar to the `params` hash in Rails.
 
 In addition, IndifferentAccess will also inject itself into sub-hashes so they behave the same.
 
-Example:
-
 ```ruby
 class MyHash < Hash
   include Hashie::Extensions::MergeInitializer
@@ -272,6 +332,18 @@ myhash['fishes'][:food] = 'flakes'
 myhash['fishes']['food'] # => "flakes"
 ```
 
+To get back a normal, not-indifferent Hash, you can use `#to_hash` on the indifferent hash. It exports the keys as strings, not symbols:
+
+```ruby
+myhash = MyHash.new
+myhash["foo"] = "bar"
+myhash[:foo]  #=> "bar"
+
+normal_hash = myhash.to_hash
+myhash["foo"]  #=> "bar"
+myhash[:foo]  #=> nil
+```
+
 ### IgnoreUndeclared
 
 This extension can be mixed in to silently ignore undeclared properties on initialization instead of raising an error. This is useful when using a Trash to capture a subset of a larger hash.
@@ -298,8 +370,8 @@ p.email      # => NoMethodError
 
 ### DeepMerge
 
-This extension allow you to easily include a recursive merging
-system to any Hash descendant:
+This extension allows you to easily include a recursive merging
+system into any Hash descendant:
 
 ```ruby
 class MyHash < Hash
@@ -427,8 +499,6 @@ books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
 
 This extension can be mixed in to allow a Hash to raise an error when attempting to extract a value using a non-existent key.
 
-### Example:
-
 ```ruby
 class StrictKeyAccessHash < Hash
   include Hashie::Extensions::StrictKeyAccess
@@ -446,8 +516,6 @@ end
 
 Mash is an extended Hash that gives simple pseudo-object functionality that can be built from hashes and easily extended. It is intended to give the user easier access to the objects within the Mash through a property-like syntax, while still retaining all Hash functionality.
 
-### Example:
-
 ```ruby
 mash = Hashie::Mash.new
 mash.name? # => false
@@ -470,9 +538,9 @@ mash.inspect # => <Hashie::Mash>
 
 **Note:** The `?` method will return false if a key has been set to false or nil. In order to check if a key has been set at all, use the `mash.key?('some_key')` method instead.
 
-Please note that a Mash will not override methods through the use of the property-like syntax. This can lead to confusion if you expect to be able to access a Mash value through the property-like syntax for a key that conflicts with a method name. However, it protects users of your library from the unexpected behavior of those methods being overridden behind the scenes.
+_How does Mash handle conflicts with pre-existing methods?_
 
-### Example:
+Please note that a Mash will not override methods through the use of the property-like syntax. This can lead to confusion if you expect to be able to access a Mash value through the property-like syntax for a key that conflicts with a method name. However, it protects users of your library from the unexpected behavior of those methods being overridden behind the scenes.
 
 ```ruby
 mash = Hashie::Mash.new
@@ -481,9 +549,96 @@ mash.zip = "Method Override?"
 mash.zip # => [[["name", "My Mash"]], [["zip", "Method Override?"]]]
 ```
 
-Mash allows you also to transform any files into a Mash objects.
+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
+```
+
+The default is to disable logging for all methods that conflict. If you would like to only disable the logging for specific methods, you can include an array of method keys:
 
-### Example:
+```ruby
+class Response < Hashie::Mash
+  disable_warnings :zip, :zap
+end
+```
+
+This behavior is cumulative. The examples above and below behave identically.
+
+```ruby
+class Response < Hashie::Mash
+  disable_warnings :zip
+  disable_warnings :zap
+end
+```
+
+Disable warnings will honor the last `disable_warnings` call. Calling without parameters will override the ignored methods list, and calling with parameters will create a new ignored methods list. This includes child classes that inherit from a class that disables warnings.
+
+```ruby
+class Message < Hashie::Mash
+  disable_warnings :zip, :zap
+  disable_warnings
+end
+
+# No errors will be logged
+Message.new(merge: 'true', compact: true)
+```
+
+```ruby
+class Message < Hashie::Mash
+  disable_warnings
+end
+
+class Response < Message
+  disable_warnings :zip, :zap
+end
+
+# 2 errors will be logged
+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.
+
+```ruby
+mash = Hashie::Mash.new(name: "Hashie", dependencies: { rake: "< 11", rspec: "~> 3.0" })
+mash.dependencies.class #=> Hashie::Mash
+
+class MyGem < Hashie::Mash; end
+my_gem = MyGem.new(name: "Hashie", dependencies: { rake: "< 11", rspec: "~> 3.0" })
+my_gem.dependencies.class #=> MyGem
+```
+
+_How does Mash handle key types which cannot be symbolized?_
+
+Mash preserves keys which cannot be converted *directly* to both a string and a symbol, such as numeric keys. Since Mash is conceived to provide psuedo-object functionality, handling keys which cannot represent a method call falls outside its scope of value.
+
+```ruby
+Hashie::Mash.new('1' => 'one string', :'1' => 'one sym', 1 => 'one num')
+# => {"1"=>"one sym", 1=>"one num"}
+```
+
+The symbol key `:'1'` is converted the string `'1'` to support indifferent access and consequently its value `'one sym'` will override the previously set `'one string'`. However, the subsequent key of `1` cannot directly convert to a symbol and therefore **not** converted to the string `'1'` that would otherwise override the previously set value of `'one sym'`.
+
+_What else can Mash do?_
+
+Mash allows you also to transform any files into a Mash objects.
 
 ```yml
 #/etc/config/settings/twitter.yml
@@ -515,7 +670,7 @@ Twitter.extend mash.to_module # NOTE: if you want another name than settings, ca
 Twitter.settings.api_key # => 'abcd'
 ```
 
-You can use another parser (by default: YamlErbParser):
+You can use another parser (by default: [YamlErbParser](lib/hashie/extensions/parsers/yaml_erb_parser.rb)):
 
 ```
 #/etc/data/user.csv
@@ -531,23 +686,17 @@ 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
-```
+The `Mash#load` method calls `YAML.safe_load(path, [], [], true)`.
 
-You can also disable the logging in subclasses of Mash:
+Specify `permitted_symbols`, `permitted_classes` and `aliases` options as needed.
 
 ```ruby
-class Response < Hashie::Mash
-  disable_warnings
-end
+Mash.load('data/user.csv', permitted_classes: [Symbol], permitted_symbols: [], aliases: false)
 ```
 
-### Mash Extension: KeepOriginalKeys
+### KeepOriginalKeys
 
-This extension can be mixed into a Mash to keep the form of any keys passed directly into the Mash. By default, Mash converts keys to strings to give indifferent access. This extension still allows indifferent access, but keeps the form of the keys to eliminate confusion when you're not expecting the keys to change.
+This extension can be mixed into a Mash to keep the form of any keys passed directly into the Mash. By default, Mash converts symbol keys to strings to give indifferent access. This extension still allows indifferent access, but keeps the form of the keys to eliminate confusion when you're not expecting the keys to change.
 
 ```ruby
 class KeepingMash < ::Hashie::Mash
@@ -564,11 +713,33 @@ mash['string_key']  #=> 'string'
 mash[:string_key]  #=> 'string'
 ```
 
-### Mash Extension: SafeAssignment
+### PermissiveRespondTo
 
-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.
+By default, Mash only states that it responds to built-in methods, affixed methods (e.g. setters, underbangs, etc.), and keys that it currently contains. That means it won't state that it responds to a getter for an unset key, as in the following example:
+
+```ruby
+mash = Hashie::Mash.new(a: 1)
+mash.respond_to? :b  #=> false
+```
+
+This means that by default Mash is not a perfect match for use with a SimpleDelegator since the delegator will not forward messages for unset keys to the Mash even though it can handle them.
+
+In order to have a SimpleDelegator-compatible Mash, you can use the `PermissiveRespondTo` extension to make Mash respond to anything.
+
+```ruby
+class PermissiveMash < Hashie::Mash
+  include Hashie::Extensions::Mash::PermissiveRespondTo
+end
+
+mash = PermissiveMash.new(a: 1)
+mash.respond_to? :b  #=> true
+```
+
+This comes at the cost of approximately 20% performance for initialization and setters and 19KB of permanent memory growth for each such class that you create.
 
-#### Example:
+### 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.
 
 ```ruby
 class SafeMash < ::Hashie::Mash
@@ -580,9 +751,9 @@ safe_mash.zip   = 'Test' # => ArgumentError
 safe_mash[:zip] = 'test' # => still ArgumentError
 ```
 
-### Mash Extension:: SymbolizeKeys
+### SymbolizeKeys
 
-This extension can be mixed into a Mash to change the default behavior of converting keys to strings. After mixing this extension into a Mash, the Mash will convert all keys to symbols.
+This extension can be mixed into a Mash to change the default behavior of converting keys to strings. After mixing this extension into a Mash, the Mash will convert all string keys to symbols. It can be useful to use with keywords argument, which required symbol keys.
 
 ```ruby
 class SymbolizedMash < ::Hashie::Mash
@@ -593,6 +764,12 @@ symbol_mash = SymbolizedMash.new
 symbol_mash['test'] = 'value'
 symbol_mash.test  #=> 'value'
 symbol_mash.to_h  #=> {test: 'value'}
+
+def example(test:)
+  puts test
+end
+
+example(symbol_mash) #=> value
 ```
 
 There is a major benefit and coupled with a major trade-off to this decision (at least on older Rubies). As a benefit, by using symbols as keys, you will be able to use the implicit conversion of a Mash via the `#to_hash` method to destructure (or splat) the contents of a Mash out to a block. This can be handy for doing iterations through the Mash's keys and values, as follows:
@@ -607,14 +784,37 @@ end
 
 However, on Rubies less than 2.0, this means that every key you send to the Mash will generate a symbol. Since symbols are not garbage-collected on older versions of Ruby, this can cause a slow memory leak when using a symbolized Mash with data generated from user input.
 
+### DefineAccessors
+
+This extension can be mixed into a Mash so it makes it behave like `OpenStruct`. It reduces the overhead of `method_missing?` magic by lazily defining field accessors when they're requested.
+
+```ruby
+class MyHash < ::Hashie::Mash
+  include Hashie::Extensions::Mash::DefineAccessors
+end
+
+mash = MyHash.new
+MyHash.method_defined?(:foo=) #=> false
+mash.foo = 123
+MyHash.method_defined?(:foo=) #=> true
+
+MyHash.method_defined?(:foo) #=> false
+mash.foo #=> 123
+MyHash.method_defined?(:foo) #=> true
+```
+
+You can also extend the existing mash without defining a class:
+
+```ruby
+mash = ::Hashie::Mash.new.with_accessors!
+```
+
 ## Dash
 
 Dash is an extended Hash that has a discrete set of defined properties and only those properties may be set on the hash. Additionally, you can set defaults for each property. You can also flag a property as required. Required properties will raise an exception if unset. Another option is message for required properties, which allow you to add custom messages for required property.
 
 You can also conditionally require certain properties by passing a Proc or Symbol. If a Proc is provided, it will be run in the context of the Dash instance. If a Symbol is provided, the value returned for the property or method of the same name will be evaluated. The property will be required if the result of the conditional is truthy.
 
-### Example:
-
 ```ruby
 class Person < Hashie::Dash
   property :name, required: true
@@ -651,8 +851,6 @@ p.occupation   # => 'Rubyist'
 
 Properties defined as symbols are not the same thing as properties defined as strings.
 
-### Example:
-
 ```ruby
 class Tricky < Hashie::Dash
   property :trick
@@ -676,13 +874,64 @@ p = Tricky.new('trick' => 'two')
 p.trick # => NoMethodError
 ```
 
-### Dash Extension: PropertyTranslation
+If you would like to update a Dash and use any default values set in the case of a `nil` value, use `#update_attributes!`.
+
+```ruby
+class WithDefaults < Hashie::Dash
+  property :description, default: 'none'
+end
+
+dash = WithDefaults.new
+dash.description  #=> 'none'
+
+dash.description = 'You committed one of the classic blunders!'
+dash.description  #=> 'You committed one of the classic blunders!'
+
+dash.description = nil
+dash.description  #=> nil
+
+dash.description = 'Only slightly less known is ...'
+dash.update_attributes!(description: nil)
+dash.description  #=> 'none'
+```
+
+### Potential Gotchas
+
+Because Dashes are subclasses of the built-in Ruby Hash class, the double-splat operator takes the Dash as-is without any conversion. This can lead to strange behavior when you use the double-splat operator on a Dash as the first part of a keyword list or built Hash. For example:
+
+```ruby
+class Foo < Hashie::Dash
+  property :bar
+end
+
+foo = Foo.new(bar: 'baz')      #=> {:bar=>"baz"}
+qux = { **foo, quux: 'corge' } #=> {:bar=> "baz", :quux=>"corge"}
+qux.is_a?(Foo)                 #=> true
+qux[:quux]
+#=> raise NoMethodError, "The property 'quux' is not defined for Foo."
+qux.key?(:quux) #=> true
+```
+
+You can work around this problem in two ways:
+
+1. Call `#to_h` on the resulting object to convert it into a Hash.
+2. Use the double-splat operator on the Dash as the last argument in the Hash literal. This will cause the resulting object to be a Hash instead of a Dash, thereby circumventing the problem.
+
+```ruby
+qux = { **foo, quux: 'corge' }.to_h #=> {:bar=> "baz", :quux=>"corge"}
+qux.is_a?(Hash)                     #=> true
+qux[:quux]                          #=> "corge"
+
+qux = { quux: 'corge', **foo } #=> {:quux=>"corge", :bar=> "baz"}
+qux.is_a?(Hash)                #=> true
+qux[:quux]                     #=> "corge"
+```
+
+### PropertyTranslation
 
 The `Hashie::Extensions::Dash::PropertyTranslation` mixin extends a Dash with
 the ability to remap keys from a source hash.
 
-### Example from inconsistent APIs
-
 Property translation is useful when you need to read data from another
 application -- such as a Java API -- where the keys are named differently from
 Ruby conventions.
@@ -702,8 +951,6 @@ person[:first_name]  #=> 'Michael'
 person[:last_name]   #=> 'Bleigh
 ```
 
-### Example using translation lambdas
-
 You can also use a lambda to translate the value. This is particularly useful
 when you want to ensure the type of data you're wrapping.
 
@@ -716,7 +963,7 @@ class DataModelHash < Hashie::Dash
 end
 
 model = DataModelHash.new(id: '123', created: '2014-04-25 22:35:28')
-model.id.class          #=> Fixnum
+model.id.class          #=> Integer (Fixnum if you are using Ruby 2.3 or lower)
 model.created_at.class  #=> Time
 ```
 
@@ -724,7 +971,7 @@ model.created_at.class  #=> Time
 
 To enable compatibility with Rails 4 use the [hashie-forbidden_attributes](https://github.com/Maxim-Filimonov/hashie-forbidden_attributes) gem.
 
-### Dash Extension: Coercion.
+### Coercion
 
 If you want to use `Hashie::Extensions::Coercion` together with `Dash` then
 you may probably want to use `Hashie::Extensions::Dash::Coercion` instead.
@@ -754,6 +1001,20 @@ class UserHash < Hashie::Dash
 end
 ```
 
+### PredefinedValues
+
+The `Hashie::Extensions::Dash::PredefinedValues` mixin extends a Dash with
+the ability to accept predefined values on a property.
+
+```ruby
+class UserHash < Hashie::Dash
+  include Hashie::Extensions::Dash::PredefinedValues
+
+  property :gender, values: %i[male female prefer_not_to_say]
+  property :age, values: (0..150)
+end
+```
+
 ## Trash
 
 A Trash is a Dash that allows you to translate keys on initialization. It mixes
@@ -785,7 +1046,7 @@ this will produce the following
 
 ```ruby
 result = Result.new(id: '123', creation_date: '2012-03-30 17:23:28')
-result.id.class         # => Fixnum
+result.id.class         # => Integer (Fixnum if you are using Ruby 2.3 or lower)
 result.created_at.class # => Time
 ```
 
@@ -795,8 +1056,6 @@ Clash is a Chainable Lazy Hash that allows you to easily construct complex hashe
 
 Essentially, a Clash is a generalized way to provide much of the same kind of "chainability" that libraries like Arel or Rails 2.x's named_scopes provide.
 
-### Example:
-
 ```ruby
 c = Hashie::Clash.new
 c.where(abc: 'def').order(:created_at)
@@ -822,8 +1081,6 @@ A good use case for the Rash is an URL router for a web framework, where URLs ne
 
 If the Rash's value is a `proc`, the `proc` will be automatically called with the regexp's MatchData (matched groups) as a block argument.
 
-### Example:
-
 ```ruby
 
 # Mapping names to appropriate greetings
@@ -840,18 +1097,21 @@ mapper["I like traffic lights"] # => "Who DOESN'T like traffic lights?!"
 mapper["Get off my lawn!"]      # => "Forget your lawn, old man!"
 ```
 
-### Auto-optimized
+### Auto-Optimized
 
 **Note:** The Rash is automatically optimized every 500 accesses (which means that it sorts the list of Regexps, putting the most frequently matched ones at the beginning).
 
 If this value is too low or too high for your needs, you can tune it by setting: `rash.optimize_every = n`.
 
+## Mascot
+[![eierlegende Wollmilchsau](./mascot.svg)](https://en.wiktionary.org/wiki/eierlegende_Wollmilchsau) Meet Hashie's "offical" mascot, the [eierlegende Wollmilchsau](https://en.wiktionary.org/wiki/eierlegende_Wollmilchsau)!
+
 ## Contributing
 
 See [CONTRIBUTING.md](CONTRIBUTING.md)
 
 ## Copyright
 
-Copyright (c) 2009-2014 Intridea, Inc. (http://intridea.com/) and [contributors](https://github.com/intridea/hashie/graphs/contributors).
+Copyright (c) 2009-2020 [Intridea, Inc.](http://intridea.com), and [contributors](https://github.com/hashie/hashie/graphs/contributors).
 
 MIT License. See [LICENSE](LICENSE) for details.
diff --git a/RELEASING.md b/RELEASING.md
new file mode 100644
index 0000000000000000000000000000000000000000..be3c4580d479a41e2cdfbe0d1cf1bd8690fd936f
--- /dev/null
+++ b/RELEASING.md
@@ -0,0 +1,126 @@
+# Releasing Hashie
+
+There're no particular rules about when to release Hashie. Release bug fixes frequenty, features not so frequently and breaking API changes rarely.
+
+## Release
+
+Run tests, check that all tests succeed locally.
+
+```sh
+bundle install
+bundle exec rake
+```
+
+Check that the last build succeeded in [Travis CI](https://travis-ci.org/hashie/hashie) for all supported platforms.
+
+### Check Next Version
+
+Increment the version, modify [lib/hashie/version.rb](lib/hashie/version.rb). [Changelog](CHANGELOG.md) entries should be helpfully categorized to assist in picking the next version number.
+
+* Increment the third number (minor version) if the release has bug fixes and/or very minor features, only (eg. change `0.5.1` to `0.5.2`). These should be in the "Fixed", "Security", or "Miscellaneous" categories in the change log.
+* Increment the second number (patch version) if the release contains major features or breaking API changes (eg. change `0.5.1` to `0.6.0`). These should be in the "Added" or "Deprecated" categories in the change log.
+* Increment the first number (major version) if the release has any changed or removed behavior on public APIs (eg. change `0.5.1` to `1.0.0`). These should be in the "Changed" or "Removed" categories in the change log.
+
+### Modify the Readme
+
+Modify the "Stable Release" section in [README.md](README.md). Change the text to reflect that this is going to be the documentation for a stable release. Remove references to the previous release of Hashie. Keep the file open, you'll have to undo this change after the release.
+
+```markdown
+## Stable Release
+
+You're reading the documentation for the stable release of Hashie, 3.3.0.
+```
+
+### Modify the Changelog
+
+Change "Unreleased" in [CHANGELOG.md](CHANGELOG.md) to the new version.
+
+```markdown
+## [3.3.0] - 2014-08-25
+
+[3.3.0]: https://github.com/hashie/hashie/compare/v<LAST_VERSION>..v<THIS_VERSION>
+```
+
+Replace `<LAST_VERSION>` and `<THIS_VERSION>` with the last and new-to-be-released versions to set up the compare view on Github.
+
+Remove any sections that only have "Your contribution here." underneath them.
+
+Commit your changes.
+
+```sh
+git add README.md CHANGELOG.md lib/hashie/version.rb
+git commit -m "Preparing for release, 3.3.0."
+git push origin master
+```
+
+### Push to RubyGems.org
+
+Release.
+
+```sh
+$ rake release
+
+hashie 3.3.0 built to pkg/hashie-3.3.0.gem.
+Tagged v3.3.0.
+Pushed git commits and tags.
+Pushed hashie 3.3.0 to rubygems.org.
+```
+
+## Prepare for the Next Version
+
+Modify the "Stable Release" section in [README.md](README.md). Change the text to reflect that this is going to be the next release.
+
+```markdown
+## Stable Release
+
+You're reading the documentation for the next release of Hashie, which should be 3.3.1.
+The current stable release is [3.3.0](https://github.com/hashie/hashie/blob/v3.3.0/README.md).
+```
+
+Add new "Unreleased" section to [CHANGELOG.md](CHANGELOG.md) using this template:
+
+```markdown
+## [Unreleased][unreleased]
+
+[unreleased]: https://github.com/hashie/hashie/compare/v<THIS_VERSION>...master
+
+### Added
+
+* Your contribution here.
+
+### Changed
+
+* Your contribution here.
+
+### Deprecated
+
+* Your contribution here.
+
+### Removed
+
+* Your contribution here.
+
+### Fixed
+
+* Your contribution here.
+
+### Security
+
+* Your contribution here.
+
+### Miscellaneous
+
+* Your contribution here.
+```
+
+Replace `<THIS_VERSION>` with the newly released versions to set up the compare view on Github.
+
+Increment the minor version, modify [lib/hashie/version.rb](lib/hashie/version.rb).
+
+Commit your changes.
+
+```sh
+git add CHANGELOG.md README.md lib/hashie/version.rb
+git commit -m "Preparing for next development iteration, 3.3.1."
+git push origin master
+```
diff --git a/Rakefile b/Rakefile
index b7b08c8cec318d782293ada5d8cdc3925f85fe7a..754418583e95ab7ab6544ed843877c46619d0e24 100644
--- a/Rakefile
+++ b/Rakefile
@@ -24,9 +24,9 @@ task :integration_specs do
   run_all_integration_specs(handler: handler, logger: ->(msg) { puts msg })
 
   if status_codes.any?
-    $stderr.puts "#{status_codes.size} integration test(s) failed"
+    warn "#{status_codes.size} integration test(s) failed"
     exit status_codes.last
   end
 end
 
-task default: [:rubocop, :spec, :integration_specs]
+task default: %i[rubocop spec integration_specs]
diff --git a/UPGRADING.md b/UPGRADING.md
index 5a9f05afe6ee87c0be07da65f88f9de87ac57ca2..b044cf1e28d91c7958f1b4afd929ced3ca6b9f1e 100644
--- a/UPGRADING.md
+++ b/UPGRADING.md
@@ -1,6 +1,122 @@
 Upgrading Hashie
 ================
 
+### Upgrading to 5.0.0
+
+#### Mash initialization key conversion
+
+Mash initialization now only converts to string keys which can be represented as symbols.
+
+```ruby
+Hashie::Mash.new(
+  {foo: "bar"} => "baz",
+  "1" => "one string",
+  :"1" => "one sym",
+  1 => "one num"
+)
+
+# Before
+{"{:foo=>\"bar\"}"=>"baz", "1"=>"one num"}
+
+# After
+{{:foo=>"bar"}=>"baz", "1"=>"one sym", 1=>"one num"}
+```
+
+#### Mash#dig with numeric keys
+
+`Hashie::Mash#dig` no longer considers numeric keys for indifferent access.
+
+```ruby
+my_mash = Hashie::Mash.new("1" => "a") # => {"1"=>"a"}
+
+my_mash.dig("1") # => "a"
+my_mash.dig(:"1") # => "a"
+
+# Before
+my_mash.dig(1) # => "a"
+
+# After
+my_mash.dig(1) # => nil
+```
+
+### Upgrading to 4.0.0
+
+#### Non-destructive Hash methods called on Mash
+
+The following non-destructive Hash methods called on Mash will now return an instance of the class it was called on.
+
+| method            | ruby |
+| ----------------- | ---- |
+| #compact          |      |
+| #invert           |      |
+| #reject           |      |
+| #select           |      |
+| #slice            | 2.5  |
+| #transform_keys   | 2.5  |
+| #transform_values | 2.4  |
+
+```ruby
+class Parents < Hashie::Mash; end
+
+parents = Parents.new(father: 'Dad', mother: 'Mom')
+cool_parents = parents.transform_values { |v| v + v[-1] + 'io'}
+
+p cool_parents
+
+# before:
+{"father"=>"Daddio", "mother"=>"Mommio"}
+ => {"father"=>"Daddio", "mother"=>"Mommio"}
+
+# after:
+#<Parents father="Daddio" mother="Mommio">
+=> {"father"=>"Dad", "mother"=>"Mom"}
+```
+
+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!
+
+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
+
+The `Hashie::Mash#load` method now accepts options, changing the interface of `Parser#initialize`. If you have a custom parser, you must update its `initialize` method.
+
+For example, `Hashie::Extensions::Parsers::YamlErbParser` now accepts `permitted_classes`, `permitted_symbols` and `aliases` options.
+
+Before:
+
+```ruby
+class Hashie::Extensions::Parsers::YamlErbParser
+  def initialize(file_path)
+    @file_path = file_path
+  end
+end
+```
+
+After:
+
+```ruby
+class Hashie::Extensions::Parsers::YamlErbParser
+  def initialize(file_path, options = {})
+    @file_path = file_path
+    @options = options
+  end
+end
+```
+
+Options can now be passed into `Mash#load`.
+
+```ruby
+Mash.load(filename, permitted_classes: [])
+```
+
 ### Upgrading to 3.5.2
 
 #### Disable logging in Mash subclasses
@@ -65,7 +181,7 @@ h.abb? # => false
 
 #### Possible coercion changes
 
-The improvements made to coercions in version 3.2.1 [issue #200](https://github.com/intridea/hashie/pull/200) do not break the documented API, but are significant enough that changes may effect undocumented side-effects. Applications that depended on those side-effects will need to be updated.
+The improvements made to coercions in version 3.2.1 [issue #200](https://github.com/hashie/hashie/pull/200) do not break the documented API, but are significant enough that changes may effect undocumented side-effects. Applications that depended on those side-effects will need to be updated.
 
 **Change**: Type coercion no longer creates new objects if the input matches the target type. Previously coerced properties always resulted in the creation of a new object, even when it wasn't necessary. This had the effect of a `dup` or `clone` on coerced properties but not uncoerced ones.
 
@@ -81,7 +197,7 @@ Applications that were attempting to rescuing the internal errors should be upda
 
 #### Compatibility with Rails 4 Strong Parameters
 
-Version 2.1 introduced support to prevent default Rails 4 mass-assignment protection behavior. This was [issue #89](https://github.com/intridea/hashie/issues/89), resolved in [#104](https://github.com/intridea/hashie/pull/104). In version 2.2 this behavior has been removed in [#147](https://github.com/intridea/hashie/pull/147) in favor of a mixin and finally extracted into a separate gem in Hashie 3.0.
+Version 2.1 introduced support to prevent default Rails 4 mass-assignment protection behavior. This was [issue #89](https://github.com/hashie/hashie/issues/89), resolved in [#104](https://github.com/hashie/hashie/pull/104). In version 2.2 this behavior has been removed in [#147](https://github.com/hashie/hashie/pull/147) in favor of a mixin and finally extracted into a separate gem in Hashie 3.0.
 
 To enable 2.1 compatible behavior with Rails 4, use the [hashie_rails](http://rubygems.org/gems/hashie_rails) gem.
 
@@ -89,7 +205,7 @@ To enable 2.1 compatible behavior with Rails 4, use the [hashie_rails](http://ru
 gem 'hashie_rails'
 ```
 
-See [#154](https://github.com/intridea/hashie/pull/154) and [Mash and Rails 4 Strong Parameters](README.md#mash-and-rails-4-strong-parameters) for more details.
+See [#154](https://github.com/hashie/hashie/pull/154) and [Mash and Rails 4 Strong Parameters](README.md#mash-and-rails-4-strong-parameters) for more details.
 
 #### Key Conversions in Hashie::Dash and Hashie::Trash
 
@@ -117,7 +233,7 @@ p.inspect # => { 'name' => 'dB.' }
 p.to_hash # => { 'name' => 'dB.' }
 ```
 
-It was not possible to achieve the behavior of preserving keys, as described in [issue #151](https://github.com/intridea/hashie/issues/151).
+It was not possible to achieve the behavior of preserving keys, as described in [issue #151](https://github.com/hashie/hashie/issues/151).
 
 Version 2.2 does not perform this conversion by default.
 
@@ -164,6 +280,4 @@ instance.to_hash # => { :first => 'First', "last" => 'Last' }
 
 The behavior with `symbolize_keys` and `stringify_keys` is unchanged.
 
-See [#152](https://github.com/intridea/hashie/pull/152) for more information.
-
-
+See [#152](https://github.com/hashie/hashie/pull/152) for more information.
diff --git a/benchmarks/keep_original_mash_keys.rb b/benchmarks/keep_original_mash_keys.rb
new file mode 100644
index 0000000000000000000000000000000000000000..3f20eb979380b00e6e493b3fb295c71a601b759e
--- /dev/null
+++ b/benchmarks/keep_original_mash_keys.rb
@@ -0,0 +1,28 @@
+require_relative '../lib/hashie'
+require 'benchmark/ips'
+
+class KeepingMash < Hashie::Mash
+  include Hashie::Extensions::Mash::KeepOriginalKeys
+end
+
+original = { test: 'value' }
+mash = Hashie::Mash.new(original)
+keeping_mash = KeepingMash.new(original)
+
+Benchmark.ips do |x|
+  x.report('keep symbol') { keeping_mash.test }
+  x.report('normal symbol') { mash.test }
+
+  x.compare!
+end
+
+original = { 'test' => 'value' }
+mash = Hashie::Mash.new(original)
+keeping_mash = KeepingMash.new(original)
+
+Benchmark.ips do |x|
+  x.report('keep string') { keeping_mash.test }
+  x.report('normal string') { mash.test }
+
+  x.compare!
+end
diff --git a/benchmarks/mash_method_access.rb b/benchmarks/mash_method_access.rb
new file mode 100755
index 0000000000000000000000000000000000000000..d76cd58f77a41aa3b0f7170ae62d4844036b0787
--- /dev/null
+++ b/benchmarks/mash_method_access.rb
@@ -0,0 +1,15 @@
+$LOAD_PATH.unshift('lib')
+
+require 'hashie'
+require 'benchmark/ips'
+
+mash = Hashie::Mash.new(test: 'value')
+
+Benchmark.ips do |x|
+  x.hold!('tmp/mash_benchmark.json')
+
+  x.report('before') { mash.test }
+  x.report('after') { mash.test }
+
+  x.compare!
+end
diff --git a/benchmarks/permissive_respond_to.rb b/benchmarks/permissive_respond_to.rb
new file mode 100755
index 0000000000000000000000000000000000000000..e7e894e991d01b0f9e6495bbdf5bf7ef6a6459cd
--- /dev/null
+++ b/benchmarks/permissive_respond_to.rb
@@ -0,0 +1,44 @@
+#!/usr/bin/env ruby
+
+$LOAD_PATH.unshift File.expand_path(File.join('..', 'lib'), __dir__)
+
+require 'hashie'
+require 'benchmark/ips'
+require 'benchmark/memory'
+
+permissive = Class.new(Hashie::Mash)
+
+Benchmark.memory do |x|
+  x.report('Default') {}
+  x.report('Make permissive') do
+    permissive.include Hashie::Extensions::Mash::PermissiveRespondTo
+  end
+end
+
+class PermissiveMash < Hashie::Mash
+  include Hashie::Extensions::Mash::PermissiveRespondTo
+end
+
+Benchmark.ips do |x|
+  x.report('Mash.new') { Hashie::Mash.new(a: 1) }
+  x.report('Permissive.new') { PermissiveMash.new(a: 1) }
+
+  x.compare!
+end
+
+Benchmark.ips do |x|
+  x.report('Mash#attr=') { Hashie::Mash.new.a = 1 }
+  x.report('Permissive#attr=') { PermissiveMash.new.a = 1 }
+
+  x.compare!
+end
+
+mash = Hashie::Mash.new(a: 1)
+permissive = PermissiveMash.new(a: 1)
+
+Benchmark.ips do |x|
+  x.report('Mash#attr= x2') { mash.a = 1 }
+  x.report('Permissive#attr= x2') { permissive.a = 1 }
+
+  x.compare!
+end
diff --git a/bin/console b/bin/console
new file mode 100755
index 0000000000000000000000000000000000000000..62d4bcd71de38cd5869a4f18873e2ecaebe36d0a
--- /dev/null
+++ b/bin/console
@@ -0,0 +1,14 @@
+#!/usr/bin/env ruby
+
+require 'bundler/setup'
+require 'hashie'
+
+# You can add fixtures and/or initialization code here to make experimenting
+# with your gem easier. You can also use a different console, if you like.
+
+# (If you use this, don't forget to add pry to your Gemfile!)
+# require "pry"
+# Pry.start
+
+require 'irb'
+IRB.start
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 0000000000000000000000000000000000000000..e52c127901ff3779ba18b809ff1f1e1e1fec2a87
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,12 @@
+#!/bin/bash
+
+set -euo pipefail
+IFS=$'\n\t'
+
+bundle install
+
+for dir in spec/integration/*; do
+  pushd "$dir"
+  bundle install
+  popd
+done
diff --git a/hashie.gemspec b/hashie.gemspec
index 132913aa8f54ff32fd73e877a3ceaec13e14d061..dbb438d064e97b203d0a6f127123e431dc1cafd9 100644
--- a/hashie.gemspec
+++ b/hashie.gemspec
@@ -7,16 +7,22 @@ Gem::Specification.new do |gem|
   gem.email         = ['michael@intridea.com', 'jollyjerry@gmail.com']
   gem.description   = 'Hashie is a collection of classes and mixins that make hashes more powerful.'
   gem.summary       = 'Your friendly neighborhood hash library.'
-  gem.homepage      = 'https://github.com/intridea/hashie'
+  gem.homepage      = 'https://github.com/hashie/hashie'
   gem.license       = 'MIT'
 
   gem.require_paths = ['lib']
-  gem.files         = %w(.yardopts CHANGELOG.md CONTRIBUTING.md LICENSE README.md UPGRADING.md Rakefile hashie.gemspec)
+  gem.files = %w[.yardopts CHANGELOG.md CONTRIBUTING.md LICENSE README.md UPGRADING.md]
+  gem.files += %w[Rakefile hashie.gemspec]
   gem.files += Dir['lib/**/*.rb']
-  gem.files += Dir['spec/**/*.rb']
-  gem.test_files = Dir['spec/**/*.rb']
 
-  gem.add_development_dependency 'rake', '< 11'
-  gem.add_development_dependency 'rspec', '~> 3.0'
-  gem.add_development_dependency 'rspec-pending_for', '~> 0.1'
+  if gem.respond_to?(:metadata)
+    gem.metadata = {
+      'bug_tracker_uri'   => 'https://github.com/hashie/hashie/issues',
+      'changelog_uri'     => 'https://github.com/hashie/hashie/blob/master/CHANGELOG.md',
+      'documentation_uri' => 'https://www.rubydoc.info/gems/hashie',
+      'source_code_uri'   => 'https://github.com/hashie/hashie'
+    }
+  end
+
+  gem.add_development_dependency 'bundler'
 end
diff --git a/lib/hashie.rb b/lib/hashie.rb
index b6e1d134a3fc6d61610811e1d0898ff10fe5a453..8f526c44ab548dcae080dbcd3410ccfd38fa3de5 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'
@@ -41,12 +41,15 @@ module Hashie
       autoload :IndifferentAccess, 'hashie/extensions/dash/indifferent_access'
       autoload :PropertyTranslation, 'hashie/extensions/dash/property_translation'
       autoload :Coercion, 'hashie/extensions/dash/coercion'
+      autoload :PredefinedValues, 'hashie/extensions/dash/predefined_values'
     end
 
     module Mash
       autoload :KeepOriginalKeys, 'hashie/extensions/mash/keep_original_keys'
+      autoload :PermissiveRespondTo, 'hashie/extensions/mash/permissive_respond_to'
       autoload :SafeAssignment, 'hashie/extensions/mash/safe_assignment'
       autoload :SymbolizeKeys, 'hashie/extensions/mash/symbolize_keys'
+      autoload :DefineAccessors, 'hashie/extensions/mash/define_accessors'
     end
 
     module Array
diff --git a/lib/hashie/clash.rb b/lib/hashie/clash.rb
index 945c14675c65e06cdbb7065a43b414e306096475..92dcab440ab70f6bd17b531c16daed34e78b4c6e 100644
--- a/lib/hashie/clash.rb
+++ b/lib/hashie/clash.rb
@@ -75,7 +75,7 @@ module Hashie
         when Hash
           self[key] = self.class.new(self[key], self)
         else
-          fail ChainError, 'Tried to chain into a non-hash key.'
+          raise ChainError, 'Tried to chain into a non-hash key.'
         end
       elsif args.any?
         merge_store(name, *args)
@@ -83,5 +83,16 @@ module Hashie
         super
       end
     end
+
+    def respond_to_missing?(method_name, _include_private = false)
+      method_name = method_name.to_s
+
+      if method_name.end_with?('!')
+        key = method_name[0...-1].to_sym
+        [NilClass, Clash, Hash].include?(self[key].class)
+      else
+        true
+      end
+    end
   end
 end
diff --git a/lib/hashie/dash.rb b/lib/hashie/dash.rb
index 4ac767611d31446aaaa55a2be71a250edc802c2f..cfb151ef901e96fb0e28399534fc49ae94869422 100644
--- a/lib/hashie/dash.rb
+++ b/lib/hashie/dash.rb
@@ -15,7 +15,7 @@ module Hashie
   class Dash < Hash
     include Hashie::Extensions::PrettyInspect
 
-    alias_method :to_s, :inspect
+    alias to_s inspect
 
     # Defines a property on the Dash. Options are
     # as follows:
@@ -42,30 +42,27 @@ module Hashie
         defaults.delete property_name
       end
 
-      unless instance_methods.map(&:to_s).include?("#{property_name}=")
-        define_method(property_name) { |&block| self.[](property_name, &block) }
-        property_assignment = "#{property_name}=".to_sym
-        define_method(property_assignment) { |value| self.[]=(property_name, value) }
-      end
+      define_getter_for(property_name)
+      define_setter_for(property_name)
 
-      if defined? @subclasses
-        @subclasses.each { |klass| klass.property(property_name, options) }
-      end
+      @subclasses.each { |klass| klass.property(property_name, options) } if defined? @subclasses
 
       condition = options.delete(:required)
       if condition
         message = options.delete(:message) || "is required for #{name}."
         required_properties[property_name] = { condition: condition, message: message }
-      else
-        fail ArgumentError, 'The :message option should be used with :required option.' if options.key?(:message)
+      elsif options.key?(:message)
+        raise ArgumentError, 'The :message option should be used with :required option.'
       end
     end
 
     class << self
       attr_reader :properties, :defaults
+      attr_reader :getters
       attr_reader :required_properties
     end
     instance_variable_set('@properties', Set.new)
+    instance_variable_set('@getters', Set.new)
     instance_variable_set('@defaults', {})
     instance_variable_set('@required_properties', {})
 
@@ -73,6 +70,7 @@ module Hashie
       super
       (@subclasses ||= Set.new) << klass
       klass.instance_variable_set('@properties', properties.dup)
+      klass.instance_variable_set('@getters', getters.dup)
       klass.instance_variable_set('@defaults', defaults.dup)
       klass.instance_variable_set('@required_properties', required_properties.dup)
     end
@@ -89,30 +87,29 @@ module Hashie
       required_properties.key? name
     end
 
+    private_class_method def self.define_getter_for(property_name)
+      return if getters.include?(property_name)
+      define_method(property_name) { |&block| self.[](property_name, &block) }
+      getters << property_name
+    end
+
+    private_class_method def self.define_setter_for(property_name)
+      setter = :"#{property_name}="
+      return if instance_methods.include?(setter)
+      define_method(setter) { |value| self.[]=(property_name, value) }
+    end
+
     # You may initialize a Dash with an attributes hash
     # just like you would many other kinds of data objects.
     def initialize(attributes = {}, &block)
       super(&block)
 
-      self.class.defaults.each_pair do |prop, value|
-        self[prop] = begin
-          val = value.dup
-          if val.is_a?(Proc)
-            val.arity == 1 ? val.call(self) : val.call
-          else
-            val
-          end
-        rescue TypeError
-          value
-        end
-      end
-
       initialize_attributes(attributes)
       assert_required_attributes_set!
     end
 
-    alias_method :_regular_reader, :[]
-    alias_method :_regular_writer, :[]=
+    alias _regular_reader []
+    alias _regular_writer []=
     private :_regular_reader, :_regular_writer
 
     # Retrieve a value from the Dash (will return the
@@ -159,25 +156,48 @@ module Hashie
       self
     end
 
+    def to_h
+      defaults = ::Hash[self.class.properties.map { |prop| [prop, self.class.defaults[prop]] }]
+
+      defaults.merge(self)
+    end
+    alias to_hash to_h
+
     def update_attributes!(attributes)
-      initialize_attributes(attributes)
+      update_attributes(attributes)
 
       self.class.defaults.each_pair do |prop, value|
+        next unless fetch(prop, nil).nil?
         self[prop] = begin
-          value.dup
+          val = value.dup
+          if val.is_a?(Proc)
+            val.arity == 1 ? val.call(self) : val.call
+          else
+            val
+          end
         rescue TypeError
           value
-        end if self[prop].nil?
+        end
       end
+
       assert_required_attributes_set!
     end
 
     private
 
     def initialize_attributes(attributes)
+      return unless attributes
+
+      cleaned_attributes = attributes.reject { |_attr, value| value.nil? }
+      update_attributes!(cleaned_attributes)
+    end
+
+    def update_attributes(attributes)
+      return unless attributes
+
       attributes.each_pair do |att, value|
         self[att] = value
-      end if attributes
+      end
     end
 
     def assert_property_exists!(property)
@@ -199,11 +219,12 @@ module Hashie
     end
 
     def fail_property_required_error!(property)
-      fail ArgumentError, "The property '#{property}' #{self.class.required_properties[property][:message]}"
+      raise ArgumentError,
+            "The property '#{property}' #{self.class.required_properties[property][:message]}"
     end
 
     def fail_no_property_error!(property)
-      fail NoMethodError, "The property '#{property}' is not defined for #{self.class.name}."
+      raise NoMethodError, "The property '#{property}' is not defined for #{self.class.name}."
     end
 
     def required?(property)
@@ -211,9 +232,9 @@ module Hashie
 
       condition = self.class.required_properties[property][:condition]
       case condition
-      when Proc   then !!(instance_exec(&condition))
-      when Symbol then !!(send(condition))
-      else             !!(condition)
+      when Proc   then !!instance_exec(&condition)
+      when Symbol then !!send(condition)
+      else             !!condition
       end
     end
   end
diff --git a/lib/hashie/extensions/active_support/core_ext/hash.rb b/lib/hashie/extensions/active_support/core_ext/hash.rb
new file mode 100644
index 0000000000000000000000000000000000000000..b8d71f7cad94d8cc8daac72798085ac496b0df97
--- /dev/null
+++ b/lib/hashie/extensions/active_support/core_ext/hash.rb
@@ -0,0 +1,14 @@
+module Hashie
+  module Extensions
+    module ActiveSupport
+      module CoreExt
+        module Hash
+          def except(*keys)
+            string_keys = keys.map { |key| convert_key(key) }
+            super(*string_keys)
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/hashie/extensions/coercion.rb b/lib/hashie/extensions/coercion.rb
index eb0a6ddc49ef06b75d5b01b7f7574f601d187365..a12490a8a7792cfac084cf67561655f5940d4164 100644
--- a/lib/hashie/extensions/coercion.rb
+++ b/lib/hashie/extensions/coercion.rb
@@ -1,5 +1,9 @@
 module Hashie
-  class CoercionError < StandardError; end
+  class CoercionError < StandardError
+    def initialize(key, value, into, message)
+      super("Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{message}")
+    end
+  end
 
   module Extensions
     module Coercion
@@ -10,22 +14,24 @@ module Hashie
         Rational   => :to_r,
         String     => :to_s,
         Symbol     => :to_sym
-      }
+      }.freeze
 
-      ABSTRACT_CORE_TYPES = if RubyVersion.new(RUBY_VERSION) >= RubyVersion.new('2.4.0')
-                              { Numeric => [Integer, Float, Complex, Rational] }
-                            else
-                              {
-                                Integer => [Fixnum, Bignum],
-                                Numeric => [Fixnum, Bignum, Float, Complex, Rational]
-                              }
-                            end
+      ABSTRACT_CORE_TYPES =
+        if RubyVersion.new(RUBY_VERSION) >= RubyVersion.new('2.4.0')
+          { Numeric => [Integer, Float, Complex, Rational] }
+        else
+          {
+            Integer => [Fixnum, Bignum],
+            Numeric => [Fixnum, Bignum, Float, Complex, Rational]
+          }
+        end
 
       def self.included(base)
         base.send :include, InstanceMethods
-        base.extend ClassMethods # NOTE: we wanna make sure we first define set_value_with_coercion before extending
-
-        base.send :alias_method, :set_value_without_coercion, :[]= unless base.method_defined?(:set_value_without_coercion)
+        base.extend ClassMethods
+        unless base.method_defined?(:set_value_without_coercion)
+          base.send :alias_method, :set_value_without_coercion, :[]=
+        end
         base.send :alias_method, :[]=, :set_value_with_coercion
       end
 
@@ -37,7 +43,7 @@ module Hashie
             begin
               value = self.class.fetch_coercion(into).call(value)
             rescue NoMethodError, TypeError => e
-              raise CoercionError, "Cannot coerce property #{key.inspect} from #{value.class} to #{into}: #{e.message}"
+              raise CoercionError.new(key, value, into, e.message)
             end
           end
 
@@ -78,7 +84,7 @@ module Hashie
           attrs.each { |key| key_coercions[key] = into }
         end
 
-        alias_method :coerce_keys, :coerce_key
+        alias coerce_keys coerce_key
 
         # Returns a hash of any existing key coercions.
         def key_coercions
@@ -97,7 +103,8 @@ module Hashie
         #
         # @param [Class] from the type you would like coerced.
         # @param [Class] into the class into which you would like the value coerced.
-        # @option options [Boolean] :strict (true) whether use exact source class only or include ancestors
+        # @option options [Boolean] :strict (true) whether use exact source class
+        #   only or include ancestors
         #
         # @example Coerce all hashes into this special type of hash
         #   class SpecialHash < Hash
@@ -159,10 +166,10 @@ module Hashie
 
         def build_coercion(type)
           if type.is_a? Enumerable
-            if type.class <= ::Hash
+            if type.class == ::Hash
               type, key_type, value_type = type.class, *type.first
               build_hash_coercion(type, key_type, value_type)
-            else # Enumerable but not Hash: Array, Set
+            else
               value_type = type.first
               type = type.class
               build_container_coercion(type, value_type)
@@ -180,7 +187,7 @@ module Hashie
               type.new(value)
             end
           else
-            fail TypeError, "#{type} is not a coercable type"
+            raise TypeError, "#{type} is not a coercable type"
           end
         end
 
diff --git a/lib/hashie/extensions/dash/indifferent_access.rb b/lib/hashie/extensions/dash/indifferent_access.rb
index 9d1b87bdf240b381e32e5b231e54e9beb515a7bb..50d4f935ce86ee267a6ae2d14731209e157aa92c 100644
--- a/lib/hashie/extensions/dash/indifferent_access.rb
+++ b/lib/hashie/extensions/dash/indifferent_access.rb
@@ -7,11 +7,32 @@ module Hashie
           base.send :include, Hashie::Extensions::IndifferentAccess
         end
 
+        def self.maybe_extend(base)
+          return unless requires_class_methods?(base)
+
+          base.extend(ClassMethods)
+        end
+
+        def self.requires_class_methods?(klass)
+          klass <= Hashie::Dash &&
+            !klass.singleton_class.included_modules.include?(ClassMethods)
+        end
+        private_class_method :requires_class_methods?
+
+        def to_h
+          defaults = ::Hash[self.class.properties.map do |prop|
+            [Hashie::Extensions::IndifferentAccess.convert_key(prop), self.class.defaults[prop]]
+          end]
+
+          defaults.merge(self)
+        end
+        alias to_hash to_h
+
         module ClassMethods
           # Check to see if the specified property has already been
           # defined.
           def property?(name)
-            name = translations[name.to_sym] if included_modules.include?(Hashie::Extensions::Dash::PropertyTranslation) && translation_exists?(name)
+            name = translations[name.to_sym] if translation_for?(name)
             name = name.to_s
             !!properties.find { |property| property.to_s == name }
           end
@@ -30,6 +51,13 @@ module Hashie
             name = name.to_s
             !!transforms.keys.find { |key| key.to_s == name }
           end
+
+          private
+
+          def translation_for?(name)
+            included_modules.include?(Hashie::Extensions::Dash::PropertyTranslation) &&
+              translation_exists?(name)
+          end
         end
       end
     end
diff --git a/lib/hashie/extensions/dash/predefined_values.rb b/lib/hashie/extensions/dash/predefined_values.rb
new file mode 100644
index 0000000000000000000000000000000000000000..2a0b7ca70de52ce705c8dd7ef36c56aefb8f1402
--- /dev/null
+++ b/lib/hashie/extensions/dash/predefined_values.rb
@@ -0,0 +1,88 @@
+module Hashie
+  module Extensions
+    module Dash
+      # Extends a Dash with the ability to accept only predefined values on a property.
+      #
+      # == Example
+      #
+      #   class PersonHash < Hashie::Dash
+      #     include Hashie::Extensions::Dash::PredefinedValues
+      #
+      #     property :gender, values: [:male, :female, :prefer_not_to_say]
+      #     property :age, values: (0..150) # a Range
+      #   end
+      #
+      #   person = PersonHash.new(gender: :male, age: -1)
+      #   # => ArgumentError: The value '-1' is not accepted for property 'age'
+      module PredefinedValues
+        def self.included(base)
+          base.instance_variable_set(:@values_for_properties, {})
+          base.extend(ClassMethods)
+          base.include(InstanceMethods)
+        end
+
+        module ClassMethods
+          attr_reader :values_for_properties
+
+          def inherited(klass)
+            super
+            klass.instance_variable_set(:@values_for_properties, values_for_properties.dup)
+          end
+
+          def property(property_name, options = {})
+            super
+
+            return unless (predefined_values = options[:values])
+
+            assert_predefined_values!(predefined_values)
+            set_predefined_values(property_name, predefined_values)
+          end
+
+          private
+
+          def assert_predefined_values!(predefined_values)
+            return if supported_type?(predefined_values)
+
+            raise ArgumentError, %(`values` accepts an Array or a Range.)
+          end
+
+          def supported_type?(predefined_values)
+            [::Array, ::Range].any? { |klass| predefined_values.is_a?(klass) }
+          end
+
+          def set_predefined_values(property_name, predefined_values)
+            @values_for_properties[property_name] = predefined_values
+          end
+        end
+
+        module InstanceMethods
+          def initialize(*)
+            super
+
+            assert_property_values!
+          end
+
+          private
+
+          def assert_property_values!
+            self.class.values_for_properties.each_key do |property|
+              value = send(property)
+
+              if value && !values_for_properties(property).include?(value)
+                fail_property_value_error!(property)
+              end
+            end
+          end
+
+          def fail_property_value_error!(property)
+            raise ArgumentError, "Invalid value for property '#{property}'"
+          end
+
+          def values_for_properties(property)
+            self.class.values_for_properties[property]
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/hashie/extensions/dash/property_translation.rb b/lib/hashie/extensions/dash/property_translation.rb
index be858b7cde05597cfabce6376c8afb1bc8f2b459..69a2d714642febd93b79255762271d8b83033546 100644
--- a/lib/hashie/extensions/dash/property_translation.rb
+++ b/lib/hashie/extensions/dash/property_translation.rb
@@ -35,12 +35,12 @@ module Hashie
       #   end
       #
       #   model = DataModelHash.new(id: '123', created: '2014-04-25 22:35:28')
-      #   model.id.class          #=> Fixnum
+      #   model.id.class          #=> Integer (Fixnum if you are using Ruby 2.3 or lower)
       #   model.created_at.class  #=> Time
       module PropertyTranslation
         def self.included(base)
           base.instance_variable_set(:@transforms, {})
-          base.instance_variable_set(:@translations_hash, {})
+          base.instance_variable_set(:@translations_hash, ::Hash.new { |hash, key| hash[key] = {} })
           base.extend(ClassMethods)
           base.send(:include, InstanceMethods)
         end
@@ -58,7 +58,9 @@ module Hashie
           end
 
           def permitted_input_keys
-            @permitted_input_keys ||= properties.map { |property| inverse_translations.fetch property, property }
+            @permitted_input_keys ||=
+              properties
+              .map { |property| inverse_translations.fetch property, property }
           end
 
           # Defines a property on the Trash. Options are as follows:
@@ -72,23 +74,16 @@ module Hashie
           def property(property_name, options = {})
             super
 
-            if options[:from]
-              if property_name == options[:from]
-                fail ArgumentError, "Property name (#{property_name}) and :from option must not be the same"
-              end
-
-              translations_hash[options[:from]] ||= {}
-              translations_hash[options[:from]][property_name] = options[:with] || options[:transform_with]
+            from = options[:from]
+            converter = options[:with]
+            transformer = options[:transform_with]
 
-              define_method "#{options[:from]}=" do |val|
-                self.class.translations_hash[options[:from]].each do |name, with|
-                  self[name] = with.respond_to?(:call) ? with.call(val) : val
-                end
-              end
-            else
-              if options[:transform_with].respond_to? :call
-                transforms[property_name] = options[:transform_with]
-              end
+            if from
+              fail_self_transformation_error!(property_name) if property_name == from
+              define_translation(from, property_name, converter || transformer)
+              define_writer_for_source_property(from)
+            elsif valid_transformer?(transformer)
+              transforms[property_name] = transformer
             end
           end
 
@@ -105,26 +100,50 @@ module Hashie
           end
 
           def translations
-            @translations ||= {}.tap do |h|
+            @translations ||= {}.tap do |translations|
               translations_hash.each do |(property_name, property_translations)|
-                if property_translations.size > 1
-                  h[property_name] = property_translations.keys
-                else
-                  h[property_name] = property_translations.keys.first
-                end
+                translations[property_name] =
+                  if property_translations.size > 1
+                    property_translations.keys
+                  else
+                    property_translations.keys.first
+                  end
               end
             end
           end
 
           def inverse_translations
-            @inverse_translations ||= {}.tap do |h|
+            @inverse_translations ||= {}.tap do |translations|
               translations_hash.each do |(property_name, property_translations)|
-                property_translations.keys.each do |k|
-                  h[k] = property_name
+                property_translations.each_key do |key|
+                  translations[key] = property_name
                 end
               end
             end
           end
+
+          private
+
+          def define_translation(from, property_name, translator)
+            translations_hash[from][property_name] = translator
+          end
+
+          def define_writer_for_source_property(property)
+            define_method "#{property}=" do |val|
+              __translations[property].each do |name, with|
+                self[name] = with.respond_to?(:call) ? with.call(val) : val
+              end
+            end
+          end
+
+          def fail_self_transformation_error!(property_name)
+            raise ArgumentError,
+                  "Property name (#{property_name}) and :from option must not be the same"
+          end
+
+          def valid_transformer?(transformer)
+            transformer.respond_to? :call
+          end
         end
 
         module InstanceMethods
@@ -134,6 +153,12 @@ module Hashie
           def []=(property, value)
             if self.class.translation_exists? property
               send("#{property}=", value)
+
+              if self.class.transformation_exists? property
+                super property, self.class.transformed_property(property, value)
+              elsif self.class.properties.include?(property)
+                super(property, value)
+              end
             elsif self.class.transformation_exists? property
               super property, self.class.transformed_property(property, value)
             elsif property_exists? property
@@ -158,6 +183,12 @@ module Hashie
             fail_no_property_error!(property) unless self.class.property?(property)
             true
           end
+
+          private
+
+          def __translations
+            self.class.translations_hash
+          end
         end
       end
     end
diff --git a/lib/hashie/extensions/deep_fetch.rb b/lib/hashie/extensions/deep_fetch.rb
index 9149f0d46f3c8ba28323c735917d53e6315a9db3..6131e8db4bafdce05ab53593faa23aefee28caa5 100644
--- a/lib/hashie/extensions/deep_fetch.rb
+++ b/lib/hashie/extensions/deep_fetch.rb
@@ -9,7 +9,8 @@ module Hashie
     #
     #  options.deep_fetch(:user, :non_existent_key) { 'a value' } #=> 'a value'
     #
-    # This is particularly useful for fetching values from deeply nested api responses or params hashes.
+    # This is particularly useful for fetching values from deeply nested api responses
+    #   or params hashes.
     module DeepFetch
       class UndefinedPathError < StandardError; end
 
@@ -19,8 +20,9 @@ module Hashie
             arg = Integer(arg) if obj.is_a? Array
             obj.fetch(arg)
           rescue ArgumentError, IndexError, NoMethodError => e
-            break block.call(arg) if block
-            raise UndefinedPathError, "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
+            break yield(arg) if block
+            raise UndefinedPathError,
+                  "Could not fetch path (#{args.join(' > ')}) at #{arg}", e.backtrace
           end
         end
       end
diff --git a/lib/hashie/extensions/deep_find.rb b/lib/hashie/extensions/deep_find.rb
index 616e65911b646cc27fe41b514ee8699e5cd754b3..0e77932f406a3d388254d4770b5dd654da1bb37d 100644
--- a/lib/hashie/extensions/deep_find.rb
+++ b/lib/hashie/extensions/deep_find.rb
@@ -1,3 +1,4 @@
+require 'hashie/extensions/deep_locate'
 module Hashie
   module Extensions
     module DeepFind
@@ -19,12 +20,17 @@ module Hashie
         _deep_find(key)
       end
 
-      alias_method :deep_detect, :deep_find
+      alias deep_detect deep_find
 
       # Performs a depth-first search on deeply nested data structures for
       # a key and returns all occurrences of the key.
       #
-      #  options = {users: [{location: {address: '123 Street'}}, {location: {address: '234 Street'}}]}
+      #  options = {
+      #    users: [
+      #      { location: {address: '123 Street'} },
+      #      { location: {address: '234 Street'}}
+      #    ]
+      #  }
       #  options.extend(Hashie::Extensions::DeepFind)
       #  options.deep_find_all(:address) # => ['123 Street', '234 Street']
       #
@@ -33,14 +39,17 @@ module Hashie
       #  end
       #
       #  my_hash = MyHash.new
-      #  my_hash[:users] = [{location: {address: '123 Street'}}, {location: {address: '234 Street'}}]
+      #  my_hash[:users] = [
+      #    {location: {address: '123 Street'}},
+      #    {location: {address: '234 Street'}}
+      #  ]
       #  my_hash.deep_find_all(:address) # => ['123 Street', '234 Street']
       def deep_find_all(key)
         matches = _deep_find_all(key)
         matches.empty? ? nil : matches
       end
 
-      alias_method :deep_select, :deep_find_all
+      alias deep_select deep_find_all
 
       private
 
@@ -49,7 +58,7 @@ module Hashie
       end
 
       def _deep_find_all(key, object = self, matches = [])
-        deep_locate_result = Hashie::Extensions::DeepLocate.deep_locate(key, object).tap do |result|
+        deep_locate_result = DeepLocate.deep_locate(key, object).tap do |result|
           result.map! { |element| element[key] }
         end
 
diff --git a/lib/hashie/extensions/deep_locate.rb b/lib/hashie/extensions/deep_locate.rb
index 930513a7c4dad03763ddc46b12bae9cc6beaec3b..2b33b23fc0c62e5f0362dfef384fe8081d71fc4b 100644
--- a/lib/hashie/extensions/deep_locate.rb
+++ b/lib/hashie/extensions/deep_locate.rb
@@ -14,10 +14,12 @@ module Hashie
       #     ...
       #   ]
       #
-      #   Hashie::Extensions::DeepLocate.deep_locate -> (key, value, object) { key == :title }, books
+      #   DeepLocate.deep_locate -> (key, value, object) { key == :title }, books
       #   # => [{:title=>"Ruby for beginners", :pages=>120}, ...]
       def self.deep_locate(comparator, object)
-        comparator = _construct_key_comparator(comparator, object) unless comparator.respond_to?(:call)
+        unless comparator.respond_to?(:call)
+          comparator = _construct_key_comparator(comparator, object)
+        end
 
         _deep_locate(comparator, object)
       end
@@ -53,30 +55,34 @@ module Hashie
       #   # http://ruby-journal.com/becareful-with-space-in-lambda-hash-rocket-syntax-between-ruby-1-dot-9-and-2-dot-0/
       #
       #   books.deep_locate -> (key, value, object) { key == :title && value.include?("Ruby") }
-      #   # => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"Ruby for the rest of us", :pages=>576}]
+      #   # => [{:title=>"Ruby for beginners", :pages=>120},
+      #   #     {:title=>"Ruby for the rest of us", :pages=>576}]
       #
       #   books.deep_locate -> (key, value, object) { key == :pages && value <= 120 }
-      #   # => [{:title=>"Ruby for beginners", :pages=>120}, {:title=>"CSS for intermediates", :pages=>80}]
+      #   # => [{:title=>"Ruby for beginners", :pages=>120},
+      #   #     {:title=>"CSS for intermediates", :pages=>80}]
       def deep_locate(comparator)
         Hashie::Extensions::DeepLocate.deep_locate(comparator, self)
       end
 
-      private
-
       def self._construct_key_comparator(search_key, object)
-        search_key = search_key.to_s if defined?(::ActiveSupport::HashWithIndifferentAccess) && object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
-        search_key = search_key.to_s if object.respond_to?(:indifferent_access?) && object.indifferent_access?
+        if object.respond_to?(:indifferent_access?) && object.indifferent_access? ||
+           activesupport_indifferent?(object)
+          search_key = search_key.to_s
+        end
 
         lambda do |non_callable_object|
           ->(key, _, _) { key == non_callable_object }
         end.call(search_key)
       end
+      private_class_method :_construct_key_comparator
 
       def self._deep_locate(comparator, object, result = [])
         if object.is_a?(::Enumerable)
           if object.any? { |value| _match_comparator?(value, comparator, object) }
             result.push object
           end
+
           (object.respond_to?(:values) ? object.values : object.entries).each do |value|
             _deep_locate(comparator, value, result)
           end
@@ -84,6 +90,7 @@ module Hashie
 
         result
       end
+      private_class_method :_deep_locate
 
       def self._match_comparator?(value, comparator, object)
         if object.is_a?(::Hash)
@@ -94,6 +101,13 @@ module Hashie
 
         comparator.call(key, value, object)
       end
+      private_class_method :_match_comparator?
+
+      def self.activesupport_indifferent?(object)
+        defined?(::ActiveSupport::HashWithIndifferentAccess) &&
+          object.is_a?(::ActiveSupport::HashWithIndifferentAccess)
+      end
+      private_class_method :activesupport_indifferent?
     end
   end
 end
diff --git a/lib/hashie/extensions/deep_merge.rb b/lib/hashie/extensions/deep_merge.rb
index 5d8fe34d6b3eff943ff3aaab5999a5841b9e1e5e..24fa679a3536b446a3211107d6de121cd7e38b5e 100644
--- a/lib/hashie/extensions/deep_merge.rb
+++ b/lib/hashie/extensions/deep_merge.rb
@@ -3,7 +3,7 @@ module Hashie
     module DeepMerge
       # Returns a new hash with +self+ and +other_hash+ merged recursively.
       def deep_merge(other_hash, &block)
-        copy = dup
+        copy = _deep_dup(self)
         copy.extend(Hashie::Extensions::DeepMerge) unless copy.respond_to?(:deep_merge!)
         copy.deep_merge!(other_hash, &block)
       end
@@ -18,17 +18,33 @@ module Hashie
 
       private
 
+      def _deep_dup(hash)
+        copy = hash.dup
+
+        copy.each do |key, value|
+          copy[key] =
+            if value.is_a?(::Hash)
+              _deep_dup(value)
+            else
+              Hashie::Utils.safe_dup(value)
+            end
+        end
+
+        copy
+      end
+
       def _recursive_merge(hash, other_hash, &block)
         other_hash.each do |k, v|
-          hash[k] = if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
-                      _recursive_merge(hash[k], v, &block)
-                    else
-                      if hash.key?(k) && block_given?
-                        block.call(k, hash[k], v)
-                      else
-                        v.respond_to?(:deep_dup) ? v.deep_dup : v
-                      end
-                    end
+          hash[k] =
+            if hash.key?(k) && hash[k].is_a?(::Hash) && v.is_a?(::Hash)
+              _recursive_merge(hash[k], v, &block)
+            elsif v.is_a?(::Hash)
+              _recursive_merge({}, v, &block)
+            elsif hash.key?(k) && block_given?
+              yield(k, hash[k], v)
+            else
+              v.respond_to?(:deep_dup) ? v.deep_dup : v
+            end
         end
         hash
       end
diff --git a/lib/hashie/extensions/ignore_undeclared.rb b/lib/hashie/extensions/ignore_undeclared.rb
index 9b506dd917a26d8868424680434ed6af24ead90f..64cf0b61b8ac2903b7dd078ba74ba27350036ec7 100644
--- a/lib/hashie/extensions/ignore_undeclared.rb
+++ b/lib/hashie/extensions/ignore_undeclared.rb
@@ -31,12 +31,11 @@ module Hashie
     module IgnoreUndeclared
       def initialize_attributes(attributes)
         return unless attributes
+
         klass = self.class
-        translations = klass.respond_to?(:translations) && klass.translations
-        attributes.each_pair do |att, value|
-          next unless klass.property?(att) || (translations && translations.include?(att))
-          self[att] = value
-        end
+        translations = klass.respond_to?(:translations) && klass.translations || []
+
+        super(attributes.select { |attr, _| klass.property?(attr) || translations.include?(attr) })
       end
 
       def property_exists?(property)
diff --git a/lib/hashie/extensions/indifferent_access.rb b/lib/hashie/extensions/indifferent_access.rb
index 55648a623c453da0315706109feb3cf8517cdebb..7702c147efa147d973a87c46fc8645f0fc7cb0fe 100644
--- a/lib/hashie/extensions/indifferent_access.rb
+++ b/lib/hashie/extensions/indifferent_access.rb
@@ -23,21 +23,26 @@ module Hashie
     #   h['baz'] # => 'blip'
     #
     module IndifferentAccess
+      include Hashie::Extensions::RubyVersionCheck
+
+      # @api private
+      def self.convert_key(key)
+        key.to_s
+      end
+
       def self.included(base)
-        Hashie::Extensions::Dash::IndifferentAccess::ClassMethods.tap do |extension|
-          base.extend(extension) if base <= Hashie::Dash && !base.singleton_class.included_modules.include?(extension)
-        end
+        Hashie::Extensions::Dash::IndifferentAccess.maybe_extend(base)
 
         base.class_eval do
           alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
           alias_method :[]=, :indifferent_writer
           alias_method :store, :indifferent_writer
-          %w(default update replace fetch delete key? values_at).each do |m|
+          %w[default update replace fetch delete key? values_at].each do |m|
             alias_method "regular_#{m}", m unless method_defined?("regular_#{m}")
             alias_method m, "indifferent_#{m}"
           end
 
-          %w(include? member? has_key?).each do |key_alias|
+          %w[include? member? has_key?].each do |key_alias|
             alias_method key_alias, :indifferent_key?
           end
 
@@ -68,15 +73,15 @@ module Hashie
       end
 
       def convert_key(key)
-        key.to_s
+        IndifferentAccess.convert_key(key)
       end
 
       # Iterates through the keys and values, reconverting them to
       # their proper indifferent state. Used when IndifferentAccess
       # is injecting itself into member hashes.
       def convert!
-        keys.each do |k|
-          regular_writer convert_key(k), indifferent_value(regular_delete(k))
+        keys.each do |k| # rubocop:disable Performance/HashEachMethods
+          indifferent_writer k, regular_delete(k)
         end
         self
       end
@@ -133,14 +138,42 @@ module Hashie
         self
       end
 
-      def merge(*)
-        super.convert!
+      def merge(*args)
+        result = super
+        return IndifferentAccess.inject!(result) if hash_lacking_indifference?(result)
+        result.convert!
       end
 
       def merge!(*)
         super.convert!
       end
 
+      def to_hash
+        {}.tap do |result|
+          each_pair { |key, value| result[key] = value }
+
+          if default_proc
+            result.default_proc = default_proc
+          else
+            result.default = default
+          end
+        end
+      end
+
+      with_minimum_ruby('2.5.0') do
+        def slice(*keys)
+          string_keys = keys.map { |key| convert_key(key) }
+          super(*string_keys)
+        end
+      end
+
+      with_minimum_ruby('3.0.0') do
+        def except(*keys)
+          string_keys = keys.map { |key| convert_key(key) }
+          super(*string_keys)
+        end
+      end
+
       protected
 
       def hash_lacking_indifference?(other)
diff --git a/lib/hashie/extensions/key_conflict_warning.rb b/lib/hashie/extensions/key_conflict_warning.rb
new file mode 100644
index 0000000000000000000000000000000000000000..875bad2c6d0aaa41231e300be0986189ffa225cc
--- /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 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/extensions/mash/define_accessors.rb b/lib/hashie/extensions/mash/define_accessors.rb
new file mode 100644
index 0000000000000000000000000000000000000000..f13ea2e5948e0cc911f136f7c2153a6cba113b06
--- /dev/null
+++ b/lib/hashie/extensions/mash/define_accessors.rb
@@ -0,0 +1,90 @@
+module Hashie
+  module Extensions
+    module Mash
+      module DefineAccessors
+        def self.included(klass)
+          klass.class_eval do
+            mod = Ext.new
+            include mod
+          end
+        end
+
+        def self.extended(obj)
+          included(obj.singleton_class)
+        end
+
+        class Ext < Module
+          def initialize
+            mod = self
+            define_method(:method_missing) do |method_name, *args, &block|
+              key, suffix = method_name_and_suffix(method_name)
+              case suffix
+              when '='.freeze
+                mod.define_writer(key, method_name)
+              when '?'.freeze
+                mod.define_predicate(key, method_name)
+              when '!'.freeze
+                mod.define_initializing_reader(key, method_name)
+              when '_'.freeze
+                mod.define_underbang_reader(key, method_name)
+              else
+                mod.define_reader(key, method_name)
+              end
+              send(method_name, *args, &block)
+            end
+          end
+
+          def define_reader(key, method_name)
+            define_method(method_name) do |&block|
+              if key? method_name
+                self.[](method_name, &block)
+              else
+                self.[](key, &block)
+              end
+            end
+          end
+
+          def define_writer(key, method_name)
+            define_method(method_name) do |value = nil|
+              if key? method_name
+                self.[](method_name, &proc)
+              else
+                assign_property(key, value)
+              end
+            end
+          end
+
+          def define_predicate(key, method_name)
+            define_method(method_name) do
+              if key? method_name
+                self.[](method_name, &proc)
+              else
+                !!self[key]
+              end
+            end
+          end
+
+          def define_initializing_reader(key, method_name)
+            define_method(method_name) do
+              if key? method_name
+                self.[](method_name, &proc)
+              else
+                initializing_reader(key)
+              end
+            end
+          end
+
+          def define_underbang_reader(key, method_name)
+            define_method(method_name) do
+              if key? method_name
+                self.[](key, &proc)
+              else
+                underbang_reader(key)
+              end
+            end
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/lib/hashie/extensions/mash/keep_original_keys.rb b/lib/hashie/extensions/mash/keep_original_keys.rb
index 1b529a7d67bc8e6f2e42fc3b332ad67c17551684..e25d8f99e7820398e988022e0d464bf1c499b3e4 100644
--- a/lib/hashie/extensions/mash/keep_original_keys.rb
+++ b/lib/hashie/extensions/mash/keep_original_keys.rb
@@ -14,14 +14,13 @@ module Hashie
       #   mash['string_key'] == mash[:string_key]  #=> true
       #   mash[:symbol_key] == mash['symbol_key']  #=> true
       module KeepOriginalKeys
-        private
-
         def self.included(descendant)
-          unless descendant <= Hashie::Mash
-            fail ArgumentError, "#{descendant} is not a kind of Hashie::Mash"
-          end
+          error_message = "#{descendant} is not a kind of Hashie::Mash"
+          raise ArgumentError, error_message unless descendant <= Hashie::Mash
         end
 
+        private
+
         # Converts the key when necessary to access the correct Mash key.
         #
         # @param [Object, String, Symbol] key the key to access.
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 0000000000000000000000000000000000000000..5f8c23154f5f3708153e9f3290cbaa60aebc6431
--- /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
diff --git a/lib/hashie/extensions/mash/safe_assignment.rb b/lib/hashie/extensions/mash/safe_assignment.rb
index 10a57dd55a687215626081253532710b2bc69dc2..dcf74888a293722e700afc5c649065016b2e7d45 100644
--- a/lib/hashie/extensions/mash/safe_assignment.rb
+++ b/lib/hashie/extensions/mash/safe_assignment.rb
@@ -3,7 +3,9 @@ module Hashie
     module Mash
       module SafeAssignment
         def custom_writer(key, *args) #:nodoc:
-          fail ArgumentError, "The property #{key} clashes with an existing method." if !key?(key) && respond_to?(key, true)
+          if !key?(key) && respond_to?(key, true)
+            raise ArgumentError, "The property #{key} clashes with an existing method."
+          end
           super
         end
 
diff --git a/lib/hashie/extensions/mash/symbolize_keys.rb b/lib/hashie/extensions/mash/symbolize_keys.rb
index 4e1d2be16bea7e06c2dcba5d4530f624b27675ea..c0f2a4deb5e9698d7b556bf735b42feb61d07542 100644
--- a/lib/hashie/extensions/mash/symbolize_keys.rb
+++ b/lib/hashie/extensions/mash/symbolize_keys.rb
@@ -5,7 +5,7 @@ module Hashie
       #
       # @example
       #   class LazyResponse < Hashie::Mash
-      #     include Hashie::Extensions::Mash::SymbolizedKeys
+      #     include Hashie::Extensions::Mash::SymbolizeKeys
       #   end
       #
       #   response = LazyResponse.new("id" => 123, "name" => "Rey").to_h
@@ -19,18 +19,18 @@ module Hashie
         # @return [void]
         # @raise [ArgumentError] when the base class isn't a Mash
         def self.included(base)
-          fail ArgumentError, "#{base} must descent from Hashie::Mash" unless base <= Hashie::Mash
+          raise ArgumentError, "#{base} must descent from Hashie::Mash" unless base <= Hashie::Mash
         end
 
         private
 
-        # Converts a key to a symbol
+        # Converts a key to a symbol, if possible
         #
         # @api private
-        # @param [String, Symbol] key the key to convert to a symbol
-        # @return [void]
+        # @param [<K>] key the key to attempt convert to a symbol
+        # @return [Symbol, K]
         def convert_key(key)
-          key.to_sym
+          key.respond_to?(:to_sym) ? key.to_sym : key
         end
       end
     end
diff --git a/lib/hashie/extensions/method_access.rb b/lib/hashie/extensions/method_access.rb
index 0ed2b22c9b26d21bd7fd702fb6f39e739e946b42..6543fee0d595985281b601172137bcfb1db5015e 100644
--- a/lib/hashie/extensions/method_access.rb
+++ b/lib/hashie/extensions/method_access.rb
@@ -27,7 +27,7 @@ module Hashie
     #
     #   user.not_declared # => NoMethodError
     module MethodReader
-      def respond_to?(name, include_private = false)
+      def respond_to_missing?(name, include_private = false)
         return true if key?(name.to_s) || key?(name.to_sym)
         super
       end
@@ -67,7 +67,7 @@ module Hashie
     #   h['awesome'] # => 'sauce'
     #
     module MethodWriter
-      def respond_to?(name, include_private = false)
+      def respond_to_missing?(name, include_private = false)
         return true if name.to_s =~ /=$/
         super
       end
@@ -106,7 +106,7 @@ module Hashie
     #   h.def? # => false
     #   h.hji? # => NoMethodError
     module MethodQuery
-      def respond_to?(name, include_private = false)
+      def respond_to_missing?(name, include_private = false)
         if query_method?(name) && indifferent_key?(key_from_query_method(name))
           true
         else
@@ -156,6 +156,22 @@ module Hashie
       end
     end
 
+    # A module shared between MethodOverridingWriter and MethodOverridingInitializer
+    # to contained shared logic. This module aids in redefining existing hash methods.
+    module RedefineMethod
+      protected
+
+      def method?(name)
+        methods.map(&:to_s).include?(name)
+      end
+
+      def redefine_method(method_name)
+        eigenclass = class << self; self; end
+        eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
+        eigenclass.__send__(:define_method, method_name, -> { self[method_name] })
+      end
+    end
+
     # MethodOverridingWriter gives you #key_name= shortcuts for
     # writing to your hash. It allows methods to be overridden by
     # #key_name= shortcuts and aliases those methods with two
@@ -181,6 +197,8 @@ module Hashie
     #   h.__zip # => [[['awesome', 'sauce'], ['zip', 'a-dee-doo-dah']]]
     #
     module MethodOverridingWriter
+      include RedefineMethod
+
       def convert_key(key)
         key.to_s
       end
@@ -205,16 +223,6 @@ module Hashie
       def already_overridden?(name)
         method?("__#{name}")
       end
-
-      def method?(name)
-        methods.map(&:to_s).include?(name)
-      end
-
-      def redefine_method(method_name)
-        eigenclass = class << self; self; end
-        eigenclass.__send__(:alias_method, "__#{method_name}", method_name)
-        eigenclass.__send__(:define_method, method_name, -> { self[method_name] })
-      end
     end
 
     # A macro module that will automatically include MethodReader,
@@ -225,10 +233,35 @@ module Hashie
     # underscores.
     module MethodAccessWithOverride
       def self.included(base)
-        [MethodReader, MethodOverridingWriter, MethodQuery].each do |mod|
+        [MethodReader, MethodOverridingWriter,
+         MethodQuery, MethodOverridingInitializer].each do |mod|
           base.send :include, mod
         end
       end
     end
+
+    # MethodOverridingInitializer allows you to override default hash
+    # methods when passing in values from an existing hash. The overriden
+    # methods are aliased with two leading underscores.
+    #
+    # @example
+    #   class MyHash < Hash
+    #     include Hashie::Extensions::MethodOverridingInitializer
+    #   end
+    #
+    #   h = MyHash.new(zip: 'a-dee-doo-dah')
+    #   h.zip # => 'a-dee-doo-dah'
+    #   h.__zip # => [[['zip', 'a-dee-doo-dah']]]
+    module MethodOverridingInitializer
+      include RedefineMethod
+
+      def initialize(hash = {})
+        hash.each do |key, value|
+          skey = key.to_s
+          redefine_method(skey) if method?(skey)
+          self[skey] = value
+        end
+      end
+    end
   end
 end
diff --git a/lib/hashie/extensions/parsers/yaml_erb_parser.rb b/lib/hashie/extensions/parsers/yaml_erb_parser.rb
index b3119393a920c6bab6bb3c8544f5d110064854e2..ed9ae4f2558f43e9b2c44bda14af43d546415884 100644
--- a/lib/hashie/extensions/parsers/yaml_erb_parser.rb
+++ b/lib/hashie/extensions/parsers/yaml_erb_parser.rb
@@ -1,22 +1,46 @@
 require 'yaml'
 require 'erb'
+require 'pathname'
+
 module Hashie
   module Extensions
     module Parsers
       class YamlErbParser
-        def initialize(file_path)
+        def initialize(file_path, options = {})
           @content = File.read(file_path)
           @file_path = file_path.is_a?(Pathname) ? file_path.to_s : file_path
+          @options = options
         end
 
         def perform
           template = ERB.new(@content)
           template.filename = @file_path
-          YAML.load template.result
+          permitted_classes = @options.fetch(:permitted_classes) { [] }
+          permitted_symbols = @options.fetch(:permitted_symbols) { [] }
+          aliases = @options.fetch(:aliases) { true }
+
+          yaml_safe_load(template, permitted_classes, permitted_symbols, aliases)
         end
 
-        def self.perform(file_path)
-          new(file_path).perform
+        def self.perform(file_path, options = {})
+          new(file_path, options).perform
+        end
+
+        private
+
+        if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('3.1.0') # Ruby 2.6+
+          def yaml_safe_load(template, permitted_classes, permitted_symbols, aliases)
+            YAML.safe_load(
+              template.result,
+              permitted_classes: permitted_classes,
+              permitted_symbols: permitted_symbols,
+              aliases: aliases
+            )
+          end
+        else
+          def yaml_safe_load(template, permitted_classes, permitted_symbols, aliases)
+            YAML.safe_load(template.result, permitted_classes, permitted_symbols, aliases)
+          end
         end
       end
     end
diff --git a/lib/hashie/extensions/ruby_version_check.rb b/lib/hashie/extensions/ruby_version_check.rb
index bcd34360bc5e5b5a3a84feee771e39956a0e4fc4..b287230e10351f3ff96a016de03354bb6b3a0159 100644
--- a/lib/hashie/extensions/ruby_version_check.rb
+++ b/lib/hashie/extensions/ruby_version_check.rb
@@ -9,7 +9,11 @@ module Hashie
 
       module ClassMethods
         def with_minimum_ruby(version)
-          yield if RubyVersion.new(RUBY_VERSION) >= RubyVersion.new(version)
+          yield if with_minimum_ruby?(version)
+        end
+
+        def with_minimum_ruby?(version)
+          RubyVersion.new(RUBY_VERSION) >= RubyVersion.new(version)
         end
       end
     end
diff --git a/lib/hashie/extensions/strict_key_access.rb b/lib/hashie/extensions/strict_key_access.rb
index 78f86f3b0b17d9520c49d2676bec753c12a218b5..4593b3fea6920e39c70e2d9a174b150b11040bc2 100644
--- a/lib/hashie/extensions/strict_key_access.rb
+++ b/lib/hashie/extensions/strict_key_access.rb
@@ -1,6 +1,7 @@
 module Hashie
   module Extensions
-    # SRP: This extension will fail an error whenever a key is accessed that does not exist in the hash.
+    # SRP: This extension will fail an error whenever a key is accessed
+    #   that does not exist in the hash.
     #
     #   EXAMPLE:
     #
@@ -15,12 +16,15 @@ module Hashie
     #     >> hash[:cow]
     #       KeyError: key not found: :cow
     #
-    # NOTE: For googlers coming from Python to Ruby, this extension makes a Hash behave more like a "Dictionary".
+    # NOTE: For googlers coming from Python to Ruby, this extension makes a Hash
+    # behave more like a "Dictionary".
     #
     module StrictKeyAccess
       class DefaultError < StandardError
-        def initialize(msg = 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense', *args)
-          super
+        def initialize
+          super('Setting or using a default with Hashie::Extensions::StrictKeyAccess'\
+                ' does not make sense'
+          )
         end
       end
 
@@ -46,27 +50,26 @@ module Hashie
       end
 
       def default(_ = nil)
-        fail DefaultError
+        raise DefaultError
       end
 
       def default=(_)
-        fail DefaultError
+        raise DefaultError
       end
 
       def default_proc
-        fail DefaultError
+        raise DefaultError
       end
 
       def default_proc=(_)
-        fail DefaultError
+        raise DefaultError
       end
 
       def key(value)
-        result = super
-        if result.nil? && (!key?(result) || self[result] != value)
-          fail KeyError, "key not found with value of #{value.inspect}"
-        else
-          result
+        super.tap do |result|
+          if result.nil? && (!key?(result) || self[result] != value)
+            raise KeyError, "key not found with value of #{value.inspect}"
+          end
         end
       end
     end
diff --git a/lib/hashie/extensions/stringify_keys.rb b/lib/hashie/extensions/stringify_keys.rb
index 41b0fe35f35abea6dfd4b0633372c12ed2930bca..bad29b698938217395b52f64fecda0bf4956ddaa 100644
--- a/lib/hashie/extensions/stringify_keys.rb
+++ b/lib/hashie/extensions/stringify_keys.rb
@@ -44,7 +44,7 @@ module Hashie
         #   test # => {'abc' => 'def'}
         def stringify_keys!(hash)
           hash.extend(Hashie::Extensions::StringifyKeys) unless hash.respond_to?(:stringify_keys!)
-          hash.keys.each do |k|
+          hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
             stringify_keys_recursively!(hash[k])
             hash[k.to_s] = hash.delete(k)
           end
diff --git a/lib/hashie/extensions/symbolize_keys.rb b/lib/hashie/extensions/symbolize_keys.rb
index 274889c16dfdcd65f945be5400b685c824e67a1e..d1da9cb83f4fba5f2ee91e58d171dd13e3487e23 100644
--- a/lib/hashie/extensions/symbolize_keys.rb
+++ b/lib/hashie/extensions/symbolize_keys.rb
@@ -44,9 +44,9 @@ module Hashie
         #   test # => {:abc => 'def'}
         def symbolize_keys!(hash)
           hash.extend(Hashie::Extensions::SymbolizeKeys) unless hash.respond_to?(:symbolize_keys!)
-          hash.keys.each do |k|
+          hash.keys.each do |k| # rubocop:disable Performance/HashEachMethods
             symbolize_keys_recursively!(hash[k])
-            hash[k.to_sym] = hash.delete(k)
+            hash[convert_key(k)] = hash.delete(k)
           end
           hash
         end
@@ -61,6 +61,17 @@ module Hashie
             symbolize_keys!(new_hash)
           end
         end
+
+        private
+
+        # Converts a key to a symbol, if possible
+        #
+        # @api private
+        # @param [<K>] key the key to attempt convert to a symbol
+        # @return [Symbol, K]
+        def convert_key(key)
+          key.respond_to?(:to_sym) ? key.to_sym : key
+        end
       end
 
       class << self
diff --git a/lib/hashie/hash.rb b/lib/hashie/hash.rb
index 87e19231bea314276fc92d06a145254bc25b54e7..a2bd7ab3e4f3608f67fb419e5a0b8806147d3b42 100644
--- a/lib/hashie/hash.rb
+++ b/lib/hashie/hash.rb
@@ -17,21 +17,22 @@ module Hashie
     # Converts a mash back to a hash (with stringified or symbolized keys)
     def to_hash(options = {})
       out = {}
-      keys.each do |k|
-        assignment_key = if options[:stringify_keys]
-                           k.to_s
-                         elsif options[:symbolize_keys]
-                           k.to_s.to_sym
-                         else
-                           k
-                         end
+      each_key do |k|
+        assignment_key =
+          if options[:stringify_keys]
+            k.to_s
+          elsif options[:symbolize_keys] && k.respond_to?(:to_sym)
+            k.to_sym
+          else
+            k
+          end
         if self[k].is_a?(Array)
           out[assignment_key] ||= []
           self[k].each do |array_object|
-            out[assignment_key] << (Hash === array_object ? flexibly_convert_to_hash(array_object, options) : array_object)
+            out[assignment_key] << maybe_convert_to_hash(array_object, options)
           end
         else
-          out[assignment_key] = (Hash === self[k] || self[k].respond_to?(:to_hash)) ? flexibly_convert_to_hash(self[k], options) : self[k]
+          out[assignment_key] = maybe_convert_to_hash(self[k], options)
         end
       end
       out
@@ -44,8 +45,14 @@ module Hashie
 
     private
 
+    def maybe_convert_to_hash(object, options)
+      return object unless object.is_a?(Hash) || object.respond_to?(:to_hash)
+
+      flexibly_convert_to_hash(object, options)
+    end
+
     def flexibly_convert_to_hash(object, options = {})
-      if object.method(:to_hash).arity == 0
+      if object.method(:to_hash).arity.zero?
         object.to_hash
       else
         object.to_hash(options)
diff --git a/lib/hashie/mash.rb b/lib/hashie/mash.rb
index 390a731b899264f6176cb5fdac40895670206b7e..d5cb30843d0552e2c8482d6415892813faa1ab5a 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
@@ -15,9 +16,12 @@ module Hashie
   #
   # * No punctuation: Returns the value of the hash for that key, or nil if none exists.
   # * Assignment (<tt>=</tt>): Sets the attribute of the given method name.
-  # * Existence (<tt>?</tt>): Returns true or false depending on whether that key has been set.
-  # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
-  # * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key.  Used to test existance in deep Mashes.
+  # * Truthiness (<tt>?</tt>): Returns true or false depending on the truthiness of
+  #   the attribute, or false if the key is not set.
+  # * Bang (<tt>!</tt>): Forces the existence of this key, used for deep Mashes. Think of it
+  #   as "touch" for mashes.
+  # * Under Bang (<tt>_</tt>): Like Bang, but returns a new Mash rather than creating a key.
+  #   Used to test existance in deep Mashes.
   #
   # == Basic Example
   #
@@ -58,51 +62,20 @@ module Hashie
   #   mash.author # => <Mash>
   #
   class Mash < Hash
-    include Hashie::Extensions::PrettyInspect
     include Hashie::Extensions::RubyVersionCheck
+    extend Hashie::Extensions::KeyConflictWarning
 
-    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
-
-    # Inheritance hook that sets class configuration when inherited.
-    #
-    # @api semipublic
-    # @return [void]
-    def self.inherited(subclass)
-      super
-      subclass.disable_warnings if disable_warnings?
-    end
+    ALLOWED_SUFFIXES = %w[? ! = _].freeze
 
     def self.load(path, options = {})
       @_mashes ||= new
 
       return @_mashes[path] if @_mashes.key?(path)
-      fail ArgumentError, "The following file doesn't exist: #{path}" unless File.file?(path)
+      raise ArgumentError, "The following file doesn't exist: #{path}" unless File.file?(path)
 
-      parser = options.fetch(:parser) {  Hashie::Extensions::Parsers::YamlErbParser }
-      @_mashes[path] = new(parser.perform(path)).freeze
+      options = options.dup
+      parser = options.delete(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
+      @_mashes[path] = new(parser.perform(path, options)).freeze
     end
 
     def to_module(mash_method_name = :settings)
@@ -114,7 +87,11 @@ module Hashie
       end
     end
 
-    alias_method :to_s, :inspect
+    def with_accessors!
+      extend Hashie::Extensions::Mash::DefineAccessors
+    end
+
+    alias to_s inspect
 
     # If you pass in an existing hash, it will
     # convert it to a Mash including recursively
@@ -125,13 +102,26 @@ module Hashie
       default ? super(default) : super(&blk)
     end
 
-    class << self; alias_method :[], :new; end
+    # Creates a new anonymous subclass with key conflict
+    # warnings disabled. You may pass an array of method
+    # symbols to restrict the disabled warnings 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 ||= {}
+      @memoized_classes[method_keys] ||= Class.new(self) do
+        disable_warnings(*method_keys)
+      end
+    end
+
+    class << self; alias [] new; end
 
-    alias_method :regular_reader, :[]
-    alias_method :regular_writer, :[]=
+    alias regular_reader []
+    alias regular_writer []=
 
-    # Retrieves an attribute set in the Mash. Will convert
-    # any key passed in to a string before retrieving.
+    # Retrieves an attribute set in the Mash. Will convert a key passed in
+    # as a symbol to a string before retrieving.
     def custom_reader(key)
       default_proc.call(self, key) if default_proc && !key?(key)
       value = regular_reader(convert_key(key))
@@ -139,18 +129,16 @@ module Hashie
       value
     end
 
-    # Sets an attribute in the Mash. Key will be converted to
-    # a string before it is set, and Hashes will be converted
-    # into Mashes for nesting purposes.
+    # Sets an attribute in the Mash. Symbol keys will be converted to
+    # strings before being set, and Hashes will be converted into Mashes
+    # for nesting purposes.
     def custom_writer(key, value, convert = true) #:nodoc:
-      key_as_symbol = (key = convert_key(key)).to_sym
-
-      log_built_in_message(key_as_symbol) if log_collision?(key_as_symbol)
-      regular_writer(key, convert ? convert_value(value) : value)
+      log_built_in_message(key) if key.respond_to?(:to_sym) && log_collision?(key.to_sym)
+      regular_writer(convert_key(key), convert ? convert_value(value) : value)
     end
 
-    alias_method :[], :custom_reader
-    alias_method :[]=, :custom_writer
+    alias [] custom_reader
+    alias []= custom_writer
 
     # This is the bang method reader, it will return a new Mash
     # if there isn't a value already assigned to the key requested.
@@ -183,45 +171,89 @@ module Hashie
       super(*keys.map { |key| convert_key(key) })
     end
 
-    alias_method :regular_dup, :dup
+    # Returns a new instance of the class it was called on, using its keys as
+    # values, and its values as keys. The new values and keys will always be
+    # strings.
+    def invert
+      self.class.new(super)
+    end
+
+    # Returns a new instance of the class it was called on, containing elements
+    # for which the given block returns false.
+    def reject(&blk)
+      self.class.new(super(&blk))
+    end
+
+    # Returns a new instance of the class it was called on, containing elements
+    # for which the given block returns true.
+    def select(&blk)
+      self.class.new(super(&blk))
+    end
+
+    alias regular_dup dup
     # Duplicates the current mash as a new mash.
     def dup
-      self.class.new(self, default)
+      self.class.new(self, default, &default_proc)
     end
 
-    alias_method :regular_key?, :key?
+    alias regular_key? key?
     def key?(key)
       super(convert_key(key))
     end
-    alias_method :has_key?, :key?
-    alias_method :include?, :key?
-    alias_method :member?, :key?
+    alias has_key? key?
+    alias include? key?
+    alias member? key?
+
+    if with_minimum_ruby?('2.6.0')
+      # Performs a deep_update on a duplicate of the
+      # current mash.
+      def deep_merge(*other_hashes, &blk)
+        dup.deep_update(*other_hashes, &blk)
+      end
 
-    # Performs a deep_update on a duplicate of the
-    # current mash.
-    def deep_merge(other_hash, &blk)
-      dup.deep_update(other_hash, &blk)
+      # Recursively merges this mash with the passed
+      # in hash, merging each hash in the hierarchy.
+      def deep_update(*other_hashes, &blk)
+        other_hashes.each do |other_hash|
+          _deep_update(other_hash, &blk)
+        end
+        self
+      end
+    else
+      # Performs a deep_update on a duplicate of the
+      # current mash.
+      def deep_merge(other_hash, &blk)
+        dup.deep_update(other_hash, &blk)
+      end
+
+      # Recursively merges this mash with the passed
+      # in hash, merging each hash in the hierarchy.
+      def deep_update(other_hash, &blk)
+        _deep_update(other_hash, &blk)
+        self
+      end
     end
-    alias_method :merge, :deep_merge
 
-    # Recursively merges this mash with the passed
-    # in hash, merging each hash in the hierarchy.
-    def deep_update(other_hash, &blk)
+    # Alias these lexically so they get the correctly defined
+    # #deep_merge and #deep_update based on ruby version.
+    alias merge deep_merge
+    alias deep_merge! deep_update
+    alias update deep_update
+    alias merge! update
+
+    def _deep_update(other_hash, &blk)
       other_hash.each_pair do |k, v|
         key = convert_key(k)
-        if regular_reader(key).is_a?(Mash) && v.is_a?(::Hash)
+        if v.is_a?(::Hash) && key?(key) && regular_reader(key).is_a?(Mash)
           custom_reader(key).deep_update(v, &blk)
         else
           value = convert_value(v, true)
-          value = convert_value(blk.call(key, self[k], value), true) if blk && self.key?(k)
+          value = convert_value(yield(key, self[k], value), true) if blk && key?(k)
           custom_writer(key, value, false)
         end
       end
-      self
     end
-    alias_method :deep_merge!, :deep_update
-    alias_method :update, :deep_update
-    alias_method :merge!, :update
+    private :_deep_update
 
     # Assigns a value to a key
     def assign_property(name, value)
@@ -263,7 +295,7 @@ module Hashie
       method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
     end
 
-    def method_missing(method_name, *args, &blk)
+    def method_missing(method_name, *args, &blk) # rubocop:disable Style/MethodMissing
       return self.[](method_name, &blk) if key?(method_name)
       name, suffix = method_name_and_suffix(method_name)
       case suffix
@@ -296,6 +328,36 @@ module Hashie
       end
     end
 
+    with_minimum_ruby('2.4.0') do
+      def transform_values(&blk)
+        self.class.new(super(&blk))
+      end
+
+      # Returns a new instance of the class it was called on, with nil values
+      # removed.
+      def compact
+        self.class.new(super)
+      end
+    end
+
+    with_minimum_ruby('2.5.0') do
+      def slice(*keys)
+        string_keys = keys.map { |key| convert_key(key) }
+        self.class.new(super(*string_keys))
+      end
+
+      def transform_keys(&blk)
+        self.class.new(super(&blk))
+      end
+    end
+
+    with_minimum_ruby('3.0.0') do
+      def except(*keys)
+        string_keys = keys.map { |key| convert_key(key) }
+        self.class.new(super(*string_keys))
+      end
+    end
+
     protected
 
     def method_name_and_suffix(method_name)
@@ -313,7 +375,7 @@ module Hashie
     end
 
     def convert_key(key) #:nodoc:
-      key.to_s
+      key.respond_to?(:to_sym) ? key.to_s : key
     end
 
     def convert_value(val, duping = false) #:nodoc:
@@ -325,8 +387,6 @@ module Hashie
       when ::Hash
         val = val.dup if duping
         self.class.new(val)
-      when Array
-        val.map { |e| convert_value(e) }
       when ::Array
         Array.new(val.map { |e| convert_value(e) })
       else
@@ -337,20 +397,26 @@ module Hashie
     private
 
     def log_built_in_message(method_key)
-      return if self.class.disable_warnings?
+      return if self.class.disable_warnings?(method_key)
 
       method_information = Hashie::Utils.method_information(method(method_key))
 
       Hashie.logger.warn(
         'You are setting a key that conflicts with a built-in method ' \
         "#{self.class}##{method_key} #{method_information}. " \
-        'This can cause unexpected behavior when accessing the key via as a ' \
+        'This can cause unexpected behavior when accessing the key as a ' \
         'property. You can still access the key via the #[] method.'
       )
     end
 
     def log_collision?(method_key)
-      respond_to?(method_key) && !self.class.disable_warnings? &&
+      return unless method_key.is_a?(String) || method_key.is_a?(Symbol)
+      return unless respond_to?(method_key)
+
+      _, suffix = method_name_and_suffix(method_key)
+
+      (!suffix || suffix == '='.freeze) &&
+        !self.class.disable_warnings?(method_key) &&
         !(regular_key?(method_key) || regular_key?(method_key.to_s))
     end
   end
diff --git a/lib/hashie/railtie.rb b/lib/hashie/railtie.rb
index 65cd007edbb191e69eca910b0fcd3c8d2992d8fa..c5f05b74750a25a2bcafdfee303e1fa87217fff5 100644
--- a/lib/hashie/railtie.rb
+++ b/lib/hashie/railtie.rb
@@ -7,6 +7,13 @@ begin
       initializer 'hashie.configure_logger', after: 'initialize_logger' do
         Hashie.logger = Rails.logger
       end
+
+      initializer 'hashie.patch_hash_except', after: 'load_active_support' do
+        if Rails::VERSION::MAJOR >= 6
+          require 'hashie/extensions/active_support/core_ext/hash'
+          Hashie::Mash.send(:include, Hashie::Extensions::ActiveSupport::CoreExt::Hash)
+        end
+      end
     end
   end
 rescue LoadError => e
diff --git a/lib/hashie/rash.rb b/lib/hashie/rash.rb
index 1e059f357253e22178c3aa3731277127629d3e71..21416845e2730724330216aad554bb6f7085f5dc 100644
--- a/lib/hashie/rash.rb
+++ b/lib/hashie/rash.rb
@@ -64,7 +64,7 @@ module Hashie
     # Raise (or yield) unless something matches the key.
     #
     def fetch(*args)
-      fail ArgumentError, "Expected 1-2 arguments, got #{args.length}" \
+      raise ArgumentError, "Expected 1-2 arguments, got #{args.length}" \
         unless (1..2).cover?(args.length)
 
       key, default = args
@@ -78,7 +78,7 @@ module Hashie
       elsif default
         default
       else
-        fail KeyError, "key not found: #{key.inspect}"
+        raise KeyError, "key not found: #{key.inspect}"
       end
     end
 
@@ -117,7 +117,7 @@ module Hashie
         end
 
       when Regexp
-        # Reverse operation: `rash[/regexp/]` returns all the hash's string keys which match the regexp
+        # Reverse operation: `rash[/regexp/]` returns all string keys matching the regexp
         @hash.each do |key, val|
           yield val if key.is_a?(String) && query =~ key
         end
@@ -125,18 +125,18 @@ module Hashie
     end
 
     def method_missing(*args, &block)
-      @hash.send(*args, &block)
+      @hash.send(*args, &block) || super
     end
 
-    def respond_to_missing?(*args)
-      @hash.respond_to?(*args)
+    def respond_to_missing?(method_name, _include_private = false)
+      @hash.respond_to?(method_name)
     end
 
     private
 
     def optimize_if_necessary!
       return unless (@lookups += 1) >= @optimize_every
-      @regexes = @regex_counts.sort_by { |_, count| -count }.map { |regex, _| regex }
+      @regexes = @regexes.sort_by { |regex| -@regex_counts[regex] }
       @lookups = 0
     end
   end
diff --git a/lib/hashie/utils.rb b/lib/hashie/utils.rb
index d8e05fe0809b512ad3522bed70a180b7f1774c58..9dc62943e3a0a1ffdb1f827944dcc3f3d3dee073 100644
--- a/lib/hashie/utils.rb
+++ b/lib/hashie/utils.rb
@@ -12,5 +12,33 @@ module Hashie
         "defined in #{bound_method.owner}"
       end
     end
+
+    # Duplicates a value or returns the value when it is not duplicable
+    #
+    # @api public
+    #
+    # @param value [Object] the value to safely duplicate
+    # @return [Object] the duplicated value
+    def self.safe_dup(value)
+      case value
+      when Complex, FalseClass, NilClass, Rational, Method, Symbol, TrueClass, *integer_classes
+        value
+      else
+        value.dup
+      end
+    end
+
+    # Lists the classes Ruby uses for integers
+    #
+    # @api private
+    # @return [Array<Class>]
+    def self.integer_classes
+      @integer_classes ||=
+        if 0.class == Integer
+          [Integer]
+        else
+          [Fixnum, Bignum] # rubocop:disable Lint/UnifiedInteger
+        end
+    end
   end
 end
diff --git a/lib/hashie/version.rb b/lib/hashie/version.rb
index 81b374d9dffd2ccbdc414f7e664a445161a622f5..e4ffc63d065b7393aa1fe438c4cd56706f83ac13 100644
--- a/lib/hashie/version.rb
+++ b/lib/hashie/version.rb
@@ -1,3 +1,3 @@
 module Hashie
-  VERSION = '3.5.5'
+  VERSION = '5.0.0'.freeze
 end
diff --git a/mascot.svg b/mascot.svg
new file mode 100644
index 0000000000000000000000000000000000000000..1c193acf3eaf2f6669c9abb3ab9fa775bfe30761
--- /dev/null
+++ b/mascot.svg
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="140px" viewBox="0 0 140 72" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+    <title>eierlegende-Wollmilchsau</title>
+    <g id="eierlegende-Wollmilchsau" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+        <g id="sheep-legs" transform="translate(25.000000, 48.000000)">
+            <polygon id="Fill-1" fill="#5B5047" points="3.28150573 22.2913257 5.47463175 22.2913257 7.71685761 1.34206219 6.97750641e-15 0"></polygon>
+            <polygon id="Fill-2" fill="#4A4847" points="3.09328969 21.01473 3.28150573 22.2913257 5.47463175 22.2913257 5.60556465 21.01473"></polygon>
+            <polygon id="Fill-3" fill="#5B5047" points="8.62520458 3.45335516 10.2209493 22.2913257 11.2929624 22.2913257 12.3649755 22.2913257 14.6563011 3.45335516"></polygon>
+            <path d="M10.0981997,20.8837971 L10.212766,22.2913257 L12.3567921,22.2913257 L12.512275,20.9983633 C11.710311,20.9738134 10.908347,20.9410802 10.0981997,20.8837971" id="Fill-4" fill="#4A4847"></path>
+            <polygon id="Fill-6" fill="#5B5047" points="33.2394763 0.00687397709 36.520982 22.2900164 38.7059247 22.2900164 40.9563339 1.34075286"></polygon>
+            <polygon id="Fill-9" fill="#4A4847" points="36.3256956 21.01473 36.5139116 22.2913257 38.7070376 22.2913257 38.8461538 21.01473"></polygon>
+            <polygon id="Fill-11" fill="#5B5047" points="41.8646809 3.45204583 43.4522422 22.2900164 45.5962684 22.2900164 47.8957774 3.45204583"></polygon>
+            <path d="M43.3387889,20.8837971 L43.4533552,22.2913257 L45.5973813,22.2913257 L45.7528642,20.9983633 C44.9509002,20.9738134 44.1407529,20.9410802 43.3387889,20.8837971" id="Fill-14" fill="#4A4847"></path>
+        </g>
+        <g id="cow" transform="translate(46.000000, 51.000000)" fill="#FF8979">
+            <path d="M-2.32583547e-15,2.1302471 L16.7815556,5.81458867e-15 L16.7947735,0.0818759995 C16.8677826,0.563894377 17.1843047,3.0902876 16.2138367,5.41631425 L16.2732066,5.46856209 C16.7632236,5.90529459 17.5917782,6.78185513 17.1135759,7.36631121 C16.7304142,7.84178675 16.0255826,7.26237727 15.5103263,6.70492281 C15.1579919,7.21018635 14.7196267,7.68129854 14.1763484,8.09005293 L14.2228817,8.14612804 C14.7288135,8.76084697 15.3624413,9.69920636 14.6916882,9.88112821 C14.0382099,10.0583648 13.3989235,9.32307319 13.0266609,8.77726008 L13.0273367,8.77773396 C12.7527621,8.90632016 12.459235,9.02193087 12.1454956,9.12271794 L12.1561102,9.17307603 C12.3062144,9.89856612 12.3645037,10.7754931 11.7990265,10.8660727 C11.1845035,10.9599558 10.9267512,10.0925341 10.8200647,9.43544213 C10.3705196,9.50835133 9.88767131,9.55504807 9.36987184,9.57263354 L9.63429194,9.56087879 L9.64055994,9.65726585 C9.68469385,10.4194892 9.60576207,11.3342537 8.95408647,11.241375 C8.26552619,11.1432392 8.13165093,10.2321899 8.12799063,9.56328502 C7.60089344,9.53627567 7.10794545,9.47941243 6.64693206,9.39692261 L6.63344985,9.44615073 C6.43020441,10.1788496 6.08189486,11.0132444 5.5857315,10.7817413 C5.07698629,10.5448366 5.20925654,9.72057923 5.40045566,9.09046993 C4.90930645,8.93380639 4.46278278,8.74468978 4.0568148,8.53066608 L4.02312698,8.59960964 C3.67370837,9.30692245 3.1720511,10.0797336 2.77197429,9.73149199 C2.34476007,9.35578573 2.69134225,8.4832462 3.01973687,7.86964299 C2.83977455,7.73299248 2.67076155,7.59092564 2.51178687,7.44484867 L2.479222,7.48411175 C2.00924766,8.04561 1.40356187,8.54559691 0.967954366,8.10823978 C0.444469007,7.58265181 0.99367085,6.75638557 1.45510889,6.23117287 C0.272977761,4.53134992 0.0480655024,2.72618559 0.00812938412,2.25061599 L0.00192613399,2.16643923 L-2.32583547e-15,2.1302471 Z" id="utter"></path>
+        </g>
+        <g id="hen" transform="translate(55.000000, 10.000000)">
+            <g id="eggs" transform="translate(19.578560, 48.872340)">
+                <g id="egg" transform="translate(11.129296, 1.391162)">
+                    <g id="Group-3" fill="#F2C48D">
+                        <path d="M4.94680851,9.09574468 C2.40180033,9.26759411 0.200490998,7.3690671 0.0286415712,4.85679214 C-0.143207856,2.35270049 1.78805237,0.184124386 4.33306056,0.0122749591 C6.87806874,-0.159574468 11.5671031,1.57528642 11.7389525,4.07937807 C11.910802,6.58346972 7.49181669,8.92389525 4.94680851,9.09574468" id="Fill-1_1_"></path>
+                    </g>
+                    <path d="M8.87479542,2.07446809 C9.28396072,2.27905074 9.63584288,2.54091653 9.94680851,2.86824877 C10.1350245,3.06464812 10.4378069,2.7700491 10.2495908,2.57364975 C9.91407529,2.22176759 9.5212766,1.93535188 9.08756137,1.71440262 C8.84206219,1.58346972 8.62929624,1.95171849 8.87479542,2.07446809" id="Fill-6_1_" fill="#FFFFFF"></path>
+                    <path d="M10.4705401,2.94189853 C10.4623568,2.94189853 10.4459902,2.94189853 10.4378069,2.94189853 C10.3232406,2.95008183 10.2168576,3.03191489 10.2250409,3.15466448 C10.2332242,3.26104746 10.3232406,3.37561375 10.4378069,3.36743044 C10.4459902,3.36743044 10.4623568,3.36743044 10.4705401,3.36743044 C10.5851064,3.35924714 10.6914894,3.27741408 10.6833061,3.15466448 C10.6833061,3.04828151 10.5932897,2.94189853 10.4705401,2.94189853" id="Fill-8_1_" fill="#FFFFFF"></path>
+                    <path d="M10.6342062,6.43617021 C9.16121113,7.58183306 7.0908347,8.39198036 5.62602291,8.49018003 C3.08101473,8.66202946 0.879705401,6.76350245 0.707855974,4.2512275 C0.626022913,2.96644845 1.09247136,1.76350245 1.91898527,0.879705401 C0.683306056,1.77168576 -0.0777414075,3.24468085 0.0286415712,4.85679214 C0.200490998,7.3608838 2.40180033,9.2594108 4.94680851,9.09574468 C6.65711948,8.9811784 9.19394435,7.89279869 10.6342062,6.43617021" id="Fill-4_1_" fill="#E3B884"></path>
+                </g>
+                <g id="egg">
+                    <path d="M8.74386252,4.75859247 C9.72585925,7.04173486 8.70294599,9.69312602 6.45253682,10.6914894 C4.21031097,11.6898527 1.59983633,10.6423895 0.617839607,8.36743044 C-0.355973813,6.08428805 -0.290507365,1.19885434 1.95171849,0.200490998 C4.19394435,-0.789689034 7.7700491,2.47545008 8.74386252,4.75859247" id="Fill-10" fill="#FFDEB6"></path>
+                    <path d="M1.04337152,3.46563011 C1.09247136,3.04828151 1.22340426,2.64729951 1.41162029,2.27086743 C1.53436989,2.03355155 1.17430442,1.8207856 1.05973813,2.05810147 C0.838788871,2.5 0.699672668,2.96644845 0.634206219,3.46563011 C0.593289689,3.73567921 1.0106383,3.72749591 1.04337152,3.46563011" id="Fill-14_1_" fill="#FFFFFF"></path>
+                    <path d="M1.33797054,1.77986907 C1.34615385,1.78805237 1.34615385,1.80441899 1.35433715,1.81260229 C1.39525368,1.91080196 1.5507365,1.94353519 1.63256956,1.88625205 C1.73895254,1.8207856 1.75531915,1.70621931 1.70621931,1.59983633 C1.69803601,1.59165303 1.69803601,1.57528642 1.6898527,1.56710311 C1.64893617,1.46890344 1.49345336,1.43617021 1.41162029,1.49345336 C1.30523732,1.5589198 1.2888707,1.67348609 1.33797054,1.77986907" id="Fill-16" fill="#FFFFFF"></path>
+                    <path d="M4.47217676,0.429623568 C6.00245499,1.39525368 7.41816694,3.03191489 7.97463175,4.34124386 C8.95662848,6.62438625 7.93371522,9.27577741 5.68330606,10.2741408 C4.5294599,10.789689 3.27741408,10.7569558 2.19721768,10.2986907 C3.41653028,11.1333879 5.01227496,11.3379705 6.45253682,10.6996727 C8.69476268,9.70130933 9.71767594,7.04991817 8.74386252,4.76677578 C8.08919804,3.22831424 6.27250409,1.26432079 4.47217676,0.429623568" id="Fill-12" fill="#F6CD9B"></path>
+                </g>
+                <g id="egg" transform="translate(6.546645, 0.000000)">
+                    <path d="M8.46563011,7.35270049 C8.35106383,9.89770867 6.37070376,11.8698854 4.03846154,11.7471358 C1.70621931,11.6243863 -0.0941080196,9.45581015 0.0204582651,6.91080196 C0.13502455,4.35761047 2.2299509,-0.102291326 4.56219313,0.0204582651 C6.88625205,0.143207856 8.5801964,4.799509 8.46563011,7.35270049" id="Fill-18_1_" fill="#FFCE95"></path>
+                    <path d="M2.41816694,2.61456628 C2.6391162,2.25450082 2.9091653,1.93535188 3.22831424,1.68166939 C3.42471358,1.51800327 3.23649755,1.1497545 3.03191489,1.31342062 C2.65548282,1.61620295 2.33633388,1.97626841 2.08265139,2.40180033 C1.93535188,2.6309329 2.27905074,2.84369885 2.41816694,2.61456628" id="Fill-22" fill="#FFFFFF"></path>
+                    <path d="M3.37561375,1.16612111 C3.37561375,1.17430442 3.37561375,1.19067103 3.37561375,1.19885434 C3.36743044,1.31342062 3.47381342,1.4198036 3.57201309,1.41162029 C3.68657938,1.40343699 3.76022913,1.32160393 3.76841244,1.19885434 C3.76841244,1.19067103 3.76841244,1.17430442 3.76841244,1.16612111 C3.77659574,1.05155483 3.67021277,0.945171849 3.57201309,0.953355155 C3.45744681,0.961538462 3.38379705,1.05155483 3.37561375,1.16612111" id="Fill-24_1_" fill="#FFFFFF"></path>
+                    <path d="M6.61620295,1.38707038 C7.51636661,2.98281506 8.04828151,5.13502455 7.98281506,6.59983633 C7.86824877,9.14484452 5.88788871,11.1170213 3.55564648,10.9942717 C2.35270049,10.9288052 1.29705401,10.3232406 0.568739771,9.40671031 C1.25613748,10.7405892 2.54091653,11.6653028 4.03846154,11.7389525 C6.37070376,11.8617021 8.35106383,9.89770867 8.46563011,7.34451718 C8.53927987,5.64238953 7.81096563,2.99099836 6.61620295,1.38707038" id="Fill-20_1_" fill="#EDC08A"></path>
+                </g>
+            </g>
+            <g id="bum" transform="translate(22.539198, 17.842813) rotate(-10.000000) translate(-22.539198, -17.842813) translate(2.039198, 4.342813)" fill-rule="nonzero">
+                <path d="M35.9126796,3.00570068 C33.7329051,1.79088687 30.3739082,1.89807632 27.9439955,1.43358869 C26.4937699,1.09357567 25.0900417,0.789866753 23.5803165,0.83560264 C21.3211785,0.90404137 18.2046172,2.2693581 14.2306326,4.93155283 C7.17881779,4.76670964 2.5491017,4.19825198 0.341484369,3.22617985 C0.27001635,11.3725784 0.142936038,15.3682177 -9.30334188e-14,23.5146162 C9.00497042,27.3734366 16.7235165,27.4448962 24.9423387,21.7995849 C27.5866554,20.227473 30.3739082,18.5481715 32.6608848,16.5115719 C33.7329051,15.6540563 35.3052015,14.1891337 35.8054776,13.0100497 C36.3057537,11.8309657 35.6625416,10.4732326 36.0198817,9.50852756 C36.5201578,7.43619811 38.9143364,4.79219158 35.9126796,3.00570068 Z" id="Path" fill="#E5E3E3"></path>
+                <path d="M22.5501133,10.5646893 C23.2454084,10.8514046 23.9798882,11.0319501 24.7289068,11.1002673 C25.4848918,11.1569266 26.2435894,11.0345996 26.9434183,10.7432153 C27.2945778,10.5649296 27.6000812,10.3083982 27.8363665,9.99340611 C28.0288968,9.72234532 28.1738407,9.42048644 28.2649816,9.10077608 C28.9254869,9.21422511 30.8830021,9.37334268 32.3725432,8.52949287 C33.0192347,8.15236323 33.5408747,7.59366242 33.8726962,6.92275882 C34.1798462,6.31215053 34.3603685,5.76050056 34.3826424,5.07740417 C36.6118806,5.33544888 37.7643729,5.10241317 39.1105279,4.12450541 C40.4566829,3.14659764 40.7907201,1.9590654 40.8385564,0.804163689 C39.4792853,1.20847738 37.603719,1.84875635 34.3727345,2.01151178 C31.1417499,2.17426721 29.1734522,1.64046977 27.780453,1.46194377 L25.0605462,0.982350803 C24.3756929,0.885963315 24.2932042,0.553988915 23.6216511,0.388707038 C24.2926567,0.565642504 23.4184397,10.5691363 22.7644208,10.3147529 C22.086943,10.0512451 21.8780884,10.2875699 22.5501133,10.5646893 Z" id="Path" fill="#8F8F8F"></path>
+                <path d="M24.4750283,0.474017805 C28.8530624,2.03539028 33.7887367,2.34698212 39.2820512,1.40879333 C39.6726853,1.33583896 40.0742564,1.2530839 40.4856575,1.15951831 L40.5932836,1.13484507 C40.4351955,2.03140696 39.9133346,3.34162481 38.0326389,3.93444449 C36.7788418,4.32965761 35.3298638,4.49656566 33.685705,4.43516864 C33.5970391,5.99834832 32.8324109,7.19305123 31.3918206,8.01927737 C29.9512303,8.8455035 28.4030885,8.94645393 26.7473952,8.32212866 C27.2280368,9.5746798 27.2576599,10.2983966 26.8362645,10.4932789 C26.6671562,10.5714863 26.1678722,10.7099263 26.1694005,10.7753982 C24.3429744,11.4332757 14.6901869,-2.72420817 24.4750283,0.474017805 Z" id="Path" fill="#C1C1C1"></path>
+            </g>
+        </g>
+        <g id="sheep-body" transform="translate(14.000000, 10.000000)">
+            <path d="M5.38461538,26.1415712 C5.38461538,26.1415712 -2.74140753,23.9729951 2.44680851,31.4770867 C2.44680851,31.4770867 -3.30605565,35.6423895 2.8396072,36.1988543 C2.8396072,36.1988543 0.376432079,43.9811784 5.38461538,43.3101473 C10.3927987,42.6472995 10.4746318,39.2927987 10.4746318,39.2927987 C10.4746318,39.2927987 10.3518822,43.2617021 12.3567921,43.4253682 C14.3617021,43.5890344 16.3747954,42.1399345 16.3747954,42.1399345 C16.3747954,42.1399345 16.7839607,46.3052373 19.5335516,45.9779051 C22.2831424,45.6423895 22.99509,42.6479542 22.99509,42.6479542 C22.99509,42.6479542 22.8314239,47.9671031 25.8592471,47.7216039 C28.6456979,47.4956755 29.8322773,45.8044589 29.4189853,42.6479542 C30.8046918,45.3698309 32.2940535,46.6434806 33.8870704,46.4689034 C36.0392799,46.2397709 37.2831424,44.2757774 37.4795417,43.9729951 C37.4795417,43.9729951 38.4042553,48.4247136 41.2602291,47.8682488 C44.1243863,47.311784 44.9754501,43.809329 44.9754501,43.809329 C44.9754501,43.809329 45.3927987,48.1873977 49.00982,47.9173486 C49.787234,47.8600655 50.4255319,47.4918167 50.6055646,47.3690671 C52.2667758,46.2725041 52.299509,44.1448445 52.299509,43.9075286 C53.2160393,45.306874 54.6235679,45.9451718 55.6710311,45.5769231 C55.8837971,45.5032733 56.309329,45.306874 56.685761,44.7340426 C57.3404255,45.3314239 58.0687398,45.896072 58.9279869,46.108838 C60.4828151,46.5016367 61.7184943,45.4214403 61.8494272,43.8747954 C61.9148936,43.113748 61.7348609,42.3608838 61.5630115,41.6162029 C62.7905074,42.2135843 64.1571195,42.7291326 65.4909984,42.4590835 C66.8248773,42.1890344 68.0196399,40.8306056 67.6268412,39.5212766 C67.8477905,40.2495908 70.5155483,39.4230769 70.8510638,39.0302782 C71.5711948,38.1873977 71.3584288,36.6080196 70.8510638,35.7160393 C72.299509,36.4525368 74.2471358,35.6178396 74.9099836,34.1366612 C75.5810147,32.6554828 75.0572831,30.8060556 73.8788871,29.6849427 C75.0081833,29.0711948 75.7201309,27.7536825 75.6055646,26.4770867 C75.5237316,25.4869067 74.8281506,23.9157119 73.6661211,23.8829787 C74.3126023,23.6211129 74.7545008,22.9255319 74.7217676,22.2299509 C74.6890344,21.5343699 74.1734861,20.8878887 73.502455,20.6833061 C74.6890344,20.3968903 74.9018003,18.6129296 74.0998363,17.688216 C73.2978723,16.7635025 71.9721768,16.5343699 70.7528642,16.4607201 C71.9639935,16.2806874 72.6677578,14.7913257 72.3076923,13.6211129 C71.9476268,12.4509002 70.801964,11.6571195 69.6153846,11.3707038 C70.9656301,10.6914894 71.6612111,9.38216039 71.391162,8.30196399 C71.1783961,7.45908347 70.4500818,7.00900164 70.3191489,6.92716858 C69.5090016,6.45253682 68.4697218,6.47708674 67.5859247,7.00081833 C68.4942717,5.77332242 67.6186579,3.8502455 66.202946,3.26923077 C64.787234,2.68821604 63.1505728,3.11374795 61.8003273,3.82569558 C62.3240589,2.49181669 61.1456628,0.871522095 59.7217676,0.75695581 C58.289689,0.642389525 56.9476268,1.80441899 56.6202946,3.20376432 C56.5139116,1.61620295 55.0409165,0.200490998 53.4533552,0.282324059 C51.8657938,0.364157119 50.5155483,2.04991817 50.8919804,3.59656301 C50.7446809,2.3608838 49.5499182,1.34615385 48.3060556,1.38707038 C47.0621931,1.42798691 45.9492635,2.52454992 45.8837971,3.76841244 C45.9328969,2.76186579 45.5155483,1.19885434 44.6563011,0.585106383 C43.4042553,-0.306873977 42.1767594,0.994271686 41.0556465,1.65711948 C40.5482815,0.388707038 38.9607201,-0.21685761 37.6350245,0.102291326 C36.309329,0.421440262 35.2536825,1.47708674 34.6072013,2.67184943 C34.1325696,1.23977087 32.5286416,0.282324059 31.0392799,0.544189853 C29.5499182,0.814238953 28.3797054,2.27086743 28.4369885,3.77659574 C27.3649755,3.27741408 26.1292962,3.15466448 24.9754501,3.40834697 C24.7626841,3.45744681 24.5417349,3.53109656 24.4026187,3.70294599 C24.2471358,3.89934534 24.2553191,4.18576105 24.2635025,4.43944354 C24.3207856,5.38870704 24.3780687,6.33797054 24.4353519,7.29541735 C23.6333879,6.61620295 22.5368249,6.29705401 21.497545,6.44435352 C21.3420622,7.99918167 22.1685761,11.2725041 23.9770867,11.698036 C23.9443535,11.6898527 15.7937807,9.75040917 15.5400982,11.698036 C15.2864157,13.6456628 16.1292962,16.3134206 16.1292962,16.3134206 C16.1292962,16.3134206 11.3502455,12.4754501 10.0736498,14.3085106 C8.7888707,16.1415712 9.27986907,20.0040917 9.27986907,20.0040917 C9.27986907,20.0040917 7.38134206,17.6554828 5.56464812,18.1301146 C4.92635025,18.2937807 4.54991817,18.7274959 4.45171849,18.8420622 C2.62684124,20.896072 5.2700491,25.9369885 5.38461538,26.1415712 Z" id="Fill-16-Copy" fill="#C1C1C1"></path>
+            <path d="M37.8477905,0.994271686 C38.101473,0.945171849 38.7070376,0.838788871 39.3289689,1.14157119 C40.3273322,1.63256956 40.5646481,2.79459902 40.5973813,2.98281506 C40.6710311,2.86006547 41.603928,1.41162029 42.9869067,1.4198036 C44.4435352,1.4198036 45.9819967,3.0400982 45.908347,5.29050736 C45.908347,3.84206219 46.7348609,2.61456628 47.806874,2.29541735 C49.2471358,1.86170213 51.2274959,3.0400982 51.5302782,5.19230769 C51.2602291,3.08919804 52.3813421,1.41162029 53.4860884,1.21522095 C54.7135843,1.00245499 56.5957447,2.46726678 56.710311,4.96317512 C56.7839607,4.49672668 57.0785597,2.98281506 58.3797054,2.11538462 C58.6824877,1.91898527 59.1162029,1.62438625 59.6644845,1.68166939 C60.4828151,1.77168576 61.0392799,2.59001637 61.2356792,3.1710311 C61.5711948,4.19394435 60.8428805,5.08657938 60.0818331,6.29705401 C62.0294599,4.27847791 63.9770867,3.07283142 65.507365,3.67021277 C65.6546645,3.72749591 66.497545,4.07119476 66.8330606,4.91407529 C67.2667758,5.99427169 66.7021277,7.46726678 65.3436989,8.53109656 C66.2131751,7.99165303 68.5679214,6.74713584 69.6890344,7.41816694 C69.8363339,7.50818331 70.4746318,7.9091653 70.6055646,8.62111293 C70.8183306,9.72585925 69.7299509,11.1497545 67.7495908,11.8780687 C68.9198036,11.4198036 70.1718494,11.6734861 70.908347,12.4836334 C70.9819967,12.5654664 71.6612111,13.3346972 71.5220949,14.2675941 C71.3420622,15.4705401 69.8281506,16.5425532 67.8396072,16.4361702 C68.7070376,16.3788871 70.400982,16.403437 72.0130933,17.4263502 C72.4631751,17.712766 73.797054,18.5556465 73.6579378,19.3330606 C73.5270049,20.0777414 72.0294599,20.601473 69.9754501,20.6260229 C71.8412439,20.7569558 73.396072,21.2397709 73.5106383,21.9517185 C73.6415712,22.7536825 71.9721768,23.9402619 69.2962357,24.7013093 C71.4893617,23.9648118 73.4779051,24.2430442 74.1898527,25.2086743 C74.6644845,25.8551555 74.705401,26.591653 74.705401,26.591653 C74.7463175,27.2545008 74.5090016,27.7454992 74.410802,27.9337152 C73.8297872,29.0957447 72.5859247,29.4558101 72.3404255,29.5212766 C73.4288052,30.0286416 74.1571195,31.0106383 74.2144026,32.0744681 C74.2553191,32.7864157 74.00982,33.5883797 73.4942717,34.0548282 C72.3977087,35.0450082 69.8854337,34.6440262 67.512275,32.7045827 C70.106383,35.2414075 71.0392799,37.7700491 70.2864157,38.6865794 C69.386252,39.7831424 66.1292962,38.4983633 65.9738134,38.4328969 C66.1538462,38.9811784 66.6448445,40.6914894 65.8837971,41.4361702 C65,42.3036007 62.5040917,41.7635025 59.9427169,39.5294599 C61.4648118,41.3952537 61.6448445,43.4983633 60.7119476,44.5458265 C60.6219313,44.6440262 59.9918167,45.2577741 59.1489362,45.2414075 C57.7741408,45.2168576 56.3584288,43.5720131 55.9738134,41.0842881 C56.3175123,43.2610475 56.2356792,44.4639935 55.7364975,44.6849427 C55.0490998,44.9795417 53.5761047,43.4738134 51.3256956,40.1677578 C52.6186579,43.1873977 51.2520458,46.0270049 49.6890344,46.4934534 C48.207856,46.9353519 45.7364975,45.5032733 44.9181669,42.6227496 C44.1489362,44.1693944 43.4451718,45.2905074 42.8232406,45.9697218 C42.3977087,46.4361702 41.8330606,46.8453355 41.0883797,46.992635 C39.5662848,47.287234 38.3551555,45.7487725 37.4713584,42.3772504 C37.1440262,42.9582651 35.8428805,45.1186579 34.0261866,45.1350245 C32.6841244,45.1432079 31.7184943,44.0466448 30.2209493,42.3527005 C29.0016367,40.9615385 28.2242226,39.6440262 27.7495908,38.7274959 C28.0032733,39.292144 28.3224223,40.1595745 28.5188216,41.2561375 C28.8052373,42.900982 29.0507365,44.2594108 28.2324059,45.2823241 C27.4959083,46.1988543 26.0229133,46.7062193 24.9263502,46.2561375 C23.1833061,45.5360065 23.1751227,42.712766 23.1751227,41.7880524 C23.1751227,40.4869067 23.502455,39.4558101 23.7561375,38.8338789 C22.1440262,42.6145663 19.7299509,44.5703764 18.4860884,44.1202946 C17.3813421,43.7193126 16.8903437,41.2643208 17.2831424,38.3919804 C15.7610475,41.2479542 13.6006547,42.9337152 12.405892,42.5245499 C11.1129296,42.0826514 10.3191489,38.9648118 11.5548282,35.0450082 C11.0310966,36.910802 10.2782324,38.2855974 9.70540098,39.1775777 C8.46153846,41.108838 7.45499182,41.6653028 7.17675941,41.8207856 C6.68576105,42.0908347 6.10474632,42.2954173 5.400982,42.4018003 L5.26186579,42.4181669 C0.949263502,42.9909984 3.71522095,36.4934534 3.71522095,36.4934534 C3.71522095,36.4934534 3.94435352,35.9615385 4.3289689,35.2823241 C3.99345336,35.3477905 3.53518822,35.3559738 2.92962357,35.2986907 C-1.70212766,34.8813421 2.99509002,32.2299509 2.99509002,32.2299509 C2.99509002,32.2299509 3.37152209,32.0171849 3.87070376,31.804419 C3.65793781,31.5671031 3.44517185,31.2888707 3.21603928,30.9615385 C0.139116203,26.50982 5.16366612,27.0335516 5.16366612,27.0335516 L7.86415712,27.3199673 L6.05564648,25.5032733 C6.05564648,25.5032733 4.52536825,23.9729951 4.28805237,22.0908347 C4.18166939,21.2397709 4.36988543,20.3232406 5.14729951,19.4558101 C6.72667758,17.712766 8.57610475,20.5851064 8.57610475,20.5851064 L10.8183306,24.0548282 L10.2045827,19.8895254 C10.2045827,19.8895254 9.69721768,16.4689034 10.8346972,14.8322422 C11.9230769,13.2774141 15.5646481,17.0253682 15.5646481,17.0253682 L18.1996727,19.7340426 L17.0130933,16.0351882 C17.0130933,16.0351882 16.2602291,13.6947627 16.4484452,11.9271686 C16.4648118,11.9189853 16.4893617,11.910802 16.5057283,11.9026187 C17.0540098,11.698036 17.5531915,11.7225859 17.8477905,11.7471358 C18.3715221,11.7798691 20.8265139,12.0008183 24.1816694,12.3854337 C22.3977087,8.47381342 22.2176759,7.48363339 22.4304419,7.35270049 C22.5450082,7.28723404 22.8641571,7.42635025 23.1833061,7.57364975 C23.4779051,7.70458265 23.6988543,7.86824877 23.8461538,7.99099836 L25.4828151,9.3903437 L25.188216,4.3903437 C25.188216,4.37397709 25.106383,4.30851064 25.1309329,4.27577741 C25.1391162,4.2594108 25.188216,4.30851064 25.188216,4.30851064 C25.5810147,4.23486088 26.0801964,4.17757774 26.6530278,4.22667758 C27.9705401,4.33306056 28.9361702,4.89770867 29.4189853,5.24140753 C29.3617021,5.0613748 28.9116203,3.42471358 29.8854337,2.27086743 C30.0327332,2.099018 30.4664484,1.58346972 31.2111293,1.44435352 C31.9394435,1.31342062 32.512275,1.63256956 32.7414075,1.75531915 C34.2716858,2.59819967 34.3944354,4.75859247 34.4026187,4.97135843 C34.492635,4.68494272 35.6382979,1.39525368 37.8477905,0.994271686 Z" id="Fill-16-Copy_1_" fill="#FFFFFF"></path>
+            <path d="M47.8477905,9.54648118 C48.314239,8.4908347 49.2553191,7.77888707 50.2454992,7.77064833 C51.3747954,7.76252046 52.1440262,8.66268412 52.3567921,8.91636661 C52.7577741,9.39099836 52.9541735,9.87381342 53.0441899,10.1765957 C53.314239,9.86563011 53.9198036,9.26006547 54.8772504,9.0309329 C56.0229133,8.75270049 57.7659574,8.99001637 58.0769231,10.0047463 C58.3306056,10.8148936 57.6268412,12.0260229 56.2438625,12.6315876 C58.5106383,12.0751227 60.2454992,12.9998363 60.4173486,13.9490998 C60.5564648,14.7101473 59.7217676,15.7166939 58.4697218,16.3468085 C58.5679214,16.2486088 59.4026187,15.4057283 59.1571195,14.6283142 C58.9443535,13.9654664 58.0769231,13.7527005 57.2094926,13.5399345 C56.2111293,13.2944354 55.3355155,13.3435352 54.7545008,13.4253682 C56.0392799,12.3451718 56.6121113,11.2649755 56.3011457,10.7412439 C56.0474632,10.3075286 55.2454992,10.3320786 55.0409165,10.3402619 C53.5515548,10.3893617 52.6513912,11.8459902 52.5286416,12.0587561 C52.8477905,10.8558101 52.3240589,9.58739771 51.2684124,8.97364975 C50.1718494,8.32716858 48.7234043,8.56448445 47.8477905,9.54648118 Z" id="Path" fill="#C1C1C1"></path>
+            <path d="M57.594108,37.3697218 C58.5188216,36.387725 58.5106383,35.2011457 58.502455,34.9720131 C58.8788871,34.9720131 59.6481178,34.91473 60.4418985,34.4564648 C60.7937807,34.2600655 61.5630115,33.8181669 61.9476268,32.88527 C62.4549918,31.6823241 61.9394435,30.5448445 61.8412439,30.3320786 C62.7986907,31.011293 63.9770867,31.1176759 64.6972177,30.610311 C65.6873977,29.91473 65.9247136,27.9507365 64.5826514,26.5513912 C64.8199673,27.5415712 64.6808511,28.5726678 64.0752864,29.112766 C63.1505728,29.9310966 61.0720131,29.6937807 59.7626841,27.90982 C59.9836334,28.1389525 61.2765957,29.4728314 61.0556465,31.2731588 C61.0065466,31.6823241 60.8510638,32.9425532 59.8445172,33.4417349 C58.9770867,33.8672668 57.7741408,33.5726678 56.9067103,32.6643208 C57.7250409,34.5464812 57.1849427,36.4368249 56.1783961,36.9851064 C55.0327332,37.6152209 52.700491,36.788707 51.8085106,34.3991817 C52.2831424,36.813257 53.8870704,38.4090016 55.4255319,38.392635 C56.6121113,38.3844517 57.5368249,37.4270049 57.594108,37.3697218 Z" id="Path" fill="#C1C1C1"></path>
+            <path d="M41.0556465,32.9589198 C41.4566285,34.0472995 41.2602291,35.209329 40.5482815,35.8967267 C39.7381342,36.6823241 38.5515548,36.5677578 38.2324059,36.5350245 C37.6104746,36.4777414 37.1440262,36.2567921 36.8657938,36.1013093 C36.8821604,36.5104746 36.8576105,37.3697218 36.3338789,38.1962357 C35.695581,39.1864157 34.2716858,40.2256956 33.3469722,39.7101473 C32.6104746,39.300982 32.2667758,37.9343699 32.8477905,36.5432079 C31.5957447,38.5153846 29.6972177,39.0391162 28.9198036,38.4826514 C28.289689,38.0325696 28.1914894,36.7232406 28.6661211,35.4057283 C28.6661211,35.5448445 28.6415712,36.7314239 29.3535188,37.1160393 C29.9590835,37.4515548 30.7364975,37.001473 31.5057283,36.5595745 C32.3977087,36.0440262 32.99509,35.4057283 33.3633388,34.9392799 C33.1833061,36.600491 33.5188216,37.7788871 34.1080196,37.9507365 C34.5908347,38.0898527 35.1472995,37.5170213 35.294599,37.3697218 C36.3338789,36.305892 35.9819967,34.6283142 35.9247136,34.3909984 C36.5220949,35.4793781 37.7823241,36.0358429 38.9689034,35.7494272 C40.2209493,35.4384615 41.1047463,34.2764321 41.0556465,32.9589198 Z" id="Path" fill="#C1C1C1"></path>
+        </g>
+        <g id="pig" transform="translate(0.000000, 1.000000)">
+            <path d="M6.20294599,12.4713584 C6.20294599,12.4713584 -5.93289689,38.690671 19.198036,41.5302782 C44.3289689,44.3698854 45.7201309,25.8346972 41.1783961,13.608838 C36.0065466,-0.31096563 13.9770867,-3.22422259 6.20294599,12.4713584" id="Fill-17_1_" fill="#FF9893"></path>
+            <path d="M14.7954173,32.3322422 C14.5908347,31.8085106 14.4599018,31.2274959 14.4189853,30.613748 C14.2144026,27.7823241 16.0392799,25.1554828 18.8216039,24.5335516 C22.193126,23.7725041 28.1833061,23.797054 29.5335516,27.7086743 C30.3273322,30.0163666 30.5400982,32.9869067 28.6006547,34.7708674 C26.6612111,36.5548282 23.5351882,36.7184943 21.0720131,36.5220949 C18.3387889,36.2929624 15.7528642,34.8445172 14.7954173,32.3322422" id="Fill-19_1_" fill="#FFC2C3"></path>
+            <path d="M26.8412439,30.3600655 C26.8412439,31.2684124 26.3993453,31.9967267 25.8592471,31.9967267 C25.3191489,31.9967267 24.8772504,31.2684124 24.8772504,30.3600655 C24.8772504,29.4599018 25.3191489,28.7234043 25.8592471,28.7234043 C26.3993453,28.7234043 26.8412439,29.4599018 26.8412439,30.3600655" id="Fill-21_1_" fill="#CD2C3D"></path>
+            <path d="M19.6399345,30.3600655 C19.6399345,31.2684124 19.198036,31.9967267 18.6579378,31.9967267 C18.1178396,31.9967267 17.6759411,31.2684124 17.6759411,30.3600655 C17.6759411,29.4599018 18.1178396,28.7234043 18.6579378,28.7234043 C19.198036,28.7234043 19.6399345,29.4599018 19.6399345,30.3600655" id="Fill-23_1_" fill="#CD2C3D"></path>
+            <path d="M23.289689,39.198036 C22.0130933,39.198036 21.0883797,38.502455 21.0801964,38.4860884 C20.9410802,38.3797054 20.9165303,38.1751227 21.0065466,38.0278232 C21.1047463,37.8805237 21.2847791,37.8396072 21.4238953,37.9459902 C21.497545,38.0032733 23.1914894,39.2635025 25.0654664,37.9378069 C25.2045827,37.8396072 25.3927987,37.8805237 25.4828151,38.0360065 C25.5728314,38.1914894 25.5319149,38.396072 25.3927987,38.4942717 C24.6563011,39.0261866 23.9279869,39.198036 23.289689,39.198036" id="Fill-25_1_" fill="#6C3730"></path>
+            <path d="M18.3306056,20.5400982 C18.3306056,21.4402619 17.594108,22.1767594 16.6939444,22.1767594 C15.7937807,22.1767594 15.0572831,21.4402619 15.0572831,20.5400982 C15.0572831,19.6399345 15.7855974,18.903437 16.6939444,18.903437 C17.6022913,18.903437 18.3306056,19.6399345 18.3306056,20.5400982" id="Fill-27" fill="#560B0F"></path>
+            <path d="M16.3666121,19.8854337 C16.3666121,20.0654664 16.2193126,20.212766 16.0392799,20.212766 C15.8592471,20.212766 15.7119476,20.0654664 15.7119476,19.8854337 C15.7119476,19.705401 15.8592471,19.5581015 16.0392799,19.5581015 C16.2193126,19.5581015 16.3666121,19.705401 16.3666121,19.8854337" id="Fill-29" fill="#FFFFFF"></path>
+            <path d="M32.0785597,20.5400982 C32.0785597,21.4402619 31.3420622,22.1767594 30.4418985,22.1767594 C29.5417349,22.1767594 28.8052373,21.4402619 28.8052373,20.5400982 C28.8052373,19.6399345 29.5335516,18.903437 30.4418985,18.903437 C31.3420622,18.903437 32.0785597,19.6399345 32.0785597,20.5400982" id="Fill-31" fill="#560B0F"></path>
+            <path d="M30.1145663,19.8854337 C30.1145663,20.0654664 29.9672668,20.212766 29.787234,20.212766 C29.6072013,20.212766 29.4599018,20.0654664 29.4599018,19.8854337 C29.4599018,19.705401 29.6072013,19.5581015 29.787234,19.5581015 C29.9672668,19.5581015 30.1145663,19.705401 30.1145663,19.8854337" id="Fill-33" fill="#FFFFFF"></path>
+            <path d="M16.3666121,23.1587561 C16.3666121,23.6988543 15.4909984,24.1407529 14.4026187,24.1407529 C13.314239,24.1407529 12.4386252,23.6988543 12.4386252,23.1587561 C12.4386252,22.6186579 13.314239,22.1767594 14.4026187,22.1767594 C15.4909984,22.1767594 16.3666121,22.6186579 16.3666121,23.1587561" id="Fill-35" fill="#F25460"></path>
+            <path d="M34.6972177,23.1587561 C34.6972177,23.6988543 33.8216039,24.1407529 32.7332242,24.1407529 C31.6448445,24.1407529 30.7692308,23.6988543 30.7692308,23.1587561 C30.7692308,22.6186579 31.6448445,22.1767594 32.7332242,22.1767594 C33.8216039,22.1767594 34.6972177,22.6186579 34.6972177,23.1587561" id="Fill-37" fill="#F25460"></path>
+            <path d="M44.6644845,5.27823241 C44.4353519,6.24386252 41.8903437,13.8297872 38.2815057,12.594108 C38.2324059,12.5777414 38.1833061,12.5613748 38.1260229,12.5368249 C35.9328969,11.6448445 34.2880524,9.50900164 33.314239,7.92962357 C32.6595745,6.85761047 32.3158756,6.03109656 32.3158756,6.03109656 C32.3158756,6.03109656 38.8052373,-2.10310966 45.8101473,0.523731588 C47.9705401,2.19312602 45.3355155,2.44680851 44.6644845,5.27823241" id="Fill-39" fill="#FF9893"></path>
+            <path d="M41.2520458,7.4304419 C41.1292962,7.9705401 39.9672668,11.603928 38.2160393,12.3567921 C38.1669394,12.3404255 38.1178396,12.3240589 38.0687398,12.299509 C35.9328969,11.3993453 34.3289689,9.24713584 33.3878887,7.65139116 C34.3453355,6.53846154 38.0523732,2.65957447 42.00491,4.19803601 C43.4206219,5.33551555 41.6939444,5.50736498 41.2520458,7.4304419" id="Fill-42_1_" fill="#FA7477"></path>
+            <path d="M1.97217676,5.27823241 C2.20130933,6.23567921 4.72176759,13.8216039 8.31423895,12.594108 C8.36333879,12.5777414 8.41243863,12.5613748 8.46972177,12.5368249 C10.6546645,11.6448445 12.2831424,9.50900164 13.2487725,7.92962357 C13.903437,6.85761047 14.2553191,6.03109656 14.2553191,6.03109656 C14.2553191,6.03109656 7.79869067,-2.10310966 0.834697218,0.523731588 C-1.31751227,2.19312602 1.30114566,2.44680851 1.97217676,5.27823241" id="Fill-44_1_" fill="#FF9893"></path>
+            <path d="M5.22913257,7.4304419 C5.35188216,7.9705401 6.51391162,11.603928 8.26513912,12.3567921 C8.31423895,12.3404255 8.36333879,12.3240589 8.41243863,12.299509 C10.5482815,11.3993453 12.1522095,9.24713584 13.0932897,7.65139116 C12.1358429,6.53846154 8.42880524,2.65957447 4.47626841,4.19803601 C3.06055646,5.33551555 4.78723404,5.50736498 5.22913257,7.4304419" id="Fill-47" fill="#FA7477"></path>
+        </g>
+    </g>
+</svg>
\ No newline at end of file
diff --git a/spec/fixtures/yaml_with_aliases.yml b/spec/fixtures/yaml_with_aliases.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f442c03e261fa568dd4b7ae02367b331c2361dd5
--- /dev/null
+++ b/spec/fixtures/yaml_with_aliases.yml
@@ -0,0 +1,11 @@
+accounts: &base_accounts
+  admin:
+    password: secret
+
+company_a:
+  accounts: &accounts
+    <<: *base_accounts
+
+# company_b:
+#   accounts: &accounts
+#     <<: *base_accounts
diff --git a/spec/fixtures/yaml_with_symbols.yml b/spec/fixtures/yaml_with_symbols.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c761e2b5bb24dd1fc67bf909e603936d1655482c
--- /dev/null
+++ b/spec/fixtures/yaml_with_symbols.yml
@@ -0,0 +1,3 @@
+:user_icon:
+  :width: 200
+  :height: 200
diff --git a/spec/hashie/array_spec.rb b/spec/hashie/array_spec.rb
index 3aba1fb029e11475fb0a1d40ac235a7249be681f..0c35b1e58292cc365e56f3e42bbcb78b344c43da 100644
--- a/spec/hashie/array_spec.rb
+++ b/spec/hashie/array_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
 describe Array do
   with_minimum_ruby('2.3.0') do
     describe '#dig' do
-      let(:array) { Hashie::Array.new([:a, :b, :c]) }
+      let(:array) { Hashie::Array.new(%i[a b c]) }
 
       it 'works with a string index' do
         expect(array.dig('0')).to eq(:a)
diff --git a/spec/hashie/dash_spec.rb b/spec/hashie/dash_spec.rb
index 87e5bc337264d7f662e5db0a3840ed9721346d4b..e75ad1a578057ffa5ac139ff02590ab7ecbf31ea 100644
--- a/spec/hashie/dash_spec.rb
+++ b/spec/hashie/dash_spec.rb
@@ -56,9 +56,9 @@ class DeferredWithSelfTest < Hashie::Dash
 end
 
 describe DashTestDefaultProc do
-  it 'as_json behaves correctly with default proc' do
+  it 'to_json behaves correctly with default proc' do
     object = described_class.new
-    expect(object.as_json).to be == { 'fields' => [] }
+    expect(object.to_json).to be == '{"fields":[]}'
   end
 end
 
@@ -129,12 +129,14 @@ describe DashTest do
   context 'writing to properties' do
     it 'fails writing a required property to nil' do
       expect { subject.first_name = nil }.to raise_error(*property_required_error('first_name'))
-      expect { required_message.first_name = nil }.to raise_error(*property_required_custom_error('first_name'))
+      expect { required_message.first_name = nil }
+        .to raise_error(*property_required_custom_error('first_name'))
     end
 
     it 'fails writing a required property to nil using []=' do
       expect { subject[:first_name] = nil }.to raise_error(*property_required_error('first_name'))
-      expect { required_message[:first_name] = nil }.to raise_error(*property_required_custom_error('first_name'))
+      expect { required_message[:first_name] = nil }
+        .to raise_error(*property_required_custom_error('first_name'))
     end
 
     it 'fails writing to a non-existent property using []=' do
@@ -263,11 +265,13 @@ describe DashTest do
     end
 
     it 'fails with non-existent properties' do
-      expect { subject.merge(middle_name: 'James') }.to raise_error(*no_property_error('middle_name'))
+      expect { subject.merge(middle_name: 'James') }
+        .to raise_error(*no_property_error('middle_name'))
     end
 
     it 'errors out when attempting to set a required property to nil' do
-      expect { subject.merge(first_name: nil) }.to raise_error(*property_required_error('first_name'))
+      expect { subject.merge(first_name: nil) }
+        .to raise_error(*property_required_error('first_name'))
     end
 
     context 'given a block' do
@@ -310,7 +314,7 @@ describe DashTest do
 
   describe 'properties' do
     it 'lists defined properties' do
-      expect(described_class.properties).to eq Set.new([:first_name, :email, :count])
+      expect(described_class.properties).to eq Set.new(%i[first_name email count])
     end
 
     it 'checks if a property exists' do
@@ -340,7 +344,7 @@ describe DashTest do
     before { subject.replace(first_name: 'Cain') }
 
     it 'return self' do
-      expect(subject.replace(email: 'bar').to_hash).to eq(email: 'bar', count: 0)
+      expect(subject.replace(email: 'bar').object_id).to eq subject.object_id
     end
 
     it 'sets all specified keys to their corresponding values' do
@@ -348,7 +352,7 @@ describe DashTest do
     end
 
     it 'leaves only specified keys and keys with default values' do
-      expect(subject.keys.sort_by(&:to_s)).to eq [:count, :first_name]
+      expect(subject.keys.sort_by(&:to_s)).to eq %i[count first_name]
       expect(subject.email).to be_nil
       expect(subject.count).to eq 0
     end
@@ -366,8 +370,12 @@ describe DashTest do
     let(:params) { { first_name: 'Alice', email: 'alice@example.com' } }
 
     context 'when there is coercion' do
-      let(:params_before) { { city: 'nyc', person: { first_name: 'Bob', email: 'bob@example.com' } } }
-      let(:params_after) { { city: 'sfo', person: { first_name: 'Alice', email: 'alice@example.com' } } }
+      let(:params_before) do
+        { city: 'nyc', person: { first_name: 'Bob', email: 'bob@example.com' } }
+      end
+      let(:params_after) do
+        { city: 'sfo', person: { first_name: 'Alice', email: 'alice@example.com' } }
+      end
 
       subject { DashWithCoercion.new(params_before) }
 
@@ -402,6 +410,70 @@ describe DashTest do
         expect(subject.count).to eq subject.class.defaults[:count]
       end
     end
+
+    context 'codependent attributes' do
+      let(:codependent) do
+        Class.new(Hashie::Dash) do
+          property :a, required: -> { b.nil? }, message: 'is required if b is not set.'
+          property :b, required: -> { a.nil? }, message: 'is required if a is not set.'
+          property :c, default: -> { 'c' }
+        end
+      end
+
+      it 'does not raise an error when only the first property is set' do
+        expect { codependent.new(a: 'ant', b: nil) }.not_to raise_error
+      end
+
+      it 'does not raise an error when only the second property is set' do
+        expect { codependent.new(a: nil, b: 'bat') }.not_to raise_error
+      end
+
+      it 'does not raise an error when both properties are set' do
+        expect { codependent.new(a: 'ant', b: 'bat') }.not_to raise_error
+      end
+
+      it 'raises an error when neither property is set' do
+        expect { codependent.new(a: nil, b: nil) }.to raise_error(ArgumentError)
+      end
+
+      context 'exporting nil values' do
+        describe '#to_h' do
+          it 'does not prune nil values' do
+            expect(codependent.new(a: 'hi', b: nil).to_h).to eq(a: 'hi', b: nil, c: 'c')
+            expect(codependent.new(a: 'hi', b: nil, c: nil).to_hash).to eq(a: 'hi', b: nil, c: 'c')
+            expect(codependent.new(a: 'hi', b: nil).merge(c: nil).to_h).to(
+              eq(a: 'hi', b: nil, c: nil)
+            )
+          end
+        end
+
+        describe '#to_hash' do
+          it 'does not prune nil values' do
+            expect(codependent.new(a: 'hi', b: nil).to_hash).to eq(a: 'hi', b: nil, c: 'c')
+            expect(codependent.new(a: 'hi', b: nil, c: nil).to_hash).to eq(a: 'hi', b: nil, c: 'c')
+            expect(codependent.new(a: 'hi', b: nil).merge(c: nil).to_hash).to(
+              eq(a: 'hi', b: nil, c: nil)
+            )
+          end
+        end
+
+        describe '**' do
+          # Note: This test is an implementation detail of MRI and may not hold for
+          # other Ruby interpreters. But it's important to note in the test suite
+          # because it can be surprising for people unfamiliar with the semantics of
+          # double-splatting.
+          #
+          # For more information, see [this link][1]:
+          #
+          # [1]: https://github.com/hashie/hashie/issues/353#issuecomment-363294886
+          it 'prunes nil values because they are not set in the dash' do
+            dash = codependent.new(a: 'hi', b: nil)
+
+            expect(**dash).to eq(a: 'hi', c: 'c')
+          end
+        end
+      end
+    end
   end
 end
 
@@ -449,6 +521,43 @@ describe Hashie::Dash, 'inheritance' do
     expect(@bottom.new).to have_key(:echo)
     expect(@bottom.new).to_not have_key('echo')
   end
+
+  context 'exporting nil values' do
+    let(:test) do
+      Class.new(Hashie::Dash) do
+        property :foo
+        property :bar
+      end
+    end
+
+    describe '#to_h' do
+      it 'does not prune nil values' do
+        expect(test.new(foo: 'hi', bar: nil).to_h).to eq(foo: 'hi', bar: nil)
+      end
+    end
+
+    describe '#to_hash' do
+      it 'does not prune nil values' do
+        expect(test.new(foo: 'hi', bar: nil).to_hash).to eq(foo: 'hi', bar: nil)
+      end
+    end
+
+    describe '**' do
+      # Note: This test is an implementation detail of MRI and may not hold for
+      # other Ruby interpreters. But it's important to note in the test suite
+      # because it can be surprising for people unfamiliar with the semantics of
+      # double-splatting.
+      #
+      # For more information, see [this link][1]:
+      #
+      # [1]: https://github.com/hashie/hashie/issues/353#issuecomment-363294886
+      it 'prunes nil values because they are not set in the dash' do
+        dash = test.new(foo: 'hi', bar: nil)
+
+        expect(**dash).to eq(foo: 'hi')
+      end
+    end
+  end
 end
 
 describe SubclassedTest do
@@ -480,7 +589,8 @@ end
 
 describe ConditionallyRequiredTest do
   it 'does not allow a conditionally required property to be set to nil if required' do
-    expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: nil) }.to raise_error(ArgumentError, "The property 'password' must be set, too.")
+    expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: nil) }
+      .to raise_error(ArgumentError, "The property 'password' must be set, too.")
   end
 
   it 'allows a conditionally required property to be set to nil if not required' do
@@ -488,7 +598,8 @@ describe ConditionallyRequiredTest do
   end
 
   it 'allows a conditionally required property to be set if required' do
-    expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: '$ecure!') }.not_to raise_error
+    expect { ConditionallyRequiredTest.new(username: 'bob.smith', password: '$ecure!') }
+      .not_to raise_error
   end
 end
 
@@ -571,3 +682,13 @@ context 'with method access' do
     it { is_expected.to eq true }
   end
 end
+
+RSpec.describe Hashie::Dash do
+  let(:test) do
+    Class.new(Hashie::Dash) do
+      property :description, default: ''
+    end
+  end
+
+  include_examples 'Dash default handling', :description
+end
diff --git a/spec/hashie/extensions/coercion_spec.rb b/spec/hashie/extensions/coercion_spec.rb
index 7d70ed4d1728d6b42bac42806ab8c7ba406d58e7..72265e15a9521edf5d0c6d2e753cd3742b3d7bcf 100644
--- a/spec/hashie/extensions/coercion_spec.rb
+++ b/spec/hashie/extensions/coercion_spec.rb
@@ -154,7 +154,7 @@ describe Hashie::Extensions::Coercion do
     it 'supports coercion for Array' do
       subject.coerce_key :foo, Array[Coercable]
 
-      instance[:foo] = %w('bar', 'bar2')
+      instance[:foo] = %w[bar bar2]
       expect(instance[:foo]).to all(be_coerced)
       expect(instance[:foo]).to be_a(Array)
     end
@@ -162,7 +162,7 @@ describe Hashie::Extensions::Coercion do
     it 'supports coercion for Set' do
       subject.coerce_key :foo, Set[Coercable]
 
-      instance[:foo] = Set.new(%w('bar', 'bar2'))
+      instance[:foo] = Set.new(%w[bar bar2])
       expect(instance[:foo]).to all(be_coerced)
       expect(instance[:foo]).to be_a(Set)
     end
@@ -170,7 +170,7 @@ describe Hashie::Extensions::Coercion do
     it 'supports coercion for Set of primitive' do
       subject.coerce_key :foo, Set[Initializable]
 
-      instance[:foo] = %w('bar', 'bar2')
+      instance[:foo] = %w[bar bar2]
       expect(instance[:foo].map(&:value)).to all(eq 'String')
       expect(instance[:foo]).to be_none(&:coerced?)
       expect(instance[:foo]).to be_a(Set)
@@ -283,7 +283,8 @@ describe Hashie::Extensions::Coercion do
 
       it 'raises errors for non-coercable types' do
         subject.coerce_key :foo, NotInitializable
-        expect { instance[:foo] = 'true' }.to raise_error(Hashie::CoercionError, /NotInitializable is not a coercable type/)
+        expect { instance[:foo] = 'true' }
+          .to raise_error(Hashie::CoercionError, /NotInitializable is not a coercable type/)
       end
 
       it 'can coerce false' do
@@ -458,8 +459,12 @@ describe Hashie::Extensions::Coercion do
           coerce_key :categories, Array[CategoryHash]
         end
 
-        let(:category) { CategoryHash.new(type: 'rubygem', products: [Hashie::Mash.new(name: 'Hashie')]) }
-        let(:product) { ProductHash.new(name: 'Hashie', categories: [Hashie::Mash.new(type: 'rubygem')]) }
+        let(:category) do
+          CategoryHash.new(type: 'rubygem', products: [Hashie::Mash.new(name: 'Hashie')])
+        end
+        let(:product) do
+          ProductHash.new(name: 'Hashie', categories: [Hashie::Mash.new(type: 'rubygem')])
+        end
 
         it 'coerces CategoryHash[:products] correctly' do
           expected = [ProductHash]
@@ -558,18 +563,30 @@ describe Hashie::Extensions::Coercion do
       end
 
       it 'raises a CoercionError when coercion is not possible' do
-        type = if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >= Hashie::Extensions::RubyVersion.new('2.4.0')
-                 Integer
-               else
-                 Fixnum
-               end
+        type =
+          if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >=
+             Hashie::Extensions::RubyVersion.new('2.4.0')
+            Integer
+          else
+            Fixnum
+          end
 
         subject.coerce_value type, Symbol
-        expect { instance[:hi] = 1 }.to raise_error(Hashie::CoercionError, /Cannot coerce property :hi from #{type} to Symbol/)
+        expect { instance[:hi] = 1 }.to raise_error(
+          Hashie::CoercionError, /Cannot coerce property :hi from #{type} to Symbol/
+        )
       end
 
       it 'coerces Integer to String' do
-        subject.coerce_value Integer, String
+        type =
+          if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >=
+             Hashie::Extensions::RubyVersion.new('2.4.0')
+            Integer
+          else
+            Fixnum
+          end
+
+        subject.coerce_value type, String
 
         {
           fixnum: 2,
@@ -579,7 +596,7 @@ describe Hashie::Extensions::Coercion do
           complex: Complex(1)
         }.each do |k, v|
           instance[k] = v
-          if v.is_a? Integer
+          if v.is_a? type
             expect(instance[k]).to be_a(String)
             expect(instance[k]).to eq(v.to_s)
           else
@@ -610,8 +627,8 @@ describe Hashie::Extensions::Coercion do
           return !!(v =~ /^(true|t|yes|y|1)$/i)
         end)
 
-        true_values = %w(true t yes y 1)
-        false_values = %w(false f no n 0)
+        true_values = %w[true t yes y 1]
+        false_values = %w[false f no n 0]
 
         true_values.each do |v|
           instance[:foo] = v
diff --git a/spec/hashie/extensions/dash/predefined_values_spec.rb b/spec/hashie/extensions/dash/predefined_values_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..a84fe809c633850df61a4c4ca57b745770ecc07d
--- /dev/null
+++ b/spec/hashie/extensions/dash/predefined_values_spec.rb
@@ -0,0 +1,58 @@
+require 'spec_helper'
+
+describe Hashie::Extensions::Dash::PredefinedValues do
+  let(:extended_dash) do
+    Class.new(Hashie::Dash) do
+      include Hashie::Extensions::Dash::PredefinedValues
+
+      property :gender, values: %i[male female prefer_not_to_say]
+      property :age, values: (0..150)
+    end
+  end
+
+  it 'allows value within the predefined list' do
+    valid_dash = extended_dash.new(gender: :male)
+    expect(valid_dash.gender).to eq(:male)
+  end
+
+  it 'rejects value outside the predefined list' do
+    expect { extended_dash.new(gender: :unicorn) }
+      .to raise_error(ArgumentError, %(Invalid value for property 'gender'))
+  end
+
+  it 'accepts a range for predefined list' do
+    expect { extended_dash.new(age: -1) }
+      .to raise_error(ArgumentError, %(Invalid value for property 'age'))
+  end
+
+  it 'allows property to be nil' do
+    expect { extended_dash.new }
+      .not_to raise_error
+  end
+
+  it 'rejects non array or range for predefined list' do
+    expect do
+      class DashWithUnsupportedValueType < Hashie::Dash
+        include Hashie::Extensions::Dash::PredefinedValues
+
+        property :name, values: -> { :foo }
+      end
+    end.to raise_error(ArgumentError, %(`values` accepts an Array or a Range.))
+  end
+
+  let(:subclass) do
+    Class.new(extended_dash) do
+      property :language, values: %i[ruby javascript]
+    end
+  end
+
+  it 'passes property predefined list to subclasses' do
+    expect { subclass.new(gender: :unicorn) }
+      .to raise_error(ArgumentError, %(Invalid value for property 'gender'))
+  end
+
+  it 'allows subclass to define predefined list' do
+    expect { subclass.new(language: :ruby) }
+      .not_to raise_error
+  end
+end
diff --git a/spec/hashie/extensions/deep_find_spec.rb b/spec/hashie/extensions/deep_find_spec.rb
index b3cc57e77e6eff071eca49ab15eaee94b8f38b8d..fddb9d1f0ddd50d3907a0daad52cef239914aed3 100644
--- a/spec/hashie/extensions/deep_find_spec.rb
+++ b/spec/hashie/extensions/deep_find_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require 'active_support/core_ext/hash/indifferent_access'
 
 describe Hashie::Extensions::DeepFind do
   subject { Class.new(Hash) { include Hashie::Extensions::DeepFind } }
@@ -36,7 +35,8 @@ describe Hashie::Extensions::DeepFind do
 
   describe '#deep_find_all' do
     it 'detects all values from a nested hash' do
-      expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
+      expect(instance.deep_find_all(:title))
+        .to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
     end
 
     it 'returns nil if it does not find any matches' do
@@ -64,40 +64,8 @@ describe Hashie::Extensions::DeepFind do
       end
 
       it 'detects all values from a nested hash' do
-        expect(instance.deep_find_all(:title)).to eq([{ type: :string }, 'Call of the Wild', 'Moby Dick', 'Main Library'])
-      end
-    end
-  end
-
-  context 'on an ActiveSupport::HashWithIndifferentAccess' do
-    subject(:instance) { hash.with_indifferent_access.extend(Hashie::Extensions::DeepFind) }
-
-    describe '#deep_find' do
-      it 'indifferently detects a value from a nested hash' do
-        expect(instance.deep_find(:address)).to eq('123 Library St.')
-        expect(instance.deep_find('address')).to eq('123 Library St.')
-      end
-
-      it 'indifferently detects a value from a nested array' do
-        expect(instance.deep_find(:title)).to eq('Call of the Wild')
-        expect(instance.deep_find('title')).to eq('Call of the Wild')
-      end
-
-      it 'indifferently returns nil if it does not find a match' do
-        expect(instance.deep_find(:wahoo)).to be_nil
-        expect(instance.deep_find('wahoo')).to be_nil
-      end
-    end
-
-    describe '#deep_find_all' do
-      it 'indifferently detects all values from a nested hash' do
-        expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
-        expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
-      end
-
-      it 'indifferently returns nil if it does not find any matches' do
-        expect(instance.deep_find_all(:wahoo)).to be_nil
-        expect(instance.deep_find_all('wahoo')).to be_nil
+        expect(instance.deep_find_all(:title))
+          .to eq([{ type: :string }, 'Call of the Wild', 'Moby Dick', 'Main Library'])
       end
     end
   end
@@ -125,8 +93,10 @@ describe Hashie::Extensions::DeepFind do
 
     describe '#deep_find_all' do
       it 'indifferently detects all values from a nested hash' do
-        expect(instance.deep_find_all(:title)).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
-        expect(instance.deep_find_all('title')).to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
+        expect(instance.deep_find_all(:title))
+          .to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
+        expect(instance.deep_find_all('title'))
+          .to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
       end
 
       it 'indifferently returns nil if it does not find any matches' do
diff --git a/spec/hashie/extensions/deep_locate_spec.rb b/spec/hashie/extensions/deep_locate_spec.rb
index d174f3c4fdd891801c062f017681de4cd8d92974..e104e76289edf00e758789267d29d3e0a6416be6 100644
--- a/spec/hashie/extensions/deep_locate_spec.rb
+++ b/spec/hashie/extensions/deep_locate_spec.rb
@@ -1,5 +1,4 @@
 require 'spec_helper'
-require 'active_support/core_ext/hash/indifferent_access'
 
 describe Hashie::Extensions::DeepLocate do
   let(:hash) do
@@ -56,7 +55,8 @@ describe Hashie::Extensions::DeepLocate do
   describe '.deep_locate' do
     context 'if called with a non-callable comparator' do
       it 'creates a key comparator on-th-fly' do
-        expect(described_class.deep_locate(:lsr10, hash)).to eq([hash[:query][:bool][:must_not][0][:range]])
+        expect(described_class.deep_locate(:lsr10, hash))
+          .to eq([hash[:query][:bool][:must_not][0][:range]])
       end
     end
 
@@ -78,7 +78,7 @@ describe Hashie::Extensions::DeepLocate do
         [
           lambda do |_key, _value, object|
             object.is_a?(Array) &&
-            !object.extend(described_class).deep_locate(:match).empty?
+              !object.extend(described_class).deep_locate(:match).empty?
           end,
           [
             hash[:query][:bool][:must],
@@ -122,16 +122,4 @@ describe Hashie::Extensions::DeepLocate do
       expect(instance.deep_locate(:bool)).to eq([hash[:query]])
     end
   end
-
-  context 'on an ActiveSupport::HashWithIndifferentAccess' do
-    let(:instance) { hash.dup.with_indifferent_access }
-
-    it 'can locate symbolic keys' do
-      expect(described_class.deep_locate(:lsr10, instance)).to eq ['lsr10' => { 'gte' => 2014 }]
-    end
-
-    it 'can locate string keys' do
-      expect(described_class.deep_locate('lsr10', instance)).to eq ['lsr10' => { 'gte' => 2014 }]
-    end
-  end
 end
diff --git a/spec/hashie/extensions/deep_merge_spec.rb b/spec/hashie/extensions/deep_merge_spec.rb
index aec2d7da8fc71e1af1c44757c0c1456072662c4a..4ff6c30891e87ae4e69bb04e3dfd0f3cae8731ea 100644
--- a/spec/hashie/extensions/deep_merge_spec.rb
+++ b/spec/hashie/extensions/deep_merge_spec.rb
@@ -13,22 +13,69 @@ describe Hashie::Extensions::DeepMerge do
   end
 
   context 'without &block' do
-    let(:h1) { subject.new.merge(a: 'a', a1: 42, b: 'b', c: { c1: 'c1', c2: { a: 'b' }, c3: { d1: 'd1' } }) }
+    let(:h1) do
+      subject.new.merge(
+        a: 'a',
+        a1: 42,
+        b: 'b',
+        c: { c1: 'c1', c2: { a: 'b' }, c3: { d1: 'd1' } },
+        d: nil,
+        d1: false,
+        d2: true,
+        d3: unbound_method,
+        d4: Complex(1),
+        d5: Rational(1)
+      )
+    end
     let(:h2) { { a: 1, a1: 1, c: { c1: 2, c2: 'c2', c3: { d2: 'd2' } }, e: { e1: 1 } } }
-    let(:expected_hash) { { a: 1, a1: 1, b: 'b', c: { c1: 2, c2: 'c2', c3: { d1: 'd1', d2: 'd2' } }, e: { e1: 1 } } }
+    let(:unbound_method) { method(:puts) }
+    let(:expected_hash) do
+      {
+        a: 1,
+        a1: 1,
+        b: 'b',
+        c: { c1: 2, c2: 'c2', c3: { d1: 'd1', d2: 'd2' } },
+        d: nil,
+        d1: false,
+        d2: true,
+        d3: unbound_method,
+        d4: Complex(1),
+        d5: Rational(1),
+        e: { e1: 1 }
+      }
+    end
 
-    it 'deep merges two hashes' do
-      expect(h1.deep_merge(h2)).to eq expected_hash
+    it 'deep merges two hashes without modifying them' do
+      result = h1.deep_merge(h2)
+
+      expect(result).to eq expected_hash
+      expect(h1).to(
+        eq(
+          a: 'a',
+          a1: 42,
+          b: 'b',
+          c: { c1: 'c1', c2: { a: 'b' }, c3: { d1: 'd1' } },
+          d: nil,
+          d1: false,
+          d2: true,
+          d3: unbound_method,
+          d4: Complex(1),
+          d5: Rational(1)
+        )
+      )
+      expect(h2).to eq(a: 1, a1: 1, c: { c1: 2, c2: 'c2', c3: { d2: 'd2' } }, e: { e1: 1 })
     end
 
     it 'deep merges another hash in place via bang method' do
-      h1.deep_merge!(h2)
+      result = h1.deep_merge!(h2)
+
+      expect(result).to eq expected_hash
       expect(h1).to eq expected_hash
     end
 
     it 'merges new nested hash entries by value, not by reference' do
       h1.deep_merge!(h2)
-      expect { h1[:e][:e1] = 'changed' }.not_to change { h2[:e][:e1] }
+      expect { h1[:e][:e1] = 'changed' }.not_to(change { h2[:e][:e1] })
     end
   end
 
diff --git a/spec/hashie/extensions/ignore_undeclared_spec.rb b/spec/hashie/extensions/ignore_undeclared_spec.rb
index 948a83424c2225650d9de596d64693c0e07f1c1e..6f2d818f16a86caac468b7456b93c535eea1c4b4 100644
--- a/spec/hashie/extensions/ignore_undeclared_spec.rb
+++ b/spec/hashie/extensions/ignore_undeclared_spec.rb
@@ -27,6 +27,18 @@ describe Hashie::Extensions::IgnoreUndeclared do
       hash = subject.new(city: 'Toronto')
       expect { hash.country = 'Canada' }.to raise_error(NoMethodError)
     end
+
+    context 'with a default value' do
+      let(:test) do
+        Class.new(Hashie::Trash) do
+          include Hashie::Extensions::IgnoreUndeclared
+
+          property :description, from: :desc, default: ''
+        end
+      end
+
+      include_examples 'Dash default handling', :description, :desc
+    end
   end
 
   context 'combined with DeepMerge' do
@@ -41,7 +53,8 @@ describe Hashie::Extensions::IgnoreUndeclared do
         property :some_other_key
       end
       hash = ForgivingTrashWithMergeAndProperty.new(some_ignored_key: 17, some_key: 12)
-      expect(hash.deep_merge(some_other_key: 55, some_ignored_key: 18)).to eq(some_key: 12, some_other_key: 55)
+      expect(hash.deep_merge(some_other_key: 55, some_ignored_key: 18))
+        .to eq(some_key: 12, some_other_key: 55)
     end
   end
 end
diff --git a/spec/hashie/extensions/indifferent_access_spec.rb b/spec/hashie/extensions/indifferent_access_spec.rb
index a4c2f43b7f41ccfd97ded96805ce08beb1ec2419..27585f1195352c490adb4b7fd8712811b346ede6 100644
--- a/spec/hashie/extensions/indifferent_access_spec.rb
+++ b/spec/hashie/extensions/indifferent_access_spec.rb
@@ -6,7 +6,7 @@ describe Hashie::Extensions::IndifferentAccess do
     include Hashie::Extensions::IndifferentAccess
 
     class << self
-      alias_method :build, :new
+      alias build new
     end
   end
 
@@ -14,7 +14,7 @@ describe Hashie::Extensions::IndifferentAccess do
     include Hashie::Extensions::IndifferentAccess
 
     class << self
-      alias_method :build, :[]
+      alias build []
     end
   end
 
@@ -22,7 +22,7 @@ describe Hashie::Extensions::IndifferentAccess do
     include Hashie::Extensions::IndifferentAccess
 
     class << self
-      alias_method :build, :try_convert
+      alias build try_convert
     end
   end
 
@@ -44,11 +44,24 @@ describe Hashie::Extensions::IndifferentAccess do
         include Hashie::Extensions::IndifferentAccess
       end.new
 
-      merged_hash = indifferent_hash.merge(:cat => 'meow')
+      merged_hash = indifferent_hash.merge(cat: 'meow')
 
       expect(merged_hash[:cat]).to eq('meow')
       expect(merged_hash['cat']).to eq('meow')
     end
+
+    it 'injects the resulting new Hash with IndifferentAccess' do
+      hash = IndifferentHashWithMergeInitializer.new(
+        :cat => 'meow',
+        'dog' => { name: 'Mango', sound: 'woof' }
+      )
+
+      dog = hash[:dog]
+      merged = dog.merge(foo: 'bar')
+
+      expect(merged[:foo]).to eq('bar')
+      expect(merged['foo']).to eq('bar')
+    end
   end
 
   describe '#merge!' do
@@ -57,13 +70,43 @@ describe Hashie::Extensions::IndifferentAccess do
         include Hashie::Extensions::IndifferentAccess
       end.new
 
-      indifferent_hash.merge!(:cat => 'meow')
+      indifferent_hash[:cat] = 'meow'
 
       expect(indifferent_hash[:cat]).to eq('meow')
       expect(indifferent_hash['cat']).to eq('meow')
     end
   end
 
+  describe '#to_hash' do
+    let(:indifferent_hash) { Class.new(::Hash) { include Hashie::Extensions::IndifferentAccess } }
+
+    it 'returns a normal hash without indifference' do
+      indifferent = indifferent_hash.new
+      indifferent['cat'] = 'meow'
+
+      subject = indifferent.to_hash
+
+      expect(subject['cat']).to eq 'meow'
+      expect(subject[:cat]).to be_nil
+    end
+
+    it 'maintains the #default_proc when set' do
+      indifferent = indifferent_hash.new { |_hash, key| "Nothing here: #{key}" }
+
+      subject = indifferent.to_hash
+
+      expect(subject['babble']).to eq 'Nothing here: babble'
+    end
+
+    it 'maintains the #default when set' do
+      indifferent = indifferent_hash.new(0)
+
+      subject = indifferent.to_hash
+
+      expect(subject['babble']).to eq 0
+    end
+  end
+
   describe 'when included in dash' do
     let(:params) { { foo: 'bar' } }
     subject { IndifferentHashWithDash.new(params) }
@@ -73,6 +116,45 @@ describe Hashie::Extensions::IndifferentAccess do
     end
   end
 
+  describe 'when overriding indifferent methods' do
+    let(:indifferent_hash) do
+      Class.new(::Hash) do
+        include Hashie::Extensions::IndifferentAccess
+
+        ALIASES = { cat: :grumpy }.freeze
+
+        # Override writer to maintain alias of the given key
+        def indifferent_writer(key, value)
+          indifferent_value = indifferent_value(value)
+
+          regular_writer convert_key(key),          indifferent_value
+          regular_writer convert_key(ALIASES[key]), indifferent_value
+        end
+        alias_method :[]=, :indifferent_writer
+      end.new
+    end
+
+    it '#indifferent_writer' do
+      indifferent_hash[:cat] = 'meow'
+
+      expect(indifferent_hash[:cat]).to eq('meow')
+      expect(indifferent_hash['cat']).to eq('meow')
+
+      expect(indifferent_hash[:grumpy]).to eq('meow')
+      expect(indifferent_hash['grumpy']).to eq('meow')
+    end
+
+    it '#merge' do
+      merged_hash = indifferent_hash.merge(cat: 'meow')
+
+      expect(merged_hash[:cat]).to eq('meow')
+      expect(merged_hash['cat']).to eq('meow')
+
+      expect(merged_hash[:grumpy]).to eq('meow')
+      expect(merged_hash['grumpy']).to eq('meow')
+    end
+  end
+
   describe 'when translating properties and ignoring undeclared' do
     let(:value) { 'baz' }
 
@@ -113,7 +195,7 @@ describe Hashie::Extensions::IndifferentAccess do
     describe '#values_at' do
       it 'indifferently finds values' do
         h = subject.build(:foo => 'bar', 'baz' => 'qux')
-        expect(h.values_at('foo', :baz)).to eq %w(bar qux)
+        expect(h.values_at('foo', :baz)).to eq %w[bar qux]
       end
 
       it 'returns the same instance of the hash that was set' do
@@ -195,7 +277,7 @@ describe Hashie::Extensions::IndifferentAccess do
         expect(h).to be_key('foo')
       end
 
-      %w(include? member? has_key?).each do |key_alias|
+      %w[include? member? has_key?].each do |key_alias|
         it "is aliased as #{key_alias}" do
           expect(h.send(key_alias.to_sym, :foo)).to be(true)
           expect(h.send(key_alias.to_sym, 'foo')).to be(true)
@@ -263,6 +345,30 @@ describe Hashie::Extensions::IndifferentAccess do
         end
       end
     end
+
+    with_minimum_ruby('2.5.0') do
+      describe '#slice' do
+        let(:h) { subject.build(foo: 'bar', baz: 'qux') }
+
+        it 'indifferently slices the hash' do
+          sliced_h = { 'foo' => 'bar' }
+          expect(h.slice('foo')).to eq sliced_h
+          expect(h.slice(:foo)).to eq sliced_h
+        end
+      end
+    end
+
+    with_minimum_ruby('3.0.0') do
+      describe '#except' do
+        let(:h) { subject.build(foo: 'bar', baz: 'qux') }
+
+        it 'indifferently excepts keys from the hash' do
+          sliced_h = { 'baz' => 'qux' }
+          expect(h.except('foo')).to eq sliced_h
+          expect(h.except(:foo)).to eq sliced_h
+        end
+      end
+    end
   end
 
   describe 'with merge initializer' do
diff --git a/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb b/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
index b11f3156578424d02738219d30c6841af5a24ef4..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644
--- a/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
+++ b/spec/hashie/extensions/indifferent_access_with_rails_hwia_spec.rb
@@ -1,208 +0,0 @@
-# This set of tests verifies that Hashie::Extensions::IndifferentAccess works with
-# ActiveSupport HashWithIndifferentAccess hashes. See #164 and #166 for details.
-
-require 'active_support/hash_with_indifferent_access'
-require 'active_support/core_ext/hash'
-require 'spec_helper'
-
-describe Hashie::Extensions::IndifferentAccess do
-  class IndifferentHashWithMergeInitializer < Hash
-    include Hashie::Extensions::MergeInitializer
-    include Hashie::Extensions::IndifferentAccess
-
-    class << self
-      alias_method :build, :new
-    end
-  end
-
-  class IndifferentHashWithArrayInitializer < Hash
-    include Hashie::Extensions::IndifferentAccess
-
-    class << self
-      alias_method :build, :[]
-    end
-  end
-
-  class IndifferentHashWithTryConvertInitializer < Hash
-    include Hashie::Extensions::IndifferentAccess
-
-    class << self
-      alias_method :build, :try_convert
-    end
-  end
-
-  class CoercableHash < Hash
-    include Hashie::Extensions::Coercion
-    include Hashie::Extensions::MergeInitializer
-  end
-
-  class MashWithIndifferentAccess < Hashie::Mash
-    include Hashie::Extensions::IndifferentAccess
-  end
-
-  shared_examples_for 'hash with indifferent access' do
-    it 'is able to access via string or symbol' do
-      indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(abc: 123)
-      h = subject.build(indifferent_hash)
-      expect(h[:abc]).to eq 123
-      expect(h['abc']).to eq 123
-    end
-
-    describe '#values_at' do
-      it 'indifferently finds values' do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(
-          :foo => 'bar', 'baz' => 'qux'
-        )
-        h = subject.build(indifferent_hash)
-        expect(h.values_at('foo', :baz)).to eq %w(bar qux)
-      end
-    end
-
-    describe '#fetch' do
-      it 'works like normal fetch, but indifferent' do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
-        h = subject.build(indifferent_hash)
-        expect(h.fetch(:foo)).to eq h.fetch('foo')
-        expect(h.fetch(:foo)).to eq 'bar'
-      end
-    end
-
-    describe '#delete' do
-      it 'deletes indifferently' do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(
-          :foo => 'bar',
-          'baz' => 'qux'
-        )
-        h = subject.build(indifferent_hash)
-        h.delete('foo')
-        h.delete(:baz)
-        expect(h).to be_empty
-      end
-    end
-
-    describe '#key?' do
-      let(:h) do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
-        subject.build(indifferent_hash)
-      end
-
-      it 'finds it indifferently' do
-        expect(h).to be_key(:foo)
-        expect(h).to be_key('foo')
-      end
-
-      %w(include? member? has_key?).each do |key_alias|
-        it "is aliased as #{key_alias}" do
-          expect(h.send(key_alias.to_sym, :foo)).to be(true)
-          expect(h.send(key_alias.to_sym, 'foo')).to be(true)
-        end
-      end
-    end
-
-    describe '#update' do
-      let(:h) do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
-        subject.build(indifferent_hash)
-      end
-
-      it 'allows keys to be indifferent still' do
-        h.update(baz: 'qux')
-        expect(h['foo']).to eq 'bar'
-        expect(h['baz']).to eq 'qux'
-      end
-
-      it 'recursively injects indifference into sub-hashes' do
-        h.update(baz: { qux: 'abc' })
-        expect(h['baz']['qux']).to eq 'abc'
-      end
-
-      it 'does not change the ancestors of the injected object class' do
-        h.update(baz: { qux: 'abc' })
-        expect({}).not_to be_respond_to(:indifferent_access?)
-      end
-    end
-
-    describe '#replace' do
-      let(:h) do
-        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
-        subject.build(indifferent_hash).replace(bar: 'baz', hi: 'bye')
-      end
-
-      it 'returns self' do
-        expect(h).to be_a(subject)
-      end
-
-      it 'removes old keys' do
-        [:foo, 'foo'].each do |k|
-          expect(h[k]).to be_nil
-          expect(h.key?(k)).to be_falsy
-        end
-      end
-
-      it 'creates new keys with indifferent access' do
-        [:bar, 'bar', :hi, 'hi'].each { |k| expect(h.key?(k)).to be_truthy }
-        expect(h[:bar]).to eq 'baz'
-        expect(h['bar']).to eq 'baz'
-        expect(h[:hi]).to eq 'bye'
-        expect(h['hi']).to eq 'bye'
-      end
-    end
-
-    describe '#try_convert' do
-      describe 'with conversion' do
-        let(:h) do
-          indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
-          subject.try_convert(indifferent_hash)
-        end
-
-        it 'is a subject' do
-          expect(h).to be_a(subject)
-        end
-      end
-
-      describe 'without conversion' do
-        let(:h) { subject.try_convert('{ :foo => bar }') }
-
-        it 'is nil' do
-          expect(h).to be_nil
-        end
-      end
-    end
-  end
-
-  describe 'with merge initializer' do
-    subject { IndifferentHashWithMergeInitializer }
-    it_should_behave_like 'hash with indifferent access'
-  end
-
-  describe 'with array initializer' do
-    subject { IndifferentHashWithArrayInitializer }
-    it_should_behave_like 'hash with indifferent access'
-  end
-
-  describe 'with try convert initializer' do
-    subject { IndifferentHashWithTryConvertInitializer }
-    it_should_behave_like 'hash with indifferent access'
-  end
-
-  describe 'with coercion' do
-    subject { CoercableHash }
-
-    let(:instance) { subject.new }
-
-    it 'supports coercion for ActiveSupport::HashWithIndifferentAccess' do
-      subject.coerce_key :foo, ActiveSupport::HashWithIndifferentAccess.new(Coercable => Coercable)
-      instance[:foo] = { 'bar_key' => 'bar_value', 'bar2_key' => 'bar2_value' }
-      expect(instance[:foo].keys).to all(be_coerced)
-      expect(instance[:foo].values).to all(be_coerced)
-      expect(instance[:foo]).to be_a(ActiveSupport::HashWithIndifferentAccess)
-    end
-  end
-
-  describe 'Mash with indifferent access' do
-    it 'is able to be created for a deep nested HashWithIndifferentAccess' do
-      indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(abc: { def: 123 })
-      MashWithIndifferentAccess.new(indifferent_hash)
-    end
-  end
-end
diff --git a/spec/hashie/extensions/mash/define_accessors_spec.rb b/spec/hashie/extensions/mash/define_accessors_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..de7abe7a13ffe1070f6197a10ce9dac925a5f578
--- /dev/null
+++ b/spec/hashie/extensions/mash/define_accessors_spec.rb
@@ -0,0 +1,90 @@
+require 'spec_helper'
+
+describe Hashie::Extensions::Mash::DefineAccessors do
+  let(:args) { [] }
+
+  shared_examples 'class with dynamically defined accessors' do
+    it 'defines reader on demand' do
+      expect(subject.method_defined?(:foo)).to be_falsey
+      instance.foo
+      expect(subject.method_defined?(:foo)).to be_truthy
+    end
+
+    it 'defines writer on demand' do
+      expect(subject.method_defined?(:foo=)).to be_falsey
+      instance.foo = :bar
+      expect(subject.method_defined?(:foo=)).to be_truthy
+    end
+
+    it 'defines predicate on demand' do
+      expect(subject.method_defined?(:foo?)).to be_falsey
+      instance.foo?
+      expect(subject.method_defined?(:foo?)).to be_truthy
+    end
+
+    it 'defines initializing reader on demand' do
+      expect(subject.method_defined?(:foo!)).to be_falsey
+      instance.foo!
+      expect(subject.method_defined?(:foo!)).to be_truthy
+    end
+
+    it 'defines underbang reader on demand' do
+      expect(subject.method_defined?(:foo_)).to be_falsey
+      instance.foo_
+      expect(subject.method_defined?(:foo_)).to be_truthy
+    end
+
+    context 'when initializing from another hash' do
+      let(:args) { [{ foo: :bar }] }
+
+      it 'does not define any accessors' do
+        expect(subject.method_defined?(:foo)).to be_falsey
+        expect(subject.method_defined?(:foo=)).to be_falsey
+        expect(subject.method_defined?(:foo?)).to be_falsey
+        expect(subject.method_defined?(:foo!)).to be_falsey
+        expect(subject.method_defined?(:foo_)).to be_falsey
+        expect(instance.foo).to eq :bar
+      end
+    end
+  end
+
+  context 'when included in Mash subclass' do
+    subject { Class.new(Hashie::Mash) { include Hashie::Extensions::Mash::DefineAccessors } }
+    let(:instance) { subject.new(*args) }
+
+    describe 'this subclass' do
+      it_behaves_like 'class with dynamically defined accessors'
+
+      describe 'when accessors are overrided in class' do
+        before do
+          subject.class_eval do
+            def foo
+              if self[:foo] != 1
+                :bar
+              else
+                super
+              end
+            end
+          end
+        end
+
+        it 'allows to call super' do
+          expect(instance.foo).to eq :bar
+          instance.foo = 2
+          expect(instance.foo).to eq :bar
+          instance.foo = 1
+          expect(instance.foo).to eq 1
+        end
+      end
+    end
+  end
+
+  context 'when Mash instance is extended' do
+    let(:instance) { Hashie::Mash.new(*args).with_accessors! }
+    subject { instance.singleton_class }
+
+    describe 'its singleton class' do
+      it_behaves_like 'class with dynamically defined accessors'
+    end
+  end
+end
diff --git a/spec/hashie/extensions/mash/permissive_respond_to_spec.rb b/spec/hashie/extensions/mash/permissive_respond_to_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..189f94a9ae1ee844cfc7c7bd71ef23e52c76be58
--- /dev/null
+++ b/spec/hashie/extensions/mash/permissive_respond_to_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+RSpec.describe Hashie::Extensions::Mash::PermissiveRespondTo do
+  class PermissiveMash < Hashie::Mash
+    include Hashie::Extensions::Mash::PermissiveRespondTo
+  end
+
+  it 'allows you to bind to unset getters' do
+    mash = PermissiveMash.new(a: 1)
+    other_mash = PermissiveMash.new(b: 2)
+
+    expect { mash.method(:b) }.not_to raise_error
+    expect(mash.method(:b).unbind.bind(other_mash).call).to eq 2
+  end
+
+  it 'works properly with SimpleDelegator' do
+    delegator = Class.new(SimpleDelegator) do
+      def initialize(hash)
+        super(PermissiveMash.new(hash))
+      end
+    end
+
+    foo = delegator.new(a: 1)
+
+    expect(foo.a).to eq 1
+    expect { foo.b }.not_to raise_error
+  end
+
+  context 'warnings' do
+    include_context 'with a logger'
+
+    it 'does not log a collision when setting normal keys' do
+      PermissiveMash.new(a: 1)
+
+      expect(logger_output).to be_empty
+    end
+
+    it 'logs a collision with a built-in method' do
+      PermissiveMash.new(zip: 1)
+
+      expect(logger_output).to match('PermissiveMash#zip defined in Enumerable')
+    end
+  end
+end
diff --git a/spec/hashie/extensions/mash/symbolize_keys_spec.rb b/spec/hashie/extensions/mash/symbolize_keys_spec.rb
index 1e5253422999a290b37cd2e9b18567c3a27c981a..bf58ea1c97b6f75bdb1f3659489aebfa036c8696 100644
--- a/spec/hashie/extensions/mash/symbolize_keys_spec.rb
+++ b/spec/hashie/extensions/mash/symbolize_keys_spec.rb
@@ -9,12 +9,30 @@ RSpec.describe Hashie::Extensions::Mash::SymbolizeKeys do
     end.to raise_error(ArgumentError)
   end
 
-  it 'symbolizes all keys in the Mash' do
-    my_mash = Class.new(Hashie::Mash) do
+  context 'when included in a Mash' do
+    class SymbolizedMash < Hashie::Mash
       include Hashie::Extensions::Mash::SymbolizeKeys
     end
 
-    expect(my_mash.new('test' => 'value').to_h).to eq(test: 'value')
+    it 'symbolizes string keys in the Mash' do
+      my_mash = SymbolizedMash.new('test' => 'value')
+      expect(my_mash.to_h).to eq(test: 'value')
+    end
+
+    it 'preserves keys which cannot be symbolized' do
+      my_mash = SymbolizedMash.new(
+        '1' => 'symbolizable one',
+        1 => 'one',
+        [1, 2, 3] => 'testing',
+        { 'test' => 'value' } => 'value'
+      )
+      expect(my_mash.to_h).to eq(
+        :'1' => 'symbolizable one',
+        1 => 'one',
+        [1, 2, 3] => 'testing',
+        { 'test' => 'value' } => 'value'
+      )
+    end
   end
 
   context 'implicit to_hash on double splat' do
@@ -26,7 +44,7 @@ RSpec.describe Hashie::Extensions::Mash::SymbolizeKeys do
     end
     let(:instance) { my_mash.new('outer' => { 'inner' => 42 }, 'testing' => [1, 2, 3]) }
 
-    subject { destructure.call(instance) }
+    subject { destructure.call(**instance) }
 
     it 'is converted on method calls' do
       expect(subject).to eq(outer: { inner: 42 }, testing: [1, 2, 3])
diff --git a/spec/hashie/extensions/method_access_spec.rb b/spec/hashie/extensions/method_access_spec.rb
index 03528e421076559e96296dea8e06ba93f148a0f5..b7490981814781e38adce95a874251bf207cde57 100644
--- a/spec/hashie/extensions/method_access_spec.rb
+++ b/spec/hashie/extensions/method_access_spec.rb
@@ -20,7 +20,7 @@ describe Hashie::Extensions::MethodReader do
   end
 
   it 'reads nil and false values out properly' do
-    h = subject.new(nil: nil, false: false)
+    h = subject.new(nil: nil, false: false) # rubocop:disable Lint/BooleanSymbol
     expect(h.nil).to eq nil
     expect(h.false).to eq false
   end
@@ -128,7 +128,14 @@ describe Hashie::Extensions::MethodAccess do
   it 'includes all of the other method mixins' do
     klass = Class.new(Hash)
     klass.send :include, Hashie::Extensions::MethodAccess
-    expect((klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodWriter, Hashie::Extensions::MethodQuery]).size).to eq 3
+
+    included_modules = klass.ancestors & [
+      Hashie::Extensions::MethodReader,
+      Hashie::Extensions::MethodWriter,
+      Hashie::Extensions::MethodQuery
+    ]
+
+    expect(included_modules.size).to eq 3
   end
 end
 
@@ -168,21 +175,59 @@ describe Hashie::Extensions::MethodOverridingWriter do
     end
 
     it 'aliases the method with two leading underscores' do
-      expect(subject.__zip).to eq [[%w(zip a-dee-doo-dah)]]
+      expect(subject.__zip).to eq [[%w[zip a-dee-doo-dah]]]
     end
 
     it 'does not re-alias when overriding an already overridden method' do
       subject.zip = 'test'
       expect(subject.zip).to eq 'test'
-      expect(subject.__zip).to eq [[%w(zip test)]]
+      expect(subject.__zip).to eq [[%w[zip test]]]
     end
   end
 end
 
 describe Hashie::Extensions::MethodAccessWithOverride do
   it 'includes all of the other method mixins' do
+    mod_list = [
+      Hashie::Extensions::MethodReader,
+      Hashie::Extensions::MethodOverridingWriter,
+      Hashie::Extensions::MethodQuery,
+      Hashie::Extensions::MethodOverridingInitializer
+    ]
+
     klass = Class.new(Hash)
     klass.send :include, Hashie::Extensions::MethodAccessWithOverride
-    expect((klass.ancestors & [Hashie::Extensions::MethodReader, Hashie::Extensions::MethodOverridingWriter, Hashie::Extensions::MethodQuery]).size).to eq 3
+
+    expect((klass.ancestors & mod_list).size).to eq 4
+  end
+end
+
+describe Hashie::Extensions::MethodOverridingInitializer do
+  class OverridingHash < Hash
+    include Hashie::Extensions::MethodOverridingInitializer
+  end
+
+  context 'when the key is a string' do
+    subject { OverridingHash.new('zip' => 'a-dee-doo-dah') }
+
+    it 'overrides the original method' do
+      expect(subject.zip).to eq 'a-dee-doo-dah'
+    end
+
+    it 'aliases the method with two leading underscores' do
+      expect(subject.__zip).to eq [[%w[zip a-dee-doo-dah]]]
+    end
+  end
+
+  context 'when the key is a symbol' do
+    subject { OverridingHash.new(zip: 'a-dee-doo-dah') }
+
+    it 'overrides the original method' do
+      expect(subject.zip).to eq 'a-dee-doo-dah'
+    end
+
+    it 'aliases the method with two leading underscores' do
+      expect(subject.__zip).to eq [[%w[zip a-dee-doo-dah]]]
+    end
   end
 end
diff --git a/spec/hashie/extensions/strict_key_access_spec.rb b/spec/hashie/extensions/strict_key_access_spec.rb
index 273cab86bb0d052354b021403ae30b3627ec4874..239e8f591fe64419e7a567e5fa9488f826fdb459 100644
--- a/spec/hashie/extensions/strict_key_access_spec.rb
+++ b/spec/hashie/extensions/strict_key_access_spec.rb
@@ -35,34 +35,33 @@ describe Hashie::Extensions::StrictKeyAccess do
     context 'lookup' do
       it('raises an error') do
         # Formatting of the error message does not vary here because raised by StrictKeyAccess
-        expect { instance.key(invalid_value) }.to raise_error KeyError,
-                                                              %(key not found with value of #{invalid_value.inspect})
+        expect { instance.key(invalid_value) }.to raise_error KeyError
       end
     end
   end
   shared_examples_for 'StrictKeyAccess raises KeyError instead of allowing defaults' do
     context '#default' do
       it 'raises an error' do
-        expect { instance.default(invalid_key) }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
-                                                                'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
+        expect { instance.default(invalid_key) }
+          .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
       end
     end
     context '#default=' do
       it 'raises an error' do
-        expect { instance.default = invalid_key }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
-                                                                 'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
+        expect { instance.default = invalid_key }
+          .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
       end
     end
     context '#default_proc' do
       it 'raises an error' do
-        expect { instance.default_proc }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
-                                                        'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
+        expect { instance.default_proc }
+          .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
       end
     end
     context '#default_proc=' do
       it 'raises an error' do
-        expect { instance.default_proc = proc {} }.to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError,
-                                                                  'Setting or using a default with Hashie::Extensions::StrictKeyAccess does not make sense'
+        expect { instance.default_proc = proc {} }
+          .to raise_error Hashie::Extensions::StrictKeyAccess::DefaultError
       end
     end
   end
diff --git a/spec/hashie/extensions/stringify_keys_spec.rb b/spec/hashie/extensions/stringify_keys_spec.rb
index f5f78d839ddad5d3004ff9bf8edaa3ae7bb5c14d..5d3269e7ca0f1855fbcdb2392e35a34fe4acf58a 100644
--- a/spec/hashie/extensions/stringify_keys_spec.rb
+++ b/spec/hashie/extensions/stringify_keys_spec.rb
@@ -14,7 +14,7 @@ shared_examples 'stringify_keys!' do
     object[:abc] = 'abc'
     object[123] = '123'
     invoke :stringify_keys!
-    expect((object.keys & %w(abc 123)).size).to eq 2
+    expect((object.keys & %w[abc 123]).size).to eq 2
   end
 
   it 'converts nested instances of the same class' do
@@ -53,7 +53,7 @@ shared_examples 'stringify_keys' do
     object[:abc] = 'def'
     copy = invoke :stringify_keys
     expect(object.keys).to eq [:abc]
-    expect(copy.keys).to eq %w(abc)
+    expect(copy.keys).to eq %w[abc]
   end
 end
 
@@ -71,7 +71,7 @@ describe Hashie::Extensions::StringifyKeys do
 
   context 'class methods' do
     subject { described_class }
-    let(:object) { Hash.new }
+    let(:object) { {} }
 
     describe '.stringify_keys' do
       include_examples 'stringify_keys'
@@ -113,7 +113,7 @@ describe Hashie do
   end
 
   subject { described_class }
-  let(:object) { Hash.new }
+  let(:object) { {} }
 
   describe '.stringify_keys' do
     include_examples 'stringify_keys'
diff --git a/spec/hashie/extensions/symbolize_keys_spec.rb b/spec/hashie/extensions/symbolize_keys_spec.rb
index b62fbe418a41fbac51d496633c7f090afe8ccc09..34ac8d8801f1046cafc5a251e64e26ce633c85cb 100644
--- a/spec/hashie/extensions/symbolize_keys_spec.rb
+++ b/spec/hashie/extensions/symbolize_keys_spec.rb
@@ -14,7 +14,7 @@ shared_examples 'symbolize_keys!' do
     object['abc'] = 'abc'
     object['def'] = 'def'
     invoke :symbolize_keys!
-    expect((object.keys & [:abc, :def]).size).to eq 2
+    expect((object.keys & %i[abc def]).size).to eq 2
   end
 
   it 'converts nested instances of the same class' do
@@ -76,7 +76,7 @@ describe Hashie::Extensions::SymbolizeKeys do
 
   context 'class methods' do
     subject { described_class }
-    let(:object) { Hash.new }
+    let(:object) { {} }
 
     describe '.symbolize_keys' do
       include_examples 'symbolize_keys'
@@ -88,8 +88,11 @@ describe Hashie::Extensions::SymbolizeKeys do
 
   context 'singleton methods' do
     subject { Hash }
-    let(:object) { subject.new.merge('a' => 1, 'b' => { 'c' => 2 }).extend(Hashie::Extensions::SymbolizeKeys) }
-    let(:expected_hash) { { a: 1, b: { c: 2 } } }
+    let(:object) do
+      subject.new.merge('a' => 1, 'b' => { 'c' => 2 }, 1 => 'numeric key')
+             .extend(Hashie::Extensions::SymbolizeKeys)
+    end
+    let(:expected_hash) { { a: 1, b: { c: 2 }, 1 => 'numeric key' } }
 
     describe '.symbolize_keys' do
       it 'does not raise error' do
@@ -118,7 +121,7 @@ describe Hashie do
   end
 
   subject { described_class }
-  let(:object) { Hash.new }
+  let(:object) { {} }
 
   describe '.symbolize_keys' do
     include_examples 'symbolize_keys'
diff --git a/spec/hashie/hash_spec.rb b/spec/hashie/hash_spec.rb
index bcfc6c78decb187586d2c82c10da3a1aa5025090..9e2b4e0a5e0f53d6c4e0a2f9caef13c004568a65 100644
--- a/spec/hashie/hash_spec.rb
+++ b/spec/hashie/hash_spec.rb
@@ -41,7 +41,7 @@ describe Hash do
   it '#to_hash with symbolize_keys set to true returns a hash with symbolized keys' do
     hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3]]
     symbolized_hash = hash.to_hash(symbolize_keys: true)
-    expect(symbolized_hash).to eq(a: 'hey', :"123" => 'bob', array: [1, 2, 3])
+    expect(symbolized_hash).to eq(a: 'hey', 123 => 'bob', array: [1, 2, 3])
   end
 
   it "#to_hash should not blow up when #to_hash doesn't accept arguments" do
@@ -64,21 +64,60 @@ describe Hash do
     end
 
     it '#to_hash returns a hash with same keys' do
-      hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
+      hash = Hashie::Hash[
+        'a' => 'hey',
+        123 => 'bob',
+        'array' => [1, 2, 3],
+        subhash: ClassRespondsToHash.new
+      ]
       stringified_hash = hash.to_hash
-      expect(stringified_hash).to eq('a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: { 'a' => 'hey', b: 'bar', 123 => 'bob', 'array' => [1, 2, 3] })
+
+      expected = {
+        'a' => 'hey',
+        123 => 'bob',
+        'array' => [1, 2, 3],
+        subhash: { 'a' => 'hey', b: 'bar', 123 => 'bob', 'array' => [1, 2, 3] }
+      }
+
+      expect(stringified_hash).to eq(expected)
     end
 
     it '#to_hash with stringify_keys set to true returns a hash with stringified_keys' do
-      hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
+      hash = Hashie::Hash[
+        'a' => 'hey',
+        123 => 'bob',
+        'array' => [1, 2, 3],
+        subhash: ClassRespondsToHash.new
+      ]
       symbolized_hash = hash.to_hash(stringify_keys: true)
-      expect(symbolized_hash).to eq('a' => 'hey', '123' => 'bob', 'array' => [1, 2, 3], 'subhash' => { 'a' => 'hey', 'b' => 'bar', '123' => 'bob', 'array' => [1, 2, 3] })
+
+      expected = {
+        'a' => 'hey',
+        '123' => 'bob',
+        'array' => [1, 2, 3],
+        'subhash' => { 'a' => 'hey', 'b' => 'bar', '123' => 'bob', 'array' => [1, 2, 3] }
+      }
+
+      expect(symbolized_hash).to eq(expected)
     end
 
     it '#to_hash with symbolize_keys set to true returns a hash with symbolized keys' do
-      hash = Hashie::Hash['a' => 'hey', 123 => 'bob', 'array' => [1, 2, 3], subhash: ClassRespondsToHash.new]
+      hash = Hashie::Hash[
+        'a' => 'hey',
+        123 => 'bob',
+        'array' => [1, 2, 3],
+        subhash: ClassRespondsToHash.new
+      ]
       symbolized_hash = hash.to_hash(symbolize_keys: true)
-      expect(symbolized_hash).to eq(a: 'hey', :"123" => 'bob', array: [1, 2, 3], subhash: { a: 'hey', b: 'bar', :'123' => 'bob', array: [1, 2, 3] })
+
+      expected = {
+        a: 'hey',
+        123 => 'bob',
+        array: [1, 2, 3],
+        subhash: { a: 'hey', b: 'bar', 123 => 'bob', array: [1, 2, 3] }
+      }
+
+      expect(symbolized_hash).to eq(expected)
     end
   end
 end
diff --git a/spec/hashie/mash_spec.rb b/spec/hashie/mash_spec.rb
index 162d9827eeabb3c76f23fd9e36a4ae3a528949d1..84a28eadf14e87044cce6847303a8a48aae2b148 100644
--- a/spec/hashie/mash_spec.rb
+++ b/spec/hashie/mash_spec.rb
@@ -75,7 +75,7 @@ describe Hashie::Mash do
   # Added due to downstream gems assuming indifferent access to be true for Mash
   # When this is not, bump major version so that downstream gems can target
   # correct version and fix accordingly.
-  # See https://github.com/intridea/hashie/pull/197
+  # See https://github.com/hashie/hashie/pull/197
   it 'maintains indifferent access when nested' do
     subject[:a] = { b: 'c' }
     expect(subject[:a][:b]).to eq 'c'
@@ -147,21 +147,44 @@ describe Hashie::Mash do
       mash[:test_key] = 'Test value'
 
       expect { mash[:test_key] = 'A new value' }.not_to raise_error
-      expect(logger_output).to be_blank
+      expect(logger_output).to be_empty
     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
+      expect(logger_output).to be_empty
+    end
+
+    it 'does not write to the logger when setting most affixed keys' do
+      underbang = Hashie::Mash.new('foo_' => 'foo')
+      bang = Hashie::Mash.new('foo!' => 'foo')
+      query = Hashie::Mash.new('foo?' => 'foo')
+
+      expect(logger_output).to be_empty
+      expect(underbang.foo_).to eq 'foo'
+      expect(bang.foo!).to eq 'foo'
+      expect(query.foo?).to eq 'foo'
+    end
+
+    it 'warns when setting a key that looks like a setter' do
+      setter = Hashie::Mash.new('foo=' => 'foo')
+
+      expect(logger_output).to match 'Hashie::Mash#foo='
+      expect('setter.foo=').not_to parse_as_valid_ruby
+
+      setter.foo = 'bar'
+
+      expect(setter.to_h).to eq 'foo=' => 'foo'
     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
@@ -172,7 +195,79 @@ describe Hashie::Mash do
 
       grandchild_class.new('trust' => { 'two' => 2 })
 
-      expect(logger_output).to be_blank
+      expect(logger_output).to be_empty
+    end
+
+    it 'writes to logger when a key is overridden that is not ignored' do
+      mash_class = Class.new(Hashie::Mash) do
+        disable_warnings :merge
+      end
+
+      mash_class.new('address' => { 'zip' => '90210' })
+      expect(logger_output).not_to be_empty
+    end
+
+    it 'does not write to logger when a key is overridden that is ignored' do
+      mash_class = Class.new(Hashie::Mash) do
+        disable_warnings :zip
+      end
+
+      mash_class.new('address' => { 'zip' => '90210' })
+      expect(logger_output).to be_empty
+    end
+
+    it 'carries over the ignored warnings list for warnings on grandchild classes' do
+      child_class = Class.new(Hashie::Mash) do
+        disable_warnings :zip, :merge
+      end
+      grandchild_class = Class.new(child_class)
+
+      grandchild_class.new('address' => { 'zip' => '90210' }, 'merge' => true)
+
+      expect(grandchild_class.disabled_warnings).to eq(%i[zip merge])
+      expect(logger_output).to be_empty
+    end
+
+    context 'multiple disable_warnings calls' do
+      context 'calling disable_warnings multiple times with parameters' do
+        it 'appends each new parameter to the ignore list' do
+          child_class = Class.new(Hashie::Mash) do
+            disable_warnings :zip
+            disable_warnings :merge
+            disable_warnings :cycle
+          end
+
+          expect(child_class.disabled_warnings).to eq(%i[zip merge cycle])
+        end
+      end
+
+      context 'calling disable_warnings without keys after calling with keys' do
+        it 'uses the last call to determine the ignore list' do
+          child_class = Class.new(Hashie::Mash) do
+            disable_warnings :zip
+            disable_warnings
+          end
+
+          child_class.new('address' => { 'zip' => '90210' }, 'merge' => true, 'cycle' => 'bi')
+
+          expect(child_class.disabled_warnings).to eq([])
+          expect(logger_output).to be_empty
+        end
+      end
+
+      context 'calling disable_parameters with keys after calling without keys' do
+        it 'only ignores logging for ignored methods' do
+          child_class = Class.new(Hashie::Mash) do
+            disable_warnings
+            disable_warnings :zip
+          end
+
+          child_class.new('address' => { 'zip' => '90210' }, 'merge' => true)
+
+          expect(logger_output).to match(/#{child_class}#merge/)
+          expect(logger_output).not_to match(/#{child_class}#zip/)
+        end
+      end
     end
   end
 
@@ -184,7 +279,8 @@ describe Hashie::Mash do
         details: {
           email: 'michael@asf.com',
           address: 'Nowhere road'
-        })
+        }
+      )
     end
 
     describe '#deep_update' do
@@ -228,15 +324,29 @@ describe Hashie::Mash do
 
       # http://www.ruby-doc.org/core-1.9.3/Hash.html#method-i-update
       it 'accepts a block' do
-        duped = subject.merge(details: { address: 'Pasadena CA' }) { |_, oldv, newv| [oldv, newv].join(', ') }
+        duped = subject.merge(details: { address: 'Pasadena CA' }) do |_, oldv, newv|
+          [oldv, newv].join(', ')
+        end
+
         expect(duped.details.address).to eq 'Nowhere road, Pasadena CA'
       end
 
       it 'copies values for non-duplicate keys when a block is supplied' do
-        duped = subject.merge(details: { address: 'Pasadena CA', state: 'West Thoughtleby' }) { |_, oldv, _| oldv }
+        m_hash = { details: { address: 'Pasadena CA', state: 'West Thoughtleby' } }
+        duped = subject.merge(m_hash) { |_, oldv, _| oldv }
+
         expect(duped.details.address).to eq 'Nowhere road'
         expect(duped.details.state).to eq 'West Thoughtleby'
       end
+
+      it 'does not raise an exception when default_proc raises an error' do
+        hash = described_class.new(a: 1) { |_k, _v| raise('Should not be raise I') }
+        other_has = described_class.new(a: 2, b: 2) { |_k, _v| raise('Should not be raise II') }
+        expected_hash = described_class.new(a: 2, b: 2)
+
+        res = hash.merge(other_has)
+        expect(res).to eq(expected_hash)
+      end
     end
 
     describe 'shallow update' do
@@ -284,7 +394,7 @@ describe Hashie::Mash do
       end
 
       it 'leaves only specified keys' do
-        expect(subject.keys.sort).to eq %w(details middle_name)
+        expect(subject.keys.sort).to eq %w[details middle_name]
         expect(subject.first_name?).to be_falsy
         expect(subject).not_to respond_to(:first_name)
         expect(subject.last_name?).to be_falsy
@@ -386,28 +496,28 @@ describe Hashie::Mash do
     end
 
     it 'responds to a set key with a suffix' do
-      %w(= ? ! _).each do |suffix|
+      %w[= ? ! _].each do |suffix|
         expect(subject).to be_respond_to(:"abc#{suffix}")
       end
     end
 
     it 'is able to access the suffixed key as a method' do
-      %w(= ? ! _).each do |suffix|
+      %w[= ? ! _].each do |suffix|
         expect(subject.method(:"abc#{suffix}")).to_not be_nil
       end
     end
 
     it 'responds to an unknown key with a suffix' do
-      %w(= ? ! _).each do |suffix|
+      %w[= ? ! _].each do |suffix|
         expect(subject).to be_respond_to(:"xyz#{suffix}")
       end
     end
 
     it 'is able to access an unknown suffixed key as a method' do
       # See https://github.com/intridea/hashie/pull/285 for more information
-      pending_for(engine: 'ruby', versions: %w(2.2.0 2.2.1 2.2.2))
+      pending_for(engine: 'ruby', versions: %w[2.2.0 2.2.1 2.2.2])
 
-      %w(= ? ! _).each do |suffix|
+      %w[= ? ! _].each do |suffix|
         expect(subject.method(:"xyz#{suffix}")).to_not be_nil
       end
     end
@@ -457,6 +567,13 @@ describe Hashie::Mash do
       expect(initial.test?).to be_truthy
     end
 
+    it 'allows propagation of a default block' do
+      h = Hashie::Mash.new { |mash, key| mash[key] = mash.class.new(&mash.default_proc) }
+      expect { h[:x][:y][:z] = :xyz }.not_to raise_error
+      expect(h.x.y.z).to eq(:xyz)
+      expect(h[:x][:y][:z]).to eq(:xyz)
+    end
+
     it 'allows assignment of an empty array in a default block' do
       initial = Hashie::Mash.new { |h, k| h[k] = [] }
       initial.hello << 100
@@ -496,6 +613,18 @@ describe Hashie::Mash do
       expect(converted.to_hash['a'].first.is_a?(Hash)).to be_truthy
       expect(converted.to_hash['a'].first['c'].first.is_a?(Hashie::Mash)).to be_falsy
     end
+
+    it 'only stringifies keys which can be converted to symbols' do
+      initial_hash = { 1 => 'a', ['b'] => 2, 'c' => 3, d: 4 }
+      converted = Hashie::Mash.new(initial_hash)
+      expect(converted).to eq(1 => 'a', ['b'] => 2, 'c' => 3, 'd' => 4)
+    end
+
+    it 'preserves keys which cannot be converted to symbols' do
+      initial_hash = { 1 => 'a', '1' => 'b', :'1' => 'c' }
+      converted = Hashie::Mash.new(initial_hash)
+      expect(converted).to eq(1 => 'a', '1' => 'c')
+    end
   end
 
   describe '#fetch' do
@@ -553,7 +682,7 @@ describe Hashie::Mash do
     end
 
     it 'includes all keys' do
-      expect(mash.to_hash.keys).to eql(%w(outer testing))
+      expect(mash.to_hash.keys).to eql(%w[outer testing])
     end
 
     it 'converts keys to symbols when symbolize_keys option is true' do
@@ -625,7 +754,7 @@ describe Hashie::Mash do
     context 'if the file exists' do
       before do
         expect(File).to receive(:file?).with(path).and_return(true)
-        expect(parser).to receive(:perform).with(path).and_return(config)
+        expect(parser).to receive(:perform).with(path, {}).and_return(config)
       end
 
       it { is_expected.to be_a(Hashie::Mash) }
@@ -657,7 +786,7 @@ describe Hashie::Mash do
 
       before do
         expect(File).to receive(:file?).with(path).and_return(true)
-        expect(parser).to receive(:perform).with(path).and_return(config)
+        expect(parser).to receive(:perform).with(path, {}).and_return(config)
       end
 
       it 'return a Mash from a file' do
@@ -673,8 +802,8 @@ describe Hashie::Mash do
       before do
         expect(File).to receive(:file?).with(path).and_return(true)
         expect(File).to receive(:file?).with("#{path}+1").and_return(true)
-        expect(parser).to receive(:perform).once.with(path).and_return(config)
-        expect(parser).to receive(:perform).once.with("#{path}+1").and_return(config)
+        expect(parser).to receive(:perform).once.with(path, {}).and_return(config)
+        expect(parser).to receive(:perform).once.with("#{path}+1", {}).and_return(config)
       end
 
       it 'cache the loaded yml file', :test_cache do
@@ -686,6 +815,50 @@ describe Hashie::Mash do
         expect(subject.object_id).to eq subject.object_id
       end
     end
+
+    context 'when the file has aliases in it' do
+      it 'can use the aliases and does not raise an error' do
+        mash = Hashie::Mash.load('spec/fixtures/yaml_with_aliases.yml')
+        expect(mash.company_a.accounts.admin.password).to eq('secret')
+      end
+      it 'can override the value of aliases' do
+        expect do
+          Hashie::Mash.load('spec/fixtures/yaml_with_aliases.yml', aliases: false)
+        end.to raise_error Psych::BadAlias, /base_accounts/
+      end
+    end
+
+    context 'when the file has symbols' do
+      it 'can override the value of permitted_classes' do
+        mash = Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml', permitted_classes: [Symbol])
+        expect(mash.user_icon.width).to eq(200)
+      end
+      it 'uses defaults for permitted_classes' do
+        expect do
+          Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml')
+        end.to raise_error Psych::DisallowedClass, /Symbol/
+      end
+      it 'can override the value of permitted_symbols' do
+        mash = Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml',
+                                 permitted_classes: [Symbol],
+                                 permitted_symbols: %i[
+                                   user_icon
+                                   width
+                                   height
+                                 ])
+        expect(mash.user_icon.width).to eq(200)
+      end
+      it 'raises an error on insufficient permitted_symbols' do
+        expect do
+          Hashie::Mash.load('spec/fixtures/yaml_with_symbols.yml',
+                            permitted_classes: [Symbol],
+                            permitted_symbols: %i[
+                              user_icon
+                              width
+                            ])
+        end.to raise_error Psych::DisallowedClass, /Symbol/
+      end
+    end
   end
 
   describe '#to_module(mash_method_name)' do
@@ -709,18 +882,6 @@ describe Hashie::Mash do
     end
   end
 
-  describe '#extractable_options?' do
-    require 'active_support'
-
-    subject { described_class.new(name: 'foo') }
-    let(:args) { [101, 'bar', subject] }
-
-    it 'can be extracted from an array' do
-      expect(args.extract_options!).to eq subject
-      expect(args).to eq [101, 'bar']
-    end
-  end
-
   describe '#reverse_merge' do
     subject { described_class.new(a: 1, b: 2) }
 
@@ -743,21 +904,210 @@ describe Hashie::Mash do
     end
   end
 
+  describe '#invert' do
+    subject(:mash) { described_class.new(a: 'apple', b: 4) }
+
+    it 'returns a Hashie::Mash' do
+      expect(mash.invert).to be_kind_of(described_class)
+    end
+
+    it 'returns a mash with the keys and values inverted' do
+      expect(mash.invert).to eq('apple' => 'a', 4 => 'b')
+    end
+
+    context 'when using with subclass' do
+      let(:subclass) { Class.new(Hashie::Mash) }
+      subject(:sub_mash) { subclass.new(a: 1, b: nil) }
+
+      it 'creates an instance of subclass' do
+        expect(sub_mash.invert).to be_kind_of(subclass)
+      end
+    end
+  end
+
+  describe '#reject' do
+    subject(:mash) { described_class.new(a: 1, b: nil) }
+
+    it 'returns a Hashie::Mash' do
+      expect(mash.reject { |_k, v| v.nil? }).to be_kind_of(described_class)
+    end
+
+    it 'rejects keys for which the block returns true' do
+      expect(mash.reject { |_k, v| v.nil? }).to eq('a' => 1)
+    end
+
+    context 'when using with subclass' do
+      let(:subclass) { Class.new(Hashie::Mash) }
+      subject(:sub_mash) { subclass.new(a: 1, b: nil) }
+
+      it 'creates an instance of subclass' do
+        expect(sub_mash.reject { |_k, v| v.nil? }).to be_kind_of(subclass)
+      end
+    end
+  end
+
+  describe '#select' do
+    subject(:mash) { described_class.new(a: 'apple', b: 4) }
+
+    it 'returns a Hashie::Mash' do
+      expect(mash.select { |_k, v| v.is_a? String }).to be_kind_of(described_class)
+    end
+
+    it 'selects keys for which the block returns true' do
+      expect(mash.select { |_k, v| v.is_a? String }).to eq('a' => 'apple')
+    end
+
+    context 'when using with subclass' do
+      let(:subclass) { Class.new(Hashie::Mash) }
+      subject(:sub_mash) { subclass.new(a: 1, b: nil) }
+
+      it 'creates an instance of subclass' do
+        expect(sub_mash.select { |_k, v| v.is_a? String }).to be_kind_of(subclass)
+      end
+    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 }) }
+
       it 'accepts both string and symbol as key' do
         expect(subject.dig(:a, :b)).to eq(1)
         expect(subject.dig('a', 'b')).to eq(1)
       end
 
-      context 'with numeric key' do
-        subject { described_class.new('1' => { b: 1 }) }
-        it 'accepts a numeric value as key' do
-          expect(subject.dig(1, :b)).to eq(1)
-          expect(subject.dig('1', :b)).to eq(1)
+      context 'when the Mash wraps a Hashie::Array' do
+        it 'handles digging into an array' do
+          mash = described_class.new(alphabet: { first_three: Hashie::Array['a', 'b', 'c'] })
+
+          expect(mash.dig(:alphabet, :first_three, 0)).to eq 'a'
         end
       end
     end
   end
+
+  with_minimum_ruby('2.4.0') do
+    describe '#transform_values' do
+      subject(:mash) { described_class.new(a: 1) }
+
+      it 'returns a Hashie::Mash' do
+        expect(mash.transform_values(&:to_s)).to be_kind_of(described_class)
+      end
+
+      it 'transforms the value' do
+        expect(mash.transform_values(&:to_s).a).to eql('1')
+      end
+
+      context 'when using with subclass' do
+        let(:subclass) { Class.new(Hashie::Mash) }
+        subject(:sub_mash) { subclass.new(a: 1).transform_values { |a| a + 2 } }
+
+        it 'creates an instance of subclass' do
+          expect(sub_mash).to be_kind_of(subclass)
+        end
+      end
+    end
+
+    describe '#compact' do
+      subject(:mash) { described_class.new(a: 1, b: nil) }
+
+      it 'returns a Hashie::Mash' do
+        expect(mash.compact).to be_kind_of(described_class)
+      end
+
+      it 'removes keys with nil values' do
+        expect(mash.compact).to eq('a' => 1)
+      end
+
+      context 'when using with subclass' do
+        let(:subclass) { Class.new(Hashie::Mash) }
+        subject(:sub_mash) { subclass.new(a: 1, b: nil) }
+
+        it 'creates an instance of subclass' do
+          expect(sub_mash.compact).to be_kind_of(subclass)
+        end
+      end
+    end
+  end
+
+  with_minimum_ruby('2.5.0') do
+    describe '#slice' do
+      subject(:mash) { described_class.new(a: 1, b: 2) }
+
+      it 'returns a Hashie::Mash' do
+        expect(mash.slice(:a)).to be_kind_of(described_class)
+      end
+
+      it 'returns a Mash with only the keys passed' do
+        expect(mash.slice(:a).to_hash).to eq('a' => 1)
+      end
+
+      context 'when using with subclass' do
+        let(:subclass) { Class.new(Hashie::Mash) }
+        subject(:sub_mash) { subclass.new(a: 1, b: 2) }
+
+        it 'creates an instance of subclass' do
+          expect(sub_mash.slice(:a)).to be_kind_of(subclass)
+        end
+      end
+    end
+
+    describe '#transform_keys' do
+      subject(:mash) { described_class.new(a: 1, b: 2) }
+
+      it 'returns a Hashie::Mash' do
+        expect(mash.transform_keys { |k| k + k }).to be_kind_of(described_class)
+      end
+
+      it 'returns a Mash with transformed keys' do
+        expect(mash.transform_keys { |k| k + k }).to eq('aa' => 1, 'bb' => 2)
+      end
+
+      context 'when using with subclass' do
+        let(:subclass) { Class.new(Hashie::Mash) }
+        subject(:sub_mash) { subclass.new(a: 1, b: 2) }
+
+        it 'creates an instance of subclass' do
+          expect(sub_mash.transform_keys { |k| k + k }).to be_kind_of(subclass)
+        end
+      end
+    end
+  end
+
+  with_minimum_ruby('2.6.0') do
+    context 'ruby 2.6 merging' do
+      subject(:mash) { Hashie::Mash.new(model: 'Honda') }
+      it 'merges multiple hashes and mashes passeed to #merge' do
+        first_hash = { model: 'Ford' }
+        second_hash = { model: 'DeLorean' }
+        expect(mash.merge(first_hash, second_hash)).to eq('model' => 'DeLorean')
+      end
+    end
+  end
+
+  with_minimum_ruby('3.0.0') do
+    context '#except' do
+      subject(:mash) { described_class.new(a: 'A', b: 'B') }
+      it 'return a Hashie::Mash' do
+        expect(mash.except(:b)).to be_kind_of(described_class)
+      end
+
+      it 'excludes keys' do
+        expect(mash.except(:b)).to eq('a' => 'A')
+      end
+    end
+  end
 end
diff --git a/spec/hashie/parsers/yaml_erb_parser_spec.rb b/spec/hashie/parsers/yaml_erb_parser_spec.rb
index 72ea1f14b01a78a0c860f410b64a9cb58db46ba3..aeef25af6b82665bce65b31ecf7d19a159e47660 100644
--- a/spec/hashie/parsers/yaml_erb_parser_spec.rb
+++ b/spec/hashie/parsers/yaml_erb_parser_spec.rb
@@ -4,12 +4,12 @@ describe Hashie::Extensions::Parsers::YamlErbParser do
   describe '.perform' do
     context 'a file' do
       let(:config) do
-        <<-EOF
+        <<-CONFIG
 ---
 foo: verbatim
 bar: <%= "erb" %>
 baz: "<%= __FILE__ %>"
-        EOF
+        CONFIG
       end
       let(:path) { 'template.yml' }
 
@@ -36,7 +36,7 @@ baz: "<%= __FILE__ %>"
         file
       end
 
-      subject { described_class.new(Pathname tempfile.path) }
+      subject { described_class.new(Pathname(tempfile.path)) }
 
       it '"#perform" can be done in case of path is a Pathname object.' do
         expect(subject.perform).to eq 'foo' => 'hello'
diff --git a/spec/hashie/rash_spec.rb b/spec/hashie/rash_spec.rb
index 914e7b4e1f32f157f7fdce617f65c3b770a1b104..11fed92fd552d31833d06f3d6511db586f6f849a 100644
--- a/spec/hashie/rash_spec.rb
+++ b/spec/hashie/rash_spec.rb
@@ -10,7 +10,7 @@ describe Hashie::Rash do
       1       => 'awesome',
       1..1000 => 'rangey',
       /(bcd)/ => proc { |m| m[1] }
-    # /.+/ => "EVERYTHING"
+      # /.+/ => "EVERYTHING"
     )
   end
 
@@ -18,7 +18,7 @@ describe Hashie::Rash do
     expect(subject['other']).to eq 'whee'
     expect(subject['well hello there']).to eq 'hello'
     expect(subject['the world is round']).to eq 'world'
-    expect(subject.all('hello world').sort).to eq %w(hello world)
+    expect(subject.all('hello world').sort).to eq %w[hello world]
   end
 
   it 'finds regexps' do
@@ -74,4 +74,10 @@ describe Hashie::Rash do
     expect(subject.respond_to?(:to_a)).to be true
     expect(subject.methods).to_not include(:to_a)
   end
+
+  it 'does not lose keys' do
+    subject.optimize_every = 1
+    expect(subject['hello']).to eq('hello')
+    expect(subject['world']).to eq('world')
+  end
 end
diff --git a/spec/hashie/trash_spec.rb b/spec/hashie/trash_spec.rb
index 80b093e32696065124a9147650ba5c18e9657b24..aaabe7022f67ea71714bdf1ff32b0d9137e1278c 100644
--- a/spec/hashie/trash_spec.rb
+++ b/spec/hashie/trash_spec.rb
@@ -153,7 +153,24 @@ describe Hashie::Trash do
     end
 
     it 'maintains translations hash mapping from the original to the translated name' do
-      expect(SomeDataModel.translations).to eq(config: [:value_a, :value_b])
+      expect(SomeDataModel.translations).to eq(config: %i[value_a value_b])
+    end
+  end
+
+  describe 'translating multiple properties from the same source hash key' do
+    class AnotherDataModel < Hashie::Trash
+      property :first_name,                          transform_with: ->(n) { n.upcase }
+      property :first_name_short, from: :first_name, transform_with: ->(n) { n[0, 3] }
+    end
+
+    subject { AnotherDataModel.new(first_name: 'Cathy') }
+
+    it 'translates the first key with the given lambda' do
+      expect(subject.first_name).to eq('CATHY')
+    end
+
+    it 'translates the second key with the given lambda and the initial value of the first key' do
+      expect(subject.first_name_short).to eq('Cat')
     end
   end
 
@@ -188,7 +205,9 @@ describe Hashie::Trash do
     end
 
     it 'transforms the value when given in constructor' do
-      expect(TrashLambdaTestWithProperties.new(first_name: 'Michael').first_name).to eq 'Michael'.reverse
+      expect(
+        TrashLambdaTestWithProperties.new(first_name: 'Michael').first_name
+      ).to eq 'Michael'.reverse
     end
 
     context 'when :from option is given' do
@@ -265,4 +284,68 @@ describe Hashie::Trash do
       expect(subject.first_name).to eq('Frodo')
     end
   end
+
+  context 'when copying properties from other properties' do
+    it 'retains the original and also sets the copy' do
+      simple = Class.new(Hashie::Trash) do
+        property :id
+        property :copy_of_id, from: :id
+      end
+
+      subject = simple.new(id: 1)
+
+      expect(subject.id).to eq(1)
+      expect(subject.copy_of_id).to eq(1)
+    end
+
+    it 'grabs the default for the original if it is not set' do
+      with_default = Class.new(Hashie::Trash) do
+        property :id, default: 0
+        property :copy_of_id, from: :id
+      end
+
+      subject = with_default.new
+
+      expect(subject.id).to eq(0)
+      expect(subject.copy_of_id).to eq(0)
+    end
+
+    it 'can be a required value' do
+      with_required = Class.new(Hashie::Trash) do
+        property :id
+        property :copy_of_id, from: :id, required: true, message: 'must be set'
+      end
+
+      expect { with_required.new }.to raise_error(
+        ArgumentError, "The property 'copy_of_id' must be set"
+      )
+    end
+
+    it 'does not set properties that do not exist' do
+      from_non_property = Class.new(Hashie::Trash) do
+        property :copy_of_value, from: :value
+      end
+
+      subject = from_non_property.new(value: 0)
+
+      expect(subject).not_to respond_to(:value)
+      expect { subject[:value] }.to raise_error(
+        NoMethodError, "The property 'value' is not defined for ."
+      )
+      expect(subject.to_h[:value]).to eq(nil)
+      expect(subject.copy_of_value).to eq(0)
+    end
+
+    it 'is not order-dependent in definition' do
+      simple = Class.new(Hashie::Trash) do
+        property :copy_of_id, from: :id
+        property :id
+      end
+
+      subject = simple.new(id: 1)
+
+      expect(subject.id).to eq(1)
+      expect(subject.copy_of_id).to eq(1)
+    end
+  end
 end
diff --git a/spec/integration/active_support/Gemfile b/spec/integration/active_support/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..eee6625f552ff3e61176d571a9feff9317b41366
--- /dev/null
+++ b/spec/integration/active_support/Gemfile
@@ -0,0 +1,15 @@
+source 'http://rubygems.org'
+
+gem 'hashie', path: '../../..'
+require File.expand_path('../../../../lib/hashie/extensions/ruby_version', __FILE__)
+
+# rubocop:disable Bundler/DuplicatedGem
+if Hashie::Extensions::RubyVersion.new(RUBY_VERSION) >= Hashie::Extensions::RubyVersion.new('2.4.0')
+  gem 'activesupport', '~> 5.x', require: false
+else
+  gem 'activesupport', '~> 4.x', require: false
+end
+# rubocop:enable Bundler/DuplicatedGem
+
+gem 'rake'
+gem 'rspec', '~> 3.5.0'
diff --git a/spec/integration/active_support/integration_spec.rb b/spec/integration/active_support/integration_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..e0503c8e730435475a6d024de9304ef52f3e1bef
--- /dev/null
+++ b/spec/integration/active_support/integration_spec.rb
@@ -0,0 +1,371 @@
+require 'active_support'
+require 'active_support/core_ext'
+require 'active_support/core_ext/hash/indifferent_access'
+require 'active_support/core_ext/hash'
+require 'hashie'
+
+RSpec.configure do |config|
+  config.expect_with :rspec do |expect|
+    expect.syntax = :expect
+  end
+end
+
+RSpec.describe Hashie::Mash do
+  describe '#extractable_options?' do
+    subject { Hashie::Mash.new(name: 'foo') }
+    let(:args) { [101, 'bar', subject] }
+
+    it 'can be extracted from an array' do
+      expect(args.extract_options!).to eq subject
+      expect(args).to eq [101, 'bar']
+    end
+  end
+end
+
+RSpec.describe Hashie::Extensions::DeepFind do
+  let(:hash) do
+    {
+      library: {
+        books: [
+          { title: 'Call of the Wild' },
+          { title: 'Moby Dick' }
+        ],
+        shelves: nil,
+        location: {
+          address: '123 Library St.',
+          title: 'Main Library'
+        }
+      }
+    }
+  end
+
+  subject(:instance) { hash.with_indifferent_access.extend(Hashie::Extensions::DeepFind) }
+
+  describe '#deep_find' do
+    it 'indifferently detects a value from a nested hash' do
+      expect(instance.deep_find(:address)).to eq('123 Library St.')
+      expect(instance.deep_find('address')).to eq('123 Library St.')
+    end
+
+    it 'indifferently detects a value from a nested array' do
+      expect(instance.deep_find(:title)).to eq('Call of the Wild')
+      expect(instance.deep_find('title')).to eq('Call of the Wild')
+    end
+
+    it 'indifferently returns nil if it does not find a match' do
+      expect(instance.deep_find(:wahoo)).to be_nil
+      expect(instance.deep_find('wahoo')).to be_nil
+    end
+  end
+
+  describe '#deep_find_all' do
+    it 'indifferently detects all values from a nested hash' do
+      expect(instance.deep_find_all(:title))
+        .to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
+      expect(instance.deep_find_all('title'))
+        .to eq(['Call of the Wild', 'Moby Dick', 'Main Library'])
+    end
+
+    it 'indifferently returns nil if it does not find any matches' do
+      expect(instance.deep_find_all(:wahoo)).to be_nil
+      expect(instance.deep_find_all('wahoo')).to be_nil
+    end
+  end
+end
+
+RSpec.describe Hashie::Extensions::DeepLocate do
+  let(:hash) do
+    {
+      from: 0,
+      size: 25,
+      query: {
+        bool: {
+          must: [
+            {
+              query_string: {
+                query: 'foobar',
+                default_operator: 'AND',
+                fields: [
+                  'title^2',
+                  '_all'
+                ]
+              }
+            },
+            {
+              match: {
+                field_1: 'value_1'
+              }
+            },
+            {
+              range: {
+                lsr09: {
+                  gte: 2014
+                }
+              }
+            }
+          ],
+          should: [
+            {
+              match: {
+                field_2: 'value_2'
+              }
+            }
+          ],
+          must_not: [
+            {
+              range: {
+                lsr10: {
+                  gte: 2014
+                }
+              }
+            }
+          ]
+        }
+      }
+    }
+  end
+
+  describe '#deep_locate' do
+    subject(:instance) { hash.with_indifferent_access.extend(described_class) }
+
+    it 'can locate symbolic keys' do
+      expect(described_class.deep_locate(:lsr10, instance)).to eq ['lsr10' => { 'gte' => 2014 }]
+    end
+
+    it 'can locate string keys' do
+      expect(described_class.deep_locate('lsr10', instance)).to eq ['lsr10' => { 'gte' => 2014 }]
+    end
+  end
+end
+
+RSpec.describe Hashie::Extensions::IndifferentAccess do
+  class Initializable
+    attr_reader :coerced, :value
+
+    def initialize(obj, coerced = nil)
+      @coerced = coerced
+      @value = obj.class.to_s
+    end
+
+    def coerced?
+      !@coerced.nil?
+    end
+  end
+
+  class Coercable < Initializable
+    def self.coerce(obj)
+      new(obj, true)
+    end
+  end
+
+  class IndifferentHashWithMergeInitializer < Hash
+    include Hashie::Extensions::MergeInitializer
+    include Hashie::Extensions::IndifferentAccess
+
+    class << self
+      alias build new
+    end
+  end
+
+  class IndifferentHashWithArrayInitializer < Hash
+    include Hashie::Extensions::IndifferentAccess
+
+    class << self
+      alias build []
+    end
+  end
+
+  class IndifferentHashWithTryConvertInitializer < Hash
+    include Hashie::Extensions::IndifferentAccess
+
+    class << self
+      alias build try_convert
+    end
+  end
+
+  class CoercableHash < Hash
+    include Hashie::Extensions::Coercion
+    include Hashie::Extensions::MergeInitializer
+  end
+
+  class MashWithIndifferentAccess < Hashie::Mash
+    include Hashie::Extensions::IndifferentAccess
+  end
+
+  shared_examples_for 'hash with indifferent access' do
+    it 'is able to access via string or symbol' do
+      indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(abc: 123)
+      h = subject.build(indifferent_hash)
+      expect(h[:abc]).to eq 123
+      expect(h['abc']).to eq 123
+    end
+
+    describe '#values_at' do
+      it 'indifferently finds values' do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(
+          :foo => 'bar', 'baz' => 'qux'
+        )
+        h = subject.build(indifferent_hash)
+        expect(h.values_at('foo', :baz)).to eq %w[bar qux]
+      end
+    end
+
+    describe '#fetch' do
+      it 'works like normal fetch, but indifferent' do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
+        h = subject.build(indifferent_hash)
+        expect(h.fetch(:foo)).to eq h.fetch('foo')
+        expect(h.fetch(:foo)).to eq 'bar'
+      end
+    end
+
+    describe '#delete' do
+      it 'deletes indifferently' do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(
+          :foo => 'bar',
+          'baz' => 'qux'
+        )
+        h = subject.build(indifferent_hash)
+        h.delete('foo')
+        h.delete(:baz)
+        expect(h).to be_empty
+      end
+    end
+
+    describe '#key?' do
+      let(:h) do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
+        subject.build(indifferent_hash)
+      end
+
+      it 'finds it indifferently' do
+        expect(h).to be_key(:foo)
+        expect(h).to be_key('foo')
+      end
+
+      %w[include? member? has_key?].each do |key_alias|
+        it "is aliased as #{key_alias}" do
+          expect(h.send(key_alias.to_sym, :foo)).to be(true)
+          expect(h.send(key_alias.to_sym, 'foo')).to be(true)
+        end
+      end
+    end
+
+    describe '#update' do
+      let(:h) do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
+        subject.build(indifferent_hash)
+      end
+
+      it 'allows keys to be indifferent still' do
+        h.update(baz: 'qux')
+        expect(h['foo']).to eq 'bar'
+        expect(h['baz']).to eq 'qux'
+      end
+
+      it 'recursively injects indifference into sub-hashes' do
+        h.update(baz: { qux: 'abc' })
+        expect(h['baz']['qux']).to eq 'abc'
+      end
+
+      it 'does not change the ancestors of the injected object class' do
+        h.update(baz: { qux: 'abc' })
+        expect({}).not_to be_respond_to(:indifferent_access?)
+      end
+    end
+
+    describe '#replace' do
+      let(:h) do
+        indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
+        subject.build(indifferent_hash).replace(bar: 'baz', hi: 'bye')
+      end
+
+      it 'returns self' do
+        expect(h).to be_a(subject)
+      end
+
+      it 'removes old keys' do
+        [:foo, 'foo'].each do |k|
+          expect(h[k]).to be_nil
+          expect(h.key?(k)).to be_falsy
+        end
+      end
+
+      it 'creates new keys with indifferent access' do
+        [:bar, 'bar', :hi, 'hi'].each { |k| expect(h.key?(k)).to be_truthy }
+        expect(h[:bar]).to eq 'baz'
+        expect(h['bar']).to eq 'baz'
+        expect(h[:hi]).to eq 'bye'
+        expect(h['hi']).to eq 'bye'
+      end
+    end
+
+    describe '#try_convert' do
+      describe 'with conversion' do
+        let(:h) do
+          indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(foo: 'bar')
+          subject.try_convert(indifferent_hash)
+        end
+
+        it 'is a subject' do
+          expect(h).to be_a(subject)
+        end
+      end
+
+      describe 'without conversion' do
+        let(:h) { subject.try_convert('{ :foo => bar }') }
+
+        it 'is nil' do
+          expect(h).to be_nil
+        end
+      end
+    end
+  end
+
+  describe 'with merge initializer' do
+    subject { IndifferentHashWithMergeInitializer }
+    it_should_behave_like 'hash with indifferent access'
+  end
+
+  describe 'with array initializer' do
+    subject { IndifferentHashWithArrayInitializer }
+    it_should_behave_like 'hash with indifferent access'
+  end
+
+  describe 'with try convert initializer' do
+    subject { IndifferentHashWithTryConvertInitializer }
+    it_should_behave_like 'hash with indifferent access'
+  end
+
+  describe 'with coercion' do
+    subject { CoercableHash }
+
+    let(:instance) { subject.new }
+
+    it 'supports coercion for ActiveSupport::HashWithIndifferentAccess' do
+      subject.coerce_key :foo, ActiveSupport::HashWithIndifferentAccess.new(Coercable => Coercable)
+      instance[:foo] = { 'bar_key' => 'bar_value', 'bar2_key' => 'bar2_value' }
+      expect(instance[:foo].keys).to all(be_coerced)
+      expect(instance[:foo].values).to all(be_coerced)
+      expect(instance[:foo]).to be_a(ActiveSupport::HashWithIndifferentAccess)
+    end
+  end
+
+  describe 'Mash with indifferent access' do
+    it 'is able to be created for a deep nested HashWithIndifferentAccess' do
+      indifferent_hash = ActiveSupport::HashWithIndifferentAccess.new(abc: { def: 123 })
+      MashWithIndifferentAccess.new(indifferent_hash)
+    end
+  end
+
+  class DashTestDefaultProc < Hashie::Dash
+    property :fields, default: -> { [] }
+  end
+
+  describe DashTestDefaultProc do
+    it 'as_json behaves correctly with default proc' do
+      object = described_class.new
+      expect(object.as_json).to be == { 'fields' => [] }
+    end
+  end
+end
diff --git a/spec/integration/elasticsearch/Gemfile b/spec/integration/elasticsearch/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..f2e62f36108cc17f3e65fa9bc8cb1bb087daf886
--- /dev/null
+++ b/spec/integration/elasticsearch/Gemfile
@@ -0,0 +1,7 @@
+source 'http://rubygems.org'
+
+gem 'elasticsearch-api', '~> 7.0.0'
+gem 'elasticsearch-model', '~> 7.0.0'
+gem 'hashie', path: '../../..'
+gem 'rake'
+gem 'rspec', '~> 3.5.0'
diff --git a/spec/integration/elasticsearch/integration_spec.rb b/spec/integration/elasticsearch/integration_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7ce73eda837a8cd8d69c02565489f991a2f69f42
--- /dev/null
+++ b/spec/integration/elasticsearch/integration_spec.rb
@@ -0,0 +1,45 @@
+require 'elasticsearch/model'
+require 'hashie'
+
+RSpec.configure do |config|
+  config.expect_with :rspec do |expect|
+    expect.syntax = :expect
+  end
+end
+
+class MyModel < Hashie::Mash
+  include Elasticsearch::Model
+
+  disable_warnings
+
+  index_name 'model'
+  document_type 'model'
+
+  def as_indexed_json(options = {})
+    { body: '{}' }.merge(options)
+  end
+end
+
+RSpec.describe 'elaasticsearch-model' do
+  # See https://github.com/hashie/hashie/issues/354#issuecomment-363306114
+  # for the reason why this doesn't work as you would expect
+  it 'raises an error when the model does not have an id' do
+    object = MyModel.new
+    stub_elasticsearch_client
+
+    expect { object.__elasticsearch__.index_document }.to raise_error(NameError)
+  end
+
+  it 'does not raise an error when the model has an id' do
+    object = MyModel.new(id: 123)
+    stub_elasticsearch_client
+
+    expect { object.__elasticsearch__.index_document }.not_to raise_error
+  end
+
+  def stub_elasticsearch_client
+    response = double('Response', body: '{}')
+    allow_any_instance_of(Elasticsearch::Transport::Client).to\
+      receive(:perform_request) { response }
+  end
+end
diff --git a/spec/integration/omniauth-oauth2/.rspec b/spec/integration/omniauth-oauth2/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..a4c51e72d1f61b97757746f4b1d8560b769dc380
--- /dev/null
+++ b/spec/integration/omniauth-oauth2/.rspec
@@ -0,0 +1,3 @@
+--colour
+--format=documentation
+--pattern=*_spec.rb
diff --git a/spec/integration/omniauth-oauth2/Gemfile b/spec/integration/omniauth-oauth2/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..91c3c2aa08e99226f423f184d1b697138fe58129
--- /dev/null
+++ b/spec/integration/omniauth-oauth2/Gemfile
@@ -0,0 +1,8 @@
+source 'http://rubygems.org'
+
+gem 'hashie', path: '../../..'
+gem 'omniauth'
+gem 'omniauth-oauth2', '~> 1.4.0'
+gem 'rails', '~> 5.0.1'
+gem 'rspec', '~> 3.5.0'
+gem 'rspec-rails'
diff --git a/spec/integration/omniauth-oauth2/app.rb b/spec/integration/omniauth-oauth2/app.rb
index 37841487c16398fd863e41113eeb5fbc434b90dd..77ced5dc79d92818ffcfa02e8de787591ba1a5b6 100644
--- a/spec/integration/omniauth-oauth2/app.rb
+++ b/spec/integration/omniauth-oauth2/app.rb
@@ -19,7 +19,7 @@ module RailsApp
   end
 end
 
-LAYOUT = <<-HTML
+LAYOUT = <<-HTML.freeze
 <!DOCTYPE html>
 <html>
 <head>
@@ -32,7 +32,7 @@ LAYOUT = <<-HTML
 </html>
 HTML
 
-INDEX = '<h1>Hello, world!</h1>'
+INDEX = '<h1>Hello, world!</h1>'.freeze
 
 class ApplicationController < ActionController::Base
   include Rails.application.routes.url_helpers
@@ -44,8 +44,7 @@ class ApplicationController < ActionController::Base
     'application/index.html.erb'   => INDEX
   )]
 
-  def index
-  end
+  def index; end
 end
 
 Bundler.require(:default, Rails.env)
diff --git a/spec/integration/omniauth-oauth2/some_site.rb b/spec/integration/omniauth-oauth2/some_site.rb
index 9369205ba57395ef688ea69af837c1179bab873c..6411c15c83b99d635e33435f781772e25773212c 100644
--- a/spec/integration/omniauth-oauth2/some_site.rb
+++ b/spec/integration/omniauth-oauth2/some_site.rb
@@ -19,8 +19,8 @@ module OmniAuth
 
       info do
         {
-          :name => raw_info['name'],
-          :email => raw_info['email']
+          name: raw_info['name'],
+          email: raw_info['email']
         }
       end
 
diff --git a/spec/integration/omniauth/.rspec b/spec/integration/omniauth/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..a4c51e72d1f61b97757746f4b1d8560b769dc380
--- /dev/null
+++ b/spec/integration/omniauth/.rspec
@@ -0,0 +1,3 @@
+--colour
+--format=documentation
+--pattern=*_spec.rb
diff --git a/spec/integration/omniauth/Gemfile b/spec/integration/omniauth/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..af40e06299816c7ee921b87017da3f6575854d23
--- /dev/null
+++ b/spec/integration/omniauth/Gemfile
@@ -0,0 +1,9 @@
+source 'http://rubygems.org'
+
+gem 'benchmark-ips'
+gem 'hashie', path: '../../..'
+gem 'omniauth'
+gem 'rack-test', '~> 0.6.3'
+gem 'rake'
+gem 'rspec', '~> 3.5.0'
+gem 'sinatra'
diff --git a/spec/integration/omniauth/Rakefile b/spec/integration/omniauth/Rakefile
new file mode 100644
index 0000000000000000000000000000000000000000..9c220f71a279a340f50c70e6665ae5c19c9af61b
--- /dev/null
+++ b/spec/integration/omniauth/Rakefile
@@ -0,0 +1,29 @@
+namespace :perf do
+  task :setup do
+    require 'omniauth'
+    require 'rack/test'
+    app = Rack::Builder.new do |b|
+      b.use Rack::Session::Cookie, secret: 'abc123'
+      b.use OmniAuth::Strategies::Developer
+      b.run ->(_env) { [200, {}, ['Not Found']] }
+    end.to_app
+    @app = Rack::MockRequest.new(app)
+
+    def call_app(path = ENV['GET_PATH'] || '/')
+      result = @app.get(path)
+      raise "Did not succeed #{result.body}" unless result.status == 200
+      result
+    end
+  end
+
+  task ips: :setup do
+    require 'benchmark/ips'
+    Benchmark.ips do |x|
+      x.hold!('../../../tmp/omniauth_benchmark.json')
+      x.report('before') { call_app }
+      x.report('after') { call_app }
+
+      x.compare!
+    end
+  end
+end
diff --git a/spec/integration/rails-without-dependency/.rspec b/spec/integration/rails-without-dependency/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..a4c51e72d1f61b97757746f4b1d8560b769dc380
--- /dev/null
+++ b/spec/integration/rails-without-dependency/.rspec
@@ -0,0 +1,3 @@
+--colour
+--format=documentation
+--pattern=*_spec.rb
diff --git a/spec/integration/rails-without-dependency/Gemfile b/spec/integration/rails-without-dependency/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..85d86fe825a16b199d505085c9f76b8696d074ea
--- /dev/null
+++ b/spec/integration/rails-without-dependency/Gemfile
@@ -0,0 +1,4 @@
+source 'http://rubygems.org'
+
+gem 'hashie', path: '../../..'
+gem 'rspec', '~> 3.5.0'
diff --git a/spec/integration/rails/.rspec b/spec/integration/rails/.rspec
new file mode 100644
index 0000000000000000000000000000000000000000..a4c51e72d1f61b97757746f4b1d8560b769dc380
--- /dev/null
+++ b/spec/integration/rails/.rspec
@@ -0,0 +1,3 @@
+--colour
+--format=documentation
+--pattern=*_spec.rb
diff --git a/spec/integration/rails/Gemfile b/spec/integration/rails/Gemfile
new file mode 100644
index 0000000000000000000000000000000000000000..da94f2f80cfeed141b5f022db0fe766fe6684806
--- /dev/null
+++ b/spec/integration/rails/Gemfile
@@ -0,0 +1,6 @@
+source 'http://rubygems.org'
+
+gem 'hashie', path: '../../..'
+gem 'rails', '~> 6.0.0'
+gem 'rspec', '~> 3.5.0'
+gem 'rspec-rails'
diff --git a/spec/integration/rails/app.rb b/spec/integration/rails/app.rb
index 9eed330524201845a5ed6d412a2d143b4694f221..106568832f9ed3da857ddc86f01fc7fc41e6945c 100644
--- a/spec/integration/rails/app.rb
+++ b/spec/integration/rails/app.rb
@@ -14,7 +14,7 @@ module RailsApp
   end
 end
 
-LAYOUT = <<-HTML
+PAGE = <<-HTML.freeze
 <!DOCTYPE html>
 <html>
 <head>
@@ -22,24 +22,16 @@ LAYOUT = <<-HTML
   <%= csrf_meta_tags %>
 </head>
 <body>
-<%= yield %>
+  <h1>Hello, world!</h1>
 </body>
 </html>
 HTML
 
-INDEX = '<h1>Hello, world!</h1>'
-
 class ApplicationController < ActionController::Base
   include Rails.application.routes.url_helpers
 
-  layout 'application'
-
-  self.view_paths = [ActionView::FixtureResolver.new(
-    'layouts/application.html.erb' => LAYOUT,
-    'application/index.html.erb'   => INDEX
-  )]
-
   def index
+    render inline: PAGE
   end
 end
 
diff --git a/spec/integration/rails/integration_spec.rb b/spec/integration/rails/integration_spec.rb
index 9b150a22c30ea4cdb9e4f62472d9ee580c9456cc..cef79f0648cfa154a5c5de294ae2d5e78604b4f6 100644
--- a/spec/integration/rails/integration_spec.rb
+++ b/spec/integration/rails/integration_spec.rb
@@ -14,11 +14,32 @@ RSpec.describe 'rails', type: :request do
     $stdout = original_stdout
   end
 
-  it 'does not log anything to STDOUT when initializing and sets the Hashie logger to the Rails logger' do
+  it 'does not log anything to STDOUT when initializing' do
     expect(stdout.string).to eq('')
+  end
+
+  it 'sets the Hashie logger to the Rails logger' do
     expect(Hashie.logger).to eq(Rails.logger)
   end
 
+  context '#except' do
+    subject { Hashie::Mash.new(x: 1, y: 2) }
+
+    it 'returns an instance of the class it was called on' do
+      class HashieKlass < Hashie::Mash; end
+      hashie_klass = HashieKlass.new(subject)
+      expect(hashie_klass.except('x')).to be_a HashieKlass
+    end
+
+    it 'works with string keys' do
+      expect(subject.except('x')).to eq Hashie::Mash.new(y: 2)
+    end
+
+    it 'works with symbol keys' do
+      expect(subject.except(:x)).to eq Hashie::Mash.new(y: 2)
+    end
+  end
+
   it 'works' do
     get '/'
     assert_select 'h1', 'Hello, world!'
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 391909ad563dd011f5113ec6cbd5fd55d9d7bf3d..5ac288a784858e2a6b1448d7eb284763a1b927b9 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -7,16 +7,18 @@ require 'pry'
 
 require 'rspec'
 require 'hashie'
+require 'json'
 require 'rspec/pending_for'
 require './spec/support/ruby_version_check'
 require './spec/support/logger'
+require './spec/support/matchers'
 
-require 'active_support'
-require 'active_support/core_ext'
+Dir[File.expand_path(File.join(__dir__, 'support', '**', '*'))].sort.each { |file| require file }
 
 RSpec.configure do |config|
   config.extend RubyVersionCheck
   config.expect_with :rspec do |expect|
     expect.syntax = :expect
   end
+  config.warnings = true
 end
diff --git a/spec/support/matchers.rb b/spec/support/matchers.rb
new file mode 100644
index 0000000000000000000000000000000000000000..cb2b3c0a2c62e742b5326f206a7d8a6d89b47afe
--- /dev/null
+++ b/spec/support/matchers.rb
@@ -0,0 +1,13 @@
+RSpec::Matchers.define :parse_as_valid_ruby do
+  require 'ripper'
+
+  match do |actual|
+    parsed = Ripper.sexp(actual)
+
+    !parsed.nil?
+  end
+
+  failure_message do |actual|
+    "expected that #{actual} would parse as valid Ruby"
+  end
+end
diff --git a/spec/support/shared_examples.rb b/spec/support/shared_examples.rb
new file mode 100644
index 0000000000000000000000000000000000000000..df05592737b4f6f946f59d7b44f720a11ef012bd
--- /dev/null
+++ b/spec/support/shared_examples.rb
@@ -0,0 +1,26 @@
+RSpec.shared_examples 'Dash default handling' do |property, name = property|
+  it 'uses the default when initializing' do
+    expect(test.new(name => nil).public_send(property)).to eq ''
+  end
+
+  it 'allows you to set the value to nil with the hash writer' do
+    trash = test.new(name => 'foo')
+    trash[name] = nil
+
+    expect(trash.public_send(property)).to be_nil
+  end
+
+  it 'allows you to set the value to nil with the method writer' do
+    trash = test.new(name => 'foo')
+    trash[name] = nil
+
+    expect(trash.public_send(property)).to be_nil
+  end
+
+  it 'uses the default when updating with defaults' do
+    trash = test.new(name => 'foo')
+    trash.update_attributes!(name => nil)
+
+    expect(trash.public_send(property)).to eq ''
+  end
+end