1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 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
|