Add Hashie::Extensions::Dash::AllowList
Extends a Dash with the ability to accept only predefined values on a property. #61
This commit is contained in:
parent
b24d6dca2c
commit
4cd2844357
|
@ -14,6 +14,7 @@ Any violations of this scheme are considered to be bugs.
|
|||
|
||||
* [#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).
|
||||
* Your contribution here.
|
||||
|
||||
### Changed
|
||||
|
|
15
README.md
15
README.md
|
@ -43,6 +43,7 @@
|
|||
- [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)
|
||||
|
@ -968,6 +969,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::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
|
||||
|
|
|
@ -41,6 +41,7 @@ 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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue