Class: Quby::Compiler::Services::DefinitionValidator
- Inherits:
-
ActiveModel::Validator
- Object
- ActiveModel::Validator
- Quby::Compiler::Services::DefinitionValidator
- Defined in:
- lib/quby/compiler/services/definition_validator.rb
Constant Summary collapse
- MAX_KEY_LENGTH =
19
- KEY_PREFIX =
'v_'
Instance Attribute Summary collapse
-
#definition ⇒ Object
readonly
Returns the value of attribute definition.
-
#questionnaire ⇒ Object
readonly
Returns the value of attribute questionnaire.
Class Method Summary collapse
Instance Method Summary collapse
- #ensure_valid_descriptions(questionnaire, flag) ⇒ Object
- #subquestions_cant_have_default_invisible(question) ⇒ Object
- #to_be_hidden_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:) ⇒ Boolean
- #to_be_shown_questions_exist_and_not_subquestion?(questionnaire, option, msg_base:) ⇒ Boolean
- #validate(definition) ⇒ Object
- #validate_fields(questionnaire) ⇒ Object
- #validate_flag_depends_on(questionnaire, flag) ⇒ Object
- #validate_flag_hides(questionnaire, flag) ⇒ Object
- #validate_flag_shows(questionnaire, flag) ⇒ Object
- #validate_flags(questionnaire) ⇒ Object
- #validate_key_uniqueness(questionnaire) ⇒ Object
- #validate_metadata(questionnaire) ⇒ Object
- #validate_outcome_tables(questionnaire) ⇒ Object
- #validate_presence_of_titles(question) ⇒ Object
- #validate_question(question) ⇒ Object
- #validate_question_options(questionnaire, question) ⇒ Object
- #validate_questions(questionnaire) ⇒ Object
- #validate_respondent_types(questionnaire) ⇒ Object
- #validate_scores(questionnaire) ⇒ Object
- #validate_table_edgecases(questionnaire) ⇒ Object
Instance Attribute Details
#definition ⇒ Object (readonly)
Returns the value of attribute definition.
13 14 15 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 13 def definition @definition end |
#questionnaire ⇒ Object (readonly)
Returns the value of attribute questionnaire.
14 15 16 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 14 def questionnaire @questionnaire end |
Class Method Details
.check_duplicate_headers(seed) ⇒ Object
234 235 236 237 238 239 240 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 234 def self.check_duplicate_headers(seed) return # TODO # column_headers = DataExport::QuestionnaireHeaders.new(questionnaire).headers # duplicate_header_names = column_headers.find_all { |e| column_headers.rindex(e) != column_headers.index(e) }.uniq # raise "key clashes for: #{duplicate_header_names}" if duplicate_header_names.present? end |
.check_score_keys_consistency(seed) ⇒ Object
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 218 def self.check_score_keys_consistency(seed) score_keys = seed["properties"][:score_keys] most_keys = score_keys.map { |score| keys_for_score(score).join }.max_by(&:length) faulty_scores = score_keys.reject do |score| most_keys.starts_with? keys_for_score(score).join end if faulty_scores.present? raise "scores mismatch other scores, check if this was intentional: #{faulty_scores} If this was intentional, rerun quby proxy with the flag '--skip_score_keys_consistency_check' *and* manually add \ scores_schema tables to the resulting seed." end end |
Instance Method Details
#ensure_valid_descriptions(questionnaire, flag) ⇒ Object
138 139 140 141 142 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 138 def ensure_valid_descriptions(questionnaire, flag) unless (flag.description_false.present? && flag.description_true.present?) || flag.description.present? fail ArgumentError, "Flag '#{flag.key}' Requires at least either both description_true and description_false or a description" end end |
#subquestions_cant_have_default_invisible(question) ⇒ Object
212 213 214 215 216 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 212 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
194 195 196 197 198 199 200 201 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 194 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
203 204 205 206 207 208 209 210 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 203 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 34 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 16 def validate(definition) questionnaire = DSL.build_from_definition(definition) (questionnaire) validate_fields(questionnaire) validate_questions(questionnaire) validate_scores(questionnaire) validate_table_edgecases(questionnaire) validate_flags(questionnaire) validate_key_uniqueness(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.}", backtrace: exception.backtrace[0..20]) end |
#validate_fields(questionnaire) ⇒ Object
50 51 52 53 54 55 56 57 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 50 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
158 159 160 161 162 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 158 def validate_flag_depends_on(questionnaire, flag) return if (missing = flag.depends_on - questionnaire.flags.keys).blank? fail ArgumentError, "Flag #{flag.key} depends_on nonexistent flag '#{missing.to_sentence}'" end |
#validate_flag_hides(questionnaire, flag) ⇒ Object
151 152 153 154 155 156 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 151 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 '#{flag.key}' has unknown hides_questions keys #{unknown_questions}" end |
#validate_flag_shows(questionnaire, flag) ⇒ Object
144 145 146 147 148 149 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 144 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 '#{flag.key}' has unknown shows_questions keys #{unknown_questions}" end |
#validate_flags(questionnaire) ⇒ Object
129 130 131 132 133 134 135 136 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 129 def validate_flags(questionnaire) questionnaire.flags.each_value do |flag| ensure_valid_descriptions(questionnaire, flag) validate_flag_shows(questionnaire, flag) validate_flag_hides(questionnaire, flag) validate_flag_depends_on(questionnaire, flag) end end |
#validate_key_uniqueness(questionnaire) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 164 def validate_key_uniqueness(questionnaire) keys = questionnaire.fields.answer_keys.to_a \ + questionnaire.score_schemas.keys.map(&:to_sym) \ + questionnaire.flags.keys.map { delete_prefix(_1, questionnaire).to_sym } \ + questionnaire.textvars.keys.map { delete_prefix(_1, questionnaire).to_sym } return if keys.size == keys.uniq.size duplicates = keys.tally.filter_map { |k,v| k if v > 1 } fail ArgumentError, "Duplicate keys: #{duplicates.to_sentence}" end |
#validate_metadata(questionnaire) ⇒ Object
36 37 38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 36 def (questionnaire) if questionnaire.title.blank? fail "Questionnaire title is missing." end if questionnaire.short_description && questionnaire.short_description.size > 255 fail "Questionnaire short_description is too long." end if questionnaire.description && questionnaire.description.size > 255 fail "Questionnaire description is too long." end end |
#validate_outcome_tables(questionnaire) ⇒ Object
187 188 189 190 191 192 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 187 def validate_outcome_tables(questionnaire) questionnaire.outcome_tables.each do |table| next if table.valid? fail "Outcome table #{table.errors.}" end end |
#validate_presence_of_titles(question) ⇒ Object
112 113 114 115 116 117 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 112 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
77 78 79 80 81 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 77 def validate_question(question) unless question.valid? fail "Question #{question.key} is invalid: #{question.errors..join(', ')}" end end |
#validate_question_options(questionnaire, question) ⇒ Object
101 102 103 104 105 106 107 108 109 110 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 101 def (questionnaire, question) question..each do |option| msg_base = "Question #{option.question.key} option #{option.key}" unless option.valid? fail "#{msg_base} #{option.errors..to_sentence}" end 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
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 59 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 question validate_values_unique question (questionnaire, question) validate_presence_of_titles question validate_no_spaces_before_question_nr_in_title question end end |
#validate_respondent_types(questionnaire) ⇒ Object
176 177 178 179 180 181 182 183 184 185 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 176 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
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 83 def validate_scores(questionnaire) questionnaire.scores.each do |score| validate_score_key_length(score) validate_score_label_present(score) score_schema = questionnaire.score_schemas[score.key] fail "Score #{score.key} does not have a score schema" unless score_schema fail "Score label langer dan 100 tekens (geeft problemen oru accare)\n #{score_schema.label}" if score_schema.label&.length > 100 end export_keys = questionnaire.score_schemas.flat_map { |_key, score_schema| score_schema.subscore_schemas.map(&:export_key) } duplicate_export_keys = export_keys.tally.select { |key, count| count > 1 }.keys fail "Score export keys not unique, duplicates: #{duplicate_export_keys}" if duplicate_export_keys.present? end |
#validate_table_edgecases(questionnaire) ⇒ Object
119 120 121 122 123 124 125 126 127 |
# File 'lib/quby/compiler/services/definition_validator.rb', line 119 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 |