class Quby::Compiler::Entities::Question

Constants

MARKDOWN_ATTRIBUTES

Attributes

allow_blank_titles[RW]

Question validation fails when there are no title and no context_free_title. When :allow_blank_titles => true passed, validation does not fail. Any other value will raise the failure.

allow_duplicate_option_values[R]

Whether to skip the uniqueness validation on radio and select option values

as[RW]

How should we display this question

autocomplete[RW]

Whether the browser should autocomplete this question (off by default)

col_span[RW]

In case of being displayed inside a table, amount of columns/rows to span

cols[RW]
context_free_title[RW]
default_invisible[RW]
default_position[RW]

Slider only: where to place the sliding thing by default Can have value :hidden for a hidden handle.

dependencies[RW]
depends_on[RW]

This question should not validate itself unless the depends_on question is answered. May also be an array of “#{question_key}_#{option_key}” strings that specify options this question depends on.

description[RW]
deselectable[RW]

Whether this radio question is deselectable

disallow_bulk[RW]

Whether we can collapse this in bulk view

display_modes[RW]

In what modes do we display this question NOTE We always display questions in print-view (if they have an answer)

extra_data[RW]

Extra data hash to store on the question item's html element

group_maximum_answered[RW]
group_minimum_answered[RW]
hidden[RW]

To hide old questions

input_data[RW]

data-attributes for the input tag.

key[RW]

Standard attributes

labels[RW]
lines[RW]

Amount of rows and cols a textarea has

maximum[RW]
minimum[RW]

Minimum and maximum values for float and integer types

options[RW]

Multiple-choice questions have options to choose from

parent[RW]

Some questions are a tree.

parent_option_key[RW]
question_group[RW]

options for grouping questions and setting a minimum or maximum number of answered questions in the group

questionnaire[RW]
row_span[RW]
sbg_key[RW]
score_header[RW]

Whether we use the :description, the :value or :none for the score header above this question

sets_textvar[RW]

Text variable name that will be replaced with the answer to this question In all following text elements that support markdown

show_values[RW]

Whether we show the value for each option :all => in all questionnaire display modes :none => in none of display modes :paged => for only in :paged display mode :bulk => for only in :bulk display mode

size[RW]

To specify size of string/number input boxes

table[RW]

Table this question might belong to

title[RW]
type[RW]

What kind of question is this?

unit[RW]

To display unit for number items

validations[RW]

Structuring

Public Class Methods

new(key, options = {}) click to toggle source

rubocop:disable CyclomaticComplexity, Metrics/MethodLength

Calls superclass method Quby::Compiler::Entities::Item::new
# File lib/quby/compiler/entities/question.rb, line 126
def initialize(key, options = {})
  super(options)

  @extra_data ||= {}
  @options = []
  @allow_duplicate_option_values = options[:allow_duplicate_option_values]
  @questionnaire = options[:questionnaire]
  @key = key
  @sbg_key = options[:sbg_key]
  @type = options[:type]
  @as = options[:as]
  @title = options[:title]
  @context_free_title = options[:context_free_title]
  @allow_blank_titles = options[:allow_blank_titles]
  @description = options[:description]
  @display_modes = options[:display_modes]
  @presentation = options[:presentation]
  @validations = []
  @parent = options[:parent]
  @hidden = options[:hidden]
  @table = options[:table]
  @parent_option_key = options[:parent_option_key]
  @autocomplete = options[:autocomplete] || "off"
  @show_values = options[:show_values] || :bulk
  @deselectable = (options[:deselectable].nil? || options[:deselectable])
  @disallow_bulk = options[:disallow_bulk]
  @score_header = options[:score_header] || :none
  @sets_textvar = "#{questionnaire.key}_#{options[:sets_textvar]}" if options[:sets_textvar]
  @unit = options[:unit]
  @lines = options[:lines] || 6
  @cols = options[:cols] || 40
  @default_invisible = options[:default_invisible] || false
  @labels ||= []

  @col_span = options[:col_span] || 1
  @row_span = options[:row_span] || 1

  set_depends_on(options[:depends_on])

  @question_group = options[:question_group]
  @group_minimum_answered = options[:group_minimum_answered]
  @group_maximum_answered = options[:group_maximum_answered]

  @input_data = {}
  @input_data[:value_tooltip] = true if options[:value_tooltip]

  # Require subquestions of required questions by default
  options[:required] = true if @parent&.validations&.first&.fetch(:type, nil) == :requires_answer
  @validations << {type: :requires_answer, explanation: options[:error_explanation]} if options[:required]

  if @type == :float
    @validations << {type: :valid_float, explanation: options[:error_explanation]}
  elsif @type == :integer
    @validations << {type: :valid_integer, explanation: options[:error_explanation]}
  end

  if options[:minimum] and (@type == :integer || @type == :float)
    fail "deprecated" # pretty sure this is not used anywhere
  end
  if options[:maximum] and (@type == :integer || @type == :float)
    fail "deprecated" # pretty sure this is not used anywhere
  end
  @default_position = options[:default_position]

  if @question_group
    if @group_minimum_answered
      @validations << {type: :answer_group_minimum, group: @question_group, value: @group_minimum_answered,
                       explanation: options[:error_explanation]}
    end
    if @group_maximum_answered
      @validations << {type: :answer_group_maximum, group: @question_group, value: @group_maximum_answered,
                       explanation: options[:error_explanation]}
    end
  end
end

Public Instance Methods

answer_keys() click to toggle source

Returns all possible answer keys of this question (excluding subquestions, including options). radio/select/scale-options do not create answer_keys, but answer values.

# File lib/quby/compiler/entities/question.rb, line 272
def answer_keys
  [key]
end
as_json(options = {}) click to toggle source
Calls superclass method Quby::Compiler::Entities::Item#as_json
# File lib/quby/compiler/entities/question.rb, line 227
def as_json(options = {})
  # rubocop:disable SymbolName
  super.merge(
    key: key,
    title: Quby::MarkdownParser.new(title).to_html,
    description: Quby::MarkdownParser.new(description).to_html,
    type: type,
    unit: unit,
    size: size,
    hidden: hidden?,
    displayModes: display_modes,
    defaultInvisible: default_invisible,
    viewSelector: view_selector,
    parentKey: parent&.key,
    parentOptionKey: parent_option_key,
    deselectable: deselectable,
    presentation: presentation,
    as: as,
    questionGroup: question_group
  )
end
claimed_keys() click to toggle source

The keys this question claims as his own. Not including options and subquestions. Includes keys for the question, inputs and answers.

# File lib/quby/compiler/entities/question.rb, line 266
def claimed_keys
  [key]
end
codebook_key(key, questionnaire, opts = {}) click to toggle source
# File lib/quby/compiler/entities/question.rb, line 330
def codebook_key(key, questionnaire, opts = {})
  key.to_s.gsub(/^v_/, "#{opts[:roqua_key] || questionnaire.key.to_s}_")
end
codebook_output_range() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 338
def codebook_output_range
  range_min = validations.find { |i| i[:type] == :minimum }&.fetch(:value, nil)
  range_max = validations.find { |i| i[:type] == :maximum }&.fetch(:value, nil)

  if range_min || range_max
    "(#{[range_min, "value", range_max].compact.join(" &lt;= ")})"
  else
    ""
  end
end
codebook_output_type() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 334
def codebook_output_type
  type
end
expand_depends_on_input_keys() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 215
def expand_depends_on_input_keys
  return unless @depends_on
  @depends_on = questionnaire.expand_input_keys(@depends_on)
  @extra_data[:"depends-on"] = @depends_on.to_json
rescue => e
  raise e.class, "Question #{key} depends_on contains an error: #{e.message}"
end
hidden?() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 298
def hidden?
  hidden
end
html_id() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 290
def html_id
  "answer_#{key}_input"
end
input_keys() click to toggle source

Returns all keys belonging to html inputs generated by this question.

# File lib/quby/compiler/entities/question.rb, line 250
def input_keys
  if options.blank?
    answer_keys
  else
    # Some options don't have a key (inner_title), they are stripped
    options.map { |opt| opt.input_key }.compact
  end
end
key_in_use?(k) click to toggle source
# File lib/quby/compiler/entities/question.rb, line 259
def key_in_use?(k)
  claimed_keys.include?(k) ||
  options.any? { |option| option.key_in_use?(k) }
end
set_depends_on(keys) click to toggle source

rubocop:disable AccessorMethodName

# File lib/quby/compiler/entities/question.rb, line 204
def set_depends_on(keys)
  return if keys.blank?
  keys = [keys] unless keys.is_a?(Array)
  @depends_on = keys
end
show_values_in_mode?(mode) click to toggle source
# File lib/quby/compiler/entities/question.rb, line 302
def show_values_in_mode?(mode)
  case show_values
  when :none then false
  when :all then true
  else show_values == mode
  end
end
subquestion?() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 314
def subquestion?
  !parent_option_key.nil?
end
subquestions() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 310
def subquestions
  options.map { |opt| opt.questions }.flatten
end
to_codebook(questionnaire, opts = {}) click to toggle source
# File lib/quby/compiler/entities/question.rb, line 318
def to_codebook(questionnaire, opts = {})
  output = []
  question_key = codebook_key(key, questionnaire, opts)
  output << "#{question_key} #{codebook_output_type} #{codebook_output_range}#{' deprecated' if hidden}"
  output << "\"#{context_free_title}\"" unless context_free_title.blank?
  options_string = options.map do |option|
    option.to_codebook(questionnaire, opts)
  end.compact.join("\n")
  output << options_string unless options.blank?
  output.join("\n")
end
variable_descriptions() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 349
def variable_descriptions
  {key => context_free_title}.with_indifferent_access
end
view_selector() click to toggle source
# File lib/quby/compiler/entities/question.rb, line 294
def view_selector
  table.blank? ? "#item_#{key}" : "[data-for=#{key}], #answer_#{key}_input"
end