Skip to content

Commit

Permalink
Only allow config to be applied once
Browse files Browse the repository at this point in the history
Ensures that services such as Sequel are only ever initialised once.
This is important as Sequel initialisation is not idempotent and can
only be ran once.
  • Loading branch information
apexatoll committed Oct 28, 2023
1 parent 8b5bf55 commit c7f8511
Show file tree
Hide file tree
Showing 4 changed files with 164 additions and 105 deletions.
7 changes: 7 additions & 0 deletions lib/kangaru/application.rb
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,17 @@ def configure(env = nil, &block)
block.call(config) if current_env?(env)
end

def configured?
@configured == true
end

def apply_config!
raise "config already applied" if configured?

config.import_external_config!

@database = setup_database!
@configured = true
end

def run!(argv)
Expand Down
4 changes: 4 additions & 0 deletions sig/kangaru/application.rbs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ module Kangaru

def configure: (?Symbol?) { (Config) -> void } -> void

def configured?: -> bool

def apply_config!: -> void

def run!: (Array[String]) -> void
Expand All @@ -20,6 +22,8 @@ module Kangaru

private

@configured: bool

def current_env?: (Symbol?) -> bool

attr_reader autoloader: Zeitwerk::Loader
Expand Down
136 changes: 80 additions & 56 deletions spec/features/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,90 +27,114 @@ module SomeGem
gem.main_file.write(main_file)
end

context "when only one configuration call is made" do
let(:configuration) do
configure_block(env:) do
<<~RUBY
config.test.value = :#{value}
RUBY
describe "setting config" do
context "when only one configuration call is made" do
let(:configuration) do
configure_block(env:) do
<<~RUBY
config.test.value = :#{value}
RUBY
end
end
end

let(:value) { :foobar }
let(:value) { :foobar }

context "and no env is specified" do
let(:env) { nil }
context "and no env is specified" do
let(:env) { nil }

it "sets the specified configuration" do
gem.load!
expect(application_config).to eq(value:)
it "sets the specified configuration" do
gem.load!
expect(application_config).to eq(value:)
end
end
end

context "and env is specified", :stub_env do
let(:env) { :some_env }
context "and env is specified", :stub_env do
let(:env) { :some_env }

context "and specified env is not current env" do
let(:current_env) { :another_env }
context "and specified env is not current env" do
let(:current_env) { :another_env }

it "does not set the config" do
gem.load!
expect(application_config).to be_empty
it "does not set the config" do
gem.load!
expect(application_config).to be_empty
end
end
end

context "and specified env is current env" do
let(:current_env) { env }
context "and specified env is current env" do
let(:current_env) { env }

it "sets the config" do
gem.load!
expect(application_config).to eq(value:)
it "sets the config" do
gem.load!
expect(application_config).to eq(value:)
end
end
end
end
end

context "when multiple configuration calls are made", :stub_env do
let(:configuration) do
<<~RUBY
#{base_configuration}
#{env_configuration}
RUBY
end

let(:base_configuration) do
configure_block(env: nil) do
context "when multiple configuration calls are made", :stub_env do
let(:configuration) do
<<~RUBY
config.test.value = :base
#{base_configuration}
#{env_configuration}
RUBY
end
end

let(:env_configuration) do
configure_block(env: configured_env) do
<<~RUBY
config.test.value = :env
RUBY
let(:base_configuration) do
configure_block(env: nil) do
<<~RUBY
config.test.value = :base
RUBY
end
end

let(:env_configuration) do
configure_block(env: configured_env) do
<<~RUBY
config.test.value = :env
RUBY
end
end

let(:configured_env) { :some_env }

context "and not in configured env" do
let(:current_env) { :another_env }

it "sets the base value" do
gem.load!
expect(application_config).to eq(value: :base)
end
end

context "and in configured env" do
let(:current_env) { configured_env }

it "sets the env overridden value" do
gem.load!
expect(application_config).to eq(value: :env)
end
end
end
end

describe "applying config" do
subject(:apply_config!) { SomeGem.apply_config! }

let(:configured_env) { :some_env }
let(:configuration) { nil }

context "and not in configured env" do
let(:current_env) { :another_env }
before { gem.load! }

it "sets the base value" do
gem.load!
expect(application_config).to eq(value: :base)
context "when config is applied once" do
it "does not raise any errors" do
expect { apply_config! }.not_to raise_error
end
end

context "and in configured env" do
let(:current_env) { configured_env }
context "when config is applied more than once" do
before { SomeGem.apply_config! }

it "sets the env overridden value" do
gem.load!
expect(application_config).to eq(value: :env)
it "raise an error" do
expect { apply_config! }.to raise_error("config already applied")
end
end
end
Expand Down
122 changes: 73 additions & 49 deletions spec/kangaru/application_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -186,70 +186,94 @@
allow(application.config).to receive(:import_external_config!)
end

context "when no configuration is set" do
it "does not raise any errors" do
expect { apply_config! }.not_to raise_error
end
context "when config has already been applied" do
before { application.instance_variable_set(:@configured, true) }

it "does not create a database" do
apply_config!
expect(Kangaru::Database).not_to have_received(:new)
it "raises an error" do
expect { apply_config! }.to raise_error("config already applied")
end
end

it "does not set the application database instance" do
expect { apply_config! }
.not_to change { application.database }
.from(nil)
end
context "when config has not already been applied" do
context "and no configuration is set" do
it "does not raise any errors" do
expect { apply_config! }.not_to raise_error
end

it "runs the external config import" do
apply_config!
it "does not create a database" do
apply_config!
expect(Kangaru::Database).not_to have_received(:new)
end

expect(application.config)
.to have_received(:import_external_config!)
.once
end
end
it "does not set the application database instance" do
expect { apply_config! }
.not_to change { application.database }
.from(nil)
end

context "when configuration sets a database adaptor" do
before do
application.config.database.adaptor = adaptor
end
it "runs the external config import" do
apply_config!

let(:adaptor) { :sqlite }
expect(application.config)
.to have_received(:import_external_config!)
.once
end

it "does not raise any errors" do
expect { apply_config! }.not_to raise_error
it "marks the application as configured" do
expect { apply_config! }
.to change { application.configured? }
.from(false)
.to(true)
end
end

it "creates a database" do
apply_config!
expect(Kangaru::Database).to have_received(:new).once
end
context "and configuration sets a database adaptor" do
before do
application.config.database.adaptor = adaptor
end

it "sets up the database" do
apply_config!
expect(database).to have_received(:setup!).once
end
let(:adaptor) { :sqlite }

it "migrates the database" do
apply_config!
expect(database).to have_received(:migrate!).once
end
it "does not raise any errors" do
expect { apply_config! }.not_to raise_error
end

it "sets the application database instance" do
expect { apply_config! }
.to change { application.database }
.from(nil)
.to(database)
end
it "creates a database" do
apply_config!
expect(Kangaru::Database).to have_received(:new).once
end

it "runs the external config import" do
apply_config!
it "sets up the database" do
apply_config!
expect(database).to have_received(:setup!).once
end

expect(application.config)
.to have_received(:import_external_config!)
.once
it "migrates the database" do
apply_config!
expect(database).to have_received(:migrate!).once
end

it "sets the application database instance" do
expect { apply_config! }
.to change { application.database }
.from(nil)
.to(database)
end

it "runs the external config import" do
apply_config!

expect(application.config)
.to have_received(:import_external_config!)
.once
end

it "marks the application as configured" do
expect { apply_config! }
.to change { application.configured? }
.from(false)
.to(true)
end
end
end
end
Expand Down

0 comments on commit c7f8511

Please sign in to comment.