settings_validation_wrapper.rb revision 87a46b6e4db760590780a339528decb20e0c2d8b
525421c923d798cdea9e5691bcee1e5e5530491dStéphane Graberclass SettingsValidationWrapper
525421c923d798cdea9e5691bcee1e5e5530491dStéphane Graber class DataPathsValidator < ActiveModel::Validator
fd5c4f905861964cb473cf3df3336447a9365962Stéphane Graber def defaults(key)
fd5c4f905861964cb473cf3df3336447a9365962Stéphane Graber [Settings.paths[key], PathsInitializer::DEFAULT_PATHS[key]]
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber end
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber def set_directory(key)
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber setting, fallback = defaults(key)
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber PathsInitializer.prepare(setting, fallback)
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber end
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber def failure_condition_met?(key)
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber setting, _fallback = defaults(key)
afeecbba0359d2b4404cdf896e6b6d0b5a8443b0Serge Hallyn setting.nil? && !File.directory?(set_directory(key))
ae5c8b8ed5feb9a47c5007c986ce01ea39b5075fSerge Hallyn end
9be53773792fc9e8bd173edc3b7ac7e144875387Serge Hallyn
0115f8fd27b1a31d367bb161a121694f92b45e62Dwight Engen def validate(record)
20ab58c777136a449b3199e0733b62fa87ecfa61Serge Hallyn PathsInitializer::DEFAULT_PATHS.each do |key, _default_value|
20ab58c777136a449b3199e0733b62fa87ecfa61Serge Hallyn dir = set_directory(key)
f5dd1d532a76a1b57cf341db821eae85ea1118c5Serge Hallyn if failure_condition_met?(key)
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur record.errors["yml__paths__#{key}".to_sym] =
b494d2ddf769220da1ef75fd24275ce68cdf297cSerge Hallyn "Implicitly set data directory path '#{dir}' is not a directory."
2aa123185e055bbe2506a6210e795e0b9375e477Serge Hallyn elsif !Settings.paths[key].is_a?(String)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber record.errors["yml__paths__#{key}".to_sym] = 'Is not a String value.'
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber elsif !File.directory?(dir)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber record.errors["yml__paths__#{key}".to_sym] = 'Is not a directory.'
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber end
2a59a68183e55e38beedb6442938e31eb7d4749cSerge Hallyn end
0a18b5458b6d0fcad9a82b96f99035254af50c7aSerge Hallyn end
0a18b5458b6d0fcad9a82b96f99035254af50c7aSerge Hallyn end
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber include ActiveModel::Validations
16216c83297543692b8dede52c9dd8a998758e9cStéphane Graber include SettingsValidationWrapper::Validators
ae5c8b8ed5feb9a47c5007c986ce01ea39b5075fSerge Hallyn
f5dd1d532a76a1b57cf341db821eae85ea1118c5Serge Hallyn # We assume that deployment is done on a linux machine that has 'nproc'.
2aa123185e055bbe2506a6210e795e0b9375e477Serge Hallyn # Counting processors is different on other machines. For them, we would need
2aa123185e055bbe2506a6210e795e0b9375e477Serge Hallyn # to use a gem.
20ab58c777136a449b3199e0733b62fa87ecfa61Serge Hallyn NPROC_PATH = `which nproc`
20ab58c777136a449b3199e0733b62fa87ecfa61Serge Hallyn NPROC_AVAILABLE = NPROC_PATH.present? && File.executable?(NPROC_PATH)
525421c923d798cdea9e5691bcee1e5e5530491dStéphane Graber
025f59ab98217b7e9caf6d3ac7e910853d95f621Serge Hallyn PRESENCE = %i(yml__name
025f59ab98217b7e9caf6d3ac7e910853d95f621Serge Hallyn yml__OMS
025f59ab98217b7e9caf6d3ac7e910853d95f621Serge Hallyn yml__OMS_qualifier
025f59ab98217b7e9caf6d3ac7e910853d95f621Serge Hallyn yml__action_mailer__delivery_method
525421c923d798cdea9e5691bcee1e5e5530491dStéphane Graber yml__action_mailer__smtp_settings__address
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__allow_unconfirmed_access_for_days
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__max_read_filesize
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__max_combined_diff_size
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__ontology_parse_timeout
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__footer
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__exception_notifier__email_prefix
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__exception_notifier__sender_address
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__exception_notifier__exception_recipients
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__paths__data
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__git__verify_url
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__git__default_branch
90341b9e39561e37797777a34d0589c14c0c2a68Dwight Engen yml__git__push_priority__commits
9be53773792fc9e8bd173edc3b7ac7e144875387Serge Hallyn yml__git__push_priority__changed_files_per_commit
0115f8fd27b1a31d367bb161a121694f92b45e62Dwight Engen yml__git__fallbacks__committer_name
cd0bcc4958e58a2750cf9086f75649d14c83ac70Stéphane Graber yml__git__fallbacks__committer_email
f5dd1d532a76a1b57cf341db821eae85ea1118c5Serge Hallyn yml__allowed_iri_schemes
f209d63a97a8a2df5324608fee7b0d7a494d69ebS.Çağlar Onur yml__external_repository_name
b494d2ddf769220da1ef75fd24275ce68cdf297cSerge Hallyn yml__formality_levels
b494d2ddf769220da1ef75fd24275ce68cdf297cSerge Hallyn yml__license_models
yml__ontology_types
yml__tasks
yml__hets__version_minimum_version
yml__hets__version_minimum_revision
yml__hets__stack_size
yml__hets__cmd_line_options
yml__hets__server_options
yml__hets__env__LANG
initializers__fqdn)
PRESENCE_IN_PRODUCTION = %i(yml__hets__executable_path
yml__hets__instances_count)
BOOLEAN = %i(yml__exception_notifier__enabled
yml__display_head_commit
yml__display_symbols_tab
yml__format_selection
yml__action_mailer__perform_deliveries
yml__action_mailer__raise_delivery_errors
yml__action_mailer__smtp_settings__enable_starttls_auto
initializers__consider_all_requests_local)
FIXNUM = %i(yml__hets__instances_count
yml__action_mailer__smtp_settings__port
yml__allow_unconfirmed_access_for_days
yml__git__push_priority__commits
yml__git__push_priority__changed_files_per_commit
yml__access_token__expiration_minutes
yml__hets__time_between_updates
yml__hets__version_minimum_revision)
FLOAT = %i(yml__hets__version_minimum_version)
STRING = %i(yml__name
yml__OMS
yml__OMS_qualifier
yml__email
yml__action_mailer__smtp_settings__address
yml__exception_notifier__email_prefix
yml__exception_notifier__sender_address
yml__paths__data
yml__git__verify_url
yml__git__default_branch
yml__git__fallbacks__committer_name
yml__git__fallbacks__committer_email
yml__external_repository_name)
ARRAY = %i(yml__footer
yml__exception_notifier__exception_recipients
yml__allowed_iri_schemes
yml__formality_levels
yml__license_models
yml__ontology_types
yml__tasks
yml__hets__cmd_line_options
yml__hets__server_options)
DIRECTORY_PRODUCTION = %i(yml__paths__data)
ELEMENT_PRESENT = %i(yml__allowed_iri_schemes
yml__hets__cmd_line_options
yml__hets__server_options)
validates_with DataPathsValidator, if: :in_production?
validates_presence_of *PRESENCE
validates_presence_of *PRESENCE_IN_PRODUCTION, if: :in_production?
BOOLEAN.each do |field|
validates field, class: {in: [TrueClass, FalseClass]}
end
FIXNUM.each { |field| validates field, class: {in: [Fixnum]} }
FLOAT.each { |field| validates field, class: {in: [Float]} }
STRING.each { |field| validates field, class: {in: [String]} }
ARRAY.each { |field| validates field, class: {in: [Array]} }
DIRECTORY_PRODUCTION.each do |field|
validates field, directory: true, if: :in_production?
end
ELEMENT_PRESENT.each { |field| validates field, elements_are_present: true }
validates :initializers__secret_token,
presence: true,
length: {minimum: 64},
if: :in_production?
validates :yml__email,
email_host: {hostname: ->(record) { record.initializers__fqdn }},
if: :in_production?
validates :yml__exception_notifier__exception_recipients,
elements_are_email: true
validates :yml__action_mailer__delivery_method,
inclusion: {in: %i(sendmail smtp file test)}
validates :yml__allow_unconfirmed_access_for_days,
numericality: {greater_than_or_equal_to: 0}
validates :yml__max_read_filesize, numericality: {greater_than: 1024}
validates :yml__max_combined_diff_size, numericality: {greater_than: 2048}
validates :yml__ontology_parse_timeout, numericality: {greater_than: 0}
validates :yml__git__verify_url, format: URI.regexp
validates :yml__git__push_priority__commits,
numericality: {greater_than_or_equal_to: 1}
validates :yml__git__push_priority__changed_files_per_commit,
numericality: {greater_than_or_equal_to: 1}
validates :yml__access_token__expiration_minutes,
numericality: {greater_than_or_equal_to: 1}
validates :yml__footer, elements_have_keys: {keys: %i(text)}
validates :yml__formality_levels,
elements_have_keys: {keys: %i(name description)}
validates :yml__license_models, elements_have_keys: {keys: %i(name url)}
validates :yml__ontology_types,
elements_have_keys: {keys: %i(name description documentation)}
validates :yml__tasks,
elements_have_keys: {keys: %i(name description)}
validates :initializers__log_level,
inclusion: {in: %i(fatal error warn info debug)}
validates :yml__hets__executable_path, executable: true, if: :in_production?
if NPROC_AVAILABLE
validates :yml__hets__instances_count,
numericality: {greater_than: 0,
less_than_or_equal_to: `nproc`.to_i},
if: :in_production?
else
validates :yml__hets__instances_count,
numericality: {greater_than: 0},
if: :in_production?
end
validates :yml__hets__time_between_updates,
numericality: {greater_than_or_equal_to: 1}
validates :yml__asynchronous_execution__log_level,
inclusion: {in: %w(UNKNOWN FATAL ERROR WARN INFO DEBUG)}
def self.base(first_portion)
case first_portion
when 'yml'
Settings
when 'initializers'
Ontohub::Application.config
else
:error
end
end
def self.get_value(object, key_chain)
key_chain.each do |key|
if object.respond_to?(key)
object = object.send(key)
else
# The nil value shall be caught by the presence validators.
return nil
end
end
object
end
protected
def in_production?
Rails.env.production?
end
# We use '__' as a separator. It will be replaced by a dot.
# This uses the fact that our settings-keys never have two consecutive
# underscores.
# yml__git__verify_url maps to Settings.git.verify_url.
# initializers__git__verify_url maps to @config.git.verify_url.
def method_missing(method_name, *_args)
portions = method_name.to_s.split('__')
object = self.class.base(portions[0])
key_chain = portions[1..-1]
if object == :error || key_chain.blank?
raise NoMethodError,
"undefined method `#{method_name}' for #{self}:#{self.class}"
end
self.class.get_value(object, key_chain)
end
end