<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>app_config.gemspec</filename>
    </added>
    <added>
      <filename>lib/closed_struct.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,2 +1,8 @@
 10/03/2008
-* recursively merge the configuration hashes
\ No newline at end of file
+* recursively merge the configuration hashes
+
+07/01/2009
+* Packaged as a gem (but still works as a Rails plugin).
+* The app config object is now an instance of ApplicationConfiguration.
+* NoMethodError raised if you try to access a config element that doesn't exist.
+* ApplicationConfiguration#reload! to reread the config files and rebuild the app config object.
\ No newline at end of file</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -1,80 +1,59 @@
 == Summary
 Application level configuration.
 
+== Features
+
+ * simple YAML config files
+ * config files support ERB
+ * config files support inheritance
+ * access config information via convenient object member notation
+
+=== Basic Usage
+
+You simply write a configuration file in YAML.  Notice you can use ERB.
+
+&lt;em&gt;config.yml&lt;/em&gt;
+  aws:
+    access_key: 123ABC
+    secret_key: ABC123
+  now: &lt;%= Time.now %&gt;
+  servers: [ {name: example1.com}, {name: example2.com} ]
+
+Then somewhere in your code, you create a global constant from the config file.  Then access the config data via object member notation.
+
+_code_
+  ::AppConfig = ApplicationConfiguration.new(&quot;config.yml&quot;)
+  AppConfig.aws.access_key  # =&gt; &quot;123ABC&quot;
+  AppConfig.aws.secret_key  # =&gt; &quot;ABC123&quot;
+  AppConfig.now             # =&gt; Tue May 05 21:55:15 -0500 2009
+  AppConfig.servers[0].name # =&gt; &quot;example1.com&quot;
+
+=== Inheritance
+
+You can have a second config file that is recursively merged with the first config file.
+
+&lt;em&gt;base.yml&lt;/em&gt;
+  app_name:  MyCoolApp
+  domain:  dev.mycoolapp.com
+
+&lt;em&gt;production.yml&lt;/em&gt;
+  domain:  www.mycoolapp.com
+
+_code_
+  ::AppConfig = ApplicationConfiguration.new(&quot;base.yml&quot;, &quot;production.yml&quot;)
+  AppConfig.app_name # =&gt; &quot;MyCoolApp&quot;
+  AppConfig.domain   # =&gt; &quot;www.mycoolapp.com&quot;
+
+=== Using in a Rails app
+
+You just need to create an initializer that looks something like this.
+
+ require 'app_config'
+ ::AppConfig = ApplicationConfiguration.new(RAILS_ROOT+&quot;/config/app_config.yml&quot;,
+                                            RAILS_ROOT+&quot;/config/environments/#{RAILS_ENV}.yml&quot;)
+
+If you installed this as a Rails plugin instead of a gem, that code is already run for you in
+the plugin's init.rb.
+
 == Author
-Christopher J. Bottaro
-
-=== Accessing the AppConfig object
-After installing this plugin, the AppConfig object will be global available.  Entries are accessed via object member notation:
- AppConfig.my_config_entry
-Nested entries are supported:
- AppConfig.my_section.some_entry
-
-=== Common config file
-Config entries defined in
- RAILS_ROOT/config/app_config.yml
-will be available to all environments.
-
-=== Environment specific config files
-You can have environment specific config files.  Environment specific config entries take precedence over common config entries.
-
-Example development environment config file:
- RAILS_ROOT/config/environments/development.yml
- 
-Example production environment config file:
- RAILS_ROOT/config/environments/production.yml
-
-==== UPDATE (10/03/2008)
-The config files are recursively merged now.  Thus the following works:
-
-&lt;tt&gt;RAILS_ROOT/config/app_config.yml:&lt;/tt&gt;
-  servers:
-    dev:  dev.domain.com
-    test: test.domain.com
-  
-&lt;tt&gt;RAILS_ROOT/config/environments/production.yml:&lt;/tt&gt;
-  servers:
-    test: testing.domain.com
-    prod: prod.domain.com
-
-&lt;tt&gt;(application code):&lt;/tt&gt;
-  AppConfig.servers.dev  # =&gt; dev.domain.com
-  AppConfig.servers.test # =&gt; testing.domain.com
-  AppConfig.servers.prod # =&gt; prod.domain.com
-
-Whereas before, the &quot;servers&quot; section of &lt;tt&gt;production.yml&lt;/tt&gt; would completely overwrite the &quot;servers&quot; section of &lt;tt&gt;app_config.yml&lt;/tt&gt; and you would get:
-
-  AppConfig.servers.dev  # =&gt; nil
-  AppConfig.servers.test # =&gt; testing.domain.com
-  AppConfig.servers.prod # =&gt; prod.domain.com
-
-=== Embedded Ruby (ERB)
- Embedded Ruby is allowed in the configuration files.  See examples below.
-
-=== Examples
-Consider the two following config files.
-
-RAILS_ROOT/config/app_config.yml:
- size: 1
- server: google.com
-
-RAILS_ROOT/config/environments/development.yml:
- size: 2
- computed: &lt;%= 1 + 2 + 3 %&gt;
- section:
-   size: 3
-   servers: [ {name: yahoo.com}, {name: amazon.com} ]
-
-Notice that the environment specific config entries overwrite the common entries.
- AppConfig.size -&gt; 2
- AppConfig.server -&gt; google.com
-
-Notice the embedded Ruby.
- AppConfig.computed -&gt; 6
-
-Notice that object member notation is maintained even in nested entries.
- AppConfig.section.size -&gt; 3
-
-Notice array notation and object member notation is maintained.
- AppConfig.section.servers[0].name -&gt; yahoo.com
- AppConfig.section.servers[1].name -&gt; amazon.com
\ No newline at end of file
+Christopher J. Bottaro
\ No newline at end of file</diff>
      <filename>README.rdoc</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,22 @@ require 'rake'
 require 'rake/testtask'
 require 'rake/rdoctask'
 
+begin
+  require 'jeweler'
+  Jeweler::Tasks.new do |gem|
+    gem.name = &quot;app_config&quot;
+    gem.summary = %Q{Application level configuration.}
+    gem.description = %Q{Application level configuration that supports YAML config file, inheritance, ERB, and object member notation.}
+    gem.email = &quot;cjbottaro@alumni.cs.utexas.edu&quot;
+    gem.homepage = &quot;http://github.com/cjbottaro/app_config&quot;
+    gem.authors = [&quot;Christopher J Bottaro&quot;]
+
+    # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
+  end
+rescue LoadError
+  puts &quot;Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com&quot;
+end
+
 desc 'Default: run unit tests.'
 task :default =&gt; :test
 
@@ -17,6 +33,6 @@ Rake::RDocTask.new(:rdoc) do |rdoc|
   rdoc.rdoc_dir = 'rdoc'
   rdoc.title    = 'AppConfig'
   rdoc.options &lt;&lt; '--line-numbers' &lt;&lt; '--inline-source'
-  rdoc.rdoc_files.include('README')
+  rdoc.rdoc_files.include('README.rdoc')
   rdoc.rdoc_files.include('lib/**/*.rb')
 end</diff>
      <filename>Rakefile</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
 --- 
-:major: 0
+:major: 1
 :minor: 0
 :patch: 0</diff>
      <filename>VERSION.yml</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
 require 'app_config'
 
-::AppConfig = ApplicationConfig.load_files(RAILS_ROOT+&quot;/config/app_config.yml&quot;,
+::AppConfig = ApplicationConfiguration.new(RAILS_ROOT+&quot;/config/app_config.yml&quot;,
                                            RAILS_ROOT+&quot;/config/environments/#{RAILS_ENV}.yml&quot;)
\ No newline at end of file</diff>
      <filename>init.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,31 +1,51 @@
-require 'ostruct'
+require 'closed_struct'
 require 'yaml'
 require 'erb'
 
-# == Summary
-# This is API documentation, NOT documentation on how to use this plugin.  For that, see the README.
-module ApplicationConfig
-  
-  # Create a config object (OpenStruct) from a yaml file.  If a second yaml file is given, then the sections of that file will overwrite the sections
-  # if the first file if they exist in the first file.
-  def self.load_files(conf_path_1, conf_path_2 = nil)
-  
-    conf1 = YAML.load(ERB.new(IO.read(conf_path_1)).result) if conf_path_1 and File.exists?(conf_path_1)
-    conf1 = {} if !conf1 or conf1.empty?
-    
-    conf2 = YAML.load(ERB.new(IO.read(conf_path_2)).result) if conf_path_2 and File.exists?(conf_path_2)
-    conf2 = {} if !conf2 or conf2.empty?
-    
-    conf = recursive_merge(conf1, conf2)
-    (!conf or conf.empty?) ? OpenStruct.new : convert(conf)
-    
+class ApplicationConfiguration
+  
+  # Create a new ApplicationConfiguration object.  &lt;tt&gt;conf_path_1&lt;/tt&gt; is the path to your YAML configuration file.
+  # If &lt;tt&gt;conf_path_2&lt;/tt&gt; is given, the contents are recursively merged with the contents of &lt;tt&gt;conf_path_1&lt;/tt&gt;.
+  # This allows you to have a &quot;base&quot; configuration with settings that are overrided by &quot;environment specific&quot;
+  # (developement, test, production, etc) settings.
+  #
+  # Ex:
+  #  ApplicationConfiguration.new(RAILS_ROOT+&quot;/config/base.yml&quot;, RAILS_ROOT+&quot;/environments/#{RAILS_ENV}_config.yml&quot;)
+  def initialize(conf_path_1, conf_path_2 = nil)
+    @conf_path_1, @conf_path_2 = conf_path_1, conf_path_2
+    reload!
+  end
+  
+  # Rereads your configuration files and rebuilds your ApplicationConfiguration object.  This is useful
+  # for when you edit your config files, but don't want to restart your web server.
+  def reload!
+    conf1 = load_conf_file(@conf_path_1)
+    conf2 = load_conf_file(@conf_path_2)
+    conf  = recursive_merge(conf1, conf2)
+    @config = convert(conf)
+  end
+  
+private
+  
+  def method_missing(name, *args)
+    if @config.respond_to?(name)
+      @config.send(name, *args)
+    else
+      super
+    end
+  end
+  
+  def load_conf_file(conf_path)
+    return {} if !conf_path or conf_path.empty?
+    File.open(conf_path, &quot;r&quot;) do |file|
+      YAML.load(ERB.new(file.read).result) || {}
+    end
   end
   
-  # Recursively converts Hashes to OpenStructs (including Hashes inside Arrays)
-  def self.convert(h) #:nodoc:
-    s = OpenStruct.new
+  # Recursively converts Hashes to ClosedStructs (including Hashes inside Arrays)
+  def convert(h) #:nodoc:
+    s = ClosedStruct.new(h.keys)
     h.each do |k, v|
-      s.new_ostruct_member(k)
       if v.instance_of?(Hash)
         s.send( (k+'=').to_sym, convert(v))
       elsif v.instance_of?(Array)
@@ -39,8 +59,8 @@ module ApplicationConfig
   end
   
   # Recursively merges hashes.  h2 will overwrite h1.
-  def self.recursive_merge(h1, h2) #:nodoc:
+  def recursive_merge(h1, h2) #:nodoc:
     h1.merge(h2){ |k, v1, v2| v2.kind_of?(Hash) ? recursive_merge(v1, v2) : v2 }
   end
-
+  
 end
\ No newline at end of file</diff>
      <filename>lib/app_config.rb</filename>
    </modified>
    <modified>
      <diff>@@ -4,48 +4,61 @@ require 'app_config'
 class AppConfigTest &lt; Test::Unit::TestCase
   
   def test_missing_files
-    config = ApplicationConfig.load_files('not_here1', 'not_here2')
-    assert_equal OpenStruct.new, config
+    assert_raise(Errno::ENOENT){ ApplicationConfiguration.new('not_here1', 'not_here2') }
   end
   
   def test_empty_files
-    config = ApplicationConfig.load_files('test/empty1.yml', 'test/empty2.yml')
-    assert_equal OpenStruct.new, config
+    config = ApplicationConfiguration.new('test/empty1.yml', 'test/empty2.yml')
+    assert_equal OpenStruct.new, config.instance_variable_get(&quot;@config&quot;)
   end
   
   def test_common
-    config = ApplicationConfig.load_files('test/app_config.yml')
+    config = ApplicationConfiguration.new('test/app_config.yml')
     assert_equal 1, config.size
     assert_equal 'google.com', config.server
   end
   
-  def test_environment_override
-    config = ApplicationConfig.load_files('test/app_config.yml', 'test/development.yml')
+  def test_override
+    config = ApplicationConfiguration.new('test/app_config.yml', 'test/development.yml')
     assert_equal 2, config.size
     assert_equal 'google.com', config.server
   end
   
   def test_nested
-    config = ApplicationConfig.load_files('test/development.yml')
+    config = ApplicationConfiguration.new('test/development.yml')
     assert_equal 3, config.section.size
   end
   
   def test_array
-    config = ApplicationConfig.load_files('test/development.yml')
+    config = ApplicationConfiguration.new('test/development.yml')
     assert_equal 'yahoo.com', config.section.servers[0].name
     assert_equal 'amazon.com', config.section.servers[1].name
   end
   
   def test_erb
-    config = ApplicationConfig.load_files('test/development.yml')
+    config = ApplicationConfiguration.new('test/development.yml')
     assert_equal 6, config.computed
   end
   
   def test_recursive_merge
-    config = ApplicationConfig.load_files('test/app_config.yml', 'test/development.yml')
+    config = ApplicationConfiguration.new('test/app_config.yml', 'test/development.yml')
     assert_equal 'support@domain.com', config.emails.support
     assert_equal 'webmaster@domain.com', config.emails.webmaster
     assert_equal 'feedback@domain.com', config.emails.feedback
   end
   
+  def test_exception_on_non_existant_values
+    config = ApplicationConfiguration.new('test/app_config.yml')
+    assert_raise(NoMethodError){ config.not_here1 = &quot;blah&quot; }
+    assert_raise(NoMethodError){ config.not_here2 }
+  end
+  
+  def test_reload
+    config = ApplicationConfiguration.new('test/app_config.yml')
+    config.size = 2
+    assert_equal 2, config.size
+    config.reload!
+    assert_equal 1, config.size
+  end
+  
 end</diff>
      <filename>test/app_config_test.rb</filename>
    </modified>
  </modified>
  <removed type="array">
    <removed>
      <filename>README</filename>
    </removed>
  </removed>
  <parents type="array">
    <parent>
      <id>00829378a24de65d394a189ea4b3fcd49209197f</id>
    </parent>
  </parents>
  <author>
    <name>cjbottaro</name>
    <email>cjbottaro@alumni.cs.utexas.edu</email>
  </author>
  <url>http://github.com/cjbottaro/app_config/commit/e49426822be0460052dee3cc8279b9f1303dc3c8</url>
  <id>e49426822be0460052dee3cc8279b9f1303dc3c8</id>
  <committed-date>2009-07-01T15:42:31-07:00</committed-date>
  <authored-date>2009-07-01T15:42:31-07:00</authored-date>
  <message>Packaged as a gem, mini rewrite, see README.rdoc and CHANGELOG.</message>
  <tree>dee5a7b5066e0e526763da8a1ef5a58b3131b6f8</tree>
  <committer>
    <name>cjbottaro</name>
    <email>cjbottaro@alumni.cs.utexas.edu</email>
  </committer>
</commit>
