|
module NestedAncestryCommon
|
|
extend ActiveSupport::Concern
|
|
|
|
included do
|
|
audited :except => [:title]
|
|
has_associated_audits
|
|
has_ancestry :orphan_strategy => :restrict
|
|
|
|
before_validation :set_title
|
|
after_save :set_other_titles, :on => [:update, :destroy]
|
|
after_save :update_matchers, :on => :update, :if => Proc.new { |obj| obj.saved_change_to_title? }
|
|
|
|
validate :title_and_lookup_key_length
|
|
|
|
# attribute used by *_names and *_name methods. default is :name
|
|
attr_name :title
|
|
end
|
|
|
|
# override title getter
|
|
def title
|
|
self[:title] || get_title
|
|
end
|
|
|
|
def parent_name
|
|
parent.title if parent.present?
|
|
end
|
|
|
|
alias_method :to_label, :title
|
|
|
|
def get_title
|
|
return name if ancestry.empty?
|
|
ancestors.map { |a| a.name + '/' }.join + name.to_s
|
|
end
|
|
|
|
alias_method :get_label, :get_title
|
|
|
|
def to_param
|
|
Parameterizable.parameterize("#{id}-#{title}")
|
|
end
|
|
|
|
module ClassMethods
|
|
def nested_attribute_for(*opts)
|
|
opts.each do |field|
|
|
# Example method
|
|
# def inherited_compute_profile_id
|
|
# read_attribute(:compute_profile_id) || nested_compute_profile_id
|
|
# end
|
|
define_method "inherited_#{field}" do
|
|
self[field] || nested(field)
|
|
end
|
|
|
|
# Example method - only override method generated by assocation if there is ancestry.
|
|
# if ancestry.present?
|
|
# def compute_profile
|
|
# ComputeProfile.find_by_id(inherited_compute_profile_id)
|
|
# end
|
|
# end
|
|
if (md = field.to_s.match(/(\w+)_id$/))
|
|
define_method md[1] do
|
|
if ancestry.present?
|
|
klass = md[1].classify
|
|
klass = "SmartProxy" if ["puppet_proxy", "puppet_ca_proxy"].include?(md[1])
|
|
klass = 'Subnet::Ipv4' if md[1] == 'subnet'
|
|
klass = 'Subnet::Ipv6' if md[1] == 'subnet6'
|
|
klass.classify.constantize.find_by_id(send("inherited_#{field}"))
|
|
else
|
|
# () is required. Otherwise, get RuntimeError: implicit argument passing of super from method defined by define_method() is not supported. Specify all arguments explicitly.
|
|
super()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def nested(attr)
|
|
self.class.sort_by_ancestry(ancestors.where("#{attr} is not NULL")).last.try(attr) if ancestry.present?
|
|
end
|
|
|
|
private
|
|
|
|
def set_title
|
|
self.title = get_title if (name_changed? || ancestry_changed? || title.blank?)
|
|
end
|
|
|
|
def set_other_titles
|
|
if saved_change_to_name? || saved_change_to_ancestry?
|
|
self.class.where('ancestry IS NOT NULL').find_each do |obj|
|
|
if obj.path_ids.include?(self.id)
|
|
obj.update(:title => obj.get_title)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def obj_type
|
|
self.class.to_s.downcase
|
|
end
|
|
|
|
def update_matchers
|
|
lookup_values = LookupValue.where(:match => "#{obj_type}=#{title_before_last_save}")
|
|
lookup_values.update_all(:match => "#{obj_type}=#{title}")
|
|
end
|
|
|
|
# This validation is because lookup_value has an attribute `match` that cannot be turned to a test field do to
|
|
# an index set on it and problems with mysql indexes on test fields.
|
|
# If the index can be fixed, `match` should be turned into text and then this validation should be removed
|
|
def title_and_lookup_key_length
|
|
if name.present?
|
|
|
|
# The match is defined (example for hostgroup) "hostgroup=" + hostgroup.title so the length of "hostgroup=" needs to be added to the
|
|
# total length of the matcher that will be created
|
|
# obj_type will be "hostgroup" and another character is added for the "=" sign
|
|
length_of_matcher = obj_type.length + 1
|
|
|
|
# the parent title + "/" is added to the name to create the title
|
|
# If the parent_id doesn't exist, don't let errors be raised by this validation
|
|
parent_model = begin
|
|
parent
|
|
rescue ActiveRecord::RecordNotFound
|
|
nil
|
|
end
|
|
length_of_matcher += parent_model.title.length + 1 if parent_model.present?
|
|
|
|
max_length_for_name = 255 - length_of_matcher
|
|
current_title_length = max_length_for_name - name.length
|
|
|
|
errors.add(:name, n_("is too long (maximum is 1 character)", "is too long (maximum is %s characters)", max_length_for_name) % max_length_for_name) if (errors[:name].empty? && current_title_length < 0)
|
|
end
|
|
end
|
|
end
|