Class: Cornucopia::Util::ConfiguredReport

Inherits:
Object
  • Object
show all
Defined in:
lib/cornucopia/util/configured_report.rb

Instance Method Summary collapse

Constructor Details

#initialize(options = {}) ⇒ ConfiguredReport

ConfiguredReport outputs an error report based on symbol based configurations

The configurations are as follows:

min_fields
more_info_fields
expand_fields
expand_inline_fields
exclude_fields
leaf_options

min_field

This is a list of the fields which are to be output at the top of the report
such that they are always visible.
Items in the min list which cannot be found will output an error.

If any options are provided in a field that is a non-expanded leaf node, those options will
be used instead of the leaf_options if any.

more_info_fields

This is a list of the fields which are to be output below the min fields
in a section that is initially hidden.  The user can expand these values
If/when they need to.
Items in the more info list which cannot be found will output an error.

If any options are provided in a field that is a non-expanded leaf node, those options will
be used instead of the leaf_options if any.

expand_fields

This is a list of the fields which are to be expanded when they are encountered.
Expanded fields are shown in a sub-table of values so that the instance variables
are then each output.
items which are to be expanded may be explicitly or implicitly exported.
items which are not encountered but are in the expand list will be ignored.

If any options are provided here, these options will be applied to all expanded items which
are leaf nodes that are not expanded.

expand_inline_fields

This is a list of the fields which are to be expanded, but unlike expanded fields
when these items are expanded, they will be placed at the same level as the current
items rather than in a sub-table.

If any options are provided here, these options will be applied to all expanded items which
are leaf nodes that are not expanded.  If a field is specified in both expand and
expand_inline, the options for expand will take precedence over expand_inline.

exclude_fields

This is a list of the fields which are not to be output when they are encountered.
There are many implicit ways to output a field (such as the expanded fields).
If a field is to be implicityly exported, it will not be exported if it is in this
list.  A field can always be explicitly exported.  Items not encountered but
in the exclude list will be ignored.

If any options are provided here, they will be ignored.

leaf_options

When a leaf node is output, this set is checked to see if there are any options to be passed into the
Cornucopia::Util::ReportTable.write_stats function.
leaf_options is only useful if you provide options.

Unlike the other nodes, leaf_options does not use the field names to specify a full path.
Instead, the leaf options allows an arry of values in the report_element field to specify a list
of leaf fields which all have the same options (if not overridden by the min or more_info lists).

field names follow a set pattern:

<object_name>__<function_property_or_hash_name>

You can have as many following __<function_or_property_name> values as you need.

OR

{ report_element: <field name>, report_options: {<options hash>} }

Examples:

self.exception.backtrace would be specified as: :self__exception__backtrace
self.my_hash[:my_key] would be specified as: :self__my_hash__my_key
self.to_s would be specified as: :self__to_s
self.as_text_area may be specified as: { report_element: :self, report_options: { prevent_shrink: true } }

There are a handful of special conditions:

if the last_line is to_s, the label that is output will not be to_s, but the previous item level

:logs

This will output the logs using Cornucopia::Util::LogCapture.capture_logs
Unlike normal items, if there are no logs to export, this will not generate an error.

:capybara_page_diagnostics

This will output Capybara infomration using
Cornucopia::Capybara::Diagnostics.output_page_detail_section.
NOTE:  This option requres a parameter be passed into the report_options for :diagnostics_name
Unlike normal items, if Capybara is not being used, this will not generate an error.


103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/cornucopia/util/configured_report.rb', line 103

def initialize(options = {})
  @min_fields           = []
  @more_info_fields     = []
  @expand_fields        = []
  @expand_inline_fields = []
  @exclude_fields       = []
  @leaf_options         = []
  @report_objects       = {}

  self.min_fields           = options[:min_fields]
  self.more_info_fields     = options[:more_info_fields]
  self.expand_fields        = options[:expand_fields]
  self.expand_inline_fields = options[:expand_inline_fields]
  self.exclude_fields       = options[:exclude_fields]
  self.leaf_options         = options[:leaf_options]
  @report                   = options[:report]
end

Instance Method Details

#add_report_objects(report_object_hash) ⇒ Object



145
146
147
# File 'lib/cornucopia/util/configured_report.rb', line 145

def add_report_objects(report_object_hash)
  @report_objects.merge! report_object_hash
end

#exclude_fields=(value) ⇒ Object



137
138
139
# File 'lib/cornucopia/util/configured_report.rb', line 137

def exclude_fields=(value)
  @exclude_fields = split_field_symbols(value)
end

#exclude_variable?(export_field, variable_name) ⇒ Boolean

Returns:

  • (Boolean)


460
461
462
# File 'lib/cornucopia/util/configured_report.rb', line 460

def exclude_variable?(export_field, variable_name)
  find_variable_in_set(@exclude_fields, export_field, variable_name)
end

#expand_field_object(export_field, expand_object, symbol_name, report_table, level, options = {}) ⇒ Object



227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
# File 'lib/cornucopia/util/configured_report.rb', line 227

def expand_field_object(export_field, expand_object, symbol_name, report_table, level, options = {})
  expand_inline = options.delete(:expand_inline)

  Cornucopia::Util::ReportTable.new(report_table:         expand_inline ? report_table : nil,
                                    nested_table:         report_table,
                                    nested_table_label:   symbol_name,
                                    suppress_blank_table: true) do |sub_vars_report|
    if expand_object.is_a?(Hash)
      expand_object.each do |sub_symbol_name, value|
        perform_expansion(export_field, level, sub_vars_report, options, sub_symbol_name, value)
      end
    elsif expand_object.respond_to?(:members)
      key_values = expand_object.members

      key_values.each do |sub_symbol_name|
        perform_expansion(export_field,
                          level,
                          sub_vars_report,
                          options,
                          sub_symbol_name,
                          expand_object.send(sub_symbol_name))
      end
    elsif expand_object.respond_to?(:each)
      each_index = 0
      expand_object.each do |value|
        perform_expansion(export_field, level, sub_vars_report, options, each_index.to_s, value)
        each_index += 1
      end
    else
      expand_object.instance_variable_names.sort.each do |variable_name|
        var_symbol_name = variable_name.to_s
        while var_symbol_name[0] == "@"
          var_symbol_name = var_symbol_name[1..-1]
        end

        # if level == sub_export_field[:report_element].length - 1 ||
        #     (level < sub_export_field[:report_element].length &&
        #         sub_export_field[:report_element][level + 1] != var_symbol_name.to_sym)
        #   sub_export_field[:report_options] = nil
        # end
        perform_expansion(export_field,
                          level,
                          sub_vars_report,
                          options,
                          var_symbol_name,
                          get_instance_variable(expand_object, variable_name, var_symbol_name))
      end
    end
  end
end

#expand_fields=(value) ⇒ Object



129
130
131
# File 'lib/cornucopia/util/configured_report.rb', line 129

def expand_fields=(value)
  @expand_fields = split_field_symbols(value)
end

#expand_inline_fields=(value) ⇒ Object



133
134
135
# File 'lib/cornucopia/util/configured_report.rb', line 133

def expand_inline_fields=(value)
  @expand_inline_fields = split_field_symbols(value)
end

#expand_variable?(export_field, variable_name) ⇒ Boolean

Returns:

  • (Boolean)


464
465
466
467
# File 'lib/cornucopia/util/configured_report.rb', line 464

def expand_variable?(export_field, variable_name)
  find_variable_in_set(@expand_fields, export_field, variable_name) ||
      find_variable_in_set(@expand_inline_fields, export_field, variable_name)
end

#expand_variable_inline?(export_field, variable_name) ⇒ Boolean

Returns:

  • (Boolean)


469
470
471
# File 'lib/cornucopia/util/configured_report.rb', line 469

def expand_variable_inline?(export_field, variable_name)
  find_variable_in_set(@expand_inline_fields, export_field, variable_name)
end

#export_field_record(export_field, parent_object, parent_object_name, report_table, level, options = {}) ⇒ Object



307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
# File 'lib/cornucopia/util/configured_report.rb', line 307

def export_field_record(export_field, parent_object, parent_object_name, report_table, level, options = {})
  parent_expanded = options.delete(:expanded_field)
  report_object   = nil
  reported        = false

  if (options.delete(:report_object_set))
    report_object = parent_object
  else
    if parent_object.respond_to?(export_field[:report_element][level]) &&
        (!parent_object.methods.include?(export_field[:report_element][level]) ||
            parent_object.method(export_field[:report_element][level]).parameters.empty? ||
            (parent_object.method(export_field[:report_element][level]).parameters.length == 1 &&
                parent_object.method(export_field[:report_element][level]).parameters[0][0] == :rest))
      report_object = parent_object.send(export_field[:report_element][level])
      reported      = true
    elsif parent_object.respond_to?(:[])
      key_value = export_field[:report_element][level]
      if key_value.to_s =~ /^-?[0-9]+$/
        key_value = key_value.to_s.to_i
      end

      begin
        report_object = parent_object.send(:[], key_value)
        reported      = true
      rescue
      end
    end

    unless reported
      instance_variable_name = instance_variables_contain(parent_object, export_field[:report_element][level])

      if instance_variable_name
        report_object = parent_object.instance_variable_get(instance_variable_name)
      else
        unless report_options(export_field)[:ignore_missing]
          report_object = nil
          print_value   = "Could not identify field: #{export_field[:report_element][0..level].join("__")} while exporting #{export_field[:report_element].join("__")}"

          report_table.write_stats "ERROR", print_value
        end
      end
    end
  end

  if (level == 0 || !report_object.nil?) &&
      (level > 0 || export_field[:report_element][level] == parent_object_name)
    if level < export_field[:report_element].length - 1
      export_field_record(export_field,
                          report_object,
                          export_field[:report_element][level],
                          report_table,
                          level + 1,
                          options)
    else
      case export_field[:report_element][level]
        when :logs
          if Cornucopia::Util::Configuration.grab_logs
            Cornucopia::Util::LogCapture.capture_logs report_table
          end

        when :capybara_page_diagnostics
          Cornucopia::Capybara::PageDiagnostics.dump_details_in_table(@report, report_table)

        else
          suppress = exclude_variable?(export_field[:report_element][0..-2], export_field[:report_element][-1])
          suppress &&= parent_expanded

          if !suppress &&
              expand_variable?(export_field[:report_element][0..-2], export_field[:report_element][-1])
            expand_field_object(export_field,
                                report_object,
                                export_field[:report_element][-1],
                                report_table,
                                level,
                                options.merge({ expand_inline:
                                                    expand_variable_inline?(export_field[:report_element][0..-2],
                                                                            export_field[:report_element][-1]) }))
          else
            print_field_object(export_field, report_object, report_table, parent_expanded, options)
          end
      end
    end
  end
rescue Exception => error
  report_table.write_stats "Configured Report Error", "#{export_field}\nError:\n#{error.to_s}\n#{error.backtrace.join("\n")}"
end

#find_leaf_options(export_field) ⇒ Object



433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
# File 'lib/cornucopia/util/configured_report.rb', line 433

def find_leaf_options(export_field)
  found_options = nil

  if export_field[:report_element][-1] == :to_s
    variable_name = export_field[:report_element][-2]
  else
    variable_name = export_field[:report_element][-1]
  end

  if export_field[:report_element].length >= 2
    if variable_name.to_s =~ /^-?[0-9]+$/
      variable_name = export_field[:report_element][-2]
    end
  end

  if @leaf_options
    @leaf_options.each do |leaf_option|
      if leaf_option[:report_element].include?(variable_name)
        found_options = leaf_option
        break
      end
    end
  end

  found_options
end

#find_variable_in_set(variable_set, export_field, variable_name) ⇒ Object



473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
# File 'lib/cornucopia/util/configured_report.rb', line 473

def find_variable_in_set(variable_set, export_field, variable_name)
  found_item = nil

  if variable_set
    variable_set.any? do |exclusion_item|
      found_item = nil

      if exclusion_item[:report_element].length == export_field.length + 1
        found_item = true

        export_field.each_with_index do |export_name, export_index|
          found_item &&= (exclusion_item[:report_element][export_index] == export_name ||
              exclusion_item[:report_element][export_index] == "*".to_sym)
        end

        found_item &&= (exclusion_item[:report_element][export_field.length] == variable_name ||
            exclusion_item[:report_element][export_field.length] == "*".to_sym)

        if found_item
          found_item = exclusion_item
        else
          found_item = nil
        end
      end

      found_item
    end
  end

  found_item
end

#generate_report(report, options = {}, &block) ⇒ Object

This function generates the report.

Options:

report_table      - If the report is to be run inside an already existing table, the table
                    to output the values into.
nested_table      - If the report is running inside an existing table already, the table
                    it is running in.
                    NOTE:  This value may not be
diagnostics_name  - The text to output when outputing capybara diagnostics if it is not expanded in-line.


158
159
160
161
162
163
164
165
166
167
168
169
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
199
200
201
202
203
204
# File 'lib/cornucopia/util/configured_report.rb', line 158

def generate_report(report, options = {}, &block)
  @report = (report ||= Cornucopia::Util::ReportBuilder.current_report)

  options_report_table = options.delete(:report_table)
  [@min_fields, @more_info_fields].each do |export_field_list|
    if export_field_list
      table_pre      = false
      table_function = :within_table
      if @min_fields != export_field_list && !options_report_table
        options_report_table = nil
        table_pre            = true

        table_function = :within_hidden_table
      end

      report.send(table_function,
                  report_table:       options_report_table,
                  nested_table:       options.delete(:nested_table),
                  nested_table_label: options.delete(:nested_table_label)
      ) do |outer_report_table|
        Cornucopia::Util::ReportTable.new(
            report_table:         table_pre ? nil : outer_report_table,
            nested_table:         outer_report_table,
            suppress_blank_table: table_pre) do |report_table|
          export_field_list.each do |export_field|
            if @report_objects[export_field[:report_element][0]] ||
                export_field[:report_element][0] == :capybara_page_diagnostics ||
                export_field[:report_element][0] == :logs
              export_field_record(export_field,
                                  @report_objects[export_field[:report_element][0]],
                                  export_field[:report_element][0],
                                  report_table,
                                  0,
                                  options.merge(report_object_set: true))
            end
          end

          if block
            if @min_fields != export_field_list
              block.yield report, report_table
            end
          end
        end
      end
    end
  end
end

#get_instance_variable(the_object, instance_variable, variable_name) ⇒ Object



544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
# File 'lib/cornucopia/util/configured_report.rb', line 544

def get_instance_variable(the_object, instance_variable, variable_name)
  fetched = false

  if the_object.respond_to?(variable_name)
    begin
      return_value = the_object.send(variable_name)
      fetched      = true
    rescue
    end
  end

  unless fetched
    return_value = the_object.instance_variable_get(instance_variable)
  end

  return_value
end

#instance_variables_contain(parent_object, variable_name) ⇒ Object



278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
# File 'lib/cornucopia/util/configured_report.rb', line 278

def instance_variables_contain(parent_object, variable_name)
  found_name = nil

  parent_object.instance_variable_names.any? do |instance_variable_name|
    if instance_variable_name.to_sym == variable_name ||
        instance_variable_name.to_sym == "@#{variable_name}".to_sym
      found_name = instance_variable_name
      true
    end
  end

  # For some reason, I've found some instance variables that are set but aren't in instance_variable_names
  unless found_name
    begin
      if parent_object.instance_variable_defined?(variable_name)
        found_name = variable_name.to_s
      end
    rescue
      # the name might be invalid, so if it raises an exception here, ignore it.
    end

    if parent_object.instance_variable_defined?("@#{variable_name}".to_sym)
      found_name = "@#{variable_name}"
    end
  end

  found_name
end

#leaf_options=(value) ⇒ Object



141
142
143
# File 'lib/cornucopia/util/configured_report.rb', line 141

def leaf_options=(value)
  @leaf_options = split_field_symbols(value)
end

#min_fields=(value) ⇒ Object



121
122
123
# File 'lib/cornucopia/util/configured_report.rb', line 121

def min_fields=(value)
  @min_fields = split_field_symbols(value)
end

#more_info_fields=(value) ⇒ Object



125
126
127
# File 'lib/cornucopia/util/configured_report.rb', line 125

def more_info_fields=(value)
  @more_info_fields = split_field_symbols(value)
end

#perform_expansion(export_field, level, sub_vars_report, options, sub_symbol_name, value) ⇒ Object



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/cornucopia/util/configured_report.rb', line 206

def perform_expansion(export_field, level, sub_vars_report, options, sub_symbol_name, value)
  sub_export_field = { report_element: export_field[:report_element].clone }
  if level == sub_export_field[:report_element].length - 1
    sub_export_field[:report_options] = export_field[:report_options]
  end

  if level == sub_export_field[:report_element].length - 1 ||
      (level < sub_export_field[:report_element].length &&
          sub_export_field[:report_element][level + 1] != sub_symbol_name.to_sym)
    sub_export_field[:report_element] = sub_export_field[:report_element][0..level]
    sub_export_field[:report_element] << sub_symbol_name.to_sym
  end

  export_field_record(sub_export_field,
                      value,
                      sub_symbol_name.to_sym,
                      sub_vars_report,
                      level + 1,
                      options.merge(report_object_set: true, expanded_field: true))
end


394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/cornucopia/util/configured_report.rb', line 394

def print_field_object(export_field, report_object, report_table, parent_expanded, options)
  unless parent_expanded &&
      exclude_variable?(export_field[:report_element][0..-2], export_field[:report_element][-1])
    if report_object == false ||
        !(report_object.nil? || (report_object.respond_to?(:empty?) && (report_object.empty? rescue nil)))
      if export_field[:report_element][-1] == :to_s
        print_name = export_field[:report_element][-2]
      else
        print_name = export_field[:report_element][-1]
      end

      if export_field[:report_element].length >= 2
        if print_name.to_s =~ /^-?[0-9]+$/
          if !parent_expanded ||
              expand_variable_inline?(export_field[:report_element][0..-3], export_field[:report_element][-2])
            print_name = "#{export_field[:report_element][-2]}[#{export_field[:report_element][-1]}]"
          end
        end
      end

      print_options = report_options(export_field)

      if print_options[:label]
        print_name = print_options[:label]
      end

      report_table.write_stats print_name, report_object, print_options
    end
  end
end

#report_options(export_field) ⇒ Object



425
426
427
428
429
430
431
# File 'lib/cornucopia/util/configured_report.rb', line 425

def report_options(export_field)
  print_options = export_field[:report_options]
  print_options ||= find_leaf_options(export_field).try(:[], :report_options)
  print_options ||= {}

  print_options
end

#split_field_symbols(full_report_element) ⇒ Object



505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
# File 'lib/cornucopia/util/configured_report.rb', line 505

def split_field_symbols(full_report_element)
  if full_report_element
    full_report_element.map do |full_symbol|
      return_value = {}

      if full_symbol.is_a?(Hash)
        return_value                  = full_symbol.clone
        return_value[:report_element] = split_full_field_symbol(return_value[:report_element])
      else
        return_value[:report_element] = split_full_field_symbol(full_symbol)
      end

      return_value
    end
  else
    []
  end
end

#split_full_field_symbol(full_symbol) ⇒ Object



524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
# File 'lib/cornucopia/util/configured_report.rb', line 524

def split_full_field_symbol(full_symbol)
  if full_symbol.is_a?(Array)
    full_symbol
  else
    field_symbols = full_symbol.to_s.split("__")

    field_symbols.reduce([]) do |array, symbol|
      if (symbol.empty?)
        array << nil
      else
        while (array.length > 0 && array[-1].blank?)
          array.pop
          symbol = "__#{symbol}"
        end
        array << symbol.to_sym
      end
    end
  end
end