configurable 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,226 +0,0 @@
1
- require 'configurable/delegate'
2
-
3
- module Configurable
4
-
5
- # DelegateHash delegates get and set operations to instance methods on a receiver.
6
- #
7
- # class Sample
8
- # attr_accessor :key
9
- # end
10
- # sample = Sample.new
11
- #
12
- # dhash = DelegateHash.new
13
- # dhash.delegates[:key] = Delegate.new(:key)
14
- # dhash.bind(sample)
15
- #
16
- # sample.key = 'value'
17
- # dhash[:key] # => 'value'
18
- #
19
- # dhash[:key] = 'another'
20
- # sample.key # => 'another'
21
- #
22
- # Non-delegate keys are sent to an underlying data store:
23
- #
24
- # dhash[:not_delegated] = 'value'
25
- # dhash[:not_delegated] # => 'value'
26
- #
27
- # dhash.store # => {:not_delegated => 'value'}
28
- # dhash.to_hash # => {:key => 'another', :not_delegated => 'value'}
29
- #
30
- # ==== IndifferentAccess
31
- #
32
- # The delegates hash maps keys to Delegate objects. In cases where multiple
33
- # keys need to map to the same delegate (for example when you want indifferent
34
- # access for strings and symbols), simply extend the delegate hash so that the
35
- # AGET ([]) method returns the correct delegate in all cases.
36
- #
37
- class DelegateHash
38
-
39
- # The bound receiver
40
- attr_reader :receiver
41
-
42
- # The underlying data store
43
- attr_reader :store
44
-
45
- # A hash of (key, Delegate) pairs identifying which
46
- # keys to delegate to the receiver
47
- attr_reader :delegates
48
-
49
- # Initializes a new DelegateHash. Note that initialize simply sets the
50
- # receiver, it does NOT map stored values the same way bind does. This
51
- # allows quick, implicit binding when the store is set up beforehand.
52
- #
53
- # For more standard binding use: DelegateHash.new.bind(receiver)
54
- def initialize(delegates={}, store={}, receiver=nil)
55
- @store = store
56
- @delegates = delegates
57
- @receiver = receiver
58
- end
59
-
60
- # Binds self to the specified receiver. Delegate values are removed from
61
- # store and sent to their writer on receiver. If the store has no value
62
- # for a delegate key, the delegate default value will be used.
63
- def bind(receiver, rebind=false)
64
- raise ArgumentError, "receiver cannot be nil" if receiver == nil
65
-
66
- if bound? && !rebind
67
- if @receiver == receiver
68
- return(self)
69
- else
70
- raise ArgumentError, "already bound to: #{@receiver}"
71
- end
72
- end
73
-
74
- @receiver = receiver
75
- map(store)
76
- self
77
- end
78
-
79
- # Returns true if self is bound to a receiver
80
- def bound?
81
- receiver != nil
82
- end
83
-
84
- # Unbinds self from the specified receiver. Delegate values
85
- # are stored in store. Returns the unbound receiver.
86
- def unbind
87
- unmap(store)
88
- @receiver = nil
89
- self
90
- end
91
-
92
- # Retrieves the value corresponding to the key. When bound, delegates pull
93
- # values from the receiver using the delegate.reader method; otherwise the
94
- # value in store will be returned. When unbound, if the store has no value
95
- # for a delegate, the delgate default value will be returned.
96
- def [](key)
97
- return store[key] unless delegate = delegates[key]
98
-
99
- case
100
- when bound?
101
- receiver.send(delegate.reader)
102
- when store.has_key?(key)
103
- store[key]
104
- else
105
- store[key] = delegate.default
106
- end
107
- end
108
-
109
- # Stores a value for the key. When bound, delegates set the value in the
110
- # receiver using the delegate.writer method; otherwise values are stored in
111
- # store.
112
- def []=(key, value)
113
- if bound? && delegate = delegates[key]
114
- receiver.send(delegate.writer, value)
115
- else
116
- store[key] = value
117
- end
118
- end
119
-
120
- # Returns the union of delegate and store keys.
121
- def keys
122
- delegates.keys | store.keys
123
- end
124
-
125
- # True if the key is an assigned delegate or store key.
126
- def has_key?(key)
127
- delegates.has_key?(key) || store.has_key?(key)
128
- end
129
-
130
- # Merges another with self.
131
- def merge!(another)
132
- if bound?
133
- (delegates.keys | another.keys).each do |key|
134
- self[key] = another[key] if another.has_key?(key)
135
- end
136
- else
137
- # optimization for the common case of an
138
- # unbound merge of another hash
139
- store.merge!(another.to_hash)
140
- end
141
- end
142
-
143
- # Calls block once for each key-value pair stored in self.
144
- def each_pair # :yields: key, value
145
- keys.each {|key| yield(key, self[key]) }
146
- end
147
-
148
- # Equal if the to_hash values of self and another are equal.
149
- def ==(another)
150
- another.respond_to?(:to_hash) && to_hash == another.to_hash
151
- end
152
-
153
- # Returns self as a hash. Any DelegateHash values are recursively
154
- # hashified, to account for nesting.
155
- def to_hash(&block)
156
- hash = {}
157
- each_pair do |key, value|
158
- if value.kind_of?(DelegateHash)
159
- value = value.to_hash(&block)
160
- end
161
-
162
- if block_given?
163
- yield(hash, key, value)
164
- else
165
- hash[key] = value
166
- end
167
- end
168
- hash
169
- end
170
-
171
- # Overrides default inspect to show the to_hash values.
172
- def inspect
173
- "#<#{self.class}:#{object_id} to_hash=#{to_hash.inspect}>"
174
- end
175
-
176
- # Ensures duplicates are unbound and store the same values as the original.
177
- def initialize_copy(orig)
178
- super
179
-
180
- @receiver = nil
181
- @store = @store.dup
182
- orig.unmap(@store) if orig.bound?
183
- end
184
-
185
- protected
186
-
187
- # helper to map delegate values from source to the receiver
188
- def map(source) # :nodoc:
189
- source_values = {}
190
- source.each_key do |key|
191
- if delegate = delegates[key]
192
- if source_values.has_key?(delegate)
193
- key = delegates.keys.find {|k| delegates[k] == delegate }
194
- raise "multiple values mapped to #{key.inspect}"
195
- end
196
-
197
- source_values[delegate] = source.delete(key)
198
- end
199
- end
200
-
201
- delegates.each_pair do |key, delegate|
202
- # map the value; if no value is set in the source then use the
203
- # delegate default. if map_default is false, then simply skip...
204
- # this ensures each config is initialized to a value when bound
205
- # UNLESS map_default is set (indicating manual initialization)
206
- value = case
207
- when source_values.has_key?(delegate)
208
- source_values[delegate]
209
- when delegate[:set_default, true]
210
- delegate.default
211
- else
212
- next
213
- end
214
-
215
- receiver.send(delegate.writer, value)
216
- end
217
- end
218
-
219
- # helper to unmap delegates from the receiver to a target hash
220
- def unmap(target) # :nodoc:
221
- delegates.each_pair do |key, delegate|
222
- target[key] = receiver.send(delegate.reader)
223
- end
224
- end
225
- end
226
- end