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

Add convenience method Dry::Configurable::Settings#replace #117

Open
wants to merge 15 commits into
base: main
Choose a base branch
from
10 changes: 5 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
# frozen_string_literal: true

source 'https://rubygems.org'
source "https://rubygems.org"

eval_gemfile 'Gemfile.devtools'
eval_gemfile "Gemfile.devtools"

gemspec

group :benchmarks do
gem 'benchmark-ips'
gem "benchmark-ips"
end

group :tools do
gem 'hotch'
gem 'pry-byebug', platform: :mri
gem "hotch"
gem "pry-byebug", platform: :mri
end
4 changes: 2 additions & 2 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# frozen_string_literal: true

require 'rspec/core/rake_task'
require "rspec/core/rake_task"

desc 'Run all specs in spec directory'
desc "Run all specs in spec directory"
RSpec::Core::RakeTask.new(:spec)

task default: :spec
8 changes: 4 additions & 4 deletions bin/console
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

require 'bundler/setup'
require 'dry/configurable'
require "bundler/setup"
require "dry/configurable"

begin
require 'pry-byebug'
require "pry-byebug"
Pry.start
rescue LoadError
require 'irb'
require "irb"
IRB.start
end
5 changes: 5 additions & 0 deletions lib/dry/configurable/setting.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,11 @@ def initialize(name, input: Undefined, default: Undefined, **options)
evaluate if input_defined?
end

def merge(setting)
input.merge(setting.input) if input_defined?
evaluate
end
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved

# @api private
def input_defined?
!input.equal?(Undefined)
Expand Down
29 changes: 29 additions & 0 deletions lib/dry/configurable/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,35 @@ def initialize(elements = EMPTY_ARRAY)
initialize_elements(elements)
end

def replace(settings)
unless settings.is_a? Dry::Configurable::Settings
raise ArgumentError, "settings must be a Dry::Configurable::Settings"
end

settings.each do |setting|
self << setting
end
end

def merge(settings)
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved
unless settings.is_a? Dry::Configurable::Settings
raise ArgumentError, "settings must be a Dry::Configurable::Settings"
end

settings.each do |setting|
merge_setting(setting)
end
end

# @api private
def merge_setting(setting)
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved
if key?(setting.name)
elements[setting.name].merge(setting)
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved
else
self << setting
end
end

# @api private
def <<(setting)
elements[setting.name] = setting
Expand Down
62 changes: 62 additions & 0 deletions spec/integration/dry/configurable/setting_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,68 @@

include_context "configurable behavior"

context "can be configured with another class's settings" do
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved
let(:other_klass) do
Class.new do
extend Dry::Configurable
end
end

it "should deep merge" do
klass.setting :database do
setting :type, "postgresql"
setting :host, "remote"
setting :port, 12_345
setting :deep do
setting :one, 1
setting :two, 2
end
end

other_klass.setting :database do
setting :host, "localhost"
setting :port
setting :deep, 3
end

other_klass._settings.merge(klass._settings.dup)
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved
expect(other_klass.config.database.host).to eql("localhost")
expect(other_klass.config.database.port).to eql(nil)
expect(other_klass.config.database.type).to eql("postgresql")
expect(other_klass.config.database.deep).to eql(3)
end

it "replaces with each" do
klass.setting :hello, "world"
klass._settings.each do |setting|
other_klass._settings << setting.dup
end
expect(other_klass.config.hello).to eql("world")
end
skinnyjames marked this conversation as resolved.
Show resolved Hide resolved

it "replaces with replace" do
klass.setting :hello, "world"
other_klass._settings.replace(klass._settings.dup)
expect(other_klass.config.hello).to eql("world")
end

it "deep replace" do
klass.setting :database do
setting :dsn, "localhost"
end

other_klass._settings.replace(klass._settings.dup)
expect(other_klass.config.database.dsn).to eql("localhost")
end

it "throws an error if the settings aren't Dry::Configurable::Settings" do
klass.setting :hello, "world"
expect { other_klass._settings.replace(klass) }.to raise_error do |error|
expect(error.class).to be(ArgumentError)
end
end
end

context "with a subclass" do
let(:subclass) do
Class.new(klass)
Expand Down