diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/hashie.rb | 1 | ||||
-rw-r--r-- | lib/hashie/extensions/dash/predefined_values.rb | 88 |
2 files changed, 89 insertions, 0 deletions
diff --git a/lib/hashie.rb b/lib/hashie.rb index 7f88ed4..8f526c4 100644 --- a/lib/hashie.rb +++ b/lib/hashie.rb @@ -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 diff --git a/lib/hashie/extensions/dash/predefined_values.rb b/lib/hashie/extensions/dash/predefined_values.rb new file mode 100644 index 0000000..2a0b7ca --- /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 |