Module: RoundhouseUi::TagsHelper

Defined in:
app/helpers/roundhouse_ui/tags_helper.rb

Overview

Renders host-defined job tags (see ADR 0002). Resolution is memoized for the life of the request: a page can render hundreds of rows, and ErrorGroups scans up to DEFAULT_SCAN_LIMIT entries, so the host's resolver must not be called once per row when it only varies by class.

Instance Method Summary collapse

Instance Method Details

#any_filter?(query, tag, queue = rh_filter.queue) ⇒ Boolean

Any filter active? Bulk-on-match is filter-gated, so this decides whether the bulk bar renders at all — and which empty-state copy is honest. Delegates to the concern's predicate where it is available, so the button the view offers and the scope the route enforces cannot drift apart. They did: this returned true only for the view, while bulk_all had no gate at all.

Returns:

  • (Boolean)


115
116
117
118
119
120
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 115

def any_filter?(query, tag, queue = rh_filter.queue)
  return controller.send(:bulk_filter_present?, query, tag) if controller.respond_to?(:bulk_filter_present?, true)

  query.present? || !tag.nil? || queue.present? ||
    rh_filter.klass.present? || rh_filter.error.present?
end

#filter_description(query, tag, queue = rh_filter.queue) ⇒ Object



88
89
90
91
92
93
94
95
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 88

def filter_description(query, tag, queue = rh_filter.queue)
  parts = []
  parts << "matching “#{query}" if query.present?
  parts << "tagged #{tag[0]}: #{tag[1]}" if tag
  parts << "in queue #{queue}" if queue.present?
  parts << like_description if rh_filter.klass.present? || rh_filter.error.present?
  parts.join(" and ")
end

#like_descriptionObject

"find more like this", said back to you. The wording has to be exact about what it selected, because the bulk Delete sitting next to it acts on precisely this set and nothing wider.



100
101
102
103
104
105
106
107
108
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 100

def like_description
  # Either half stands on its own. "every Timeout::Error, whatever the class"
  # is a question worth asking, so clearing the class must not take the error
  # with it — which is what it used to do.
  return "failing with #{rh_filter.error}" if rh_filter.klass.blank?
  return "like #{rh_filter.klass}" if rh_filter.error.blank?

  "like #{rh_filter.klass} failing with #{rh_filter.error}"
end

#rh_filterObject

Human description of the active filter set, so a bulk confirm names every constraint that will be applied — not just the text query. Every active constraint must appear here. A confirm that names only the text query while a queue or tag also narrows the set understates what is about to be destroyed. filter is the controller's parse, reached through the helper. One source for the prose, the predicate and the pill — a description built from a second reading of the filter is a description that can disagree with what was applied.



84
85
86
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 84

def rh_filter
  controller.respond_to?(:filter, true) ? controller.send(:filter) : RoundhouseUi::FilterQuery.none
end

#tag_badge(key, value) ⇒ Object

Value only — the key is near-constant down a column, so repeating it is noise. It stays in the tooltip for hosts with more than one dimension.



124
125
126
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 124

def tag_badge(key, value)
  (:span, value, class: "rh-pill rh-pill-tag", title: "#{key}: #{value}")
end

#tag_badges(tags) ⇒ Object

Pills for a resolved tag Hash. Renders nothing when there are no tags, so every call site can be unconditional.



34
35
36
37
38
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 34

def tag_badges(tags)
  return if tags.blank?

  safe_join(tags.map { |key, value| tag_badge(key, value) }, " ")
end

#tag_cacheObject

The per-request memo handed to Tags.for, shared with JobSetBrowsing so a class (or job) resolved while scanning is not resolved again when its badge renders. Tags.for chooses the key: class name normally, jid in per-job mode.



11
12
13
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 11

def tag_cache
  @rh_tag_cache ||= {}
end

#tag_cell(tags) ⇒ Object

Cell contents. Stacks when a host defines more than one dimension.



55
56
57
58
59
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 55

def tag_cell(tags)
  return (:span, "", class: "rh-sub") if tags.blank?

  tag_badges(tags)
end

#tag_column?Boolean

Tags get their own table column rather than an inline badge: class names vary wildly in length, so inline badges land at ragged x-positions and can't be scanned down. Only worth a column when a host configured tags.

Returns:

  • (Boolean)


43
44
45
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 43

def tag_column?
  RoundhouseUi.job_tags.present?
end

#tag_column_labelObject

Header for that column. With the usual single dimension this is the tag's own name ("squad"); with several there's no one right label.



49
50
51
52
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 49

def tag_column_label
  keys = Tags.filters&.keys
  keys&.one? ? keys.first.titleize : "Tags"
end

#tag_vocabularyObject

Values offered as filter chips where no counts are available. Prefers the host's declared vocabulary, so the chips stay put as you filter; falls back to whatever the visible rows happen to carry, which at least beats nothing but shifts as you page.



65
66
67
68
69
70
71
72
73
74
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 65

def tag_vocabulary
  declared = Tags.filters
  return declared if declared.present?

  seen = Hash.new { |h, k| h[k] = [] }
  Array(@jobs).each do |job|
    tags_for(job).each { |key, value| seen[key] << value unless seen[key].include?(value) }
  end
  seen.transform_values(&:sort)
end

#tags_for(entry) ⇒ Object

Tags for one job entry, as a { "key" => "value" } Hash.



16
17
18
19
20
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 16

def tags_for(entry)
  return Tags::EMPTY unless RoundhouseUi.job_tags

  Tags.for(klass: entry.klass, item: entry.item, cache: tag_cache)
end

#tags_for_class(klass) ⇒ Object

Tags for a class name alone — used by grouped Errors, where every entry in a group shares a class, so a class-derived tag is constant for the group. Passes no payload, so a per-job resolver correctly declines rather than attributing one job's tags to the whole group.



26
27
28
29
30
# File 'app/helpers/roundhouse_ui/tags_helper.rb', line 26

def tags_for_class(klass)
  return Tags::EMPTY unless RoundhouseUi.job_tags

  Tags.for(klass: klass, item: {}, cache: tag_cache)
end