• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

MushroomObserver / mushroom-observer / 13133059471

04 Feb 2025 10:09AM UTC coverage: 93.381% (+0.06%) from 93.318%
13133059471

Pull #2696

github

nimmolo
remove unused methods
Pull Request #2696: Update logic in `Query::Validation`

83 of 85 new or added lines in 10 files covered. (97.65%)

7 existing lines in 4 files now uncovered.

27354 of 29293 relevant lines covered (93.38%)

567.66 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.14
/app/classes/query/modules/validation.rb
1
# frozen_string_literal: true
2

3
# validation of Query parameters
4
module Query::Modules::Validation
1✔
5
  attr_accessor :params, :params_cache
1✔
6

7
  def validate_params
1✔
8
    old_params = @params.dup.compact.symbolize_keys
2,922✔
9
    new_params = {}
2,922✔
10
    permitted_params = parameter_declarations.slice(*old_params.keys)
2,922✔
11
    permitted_params.each do |param, param_type|
2,922✔
12
      val = old_params[param]
5,015✔
13
      val = validate_value(param_type, param, val) if val.present?
5,015✔
14
      new_params[param] = val
4,995✔
15
    end
16
    check_for_unexpected_params(old_params)
2,902✔
17
    @params = new_params
2,901✔
18
  end
19

20
  def check_for_unexpected_params(old_params)
1✔
21
    unexpected_params = old_params.except(*parameter_declarations.keys)
2,902✔
22
    return if unexpected_params.keys.empty?
2,902✔
23

24
    str = unexpected_params.keys.map(&:to_s).join("', '")
1✔
25
    raise("Unexpected parameter(s) '#{str}' for #{model} query.")
1✔
26
  end
27

28
  def validate_value(param_type, param, val)
1✔
29
    if param_type.is_a?(Array)
4,907✔
30
      array_validate(param, val, param_type.first).flatten
1,543✔
31
    else
32
      # scalar_validate with ambiguous lookup could return an array
33
      val = scalar_validate(param, val, param_type)
3,364✔
34
      val = val.first if val.is_a?(Array)
3,349✔
35
      val
3,349✔
36
    end
37
  end
38

39
  def array_validate(param, val, param_type)
1✔
40
    case val
1,543✔
41
    when Array
42
      # Lookup in scalar_validate could return multiple matches per val
43
      # so the returned array could contain nested arrays.
44
      val[0, MO.query_max_array].map do |val2|
464✔
45
        scalar_validate(param, val2, param_type)
799✔
46
      end
47
    when ::API2::OrderedRange
48
      [scalar_validate(param, val.begin, param_type),
39✔
49
       scalar_validate(param, val.end, param_type)]
50
    else
51
      [scalar_validate(param, val, param_type)]
1,040✔
52
    end
53
  end
54

55
  def scalar_validate(param, val, param_type)
1✔
56
    case param_type
5,452✔
57
    when Symbol
58
      send(:"validate_#{param_type}", param, val)
3,435✔
59
    when Class
60
      validate_class_param(param, val, param_type)
1,848✔
61
    when Hash
62
      validate_hash_param(param, val, param_type)
169✔
63
    else
64
      raise("Invalid declaration of :#{param} for #{model} " \
×
65
            "query! (invalid type: #{param_type.class.name})")
66
    end
67
  end
68

69
  def validate_class_param(param, val, param_type)
1✔
70
    # :names may come with modifier "flag" params that indicate synonyms, etc.
71
    # Immediately look those up and add any new ids to the :names array.
72
    if param == :names
1,848✔
73
      validate_names_record(param, val, param_type)
627✔
74
    elsif param_type.respond_to?(:descends_from_active_record?)
1,221✔
75
      validate_record(param, val, param_type)
1,221✔
76
    else
NEW
77
      raise(
×
78
        "Don't know how to parse #{param_type} :#{param} for #{model} query."
79
      )
80
    end
81
  end
82

83
  def validate_hash_param(param, val, param_type)
1✔
84
    if [:string, :boolean].include?(param_type.keys.first)
169✔
85
      validate_enum(param, val, param_type)
165✔
86
    else
87
      validate_nested_params(param, val, param_type)
4✔
88
    end
89
  end
90

91
  def validate_nested_params(_param, val, hash)
1✔
92
    val2 = {}
4✔
93
    hash.each do |key, arg_type|
4✔
94
      val2[key] = scalar_validate(key, val[key], arg_type)
7✔
95
    end
96
    val2
1✔
97
  end
98

99
  def validate_enum(param, val, hash)
1✔
100
    if hash.keys.length != 1
165✔
101
      raise(
×
102
        "Invalid enum declaration for :#{param} for #{model} " \
103
        "query! (wrong number of keys in hash)"
104
      )
105
    end
106

107
    arg_type = hash.keys.first
165✔
108
    set = hash.values.first
165✔
109
    unless set.is_a?(Array)
165✔
110
      raise(
×
111
        "Invalid enum declaration for :#{param} for #{model} " \
112
        "query! (expected value to be an array of allowed values)"
113
      )
114
    end
115

116
    val2 = scalar_validate(param, val, arg_type)
165✔
117
    if (arg_type == :string) && set.include?(val2.to_sym)
164✔
118
      val2 = val2.to_sym
89✔
119
    elsif set.exclude?(val2)
75✔
120
      raise("Value for :#{param} should be one of the following: " \
2✔
121
            "#{set.inspect}.")
122
    end
123
    val2
162✔
124
  end
125

126
  def validate_boolean(param, val)
1✔
127
    case val
1,090✔
128
    # Disable cop because we do mean to symbols with boolean names
129
    # rubocop:disable Lint/BooleanSymbol
130
    when :true, :yes, :on, "true", "yes", "on", "1", 1, true
131
      true
1,086✔
132
    when :false, :no, :off, "false", "no", "off", "0", 0, false, nil
133
      false
4✔
134
    # rubocop:enable Lint/BooleanSymbol
135
    else
136
      raise("Value for :#{param} should be boolean, got: #{val.inspect}")
×
137
    end
138
  end
139

140
  # def validate_integer(param, val)
141
  #   if val.is_a?(Integer) || val.is_a?(String) && val.match(/^-?\d+$/)
142
  #     val.to_i
143
  #   elsif val.blank?
144
  #     nil
145
  #   else
146
  #     raise("Value for :#{param} should be an integer, got: #{val.inspect}")
147
  #   end
148
  # end
149

150
  def validate_float(param, val)
1✔
151
    if val.is_a?(Integer) || val.is_a?(Float) ||
86✔
152
       (val.is_a?(String) && val.match(/^-?(\d+(\.\d+)?|\.\d+)$/))
7✔
153
      val.to_f
84✔
154
    else
155
      raise("Value for :#{param} should be a float, got: #{val.inspect}")
2✔
156
    end
157
  end
158

159
  def validate_record(param, val, type = ActiveRecord::Base)
1✔
160
    if val.is_a?(type)
1,370✔
161
      raise("Value for :#{param} is an unsaved #{type} instance.") unless val.id
335✔
162

163
      set_cached_parameter_instance(param, val)
335✔
164
      val.id
335✔
165
    elsif could_be_record_id?(param, val)
1,035✔
166
      val.to_i
977✔
167
    elsif val.is_a?(String) && param != :ids
58✔
168
      # Lookups for each val may return more than one record, though the lookup
169
      # string is generally unique. For an example, search `two_agaricus_bug`.
170
      lookup_records_by_name(param, val, type, method: :ids, all: true)
51✔
171
    else
172
      raise("Value for :#{param} should be id, string " \
7✔
173
            "or #{type} instance, got: #{val.inspect}")
174
    end
175
  end
176

177
  def validate_string(param, val)
1✔
178
    if val.is_any?(Integer, Float, String, Symbol)
2,055✔
179
      val.to_s
2,047✔
180
    else
181
      raise("Value for :#{param} should be a string or symbol, " \
8✔
182
            "got a #{val.class}: #{val.inspect}")
183
    end
184
  end
185

186
  def validate_date(param, val)
1✔
187
    if val.acts_like?(:date)
55✔
188
      format("%04d-%02d-%02d", val.year, val.mon, val.day)
19✔
189
    elsif /^\d\d\d\d(-\d\d?){0,2}$/i.match?(val.to_s) ||
36✔
190
          /^\d\d?(-\d\d?)?$/i.match?(val.to_s)
191
      val
36✔
192
    elsif val.blank? || val.to_s == "0"
×
193
      nil
194
    else
195
      raise("Value for :#{param} should be a date (YYYY-MM-DD or MM-DD), " \
×
196
            "got: #{val.inspect}")
197
    end
198
  end
199

200
  def validate_time(param, val)
1✔
201
    if val.acts_like?(:time)
104✔
202
      val = val.utc
62✔
203
      format("%04d-%02d-%02d-%02d-%02d-%02d",
62✔
204
             val.year, val.mon, val.day, val.hour, val.min, val.sec)
205
    elsif /^\d\d\d\d(-\d\d?){0,5}$/i.match?(val.to_s)
42✔
206
      val
42✔
207
    elsif val.blank? || val.to_s == "0"
×
208
      nil
209
    else
210
      raise(
×
211
        "Value for :#{param} should be a UTC time (YYYY-MM-DD-HH-MM-SS), " \
212
        "got: #{val.class.name}::#{val.inspect}"
213
      )
214
    end
215
  end
216

217
  def validate_query(param, val)
1✔
218
    case val
45✔
219
    when Query::Base
220
      val.record.id
9✔
221
    when Integer
222
      val
36✔
223
    else
224
      raise(
×
225
        "Value for :#{param} should be a Query class, got: #{val.inspect}"
226
      )
227
    end
228
  end
229

230
  def find_cached_parameter_instance(model, param)
1✔
231
    val = if could_be_record_id?(param, params[param])
279✔
232
            model.find(params[param])
279✔
233
          else
NEW
234
            lookup_records_by_name(param, params[param], model)
×
235
          end
236
    set_cached_parameter_instance(param, val)
279✔
237
  end
238

239
  # Cache the instance for later use, in case we both instantiate and
240
  # execute query in the same action.
241
  def set_cached_parameter_instance(param, val)
1✔
242
    @params_cache ||= {}
614✔
243
    @params_cache[param] = val
614✔
244
  end
245

246
  def could_be_record_id?(param, val)
1✔
247
    val.is_a?(Integer) ||
1,314✔
248
      val.is_a?(String) && val.match(/^[1-9]\d*$/) ||
249
      # (blasted admin user has id = 0!)
250
      val.is_a?(String) && (val == "0") && (param == :user)
54✔
251
  end
252

253
  def validate_names_record(param, val, type)
1✔
254
    names_params = *names_parameter_declarations.except(:names).keys
627✔
255
    lookup_params = @params.slice(*names_params).compact
627✔
256
    if lookup_params.blank?
627✔
257
      validate_record(param, val, type)
149✔
258
    else
259
      lookup_records_by_name(param, val, type,
478✔
260
                             lookup_params:, method: :ids, all: true)
261
    end
262
  end
263

264
  def lookup_records_by_name(param, val, type, **args)
1✔
265
    lookup_params = args[:lookup_params] || {}
529✔
266
    method = args[:method] || :instances
529✔
267
    all = args[:all] || false
529✔
268
    lookup = lookup_class(param, val, type)
529✔
269

270
    results = lookup.new(val, lookup_params).send(method)
529✔
271
    raise("Couldn't find an id for : #{val.inspect}") unless results
529✔
272

273
    if !all || results.size == 1
529✔
274
      results.first
187✔
275
    else
276
      results
342✔
277
    end
278
  end
279

280
  def lookup_class(param, val, type)
1✔
281
    # We're only validating the projects passed as the param.
282
    # Projects' species_lists will be looked up later.
283
    lookup = if param == :project_lists
529✔
284
               Lookup::Projects
2✔
285
             else
286
               "Lookup::#{type.name.pluralize}".constantize
527✔
287
             end
288
    raise("#{lookup} not defined for : #{val.inspect}") unless defined?(lookup)
529✔
289

290
    lookup
529✔
291
  end
292
end
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc