Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge nil values by default #196

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

## Unreleased

...
* **WARNING:** `nil` values will from now on overwrite an existing value when merging configs! This change of behavior can be reverted via `config.merge_nil_values = false` in your Config initializer ([#196](https://github.com/railsconfig/config/pull/196))

## 1.6.1

Expand Down
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,27 @@ located at `config/initializers/config.rb`.

* `overwrite_arrays` - overwrite arrays found in previously loaded settings file. Default: `true`
* `knockout_prefix` - ability to remove elements of the array set in earlier loaded settings file. Makes sense only when `overwrite_arrays = false`, otherwise array settings would be overwritten by default. Default: `nil`
* `merge_nil_values` - `nil` values will overwrite an existing value when merging configs. Default: `true`.

```ruby
# merge_nil_values is true by default
c = Config.load_files("./spec/fixtures/development.yml") # => #<Config::Options size=2, ...>
c.size # => 2
c.merge!(size: nil) => #<Config::Options size=nil, ...>
c.size # => nil
```

```ruby
# To reject nil values when merging settings:
Config.setup do |config|
config.merge_nil_values = false
end

c = Config.load_files("./spec/fixtures/development.yml") # => #<Config::Options size=2, ...>
c.size # => 2
c.merge!(size: nil) => #<Config::Options size=nil, ...>
c.size # => 2
```

Check [Deep Merge](https://github.com/danielsdeleo/deep_merge) for more details.

Expand Down
3 changes: 2 additions & 1 deletion lib/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@ module Config
@@fail_on_missing = false

# deep_merge options
mattr_accessor :knockout_prefix, :overwrite_arrays
mattr_accessor :knockout_prefix, :merge_nil_values, :overwrite_arrays
@@knockout_prefix = nil
@@merge_nil_values = true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't fix this cop offense to match the style of the existing code and thereby not change style in this feature.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍

@@overwrite_arrays = true

def self.setup
Expand Down
15 changes: 11 additions & 4 deletions lib/config/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,14 @@ def reload!
if conf.empty?
conf = source_conf
else
DeepMerge.deep_merge!(source_conf,
DeepMerge.deep_merge!(
source_conf,
conf,
preserve_unmergeables: false,
knockout_prefix: Config.knockout_prefix,
overwrite_arrays: Config.overwrite_arrays)
overwrite_arrays: Config.overwrite_arrays,
merge_nil_values: Config.merge_nil_values
)
end
end

Expand Down Expand Up @@ -125,10 +128,14 @@ def to_json(*args)

def merge!(hash)
current = to_hash
DeepMerge.deep_merge!(hash.dup,
DeepMerge.deep_merge!(
hash.dup,
current,
preserve_unmergeables: false,
overwrite_arrays: Config.overwrite_arrays)
knockout_prefix: Config.knockout_prefix,
overwrite_arrays: Config.overwrite_arrays,
merge_nil_values: Config.merge_nil_values
)
marshal_load(__convert(current).marshal_dump)
self
end
Expand Down
5 changes: 5 additions & 0 deletions lib/generators/config/templates/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@
#
# config.knockout_prefix = nil

# Overwrite an existing value when merging a `nil` value.
# When set to `false`, the existing value is retained after merge.
#
# config.merge_nil_values = true

# Overwrite arrays found in previously loaded settings file. When set to `false`, arrays will be merged.
#
# config.overwrite_arrays = true
Expand Down
59 changes: 55 additions & 4 deletions spec/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,8 @@

context "Merging nested hash at runtime" do
let(:config) { Config.load_files("#{fixture_path}/deep_merge/config1.yml") }
let(:hash) { { :inner => { :something1 => 'changed1', :something3 => 'changed3' } } }
let(:hash) { { inner: { something1: 'changed1', something3: 'changed3' } } }
let(:hash_with_nil) { { inner: { something1: nil } } }

it 'should preserve first level keys' do
expect { config.merge!(hash) }.to_not change { config.keys }
Expand All @@ -210,11 +211,61 @@
end

it 'should add new nested key' do
expect { config.merge!(hash) }.to change { config.inner.something3 }.from(nil).to("changed3")
expect { config.merge!(hash) }
.to change { config.inner.something3 }.from(nil).to('changed3')
end

it 'should rewrite a merged value' do
expect { config.merge!(hash) }.to change { config.inner.something1 }.from('blah1').to('changed1')
expect { config.merge!(hash) }
.to change { config.inner.something1 }.from('blah1').to('changed1')
end

it 'should update a string to nil ' do
expect { config.merge!(hash_with_nil) }
.to change { config.inner.something1 }.from('blah1').to(nil)
end

it 'should update something nil to true' do
expect { config.merge!(inner: { somethingnil: true }) }
.to change { config.inner.somethingnil }.from(nil).to(true)
end

it 'should update something nil to false' do
expect { config.merge!(inner: { somethingnil: false }) }
.to change { config.inner.somethingnil }.from(nil).to(false)
end

it 'should update something false to true' do
expect { config.merge!(inner: { somethingfalse: true }) }
.to change { config.inner.somethingfalse }.from(false).to(true)
end

it 'should update something false to nil' do
expect { config.merge!(inner: { somethingfalse: nil }) }
.to change { config.inner.somethingfalse }.from(false).to(nil)
end

it 'should update something true to false' do
expect { config.merge!(inner: { somethingtrue: false }) }
.to change { config.inner.somethingtrue }.from(true).to(false)
end

it 'should update something true to nil' do
expect { config.merge!(inner: { somethingtrue: nil }) }
.to change { config.inner.somethingtrue }.from(true).to(nil)
end

context 'with Config.merge_nil_values = false' do
let(:config) do
Config.merge_nil_values = false
Config.load_files("#{fixture_path}/deep_merge/config1.yml")
end

it 'should not overwrite values with nil' do
old_value = config.inner.something1
config.merge!(hash_with_nil)
expect(config.inner.something1).to eq(old_value)
end
end
end

Expand Down Expand Up @@ -355,7 +406,7 @@
end

it 'should merge hashes from multiple configs' do
expect(config.inner.marshal_dump.keys.size).to eq(3)
expect(config.inner.marshal_dump.keys.size).to eq(6)
expect(config.inner2.inner2_inner.marshal_dump.keys.size).to eq(3)
end

Expand Down
3 changes: 3 additions & 0 deletions spec/fixtures/deep_merge/config1.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,9 @@ server: google.com
inner:
something1: "blah1"
something2: "blah2"
somethingnil:
somethingfalse: false
somethingtrue: true

inner2:
inner2_inner:
Expand Down