class ActiveTsv::Relation

Constants

BUF_SIZE

Attributes

group_values[RW]
model[R]
order_values[RW]
where_values[RW]

Public Class Methods

new(model) click to toggle source
# File lib/active_tsv/relation.rb, line 14
def initialize(model)
  @model = model
  @where_values = []
  @order_values = []
  @group_values = []
end

Public Instance Methods

==(other) click to toggle source
# File lib/active_tsv/relation.rb, line 27
def ==(other)
  where_values == other.where_values &&
    order_values == other.order_values &&
    group_values == other.group_values
end
count() click to toggle source
Calls superclass method
# File lib/active_tsv/relation.rb, line 121
def count
  if @group_values.empty?
    super
  else
    h = if @group_values.one?
      group_by { |i| i[@group_values.first] }
    else
      group_by { |i| @group_values.map { |c| i[c] } }
    end
    h.each do |k, v|
      h[k] = v.count
    end
    h
  end
end
each(*args, &block) click to toggle source
# File lib/active_tsv/relation.rb, line 149
def each(*args, &block)
  to_a.each(*args, &block)
end
exists?() click to toggle source
# File lib/active_tsv/relation.rb, line 75
def exists?
  !first.nil?
end
find(*ids) click to toggle source
# File lib/active_tsv/relation.rb, line 33
def find(*ids)
  case ids.length
  when 0
    raise ActiveTsv::RecordNotFound, "Couldn't find #{@model} without an ID"
  when 1
    id = ids.first
    record = where(@model.primary_key => id).first
    unless record
      raise ActiveTsv::RecordNotFound, "Couldn't find #{@model} with '#{@model.primary_key}'=#{id}"
    end
    record
  else
    records = where(@model.primary_key => ids).to_a
    unless ids.length == records.length
      raise ActiveTsv::RecordNotFound, "Couldn't find all #{@model} with '#{@model.primary_key}': (#{ids.join(', ')}) (found #{records.length} results, but was looking for #{ids.length})"
    end
    records
  end
end
first() click to toggle source
# File lib/active_tsv/relation.rb, line 79
def first
  if @order_values.empty?
    each_model.first
  else
    to_a.first
  end
end
group(*columns) click to toggle source
# File lib/active_tsv/relation.rb, line 143
def group(*columns)
  @group_values += columns
  @group_values.uniq!
  self
end
initialize_copy(copy) click to toggle source
# File lib/active_tsv/relation.rb, line 21
def initialize_copy(copy)
  copy.where_values = where_values.dup
  copy.order_values = order_values.dup
  copy.group_values = group_values.dup
end
inspect() click to toggle source
# File lib/active_tsv/relation.rb, line 157
def inspect
  a = to_value_a.take(11).map! { |i| @model.new(i) }.map!(&:inspect)
  a[10] = '...' if a.length == 11

  "#<#{self.class.name} [#{a.join(', ')}]>"
end
last() click to toggle source
# File lib/active_tsv/relation.rb, line 87
def last
  if @where_values.empty? && @order_values.empty?
    last_value = File.open(@model.table_path, "r:#{@model.encoding}:UTF-8") do |f|
      f.seek(0, IO::SEEK_END)
      buf_size = [f.size, self.class::BUF_SIZE].min
      while true
        f.seek(-buf_size, IO::SEEK_CUR)
        buf = f.read(buf_size)
        if index = buf.rindex($INPUT_RECORD_SEPARATOR, -2)
          f.seek(-buf_size + index + 1, IO::SEEK_CUR)
          break f.read.chomp
        else
          f.seek(-buf_size, IO::SEEK_CUR)
        end
      end
    end
    @model.new(CSV.new(last_value, col_sep: @model::SEPARATER).shift)
  else
    @model.new(to_value_a.last)
  end
end
maximum(column) click to toggle source
# File lib/active_tsv/relation.rb, line 164
def maximum(column)
  pluck(column).max
end
minimum(column) click to toggle source
# File lib/active_tsv/relation.rb, line 168
def minimum(column)
  pluck(column).min
end
order(*columns) click to toggle source
# File lib/active_tsv/relation.rb, line 137
def order(*columns)
  @order_values += order_conditions(columns)
  @order_values.uniq!
  self
end
pluck(*fields) click to toggle source
# File lib/active_tsv/relation.rb, line 63
def pluck(*fields)
  key_to_value_index = @model.column_names.each_with_index.to_h
  if fields.empty?
    to_value_a
  elsif fields.one?
    to_value_a.map! { |v| v[key_to_value_index[fields.first.to_s]] }
  else
    indexes = fields.map(&:to_s).map! { |field| key_to_value_index[field] }
    to_value_a.map! { |v| v.values_at(*indexes) }
  end
end
take(n = nil) click to toggle source
# File lib/active_tsv/relation.rb, line 109
def take(n = nil)
  if n
    if @order_values.empty?
      each_model.take(n)
    else
      to_value_a.take(n).map! { |i| @model.new(i) }
    end
  else
    first
  end
end
to_a() click to toggle source
# File lib/active_tsv/relation.rb, line 153
def to_a
  to_value_a.map! { |v| @model.new(v) }
end
where(where_value = nil) click to toggle source
# File lib/active_tsv/relation.rb, line 53
def where(where_value = nil)
  if where_value
    dup.tap do |r|
      r.where_values << Condition::Equal.new(where_value)
    end
  else
    WhereChain.new(dup)
  end
end

Private Instance Methods

each_value() { |value| ... } click to toggle source
# File lib/active_tsv/relation.rb, line 196
def each_value
  return to_enum(__method__) unless block_given?

  key_to_value_index = @model.column_names.each_with_index.to_h
  @model.open do |csv|
    csv.gets if !@model.custom_column_name?
    csv.each do |value|
      yield value if @where_values.all? do |cond|
        case cond
        when Condition::Equal
          cond.values.all? do |k, v|
            index = key_to_value_index[k.to_s]
            raise StatementInvalid, "no such column: #{k}" unless index
            if v.respond_to?(:to_a)
              v.to_a.any? { |vv| value[index] == vv.to_s }
            else
              value[index] == v.to_s
            end
          end
        when Condition::NotEqual
          cond.values.all? do |k, v|
            index = key_to_value_index[k.to_s]
            raise StatementInvalid, "no such column: #{k}" unless index
            if v.respond_to?(:to_a)
              !v.to_a.any? { |vv| value[index] == vv.to_s }
            else
              !(value[index] == v.to_s)
            end
          end
        end
      end
    end
to_value_a() click to toggle source
# File lib/active_tsv/relation.rb, line 174
def to_value_a
  ret = each_value.to_a
  if @order_values.empty?.!
    key_to_value_index = @model.column_names.each_with_index.to_h
    if @order_values.one?
      order_condition = @order_values.first
      index = key_to_value_index[order_condition.column]
      ret.sort_by! { |i| i[index] }
      ret.reverse! if order_condition.descending?
    else
      ret.sort! do |a, b|
        @order_values.each.with_index(1) do |order_condition, index|
          comp = a[key_to_value_index[order_condition.column]] <=> b[key_to_value_index[order_condition.column]]
          break 0 if comp == 0 && index == @order_values.length
          break comp * order_condition.to_i if comp != 0
        end
      end
    end
  end
  ret
end