Skip to content

Commit

Permalink
Implemented merging defaults settings with namespaced settings using …
Browse files Browse the repository at this point in the history
…default_namespace
  • Loading branch information
ayanko committed Mar 25, 2011
1 parent 9bcaf8e commit c65f38e
Show file tree
Hide file tree
Showing 6 changed files with 244 additions and 2 deletions.
53 changes: 53 additions & 0 deletions README.rdoc
Expand Up @@ -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],
Expand Down
22 changes: 20 additions & 2 deletions lib/settingslogic.rb
Expand Up @@ -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
Expand Down Expand Up @@ -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!
Expand Down Expand Up @@ -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
33 changes: 33 additions & 0 deletions spec/settings2.yml
@@ -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
3 changes: 3 additions & 0 deletions spec/settings4.rb
@@ -0,0 +1,3 @@
class Settings4 < Settingslogic
source "#{File.dirname(__FILE__)}/settings2.yml"
end
134 changes: 134 additions & 0 deletions spec/settingslogic_spec.rb
Expand Up @@ -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
1 change: 1 addition & 0 deletions spec/spec_helper.rb
Expand Up @@ -8,6 +8,7 @@
require 'settings'
require 'settings2'
require 'settings3'
require 'settings4'

# Needed to test Settings3
Object.send :define_method, 'collides' do
Expand Down

0 comments on commit c65f38e

Please sign in to comment.