Commit ef464d5f authored by Robert Speicher's avatar Robert Speicher 🌴 Committed by Clement Ho

Merge branch 'sh-refactor-current-settings' into 'master'

If migrations are pending, make CurrentSettings use existing values and populate missing columns with defaults

See merge request !12253
parent d2640841
......@@ -10,43 +10,49 @@ module Gitlab
delegate :sidekiq_throttling_enabled?, to: :current_application_settings
def fake_application_settings
OpenStruct.new(::ApplicationSetting.defaults)
def fake_application_settings(defaults = ApplicationSetting.defaults)
FakeApplicationSettings.new(defaults)
end
private
def ensure_application_settings!
unless ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
settings = retrieve_settings_from_database?
end
return in_memory_application_settings if ENV['IN_MEMORY_APPLICATION_SETTINGS'] == 'true'
settings || in_memory_application_settings
cached_application_settings || uncached_application_settings
end
def retrieve_settings_from_database?
settings = retrieve_settings_from_database_cache?
return settings if settings.present?
return fake_application_settings unless connect_to_db?
def cached_application_settings
begin
db_settings = ::ApplicationSetting.current
# In case Redis isn't running or the Redis UNIX socket file is not available
ApplicationSetting.cached
rescue ::Redis::BaseError, ::Errno::ENOENT
db_settings = ::ApplicationSetting.last
# In case Redis isn't running or the Redis UNIX socket file is not available
end
db_settings || ::ApplicationSetting.create_from_defaults
end
def retrieve_settings_from_database_cache?
def uncached_application_settings
return fake_application_settings unless connect_to_db?
# This loads from the database into the cache, so handle Redis errors
begin
settings = ApplicationSetting.cached
db_settings = ApplicationSetting.current
rescue ::Redis::BaseError, ::Errno::ENOENT
# In case Redis isn't running or the Redis UNIX socket file is not available
settings = nil
end
settings
# If there are pending migrations, it's possible there are columns that
# need to be added to the application settings. To prevent Rake tasks
# and other callers from failing, use any loaded settings and return
# defaults for missing columns.
if ActiveRecord::Migrator.needs_migration?
defaults = ApplicationSetting.defaults
defaults.merge!(db_settings.attributes.symbolize_keys) if db_settings.present?
return fake_application_settings(defaults)
end
return db_settings if db_settings.present?
ApplicationSetting.create_from_defaults || in_memory_application_settings
end
def in_memory_application_settings
......
# This class extends an OpenStruct object by adding predicate methods to mimic
# ActiveRecord access. We rely on the initial values being true or false to
# determine whether to define a predicate method because for a newly-added
# column that has not been migrated yet, there is no way to determine the
# column type without parsing db/schema.rb.
module Gitlab
class FakeApplicationSettings < OpenStruct
def initialize(options = {})
super
FakeApplicationSettings.define_predicate_methods(options)
end
# Mimic ActiveRecord predicate methods for boolean values
def self.define_predicate_methods(options)
options.each do |key, value|
next if key.to_s.end_with?('?')
next unless [true, false].include?(value)
define_method "#{key}?" do
actual_key = key.to_s.chomp('?')
self[actual_key]
end
end
end
end
end
FactoryGirl.define do
factory :application_setting do
end
end
......@@ -32,6 +32,37 @@ describe Gitlab::CurrentSettings do
expect(current_application_settings).to be_a(ApplicationSetting)
end
context 'with migrations pending' do
before do
expect(ActiveRecord::Migrator).to receive(:needs_migration?).and_return(true)
end
it 'returns an in-memory ApplicationSetting object' do
settings = current_application_settings
expect(settings).to be_a(OpenStruct)
expect(settings.sign_in_enabled?).to eq(settings.sign_in_enabled)
expect(settings.sign_up_enabled?).to eq(settings.sign_up_enabled)
end
it 'uses the existing database settings and falls back to defaults' do
db_settings = create(:application_setting,
home_page_url: 'http://mydomain.com',
signup_enabled: false)
settings = current_application_settings
app_defaults = ApplicationSetting.last
expect(settings).to be_a(OpenStruct)
expect(settings.home_page_url).to eq(db_settings.home_page_url)
expect(settings.signup_enabled?).to be_falsey
expect(settings.signup_enabled).to be_falsey
# Check that unspecified values use the defaults
settings.reject! { |key, _| [:home_page_url, :signup_enabled].include? key }
settings.each { |key, _| expect(settings[key]).to eq(app_defaults[key]) }
end
end
end
context 'with DB unavailable' do
......
require 'spec_helper'
describe Gitlab::FakeApplicationSettings do
let(:defaults) { { signin_enabled: false, foobar: 'asdf', signup_enabled: true, 'test?' => 123 } }
subject { described_class.new(defaults) }
it 'wraps OpenStruct variables properly' do
expect(subject.signin_enabled).to be_falsey
expect(subject.signup_enabled).to be_truthy
expect(subject.foobar).to eq('asdf')
end
it 'defines predicate methods' do
expect(subject.signin_enabled?).to be_falsey
expect(subject.signup_enabled?).to be_truthy
end
it 'predicate method changes when value is updated' do
subject.signin_enabled = true
expect(subject.signin_enabled?).to be_truthy
end
it 'does not define a predicate method' do
expect(subject.foobar?).to be_nil
end
it 'does not override an existing predicate method' do
expect(subject.test?).to eq(123)
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment