Skip to content

Instantly share code, notes, and snippets.

@Heath101
Created August 6, 2014 18:57
Show Gist options
  • Save Heath101/043bc9a5cc5296d9a063 to your computer and use it in GitHub Desktop.
Save Heath101/043bc9a5cc5296d9a063 to your computer and use it in GitHub Desktop.
Underscore Accessor
require 'minitest/autorun'
class Hash
def method_missing( symbol, *args )
if symbol.to_s.start_with?('_')
if symbol.to_s.end_with?('=')
key = symbol.to_s.sub('_', '').chop
if self.has_key?(key.to_sym)
self[key.to_sym] = args.first
elsif self.has_key?(key)
self[key] = args.first
else
self[key.to_sym] = args.first
end
else
key = symbol.to_s.sub('_', '')
if self.has_key?(key.to_sym)
self[key.to_sym]
else
self[key]
end
end
end
end
end
describe 'Underscore accessor' do
describe 'returning correct values' do
it 'returns nil for empty hash' do
assert_equal(nil, {}._foo)
end
it 'returns correct value for existing keys' do
assert_equal('bar', {foo: 'bar'}._foo)
assert_equal('bar', {'foo' => 'bar'}._foo)
assert_equal('sym', {foo: 'sym', 'foo' => 'str'}._foo)
end
end
describe 'setting keys' do
it 'returns value when setting non-existent key' do
assert_equal('bar', {}._foo = 'bar')
end
it 'sets value for non-existent key, as a symbol key' do
test = {}
test._foo = 'bar'
assert_equal('bar', test[:foo])
assert_equal(nil, test['foo'])
end
it 'sets value for symbol key' do
test = {foo: 'bar'}
test._foo = 'sym'
assert_equal('sym', test[:foo])
end
it 'sets value for a string key' do
test = {'fuu' => 'baz'}
test._fuu = 'str'
assert_equal('str', test['fuu'])
end
it 'sets only symbol value when key exists as both symbol and string' do
test = {foo: 'bar', 'foo' => 'baz'}
test._foo = 'sym'
assert_equal('sym', test[:foo])
assert_equal('baz', test['foo'])
end
end
describe 'edge cases' do
it 'retrieves value when value is false' do
assert_equal(false, {:enabled=>false, "enabled"=>true}._enabled)
end
it 'sets value when initial value is nil' do
test = {:enabled=>nil, "enabled"=>true}
test._enabled = false
assert_equal(false, test[:enabled])
end
end
end
@Heath101
Copy link
Author

class Hash
  def method_missing( symbol, *args )
    if underscore_method? symbol
        string_key = symbol.to_s[1..-1]

      if string_key.end_with?('=')
        send(:[]=, accessor_for(string_key.chop), args.first)
      else
        send(:[], accessor_for(string_key))
      end
    end
  end

  private
    def underscore_method?(sym)
        sym.to_s.start_with?('_')
    end

    def accessor_for(string_key)
    symbol_key = string_key.to_sym
    (has_key?(symbol_key) || !has_key?(string_key)) ? symbol_key : string_key
    end
end

@Heath101
Copy link
Author

class Hash
  def method_missing( method, *args )
    if starts_with_underscore? method
      send(msg_for(method), key_for(method), args.first)
    end
  end

  private
  def msg_for(method)
    method.to_s.end_with?('=') ? :store : :fetch
  end

  def key_for(method)
    key = method.to_s.sub('_','').sub('=','')
    (has_key?(key.to_sym) || !has_key?(key)) ? key.to_sym : key
  end

    def starts_with_underscore?(sym)
        sym.to_s.start_with?('_')
    end
end

@Heath101
Copy link
Author

I don't like the method name 'msg_for' because it doesn't really say what it does to method.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment