jlawler-activeconfig 0.1.4 → 0.2.0

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.
data/Rakefile ADDED
@@ -0,0 +1,54 @@
1
+ require 'rubygems'
2
+ Gem::manage_gems
3
+ require 'rake/gempackagetask'
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |s|
7
+ s.name = 'activeconfig'
8
+ s.author = 'Jeremy Lawler'
9
+ s.email = 'jlawler@cashnetusa.com'
10
+ s.homepage = 'http://jlawler.github.com/activeconfig/'
11
+ s.summary = 'An extremely flexible configuration system'
12
+ s.authors = ["Jeremy Lawler"]
13
+ end
14
+ rescue LoadError
15
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
16
+ end
17
+
18
+ task :default => :test
19
+ active_config_multi_paths=[File.expand_path(File.dirname(__FILE__) + "/test/active_config_test_multi/patha"),':',File.expand_path(File.dirname(__FILE__) + "/test/active_config_test_multi/pathb")]
20
+
21
+ task :rdoc do
22
+ sh "rm -rf #{File.dirname(__FILE__)}/doc"
23
+ sh "cd lib && rdoc -o ../doc "
24
+ end
25
+ task :test do
26
+ sh "ruby -I lib test/active_config_test.rb"
27
+ puts "\n\n"
28
+ exception1,exception2,exception3=nil,nil,nil
29
+ begin
30
+ ENV['ACTIVE_CONFIG_PATH'] = active_config_multi_paths.join('')
31
+ sh "ruby -I lib test/active_config_test_multi.rb"
32
+ rescue Object => exception1
33
+ end
34
+ puts "\n\n"
35
+ begin
36
+ ENV['ACTIVE_CONFIG_PATH'] = active_config_multi_paths.reverse.join('')
37
+ sh "ruby -I lib test/active_config_test_multi.rb"
38
+ sh "ruby -I lib test/env_test.rb"
39
+ rescue Object => exception2
40
+ end
41
+ x=exception1 ||exception2
42
+ raise x if x
43
+ end
44
+ task :cnu_config_test do
45
+ sh "ruby -I lib test/cnu_config_test.rb"
46
+ puts "\n\n"
47
+ begin
48
+ ENV['CNU_CONFIG_PATH'] = cnu_config_multi_paths.join('')
49
+ sh "ruby -I lib test/cnu_config_test.rb"
50
+ rescue Object => exception3
51
+ end
52
+ end
53
+
54
+
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 0
3
+ :major: 0
4
+ :minor: 2
data/bin/active_config ADDED
@@ -0,0 +1,46 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ =begin rdoc
4
+
5
+ # Prints info from to STDOUT.
6
+ #
7
+ # Example:
8
+ #
9
+ # > active_config httpd.httpd.domain.portal.primary httpd.httpd.domain.frontend.primary
10
+ # portal.cashnetusa.com
11
+ # www.cashnetusa.com
12
+ #
13
+ # > ACTIVE_CONFIG_OVERLAY=gb active_config httpd.httpd.domain.portal.primary httpd.httpd.domain.frontend.primary
14
+ # portaluk.cashnetusa.com
15
+ # www.quickquid.co.uk
16
+ #
17
+ # > ACTIVE_CONFIG_OVERLAY=gb RAILS_ENV=production active_config --PRODDB 'database[RAILS_ENV].database'
18
+ # PRODDB=activeapp_dev_uk
19
+ # > ACTIVE_CONFIG_OVERLAY=us RAILS_ENV=production active_config --PRODDB 'database[RAILS_ENV].database'
20
+ # PRODDB=activeapp_dev
21
+
22
+ =end
23
+
24
+ $:.unshift File.expand_path(File.dirname(__FILE__) + '/lib/ruby')
25
+
26
+ # Need to remove active_config's dependency on activesupport.
27
+ require 'rubygems'
28
+ gem 'activesupport'
29
+
30
+ # Yuck!
31
+ RAILS_ENV = ENV['RAILS_ENV'] || 'development'
32
+
33
+ require 'active_config'
34
+
35
+ name = ''
36
+
37
+ ARGV.each do | x |
38
+ if x =~ /^--(.+)/
39
+ name = "#{$1}="
40
+ else
41
+ puts(name + (eval(".#{x}").to_s))
42
+ end
43
+ end
44
+
45
+ exit 0
46
+
@@ -0,0 +1,110 @@
1
+ class ActiveConfig
2
+ class HashConfig < HashWithIndifferentAccess
3
+ # HashWithIndifferentAccess#dup always returns HashWithIndifferentAccess!
4
+ # -- kurt 2007/10/18
5
+ def dup
6
+ self.class.new(self)
7
+ end
8
+
9
+
10
+ # dotted notation can now be used with arguments (useful for creating mock objects)
11
+ # in the YAML file the method name is a key (just like usual), argument(s)
12
+ # form a nested key, and the value will get returned.
13
+ #
14
+ # For example loading to variable foo a yaml file that looks like:
15
+ # customer:
16
+ # id: 12345678
17
+ # verified:
18
+ # phone: verified
19
+ # :address: info_not_available
20
+ # ? [name, employer]
21
+ # : not_verified
22
+ #
23
+ # Allows the following calls:
24
+ # foo.customer.id --> 12345678
25
+ # foo.customer.verified.phone --> verified
26
+ # foo.customer.verified("phone") --> verified
27
+ # foo.customer.verified("name", "employer") --> not_verified
28
+ # foo.customer.verified(:address) --> info_not_available
29
+ #
30
+ # Note that :address is specified as a symbol, where phone is just a string.
31
+ # Depending on what kind of parameter the method being mocked out is going
32
+ # to be called with, define in the YAML file either a string or a symbol.
33
+ # This also works inside the composite array keys.
34
+ def method_missing(method, *args)
35
+ method = method.to_s
36
+ # STDERR.puts "CHF#method_missing(#{method.inspect}, #{args.inspect}) on #{self.inspect}:#{self.class}" if method == 'dup'
37
+ value = self[method]
38
+ case args.size
39
+ when 0:
40
+ # e.g.: ActiveConfig.global.method
41
+ ;
42
+ when 1:
43
+ # e.g.: ActiveConfig.global.method(one_arg)
44
+ value = value.send(args[0])
45
+ else
46
+ # e.g.: ActiveConfig.global.method(arg_one, args_two, ...)
47
+ value = value[args]
48
+ end
49
+ # value = convert_value(value)
50
+ value
51
+ end
52
+
53
+ ##
54
+ # Why the &*#^@*^&$ isn't HashWithIndifferentAccess actually doing this?
55
+ #
56
+ def [](key)
57
+ key = key.to_s if key.kind_of?(Symbol)
58
+ super(key)
59
+ end
60
+
61
+ # HashWithIndifferentAccess#default is broken!
62
+ define_method(:default_Hash, Hash.instance_method(:default))
63
+
64
+ ##
65
+ # Allow hash.default => hash['default']
66
+ # without breaking Hash's usage of default(key)
67
+ #
68
+ @@no_key = [ :no_key ] # magically unique value.
69
+ def default(key = @@no_key)
70
+ key = key.to_s if key.is_a?(Symbol)
71
+ key == @@no_key ? self['default'] : default_Hash(key == @@no_key ? nil : key)
72
+ end
73
+
74
+ ##
75
+ # HashWithIndifferentAccess#update is broken!
76
+ # This took way too long to figure this out:
77
+ #
78
+ # Hash#update returns self,
79
+ # BUT,
80
+ # HashWithIndifferentAccess#update does not!
81
+ #
82
+ # { :a => 1 }.update({ :b => 2, :c => 3 })
83
+ # => { :a => 1, :b => 2, :c => 3 }
84
+ #
85
+ # HashWithIndifferentAccess.new({ :a => 1 }).update({ :b => 2, :c => 3 })
86
+ # => { :b => 2, :c => 3 } # WTF?
87
+ #
88
+ # Subclasses should *never* override methods and break their protocols!!!!
89
+ # -- kurt 2007/03/28
90
+ #
91
+ def update(hash)
92
+ super(hash)
93
+ self
94
+ end
95
+
96
+ ##
97
+ # Override WithIndifferentAccess#convert_value
98
+ # return instances of this class for Hash values.
99
+ #
100
+ def convert_value(value)
101
+ # STDERR.puts "convert_value(#{value.inspect}:#{value.class})"
102
+ if value.class == Hash
103
+ value = self.class.new(value)
104
+ value.freeze if self.frozen?
105
+ end
106
+ value
107
+ end
108
+
109
+ end
110
+ 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,85 @@
1
+ class ActiveConfig
2
+ class HashWithHooks < HashWithIndifferentAccess
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|return (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, :local],
46
+ :hostname,
47
+ [:hostname, :local],
48
+ :local,
49
+ ]
50
+ end
51
+ def method_missing method, val=nil
52
+ super if method.to_s=~/^_/
53
+ if method.to_s=~/^(.*)=$/
54
+ ac_instance._flush_cache
55
+ return @symbols[$1]=val
56
+ end
57
+ ret=@symbols[method]
58
+ if ret
59
+ return ret.call(@symbols) if ret.respond_to?(:call)
60
+ return ret
61
+ end
62
+ super
63
+ end
64
+ def for file
65
+ suffixes.map { |this_suffix| [file,*this_suffix].compact.join('_')}.compact.uniq
66
+ end
67
+ def suffixes ary=@priority
68
+ ary.map{|e|
69
+ if Array===e
70
+ t=self.suffixes(e).compact
71
+ t.size > 0 ? t.join('_') : nil
72
+ elsif @symbols[e]
73
+ method_missing(e)
74
+ else
75
+ e && e.to_s
76
+ end
77
+ }
78
+ end
79
+ def file fname
80
+ suffixes.map{|m|m="#{m}" if m
81
+ "#{fname}#{m}.yml"
82
+ }
83
+ end
84
+ end
85
+ end