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



229
230
231
# File 'app/helpers/application_helper.rb', line 229

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

#api_dataObject



110
111
112
# File 'app/helpers/application_helper.rb', line 110

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



151
152
153
154
155
# File 'app/helpers/application_helper.rb', line 151

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



164
165
166
167
# File 'app/helpers/application_helper.rb', line 164

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



279
280
281
282
283
284
285
286
287
# File 'app/helpers/application_helper.rb', line 279

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



223
224
225
# File 'app/helpers/application_helper.rb', line 223

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

#display_user_error(display_text, link = nil) ⇒ Object



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

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]



120
121
122
# File 'app/helpers/application_helper.rb', line 120

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



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

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


311
312
313
# File 'app/helpers/application_helper.rb', line 311

def fresh_service_link
  link_to 'FreshService', configatron.fresh_sevice_new_ticket_url
end


305
306
307
308
309
# File 'app/helpers/application_helper.rb', line 305

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



301
302
303
# File 'app/helpers/application_helper.rb', line 301

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



297
298
299
# File 'app/helpers/application_helper.rb', line 297

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



367
368
369
370
371
372
373
374
375
# File 'app/helpers/application_helper.rb', line 367

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_in_list(messages) ⇒ Object



106
107
108
# File 'app/helpers/application_helper.rb', line 106

def render_in_list(messages)
  tag.ul { messages.each { |message| concat tag.li(message) } }
end

#render_message(messages) ⇒ ActiveSupport::SafeBuffer

A helper method for render_flashes. If messages is a Hash, renders them as a described list, with the keys as the description and the values as the items. If messages is an Array with multiple messages, renders them as a list. If a single message, renders as a single div.

Parameters:

  • messages (Hash, Array<String>, String)

    The flash message or messages to be rendered

Returns:

  • (ActiveSupport::SafeBuffer)

    HTML-safe string for rendering the messages



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
# File 'app/helpers/application_helper.rb', line 87

def render_message(messages) # rubocop:disable Metrics/MethodLength
  case messages
  when Hash
    safe_join(
      messages.map do |description, items|
        tag.div(description) + render_in_list(Array(items))
      end
    )
  when Array
    if messages.size > 1
      render_in_list(messages)
    else # messages has only one element, render it as a single div
      tag.div(messages.first)
    end
  else
    tag.div(messages)
  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



328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'app/helpers/application_helper.rb', line 328

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



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

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



205
206
207
208
# File 'app/helpers/application_helper.rb', line 205

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



210
211
212
213
214
215
216
217
218
219
220
221
# File 'app/helpers/application_helper.rb', line 210

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



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

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



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# File 'app/helpers/application_helper.rb', line 252

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


272
273
274
275
276
277
# File 'app/helpers/application_helper.rb', line 272

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



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

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