diff --git a/lib/config/options.rb b/lib/config/options.rb index 40e49f6a..fa89432c 100644 --- a/lib/config/options.rb +++ b/lib/config/options.rb @@ -14,18 +14,18 @@ def empty? marshal_dump.empty? end - def add_source!(source) + def add_source!(source, namespace = nil) # handle yaml file paths - source = (Sources::YAMLSource.new(source)) if source.is_a?(String) - source = (Sources::HashSource.new(source)) if source.is_a?(Hash) + source = (Sources::YAMLSource.new(source, namespace)) if source.is_a?(String) + source = (Sources::HashSource.new(source, namespace)) if source.is_a?(Hash) @config_sources ||= [] @config_sources << source end - def prepend_source!(source) - source = (Sources::YAMLSource.new(source)) if source.is_a?(String) - source = (Sources::HashSource.new(source)) if source.is_a?(Hash) + def prepend_source!(source, namespace = nil) + source = (Sources::YAMLSource.new(source, namespace)) if source.is_a?(String) + source = (Sources::HashSource.new(source, namespace)) if source.is_a?(Hash) @config_sources ||= [] @config_sources.unshift(source) diff --git a/lib/config/sources/hash_source.rb b/lib/config/sources/hash_source.rb index f32dda2a..2e0c6ad5 100644 --- a/lib/config/sources/hash_source.rb +++ b/lib/config/sources/hash_source.rb @@ -3,13 +3,21 @@ module Sources class HashSource attr_accessor :hash - def initialize(hash) + def initialize(hash, namespace = nil) @hash = hash + + # Make sure @namespace is an array if it exists at all + if namespace + @namespace = namespace + @namespace = [@namespace] unless @namespace.is_a?(Array) + end end # returns hash that was passed in to initialize def load - hash.is_a?(Hash) ? hash : {} + return {} unless hash.is_a?(Hash) + return hash unless @namespace + return @namespace.reverse.inject(hash) { |a, n| { n => a } } end end end diff --git a/lib/config/sources/yaml_source.rb b/lib/config/sources/yaml_source.rb index b14eb676..3d17db6e 100644 --- a/lib/config/sources/yaml_source.rb +++ b/lib/config/sources/yaml_source.rb @@ -6,15 +6,26 @@ module Sources class YAMLSource attr_accessor :path - def initialize(path) + def initialize(path, namespace = nil) @path = path.to_s + + # Make sure @namespace is an array if it exists at all + if namespace + @namespace = namespace + @namespace = [@namespace] unless @namespace.is_a?(Array) + end end # returns a config hash from the YML file def load result = YAML.load(ERB.new(IO.read(@path)).result) if @path and File.exist?(@path) - - result || {} + return {} unless result + return result unless @namespace + + # Resolves namespacing multiple layers deep + # i.e. ['layer1', 'layer2'] comes out to {'layer1' => {'layer2' => content}} + return @namespace.reverse.inject(result) { |a, n| { n => a } } + rescue Psych::SyntaxError => e raise "YAML syntax error occurred while parsing #{@path}. " \ diff --git a/spec/sources/hash_source_spec.rb b/spec/sources/hash_source_spec.rb index c6d22958..ddfe3869 100644 --- a/spec/sources/hash_source_spec.rb +++ b/spec/sources/hash_source_spec.rb @@ -9,7 +9,7 @@ module Config::Sources context "basic hash" do let(:source) do - HashSource.new( + example_hash = { "size" => 2, "section" => { @@ -17,7 +17,7 @@ module Config::Sources "servers" => [ {"name" => "yahoo.com"}, {"name" => "amazon.com"} ] } } - ) + HashSource.new(example_hash) end it "should properly read the settings" do @@ -33,6 +33,58 @@ module Config::Sources end end + context "basic hash with single namespace" do + let(:source) do + example_hash = + { + "size" => 2, + "section" => { + "size" => 3, + "servers" => [ {"name" => "yahoo.com"}, {"name" => "amazon.com"} ] + } + } + HashSource.new(example_hash, 'test_namespace') + end + + it "should properly read the settings" do + results = source.load + expect(results['test_namespace']["size"]).to eq(2) + end + + it "should properly read nested settings" do + results = source.load + expect(results['test_namespace']["section"]["size"]).to eq(3) + expect(results['test_namespace']["section"]["servers"]).to be_instance_of(Array) + expect(results['test_namespace']["section"]["servers"].size).to eq(2) + end + end + + context "basic hash with nested namespace" do + let(:source) do + example_hash = + { + "size" => 2, + "section" => { + "size" => 3, + "servers" => [ {"name" => "yahoo.com"}, {"name" => "amazon.com"} ] + } + } + HashSource.new(example_hash, ['test_namespace', 'test_layer_2']) + end + + it "should properly read the settings" do + results = source.load + expect(results['test_namespace']['test_layer_2']["size"]).to eq(2) + end + + it "should properly read nested settings" do + results = source.load + expect(results['test_namespace']['test_layer_2']["section"]["size"]).to eq(3) + expect(results['test_namespace']['test_layer_2']["section"]["servers"]).to be_instance_of(Array) + expect(results['test_namespace']['test_layer_2']["section"]["servers"].size).to eq(2) + end + end + context "parameter is not a hash" do let(:source) do HashSource.new "hello world" diff --git a/spec/sources/yaml_source_spec.rb b/spec/sources/yaml_source_spec.rb index ee93410f..e61fc1e8 100644 --- a/spec/sources/yaml_source_spec.rb +++ b/spec/sources/yaml_source_spec.rb @@ -25,6 +25,42 @@ module Config::Sources end end + context "basic yml file with single namespace" do + let(:source) do + YAMLSource.new "#{fixture_path}/development.yml", 'test_namespace' + end + + it "should properly read the settings" do + results = source.load + expect(results['test_namespace']["size"]).to eq(2) + end + + it "should properly read nested settings" do + results = source.load + expect(results['test_namespace']["section"]["size"]).to eq(3) + expect(results['test_namespace']["section"]["servers"]).to be_instance_of(Array) + expect(results['test_namespace']["section"]["servers"].size).to eq(2) + end + end + + context "basic yml file with nested namespace" do + let(:source) do + YAMLSource.new "#{fixture_path}/development.yml", ['test_namespace', 'test_layer_2'] + end + + it "should properly read the settings" do + results = source.load + expect(results['test_namespace']['test_layer_2']["size"]).to eq(2) + end + + it "should properly read nested settings" do + results = source.load + expect(results['test_namespace']['test_layer_2']["section"]["size"]).to eq(3) + expect(results['test_namespace']['test_layer_2']["section"]["servers"]).to be_instance_of(Array) + expect(results['test_namespace']['test_layer_2']["section"]["servers"].size).to eq(2) + end + end + context "yml file with erb tags" do let(:source) do YAMLSource.new "#{fixture_path}/with_erb.yml"