Skip to content

Commit

Permalink
Support arbitrary sources in Config.load_files and Config.load_and_se…
Browse files Browse the repository at this point in the history
…t_settings (#315)

* Add instructions for running specs

* Backfile test for Pathname support in load_files

There's a .to_s call in there that's likely to support these kinds of
objects, but there wasn't a test for it.

* Rename `file` variables to `source`

In an upcoming change, we'll allow arbirtary sources as parameters to
these methods, instead of just filenames. The method names remain
unchanged for now for backwards compatibility.

* Support arbitrary Source objects in Config.load_files

* Add test for loading HashSource from load_files

* Remove uniq call in load_files

This uniq call causes issues when mixing the types of sources. This
change can technically break some applications that were depending on
the de-deduplication of source files.

* Remove .to_s call in load_files on source

This change effectively conslidates the logic for coercing source
specifications to Source objects into `Config::Options#add_source!`.
For sources specified as Strings or Pathnames, there is effectively no
change, but for sources specified as something else that responds to
`.to_s`, this change could be breaking. In those cases, application
developers can explicitly call `.to_s` on those objects before handing
them to config.

* Add CHANGELOG entry
  • Loading branch information
cjlarose authored Jan 22, 2022
1 parent 9c35a5d commit 3dd6817
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 8 deletions.
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,11 @@

## Unreleased

...
### BREAKING CHANGES

* Support `HashSource` and `EnvSource` instances in `Config.load_files` and `Config.load_and_set_settings`. ([#315](https://github.com/rubyconfig/config/pull/315)). There are a few subtle breaking changes:
* Previously, `Config.load_files` (called from `Config.load_and_set_settings`) would call `.to_s` on each of its arguments. Now, this responsibility is defered to YAMLSource. In effect, if your application passes String or Pathname objects to `Config.load_files`, no changes are necessary, but if you were somehow relying on the `.to_s` call for some other type of object, you'll now need to call `.to_s` on that object before passing it to `Config`.
* Before this change, `Config.load_files` would call `uniq` on its argument array. This call has been removed, so duplicate file paths are not removed before further processing. In some cases, this can cause differences in behavior since later config files override the values in earlier ones. In most cases, it's best to ensure that duplicate paths are not passed to `Config.load_files`.

## 3.1.1

Expand Down
27 changes: 27 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,33 @@ You are very warmly welcome to help. Please follow our [contribution guidelines]

Any and all contributions offered in any form, past present or future are understood to be in complete agreement and acceptance with [MIT](LICENSE) license.

### Running specs

Setup

```sh
bundle install
bundle exec appraisal install
```

List defined appraisals:

```sh
bundle exec appraisal list
```

Run specs for specific appraisal:

```sh
bundle exec appraisal rails-6.1 rspec
```

Run specs for all appraisals:

```sh
bundle exec appraisal rspec
```

## Authors

* [Piotr Kuczynski](http://github.com/pkuczynski)
Expand Down
10 changes: 5 additions & 5 deletions lib/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,12 @@ def self.setup

# Create a populated Options instance from a settings file. If a second file is given, then the sections of that
# file will overwrite existing sections of the first file.
def self.load_files(*files)
def self.load_files(*sources)
config = Options.new

# add settings sources
[files].flatten.compact.uniq.each do |file|
config.add_source!(file.to_s)
[sources].flatten.compact.each do |source|
config.add_source!(source)
end

config.add_source!(Sources::EnvSource.new(ENV)) if Config.use_env
Expand All @@ -50,10 +50,10 @@ def self.load_files(*files)
end

# Loads and sets the settings constant!
def self.load_and_set_settings(*files)
def self.load_and_set_settings(*sources)
name = Config.const_name
Object.send(:remove_const, name) if Object.const_defined?(name)
Object.const_set(name, Config.load_files(files))
Object.const_set(name, Config.load_files(sources))
end

def self.setting_files(config_root, env)
Expand Down
4 changes: 2 additions & 2 deletions lib/config/options.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,15 @@ def empty?

def add_source!(source)
# handle yaml file paths
source = (Sources::YAMLSource.new(source)) if source.is_a?(String)
source = (Sources::YAMLSource.new(source)) if source.is_a?(String) || source.is_a?(Pathname)
source = (Sources::HashSource.new(source)) if source.is_a?(Hash)

@config_sources ||= []
@config_sources << source
end

def prepend_source!(source)
source = (Sources::YAMLSource.new(source)) if source.is_a?(String)
source = (Sources::YAMLSource.new(source)) if source.is_a?(String) || source.is_a?(Pathname)
source = (Sources::HashSource.new(source)) if source.is_a?(Hash)

@config_sources ||= []
Expand Down
29 changes: 29 additions & 0 deletions spec/config_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,35 @@
expect(config.another).to eq("something")
end

it 'should load config files specified as Pathname objects' do
path = Pathname.new(fixture_path).join('settings.yml')
config = Config.load_files(path)
expect(config.server).to eq('google.com')
end

it 'should load config files specified as objects responding to :load' do
source = double 'source'
allow(source).to receive(:load) do
{ 'server' => 'google.com' }
end
config = Config.load_files(source)
expect(config.server).to eq('google.com')
end

it 'should load config from HashSource' do
source = Config::Sources::HashSource.new({ 'server' => 'google.com' })
config = Config.load_files(source)
expect(config.server).to eq('google.com')
end

it 'should load config from files and HashSource' do
file_source = "#{fixture_path}/settings.yml"
hash_source = Config::Sources::HashSource.new({ 'size' => 12 })
config = Config.load_files(file_source, hash_source)
expect(config.server).to eq('google.com')
expect(config.size).to eq(12)
end

it "should load empty config for a missing file path" do
config = Config.load_files("#{fixture_path}/some_file_that_doesnt_exist.yml")
expect(config).to be_empty
Expand Down

0 comments on commit 3dd6817

Please sign in to comment.