asker-tool 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +674 -0
  3. data/README.md +53 -0
  4. data/bin/asker +4 -0
  5. data/docs/changelog/v2.1.md +99 -0
  6. data/docs/commands.md +15 -0
  7. data/docs/contributions.md +18 -0
  8. data/docs/history.md +40 -0
  9. data/docs/idea.md +44 -0
  10. data/docs/inputs/README.md +39 -0
  11. data/docs/inputs/code.md +69 -0
  12. data/docs/inputs/concepts.md +142 -0
  13. data/docs/inputs/jedi.md +68 -0
  14. data/docs/inputs/tables.md +112 -0
  15. data/docs/inputs/templates.md +87 -0
  16. data/docs/install/README.md +38 -0
  17. data/docs/install/manual.md +26 -0
  18. data/docs/install/scripts.md +26 -0
  19. data/docs/revise/asker-file.md +41 -0
  20. data/docs/revise/buenas-practicas/01-convocatoria.md +30 -0
  21. data/docs/revise/buenas-practicas/02-formulario.md +35 -0
  22. data/docs/revise/buenas-practicas/03-descripcion.md +63 -0
  23. data/docs/revise/buenas-practicas/04-resultados.md +17 -0
  24. data/docs/revise/buenas-practicas/05-reproducir.md +10 -0
  25. data/docs/revise/ejemplos/01/README.md +27 -0
  26. data/docs/revise/ejemplos/02/README.md +31 -0
  27. data/docs/revise/ejemplos/03/README.md +31 -0
  28. data/docs/revise/ejemplos/04/README.md +37 -0
  29. data/docs/revise/ejemplos/05/README.md +25 -0
  30. data/docs/revise/ejemplos/06/README.md +43 -0
  31. data/docs/revise/ejemplos/README.md +11 -0
  32. data/docs/revise/projects.md +74 -0
  33. data/lib/asker.rb +103 -0
  34. data/lib/asker/ai/ai.rb +70 -0
  35. data/lib/asker/ai/ai_calculate.rb +55 -0
  36. data/lib/asker/ai/concept_ai.rb +49 -0
  37. data/lib/asker/ai/question.rb +58 -0
  38. data/lib/asker/ai/stages/base_stage.rb +16 -0
  39. data/lib/asker/ai/stages/main.rb +8 -0
  40. data/lib/asker/ai/stages/stage_b.rb +87 -0
  41. data/lib/asker/ai/stages/stage_d.rb +160 -0
  42. data/lib/asker/ai/stages/stage_f.rb +156 -0
  43. data/lib/asker/ai/stages/stage_i.rb +140 -0
  44. data/lib/asker/ai/stages/stage_s.rb +52 -0
  45. data/lib/asker/ai/stages/stage_t.rb +170 -0
  46. data/lib/asker/application.rb +30 -0
  47. data/lib/asker/checker.rb +356 -0
  48. data/lib/asker/cli.rb +85 -0
  49. data/lib/asker/code/ai/base_code_ai.rb +48 -0
  50. data/lib/asker/code/ai/code_ai_factory.rb +26 -0
  51. data/lib/asker/code/ai/javascript_code_ai.rb +167 -0
  52. data/lib/asker/code/ai/python_code_ai.rb +167 -0
  53. data/lib/asker/code/ai/ruby_code_ai.rb +169 -0
  54. data/lib/asker/code/ai/sql_code_ai.rb +69 -0
  55. data/lib/asker/code/code.rb +53 -0
  56. data/lib/asker/data/column.rb +62 -0
  57. data/lib/asker/data/concept.rb +183 -0
  58. data/lib/asker/data/data_field.rb +87 -0
  59. data/lib/asker/data/row.rb +93 -0
  60. data/lib/asker/data/table.rb +96 -0
  61. data/lib/asker/data/template.rb +65 -0
  62. data/lib/asker/data/world.rb +53 -0
  63. data/lib/asker/exporter/code_gift_exporter.rb +35 -0
  64. data/lib/asker/exporter/code_screen_exporter.rb +45 -0
  65. data/lib/asker/exporter/concept_ai_gift_exporter.rb +33 -0
  66. data/lib/asker/exporter/concept_ai_screen_exporter.rb +115 -0
  67. data/lib/asker/exporter/concept_ai_yaml_exporter.rb +33 -0
  68. data/lib/asker/exporter/concept_doc_exporter.rb +21 -0
  69. data/lib/asker/exporter/concept_screen_exporter.rb +25 -0
  70. data/lib/asker/exporter/main.rb +9 -0
  71. data/lib/asker/files/config.ini +40 -0
  72. data/lib/asker/formatter/code_string_formatter.rb +16 -0
  73. data/lib/asker/formatter/concept_doc_formatter.rb +37 -0
  74. data/lib/asker/formatter/concept_string_formatter.rb +66 -0
  75. data/lib/asker/formatter/question_gift_formatter.rb +65 -0
  76. data/lib/asker/formatter/question_hash_formatter.rb +40 -0
  77. data/lib/asker/formatter/question_moodlexml_formatter.rb +71 -0
  78. data/lib/asker/formatter/rb2haml_formatter.rb +26 -0
  79. data/lib/asker/lang/lang.rb +42 -0
  80. data/lib/asker/lang/lang_factory.rb +19 -0
  81. data/lib/asker/lang/text_actions.rb +150 -0
  82. data/lib/asker/loader/code_loader.rb +53 -0
  83. data/lib/asker/loader/content_loader.rb +101 -0
  84. data/lib/asker/loader/directory_loader.rb +58 -0
  85. data/lib/asker/loader/file_loader.rb +33 -0
  86. data/lib/asker/loader/image_url_loader.rb +61 -0
  87. data/lib/asker/loader/input_loader.rb +24 -0
  88. data/lib/asker/loader/project_loader.rb +71 -0
  89. data/lib/asker/logger.rb +21 -0
  90. data/lib/asker/project.rb +170 -0
  91. metadata +261 -0
@@ -0,0 +1,93 @@
1
+
2
+ require_relative 'column'
3
+
4
+ class Row
5
+ attr_reader :table, :index, :id
6
+ attr_reader :langs, :types, :raws, :columns
7
+ attr_reader :simple
8
+
9
+ def initialize( table, index, xml_data )
10
+ @table = table
11
+ @index = index
12
+ @id = @table.id + "." + @index.to_s
13
+ @langs = @table.langs
14
+ @types = @table.types
15
+ @raws = []
16
+ @columns = []
17
+ @simple = { :lang => true, :type => true }
18
+ read_data_from_xml(xml_data)
19
+ end
20
+
21
+ def simple_off(option)
22
+ @simple[option]=false
23
+ @table.simple_off(option)
24
+ end
25
+
26
+ private
27
+
28
+ def read_data_from_xml(pXMLdata)
29
+ if pXMLdata.elements.count==0 then
30
+ build_row_with_1_column(pXMLdata)
31
+ else
32
+ build_row_with_N_columns(pXMLdata)
33
+ end
34
+
35
+ raise "[ERROR] Row: #{pXMLdata}" if @columns.size!=@table.fields.size
36
+ end
37
+
38
+ def build_row_with_1_column(pXMLdata)
39
+ # When row tag only has text, we add this text as one value array
40
+ # This is usefull for tables with only one columns
41
+ @columns = [ Column.new( self, @raws.size, pXMLdata) ]
42
+ @raws = [ pXMLdata.text.strip.to_s ]
43
+
44
+ #read attributes from XML data
45
+ if pXMLdata.attributes['lang'] then
46
+ code = pXMLdata.attributes['lang'].strip
47
+ if code != @langs[0].code then
48
+ @langs = [ LangFactory.instance.get(code) ]
49
+ @simple[:lang]= false
50
+ @table.simple_off(:lang)
51
+ end
52
+ end
53
+
54
+ if pXMLdata.attributes['type'] then
55
+ type = pXMLdata.attributes['type'].strip
56
+ if type != @types[0] then
57
+ @types = [ type ]
58
+ @simple[:type]= false
59
+ @table.simple_off(:type)
60
+ end
61
+ end
62
+ end
63
+
64
+ def build_row_with_N_columns(pXMLdata)
65
+ pXMLdata.elements.each do |i|
66
+ case i.name
67
+ when 'lang'
68
+ j = i.text.split(",")
69
+ codes = @langs.map {|i| i.code }
70
+
71
+ if j.join(",")!=codes.join(",")
72
+ @langs = []
73
+ j.each { |k| @langs << LangFactory.instance.get(k.strip.to_s) }
74
+ @simple[:lang]=false
75
+ @table.simple_off(:lang)
76
+ end
77
+ when 'type'
78
+ j = i.text.split(",")
79
+ if j.join(",")!=@types.join(",") then
80
+ @types = []
81
+ j.each { |k| @types << k.strip.to_s }
82
+ @simple[:type]=false
83
+ @table.simple_off(:type)
84
+ end
85
+ when 'col' # When row tag has several columns, we add every value to the array
86
+ #Column Objects
87
+ @columns << Column.new( self, @raws.size, i)
88
+ @raws << i.text.to_s
89
+ end
90
+ end
91
+ end
92
+
93
+ end
@@ -0,0 +1,96 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative 'row'
4
+ require_relative 'template'
5
+
6
+ # Contains data table information
7
+ class Table
8
+ attr_reader :name, :id
9
+ attr_reader :fields, :langs, :types, :sequence
10
+ attr_reader :datarows, :rows
11
+ attr_reader :simple
12
+
13
+ def initialize(concept, xml_data)
14
+ @concept = concept
15
+
16
+ # read attributes from XML data
17
+ t = xml_data.attributes['fields'].to_s.strip.split(',')
18
+ t.each { |i| i.strip! }
19
+ @fields = t || []
20
+ @types = ['text'] * @fields.size
21
+ @langs = [@concept.lang] * @fields.size
22
+
23
+ @name = ''
24
+ @fields.each { |i| @name=@name + '$' + i.to_s.strip.downcase}
25
+ @id = @concept.name.to_s + '.' + @name
26
+ @simple = { lang: true, type: true }
27
+
28
+ @sequence = []
29
+ if xml_data.attributes['sequence']
30
+ t = xml_data.attributes['sequence'].to_s || ""
31
+ @sequence = t.split(",")
32
+ # puts "[DEPRECATED] sequence attr on table <#{@name}>"
33
+ end
34
+
35
+ @datarows = [] #DEV experiment replace row data with row objects
36
+ read_data_from_xml(xml_data)
37
+ @rows = @datarows.map { |r| r.raws }
38
+ end
39
+
40
+ def to_s
41
+ @name.to_s
42
+ end
43
+
44
+ def sequence?
45
+ @sequence.size > 0
46
+ end
47
+
48
+ def types(index = :all)
49
+ @types = (['text'] * @fields.size) if @types.nil?
50
+ return @types if index == :all
51
+ @types[index]
52
+ end
53
+
54
+ def simple_off(option)
55
+ @simple[option] = false
56
+ end
57
+
58
+ private
59
+
60
+ def read_data_from_xml(xml_data)
61
+ xml_data.elements.each do |i|
62
+ case i.name
63
+ when 'lang'
64
+ j = i.text.split(',')
65
+ codes = @langs.map(&:code)
66
+
67
+ if j.join(',') != codes.join(',')
68
+ simple_off(:lang)
69
+ @langs = []
70
+ j.each do |k|
71
+ if k.strip == '*' || k.strip == ''
72
+ @langs << @concept.lang
73
+ else
74
+ @langs << LangFactory.instance.get(k.strip.to_s)
75
+ end
76
+ end
77
+ end
78
+ when 'row'
79
+ @datarows << Row.new(self, @datarows.size, i)
80
+ when 'sequence'
81
+ @sequence = i.text.split(',')
82
+ when 'template'
83
+ @datarows += Template.new(self, @datarows.size, i).datarows
84
+ when 'type'
85
+ j = i.text.split(',')
86
+ if j.join(',') != @types.join(',')
87
+ simple_off(:type)
88
+ @types = []
89
+ j.each { |k| @types << k.strip.to_s }
90
+ end
91
+ else
92
+ puts Rainbow("[ERROR] concept/table#xml_data with #{i.name}").red.bright
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,65 @@
1
+
2
+ require 'rexml/document'
3
+ require_relative 'row'
4
+
5
+ # This class process "template" tag used by Tables
6
+ class Template
7
+ attr_reader :datarows
8
+
9
+ def initialize(table, index, xml)
10
+ @mode = :simple
11
+ vars = load_vars_from(xml)
12
+ template = load_template_from(xml)
13
+ data_string = apply_vars_to_template(vars, template)
14
+ @datarows = read_rows_from(table, index, data_string)
15
+ end
16
+
17
+ def load_vars_from(xml)
18
+ vars = {}
19
+ v = xml.attributes
20
+ v.keys.each do |i|
21
+ if i == 'mode'
22
+ @mode = v[i].to_sym
23
+ else
24
+ vars[i] = v[i].split(',')
25
+ end
26
+ end
27
+ # fill_vars_values(vars,mode)
28
+ vars
29
+ end
30
+
31
+ def fill_vars_values(vars, mode)
32
+ # create sizes array
33
+ end
34
+
35
+ def load_template_from(xml)
36
+ template = ''
37
+ xml.elements.each { |i| template << i.to_s + "\n" }
38
+ template
39
+ end
40
+
41
+ def apply_vars_to_template(vars, template)
42
+ output = ''
43
+ return if vars.size.zero?
44
+ max = vars.first[1].size
45
+ (1..max).each do |index|
46
+ t = template.dup
47
+ vars.each_pair { |k, v| t.gsub!(k, v[index - 1]) }
48
+ output += t
49
+ end
50
+ output
51
+ end
52
+
53
+ def read_rows_from(table, index, data_string)
54
+ datarows = []
55
+ data = "<template>\n#{data_string}\n</template>"
56
+ xml = REXML::Document.new(data)
57
+ xml.root.elements.each do |i|
58
+ if i.name == 'row'
59
+ datarows << Row.new(table, index, i)
60
+ index += 1
61
+ end
62
+ end
63
+ datarows
64
+ end
65
+ end
@@ -0,0 +1,53 @@
1
+
2
+ require_relative '../loader/image_url_loader'
3
+ require_relative '../project'
4
+
5
+ class World
6
+ attr_reader :concepts, :filenames, :contexts, :image_urls
7
+
8
+ def initialize(concepts, show_progress=true)
9
+ find_neighbors_for_every_concept(concepts)
10
+
11
+ @concepts = {}
12
+ @filenames = []
13
+ @contexts = []
14
+ @image_urls = {}
15
+
16
+ concepts.each do |c|
17
+ if c.process
18
+ @concepts[c.name] = c
19
+ @filenames << c.filename
20
+ @contexts << c.context
21
+ end
22
+ end
23
+ @filenames.uniq!
24
+ @contexts.uniq!
25
+
26
+ threads = []
27
+ concepts.each do |c|
28
+ print('.') if show_progress
29
+ # puts "[DEBUG] #{c.name}\n"
30
+ # filter = [ c.name.clone ] + c.context.clone
31
+ filter = c.name.clone
32
+ threads << Thread.new { @image_urls[c.name] = ImageUrlLoader::load(filter) }
33
+ end
34
+ @contexts.each do |filter|
35
+ print('.') if show_progress
36
+ threads << Thread.new { @image_urls[ filter.join('.').to_s ] = ImageUrlLoader::load(filter) }
37
+ end
38
+ threads.each { |t| t.join } # wait for all threads to finish
39
+ print("\n") if show_progress
40
+ end
41
+
42
+ def find_neighbors_for_every_concept(concepts)
43
+ concepts.each do |i|
44
+ concepts.each do |j|
45
+ if (i.id!=j.id) then
46
+ i.try_adding_neighbor(j)
47
+ i.try_adding_references(j)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../project'
4
+ require_relative '../formatter/question_gift_formatter'
5
+
6
+ # UNDER DEVELOPMENT
7
+ # Use to export questions from Code to gift format
8
+ module CodeGiftExporter
9
+ def self.export_all(codes)
10
+ codes.each do |code|
11
+ code.make_questions
12
+ export code
13
+ end
14
+ end
15
+
16
+ def self.export(code)
17
+ return false unless code.process?
18
+
19
+ file = Project.instance.outputfile
20
+ file.write head(code)
21
+
22
+ code.questions.each do |question|
23
+ file.write QuestionGiftFormatter.to_s(question)
24
+ end
25
+ true
26
+ end
27
+
28
+ def self.head(code)
29
+ s = "\n"
30
+ s += '// ' + '=' * 50 + "\n"
31
+ s += "// Code #{code.type}: #{code.filename} (#{code.questions.size})\n"
32
+ s += '// ' + '=' * 50 + "\n"
33
+ s
34
+ end
35
+ end
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminal-table'
4
+
5
+ # Export Code into Screen
6
+ module CodeScreenExporter
7
+ def self.export_all(codes)
8
+ project = Project.instance
9
+ return if project.show_mode == :none || codes.nil? || codes.size.zero?
10
+
11
+ total_c = total_q = total_e = 0
12
+ my_screen_table = Terminal::Table.new do |st|
13
+ st << %w[Filename Type Questions Lines xFactor]
14
+ st << :separator
15
+ end
16
+
17
+ codes.each do |code|
18
+ next unless code.process?
19
+
20
+ e = code.lines.size
21
+ q = code.questions.size
22
+ factor = 'Unkown'
23
+ factor = (q.to_f / e).round(2).to_s unless e.zero?
24
+ my_screen_table.add_row [Rainbow(File.basename(code.filename)).green,
25
+ code.type,
26
+ q,
27
+ e,
28
+ factor]
29
+ total_c += 1
30
+ total_q += q
31
+ total_e += e
32
+ end
33
+
34
+ my_screen_table.add_separator
35
+ my_screen_table.add_row [Rainbow("TOTAL = #{total_c}").bright,
36
+ ' ',
37
+ Rainbow(total_q.to_s).bright,
38
+ Rainbow(total_e.to_s).bright,
39
+ Rainbow((total_q / total_e.to_f).round(2)).bright]
40
+ return unless total_c.positive?
41
+
42
+ project.verboseln "\n[INFO] Showing CODE statistics"
43
+ project.verboseln my_screen_table.to_s
44
+ end
45
+ end
@@ -0,0 +1,33 @@
1
+ # encoding: utf-8
2
+
3
+ require_relative '../project'
4
+ require_relative '../formatter/question_gift_formatter'
5
+
6
+ # Use to export data from ConceptIA to gift format
7
+ module ConceptAIGiftExporter
8
+ def self.export_all(concepts_ai)
9
+ concepts_ai.each { |concept_ai| export(concept_ai) }
10
+ end
11
+
12
+ def self.export(concept_ai)
13
+ return unless concept_ai.process?
14
+
15
+ file = Project.instance.outputfile
16
+ file.write head(concept_ai.name)
17
+
18
+ stages = Project.instance.stages
19
+ stages.each_key do |stage|
20
+ concept_ai.questions[stage].each do |question|
21
+ file.write(QuestionGiftFormatter.to_s(question))
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.head(name)
27
+ s = "\n"
28
+ s += '// ' + '=' * 50 + "\n"
29
+ s += "// Concept name: #{name}\n"
30
+ s += '// ' + '=' * 50 + "\n"
31
+ s
32
+ end
33
+ end
@@ -0,0 +1,115 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'terminal-table'
4
+
5
+ # Show ConceptAI info on screen
6
+ class ConceptAIScreenExporter
7
+ def self.export_all(concepts_ai)
8
+ @concepts_ai = concepts_ai
9
+ project = Project.instance
10
+ return if project.show_mode == :none
11
+
12
+ # Create table HEAD
13
+ screen_table = Terminal::Table.new do |st|
14
+ st << ['Concept','Questions','Entries','xFactor',
15
+ 'd','b','f','i','s','t']
16
+ st << :separator
17
+ end
18
+
19
+ # Create table BODY
20
+ total = {}
21
+ total[:q] = total[:e] = total[:c] = 0
22
+ total[:sd] = total[:sb] = total[:sf] = 0
23
+ total[:si] = total[:ss] = total[:st] = 0
24
+
25
+ @concepts_ai.each do |concept_ai|
26
+ if concept_ai.process?
27
+ e = concept_ai.texts.size
28
+ concept_ai.tables.each { |t| e += t.fields.size * t.rows.size }
29
+
30
+ sd = concept_ai.questions[:d].size
31
+ sb = concept_ai.questions[:b].size
32
+ sf = concept_ai.questions[:f].size
33
+ si = concept_ai.questions[:i].size
34
+ ss = concept_ai.questions[:s].size
35
+ st = concept_ai.questions[:t].size
36
+ t = sd + sb + sf + si + ss + st
37
+
38
+ if e == 0
39
+ factor = 'Unkown'
40
+ else
41
+ factor = (t.to_f/e.to_f).round(2).to_s
42
+ end
43
+ screen_table.add_row [Rainbow(concept_ai.name(:screen)).green.bright,
44
+ t, e, factor, sd, sb, sf, si, ss, st]
45
+
46
+ total[:q] += t ; total[:e] += e; total[:c] += 1
47
+ total[:sd] += sd; total[:sb] += sb; total[:sf] += sf
48
+ total[:si] += si; total[:ss] += ss; total[:st] += st
49
+ end
50
+ end
51
+ return if total[:c] == 0 # No concepts to be process?
52
+
53
+ # Add row with excluded questions
54
+ export_excluded_questions(screen_table, @concepts_ai)
55
+
56
+ # Create table TAIL
57
+ screen_table.add_separator
58
+ screen_table.add_row [Rainbow("TOTAL = #{total[:c]}").bright,
59
+ Rainbow(total[:q].to_s).bright,
60
+ Rainbow(total[:e].to_s).bright,
61
+ Rainbow((total[:q].to_f/total[:e].to_f).round(2)).bright,
62
+ total[:sd], total[:sb], total[:sf],
63
+ total[:si], total[:ss], total[:st]]
64
+ export_notes
65
+ project.verbose screen_table.to_s + "\n"
66
+ end
67
+
68
+ def self.export_excluded_questions(screen_table, concepts_ai)
69
+ # Create table BODY
70
+ total = {}
71
+ total[:q] = total[:c] = 0
72
+ total[:sd] = total[:sb] = total[:sf] = 0
73
+ total[:si] = total[:ss] = total[:st] = 0
74
+
75
+ concepts_ai.each do |concept_ai|
76
+ if concept_ai.process?
77
+ sd = concept_ai.excluded_questions[:d].size
78
+ sb = concept_ai.excluded_questions[:b].size
79
+ sf = concept_ai.excluded_questions[:f].size
80
+ si = concept_ai.excluded_questions[:i].size
81
+ ss = concept_ai.excluded_questions[:s].size
82
+ st = concept_ai.excluded_questions[:t].size
83
+ t = sd + sb + sf + si + ss + st
84
+
85
+ total[:q] += t ; total[:c] += 1
86
+ total[:sd] += sd; total[:sb] += sb; total[:sf] += sf
87
+ total[:si] += si; total[:ss] += ss; total[:st] += st
88
+ end
89
+ end
90
+ screen_table.add_row [Rainbow('Excluded questions').yellow.bright,
91
+ total[:q], '-', '-',
92
+ total[:sd], total[:sb],
93
+ total[:sf], total[:si],
94
+ total[:ss], total[:st]]
95
+ end
96
+
97
+ def self.export_notes
98
+ p = Project.instance
99
+ p.verbose "\n[INFO] Showing CONCEPT statistics\n"
100
+ p.verbose ' * Exclude questions: ' +
101
+ Application.instance.config['questions']['exclude'].to_s
102
+ p.verbose ' * Annotations:'
103
+ p.verbose ' ├── (d) Definitions <= Concept.def'
104
+ p.verbose ' ├── (b) Table Matching <= ' \
105
+ 'Concept.table.rows.columns'
106
+ p.verbose ' ├── (f) Tables 1 Field <= Concept.table.fields.size==1'
107
+ p.verbose ' ├── (i) Images URL <= ' \
108
+ "Concept.def{:type => 'image_url'}"
109
+ p.verbose ' ├── (s) Sequences <= ' \
110
+ "Concept.table{:sequence => '...'}"
111
+ p.verbose ' └── (t) Table Rows&Cols <= ' \
112
+ 'Concept.table.rows.columns'
113
+ p.verbose "\n"
114
+ end
115
+ end