Added Hashie::Extensions::Mash::ActiveModel for compatibility with Rails 4 Strong Parameters.

This commit is contained in:
dblock 2014-04-30 13:26:31 -04:00
parent 3cdd44af36
commit 5ac85162db
6 changed files with 63 additions and 15 deletions

View File

@ -1,6 +1,7 @@
## Next
* Your contribution here.
* [#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).
* [#89](https://github.com/intridea/hashie/issues/89): Added Hashie::Extensions::Mash::ActiveModel for compatibility with Rails 4.x Strong Parameters - [@dblock](https://github.com/dblock).
## 2.1.1 (4/12/2014)

View File

@ -238,6 +238,16 @@ p[:awesome] # => NoMethodError
p[:occupation] # => 'Rubyist'
```
### Mash and Rails 4 Strong Parameters
Add the following initializer in config/initializers/mash.rb when using Mash with [Rails 4 Strong Parameters](http://edgeguides.rubyonrails.org/action_controller_overview.html#strong-parameters). This prevents Mash from responding to `:permitted?` and therefore triggering an ActiveModel `ForbiddenAttributesProtection` exception.
```ruby
class Mash
include Hashie::Extensions::Mash::ActiveModel
end
```
## Trash
A Trash is a Dash that allows you to translate keys on initialization.

View File

@ -22,5 +22,9 @@ module Hashie
autoload :StringifyKeys, 'hashie/extensions/key_conversion'
autoload :SymbolizeKeys, 'hashie/extensions/key_conversion'
autoload :DeepFetch, 'hashie/extensions/deep_fetch'
module Mash
autoload :ActiveModel, 'hashie/extensions/mash/active_model'
end
end
end

View File

@ -0,0 +1,18 @@
module Hashie
module Extensions
module Mash
# Extends Mash to behave in a way compatible with ActiveModel.
module ActiveModel
def respond_to?(name, include_private = false)
return false if name == :permitted?
super
end
def method_missing(name, *args)
fail ArgumentError if name == :permitted?
super
end
end
end
end
end

View File

@ -57,6 +57,7 @@ module Hashie
class Mash < Hash
ALLOWED_SUFFIXES = %w(? ! = _)
include Hashie::PrettyInspect
alias_method :to_s, :inspect
# If you pass in an existing hash, it will
@ -185,11 +186,15 @@ module Hashie
self
end
# Will return true if the Mash has had a key
# set in addition to normal respond_to? functionality.
def respond_to?(method_name, include_private = false)
return true if key?(method_name) || prefix_method?(method_name)
super
return true if key?(method_name)
_, suffix = method_suffix(method_name)
case suffix
when '=', '?', '!', '_'
return true
else
super
end
end
def prefix_method?(method_name)
@ -199,17 +204,16 @@ module Hashie
def method_missing(method_name, *args, &blk)
return self.[](method_name, &blk) if key?(method_name)
suffixes_regex = ALLOWED_SUFFIXES.join
match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
case match[2]
name, suffix = method_suffix(method_name)
case suffix
when '='
self[match[1]] = args.first
self[name] = args.first
when '?'
!!self[match[1]]
!!self[name]
when '!'
initializing_reader(match[1])
initializing_reader(name)
when '_'
underbang_reader(match[1])
underbang_reader(name)
else
default(method_name)
end
@ -217,6 +221,12 @@ module Hashie
protected
def method_suffix(method_name)
suffixes_regex = ALLOWED_SUFFIXES.join
match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
[match[1], match[2]]
end
def convert_key(key) #:nodoc:
key.to_s
end

View File

@ -328,9 +328,9 @@ describe Hashie::Mash do
end
end
it 'does not respond to an unknown key with a suffix' do
it 'responds to an unknown key with a suffix' do
%w(= ? ! _).each do |suffix|
expect(Hashie::Mash.new(abc: 'def')).not_to be_respond_to(:"xyz#{suffix}")
expect(Hashie::Mash.new(abc: 'def')).to be_respond_to(:"xyz#{suffix}")
end
end
@ -339,7 +339,12 @@ describe Hashie::Mash do
end
it 'does not respond to permitted?' do
expect(Hashie::Mash.new).not_to be_respond_to(:permitted?)
expect(Hashie::Mash.new).to be_respond_to(:permitted?)
klass = Class.new(Hashie::Mash) do
include Hashie::Extensions::Mash::ActiveModel
end
expect(klass.new).not_to be_respond_to(:permitted?)
expect { klass.new.permitted? }.to raise_error(ArgumentError)
end
end