Module: CurrentScope::ApplicationHelper
- Defined in:
- app/helpers/current_scope/application_helper.rb
Class Method Summary collapse
-
.warn_subject_label_broken_once(error) ⇒ Object
The configured value could not even be classified — see the rescue in configured_subject_label.
- .warn_subject_label_raised_once(label, error) ⇒ Object
-
.warn_unknown_subject_label_once(label) ⇒ Object
Warn ONCE per (reason, label): a misconfigured subject_label is the same mistake on every row, so one line is a signal and one-per-subject is noise the admin will scroll past.
-
.warn_unusable_subject_label_once(label) ⇒ Object
Not a Symbol, String, Proc, or nil — so it can't name a method and can't be called.
Instance Method Summary collapse
-
#configured_subject_label(subject) ⇒ Object
The configured subject_label (Symbol or Proc) applied to a subject, or nil when unset, when it resolves to a blank value, or when it fails.
-
#current_scope_gid_label(gid) ⇒ Object
Best-effort label for a stored GID string (event actor/subject).
- #current_scope_holder_resource_label(scoped_assignment) ⇒ Object
-
#current_scope_holder_subject_label(assignment) ⇒ Object
Members-list labels that survive a stale/renamed polymorphic type — a removed subject or resource class must not 500 the page; fall back to "Type #id" the way the audit ledger does.
-
#current_scope_label(record) ⇒ Object
Best-effort human label for any host subject/resource.
-
#current_scope_subject_label(subject) ⇒ Object
Human label for a subject (user/account), honouring config.subject_label so a host on UUID keys can show email or a full name instead of an opaque id.
Class Method Details
.warn_subject_label_broken_once(error) ⇒ Object
The configured value could not even be classified — see the rescue in configured_subject_label. Keyed by the error's class, and says nothing about the label itself, because reading the label is what failed.
184 185 186 187 188 189 190 191 192 |
# File 'app/helpers/current_scope/application_helper.rb', line 184 def warn_subject_label_broken_once(error) return unless new_subject_label_warning?(:broken, safe_class(error)) Rails.logger&.warn( "[CurrentScope] config.subject_label could not be read — #{safe_class(error)}: " \ "#{(error)}. Every subject is falling back to the default label. Set it to " \ "a Symbol (e.g. :email), a Proc taking the subject, or nil." ) end |
.warn_subject_label_raised_once(label, error) ⇒ Object
170 171 172 173 174 175 176 177 178 179 |
# File 'app/helpers/current_scope/application_helper.rb', line 170 def warn_subject_label_raised_once(label, error) return unless new_subject_label_warning?(:raised, label) Rails.logger&.warn( "[CurrentScope] config.subject_label raised #{safe_class(error)}: #{(error)} " \ "— that subject fell back to the default label rather than erroring the page. A " \ "subject_label must be total: it runs for every subject, including ones with nil or " \ "blank attributes." ) end |
.warn_unknown_subject_label_once(label) ⇒ Object
Warn ONCE per (reason, label): a misconfigured subject_label is the same mistake on every row, so one line is a signal and one-per-subject is noise the admin will scroll past. Keyed by the label value, so changing the config in dev warns again for the new value.
Always-on rather than behind a warn_on_* flag (cf. warn_on_nil_sod_record, which defaults off because a nil record is often legitimate): a label the subject can't answer, or one that raises, is unambiguously a mistake. Mirrors Event.warn_missing_events_table_once.
Says "this subject" rather than "subjects": under STI some subclasses may answer and others not, and warn-once means the first non-responder would otherwise speak for all of them.
145 146 147 148 149 150 151 152 153 154 |
# File 'app/helpers/current_scope/application_helper.rb', line 145 def warn_unknown_subject_label_once(label) return unless new_subject_label_warning?(:unknown, label) Rails.logger&.warn( "[CurrentScope] config.subject_label is #{label.inspect}, but this subject does not " \ "respond to it — it is falling back to the default label. If no subject responds to it, " \ "this config does nothing. Set it to a method the subject responds to (e.g. :email), a " \ "Proc, or nil." ) end |
.warn_unusable_subject_label_once(label) ⇒ Object
Not a Symbol, String, Proc, or nil — so it can't name a method and can't
be called. Reports the TYPE, not the value: this is the one warning whose
subject is an arbitrary host object, and calling inspect on it could
itself raise — inside the very path that exists to stop a raise.
160 161 162 163 164 165 166 167 168 |
# File 'app/helpers/current_scope/application_helper.rb', line 160 def warn_unusable_subject_label_once(label) return unless new_subject_label_warning?(:unusable, label.class) Rails.logger&.warn( "[CurrentScope] config.subject_label is a #{label.class} — it can't name a method and " \ "can't be called, so every subject is falling back to the default label. Set it to a " \ "Symbol (e.g. :email), a Proc taking the subject, or nil." ) end |
Instance Method Details
#configured_subject_label(subject) ⇒ Object
The configured subject_label (Symbol or Proc) applied to a subject, or nil when unset, when it resolves to a blank value, or when it fails.
subject_label is arbitrary host code running once per row on the admin's
main tool for granting and reviewing roles. One subject with incomplete
data (a Proc doing u.email.upcase on a subject whose email is nil) must
not take the whole page down — the same intent the holder-label helpers
below already encode for stale polymorphic types. A failure here costs one
label; a raise costs the page.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
# File 'app/helpers/current_scope/application_helper.rb', line 45 def configured_subject_label(subject) label = CurrentScope.config.subject_label return if label.nil? return resolve_subject_label(label) { label.call(subject) } if label.respond_to?(:call) # Only a Symbol or String names a method. Checked BEFORE respond_to?, # which raises TypeError on anything else ("false is not a symbol nor a # string") — and that raise would land outside the rescue below and 500 # the page, which is the bug this method exists to prevent, reached # through a different door. unless label.is_a?(Symbol) || label.is_a?(String) CurrentScope::ApplicationHelper.warn_unusable_subject_label_once(label) return end if subject.respond_to?(label) resolve_subject_label(label) { subject.public_send(label) } else # Not a mistake we can render around: this config does nothing for EVERY # subject, silently, which is why it needs saying out loud once. CurrentScope::ApplicationHelper.warn_unknown_subject_label_once(label) nil end rescue StandardError => e # Last resort, and the reason it exists: every branch above calls a method # ON the host's config value to classify it — `nil?`, `respond_to?`, # `is_a?`. A pathological value breaks the classification itself, before # any specific guard can classify it, so the guards cannot be where this # is caught. A BasicObject has no #nil?; a delegator can raise from # respond_to?. # # Deliberately does NOT touch `label`: it is the thing that just proved it # cannot be touched. The warning is keyed off the error alone. CurrentScope::ApplicationHelper.warn_subject_label_broken_once(e) nil end |
#current_scope_gid_label(gid) ⇒ Object
Best-effort label for a stored GID string (event actor/subject). Falls back to the raw GID when the record is gone — the ledger outlives the identities it names.
100 101 102 103 104 105 106 107 |
# File 'app/helpers/current_scope/application_helper.rb', line 100 def current_scope_gid_label(gid) record = GlobalID::Locator.locate(gid) record ? current_scope_label(record) : gid rescue ActiveRecord::RecordNotFound, NameError # locate RAISES for a deleted record or renamed class (nil is only for # unparseable strings) — same contract the holder helpers rescue above. gid end |
#current_scope_holder_resource_label(scoped_assignment) ⇒ Object
91 92 93 94 95 |
# File 'app/helpers/current_scope/application_helper.rb', line 91 def current_scope_holder_resource_label(scoped_assignment) current_scope_label(scoped_assignment.resource) rescue NameError, ActiveRecord::RecordNotFound "#{scoped_assignment.resource_type} ##{scoped_assignment.resource_id}" end |
#current_scope_holder_subject_label(assignment) ⇒ Object
Members-list labels that survive a stale/renamed polymorphic type — a removed subject or resource class must not 500 the page; fall back to "Type #id" the way the audit ledger does.
85 86 87 88 89 |
# File 'app/helpers/current_scope/application_helper.rb', line 85 def current_scope_holder_subject_label(assignment) current_scope_subject_label(assignment.subject) rescue NameError, ActiveRecord::RecordNotFound "#{assignment.subject_type} ##{assignment.subject_id}" end |
#current_scope_label(record) ⇒ Object
Best-effort human label for any host subject/resource. Delegates to the one shared chain (CurrentScope.label_for) — the same definition the audit ledger denormalizes (see Event.label_for), so the picker, the chips, and the ledger agree by construction, not by parallel maintenance.
7 8 9 10 11 |
# File 'app/helpers/current_scope/application_helper.rb', line 7 def current_scope_label(record) return "(none)" if record.nil? CurrentScope.label_for(record) end |
#current_scope_subject_label(subject) ⇒ Object
Human label for a subject (user/account), honouring config.subject_label so a host on UUID keys can show email or a full name instead of an opaque id. Falls back to the best-effort current_scope_label.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'app/helpers/current_scope/application_helper.rb', line 16 def current_scope_subject_label(subject) return "(none)" if subject.nil? # A configured label that resolves to nil/blank (e.g. :email on a subject # with no email yet) must not render as an empty row — fall through to the # default human-identifier chain rather than showing "". configured = configured_subject_label(subject) return configured if configured # Default: a subject is a person, so prefer human identifiers over a # generic "Class #id" — including when the model includes Scopeable, whose # current_scope_label is id-based. Covers `email` and Rails 8 auth's # `email_address`. Config overrides this entirely. full_name = [ subject.try(:first_name), subject.try(:last_name) ].compact.join(" ").presence # .presence on each identifier: a stored empty/whitespace email ("") is # truthy in Ruby and would otherwise short-circuit to a blank label. subject.try(:email).presence || subject.try(:email_address).presence || subject.try(:name).presence || full_name || current_scope_label(subject) end |