# frozen_string_literal: true module AceOfBase # A query builder for querying our data. class Query INVALID_FIELD_MESSAGE = 'Field %s is not a valid field name' def initialize(storage) @fields = [] @order_bys = [] @filters = [] @storage = storage end def select(*fields) ensure_valid_fields!(fields) @fields = fields self end def order_by(*fields) ensure_valid_fields!(fields) @order_bys = fields self end def filter(field, value) ensure_valid_fields!([field]) @filters << [field, value] self end def execute @storage .select(&method(:select_with_filters)) .sort(&method(:sort_with_order_bys)) .map(&method(:take_fields)) end private attr_reader :fields, :order_bys, :filters, :storage def ensure_valid_fields!(fields) fields.each do |field| raise ArgumentError, format(INVALID_FIELD_MESSAGE, field) \ unless Record::ATTRIBUTES.include?(field.to_sym) end end def take_fields(record) selected_fields.map { |field| record.public_send(field) } end def sort_with_order_bys(lhs, rhs) @order_bys .lazy .map { |attr| lhs.public_send(attr) <=> rhs.public_send(attr) } .find(-> { 0 }, &:nonzero?) end def select_with_filters(record) @filters.all? do |(key, value)| record.public_send(key) == value end end def selected_fields return Record::ATTRIBUTES if fields.empty? fields end end end