summaryrefslogtreecommitdiff
path: root/lib/hashie/extensions/dash/predefined_values.rb
diff options
context:
space:
mode:
Diffstat (limited to 'lib/hashie/extensions/dash/predefined_values.rb')
-rw-r--r--lib/hashie/extensions/dash/predefined_values.rb88
1 files changed, 88 insertions, 0 deletions
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