feat(orm): extend relation with initialize and includes
This commit is contained in:
1 parent
18c885feff
commit
9af9e0cd04
1 file changed
+129
-8
+129
-8
@@ -2,22 +2,50 @@ module Prism
|
|||||||
module Orm
|
module Orm
|
||||||
class Relation
|
class Relation
|
||||||
include Enumerable
|
include Enumerable
|
||||||
|
include Pagination
|
||||||
|
include CollectionSerialization
|
||||||
|
|
||||||
def initialize(model, conditions: {}, order_by: nil, limit_value: nil, offset_value: nil)
|
attr_reader :condition
|
||||||
|
|
||||||
|
def initialize(model, condition: Query::MatchAll.new, order_by: nil, limit_value: nil,
|
||||||
|
offset_value: nil, distinct_value: false, includes_value: [],
|
||||||
|
page_value: nil, per_page_value: nil)
|
||||||
@model = model
|
@model = model
|
||||||
@conditions = conditions
|
@condition = condition
|
||||||
@order_by = order_by
|
@order_by = order_by
|
||||||
@limit_value = limit_value
|
@limit_value = limit_value
|
||||||
@offset_value = offset_value
|
@offset_value = offset_value
|
||||||
|
@distinct_value = distinct_value
|
||||||
|
@includes_value = includes_value
|
||||||
|
@page_value = page_value
|
||||||
|
@per_page_value = per_page_value
|
||||||
|
end
|
||||||
|
|
||||||
|
def includes(*names)
|
||||||
|
spawn(includes_value: @includes_value + names.flatten.map(&:to_sym))
|
||||||
end
|
end
|
||||||
|
|
||||||
def where(conditions)
|
def where(conditions)
|
||||||
spawn(conditions: @conditions.merge(conditions.transform_keys(&:to_s)))
|
spawn(condition: @condition.and(Query::HashNode.new(conditions, model: @model)))
|
||||||
|
end
|
||||||
|
|
||||||
|
def not(conditions)
|
||||||
|
spawn(condition: @condition.and(Query::HashNode.new(conditions, model: @model).negate))
|
||||||
|
end
|
||||||
|
|
||||||
|
def or(other)
|
||||||
|
other_condition = other.is_a?(Relation) ? other.condition : Query::HashNode.new(other, model: @model)
|
||||||
|
spawn(condition: @condition.or(other_condition))
|
||||||
|
end
|
||||||
|
|
||||||
|
def none
|
||||||
|
spawn(condition: Query::MatchNone.new)
|
||||||
end
|
end
|
||||||
|
|
||||||
def order(column, direction = :asc)
|
def order(column, direction = :asc)
|
||||||
spawn(order_by: [column.to_s, direction.to_sym])
|
spawn(order_by: [column.to_s, direction.to_sym])
|
||||||
end
|
end
|
||||||
|
alias_method :reorder, :order
|
||||||
|
|
||||||
def limit(count)
|
def limit(count)
|
||||||
spawn(limit_value: count)
|
spawn(limit_value: count)
|
||||||
@@ -27,17 +55,27 @@ module Prism
|
|||||||
spawn(offset_value: count)
|
spawn(offset_value: count)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def distinct
|
||||||
|
spawn(distinct_value: true)
|
||||||
|
end
|
||||||
|
|
||||||
def find_by(conditions)
|
def find_by(conditions)
|
||||||
where(conditions).first
|
where(conditions).first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def find_by!(conditions)
|
||||||
|
find_by(conditions) || raise(RecordNotFound, "no #{@model.name} matching #{conditions.inspect}")
|
||||||
|
end
|
||||||
|
|
||||||
def each
|
def each
|
||||||
return enum_for(:each) unless block_given?
|
return enum_for(:each) unless block_given?
|
||||||
rows.each { |row| yield @model.instantiate(row) }
|
to_a.each { |record| yield record }
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_a
|
def to_a
|
||||||
rows.map { |row| @model.instantiate(row) }
|
records = rows.map { |row| @model.instantiate(row) }
|
||||||
|
preload(records)
|
||||||
|
records
|
||||||
end
|
end
|
||||||
|
|
||||||
def count
|
def count
|
||||||
@@ -52,24 +90,107 @@ module Prism
|
|||||||
count ? to_a.first(count) : to_a.first
|
count ? to_a.first(count) : to_a.first
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def first!
|
||||||
|
first || raise(RecordNotFound, "no #{@model.name} found")
|
||||||
|
end
|
||||||
|
|
||||||
def last(count = nil)
|
def last(count = nil)
|
||||||
count ? to_a.last(count) : to_a.last
|
count ? to_a.last(count) : to_a.last
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def last!
|
||||||
|
last || raise(RecordNotFound, "no #{@model.name} found")
|
||||||
|
end
|
||||||
|
|
||||||
def pluck(attribute)
|
def pluck(attribute)
|
||||||
key = attribute.to_s
|
key = attribute.to_s
|
||||||
rows.map { |row| row[key] }
|
values = rows.map { |row| row[key] }
|
||||||
|
@distinct_value ? values.uniq : values
|
||||||
|
end
|
||||||
|
|
||||||
|
def pick(*attributes)
|
||||||
|
row = rows.first
|
||||||
|
return nil unless row
|
||||||
|
keys = attributes.map(&:to_s)
|
||||||
|
keys.size == 1 ? row[keys.first] : keys.map { |key| row[key] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def ids
|
||||||
|
pluck('id')
|
||||||
|
end
|
||||||
|
|
||||||
|
def sum(column)
|
||||||
|
numeric_values(column).sum
|
||||||
|
end
|
||||||
|
|
||||||
|
def average(column)
|
||||||
|
values = numeric_values(column)
|
||||||
|
return nil if values.empty?
|
||||||
|
values.sum.to_f / values.size
|
||||||
|
end
|
||||||
|
alias_method :avg, :average
|
||||||
|
|
||||||
|
def minimum(column)
|
||||||
|
numeric_values(column).min
|
||||||
|
end
|
||||||
|
alias_method :min_of, :minimum
|
||||||
|
|
||||||
|
def maximum(column)
|
||||||
|
numeric_values(column).max
|
||||||
|
end
|
||||||
|
alias_method :max_of, :maximum
|
||||||
|
|
||||||
|
def group(column)
|
||||||
|
key = column.to_s
|
||||||
|
to_a.group_by { |record| record[key] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def group_count(column)
|
||||||
|
key = column.to_s
|
||||||
|
rows.each_with_object(Hash.new(0)) { |row, counts| counts[row[key]] += 1 }
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_each(batch_size: 1000)
|
||||||
|
return enum_for(:find_each, batch_size: batch_size) unless block_given?
|
||||||
|
find_in_batches(batch_size: batch_size) { |batch| batch.each { |record| yield record } }
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_in_batches(batch_size: 1000)
|
||||||
|
return enum_for(:find_in_batches, batch_size: batch_size) unless block_given?
|
||||||
|
to_a.each_slice(batch_size) { |batch| yield batch }
|
||||||
|
end
|
||||||
|
|
||||||
|
def respond_to_missing?(name, include_private = false)
|
||||||
|
model_scopes.key?(name) || super
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_missing(name, *args, &block)
|
||||||
|
body = model_scopes[name]
|
||||||
|
return super unless body
|
||||||
|
instance_exec(*args, &body)
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
|
def model_scopes
|
||||||
|
@model.respond_to?(:scopes) ? @model.scopes : {}
|
||||||
|
end
|
||||||
|
|
||||||
|
def numeric_values(column)
|
||||||
|
pluck(column).compact
|
||||||
|
end
|
||||||
|
|
||||||
def spawn(overrides)
|
def spawn(overrides)
|
||||||
self.class.new(
|
self.class.new(
|
||||||
@model,
|
@model,
|
||||||
conditions: overrides.fetch(:conditions, @conditions),
|
condition: overrides.fetch(:condition, @condition),
|
||||||
order_by: overrides.fetch(:order_by, @order_by),
|
order_by: overrides.fetch(:order_by, @order_by),
|
||||||
limit_value: overrides.fetch(:limit_value, @limit_value),
|
limit_value: overrides.fetch(:limit_value, @limit_value),
|
||||||
offset_value: overrides.fetch(:offset_value, @offset_value)
|
offset_value: overrides.fetch(:offset_value, @offset_value),
|
||||||
|
distinct_value: overrides.fetch(:distinct_value, @distinct_value),
|
||||||
|
includes_value: overrides.fetch(:includes_value, @includes_value),
|
||||||
|
page_value: overrides.fetch(:page_value, @page_value),
|
||||||
|
per_page_value: overrides.fetch(:per_page_value, @per_page_value)
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user