foreman/app/models/setting.rb @ bb3572ff
76607ed5 | Ohad Levy | class Setting < ActiveRecord::Base
|
|
8b737c9c | Joseph Magen | extend FriendlyId
|
|
friendly_id :name
|
|||
8c618ae8 | Greg Sutcliffe | include ActiveModel::Validations
|
|
86007852 | Greg Sutcliffe | self.inheritance_column = 'category'
|
|
8da68b88 | Tomas Strachota | TYPES= %w{ integer boolean hash array string }
|
|
60fe43b2 | Ondrej Prazak | FROZEN_ATTRS = %w{ name category full_name }
|
|
81884922 | Craig Parker | NONZERO_ATTRS = %w{ puppet_interval idle_timeout entries_per_page max_trend outofsync_interval }
|
|
bceb2f66 | Shlomi Zadok | BLANK_ATTRS = %w{ trusted_puppetmaster_hosts login_delegation_logout_url authorize_login_delegation_auth_source_user_autocreate root_pass default_location default_organization websockets_ssl_key websockets_ssl_cert oauth_consumer_key oauth_consumer_secret }
|
|
8c618ae8 | Greg Sutcliffe | URI_ATTRS = %w{ foreman_url unattended_url }
|
|
class UriValidator < ActiveModel::EachValidator
|
|||
def validate_each(record, attribute, value)
|
|||
06b10bee | Ondrej Prazak | record.errors.add attribute, _("must be a valid URI") unless %w(http https).include? URI(value).scheme
|
|
8c618ae8 | Greg Sutcliffe | rescue URI::InvalidURIError
|
|
record.errors.add attribute, _("must be a valid URI")
|
|||
end
|
|||
end
|
|||
05921293 | Ivan Necas | ||
97c34475 | Lukas Zapletal | class ValueValidator < ActiveModel::Validator
|
|
def validate(record)
|
|||
record.send("validate_#{record.name}", record)
|
|||
end
|
|||
end
|
|||
60fe43b2 | Ondrej Prazak | attr_accessible :name, :value, :description, :category, :settings_type, :default, :full_name
|
|
3034e8e2 | Ori Rabin | ||
validates_lengths_from_database
|
|||
05921293 | Ivan Necas | # audit the changes to this model
|
|
audited :only => [:value], :on => [:update], :allow_mass_assignment => true
|
|||
f2c78d4a | Joseph Magen | validates :name, :presence => true, :uniqueness => true
|
|
validates :description, :presence => true
|
|||
validates :default, :presence => true, :unless => Proc.new {|s| s.settings_type == "boolean" || BLANK_ATTRS.include?(s.name) }
|
|||
validates :default, :inclusion => {:in => [true,false]}, :if => Proc.new {|s| s.settings_type == "boolean"}
|
|||
c737b10e | Ori Rabin | validates :value, :numericality => true, :length => {:maximum => 8}, :if => Proc.new {|s| s.settings_type == "integer"}
|
|
f2c78d4a | Joseph Magen | validates :value, :numericality => {:greater_than => 0}, :if => Proc.new {|s| NONZERO_ATTRS.include?(s.name) }
|
|
validates :value, :inclusion => {:in => [true,false]}, :if => Proc.new {|s| s.settings_type == "boolean"}
|
|||
validates :value, :presence => true, :if => Proc.new {|s| s.settings_type == "array" && !BLANK_ATTRS.include?(s.name) }
|
|||
validates :settings_type, :inclusion => {:in => TYPES}, :allow_nil => true, :allow_blank => true
|
|||
d7cdc084 | Shlomi Zadok | validates :value, :uri => true, :presence => true, :if => Proc.new {|s| URI_ATTRS.include?(s.name) }
|
|
97c34475 | Lukas Zapletal | validates_with ValueValidator, :if => Proc.new {|s| s.respond_to?("validate_#{s.name}") }
|
|
8da68b88 | Tomas Strachota | before_validation :set_setting_type_from_value
|
|
before_save :clear_value_when_default
|
|||
before_save :clear_cache
|
|||
validate :validate_frozen_attributes
|
|||
4dbf6624 | Dominic Cleal | after_find :readonly_when_overridden_in_SETTINGS
|
|
bb3572ff | Daniel Lobato | default_scope -> { order(:name) }
|
|
86007852 | Greg Sutcliffe | ||
# The DB may contain settings from disabled plugins - filter them out here
|
|||
bb3572ff | Daniel Lobato | scope :live_descendants, -> { where(:category => self.descendants.map(&:to_s)) unless Rails.env.development? }
|
|
76607ed5 | Ohad Levy | ||
scoped_search :on => :name, :complete_value => :true
|
|||
scoped_search :on => :description, :complete_value => :true
|
|||
017e1049 | Ohad Levy | def self.per_page; 20 end # can't use our own settings
|
|
c45a0014 | Ohad Levy | ||
76607ed5 | Ohad Levy | def self.[](name)
|
|
d9a2ebac | Ohad Levy | name = name.to_s
|
|
8da68b88 | Tomas Strachota | cache_value = Setting.cache.read(name)
|
|
3f1a3a4d | Joseph Mitchell Magen | if cache_value.nil?
|
|
86007852 | Greg Sutcliffe | value = where(:name => name).first.try(:value)
|
|
8da68b88 | Tomas Strachota | Setting.cache.write(name, value)
|
|
86007852 | Greg Sutcliffe | return value
|
|
3f1a3a4d | Joseph Mitchell Magen | else
|
|
8da68b88 | Tomas Strachota | return cache_value
|
|
76607ed5 | Ohad Levy | end
|
|
end
|
|||
def self.[]=(name, value)
|
|||
d9a2ebac | Ohad Levy | name = name.to_s
|
|
record = find_or_create_by_name name
|
|||
76607ed5 | Ohad Levy | record.value = value
|
|
29eebabc | Ohad Levy | record.save!
|
|
76607ed5 | Ohad Levy | end
|
|
def self.method_missing(method, *args)
|
|||
super(method, *args)
|
|||
rescue NoMethodError
|
|||
method_name = method.to_s
|
|||
#setter method
|
|||
a6b0eeb0 | Joseph Magen | if method_name =~ /=\Z/
|
|
76607ed5 | Ohad Levy | self[method_name.chomp("=")] = args.first
|
|
8c618ae8 | Greg Sutcliffe | #getter
|
|
76607ed5 | Ohad Levy | else
|
|
self[method_name]
|
|||
end
|
|||
end
|
|||
911d3f5c | Tomas Strachota | def value=(v)
|
|
v = v.to_yaml unless v.nil?
|
|||
29eebabc | Ohad Levy | write_attribute :value, v
|
|
end
|
|||
76607ed5 | Ohad Levy | def value
|
|
29eebabc | Ohad Levy | v = read_attribute(:value)
|
|
v.nil? ? default : YAML.load(v)
|
|||
76607ed5 | Ohad Levy | end
|
|
29eebabc | Ohad Levy | alias_method :value_before_type_cast, :value
|
|
def default
|
|||
911d3f5c | Tomas Strachota | d = read_attribute(:default)
|
|
d.nil? ? nil : YAML.load(d)
|
|||
29eebabc | Ohad Levy | end
|
|
def default=(v)
|
|||
write_attribute :default, v.to_yaml
|
|||
end
|
|||
alias_method :default_before_type_cast, :default
|
|||
76607ed5 | Ohad Levy | ||
96144a47 | Daniel Lobato | def parse_string_value(val)
|
|
29eebabc | Ohad Levy | case settings_type
|
|
when "boolean"
|
|||
c74610f9 | Walden Raines | boolean = Foreman::Cast.to_bool(val)
|
|
if boolean.nil?
|
|||
8da68b88 | Tomas Strachota | invalid_value_error _("must be boolean")
|
|
return false
|
|||
end
|
|||
c74610f9 | Walden Raines | self.value = boolean
|
|
29eebabc | Ohad Levy | when "integer"
|
|
a6b0eeb0 | Joseph Magen | if val =~ /\A\d+\Z/
|
|
8da68b88 | Tomas Strachota | self.value = val.to_i
|
|
else
|
|||
invalid_value_error _("must be integer")
|
|||
return false
|
|||
end
|
|||
df886c98 | Ohad Levy | when "array"
|
|
a6b0eeb0 | Joseph Magen | if val =~ /\A\[.*\]\Z/
|
|
df886c98 | Ohad Levy | begin
|
|
8da68b88 | Tomas Strachota | self.value = YAML.load(val.gsub(/(\,)(\S)/, "\\1 \\2"))
|
|
df886c98 | Ohad Levy | rescue => e
|
|
8da68b88 | Tomas Strachota | invalid_value_error e.to_s
|
|
df886c98 | Ohad Levy | return false
|
|
end
|
|||
else
|
|||
8da68b88 | Tomas Strachota | invalid_value_error _("must be an array")
|
|
df886c98 | Ohad Levy | return false
|
|
end
|
|||
8da68b88 | Tomas Strachota | ||
when "string", nil
|
|||
#string is taken as default setting type for parsing
|
|||
self.value = val.to_s.strip
|
|||
when "hash"
|
|||
raise Foreman::Exception, "parsing hash from string is not supported"
|
|||
else
|
|||
raise Foreman::Exception.new(N_("parsing settings type '%s' from string is not defined"), settings_type)
|
|||
end
|
|||
96144a47 | Daniel Lobato | true
|
|
8da68b88 | Tomas Strachota | end
|
|
5f029ed6 | Daniel Lobato | def self.create(opts)
|
|
8da68b88 | Tomas Strachota | if (s = Setting.find_by_name(opts[:name].to_s)).nil?
|
|
60fe43b2 | Ondrej Prazak | column_check(opts)
|
|
4dbf6624 | Dominic Cleal | super opts.merge(:value => SETTINGS[opts[:name].to_sym] || opts[:value])
|
|
8da68b88 | Tomas Strachota | else
|
|
4dbf6624 | Dominic Cleal | create_existing(s, opts)
|
|
76607ed5 | Ohad Levy | end
|
|
end
|
|||
5f029ed6 | Daniel Lobato | def self.create!(opts)
|
|
8da68b88 | Tomas Strachota | if (s = Setting.find_by_name(opts[:name].to_s)).nil?
|
|
60fe43b2 | Ondrej Prazak | column_check(opts)
|
|
4dbf6624 | Dominic Cleal | super opts.merge(:value => SETTINGS[opts[:name].to_sym] || opts[:value])
|
|
8da68b88 | Tomas Strachota | else
|
|
4dbf6624 | Dominic Cleal | create_existing(s, opts)
|
|
8da68b88 | Tomas Strachota | end
|
|
end
|
|||
private
|
|||
4dbf6624 | Dominic Cleal | def self.create_existing(s, opts)
|
|
bypass_readonly(s) do
|
|||
60fe43b2 | Ondrej Prazak | attrs = column_check([:default, :description, :full_name])
|
|
to_update = Hash[opts.select { |k,v| attrs.include? k }]
|
|||
4dbf6624 | Dominic Cleal | to_update.merge!(:value => SETTINGS[opts[:name].to_sym]) if SETTINGS.key?(opts[:name].to_sym)
|
|
s.update_attributes(to_update)
|
|||
fb293de9 | Ondrej Prazak | s.update_column :category, opts[:category] if s.category != opts[:category]
|
|
60fe43b2 | Ondrej Prazak | s.update_column :full_name, opts[:full_name] if !column_check([:full_name]).empty?
|
|
4dbf6624 | Dominic Cleal | end
|
|
s
|
|||
end
|
|||
def self.bypass_readonly(s, &block)
|
|||
s.instance_variable_set("@readonly", false) if (old_readonly = s.readonly?)
|
|||
yield(s)
|
|||
ensure
|
|||
s.readonly! if old_readonly
|
|||
end
|
|||
8da68b88 | Tomas Strachota | def self.cache
|
|
Rails.cache
|
|||
end
|
|||
5f029ed6 | Daniel Lobato | def invalid_value_error(error)
|
|
76fec074 | Joseph Mitchell Magen | errors.add(:value, _("is invalid: %s") % error)
|
|
8da68b88 | Tomas Strachota | end
|
|
def set_setting_type_from_value
|
|||
return true unless settings_type.nil?
|
|||
t = default.class.to_s.downcase
|
|||
if TYPES.include?(t)
|
|||
self.settings_type = t
|
|||
else
|
|||
self.settings_type = "integer" if default.is_a?(Integer)
|
|||
self.settings_type = "boolean" if default.is_a?(TrueClass) or default.is_a?(FalseClass)
|
|||
end
|
|||
end
|
|||
def validate_frozen_attributes
|
|||
76607ed5 | Ohad Levy | return true if new_record?
|
|
0323590f | Dominic Cleal | changed_attributes.each do |c,old|
|
|
# Allow settings_type to change at first (from nil) since it gets populated during validation
|
|||
if FROZEN_ATTRS.include?(c.to_s) || (c.to_s == :settings_type && !old.nil?)
|
|||
93117958 | Bryan Kearney | errors.add(c, _("is not allowed to change"))
|
|
76607ed5 | Ohad Levy | return false
|
|
end
|
|||
end
|
|||
true
|
|||
end
|
|||
86007852 | Greg Sutcliffe | ||
8da68b88 | Tomas Strachota | def clear_value_when_default
|
|
if read_attribute(:value) == read_attribute(:default)
|
|||
write_attribute(:value, nil)
|
|||
end
|
|||
end
|
|||
def clear_cache
|
|||
# ensures we don't have cache left overs in settings
|
|||
83bd400c | Dominic Cleal | if Setting.cache.delete(name.to_s) == false
|
|
Rails.logger.warn "Failed to remove #{name} from cache"
|
|||
end
|
|||
8da68b88 | Tomas Strachota | end
|
|
4dbf6624 | Dominic Cleal | def readonly_when_overridden_in_SETTINGS
|
|
readonly! if !new_record? && SETTINGS.key?(name.to_sym)
|
|||
end
|
|||
86007852 | Greg Sutcliffe | # Methods for loading default settings
|
|
def self.load_defaults
|
|||
# We may be executing something like rake db:migrate:reset, which destroys this table; only continue if the table exists
|
|||
Setting.first rescue return false
|
|||
# STI classes will load their own defaults
|
|||
true
|
|||
end
|
|||
60fe43b2 | Ondrej Prazak | def self.set(name, description, default, full_name = nil, value = nil)
|
|
{:name => name, :value => value, :description => description, :default => default, :full_name => full_name}
|
|||
86007852 | Greg Sutcliffe | end
|
|
def self.model_name
|
|||
ActiveModel::Name.new(Setting)
|
|||
end
|
|||
60fe43b2 | Ondrej Prazak | ||
def self.column_check(opts)
|
|||
opts.keep_if { |k, v| Setting.column_names.include?(k.to_s) }
|
|||
end
|
|||
76607ed5 | Ohad Levy | end
|