Module: Wisco::Commands::List

Defined in:
lib/wisco/commands/list.rb

Constant Summary collapse

TREE_FORK =
'├── '
TREE_LAST =
'└── '
TREE_PIPE =
''
TREE_BLANK =
'    '
EXPANDABLE_KEYS =
%i[actions triggers object_definitions methods pick_lists].freeze
SORT_FIELDS =
%w[key title].freeze

Class Method Summary collapse

Class Method Details

.build_connection(conn) ⇒ Object



127
128
129
130
131
132
# File 'lib/wisco/commands/list.rb', line 127

def build_connection(conn)
  return { 'fields' => [] } unless conn.is_a?(Hash)

  fields = conn[:fields] || []
  { 'fields' => fields.map { |f| safe_serialize(f) }.compact }
end

.build_connector_hash(connector) ⇒ Object



116
117
118
119
120
121
122
123
124
125
# File 'lib/wisco/commands/list.rb', line 116

def build_connector_hash(connector)
  {
    'title'      => connector[:title].to_s,
    'connection' => build_connection(connector[:connection]),
    'actions'    => build_section(connector[:actions]),
    'triggers'   => build_section(connector[:triggers]),
    'methods'    => (connector[:methods]    || {}).keys.map(&:to_s).sort,
    'pick_lists' => (connector[:pick_lists] || {}).keys.map(&:to_s).sort
  }
end

.build_section(section) ⇒ Object



134
135
136
137
138
139
140
141
142
143
144
# File 'lib/wisco/commands/list.rb', line 134

def build_section(section)
  return {} unless section.is_a?(Hash)

  section.keys.sort_by(&:to_s).each_with_object({}) do |key, h|
    item = section[key]
    h[key.to_s] = {
      'title'    => title_for(key, item),
      'subtitle' => subtitle_for(item).to_s
    }
  end
end

.humanise_key(key) ⇒ Object



172
173
174
# File 'lib/wisco/commands/list.rb', line 172

def humanise_key(key)
  key.to_s.split('_').map(&:capitalize).join(' ')
end

.render_children_tree(value, prefix) ⇒ Object



251
252
253
254
255
256
257
# File 'lib/wisco/commands/list.rb', line 251

def render_children_tree(value, prefix)
  child_keys = value.keys.sort
  child_keys.each_with_index do |ck, ci|
    cl = ci == child_keys.size - 1
    puts "#{prefix}#{cl ? TREE_LAST : TREE_FORK}#{ck}"
  end
end

.render_connection_tree(conn, prefix) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
# File 'lib/wisco/commands/list.rb', line 229

def render_connection_tree(conn, prefix)
  keys = conn.keys
  keys.each_with_index do |key, idx|
    last = idx == keys.size - 1
    connector = last ? TREE_LAST : TREE_FORK
    child_prefix = prefix + (last ? TREE_BLANK : TREE_PIPE)
    value = conn[key]

    if value.is_a?(Hash)
      puts "#{prefix}#{connector}#{key}"
      sub_keys = value.keys
      sub_keys.each_with_index do |sk, si|
        sl = si == sub_keys.size - 1
        sc = sl ? TREE_LAST : TREE_FORK
        puts "#{child_prefix}#{sc}#{sk}"
      end
    else
      puts "#{prefix}#{connector}#{key} #{value_label(value)}"
    end
  end
end

.render_markdown_table(headers, rows) ⇒ Object



188
189
190
191
192
193
194
195
196
197
198
199
200
# File 'lib/wisco/commands/list.rb', line 188

def render_markdown_table(headers, rows)
  all_rows = [headers] + rows
  widths = headers.length.times.map do |i|
    all_rows.map { |r| r[i].to_s.length }.max
  end

  fmt = widths.map { |w| "%-#{w}s" }.join(' | ')
  sep = widths.map { |w| '-' * w }.join('-|-')

  puts "| #{format(fmt, *headers)} |"
  puts "|-#{sep}-|"
  rows.each { |r| puts "| #{format(fmt, *r)} |" }
end

.run(subcommand, target, sort: nil, format: nil) ⇒ Object



15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'lib/wisco/commands/list.rb', line 15

def run(subcommand, target, sort: nil, format: nil)
  validate_sort!(sort)

  if format
    run_formatted(target, format: format)
    return
  end

  case subcommand
  when nil        then run_tree(target)
  when 'actions'  then run_actions(target, sort: sort)
  when 'triggers' then run_triggers(target, sort: sort)
  when 'all'      then run_all(target, sort: sort)
  else
    warn "Error: Unknown list subcommand '#{subcommand}'"
    warn "Run '#{Wisco::CLI_NAME} --help' for usage."
    exit 1
  end
end

.run_actions(target_dir, sort: nil) ⇒ Object



62
63
64
65
66
67
68
69
70
71
72
73
74
# File 'lib/wisco/commands/list.rb', line 62

def run_actions(target_dir, sort: nil)
  connector = Wisco::Connector.load_connector_from_config(target_dir)
  actions = connector[:actions]

  if actions.nil? || actions.empty?
    puts 'No actions defined.'
    return
  end

  rows = actions.map { |key, item| [key, title_for(key, item), subtitle_for(item)] }
  rows = sort_rows(rows, sort)
  render_markdown_table(%w[Key Title Subtitle], rows)
end

.run_all(target_dir, sort: nil) ⇒ Object



90
91
92
93
94
95
96
97
98
99
# File 'lib/wisco/commands/list.rb', line 90

def run_all(target_dir, sort: nil)
  puts '## Overview'
  run_tree(target_dir)
  puts
  puts '## Actions'
  run_actions(target_dir, sort: sort)
  puts
  puts '## Triggers'
  run_triggers(target_dir, sort: sort)
end

.run_formatted(target_dir, format:) ⇒ Object


Machine-readable output




105
106
107
108
109
110
111
112
113
114
# File 'lib/wisco/commands/list.rb', line 105

def run_formatted(target_dir, format:)
  require 'yaml' if format.downcase == 'yaml'
  connector = Wisco::Connector.load_connector_from_config(target_dir)
  data = build_connector_hash(connector)
  if format.downcase == 'yaml'
    puts data.to_yaml
  else
    puts JSON.pretty_generate(data)
  end
end

.run_tree(target_dir) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/wisco/commands/list.rb', line 35

def run_tree(target_dir)
  connector = Wisco::Connector.load_connector_from_config(target_dir)
  puts connector[:title]

  keys = connector.keys
  keys.each_with_index do |key, idx|
    last = idx == keys.size - 1
    connector_str = last ? TREE_LAST : TREE_FORK
    child_prefix = last ? TREE_BLANK : TREE_PIPE
    value = connector[key]

    if key == :connection && value.is_a?(Hash)
      puts "#{connector_str}#{key}"
      render_connection_tree(value, child_prefix)
    elsif EXPANDABLE_KEYS.include?(key) && value.is_a?(Hash) && !value.empty?
      puts "#{connector_str}#{key} [#{value.size}]"
      render_children_tree(value, child_prefix)
    elsif value.is_a?(Hash)
      puts "#{connector_str}#{key} [#{value.size}]"
    elsif value.is_a?(Array)
      puts "#{connector_str}#{key} [#{value.size}]"
    else
      puts "#{connector_str}#{key}"
    end
  end
end

.run_triggers(target_dir, sort: nil) ⇒ Object



76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/wisco/commands/list.rb', line 76

def run_triggers(target_dir, sort: nil)
  connector = Wisco::Connector.load_connector_from_config(target_dir)
  triggers = connector[:triggers]

  if triggers.nil? || triggers.empty?
    puts 'No triggers defined.'
    return
  end

  rows = triggers.map { |key, item| [key, title_for(key, item), subtitle_for(item)] }
  rows = sort_rows(rows, sort)
  render_markdown_table(%w[Key Title Subtitle], rows)
end

.safe_serialize(value) ⇒ Object

Recursively converts a connector value to a JSON-safe structure. Proc/lambda values are dropped (returned as nil so callers can compact).



148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/wisco/commands/list.rb', line 148

def safe_serialize(value)
  case value
  when Hash
    value.each_with_object({}) do |(k, v), h|
      next if v.is_a?(Proc)

      serialized = safe_serialize(v)
      h[k.to_s] = serialized
    end
  when Array  then value.map { |v| safe_serialize(v) }
  when Proc   then nil
  when Symbol then value.to_s
  else             value
  end
end

.sort_rows(rows, sort) ⇒ Object



209
210
211
212
213
214
# File 'lib/wisco/commands/list.rb', line 209

def sort_rows(rows, sort)
  return rows if sort.nil?

  index = SORT_FIELDS.index(sort)
  rows.sort_by { |row| [row[index].to_s.downcase, row[0].to_s.downcase] }
end

.strip_html(str) ⇒ Object


List utilities




168
169
170
# File 'lib/wisco/commands/list.rb', line 168

def strip_html(str)
  str.to_s.gsub(/<[^>]+>/, '').squeeze(' ').strip
end

.subtitle_for(item) ⇒ Object



180
181
182
183
184
185
186
# File 'lib/wisco/commands/list.rb', line 180

def subtitle_for(item)
  return '' unless item.is_a?(Hash)
  return item[:subtitle] if item[:subtitle]

  strip_html(item[:description]) if item[:description]
  item[:subtitle] || strip_html(item[:description].to_s)
end

.title_for(key, item) ⇒ Object



176
177
178
# File 'lib/wisco/commands/list.rb', line 176

def title_for(key, item)
  item.is_a?(Hash) && item[:title] ? item[:title] : humanise_key(key)
end

.validate_sort!(sort) ⇒ Object



202
203
204
205
206
207
# File 'lib/wisco/commands/list.rb', line 202

def validate_sort!(sort)
  return if sort.nil? || SORT_FIELDS.include?(sort)

  warn "Error: Unsupported sort field '#{sort}'. Valid values: #{SORT_FIELDS.join(', ')}."
  exit 1
end

.value_label(value) ⇒ Object


Tree rendering




220
221
222
223
224
225
226
227
# File 'lib/wisco/commands/list.rb', line 220

def value_label(value)
  case value
  when Hash  then "[#{value.size}]"
  when Array then "[#{value.size}]"
  when Proc  then '(lambda)'
  else            value.to_s
  end
end