Permalink
Browse files

Implemented merging defaults settings with namespaced settings using …

…default_namespace
  • Loading branch information...
1 parent 9bcaf8e commit c65f38e56003040bf6a9d2379cd71b60285ea02c @ayanko ayanko committed Mar 25, 2011
Showing with 244 additions and 2 deletions.
  1. +53 −0 README.rdoc
  2. +20 −2 lib/settingslogic.rb
  3. +33 −0 spec/settings2.yml
  4. +3 −0 spec/settings4.rb
  5. +134 −0 spec/settingslogic_spec.rb
  6. +1 −0 spec/spec_helper.rb
View
@@ -139,6 +139,59 @@ Finally, you can reload all your settings later as well:
This is useful if you want to support changing your settings YAML without restarting your app.
+=== 5. Default namespace using
+
+Sometimes you want to have some defaults/common section in your settings file and inherit it for each namespace:
+
+ # app/config/application.yml
+ defaults:
+ cool:
+ saweet: nested settings
+ neat_setting: 24
+ awesome_setting: <%= "Did you know 5 + 5 = #{5 + 5}?" %>
+
+ development:
+ neat_setting: 800
+
+ production:
+
+Just declare default_namespace in your model:
+
+ class Settings < Settingslogic
+ source "#{Rails.root}/config/application.yml"
+ namespace Rails.env
+ default_namespace "defaults"
+ end
+
+Access it
+
+ >> Rails.env
+ => "development"
+ >> Settings.cool.saweet
+ => "nested settings"
+ >> Settings.neat_setting
+ => 800
+
+ >> Rails.env
+ => "production"
+ >> Settings.cool.saweet
+ => "nested settings"
+ >> Settings.neat_setting
+ => 24
+
+ >> Rails.env
+ => "test"
+ >> Settings.cool.saweet
+ => "nested settings"
+ >> Settings.neat_setting
+ => 24
+
+Notes:
+
+* If specified namespace does not exist you will have default settings. So you don't need to write extra borring line if you have a lot of namespaces that just use default settings.
+* If specified default namespace does not exists you will have just settings for your namespace
+* If both (default_namespace and namespace) do not exist you will have all settings from file.
+
== Author
Copyright (c) 2008-2010 {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com],
View
@@ -28,6 +28,14 @@ def source(value = nil)
end
end
+ def default_namespace(value = nil)
+ if value.nil?
+ @default_namespace
+ else
+ @default_namespace = value
+ end
+ end
+
def namespace(value = nil)
if value.nil?
@namespace
@@ -103,8 +111,8 @@ def initialize(hash_or_file = self.class.source, section = nil)
self.replace hash_or_file
else
hash = YAML.load(ERB.new(File.read(hash_or_file)).result).to_hash
- hash = hash[self.class.namespace] if self.class.namespace
- self.replace hash
+ hashes = [self.class.default_namespace, self.class.namespace].compact.map { |k| hash[k] || {} }
+ self.replace( hashes.size == 2 ? hash_deep_merge(*hashes) : hashes.first || hash )
end
@section = section || self.class.source # so end of error says "in application.yml"
create_accessors!
@@ -156,4 +164,14 @@ def #{key}
end
EndEval
end
+
+ # http://api.rubyonrails.org/classes/ActiveSupport/CoreExtensions/Hash/DeepMerge.html
+ def hash_deep_merge(hash, other_hash)
+ hash.merge(other_hash) do |key, oldval, newval|
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? self.hash_deep_merge(oldval, newval) : newval
+ end
+ end
+
end
View
@@ -0,0 +1,33 @@
+defaults:
+ binaries:
+ git: /usr/bin/git
+ mysql: /usr/bin/mysql
+ deep:
+ level: 0
+ h1:
+ level: 1
+ h2:
+ level: 2
+ type: rgb
+ colors:
+ - red
+ - green
+ - blue
+
+production:
+ binaries:
+ mysql: /usr/bin/mysql51
+
+test:
+ deep:
+ h1:
+ list:
+ - a
+ - b
+ h2:
+ type: cmyk
+ colors:
+ - cyan
+ - magenta
+ - yellow
+ - black
View
@@ -0,0 +1,3 @@
+class Settings4 < Settingslogic
+ source "#{File.dirname(__FILE__)}/settings2.yml"
+end
View
@@ -138,4 +138,138 @@ class NoSource < Settingslogic; end
it "should be a hash" do
Settings.send(:instance).should be_is_a(Hash)
end
+
+ describe "without default_namespace and with namespace" do
+ it "should not merge settings from default_namespace" do
+ Settings4.instance_variable_set(:@default_namespace, nil)
+ Settings4.namespace('production')
+ Settings4.reload!
+
+ Settings4.to_hash.should == {
+ 'binaries' => {
+ 'mysql' => "/usr/bin/mysql51",
+ }
+ }
+ end
+
+ it "should have empty settings if namespace does NOT exists" do
+ Settings4.instance_variable_set(:@default_namespace, nil)
+ Settings4.namespace('testing')
+ Settings4.reload!
+ Settings4.to_hash.should == { }
+ end
+ end
+
+ describe "with default_namespace and without namespace" do
+ it "should merge default_namespace and namespace settings" do
+ Settings4.default_namespace('defaults')
+ Settings4.instance_variable_set(:@namespace, nil)
+ Settings4.reload!
+
+ Settings4.to_hash.should == {
+ 'binaries' => {
+ 'git' => "/usr/bin/git",
+ 'mysql' => "/usr/bin/mysql",
+ },
+ 'deep' => {
+ 'level' => 0,
+ 'h1' => {
+ 'level' => 1,
+ 'h2' => {
+ 'level' => 2,
+ 'type' => 'rgb',
+ 'colors' => %w(red green blue)
+ }
+ }
+ }
+ }
+ end
+
+ it "should have empty settings if defaults_namespace does NOT exists" do
+ Settings4.default_namespace('common')
+ Settings4.instance_variable_set(:@namespace, nil)
+ Settings4.reload!
+ Settings4.to_hash.should == { }
+ end
+ end
+
+ describe "with default_namespace and namespace" do
+ it "should merge default_namespace and namespace settings" do
+ Settings4.default_namespace('defaults')
+ Settings4.namespace('production')
+ Settings4.reload!
+
+ Settings4.to_hash.should == {
+ 'binaries' => {
+ 'git' => "/usr/bin/git",
+ 'mysql' => "/usr/bin/mysql51",
+ },
+ 'deep' => {
+ 'level' => 0,
+ 'h1' => {
+ 'level' => 1,
+ 'h2' => {
+ 'level' => 2,
+ 'type' => 'rgb',
+ 'colors' => %w(red green blue)
+ }
+ }
+ }
+ }
+ end
+
+ it "should deep merge complex default_namespace and namespace settings" do
+ Settings4.default_namespace('defaults')
+ Settings4.namespace('test')
+ Settings4.reload!
+
+ Settings4.to_hash.should == {
+ 'binaries' => {
+ 'git' => "/usr/bin/git",
+ 'mysql' => "/usr/bin/mysql",
+ },
+ 'deep' => {
+ 'level' => 0,
+ 'h1' => {
+ 'list' => %w(a b),
+ 'level' => 1,
+ 'h2' => {
+ 'level' => 2,
+ 'type' => 'cmyk',
+ 'colors' => %w(cyan magenta yellow black)
+ }
+ }
+ }
+ }
+ end
+
+ it "should use custome namespace names" do
+ Settings4.default_namespace('production')
+ Settings4.namespace('test')
+ Settings4.reload!
+
+ Settings4.to_hash.should == {
+ 'binaries' => {
+ 'mysql' => "/usr/bin/mysql51",
+ },
+ 'deep' => {
+ 'h1' => {
+ 'list' => %w(a b),
+ 'h2' => {
+ 'type' => 'cmyk',
+ 'colors' => %w(cyan magenta yellow black)
+ }
+ }
+ }
+ }
+ end
+
+ it "should have empty settings if defaults_namespace and namespace does NOT exist" do
+ Settings4.default_namespace('common')
+ Settings4.namespace('testing')
+ Settings4.reload!
+ Settings4.to_hash.should == { }
+ end
+ end
+
end
View
@@ -8,6 +8,7 @@
require 'settings'
require 'settings2'
require 'settings3'
+require 'settings4'
# Needed to test Settings3
Object.send :define_method, 'collides' do

0 comments on commit c65f38e

Please sign in to comment.