mc-settings 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,13 +1,18 @@
1
1
  = Application Settings Manager
2
2
 
3
+ == Summary
4
+
3
5
  This gem provides an easy and capistrano-friendly way to manage application configuration across
4
6
  multiple environments, such as development, QA, staging, production, etc.
5
7
 
6
8
  Applications typically rely on configuration settings, such as host names, URLs, usernames and many
7
- more. Some change between environemnts, some do not. This gem makes managing this hierarchy of
8
- settings values easy and provides convenient and compact syntax to access the settings.
9
+ more. Some change between environemnts, some do not. This gem assumes that application configuration
10
+ is represented by a Hash of arbitrary depth, and provides convenient and compact syntax to access the
11
+ settings through a singleton instance inside Setting class.
9
12
 
10
- Configuration is stored in YAML files, in the following format, with it's top level structure being a hash with keys being the names of individual settings. For example:
13
+ Configuration is stored in one or more YAML files with the top-level data structure being a Hash,
14
+ with keys being the names of individual settings. For example, consider the following sample
15
+ application configuration file:
11
16
 
12
17
  tax:
13
18
  default: 0.0
@@ -22,55 +27,122 @@ Configuration is stored in YAML files, in the following format, with it's top le
22
27
  - 'NY'
23
28
  math_pi: 3.14159526
24
29
 
25
- etc.
30
+ Setting Gem provides Setting.load(..) method to load configuration from files in a way that allows
31
+ some configuration files to override previously loaded values, and then offers a simple method API
32
+ to access the values, for example Setting.tax(:california) or Setting.tax. Supporting default values
33
+ in 2nd, 3rd, etc.. -level hashes is one of the advantages of using this gem.
34
+
35
+ By loading configuration from files, Setting is inherently compatible with Capistrano deployment
36
+ methodology, where a certain set of files may become "activated" by simply sym-linking them into
37
+ the appropriate settings folder.
38
+
39
+ Note: using example above, "1st level" hash is the one with keys "tax", "states" and "math_pi".
40
+ 2nd-level hash is, for example, the tax definition one, with keys "default" and "california".
26
41
 
27
- == Usage
42
+ == Usage in Code
28
43
 
29
- Once settings are initialized (see below), they can be used in code in the following way:
44
+ Once configuration is initialized using Setting#load or Setting#reload methods (see below), they can be used in
45
+ code in the following way:
30
46
 
31
- * Setting.key_name is optimized to return default value if available.
32
- * Setting.key_name(:sub_key_name) returns value from the 2nd level hash if available.
33
- * Setting[:key_name] & Setting['key_name'] return entire 2nd-level hash without any regard for whether default value exists.
47
+ * Setting.key_name is optimized to return default value if available instead of a Hash.
48
+ * Setting.key_name(:sub_key_name) returns a value from the 2nd level hash.
49
+ * Setting.key_name(:sub_key_name, :sub_sub_key_name) returns value from the 3rd level hash if available. The algorithm is recursive, so only the maximum method stack depth will limit the number of nested hash values you can access this way.
50
+ * Special syntax Setting[:key_name], Setting[:key_name][:sub_key_name], etc also supported. This method, however, does not support default values (see below).
34
51
 
35
- For example, given the above YAML file:
52
+ Method notation is recommended over square bracket notation for acessing single values. However, square bracket notation
53
+ may be useful when you want to fetch the entire 2nd level hash that includes the default value, instead of the
54
+ default value itself.
55
+
56
+ For example, given the above YAML file, you can access the settings in your code as follows:
36
57
 
37
58
  Setting.tax => 0.0
38
59
  Setting.tax(:california) => 7.5
39
- Setting[:tax] => { 'default' => 0.0, 'california' => 7.5 }
60
+ Setting.math_pi => 3.14159526
61
+ Setting[:math_pi] => 3.14159526
40
62
  Setting.states => [ 'CA', 'WA', 'NY' ]
41
63
  Setting.states['ship_to'] => [ 'CA', 'NY' ]
42
64
 
43
- The following usage is supported for backwards compatibility, but is deprecated and should
44
- not be used for new projects:
65
+ Method calling notation allows passing an array of keys for nested hashes. It also supports default values
66
+ if provided.
67
+
68
+ Setting.tax => 0.0
69
+
70
+ Square bracket syntax returns the actual 2nd-level hash, without any regard for a default value:
45
71
 
46
- Setting.available_settings['key_name']
47
- Setting.available_settings['key_name']['sub_key_name']
72
+ Setting[:tax] => { 'default' => 0.0, 'california' => 7.5 }
48
73
 
49
- == Settings Loading
74
+ == Loading Settings
50
75
 
51
- This gem should be initialized in your environment.rb (if using Rails), or in any other
52
- application initialization block.
76
+ The gem should be initialized in your environment.rb (if using Rails), or in any other
77
+ application initialization block. Setting.load() method is provided for loading settings, and it
78
+ can be called only once in application lifecycle, or it will throw an exception. If you need to reload
79
+ settings completely, you can use reload() method with similar arguments.
53
80
 
54
81
  Consider an example:
55
82
 
56
- Setting.load(:files => ["default.yml", "environments/#{Rails.env}.yml"],
57
- :path => "#{Rails.root}/config/settings",
83
+ Setting.load(:path => "#{Rails.root}/config/settings",
84
+ :files => ["default.yml", "environments/#{Rails.env}.yml"],
58
85
  :local => true)
59
86
 
60
- This parameter hash tells Setting to load settings hashes, in order, from the following
61
- files under "development" (assuming you have two files "custom.yml" and "secret.yml" under your
62
- RAILS_ROOT/config/settings/local folder):
87
+ The argument is a parameter hash that configures which YML files to load, and in what order, and from where.
88
+
89
+ * :path specifies the "root" folder where settings files will be loaded from
90
+ * :files is an array that lists file names relative to the :path. In the example above, settings folder contains subfolder "environments" where Rails-specific environment files are located (such as "development.yml", "staging.yml", "production.yml", etc)
91
+ * :local can be optionally specified as a true value, and if specified Setting gem will load all *.yml files that live under the :path/local folder.
92
+
93
+ Below is list of YML files loaded in order specified in the above example, assuming that "development" is
94
+ the Rails environment, and "local" folder exists with 3 additional YML files in it:
63
95
 
64
96
  config/settings/default.yml
65
97
  config/settings/environments/development.yml
66
- config/settings/local/custom.yml
67
- config/settings/local/secret.yml
98
+ config/settings/local/authorize-net.yml
99
+ config/settings/local/paypal.yml
100
+ config/settings/local/other.yml
101
+
102
+ Each YML file defines a ruby Hash. During file loading, the hashes are merged, so that values loaded in early files may
103
+ be overwritten by values in subsequent files. This is deliberate and by design: it allows you to create
104
+ small "override" files for each environment, or even each machine you want to deploy to. Exactly how you split your
105
+ application settings in files is up to you.
106
+
107
+ == Nested Hashes and Default Values
108
+
109
+ MC Setting gem provides a convenient way to access nested values, including full support for the default values within
110
+ nested hashes (as of 0.1.1).
111
+
112
+ Consider the following nested hash example:
113
+
114
+ default.yml:
115
+
116
+ services:
117
+ inventory:
118
+ url: http://ims.mycompany.com:3443/inventory_manager
119
+ name: Inventory Management
120
+ shipping:
121
+ url: http://ship.mycompany.com:3443/shipper
122
+ name: Shipping
123
+
124
+ Setting.load(:files => ['default.yml'], :path => ...)
125
+
126
+ Setting.tax(:inventory) => { :url => "http://localhost:3443/inventory_manager" :name => "Inventory Management"}
127
+ Setting.tax(:inventory, :url) => "http://localhost:3443/inventory_manager"
128
+
129
+ staging.yml is changing URLs for services so they work in the staging environment. Service URLs have been changed for
130
+ staging only:
131
+
132
+ services:
133
+ inventory:
134
+ url: http://localhost:8009/inventory_manager
135
+ shipping:
136
+ url: http://localhost:8008/shipper
137
+
138
+ Setting.load(:files => ['default.yml', 'staging.yml'], :path => ...)
139
+
140
+ Setting.tax(:inventory) => { :url => "http://localhost:8009/inventory_manager" :name => "Inventory Management"}
141
+ Setting.tax(:inventory, :url) => "http://localhost:8008/inventory_manager"
68
142
 
69
- The following is the sequence of loading explained:
143
+ == Capistrano Recommendations
70
144
 
71
- * Read all files in :files list in order, and load their settings.
72
- * Last file loaded overrides recursively previous value (if already exist).
73
- * Load all #{path}/local/*.yml files to Settings, overriding recursively common keys. Files are loaded in order sorted by name.
145
+ TBD.
74
146
 
75
147
  == Copyright
76
148
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.1.2
@@ -111,7 +111,7 @@ class Setting
111
111
  if block_given?
112
112
  v = yield(v, args)
113
113
  end
114
-
114
+
115
115
  if v.is_a?(Fixnum) && bool
116
116
  v.to_i > 0
117
117
  else
@@ -123,7 +123,7 @@ class Setting
123
123
  # contains 'default' value, or just 1 element.
124
124
 
125
125
  def collapse_hashes(v, args)
126
- if v.is_a?(Hash)
126
+ out = if v.is_a?(Hash)
127
127
  if args.empty?
128
128
  if v.has_key?("default")
129
129
  v['default'].nil? ? "" : v['default']
@@ -133,11 +133,17 @@ class Setting
133
133
  v
134
134
  end
135
135
  else
136
- v[args[0].to_s]
136
+ v[args.shift.to_s]
137
137
  end
138
138
  else
139
139
  v
140
140
  end
141
+
142
+ if out.is_a?(Hash)
143
+ collapse_hashes(out, args)
144
+ else
145
+ out
146
+ end
141
147
  end
142
148
 
143
149
  def loaded?
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{mc-settings}
8
- s.version = "0.1.1"
8
+ s.version = "0.1.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Edwin Cruz", "Colin Shield"]
12
- s.date = %q{2011-01-14}
12
+ s.date = %q{2011-01-18}
13
13
  s.description = %q{implement custom keys indenendently of environment}
14
14
  s.email = %q{rubydev@modcloth.com}
15
15
  s.extra_rdoc_files = [
@@ -27,6 +27,7 @@ Gem::Specification.new do |s|
27
27
  "lib/mc-settings.rb",
28
28
  "lib/setting.rb",
29
29
  "mc-settings.gemspec",
30
+ "spec/fixtures/joes-colors.yml",
30
31
  "spec/fixtures/sample.yml",
31
32
  "spec/mc_settings_spec.rb",
32
33
  "spec/spec_helper.rb",
@@ -0,0 +1,9 @@
1
+ color:
2
+ pants:
3
+ favorite:
4
+ :orange
5
+ shorts:
6
+ favorite:
7
+ :white
8
+ default:
9
+ :stripes
@@ -14,3 +14,22 @@ boolean_true:
14
14
  boolean_false:
15
15
  default: false
16
16
  negated: true
17
+ color:
18
+ default:
19
+ :grey
20
+ pants:
21
+ default:
22
+ :purple
23
+ favorite:
24
+ :red
25
+ school:
26
+ :blue
27
+ shorts:
28
+ default:
29
+ :plaid
30
+ favorite:
31
+ :yellow
32
+ school:
33
+ :black
34
+
35
+
@@ -6,8 +6,8 @@ describe Setting do
6
6
  before :each do
7
7
  stub_setting_files
8
8
  Setting.reload(
9
- :files => ["default.yml", "environments/test.yml"],
10
9
  :path => "config/settings",
10
+ :files => ["default.yml", "environments/test.yml"],
11
11
  :local => true)
12
12
  end
13
13
 
@@ -22,15 +22,11 @@ describe Setting do
22
22
  end
23
23
 
24
24
  it "handles multiple values" do
25
- Setting['six'].should == {"default"=>"default value", "extra"=>"recursively overriden", "deep_level"=>{"value"=>"even deeper level"}}
25
+ Setting[:six].should == {"default"=>"default value", "extra"=>"recursively overriden", "deep_level"=>{"value"=>"even deeper level"}}
26
26
  Setting.available_settings['six']['default'].should == "default value"
27
27
  Setting.seven.should == "seven from custom"
28
28
  end
29
29
 
30
- it "should support symbols as keys" do
31
- Setting[:six].should == {"default"=>"default value", "extra"=>"recursively overriden"}
32
- end
33
-
34
30
  it "handles default key" do
35
31
  Setting.default_setting.should == 1
36
32
  Setting['seven']['default'].should == "seven from custom"
@@ -50,7 +46,7 @@ describe Setting do
50
46
 
51
47
  it "should merge keys recursivelly" do
52
48
  Setting.six(:extra).should == "recursively overriden"
53
- Setting.six(:deep_level)[:value] = "even deeper level"
49
+ Setting.six(:deep_level, :value).should == "even deeper level"
54
50
  end
55
51
 
56
52
  it "should create keys if it does not exist" do
@@ -62,45 +58,35 @@ describe Setting do
62
58
  it "should keep its values" do
63
59
  3.times do |time|
64
60
  Thread.new {
65
- Proc.new{
66
- Setting.available_settings.shoud_not be_empty
67
- }
61
+ Setting.available_settings.shoud_not be_empty
68
62
  }
69
63
  end
70
64
  end
71
65
  end
72
66
 
73
- context "When running inside a proc" do
74
- it "should keep its values" do
75
- Proc.new{
76
- Setting.available_settings.shoud_not be_empty
77
- }
78
- end
79
- end
80
-
81
67
  context "Test from file" do
82
68
  before :each do
83
69
  Setting.reload(
84
- :files => ['sample.yml'],
85
- :path => File.join(File.dirname(__FILE__)) + '/fixtures'
70
+ :path => File.join(File.dirname(__FILE__)) + '/fixtures',
71
+ :files => ['sample.yml']
86
72
  )
87
73
  end
88
74
 
89
75
  it 'should support [] syntax' do
90
76
  Setting['tax']['default'].should == 0.0
91
- Setting['tax'].should == {'default' => 0.0, 'california' => 7.5}
77
+ Setting['tax'].should == { 'default' => 0.0, 'california' => 7.5 }
92
78
  end
93
79
 
94
80
  it 'should support method invocation syntax' do
95
81
  Setting.tax.should == 0.0
96
82
 
97
- Setting.tax(:default).should == Setting.tax
98
- Setting.tax('default').should == Setting.tax
99
- Setting.tax(:california).should == 7.5
83
+ Setting.tax(:default).should == Setting.tax
84
+ Setting.tax('default').should == Setting.tax
85
+ Setting.tax(:california).should == 7.5
100
86
 
101
- Setting.states.should == ['CA', 'WA', 'NY']
102
- Setting.states(:default).should == Setting.states
103
- Setting.states(:ship_to).should == ['CA', 'NY']
87
+ Setting.states.should == ['CA', 'WA', 'NY']
88
+ Setting.states(:default).should == Setting.states
89
+ Setting.states(:ship_to).should == ['CA', 'NY']
104
90
  end
105
91
 
106
92
  it 'should correctly process Boolean values' do
@@ -110,6 +96,28 @@ describe Setting do
110
96
  Setting.boolean_false?(:default).should be(false)
111
97
  Setting.boolean_false?(:negated).should be(true)
112
98
  end
99
+ end
100
+
101
+ context "Test recursive overrides and nested hashes" do
102
+ before :each do
103
+ Setting.reload(
104
+ :path => File.join(File.dirname(__FILE__)) + '/fixtures',
105
+ :files => ['sample.yml', 'joes-colors.yml']
106
+ )
107
+ end
108
+
109
+ it 'should override colors with Joes and support nested hashes' do
110
+ Setting.color.should == :grey # default
111
+ Setting.color(:pants).should == :purple # default
112
+
113
+ Setting.color(:pants, :school).should == :blue # in sample
114
+ Setting.color(:pants, :favorite).should == :orange # joes override
115
+
116
+ Setting.color(:shorts, :school).should == :black # in sample
117
+ Setting.color(:shorts, :favorite).should == :white # joe's override
118
+
119
+ Setting.color(:shorts).should == :stripes # joe's override of default
120
+ end
113
121
 
114
122
  end
115
123
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mc-settings
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 31
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 1
9
- - 1
10
- version: 0.1.1
9
+ - 2
10
+ version: 0.1.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Edwin Cruz
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-01-14 00:00:00 -08:00
19
+ date: 2011-01-18 00:00:00 -08:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
@@ -113,6 +113,7 @@ files:
113
113
  - lib/mc-settings.rb
114
114
  - lib/setting.rb
115
115
  - mc-settings.gemspec
116
+ - spec/fixtures/joes-colors.yml
116
117
  - spec/fixtures/sample.yml
117
118
  - spec/mc_settings_spec.rb
118
119
  - spec/spec_helper.rb