Class: Yagni::Hash

Inherits:
Object
  • Object
show all
Defined in:
lib/yagni/hash.rb

Overview

Yagni::Hash is a class that provides easy access to members, in a method-like syntax.

Usage:

h = Yagni::Hash.new({ :my => 'hash',
                     :is => { :just => 'awesome!' }
                   })

h.my      #=> "hash"
h.is.just #=> "awesome!"

You can also pass a block with your data, allowing us to give you a method-like access syntax, as long as you provide us the corresponding hash. The code passed in initialization will only be called when you first try to access a key.

# File: data.json

{
  "my": "json",

"is": {
        "just": "awesome!"
      }
}

# File: test.rb
require 'json'
require 'yagni'
y = Yagni::Hash.new { JSON.parse File.read('data.json') }
y.my      #=> "json"
# You need to call Yagni::Hash#reload, otherwise we would have to
# monkey patch Object::Hash, and that would be terrible!
y.reload  #=> true
y.is.just #=> "awesome!"

Instance Method Summary collapse

Constructor Details

#initialize(hash = nil, &block) ⇒ Hash

Creates a new Yagni::Hash instance. You can pass it an Object::Hash instance directly, or a code block, which will return our hash. The data will be lazily initialized, meaning that the block will be only called when you first try to access a key.



47
48
49
50
51
52
53
54
55
# File 'lib/yagni/hash.rb', line 47

def initialize(hash=nil, &block)
  @data = @context = hash
  
  setup_context if @context

  @loader = block

  @loaded = !block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args, &block) ⇒ Object

If the method passed is a key from the current context, we return it. Otherwise, self is returned so that the user can chain keys in a method-like syntax.



66
67
68
69
70
71
72
73
74
# File 'lib/yagni/hash.rb', line 66

def method_missing(meth, *args, &block)
  load_data unless @loaded

  return change_context_to(meth) && self if nested? @context[meth]

  return change_context_to(meth) if @context[meth]
  
  super
end

Instance Method Details

#change_context_to(key) ⇒ Object

Updates the current context and symbolize its keys.



108
109
110
111
112
113
# File 'lib/yagni/hash.rb', line 108

def change_context_to(key)
  @context = @context[key]
  symbolize_keys!(@context) if nested? @context

  @context
end

#load_dataObject

Calls the passed block on initialization (which should provide us the data)



78
79
80
81
82
83
84
# File 'lib/yagni/hash.rb', line 78

def load_data
  @data = @loader.call
  symbolize_keys! @data
  @loaded = true

  @context = @data.dup
end

#nested?(obj) ⇒ Boolean

Checks wheter the given object is another hash (as in Object::Hash)

Returns:

  • (Boolean)


103
104
105
# File 'lib/yagni/hash.rb', line 103

def nested?(obj)
  obj && obj.kind_of?(Object::Hash)
end

#reloadObject

reloads the object. In other words, we change the context to the root of the initial data.



97
98
99
100
# File 'lib/yagni/hash.rb', line 97

def reload
  @context = @data
  true
end

#respond_to?(name) ⇒ Boolean

Updates respond_to? so that we answer true for key names

Returns:

  • (Boolean)


58
59
60
61
# File 'lib/yagni/hash.rb', line 58

def respond_to?(name)
  return true if @data && (@data.key?(name.to_s) || @data.key?(name.to_sym))
  super
end

#setup_contextObject

Symbolizes our data and make a copy of it as the current context



116
117
118
119
# File 'lib/yagni/hash.rb', line 116

def setup_context
  symbolize_keys! @data
  @context = @data.dup
end

#symbolize_keys!(hash) ⇒ Object

Converts all the keys of the passed hash to symbols.



87
88
89
90
91
92
93
# File 'lib/yagni/hash.rb', line 87

def symbolize_keys!(hash)
  hash.keys.each do |key|
    hash[key.to_sym] = hash.delete(key)
  end

  hash
end