cjbottaro-app_config 1.0.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/README.rdoc ADDED
@@ -0,0 +1,59 @@
1
+ == Summary
2
+ Application level configuration.
3
+
4
+ == Features
5
+
6
+ * simple YAML config files
7
+ * config files support ERB
8
+ * config files support inheritance
9
+ * access config information via convenient object member notation
10
+
11
+ === Basic Usage
12
+
13
+ You simply write a configuration file in YAML. Notice you can use ERB.
14
+
15
+ <em>config.yml</em>
16
+ aws:
17
+ access_key: 123ABC
18
+ secret_key: ABC123
19
+ now: <%= Time.now %>
20
+ servers: [ {name: example1.com}, {name: example2.com} ]
21
+
22
+ Then somewhere in your code, you create a global constant from the config file. Then access the config data via object member notation.
23
+
24
+ _code_
25
+ ::AppConfig = ApplicationConfiguration.new("config.yml")
26
+ AppConfig.aws.access_key # => "123ABC"
27
+ AppConfig.aws.secret_key # => "ABC123"
28
+ AppConfig.now # => Tue May 05 21:55:15 -0500 2009
29
+ AppConfig.servers[0].name # => "example1.com"
30
+
31
+ === Inheritance
32
+
33
+ You can have a second config file that is recursively merged with the first config file.
34
+
35
+ <em>base.yml</em>
36
+ app_name: MyCoolApp
37
+ domain: dev.mycoolapp.com
38
+
39
+ <em>production.yml</em>
40
+ domain: www.mycoolapp.com
41
+
42
+ _code_
43
+ ::AppConfig = ApplicationConfiguration.new("base.yml", "production.yml")
44
+ AppConfig.app_name # => "MyCoolApp"
45
+ AppConfig.domain # => "www.mycoolapp.com"
46
+
47
+ === Using in a Rails app
48
+
49
+ You just need to create an initializer that looks something like this.
50
+
51
+ require 'app_config'
52
+ ::AppConfig = ApplicationConfiguration.new(RAILS_ROOT+"/config/app_config.yml",
53
+ RAILS_ROOT+"/config/environments/#{RAILS_ENV}.yml")
54
+
55
+ If you installed this as a Rails plugin instead of a gem, that code is already run for you in
56
+ the plugin's init.rb.
57
+
58
+ == Author
59
+ Christopher J. Bottaro
data/Rakefile ADDED
@@ -0,0 +1,38 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+ require 'rake/rdoctask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gem|
8
+ gem.name = "app_config"
9
+ gem.summary = %Q{Application level configuration.}
10
+ gem.description = %Q{Application level configuration that supports YAML config file, inheritance, ERB, and object member notation.}
11
+ gem.email = "cjbottaro@alumni.cs.utexas.edu"
12
+ gem.homepage = "http://github.com/cjbottaro/app_config"
13
+ gem.authors = ["Christopher J Bottaro"]
14
+
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ rescue LoadError
18
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
19
+ end
20
+
21
+ desc 'Default: run unit tests.'
22
+ task :default => :test
23
+
24
+ desc 'Test the app_config plugin.'
25
+ Rake::TestTask.new(:test) do |t|
26
+ t.libs << 'lib'
27
+ t.pattern = 'test/**/*_test.rb'
28
+ t.verbose = true
29
+ end
30
+
31
+ desc 'Generate documentation for the app_config plugin.'
32
+ Rake::RDocTask.new(:rdoc) do |rdoc|
33
+ rdoc.rdoc_dir = 'rdoc'
34
+ rdoc.title = 'AppConfig'
35
+ rdoc.options << '--line-numbers' << '--inline-source'
36
+ rdoc.rdoc_files.include('README.rdoc')
37
+ rdoc.rdoc_files.include('lib/**/*.rb')
38
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 1
3
+ :minor: 0
4
+ :patch: 0
data/lib/app_config.rb ADDED
@@ -0,0 +1,66 @@
1
+ require 'closed_struct'
2
+ require 'yaml'
3
+ require 'erb'
4
+
5
+ class ApplicationConfiguration
6
+
7
+ # Create a new ApplicationConfiguration object. <tt>conf_path_1</tt> is the path to your YAML configuration file.
8
+ # If <tt>conf_path_2</tt> is given, the contents are recursively merged with the contents of <tt>conf_path_1</tt>.
9
+ # This allows you to have a "base" configuration with settings that are overrided by "environment specific"
10
+ # (developement, test, production, etc) settings.
11
+ #
12
+ # Ex:
13
+ # ApplicationConfiguration.new(RAILS_ROOT+"/config/base.yml", RAILS_ROOT+"/environments/#{RAILS_ENV}_config.yml")
14
+ def initialize(conf_path_1, conf_path_2 = nil)
15
+ @conf_path_1, @conf_path_2 = conf_path_1, conf_path_2
16
+ reload!
17
+ end
18
+
19
+ # Rereads your configuration files and rebuilds your ApplicationConfiguration object. This is useful
20
+ # for when you edit your config files, but don't want to restart your web server.
21
+ def reload!
22
+ conf1 = load_conf_file(@conf_path_1)
23
+ conf2 = load_conf_file(@conf_path_2)
24
+ conf = recursive_merge(conf1, conf2)
25
+ @config = convert(conf)
26
+ end
27
+
28
+ private
29
+
30
+ def method_missing(name, *args)
31
+ if @config.respond_to?(name)
32
+ @config.send(name, *args)
33
+ else
34
+ super
35
+ end
36
+ end
37
+
38
+ def load_conf_file(conf_path)
39
+ return {} if !conf_path or conf_path.empty?
40
+ File.open(conf_path, "r") do |file|
41
+ YAML.load(ERB.new(file.read).result) || {}
42
+ end
43
+ end
44
+
45
+ # Recursively converts Hashes to ClosedStructs (including Hashes inside Arrays)
46
+ def convert(h) #:nodoc:
47
+ s = ClosedStruct.new(h.keys)
48
+ h.each do |k, v|
49
+ if v.instance_of?(Hash)
50
+ s.send( (k+'=').to_sym, convert(v))
51
+ elsif v.instance_of?(Array)
52
+ converted_array = v.collect { |e| e.instance_of?(Hash) ? convert(e) : e }
53
+ s.send( (k+'=').to_sym, converted_array)
54
+ else
55
+ s.send( (k+'=').to_sym, v)
56
+ end
57
+ end
58
+ s
59
+ end
60
+
61
+ # Recursively merges hashes. h2 will overwrite h1.
62
+ def recursive_merge(h1, h2) #:nodoc:
63
+ h1.merge(h2){ |k, v1, v2| v2.kind_of?(Hash) ? recursive_merge(v1, v2) : v2 }
64
+ end
65
+
66
+ end
@@ -0,0 +1,39 @@
1
+ require 'ostruct'
2
+
3
+ class ClosedStruct < OpenStruct # :nodoc:
4
+
5
+ def initialize(*args)
6
+ if args.length == 1
7
+ super(args.first)
8
+ else
9
+ h = args.inject({}){ |memo, k| memo[k] = nil; memo }
10
+ super(h)
11
+ end
12
+ @closed = true
13
+ end
14
+
15
+ def new_ostruct_member(name)
16
+ if @closed
17
+ raise RuntimeError, "cannot add members to closed struct"
18
+ else
19
+ super
20
+ end
21
+ end
22
+
23
+ def method_missing(name, *args)
24
+ raise NoMethodError, "undefined method '#{name}' for #{self}"
25
+ end
26
+
27
+ def id
28
+ if @table.has_key?(:id)
29
+ @table[:id]
30
+ else
31
+ method_missing(:id)
32
+ end
33
+ end
34
+
35
+ def to_h
36
+ @table.dup
37
+ end
38
+
39
+ end
@@ -0,0 +1,5 @@
1
+ size: 1
2
+ server: google.com
3
+ emails:
4
+ support: support@domain.com
5
+ webmaster: web@domain.com
@@ -0,0 +1,64 @@
1
+ require 'test/unit'
2
+ require 'app_config'
3
+
4
+ class AppConfigTest < Test::Unit::TestCase
5
+
6
+ def test_missing_files
7
+ assert_raise(Errno::ENOENT){ ApplicationConfiguration.new('not_here1', 'not_here2') }
8
+ end
9
+
10
+ def test_empty_files
11
+ config = ApplicationConfiguration.new('test/empty1.yml', 'test/empty2.yml')
12
+ assert_equal OpenStruct.new, config.instance_variable_get("@config")
13
+ end
14
+
15
+ def test_common
16
+ config = ApplicationConfiguration.new('test/app_config.yml')
17
+ assert_equal 1, config.size
18
+ assert_equal 'google.com', config.server
19
+ end
20
+
21
+ def test_override
22
+ config = ApplicationConfiguration.new('test/app_config.yml', 'test/development.yml')
23
+ assert_equal 2, config.size
24
+ assert_equal 'google.com', config.server
25
+ end
26
+
27
+ def test_nested
28
+ config = ApplicationConfiguration.new('test/development.yml')
29
+ assert_equal 3, config.section.size
30
+ end
31
+
32
+ def test_array
33
+ config = ApplicationConfiguration.new('test/development.yml')
34
+ assert_equal 'yahoo.com', config.section.servers[0].name
35
+ assert_equal 'amazon.com', config.section.servers[1].name
36
+ end
37
+
38
+ def test_erb
39
+ config = ApplicationConfiguration.new('test/development.yml')
40
+ assert_equal 6, config.computed
41
+ end
42
+
43
+ def test_recursive_merge
44
+ config = ApplicationConfiguration.new('test/app_config.yml', 'test/development.yml')
45
+ assert_equal 'support@domain.com', config.emails.support
46
+ assert_equal 'webmaster@domain.com', config.emails.webmaster
47
+ assert_equal 'feedback@domain.com', config.emails.feedback
48
+ end
49
+
50
+ def test_exception_on_non_existant_values
51
+ config = ApplicationConfiguration.new('test/app_config.yml')
52
+ assert_raise(NoMethodError){ config.not_here1 = "blah" }
53
+ assert_raise(NoMethodError){ config.not_here2 }
54
+ end
55
+
56
+ def test_reload
57
+ config = ApplicationConfiguration.new('test/app_config.yml')
58
+ config.size = 2
59
+ assert_equal 2, config.size
60
+ config.reload!
61
+ assert_equal 1, config.size
62
+ end
63
+
64
+ end
@@ -0,0 +1,8 @@
1
+ size: 2
2
+ computed: <%= 1 + 2 + 3 %>
3
+ section:
4
+ size: 3
5
+ servers: [ {name: yahoo.com}, {name: amazon.com} ]
6
+ emails:
7
+ webmaster: webmaster@domain.com
8
+ feedback: feedback@domain.com
data/test/empty1.yml ADDED
File without changes
data/test/empty2.yml ADDED
File without changes
metadata ADDED
@@ -0,0 +1,63 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cjbottaro-app_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Christopher J Bottaro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-01 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Application level configuration that supports YAML config file, inheritance, ERB, and object member notation.
17
+ email: cjbottaro@alumni.cs.utexas.edu
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.rdoc
24
+ files:
25
+ - README.rdoc
26
+ - Rakefile
27
+ - VERSION.yml
28
+ - lib/app_config.rb
29
+ - lib/closed_struct.rb
30
+ - test/app_config.yml
31
+ - test/app_config_test.rb
32
+ - test/development.yml
33
+ - test/empty1.yml
34
+ - test/empty2.yml
35
+ has_rdoc: true
36
+ homepage: http://github.com/cjbottaro/app_config
37
+ licenses:
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.5
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Application level configuration.
62
+ test_files:
63
+ - test/app_config_test.rb