configliere 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +1 -3
- data/CHANGELOG.textile +19 -0
- data/README.textile +49 -15
- data/VERSION +1 -1
- data/configliere.gemspec +12 -4
- data/examples/config_block.rb +11 -0
- data/examples/encrypted_script.rb +17 -0
- data/lib/configliere/commandline.rb +23 -6
- data/lib/configliere/config_block.rb +29 -13
- data/lib/configliere/config_file.rb +3 -1
- data/lib/configliere/core_ext/hash.rb +4 -2
- data/lib/configliere/core_ext/sash.rb +169 -0
- data/lib/configliere/define.rb +20 -2
- data/lib/configliere/encrypted.rb +2 -2
- data/lib/configliere/environment.rb +5 -4
- data/lib/configliere/param.rb +57 -34
- data/spec/configliere/config_file_spec.rb +7 -0
- data/spec/configliere/core_ext/hash_spec.rb +78 -0
- data/spec/configliere/core_ext/sash_spec.rb +313 -0
- data/spec/configliere/environment_spec.rb +2 -1
- data/spec/configliere/param_spec.rb +16 -10
- metadata +12 -4
- data/lib/configliere/commandline/commands.rb +0 -30
- data/lib/configliere/commandline/options.rb +0 -4
data/lib/configliere/define.rb
CHANGED
@@ -13,13 +13,15 @@ module Configliere
|
|
13
13
|
#
|
14
14
|
def define param, definitions={}
|
15
15
|
self.param_definitions[param].merge! definitions
|
16
|
+
self.use(:environment) if definitions.include?(:encrypted)
|
17
|
+
self.use(:encrypted) if definitions.include?(:encrypted)
|
16
18
|
self[param] = definitions[:default] if definitions.include?(:default)
|
17
19
|
self.environment_variables definitions[:environment], param if definitions.include?(:environment)
|
18
20
|
end
|
19
21
|
|
20
22
|
def param_definitions
|
21
23
|
# initialize the param_definitions as an auto-vivifying hash if it's never been set
|
22
|
-
@param_definitions ||=
|
24
|
+
@param_definitions ||= Sash.new{|hsh, key| hsh[key] = Sash.new }
|
23
25
|
end
|
24
26
|
|
25
27
|
# performs type coercion
|
@@ -153,7 +155,23 @@ module Configliere
|
|
153
155
|
end
|
154
156
|
hsh
|
155
157
|
end
|
156
|
-
|
158
|
+
|
159
|
+
# Pretend that any #define'd parameter is a method
|
160
|
+
#
|
161
|
+
# @example
|
162
|
+
# Settings.define :foo
|
163
|
+
# Settings.foo = 4
|
164
|
+
# Settings.foo #=> 4
|
165
|
+
def method_missing meth, *args
|
166
|
+
meth.to_s =~ /^(\w+)(=)?$/
|
167
|
+
name, setter = [$1, $2]
|
168
|
+
super unless name && param_definitions.include?(name)
|
169
|
+
if setter && (args.size == 1)
|
170
|
+
self[$1] = args.first
|
171
|
+
elsif (!setter) && args.empty?
|
172
|
+
self[meth]
|
173
|
+
else super ; end
|
174
|
+
end
|
157
175
|
end
|
158
176
|
|
159
177
|
Param.class_eval do
|
@@ -32,7 +32,7 @@ module Configliere
|
|
32
32
|
def export
|
33
33
|
hsh = super()
|
34
34
|
encrypted_params.each do |param|
|
35
|
-
val = hsh.deep_delete(*
|
35
|
+
val = hsh.deep_delete(*convert_key(param)) or next
|
36
36
|
hsh.deep_set( *(dotted_to_encrypted_keys(param) | [encrypted(val)]) )
|
37
37
|
end
|
38
38
|
hsh
|
@@ -50,7 +50,7 @@ module Configliere
|
|
50
50
|
# dotted_to_encrypted_keys('amazon.api.key')
|
51
51
|
# #=> [:amazon, :api, :encrypted_key]
|
52
52
|
def dotted_to_encrypted_keys param
|
53
|
-
encrypted_path =
|
53
|
+
encrypted_path = convert_key(param).dup
|
54
54
|
encrypted_path[-1] = "encrypted_#{encrypted_path.last}".to_sym
|
55
55
|
encrypted_path
|
56
56
|
end
|
@@ -9,12 +9,12 @@ module Configliere
|
|
9
9
|
envs.each do |env|
|
10
10
|
case env
|
11
11
|
when Hash
|
12
|
-
env.each do |
|
13
|
-
adopt_environment_variable!
|
12
|
+
env.each do |param, env|
|
13
|
+
adopt_environment_variable! param, env
|
14
14
|
end
|
15
15
|
else
|
16
16
|
param = env.to_s.downcase.to_sym
|
17
|
-
adopt_environment_variable!
|
17
|
+
adopt_environment_variable! param, env
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -24,7 +24,8 @@ module Configliere
|
|
24
24
|
end
|
25
25
|
|
26
26
|
protected
|
27
|
-
def adopt_environment_variable!
|
27
|
+
def adopt_environment_variable! param, env
|
28
|
+
env = env.to_s
|
28
29
|
param_definitions[param][:environment] ||= env
|
29
30
|
val = ENV[env]
|
30
31
|
self[param] = val if val
|
data/lib/configliere/param.rb
CHANGED
@@ -1,22 +1,48 @@
|
|
1
|
+
require 'configliere/core_ext/sash.rb'
|
1
2
|
module Configliere
|
3
|
+
class ParamParent < ::Hash
|
4
|
+
def finally *args, &block
|
5
|
+
nil #no-op
|
6
|
+
end
|
7
|
+
# default export method: self
|
8
|
+
def export
|
9
|
+
to_hash
|
10
|
+
end
|
11
|
+
# terminate resolution chain
|
12
|
+
def resolve!
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate!
|
16
|
+
true
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
2
20
|
#
|
3
21
|
# Hash of fields to store.
|
4
22
|
#
|
5
23
|
# Any field name beginning with 'decrypted_' automatically creates a
|
6
24
|
# counterpart 'encrypted_' field using the encrypt_pass.
|
7
25
|
#
|
8
|
-
class Param < ::
|
26
|
+
class Param < Configliere::ParamParent
|
9
27
|
|
10
|
-
#
|
28
|
+
# @param constructor<Object>
|
29
|
+
# The default value for the mash. Defaults to an empty hash.
|
11
30
|
#
|
12
|
-
# @
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
31
|
+
# @details [Alternatives]
|
32
|
+
# If constructor is a Hash, a new mash will be created based on the keys of
|
33
|
+
# the hash and no default value will be set.
|
34
|
+
def initialize(constructor = {})
|
35
|
+
if constructor.is_a?(Hash)
|
36
|
+
super()
|
37
|
+
update(constructor) unless constructor.empty?
|
38
|
+
else
|
39
|
+
super(constructor)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Hash] The mash as a Hash with string keys.
|
44
|
+
def to_hash
|
45
|
+
Hash.new(default).merge(self)
|
20
46
|
end
|
21
47
|
|
22
48
|
#
|
@@ -47,53 +73,50 @@ module Configliere
|
|
47
73
|
|
48
74
|
def []= param, val
|
49
75
|
if param =~ /\./
|
50
|
-
return deep_set( *(
|
76
|
+
return deep_set( *(convert_key(param) | [val]) )
|
51
77
|
else
|
52
|
-
super param
|
78
|
+
super param, val
|
53
79
|
end
|
54
80
|
end
|
55
81
|
|
56
82
|
def [] param
|
57
83
|
if param =~ /\./
|
58
|
-
return deep_get( *
|
84
|
+
return deep_get( *convert_key(param) )
|
59
85
|
else
|
60
|
-
super param
|
86
|
+
super param
|
61
87
|
end
|
62
88
|
end
|
63
89
|
|
64
90
|
def delete param
|
65
91
|
if param =~ /\./
|
66
|
-
return deep_delete( *
|
92
|
+
return deep_delete( *convert_key(param) )
|
67
93
|
else
|
68
|
-
super param
|
94
|
+
super param
|
69
95
|
end
|
70
96
|
end
|
71
97
|
|
72
|
-
# returns an actual Hash, not a Param < Hash
|
73
|
-
def to_hash
|
74
|
-
{}.merge! self
|
75
|
-
end
|
76
|
-
|
77
98
|
def use *args
|
78
99
|
hsh = args.pop if args.last.is_a?(Hash)
|
79
100
|
Configliere.use *args
|
80
|
-
|
101
|
+
self.deep_merge!(hsh) unless hsh.nil?
|
81
102
|
end
|
82
103
|
|
104
|
+
# see Configliere::ConfigBlock#finally
|
105
|
+
def finally *args, &block
|
106
|
+
use :config_block
|
107
|
+
super
|
108
|
+
end
|
83
109
|
protected
|
84
|
-
#
|
85
|
-
#
|
86
|
-
|
87
|
-
|
110
|
+
# @param key<Object> The key to convert.
|
111
|
+
#
|
112
|
+
# @param [Object]
|
113
|
+
# The converted key. A dotted param ('moon.cheese.type') becomes
|
114
|
+
# an array of sequential keys for deep_set and deep_get
|
115
|
+
#
|
116
|
+
# @api private
|
117
|
+
def convert_key dotted
|
118
|
+
dotted.to_s.split(".").map{|key| key.to_sym }
|
88
119
|
end
|
89
120
|
|
90
|
-
# simple (no-arg) method_missing callse
|
91
|
-
def method_missing meth, *args
|
92
|
-
if args.empty? && meth.to_s =~ /^\w+$/
|
93
|
-
self[meth]
|
94
|
-
elsif args.size == 1 && meth.to_s =~ /^(\w+)=$/
|
95
|
-
self[$1] = args.first
|
96
|
-
else super(meth, *args) end
|
97
|
-
end
|
98
121
|
end
|
99
122
|
end
|
@@ -4,6 +4,7 @@ Configliere.use :config_file
|
|
4
4
|
describe "Configliere::ConfigFile" do
|
5
5
|
before do
|
6
6
|
@config = Configliere.new :my_param => 'val'
|
7
|
+
FileUtils.stub! :mkdir_p
|
7
8
|
end
|
8
9
|
|
9
10
|
describe 'loads a config file' do
|
@@ -50,6 +51,12 @@ describe "Configliere::ConfigFile" do
|
|
50
51
|
fake_file.should_receive(:<<).with("--- \n:my_param: val\n")
|
51
52
|
@config.save! '/fake/path.yaml'
|
52
53
|
end
|
54
|
+
it 'ensures the directory exists' do
|
55
|
+
fake_file = StringIO.new('', 'w')
|
56
|
+
File.stub!(:open).with('/fake/path.yaml', 'w').and_yield(fake_file)
|
57
|
+
FileUtils.should_receive(:mkdir_p).with('/fake')
|
58
|
+
@config.save! '/fake/path.yaml'
|
59
|
+
end
|
53
60
|
end
|
54
61
|
end
|
55
62
|
|
@@ -0,0 +1,78 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../spec_helper'))
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
before(:each) do
|
5
|
+
@hash = { :hsh1a => { :hsh2 => { :key3 => "val3" }, :key2 => "val2" }, :key1b => 'val1b' }
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "#deep_merge!" do
|
9
|
+
it "merges two subhashes when they share a key" do
|
10
|
+
@hash.deep_merge!(:hsh1a => { :hsh2 => { :key3a => "val3a" } })
|
11
|
+
@hash.should == { :hsh1a => { :hsh2 => { :key3a => "val3a", :key3 => "val3" }, :key2 => "val2" }, :key1b => 'val1b' }
|
12
|
+
end
|
13
|
+
it "preserves values in the original" do
|
14
|
+
@hash.deep_merge! :other_key => "other_val"
|
15
|
+
@hash[:hsh1a][:key2].should == "val2"
|
16
|
+
@hash[:other_key].should == "other_val"
|
17
|
+
end
|
18
|
+
it "replaces values from the given Hash" do
|
19
|
+
@hash.deep_merge!(:hsh1a => { :hsh2 => { :key3 => "new_val3" }, :key2 => { "other2" => "other_val2" }})
|
20
|
+
@hash[:hsh1a][:hsh2][:key3].should == 'new_val3'
|
21
|
+
@hash[:hsh1a][:key2].should == { "other2" => "other_val2" }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
|
26
|
+
describe "#deep_set" do
|
27
|
+
it 'should set a new value (single arg)' do
|
28
|
+
@hash.deep_set :new_key, 'new_val'
|
29
|
+
@hash[:new_key].should == 'new_val'
|
30
|
+
end
|
31
|
+
it 'should set a new value (multiple args)' do
|
32
|
+
@hash.deep_set :hsh1a, :hsh2, :new_key, 'new_val'
|
33
|
+
@hash[:hsh1a][:hsh2][:new_key].should == 'new_val'
|
34
|
+
end
|
35
|
+
it 'should replace an existing value (single arg)' do
|
36
|
+
@hash.deep_set :key1b, 'new_val'
|
37
|
+
@hash[:key1b].should == 'new_val'
|
38
|
+
end
|
39
|
+
it 'should replace an existing value (multiple args)' do
|
40
|
+
@hash.deep_set :hsh1a, :hsh2, 'new_val'
|
41
|
+
@hash[:hsh1a][:hsh2].should == 'new_val'
|
42
|
+
end
|
43
|
+
it 'should auto-vivify intermediate hashes' do
|
44
|
+
@hash.deep_set :one, :two, :three, :four, 'new_val'
|
45
|
+
@hash[:one][:two][:three][:four].should == 'new_val'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#deep_delete" do
|
50
|
+
it 'should remove the key from the array (multiple args)' do
|
51
|
+
@hash.deep_delete(:hsh1a)
|
52
|
+
@hash[:hsh1a].should be_nil
|
53
|
+
@hash.should == { :key1b => 'val1b'}
|
54
|
+
end
|
55
|
+
it 'should remove the key from the array (multiple args)' do
|
56
|
+
@hash.deep_delete(:hsh1a, :hsh2, :key3)
|
57
|
+
@hash[:hsh1a][:hsh2][:key3].should be_nil
|
58
|
+
@hash.should == {:key1b=>"val1b", :hsh1a=>{:key2=>"val2", :hsh2=>{}}}
|
59
|
+
end
|
60
|
+
it 'should return the value if present (single args)' do
|
61
|
+
returned_val = @hash.deep_delete(:key1b)
|
62
|
+
returned_val.should == 'val1b'
|
63
|
+
end
|
64
|
+
it 'should return the value if present (multiple args)' do
|
65
|
+
returned_val = @hash.deep_delete(:hsh1a, :hsh2, :key3)
|
66
|
+
returned_val.should == 'val3'
|
67
|
+
end
|
68
|
+
it 'should return nil if the key is absent (single arg)' do
|
69
|
+
returned_val = @hash.deep_delete(:hsh1a, :hsh2, :missing_key)
|
70
|
+
returned_val.should be_nil
|
71
|
+
end
|
72
|
+
it 'should return nil if the key is absent (multiple args)' do
|
73
|
+
returned_val = @hash.deep_delete(:missing_key)
|
74
|
+
returned_val.should be_nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,313 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../../spec_helper'))
|
2
|
+
|
3
|
+
class AwesomeHash < Hash
|
4
|
+
end
|
5
|
+
|
6
|
+
describe Sash do
|
7
|
+
before(:each) do
|
8
|
+
@hash = { "str_key" => "strk_val", :sym_key => "symk_val" }
|
9
|
+
@sub = AwesomeHash.new("str_key" => "strk_val", :sym_key => "symk_val")
|
10
|
+
end
|
11
|
+
|
12
|
+
|
13
|
+
describe "#deep_merge!" do
|
14
|
+
before do
|
15
|
+
@sash = Sash.new :hsh1 => { :hsh2 => { :key3 => "val3" }, :key2 => "val2" }
|
16
|
+
end
|
17
|
+
it "merges two subhashes when they share a key" do
|
18
|
+
@sash.deep_merge!(:hsh1 => { :hsh2 => { :key3a => "val3a" } })
|
19
|
+
@sash.should == { :hsh1 => { :hsh2 => { :key3a => "val3a", :key3 => "val3" }, :key2 => "val2" } }
|
20
|
+
end
|
21
|
+
it "merges two subhashes when they share a symbolized key" do
|
22
|
+
@sash.deep_merge!(:hsh1 => { "hsh2" => { "key3a" => "val3a" } })
|
23
|
+
@sash.should == { :hsh1 => { :hsh2 => { :key3a => "val3a", :key3 => "val3" }, :key2 => "val2" } }
|
24
|
+
end
|
25
|
+
it "preserves values in the original" do
|
26
|
+
@sash.deep_merge! :other_key => "other_val"
|
27
|
+
@sash[:other_key].should == "other_val"
|
28
|
+
@sash[:hsh1][:key2].should == "val2"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "converts all keys into symbols when param is a Hash" do
|
32
|
+
@sash.deep_merge!(:hsh1 => { "hsh2" => { "key3a" => "val3a" } })
|
33
|
+
@sash.should == { :hsh1 => { :hsh2 => { :key3a => "val3a", :key3 => "val3" }, :key2 => "val2" } }
|
34
|
+
end
|
35
|
+
it "converts all Hash values into Sashes if param is a Hash" do
|
36
|
+
@sash.deep_merge!({:hsh1 => { :hsh2 => { :key3a => "val3a" } }, :other1 => { "other2" => "other_val2" }})
|
37
|
+
@sash[:hsh1].should be_an_instance_of(Sash)
|
38
|
+
@sash[:hsh1][:hsh2].should be_an_instance_of(Sash)
|
39
|
+
@sash[:other1].should be_an_instance_of(Sash)
|
40
|
+
end
|
41
|
+
it "replaces values from the given Hash" do
|
42
|
+
@sash.deep_merge!(:hsh1 => { :hsh2 => { :key3 => "new_val3" }, :key2 => { "other2" => "other_val2" }})
|
43
|
+
@sash[:hsh1][:hsh2][:key3].should == 'new_val3'
|
44
|
+
@sash[:hsh1][:key2].should == { :other2 => "other_val2" }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#initialize" do
|
49
|
+
it 'converts all keys into symbols when param is a Hash' do
|
50
|
+
sash = Sash.new(@hash)
|
51
|
+
sash.keys.any? { |key| key.is_a?(String) }.should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'converts all pure Hash values into Sashes if param is a Hash' do
|
55
|
+
sash = Sash.new :sym_key => @hash
|
56
|
+
|
57
|
+
sash[:sym_key].should be_an_instance_of(Sash)
|
58
|
+
# sanity check
|
59
|
+
sash[:sym_key][:sym_key].should == "symk_val"
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'doesn not convert Hash subclass values into Sashes' do
|
63
|
+
sash = Sash.new :sub => @sub
|
64
|
+
sash[:sub].should be_an_instance_of(AwesomeHash)
|
65
|
+
end
|
66
|
+
|
67
|
+
it 'converts all value items if value is an Array' do
|
68
|
+
sash = Sash.new :arry => { :sym_key => [@hash] }
|
69
|
+
|
70
|
+
sash[:arry].should be_an_instance_of(Sash)
|
71
|
+
# sanity check
|
72
|
+
sash[:arry][:sym_key].first[:sym_key].should == "symk_val"
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'delegates to superclass constructor if param is not a Hash' do
|
77
|
+
sash = Sash.new("dash berlin")
|
78
|
+
|
79
|
+
sash["unexisting key"].should == "dash berlin"
|
80
|
+
end
|
81
|
+
end # describe "#initialize"
|
82
|
+
|
83
|
+
|
84
|
+
|
85
|
+
describe "#update" do
|
86
|
+
it 'converts all keys into symbols when param is a Hash' do
|
87
|
+
sash = Sash.new(@hash)
|
88
|
+
sash.update("starry" => "night")
|
89
|
+
|
90
|
+
sash.keys.any?{|key| key.is_a?(String) }.should be_false
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'converts all Hash values into Sashes if param is a Hash' do
|
94
|
+
sash = Sash.new :hash => @hash
|
95
|
+
sash.update(:hash => { :sym_key => "is buggy in Ruby 1.8.6" })
|
96
|
+
|
97
|
+
sash[:hash].should be_an_instance_of(Sash)
|
98
|
+
end
|
99
|
+
end # describe "#update"
|
100
|
+
|
101
|
+
|
102
|
+
|
103
|
+
describe "#[]=" do
|
104
|
+
it 'converts key into symbol' do
|
105
|
+
sash = Sash.new(@hash)
|
106
|
+
sash["str_key"] = { "starry" => "night" }
|
107
|
+
|
108
|
+
sash.keys.any?{|key| key.is_a?(String) }.should be_false
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'converts all Hash value into Sash' do
|
112
|
+
sash = Sash.new :hash => @hash
|
113
|
+
sash[:hash] = { :sym_key => "is buggy in Ruby 1.8.6" }
|
114
|
+
|
115
|
+
sash[:hash].should be_an_instance_of(Sash)
|
116
|
+
end
|
117
|
+
end # describe "#[]="
|
118
|
+
|
119
|
+
|
120
|
+
|
121
|
+
describe "#key?" do
|
122
|
+
before(:each) do
|
123
|
+
@sash = Sash.new(@hash)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'converts key before lookup' do
|
127
|
+
@sash.key?("str_key").should be_true
|
128
|
+
@sash.key?(:str_key).should be_true
|
129
|
+
|
130
|
+
@sash.key?("sym_key").should be_true
|
131
|
+
@sash.key?(:sym_key).should be_true
|
132
|
+
|
133
|
+
@sash.key?(:rainclouds).should be_false
|
134
|
+
@sash.key?("rainclouds").should be_false
|
135
|
+
end
|
136
|
+
|
137
|
+
it 'is aliased as include?' do
|
138
|
+
@sash.include?("str_key").should be_true
|
139
|
+
@sash.include?(:str_key).should be_true
|
140
|
+
|
141
|
+
@sash.include?("sym_key").should be_true
|
142
|
+
@sash.include?(:sym_key).should be_true
|
143
|
+
|
144
|
+
@sash.include?(:rainclouds).should be_false
|
145
|
+
@sash.include?("rainclouds").should be_false
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'is aliased as member?' do
|
149
|
+
@sash.member?("str_key").should be_true
|
150
|
+
@sash.member?(:str_key).should be_true
|
151
|
+
|
152
|
+
@sash.member?("sym_key").should be_true
|
153
|
+
@sash.member?(:sym_key).should be_true
|
154
|
+
|
155
|
+
@sash.member?(:rainclouds).should be_false
|
156
|
+
@sash.member?("rainclouds").should be_false
|
157
|
+
end
|
158
|
+
end # describe "#key?"
|
159
|
+
|
160
|
+
def arrays_should_be_equal arr1, arr2
|
161
|
+
arr1.sort_by{|s| s.to_s }.should == arr2.sort_by{|s| s.to_s }
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#dup" do
|
165
|
+
it 'returns instance of Sash' do
|
166
|
+
Sash.new(@hash).dup.should be_an_instance_of(Sash)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'preserves keys' do
|
170
|
+
sash = Sash.new(@hash)
|
171
|
+
dup = sash.dup
|
172
|
+
|
173
|
+
arrays_should_be_equal sash.keys, dup.keys
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'preserves value' do
|
177
|
+
sash = Sash.new(@hash)
|
178
|
+
dup = sash.dup
|
179
|
+
|
180
|
+
arrays_should_be_equal sash.values, dup.values
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
|
185
|
+
|
186
|
+
describe "#to_hash" do
|
187
|
+
it 'returns instance of Hash' do
|
188
|
+
Sash.new(@hash).to_hash.should be_an_instance_of(Hash)
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'preserves keys' do
|
192
|
+
sash = Sash.new(@hash)
|
193
|
+
converted = sash.to_hash
|
194
|
+
arrays_should_be_equal sash.keys, converted.keys
|
195
|
+
end
|
196
|
+
|
197
|
+
it 'preserves value' do
|
198
|
+
sash = Sash.new(@hash)
|
199
|
+
converted = sash.to_hash
|
200
|
+
arrays_should_be_equal sash.values, converted.values
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
|
205
|
+
|
206
|
+
describe "#stringify_keys" do
|
207
|
+
it 'returns instance of Sash' do
|
208
|
+
Sash.new(@hash).stringify_keys.should be_an_instance_of(Hash)
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'converts keys to symbols' do
|
212
|
+
sash = Sash.new(@hash)
|
213
|
+
converted = sash.stringify_keys
|
214
|
+
|
215
|
+
converted_keys = converted.keys.sort{|k1, k2| k1.to_s <=> k2.to_s}
|
216
|
+
orig_keys = sash.keys.map{|k| k.to_sym}.sort{|i1, i2| i1.to_s <=> i2.to_s}
|
217
|
+
|
218
|
+
converted_keys.should == orig_keys
|
219
|
+
end
|
220
|
+
|
221
|
+
it 'preserves value' do
|
222
|
+
sash = Sash.new(@hash)
|
223
|
+
converted = sash.stringify_keys
|
224
|
+
|
225
|
+
arrays_should_be_equal sash.values, converted.values
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
|
231
|
+
describe "#delete" do
|
232
|
+
it 'converts Symbol key into String before deleting' do
|
233
|
+
sash = Sash.new(@hash)
|
234
|
+
|
235
|
+
sash.delete(:sym_key)
|
236
|
+
sash.key?("hash").should be_false
|
237
|
+
end
|
238
|
+
|
239
|
+
it 'works with String keys as well' do
|
240
|
+
sash = Sash.new(@hash)
|
241
|
+
|
242
|
+
sash.delete("str_key")
|
243
|
+
sash.key?("str_key").should be_false
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
|
248
|
+
|
249
|
+
describe "#merge" do
|
250
|
+
before(:each) do
|
251
|
+
@sash = Sash.new(@hash).merge(:no => "in between")
|
252
|
+
end
|
253
|
+
|
254
|
+
it 'returns instance of Sash' do
|
255
|
+
@sash.should be_an_instance_of(Sash)
|
256
|
+
end
|
257
|
+
|
258
|
+
it 'merges in give Hash' do
|
259
|
+
@sash["no"].should == "in between"
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
|
264
|
+
|
265
|
+
describe "#fetch" do
|
266
|
+
before(:each) do
|
267
|
+
@sash = Sash.new(@hash).merge(:no => "in between")
|
268
|
+
end
|
269
|
+
|
270
|
+
it 'converts key before fetching' do
|
271
|
+
@sash.fetch("no").should == "in between"
|
272
|
+
end
|
273
|
+
|
274
|
+
it 'returns alternative value if key lookup fails' do
|
275
|
+
@sash.fetch("flying", "screwdriver").should == "screwdriver"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
|
280
|
+
describe "#default" do
|
281
|
+
before(:each) do
|
282
|
+
@sash = Sash.new(:yet_another_technical_revolution)
|
283
|
+
end
|
284
|
+
|
285
|
+
it 'returns default value unless key exists in sash' do
|
286
|
+
@sash.default("peak oil is now behind, baby").should == :yet_another_technical_revolution
|
287
|
+
end
|
288
|
+
|
289
|
+
it 'returns existing value if key is String and exists in sash' do
|
290
|
+
@sash.update("no" => "in between")
|
291
|
+
@sash.default("no").should == "in between"
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
|
296
|
+
describe "#values_at" do
|
297
|
+
before(:each) do
|
298
|
+
@sash = Sash.new(@hash).merge(:no => "in between")
|
299
|
+
end
|
300
|
+
|
301
|
+
it 'is indifferent to whether keys are strings or symbols' do
|
302
|
+
@sash.values_at("sym_key", :str_key, :no).should == ["symk_val", "strk_val", "in between"]
|
303
|
+
end
|
304
|
+
end
|
305
|
+
|
306
|
+
|
307
|
+
describe "#symbolize_keys!" do
|
308
|
+
it 'returns the sash itself' do
|
309
|
+
sash = Sash.new(@hash)
|
310
|
+
sash.symbolize_keys!.object_id.should == sash.object_id
|
311
|
+
end
|
312
|
+
end
|
313
|
+
end
|