jirify 0.1.6 → 0.1.7

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: 30fe694d532ff7f42489b0e15e9c5e3baf4f3ec00f82b14a2132621bd6132bd0
4
- data.tar.gz: a01941f15a19def128da94ea49d05fd9aeace88b283c0a0c4be108ebb54874bf
3
+ metadata.gz: eb5f0f6dfffdfe5bf7ba883f13791a579a5defaaa5887cb210bb2c8d4022dbe6
4
+ data.tar.gz: 44f3bff0804ebe0a796d94417d690a3babb17fab8b3d3403a7882195defe8b67
5
5
  SHA512:
6
- metadata.gz: 4bcd7b93913fdf79bec9914d42d280c0aae9f1a237e1d7eb47d75b153181d0b26af7b61c05094d816ffd7cbcbf6090c51c45277146b874e25c69faa1a411e10a
7
- data.tar.gz: 17b11d2fff2083801a49149c615264261825bba23255cdce8ee2fcc9ce01b630a30644b0936e126be549414e65fbd1e4eece9b1d262e2f8921567aed1ca9caad
6
+ metadata.gz: 6d5069a25d999001f6e63a273d2ea32a3bd89b96b8586f77562cd4c16cd87806a892953a17e8656f460ed3b15c53ff58621a60120d95c689891f1eeecf83b85a
7
+ data.tar.gz: d4a806b94ab345ba3d89feb15f551ff8f3801a4713004331fc143b36b5abbd80ab24f1225b25561e7962c9eef8b72850413191898dd4a908e3c2b885688184a6
data/README.md CHANGED
@@ -1,10 +1,34 @@
1
1
  # jirify
2
+ [![Gem Version](https://badge.fury.io/rb/jirify.svg)](https://badge.fury.io/rb/jirify)
3
+ [![Build Status](https://travis-ci.org/GeorgeSG/jirify.svg?branch=master)](https://travis-ci.org/GeorgeSG/jirify)
4
+ [![Coverage Status](https://coveralls.io/repos/github/GeorgeSG/jirify/badge.svg?branch=master)](https://coveralls.io/github/GeorgeSG/jirify?branch=master)
5
+
2
6
  A simple ruby gem that helps me work with jira
3
7
 
4
- # How to use
8
+ ## Installation
5
9
  1. Run `gem install jirify`.
6
- 1. Execute `jira setup` and go through the setup process OR if you had the previous `config.yml` or `.jirify` file you can just move it to the `~/.jirify/` folder.
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.
7
12
  1. Execute `jira` and `jira <command> help` to learn about available commands.
8
13
 
9
- # To Do
14
+ ## Config Explained
15
+ Currently, the config structure of `jirify` is:
16
+ - `$HOME/.jirify` folder that contains:
17
+ - `.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
+ - `.cache` - cache for completion script
20
+
21
+ ### Config file: `$HOME/.jirify/.jirify`
22
+ ```yaml
23
+ options:
24
+ username: <atlassian username (email)>
25
+ token: <token generated from https://id.atlassian.com>
26
+ site: <JIRA url>
27
+ project: <JIRA project key>
28
+ filter_by_labels:
29
+ - <label to filter by when displaying sprint>
30
+ verbose: <force jirify to always be verbose>
31
+ ```
32
+
33
+ ## To Do
10
34
  - Add ability to define mapping between custom statuses and custom transitions in config.
@@ -27,12 +27,12 @@ module Jirify
27
27
  desc: 'Show only issues with the specified statuses'
28
28
  def mine
29
29
  statuses = build_issue_statuses(options)
30
- issues = Jirify::Issue.list_mine(statuses, options[:all])
30
+ issues = Models::Issue.list_mine(statuses, options[:all])
31
31
  issues.each do |issue|
32
32
  if options[:key_only]
33
- puts issue.key
33
+ say issue.key
34
34
  else
35
- issue.print Config.always_verbose || options[:verbose]
35
+ say issue.to_s Config.always_verbose || options[:verbose]
36
36
  end
37
37
  end
38
38
  end
@@ -53,9 +53,9 @@ module Jirify
53
53
  issue = get_issue_or_exit issue_id
54
54
 
55
55
  if issue.assignee.nil?
56
- puts 'Unassigned'.yellow
56
+ say 'Unassigned'.yellow
57
57
  else
58
- puts issue.assignee.name
58
+ say issue.assignee.name
59
59
  end
60
60
  end
61
61
 
@@ -64,11 +64,11 @@ module Jirify
64
64
  issue = get_issue_or_exit issue_id
65
65
 
66
66
  if issue.assignee.nil?
67
- puts 'Issue already unassigned'.yellow
67
+ say 'Issue already unassigned'.yellow
68
68
  exit(0)
69
69
  end
70
70
 
71
- puts "Previous assignee: #{issue.assignee.name}. Unassigning..."
71
+ say "Previous assignee: #{issue.assignee.name}. Unassigning..."
72
72
  issue.unassign!
73
73
  end
74
74
 
@@ -76,7 +76,7 @@ module Jirify
76
76
  def take(issue_id)
77
77
  issue = get_issue_or_exit issue_id
78
78
 
79
- puts "Assigning #{issue.key} to #{Config.username}..."
79
+ say "Assigning #{issue.key} to #{Config.username}..."
80
80
  issue.assign_to_me!
81
81
  end
82
82
 
@@ -88,28 +88,28 @@ module Jirify
88
88
  def status(issue_id)
89
89
  issue = get_issue_or_exit issue_id
90
90
 
91
- puts issue.status.name
91
+ say "Status: #{issue.status.name}"
92
92
  end
93
93
 
94
94
  desc 'transitions [ISSUE]', 'Display available transitions'
95
95
  def transitions(issue_id)
96
96
  issue = get_issue_or_exit issue_id
97
97
 
98
- puts 'Available transitions:'
99
- puts issue.transitions.names
98
+ say 'Available transitions:'
99
+ issue.transitions.names.each { |name| say name }
100
100
  end
101
101
 
102
102
  desc 'transition [ISSUE] [TRANSITION]', 'Manually perform a transition'
103
103
  def transition(issue_id, transition_name)
104
104
  issue = get_issue_or_exit issue_id
105
- transition = issue.transitions.list.find { |t| t.name == transition_name }
105
+ transition = issue.transitions.find_by_name(transition_name)
106
106
 
107
107
  if transition.nil?
108
- puts "ERROR: Issue can't transition to #{transition_name}".red
108
+ say "ERROR: Issue can't transition to #{transition_name}".red
109
109
  exit(0)
110
110
  end
111
111
 
112
- puts "Transitioning #{issue.key} with #{transition_name}...".green
112
+ say "Transitioning #{issue.key} with #{transition_name}...".green
113
113
  issue.transition! transition
114
114
  end
115
115
 
@@ -129,10 +129,10 @@ module Jirify
129
129
  check_assigned_to_self issue
130
130
 
131
131
  if issue.blocked?
132
- puts 'Unblocking issue...'
132
+ say 'Unblocking issue...'
133
133
  issue.unblock!
134
134
  else
135
- puts 'Issue wasn\'t blocked anyway :)'.green
135
+ say 'Issue wasn\'t blocked anyway :)'.green
136
136
  end
137
137
  end
138
138
 
@@ -198,10 +198,10 @@ module Jirify
198
198
  protected
199
199
 
200
200
  def get_issue_or_exit(issue_id)
201
- issue = Jirify::Issue.find_by_id(issue_id)
201
+ issue = Models::Issue.find_by_id(issue_id)
202
202
 
203
203
  if issue.nil?
204
- puts 'ERROR: Issue not found'.red
204
+ say 'ERROR: Issue not found'.red
205
205
  exit(0)
206
206
  else
207
207
  issue
@@ -209,13 +209,13 @@ module Jirify
209
209
  end
210
210
 
211
211
  def check_assigned_to_self(issue)
212
- unless issue.mine?
213
- exit(0) unless yes? 'WARNING! This issue is not assigned to you!'\
214
- ' Are you sure you want to continue? [Y/n]:'.yellow
215
- end
212
+ return if issue.mine?
213
+
214
+ exit(0) unless yes? 'WARNING! This issue is not assigned to you!'\
215
+ ' Are you sure you want to continue? [Y/n]:'.yellow
216
216
  end
217
217
 
218
- def build_issue_statuses(options)
218
+ def build_issue_statuses(options) # rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity
219
219
  if options[:status]
220
220
  statuses = [options[:status]]
221
221
  else
@@ -1,12 +1,12 @@
1
1
  module Jirify
2
2
  module Subcommands
3
3
  class Projects < Thor
4
+ default_task :list
5
+
4
6
  desc 'list', 'List all projects'
5
7
  def list
6
- puts Jirify::Project.all.map(&:name)
8
+ Models::Project.all.map(&:name).each { |name| say name }
7
9
  end
8
-
9
- default_task :list
10
10
  end
11
11
  end
12
12
  end
@@ -33,7 +33,7 @@ module Jirify
33
33
 
34
34
  Config.write(options)
35
35
 
36
- say "Done!"
36
+ say 'Done!'
37
37
  say "If you want to enable bash completion, source #{Config.config_folder}/jirify.bash_completion.sh"
38
38
  end
39
39
 
@@ -4,9 +4,12 @@ module Jirify
4
4
  method_option :mine, type: :boolean, aliases: '-m', desc: 'Show only issues assigned to me'
5
5
  method_option :all_columns, type: :boolean, aliases: '-a', desc: 'Show all columns'
6
6
  def sprint
7
- Jirify::Sprint.current(Config.always_verbose || options[:verbose],
8
- options[:all_columns],
9
- options[:mine])
7
+ verbose = Config.always_verbose || options[:verbose]
8
+ issues = Models::Sprint.issues_in_current_sprint(options[:mine])
9
+
10
+ say UI::SprintTable.new(issues).to_table(options[:all_columns], verbose)
11
+ rescue UI::WindowTooNarrow
12
+ say 'ERROR: Your terminal window is too narrow to print the sprint table!'.red
10
13
  end
11
14
  end
12
15
  end
data/lib/jirify/config.rb CHANGED
@@ -22,9 +22,9 @@ module Jirify
22
22
  end
23
23
 
24
24
  def initialize!
25
- FileUtils::mkdir_p CONFIG_FOLDER
26
- FileUtils::touch CONFIG_FILE
27
- FileUtils::cp "#{File.expand_path('..', File.dirname(__dir__))}/jirify.bash_completion.sh", CONFIG_FOLDER
25
+ FileUtils.mkdir_p CONFIG_FOLDER
26
+ FileUtils.touch CONFIG_FILE
27
+ FileUtils.cp "#{File.expand_path('..', File.dirname(__dir__))}/jirify.bash_completion.sh", CONFIG_FOLDER
28
28
  end
29
29
 
30
30
  def write(config)
@@ -1,28 +1,30 @@
1
1
  module Jirify
2
- class Base
3
- def initialize(entity)
4
- @entity = entity
5
- end
6
-
7
- def method_missing(method, *args, &_block)
8
- if @entity.respond_to? method
9
- @entity.send method, *args
10
- else
11
- super
2
+ module Models
3
+ class Base
4
+ def initialize(entity)
5
+ @entity = entity
12
6
  end
13
- end
14
7
 
15
- def respond_to_missing?(method, *)
16
- @entity.respond_to? method
17
- end
8
+ def method_missing(method, *args, &_block)
9
+ if @entity.respond_to? method
10
+ @entity.send method, *args
11
+ else
12
+ super
13
+ end
14
+ end
18
15
 
19
- class << self
20
- def client
21
- @client ||= JIRA::Client.new(Config.client_options)
16
+ def respond_to_missing?(method, *)
17
+ @entity.respond_to? method
22
18
  end
23
19
 
24
- def project
25
- @project ||= Config.options['project']
20
+ class << self
21
+ def client
22
+ @client ||= JIRA::Client.new(Config.client_options)
23
+ end
24
+
25
+ def project
26
+ @project ||= Config.options['project']
27
+ end
26
28
  end
27
29
  end
28
30
  end
@@ -1,90 +1,92 @@
1
1
  module Jirify
2
- class Issue < Base
3
- class InvalidTransitionError < StandardError; end
2
+ module Models
3
+ class Issue < Base
4
+ class InvalidTransitionError < StandardError; end
4
5
 
5
- def mine?
6
- !assignee.nil? && assignee.emailAddress == Config.username
7
- end
6
+ def mine?
7
+ !assignee.nil? && assignee.emailAddress == Config.username
8
+ end
8
9
 
9
- def assign_to_me!
10
- @entity.assign_to!(Config.username.split('@')[0])
11
- end
10
+ def assign_to_me!
11
+ @entity.assign_to!(Config.username.split('@')[0])
12
+ end
12
13
 
13
- def unassign!
14
- @entity.assign_to!(nil)
15
- end
14
+ def unassign!
15
+ @entity.assign_to!(nil)
16
+ end
16
17
 
17
- def status
18
- @status ||= Jirify::Status.new @entity.status
19
- end
18
+ def status
19
+ @status ||= Status.new @entity.status
20
+ end
20
21
 
21
- def status?(status_name)
22
- status_name = status_name.to_s if status_name.is_a? Symbol
23
- status.name == status_name
24
- end
22
+ def status?(status_name)
23
+ status_name = status_name.to_s if status_name.is_a? Symbol
24
+ status.name == status_name
25
+ end
25
26
 
26
- Config.statuses.keys.each do |status_key|
27
- define_method "#{status_key.to_sym}?" do
28
- status.name == Config.statuses[status_key]
27
+ Config.statuses.keys.each do |status_key|
28
+ define_method "#{status_key.to_sym}?" do
29
+ status.name == Config.statuses[status_key]
30
+ end
29
31
  end
30
- end
31
32
 
32
- def transitions(reload = false)
33
- if reload
34
- @transitions = Jirify::TransitionList.all @entity
35
- else
36
- @transitions ||= Jirify::TransitionList.all @entity
33
+ def transitions(reload = false)
34
+ if reload
35
+ @transitions = TransitionList.all @entity
36
+ else
37
+ @transitions ||= TransitionList.all @entity
38
+ end
37
39
  end
38
- end
39
40
 
40
- Config.transitions.keys.each do |transition_name|
41
- define_method "#{transition_name}!".to_sym do
42
- transition = transitions(true).send(transition_name.to_sym)
41
+ Config.transitions.keys.each do |transition_name|
42
+ define_method "#{transition_name}!".to_sym do
43
+ transition = transitions(true).send(transition_name.to_sym)
43
44
 
44
- if transition.nil?
45
- puts "ERROR: Issue can't be transitioned with \"#{transition_name}\"".red
46
- exit(0)
47
- end
45
+ if transition.nil?
46
+ puts "ERROR: Issue can't be transitioned with \"#{transition_name}\"".red
47
+ exit(0)
48
+ end
48
49
 
49
- puts "Transitioning #{key} with \"#{transition_name}\"...".green
50
- @entity.transition! transition
50
+ puts "Transitioning #{key} with \"#{transition_name}\"...".green
51
+ @entity.transition! transition
52
+ end
51
53
  end
52
- end
53
54
 
54
- def print(verbose)
55
- url = "#{Config.issue_browse_url}#{key}".blue
55
+ def to_s(verbose)
56
+ url = "#{Config.issue_browse_url}#{key}".blue
56
57
 
57
- if verbose
58
- puts "#{status.pretty_name} #{key.ljust(7)}: #{summary} (#{url})"
59
- else
60
- puts "#{key.ljust(7)}: (#{url})"
58
+ if verbose
59
+ "#{status.pretty_name} #{key.ljust(7)}: #{summary} (#{url})"
60
+ else
61
+ "#{key.ljust(7)}: (#{url})"
62
+ end
61
63
  end
62
- end
63
64
 
64
- class << self
65
- def list_mine(statuses = [], all = false)
66
- my_issues = find_mine(all).sort_by { |issue| issue.status.name }
65
+ class << self
66
+ def list_mine(statuses = [], all = false)
67
+ my_issues = find_mine(all).sort_by { |issue| issue.status.name }
67
68
 
68
- my_issues.select do |issue|
69
- statuses.empty? || statuses.any? { |status| issue.status? status }
69
+ my_issues.select do |issue|
70
+ statuses.empty? || statuses.any? { |status| issue.status? status }
71
+ end
70
72
  end
71
- end
72
73
 
73
- def find_mine(all)
74
- client.Issue.jql(my_issues_jql(all)).map { |issue| Issue.new issue }
75
- end
74
+ def find_mine(all)
75
+ client.Issue.jql(my_issues_jql(all)).map { |issue| Issue.new issue }
76
+ end
76
77
 
77
- def find_by_id(issue_id)
78
- Issue.new client.Issue.find(issue_id)
79
- rescue StandardError
80
- nil
81
- end
78
+ def find_by_id(issue_id)
79
+ Issue.new client.Issue.find(issue_id)
80
+ rescue StandardError
81
+ nil
82
+ end
82
83
 
83
- protected
84
+ protected
84
85
 
85
- def my_issues_jql(all_issues)
86
- all_clause = 'AND sprint in openSprints()' unless all_issues
87
- "project='#{project}' #{all_clause} AND assignee='#{Config.username}'"
86
+ def my_issues_jql(all_issues)
87
+ all_clause = 'AND sprint in openSprints()' unless all_issues
88
+ "project='#{project}' #{all_clause} AND assignee='#{Config.username}'"
89
+ end
88
90
  end
89
91
  end
90
92
  end
@@ -1,8 +1,10 @@
1
1
  module Jirify
2
- class Project < Base
3
- class << self
4
- def all
5
- client.Project.all.map { |project| Project.new project }
2
+ module Models
3
+ class Project < Base
4
+ class << self
5
+ def all
6
+ client.Project.all.map { |project| Project.new project }
7
+ end
6
8
  end
7
9
  end
8
10
  end
@@ -1,53 +1,24 @@
1
- require 'terminal-table'
2
- require 'json'
3
-
4
1
  module Jirify
5
- class Sprint < Base
6
- class << self
7
- def current(verbose = false, all_columns = false, only_mine = false)
8
- issues = client.Issue.jql current_sprint_jql(only_mine), max_results: 200
9
-
10
- grouped_issues = issues.group_by do |issue|
11
- all_columns ? issue.status.name : issue.status.statusCategory['name']
2
+ module Models
3
+ class Sprint < Base
4
+ class << self
5
+ def issues_in_current_sprint(only_mine = false, max_results = 200)
6
+ issues = client.Issue.jql current_sprint_jql(only_mine), max_results: max_results
7
+ issues.map { |issue| Issue.new issue }
12
8
  end
13
9
 
14
- print_grouped_issues_as_table(grouped_issues, verbose)
15
- end
16
-
17
- protected
18
-
19
- def current_sprint_jql(only_mine)
20
- labels = Config.options['filter_by_labels']
21
- labels = labels.join(', ') if labels
10
+ protected
22
11
 
23
- labels_clause = "AND labels in (#{labels})" if labels
24
- mine_clause = "AND assignee='#{Config.username}'" if only_mine
25
- sprint_clause = 'AND sprint in openSprints()'
12
+ def current_sprint_jql(only_mine)
13
+ labels = Config.options['filter_by_labels']
14
+ labels = labels.join(', ') if labels
26
15
 
27
- "project='#{project}' #{sprint_clause} #{labels_clause} #{mine_clause}"
28
- end
29
-
30
- def print_grouped_issues_as_table(grouped_issues, verbose)
31
- all_groups = grouped_issues.values
32
-
33
- l = all_groups.map(&:length).max
34
- transposed = all_groups.map { |e| e.values_at(0...l) }.transpose
16
+ labels_clause = "AND labels in (#{labels})" if labels
17
+ mine_clause = "AND assignee='#{Config.username}'" if only_mine
18
+ sprint_clause = 'AND sprint in openSprints()'
35
19
 
36
- transposed = transposed.map do |row|
37
- row.map do |issue|
38
- if issue
39
- key = issue.key.ljust(7)
40
- url = "(#{Config.issue_browse_url}/#{issue.key})".blue
41
- summary = "#{issue.summary[0, 35]}...\n" if verbose
42
-
43
- "#{key}: #{summary}#{url}"
44
- else
45
- ''
46
- end
47
- end
20
+ "project='#{project}' #{sprint_clause} #{labels_clause} #{mine_clause}"
48
21
  end
49
-
50
- puts Terminal::Table.new headings: grouped_issues.keys, rows: transposed unless transposed.empty?
51
22
  end
52
23
  end
53
24
  end
@@ -1,48 +1,47 @@
1
1
  module Jirify
2
- class Status < Base
3
- def pretty_name
4
- justified = "(#{name})".rjust(longest_status_name + 2)
5
- case name
6
- when Config.statuses['blocked'] then justified.red
7
- when Config.statuses['done'] then justified.green
8
- when Config.statuses['in_progress'] then justified.blue
9
- when Config.statuses['in_review'] then justified.yellow
10
- when Config.statuses['todo'] then justified.black
11
- else justified
2
+ module Models
3
+ class Status < Base
4
+ def pretty_name
5
+ justified = "(#{name})".rjust(longest_status_name + 2)
6
+ case name
7
+ when Config.statuses['blocked'] then justified.red
8
+ when Config.statuses['done'] then justified.green
9
+ when Config.statuses['in_progress'] then justified.blue
10
+ when Config.statuses['in_review'] then justified.yellow
11
+ when Config.statuses['todo'] then justified.black
12
+ else justified
13
+ end
12
14
  end
13
- end
14
-
15
- protected
16
-
17
- def longest_status_name
18
- Config.statuses.values.map(&:length).max
19
- end
20
15
 
21
- class << self
22
- def all
23
- @all ||= client.Status.all
24
- end
16
+ def <=>(other)
17
+ this_index = Status.status_order.index(name)
18
+ other_index = Status.status_order.index(other.name)
19
+ return 1 if other_index.nil?
20
+ return 0 if this_index.nil?
25
21
 
26
- def to_do
27
- @to_do ||= find_by_name Config.statuses['todo']
22
+ this_index - other_index
28
23
  end
29
24
 
30
- def in_progress
31
- @in_progress ||= find_by_name Config.statuses['in_progress']
32
- end
33
-
34
- def in_review
35
- @in_review ||= find_by_name Config.statuses['in_review']
36
- end
25
+ protected
37
26
 
38
- def closed
39
- @closed ||= find_by_name Config.statuses['done']
27
+ def longest_status_name
28
+ Config.statuses.values.map(&:length).max
40
29
  end
41
30
 
42
- protected
43
-
44
- def find_by_name(status_name)
45
- all.select { |status| status.name == status_name }
31
+ class << self
32
+ def all
33
+ client.Status.all.map { |status| Status.new status }
34
+ end
35
+
36
+ def status_order
37
+ [
38
+ Config.statuses['blocked'],
39
+ Config.statuses['todo'],
40
+ Config.statuses['in_progress'],
41
+ Config.statuses['in_review'],
42
+ Config.statuses['done']
43
+ ]
44
+ end
46
45
  end
47
46
  end
48
47
  end
@@ -1,28 +1,30 @@
1
1
  module Jirify
2
- class TransitionList < Base
3
- attr_accessor :list
2
+ module Models
3
+ class TransitionList < Base
4
+ attr_accessor :list
4
5
 
5
- def initialize(list)
6
- @list = list
7
- end
8
-
9
- def names
10
- @list.map(&:name)
11
- end
6
+ def initialize(list)
7
+ @list = list
8
+ end
12
9
 
13
- Config.transitions.keys.each do |transition_name|
14
- define_method(transition_name.to_sym) { find_by_name transition_name }
15
- end
10
+ def find_by_name(name)
11
+ @list.find { |transition| transition.name == name }
12
+ end
16
13
 
17
- protected
14
+ def names
15
+ @list.map(&:name)
16
+ end
18
17
 
19
- def find_by_name(name)
20
- @list.find { |transition| transition.name == Config.transitions[name] }
21
- end
18
+ Config.transitions.keys.each do |transition_name|
19
+ define_method(transition_name.to_sym) do
20
+ find_by_name Config.transitions[transition_name]
21
+ end
22
+ end
22
23
 
23
- class << self
24
- def all(issue)
25
- TransitionList.new client.Transition.all(issue: issue)
24
+ class << self
25
+ def all(issue)
26
+ TransitionList.new client.Transition.all(issue: issue)
27
+ end
26
28
  end
27
29
  end
28
30
  end
@@ -0,0 +1,63 @@
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 key
14
+ issue.key
15
+ end
16
+
17
+ def summary
18
+ issue.summary
19
+ end
20
+
21
+ def url
22
+ "#{Config.issue_browse_url}#{issue.key}"
23
+ end
24
+
25
+ def to_s(verbose)
26
+ raise UI::WindowTooNarrow, 'The terminal window is too narrow.' if max_cell_length <= key.size
27
+
28
+ verbose ? to_verbose_cell : to_short_cell
29
+ end
30
+
31
+ protected
32
+
33
+ def display_url?
34
+ url.size <= max_cell_length
35
+ end
36
+
37
+ def to_verbose_cell
38
+ key_and_summary = "#{key}: #{summary}"
39
+
40
+ if key_and_summary.size <= max_cell_length
41
+ row = key_and_summary
42
+ else
43
+ row = "#{key}\n#{summary[0...max_cell_length - 3]}..."
44
+ end
45
+
46
+ row << "\n#{url.blue}" if display_url?
47
+ row
48
+ end
49
+
50
+ def to_short_cell
51
+ return key unless display_url?
52
+
53
+ single_row = "#{key}: #{url}"
54
+
55
+ if single_row.size <= max_cell_length
56
+ "#{key}: #{url.blue}"
57
+ else
58
+ "#{key}\n#{url.blue}"
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,80 @@
1
+ require 'io/console'
2
+ require 'terminal-table'
3
+
4
+ module Jirify
5
+ module UI
6
+ class SprintTable
7
+ attr_reader :issues
8
+ def initialize(issues)
9
+ @issues = issues
10
+ end
11
+
12
+ def to_table(all_columns, verbose)
13
+ grouped_issues = issues.group_by do |issue|
14
+ all_columns ? issue.status.name : issue.status.statusCategory['name']
15
+ end
16
+
17
+ grouped_issues_as_table(grouped_issues, verbose)
18
+ end
19
+
20
+ protected
21
+
22
+ def terminal_width
23
+ IO.console.winsize[1]
24
+ end
25
+
26
+ def table_style
27
+ { width: terminal_width, border_x: '-', border_i: 'x' }
28
+ end
29
+
30
+ def headings(grouped_issues)
31
+ grouped_issues.keys.sort_by do |name|
32
+ status_index = Models::Status.status_order.index(name)
33
+ if status_index.nil?
34
+ grouped_issues.keys.length
35
+ else
36
+ status_index
37
+ end
38
+ end
39
+ end
40
+
41
+ def grouped_issues_as_table(grouped_issues, verbose)
42
+ transposed = transpose(grouped_issues.values, verbose)
43
+ return nil if transposed.empty?
44
+
45
+ Terminal::Table.new(
46
+ headings: headings(grouped_issues),
47
+ rows: transposed,
48
+ style: table_style
49
+ )
50
+ end
51
+
52
+ def transpose(grouped_issues, verbose)
53
+ col_padding_per_row = grouped_issues.size * 4
54
+ max_cell_length = (terminal_width - col_padding_per_row) / grouped_issues.size
55
+
56
+ # workaround - not all groups have the same size
57
+ l = grouped_issues.map(&:length).max
58
+ grouped_as_array = grouped_issues.map { |e| e.values_at(0...l) }
59
+
60
+ grouped_as_array.sort_by! do |row|
61
+ issue = row.find { |i| !i.nil? }
62
+ issue.status
63
+ end
64
+
65
+ transposed = grouped_as_array.transpose
66
+
67
+ transposed.map! do |row|
68
+ row.map do |issue|
69
+ next if issue.nil?
70
+ SprintCell.new(issue, max_cell_length).to_s(verbose)
71
+ end
72
+ end
73
+
74
+ transposed = transposed.zip([:separator] * transposed.size).flatten(1)
75
+ transposed.pop
76
+ transposed
77
+ end
78
+ end
79
+ end
80
+ end
@@ -1,3 +1,3 @@
1
1
  module Jirify
2
- VERSION = '0.1.6'.freeze
2
+ VERSION = '0.1.7'.freeze
3
3
  end
data/lib/jirify.rb CHANGED
@@ -1,5 +1,5 @@
1
- require 'thor'
2
1
  require 'jira-ruby'
2
+ require 'thor'
3
3
  require 'colorize'
4
4
 
5
5
  require 'jirify/version'
@@ -13,6 +13,9 @@ require 'jirify/models/issue'
13
13
  require 'jirify/models/sprint'
14
14
  require 'jirify/models/project'
15
15
 
16
+ require 'jirify/ui/sprint_cell'
17
+ require 'jirify/ui/sprint_table'
18
+
16
19
  require 'jirify/cli/setup'
17
20
  require 'jirify/cli/sprint'
18
21
  require 'jirify/cli/issue'
@@ -24,7 +27,7 @@ module Jirify
24
27
 
25
28
  desc 'version', 'Prints Jirify version'
26
29
  def version
27
- puts "Current Jirify version: #{VERSION}"
30
+ say "Current Jirify version: #{VERSION}"
28
31
  end
29
32
 
30
33
  desc 'setup [SUBCOMMAND]', 'Jirify setup tools'
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.1.6
4
+ version: 0.1.7
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-07 00:00:00.000000000 Z
11
+ date: 2018-07-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -17,6 +17,9 @@ dependencies:
17
17
  - - "~>"
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.8'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 0.8.1
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
@@ -24,6 +27,9 @@ dependencies:
24
27
  - - "~>"
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0.8'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 0.8.1
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: jira-ruby
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -45,6 +51,9 @@ dependencies:
45
51
  - - "~>"
46
52
  - !ruby/object:Gem::Version
47
53
  version: '2.4'
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ version: 2.4.3
48
57
  type: :runtime
49
58
  prerelease: false
50
59
  version_requirements: !ruby/object:Gem::Requirement
@@ -52,6 +61,9 @@ dependencies:
52
61
  - - "~>"
53
62
  - !ruby/object:Gem::Version
54
63
  version: '2.4'
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: 2.4.3
55
67
  - !ruby/object:Gem::Dependency
56
68
  name: terminal-table
57
69
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +92,168 @@ dependencies:
80
92
  - - "~>"
81
93
  - !ruby/object:Gem::Version
82
94
  version: '0.20'
95
+ - !ruby/object:Gem::Dependency
96
+ name: coveralls
97
+ requirement: !ruby/object:Gem::Requirement
98
+ requirements:
99
+ - - "~>"
100
+ - !ruby/object:Gem::Version
101
+ version: '0.7'
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: 0.7.1
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '0.7'
112
+ - - ">="
113
+ - !ruby/object:Gem::Version
114
+ version: 0.7.1
115
+ - !ruby/object:Gem::Dependency
116
+ name: guard
117
+ requirement: !ruby/object:Gem::Requirement
118
+ requirements:
119
+ - - "~>"
120
+ - !ruby/object:Gem::Version
121
+ version: '2.14'
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: 2.14.2
125
+ type: :development
126
+ prerelease: false
127
+ version_requirements: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '2.14'
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: 2.14.2
135
+ - !ruby/object:Gem::Dependency
136
+ name: guard-rspec
137
+ requirement: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - "~>"
140
+ - !ruby/object:Gem::Version
141
+ version: '4.7'
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: 4.7.3
145
+ type: :development
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '4.7'
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 4.7.3
155
+ - !ruby/object:Gem::Dependency
156
+ name: rake
157
+ requirement: !ruby/object:Gem::Requirement
158
+ requirements:
159
+ - - "~>"
160
+ - !ruby/object:Gem::Version
161
+ version: '12.1'
162
+ type: :development
163
+ prerelease: false
164
+ version_requirements: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - "~>"
167
+ - !ruby/object:Gem::Version
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
+ - !ruby/object:Gem::Dependency
190
+ name: rspec
191
+ requirement: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: '3.7'
196
+ type: :development
197
+ prerelease: false
198
+ version_requirements: !ruby/object:Gem::Requirement
199
+ requirements:
200
+ - - "~>"
201
+ - !ruby/object:Gem::Version
202
+ version: '3.7'
203
+ - !ruby/object:Gem::Dependency
204
+ name: rubocop
205
+ requirement: !ruby/object:Gem::Requirement
206
+ requirements:
207
+ - - "~>"
208
+ - !ruby/object:Gem::Version
209
+ version: '0.57'
210
+ - - ">="
211
+ - !ruby/object:Gem::Version
212
+ version: 0.57.2
213
+ type: :development
214
+ prerelease: false
215
+ version_requirements: !ruby/object:Gem::Requirement
216
+ requirements:
217
+ - - "~>"
218
+ - !ruby/object:Gem::Version
219
+ version: '0.57'
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: 0.57.2
223
+ - !ruby/object:Gem::Dependency
224
+ name: rubocop-rspec
225
+ requirement: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - "~>"
228
+ - !ruby/object:Gem::Version
229
+ version: '1.27'
230
+ type: :development
231
+ prerelease: false
232
+ version_requirements: !ruby/object:Gem::Requirement
233
+ requirements:
234
+ - - "~>"
235
+ - !ruby/object:Gem::Version
236
+ version: '1.27'
237
+ - !ruby/object:Gem::Dependency
238
+ name: simplecov
239
+ requirement: !ruby/object:Gem::Requirement
240
+ requirements:
241
+ - - "~>"
242
+ - !ruby/object:Gem::Version
243
+ version: '0.10'
244
+ - - ">="
245
+ - !ruby/object:Gem::Version
246
+ version: 0.10.2
247
+ type: :development
248
+ prerelease: false
249
+ version_requirements: !ruby/object:Gem::Requirement
250
+ requirements:
251
+ - - "~>"
252
+ - !ruby/object:Gem::Version
253
+ version: '0.10'
254
+ - - ">="
255
+ - !ruby/object:Gem::Version
256
+ version: 0.10.2
83
257
  description: JIRA for your terminal
84
258
  email: georgi@gardev.com
85
259
  executables:
@@ -103,12 +277,15 @@ files:
103
277
  - lib/jirify/models/status.rb
104
278
  - lib/jirify/models/transition_list.rb
105
279
  - lib/jirify/monkey_patches/jira_issue.rb
280
+ - lib/jirify/ui/sprint_cell.rb
281
+ - lib/jirify/ui/sprint_table.rb
106
282
  - lib/jirify/version.rb
107
283
  homepage: https://github.com/GeorgeSG/jirify
108
284
  licenses:
109
285
  - MIT
110
- metadata: {}
111
- post_install_message:
286
+ metadata:
287
+ source_code_uri: https://github.com/GeorgeSG/jirify
288
+ post_install_message: Thanks for installing! Run <jira setup> for initial configuration.
112
289
  rdoc_options: []
113
290
  require_paths:
114
291
  - lib