Class: Hashie::Blash

Inherits:
Hash
  • Object
show all
Includes:
PrettyInspect
Defined in:
lib/hashie/blash.rb

Overview

Blash allows you to create Mash-like objects with a block syntax for creating nested hashes. This is useful for building deeply nested configuration hashes, similar in style to many dsl based configuration files.

A Blash will look at the methods you pass it and perform operations based on the following rules:

  • No punctuation: Returns the value of the hash for that key, or nil if none exists.

  • With Block ({...}): Sets key to and yields a new blash

  • Assignment (=): Sets the attribute of the given method name.

  • Existence (?): Returns true or false depending on whether that key has been set.

Basic Example

blash = Blash.new
blash.name? # => false
blash.name = "Bob"
blash.name # => "Bob"
blash.name? # => true

Hash Conversion Example

hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 44, :h => 29}, 12]}
blash = Blash.new(hash)
blash.a.b # => 23
blash.a.d.e # => "abc"
blash.f.first.g # => 44
blash.f.last # => 12

Block Example

blash = Blash.new
blash.author do |a|
  a.name = "Michael Bleigh"
end
blash.author # => <Blash name="Michael Bleigh">
blash.author.name # => "Michael Bleigh"

Instance Method Summary collapse

Constructor Details

#initialize(source_hash = nil, default = nil, &blk) ⇒ Blash

If you pass in an existing hash, it will convert it to a Blash including recursively descending into arrays and hashes, converting them as well.



51
52
53
54
# File 'lib/hashie/blash.rb', line 51

def initialize(source_hash = nil, default = nil, &blk)
  deep_update(source_hash) if source_hash
  default ? super(default) : super(&blk)
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, &blk) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
# File 'lib/hashie/blash.rb', line 169

def method_missing(method_name, *args, &blk)
  match = method_name.to_s.match(/(.*?)([?=]?)$/)

  if block_given?
    super unless match[2].empty?

    raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" if args.size > 1

    if key?(method_name) && ! self.[](method_name).is_a?(Blash)
      raise TypeError, "key '#{method_name}' already contains a #{self.[](method_name).class}"
    end

    val = self.[](method_name) || initializing_reader(method_name)

    yield val

    return val
  else
    if key?(method_name)
      raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
      return self.[](method_name, &blk)
    end

    case match[2]
    when "="
      raise ArgumentError, "wrong number of arguments (#{args.size} for 1)" unless args.size == 1
      self[match[1]] = args.first
    when "?"
      raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
      !!self[match[1]]
    when ""
      raise ArgumentError, "wrong number of arguments (#{args.size} for 0)" unless args.empty?
      default(method_name)
    else
      super
    end
  end
end

Instance Method Details

#custom_reader(key) {|value| ... } ⇒ Object Also known as: []

Retrieves an attribute set in the Blash. Will convert any key passed in to a string before retrieving.

Yields:

  • (value)


71
72
73
74
75
# File 'lib/hashie/blash.rb', line 71

def custom_reader(key)
  value = regular_reader(convert_key(key))
  yield value if block_given?
  value
end

#custom_writer(key, value) ⇒ Object Also known as: []=

Sets an attribute in the Blash. Key will be converted to a string before it is set, and Hashes will be converted into Blashes for nesting purposes.



80
81
82
# File 'lib/hashie/blash.rb', line 80

def custom_writer(key,value) #:nodoc:
  regular_writer(convert_key(key), convert_value(value))
end

#deep_merge(other_hash, &blk) ⇒ Object Also known as: merge

Performs a deep_update on a duplicate of the current blash.



118
119
120
# File 'lib/hashie/blash.rb', line 118

def deep_merge(other_hash, &blk)
  dup.deep_update(other_hash, &blk)
end

#deep_update(other_hash, &blk) ⇒ Object Also known as: deep_merge!, update

Recursively merges this blash with the passed in hash, merging each hash in the hierarchy.



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/hashie/blash.rb', line 125

def deep_update(other_hash, &blk)
  other_hash.each_pair do |k,v|
    key = convert_key(k)
    if regular_reader(key).is_a?(Blash) and v.is_a?(::Hash)
      custom_reader(key).deep_update(v, &blk)
    else
      value = convert_value(v, true)
      value = blk.call(key, self[k], value) if blk
      custom_writer(key, value)
    end
  end
  self
end

#delete(key) ⇒ Object



99
100
101
# File 'lib/hashie/blash.rb', line 99

def delete(key)
  super(convert_key(key))
end

#dupObject

Duplicates the current blash as a new blash.



105
106
107
# File 'lib/hashie/blash.rb', line 105

def dup
  self.class.new(self, self.default)
end

#fetch(key, *args) ⇒ Object



95
96
97
# File 'lib/hashie/blash.rb', line 95

def fetch(key, *args)
  super(convert_key(key), *args)
end

#idObject

:nodoc:



58
59
60
# File 'lib/hashie/blash.rb', line 58

def id #:nodoc:
  self["id"]
end

#initializing_reader(key) ⇒ Object

This is the bang method reader, it will return a new Blash if there isn’t a value already assigned to the key requested.



89
90
91
92
93
# File 'lib/hashie/blash.rb', line 89

def initializing_reader(key)
  ck = convert_key(key)
  regular_writer(ck, self.class.new) unless key?(ck)
  regular_reader(ck)
end

#key?(key) ⇒ Boolean Also known as: has_key?, include?, member?

Returns:

  • (Boolean)


109
110
111
# File 'lib/hashie/blash.rb', line 109

def key?(key)
  super(convert_key(key))
end

#regular_dupObject



103
# File 'lib/hashie/blash.rb', line 103

alias_method :regular_dup, :dup

#replace(other_hash) ⇒ Object



156
157
158
159
160
# File 'lib/hashie/blash.rb', line 156

def replace(other_hash)
  (keys - other_hash.keys).each { |key| delete(key) }
  other_hash.each { |key, value| self[key] = value }
  self
end

#respond_to?(method_name, include_private = false) ⇒ Boolean

Will return true if the Blash has had a key set in addition to normal respond_to? functionality.

Returns:

  • (Boolean)


164
165
166
167
# File 'lib/hashie/blash.rb', line 164

def respond_to?(method_name, include_private=false)
  return true if key?(method_name) || method_name.to_s.slice(/[=?]\Z/)
  super
end

#shallow_merge(other_hash) ⇒ Object

Performs a shallow_update on a duplicate of the current blash



143
144
145
# File 'lib/hashie/blash.rb', line 143

def shallow_merge(other_hash)
  dup.shallow_update(other_hash)
end

#shallow_update(other_hash) ⇒ Object

Merges (non-recursively) the hash from the argument, changing the receiving hash



149
150
151
152
153
154
# File 'lib/hashie/blash.rb', line 149

def shallow_update(other_hash)
  other_hash.each_pair do |k,v|
    regular_writer(convert_key(k), convert_value(v, true))
  end
  self
end

#typeObject

:nodoc:



62
63
64
# File 'lib/hashie/blash.rb', line 62

def type #:nodoc:
  self["type"]
end