class Hashie::Mash

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:

Basic Example

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

Hash Conversion Example

hash = {:a => {:b => 23, :d => {:e => "abc"}}, :f => [{:g => 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 = Mash.new
mash.author!.name = "Michael Bleigh"
mash.author # => <Mash name="Michael Bleigh">

Under Bang Example

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

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

Constants

ALLOWED_SUFFIXES

Public Class Methods

[](source_hash = nil, default = nil, &blk)
Alias for: new
load(path, options = {}) click to toggle source
# File lib/hashie/mash.rb, line 70
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
new(source_hash = nil, default = nil, &blk) click to toggle source

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.

Calls superclass method
# File lib/hashie/mash.rb, line 100
def initialize(source_hash = nil, default = nil, &blk)
  deep_update(source_hash) if source_hash
  default ? super(default) : super(&blk)
end
Also aliased as: []
quiet(*method_keys) click to toggle source

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.

# File lib/hashie/mash.rb, line 111
def self.quiet(*method_keys)
  @memoized_classes ||= {}
  @memoized_classes[method_keys] ||= Class.new(self) do
    disable_warnings(*method_keys)
  end
end

Public Instance Methods

[](key)
Also aliased as: regular_reader
Alias for: custom_reader
assign_property(name, value) click to toggle source

Assigns a value to a key

# File lib/hashie/mash.rb, line 259
def assign_property(name, value)
  self[name] = value
end
compact() click to toggle source

Returns a new instance of the class it was called on, with nil values removed.

Calls superclass method
# File lib/hashie/mash.rb, line 338
def compact
  self.class.new(super)
end
custom_reader(key) { |value| ... } click to toggle source

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

# File lib/hashie/mash.rb, line 125
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
Also aliased as: []
deep_merge(*other_hashes, &blk) click to toggle source

Performs a deep_update on a duplicate of the current mash.

# File lib/hashie/mash.rb, line 210
def deep_merge(*other_hashes, &blk)
  dup.deep_update(*other_hashes, &blk)
end
Also aliased as: merge
deep_merge!(*other_hashes, &blk)
Alias for: deep_update
deep_update(*other_hashes, &blk) click to toggle source

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

# File lib/hashie/mash.rb, line 216
def deep_update(*other_hashes, &blk)
  other_hashes.each do |other_hash|
    _deep_update(other_hash, &blk)
  end
  self
end
Also aliased as: deep_merge!, update
delete(key) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 166
def delete(key)
  super(convert_key(key))
end
dig(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 326
def dig(*keys)
  super(*keys.map { |key| convert_key(key) })
end
dup() click to toggle source

Duplicates the current mash as a new mash.

# File lib/hashie/mash.rb, line 195
def dup
  self.class.new(self, default, &default_proc)
end
Also aliased as: regular_dup
except(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 355
def except(*keys)
  string_keys = keys.map { |key| convert_key(key) }
  self.class.new(super(*string_keys))
end
extractable_options?() click to toggle source

play nice with ActiveSupport Array#extract_options!

# File lib/hashie/mash.rb, line 316
def extractable_options?
  true
end
fetch(key, *args) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 162
def fetch(key, *args)
  super(convert_key(key), *args)
end
has_key?(key)
Alias for: key?
include?(key)
Alias for: key?
initializing_reader(key) click to toggle source

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

# File lib/hashie/mash.rb, line 145
def initializing_reader(key)
  ck = convert_key(key)
  regular_writer(ck, self.class.new) unless key?(ck)
  regular_reader(ck)
end
invert() click to toggle source

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.

Calls superclass method
# File lib/hashie/mash.rb, line 177
def invert
  self.class.new(super)
end
key?(key) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 200
def key?(key)
  super(convert_key(key))
end
Also aliased as: regular_key?, has_key?, include?, member?
member?(key)
Alias for: key?
merge(*other_hashes, &blk)

Alias these lexically so they get the correctly defined deep_merge and deep_update based on ruby version.

Alias for: deep_merge
merge!(*other_hashes, &blk)
Alias for: update
method_missing(method_name, *args, &blk) click to toggle source
# File lib/hashie/mash.rb, line 298
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
prefix_method?(method_name) click to toggle source
# File lib/hashie/mash.rb, line 293
def prefix_method?(method_name)
  method_name = method_name.to_s
  method_name.end_with?(*ALLOWED_SUFFIXES) && key?(method_name.chop)
end
regular_dup()
Alias for: dup
regular_key?(key)
Alias for: key?
regular_reader(key)
Alias for: []
reject(&blk) click to toggle source

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

Calls superclass method
# File lib/hashie/mash.rb, line 183
def reject(&blk)
  self.class.new(super(&blk))
end
replace(other_hash) click to toggle source
# File lib/hashie/mash.rb, line 277
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) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 283
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) click to toggle source

another ActiveSupport method, see issue #270

# File lib/hashie/mash.rb, line 321
def reverse_merge(other_hash)
  self.class.new(other_hash).merge(self)
end
select(&blk) click to toggle source

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

Calls superclass method
# File lib/hashie/mash.rb, line 189
def select(&blk)
  self.class.new(super(&blk))
end
shallow_merge(other_hash) click to toggle source

Performs a shallow_update on a duplicate of the current mash

# File lib/hashie/mash.rb, line 264
def shallow_merge(other_hash)
  dup.shallow_update(other_hash)
end
shallow_update(other_hash) click to toggle source

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

# File lib/hashie/mash.rb, line 270
def shallow_update(other_hash)
  other_hash.each_pair do |k, v|
    regular_writer(convert_key(k), convert_value(v, true))
  end
  self
end
slice(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 344
def slice(*keys)
  string_keys = keys.map { |key| convert_key(key) }
  self.class.new(super(*string_keys))
end
to_module(mash_method_name = :settings) click to toggle source
# File lib/hashie/mash.rb, line 81
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
transform_keys(&blk) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 349
def transform_keys(&blk)
  self.class.new(super(&blk))
end
transform_values(&blk) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 332
def transform_values(&blk)
  self.class.new(super(&blk))
end
underbang_reader(key) click to toggle source

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.

# File lib/hashie/mash.rb, line 153
def underbang_reader(key)
  ck = convert_key(key)
  if key?(ck)
    regular_reader(ck)
  else
    self.class.new
  end
end
update(*other_hashes, &blk)
Also aliased as: merge!
Alias for: deep_update
values_at(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 170
def values_at(*keys)
  super(*keys.map { |key| convert_key(key) })
end
with_accessors!() click to toggle source
# File lib/hashie/mash.rb, line 90
def with_accessors!
  extend Hashie::Extensions::Mash::DefineAccessors
end

Protected Instance Methods

method_name_and_suffix(method_name) click to toggle source
# File lib/hashie/mash.rb, line 363
def method_name_and_suffix(method_name)
  method_name = method_name.to_s
  if method_name.end_with?(*ALLOWED_SUFFIXES)
    [method_name[0..-2], method_name[-1]]
  else
    [method_name[0..-1], nil]
  end
end
method_suffix(method_name) click to toggle source
# File lib/hashie/mash.rb, line 372
def method_suffix(method_name)
  method_name = method_name.to_s
  method_name[-1] if method_name.end_with?(*ALLOWED_SUFFIXES)
end

Private Instance Methods

_deep_update(other_hash) { |key, self, value| ... } click to toggle source
# File lib/hashie/mash.rb, line 244
def _deep_update(other_hash, &blk)
  other_hash.each_pair do |k, v|
    key = convert_key(k)
    if v.is_a?(::Hash) && key?(key) && regular_reader(key).is_a?(Mash)
      custom_reader(key).deep_update(v, &blk)
    else
      value = convert_value(v, true)
      value = convert_value(yield(key, self[k], value), true) if blk && key?(k)
      custom_writer(key, value, false)
    end
  end
end
log_built_in_message(method_key) click to toggle source
# File lib/hashie/mash.rb, line 399
def log_built_in_message(method_key)
  return if self.class.disable_warnings?(method_key)

  method_information = Hashie::Utils.method_information(method(method_key))

  Hashie.logger.warn(
    'You are setting a key that conflicts with a built-in method ' \
    "#{self.class}##{method_key} #{method_information}. " \
    'This can cause unexpected behavior when accessing the key as a ' \
    'property. You can still access the key via the #[] method.'
  )
end
log_collision?(method_key) click to toggle source
# File lib/hashie/mash.rb, line 412
def log_collision?(method_key)
  return unless method_key.is_a?(String) || method_key.is_a?(Symbol)
  return unless respond_to?(method_key)

  _, suffix = method_name_and_suffix(method_key)

  (!suffix || suffix == '='.freeze) &&
    !self.class.disable_warnings?(method_key) &&
    !(regular_key?(method_key) || regular_key?(method_key.to_s))
end