activeconfig 0.5.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,213 @@
1
+
2
+ class ActiveConfig
3
+ class HashConfig < Hash
4
+ def initialize(constructor = {})
5
+ super()
6
+ update(constructor)
7
+ end
8
+ def default(key = nil)
9
+ if key.is_a?(Symbol) && include?(key = key.to_s)
10
+ self[key]
11
+ else
12
+ super
13
+ end
14
+ end
15
+
16
+ alias_method :regular_writer, :[]= unless method_defined?(:regular_writer)
17
+ alias_method :regular_update, :update unless method_defined?(:regular_update)
18
+
19
+ # Assigns a new value to the hash:
20
+ #
21
+ # hash = HashWithIndifferentAccess.new
22
+ # hash[:key] = "value"
23
+ #
24
+ def []=(key, value)
25
+ regular_writer(convert_key(key), convert_value(value))
26
+ end
27
+
28
+ # Updates the instantized hash with values from the second:
29
+ #
30
+ # hash_1 = HashWithIndifferentAccess.new
31
+ # hash_1[:key] = "value"
32
+ #
33
+ # hash_2 = HashWithIndifferentAccess.new
34
+ # hash_2[:key] = "New Value!"
35
+ #
36
+ # hash_1.update(hash_2) # => {"key"=>"New Value!"}
37
+ #
38
+ def update(other_hash)
39
+ other_hash.each_pair { |key, value| regular_writer(convert_key(key), convert_value(value)) }
40
+ self
41
+ end
42
+
43
+ alias_method :merge!, :update
44
+
45
+ # Checks the hash for a key matching the argument passed in:
46
+ #
47
+ # hash = HashWithIndifferentAccess.new
48
+ # hash["key"] = "value"
49
+ # hash.key? :key # => true
50
+ # hash.key? "key" # => true
51
+ #
52
+ def key?(key)
53
+ super(convert_key(key))
54
+ end
55
+
56
+ alias_method :include?, :key?
57
+ alias_method :has_key?, :key?
58
+ alias_method :member?, :key?
59
+
60
+ # Fetches the value for the specified key, same as doing hash[key]
61
+ def fetch(key, *extras)
62
+ super(convert_key(key), *extras)
63
+ end
64
+
65
+ # Returns an array of the values at the specified indices:
66
+ #
67
+ # hash = HashWithIndifferentAccess.new
68
+ # hash[:a] = "x"
69
+ # hash[:b] = "y"
70
+ # hash.values_at("a", "b") # => ["x", "y"]
71
+ #
72
+ def values_at(*indices)
73
+ indices.collect {|key| self[convert_key(key)]}
74
+ end
75
+
76
+ # Returns an exact copy of the hash.
77
+ def dup
78
+ self.class.new(self)
79
+ end
80
+
81
+ # Merges the instantized and the specified hashes together, giving precedence to the values from the second hash
82
+ # Does not overwrite the existing hash.
83
+ def merge(hash)
84
+ self.dup.update(hash)
85
+ end
86
+
87
+ # Removes a specified key from the hash.
88
+ def delete(key)
89
+ super(convert_key(key))
90
+ end
91
+
92
+ # Convert to a Hash with String keys.
93
+ def to_hash
94
+ Hash.new(default).merge(self)
95
+ end
96
+
97
+
98
+ # HashWithIndifferentAccess#dup always returns HashWithIndifferentAccess!
99
+ # -- kurt 2007/10/18
100
+ def dup
101
+ self.class.new(self)
102
+ end
103
+
104
+ def self._make_indifferent_and_freeze(x)
105
+ _make_indifferent(x,:freeze => true)
106
+ end
107
+ def freeze!
108
+ return false if self.frozen?
109
+ self.each_pair do | k, v |
110
+ self[self.class.recursive_freeze(k)] = self.class.recursive_freeze(v)
111
+ end
112
+ self.freeze
113
+ self
114
+ end
115
+ def self.recursive_freeze x
116
+ return x if x.frozen?
117
+ case x
118
+ when HashConfig,Hash
119
+ x.each_pair do | k, v |
120
+ x[recursive_freeze(k)] = recursive_freeze(v)
121
+ end
122
+ when Array
123
+ x.collect! {|v|freeze(v)}
124
+ end
125
+ x.freeze
126
+ end
127
+ def self._make_indifferent(x,opts={})
128
+ return x if opts[:freeze] and x.frozen?
129
+ case x
130
+ when HashConfig
131
+ x.each_pair do | k, v |
132
+ x[k.freeze] = _make_indifferent(v,opts)
133
+ end
134
+ when Hash
135
+ x = HashConfig.new.merge(x)
136
+ x.each_pair do | k, v |
137
+ x[k.freeze] = _make_indifferent(v,opts)
138
+ end
139
+ # STDERR.puts "x = #{x.inspect}:#{x.class}"
140
+ when Array
141
+ x.collect! do | v |
142
+ _make_indifferent(v,opts)
143
+ end
144
+ end
145
+
146
+ x.freeze if opts[:freeze]
147
+ x
148
+ end
149
+
150
+ # dotted notation can now be used with arguments (useful for creating mock objects)
151
+ # in the YAML file the method name is a key (just like usual), argument(s)
152
+ # form a nested key, and the value will get returned.
153
+ #
154
+ # For example loading to variable foo a yaml file that looks like:
155
+ # customer:
156
+ # id: 12345678
157
+ # verified:
158
+ # phone: verified
159
+ # :address: info_not_available
160
+ # ? [name, employer]
161
+ # : not_verified
162
+ #
163
+ # Allows the following calls:
164
+ # foo.customer.id --> 12345678
165
+ # foo.customer.verified.phone --> verified
166
+ # foo.customer.verified("phone") --> verified
167
+ # foo.customer.verified("name", "employer") --> not_verified
168
+ # foo.customer.verified(:address) --> info_not_available
169
+ #
170
+ # Note that :address is specified as a symbol, where phone is just a string.
171
+ # Depending on what kind of parameter the method being mocked out is going
172
+ # to be called with, define in the YAML file either a string or a symbol.
173
+ # This also works inside the composite array keys.
174
+ def method_missing(method, *args)
175
+ method = method.to_s
176
+ if args.size==1 and method.to_s=~/=$/
177
+ return self[method.to_s.sub(/=$/,'')]=args[0]
178
+ end
179
+ args.inject(self[method]){|s,e|s and s[e]}
180
+ end
181
+
182
+ ##
183
+ # Why the &*#^@*^&$ isn't HashWithIndifferentAccess actually doing this?
184
+ #
185
+ def [](key)
186
+ key = key.to_s if key.kind_of?(Symbol)
187
+ super(key)
188
+ end
189
+
190
+ # HashWithIndifferentAccess#default is broken!
191
+ define_method(:default_Hash, Hash.instance_method(:default))
192
+
193
+ ##
194
+ # Allow hash.default => hash['default']
195
+ # without breaking Hash's usage of default(key)
196
+ #
197
+ @@no_key = [ :no_key ] # magically unique value.
198
+ def default(key = @@no_key)
199
+ key = key.to_s if key.is_a?(Symbol)
200
+ key == @@no_key ? self['default'] : default_Hash(key == @@no_key ? nil : key)
201
+ end
202
+
203
+ protected
204
+ def convert_key(key)
205
+ key.kind_of?(Symbol) ? key.to_s : key
206
+ end
207
+
208
+ def convert_value(value)
209
+ return value
210
+ end
211
+
212
+ end
213
+ end
@@ -0,0 +1,72 @@
1
+ unless Hash.instance_methods.include? 'weave'
2
+ class Hash
3
+ # source: http://rubyforge.org/projects/facets/
4
+ # version: 1.7.46
5
+ # license: Ruby License
6
+ # NOTE: remove this method if the Facets gem is installed.
7
+ # BUG: weave is destructive to values in the source hash that are arrays!
8
+ # (this is acceptable for our use as the basis for weave!)
9
+ # -------------------------------------------------------------
10
+ # Weaves two hashes producing a new hash. The two hashes need
11
+ # to be compatible according to the following rules for each node:
12
+ #
13
+ # <tt>
14
+ # hash, hash => hash (recursive +)
15
+ # hash, array => error
16
+ # hash, value => error
17
+ # array, hash => error
18
+ # array, array => array + array
19
+ # array, value => array << value
20
+ # value, hash => error
21
+ # value, array => array.unshift(valueB)
22
+ # valueA, valueB => valueB
23
+ # </tt>
24
+ #
25
+ # Example:
26
+ #
27
+ # # to do
28
+ #
29
+ def weave(h, dont_clobber=true)
30
+ return self unless h
31
+ raise ArgumentError, "Hash expected" unless h.kind_of?(Hash)
32
+ s = self.dup # self.clone does not remove freeze!
33
+ h.each { |k,node|
34
+ node_is_hash = node.kind_of?(Hash)
35
+ node_is_array = node.kind_of?(Array)
36
+ if s.has_key?(k)
37
+ self_node_is_hash = s[k].kind_of?(Hash)
38
+ self_node_is_array = s[k].kind_of?(Array)
39
+ if self_node_is_hash
40
+ if node_is_hash
41
+ s[k] = s[k].weave(node, dont_clobber)
42
+ elsif node_is_array
43
+ dont_clobber ? raise(ArgumentError, "{} <= [] is a tad meaningless") : s[k] = node
44
+ else
45
+ s[k] = node
46
+ end
47
+ elsif self_node_is_array
48
+ if node_is_hash
49
+ dont_clobber ? s[k] = s[k] << node : s[k] = node
50
+ elsif node_is_array
51
+ dont_clobber ? s[k] += node : s[k] = node
52
+ else
53
+ dont_clobber ? s[k] = s[k] << node : s[k] = node
54
+ end
55
+ else
56
+ if node_is_hash
57
+ s[k] = node
58
+ elsif node_is_array
59
+ dont_clobber ? s[k].unshift( node ) : s[k] = node
60
+ else
61
+ s[k] = node
62
+ end
63
+ end
64
+ else
65
+ s[k] = node
66
+ end
67
+ }
68
+ s
69
+ end
70
+ def weave!(h, dont_clobber = true) self.merge! self.weave(h, dont_clobber) end
71
+ end
72
+ end
@@ -0,0 +1,86 @@
1
+ class ActiveConfig
2
+ class HashWithHooks < HashConfig
3
+ attr_accessor :write_hooks
4
+ alias_method :regular_writer_hwh, :regular_writer unless method_defined?(:regular_writer_hwh)
5
+ def write_hooks
6
+ @write_hooks||=[]
7
+ end
8
+ def regular_writer *args
9
+ write_hooks.each{|p|p.call}
10
+ regular_writer_hwh(*args)
11
+ end
12
+ def add_write_hook func=nil,&block
13
+ self.write_hooks||=[]
14
+ self.write_hooks << func if Proc===func
15
+ self.write_hooks << block if Kernel.block_given?
16
+ end
17
+ end
18
+ class Suffixes
19
+ attr_writer :priority
20
+ attr_accessor :ac_instance
21
+ attr :symbols
22
+
23
+ def overlay= new_overlay
24
+ ac_instance._flush_cache
25
+ @symbols[:overlay]=(new_overlay.respond_to?(:upcase) ? new_overlay.upcase : new_overlay)
26
+ end
27
+ def initialize(*args)
28
+ ac_instance=args.shift
29
+ @symbols=HashWithHooks.new
30
+ @symbols[:hostname]=proc {|sym_table| ENV['ACTIVE_CONFIG_HOSTNAME'] ||
31
+ Socket.gethostname
32
+ }
33
+ @symbols[:hostname_short]=proc {|sym_table| sym_table[:hostname].call(sym_table).sub(/\..*$/, '').freeze}
34
+ @symbols[:rails_env]=proc { |sym_table| (RAILS_ENV if defined?(RAILS_ENV))||ENV['RAILS_ENV']}
35
+ @symbols[:overlay]=proc { |sym_table| ENV['ACTIVE_CONFIG_OVERLAY']}
36
+ @symbols.add_write_hook do
37
+ ac_instance.flush_cache
38
+ end
39
+ @priority=[
40
+ nil,
41
+ :rails_env,
42
+ [:rails_env,:local],
43
+ :overlay,
44
+ [:overlay,:local],
45
+ [:hostname_short],
46
+ [:hostname_short, :local],
47
+ :hostname,
48
+ [:hostname, :local],
49
+ :local,
50
+ ]
51
+ end
52
+ def method_missing method, val=nil
53
+ super if method.to_s=~/^_/
54
+ if method.to_s=~/^(.*)=$/
55
+ ac_instance._flush_cache
56
+ return @symbols[$1]=val
57
+ end
58
+ ret=@symbols[method]
59
+ if ret
60
+ return ret.call(@symbols) if ret.respond_to?(:call)
61
+ return ret
62
+ end
63
+ super
64
+ end
65
+ def for file
66
+ suffixes.map { |this_suffix| [file,*this_suffix].compact.join('_')}.compact.uniq
67
+ end
68
+ def suffixes ary=@priority
69
+ ary.map{|e|
70
+ if Array===e
71
+ t=self.suffixes(e).compact
72
+ t.size > 0 ? t.join('_') : nil
73
+ elsif @symbols[e]
74
+ method_missing(e)
75
+ else
76
+ e && e.to_s
77
+ end
78
+ }
79
+ end
80
+ def file fname
81
+ suffixes.map{|m|m="#{m}" if m
82
+ "#{fname}#{m}.yml"
83
+ }
84
+ end
85
+ end
86
+ end
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path("../../lib",__FILE__)
3
+ ENV['ACTIVE_CONFIG_PATH']=[File.expand_path("../active_config_test_collision/patha",__FILE__),File.expand_path("../active_config_test_collision/pathb",__FILE__)].join(':')
4
+
5
+
6
+ # even if a gem is installed, load cnu_config and active_config locally
7
+ dir = File.dirname __FILE__
8
+ $LOAD_PATH.unshift File.join(dir, "..", "lib")
9
+
10
+ # Configure ActiveConfig to use our test config files.
11
+ RAILS_ENV = 'development'
12
+ ENV.delete('ACTIVE_CONFIG_OVERLAY') # Avoid gb magic.
13
+
14
+ # Test environment.
15
+ require 'rubygems'
16
+
17
+ # Test target
18
+ require 'active_config'
19
+
20
+ # Test dependencies
21
+ require 'test/unit'
22
+
23
+
24
+
25
+
26
+
27
+ class ActiveConfig::TestMulti < Test::Unit::TestCase
28
+
29
+
30
+ def teardown
31
+ super
32
+ end
33
+
34
+ def test_collision
35
+ assert_raise ActiveConfig::DuplicateConfig do
36
+ ActiveConfig.new
37
+ end
38
+ end
39
+
40
+ end
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path("../../lib",__FILE__)
3
+ ENV['ACTIVE_CONFIG_PATH']=[File.expand_path("../active_config_test_multi/patha",__FILE__),File.expand_path("../active_config_test_multi/pathb",__FILE__)].join(':')
4
+
5
+ # TEST_CONFIG_BEGIN
6
+ # enabled: true
7
+ # TEST_CONFIG_END
8
+
9
+ # Test target dependencies
10
+
11
+ # even if a gem is installed, load cnu_config and active_config locally
12
+ dir = File.dirname __FILE__
13
+ $LOAD_PATH.unshift File.join(dir, "..", "lib")
14
+
15
+ # Configure ActiveConfig to use our test config files.
16
+ RAILS_ENV = 'development'
17
+ ENV.delete('ACTIVE_CONFIG_OVERLAY') # Avoid gb magic.
18
+
19
+ # Test environment.
20
+ require 'rubygems'
21
+
22
+ # Test target
23
+ require 'active_config'
24
+
25
+ # Test dependencies
26
+ require 'test/unit'
27
+ require 'fileutils' # FileUtils.touch
28
+ require 'benchmark'
29
+
30
+
31
+
32
+
33
+
34
+ class ActiveConfig::TestMulti < Test::Unit::TestCase
35
+ def active_config
36
+ @active_config||=ActiveConfig.new
37
+ end
38
+ def setup
39
+ super
40
+ begin
41
+ active_config._flush_cache
42
+ active_config._verbose = nil # default
43
+ active_config.reload(true)
44
+ active_config._reload_disabled = nil # default
45
+ active_config._reload_delay = nil # default
46
+ rescue => err
47
+ # NOTHING
48
+ end
49
+ end
50
+
51
+
52
+ def teardown
53
+ super
54
+ end
55
+
56
+ def test_multi
57
+ assert_equal "WIN", active_config.test.default
58
+ end
59
+
60
+ end