autoconfig 0.1.2 → 1.0.0

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