allow protected keys to be set/unset + config_db tests
This commit is contained in:
parent
9beaac2627
commit
6115979bae
7 changed files with 179 additions and 13 deletions
|
@ -41,6 +41,7 @@ module Admin::ConfigsHelper
|
||||||
# @todo find out how to pass +checked_value+ and +unchecked_value+ to +input_field+
|
# @todo find out how to pass +checked_value+ and +unchecked_value+ to +input_field+
|
||||||
def config_input_field(form, key, options = {})
|
def config_input_field(form, key, options = {})
|
||||||
return unless @cfg.allowed_key? :key
|
return unless @cfg.allowed_key? :key
|
||||||
|
options[:required] ||= false
|
||||||
config_input_field_options form, key, options
|
config_input_field_options form, key, options
|
||||||
config_input_tooltip_options form, key, options
|
config_input_tooltip_options form, key, options
|
||||||
if options[:as] == :boolean
|
if options[:as] == :boolean
|
||||||
|
|
|
@ -7,6 +7,9 @@ Foodsoft::Application.configure do
|
||||||
# and recreated between test runs. Don't rely on the data there!
|
# and recreated between test runs. Don't rely on the data there!
|
||||||
config.cache_classes = true
|
config.cache_classes = true
|
||||||
|
|
||||||
|
# We clear the cache for each test, let's do that in memory.
|
||||||
|
config.cache_store = :memory_store
|
||||||
|
|
||||||
# Do not eager load code on boot. This avoids loading your whole application
|
# Do not eager load code on boot. This avoids loading your whole application
|
||||||
# just for the purpose of running a single test. If you are using a tool that
|
# just for the purpose of running a single test. If you are using a tool that
|
||||||
# preloads Rails for running tests, you may have to set it to true.
|
# preloads Rails for running tests, you may have to set it to true.
|
||||||
|
|
|
@ -8,6 +8,26 @@
|
||||||
# In addition to the configuration file, values can be overridden in the database
|
# In addition to the configuration file, values can be overridden in the database
|
||||||
# using {RailsSettings::CachedSettings} as +foodcoop.<foodcoop_scope>.**+.
|
# using {RailsSettings::CachedSettings} as +foodcoop.<foodcoop_scope>.**+.
|
||||||
#
|
#
|
||||||
|
# Some values may not be set in the database (e.g. the database connection to
|
||||||
|
# sharedlists, or +default_scope+), these are defined as children of the
|
||||||
|
# +protected+ key. The default contains a sensible list, but you can modify
|
||||||
|
# that. Here's an almost minimal example:
|
||||||
|
#
|
||||||
|
# default:
|
||||||
|
# default_scope: f
|
||||||
|
# host: order.foodstuff.test # hostname for urls in emails
|
||||||
|
#
|
||||||
|
# name: Fairy Foodstuff # the name of our foodcoop
|
||||||
|
# contact:
|
||||||
|
# # ...
|
||||||
|
# email: fairy@foodstuff.test # general contact email address
|
||||||
|
#
|
||||||
|
# price_markup: 6 # foodcoop margin
|
||||||
|
#
|
||||||
|
# protected:
|
||||||
|
# shared_lists: false # allow database connection override
|
||||||
|
# use_messages: true # foodcoops can't disable the use of messages
|
||||||
|
#
|
||||||
class FoodsoftConfig
|
class FoodsoftConfig
|
||||||
|
|
||||||
# @!attribute scope
|
# @!attribute scope
|
||||||
|
@ -132,10 +152,14 @@ class FoodsoftConfig
|
||||||
# @return [Boolean] Whether this key may be set in the database
|
# @return [Boolean] Whether this key may be set in the database
|
||||||
def allowed_key?(key)
|
def allowed_key?(key)
|
||||||
# fast check for keys without nesting
|
# fast check for keys without nesting
|
||||||
return !self.config[:protected].keys.include?(key.to_s)
|
return !self.config[:protected][key]
|
||||||
# @todo allow to check nested keys as well
|
# @todo allow to check nested keys as well
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# @return [Hash] Full configuration.
|
||||||
|
def to_hash
|
||||||
|
Hash[keys.map {|k| [k, self[k]]} ]
|
||||||
|
end
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
@ -185,16 +209,19 @@ class FoodsoftConfig
|
||||||
ActiveRecord::Base.establish_connection(database_config)
|
ActiveRecord::Base.establish_connection(database_config)
|
||||||
end
|
end
|
||||||
|
|
||||||
# When new options are introduced, put backward-compatible defaults here, so that
|
# Completes foodcoop configuration with program defaults.
|
||||||
# configuration files that haven't been updated, still work as they did. This also
|
# @see #foodsoft_config
|
||||||
# makes sure that the configuration editor picks up the defaults.
|
|
||||||
# @see #default_foodsoft_config
|
|
||||||
def set_missing
|
def set_missing
|
||||||
config.replace(default_config.merge(config))
|
config.replace(default_config.deep_merge(config))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Returns program-default configuration.
|
||||||
|
# When new options are introduced, put backward-compatible defaults here, so that
|
||||||
|
# configuration files that haven't been updated, still work as they did. This also
|
||||||
|
# makes sure that the configuration editor picks up the defaults.
|
||||||
# @return [Hash] Program-default foodcoop configuration.
|
# @return [Hash] Program-default foodcoop configuration.
|
||||||
# @see #default_config
|
# @see #default_config
|
||||||
|
# @see #set_missing
|
||||||
def get_default_config
|
def get_default_config
|
||||||
cfg = {
|
cfg = {
|
||||||
use_nick: true,
|
use_nick: true,
|
||||||
|
@ -202,14 +229,14 @@ class FoodsoftConfig
|
||||||
# English is the default language, and this makes it show up as default.
|
# English is the default language, and this makes it show up as default.
|
||||||
default_locale: 'en',
|
default_locale: 'en',
|
||||||
foodsoft_url: 'https://github.com/foodcoops/foodsoft',
|
foodsoft_url: 'https://github.com/foodcoops/foodsoft',
|
||||||
# The following keys cannot be set by foodcoops themselves.
|
# The following keys cannot, by default, be set by foodcoops themselves.
|
||||||
protected: {
|
protected: {
|
||||||
multi_coop_install: nil,
|
multi_coop_install: true,
|
||||||
default_scope: nil,
|
default_scope: true,
|
||||||
notification: nil,
|
notification: true,
|
||||||
shared_lists: nil,
|
shared_lists: true,
|
||||||
protected: nil,
|
protected: true,
|
||||||
database: nil
|
database: true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
# allow engines to easily add to this
|
# allow engines to easily add to this
|
||||||
|
|
|
@ -13,6 +13,9 @@ default: &defaults
|
||||||
contact:
|
contact:
|
||||||
email: fc@minimal.test
|
email: fc@minimal.test
|
||||||
|
|
||||||
|
# required by configuration form (but otherwise not)
|
||||||
|
homepage: http://www.minimal.test/
|
||||||
|
|
||||||
# true by default to keep compat with older installations, but test with false here
|
# true by default to keep compat with older installations, but test with false here
|
||||||
use_nick: false
|
use_nick: false
|
||||||
|
|
||||||
|
|
45
spec/integration/config_spec.rb
Normal file
45
spec/integration/config_spec.rb
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe 'admin/configs', type: :feature do
|
||||||
|
let(:name) { Faker::Lorem.words(rand(2..4)).join(' ') }
|
||||||
|
|
||||||
|
describe type: :feature, js: true do
|
||||||
|
let(:admin) { create :admin }
|
||||||
|
before { login admin }
|
||||||
|
|
||||||
|
it 'has initial value' do
|
||||||
|
FoodsoftConfig[:name] = name
|
||||||
|
visit admin_config_path
|
||||||
|
within('form.config') do
|
||||||
|
expect(find_field('config_name').value).to eq name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can modify a value' do
|
||||||
|
visit admin_config_path
|
||||||
|
fill_in 'config_name', with: name
|
||||||
|
within('form.config') do
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
expect(find_field('config_name').value).to eq name
|
||||||
|
end
|
||||||
|
expect(FoodsoftConfig[:name]).to eq name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'keeps config the same without changes' do
|
||||||
|
orig_values = get_full_config
|
||||||
|
visit admin_config_path
|
||||||
|
within('form.config') do
|
||||||
|
find('input[type="submit"]').click
|
||||||
|
expect(find_field('config_name').value).to eq FoodsoftConfig[:name]
|
||||||
|
end
|
||||||
|
expect(get_full_config).to eq orig_values
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_full_config
|
||||||
|
cfg = FoodsoftConfig.to_hash.deep_dup
|
||||||
|
cfg.each {|k,v| v.reject! {|k,v| v.blank?} if v.is_a? Hash}
|
||||||
|
cfg
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
85
spec/lib/foodsoft_config_spec.rb
Normal file
85
spec/lib/foodsoft_config_spec.rb
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
require_relative '../spec_helper'
|
||||||
|
|
||||||
|
describe FoodsoftConfig do
|
||||||
|
let(:name) { Faker::Lorem.words(rand(2..4)).join(' ') }
|
||||||
|
let(:other_name) { Faker::Lorem.words(rand(2..4)).join(' ') }
|
||||||
|
|
||||||
|
it 'returns a default value' do
|
||||||
|
expect(FoodsoftConfig[:protected][:database]).to be_true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns an empty default value' do
|
||||||
|
expect(FoodsoftConfig[:protected][:LIUhniuyGNKUQTWfbiOQIWYexngo78hqexul]).to be_false
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'returns a configuration value' do
|
||||||
|
FoodsoftConfig.config[:name] = name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can set a configuration value' do
|
||||||
|
FoodsoftConfig[:name] = name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can override a configuration value' do
|
||||||
|
FoodsoftConfig.config[:name] = name
|
||||||
|
FoodsoftConfig[:name] = other_name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq other_name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'cannot set a default protected value' do
|
||||||
|
old = FoodsoftConfig[:database]
|
||||||
|
FoodsoftConfig[:database] = name
|
||||||
|
expect(FoodsoftConfig.config[:database]).to eq old
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can unprotect a default protected value' do
|
||||||
|
FoodsoftConfig.config[:protected][:database] = false
|
||||||
|
old = FoodsoftConfig[:database]
|
||||||
|
FoodsoftConfig[:database] = name
|
||||||
|
expect(FoodsoftConfig[:database]).to eq name
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'can protect a value', type: :feature do
|
||||||
|
before do
|
||||||
|
FoodsoftConfig.config[:protected][:name] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'can protect a value' do
|
||||||
|
old_name = FoodsoftConfig[:name]
|
||||||
|
FoodsoftConfig[:name] = name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq old_name
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'and unprotect it again' do
|
||||||
|
old_name = FoodsoftConfig[:name]
|
||||||
|
FoodsoftConfig.config[:protected][:name] = false
|
||||||
|
FoodsoftConfig[:name] = name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq name
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'has indifferent access', type: :feature do
|
||||||
|
it 'with symbol' do
|
||||||
|
FoodsoftConfig[:name] = name
|
||||||
|
expect(FoodsoftConfig[:name]).to eq FoodsoftConfig['name']
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'with string' do
|
||||||
|
FoodsoftConfig['name'] = name
|
||||||
|
expect(FoodsoftConfig['name']).to eq FoodsoftConfig[:name]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'with nested symbol' do
|
||||||
|
FoodsoftConfig[:protected][:database] = true
|
||||||
|
expect(FoodsoftConfig[:protected]['database']).to eq FoodsoftConfig[:protected][:database]
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'with nested string' do
|
||||||
|
FoodsoftConfig[:protected]['database'] = true
|
||||||
|
expect(FoodsoftConfig[:protected]['database']).to eq FoodsoftConfig[:protected][:database]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
|
@ -35,6 +35,8 @@ RSpec.configure do |config|
|
||||||
end
|
end
|
||||||
config.after(:each) do
|
config.after(:each) do
|
||||||
DatabaseCleaner.clean
|
DatabaseCleaner.clean
|
||||||
|
# Need to clear cache for RailsSettings::CachedSettings
|
||||||
|
Rails.cache.clear
|
||||||
end
|
end
|
||||||
|
|
||||||
# reload foodsoft configuration, so that tests can use FoodsoftConfig.config[:foo]=x
|
# reload foodsoft configuration, so that tests can use FoodsoftConfig.config[:foo]=x
|
||||||
|
|
Loading…
Reference in a new issue