Class: Quby::Questionnaires::Services::DefinitionValidator

Inherits:
ActiveModel::Validator
  • Object
show all
Defined in:
lib/quby/questionnaires/services/definition_validator.rb

Constant Summary collapse

MAX_KEY_LENGTH =
19
KEY_PREFIX =
'v_'

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#definitionObject (readonly)

Returns the value of attribute definition.



13
14
15
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 13

def definition
  @definition
end

#questionnaireObject (readonly)

Returns the value of attribute questionnaire.



14
15
16
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 14

def questionnaire
  @questionnaire
end

Instance Method Details

#subquestions_cant_have_default_invisible(question) ⇒ Object



170
171
172
173
174
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 170

def subquestions_cant_have_default_invisible(question)
  if question.subquestion? && question.default_invisible
    fail "Question #{question.key} is a subquestion with default_invisible."
  end
end

#to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:) ⇒ Boolean

Returns:

  • (Boolean)


152
153
154
155
156
157
158
159
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 152

def to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:)
  return if option.hides_questions.blank?
  msg_base += " hides_questions"
  option.hides_questions.each do |key|
    validate_question_key_exists?(questionnaire, key, msg_base: msg_base)
    validate_not_subquestion(questionnaire, key, msg_base: msg_base)
  end
end

#to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:) ⇒ Boolean

Returns:

  • (Boolean)


161
162
163
164
165
166
167
168
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 161

def to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:)
  return if option.shows_questions.blank?
  msg_base += " shows_questions"
  option.shows_questions.each do |key|
    validate_question_key_exists?(questionnaire, key, msg_base: msg_base)
    validate_not_subquestion(questionnaire, key, msg_base: msg_base)
  end
end

#validate(definition) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 16

def validate(definition)
  questionnaire = DSL.build_from_definition(definition)
  validate_fields(questionnaire)
  validate_title(questionnaire)
  validate_questions(questionnaire)
  validate_scores(questionnaire)
  validate_table_edgecases(questionnaire)
  validate_flags(questionnaire)
  validate_respondent_types(questionnaire)
  validate_outcome_tables(questionnaire)
  validate_markdown_fields(questionnaire) if questionnaire.validate_html
  validate_raw_content_items(questionnaire) if questionnaire.validate_html
# Some compilation errors are Exceptions (pure syntax errors) and some StandardErrors (NameErrors)
rescue Exception => exception # rubocop:disable Lint/RescueException
  definition.errors.add(:sourcecode, {message: "Questionnaire error: #{definition.key}\n" \
                                               "#{exception.message}",
                                      backtrace: exception.backtrace[0..5].join("<br/>")})
end

#validate_fields(questionnaire) ⇒ Object



35
36
37
38
39
40
41
42
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 35

def validate_fields(questionnaire)
  questionnaire.fields.input_keys
                      .find { |k| !k.is_a?(Symbol) }
                     &.tap { |k| fail "Input key #{k} is not a symbol" }
  questionnaire.fields.answer_keys
                      .find { |k| !k.is_a?(Symbol) }
                     &.tap { |k| fail "Answer key #{k} is not a symbol" }
end

#validate_flag_depends_on(questionnaire, flag) ⇒ Object



128
129
130
131
132
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 128

def validate_flag_depends_on(questionnaire, flag)
  return if flag.depends_on.blank? || questionnaire.flags.key?(flag.depends_on)

  fail ArgumentError, "Flag #{flag.key} depends_on nonexistent flag '#{flag.depends_on}'"
end

#validate_flag_hides(questionnaire, flag) ⇒ Object



121
122
123
124
125
126
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 121

def validate_flag_hides(questionnaire, flag)
  unknown_questions = flag.hides_questions.select { |key| !questionnaire.key_in_use?(key) }
  return if unknown_questions.blank?

  fail ArgumentError, "Flag '#{key}' has unknown hides_questions keys #{unknown_questions}"
end

#validate_flag_shows(questionnaire, flag) ⇒ Object



114
115
116
117
118
119
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 114

def validate_flag_shows(questionnaire, flag)
  unknown_questions = flag.shows_questions.select { |key| !questionnaire.key_in_use?(key) }
  return if unknown_questions.blank?

  fail ArgumentError, "Flag '#{key}' has unknown shows_questions keys #{unknown_questions}"
end

#validate_flags(questionnaire) ⇒ Object



106
107
108
109
110
111
112
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 106

def validate_flags(questionnaire)
  questionnaire.flags.each_value do |flag|
    validate_flag_shows(questionnaire, flag)
    validate_flag_hides(questionnaire, flag)
    validate_flag_depends_on(questionnaire, flag)
  end
end

#validate_outcome_tables(questionnaire) ⇒ Object



145
146
147
148
149
150
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 145

def validate_outcome_tables(questionnaire)
  questionnaire.outcome_tables.each do |table|
    next if table.valid?
    fail "Outcome table #{table.errors.full_messages}"
  end
end

#validate_presence_of_titles(question) ⇒ Object



89
90
91
92
93
94
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 89

def validate_presence_of_titles(question)
  return if question.allow_blank_titles
  if !question.subquestion? && question.title.blank? && question.context_free_title.blank?
    fail "Question #{question.key} must define either `:title` or `:context_free_title`."
  end
end

#validate_question(question) ⇒ Object



68
69
70
71
72
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 68

def validate_question(question)
  unless question.valid?
    fail "Question #{question.key} is invalid: #{question.errors.full_messages.join(', ')}"
  end
end

#validate_question_options(questionnaire, question) ⇒ Object



81
82
83
84
85
86
87
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 81

def validate_question_options(questionnaire, question)
  question.options.each do |option|
    msg_base = "Question #{option.question.key} option #{option.key}"
    to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
    to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base: msg_base)
  end
end

#validate_questions(questionnaire) ⇒ Object



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 50

def validate_questions(questionnaire)
  questionnaire.answer_keys.each do |key|
    validate_key_format(key)
  end

  questionnaire.question_hash.each_value do |question|
    validate_question(question)
    subquestions_cant_have_default_invisible question
    validate_subquestion_absence_in_select question
    validate_placeholder_options_nil_values question
    validate_values_unique question

    validate_question_options(questionnaire, question)
    validate_presence_of_titles question
    validate_no_spaces_before_question_nr_in_title question
  end
end

#validate_respondent_types(questionnaire) ⇒ Object



134
135
136
137
138
139
140
141
142
143
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 134

def validate_respondent_types(questionnaire)
  valid_respondent_types = Entities::Questionnaire::RESPONDENT_TYPES

  invalid_types = questionnaire.respondent_types - valid_respondent_types

  if invalid_types.present?
    fail "Invalid respondent types: :#{invalid_types.join(', :')}\n"\
         "Choose one or more from: :#{valid_respondent_types.join(', :')}"
  end
end

#validate_scores(questionnaire) ⇒ Object



74
75
76
77
78
79
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 74

def validate_scores(questionnaire)
  questionnaire.scores.each do |score|
    validate_score_key_length(score)
    validate_score_label_present(score)
  end
end

#validate_table_edgecases(questionnaire) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 96

def validate_table_edgecases(questionnaire)
  questionnaire.panels.each do |panel|
    tables = panel.items.select { |item| item.is_a?(Entities::Table) }
    tables.each do |table|
      questions = table.items.select { |item| item.is_a?(Entities::Question) }
      questions.each { |question| validate_table_question(question) }
    end
  end
end

#validate_title(questionnaire) ⇒ Object



44
45
46
47
48
# File 'lib/quby/questionnaires/services/definition_validator.rb', line 44

def validate_title(questionnaire)
  if questionnaire.title.blank?
    fail "Questionnaire title is missing."
  end
end