kennethkalmer/powerdns-on-rails

View on GitHub
app/models/record_template.rb

Summary

Maintainability
A
3 hrs
Test Coverage
class RecordTemplate < ActiveRecord::Base

  belongs_to :zone_template

  # General validations
  validates_presence_of :zone_template_id
  validates_associated :zone_template
  validates_presence_of :record_type
  validates_format_of :contact, :if => :soa?, :with => /\A[^@\s]+@(%ZONE%|([^@\s]+\.)+[^@\s]+)\z/

  before_save :update_soa_content
  before_validation :inherit_ttl
  after_initialize :update_convenience_accessors
  validate :validate_record_template

  # We need to cope with the SOA convenience
  SOA::SOA_FIELDS.each do |f|
    attr_accessor f
  end

  class << self
    def record_types
      Record.record_types
    end
  end

  # Hook into #reload
  def reload_with_content
    reload_without_content
    update_convenience_accessors
  end
  alias_method_chain :reload, :content

  # Convert this template record into a instance +record_type+ with the
  # attributes of the template copied over to the instance
  def build( domain_name = nil )
    # get the class of the record_type
    record_class = self.record_type.constantize

    # duplicate our own attributes, strip out the ones the destination doesn't
    # have (and the id as well)
    attrs = self.attributes.dup.with_indifferent_access
    attrs.delete_if { |k,_| !record_class.columns.map( &:name ).include?( k ) }
    attrs.delete( :id )

    # parse each attribute, looking for %ZONE%
    if domain_name.present?
      attrs.keys.each do |k|
        attrs[k] = attrs[k].gsub( '%ZONE%', domain_name ) if attrs[k].is_a?( String )
      end
    end

    # Handle SOA convenience fields if needed
    if soa?
      SOA::SOA_FIELDS.each do |soa_field|
        soa_val = instance_variable_get("@#{soa_field}")

        if domain_name.present? && soa_val.is_a?( String )
          soa_val = soa_val.gsub( '%ZONE%', domain_name )
        end

        attrs[soa_field] = soa_val
      end
      attrs[:serial] = 0
    end

    # instantiate a new destination with our duplicated attributes & validate
    record_class.new( attrs )
  end

  def soa?
    self.record_type == 'SOA'
  end

  def content
    soa? ? SOA::SOA_FIELDS.map{ |f| instance_variable_get("@#{f}") || 0 }.join(' ') : self[:content]
  end

  # Manage TTL inheritance here
  def inherit_ttl
    unless self.zone_template_id.nil?
      self.ttl ||= self.zone_template.ttl
    end
  end

  # Manage SOA content
  def update_soa_content #:nodoc:
    self[:content] = content
  end

  # Here we perform some magic to inherit the validations from the "destination"
  # model without any duplication of rules. This allows us to simply extend the
  # appropriate record and gain those validations in the templates
  def validate_record_template #:nodoc:
    unless self.record_type.blank?
      record = build
      record.errors.each do |k,v|
        # skip associations we don't have, validations we don't care about
        next if k == :domain_id || k == :name || k == :contact

        self.errors.add( k, v )
      end unless record.valid?
    end
  end

  private

  # Update our convenience accessors when the object has changed
  def update_convenience_accessors
    return unless self.record_type == 'SOA'

    # Setup our convenience values
    @primary_ns, @contact, @serial, @refresh, @retry, @expire, @minimum =
      self[:content].split(/\s+/) unless self[:content].blank?
    %w{ serial refresh retry expire minimum }.each do |i|
      value = instance_variable_get("@#{i}")
      value = value.to_i unless value.nil?
      send("#{i}=", value )
    end
  end
end