Class: Inferno::Entities::Input

Inherits:
Object
  • Object
show all
Includes:
Attributes
Defined in:
lib/inferno/entities/input.rb

Overview

This class represents an Input for a runnable.

Constant Summary collapse

ATTRIBUTES =
[
  :name,
  :title,
  :description,
  :type,
  :default,
  :optional,
  :options,
  :locked,
  :hidden,
  :value,
  :enable_when
].freeze
UNINHERITABLE_ATTRIBUTES =

These attributes require special handling when merging input definitions.

[
  # Locking, hiding, or conditional display only have meaning at the level
  # they are applied. Consider:
  # - ParentGroup
  #   - Group 1, input :a
  #   - Group 2, input :a, locked: true, hidden: true, enable_when: {...}, optional: true
  # The input 'a' should only be locked, hidden, or conditionally shown when
  # running Group 2 in isolation. It should not inherit those when running
  # Group 1 or the ParentGroup.
  :locked,
  :hidden,
  :enable_when,
  # Input type is sometimes only a UI concern (e.g. text vs. textarea), so
  # it is common to not redeclare the type everywhere it's used and needs
  # special handling to avoid clobbering the type with the default (text)
  # type.
  :type
].freeze
INHERITABLE_ATTRIBUTES =

These are the attributes that can be directly copied when merging a runnable’s input with the input of one of its children.

(ATTRIBUTES - UNINHERITABLE_ATTRIBUTES).freeze
MERGEABLE_ATTRIBUTES =

These are the attributes that can be directly copied when merging a runnable’s input with an input configuration.

(ATTRIBUTES - [:type, :options]).freeze

Instance Method Summary collapse

Methods included from Attributes

included

Constructor Details

#initialize(**params) ⇒ Input

Returns a new instance of Input.



52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# File 'lib/inferno/entities/input.rb', line 52

def initialize(**params)
  bad_params = params.keys - ATTRIBUTES

  raise Exceptions::UnknownAttributeException.new(bad_params, self.class) if bad_params.present?

  if params[:hidden] && !params[:optional] && !params[:locked]
    raise Exceptions::InvalidAttributeException.new(
      :hidden,
      self.class,
      "Input '#{params[:name]}' cannot be hidden unless it is optional or locked."
    )
  end

  assert_enable_when_shape!(params)

  params
    .compact
    .each { |key, value| send("#{key}=", value) }

  self.name = name.to_s if params[:name].present?
end

Instance Method Details

#==(other) ⇒ Object



209
210
211
212
213
# File 'lib/inferno/entities/input.rb', line 209

def ==(other)
  return false unless other.is_a? Input

  ATTRIBUTES.all? { |attribute| send(attribute) == other.send(attribute) }
end

#assert_enable_when_shape!(params) ⇒ Object



74
75
76
77
78
79
80
81
82
83
84
# File 'lib/inferno/entities/input.rb', line 74

def assert_enable_when_shape!(params)
  enable_when = params[:enable_when]
  return if enable_when.blank?
  return if enable_when_valid?(enable_when)

  raise Exceptions::InvalidAttributeException.new(
    :enable_when,
    self.class,
    'must be a Hash with a non-empty String :input_name and a String :value'
  )
end

#enable_when_valid?(enable_when) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
89
90
91
92
# File 'lib/inferno/entities/input.rb', line 86

def enable_when_valid?(enable_when)
  type_is_hash = enable_when.is_a?(Hash)
  input_name_string_exists = enable_when[:input_name].is_a?(String) && enable_when[:input_name].present?
  value_string_exists = enable_when.key?(:value) && enable_when[:value].is_a?(String)

  type_is_hash && input_name_string_exists && value_string_exists
end

#merge(other_input, merge_all: false) ⇒ Object

Merge this input with an input from a configuration. Fields defined in the configuration take precedence over those defined on this input.



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/inferno/entities/input.rb', line 114

def merge(other_input, merge_all: false)
  return self if other_input.nil?

  attributes_to_merge = merge_all ? ATTRIBUTES : MERGEABLE_ATTRIBUTES

  attributes_to_merge.each do |attribute|
    merge_attribute(attribute, primary_source: other_input, secondary_source: self)
  end

  self.type = other_input.type if other_input.type.present? && other_input.type != 'text'

  merge_options(primary_source: other_input, secondary_source: self)

  self
end

#merge_attribute(attribute, primary_source:, secondary_source:) ⇒ Object

Merge an individual attribute. If the primary source contains the attribute, that value will be used. Otherwise the value from the secondary source will be used.

Parameters:

  • attribute (Symbol)
  • primary_source (Input)
  • secondary_source (Input)


137
138
139
140
141
142
143
144
# File 'lib/inferno/entities/input.rb', line 137

def merge_attribute(attribute, primary_source:, secondary_source:)
  value = primary_source.send(attribute)
  value = secondary_source.send(attribute) if value.nil?

  return if value.nil?

  send("#{attribute}=", value)
end

#merge_components(primary_components:, secondary_components:) ⇒ Object

Merge component hashes.

Parameters:

  • primary_components (Input)
  • secondary_components (Input)


170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'lib/inferno/entities/input.rb', line 170

def merge_components(primary_components:, secondary_components:) # rubocop:disable Metrics/CyclomaticComplexity
  primary_components
    .each { |component| component[:name] = component[:name].to_sym }
  secondary_components
    .each { |component| component[:name] = component[:name].to_sym }

  return if primary_components.blank? && secondary_components.blank?

  component_keys =
    (primary_components + secondary_components)
      .map { |component| component[:name] }
      .uniq

  merged_components = component_keys.map do |key|
    primary_component = primary_components.find { |component| component[:name] == key }
    secondary_component = secondary_components.find { |component| component[:name] == key }

    next secondary_component if primary_component.blank?

    next primary_component if secondary_component.blank?

    Input.new(**secondary_component).merge(Input.new(**primary_component), merge_all: true).to_hash
  end

  merged_components.each { |component| component[:name] = component[:name].to_sym }

  self.options ||= {}
  self.options[:components] = merged_components
end

#merge_options(primary_source:, secondary_source:) ⇒ Object

Merge input options. This performs a normal merge for all options except for the “components” field, the members of which are individually merged by ‘merge_components`

Parameters:



152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/inferno/entities/input.rb', line 152

def merge_options(primary_source:, secondary_source:)
  primary_options = primary_source.options.dup || {}
  secondary_options = secondary_source.options.dup || {}

  return if primary_options.blank? && secondary_options.blank?

  primary_components = primary_options.delete(:components) || []
  secondary_components = secondary_options.delete(:components) || []

  send('options=', secondary_options.merge(primary_options))

  merge_components(primary_components:, secondary_components:)
end

#merge_with_child(child_input) ⇒ Object

Merge this input with an input belonging to a child. Fields defined on this input take precedence over those defined on the child input.



97
98
99
100
101
102
103
104
105
106
107
108
109
# File 'lib/inferno/entities/input.rb', line 97

def merge_with_child(child_input)
  return self if child_input.nil?

  INHERITABLE_ATTRIBUTES.each do |attribute|
    merge_attribute(attribute, primary_source: self, secondary_source: child_input)
  end

  self.type = child_input.type if child_input.present? && child_input.type != 'text'

  merge_options(primary_source: self, secondary_source: child_input)

  self
end

#to_hashObject



200
201
202
203
204
205
206
207
# File 'lib/inferno/entities/input.rb', line 200

def to_hash
  ATTRIBUTES.each_with_object({}) do |attribute, hash|
    value = send(attribute)
    next if value.nil?

    hash[attribute] = value
  end
end