Project

General

Profile

Download (8.22 KB) Statistics
| Branch: | Tag: | Revision:
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"
6c05bd5b Ivan Nečas
if val.to_s =~ /\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