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
disable_warnings() click to toggle source

Disable the logging of warnings based on keys conflicting keys/methods

@api semipublic @return [void]

# File lib/hashie/mash.rb, line 76
def self.disable_warnings
  raise CannotDisableMashWarnings if self == Hashie::Mash
  @disable_warnings = true
end
disable_warnings?() click to toggle source

Checks whether this class disables warnings for conflicting keys/methods

@api semipublic @return [Boolean]

# File lib/hashie/mash.rb, line 85
def self.disable_warnings?
  @disable_warnings ||= false
end
inherited(subclass) click to toggle source

Inheritance hook that sets class configuration when inherited.

@api semipublic @return [void]

Calls superclass method
# File lib/hashie/mash.rb, line 93
def self.inherited(subclass)
  super
  subclass.disable_warnings if disable_warnings?
end
load(path, options = {}) click to toggle source
# File lib/hashie/mash.rb, line 98
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)

  parser = options.fetch(:parser) { Hashie::Extensions::Parsers::YamlErbParser }
  @_mashes[path] = new(parser.perform(path)).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 123
def initialize(source_hash = nil, default = nil, &blk)
  deep_update(source_hash) if source_hash
  default ? super(default) : super(&blk)
end
Also aliased as: []

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 227
def assign_property(name, value)
  self[name] = value
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 135
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_hash, &blk) click to toggle source

Performs a #deep_update on a duplicate of the current mash.

# File lib/hashie/mash.rb, line 202
def deep_merge(other_hash, &blk)
  dup.deep_update(other_hash, &blk)
end
Also aliased as: merge
deep_merge!(other_hash, &blk)
Alias for: deep_update
deep_update(other_hash) { |key, self, value| ... } 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 209
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(yield(key, self[k], value), true) if blk && key?(k)
      custom_writer(key, value, false)
    end
  end
  self
end
Also aliased as: deep_merge!, update
delete(key) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 178
def delete(key)
  super(convert_key(key))
end
dig(*keys) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 294
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 188
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 284
def extractable_options?
  true
end
fetch(key, *args) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 174
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 157
def initializing_reader(key)
  ck = convert_key(key)
  regular_writer(ck, self.class.new) unless key?(ck)
  regular_reader(ck)
end
key?(key) click to toggle source
Calls superclass method
# File lib/hashie/mash.rb, line 193
def key?(key)
  super(convert_key(key))
end
Also aliased as: regular_key?, has_key?, include?, member?
member?(key)
Alias for: key?
merge(other_hash, &blk)
Alias for: deep_merge
merge!(other_hash, &blk)
Alias for: update
method_missing(method_name, *args, &blk) click to toggle source
# File lib/hashie/mash.rb, line 266
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 261
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: []
replace(other_hash) click to toggle source
# File lib/hashie/mash.rb, line 245
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 251
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 289
def reverse_merge(other_hash)
  self.class.new(other_hash).merge(self)
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 232
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 238
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) click to toggle source
# File lib/hashie/mash.rb, line 108
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) 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 165
def underbang_reader(key)
  ck = convert_key(key)
  if key?(ck)
    regular_reader(ck)
  else
    self.class.new
  end
end
update(other_hash, &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 182
def values_at(*keys)
  super(*keys.map { |key| convert_key(key) })
end

Protected Instance Methods

method_name_and_suffix(method_name) click to toggle source
# File lib/hashie/mash.rb, line 301
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 310
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

log_built_in_message(method_key) click to toggle source
# File lib/hashie/mash.rb, line 339
def log_built_in_message(method_key)
  return if self.class.disable_warnings?

  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 352
def log_collision?(method_key)
  respond_to?(method_key) && !self.class.disable_warnings? &&
    !(regular_key?(method_key) || regular_key?(method_key.to_s))
end