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 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
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 101
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 112
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 262
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 341
def compact
  self.class.new(super)
end
custom_reader(key) { |value| ... } click to toggle source

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

# 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
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 213
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 219
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 169
def delete(key)
  super(convert_key(key))
end
dig(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 329
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 198
def dup
  self.class.new(self, default, &default_proc)
end
Also aliased as: regular_dup
extractable_options?() click to toggle source

play nice with ActiveSupport Array#extract_options!

# File lib/hashie/mash.rb, line 319
def extractable_options?
  true
end
fetch(key, *args) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 165
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 148
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 180
def invert
  self.class.new(super)
end
key?(key) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 203
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 301
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 296
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 186
def reject(&blk)
  self.class.new(super(&blk))
end
replace(other_hash) click to toggle source
# File lib/hashie/mash.rb, line 280
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 286
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 324
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 192
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 267
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 273
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 347
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 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
transform_keys(&blk) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 352
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 335
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 156
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 173
def values_at(*keys)
  super(*keys.map { |key| convert_key(key) })
end
with_accessors!() click to toggle source
# File lib/hashie/mash.rb, line 91
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 359
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 368
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 247
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 395
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 408
def log_collision?(method_key)
  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