Add MethodOverridingInitializer extension
This commit is contained in:
parent
7b41599177
commit
1d94321639
|
@ -12,6 +12,7 @@ scheme are considered to be bugs.
|
|||
|
||||
### Added
|
||||
|
||||
* [#455](https://github.com/intridea/hashie/pull/455): Allow overriding methods when passing in a hash - [@lnestor](https://github.com/lnestor).
|
||||
* Your contribution here.
|
||||
|
||||
### Changed
|
||||
|
|
20
README.md
20
README.md
|
@ -243,6 +243,26 @@ 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.
|
||||
|
|
|
@ -154,6 +154,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
|
||||
|
@ -179,6 +195,8 @@ module Hashie
|
|||
# h.__zip # => [[['awesome', 'sauce'], ['zip', 'a-dee-doo-dah']]]
|
||||
#
|
||||
module MethodOverridingWriter
|
||||
include RedefineMethod
|
||||
|
||||
def convert_key(key)
|
||||
key.to_s
|
||||
end
|
||||
|
@ -203,16 +221,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,
|
||||
|
@ -223,10 +231,34 @@ 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
|
||||
|
|
|
@ -181,8 +181,46 @@ 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
|
||||
|
|
Loading…
Reference in New Issue