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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
|
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
|