forked from fixin.me/fixin.me
66 lines
1.9 KiB
Ruby
66 lines
1.9 KiB
Ruby
class Quantity < ApplicationRecord
|
|
belongs_to :user, optional: true
|
|
belongs_to :parent, optional: true, class_name: "Quantity"
|
|
has_many :subquantities, class_name: "Quantity", inverse_of: :parent,
|
|
dependent: :restrict_with_error
|
|
|
|
validate if: ->{ parent.present? } do
|
|
errors.add(:parent, :user_mismatch) unless user == parent.user
|
|
end
|
|
validates :name, presence: true, length: {maximum: type_for_attribute(:name).limit}
|
|
validates :description, length: {maximum: type_for_attribute(:description).limit}
|
|
|
|
scope :defaults, ->{ where(user: nil) }
|
|
scope :ordered, ->{
|
|
cte = Arel::Table.new('cte')
|
|
numbered = Arel::Table.new('numbered')
|
|
Quantity.with(numbered: numbered(:parent_id, :name)).with_recursive(
|
|
cte:
|
|
[
|
|
Arel::SelectManager.new.project(
|
|
numbered[Arel.star],
|
|
numbered.cast(numbered[:child_number], 'BINARY').as('path')
|
|
).from(numbered).where(numbered[:parent_id].eq(nil)),
|
|
Arel::SelectManager.new.project(
|
|
numbered[Arel.star],
|
|
cte[:path].concat(numbered[:child_number])
|
|
).from(numbered).join(cte).on(numbered[:parent_id].eq(cte[:id]))
|
|
]
|
|
).select(cte[Arel.star]).from(cte).order(cte[:path])
|
|
}
|
|
|
|
scope :numbered, ->(parent_column, order_column){
|
|
select(
|
|
arel_table[Arel.star],
|
|
Arel::Nodes::NamedFunction.new(
|
|
'LPAD',
|
|
[
|
|
Arel::Nodes::NamedFunction.new(
|
|
'ROW_NUMBER', []
|
|
).over(
|
|
Arel::Nodes::Window.new.partition(parent_column).order(order_column)
|
|
),
|
|
Arel::SelectManager.new.project(
|
|
Arel::Nodes::NamedFunction.new(
|
|
'LENGTH', [Arel::Nodes::NamedFunction.new('COUNT', [Arel.star])]
|
|
)
|
|
),
|
|
Arel::Nodes.build_quoted('0')
|
|
],
|
|
).as('child_number')
|
|
)
|
|
}
|
|
|
|
def to_s
|
|
name
|
|
end
|
|
|
|
def movable?
|
|
subunits.empty?
|
|
end
|
|
|
|
def default?
|
|
parent_id.nil?
|
|
end
|
|
end
|