jamesdabbs/pi-base

View on GitHub
app/models/formula/atom.rb

Summary

Maintainability
A
1 hr
Test Coverage
class Formula::Atom < Formula
  attr_reader :property, :value

  def initialize property, value, negated=false
    # TODO: coerce these between ints, bools & models on demand
    @property    = property
    @value       = negated ? ~value : value
  end

  # -- Common formula interface -----

  def == other
    property == other.property && value == other.value
  end

  def self.parse_text str
    negated  = str =~ /~\s*/
    p,v      = str.gsub(/~\s*/, '').split('=').map &:strip
    property = Atom.parse_name_or_id p, Property
    value    = Atom.parse_name_or_id v, Value, Value.true
    new property, value, negated
  end

  def self.dump atom
    "#{atom.property.name} = #{atom.value.name}"
  end

  def to_s &block
    block ? block.call(self) : name
  end

  def spaces where=true
    if where.nil?
      set = @property.traits.pluck :space_id
      spaces = set.empty? ? Space : Space.where('id NOT IN (?)', set)
      spaces.pluck :id
    elsif where
      @property.traits.where(value_id: @value.id).pluck :space_id
    else
      @property.traits.where(value_id: @value.compliment).pluck :space_id
    end
  end

  def ~
    Atom.new @property, ~@value
  end

  def verify space
    witness = space.traits.where(property: @property, value: @value).first
    witness.nil? ? false : [witness]
  end

  def force space, assumptions, theorem, index
    trait = space.traits.create! property: @property, value: @value, deduced: true
    proof = Proof.create! trait: trait, theorem: theorem, theorem_index: index
    assumptions.each do |assumption|
      proof.assumptions.create! trait: assumption
      if assumption.supporters.empty?
        trait.supporters.create! assumed_id: assumption.id
      else
        assumption.supporters.each do |supporter|
          trait.supporters.create! assumed_id: supporter.assumed_id
        end
      end
    end
    trait
  end

  def atoms
    [self]
  end

  def as_json opts={}
    { _type: :atom, property: @property.id, value: @value.id }
  end

  private # ----------

  def name
    case value
    when Value.true
      property.name
    when Value.false
      "¬#{property.name}"
    else
      "#{property.name} = #{value.name}"
    end
  end

  def self.parse_name_or_id str, klass, default=nil
    return default if !str.present? && default
    str.to_i.zero? ? klass.where(name: str).first! : klass.find(str.to_i)
  rescue ActiveRecord::RecordNotFound => e
    raise ParseError.new "Unrecognized #{klass}: #{str}"
  end
end