jirify 0.2.1 → 0.2.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.
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