git_toolbox 0.7.2 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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