diff --git a/.travis.yml b/.travis.yml index cb2de4d..c33b157 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,5 +2,6 @@ rvm: - 1.8.7 - 1.9.2 - 1.9.3 + - 2.1.2 # - rbx - jruby diff --git a/README.md b/README.md index c345027..dbb8147 100644 --- a/README.md +++ b/README.md @@ -43,6 +43,14 @@ You can make Hashr raise an `IndexError` though like this: config.foo? # => false config.foo # => raises an IndexError "Key :foo is not defined." +Or you can set a custom error class like this: + class CustomError < StandardError; ;end + + Hashr.raise_missing_keys = CustomError + config = Hashr.new + config.foo? # => false + config.foo # => raises a CustomError "Key :foo is not defined." + You can also anonymously overwrite core Hash methods like this: config = Hashr.new(:count => 3) do diff --git a/lib/hashr.rb b/lib/hashr.rb index 030facc..6db1556 100644 --- a/lib/hashr.rb +++ b/lib/hashr.rb @@ -5,6 +5,25 @@ class Hashr < Hash TEMPLATE = new + class Keys + def initialize(hashr) + @hashr, @raise_missing = hashr, hashr.class.raise_missing_keys + end + + def fetch!(name) + if !@hashr.key?(name) && @raise_missing + error_class = @raise_missing.is_a?(Class) ? @raise_missing : IndexError + raise(error_class.new("Key #{name.inspect} is not defined.").tap { |e| e.set_backtrace(caller)}) + else + @hashr[name] + end + end + + def respond_to?(method) + @raise_missing ? @hashr.key?(method) : true + end + end + class << self attr_accessor :raise_missing_keys @@ -59,11 +78,7 @@ def set(path, value, stack = []) end def respond_to?(method) - if self.class.raise_missing_keys - key?(method) - else - true - end + Keys.new(self).respond_to?(method) end def method_missing(name, *args, &block) @@ -73,8 +88,7 @@ def method_missing(name, *args, &block) when '=' self[name.to_s[0..-2].to_sym] = args.first else - raise(IndexError.new("Key #{name.inspect} is not defined.")) if !key?(name) && self.class.raise_missing_keys - self[name] + Keys.new(self).fetch!(name) end end diff --git a/test/hashr_test.rb b/test/hashr_test.rb index 5692800..3e17131 100644 --- a/test/hashr_test.rb +++ b/test/hashr_test.rb @@ -27,6 +27,11 @@ def teardown assert_raises(IndexError) { Hashr.new(:foo => 'foo').bar } end + test 'method access on a non-existing key raises a FooError when raise_missing_keys is FooError' do + Hashr.raise_missing_keys = FooError + assert_raises(FooError) { Hashr.new(:foo => 'foo').bar } + end + test 'method access on an existing nested key returns the value' do assert_equal 'bar', Hashr.new(:foo => { :bar => 'bar' }).foo.bar end diff --git a/test/test_helper.rb b/test/test_helper.rb index ceddc1d..260b057 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -2,3 +2,10 @@ require 'minitest/autorun' require 'test_declarative' require 'hashr' +require 'test_support' + +module Minitest + class Test + include TestSupport + end +end diff --git a/test/test_support.rb b/test/test_support.rb new file mode 100644 index 0000000..38e9da8 --- /dev/null +++ b/test/test_support.rb @@ -0,0 +1,4 @@ +module TestSupport + class FooError < StandardError + end +end