geet 0.3.1 → 0.3.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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +3 -3
  3. data/README.md +25 -24
  4. data/bin/geet +21 -3
  5. data/geet.gemspec +2 -2
  6. data/lib/geet/commandline/configuration.rb +5 -5
  7. data/lib/geet/git/repository.rb +34 -15
  8. data/lib/geet/services/abstract_create_issue.rb +33 -0
  9. data/lib/geet/services/create_gist.rb +6 -4
  10. data/lib/geet/services/create_issue.rb +33 -87
  11. data/lib/geet/services/create_label.rb +7 -6
  12. data/lib/geet/services/create_pr.rb +42 -86
  13. data/lib/geet/services/list_issues.rb +13 -14
  14. data/lib/geet/services/list_labels.rb +4 -3
  15. data/lib/geet/services/list_milestones.rb +12 -11
  16. data/lib/geet/services/list_prs.rb +4 -3
  17. data/lib/geet/services/merge_pr.rb +12 -11
  18. data/lib/geet/shared/constants.rb +9 -0
  19. data/lib/geet/utils/attributes_selection_manager.rb +87 -0
  20. data/lib/geet/utils/manual_list_selection.rb +41 -27
  21. data/lib/geet/utils/string_matching_selection.rb +32 -0
  22. data/lib/geet/version.rb +1 -1
  23. data/spec/integration/create_gist_spec.rb +4 -4
  24. data/spec/integration/create_issue_spec.rb +6 -6
  25. data/spec/integration/create_label_spec.rb +3 -3
  26. data/spec/integration/create_pr_spec.rb +13 -13
  27. data/spec/integration/list_issues_spec.rb +5 -5
  28. data/spec/integration/list_labels_spec.rb +3 -3
  29. data/spec/integration/list_milestones_spec.rb +2 -2
  30. data/spec/integration/list_prs_spec.rb +2 -2
  31. data/spec/integration/merge_pr_spec.rb +2 -2
  32. data/spec/vcr_cassettes/create_issue_upstream.yml +1 -1
  33. data/spec/vcr_cassettes/create_pr.yml +181 -102
  34. data/spec/vcr_cassettes/create_pr_in_auto_mode_create_upstream.yml +1 -1
  35. data/spec/vcr_cassettes/create_pr_in_auto_mode_with_push.yml +1 -1
  36. data/spec/vcr_cassettes/create_pr_upstream.yml +2 -2
  37. metadata +8 -5
  38. data/lib/geet/utils/pattern_matching_selection.rb +0 -27
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'manual_list_selection'
4
+ require_relative 'string_matching_selection'
5
+ require_relative '../shared/constants'
6
+
7
+ module Geet
8
+ module Utils
9
+ # Manages the retrieval and selection of attributes.
10
+ #
11
+ # Selecting an attribute happens in two steps: retrieval and selection.
12
+ #
13
+ # With this structure, the retrieval happens in parallel, cutting the time considerably when
14
+ # multiple attributes are required (typically, three).
15
+ #
16
+ class AttributesSelectionManager
17
+ include Geet::Shared::Constants
18
+
19
+ # Initialize the instance, and starts the background threads.
20
+ #
21
+ def initialize(repository, out: output)
22
+ @repository = repository
23
+ @out = out
24
+ @selections_data = []
25
+ end
26
+
27
+ def add_attribute(repository_call, description, pattern, selection_type, name_method: nil, &pre_selection_hook)
28
+ raise "Unrecognized selection type #{selection_type.inspect}" if ![:single, :multiple].include?(selection_type)
29
+
30
+ finder_thread = find_attribute_entries(repository_call)
31
+
32
+ @selections_data << [finder_thread, description, pattern, selection_type, name_method, pre_selection_hook]
33
+ end
34
+
35
+ # Select and return the attributes, in the same order they've been added.
36
+ #
37
+ def select_attributes
38
+ @selections_data.map do |finder_thread, description, pattern, selection_type, name_method, pre_selection_hook|
39
+ entries = finder_thread.value
40
+
41
+ entries = pre_selection_hook.(entries) if pre_selection_hook
42
+
43
+ case selection_type
44
+ when :single
45
+ select_entry(description, entries, pattern, name_method)
46
+ when :multiple
47
+ select_entries(description, entries, pattern, name_method)
48
+ end
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def find_attribute_entries(repository_call)
55
+ @out.puts "Finding #{repository_call}..."
56
+
57
+ Thread.new do
58
+ @repository.send(repository_call)
59
+ end
60
+ end
61
+
62
+ # Sample call:
63
+ #
64
+ # select_entry('milestone', all_milestones, '0.1.0', :title)
65
+ #
66
+ def select_entry(entry_type, entries, pattern, name_method)
67
+ if pattern == MANUAL_LIST_SELECTION_FLAG
68
+ Geet::Utils::ManualListSelection.new.select_entry(entry_type, entries, name_method: name_method)
69
+ else
70
+ Geet::Utils::StringMatchingSelection.new.select_entry(entry_type, entries, pattern, name_method: name_method)
71
+ end
72
+ end
73
+
74
+ # Sample call:
75
+ #
76
+ # select_entries('reviewer', all_collaborators, 'donaldduck', nil)
77
+ #
78
+ def select_entries(entry_type, entries, pattern, name_method)
79
+ if pattern == MANUAL_LIST_SELECTION_FLAG
80
+ Geet::Utils::ManualListSelection.new.select_entries(entry_type, entries, name_method: name_method)
81
+ else
82
+ Geet::Utils::StringMatchingSelection.new.select_entries(entry_type, entries, pattern, name_method: name_method)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -9,56 +9,70 @@ module Geet
9
9
 
10
10
  PAGER_SIZE = 16
11
11
 
12
+ # Shows a prompt for selecting an entry from a list.
13
+ #
14
+ # Returns nil, without showing the prompt, if there are no entries.
15
+ #
12
16
  # entry_type: description of the entries type.
13
- # entries: array of objects; if they're not strings, must also pass :instance_method.
17
+ # entries: array of objects; if they're not strings, must also pass :name_method.
14
18
  # this value must not be empty.
15
- # selection_type: :single or :multiple
16
- # instance_method: required when non-string objects are passed as entries; its invocation on
19
+ # name_method: required when non-string objects are passed as entries; its invocation on
17
20
  # each object must return a string, which is used as key.
18
21
  #
19
- # returns: the selected entry or array of entries. for single selection, if no entries are
20
- # chosen, nil is returned.
22
+ # returns: the selected entry. if no entries are nil is returned.
23
+ #
24
+ def select_entry(entry_type, entries, name_method: nil)
25
+ return nil if entries.empty?
26
+
27
+ check_entries(entries, entry_type)
28
+
29
+ entries = create_entries_map(entries, name_method)
30
+ entries = add_no_selection_entry(entries)
31
+
32
+ show_prompt(:select, entry_type, entries)
33
+ end
34
+
35
+ # Shows a prompt for selecting an entry from a list.
36
+ #
37
+ # Returns an empty array, without showing the prompt, if there are no entries.
38
+ #
39
+ # See #select_entry for the parameters.
21
40
  #
22
- def select(entry_type, entries, selection_type, instance_method: nil)
23
- check_entries(entries)
41
+ # returns: array of entries.
42
+ #
43
+ def select_entries(entry_type, entries, name_method: nil)
44
+ return [] if entries.empty?
24
45
 
25
- entries = create_entries_map(entries, instance_method)
46
+ check_entries(entries, entry_type)
26
47
 
27
- result = show_prompt(entry_type, selection_type, entries)
48
+ entries = create_entries_map(entries, name_method)
28
49
 
29
- result
50
+ show_prompt(:multi_select, entry_type, entries)
30
51
  end
31
52
 
32
53
  private
33
54
 
34
- def check_entries(entries)
55
+ def check_entries(entries, entry_type)
35
56
  raise "No #{entry_type} provided!" if entries.empty?
36
57
  end
37
58
 
38
- def create_entries_map(entries, instance_method)
59
+ def create_entries_map(entries, name_method)
39
60
  entries.each_with_object({}) do |entry, current_map|
40
- key = instance_method ? entry.send(instance_method) : entry
61
+ key = name_method ? entry.send(name_method) : entry
41
62
  current_map[key] = entry
42
63
  end
43
64
  end
44
65
 
45
- def show_prompt(entry_type, selection_type, entries)
46
- prompt_title = "Please select the #{entry_type}(s):"
47
-
48
- case selection_type
49
- when :single
50
- entries = add_no_selection_entry(entries)
51
- TTY::Prompt.new.select(prompt_title, entries, filter: true, per_page: PAGER_SIZE)
52
- when :multiple
53
- TTY::Prompt.new.multi_select(prompt_title, entries, filter: true, per_page: PAGER_SIZE)
54
- else
55
- raise "Unexpected selection type: #{selection_type.inspect}"
56
- end
57
- end
58
-
59
66
  def add_no_selection_entry(entries)
60
67
  {NO_SELECTION_KEY => nil}.merge(entries)
61
68
  end
69
+
70
+ def show_prompt(invocation_method, entry_type, entries)
71
+ # Arguably inexact phrasing for avoiding language complexities.
72
+ prompt_title = "Please select the #{entry_type}(s):"
73
+
74
+ TTY::Prompt.new.send(invocation_method, prompt_title, entries, filter: true, per_page: PAGER_SIZE)
75
+ end
62
76
  end
63
77
  end
64
78
  end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Geet
4
+ module Utils
5
+ class StringMatchingSelection
6
+ def select_entry(entry_type, entries, pattern, name_method: nil)
7
+ entries_found = entries.select do |entry|
8
+ entry = entry.send(name_method) if name_method
9
+ entry.downcase == pattern.downcase
10
+ end
11
+
12
+ case entries_found.size
13
+ when 1
14
+ entries_found.first
15
+ when 0
16
+ raise "No entry found for #{entry_type} pattern: #{pattern.inspect}"
17
+ else
18
+ raise "Multiple entries found for #{entry_type} pattern #{pattern.inspect}: #{entries_found}"
19
+ end
20
+ end
21
+
22
+ def select_entries(entry_type, entries, raw_patterns, name_method: nil)
23
+ patterns = raw_patterns.split(',')
24
+
25
+ patterns.map do |pattern|
26
+ # Haha.
27
+ select_entry(entry_type, entries, pattern, name_method: name_method)
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/geet/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Geet
4
- VERSION = '0.3.1'
4
+ VERSION = '0.3.2'
5
5
  end
@@ -18,8 +18,8 @@ describe Geet::Services::CreateGist do
18
18
  actual_output = StringIO.new
19
19
 
20
20
  VCR.use_cassette('create_gist_public') do
21
- described_class.new.execute(
22
- tempfile.path, description: 'testdescription', publik: true, no_browse: true, output: actual_output
21
+ described_class.new(out: actual_output).execute(
22
+ tempfile.path, description: 'testdescription', publik: true, no_browse: true
23
23
  )
24
24
  end
25
25
 
@@ -35,8 +35,8 @@ describe Geet::Services::CreateGist do
35
35
  actual_output = StringIO.new
36
36
 
37
37
  VCR.use_cassette('create_gist_private') do
38
- described_class.new.execute(
39
- tempfile.path, description: 'testdescription', no_browse: true, output: actual_output
38
+ described_class.new(out: actual_output).execute(
39
+ tempfile.path, description: 'testdescription', no_browse: true
40
40
  )
41
41
  end
42
42
 
@@ -16,7 +16,7 @@ describe Geet::Services::CreateIssue do
16
16
 
17
17
  expected_output = <<~STR
18
18
  Finding labels...
19
- Finding milestone...
19
+ Finding milestones...
20
20
  Finding collaborators...
21
21
  Creating the issue...
22
22
  Adding labels bug, invalid...
@@ -28,10 +28,10 @@ describe Geet::Services::CreateIssue do
28
28
  actual_output = StringIO.new
29
29
 
30
30
  actual_created_issue = VCR.use_cassette('create_issue') do
31
- described_class.new(repository).execute(
31
+ described_class.new(repository, out: actual_output).execute(
32
32
  'Title', 'Description',
33
- label_patterns: 'bug,invalid', milestone_pattern: '0.0.1', assignee_patterns: 'nald-ts,nald-fr',
34
- no_open_issue: true, output: actual_output
33
+ labels: 'bug,invalid', milestone: '0.0.1', assignees: 'donald-ts,donald-fr',
34
+ no_open_issue: true
35
35
  )
36
36
  end
37
37
 
@@ -56,8 +56,8 @@ describe Geet::Services::CreateIssue do
56
56
  actual_output = StringIO.new
57
57
 
58
58
  actual_created_issue = VCR.use_cassette('create_issue_upstream') do
59
- create_options = { no_open_issue: true, output: actual_output }
60
- described_class.new(upstream_repository).execute('Title', 'Description', create_options)
59
+ create_options = { no_open_issue: true, out: actual_output }
60
+ described_class.new(upstream_repository, out: actual_output).execute('Title', 'Description', create_options)
61
61
  end
62
62
 
63
63
  expect(actual_output.string).to eql(expected_output)
@@ -22,7 +22,7 @@ describe Geet::Services::CreateLabel do
22
22
  actual_output = StringIO.new
23
23
 
24
24
  actual_created_label = VCR.use_cassette('create_label') do
25
- described_class.new(repository).execute('my_label', color: 'c64c64', output: actual_output)
25
+ described_class.new(repository, out: actual_output).execute('my_label', color: 'c64c64')
26
26
  end
27
27
 
28
28
  expect(actual_output.string).to eql(expected_output)
@@ -44,7 +44,7 @@ describe Geet::Services::CreateLabel do
44
44
  actual_output = StringIO.new
45
45
 
46
46
  actual_created_label = VCR.use_cassette('create_label_upstream') do
47
- described_class.new(upstream_repository).execute('my_label', color: 'c64c64', output: actual_output)
47
+ described_class.new(upstream_repository, out: actual_output).execute('my_label', color: 'c64c64')
48
48
  end
49
49
 
50
50
  expect(actual_output.string).to eql(expected_output)
@@ -67,7 +67,7 @@ describe Geet::Services::CreateLabel do
67
67
  actual_output = StringIO.new
68
68
 
69
69
  actual_created_label = VCR.use_cassette('create_label_with_random_color') do
70
- described_class.new(repository).execute('my_label', output: actual_output)
70
+ described_class.new(repository, out: actual_output).execute('my_label')
71
71
  end
72
72
 
73
73
  expected_output = format(expected_output_template, color: actual_created_label.color)
@@ -17,32 +17,32 @@ describe Geet::Services::CreatePr do
17
17
 
18
18
  expected_output = <<~STR
19
19
  Finding labels...
20
- Finding milestone...
20
+ Finding milestones...
21
21
  Finding collaborators...
22
22
  Creating PR...
23
23
  Assigning authenticated user...
24
- Adding labels other_bug, invalid...
25
- Setting milestone 0.0.1...
26
- Requesting review from donald-ts, donald-fr...
27
- PR address: https://github.com/donaldduck/testrepo/pull/3
24
+ Adding labels bug, invalid...
25
+ Setting milestone milestone 1...
26
+ Requesting review from donald-fr...
27
+ PR address: https://github.com/donaldduck/testrepo/pull/39
28
28
  STR
29
29
 
30
30
  actual_output = StringIO.new
31
31
 
32
32
  actual_created_pr = VCR.use_cassette('create_pr') do
33
- service_instance = described_class.new(repository, git_client: git_client)
33
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
34
34
  service_instance.execute(
35
35
  'Title', 'Description',
36
- label_patterns: '_bug,invalid', milestone_pattern: '0.0.1', reviewer_patterns: 'nald-ts,nald-fr',
36
+ labels: 'bug,invalid', milestone: 'milestone 1', reviewers: 'donald-fr',
37
37
  no_open_pr: true, output: actual_output
38
38
  )
39
39
  end
40
40
 
41
41
  expect(actual_output.string).to eql(expected_output)
42
42
 
43
- expect(actual_created_pr.number).to eql(3)
43
+ expect(actual_created_pr.number).to eql(39)
44
44
  expect(actual_created_pr.title).to eql('Title')
45
- expect(actual_created_pr.link).to eql('https://github.com/donaldduck/testrepo/pull/3')
45
+ expect(actual_created_pr.link).to eql('https://github.com/donaldduck/testrepo/pull/39')
46
46
  end
47
47
  end
48
48
 
@@ -60,7 +60,7 @@ describe Geet::Services::CreatePr do
60
60
  actual_output = StringIO.new
61
61
 
62
62
  actual_created_pr = VCR.use_cassette('create_pr_upstream') do
63
- service_instance = described_class.new(upstream_repository, git_client: git_client)
63
+ service_instance = described_class.new(upstream_repository, out: actual_output, git_client: git_client)
64
64
  service_instance.execute('Title', 'Description', no_open_pr: true, output: actual_output)
65
65
  end
66
66
 
@@ -83,7 +83,7 @@ describe Geet::Services::CreatePr do
83
83
  actual_output = StringIO.new
84
84
 
85
85
  operation = -> do
86
- service_instance = described_class.new(repository, git_client: git_client)
86
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
87
87
  service_instance.execute('Title', 'Description', output: actual_output, automated_mode: true, no_open_pr: true)
88
88
  end
89
89
 
@@ -110,7 +110,7 @@ describe Geet::Services::CreatePr do
110
110
  actual_output = StringIO.new
111
111
 
112
112
  actual_created_pr = VCR.use_cassette('create_pr_in_auto_mode_with_push') do
113
- service_instance = described_class.new(repository, git_client: git_client)
113
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
114
114
  service_instance.execute('Title', 'Description', output: actual_output, automated_mode: true, no_open_pr: true)
115
115
  end
116
116
 
@@ -135,7 +135,7 @@ describe Geet::Services::CreatePr do
135
135
  actual_output = StringIO.new
136
136
 
137
137
  actual_created_pr = VCR.use_cassette('create_pr_in_auto_mode_create_upstream') do
138
- service_instance = described_class.new(repository, git_client: git_client)
138
+ service_instance = described_class.new(repository, out: actual_output, git_client: git_client)
139
139
  service_instance.execute('Title', 'Description', output: actual_output, automated_mode: true, no_open_pr: true)
140
140
  end
141
141
 
@@ -23,7 +23,7 @@ describe Geet::Services::ListIssues do
23
23
  actual_output = StringIO.new
24
24
 
25
25
  service_result = VCR.use_cassette('github_com/list_issues') do
26
- described_class.new(repository).execute(output: actual_output)
26
+ described_class.new(repository, out: actual_output).execute
27
27
  end
28
28
 
29
29
  actual_issue_numbers = service_result.map(&:number)
@@ -37,7 +37,7 @@ describe Geet::Services::ListIssues do
37
37
  allow(git_client).to receive(:remote).with('origin').and_return('git@github.com:donaldduck/testrepo_gh')
38
38
 
39
39
  expected_output = <<~STR
40
- Finding assignee...
40
+ Finding collaborators...
41
41
  12. test issue 3 (https://github.com/donaldduck/testrepo_gh/issues/12)
42
42
  10. test issue 1 (https://github.com/donaldduck/testrepo_gh/issues/10)
43
43
  STR
@@ -46,7 +46,7 @@ describe Geet::Services::ListIssues do
46
46
  actual_output = StringIO.new
47
47
 
48
48
  service_result = VCR.use_cassette('github_com/list_issues_with_assignee') do
49
- described_class.new(repository).execute(assignee_pattern: 'donald-fr', output: actual_output)
49
+ described_class.new(repository, out: actual_output).execute(assignee: 'donald-fr', )
50
50
  end
51
51
 
52
52
  actual_issue_numbers = service_result.map(&:number)
@@ -69,7 +69,7 @@ describe Geet::Services::ListIssues do
69
69
  actual_output = StringIO.new
70
70
 
71
71
  service_result = VCR.use_cassette('github_com/list_issues_upstream') do
72
- described_class.new(upstream_repository).execute(output: actual_output)
72
+ described_class.new(upstream_repository, out: actual_output).execute
73
73
  end
74
74
 
75
75
  actual_issue_numbers = service_result.map(&:number)
@@ -92,7 +92,7 @@ describe Geet::Services::ListIssues do
92
92
  actual_output = StringIO.new
93
93
 
94
94
  service_result = VCR.use_cassette('gitlab_com/list_issues') do
95
- described_class.new(repository).execute(output: actual_output)
95
+ described_class.new(repository, out: actual_output).execute
96
96
  end
97
97
 
98
98
  actual_issue_numbers = service_result.map(&:number)
@@ -24,7 +24,7 @@ describe Geet::Services::ListLabels do
24
24
 
25
25
  actual_output = StringIO.new
26
26
  actual_labels = VCR.use_cassette('github.com/list_labels') do
27
- described_class.new(repository).execute(output: actual_output)
27
+ described_class.new(repository, out: actual_output).execute
28
28
  end
29
29
 
30
30
  actual_label_names = actual_labels.map(&:name)
@@ -45,7 +45,7 @@ describe Geet::Services::ListLabels do
45
45
 
46
46
  actual_output = StringIO.new
47
47
  actual_labels = VCR.use_cassette('github.com/list_labels_upstream') do
48
- described_class.new(upstream_repository).execute(output: actual_output)
48
+ described_class.new(upstream_repository, out: actual_output).execute
49
49
  end
50
50
 
51
51
  actual_label_names = actual_labels.map(&:name)
@@ -73,7 +73,7 @@ describe Geet::Services::ListLabels do
73
73
 
74
74
  actual_output = StringIO.new
75
75
  actual_labels = VCR.use_cassette('gitlab.com/list_labels') do
76
- described_class.new(repository).execute(output: actual_output)
76
+ described_class.new(repository, out: actual_output).execute
77
77
  end
78
78
 
79
79
  actual_label_names = actual_labels.map(&:name)