Class: Hashie::Mash

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

Overview

Mash allows you to create pseudo-objects that have method-like accessors for hash keys. This is useful for such implementations as an API-accessing library that wants to fake robust objects without the overhead of actually doing so. Think of it as OpenStruct with some additional goodies.

A Mash 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.
  • Assignment (=): Sets the attribute of the given method name.
  • Existence (?): Returns true or false depending on whether that key has been set.
  • Bang (!): Forces the existence of this key, used for deep Mashes. Think of it as "touch" for mashes.
  • Under Bang (_): Like Bang, but returns a new Mash rather than creating a key. Used to test existance in deep Mashes.

== Basic Example

mash = Mash.new mash.name? # => false mash.name = "Bob" mash.name # => "Bob" mash.name? # => true

== Hash Conversion Example

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

== Bang Example

mash = Mash.new mash.author # => nil mash.author! # =>

mash = Mash.new mash.author!.name = "Michael Bleigh" mash.author # =>

== Under Bang Example

mash = Mash.new mash.author # => nil mash.author_ # => mash.author_.name # => nil

mash = Mash.new mash.author_.name = "Michael Bleigh" (assigned to temp object) mash.author # =>

Constant Summary collapse

ALLOWED_SUFFIXES =
%w(? ! = _)

Instance Method Summary collapse

Methods included from PrettyInspect

#hashie_inspect, included

Methods inherited from Hash

#to_hash, #to_json

Methods included from HashExtensions

#hashie_stringify_keys, #hashie_stringify_keys!, included, #to_mash

Constructor Details

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

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



66
67
68
69
# File 'lib/hashie/mash.rb', line 66

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



200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
# File 'lib/hashie/mash.rb', line 200

def method_missing(method_name, *args, &blk)
  return self.[](method_name, &blk) if key?(method_name)
  suffixes_regex = ALLOWED_SUFFIXES.join
  match = method_name.to_s.match(/(.*?)([#{suffixes_regex}]?)$/)
  case match[2]
  when '='
    self[match[1]] = args.first
  when '?'
    !!self[match[1]]
  when '!'
    initializing_reader(match[1])
  when '_'
    underbang_reader(match[1])
  else
    default(method_name)
  end
end

Instance Method Details

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

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

Yields:

  • (value)


86
87
88
89
90
# File 'lib/hashie/mash.rb', line 86

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

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

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



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

def custom_writer(key, value, convert = true) #:nodoc:
  regular_writer(convert_key(key), convert ? convert_value(value) : value)
end

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

Performs a deep_update on a duplicate of the current mash.



144
145
146
# File 'lib/hashie/mash.rb', line 144

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 mash with the passed in hash, merging each hash in the hierarchy.



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/hashie/mash.rb', line 151

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

#delete(key) ⇒ Object



125
126
127
# File 'lib/hashie/mash.rb', line 125

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

#dupObject

Duplicates the current mash as a new mash.



131
132
133
# File 'lib/hashie/mash.rb', line 131

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

#fetch(key, *args) ⇒ Object



121
122
123
# File 'lib/hashie/mash.rb', line 121

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

#idObject

:nodoc:



73
74
75
# File 'lib/hashie/mash.rb', line 73

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

#initializing_reader(key) ⇒ Object

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



104
105
106
107
108
# File 'lib/hashie/mash.rb', line 104

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)


135
136
137
# File 'lib/hashie/mash.rb', line 135

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

#prefix_method?(method_name) ⇒ Boolean

Returns:

  • (Boolean)


195
196
197
198
# File 'lib/hashie/mash.rb', line 195

def prefix_method?(method_name)
  method_name = method_name.to_s
  method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
end

#regular_dupObject



129
# File 'lib/hashie/mash.rb', line 129

alias_method :regular_dup, :dup

#replace(other_hash) ⇒ Object



182
183
184
185
186
# File 'lib/hashie/mash.rb', line 182

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 Mash has had a key set in addition to normal respond_to? functionality.

Returns:

  • (Boolean)


190
191
192
193
# File 'lib/hashie/mash.rb', line 190

def respond_to?(method_name, include_private = false)
  return true if key?(method_name) || prefix_method?(method_name)
  super
end

#shallow_merge(other_hash) ⇒ Object

Performs a shallow_update on a duplicate of the current mash



169
170
171
# File 'lib/hashie/mash.rb', line 169

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



175
176
177
178
179
180
# File 'lib/hashie/mash.rb', line 175

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:



77
78
79
# File 'lib/hashie/mash.rb', line 77

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

#underbang_reader(key) ⇒ Object

This is the under bang method reader, it will return a temporary new Mash if there isn't a value already assigned to the key requested.



112
113
114
115
116
117
118
119
# File 'lib/hashie/mash.rb', line 112

def underbang_reader(key)
  ck = convert_key(key)
  if key?(ck)
    regular_reader(ck)
  else
    self.class.new
  end
end