git_toolbox 0.7.2 → 0.9.0

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.
@@ -18,15 +18,14 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  require 'highline'
21
- require 'get/commons/git'
21
+ require_relative '../../commons/git'
22
22
 
23
- # Module for asking to the user informations about a commit message.
23
+ # Module for asking to the user information about a commit message.
24
24
  module PromptHandler
25
- @@cli = HighLine.new
26
-
27
- @@custom_values_initialized = nil
28
- @@custom_types = []
29
- @@custom_scopes = []
25
+ Common.module_instance_value(self, 'cli', 'HighLine.new')
26
+ Common.module_instance_attr(self, 'custom_values_initialized', false)
27
+ Common.module_instance_value(self, 'custom_types', [])
28
+ Common.module_instance_value(self, 'custom_scopes', [])
30
29
 
31
30
  STRING_VALUE_VALIDATOR = /\s*\S+\s*/
32
31
  BODY_END_DELIMITER = "\n\n\n"
@@ -46,14 +45,15 @@ module PromptHandler
46
45
 
47
46
  def ask_for_type
48
47
  extract_types_and_scopes
49
- @@cli.choose do |menu|
48
+
49
+ MOD_REF.cli.choose do |menu|
50
50
  menu.flow = :columns_down
51
51
  menu.prompt = 'Choose the type of your commit: '
52
- DEFAULT_TYPES.union(@@custom_types).each do |type|
52
+ DEFAULT_TYPES.union(MOD_REF.custom_types).each do |type|
53
53
  menu.choice(type.to_sym)
54
54
  end
55
55
  menu.choice('Create a new type (rarely needed)') do |_|
56
- @@cli.ask('Write the new type to use', String) do |question|
56
+ MOD_REF.cli.ask('Write the new type to use', String) do |question|
57
57
  question.verify_match = true
58
58
  question.validate = STRING_VALUE_VALIDATOR
59
59
  end
@@ -63,14 +63,14 @@ module PromptHandler
63
63
 
64
64
  def ask_for_scope
65
65
  extract_types_and_scopes
66
- @@cli.choose do |menu|
66
+ MOD_REF.cli.choose do |menu|
67
67
  menu.flow = :columns_down
68
68
  menu.prompt = 'Choose the scope of your commit: '
69
- @@custom_scopes.each do |scope|
69
+ MOD_REF.custom_scopes.each do |scope|
70
70
  menu.choice(scope.to_sym)
71
71
  end
72
72
  menu.choice('Create a new scope') do |_|
73
- @@cli.ask('Write the new scope to use', String) do |question|
73
+ MOD_REF.cli.ask('Write the new scope to use', String) do |question|
74
74
  question.verify_match = true
75
75
  question.validate = STRING_VALUE_VALIDATOR
76
76
  end
@@ -80,13 +80,13 @@ module PromptHandler
80
80
  end
81
81
 
82
82
  def ask_for_breaking
83
- @@cli.agree('Does the commit contain a breaking change? (yes/no) ') do |question|
83
+ MOD_REF.cli.agree('Does the commit contain a breaking change? (yes/no) ') do |question|
84
84
  question.default = false
85
85
  end
86
86
  end
87
87
 
88
88
  def ask_for_summary
89
- @@cli.ask('The summary of the commit:') do |question|
89
+ MOD_REF.cli.ask('The summary of the commit:') do |question|
90
90
  question.verify_match = true
91
91
  question.validate = STRING_VALUE_VALIDATOR
92
92
  end
@@ -94,31 +94,33 @@ module PromptHandler
94
94
 
95
95
  def ask_for_message
96
96
  # This method needs a special implementation as the body message can span multiple lines.
97
- @@cli.puts('The body of the commit (ends after 3 new lines):')
98
- @@cli.input.gets(BODY_END_DELIMITER)
97
+ MOD_REF.cli.puts('The body of the commit (ends after 3 new lines):')
98
+ MOD_REF.cli.input.gets(BODY_END_DELIMITER)
99
99
  end
100
100
 
101
101
  private
102
102
 
103
103
  FIRST_COMMIT = nil
104
104
 
105
+ Common.add_module_self_reference(self)
106
+
105
107
  # This method tries to optimize input parsing by performing multiple operations in one go.
106
108
  # So its complexity is a bit higher as it needs to make multiple checks.
107
109
  # rubocop:disable Metrics/CyclomaticComplexity
108
110
  def extract_types_and_scopes
109
- return unless @@custom_values_initialized.nil?
111
+ return if MOD_REF.custom_values_initialized
110
112
 
111
113
  Git.with_commit_list_from(FIRST_COMMIT) do |commit_list|
112
- commit_list.map do |element|
114
+ commit_list.reverse.map do |element|
113
115
  match = Git::CONVENTIONAL_COMMIT_REGEX.match(element)
114
116
  next if match.nil?
115
117
 
116
- type_already_added = DEFAULT_TYPES.include?(match[1].to_sym) || @@custom_types.include?(match[1])
117
- @@custom_types.append(match[1]) unless type_already_added
118
- @@custom_scopes.append(match[3]) unless match[3].nil? || @@custom_scopes.include?(match[3])
118
+ type_already_added = DEFAULT_TYPES.include?(match[1].to_sym) || MOD_REF.custom_types.include?(match[1])
119
+ MOD_REF.custom_types.append(match[1]) unless type_already_added
120
+ MOD_REF.custom_scopes.append(match[3]) unless match[3].nil? || MOD_REF.custom_scopes.include?(match[3])
119
121
  end
120
122
  end
121
- @@custom_values_initialized = true
123
+ MOD_REF.custom_values_initialized = true
122
124
  end
123
125
  # rubocop:enable Metrics/CyclomaticComplexity
124
126
  end
@@ -22,12 +22,16 @@ module BashCompletion
22
22
  def bash_completion(main_module, command_name)
23
23
  generate_functions(main_module, command_name, INITIAL_LEVEL)
24
24
 
25
- "#{HEADER}\n#{@@function_stack.reverse.join("\n")}"
25
+ "#{HEADER}\n#{MOD_REF.function_stack.reverse.join("\n")}"
26
26
  end
27
27
 
28
28
  private
29
29
 
30
30
  INITIAL_LEVEL = 1
31
+ NOT_NEGABLE_OPTIONS = [
32
+ 'help',
33
+ 'version',
34
+ ].freeze
31
35
 
32
36
  HEADER = <<~HEADER
33
37
  #!/usr/bin/env bash
@@ -37,7 +41,8 @@ module BashCompletion
37
41
  #
38
42
  HEADER
39
43
 
40
- @@function_stack = []
44
+ Common.add_module_self_reference(self)
45
+ Common.module_instance_value(self, 'function_stack', [])
41
46
 
42
47
  def full_subcommand_name(base, name)
43
48
  "#{base}_#{name}"
@@ -74,18 +79,19 @@ module BashCompletion
74
79
  short_options = []
75
80
  subcommands = []
76
81
 
77
- command_class.class_variable_get(:@@option_parser).specs.each_value do |option|
82
+ command_class.instance.option_parser.specs.each_value do |option|
78
83
  long_options.push("--#{option.long}")
84
+ long_options.push("--no-#{option.long}") if option.flag? && !NOT_NEGABLE_OPTIONS.include?(option.long)
79
85
  short_options.push("-#{option.short.nil? ? option.long[0] : option.short}") if option.short != :none
80
86
  end
81
87
 
82
- command_class.class_variable_get(:@@subcommands).each_key do |subcommand|
88
+ command_class.instance.subcommands.each_key do |subcommand|
83
89
  subcommands.push(subcommand.to_s)
84
90
  end
85
91
 
86
- @@function_stack.push(completion_function_definition(name, level, long_options, short_options, subcommands))
92
+ MOD_REF.function_stack.push(completion_function_definition(name, level, long_options, short_options, subcommands))
87
93
 
88
- command_class.class_variable_get(:@@subcommands).each do |element|
94
+ command_class.instance.subcommands.each do |element|
89
95
  generate_functions(element[1].class, full_subcommand_name(name, element[0].to_s), level + 1)
90
96
  end
91
97
  end
@@ -17,66 +17,77 @@
17
17
 
18
18
  # frozen_string_literal: true
19
19
 
20
- require 'English'
21
- require 'get/commons/common'
22
- require 'get/subcommand/command'
23
- require 'get/subcommand/complete/bash_completion'
24
- require 'get'
20
+ require_relative '../../commons/common'
21
+ require_relative '../command'
22
+ require_relative './bash_completion'
23
+ require_relative '../../../get'
25
24
 
26
25
  # Class length is disabled as most of its length is given by formatting.
27
26
  # rubocop:disable Metrics/ClassLength
28
27
  # Subcommand, prints the bash completion script.
29
28
  class Complete < Command
30
- def self.command
31
- @@command ||= new
32
- @@command
33
- end
34
-
35
- private_class_method :new
36
-
37
29
  private
38
30
 
39
31
  include BashCompletion
40
32
 
41
- @@command = nil
42
-
43
- @@usage = 'complete -h|(<subcommand> [<subcommand-options])'
44
- @@description = 'Print the shell completion script.'
45
- @@subcommands = {}
46
- # This block is Optimist configuration. It is as long as the number of options of the command.
47
- # rubocop:disable Metrics/BlockLength
48
- @@option_parser = Optimist::Parser.new do
49
- subcommand_max_length = @@subcommands.keys.map { |k| k.to_s.length }.max
50
- subcommand_section = <<~SUBCOMMANDS unless @@subcommands.empty?
51
- Subcommands:
52
- #{@@subcommands.keys.map { |k| " #{k.to_s.ljust(subcommand_max_length)} => #{@@subcommands[k].description}" }.join("\n")}
53
- SUBCOMMANDS
54
- usage @@usage
55
- synopsis @@description + (subcommand_section.nil? ? '' : "\n") + subcommand_section.to_s
56
- opt :shell,
57
- 'Select the type of shell of which the completion will be generated.',
58
- { type: :string, default: 'bash' }
59
- educate_on_error
60
- stop_on @@subcommands.keys.map(&:to_s)
61
- end
62
- # rubocop:enable Metrics/BlockLength
63
-
64
33
  def initialize
65
- super(@@usage, @@description) do
66
- @options = Common.with_subcommand_exception_handling @@option_parser do
67
- @@option_parser.parse
68
- end
69
-
34
+ super() do
35
+ @usage = 'complete -h|(<subcommand> [<subcommand-options])'
36
+ @description = 'Print the shell completion script. ' \
37
+ 'Run \'get complete --info\' to learn how to install the completion.'
38
+ @subcommands = {}
70
39
  @completions = {
71
- bash: proc { bash_completion(Get, 'get') }
40
+ bash: {
41
+ generator: proc { bash_completion(Get, 'get') },
42
+ info: <<~INFO
43
+ Add the following line to your .bashrc (or a sourced script):
44
+ eval "$(get complete)" && complete -F _get_completion get
45
+ INFO
46
+ }
72
47
  }
48
+ end
49
+ end
50
+
51
+ protected
52
+
53
+ def setup_option_parser
54
+ @option_parser = Optimist::Parser.new(
55
+ @usage,
56
+ full_description,
57
+ stop_condition
58
+ ) do |usage_header, description, stop_condition|
59
+ usage usage_header
60
+ synopsis description
61
+ opt :shell,
62
+ 'Select the type of shell of which the completion will be generated.',
63
+ { type: :string, default: 'bash' }
64
+ opt :info,
65
+ 'Print the instructions to install the completion for the selected shell.',
66
+ { short: :none }
67
+ educate_on_error
68
+ stop_on stop_condition
69
+ end
70
+ end
71
+
72
+ def setup_action
73
+ @action = lambda do
74
+ @options = Common.with_subcommand_exception_handling @option_parser do
75
+ @option_parser.parse
76
+ end
73
77
 
74
78
  selected_shell = @options[:shell].to_sym
75
79
 
80
+ print_info_and_exit(selected_shell) if @options[:info]
81
+
76
82
  Common.error "Completion for shell '#{selected_shell}' not available." unless @completions.key?(selected_shell)
77
83
 
78
- puts @completions[selected_shell].call
84
+ puts @completions[selected_shell][:generator].call
79
85
  end
80
86
  end
87
+
88
+ def print_info_and_exit(selected_shell)
89
+ puts @completions[selected_shell][:info]
90
+ exit 0
91
+ end
81
92
  end
82
93
  # rubocop:enable Metrics/ClassLength
@@ -22,50 +22,67 @@ module ChangeHandler
22
22
  # Array with change types in ascending order of importance.
23
23
  CHANGE_TYPE = %i[NONE PATCH MINOR MAJOR].freeze
24
24
 
25
- @@major_trigger = 'is_breaking'
26
- @@minor_trigger = "type == 'feat'"
27
- @@patch_trigger = "type == 'fix'"
25
+ DEFAULT_MAJOR_TRIGGER = 'is_breaking'
26
+ DEFAULT_MINOR_TRIGGER = "type == 'feat'"
27
+ DEFAULT_PATCH_TRIGGER = "type == 'fix'"
28
28
 
29
- module_function
29
+ Common.module_instance_attr(self, 'major_trigger', :DEFAULT_MAJOR_TRIGGER)
30
+ Common.module_instance_attr(self, 'minor_trigger', :DEFAULT_MINOR_TRIGGER)
31
+ Common.module_instance_attr(self, 'patch_trigger', :DEFAULT_PATCH_TRIGGER)
32
+
33
+ def updated_stable_version(stable_version)
34
+ return Git::DEFAULT_RELEASE_VERSION if stable_version.nil?
35
+
36
+ greatest_change_from_stable_version = Git.with_commit_list_from(stable_version) do |commits_from_version|
37
+ greatest_change_in(commits_from_version)
38
+ end
39
+ split_version = stable_version.split('.')
40
+ case greatest_change_from_stable_version
41
+ when :MAJOR
42
+ "#{split_version[0].to_i + 1}.0.0"
43
+ when :MINOR
44
+ "#{split_version[0].to_i}.#{split_version[1].to_i + 1}.0"
45
+ when :PATCH
46
+ "#{split_version[0].to_i}.#{split_version[1].to_i}.#{split_version[2].to_i + 1}"
47
+ else
48
+ "#{split_version[0].to_i}.#{split_version[1].to_i}.#{split_version[2].to_i}"
49
+ end
50
+ end
51
+
52
+ private
53
+
54
+ def greatest_change_in(commit_list)
55
+ commit_list
56
+ .grep(Git::CONVENTIONAL_COMMIT_REGEX)
57
+ .map { |commit| to_change(commit) }
58
+ .max { |a, b| CHANGE_TYPE.index(a) <=> CHANGE_TYPE.index(b) }
59
+ end
30
60
 
31
61
  # In this block method arguments can be used by user.
32
62
  # Also `eval` is needed to allow users to define their custom triggers.
33
63
  # rubocop:disable Lint/UnusedMethodArgument
34
64
  # rubocop:disable Security/Eval
35
65
  def triggers_major?(type, scope, is_breaking)
36
- eval(@@major_trigger)
66
+ eval(ChangeHandler.major_trigger)
37
67
  end
38
68
 
39
69
  def triggers_minor?(type, scope)
40
- eval(@@minor_trigger)
70
+ eval(ChangeHandler.minor_trigger)
41
71
  end
42
72
 
43
73
  def triggers_patch?(type, scope)
44
- eval(@@patch_trigger)
74
+ eval(ChangeHandler.patch_trigger)
45
75
  end
46
76
  # rubocop:enable Lint/UnusedMethodArgument
47
77
  # rubocop:enable Security/Eval
48
78
 
49
- # Open String class to inject method to convert a (commit) string into
50
- # a change.
51
- class ::String
52
- # Convert the string (as a conventional commit string) into a change type.
53
- def to_change
54
- groups = Git::CONVENTIONAL_COMMIT_REGEX.match(self)
55
- return :MAJOR if ChangeHandler.triggers_major?(groups[1], groups[3], !groups[4].nil?)
56
- return :MINOR if ChangeHandler.triggers_minor?(groups[1], groups[2])
57
- return :PATCH if ChangeHandler.triggers_patch?(groups[1], groups[2])
58
-
59
- :NONE
60
- end
61
- end
62
-
63
- public
79
+ # Convert the string (as a conventional commit string) into a change type.
80
+ def to_change(commit_message)
81
+ groups = Git::CONVENTIONAL_COMMIT_REGEX.match(commit_message)
82
+ return :MAJOR if triggers_major?(groups[1], groups[3], !groups[4].nil?)
83
+ return :MINOR if triggers_minor?(groups[1], groups[3])
84
+ return :PATCH if triggers_patch?(groups[1], groups[3])
64
85
 
65
- def greatest_change_in(commit_list)
66
- commit_list
67
- .grep(Git::CONVENTIONAL_COMMIT_REGEX)
68
- .map(&:to_change)
69
- .max { |a, b| CHANGE_TYPE.index(a) <=> CHANGE_TYPE.index(b) }
86
+ :NONE
70
87
  end
71
88
  end