diff --git a/README.textile b/README.textile index 059de66..ea46755 100644 --- a/README.textile +++ b/README.textile @@ -63,7 +63,7 @@ For an extensive usage in production, see the "wukong gem.":http://github.com/mr h2. Notice -Configliere 4.x now has 100% spec coverage, more powerful commandline handling, zero required dependencies. However, it also strips out several obscure features and much magical code, which breaks said obscure features and magic-dependent code. See the "CHANGELOG.":CHANGELOG.textile for details as you upgrade. +Configliere 4.x now has 100% spec coverage, more powerful commandline handling, and minimal required dependencies. However, it also strips out several obscure features and much magical code, which breaks said obscure features and magic-dependent code. See the "CHANGELOG.":CHANGELOG.textile for details as you upgrade. h2. Design goals: diff --git a/lib/configliere.rb b/lib/configliere.rb index 4ce0515..4345228 100644 --- a/lib/configliere.rb +++ b/lib/configliere.rb @@ -1,6 +1,10 @@ require 'date' # type conversion require 'time' # type conversion require 'fileutils' # so save! can mkdir + +require 'multi_json' +require 'yaml' + require 'configliere/deep_hash' # magic hash for params require 'configliere/param' # params container require 'configliere/define' # define param behavior diff --git a/lib/configliere/commandline.rb b/lib/configliere/commandline.rb index bbd346b..7dbf60f 100644 --- a/lib/configliere/commandline.rb +++ b/lib/configliere/commandline.rb @@ -70,7 +70,7 @@ def process_argv! # -a val when arg =~ /\A-(\w)\z/ flag = find_param_for_flag($1) - unless flag then @unknown_argvs << flag ; next ; end + unless flag then @unknown_argvs << $1 ; next ; end if (not args.empty?) && (args.first !~ /\A-/) val = args.shift else @@ -80,7 +80,7 @@ def process_argv! # -a=val when arg =~ /\A-(\w)=(.*)\z/ flag, val = [find_param_for_flag($1), $2] - unless flag then @unknown_argvs << flag ; next ; end + unless flag then @unknown_argvs << $1 ; next ; end self[flag] = parse_value(val) else self.rest << arg diff --git a/lib/configliere/config_file.rb b/lib/configliere/config_file.rb index 9844291..cf2b8f3 100644 --- a/lib/configliere/config_file.rb +++ b/lib/configliere/config_file.rb @@ -48,7 +48,6 @@ def read filename, options={} end def read_yaml yaml_str, options={} - require 'yaml' new_data = YAML.load(yaml_str) || {} # Extract the :env (production/development/etc) if options[:env] @@ -58,11 +57,7 @@ def read_yaml yaml_str, options={} self end - # - # we depend on you to require some sort of JSON - # def read_json json_str, options={} - require 'multi_json' new_data = MultiJson.load(json_str) || {} # Extract the :env (production/development/etc) if options[:env] diff --git a/lib/configliere/define.rb b/lib/configliere/define.rb index 5ae0685..f1f1877 100644 --- a/lib/configliere/define.rb +++ b/lib/configliere/define.rb @@ -173,6 +173,20 @@ def method_missing meth, *args else super ; end end + # Must override respond_to_missing? whenever overriding method_missing + # + # @example + # Settings.respond_to? :foo + # #=> false + # Settings.define :foo + # Settings.respond_to? :foo + # #=> true + def respond_to_missing?(meth, include_private = false) + meth.to_s =~ /^(\w+)(=)?$/ or return super + name, setter = [$1.to_sym, $2] + has_definition?(name) ? true : super + end + end # Define is included by default diff --git a/spec/configliere/commandline_spec.rb b/spec/configliere/commandline_spec.rb index 39df8b6..c4ab15c 100644 --- a/spec/configliere/commandline_spec.rb +++ b/spec/configliere/commandline_spec.rb @@ -1,133 +1,149 @@ -require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'spec_helper' +require 'configliere/commandline' -describe "Configliere::Commandline" do - before do - @config = Configliere::Param.new :date => '11-05-1955', :cat => :hat - @config.use :commandline - end - after do - ::ARGV.replace [] - end +describe Configliere::Commandline do + + subject{ Configliere::Param.new(:date => '11-05-1955', :cat => :hat).use :commandline } + + after{ ::ARGV.replace [] } - describe "with long-format argvs" do + context "with long-format argvs" do it 'accepts --param=val pairs' do ::ARGV.replace ['--enchantment=under_sea'] - @config.resolve! - @config.should == { :enchantment => 'under_sea', :date => '11-05-1955', :cat => :hat} + subject.resolve! + subject.should == { :enchantment => 'under_sea', :date => '11-05-1955', :cat => :hat} end + it 'accepts --dotted.param.name=val pairs as deep keys' do ::ARGV.replace ['--dotted.param.name=my_val'] - @config.resolve! - @config.rest.should be_empty - @config.should == { :dotted => { :param => { :name => 'my_val' }}, :date => '11-05-1955', :cat => :hat } + subject.resolve! + subject.rest.should be_empty + subject.should == { :dotted => { :param => { :name => 'my_val' }}, :date => '11-05-1955', :cat => :hat } end + it 'NO LONGER accepts --dashed-param-name=val pairs as deep keys' do ::ARGV.replace ['--dashed-param-name=my_val'] - @config.should_receive(:warn).with("Configliere uses _underscores not dashes for params") - @config.resolve! - @config.rest.should be_empty - @config.should == { :'dashed-param-name' => 'my_val', :date => '11-05-1955', :cat => :hat } + subject.should_receive(:warn).with("Configliere uses _underscores not dashes for params") + subject.resolve! + subject.rest.should be_empty + subject.should == { :'dashed-param-name' => 'my_val', :date => '11-05-1955', :cat => :hat } end + it 'adopts only the last-seen of duplicate commandline flags' do ::ARGV.replace ['--date=A', '--date=B'] - @config.resolve! - @config.rest.should be_empty - @config.should == { :date => 'B', :cat => :hat} + subject.resolve! + subject.rest.should be_empty + subject.should == { :date => 'B', :cat => :hat} end + it 'does NOT set a bare parameter (no "=") followed by a non-param to that value' do ::ARGV.replace ['--date', '11-05-1985', '--heavy', '--power.source', 'household waste', 'go'] - @config.resolve! - @config.rest.should == ['11-05-1985', 'household waste', 'go'] - @config.should == { :date => true, :heavy => true, :power => { :source => true }, :cat => :hat } + subject.resolve! + subject.rest.should == ['11-05-1985', 'household waste', 'go'] + subject.should == { :date => true, :heavy => true, :power => { :source => true }, :cat => :hat } end + it 'sets a bare parameter (no "=") to true' do ::ARGV.replace ['--date', '--deep.param'] - @config.resolve! - @config.rest.should be_empty - @config.should == { :date => true, :deep => { :param => true }, :cat => :hat} + subject.resolve! + subject.rest.should be_empty + subject.should == { :date => true, :deep => { :param => true }, :cat => :hat} end + it 'sets an explicit blank to nil' do ::ARGV.replace ['--date=', '--deep.param='] - @config.resolve! - @config.should == { :date => nil, :deep => { :param => nil }, :cat => :hat} + subject.resolve! + subject.should == { :date => nil, :deep => { :param => nil }, :cat => :hat} end it 'captures non --param args into Settings.rest' do ::ARGV.replace ['--date', 'file1', 'file2'] - @config.resolve! - @config.should == { :date => true, :cat => :hat} - @config.rest.should == ['file1', 'file2'] + subject.resolve! + subject.should == { :date => true, :cat => :hat} + subject.rest.should == ['file1', 'file2'] end it 'stops processing args on "--"' do ::ARGV.replace ['--date=A', '--', '--date=B'] - @config.resolve! - @config.rest.should == ['--date=B'] - @config.should == { :date => 'A', :cat => :hat} + subject.resolve! + subject.rest.should == ['--date=B'] + subject.should == { :date => 'A', :cat => :hat} end it 'places undefined argvs into #unknown_argvs' do - @config.define :raven, :description => 'squawk' + subject.define :raven, :description => 'squawk' ::ARGV.replace ['--never=more', '--lenore', '--raven=ray_lewis'] - @config.resolve! - @config.unknown_argvs.should == [:never, :lenore] - @config.should == { :date => '11-05-1955', :cat => :hat, :never => 'more', :lenore => true, :raven => 'ray_lewis' } + subject.resolve! + subject.unknown_argvs.should == [:never, :lenore] + subject.should == { :date => '11-05-1955', :cat => :hat, :never => 'more', :lenore => true, :raven => 'ray_lewis' } end end - describe "with single-letter flags" do + context "with single-letter flags" do before do - @config.define :date, :flag => :d - @config.define :cat, :flag => 'c' - @config.define :process, :flag => :p + subject.define :date, :flag => :d + subject.define :cat, :flag => 'c' + subject.define :process, :flag => :p end it 'accepts them separately' do ::ARGV.replace ['-p', '-c'] - @config.resolve! - @config.rest.should == [] - @config.should == { :date => '11-05-1955', :cat => true, :process => true} + subject.resolve! + subject.rest.should == [] + subject.should == { :date => '11-05-1955', :cat => true, :process => true} end it 'accepts them as a group ("-abc")' do ::ARGV.replace ['-pc'] - @config.resolve! - @config.rest.should == [] - @config.should == { :date => '11-05-1955', :cat => true, :process => true} + subject.resolve! + subject.rest.should == [] + subject.should == { :date => '11-05-1955', :cat => true, :process => true} end it 'accepts a value with -d=new_val' do ::ARGV.replace ['-d=new_val', '-c'] - @config.resolve! - @config.rest.should == [] - @config.should == { :date => 'new_val', :cat => true } + subject.resolve! + subject.rest.should == [] + subject.should == { :date => 'new_val', :cat => true } + end + + it 'stores unknown flags with values in unknown_argvs' do + ::ARGV.replace ['-f=path/to/file'] + subject.resolve! + subject.unknown_argvs.should == ['f'] end it 'accepts a space-separated value (-d new_val)' do ::ARGV.replace ['-d', 'new_val', '-c', '-p'] - @config.resolve! - @config.rest.should == [] - @config.should == { :date => 'new_val', :cat => true, :process => true } + subject.resolve! + subject.rest.should == [] + subject.should == { :date => 'new_val', :cat => true, :process => true } end it 'accepts a space-separated value only if the next arg is not a flag' do ::ARGV.replace ['-d', 'new_val', '-c', '-p', 'vigorously'] - @config.resolve! - @config.rest.should == [] - @config.should == { :date => 'new_val', :cat => true, :process => 'vigorously' } + subject.resolve! + subject.rest.should == [] + subject.should == { :date => 'new_val', :cat => true, :process => 'vigorously' } end it 'stores unknown single-letter flags in unknown_argvs' do ::ARGV.replace ['-dcz'] - lambda{ @config.resolve! }.should_not raise_error(Configliere::Error) - @config.should == { :date => true, :cat => true } - @config.unknown_argvs.should == ['z'] + lambda{ subject.resolve! }.should_not raise_error(Configliere::Error) + subject.should == { :date => true, :cat => true } + subject.unknown_argvs.should == ['z'] + end + + it 'stores unknown single-letter flags in unknown_argvs, even when singular' do + ::ARGV.replace ['-T'] + lambda{ subject.resolve! }.should_not raise_error(Configliere::Error) + subject.unknown_argvs.should == ['T'] end end def capture_help_message stderr_output = '' - @config.should_receive(:warn){|str| stderr_output << str } + subject.should_receive(:warn){|str| stderr_output << str } begin yield fail('should exit via system exit') @@ -137,87 +153,86 @@ def capture_help_message stderr_output end - describe "the help message" do + context "the help message" do it 'displays help' do ::ARGV.replace ['--help'] - stderr_output = capture_help_message{ @config.resolve! } + stderr_output = capture_help_message{ subject.resolve! } stderr_output.should_not be_nil stderr_output.should_not be_empty - @config.help.should_not be_nil - @config.help.should_not be_empty + subject.help.should_not be_nil + subject.help.should_not be_empty end it "displays the single-letter flags" do - @config.define :cat, :flag => :c, :description => "I like single-letter commands." + subject.define :cat, :flag => :c, :description => "I like single-letter commands." ::ARGV.replace ['--help'] - stderr_output = capture_help_message{ @config.resolve! } + stderr_output = capture_help_message{ subject.resolve! } stderr_output.should match(/-c,/m) end it "displays command line options" do ::ARGV.replace ['--help'] - @config.define :logfile, :type => String, :description => "Log file name", :default => 'myapp.log', :required => false - @config.define :debug, :type => :boolean, :description => "Log debug messages to console?", :required => false - @config.define :dest_time, :type => DateTime, :description => "Arrival time", :required => true - @config.define :takes_opt, :flag => 't', :description => "Takes a single-letter flag '-t'" - @config.define :foobaz, :internal => true, :description => "You won't see me" - @config.define :password, :required => true, :encrypted => true - @config.define 'delorean.power_source', :env_var => 'POWER_SOURCE', :description => 'Delorean subsytem supplying power to the Flux Capacitor.' - @config.description = 'This is a sample script to demonstrate the help message. Notice how pretty everything lines up YAY' + subject.define :logfile, :type => String, :description => "Log file name", :default => 'myapp.log', :required => false + subject.define :debug, :type => :boolean, :description => "Log debug messages to console?", :required => false + subject.define :dest_time, :type => DateTime, :description => "Arrival time", :required => true + subject.define :takes_opt, :flag => 't', :description => "Takes a single-letter flag '-t'" + subject.define :foobaz, :internal => true, :description => "You won't see me" + subject.define :password, :required => true, :encrypted => true + subject.define 'delorean.power_source', :env_var => 'POWER_SOURCE', :description => 'Delorean subsytem supplying power to the Flux Capacitor.' + subject.description = 'This is a sample script to demonstrate the help message. Notice how pretty everything lines up YAY' - stderr_output = capture_help_message{ @config.resolve! } + stderr_output = capture_help_message{ subject.resolve! } stderr_output.should_not be_nil stderr_output.should_not be_empty - stderr_output.should =~ %r{--debug\s}s # type :boolean - stderr_output.should =~ %r{--logfile=String\s}s # type String - stderr_output.should =~ %r{--dest_time=DateTime[^\n]+\[Required\]}s # shows required params - stderr_output.should =~ %r{--password=String[^\n]+\[Encrypted\]}s # shows encrypted params - stderr_output.should =~ %r{--delorean.power_source=String\s}s # undefined type - stderr_output.should =~ %r{--password=String\s*password}s # uses name as dummy description - stderr_output.should =~ %r{-t, --takes_opt}s # single-letter flags + stderr_output.should =~ %r{--debug\s}s # type :boolean + stderr_output.should =~ %r{--logfile=String\s}s # type String + stderr_output.should =~ %r{--dest_time=DateTime[^\n]+\[Required\]}s # shows required params + stderr_output.should =~ %r{--password=String[^\n]+\[Encrypted\]}s # shows encrypted params + stderr_output.should =~ %r{--delorean.power_source=String\s}s # undefined type + stderr_output.should =~ %r{--password=String\s*password}s # uses name as dummy description + stderr_output.should =~ %r{-t, --takes_opt}s # single-letter flags - stderr_output.should =~ %r{delorean\.power_source[^\n]+Env Var: POWER_SOURCE}s # environment variable - stderr_output.should =~ %r{This is a sample script}s # extra description + stderr_output.should =~ %r{delorean\.power_source[^\n]+Env Var: POWER_SOURCE}s # environment variable + stderr_output.should =~ %r{This is a sample script}s # extra description end it 'lets me die' do stderr_output = '' - @config.should_receive(:dump_help).with("****\nhi mom\n****") - @config.should_receive(:exit).with(3) - @config.die("hi mom", 3) + subject.should_receive(:dump_help).with("****\nhi mom\n****") + subject.should_receive(:exit).with(3) + subject.die("hi mom", 3) end end - describe 'recycling a commandline' do + context 'recycling a commandline' do it 'exports dashed flags' do - @config.define :has_underbar, :type => Integer, :default => 1 - @config.define :not_here, :type => Integer - @config.define :is_truthy, :type => :boolean, :default => true - @config.define :is_falsehood, :type => :boolean, :default => false - @config.dashed_flags(:has_underbar, :not_here, :is_truthy, :is_falsehood, :date, :cat - ).should == ["--has-underbar=1", "--is-truthy", "--date=11-05-1955", "--cat=hat"] + subject.define :has_underbar, :type => Integer, :default => 1 + subject.define :missing, :type => Integer + subject.define :truthy, :type => :boolean, :default => true + subject.define :falsehood, :type => :boolean, :default => false + subject.dashed_flags(:has_underbar, :missing, :truthy).should == %w[ --has-underbar=1 --truthy ] + subject.dashed_flags(:falsehood, :date, :cat).should == %w[ --date=11-05-1955 --cat=hat ] end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end - @config.should_receive(:dummy) - @config.validate!.should equal(@config) + subject.should_receive(:dummy) + subject.validate!.should equal(subject) Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end - end diff --git a/spec/configliere/commands_spec.rb b/spec/configliere/commands_spec.rb index ae955f4..806e6f2 100644 --- a/spec/configliere/commands_spec.rb +++ b/spec/configliere/commands_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'spec_helper' require 'configliere/commands' describe Configliere::Commands do @@ -66,7 +66,7 @@ end end - describe "a complex command" do + context "a complex command" do before do subject.defaults :outer => 'val 1' subject.define_command "the_command", :description => "the command" do |cmd| @@ -90,7 +90,7 @@ end end - describe "the help message" do + context "the help message" do before do subject.define_command :run, :description => "forrest" subject.define_command :stop, :description => "hammertime" @@ -111,7 +111,7 @@ end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end subject.should_receive(:dummy) @@ -120,7 +120,7 @@ end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end subject.should_receive(:dummy) @@ -128,5 +128,4 @@ Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end - end diff --git a/spec/configliere/config_block_spec.rb b/spec/configliere/config_block_spec.rb index d49e688..09e426d 100644 --- a/spec/configliere/config_block_spec.rb +++ b/spec/configliere/config_block_spec.rb @@ -1,41 +1,39 @@ -require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'spec_helper' Configliere.use :config_block -describe "Configliere::ConfigBlock" do - before do - @config = Configliere::Param.new :normal_param => 'normal' - end +describe Configliere::ConfigBlock do + + subject{ Configliere::Param.new :normal_param => 'normal' } - describe 'resolving' do + context 'resolving' do it 'runs blocks' do - @block_watcher = 'watcher' - # @block_watcher.should_receive(:fnord).with(@config) - @block_watcher.should_receive(:fnord) - @config.finally{|arg| @block_watcher.fnord(arg) } - @config.resolve! + outside_scope = double :watcher, :fnord => true + outside_scope.should_receive(:fnord) + subject.finally{ |arg| outside_scope.fnord(arg) } + subject.resolve! end + it 'resolves blocks last' do Configliere.use :config_block, :encrypted - @config.should_receive(:resolve_types!).ordered - @config.should_receive(:resolve_finally_blocks!).ordered - @config.resolve! + subject.should_receive(:resolve_types!).ordered + subject.should_receive(:resolve_finally_blocks!).ordered + subject.resolve! end it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end - @config.should_receive(:dummy) - @config.validate!.should equal(@config) + subject.should_receive(:dummy) + subject.validate!.should equal(subject) Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end - end diff --git a/spec/configliere/config_file_spec.rb b/spec/configliere/config_file_spec.rb index ffb5e21..c6be449 100644 --- a/spec/configliere/config_file_spec.rb +++ b/spec/configliere/config_file_spec.rb @@ -1,4 +1,4 @@ -require File.expand_path('../spec_helper', File.dirname(__FILE__)) +require 'spec_helper' describe Configliere::ConfigFile do let(:default_params) { { :my_param => 'default_val', :also_a_param => true } } @@ -11,8 +11,8 @@ context '#read' do let(:file_params) { { :my_param => 'val_from_file' } } - let(:file_string) { file_params.to_yaml } - let(:file_path) { '/absolute/path.yaml' } + let(:file_string) { file_params.to_yaml } + let(:file_path) { '/absolute/path.yaml' } before{ File.stub(:open).and_return(file_string) } @@ -164,7 +164,7 @@ end end - describe '#validate!' do + context '#validate!' do around do |example| Configliere::ParamParent.class_eval{ def validate!() parent_method ; end } example.run diff --git a/spec/configliere/deep_hash_spec.rb b/spec/configliere/deep_hash_spec.rb index 60d499b..59dfa63 100644 --- a/spec/configliere/deep_hash_spec.rb +++ b/spec/configliere/deep_hash_spec.rb @@ -1,95 +1,94 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' class AwesomeHash < DeepHash ; end describe DeepHash do - before(:each) do - @deep_hash = DeepHash.new({ :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' }) - @hash = { "str_key" => "strk_val", :sym_key => "symk_val"} - @sub = AwesomeHash.new("str_key" => "strk_val", :sym_key => "symk_val") - end - describe "#initialize" do + subject{ described_class.new({ :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' }) } + + let(:test_hash) { { "str_key" => "strk_val", :sym_key => "symk_val" } } + let(:subclass_hash){ AwesomeHash.new(test_hash) } + + context "#initialize" do it 'adopts a Hash when given' do - deep_hash = DeepHash.new(@hash) - @hash.each{|k,v| deep_hash[k].should == v } - deep_hash.keys.any?{|key| key.is_a?(String) }.should be_false + deep_hash = described_class.new(test_hash) + test_hash.each{ |k,v| deep_hash[k].should == v } + deep_hash.keys.any?{ |key| key.is_a?(String) }.should be_false end - it 'converts all pure Hash values into DeepHashes if param is a Hash' do - deep_hash = DeepHash.new :sym_key => @hash - deep_hash[:sym_key].should be_an_instance_of(DeepHash) - # sanity check + it 'converts all pure Hash values into DeepHash if param is a Hash' do + deep_hash = described_class.new :sym_key => test_hash + deep_hash[:sym_key].should be_an_instance_of(described_class) deep_hash[:sym_key][:sym_key].should == "symk_val" end it 'does not convert Hash subclass values into DeepHashes' do - deep_hash = DeepHash.new :sub => @sub + deep_hash = described_class.new :sub => subclass_hash deep_hash[:sub].should be_an_instance_of(AwesomeHash) end it 'converts all value items if value is an Array' do - deep_hash = DeepHash.new :arry => { :sym_key => [@hash] } - deep_hash[:arry].should be_an_instance_of(DeepHash) - # sanity check + deep_hash = described_class.new :arry => { :sym_key => [test_hash] } + deep_hash[:arry].should be_an_instance_of(described_class) deep_hash[:arry][:sym_key].first[:sym_key].should == "symk_val" end it 'delegates to superclass constructor if param is not a Hash' do - deep_hash = DeepHash.new("dash berlin") - + deep_hash = described_class.new("dash berlin") deep_hash["unexisting key"].should == "dash berlin" end - end # describe "#initialize" + end - describe "#update" do + context "#update" do it 'converts all keys into symbols when param is a Hash' do - deep_hash = DeepHash.new(@hash) + deep_hash = described_class.new(test_hash) deep_hash.update("starry" => "night") - deep_hash.keys.any?{|key| key.is_a?(String) }.should be_false end it 'converts all Hash values into DeepHashes if param is a Hash' do - deep_hash = DeepHash.new :hash => @hash + deep_hash = DeepHash.new :hash => test_hash deep_hash.update(:hash => { :sym_key => "is buggy in Ruby 1.8.6" }) - deep_hash[:hash].should be_an_instance_of(DeepHash) end - end # describe "#update" + end - describe '#[]=' do + context '#[]=' do it 'symbolizes keys' do - @deep_hash['leaf_at_top'] = :fedora - @deep_hash['new'] = :unseen - @deep_hash.should == {:nested_1 => {:nested_2 => {:leaf_3 => "val3"}, :leaf_2 => ['arr']}, :leaf_at_top => :fedora, :new => :unseen} + subject['leaf_at_top'] = :fedora + subject['new'] = :unseen + subject.should == {:nested_1 => {:nested_2 => {:leaf_3 => "val3"}, :leaf_2 => ['arr']}, :leaf_at_top => :fedora, :new => :unseen} end + it 'deep-sets dotted vals, replacing values' do - @deep_hash['moon.man'] = :cheesy - @deep_hash[:moon][:man].should == :cheesy + subject['moon.man'] = :cheesy + subject[:moon][:man].should == :cheesy end + it 'deep-sets dotted vals, creating new values' do - @deep_hash['moon.cheese.type'] = :tilsit - @deep_hash[:moon][:cheese][:type].should == :tilsit + subject['moon.cheese.type'] = :tilsit + subject[:moon][:cheese][:type].should == :tilsit end + it 'deep-sets dotted vals, auto-vivifying intermediate hashes' do - @deep_hash['this.that.the_other'] = :fuhgeddaboudit - @deep_hash[:this][:that][:the_other].should == :fuhgeddaboudit + subject['this.that.the_other'] = :fuhgeddaboudit + subject[:this][:that][:the_other].should == :fuhgeddaboudit end + it 'converts all Hash value into DeepHash' do - deep_hash = DeepHash.new :hash => @hash + deep_hash = described_class.new :hash => test_hash deep_hash[:hash] = { :sym_key => "is buggy in Ruby 1.8.6" } - deep_hash[:hash].should be_an_instance_of(DeepHash) + deep_hash[:hash].should be_an_instance_of(described_class) end it "only accepts #to_sym'bolizable things as keys" do - lambda{ @deep_hash[{ :a => :b }] = 'hi' }.should raise_error(NoMethodError, /undefined method `to_sym'/) - lambda{ @deep_hash[Object.new] = 'hi' }.should raise_error(NoMethodError, /undefined method `to_sym'/) - lambda{ @deep_hash[:a] = 'hi' }.should_not raise_error + expect{ subject[{ :a => :b }] = 'hi' }.to raise_error(NoMethodError, /undefined method `to_sym'/) + expect{ subject[Object.new] = 'hi' }.to raise_error(NoMethodError, /undefined method `to_sym'/) + expect{ subject[:a] = 'hi' }.to_not raise_error end end - describe '#[]' do + context '#[]' do let(:orig_hash){ { :hat => :cat, :basket => :lotion, :moon => { :man => :smiling, :cheese => {:type => :tilsit} } } } let(:subject ){ Configliere::Param.new({ :hat => :cat, :basket => :lotion, :moon => { :man => :smiling, :cheese => {:type => :tilsit} } }) } @@ -101,6 +100,7 @@ class AwesomeHash < DeepHash ; end subject['moon.non'].should be_nil subject.should == orig_hash # shouldn't change from reading (specifically, shouldn't autovivify) end + it 'indexing through a non-hash will raise an error', :if => (defined?(RUBY_ENGINE) && (RUBY_ENGINE !~ /rbx/)) do err_klass = (RUBY_VERSION >= "1.9.0") ? TypeError : NoMethodError expect{ subject['hat.dog'] }.to raise_error(err_klass, /Symbol/) @@ -108,8 +108,8 @@ class AwesomeHash < DeepHash ; end end it "only accepts #to_sym'bolizable things as keys" do - lambda{ subject[{ :a => :b }] }.should raise_error(NoMethodError, /undefined method `to_sym'/) - lambda{ subject[Object.new] }.should raise_error(NoMethodError, /undefined method `to_sym'/) + expect{ subject[{ :a => :b }] }.to raise_error(NoMethodError, /undefined method `to_sym'/) + expect{ subject[Object.new] }.to raise_error(NoMethodError, /undefined method `to_sym'/) end end @@ -117,52 +117,52 @@ def arrays_should_be_equal arr1, arr2 arr1.sort_by{|s| s.to_s }.should == arr2.sort_by{|s| s.to_s } end - describe "#to_hash" do + context "#to_hash" do it 'returns instance of Hash' do - DeepHash.new(@hash).to_hash.should be_an_instance_of(Hash) + described_class.new(test_hash).to_hash.should be_an_instance_of(Hash) end it 'preserves keys' do - deep_hash = DeepHash.new(@hash) - converted = deep_hash.to_hash + deep_hash = described_class.new(test_hash) + converted = deep_hash.to_hash arrays_should_be_equal deep_hash.keys, converted.keys end it 'preserves value' do - deep_hash = DeepHash.new(@hash) + deep_hash = described_class.new(test_hash) converted = deep_hash.to_hash arrays_should_be_equal deep_hash.values, converted.values end end - describe '#compact' do + context '#compact' do it 'removes nils but not empties or falsehoods' do - DeepHash.new({ :a => nil }).compact.should == {} - DeepHash.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact.should == { :b => false, :c => {}, :d => "", :remains => true } + described_class.new({ :a => nil }).compact.should == {} + described_class.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact.should == { :b => false, :c => {}, :d => "", :remains => true } end it 'leaves original alone' do - deep_hash = DeepHash.new({ :a => nil, :remains => true }) + deep_hash = described_class.new({ :a => nil, :remains => true }) deep_hash.compact.should == { :remains => true } deep_hash.should == { :a => nil, :remains => true } end end - describe '#compact!' do + context '#compact!' do it 'removes nils but not empties or falsehoods' do - DeepHash.new({ :a => nil}).compact!.should == {} - DeepHash.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact!.should == { :b => false, :c => {}, :d => "", :remains => true } + described_class.new({ :a => nil }).compact!.should == {} + described_class.new({ :a => nil, :b => false, :c => {}, :d => "", :remains => true }).compact!.should == { :b => false, :c => {}, :d => "", :remains => true } end it 'modifies in-place' do - deep_hash = DeepHash.new({ :a => nil, :remains => true }) + deep_hash = described_class.new({ :a => nil, :remains => true }) deep_hash.compact!.should == { :remains => true } deep_hash.should == { :remains => true } end end - describe '#slice' do - let(:subject ){ Configliere::Param.new({ :a => 'x', :b => 'y', :c => 10 }) } + context '#slice' do + let(:subject){ Configliere::Param.new({ :a => 'x', :b => 'y', :c => 10 }) } it 'returns a new hash with only the given keys' do subject.slice(:a, :b).should == { :a => 'x', :b => 'y' } @@ -195,8 +195,8 @@ def arrays_should_be_equal arr1, arr2 end end - describe '#extract' do - let(:subject ){ Configliere::Param.new({ :a => 'x', :b => 'y', :c => 10 }) } + context '#extract' do + let(:subject){ Configliere::Param.new({ :a => 'x', :b => 'y', :c => 10 }) } it 'replaces the hash with only the given keys' do subject.extract!(:a, :b).should == { :a => 'x', :b => 'y' } @@ -214,32 +214,30 @@ def arrays_should_be_equal arr1, arr2 end it 'is OK when empty' do - DeepHash.new.slice!(:a, :b, :c).should == {} + described_class.new.slice!(:a, :b, :c).should == {} end it 'returns an instance of the same class' do - subject.slice(:a).should be_a(DeepHash) + subject.slice(:a).should be_a(described_class) end end - describe "#delete" do + context "#delete" do it 'converts Symbol key into String before deleting' do - deep_hash = DeepHash.new(@hash) - + deep_hash = described_class.new(test_hash) deep_hash.delete(:sym_key) deep_hash.key?("hash").should be_false end it 'works with String keys as well' do - deep_hash = DeepHash.new(@hash) - + deep_hash = described_class.new(test_hash) deep_hash.delete("str_key") deep_hash.key?("str_key").should be_false end end - describe "#fetch" do - let(:subject ){ Configliere::Param.new({ :no => :in_between }) } + context "#fetch" do + let(:subject){ Configliere::Param.new({ :no => :in_between }) } it 'converts key before fetching' do subject.fetch("no").should == :in_between @@ -250,7 +248,7 @@ def arrays_should_be_equal arr1, arr2 end end - describe "#values_at" do + context "#values_at" do let(:subject ){ Configliere::Param.new({ :no => :in_between, "str_key" => "strk_val", :sym_key => "symk_val"}) } @@ -260,293 +258,298 @@ def arrays_should_be_equal arr1, arr2 end it 'responds to #symbolize_keys, #symbolize_keys! and #stringify_keys but not #stringify_keys!' do - DeepHash.new.should respond_to(:symbolize_keys ) - DeepHash.new.should respond_to(:symbolize_keys!) - DeepHash.new.should respond_to(:stringify_keys ) - DeepHash.new.should_not respond_to(:stringify_keys!) + described_class.new.should respond_to(:symbolize_keys ) + described_class.new.should respond_to(:symbolize_keys!) + described_class.new.should respond_to(:stringify_keys ) + described_class.new.should_not respond_to(:stringify_keys!) end - describe '#symbolize_keys' do + context '#symbolize_keys' do it 'returns a dup of itself' do - deep_hash = DeepHash.new(@hash) + deep_hash = described_class.new(test_hash) deep_hash.symbolize_keys.should == deep_hash end end - describe '#symbolize_keys!' do + context '#symbolize_keys!' do it 'with bang returns the deep_hash itself' do - deep_hash = DeepHash.new(@hash) + deep_hash = described_class.new(test_hash) deep_hash.symbolize_keys!.object_id.should == deep_hash.object_id end end - describe '#stringify_keys' do + context '#stringify_keys' do it 'converts keys that are all symbols' do - @deep_hash.stringify_keys.should == + subject.stringify_keys.should == { 'nested_1' => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, 'leaf_at_top' => 'val1b' } end it 'returns a Hash, not a DeepHash' do - @deep_hash.stringify_keys.class.should == Hash - @deep_hash.stringify_keys.should_not be_a(DeepHash) + subject.stringify_keys.class.should == Hash + subject.stringify_keys.should_not be_a(DeepHash) end it 'only stringifies and hashifies the top level' do - stringified = @deep_hash.stringify_keys + stringified = subject.stringify_keys stringified.should == { 'nested_1' => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, 'leaf_at_top' => 'val1b' } stringified['nested_1'].should be_a(DeepHash) end end - describe '#assert_valid_keys' do - before do - @deep_hash = DeepHash.new({ :failure => "stuff", :funny => "business" }) - end + context '#assert_valid_keys' do + let(:subject){ DeepHash.new({ :failure => "stuff", :funny => "business" }) } it 'is true and does not raise when valid' do - @deep_hash.assert_valid_keys([ :failure, :funny ]).should be_nil - @deep_hash.assert_valid_keys(:failure, :funny).should be_nil + subject.assert_valid_keys([ :failure, :funny ]).should be_nil + subject.assert_valid_keys(:failure, :funny).should be_nil end it 'fails when invalid' do - @deep_hash[:failore] = @deep_hash.delete(:failure) - lambda{ @deep_hash.assert_valid_keys([ :failure, :funny ]) }.should raise_error(ArgumentError, "Unknown key(s): failore") - lambda{ @deep_hash.assert_valid_keys(:failure, :funny) }.should raise_error(ArgumentError, "Unknown key(s): failore") + subject[:failore] = subject.delete(:failure) + expect{ subject.assert_valid_keys([ :failure, :funny ]) }.to raise_error(ArgumentError, "Unknown key(s): failore") + expect{ subject.assert_valid_keys(:failure, :funny) }.to raise_error(ArgumentError, "Unknown key(s): failore") end end - describe "#merge" do + context "#merge" do it 'merges given Hash' do - merged = @deep_hash.merge(:no => "in between") + merged = subject.merge(:no => "in between") merged.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' } end it 'returns a new instance' do - merged = @deep_hash.merge(:no => "in between") - merged.should_not equal(@deep_hash) + merged = subject.merge(:no => "in between") + merged.should_not equal(subject) end it 'returns instance of DeepHash' do - merged = @deep_hash.merge(:no => "in between") + merged = subject.merge(:no => "in between") merged.should be_an_instance_of(DeepHash) merged[:no].should == "in between" merged["no"].should == "in between" end it "converts all Hash values into DeepHashes" do - merged = @deep_hash.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + merged = subject.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) merged[:nested_1].should be_an_instance_of(DeepHash) merged[:nested_1][:nested_2].should be_an_instance_of(DeepHash) merged[:other1].should be_an_instance_of(DeepHash) end it "converts string keys to symbol keys even if they occur deep in the given hash" do - merged = @deep_hash.merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) + merged = subject.merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) merged[:a].should == { :b => { :c => { :d => :e }}} merged[:a].should_not == { 'b' => { 'c' => { :d => :e }}} end it "DOES merge values where given hash has nil value" do - merged = @deep_hash.merge(:a => { :b => nil }, :c => nil, :leaf_3_also => nil) + merged = subject.merge(:a => { :b => nil }, :c => nil, :leaf_3_also => nil) merged[:a][:b].should be_nil merged[:c].should be_nil merged[:leaf_3_also].should be_nil end it "replaces child hashes, and does not merge them" do - merged = @deep_hash.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - merged.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } + merged = subject.merge({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + merged.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } end end - describe "#merge!" do + context "#merge!" do it 'merges given Hash' do - @deep_hash.merge!(:no => "in between") - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' } + subject.merge!(:no => "in between") + subject.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' } end it 'returns a new instance' do - @deep_hash.merge!(:no => "in between") - @deep_hash.should equal(@deep_hash) + subject.merge!(:no => "in between") + subject.should equal(subject) end it 'returns instance of DeepHash' do - @deep_hash.merge!(:no => "in between") - @deep_hash.should be_an_instance_of(DeepHash) - @deep_hash[:no].should == "in between" - @deep_hash["no"].should == "in between" + subject.merge!(:no => "in between") + subject.should be_an_instance_of(DeepHash) + subject[:no].should == "in between" + subject["no"].should == "in between" end it "converts all Hash values into DeepHashes" do - @deep_hash.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - @deep_hash[:nested_1].should be_an_instance_of(DeepHash) - @deep_hash[:nested_1][:nested_2].should be_an_instance_of(DeepHash) - @deep_hash[:other1].should be_an_instance_of(DeepHash) + subject.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + subject[:nested_1].should be_an_instance_of(DeepHash) + subject[:nested_1][:nested_2].should be_an_instance_of(DeepHash) + subject[:other1].should be_an_instance_of(DeepHash) end it "converts string keys to symbol keys even if they occur deep in the given hash" do - @deep_hash.merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) - @deep_hash[:a].should == { :b => { :c => { :d => :e }}} - @deep_hash[:a].should_not == { 'b' => { 'c' => { :d => :e }}} + subject.merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) + subject[:a].should == { :b => { :c => { :d => :e }}} + subject[:a].should_not == { 'b' => { 'c' => { :d => :e }}} end it "DOES merge values where given hash has nil value" do - @deep_hash.merge!(:a => { :b => nil }, :c => nil, :leaf_3_also => nil) - @deep_hash[:a][:b].should be_nil - @deep_hash[:c].should be_nil - @deep_hash[:leaf_3_also].should be_nil + subject.merge!(:a => { :b => nil }, :c => nil, :leaf_3_also => nil) + subject[:a][:b].should be_nil + subject[:c].should be_nil + subject[:leaf_3_also].should be_nil end it "replaces child hashes, and does not merge them" do - @deep_hash = @deep_hash.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } - @deep_hash.should_not == { :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }, :leaf_at_top => 'val1b' } + subject.merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + subject.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } + subject.should_not == { :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }, :leaf_at_top => 'val1b' } end end - describe "#reverse_merge" do + context "#reverse_merge" do it 'merges given Hash' do - @deep_hash.reverse_merge!(:no => "in between", :leaf_at_top => 'NOT_USED') - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' } + subject.reverse_merge!(:no => "in between", :leaf_at_top => 'NOT_USED') + subject.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b', :no => 'in between' } end it 'returns a new instance' do - @deep_hash.reverse_merge!(:no => "in between") - @deep_hash.should equal(@deep_hash) + subject.reverse_merge!(:no => "in between") + subject.should equal(subject) end it 'returns instance of DeepHash' do - @deep_hash.reverse_merge!(:no => "in between") - @deep_hash.should be_an_instance_of(DeepHash) - @deep_hash[:no].should == "in between" - @deep_hash["no"].should == "in between" + subject.reverse_merge!(:no => "in between") + subject.should be_an_instance_of(DeepHash) + subject[:no].should == "in between" + subject["no"].should == "in between" end it "converts all Hash values into DeepHashes" do - @deep_hash.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - @deep_hash[:nested_1].should be_an_instance_of(DeepHash) - @deep_hash[:nested_1][:nested_2].should be_an_instance_of(DeepHash) - @deep_hash[:other1].should be_an_instance_of(DeepHash) + subject.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + subject[:nested_1].should be_an_instance_of(DeepHash) + subject[:nested_1][:nested_2].should be_an_instance_of(DeepHash) + subject[:other1].should be_an_instance_of(DeepHash) end it "converts string keys to symbol keys even if they occur deep in the given hash" do - merged = @deep_hash.reverse_merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) + merged = subject.reverse_merge({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) merged[:a].should == { :b => { :c => { :d => :e }}} merged[:a].should_not == { 'b' => { 'c' => { :d => :e }}} end it "DOES merge values where given hash has nil value" do - @deep_hash.reverse_merge!(:a => { :b => nil }, :c => nil) - @deep_hash[:a][:b].should be_nil - @deep_hash[:c].should be_nil + subject.reverse_merge!(:a => { :b => nil }, :c => nil) + subject[:a][:b].should be_nil + subject[:c].should be_nil end it "replaces child hashes, and does not merge them" do - @deep_hash = @deep_hash.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } + deep_hash = subject.reverse_merge!({ :nested_1 => { 'nested_2' => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :other1 => { :other2 => "other_val2" }, :leaf_at_top => 'val1b' } end end - describe "#deep_merge!" do + context "#deep_merge!" do it "merges two subhashes when they share a key" do - @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }) - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' } + subject.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }) + subject.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => 'val1b' } end it "merges two subhashes when they share a symbolized key" do - @deep_hash.deep_merge!(:nested_1 => { "nested_2" => { "leaf_3_also" => "val3a" } }) - @deep_hash.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => "val1b" } + subject.deep_merge!(:nested_1 => { "nested_2" => { "leaf_3_also" => "val3a" } }) + subject.should == { :nested_1 => { :nested_2 => { :leaf_3_also => "val3a", :leaf_3 => "val3" }, :leaf_2 => ['arr'] }, :leaf_at_top => "val1b" } end it "preserves values in the original" do - @deep_hash.deep_merge! :other_key => "other_val" - @deep_hash[:nested_1][:leaf_2].should == ['arr'] - @deep_hash[:other_key].should == "other_val" + subject.deep_merge! :other_key => "other_val" + subject[:nested_1][:leaf_2].should == ['arr'] + subject[:other_key].should == "other_val" end it "converts all Hash values into DeepHashes" do - @deep_hash.deep_merge!({:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) - @deep_hash[:nested_1].should be_an_instance_of(DeepHash) - @deep_hash[:nested_1][:nested_2].should be_an_instance_of(DeepHash) - @deep_hash[:other1].should be_an_instance_of(DeepHash) + subject.deep_merge!({:nested_1 => { :nested_2 => { :leaf_3_also => "val3a" } }, :other1 => { "other2" => "other_val2" }}) + subject[:nested_1].should be_an_instance_of(DeepHash) + subject[:nested_1][:nested_2].should be_an_instance_of(DeepHash) + subject[:other1].should be_an_instance_of(DeepHash) end it "converts string keys to symbol keys even if they occur deep in the given hash" do - @deep_hash.deep_merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) - @deep_hash[:a].should == { :b => { :c => { :d => :e }}} - @deep_hash[:a].should_not == { 'b' => { 'c' => { :d => :e }}} + subject.deep_merge!({ 'a' => { 'b' => { 'c' => { :d => :e }}}}) + subject[:a].should == { :b => { :c => { :d => :e }}} + subject[:a].should_not == { 'b' => { 'c' => { :d => :e }}} end it "replaces values from the given hash" do - @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => "new_val3" }, :leaf_2 => { "other2" => "other_val2" }}) - @deep_hash[:nested_1][:nested_2][:leaf_3].should == 'new_val3' - @deep_hash[:nested_1][:leaf_2].should == { :other2 => "other_val2" } + subject.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => "new_val3" }, :leaf_2 => { "other2" => "other_val2" }}) + subject[:nested_1][:nested_2][:leaf_3].should == 'new_val3' + subject[:nested_1][:leaf_2].should == { :other2 => "other_val2" } end it "replaces arrays and does not append to them" do - @deep_hash.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => [] }, :leaf_2 => ['val2'] }) - @deep_hash[:nested_1][:nested_2][:leaf_3].should == [] - @deep_hash[:nested_1][:leaf_2].should == ['val2'] + subject.deep_merge!(:nested_1 => { :nested_2 => { :leaf_3 => [] }, :leaf_2 => ['val2'] }) + subject[:nested_1][:nested_2][:leaf_3].should == [] + subject[:nested_1][:leaf_2].should == ['val2'] end it "does not replaces values where given hash has nil value" do - @deep_hash.deep_merge!(:nested_1 => { :leaf_2 => nil }, :leaf_at_top => '') - @deep_hash[:nested_1][:leaf_2].should == ['arr'] - @deep_hash[:leaf_at_top].should == "" + subject.deep_merge!(:nested_1 => { :leaf_2 => nil }, :leaf_at_top => '') + subject[:nested_1][:leaf_2].should == ['arr'] + subject[:leaf_at_top].should == "" end end - - describe "#deep_set" do + context "#deep_set" do it 'should set a new value (single arg)' do - @deep_hash.deep_set :new_key, 'new_val' - @deep_hash[:new_key].should == 'new_val' + subject.deep_set :new_key, 'new_val' + subject[:new_key].should == 'new_val' end + it 'should set a new value (multiple args)' do - @deep_hash.deep_set :nested_1, :nested_2, :new_key, 'new_val' - @deep_hash[:nested_1][:nested_2][:new_key].should == 'new_val' + subject.deep_set :nested_1, :nested_2, :new_key, 'new_val' + subject[:nested_1][:nested_2][:new_key].should == 'new_val' end + it 'should replace an existing value (single arg)' do - @deep_hash.deep_set :leaf_at_top, 'new_val' - @deep_hash[:leaf_at_top].should == 'new_val' + subject.deep_set :leaf_at_top, 'new_val' + subject[:leaf_at_top].should == 'new_val' end + it 'should replace an existing value (multiple args)' do - @deep_hash.deep_set :nested_1, :nested_2, 'new_val' - @deep_hash[:nested_1][:nested_2].should == 'new_val' + subject.deep_set :nested_1, :nested_2, 'new_val' + subject[:nested_1][:nested_2].should == 'new_val' end + it 'should auto-vivify intermediate hashes' do - @deep_hash.deep_set :one, :two, :three, :four, 'new_val' - @deep_hash[:one][:two][:three][:four].should == 'new_val' + subject.deep_set :one, :two, :three, :four, 'new_val' + subject[:one][:two][:three][:four].should == 'new_val' end end - describe "#deep_delete" do + context "#deep_delete" do it 'should remove the key from the array (multiple args)' do - @deep_hash.deep_delete(:nested_1) - @deep_hash[:nested_1].should be_nil - @deep_hash.should == { :leaf_at_top => 'val1b'} + subject.deep_delete(:nested_1) + subject[:nested_1].should be_nil + subject.should == { :leaf_at_top => 'val1b'} end + it 'should remove the key from the array (multiple args)' do - @deep_hash.deep_delete(:nested_1, :nested_2, :leaf_3) - @deep_hash[:nested_1][:nested_2][:leaf_3].should be_nil - @deep_hash.should == {:leaf_at_top => "val1b", :nested_1 => {:leaf_2 => ['arr'], :nested_2 => {}}} + subject.deep_delete(:nested_1, :nested_2, :leaf_3) + subject[:nested_1][:nested_2][:leaf_3].should be_nil + subject.should == {:leaf_at_top => "val1b", :nested_1 => {:leaf_2 => ['arr'], :nested_2 => {}}} end + it 'should return the value if present (single args)' do - returned_val = @deep_hash.deep_delete(:leaf_at_top) + returned_val = subject.deep_delete(:leaf_at_top) returned_val.should == 'val1b' end + it 'should return the value if present (multiple args)' do - returned_val = @deep_hash.deep_delete(:nested_1, :nested_2, :leaf_3) + returned_val = subject.deep_delete(:nested_1, :nested_2, :leaf_3) returned_val.should == 'val3' end + it 'should return nil if the key is absent (single arg)' do - returned_val = @deep_hash.deep_delete(:nested_1, :nested_2, :missing_key) + returned_val = subject.deep_delete(:nested_1, :nested_2, :missing_key) returned_val.should be_nil end + it 'should return nil if the key is absent (multiple args)' do - returned_val = @deep_hash.deep_delete(:missing_key) + returned_val = subject.deep_delete(:missing_key) returned_val.should be_nil end end - end diff --git a/spec/configliere/define_spec.rb b/spec/configliere/define_spec.rb index 26c4390..2c1c795 100644 --- a/spec/configliere/define_spec.rb +++ b/spec/configliere/define_spec.rb @@ -1,83 +1,76 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' -describe "Configliere::Define" do - before do - @config = Configliere::Param.new :normal_param => 'normal' - end +describe Configliere::Define do + + subject{ Configliere::Param.new :normal_param => 'normal' } - describe 'defining any aspect of a param' do + context 'defining any aspect of a param' do it 'adopts values' do - @config.define :simple, :description => 'desc' - @config.definition_of(:simple).should == { :description => 'desc'} + subject.define :simple, :description => 'desc' + subject.definition_of(:simple).should == { :description => 'desc'} end it 'returns self' do - ret_val = @config.define :simple, :description => 'desc' - ret_val.should equal(@config) + ret_val = subject.define :simple, :description => 'desc' + ret_val.should equal(subject) end it 'merges new definitions' do - @config.define :described_in_steps, :description => 'desc 1' - @config.define :described_in_steps, :description => 'desc 2' - @config.definition_of(:described_in_steps).should == { :description => 'desc 2'} - @config.define :described_in_steps, :encrypted => true - @config.definition_of(:described_in_steps).should == { :encrypted => true, :description => 'desc 2'} + subject.define :described_in_steps, :description => 'desc 1' + subject.define :described_in_steps, :description => 'desc 2' + subject.definition_of(:described_in_steps).should == { :description => 'desc 2'} + subject.define :described_in_steps, :encrypted => true + subject.definition_of(:described_in_steps).should == { :encrypted => true, :description => 'desc 2'} end it 'lists params defined as the given aspect' do - @config.define :has_description, :description => 'desc 1' - @config.define :also_has_description, :description => 'desc 2' - @config.define :no_description, :something_else => 'foo' - @config.params_with(:description).should include(:has_description) - @config.params_with(:description).should include(:also_has_description) - @config.params_with(:description).should_not include(:no_description) + subject.define :has_description, :description => 'desc 1' + subject.define :also_has_description, :description => 'desc 2' + subject.define :no_description, :something_else => 'foo' + subject.params_with(:description).should include(:has_description) + subject.params_with(:description).should include(:also_has_description) + subject.params_with(:description).should_not include(:no_description) end end - describe 'definition_of' do + context 'definition_of' do it 'with a param, gives me the description hash' do - @config.define :has_description, :description => 'desc 1' - @config.definition_of(:has_description).should == { :description => 'desc 1' } + subject.define :has_description, :description => 'desc 1' + subject.definition_of(:has_description).should == { :description => 'desc 1' } end + it 'with a param and attr, gives me the description hash' do - @config.define :has_description, :description => 'desc 1' - @config.definition_of(:has_description, :description).should == 'desc 1' + subject.define :has_description, :description => 'desc 1' + subject.definition_of(:has_description, :description).should == 'desc 1' end + it 'symbolizes the param' do - @config.define :has_description, :description => 'desc 1' - @config.definition_of('has_description').should == { :description => 'desc 1' } - @config.definition_of('has_description', 'description').should be_nil + subject.define :has_description, :description => 'desc 1' + subject.definition_of('has_description').should == { :description => 'desc 1' } + subject.definition_of('has_description', 'description').should be_nil end end - describe 'has_definition?' do + context 'has_definition?' do before do - @config.define :i_am_defined, :description => 'desc 1' - end - it 'is true if defined (one arg)' do - @config.has_definition?(:i_am_defined).should == true - end - it 'is false if not defined (one arg)' do - @config.has_definition?(:i_am_not_defined).should == false - end - it 'is true if defined and attribute is defined' do - @config.has_definition?(:i_am_defined, :description).should == true - end - it 'is false if defined and attribute is defined' do - @config.has_definition?(:i_am_defined, :zoink).should == false - end - it 'is false if not defined and attribute is given' do - @config.has_definition?(:i_am_not_defined, :zoink).should == false + subject.define :i_am_defined, :description => 'desc 1' end + + it{ should have_definition(:i_am_defined) } + it{ should_not have_definition(:i_am_not_defined) } + it{ should have_definition(:i_am_defined, :description) } + it{ should_not have_definition(:i_am_defined, :zoink) } + it{ should_not have_definition(:i_am_not_defined, :zoink) } + end it 'takes a description' do - @config.define :has_description, :description => 'desc 1' - @config.define :also_has_description, :description => 'desc 2' - @config.definition_of(:has_description, :description).should == 'desc 1' + subject.define :has_description, :description => 'desc 1' + subject.define :also_has_description, :description => 'desc 2' + subject.definition_of(:has_description, :description).should == 'desc 1' end - describe 'type coercion' do + context 'type coercion' do [ [:boolean, '0', false], [:boolean, 0, false], [:boolean, '', false], [:boolean, [], true], [:boolean, nil, nil], [:boolean, '1', true], [:boolean, 1, true], [:boolean, '5', true], [:boolean, 'true', true], @@ -94,119 +87,136 @@ [Array, nil, nil ], ].each do |type, orig, desired| it "for #{type} converts #{orig.inspect} to #{desired.inspect}" do - @config.define :has_type, :type => type - @config[:has_type] = orig ; @config.resolve! ; @config[:has_type].should == desired + subject.define :has_type, :type => type + subject[:has_type] = orig ; subject.resolve! ; subject[:has_type].should == desired end end it 'raises an error (FIXME: on resolve, which is not that great) if you define an unknown type' do - @config.define :has_type, :type => 'bogus, man' - @config[:has_type] = "WHOA" ; - expect{ @config.resolve! }.to raise_error(ArgumentError, /Unknown type.*bogus, man/) + subject.define :has_type, :type => 'bogus, man' + subject[:has_type] = "WHOA" ; + expect{ subject.resolve! }.to raise_error(ArgumentError, /Unknown type.*bogus, man/) end it 'converts :now to the current moment' do - @config.define :has_type, :type => DateTime - @config[:has_type] = 'now' ; @config.resolve! ; @config[:has_type].should be_within(4).of(DateTime.now) - @config[:has_type] = :now ; @config.resolve! ; @config[:has_type].should be_within(4).of(DateTime.now) - @config.define :has_type, :type => Date - @config[:has_type] = :now ; @config.resolve! ; @config[:has_type].should be_within(4).of(Date.today) - @config[:has_type] = 'now' ; @config.resolve! ; @config[:has_type].should be_within(4).of(Date.today) + subject.define :has_type, :type => DateTime + subject[:has_type] = 'now' ; subject.resolve! ; subject[:has_type].should be_within(4).of(DateTime.now) + subject[:has_type] = :now ; subject.resolve! ; subject[:has_type].should be_within(4).of(DateTime.now) + subject.define :has_type, :type => Date + subject[:has_type] = :now ; subject.resolve! ; subject[:has_type].should be_within(4).of(Date.today) + subject[:has_type] = 'now' ; subject.resolve! ; subject[:has_type].should be_within(4).of(Date.today) end end - describe 'creates magical methods' do + context 'creates magical methods' do before do - @config.define :has_magic_method, :default => 'val1' - @config[:no_magic_method] = 'val2' + subject.define :has_magic_method, :default => 'val1' + subject[:no_magic_method] = 'val2' end + it 'answers to a getter if the param is defined' do - @config.has_magic_method.should == 'val1' + subject.has_magic_method.should == 'val1' end + it 'answers to a setter if the param is defined' do - @config.has_magic_method = 'new_val1' - @config.has_magic_method.should == 'new_val1' - @config[:has_magic_method].should == 'new_val1' + subject.has_magic_method = 'new_val1' + subject.has_magic_method.should == 'new_val1' + subject[:has_magic_method].should == 'new_val1' end + + it 'answers respond_to? correctly if the param is defined' do + subject.should respond_to(:has_magic_method) + end + it 'does not answer to a getter if the param is not defined' do - lambda{ @config.no_magic_method }.should raise_error(NoMethodError) + expect{ subject.no_magic_method }.to raise_error(NoMethodError) end + it 'does not answer to a setter if the param is not defined' do - lambda{ @config.no_magic_method = 3 }.should raise_error(NoMethodError) + expect{ subject.no_magic_method = 3 }.to raise_error(NoMethodError) + end + + it 'does not answer to respond_to? if the param is not defined' do + subject.should_not respond_to(:no_magic_method) end end - describe 'defining requireds' do + context 'defining requireds' do before do - @config.define :param_1, :required => true - @config.define :param_2, :required => true - @config.define :optional_1, :required => false - @config.define :optional_2 + subject.define :param_1, :required => true + subject.define :param_2, :required => true + subject.define :optional_1, :required => false + subject.define :optional_2 end + it 'lists required params' do - @config.params_with(:required).should include(:param_1) - @config.params_with(:required).should include(:param_2) + subject.params_with(:required).should include(:param_1) + subject.params_with(:required).should include(:param_2) end + it 'counts false values as present' do - @config.defaults :param_1 => true, :param_2 => false - @config.validate!.should equal(@config) + subject.defaults :param_1 => true, :param_2 => false + subject.validate!.should equal(subject) end + it 'counts nil-but-set values as missing' do - @config.defaults :param_1 => true, :param_2 => nil - lambda{ @config.validate! }.should raise_error(RuntimeError) + subject.defaults :param_1 => true, :param_2 => nil + expect{ subject.validate! }.to raise_error(RuntimeError) end + it 'counts never-set values as missing' do - lambda{ @config.validate! }.should raise_error(RuntimeError) + expect{ subject.validate! }.to raise_error(RuntimeError) end + it 'lists all missing values when it raises' do - lambda{ @config.validate! }.should raise_error(RuntimeError, "Missing values for: param_1, param_2") + expect{ subject.validate! }.to raise_error(RuntimeError, "Missing values for: param_1, param_2") end end - describe 'defining deep keys' do + context 'defining deep keys' do it 'allows required params' do - @config.define 'delorean.power_supply', :required => true - @config[:'delorean.power_supply'] = 'household waste' - @config.params_with(:required).should include(:'delorean.power_supply') - @config.should == { :normal_param=>"normal", :delorean => { :power_supply => 'household waste' } } - lambda{ @config.validate! }.should_not raise_error + subject.define 'delorean.power_supply', :required => true + subject[:'delorean.power_supply'] = 'household waste' + subject.params_with(:required).should include(:'delorean.power_supply') + subject.should == { :normal_param=>"normal", :delorean => { :power_supply => 'household waste' } } + lambda{ subject.validate! }.should_not raise_error end it 'allows flags' do - @config.define 'delorean.power_supply', :flag => 'p' - @config.use :commandline + subject.define 'delorean.power_supply', :flag => 'p' + subject.use :commandline ARGV.replace ['-p', 'household waste'] - @config.params_with(:flag).should include(:'delorean.power_supply') - @config.resolve! - @config.should == { :normal_param=>"normal", :delorean => { :power_supply => 'household waste' } } + subject.params_with(:flag).should include(:'delorean.power_supply') + subject.resolve! + subject.should == { :normal_param=>"normal", :delorean => { :power_supply => 'household waste' } } ARGV.replace [] end it 'type converts' do - @config.define 'delorean.power_supply', :type => Array - @config.use :commandline + subject.define 'delorean.power_supply', :type => Array + subject.use :commandline ARGV.replace ['--delorean.power_supply=household waste,plutonium,lightning'] - @config.definition_of('delorean.power_supply', :type).should == Array - @config.resolve! - @config.should == { :normal_param=>"normal", :delorean => { :power_supply => ['household waste', 'plutonium', 'lightning'] } } + subject.definition_of('delorean.power_supply', :type).should == Array + subject.resolve! + subject.should == { :normal_param=>"normal", :delorean => { :power_supply => ['household waste', 'plutonium', 'lightning'] } } ARGV.replace [] end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end - @config.should_receive(:dummy) - @config.validate!.should equal(@config) + subject.should_receive(:dummy) + subject.validate!.should equal(subject) Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end diff --git a/spec/configliere/encrypted_spec.rb b/spec/configliere/encrypted_spec.rb index acaaaba..c21450e 100644 --- a/spec/configliere/encrypted_spec.rb +++ b/spec/configliere/encrypted_spec.rb @@ -1,35 +1,36 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' module Configliere ; module Crypter ; CIPHER_TYPE = 'aes-128-cbc' ; end ; end describe "Configliere::Encrypted", :if => check_openssl do require 'configliere/crypter' + subject{ Configliere::Param.new :secret => 'encrypt_me', :normal_param => 'normal' } + before do - @config = Configliere::Param.new :secret => 'encrypt_me', :normal_param => 'normal' - @config.use :encrypted - @config.define :secret, :encrypted => true - @config[:encrypt_pass] = 'pass' + subject.use :encrypted + subject.define :secret, :encrypted => true + subject[:encrypt_pass] = 'pass' end if Configliere::Crypter::CIPHER_TYPE == 'aes-256-cbc' - let(:encrypted_str){ "KohCTcXr1aAulopntmZ8f5Gqa7PzsBmz+R2vFGYrAeg=\n" } + let(:encrypted_str) { "KohCTcXr1aAulopntmZ8f5Gqa7PzsBmz+R2vFGYrAeg=\n" } let(:encrypted_foo_val){ "cc+Bp5jMUBHFCvPNZIfleeatB4IGaaXjVINl12HOpcs=\n" } elsif Configliere::Crypter::CIPHER_TYPE == 'aes-128-cbc' - let(:encrypted_str){ "mHse6HRTANh8JpIfIuyANQ8b2rXAf0+/3pzQnYsd8LE=\n" } + let(:encrypted_str) { "mHse6HRTANh8JpIfIuyANQ8b2rXAf0+/3pzQnYsd8LE=\n" } let(:encrypted_foo_val){ "cc+Bp5jMUBHFCvPNZIfleZYRoDmLK1LSxPkAMemhDTQ=\n" } else warn "Can't make test strings for #{Configliere::Crypter::CIPHER_TYPE} cipher" end - let(:foo_val_iv){ Base64.decode64(encrypted_foo_val)[0..15] } + let(:foo_val_iv){ Base64.decode64(encrypted_foo_val)[0..15] } - describe "Crypter" do + context Configliere::Crypter do it "encrypts" do # Force the same initialization vector as used to prepare the test value - @cipher = Configliere::Crypter.send(:new_cipher, :encrypt, 'sekrit') - Configliere::Crypter.should_receive(:new_cipher).and_return(@cipher) - @cipher.should_receive(:random_iv).and_return foo_val_iv + cipher = Configliere::Crypter.send(:new_cipher, :encrypt, 'sekrit') + Configliere::Crypter.should_receive(:new_cipher).and_return(cipher) + cipher.should_receive(:random_iv).and_return foo_val_iv # OK so do the test now. Configliere::Crypter.encrypt('foo_val', 'sekrit').should == encrypted_foo_val end @@ -39,105 +40,106 @@ module Configliere ; module Crypter ; CIPHER_TYPE = 'aes-128-cbc' ; end ; end end end - - describe 'defines encrypted params' do + context 'defines encrypted params' do it 'with :encrypted => true' do - @config.send(:encrypted_params).should include(:secret) + subject.send(:encrypted_params).should include(:secret) end + it 'but not if :encrypted => false' do - @config.define :another_param, :encrypted => false - @config.send(:encrypted_params).should_not include(:another_param) - @config.send(:encrypted_params).should include(:secret) + subject.define :another_param, :encrypted => false + subject.send(:encrypted_params).should_not include(:another_param) + subject.send(:encrypted_params).should include(:secret) end + it 'only if :encrypted is given' do - @config.send(:encrypted_params).should_not include(:missing_param) + subject.send(:encrypted_params).should_not include(:missing_param) end end - describe 'the encrypt_pass' do + context 'the encrypt_pass' do it 'will take an environment variable if any exists' do - @config[:encrypt_pass] = nil + subject[:encrypt_pass] = nil ENV.should_receive(:[]).with('ENCRYPT_PASS').at_least(:once).and_return('monkey') - @config.send(:export) - @config.send(:instance_variable_get, "@encrypt_pass").should == 'monkey' + subject.send(:export) + subject.send(:instance_variable_get, "@encrypt_pass").should == 'monkey' end + it 'will take an internal value if given, and remove it' do - @config[:encrypt_pass] = 'hello' - @config.send(:export) - @config.send(:instance_variable_get, "@encrypt_pass").should == 'hello' - @config[:encrypt_pass].should be_nil - @config.has_key?(:encrypt_pass).should_not be_true + subject[:encrypt_pass] = 'hello' + subject.send(:export) + subject.send(:instance_variable_get, "@encrypt_pass").should == 'hello' + subject[:encrypt_pass].should be_nil + subject.has_key?(:encrypt_pass).should_not be_true end end - describe 'encrypts' do + context 'encrypts' do it 'all params with :encrypted' do Configliere::Crypter.should_receive(:encrypt).with('encrypt_me', 'pass').and_return('ok_encrypted') - @config.send(:export).should == { :normal_param => 'normal', :encrypted_secret => 'ok_encrypted'} + subject.send(:export).should == { :normal_param => 'normal', :encrypted_secret => 'ok_encrypted'} end it 'fails unless encrypt_pass is set' do # create the config but don't set an encrypt_pass - @config = Configliere::Param.new :secret => 'encrypt_me', :normal_param => 'normal' - @config.use :encrypted - lambda{ @config.send(:encrypted, @config[:secret]) }.should raise_error('Missing encryption password!') + subject = Configliere::Param.new :secret => 'encrypt_me', :normal_param => 'normal' + subject.use :encrypted + expect{ subject.send(:encrypted, subject[:secret]) }.to raise_error('Missing encryption password!') end end - describe 'decrypts' do + context 'decrypts' do it 'all params marked encrypted' do - @config.delete :secret - @config.defaults :encrypted_secret => 'decrypt_me' + subject.delete :secret + subject.defaults :encrypted_secret => 'decrypt_me' Configliere::Crypter.should_receive(:decrypt).with('decrypt_me', 'pass').and_return('ok_decrypted') - @config.send(:resolve_encrypted!) - @config.should == { :normal_param => 'normal', :secret => 'ok_decrypted' } + subject.send(:resolve_encrypted!) + subject.should == { :normal_param => 'normal', :secret => 'ok_decrypted' } end end - describe 'loading a file' do + context 'loading a file' do it 'encrypts' do Configliere::Crypter.should_receive(:encrypt).and_return(encrypted_str) FileUtils.stub(:mkdir_p) File.should_receive(:open).and_yield([]) YAML.should_receive(:dump).with({ :normal_param => "normal", :encrypted_secret => encrypted_str }) - @config.save! '/fake/file' + subject.save! '/fake/file' end + it 'decrypts' do - # encrypted_str = Configliere::Crypter.encrypt('decrypt_me', 'pass') - @hsh = { :loaded_param => "loaded", :encrypted_secret => encrypted_str } + hsh = { :loaded_param => "loaded", :encrypted_secret => encrypted_str } File.stub(:open) - YAML.should_receive(:load).and_return(@hsh) - @config.read 'file.yaml' - @config.resolve! - @config.should_not include(:encrypted_secret) - @config.should == { :loaded_param => "loaded", :secret => 'decrypt_me', :normal_param => 'normal' } + YAML.should_receive(:load).and_return(hsh) + subject.read 'file.yaml' + subject.resolve! + subject.should_not include(:encrypted_secret) + subject.should == { :loaded_param => "loaded", :secret => 'decrypt_me', :normal_param => 'normal' } end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end it 'removes the encrypt_pass from sight' do - @config[:encrypt_pass] = 'hello' - @config.resolve! - @config.send(:instance_variable_get, "@encrypt_pass").should == 'hello' - @config[:encrypt_pass].should be_nil - @config.has_key?(:encrypt_pass).should_not be_true + subject[:encrypt_pass] = 'hello' + subject.resolve! + subject.send(:instance_variable_get, "@encrypt_pass").should == 'hello' + subject[:encrypt_pass].should be_nil + subject.has_key?(:encrypt_pass).should_not be_true end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end - @config.should_receive(:dummy) - @config.validate!.should equal(@config) + subject.should_receive(:dummy) + subject.validate!.should equal(subject) Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end - end diff --git a/spec/configliere/env_var_spec.rb b/spec/configliere/env_var_spec.rb index 4f85c16..fc3631d 100644 --- a/spec/configliere/env_var_spec.rb +++ b/spec/configliere/env_var_spec.rb @@ -1,53 +1,50 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' +require 'configliere/env_var' -describe "Configliere::EnvVar" do - before do - @config = Configliere::Param.new - @config.use :env_var - end +describe Configliere::EnvVar do + + subject{ Configliere::Param.new.use :env_var } - describe 'environment variables can be defined' do + context 'environment variables can be defined' do it 'with #env_vars, a simple value like "HOME" uses the corresponding key :home' do ENV.should_receive(:[]).with('HOME').and_return('/fake/path') - @config.env_vars 'HOME' - @config[:home].should == '/fake/path' + subject.env_vars 'HOME' + subject[:home].should == '/fake/path' end it 'with #env_vars, a hash pairs environment variables into the individual params' do ENV.should_receive(:[]).with('HOME').and_return('/fake/path') ENV.should_receive(:[]).with('POWER_SUPPLY').and_return('1.21 jigawatts') - @config.env_vars :home => 'HOME', 'delorean.power_supply' => 'POWER_SUPPLY' - @config[:home].should == '/fake/path' - @config[:delorean][:power_supply].should == '1.21 jigawatts' + subject.env_vars :home => 'HOME', 'delorean.power_supply' => 'POWER_SUPPLY' + subject[:home].should == '/fake/path' + subject[:delorean][:power_supply].should == '1.21 jigawatts' end it 'with #define' do ENV.should_receive(:[]).with('HOME').and_return('/fake/path') ENV.should_receive(:[]).with('POWER_SUPPLY').and_return('1.21 jigawatts') - @config.define :home, :env_var => 'HOME' - @config.define 'delorean.power_supply', :env_var => 'POWER_SUPPLY' - @config[:home].should == '/fake/path' - @config[:delorean][:power_supply].should == '1.21 jigawatts' + subject.define :home, :env_var => 'HOME' + subject.define 'delorean.power_supply', :env_var => 'POWER_SUPPLY' + subject[:home].should == '/fake/path' + subject[:delorean][:power_supply].should == '1.21 jigawatts' end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end - describe '#validate!' do + context '#validate!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def validate!() dummy ; end ; end - @config.should_receive(:dummy) - @config.validate!.should equal(@config) + subject.should_receive(:dummy) + subject.validate!.should equal(subject) Configliere::ParamParent.class_eval do def validate!() self ; end ; end end end - end - diff --git a/spec/configliere/param_spec.rb b/spec/configliere/param_spec.rb index a00b051..dd09516 100644 --- a/spec/configliere/param_spec.rb +++ b/spec/configliere/param_spec.rb @@ -1,48 +1,49 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' -describe "Configliere::Param" do - before do - @config = Configliere::Param.new :hat => :cat, :basket => :lotion, :moon => { :man => :smiling } - end +describe Configliere::Param do + + subject{ described_class.new(:hat => :cat, :basket => :lotion, :moon => { :man => :smiling }) } - describe 'calling #defaults' do + context 'calling #defaults' do it 'deep_merges new params' do - @config.defaults :basket => :tasket, :moon => { :cow => :jumping } - @config.should == { :hat => :cat, :basket => :tasket, :moon => { :man => :smiling, :cow => :jumping } } + subject.defaults :basket => :tasket, :moon => { :cow => :jumping } + subject.should == { :hat => :cat, :basket => :tasket, :moon => { :man => :smiling, :cow => :jumping } } end + it 'returns self, to allow chaining' do - obj = @config.defaults(:basket => :ball) - obj.should equal(@config) + return_value = subject.defaults(:basket => :ball) + return_value.should equal(subject) end end - describe 'adding plugins with #use' do - before do - Configliere.should_receive(:use).with(:foobar) - end + context 'adding plugins with #use' do it 'requires the corresponding library' do - obj = @config.use(:foobar) + Configliere.should_receive(:use).with(:foobar) + subject.use(:foobar) end + it 'returns self, to allow chaining' do - obj = @config.use(:foobar) - obj.should equal(@config) + Configliere.should_receive(:use).with(:foobar) + return_value = subject.use(:foobar) + return_value.should equal(subject) end + it 'invokes the on_use handler' do - Configliere::Param.on_use(:foobar) do + Configliere.should_receive(:use).with(:foobar) + described_class.on_use(:foobar) do method_on_config(:param) end - @config.should_receive(:method_on_config).with(:param) - @config.use(:foobar) + subject.should_receive(:method_on_config).with(:param) + subject.use(:foobar) end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end - end diff --git a/spec/configliere/prompt_spec.rb b/spec/configliere/prompt_spec.rb index 5926c20..cbdd509 100644 --- a/spec/configliere/prompt_spec.rb +++ b/spec/configliere/prompt_spec.rb @@ -1,49 +1,54 @@ -require File.expand_path(File.dirname(__FILE__) + '/../spec_helper') +require 'spec_helper' # Highline does not work with JRuby 1.7.0+ as of Mid 2012. See https://github.com/JEG2/highline/issues/41. describe "Configliere::Prompt", :if => load_sketchy_lib('highline/import') do - before do - @config = Configliere::Param.new - @config.use :prompt - @config.define :underpants, :description => 'boxers or briefs' + + subject{ Configliere::Param.new.use :prompt } + + before do + subject.define :underpants, :description => 'boxers or briefs' end - describe 'when the value is already set, #prompt_for' do + context 'when the value is already set, #prompt_for' do it 'returns the value' do - @config[:underpants] = :boxers - @config.prompt_for(:underpants).should == :boxers + subject[:underpants] = :boxers + subject.prompt_for(:underpants).should == :boxers end + it 'returns the value even if nil' do - @config[:underpants] = nil - @config.prompt_for(:underpants).should == nil + subject[:underpants] = nil + subject.prompt_for(:underpants).should == nil end + it 'returns the value even if nil' do - @config[:underpants] = false - @config.prompt_for(:underpants).should == false + subject[:underpants] = false + subject.prompt_for(:underpants).should == false end end - describe 'when prompting, #prompt_for' do + context 'when prompting, #prompt_for' do it 'prompts for a value if missing' do - @config.should_receive(:ask).with("surprise_param? ") - @config.prompt_for(:surprise_param) + subject.should_receive(:ask).with("surprise_param? ") + subject.prompt_for(:surprise_param) end + it 'uses an explicit hint' do - @config.should_receive(:ask).with("underpants (wearing any)? ") - @config.prompt_for(:underpants, "wearing any") + subject.should_receive(:ask).with("underpants (wearing any)? ") + subject.prompt_for(:underpants, "wearing any") end + it 'uses the description as hint if none given' do - @config.should_receive(:ask).with("underpants (boxers or briefs)? ") - @config.prompt_for(:underpants) + subject.should_receive(:ask).with("underpants (boxers or briefs)? ") + subject.prompt_for(:underpants) end end - describe '#resolve!' do + context '#resolve!' do it 'calls super and returns self' do Configliere::ParamParent.class_eval do def resolve!() dummy ; end ; end - @config.should_receive(:dummy) - @config.resolve!.should equal(@config) + subject.should_receive(:dummy) + subject.resolve!.should equal(subject) Configliere::ParamParent.class_eval do def resolve!() self ; end ; end end end diff --git a/spec/configliere_spec.rb b/spec/configliere_spec.rb index 8d1e8da..52a651f 100644 --- a/spec/configliere_spec.rb +++ b/spec/configliere_spec.rb @@ -1,15 +1,17 @@ -require File.expand_path('spec_helper', File.dirname(__FILE__)) +require 'spec_helper' + +describe 'Configliere' do -describe "Configliere" do it 'creates a global variable Settings, for universality' do Settings.class.should == Configliere::Param end + it 'creates a global method Settings, so you can say Settings(:foo => :bar)' do Settings.should_receive(:defaults).with(:foo => :bar) Settings(:foo => :bar) end it 'requires corresponding plugins when you call use' do - lambda{ Configliere.use(:param, :foo) }.should raise_error(LoadError, /no.*load.*configliere\/foo/) + expect{ Configliere.use(:param, :foo) }.to raise_error(LoadError, /no.*load.*configliere\/foo/) end end