pwim-app_config 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ 10/03/2008
2
+ * recursively merge the configuration hashes
3
+
4
+ 07/01/2009
5
+ * Packaged as a gem (but still works as a Rails plugin).
6
+ * The app config object is now an instance of ApplicationConfiguration.
7
+ * NoMethodError raised if you try to access a config element that doesn't exist.
8
+ * ApplicationConfiguration#reload! to reread the config files and rebuild the app config object.
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 0
3
+ :patch: 2
4
+ :major: 1
@@ -0,0 +1,58 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run `rake gemspec`
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{app_config}
8
+ s.version = "1.0.1"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Christopher J Bottaro"]
12
+ s.date = %q{2009-08-28}
13
+ s.description = %q{Application level configuration that supports YAML config file, inheritance, ERB, and object member notation.}
14
+ s.email = %q{cjbottaro@alumni.cs.utexas.edu}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ "CHANGELOG",
20
+ "README.rdoc",
21
+ "Rakefile",
22
+ "VERSION.yml",
23
+ "app_config.gemspec",
24
+ "init.rb",
25
+ "install.rb",
26
+ "lib/app_config.rb",
27
+ "lib/closed_struct.rb",
28
+ "rails/init.rb",
29
+ "tasks/app_config_tasks.rake",
30
+ "test/app_config.yml",
31
+ "test/app_config_test.rb",
32
+ "test/closed_struct_test.rb",
33
+ "test/development.yml",
34
+ "test/empty1.yml",
35
+ "test/empty2.yml",
36
+ "uninstall.rb"
37
+ ]
38
+ s.has_rdoc = true
39
+ s.homepage = %q{http://github.com/cjbottaro/app_config}
40
+ s.rdoc_options = ["--charset=UTF-8"]
41
+ s.require_paths = ["lib"]
42
+ s.rubygems_version = %q{1.3.1}
43
+ s.summary = %q{Application level configuration.}
44
+ s.test_files = [
45
+ "test/app_config_test.rb",
46
+ "test/closed_struct_test.rb"
47
+ ]
48
+
49
+ if s.respond_to? :specification_version then
50
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
+ s.specification_version = 2
52
+
53
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
54
+ else
55
+ end
56
+ else
57
+ end
58
+ end
data/init.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'app_config'
2
+
3
+ ::AppConfig = ApplicationConfiguration.new(RAILS_ROOT+"/config/app_config.yml",
4
+ RAILS_ROOT+"/config/environments/#{RAILS_ENV}.yml")
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,50 @@
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 = ClosedStruct.r_new(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 merges hashes. h2 will overwrite h1.
46
+ def recursive_merge(h1, h2) #:nodoc:
47
+ h1.merge(h2){ |k, v1, v2| v2.kind_of?(Hash) ? recursive_merge(v1, v2) : v2 }
48
+ end
49
+
50
+ end
@@ -0,0 +1,60 @@
1
+ require 'ostruct'
2
+
3
+ # Like OpenStruct, but raises an exception if you try to access a member that wasn't specified in the initializer.
4
+ class ClosedStruct < OpenStruct
5
+
6
+ def self.r_new(hash)
7
+ closed_struct = ClosedStruct.new(hash)
8
+ closed_struct.send(:recursive_initialize)
9
+ closed_struct
10
+ end
11
+
12
+ def initialize(*args)
13
+ if args.length == 1 and args.first.kind_of?(Hash)
14
+ super(args.first)
15
+ elsif args.length > 1 and args.all?{ |arg| [Symbol, String].include?(arg.class) }
16
+ args = args.inject({}){ |memo, arg| memo[arg.to_sym] = nil; memo }
17
+ super(args)
18
+ else
19
+ raise ArgumentError, "invalid arguments: #{args.inspect}"
20
+ end
21
+ @closed = true
22
+ end
23
+
24
+ def new_ostruct_member(name)
25
+ if @closed
26
+ raise RuntimeError, "cannot add members to closed struct"
27
+ else
28
+ super
29
+ end
30
+ end
31
+
32
+ def method_missing(name, *args)
33
+ raise NoMethodError, "undefined method '#{name}' for #{self}"
34
+ end
35
+
36
+ def id
37
+ if @table.has_key?(:id)
38
+ @table[:id]
39
+ else
40
+ method_missing(:id)
41
+ end
42
+ end
43
+
44
+ def to_h
45
+ @table.dup
46
+ end
47
+
48
+ private
49
+
50
+ def recursive_initialize
51
+ @table.each do |k, v|
52
+ if v.kind_of?(Hash)
53
+ @table[k] = ClosedStruct.r_new(v)
54
+ elsif v.kind_of?(Array)
55
+ @table[k] = v.collect{ |e| e.kind_of?(Hash) ? ClosedStruct.r_new(e) : e }
56
+ end
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,4 @@
1
+ require 'app_config'
2
+
3
+ ::AppConfig = ApplicationConfiguration.new(RAILS_ROOT+"/config/app_config.yml",
4
+ RAILS_ROOT+"/config/environments/#{RAILS_ENV}.yml")
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :app_config do
3
+ # # Task goes here
4
+ # 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,33 @@
1
+ require 'test/unit'
2
+ require 'app_config'
3
+
4
+ class ClosedStructTest < Test::Unit::TestCase
5
+
6
+ def test_from_hash
7
+ s = ClosedStruct.new :a => "a", "b" => "b", :c => 123
8
+ assert_equal "a", s.a
9
+ assert_equal "b", s.b
10
+ assert_equal 123, s.c
11
+ assert_raise(NoMethodError){ s.d }
12
+ end
13
+
14
+ def test_from_array
15
+ s = ClosedStruct.new :a, :b, :c
16
+ s.b = "b"
17
+ assert_nil s.a
18
+ assert_equal "b", s.b
19
+ assert_nil s.c
20
+ assert_raise(NoMethodError){ s.d }
21
+ end
22
+
23
+ def test_nested_hash
24
+ s = ClosedStruct.r_new :a => :a, :b => { :c => :c }, :d => :d
25
+ assert_equal :c, s.b.c
26
+ end
27
+
28
+ def test_nested_hashes_in_array
29
+ s = ClosedStruct.r_new :a => :a, :b => [ {:c => :c }, { :d => :d } ], :e => :e
30
+ assert_equal :c, s.b[0].c
31
+ end
32
+
33
+ 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
File without changes
File without changes
@@ -0,0 +1 @@
1
+ # Uninstall hook code here
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pwim-app_config
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Christopher J Bottaro
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-08-28 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
+ - CHANGELOG
26
+ - README.rdoc
27
+ - Rakefile
28
+ - VERSION.yml
29
+ - app_config.gemspec
30
+ - init.rb
31
+ - install.rb
32
+ - lib/app_config.rb
33
+ - lib/closed_struct.rb
34
+ - rails/init.rb
35
+ - tasks/app_config_tasks.rake
36
+ - test/app_config.yml
37
+ - test/app_config_test.rb
38
+ - test/closed_struct_test.rb
39
+ - test/development.yml
40
+ - test/empty1.yml
41
+ - test/empty2.yml
42
+ - uninstall.rb
43
+ has_rdoc: true
44
+ homepage: http://github.com/cjbottaro/app_config
45
+ licenses:
46
+ post_install_message:
47
+ rdoc_options:
48
+ - --charset=UTF-8
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: "0"
56
+ version:
57
+ required_rubygems_version: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: "0"
62
+ version:
63
+ requirements: []
64
+
65
+ rubyforge_project:
66
+ rubygems_version: 1.3.5
67
+ signing_key:
68
+ specification_version: 2
69
+ summary: Application level configuration.
70
+ test_files:
71
+ - test/app_config_test.rb
72
+ - test/closed_struct_test.rb