Module: ApplicationHelper

Includes:
ControllerHelper
Defined in:
app/helpers/application_helper.rb

Overview

rubocop:todo Metrics/ModuleLength

Instance Method Summary collapse

Methods included from ControllerHelper

#add

Methods included from Informatics::Globals

#application, #application=, #defaults, #defaults=, #global_searchable_classes, #search_options

Instance Method Details

#about(title = '') ⇒ Object

From Pipelines



209
210
211
# File 'app/helpers/application_helper.rb', line 209

def about(title = '')
  add :about, title
end

#api_dataObject



90
91
92
# File 'app/helpers/application_helper.rb', line 90

def api_data
  { api_version: RELEASE.api_version }
end

#apple_iconString

Return the appropriate apple icon for the current environment

Returns:

  • (String)

    The path to the apple icon



70
71
72
# File 'app/helpers/application_helper.rb', line 70

def apple_icon
  "apple-icon#{icon_suffix}.png"
end

#badge(status, type: 'generic-badge', style: status) ⇒ type

Renders a badge containing the supplied text, with appropriate styling. By default the 'badge-#{status}' class is applied. These states are mapped to bootstrap colours in components.scss (grep '// State-colour extensions')

If you can't map the text directly to a style, such as if you are displaying a number that you want to change its colours at certain thresholds, then you can override the applied style with the style: argument.

If the string passed in is empty, no badge will be rendered

Examples:

Render a request state badge.

badge(request.state, type: 'request')

Render the size of a batch, which is red if too large.

status = batch.size > MAX_SIZE ? 'danger' : 'success'
badge(batch.size, type: 'batch-size', style: status )

Parameters:

  • status (String)

    The text to display in the badge. Will also be used to set the style if not otherwise specified

  • type (String) (defaults to: 'generic-badge')

    Optional: Additional css-class applied to the badge (generic-badge by default)

  • style (String) (defaults to: status)

    Optional: Override the badge-* class otherwise set directly from the status.

Returns:

  • (type)

    HTML to render a badge



131
132
133
134
135
# File 'app/helpers/application_helper.rb', line 131

def badge(status, type: 'generic-badge', style: status)
  return if status.blank?

  tag.span(status, class: "#{type} badge badge-#{style}")
end

#counter_badge(counter, suffix = '') ⇒ String

Used to add a counter to headers or links. Renders a blue badge containing the supplied number Only supply a suffix if it can't be worked out from the context what is being counted.

Parameters:

  • counter (Integer)

    The value to show in the badge

  • suffix (Integer, String) (defaults to: '')

    Optional: The type of thing being counted.

Returns:

  • (String)

    HTML to render a badge



144
145
146
147
# File 'app/helpers/application_helper.rb', line 144

def counter_badge(counter, suffix = '')
  status = suffix.present? ? pluralize(counter, suffix) : counter
  badge(status, type: 'counter-badge', style: 'primary')
end

#custom_text(identifier, differential = nil) ⇒ Object

Should return either the custom text or a blank string



7
8
9
10
11
12
13
14
15
# File 'app/helpers/application_helper.rb', line 7

def custom_text(identifier, differential = nil)
  Rails
    .cache
    .fetch("#{identifier}-#{differential}") do
      custom_text = CustomText.find_by(identifier:, differential:)

      custom_text.try(:content) || ''
    end
end

#display_boolean_results(result) ⇒ Object



259
260
261
262
263
264
265
266
267
# File 'app/helpers/application_helper.rb', line 259

def display_boolean_results(result)
  return 'NA' if result.blank?

  if %w[pass 1 true].include?(result)
    icon('far', 'check-circle', title: result)
  else
    icon('fas', 'exclamation-circle', class: 'text-danger', title: result)
  end
end

#display_follow(item, user, msg) ⇒ Object



203
204
205
# File 'app/helpers/application_helper.rb', line 203

def display_follow(item, user, msg)
  user.follower_of?(item) ? "Unfollow #{msg}" : "Follow #{msg}"
end

#display_user_error(display_text, link = nil) ⇒ Object



104
105
106
# File 'app/helpers/application_helper.rb', line 104

def display_user_error(display_text, link = nil)
  alert(:danger) { link.present? ? link_to(display_text, link) : display_text }
end

#display_user_guide(display_text, link = nil) ⇒ type

Renders a user guide with optional link. Applies appropriate styling

Parameters:

  • display_text (String)

    The text of the user guide

  • link (String) (defaults to: nil)

    Optional url to link the guide to.

Returns:

  • (type)

    [description]



100
101
102
# File 'app/helpers/application_helper.rb', line 100

def display_user_guide(display_text, link = nil)
  alert(:user_guide) { concat link.present? ? link_to(display_text, link) : display_text }
end

rubocop:todo Metrics/MethodLength



150
151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'app/helpers/application_helper.rb', line 150

def dynamic_link_to(summary_item) # rubocop:todo Metrics/AbcSize
  object = summary_item.object
  if object.instance_of?(Submission)
    link_to("Submission #{object.id}", study_information_submission_path(object.study, object))
  elsif object.instance_of?(Receptacle)
    link_to("#{object.label.capitalize} #{object.name}", receptacle_path(object))
  elsif object.instance_of?(Labware)
    link_to("#{object.label.capitalize} #{object.name}", labware_path(object))
  elsif object.instance_of?(Request)
    link_to("Request #{object.id}", request_path(object))
  else
    'No link available'
  end
end

#faviconString

Return the appropriate favicon for the current environment

Returns:

  • (String)

    The path to the favicon



64
65
66
# File 'app/helpers/application_helper.rb', line 64

def favicon
  "favicon#{icon_suffix}.ico"
end


291
292
293
# File 'app/helpers/application_helper.rb', line 291

def fresh_sevice_link
  link_to 'FreshService', configatron.fresh_sevice_new_ticket_url
end


285
286
287
288
289
# File 'app/helpers/application_helper.rb', line 285

def help_link(text, entry = '', options = {})
  url = "#{configatron.help_link_base_url}/#{entry}"
  options[:class] = "#{options[:class]} external_help"
  link_to text, url, options
end

#help_textObject



281
282
283
# File 'app/helpers/application_helper.rb', line 281

def help_text(&)
  tag.small(class: 'form-text text-muted col', &)
end

#hidden_label_tag_for_testing(name, text = nil, options = {}) ⇒ Object

Creates a label that is hidden from the view so that testing is easier



277
278
279
# File 'app/helpers/application_helper.rb', line 277

def hidden_label_tag_for_testing(name, text = nil, options = {})
  label_tag(name, text, options.merge(style: 'display:none;'))
end

#icon_suffixString

Returns the appropriate icon suffix for the current environment Returns empty string for production Returns “-#environment” for training, staging Returns “-development” for any other environment

Returns:

  • (String)

    The suffix to append to the icon name



44
45
46
47
48
49
50
51
52
53
54
# File 'app/helpers/application_helper.rb', line 44

def icon_suffix
  environment = Rails.env
  case environment
  when 'production'
    ''
  when 'training', 'staging'
    "-#{environment}"
  else
    '-development'
  end
end

#legacy_javascript_tagString

Ideally we don't want inline script tags, however there is a fair chunk of legacy code, some of which isn't trivial to migrate, as it uses erb to generate javascript, rather than using data-attributes.

This tag: - Ensures we add a nonce for security - If the page is still loading, delays script execution until DOMContentLoaded to ensure that the modern JS has had a chance to export jQuery - If the page has already loaded, executes the script immediately. This is needed for use cases where the partial that renders this script is loaded after the main page has loaded e.g. the admin study edit page, within the admin study index page.

Returns:

  • (String)

    Script tag



347
348
349
350
351
352
353
354
355
# File 'app/helpers/application_helper.rb', line 347

def legacy_javascript_tag
  javascript_tag nonce: true do
    concat 'if (document.readyState === "loading") {window.addEventListener("DOMContentLoaded", function() {'.html_safe # rubocop:disable Layout/LineLength
    yield
    concat '});} else {'.html_safe
    yield
    concat '}'.html_safe
  end
end

#remote_error(identifier = 'remote_error') ⇒ Object

TODO:

Probably remove this and the references to it in app/views/studies/information/_items.html.erb Or possibly restore the intended behaviour in app/assets/javascripts/sequencescape/ajax_link_handling.js

Renders a non-displayed error div warning of a data failure Appears to have been intended to be used to provide error feedback on the studies in app/views/studies/information/_items.html.erb but actual behaviour will result in the error payload being placed in the div, but remaining invisible.

Parameters:

  • identifier (String) (defaults to: 'remote_error')

    The id of the element



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

def remote_error(identifier = 'remote_error')
  tag.div(id: identifier, class: 'error', style: 'display:none;') do
    'An error has occurred and the results can not be shown at the moment'
  end
end

#render_flashesObject



74
75
76
77
# File 'app/helpers/application_helper.rb', line 74

def render_flashes
  flash.each { |key, message| concat(alert(key, id: "message_#{key}") { render_message(message) }) }
  nil
end

#render_message(messages) ⇒ Object

A helper method for render_flashes - If multiple messages, render them as a list, else render as a single div

Parameters:

  • messages (Array<String>, String)

    The flash message or messages to be rendered



81
82
83
84
85
86
87
88
# File 'app/helpers/application_helper.rb', line 81

def render_message(messages)
  messages = Array(messages)
  if messages.size > 1
    tag.ul { messages.each { |m| concat tag.li(m) } }
  else
    tag.div(messages.first)
  end
end

#render_parsed_json(json) ⇒ String

Handles rendering of JSON to a series of nested lists. Does the following: String: Rendered as-is Array: Unordered list (Strictly speaking arrays are ordered, but we probably don't care.) Object: Descriptive list Other: Calls to_s Processes each in turn and called recursively

rubocop:todo Metrics/MethodLength

Parameters:

  • json (Hash, String, Array, , #to_s)

    The Object to render

Returns:

  • (String)

    HTML formatted for rendering



308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
# File 'app/helpers/application_helper.rb', line 308

def render_parsed_json(json) # rubocop:todo Metrics/AbcSize
  case json
  when String
    json
  when Array
    tag.ul { json.each { |elem| concat tag.li(render_parsed_json(elem)) } }
  when Hash
    tag.dl do
      json.each do |key, value|
        # Strictly speaking json should only have strings as keys. But the same constraint doesn't apply to hashes,
        # so we're a little more permissive here for flexibilities sake
        concat tag.dt(render_parsed_json(key))
        concat tag.dd(render_parsed_json(value))
      end
    end
  else
    json.to_s
  end
end

rubocop:enable Metrics/MethodLength



167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
# File 'app/helpers/application_helper.rb', line 167

def request_count_link(study, asset, state, request_type) # rubocop:todo Metrics/AbcSize
  matching_requests =
    asset.requests.select { |request| (request.request_type_id == request_type.id) and request.state == state }
  html_options, count = { title: "#{asset.try(:human_barcode) || asset.id} #{state}" }, matching_requests.size

  # 0 requests => no link, just '0'
  # 1 request  => request summary page
  # N requests => summary overview
  if count == 1
    url_path = request_path(matching_requests.first)
    link_to count, url_path, html_options
  elsif count > 1
    url_path = study_requests_path(study, state: state, request_type_id: request_type.id, asset_id: asset.id)
    link_to count, url_path, html_options
  end
end

rubocop:todo Metrics/ParameterLists



185
186
187
188
# File 'app/helpers/application_helper.rb', line 185

def request_link(object, count, request_type, status = nil, options = {}, link_options = {})
  # rubocop:enable Metrics/ParameterLists
  link_to_if(count != 0, count, request_list_path(object, request_type, status, options), link_options)
end

#request_list_path(object, request_type = nil, status = nil, options = {}) ⇒ Object



190
191
192
193
194
195
196
197
198
199
200
201
# File 'app/helpers/application_helper.rb', line 190

def request_list_path(object, request_type = nil, status = nil, options = {})
  options[:state] = status unless status.nil?
  options[:request_type_id] = request_type.id unless request_type.nil?

  if object.instance_of?(Receptacle)
    receptacle_path(object, options)
  elsif object.instance_of?(Labware)
    labware_path(object, options)
  elsif object.instance_of?(Study)
    study_requests_path(object, options)
  end
end

#required_markerString

Inserts the icon used to indicate a field is required. This will also be displayed on any <label> tags with the .required class

Returns:

  • (String)

    HTML representing the required marker



35
36
37
# File 'app/helpers/application_helper.rb', line 35

def required_marker
  icon('fas', 'asterisk', class: 'text-warning', title: 'required')
end

#sequencescape_logoString

Return the appropriate Sequencescape logo for the current environment

Returns:

  • (String)

    The path to the logo image



58
59
60
# File 'app/helpers/application_helper.rb', line 58

def 
  "images/logo-gradient#{icon_suffix}.svg"
end

#sorted_requests_for_search(requests) ⇒ Object



269
270
271
272
273
274
# File 'app/helpers/application_helper.rb', line 269

def sorted_requests_for_search(requests)
  sorted_requests = requests.select { |r| r.pipeline_id.nil? }
  new_requests = requests - sorted_requests
  new_requests.sort_by(&:pipeline_id)
  requests += sorted_requests
end

#tab(name, target: nil, active: false, id: nil) ⇒ Object



232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
# File 'app/helpers/application_helper.rb', line 232

def tab(name, target: nil, active: false, id: nil)
  target ||= name.parameterize
  active_class = active ? 'active' : ''
  id ||= "#{name}-tab".parameterize
  tag.li(class: 'nav-item') do
    link_to name,
            "##{target}",
            id: id,
            data: {
              toggle: 'tab'
            },
            role: 'tab',
            aria_controls: target,
            class: ['nav-link', active_class]
  end
end

#tab_pane(name, id: nil, tab_id: nil, active: false) ⇒ Object

yield


252
253
254
255
256
257
# File 'app/helpers/application_helper.rb', line 252

def tab_pane(name, id: nil, tab_id: nil, active: false, &)
  tab_id ||= "#{name}-tab".parameterize
  id ||= name.parameterize
  active_class = active ? 'active' : ''
  tag.div(class: ['tab-pane', 'fade', 'show', active_class], id: id, role: 'tabpanel', aria_labelledby: tab_id, &)
end

#tabulated_error_messages_for(*params) ⇒ Object

rubocop:todo Metrics/AbcSize



213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'app/helpers/application_helper.rb', line 213

def tabulated_error_messages_for(*params) # rubocop:todo Metrics/AbcSize
  options = params.last.is_a?(Hash) ? params.pop.symbolize_keys : {}
  objects = params.filter_map { |object_name| instance_variable_get(:"@#{object_name}") }
  count = objects.inject(0) { |sum, object| sum + object.errors.count }
  if count.zero?
    ''
  else
    error_messages = objects.map { |object| object.errors.full_messages.map { |msg| tag.div(msg) } }.join
    [
      tag.td(class: 'error item') { "Your #{params.first} has not been created." },
      tag.td(class: 'error') { raw(error_messages) }
    ].join.html_safe
  end
end