autoconfig 0.1.2 → 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.
Files changed (3) hide show
  1. data/README.markdown +47 -2
  2. data/lib/autoconfig.rb +97 -39
  3. metadata +7 -6
data/README.markdown CHANGED
@@ -17,7 +17,7 @@ Autoconfig in an automated way to create flexible configuration structures repre
17
17
  1. require 'autoconfig'
18
18
  2. profit!
19
19
 
20
- ## Example
20
+ ## Basic Example
21
21
 
22
22
  Lets say you have application.yml in your config folder:
23
23
 
@@ -33,8 +33,53 @@ Lets say you have application.yml in your config folder:
33
33
  After requiring 'autoconfig' you should expect ApplicationConfig structure that will contain all the information. In production environment
34
34
  ApplicationConfig.web.hostname call will return "the.production.com".
35
35
 
36
+ ## Advance Example
37
+
38
+ Check it out under test/config/one
39
+
40
+ defaults:
41
+ one: !REQUIRED
42
+ two: two
43
+ three: three
44
+ cache_classes: true
45
+ tree1:
46
+ tree2: hey
47
+ tree3:
48
+ tree4: blah
49
+
50
+ defaults[test,cucumber,development]:
51
+ cache_classes: false
52
+
53
+ defaults[test,cucumber]:
54
+ one: one
55
+
56
+ development:
57
+ one: yup
58
+
59
+ test:
60
+ something: hello
61
+ tree1:
62
+ tree3:
63
+ tree4: bleh
64
+
65
+ production:
66
+ three: five
67
+
68
+ Autoconfig can support:
69
+ * specifying which keys are required via !REQUIRED yaml type
70
+ * environment specific defaults shared across some environments
71
+ * deep nested structures
72
+
36
73
  ## Configuration
37
74
 
38
75
  By default, it will look at config directory of your project and transfer most of your .yml files (it ignores database ones). However,
39
- you have full control over where it needs to look and what it needs to convert.
76
+ you have full control over where it needs to look and what it needs to convert. Here is a set of environment flags you could use:
40
77
 
78
+ * `AUTOCONFIG_ROOT` - allows setting of the application root. By default it will try to use APP_ROOT, rails root (if rails app) or pwd directory.
79
+ * `AUTOCONFIG_PATERN` - used to construct a path to your configuration files. By default it is set to config/*.yml
80
+ * `AUTOCONFIG_PATH` - allows setting a path to your configuration files. If set autoconfig will ignore patern and root, otherwise
81
+ it look at root and patern to get path to your files.
82
+ * `AUTOCONFIG_ENV` - allows setting environment in which autoconfig runs, by default it will try to use APP_ENV, Rails.env(if rails app) or
83
+ development (in that order)
84
+ * `AUTOCONFIG_IGNORE` - when set it will not create configs for files in the ignore. When not set it will just ignore database.yml. Takes a whitespace
85
+ separated list. Ex: 'cucumber cache'
data/lib/autoconfig.rb CHANGED
@@ -1,66 +1,124 @@
1
1
  require 'ostruct'
2
2
  require 'yaml'
3
+ require 'erb'
4
+ require 'set'
3
5
 
4
6
  module StringCamelize
5
7
  # Taken straight from active support inflector.rb, line 161
6
8
  def camelize(first_letter_in_uppercase = true)
7
9
  if first_letter_in_uppercase
8
- self.to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
10
+ to_s.gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
9
11
  else
10
- self.first + camelize(self)[1..-1]
12
+ first + camelize(self)[1..-1]
11
13
  end
12
14
  end
13
15
  end
14
16
  String.send(:include, StringCamelize) unless String.instance_methods.include?("camelize")
15
17
 
16
- module AutoConfig
17
- def self.root
18
- rails_root = (rails? && (Rails.root || File.expand_path('../', ENV['BUNDLE_GEMFILE'])))
19
- ENV['AUTOCONFIG_ROOT'] || ENV['APP_ROOT'] || rails_root || base_dir
20
- end
18
+ class AutoConfig
19
+ REQUIRED = :REQUIRED
20
+ YAML.add_builtin_type( 'REQUIRED' ){ REQUIRED }
21
21
 
22
- def self.pattern
23
- ENV['AUTOCONFIG_PATTERN'] || 'config/*.yml'
24
- end
22
+ class << self
23
+ def root
24
+ rails_root = (rails? && (Rails.root || File.expand_path('../', ENV['BUNDLE_GEMFILE'])))
25
+ ENV['AUTOCONFIG_ROOT'] || ENV['APP_ROOT'] || rails_root || Dir.pwd
26
+ end
25
27
 
26
- def self.path
27
- ENV['AUTOCONFIG_PATH'] || File.expand_path(pattern, root)
28
- end
28
+ def pattern
29
+ ENV['AUTOCONFIG_PATTERN'] || 'config/*.yml'
30
+ end
29
31
 
30
- def self.environment
31
- ENV['AUTOCONFIG_ENV'] || ENV['APP_ENV'] || (rails? && Rails.env) || base_dir
32
- end
32
+ def path
33
+ ENV['AUTOCONFIG_PATH'] || File.expand_path(pattern, root)
34
+ end
33
35
 
34
- def self.rails?
35
- Object::const_defined? "Rails"
36
- end
36
+ def environment
37
+ ENV['AUTOCONFIG_ENV'] || ENV['APP_ENV'] || (rails? && Rails.env) || 'development'
38
+ end
37
39
 
38
- def self.base_dir
39
- File.dirname(File.expand_path(__FILE__))
40
- end
40
+ def rails?
41
+ Object::const_defined? "Rails"
42
+ end
41
43
 
42
- def self.ignored_filenames
43
- names = ENV['AUTOCONFIG_IGNORE'] ? "database|" + ENV['AUTOCONFIG_IGNORE'].gsub(/\s/,'|') : 'database'
44
- Regexp.new(names)
45
- end
44
+ def ignored_filenames
45
+ names = ENV['AUTOCONFIG_IGNORE'] ? "database|" + ENV['AUTOCONFIG_IGNORE'].gsub(/\s/,'|') : 'database'
46
+ Regexp.new(names)
47
+ end
48
+
49
+ def reload
50
+ wipe
51
+ files = Dir.glob(path)
52
+ begin
53
+ old_verbose, $VERBOSE = $VERBOSE, nil
54
+
55
+ files.each do |file|
56
+ name = File.basename(file, '.yml')
57
+ next if name.match(ignored_filenames)
46
58
 
47
- files = Dir.glob(path)
59
+ constant_name = "#{name}Config".gsub('-','_').camelize
60
+ yaml_config = YAML::load(ERB.new(IO.read(file)).result)
48
61
 
49
- begin
50
- old_verbose, $VERBOSE = $VERBOSE, nil
62
+ @ordered_stanza_labels[constant_name] = []
63
+ @ordered_stanza_labels[constant_name] << 'defaults' if yaml_config.key? 'defaults'
64
+ @ordered_stanza_labels[constant_name] += yaml_config.keys.grep(/^defaults\[.*#{environment}/).sort_by{ |a| a.count(',') }
65
+ @ordered_stanza_labels[constant_name] << environment if yaml_config.key? environment
51
66
 
52
- files.each do |file|
53
- name = File.basename(file, '.yml')
54
- next if name.match(ignored_filenames)
67
+ config = @ordered_stanza_labels[constant_name].inject({}) do |acc, label|
68
+ deep_merge(acc,yaml_config[label])
69
+ end
55
70
 
56
- config = YAML.load_file(file)
57
- app_config = config['common'] || {}
58
- app_config.update(config['defaults'] || {})
59
- app_config.update(config[environment] || {})
71
+ ensure_requirements_met_and_ostructify!(config, constant_name )
60
72
 
61
- Object::const_set("#{name}Config".gsub('-','_').camelize.intern, OpenStruct.new(app_config))
73
+ Object::const_set(constant_name.intern, OpenStruct.new(config))
74
+ end
75
+
76
+ raise @errors.to_a.join( "\n" ) unless @errors.empty?
77
+
78
+ ensure
79
+ $VERBOSE = old_verbose
80
+ end
81
+ end
82
+
83
+ private
84
+ # unsets created constants
85
+ def wipe
86
+ unless @ordered_stanza_labels.nil?
87
+ @ordered_stanza_labels.keys.each{|const| Object::const_set(const.intern, nil) }
88
+ end
89
+ @ordered_stanza_labels = {}
90
+ @errors = Set.new
91
+ end
92
+
93
+ # merges two hashes with nested hashes if present
94
+ def deep_merge( hash1, hash2 )
95
+ hash1 = hash1.dup
96
+ ( hash1.keys + hash2.keys ).each do | key |
97
+ if hash1.key?( key ) && hash2.key?( key ) &&
98
+ hash1[key].is_a?( Hash ) && hash2[key].is_a?( Hash )
99
+ hash1[key] = deep_merge( hash1[key], hash2[key] )
100
+ elsif hash2.key?( key )
101
+ hash1[key] = hash2[key]
102
+ end
103
+ end
104
+ hash1
105
+ end
106
+ # Mutator method that does two things:
107
+ # * checks if any of the keys were required and not set. Upon finding
108
+ # it adds key to the error set
109
+ # * recursively sets open structs for deep hashes
110
+ def ensure_requirements_met_and_ostructify!( hash, path )
111
+ hash.each do | key, value |
112
+ case
113
+ when value.respond_to?( :keys ) && value.respond_to?( :values )
114
+ ensure_requirements_met_and_ostructify!( value, path + '.' + key )
115
+ when value == REQUIRED
116
+ @errors << "#{path}.#{key} is REQUIRED"
117
+ end
118
+ hash[key] = OpenStruct.new(value) if value.is_a?( Hash )
119
+ end
62
120
  end
63
- ensure
64
- $VERBOSE = old_verbose
65
121
  end
66
122
  end
123
+
124
+ AutoConfig.reload
metadata CHANGED
@@ -1,26 +1,27 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: autoconfig
3
3
  version: !ruby/object:Gem::Version
4
- hash: 31
4
+ hash: 23
5
5
  prerelease:
6
6
  segments:
7
- - 0
8
7
  - 1
9
- - 2
10
- version: 0.1.2
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - tjbladez
14
+ - timgaleckas
14
15
  autorequire:
15
16
  bindir: bin
16
17
  cert_chain: []
17
18
 
18
- date: 2011-01-10 00:00:00 -06:00
19
+ date: 2011-10-12 00:00:00 -05:00
19
20
  default_executable:
20
21
  dependencies: []
21
22
 
22
23
  description: Automated way to create flexible configuration structures representing your YAML configuration
23
- email: tjbladez@gmail.com
24
+ email: nick@tjbladez.com, tim@galeckas.com
24
25
  executables: []
25
26
 
26
27
  extensions: []