Class: Hashie::Mash

Inherits:
Hash
  • Object
show all
Extended by:
Extensions::KeyConflictWarning
Includes:
Extensions::PrettyInspect, Extensions::RubyVersionCheck
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.
  • Truthiness (?): Returns true or false depending on the truthiness of the attribute, or false if the key is not 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[? ! = _].freeze

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Extensions::KeyConflictWarning

disable_warnings, disable_warnings?, disabled_warnings, inherited

Methods included from Extensions::RubyVersionCheck

included

Methods included from Extensions::PrettyInspect

#hashie_inspect, included

Methods inherited from Hash

#to_hash, #to_json, #to_mash

Methods included from Extensions::StringifyKeys

#stringify_keys, #stringify_keys!

Methods included from Extensions::StringifyKeys::ClassMethods

#stringify_keys, #stringify_keys!, #stringify_keys_recursively!

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.


101
102
103
104
# File 'lib/hashie/mash.rb', line 101

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

rubocop:disable Style/MethodMissing


299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
# File 'lib/hashie/mash.rb', line 299

def method_missing(method_name, *args, &blk) # rubocop:disable Style/MethodMissing
  return self.[](method_name, &blk) if key?(method_name)
  name, suffix = method_name_and_suffix(method_name)
  case suffix
  when '='.freeze
    assign_property(name, args.first)
  when '?'.freeze
    !!self[name]
  when '!'.freeze
    initializing_reader(name)
  when '_'.freeze
    underbang_reader(name)
  else
    self[method_name]
  end
end

Class Method Details

.load(path, options = {}) ⇒ Object

Raises:

  • (ArgumentError)

71
72
73
74
75
76
77
78
79
80
# File 'lib/hashie/mash.rb', line 71

def self.load(path, options = {})
  @_mashes ||= new

  return @_mashes[path] if @_mashes.key?(path)
  raise ArgumentError, "The following file doesn't exist: #{path}" unless File.file?(path)

  options = options.dup
  parser = options.delete(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
  @_mashes[path] = new(parser.perform(path, options)).freeze
end

.quiet(*method_keys) ⇒ Object

Creates a new anonymous subclass with key conflict warnings disabled. You may pass an array of method symbols to restrict the disabled warnings to. Hashie::Mash.quiet.new(hash) all warnings disabled. Hashie::Mash.quiet(:zip).new(hash) only zip warning is disabled.


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

def self.quiet(*method_keys)
  @memoized_classes ||= {}
  @memoized_classes[method_keys] ||= Class.new(self) do
    disable_warnings(*method_keys)
  end
end

Instance Method Details

#assign_property(name, value) ⇒ Object

Assigns a value to a key


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

def assign_property(name, value)
  self[name] = value
end

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

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

Yields:

  • (value)

126
127
128
129
130
131
# File 'lib/hashie/mash.rb', line 126

def custom_reader(key)
  default_proc.call(self, key) if default_proc && !key?(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. Symbol keys will be converted to strings before being set, and Hashes will be converted into Mashes for nesting purposes.


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

def custom_writer(key, value, convert = true) #:nodoc:
  log_built_in_message(key) if key.respond_to?(:to_sym) && log_collision?(key.to_sym)
  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.


211
212
213
# File 'lib/hashie/mash.rb', line 211

def deep_merge(*other_hashes, &blk)
  dup.deep_update(*other_hashes, &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.


217
218
219
220
221
222
# File 'lib/hashie/mash.rb', line 217

def deep_update(*other_hashes, &blk)
  other_hashes.each do |other_hash|
    _deep_update(other_hash, &blk)
  end
  self
end

#delete(key) ⇒ Object


167
168
169
# File 'lib/hashie/mash.rb', line 167

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

#dupObject

Duplicates the current mash as a new mash.


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

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

#extractable_options?Boolean

play nice with ActiveSupport Array#extract_options!

Returns:

  • (Boolean)

317
318
319
# File 'lib/hashie/mash.rb', line 317

def extractable_options?
  true
end

#fetch(key, *args) ⇒ Object


163
164
165
# File 'lib/hashie/mash.rb', line 163

def fetch(key, *args)
  super(convert_key(key), *args)
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.


146
147
148
149
150
# File 'lib/hashie/mash.rb', line 146

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

#invertObject

Returns a new instance of the class it was called on, using its keys as values, and its values as keys. The new values and keys will always be strings.


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

def invert
  self.class.new(super)
end

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

Returns:

  • (Boolean)

201
202
203
# File 'lib/hashie/mash.rb', line 201

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

#prefix_method?(method_name) ⇒ Boolean

Returns:

  • (Boolean)

294
295
296
297
# File 'lib/hashie/mash.rb', line 294

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

#regular_dupObject


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

alias regular_dup dup

#regular_key?Object


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

alias regular_key? key?

#reject(&blk) ⇒ Object

Returns a new instance of the class it was called on, containing elements for which the given block returns false.


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

def reject(&blk)
  self.class.new(super(&blk))
end

#replace(other_hash) ⇒ Object


278
279
280
281
282
# File 'lib/hashie/mash.rb', line 278

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

#respond_to_missing?(method_name, *args) ⇒ Boolean

Returns:

  • (Boolean)

284
285
286
287
288
289
290
291
292
# File 'lib/hashie/mash.rb', line 284

def respond_to_missing?(method_name, *args)
  return true if key?(method_name)
  suffix = method_suffix(method_name)
  if suffix
    true
  else
    super
  end
end

#reverse_merge(other_hash) ⇒ Object

another ActiveSupport method, see issue #270


322
323
324
# File 'lib/hashie/mash.rb', line 322

def reverse_merge(other_hash)
  self.class.new(other_hash).merge(self)
end

#select(&blk) ⇒ Object

Returns a new instance of the class it was called on, containing elements for which the given block returns true.


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

def select(&blk)
  self.class.new(super(&blk))
end

#shallow_merge(other_hash) ⇒ Object

Performs a shallow_update on a duplicate of the current mash


265
266
267
# File 'lib/hashie/mash.rb', line 265

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


271
272
273
274
275
276
# File 'lib/hashie/mash.rb', line 271

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

#to_module(mash_method_name = :settings) ⇒ Object


82
83
84
85
86
87
88
89
# File 'lib/hashie/mash.rb', line 82

def to_module(mash_method_name = :settings)
  mash = self
  Module.new do |m|
    m.send :define_method, mash_method_name.to_sym do
      mash
    end
  end
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.


154
155
156
157
158
159
160
161
# File 'lib/hashie/mash.rb', line 154

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

#values_at(*keys) ⇒ Object


171
172
173
# File 'lib/hashie/mash.rb', line 171

def values_at(*keys)
  super(*keys.map { |key| convert_key(key) })
end

#with_accessors!Object


91
92
93
# File 'lib/hashie/mash.rb', line 91

def with_accessors!
  extend Hashie::Extensions::Mash::DefineAccessors
end