Class: Configurable::ConfigHash

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

Overview

ConfigHash acts like a hash that maps get and set operations as specified in a Configurable’s class configurations.

class Sample
  include Configurable
  config :key
end

sample = Sample.new
sample.config.class               # => ConfigHash

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

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

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

sample.config[:not_delegated] = 'value'
sample.config[:not_delegated]     # => 'value'

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

IndifferentAccess

A ConfigHash uses the receiver class configurations to determine when and how to map get/set operations. In cases where multiple keys need to map in the same way (for example when you want indifferent access for strings and symbols), simply extend the class configurations so that the AGET ([]) method returns the correct Config in all cases.

Inconsistency

ConfigHashes can fall into an inconsistent state if you manually add values to store that would normally be mapped to the receiver. This is both easy to avoid and easy to repair.

To avoid inconsistency, don’t manually add values to the store and set import_store to true during initialization. To repair inconsistency, import the current store to self.

config_hash = Sample.new.config
config_hash[:key] = 'a'
config_hash.store[:key] = 'b'

config_hash[:key]          # => 'a'
config_hash.to_hash        # => {:key => 'b'}
config_hash.inconsistent?  # => true

config_hash.import(config_hash.store)

config_hash[:key]          # => 'b'
config_hash.to_hash        # => {:key => 'b'}
config_hash.inconsistent?  # => false

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(receiver, store = {}, import_store = true) ⇒ ConfigHash

Initializes a new ConfigHash. Initialize normally imports values from store to ensure it doesn’t contain entries that could be stored on the receiver.

Setting import_store to false allows quick initialization but can result in an inconsistent state.



77
78
79
80
81
82
# File 'lib/configurable/config_hash.rb', line 77

def initialize(receiver, store={}, import_store=true)
  @receiver = receiver
  @store = store
  
  import(store) if import_store
end

Instance Attribute Details

#receiverObject (readonly)

The bound receiver



65
66
67
# File 'lib/configurable/config_hash.rb', line 65

def receiver
  @receiver
end

#storeObject (readonly)

The underlying data store; setting values in store directly can result in an inconsistent state. Use []= instead.



69
70
71
# File 'lib/configurable/config_hash.rb', line 69

def store
  @store
end

Instance Method Details

#==(another) ⇒ Object

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



166
167
168
# File 'lib/configurable/config_hash.rb', line 166

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

#[](key) ⇒ Object

Retrieves the value for the key, either from the receiver or the store.



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

def [](key)
  if config = configs[key]
    config.get(receiver)
  else
    store[key]
  end
end

#[]=(key, value) ⇒ Object

Stores a value for the key, either on the receiver or in the store.



120
121
122
123
124
125
126
# File 'lib/configurable/config_hash.rb', line 120

def []=(key, value)
  if config = configs[key]
    config.set(receiver, value)
  else
    store[key] = value
  end
end

#configsObject

Returns receiver.class.configurations.



85
86
87
# File 'lib/configurable/config_hash.rb', line 85

def configs
  receiver.class.configurations
end

#each_pairObject

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



155
156
157
158
159
160
161
162
163
# File 'lib/configurable/config_hash.rb', line 155

def each_pair # :yields: key, value
  configs.each_pair do |key, config|
    yield(key, config.get(receiver))
  end
  
  store.each_pair do |key, value|
    yield(key, value)
  end
end

#has_key?(key) ⇒ Boolean

True if the key is a key in configs or store.

Returns:

  • (Boolean)


134
135
136
# File 'lib/configurable/config_hash.rb', line 134

def has_key?(key)
  configs[key] != nil || store.has_key?(key) 
end

#import(store) ⇒ Object

Imports stored values that can be mapped to the receiver. The values are removed from store in the process. Returns self.

Primarily used to create a consistent state for self (see above).



93
94
95
96
97
98
99
100
101
# File 'lib/configurable/config_hash.rb', line 93

def import(store)
  configs = self.configs # cache as an optimization
  store.keys.each do |key|
    next unless config = configs[key]
    config.set(receiver, store.delete(key))
  end
  
  self
end

#inconsistent?Boolean

Returns true if the store has entries that can be stored on the receiver.

Returns:

  • (Boolean)


105
106
107
108
# File 'lib/configurable/config_hash.rb', line 105

def inconsistent?
  configs = self.configs # cache as an optimization
  store.keys.any? {|key| configs[key] }
end

#inspectObject

Returns an inspection string.



194
195
196
# File 'lib/configurable/config_hash.rb', line 194

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

#keysObject

Returns the union of configs and store keys.



129
130
131
# File 'lib/configurable/config_hash.rb', line 129

def keys
  configs.keys | store.keys
end

#merge!(another) ⇒ Object

Merges another with self.



139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/configurable/config_hash.rb', line 139

def merge!(another)
  # cache configs and inline set as a significant optimization
  configs = self.configs
  (configs.keys | another.keys).each do |key|
    next unless another.has_key?(key)
    
    value = another[key]
    if config = configs[key]
      config.set(receiver, value)
    else
      store[key] = value
    end
  end
end

#to_hash(scrub = false, &block) ⇒ Object

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



172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
# File 'lib/configurable/config_hash.rb', line 172

def to_hash(scrub=false, &block)
  hash = {}
  each_pair do |key, value|
    if value.kind_of?(ConfigHash)
      value = value.to_hash(scrub, &block)
    end
    
    if scrub
      config = configs[key]
      next if config && config.default == value
    end
    
    if block_given?
      yield(hash, key, value)
    else
      hash[key] = value
    end
  end
  hash
end