Compare commits

..

5 Commits

4 changed files with 41 additions and 25 deletions

View File

@ -379,7 +379,8 @@ table.items td {
}
/* For <a> to fill <td> completely, we use an ::after pseudoelement. */
table.items td.link {
padding: 0 0 0 1em;
padding: 0;
padding-inline-start: calc(1em + var(--depth) * 0.8em);
position: relative;
}
table.items td.link a {

View File

@ -12,38 +12,33 @@ class Quantity < ApplicationRecord
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])
Quantity.with(numbered: numbered(:parent_id, :name)).with_recursive(quantities: [
Arel::SelectManager.new.project(
numbered[Arel.star],
numbered.cast(numbered[:child_number], 'BINARY').as('path'),
Arel::Nodes.build_quoted(0).as('depth')
).from(numbered).where(numbered[:parent_id].eq(nil)),
Arel::SelectManager.new.project(
numbered[Arel.star],
arel_table[:path].concat(numbered[:child_number]),
arel_table[:depth] + 1
).from(numbered).join(arel_table).on(numbered[:parent_id].eq(arel_table[:id]))
]).select(arel_table[Arel.star]).from(arel_table).order(arel_table[:path])
}
# TODO: extract named functions to custom Arel extension
# https://gist.github.com/ProGM/c6df08da14708dcc28b5ca325df37ceb#extending-arel
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::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::NamedFunction.new('LENGTH', [Arel.star.count])])
),
Arel::Nodes.build_quoted('0')
],
@ -62,4 +57,24 @@ class Quantity < ApplicationRecord
def default?
parent_id.nil?
end
# Return: record, its ancestors and succesive record in order of appearance,
# including :depth attribute. Used for table view reload.
def successive
quantities = Quantity.arel_table
ancestors = Arel::Table.new('ancestors')
Quantity.with(
ancestors: user.quantities.ordered.select(
Arel::Nodes::NamedFunction.new('LAG', [quantities[:id]]).over.as('lag_id')
)
)
.with_recursive(quantities: [
Arel::SelectManager.new.project(ancestors[Arel.star]).from(ancestors)
.where(ancestors[:id].eq(id).or(ancestors[:lag_id].eq(id))),
Arel::SelectManager.new.project(ancestors[Arel.star]).from(ancestors)
.join(quantities).on(quantities[:parent_id].eq(ancestors[:id]))
.where(quantities[:lag_id].not_eq(id))
]).order(quantities[:path])
# return: .first == self ? nul, ancestors : ancestors.pop, ancestors
end
end

View File

@ -10,7 +10,6 @@
</td>
<td class="number">
<% unless @unit.base.nil? %>
<%= form.hidden_field :base_id %>
<%= form.number_field :multiplier, required: true, size: 10, min: :step %>
<% end %>
</td>

View File

@ -7,7 +7,8 @@
<%= turbo_stream.disable ids[:link] -%>
<%= turbo_stream.append :unit_form do %>
<%- tabular_form_with model: @unit, html: {id: ids[:form_tag]} do %>
<%- tabular_form_with model: @unit, html: {id: ids[:form_tag]} do |form| %>
<%= form.hidden_field :base_id unless @unit.base.nil? %>
<% end %>
<% end %>