feat(orm): add chainable relation for querying records
This commit is contained in:
1 parent
4fbb7bc8d0
commit
b131acd7fe
1 file changed
+103
@@ -0,0 +1,103 @@
|
||||
module Prism
|
||||
module Orm
|
||||
class Relation
|
||||
include Enumerable
|
||||
|
||||
def initialize(model, conditions: {}, order_by: nil, limit_value: nil, offset_value: nil)
|
||||
@model = model
|
||||
@conditions = conditions
|
||||
@order_by = order_by
|
||||
@limit_value = limit_value
|
||||
@offset_value = offset_value
|
||||
end
|
||||
|
||||
def where(conditions)
|
||||
spawn(conditions: @conditions.merge(conditions.transform_keys(&:to_s)))
|
||||
end
|
||||
|
||||
def order(column, direction = :asc)
|
||||
spawn(order_by: [column.to_s, direction.to_sym])
|
||||
end
|
||||
|
||||
def limit(count)
|
||||
spawn(limit_value: count)
|
||||
end
|
||||
|
||||
def offset(count)
|
||||
spawn(offset_value: count)
|
||||
end
|
||||
|
||||
def find_by(conditions)
|
||||
where(conditions).first
|
||||
end
|
||||
|
||||
def each
|
||||
return enum_for(:each) unless block_given?
|
||||
rows.each { |row| yield @model.instantiate(row) }
|
||||
end
|
||||
|
||||
def to_a
|
||||
rows.map { |row| @model.instantiate(row) }
|
||||
end
|
||||
|
||||
def count
|
||||
rows.size
|
||||
end
|
||||
|
||||
def exists?
|
||||
!rows.empty?
|
||||
end
|
||||
|
||||
def first(count = nil)
|
||||
count ? to_a.first(count) : to_a.first
|
||||
end
|
||||
|
||||
def last(count = nil)
|
||||
count ? to_a.last(count) : to_a.last
|
||||
end
|
||||
|
||||
def pluck(attribute)
|
||||
key = attribute.to_s
|
||||
rows.map { |row| row[key] }
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def spawn(overrides)
|
||||
self.class.new(
|
||||
@model,
|
||||
conditions: overrides.fetch(:conditions, @conditions),
|
||||
order_by: overrides.fetch(:order_by, @order_by),
|
||||
limit_value: overrides.fetch(:limit_value, @limit_value),
|
||||
offset_value: overrides.fetch(:offset_value, @offset_value)
|
||||
)
|
||||
end
|
||||
|
||||
def rows
|
||||
filtered = @model.store.all(@model.table_name).select { |row| matches?(row) }
|
||||
filtered = apply_order(filtered)
|
||||
filtered = filtered.drop(@offset_value) if @offset_value
|
||||
filtered = filtered.first(@limit_value) if @limit_value
|
||||
filtered
|
||||
end
|
||||
|
||||
def matches?(row)
|
||||
@conditions.all? do |key, expected|
|
||||
actual = row[key.to_s]
|
||||
case expected
|
||||
when Range then expected.cover?(actual)
|
||||
when Array then expected.include?(actual)
|
||||
else actual == expected
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def apply_order(rows)
|
||||
return rows unless @order_by
|
||||
column, direction = @order_by
|
||||
sorted = rows.sort_by { |row| row[column] }
|
||||
direction == :desc ? sorted.reverse : sorted
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in new issue
Block a user