jlawler-activeconfig 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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