Class: SlashedHash

Inherits:
Hash
  • Object
show all
Includes:
StandardHashMethodsInRuby
Defined in:
lib/hash_magic.rb

Overview

A SlashedHash is a hash whose values can be accessed in the normal manner, or with keys that are slash(‘/’)-separated strings. To get the whole hash as a single flattened level, call SlashedHash#flat. All keys are converted to strings. All end-of-the-chain values are kept in whatever value they are.

s = {'a' => 'b', 'c' => {'d' => :e}}.slashed
s['a'] #=> 'b'
s['c'] #=> {slashed: 'd'=>:e}
s['c']['d'] #=> :e
s['c/d'] #=> :e

Instance Method Summary collapse

Constructor Details

#initialize(hsh = {}) ⇒ SlashedHash

Returns a new instance of SlashedHash.

Raises:

  • (ArgumentError)


171
172
173
174
175
# File 'lib/hash_magic.rb', line 171

def initialize(hsh={})
  raise ArgumentError, "must be a hash or array of slashed values" unless hsh.is_a?(Hash) || hsh.is_a?(Array)
  @constructor = hsh.is_a?(Hash) ? hsh.class : Hash
  @flat = flatten_to_hash(hsh)
end

Instance Method Details

#==(other) ⇒ Object

:nodoc:



281
282
283
284
285
286
287
288
289
# File 'lib/hash_magic.rb', line 281

def ==(other) # :nodoc:
  if other.is_a?(SlashedHash)
    @slashed == other.instance_variable_get(:@slashed)
  elsif other.is_a?(Hash)
    self == SlashedHash.new(other)
  else
    raise TypeError, "Cannot compare #{other.class.name} with SlashedHash"
  end
end

#[](key) ⇒ Object

Behaves like the usual Hash#[] method, but you can access nested hash values by composing a single key of the traversing keys joined by ‘/’:

hash['c']['d'] # is the same as:
hash['c/d']


182
183
184
185
186
187
188
189
190
191
# File 'lib/hash_magic.rb', line 182

def [](key)
  rg = Regexp.new("^#{key}/?")
  start_obj = if @constructor == OrderedHash
    @constructor.new((@flat.instance_variable_get(:@keys_in_order) || []).collect {|e| e.gsub(rg,'')})
  else
    @constructor.new
  end
  v = @flat.has_key?(key) ? @flat[key] : self.class.new(@flat.reject {|k,v| !(k == key || k =~ rg)}.inject(start_obj) {|h,(k,v)| h[k.gsub(rg,'')] = v; h})
  v.is_a?(self.class) && v.empty? ? nil : v
end

#[]=(key, value) ⇒ Object

Same as above, except sets value rather than retrieving it.



193
194
195
196
197
198
199
200
201
202
# File 'lib/hash_magic.rb', line 193

def []=(key,value)
  @flat.reject! {|k,v| k == key || k =~ Regexp.new("^#{key}/")}
  if value.is_a?(Hash)
    flatten_to_hash(value).each do |hk,hv|
      @flat[key.to_s+'/'+hk.to_s] = hv
    end
  else
    @flat[key.to_s] = value
  end
end

#clearObject

:nodoc:



203
204
205
# File 'lib/hash_magic.rb', line 203

def clear # :nodoc:
  @flat.clear
end

#delete(key, &block) ⇒ Object

You can use slashed keys here, too.



224
225
226
227
228
229
# File 'lib/hash_magic.rb', line 224

def delete(key,&block)
  value = @flat.has_key?(key) ? @flat[key] : self.class.new(@flat.reject {|k,v| !(k == key || k =~ Regexp.new("^#{key}/"))}.inject({}) {|h,(k,v)| h[k.split('/',2)[1]] = v; h})
  return block.call(key) if value.is_a?(self.class) && value.empty? && block_given?
  @flat.keys.reject {|k| !(k == key || k =~ Regexp.new("^#{key}/"))}.each {|k| @flat.delete(k)}
  return value
end

#empty?Boolean

:nodoc:

Returns:

  • (Boolean)


230
231
232
# File 'lib/hash_magic.rb', line 230

def empty? # :nodoc:
  @flat.empty?
end

#expandObject

Expands the whole hash to Hash objects … not useful very often, it seems.



257
258
259
# File 'lib/hash_magic.rb', line 257

def expand
  inject({}) {|h,(k,v)| h[k] = v.is_a?(SlashedHash) ? v.expand : v; h}
end

#fetch(key, default = :ehisehoah0928309q98y30, &block) ⇒ Object

:nodoc:



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
# File 'lib/hash_magic.rb', line 206

def fetch(key,default=:ehisehoah0928309q98y30,&block) # :nodoc:
  value = @flat.has_key?(key) ? @flat[key] : self.class.new(@flat.reject {|k,v| !(k == key || k =~ Regexp.new("^#{key}/"))}.inject({}) {|h,(k,v)| h[k.split('/',2)[1]] = v; h})
  if value.is_a?(self.class) && value.empty?
    if default == :ehisehoah0928309q98y30
      if block_given?
        block.call(key)
      else
        raise IndexError
      end
      value
    else
      default
    end
  else
    value
  end
end

#flatObject

Gives a list of all keys in all levels in the multi-level hash, joined by slashes.

{'a'=>{'b'=>'c', 'c'=>'d'}, 'b'=>'c'}.slashed.flat.keys
=> ['a/b', 'a/c', 'b']


253
254
255
# File 'lib/hash_magic.rb', line 253

def flat
  @flat
end

#index(value) ⇒ Object

This gives you the slashed key of the value, no matter where the value is in the tree.



234
235
236
# File 'lib/hash_magic.rb', line 234

def index(value)
  @flat.index(value)
end

#inspectObject

:nodoc:



237
238
239
# File 'lib/hash_magic.rb', line 237

def inspect # :nodoc:
  @flat.inspect.insert(1,'slashed: ')
end

#keysObject

This gives you only the top-level keys, no slashes. To get the list of slashed keys, do hash.flat.keys



241
242
243
# File 'lib/hash_magic.rb', line 241

def keys
  @flat.inject([]) {|a,(k,v)| a << [k.split('/',2)].flatten[0]; a}.uniq
end

#ordered(*keys_in_order) ⇒ Object

Same as ordered! but returns a new SlashedHash object instead of modifying the same.



267
268
269
# File 'lib/hash_magic.rb', line 267

def ordered(*keys_in_order)
  dup.ordered!(*keys_in_order)
end

#ordered!(*keys_in_order) ⇒ Object

Sets the SlashedArray as ordered. The *keys_in_order must be a flat array of slashed keys that specify the order for each level:

s = {'a'=>{'b'=>'c', 'c'=>'d'}, 'b'=>'c'}.slashed
s.ordered!('b', 'a/c', 'a/b')
s.expand # => {'b'=>'c', 'a'=>{'c'=>'d', 'b'=>'c'}}
# Note that the expanded hashes will *still* be ordered!


275
276
277
278
279
280
# File 'lib/hash_magic.rb', line 275

def ordered!(*keys_in_order)
  return self if @constructor == OrderedHash
  @constructor = OrderedHash
  @flat = @flat.ordered(*keys_in_order)
  self
end

#rehashObject

This is rewritten to mean something slightly different than usual: Use this to restructure the hash, for cases when you end up with an array holding several hashes.



246
247
248
# File 'lib/hash_magic.rb', line 246

def rehash # :nodoc:
  @flat.rehash
end

#slashedObject

:nodoc:



263
264
265
# File 'lib/hash_magic.rb', line 263

def slashed # :nodoc:
  self
end

#to_string_arrayObject



260
261
262
# File 'lib/hash_magic.rb', line 260

def to_string_array
  flatten_to_array(flat,[])
end