module StateOfTheNation::ClassMethods

Attributes

finish_key[RW]
ignore_empty[RW]
parent_association[RW]
prevent_multiple_active[RW]
start_key[RW]

Public Instance Methods

considered_active(ignore_empty: false) click to toggle source
# File lib/state_of_the_nation.rb, line 18
def considered_active(ignore_empty: false)
  @ignore_empty = ignore_empty

  def from(start_key)
    @start_key = start_key
    self
  end

  def until(finish_key)
    @finish_key = finish_key

    define_method "active?" do |time = Time.now.utc|
      (finish.blank? || round_if_should(finish) > round_if_should(time)) && round_if_should(start) <= round_if_should(time)
    end

    define_method "active_in_interval?" do |interval_start, interval_end|
      record_start = round_if_should(start)
      record_end = round_if_should(finish)
      if ignore_empty && record_start == record_end
        false
      elsif interval_start.nil? && interval_end.nil?
        true
      elsif interval_start == interval_end
        active?(interval_start)
      elsif interval_start.nil?
        record_start < interval_end
      elsif interval_end.nil?
        record_end.nil? || record_end > interval_start
      elsif record_end.nil?
        interval_end > record_start
      else
        record_start < interval_end && record_end > interval_start
      end
    end

    scope :active, lambda { |time = Time.now.utc|
      where(QueryString.query_for(:active_scope, self), round_if_should(time), round_if_should(time))
    }
  end

  private def round_if_should(time)
    return time if !should_round_timestamps?
    time.respond_to?(:round) ? time.round : time
  end

  private def should_round_timestamps?
    # MySQL datetime fields do not support millisecond resolution while
    # PostgreSQL's do. To prevent issues with near identical timestamps not
    # comparing as expected in .active? methods we'll choose the resolution
    # appropriate for the database adapter backing the model.
    case self.connection.adapter_name
    when /PostgreSQL/
      false
    else
      true
    end
  end

  self
end
from(start_key) click to toggle source
# File lib/state_of_the_nation.rb, line 21
def from(start_key)
  @start_key = start_key
  self
end
has_active(association_plural) click to toggle source
# File lib/state_of_the_nation.rb, line 79
def has_active(association_plural)
  @association_plural = association_plural
  add_child_methods(plural: @association_plural, single: false, with_identity_cache: false)

  def with_identity_cache
    add_child_methods(plural: @association_plural, single: false, with_identity_cache: true)
    self
  end

  self
end
has_uniquely_active(association_singular) click to toggle source
# File lib/state_of_the_nation.rb, line 91
def has_uniquely_active(association_singular)
  @association_plural = association_singular.to_s.pluralize
  add_child_methods(plural: @association_plural, single: true, with_identity_cache: false)

  def with_identity_cache
    add_child_methods(plural: @association_plural, single: true, with_identity_cache: true)
    self
  end

  self
end
round_if_should(time) click to toggle source
# File lib/state_of_the_nation.rb, line 58
        def round_if_should(time)
  return time if !should_round_timestamps?
  time.respond_to?(:round) ? time.round : time
end
should_round_timestamps?() click to toggle source
# File lib/state_of_the_nation.rb, line 63
        def should_round_timestamps?
  # MySQL datetime fields do not support millisecond resolution while
  # PostgreSQL's do. To prevent issues with near identical timestamps not
  # comparing as expected in .active? methods we'll choose the resolution
  # appropriate for the database adapter backing the model.
  case self.connection.adapter_name
  when /PostgreSQL/
    false
  else
    true
  end
end
until(finish_key) click to toggle source
# File lib/state_of_the_nation.rb, line 26
def until(finish_key)
  @finish_key = finish_key

  define_method "active?" do |time = Time.now.utc|
    (finish.blank? || round_if_should(finish) > round_if_should(time)) && round_if_should(start) <= round_if_should(time)
  end

  define_method "active_in_interval?" do |interval_start, interval_end|
    record_start = round_if_should(start)
    record_end = round_if_should(finish)
    if ignore_empty && record_start == record_end
      false
    elsif interval_start.nil? && interval_end.nil?
      true
    elsif interval_start == interval_end
      active?(interval_start)
    elsif interval_start.nil?
      record_start < interval_end
    elsif interval_end.nil?
      record_end.nil? || record_end > interval_start
    elsif record_end.nil?
      interval_end > record_start
    else
      record_start < interval_end && record_end > interval_start
    end
  end

  scope :active, lambda { |time = Time.now.utc|
    where(QueryString.query_for(:active_scope, self), round_if_should(time), round_if_should(time))
  }
end
with_identity_cache() click to toggle source
# File lib/state_of_the_nation.rb, line 83
def with_identity_cache
  add_child_methods(plural: @association_plural, single: false, with_identity_cache: true)
  self
end

Private Instance Methods

add_child_methods(plural:, single:, with_identity_cache:) click to toggle source
# File lib/state_of_the_nation.rb, line 105
def add_child_methods(plural:, single:, with_identity_cache:)
  child_class = self.reflect_on_association(plural).klass
  name = self.name.demodulize.underscore.to_sym
  child_class.instance_variable_set(:@parent_association, name)
  child_class.instance_variable_set(:@prevent_multiple_active, single)

  association = single ? plural.singularize : plural

  define_method "active_#{association}" do |time = Time.now.utc|
    method_name = with_identity_cache ? "fetch_#{plural}" : plural.to_sym
    collection = send(method_name).select { |r| r.send("active?", time) }
    single ? collection.first : collection
  end
end