reviewer 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +4 -4
  2. data/.flayignore +1 -0
  3. data/.github/workflows/main.yml +11 -3
  4. data/.gitignore +5 -0
  5. data/.reviewer.example.yml +27 -23
  6. data/.reviewer.yml +58 -5
  7. data/.rubocop.yml +2 -0
  8. data/.ruby-version +1 -1
  9. data/CHANGELOG.md +14 -0
  10. data/Gemfile +0 -3
  11. data/Gemfile.lock +54 -29
  12. data/README.md +5 -50
  13. data/exe/fmt +1 -1
  14. data/exe/rvw +1 -1
  15. data/lib/reviewer.rb +39 -26
  16. data/lib/reviewer/arguments.rb +25 -9
  17. data/lib/reviewer/arguments/files.rb +37 -5
  18. data/lib/reviewer/arguments/keywords.rb +23 -9
  19. data/lib/reviewer/arguments/tags.rb +26 -3
  20. data/lib/reviewer/batch.rb +64 -0
  21. data/lib/reviewer/command.rb +100 -0
  22. data/lib/reviewer/command/string.rb +72 -0
  23. data/lib/reviewer/command/string/env.rb +40 -0
  24. data/lib/reviewer/command/string/flags.rb +40 -0
  25. data/lib/reviewer/command/string/verbosity.rb +51 -0
  26. data/lib/reviewer/command/verbosity.rb +65 -0
  27. data/lib/reviewer/configuration.rb +24 -4
  28. data/lib/reviewer/conversions.rb +27 -0
  29. data/lib/reviewer/guidance.rb +73 -0
  30. data/lib/reviewer/history.rb +38 -0
  31. data/lib/reviewer/keywords.rb +9 -0
  32. data/lib/reviewer/keywords/git.rb +14 -0
  33. data/lib/reviewer/keywords/git/staged.rb +48 -0
  34. data/lib/reviewer/loader.rb +2 -3
  35. data/lib/reviewer/output.rb +92 -0
  36. data/lib/reviewer/printer.rb +25 -0
  37. data/lib/reviewer/runner.rb +43 -72
  38. data/lib/reviewer/runner/strategies/quiet.rb +90 -0
  39. data/lib/reviewer/runner/strategies/verbose.rb +63 -0
  40. data/lib/reviewer/shell.rb +58 -0
  41. data/lib/reviewer/shell/result.rb +69 -0
  42. data/lib/reviewer/shell/timer.rb +57 -0
  43. data/lib/reviewer/tool.rb +109 -40
  44. data/lib/reviewer/tool/settings.rb +18 -32
  45. data/lib/reviewer/tools.rb +38 -3
  46. data/lib/reviewer/version.rb +1 -1
  47. data/reviewer.gemspec +10 -2
  48. metadata +143 -16
  49. data/lib/reviewer/arguments/keywords/git.rb +0 -16
  50. data/lib/reviewer/arguments/keywords/git/staged.rb +0 -64
  51. data/lib/reviewer/logger.rb +0 -62
  52. data/lib/reviewer/tool/command.rb +0 -80
  53. data/lib/reviewer/tool/env.rb +0 -38
  54. data/lib/reviewer/tool/flags.rb +0 -38
  55. data/lib/reviewer/tool/verbosity.rb +0 -39
data/exe/fmt CHANGED
@@ -4,4 +4,4 @@
4
4
 
5
5
  require_relative '../lib/reviewer'
6
6
 
7
- Reviewer.format
7
+ Reviewer.format(clear_screen: true)
data/exe/rvw CHANGED
@@ -4,4 +4,4 @@
4
4
 
5
5
  require_relative '../lib/reviewer'
6
6
 
7
- Reviewer.review
7
+ Reviewer.review(clear_screen: true)
data/lib/reviewer.rb CHANGED
@@ -1,13 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/string'
4
3
  require 'benchmark'
4
+ require 'forwardable'
5
+ require 'logger'
6
+
7
+ require_relative 'reviewer/conversions'
5
8
 
6
9
  require_relative 'reviewer/arguments'
10
+ require_relative 'reviewer/batch'
11
+ require_relative 'reviewer/command'
7
12
  require_relative 'reviewer/configuration'
13
+ require_relative 'reviewer/guidance'
14
+ require_relative 'reviewer/history'
15
+ require_relative 'reviewer/keywords'
8
16
  require_relative 'reviewer/loader'
9
- require_relative 'reviewer/logger'
17
+ require_relative 'reviewer/output'
18
+ require_relative 'reviewer/printer'
10
19
  require_relative 'reviewer/runner'
20
+ require_relative 'reviewer/shell'
11
21
  require_relative 'reviewer/tool'
12
22
  require_relative 'reviewer/tools'
13
23
  require_relative 'reviewer/version'
@@ -17,21 +27,26 @@ module Reviewer
17
27
  class Error < StandardError; end
18
28
 
19
29
  class << self
20
- attr_writer :configuration
30
+ # Resets the loaded tools
31
+ def reset!
32
+ @tools = nil
33
+ end
21
34
 
22
35
  # Runs the `review` command for the specified tools/files. Reviewer expects all configured
23
36
  # commands that are not disabled to have an entry for the `review` command.
37
+ # @param clear_streen [boolean] clears the screen to reduce noise when true
24
38
  #
25
39
  # @return [void] Prints output to the console
26
- def review
27
- perform(:review)
40
+ def review(clear_screen: false)
41
+ perform(:review, clear_screen: clear_screen)
28
42
  end
29
43
 
30
44
  # Runs the `format` command for the specified tools/files for which it is configured.
45
+ # @param clear_streen [boolean] clears the screen to reduce noise when true
31
46
  #
32
47
  # @return [void] Prints output to the console
33
- def format
34
- perform(:format)
48
+ def format(clear_screen: false)
49
+ perform(:format, clear_screen: clear_screen)
35
50
  end
36
51
 
37
52
  # The collection of arguments that were passed via the command line.
@@ -52,9 +67,16 @@ module Reviewer
52
67
  # The primary output method for Reviewer to consistently display success/failure details for a
53
68
  # unique run of each tool and the collective summary when relevant.
54
69
  #
55
- # @return [Reviewer::Logger] prints formatted output to the command line.
56
- def logger
57
- @logger ||= Logger.new
70
+ # @return [Reviewer::Output] prints formatted output to the console.
71
+ def output
72
+ @output ||= Output.new
73
+ end
74
+
75
+ # A file store for sharing information across runs
76
+ #
77
+ # @return [Reviewer::History] a YAML::Store (or Pstore) containing data on tools
78
+ def history
79
+ @history ||= History.new
58
80
  end
59
81
 
60
82
  # Exposes the configuration options for Reviewer.
@@ -86,22 +108,13 @@ module Reviewer
86
108
  # perform(:review)
87
109
  #
88
110
  # @return [Hash] the exit status (in integer format) for each command run
89
- def perform(command_type)
90
- results = {}
91
-
92
- elapsed_time = Benchmark.realtime do
93
- tools.current.each do |tool|
94
- runner = Runner.new(tool, command_type, logger: logger)
95
- exit_status = runner.run
96
- results[tool.key] = exit_status
97
-
98
- # If a single tool fails, stop there.
99
- break unless exit_status <= tool.max_exit_status
100
- end
101
- end
102
- logger.total_time(elapsed_time)
103
-
104
- results
111
+ def perform(command_type, clear_screen: false)
112
+ system('clear') if clear_screen
113
+
114
+ results = Batch.run(command_type, tools.current)
115
+
116
+ # Return the largest exit status
117
+ exit results.values.max
105
118
  end
106
119
  end
107
120
  end
@@ -1,11 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'slop'
4
+
3
5
  require_relative 'arguments/keywords'
4
6
  require_relative 'arguments/files'
5
7
  require_relative 'arguments/tags'
6
8
 
7
- require 'slop'
8
-
9
9
  module Reviewer
10
10
  # Handles option parsing for `rvw` and `fmt` commands
11
11
  #
@@ -21,45 +21,61 @@ module Reviewer
21
21
  class Arguments
22
22
  attr_accessor :options
23
23
 
24
+ attr_reader :output
25
+
26
+ # A catch all for aguments passed to reviewer via the command-line.
27
+ # @param options = ARGV [Hash] options to parse and extract the relevant values for a run
28
+ #
29
+ # @return [Reviewer::Arguments] the full collection of arguments provided via the command line
24
30
  def initialize(options = ARGV)
31
+ @output = Output.new
25
32
  @options = Slop.parse options do |opts|
26
33
  opts.array '-f', '--files', 'a list of comma-separated files or paths', delimiter: ',', default: []
27
34
  opts.array '-t', '--tags', 'a list of comma-separated tags', delimiter: ',', default: []
28
35
 
29
36
  opts.on '-v', '--version', 'print the version' do
30
- puts VERSION
37
+ @output.info VERSION
31
38
  exit
32
39
  end
33
40
 
34
41
  opts.on '-h', '--help', 'print the help' do
35
- puts opts
42
+ @output.info opts
36
43
  exit
37
44
  end
38
45
  end
39
46
  end
40
47
 
41
- def inspect
48
+ # Converts the arguments to a hash for versatility
49
+ #
50
+ # @return [Hash] The files, tags, and keywords collected from the command line options
51
+ def to_h
42
52
  {
43
53
  files: files.raw,
44
54
  tags: tags.raw,
45
55
  keywords: keywords.raw
46
56
  }
47
57
  end
58
+ alias inspect to_h
48
59
 
60
+ # The tag arguments collected from the command line via the `-t` or `--tags` flag
61
+ #
62
+ # @return [Arguments::Tags] an colelction of the tag arguments collected from the command-line
49
63
  def tags
50
64
  @tags ||= Arguments::Tags.new(provided: options[:tags])
51
65
  end
52
66
 
67
+ # The file arguments collected from the command line via the `-f` or `--files` flag
68
+ #
69
+ # @return [Arguments::Files] an collection of the file arguments collected from the command-line
53
70
  def files
54
71
  @files ||= Arguments::Files.new(provided: options[:files])
55
72
  end
56
73
 
74
+ # The leftover arguments collected from the command line without being associated with a flag
75
+ #
76
+ # @return [Arguments::Keywords] an collection of the leftover arguments as keywords
57
77
  def keywords
58
78
  @keywords ||= Arguments::Keywords.new(options.arguments)
59
79
  end
60
-
61
- def tool_names
62
- @tool_names ||= keywords.for_tool_names.to_a
63
- end
64
80
  end
65
81
  end
@@ -2,34 +2,58 @@
2
2
 
3
3
  module Reviewer
4
4
  class Arguments
5
- # Generates the list of files to run the command against
5
+ # Generates a Ruby-friendly list (Array) of files to run the command against from the provided
6
+ # command line arguments
6
7
  class Files
7
8
  attr_reader :provided, :keywords
8
9
 
9
10
  alias raw provided
10
11
 
12
+ # Generates and instance of files from the provided arguments
13
+ # @param provided: Reviewer.arguments.files.raw [Array, String] file arguments provided
14
+ # directly via the -f or --files flag on the command line.
15
+ # @param keywords: Reviewer.arguments.keywords [Array, String] keywords that can potentially
16
+ # be translated to a list of files (ex. 'staged')
17
+ #
18
+ # @return [Arguments::Files] the container for determining targeted files from the provided
19
+ # command line arguments
11
20
  def initialize(provided: Reviewer.arguments.files.raw, keywords: Reviewer.arguments.keywords)
12
21
  @provided = Array(provided)
13
22
  @keywords = Array(keywords)
14
23
  end
15
24
 
25
+ # Provides the full list of file/path values derived from the command-line arguments
26
+ #
27
+ # @return [Array<String>] full collection of the file arguments as a string
16
28
  def to_a
17
29
  file_list
18
30
  end
19
31
 
32
+ # Provides the full list of file/path values derived from the command-line arguments
33
+ #
34
+ # @return [String] comma-separated string of the derived tag values
20
35
  def to_s
21
36
  to_a.join(',')
22
37
  end
23
38
 
24
- def inspect
39
+ # Summary of the state of the file arguments
40
+ #
41
+ # @return [Hash] represents the summary of the file values parsed from the command-line
42
+ def to_h
25
43
  {
26
- provided: provided,
44
+ provided: provided.sort,
27
45
  from_keywords: from_keywords
28
46
  }
29
47
  end
48
+ alias inspect to_h
30
49
 
31
50
  private
32
51
 
52
+ # Combines the sorted list of unique files/paths by merging the explicitly-provided file
53
+ # arguments as well as those that were translated from any relevant keyword arguments.
54
+ #
55
+ # @return [Array] full list of files/paths passed via command-line including those extracted
56
+ # as a result of a keyword argument like `staged`
33
57
  def file_list
34
58
  @file_list ||= [
35
59
  *provided,
@@ -37,6 +61,10 @@ module Reviewer
37
61
  ].compact.sort.uniq
38
62
  end
39
63
 
64
+ # Converts relevant keywords to the list of files they implicitly represent.
65
+ #
66
+ # @return [Array] list of files/paths translated from any keyword arguments that represent a
67
+ # list of files
40
68
  def from_keywords
41
69
  return [] unless keywords.any?
42
70
 
@@ -44,12 +72,16 @@ module Reviewer
44
72
  next unless respond_to?(keyword.to_sym, true)
45
73
 
46
74
  send(keyword.to_sym)
47
- end.flatten.uniq
75
+ end.flatten.compact.uniq
48
76
  end
49
77
 
78
+ # If `staged` is passed as a keyword via the command-line, this will get the list of staged
79
+ # files via Git
80
+ #
81
+ # @return [Array] list of the currently staged files
50
82
  def staged
51
83
  # Use git for list of staged fields
52
- ::Reviewer::Arguments::Keywords::Git::Staged.list
84
+ ::Reviewer::Keywords::Git::Staged.list
53
85
  end
54
86
  end
55
87
  end
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'keywords/git'
4
-
5
3
  module Reviewer
6
4
  class Arguments
7
5
  # Handles interpreting all 'leftover' arguments and translating them to file-related,
@@ -13,19 +11,34 @@ module Reviewer
13
11
 
14
12
  alias raw provided
15
13
 
14
+ # Generates an instace of parsed keywords from the provided arguments
15
+ # @param *provided [Array<String>] the leftover (non-flag) arguments from the command line
16
+ #
17
+ # @return [Arguments::Keywords]
16
18
  def initialize(*provided)
17
19
  @provided = Array(provided.flatten)
18
20
  end
19
21
 
22
+ # Proves the full list of raw keyword arguments explicitly passed via command-line as an array
23
+ #
24
+ # @return [Array] full collection of the provided keyword arguments as a string
20
25
  def to_a
21
26
  provided
22
27
  end
23
28
 
29
+ # Provides the full list of raw keyword arguments explicitly passed via command-line as a
30
+ # comma-separated string
31
+ #
32
+ # @return [String] comma-separated list of the file arguments as a string
24
33
  def to_s
25
34
  to_a.join(',')
26
35
  end
27
36
 
28
- def inspect
37
+ # Summary of the state of keyword arguments based on how Reviewer parsed them
38
+ #
39
+ # @return [Hash] represents the summary of the keyword values parsed from the command-line and
40
+ # grouped based on how they were parsed
41
+ def to_h
29
42
  {
30
43
  provided: provided,
31
44
  recognized: recognized,
@@ -35,6 +48,7 @@ module Reviewer
35
48
  for_tool_names: for_tool_names
36
49
  }
37
50
  end
51
+ alias inspect to_h
38
52
 
39
53
  # Extracts reserved keywords from the provided arguments
40
54
  #
@@ -82,17 +96,17 @@ module Reviewer
82
96
  #
83
97
  # @return [Array<String>] all unique configured tags
84
98
  def configured_tags
85
- enabled_tools.map(&:tags).flatten.uniq.sort
99
+ tools.enabled.map(&:tags).flatten.uniq.sort
86
100
  end
87
101
 
88
102
  # Provides the complete list of all configured tool names for enabled tools
89
103
  #
90
- # @return [Array<String>] all unique configured and enabled tools
104
+ # @return [Array<String>] all unique configured tools
91
105
  def configured_tool_names
92
106
  # We explicitly don't sort the tool names list because Reviewer uses the configuration order
93
107
  # to determine the execution order. So not sorting maintains the predicted order it will run
94
108
  # in and leaves the option to sort to the consuming code if needed
95
- enabled_tools.map { |tool| tool.key.to_s }.flatten.uniq
109
+ tools.all.map { |tool| tool.key.to_s }
96
110
  end
97
111
 
98
112
  private
@@ -100,8 +114,8 @@ module Reviewer
100
114
  # Provides a collection of enabled Tools for convenient access
101
115
  #
102
116
  # @return [Array<Reviewer::Tool>] collection of all currently enabled tools
103
- def enabled_tools
104
- @enabled_tools ||= Reviewer.tools.enabled
117
+ def tools
118
+ @tools ||= Reviewer.tools
105
119
  end
106
120
 
107
121
  # Syntactic sugar for finding intersections with valid keywords
@@ -109,7 +123,7 @@ module Reviewer
109
123
  #
110
124
  # @return [Array<String>] the list of intersecting values
111
125
  def intersection_with(values)
112
- values.intersection(provided).uniq.sort
126
+ (values & provided).uniq.sort
113
127
  end
114
128
  end
115
129
  end
@@ -8,28 +8,51 @@ module Reviewer
8
8
 
9
9
  alias raw provided
10
10
 
11
+ # Generates an instace of parsed tags from the provided arguments
12
+ # @param provided: Reviewer.arguments.tags.raw [Array<String>] tag arguments provided
13
+ # directly via the -t or --tags flag on the command line.
14
+ # @param keywords: Reviewer.arguments.keywords [Array, String] keywords that can potentially
15
+ # be translated to a list of tags based on the tags used in the configuration file
16
+ #
17
+ # @return [Arguments::Tags] the container for extracting tags from the provided command line
18
+ # arguments
11
19
  def initialize(provided: Reviewer.arguments.tags.raw, keywords: Reviewer.arguments.keywords.for_tags)
12
20
  @provided = Array(provided)
13
21
  @keywords = Array(keywords)
14
22
  end
15
23
 
24
+ # Provides the full list of tags values derived from the command-line arguments
25
+ #
26
+ # @return [Array<String>] full collection of the tag arguments as a string
16
27
  def to_a
17
28
  tag_list
18
29
  end
19
30
 
31
+ # Provides the full list of tag values derived from the command-line arguments
32
+ #
33
+ # @return [String] comma-separated string of the derived tag values
20
34
  def to_s
21
35
  to_a.join(',')
22
36
  end
23
37
 
24
- def inspect
38
+ # Summary of the state of the tag arguments
39
+ #
40
+ # @return [Hash] represents the summary of the tag values parsed from the command-line
41
+ def to_h
25
42
  {
26
- provided: provided,
27
- from_keywords: keywords
43
+ provided: provided.sort,
44
+ from_keywords: keywords.sort
28
45
  }
29
46
  end
47
+ alias inspect to_h
30
48
 
31
49
  private
32
50
 
51
+ # Combines the sorted list of unique tags by merging the explicitly-provided tag arguments
52
+ # as well as those that were recognized from any relevant keyword arguments.
53
+ #
54
+ # @return [Array] full list of tags passed via command-line including those matching keyword
55
+ # arguments
33
56
  def tag_list
34
57
  @tag_list ||= [
35
58
  *provided,
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Reviewer
4
+ # Provides a structure for running commands for a given set of tools
5
+ class Batch
6
+ class UnrecognizedCommandError < ArgumentError; end
7
+
8
+ attr_reader :command_type, :tools, :output, :results
9
+
10
+ def initialize(command_type, tools, output: Reviewer.output)
11
+ @command_type = command_type
12
+ @tools = tools
13
+ @output = output
14
+ @results = {}
15
+ end
16
+
17
+ def run
18
+ benchmark_batch do
19
+ tools.each do |tool|
20
+ runner = Runner.new(tool, command_type, strategy)
21
+
22
+ # With multiple tools, run each one quietly.
23
+ # Otherwise, with just one tool
24
+ runner.run
25
+
26
+ # Record the exit status
27
+ capture_results(runner)
28
+
29
+ # If the tool fails, stop running other tools
30
+ break unless runner.success?
31
+ end
32
+ end
33
+
34
+ results
35
+ end
36
+
37
+ def self.run(*args)
38
+ new(*args).run
39
+ end
40
+
41
+ private
42
+
43
+ def multiple_tools?
44
+ tools.size > 1
45
+ end
46
+
47
+ def strategy
48
+ multiple_tools? ? Runner::Strategies::Quiet : Runner::Strategies::Verbose
49
+ end
50
+
51
+ def capture_results(runner)
52
+ @results[runner.tool.key] = runner.exit_status
53
+ end
54
+
55
+ # Records and prints the total runtime of a block
56
+ # @param &block [type] section of code to be timed
57
+ #
58
+ # @return [void] prints the elapsed time
59
+ def benchmark_batch(&block)
60
+ elapsed_time = Benchmark.realtime(&block)
61
+ output.info "\nTotal Time ".white + "#{elapsed_time.round(1)}s".bold
62
+ end
63
+ end
64
+ end