162 lines
3.5 KiB
Ruby
162 lines
3.5 KiB
Ruby
require 'hashery/crud_hash'
|
|
|
|
module Hashery
|
|
|
|
# A PropertyHash is the same as a regular Hash except it strictly limits the
|
|
# allowed keys.
|
|
#
|
|
# There are two ways to use it.
|
|
#
|
|
# 1) As an object in itself.
|
|
#
|
|
# h = PropertyHash.new(:a=>1, :b=>2)
|
|
# h[:a] #=> 1
|
|
# h[:a] = 3
|
|
# h[:a] #=> 3
|
|
#
|
|
# But if we try to set key that was not fixed, then we will get an error.
|
|
#
|
|
# h[:x] = 5 #=> ArgumentError
|
|
#
|
|
# 2) As a superclass.
|
|
#
|
|
# class MyPropertyHash < PropertyHash
|
|
# property :a, :default => 1
|
|
# property :b, :default => 2
|
|
# end
|
|
#
|
|
# h = MyPropertyHash.new
|
|
# h[:a] #=> 1
|
|
# h[:a] = 3
|
|
# h[:a] #=> 3
|
|
#
|
|
# Again, if we try to set key that was not fixed, then we will get an error.
|
|
#
|
|
# h[:x] = 5 #=> ArgumentError
|
|
#
|
|
class PropertyHash < CRUDHash
|
|
|
|
#
|
|
# Get a list of properties with default values.
|
|
#
|
|
# Returns [Hash] of properties and their default values.
|
|
#
|
|
def self.properties
|
|
@properties ||= (
|
|
parent = ancestors[1]
|
|
if parent.respond_to?(:properties)
|
|
parent.properties
|
|
else
|
|
{}
|
|
end
|
|
)
|
|
end
|
|
|
|
#
|
|
# Define a property.
|
|
#
|
|
# key - Name of property.
|
|
# opts - Property options.
|
|
# :default - Default value of property.
|
|
#
|
|
# Returns default value.
|
|
#
|
|
def self.property(key, opts={})
|
|
properties[key] = opts[:default]
|
|
end
|
|
|
|
#
|
|
# Initialize new instance of PropertyHash.
|
|
#
|
|
# properties - [Hash] Priming properties with default values, or
|
|
# if it doesn't respond to #each_pair, a default object.
|
|
# default_proc - [Proc] Procedure for default value of properties
|
|
# for properties without specific defaults.
|
|
#
|
|
def initialize(properties={}, &default_proc)
|
|
if properties.respond_to?(:each_pair)
|
|
super(&default_proc)
|
|
fixed = self.class.properties.merge(properties)
|
|
fixed.each_pair do |key, value|
|
|
store!(key, value)
|
|
end
|
|
else
|
|
super(*[properties].compact, &default_proc)
|
|
end
|
|
end
|
|
|
|
# Alias original #store method and make private.
|
|
alias :store! :store
|
|
private :store!
|
|
|
|
#
|
|
# Create a new property, on-the-fly.
|
|
#
|
|
# key - Name of property.
|
|
# opts - Property options.
|
|
# :default - Default value of property.
|
|
#
|
|
# Returns default value.
|
|
#
|
|
def property(key, opts={})
|
|
if opts[:default]
|
|
store!(key, opts[:default])
|
|
else
|
|
store!(key, retrieve(key))
|
|
end
|
|
end
|
|
|
|
#
|
|
# Store key value pair, ensuring the key is a valid property first.
|
|
#
|
|
# key - The `Object` to act as indexing key.
|
|
# value - The `Object` to associate with key.
|
|
#
|
|
# Raises ArgumentError if key is not a valid property.
|
|
#
|
|
# Returns +value+.
|
|
#
|
|
def store(key, value)
|
|
assert_key!(key)
|
|
super(key, value)
|
|
end
|
|
|
|
#
|
|
#def update(h)
|
|
# h.keys.each{ |k| assert_key!(k) }
|
|
# super(h)
|
|
#end
|
|
|
|
#
|
|
#def merge!(h)
|
|
# h.keys.each{ |k| assert_key!(k) }
|
|
# super(h)
|
|
#end
|
|
|
|
#
|
|
# Like #store but takes a two-element Array of `[key, value]`.
|
|
#
|
|
# Returns value.
|
|
#
|
|
#def <<(a)
|
|
# k,v = *a
|
|
# store(k,v)
|
|
#end
|
|
|
|
private
|
|
|
|
#
|
|
# Asserta that a key is a defined property.
|
|
#
|
|
# Raises ArgumentError if key is not a property.
|
|
#
|
|
def assert_key!(key)
|
|
unless key?(key)
|
|
raise ArgumentError, "property is not defined -- #{key.inspect}"
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
end
|