Class: Configurable::DelegateHash

Inherits:
Object
  • Object
show all
Defined in:
lib/configurable/delegate_hash.rb

Overview

DelegateHash delegates get and set operations to instance methods on a receiver.

class Sample
  attr_accessor :key
end
sample = Sample.new

dhash = DelegateHash.new
dhash.delegates[:key] = Delegate.new(:key)
dhash.bind(sample)

sample.key = 'value'
dhash[:key]                # => 'value'

dhash[:key] = 'another'
sample.key                # => 'another'

Non-delegate keys are sent to an underlying data store:

dhash[:not_delegated] = 'value'
dhash[:not_delegated]      # => 'value'

dhash.store                # => {:not_delegated => 'value'}
dhash.to_hash              # => {:key => 'another', :not_delegated => 'value'}

IndifferentAccess

The delegates hash maps keys to Delegate objects. In cases where multiple keys need to map to the same delegate (for example when you want indifferent access for strings and symbols), simply extend the delegate hash so that the AGET ([]) method returns the correct delegate in all cases.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(delegates = {}, store = {}, receiver = nil) ⇒ DelegateHash

Initializes a new DelegateHash. Note that initialize simply sets the receiver, it does NOT map stored values the same way bind does. This allows quick, implicit binding when the store is set up beforehand.

For more standard binding use: DelegateHash.new.bind(receiver)



54
55
56
57
58
# File 'lib/configurable/delegate_hash.rb', line 54

def initialize(delegates={}, store={}, receiver=nil)
  @store = store
  @delegates = delegates
  @receiver = receiver
end

Instance Attribute Details

#delegatesObject (readonly)

A hash of (key, Delegate) pairs identifying which keys to delegate to the receiver



47
48
49
# File 'lib/configurable/delegate_hash.rb', line 47

def delegates
  @delegates
end

#receiverObject (readonly)

The bound receiver



40
41
42
# File 'lib/configurable/delegate_hash.rb', line 40

def receiver
  @receiver
end

#storeObject (readonly)

The underlying data store



43
44
45
# File 'lib/configurable/delegate_hash.rb', line 43

def store
  @store
end

Instance Method Details

#==(another) ⇒ Object

Equal if the to_hash values of self and another are equal.



149
150
151
# File 'lib/configurable/delegate_hash.rb', line 149

def ==(another)
  another.respond_to?(:to_hash) && to_hash == another.to_hash
end

#[](key) ⇒ Object

Retrieves the value corresponding to the key. When bound, delegates pull values from the receiver using the delegate.reader method; otherwise the value in store will be returned. When unbound, if the store has no value for a delegate, the delgate default value will be returned.



96
97
98
99
100
101
102
103
104
105
106
107
# File 'lib/configurable/delegate_hash.rb', line 96

def [](key)
  return store[key] unless delegate = delegates[key]
  
  case
  when bound?
    receiver.send(delegate.reader)
  when store.has_key?(key)
    store[key]
  else
    store[key] = delegate.default
  end
end

#[]=(key, value) ⇒ Object

Stores a value for the key. When bound, delegates set the value in the receiver using the delegate.writer method; otherwise values are stored in store.



112
113
114
115
116
117
118
# File 'lib/configurable/delegate_hash.rb', line 112

def []=(key, value)
  if bound? && delegate = delegates[key]
    receiver.send(delegate.writer, value)
  else
    store[key] = value
  end
end

#bind(receiver, rebind = false) ⇒ Object

Binds self to the specified receiver. Delegate values are removed from store and sent to their writer on receiver. If the store has no value for a delegate key, the delegate default value will be used.

Raises:

  • (ArgumentError)


63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/configurable/delegate_hash.rb', line 63

def bind(receiver, rebind=false)
  raise ArgumentError, "receiver cannot be nil" if receiver == nil
  
  if bound? && !rebind
    if @receiver == receiver
      return(self)
    else
      raise ArgumentError, "already bound to: #{@receiver}"
    end
  end
  
  @receiver = receiver
  map(store)
  self
end

#bound?Boolean

Returns true if self is bound to a receiver

Returns:

  • (Boolean)


80
81
82
# File 'lib/configurable/delegate_hash.rb', line 80

def bound?
  receiver != nil
end

#each_pairObject

Calls block once for each key-value pair stored in self.



144
145
146
# File 'lib/configurable/delegate_hash.rb', line 144

def each_pair # :yields: key, value
  keys.each {|key| yield(key, self[key]) }
end

#has_key?(key) ⇒ Boolean

True if the key is an assigned delegate or store key.

Returns:

  • (Boolean)


126
127
128
# File 'lib/configurable/delegate_hash.rb', line 126

def has_key?(key)
  delegates.has_key?(key) || store.has_key?(key) 
end

#initialize_copy(orig) ⇒ Object

Ensures duplicates are unbound and store the same values as the original.



177
178
179
180
181
182
183
# File 'lib/configurable/delegate_hash.rb', line 177

def initialize_copy(orig)
  super
  
  @receiver = nil
  @store = @store.dup
  orig.unmap(@store) if orig.bound?
end

#inspectObject

Overrides default inspect to show the to_hash values.



172
173
174
# File 'lib/configurable/delegate_hash.rb', line 172

def inspect
  "#<#{self.class}:#{object_id} to_hash=#{to_hash.inspect}>"
end

#keysObject

Returns the union of delegate and store keys.



121
122
123
# File 'lib/configurable/delegate_hash.rb', line 121

def keys
  delegates.keys | store.keys
end

#merge!(another) ⇒ Object

Merges another with self.



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/configurable/delegate_hash.rb', line 131

def merge!(another)
  if bound?
    (delegates.keys | another.keys).each do |key|
      self[key] = another[key] if another.has_key?(key)
    end
  else
    # optimization for the common case of an 
    # unbound merge of another hash
    store.merge!(another.to_hash)
  end
end

#to_hash(&block) ⇒ Object

Returns self as a hash. Any DelegateHash values are recursively hashified, to account for nesting.



155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/configurable/delegate_hash.rb', line 155

def to_hash(&block)
  hash = {}
  each_pair do |key, value|
    if value.kind_of?(DelegateHash)
      value = value.to_hash(&block)
    end
    
    if block_given?
      yield(hash, key, value)
    else
      hash[key] = value
    end
  end
  hash
end

#unbindObject

Unbinds self from the specified receiver. Delegate values are stored in store. Returns the unbound receiver.



86
87
88
89
90
# File 'lib/configurable/delegate_hash.rb', line 86

def unbind
  unmap(store)
  @receiver = nil
  self
end