jirify 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b3bdf3d78cad93234c987b4f0fe19a1c90909107532c2f6b371dd94078e9574f
4
- data.tar.gz: bb9d83d6a6506ab4e310378656f0750eaded4c8a111d50f14d3664a858d1efa4
3
+ metadata.gz: 07d9648da98d02b0aea77481668a386b9182bdf777ca242ad61c6e5c7e793602
4
+ data.tar.gz: 494f816f3e49f388343949ea025fd615065e895f0f8036801b85f120206197d0
5
5
  SHA512:
6
- metadata.gz: 0fc9f799bcc20fecc4cb44fbb528de7a99fe981b63046379f9d5339ae8c29a64ed9dd598f8a8c3c5894f74ef1494a3172ef2a0cc1fd432628a2704694d34f5fb
7
- data.tar.gz: f4a5c0203a6317822c3a62b5863f4e80c3760c08fa51f0014ab9d59dc68a8e3c301a831cf4dcab20dec23104bb9d71fbaeebc4f2bc3bcef952e05f9e8f277b21
6
+ metadata.gz: 6bff2d97e9fe24848423aa4b3f4108e53bba6953603a04b3fe703c9aeba4ccec242a0f3d5308d3889b7efae71cecc2a119ba3003c186d78b663ec55a6ec4738a
7
+ data.tar.gz: 71c8810f34cd135794a64a492b0510d8a220e7ef9868e3abde4c1a601129c43e66182fbb17a0ad899baabae186a450d3cc11fdb2c43e69fbe91c35de5c2933c9
data/README.md CHANGED
@@ -8,14 +8,17 @@ A simple ruby gem that helps me work with jira
8
8
  ## Installation
9
9
  1. Run `gem install jirify`.
10
10
  1. Execute `jira setup` and go through the setup process.
11
- 1. Optionally source `$HOME/.jirify/jirify.bash_completion.sh` to have autocomplete in bash.
11
+ 1. Optionally source `$HOME/.jirify/jirify.bash_completion.sh` to have
12
+ autocomplete in bash.
12
13
  1. Execute `jira` and `jira <command> help` to learn about available commands.
13
14
 
14
15
  ## Config Explained
15
16
  Currently, the config structure of `jirify` is:
16
17
  - `$HOME/.jirify` folder that contains:
17
18
  - `.jirify` - yaml file generated by `jira setup`
18
- - `jirify.bash_completion.sh` - bash completion script you can source. This is placed here by `jira setup`, so if you don't see it or you want to refresh it, run `jira setup` again.
19
+ - `jirify.bash_completion.sh` - bash completion script you can source.
20
+ This is placed here by `jira setup`, so if you don't see it or you want to
21
+ refresh it, run `jira setup` again.
19
22
  - `.cache` - cache for completion script
20
23
 
21
24
  ### Config file: `$HOME/.jirify/.jirify`
@@ -31,4 +34,18 @@ options:
31
34
  ```
32
35
 
33
36
  ## To Do
34
- - Add ability to define mapping between custom statuses and custom transitions in config.
37
+ - Tests:
38
+ - [ ] Test CLI classes.
39
+ - [ ] Test UI classes.
40
+ - [ ] Test `Models::Issue`.
41
+ - [ ] Test `Models::Transition`, `Models::BaseList` (?).
42
+ - [ ] Test `Config::copy_bash_completion!`.
43
+ - [ ] Refactor Models - `BaseList` doesn't make much sense.
44
+ - [x] ~~Refactor `SprintCell`:~~
45
+ - [x] ~~It should be a more general `IssueRenderer`.~~
46
+ - [x] ~~Use it for `jira issues describe` with additional option to draw border.~~
47
+ - [ ] Adjust `jira issues mine` to terminal width.
48
+ - [ ] Add ability to define mapping between custom statuses and custom
49
+ transitions in config.
50
+ - [ ] Add ability to specify custom filters when listing issues (both
51
+ `jira sprint` and `jira issues`)
@@ -47,14 +47,20 @@ module Jirify
47
47
  desc 'describe [ISSUE]', 'Describes an issue'
48
48
  def describe(issue_id)
49
49
  issue = get_issue_or_exit issue_id
50
- cell = UI::SprintCell.new(issue, IO.console.winsize[1])
50
+
51
+ issue_renderer = UI::IssueRenderer.new(issue)
51
52
 
52
53
  options = {}
54
+
53
55
  options[:url] = true
54
56
  options[:summary] = true
55
57
  options[:assignee] = true
56
-
57
- say cell.to_s(options)
58
+ begin
59
+ say issue_renderer.as_card(options)
60
+ rescue UI::WindowTooNarrow
61
+ say ColorizedString['ERROR: Your terminal window is too narrow to print the sprint table!']
62
+ .white.on_red.bold
63
+ end
58
64
  end
59
65
 
60
66
  #-------------------------#
@@ -214,7 +220,7 @@ module Jirify
214
220
  issue = Models::Issue.find_by_id(issue_id)
215
221
 
216
222
  if issue.nil?
217
- say ColorizedString[' ERROR: Issue not found '].white.on_red.bold
223
+ say ColorizedString['ERROR: Issue not found'].white.on_red.bold
218
224
  exit(0)
219
225
  else
220
226
  issue
@@ -10,15 +10,16 @@ module Jirify
10
10
  verbose = Config.always_verbose || options[:verbose]
11
11
  issues = Models::Sprint.issues_in_current_sprint(options[:mine])
12
12
 
13
- duplicate_options = options.dup
13
+ modified_options = options.dup
14
14
  if verbose
15
- duplicate_options[:assignee] = true
16
- duplicate_options[:url] = true
17
- duplicate_options[:summary] = true
15
+ modified_options[:assignee] = true
16
+ modified_options[:url] = true
17
+ modified_options[:summary] = true
18
18
  end
19
- say UI::SprintTable.new(issues).to_table(duplicate_options)
20
- rescue UI::WindowTooNarrow, StandardError
21
- say ColorizedString[' ERROR: Your terminal window is too narrow to print the sprint table! ']
19
+
20
+ say UI::SprintTable.new(issues).to_table(modified_options)
21
+ rescue UI::WindowTooNarrow
22
+ say ColorizedString['ERROR: Your terminal window is too narrow to print the sprint table!']
22
23
  .white.on_red.bold
23
24
  end
24
25
  end
data/lib/jirify/config.rb CHANGED
@@ -40,7 +40,7 @@ module Jirify
40
40
 
41
41
  def verbose=(value)
42
42
  unless initialized?
43
- puts ColorizedString[' ERROR: You must initialize Jirify first! '].white.on_red.bold
43
+ puts ColorizedString['ERROR: You must initialize Jirify first!'].white.on_red.bold
44
44
  exit(0)
45
45
  end
46
46
 
@@ -51,7 +51,7 @@ module Jirify
51
51
 
52
52
  def options
53
53
  unless initialized?
54
- puts ColorizedString[' ERROR: You must initialize Jirify first! '].white.on_red.bold
54
+ puts ColorizedString['ERROR: You must initialize Jirify first!'].white.on_red.bold
55
55
  exit(0)
56
56
  end
57
57
 
@@ -0,0 +1,100 @@
1
+ module Jirify
2
+ module UI
3
+ class WindowTooNarrow < StandardError; end
4
+
5
+ class IssueRenderer
6
+ attr_reader :issue
7
+
8
+ def initialize(issue)
9
+ @issue = issue
10
+ end
11
+
12
+ def as_table_cell(options)
13
+ options[:max_length] ||= IO.console.winsize[1] - 4
14
+ options[:summary] ||= false
15
+ options[:url] ||= false
16
+ options[:assignee] ||= false
17
+
18
+ raise UI::WindowTooNarrow, 'The terminal window is too narrow.' if options[:max_length] <= key.size
19
+
20
+ row = ''
21
+ row << "#{ColorizedString[key].bold}"
22
+ row << summary_line(options) if show_summary?(options)
23
+ row << url_line if show_url?(options)
24
+ row << assignee_line(options) if show_assignee?(options)
25
+ row
26
+ end
27
+
28
+ def as_card(options)
29
+ card = as_table_cell(options)
30
+ Terminal::Table.new(rows: [[card]])
31
+ rescue RuntimeError
32
+ raise UI::WindowTooNarrow, 'The terminal window is too narrow.'
33
+ end
34
+
35
+ protected
36
+
37
+ def key
38
+ issue.key
39
+ end
40
+
41
+ def summary
42
+ issue.summary
43
+ end
44
+
45
+ def show_assignee?(options)
46
+ options[:assignee] && !options[:mine]
47
+ end
48
+
49
+ def assignee
50
+ if issue.assignee.nil?
51
+ 'Unassigned'
52
+ else
53
+ issue.assignee.displayName
54
+ end
55
+ end
56
+
57
+ def assignee_line(options)
58
+ max_length = options[:max_length]
59
+ assignee_line = assignee
60
+ assignee_line = "#{assignee[0...max_length - 3]}..." if assignee_line.size >= max_length
61
+
62
+ "\n#{assignee_line.magenta}"
63
+ end
64
+
65
+ def show_url?(options)
66
+ url.size <= options[:max_length] && options[:url]
67
+ end
68
+
69
+ def url
70
+ "#{Config.issue_browse_url}#{issue.key}"
71
+ end
72
+
73
+ def url_line
74
+ "\n#{ColorizedString[url].blue.underline}"
75
+ end
76
+
77
+ def show_summary?(options)
78
+ options[:summary]
79
+ end
80
+
81
+ def summary_line(options)
82
+ "\n#{wrap(summary.strip, options[:max_length])}"
83
+ end
84
+
85
+ def wrap(string, columns, character = "\n")
86
+ return '' if string.nil?
87
+
88
+ start_pos = columns
89
+ while start_pos < string.length
90
+ space = string.rindex(' ', start_pos) || start_pos - 1
91
+ string.slice!(space)
92
+ string.insert(space, character)
93
+ start_pos = space + columns + 1
94
+ end
95
+
96
+ string
97
+ end
98
+ end
99
+ end
100
+ end
@@ -9,11 +9,18 @@ module Jirify
9
9
  end
10
10
 
11
11
  def to_table(options)
12
+ return '' if issues.empty?
13
+
14
+ # group issues by status name
12
15
  grouped_issues = issues.group_by do |issue|
13
16
  options[:all_columns] ? issue.status.name : issue.status.statusCategory['name']
14
17
  end
15
18
 
16
- grouped_issues_as_table(grouped_issues, options)
19
+ Terminal::Table.new(
20
+ headings: headings(grouped_issues),
21
+ rows: issues_as_rows(grouped_issues.values, options),
22
+ style: table_style
23
+ )
17
24
  end
18
25
 
19
26
  protected
@@ -26,63 +33,81 @@ module Jirify
26
33
  { width: terminal_width, border_x: '-', border_i: 'x' }
27
34
  end
28
35
 
36
+ def get_max_cell_length(grouped_issues)
37
+ col_padding_per_row = grouped_issues.size * 3 + 1
38
+ (terminal_width - col_padding_per_row) / grouped_issues.size
39
+ end
40
+
29
41
  def headings(grouped_issues)
30
- sorted_headings = grouped_issues.keys.sort_by do |name|
42
+ max_cell_length = get_max_cell_length(grouped_issues)
43
+
44
+ sorted_headings = sort_headings(grouped_issues.keys)
45
+ sorted_headings.map do |heading|
46
+ original_heading = heading
47
+ heading = fit_heading_to_cell(heading, max_cell_length)
48
+
49
+ case original_heading
50
+ when Config.statuses['todo'] then ColorizedString[heading].white.on_black.bold
51
+ when Config.statuses['in_progress'] then ColorizedString[heading].white.on_blue.bold
52
+ when Config.statuses['in_review'] then ColorizedString[heading].white.on_yellow.bold
53
+ when Config.statuses['blocked'] then ColorizedString[heading].white.on_red.bold
54
+ when Config.statuses['done'] then ColorizedString[heading].white.on_green.bold
55
+ else ColorizedString[heading].white.on_green.bold
56
+ end
57
+ end
58
+ end
59
+
60
+ def sort_headings(heading_names)
61
+ heading_names.sort_by! do |name|
31
62
  status_index = Models::Status.status_order.index(name)
32
63
  if status_index.nil?
64
+ # If status is not defined in order, put it at the end
33
65
  grouped_issues.keys.length
34
66
  else
35
67
  status_index
36
68
  end
37
69
  end
38
-
39
- sorted_headings.map! do |name|
40
- case name
41
- when Config.statuses['todo'] then ColorizedString[name].white.on_black.bold
42
- when Config.statuses['in_progress'] then ColorizedString[name].white.on_blue.bold
43
- when Config.statuses['in_review'] then ColorizedString[name].white.on_yellow.bold
44
- when Config.statuses['blocked'] then ColorizedString[name].white.on_red.bold
45
- when Config.statuses['done'] then ColorizedString[name].white.on_green.bold
46
- else ColorizedString[name].white.on_green.bold
47
- end
48
- end
49
70
  end
50
71
 
51
- def grouped_issues_as_table(grouped_issues, options)
52
- transposed = transpose(grouped_issues.values, options)
53
- return nil if transposed.empty?
54
-
55
- Terminal::Table.new(
56
- headings: headings(grouped_issues),
57
- rows: transposed,
58
- style: table_style
59
- )
72
+ def fit_heading_to_cell(name, max_cell_length)
73
+ if name.length >= max_cell_length
74
+ # If the heading name is longer than the max length, add ellipsis.
75
+ "#{name[0...max_cell_length - 3]}..."
76
+ else
77
+ # Add spaces around the heading name to center it.
78
+ buffer = max_cell_length - name.length
79
+ left = ' ' * (buffer / 2)
80
+ right = ' ' * (buffer / 2)
81
+ right += ' ' if buffer.odd?
82
+
83
+ "#{left}#{name}#{right}"
84
+ end
60
85
  end
61
86
 
62
- def transpose(grouped_issues, options)
63
- col_padding_per_row = grouped_issues.size * 4
64
- max_cell_length = (terminal_width - col_padding_per_row) / grouped_issues.size
87
+ def issues_as_rows(grouped_issues, options)
88
+ options[:max_length] = get_max_cell_length(grouped_issues)
65
89
 
66
- # workaround - not all groups have the same size
90
+ # Workaround - not all groups have the same size.
67
91
  l = grouped_issues.map(&:length).max
68
92
  grouped_as_array = grouped_issues.map { |e| e.values_at(0...l) }
69
93
 
70
- grouped_as_array.sort_by! do |row|
71
- issue = row.find { |i| !i.nil? }
72
- issue.status
73
- end
74
-
94
+ # Sort columns by status and transpose them to become rows.
95
+ grouped_as_array.sort_by! { |row| row.first.status }
75
96
  transposed = grouped_as_array.transpose
76
97
 
98
+ # Map every Issue in every row to its display representation.
77
99
  transposed.map! do |row|
78
100
  row.map do |issue|
79
101
  next if issue.nil?
80
- SprintCell.new(issue, max_cell_length).to_s(options)
102
+ IssueRenderer.new(issue).as_table_cell(options)
81
103
  end
82
104
  end
83
105
 
106
+ # add separators after each row
84
107
  transposed = transposed.zip([:separator] * transposed.size).flatten(1)
108
+ # remove the separator after the last row, it's not needed
85
109
  transposed.pop
110
+
86
111
  transposed
87
112
  end
88
113
  end
@@ -1,3 +1,3 @@
1
1
  module Jirify
2
- VERSION = '0.2.1'.freeze
2
+ VERSION = '0.2.2'.freeze
3
3
  end
data/lib/jirify.rb CHANGED
@@ -18,7 +18,7 @@ require 'jirify/models/sprint'
18
18
  require 'jirify/models/project'
19
19
  require 'jirify/models/project_list'
20
20
 
21
- require 'jirify/ui/sprint_cell'
21
+ require 'jirify/ui/issue_renderer'
22
22
  require 'jirify/ui/sprint_table'
23
23
 
24
24
  require 'jirify/cli/setup'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jirify
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Georgi Gardev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-07-08 00:00:00.000000000 Z
11
+ date: 2018-07-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -166,26 +166,6 @@ dependencies:
166
166
  - - "~>"
167
167
  - !ruby/object:Gem::Version
168
168
  version: '12.1'
169
- - !ruby/object:Gem::Dependency
170
- name: rake-release
171
- requirement: !ruby/object:Gem::Requirement
172
- requirements:
173
- - - "~>"
174
- - !ruby/object:Gem::Version
175
- version: '1.0'
176
- - - ">="
177
- - !ruby/object:Gem::Version
178
- version: 1.0.1
179
- type: :development
180
- prerelease: false
181
- version_requirements: !ruby/object:Gem::Requirement
182
- requirements:
183
- - - "~>"
184
- - !ruby/object:Gem::Version
185
- version: '1.0'
186
- - - ">="
187
- - !ruby/object:Gem::Version
188
- version: 1.0.1
189
169
  - !ruby/object:Gem::Dependency
190
170
  name: rspec
191
171
  requirement: !ruby/object:Gem::Requirement
@@ -280,7 +260,7 @@ files:
280
260
  - lib/jirify/models/transition.rb
281
261
  - lib/jirify/models/transition_list.rb
282
262
  - lib/jirify/monkey_patches/jira_issue.rb
283
- - lib/jirify/ui/sprint_cell.rb
263
+ - lib/jirify/ui/issue_renderer.rb
284
264
  - lib/jirify/ui/sprint_table.rb
285
265
  - lib/jirify/version.rb
286
266
  homepage: https://github.com/GeorgeSG/jirify
@@ -1,84 +0,0 @@
1
- module Jirify
2
- module UI
3
- class WindowTooNarrow < StandardError; end
4
-
5
- class SprintCell
6
- attr_reader :issue, :max_cell_length
7
-
8
- def initialize(issue, max_cell_length)
9
- @issue = issue
10
- @max_cell_length = max_cell_length
11
- end
12
-
13
- def to_s(options)
14
- raise UI::WindowTooNarrow, 'The terminal window is too narrow.' if max_cell_length <= key.size
15
-
16
- row = ''
17
- row << key_and_summary_lines(options, max_cell_length)
18
- row << "\n#{ColorizedString[url].blue.underline}" if display_url?(options)
19
- row << assignee_line(max_cell_length) if show_assignee?(options)
20
- row
21
- end
22
-
23
- protected
24
-
25
- def key
26
- issue.key
27
- end
28
-
29
- def summary
30
- issue.summary
31
- end
32
-
33
- def show_assignee?(options)
34
- options[:assignee] && !options[:mine]
35
- end
36
-
37
- def assignee
38
- if issue.assignee.nil?
39
- 'Unassigned'
40
- else
41
- issue.assignee.displayName
42
- end
43
- end
44
-
45
- def url
46
- "#{Config.issue_browse_url}#{issue.key}"
47
- end
48
-
49
- def display_url?(options)
50
- url.size <= max_cell_length && options[:url]
51
- end
52
-
53
- def wrap(string, columns, character = "\n")
54
- return '' if string.nil?
55
-
56
- start_pos = columns
57
- while start_pos < string.length
58
- space = string.rindex(' ', start_pos) || start_pos + 1
59
- string.slice!(space)
60
- string.insert(space, character)
61
- start_pos = space + columns + 1
62
- end
63
-
64
- string
65
- end
66
-
67
- def key_and_summary_lines(options, max_cell_length)
68
- bold_key = ColorizedString[key].bold
69
- return bold_key unless options[:summary]
70
-
71
- row = "#{bold_key}\n"
72
- row << wrap(summary.strip, max_cell_length)
73
- row
74
- end
75
-
76
- def assignee_line(max_cell_length)
77
- assignee_line = assignee
78
- assignee_line = "#{assignee[0...max_cell_length - 3]}..." if assignee_line.size >= max_cell_length
79
-
80
- "\n#{assignee_line.magenta}"
81
- end
82
- end
83
- end
84
- end