kood 0.0.1
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.
- data/.gitignore +47 -0
- data/.yardopts +4 -0
- data/Gemfile +2 -0
- data/LICENSE +22 -0
- data/README.md +9 -0
- data/Rakefile +40 -0
- data/bin/kood +4 -0
- data/kood.gemspec +26 -0
- data/lib/kood-plugin-example.rb +10 -0
- data/lib/kood.rb +14 -0
- data/lib/kood/adapter/git.rb +56 -0
- data/lib/kood/adapter/user_config.rb +36 -0
- data/lib/kood/board.rb +160 -0
- data/lib/kood/card.rb +128 -0
- data/lib/kood/cli.rb +121 -0
- data/lib/kood/cli/board.rb +139 -0
- data/lib/kood/cli/card.rb +188 -0
- data/lib/kood/cli/edit.rb +40 -0
- data/lib/kood/cli/helpers/shell.rb +124 -0
- data/lib/kood/cli/helpers/table.rb +195 -0
- data/lib/kood/cli/list.rb +70 -0
- data/lib/kood/cli/plugin.rb +37 -0
- data/lib/kood/cli/switch.rb +10 -0
- data/lib/kood/core.rb +95 -0
- data/lib/kood/errors.rb +8 -0
- data/lib/kood/extensions/grit.rb +65 -0
- data/lib/kood/list.rb +36 -0
- data/lib/kood/version.rb +3 -0
- data/man/kood-board.1 +74 -0
- data/man/kood-board.1.html +150 -0
- data/man/kood-board.1.ronn +65 -0
- data/man/kood-card.1 +40 -0
- data/man/kood-card.1.html +140 -0
- data/man/kood-card.1.ronn +52 -0
- data/spec/kood/cli_spec.rb +280 -0
- data/spec/spec_helper.rb +67 -0
- data/test/kood/cli_test.rb +67 -0
- metadata +198 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
class Kood::CLI < Thor
|
|
2
|
+
|
|
3
|
+
desc "edit [<CARD-ID|CARD-TITLE>]", "Launch the configured editor to modify the card"
|
|
4
|
+
def edit(card_id_or_title = nil)
|
|
5
|
+
Kood::Board.current!.with_context do |current_board|
|
|
6
|
+
card = Kood::Card.find_by_partial_id_or_title!(card_id_or_title)
|
|
7
|
+
success, command = false, ""
|
|
8
|
+
|
|
9
|
+
editor = [ ENV['KOOD_EDITOR'], ENV['EDITOR'] ].find { |e| !e.nil? && !e.empty? }
|
|
10
|
+
return error "To edit a card set $EDITOR or $KOOD_EDITOR." unless editor
|
|
11
|
+
|
|
12
|
+
changed = card.edit_file do |filepath|
|
|
13
|
+
command = "#{ editor } #{ filepath }"
|
|
14
|
+
success = system(command)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
if not success
|
|
18
|
+
error "Could not run `#{ command }`."
|
|
19
|
+
elsif changed
|
|
20
|
+
ok "Card updated."
|
|
21
|
+
else
|
|
22
|
+
error "The editor exited without changes. Run `kood update` to persist changes."
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
desc "update [<CARD-ID|CARD-TITLE>]", "Persist changes made to cards", hide: true
|
|
28
|
+
def update(card_id_or_title = nil)
|
|
29
|
+
Kood::Board.current!.with_context do |current_board|
|
|
30
|
+
card = Kood::Card.find_by_partial_id_or_title!(card_id_or_title)
|
|
31
|
+
changed = card.edit_file
|
|
32
|
+
|
|
33
|
+
if changed
|
|
34
|
+
ok "Card updated."
|
|
35
|
+
else
|
|
36
|
+
error "No changes to persist."
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
module Kood
|
|
2
|
+
# Utility functions related to the Shell.
|
|
3
|
+
#
|
|
4
|
+
# Contains functions for colors from [git.io/thor](//git.io/thor) and for terminal size
|
|
5
|
+
# from [git.io/hirb](//git.io/hirb). A special Thank You for both authors.
|
|
6
|
+
#
|
|
7
|
+
module Shell
|
|
8
|
+
extend self
|
|
9
|
+
|
|
10
|
+
# Embed in a String to clear all previous ANSI sequences.
|
|
11
|
+
CLEAR = "\e[0m"
|
|
12
|
+
# The start of an ANSI bold sequence.
|
|
13
|
+
BOLD = "\e[1m"
|
|
14
|
+
|
|
15
|
+
# Set the terminal's foreground ANSI color to black.
|
|
16
|
+
BLACK = "\e[30m"
|
|
17
|
+
# Set the terminal's foreground ANSI color to red.
|
|
18
|
+
RED = "\e[31m"
|
|
19
|
+
# Set the terminal's foreground ANSI color to green.
|
|
20
|
+
GREEN = "\e[32m"
|
|
21
|
+
# Set the terminal's foreground ANSI color to yellow.
|
|
22
|
+
YELLOW = "\e[33m"
|
|
23
|
+
# Set the terminal's foreground ANSI color to blue.
|
|
24
|
+
BLUE = "\e[34m"
|
|
25
|
+
# Set the terminal's foreground ANSI color to magenta.
|
|
26
|
+
MAGENTA = "\e[35m"
|
|
27
|
+
# Set the terminal's foreground ANSI color to cyan.
|
|
28
|
+
CYAN = "\e[36m"
|
|
29
|
+
# Set the terminal's foreground ANSI color to white.
|
|
30
|
+
WHITE = "\e[37m"
|
|
31
|
+
|
|
32
|
+
# Set the terminal's background ANSI color to black.
|
|
33
|
+
ON_BLACK = "\e[40m"
|
|
34
|
+
# Set the terminal's background ANSI color to red.
|
|
35
|
+
ON_RED = "\e[41m"
|
|
36
|
+
# Set the terminal's background ANSI color to green.
|
|
37
|
+
ON_GREEN = "\e[42m"
|
|
38
|
+
# Set the terminal's background ANSI color to yellow.
|
|
39
|
+
ON_YELLOW = "\e[43m"
|
|
40
|
+
# Set the terminal's background ANSI color to blue.
|
|
41
|
+
ON_BLUE = "\e[44m"
|
|
42
|
+
# Set the terminal's background ANSI color to magenta.
|
|
43
|
+
ON_MAGENTA = "\e[45m"
|
|
44
|
+
# Set the terminal's background ANSI color to cyan.
|
|
45
|
+
ON_CYAN = "\e[46m"
|
|
46
|
+
# Set the terminal's background ANSI color to white.
|
|
47
|
+
ON_WHITE = "\e[47m"
|
|
48
|
+
|
|
49
|
+
# Set color by using a string or one of the defined constants. If a third
|
|
50
|
+
# option is set to true, it also adds bold to the string. This is based
|
|
51
|
+
# on Highline implementation and it automatically appends CLEAR to the end
|
|
52
|
+
# of the returned String.
|
|
53
|
+
#
|
|
54
|
+
# Pass foreground, background and bold options to this method as
|
|
55
|
+
# symbols.
|
|
56
|
+
#
|
|
57
|
+
# Example:
|
|
58
|
+
#
|
|
59
|
+
# set_color "Hi!", :red, :on_white, :bold
|
|
60
|
+
#
|
|
61
|
+
def set_color(string, *colors)
|
|
62
|
+
ansi_colors = colors.map { |color| lookup_color(color) }
|
|
63
|
+
"#{ansi_colors.join}#{string}#{CLEAR}"
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Determines if a shell command exists by searching for it in `ENV['PATH']`.
|
|
67
|
+
# Utility function gently stolen from [git.io/hirb](//git.io/hirb)
|
|
68
|
+
def command_exists?(command)
|
|
69
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).any? {|d| File.exists? File.join(d, command) }
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Returns width and height of terminal when detected, nil if not detected.
|
|
73
|
+
# Utility function gently stolen from [git.io/hirb](//git.io/hirb)
|
|
74
|
+
# @return [Array] width, height
|
|
75
|
+
def terminal_size
|
|
76
|
+
if (ENV['COLUMNS'] =~ /^\d+$/) && (ENV['LINES'] =~ /^\d+$/)
|
|
77
|
+
[ENV['COLUMNS'].to_i, ENV['LINES'].to_i]
|
|
78
|
+
elsif (RUBY_PLATFORM =~ /java/ || (!STDIN.tty? && ENV['TERM'])) && command_exists?('tput')
|
|
79
|
+
[`tput cols`.to_i, `tput lines`.to_i]
|
|
80
|
+
elsif STDIN.tty? && command_exists?('stty')
|
|
81
|
+
`stty size`.scan(/\d+/).map { |s| s.to_i }.reverse
|
|
82
|
+
else
|
|
83
|
+
nil
|
|
84
|
+
end
|
|
85
|
+
rescue
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Check if terminal supports unicode characters
|
|
90
|
+
def unicode?
|
|
91
|
+
"\u2501" != 'u2501'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Check if terminal supports colors. Condition stolen from Thor.
|
|
95
|
+
def color_support?
|
|
96
|
+
!(RbConfig::CONFIG['host_os'] =~ /mswin|mingw/) || ENV['ANSICON']
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Horizontal delimiter for box drawing
|
|
100
|
+
# @return [String]
|
|
101
|
+
def horizontal_bar
|
|
102
|
+
unicode? ? "\u2501" : '-'
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Vertical delimiter for box drawing
|
|
106
|
+
# @return [String]
|
|
107
|
+
def vertical_bar
|
|
108
|
+
unicode? ? "\u2503" : '|'
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Try to convert a string to a float or integer. Returns the converted object or the
|
|
112
|
+
# original string if it cannot be converted. Based on code from
|
|
113
|
+
# [stackoverflow.com/a/8072164/543293](//stackoverflow.com/a/8072164/543293)
|
|
114
|
+
def type_cast(v)
|
|
115
|
+
((float = Float(v)) && (float % 1.0 == 0) ? float.to_i : float) rescue v
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
protected
|
|
119
|
+
|
|
120
|
+
def lookup_color(color)
|
|
121
|
+
self.class.const_get(color.to_s.upcase)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
require_relative "shell"
|
|
2
|
+
|
|
3
|
+
module Kood
|
|
4
|
+
class Column < Array
|
|
5
|
+
include Shell
|
|
6
|
+
|
|
7
|
+
attr_accessor :width, :rows, :separator
|
|
8
|
+
|
|
9
|
+
def initialize(width)
|
|
10
|
+
@width = width
|
|
11
|
+
@rows = []
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Add a new row to this column
|
|
15
|
+
# @param [String] row All contents to be added to the row of the column. It may be
|
|
16
|
+
# split in several lines.
|
|
17
|
+
# @param [Hash] options The `:separator` option adds a horizontal line to the end of
|
|
18
|
+
# the row. The `:align` option is used for text alignment; acceptable values are
|
|
19
|
+
# `ljust`, `lright` and `center`. The `:slice` option slices the given `row`
|
|
20
|
+
# parameter in order to fit in the column width. The following keys with default
|
|
21
|
+
# values will be added if not present: `{ separator: true, align: 'ljust',
|
|
22
|
+
# slice: true }`
|
|
23
|
+
def add_row(row, options = {})
|
|
24
|
+
options = { separator: true, align: 'ljust', slice: true }.merge(options)
|
|
25
|
+
row = row.to_s.force_encoding("UTF-8")
|
|
26
|
+
|
|
27
|
+
if @width and options[:slice]
|
|
28
|
+
sliced_rows = []
|
|
29
|
+
row.split("\n").each do |row|
|
|
30
|
+
sliced_rows += self.slice_row(row, options)
|
|
31
|
+
end
|
|
32
|
+
sliced_rows << self.separator if options[:separator]
|
|
33
|
+
self.add_rows(sliced_rows)
|
|
34
|
+
else
|
|
35
|
+
@rows.push(row)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# A sequence of dashes with the same width of the column
|
|
40
|
+
# @return [String]
|
|
41
|
+
def separator
|
|
42
|
+
self.horizontal_bar * @width
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
protected
|
|
46
|
+
|
|
47
|
+
def add_rows(rows)
|
|
48
|
+
@rows.push(*rows)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def slice_row(row, options = {})
|
|
52
|
+
sliced_rows = []
|
|
53
|
+
i = 0
|
|
54
|
+
|
|
55
|
+
while slice = row[i, @width]
|
|
56
|
+
break if slice.blank? and !row.blank?
|
|
57
|
+
i += @width
|
|
58
|
+
|
|
59
|
+
# This slice may start with space(s). If it does, remove them, grab some extra
|
|
60
|
+
# characters and add them to the slice
|
|
61
|
+
i, slice = lstrip_row(i, slice, row)
|
|
62
|
+
|
|
63
|
+
# If this slice does not end with a space, and the first character of the next
|
|
64
|
+
# slice is not a space, it means we would be cutting a word in half. If this
|
|
65
|
+
# is not a big word move it to the next line
|
|
66
|
+
i, slice = hyphenize_row(i, slice, row)
|
|
67
|
+
|
|
68
|
+
slice = align_row(slice, options)
|
|
69
|
+
slice = colorize_row(slice, options) if options.key? :color
|
|
70
|
+
|
|
71
|
+
sliced_rows << slice
|
|
72
|
+
end
|
|
73
|
+
sliced_rows
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def lstrip_row(i, slice, row)
|
|
77
|
+
slice = slice.lstrip
|
|
78
|
+
len_diff = @width - slice.length
|
|
79
|
+
if slice.length < @width and not row[i, len_diff].blank?
|
|
80
|
+
slice += row[i, len_diff]
|
|
81
|
+
i += len_diff
|
|
82
|
+
end
|
|
83
|
+
return i, slice
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def hyphenize_row(i, slice, row)
|
|
87
|
+
if slice[-1] != ' ' and row[i] and row[i] != ' '
|
|
88
|
+
last_word = slice.split.last
|
|
89
|
+
len_diff = slice.length - last_word.length
|
|
90
|
+
if not last_word.blank? and last_word.length <= (@width * 0.25).floor
|
|
91
|
+
unless slice[0, len_diff].blank?
|
|
92
|
+
slice = slice[0, len_diff]
|
|
93
|
+
i -= last_word.length
|
|
94
|
+
end
|
|
95
|
+
elsif not last_word.blank? # Cut the word and add an hyphen
|
|
96
|
+
slice = slice[0..-2] +"-"
|
|
97
|
+
i -= 1
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
return i, slice
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def align_row(slice, options = {})
|
|
104
|
+
slice.send(options[:align], @width)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def colorize_row(slice, options = {})
|
|
108
|
+
options.key?(:color) ? set_color(slice, *options[:color]) : slice
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
class Table
|
|
113
|
+
include Shell
|
|
114
|
+
|
|
115
|
+
attr_accessor :width, :cols, :col_width
|
|
116
|
+
|
|
117
|
+
def initialize(num_columns, width = nil)
|
|
118
|
+
terminal_width = width || terminal_size[0] || 70
|
|
119
|
+
spare_cols = (terminal_width - 3 * num_columns -1) % num_columns
|
|
120
|
+
@width = terminal_width - spare_cols
|
|
121
|
+
@num_cols = num_columns
|
|
122
|
+
@col_width = (@width - 3 * num_columns -1) / num_columns
|
|
123
|
+
@cols = []
|
|
124
|
+
raise "There is not enough space to accommodate all columns" if @col_width < 5
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def new_column
|
|
128
|
+
raise "Unable to add more columns to table" if @cols.size == @num_cols
|
|
129
|
+
column = Kood::Column.new(@col_width)
|
|
130
|
+
@cols << column
|
|
131
|
+
column
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def to_s(options = { separator: true })
|
|
135
|
+
max_col_rows = biggest_column.rows
|
|
136
|
+
return empty_row(options) if max_col_rows.length.zero?
|
|
137
|
+
|
|
138
|
+
output = max_col_rows.each_with_index.map do |col_row, i|
|
|
139
|
+
# Don't print the last separator if this is the last thing to be printed
|
|
140
|
+
row(i, options) unless i == max_col_rows.length-1 and col_row == @cols[0].separator
|
|
141
|
+
end.join.chomp
|
|
142
|
+
|
|
143
|
+
improve_cell_corners(output)
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# This code comes from [git.io/command_line_reporter](//git.io/command_line_reporter).
|
|
147
|
+
# A special Thank You to the authors.
|
|
148
|
+
def separator(type = 'middle')
|
|
149
|
+
if unicode?
|
|
150
|
+
case type
|
|
151
|
+
when 'first' then left, center, right = "\u250F", "\u2533", "\u2513"
|
|
152
|
+
when 'middle' then left, center, right = "\u2523", "\u254A", "\u252B"
|
|
153
|
+
when 'last' then left, center, right = "\u2517", "\u253B", "\u251B"
|
|
154
|
+
end
|
|
155
|
+
else
|
|
156
|
+
left = right = center = '+'
|
|
157
|
+
end
|
|
158
|
+
left + @cols.map { |c| horizontal_bar * (c.width + 2) }.join(center) + right
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
private
|
|
162
|
+
|
|
163
|
+
# Returns the column with the largest number of rows
|
|
164
|
+
def biggest_column
|
|
165
|
+
@cols.max_by { |col| col.rows.length }
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Returns an empty (full width) row
|
|
169
|
+
def empty_row(options = { separator: true })
|
|
170
|
+
vbar = options[:separator] ? self.vertical_bar : " "
|
|
171
|
+
@cols.map { |col| vbar + " #{ " "*@col_width } " }.join + vbar
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Returns an entire row from the table (which may cross several columns)
|
|
175
|
+
def row(row_index, options = { separator: true })
|
|
176
|
+
vbar = options[:separator] ? self.vertical_bar : " "
|
|
177
|
+
out = @cols.map { |col| vbar + " #{ col.rows[row_index] || " "*@col_width } " }.join
|
|
178
|
+
out + vbar + "\n"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Improves cell corners | |
|
|
182
|
+
# For example, |--|--| becomes |--+--|
|
|
183
|
+
# | |
|
|
184
|
+
def improve_cell_corners(table_str)
|
|
185
|
+
# The ([\e\[\d*m]*) groups are meant to handle colored horizontal bars. This does
|
|
186
|
+
# not take into account vertical colored bars and assumes the color code is
|
|
187
|
+
# positioned before the first horizontal bar or after the last one.
|
|
188
|
+
str = table_str.dup.gsub(/\u2501([\e\[\d*m]*) \u2503 ([\e\[\d*m]*)\u2501/) do
|
|
189
|
+
"\u2501#{ $1 }\u2501\u254B\u2501#{ $2 }\u2501"
|
|
190
|
+
end
|
|
191
|
+
str.gsub!(/\u2501([\e\[\d*m]*) \u2503/) { "\u2501#{ $1 }\u2501\u252B" }
|
|
192
|
+
str.gsub(/\u2503 ([\e\[\d*m]*)\u2501/) { "\u2523\u2501#{ $1 }\u2501" }
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class Kood::CLI < Thor
|
|
2
|
+
|
|
3
|
+
desc "list [OPTIONS] [<LIST-ID>]", "Display and manage lists"
|
|
4
|
+
#
|
|
5
|
+
# Delete a list. If <list-id> is present, the specified list will be deleted.
|
|
6
|
+
method_option :delete, :aliases => '-d', :type => :boolean
|
|
7
|
+
#
|
|
8
|
+
# Copy a list. <list-id> will be copied to <new-list-id>.
|
|
9
|
+
# <list-id> is kept intact and a new one is created with the exact same data.
|
|
10
|
+
method_option :copy, :aliases => '-c', :type => :string
|
|
11
|
+
#
|
|
12
|
+
# Move a list to another board. <list-id> will be moved to <board-id>.
|
|
13
|
+
method_option :move, :aliases => '-m', :type => :string
|
|
14
|
+
def list(list_id = nil)
|
|
15
|
+
Kood::Board.current!.with_context do |current_board|
|
|
16
|
+
# If no arguments and options are specified, the command displays all existing lists
|
|
17
|
+
if list_id.nil? and no_method_options?
|
|
18
|
+
list_existing_lists(current_board)
|
|
19
|
+
|
|
20
|
+
# If the <list-id> argument is present without options, a new list will be created
|
|
21
|
+
elsif no_method_options?
|
|
22
|
+
create_list(current_board, list_id)
|
|
23
|
+
|
|
24
|
+
# Since <list-id> is present, operate on the specified list
|
|
25
|
+
else
|
|
26
|
+
operate_on_list(current_board, list_id)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
map 'lists' => 'list'
|
|
31
|
+
|
|
32
|
+
private
|
|
33
|
+
|
|
34
|
+
def operate_on_list(current_board, list_id)
|
|
35
|
+
list = Kood::List.get!(list_id)
|
|
36
|
+
|
|
37
|
+
if options.copy.present?
|
|
38
|
+
# TODO
|
|
39
|
+
end # The copied list may be deleted or moved now
|
|
40
|
+
|
|
41
|
+
if options.move.present?
|
|
42
|
+
# TODO
|
|
43
|
+
# If the list was moved, it cannot be deleted
|
|
44
|
+
|
|
45
|
+
elsif options.key? 'delete'
|
|
46
|
+
delete_list(current_board, list_id)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def list_existing_lists(current_board)
|
|
51
|
+
error "No lists were found." if current_board.lists.empty?
|
|
52
|
+
puts current_board.list_ids
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def create_list(current_board, list_id)
|
|
56
|
+
list = current_board.lists.create(id: list_id)
|
|
57
|
+
|
|
58
|
+
if list.persisted?
|
|
59
|
+
ok "List created."
|
|
60
|
+
else
|
|
61
|
+
msgs = list.errors.full_messages.join("\n")
|
|
62
|
+
error "#{ msgs }."
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def delete_list(current_board, list_id)
|
|
67
|
+
current_board.lists.destroy(list_id)
|
|
68
|
+
ok "List deleted."
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class Kood::CLI < Thor
|
|
2
|
+
|
|
3
|
+
private
|
|
4
|
+
|
|
5
|
+
# Load third-party commands / plugins
|
|
6
|
+
#
|
|
7
|
+
def self.load_plugins
|
|
8
|
+
program = File.basename $PROGRAM_NAME
|
|
9
|
+
command = ARGV.first
|
|
10
|
+
command = ARGV[1] if command.eql? 'help' and ARGV.length > 1 # Help command
|
|
11
|
+
|
|
12
|
+
if program.eql? 'kood' # File is being imported from the bin and not from the test suite
|
|
13
|
+
unless command.nil? or Kood::CLI.method_defined? command # Check if command is unknown
|
|
14
|
+
begin
|
|
15
|
+
plugin_name = command # The command is the name of the plugin
|
|
16
|
+
|
|
17
|
+
# Require the plugin, which must be accessible and follow the naming convention
|
|
18
|
+
require "kood-plugin-#{ plugin_name }"
|
|
19
|
+
|
|
20
|
+
# Transform plugin name to a valid class name (for example, foo_bar becomes FooBar)
|
|
21
|
+
plugin_class_name = Thor::Util.camel_case(plugin_name)
|
|
22
|
+
|
|
23
|
+
# Get the class and register it (the plugin must extend Thor)
|
|
24
|
+
plugin_class = Kood::Plugin.const_get(plugin_class_name)
|
|
25
|
+
Kood::CLI.register(plugin_class, plugin_name, plugin_name, "Kood plugin")
|
|
26
|
+
rescue LoadError
|
|
27
|
+
# TODO Thor supports partial subcommands and aliases for subcommands. The
|
|
28
|
+
# `method_defined?` condition is not enough. For now, we don't exit here and
|
|
29
|
+
# everything should still work as expected, but this could be improved.
|
|
30
|
+
#
|
|
31
|
+
# puts "Could not find command or plugin \"#{ plugin_name }\"."
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|