<commit_before>module TimeLineable
  extend ActiveSupport::Concern

  # Stopped_at is never included in the period because it is the started_at of the next period!
  included do
    validates_presence_of :started_at # , :if => :has_previous?
    validates_presence_of :stopped_at, if: :has_followings?

    scope :at,      ->(at) { where(arel_table[:started_at].lteq(at).and(arel_table[:stopped_at].eq(nil).or(arel_table[:stopped_at].gt(at)))) }
    scope :after,   ->(at) { where(arel_table[:started_at].gt(at)) }
    scope :before,  ->(at) { where(arel_table[:started_at].lt(at)) }
    scope :current, -> { at(Time.zone.now) }

    before_validation do
      following = begin
                    siblings.after(started_at).order(:started_at).first
                  rescue
                    nil
                  end
      if started_at && following
        self.stopped_at = following.started_at
      else
        self.started_at ||= Time.new(1, 1, 1, 0, 0, 0, '+00:00')
        self.stopped_at ||= nil
      end
    end

    validate do
      if self.started_at && stopped_at
        if stopped_at <= self.started_at
          errors.add(:stopped_at, :posterior, to: self.started_at.l)
        end
      end
    end

    before_update do
      old = old_record
      if old.started_at != self.started_at && previous = old.previous
        previous.update_column(:stopped_at, old.stopped_at)
      end
    end

    after_save do
      if previous = siblings.before(self.started_at).reorder('started_at DESC').first || siblings.find_by(started_at: nil)
        previous.update_column(:stopped_at, self.started_at)
      end
    end

    after_destroy do
      if previous = self.previous
        self.previous.update_column(:stopped_at, stopped_at)
      end
    end
  end

  module ClassMethods
    def first_of_all
      reorder(:started_at).first
    end

    def last_of_all
      reorder(:started_at).last
    end
  end

  def previous
    return nil unless self.started_at
    siblings.before(self.started_at).order(started_at: :desc).first
  end

  def following
    return nil unless stopped_at
    siblings.after(self.started_at).order(started_at: :asc).first
  end

  def followings
    siblings.after(self.started_at)
  end

  def has_previous?
    siblings.before(self.started_at).any?
  rescue
    false
  end

  def has_followings?
    followings.any?
  rescue
    false
  end

  def last_for_now?
    followings.before(Time.zone.now).empty?
  end

  private

  def siblings
    raise NotImplementedError, 'Private method :siblings must be implemented'
  end
end<commit_msg>Modified TimeLineable to prevent possible future bugs due to bad sibling definition.<commit_after>module TimeLineable
  extend ActiveSupport::Concern

  # Stopped_at is never included in the period because it is the started_at of the next period!
  included do
    validates_presence_of :started_at # , :if => :has_previous?
    validates_presence_of :stopped_at, if: :has_followings?

    scope :at,      ->(at) { where(arel_table[:started_at].lteq(at).and(arel_table[:stopped_at].eq(nil).or(arel_table[:stopped_at].gt(at)))) }
    scope :after,   ->(at) { where(arel_table[:started_at].gt(at)) }
    scope :before,  ->(at) { where(arel_table[:started_at].lt(at)) }
    scope :current, -> { at(Time.zone.now) }

    before_validation do
      following_object = begin
                    following
                  rescue
                    nil
                  end
      if started_at && following_object
        self.stopped_at = following_object.started_at
      else
        self.started_at ||= Time.zone.now if other_siblings.any?
        self.started_at ||= Time.new(1, 1, 1, 0, 0, 0, '+00:00')
        self.stopped_at ||= nil
      end
    end

    validate do
      if self.started_at && stopped_at
        if stopped_at <= self.started_at
          errors.add(:stopped_at, :posterior, to: self.started_at.l)
        end
      end
    end

    before_update do
      old = old_record
      if old.started_at != self.started_at && previous = old.previous
        previous.update_column(:stopped_at, old.stopped_at)
      end
    end

    after_save do
      if previous = other_siblings.before(self.started_at).reorder('started_at DESC').first || siblings.find_by(started_at: nil)
        previous.update_column(:stopped_at, self.started_at)
      end
    end

    after_destroy do
      if previous = self.previous
        self.previous.update_column(:stopped_at, stopped_at)
      end
    end
  end

  module ClassMethods
    def first_of_all
      reorder(:started_at).first
    end

    def last_of_all
      reorder(:started_at).last
    end
  end

  def previous
    return nil unless self.started_at
    other_siblings.before(self.started_at).order(started_at: :desc).first
  end

  def following
    return nil unless stopped_at
    followings.order(started_at: :asc).first
  end

  def followings
    other_siblings.after(self.started_at)
  end

  def has_previous?
    other_siblings.before(self.started_at).any?
  rescue
    false
  end

  def has_followings?
    followings.any?
  rescue
    false
  end

  def last_for_now?
    followings.before(Time.zone.now).empty?
  end

  private

  def siblings
    raise NotImplementedError, 'Private method :siblings must be implemented'
  end

  def other_siblings
    safe_id = id
    safe_id ||= 0
    siblings.where.not(id: safe_id)
  end
end
{'fraction_non_alphanumeric': 0.09287696577243293, 'fraction_numerical': 0.0038852913968547642, 'mean_word_length': 2.685071574642127, 'pattern_counts': {'":': 0, '<': 5, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4994222', 'n_tokens_mistral': 1839, 'n_tokens_neox': 1743, 'n_words': 389}