reviewer 0.1.4 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.alexignore +1 -0
  3. data/.github/workflows/main.yml +5 -3
  4. data/.reviewer.example.yml +20 -10
  5. data/.reviewer.future.yml +221 -0
  6. data/.reviewer.yml +45 -8
  7. data/.reviewer_stdout +0 -0
  8. data/.rubocop.yml +1 -0
  9. data/Gemfile.lock +29 -28
  10. data/LICENSE.txt +4 -20
  11. data/README.md +26 -7
  12. data/lib/reviewer/arguments/files.rb +14 -8
  13. data/lib/reviewer/arguments/keywords.rb +5 -2
  14. data/lib/reviewer/arguments/tags.rb +11 -4
  15. data/lib/reviewer/arguments.rb +11 -4
  16. data/lib/reviewer/batch.rb +41 -14
  17. data/lib/reviewer/command/string/env.rb +4 -0
  18. data/lib/reviewer/command/string/flags.rb +12 -1
  19. data/lib/reviewer/command/string.rb +12 -18
  20. data/lib/reviewer/command.rb +7 -32
  21. data/lib/reviewer/configuration.rb +8 -1
  22. data/lib/reviewer/conversions.rb +0 -11
  23. data/lib/reviewer/guidance.rb +9 -5
  24. data/lib/reviewer/history.rb +32 -1
  25. data/lib/reviewer/keywords/git/staged.rb +16 -0
  26. data/lib/reviewer/output/printer.rb +44 -0
  27. data/lib/reviewer/output/scrubber.rb +48 -0
  28. data/lib/reviewer/output/token.rb +85 -0
  29. data/lib/reviewer/output.rb +73 -43
  30. data/lib/reviewer/runner/strategies/captured.rb +157 -0
  31. data/lib/reviewer/runner/strategies/{verbose.rb → passthrough.rb} +13 -13
  32. data/lib/reviewer/runner.rb +71 -13
  33. data/lib/reviewer/shell/result.rb +22 -7
  34. data/lib/reviewer/shell/timer.rb +15 -0
  35. data/lib/reviewer/shell.rb +7 -11
  36. data/lib/reviewer/tool/settings.rb +12 -5
  37. data/lib/reviewer/tool.rb +25 -3
  38. data/lib/reviewer/version.rb +1 -1
  39. data/lib/reviewer.rb +11 -10
  40. data/reviewer.gemspec +9 -5
  41. data/structure.svg +1 -0
  42. metadata +34 -28
  43. data/.ruby-version +0 -1
  44. data/lib/reviewer/command/string/verbosity.rb +0 -51
  45. data/lib/reviewer/command/verbosity.rb +0 -65
  46. data/lib/reviewer/printer.rb +0 -25
  47. data/lib/reviewer/runner/strategies/quiet.rb +0 -90
@@ -9,14 +9,20 @@ module Reviewer
9
9
 
10
10
  alias raw provided
11
11
 
12
- # Generates and instance of files from the provided arguments
12
+ # Generates an instance of files from the provided arguments
13
13
  # @param provided: Reviewer.arguments.files.raw [Array, String] file arguments provided
14
14
  # directly via the -f or --files flag on the command line.
15
15
  # @param keywords: Reviewer.arguments.keywords [Array, String] keywords that can potentially
16
16
  # be translated to a list of files (ex. 'staged')
17
17
  #
18
- # @return [Arguments::Files] the container for determining targeted files from the provided
19
- # command line arguments
18
+ # @example Using the `-f` flag: `rvw -f ./file.rb`
19
+ # reviewer = Reviewer::Arguments::Files.new(provided: ['./file.rb'], keywords: [])
20
+ # reviewer.to_a # => ['./file.rb']
21
+ # @example Using the `--files` flag: `rvw --files ./file.rb,./directory/file.rb
22
+ # reviewer = Reviewer::Arguments::Files.new(provided: ['./file.rb','./directory/file.rb'], keywords: [])
23
+ # reviewer.to_a # => ['./file.rb','./directory/file.rb']
24
+ #
25
+ # @return [self]
20
26
  def initialize(provided: Reviewer.arguments.files.raw, keywords: Reviewer.arguments.keywords)
21
27
  @provided = Array(provided)
22
28
  @keywords = Array(keywords)
@@ -38,7 +44,7 @@ module Reviewer
38
44
 
39
45
  # Summary of the state of the file arguments
40
46
  #
41
- # @return [Hash] represents the summary of the file values parsed from the command-line
47
+ # @return [Hash<Symbol, Array<String>>] summarizes all of the resulting file values
42
48
  def to_h
43
49
  {
44
50
  provided: provided.sort,
@@ -50,7 +56,7 @@ module Reviewer
50
56
  private
51
57
 
52
58
  # 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.
59
+ # arguments as well as those that were translated from any relevant keyword arguments.
54
60
  #
55
61
  # @return [Array] full list of files/paths passed via command-line including those extracted
56
62
  # as a result of a keyword argument like `staged`
@@ -63,8 +69,8 @@ module Reviewer
63
69
 
64
70
  # Converts relevant keywords to the list of files they implicitly represent.
65
71
  #
66
- # @return [Array] list of files/paths translated from any keyword arguments that represent a
67
- # list of files
72
+ # @return [Array<String>] list of files/paths translated from any keyword arguments that
73
+ # represent a list of files
68
74
  def from_keywords
69
75
  return [] unless keywords.any?
70
76
 
@@ -76,7 +82,7 @@ module Reviewer
76
82
  end
77
83
 
78
84
  # If `staged` is passed as a keyword via the command-line, this will get the list of staged
79
- # files via Git
85
+ # files via Git
80
86
  #
81
87
  # @return [Array] list of the currently staged files
82
88
  def staged
@@ -4,6 +4,9 @@ module Reviewer
4
4
  class Arguments
5
5
  # Handles interpreting all 'leftover' arguments and translating them to file-related,
6
6
  # tag-related, or tool-related arguments
7
+ #
8
+ # @!attribute provided
9
+ # @return [Array<String>] the keywords extracted from the command-line arguments
7
10
  class Keywords
8
11
  RESERVED = %w[staged].freeze
9
12
 
@@ -11,10 +14,10 @@ module Reviewer
11
14
 
12
15
  alias raw provided
13
16
 
14
- # Generates an instace of parsed keywords from the provided arguments
17
+ # Generates an instance of parsed keywords from the provided arguments
15
18
  # @param *provided [Array<String>] the leftover (non-flag) arguments from the command line
16
19
  #
17
- # @return [Arguments::Keywords]
20
+ # @return [self]
18
21
  def initialize(*provided)
19
22
  @provided = Array(provided.flatten)
20
23
  end
@@ -8,14 +8,21 @@ module Reviewer
8
8
 
9
9
  alias raw provided
10
10
 
11
- # Generates an instace of parsed tags from the provided arguments
11
+ # Generates an instace of parsed tags from the provided arguments by merging tag arguments
12
+ # that were provided via either flags or keywords
12
13
  # @param provided: Reviewer.arguments.tags.raw [Array<String>] tag arguments provided
13
14
  # directly via the -t or --tags flag on the command line.
14
15
  # @param keywords: Reviewer.arguments.keywords [Array, String] keywords that can potentially
15
16
  # be translated to a list of tags based on the tags used in the configuration file
16
17
  #
17
- # @return [Arguments::Tags] the container for extracting tags from the provided command line
18
- # arguments
18
+ # @example Using keywords: `rvw ruby` (assuming a 'ruby' tag is defined)
19
+ # Reviewer::Arguments::Tags.new.to_a # => ['ruby']
20
+ # @example Using the `-t` flag: `rvw -t ruby`
21
+ # Reviewer::Arguments::Tags.new.to_a # => ['ruby']
22
+ # @example Using the `--tags` flag: `rvw -t ruby,css`
23
+ # Reviewer::Arguments::Tags.new.to_a # => ['css', 'ruby']
24
+ #
25
+ # @return [self]
19
26
  def initialize(provided: Reviewer.arguments.tags.raw, keywords: Reviewer.arguments.keywords.for_tags)
20
27
  @provided = Array(provided)
21
28
  @keywords = Array(keywords)
@@ -49,7 +56,7 @@ module Reviewer
49
56
  private
50
57
 
51
58
  # 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.
59
+ # as well as those that were recognized from any relevant keyword arguments.
53
60
  #
54
61
  # @return [Array] full list of tags passed via command-line including those matching keyword
55
62
  # arguments
@@ -23,10 +23,17 @@ module Reviewer
23
23
 
24
24
  attr_reader :output
25
25
 
26
- # A catch all for aguments passed to reviewer via the command-line.
26
+ # A catch all for aguments passed to reviewer via the command-line so they can be interpreted
27
+ # and made available via the relevant classes.
27
28
  # @param options = ARGV [Hash] options to parse and extract the relevant values for a run
28
29
  #
29
- # @return [Reviewer::Arguments] the full collection of arguments provided via the command line
30
+ # @example Using all options: `rvw keyword_one keyword_two --files ./example.rb,./example_test.rb --tags syntax`
31
+ # reviewer = Reviewer::Arguments.new
32
+ # reviewer.files.to_a # => ['./example.rb','./example_test.rb']
33
+ # reviewer.tags.to_a # => ['syntax']
34
+ # reviewer.keywords.to_a # => ['keyword_one', 'keyword_two']
35
+ #
36
+ # @return [self]
30
37
  def initialize(options = ARGV)
31
38
  @output = Output.new
32
39
  @options = Slop.parse options do |opts|
@@ -34,12 +41,12 @@ module Reviewer
34
41
  opts.array '-t', '--tags', 'a list of comma-separated tags', delimiter: ',', default: []
35
42
 
36
43
  opts.on '-v', '--version', 'print the version' do
37
- @output.info VERSION
44
+ @output.help VERSION
38
45
  exit
39
46
  end
40
47
 
41
48
  opts.on '-h', '--help', 'print the help' do
42
- @output.info opts
49
+ @output.help opts
43
50
  exit
44
51
  end
45
52
  end
@@ -7,6 +7,12 @@ module Reviewer
7
7
 
8
8
  attr_reader :command_type, :tools, :output, :results
9
9
 
10
+ # Generates an instance of Batch for running multiple tools together
11
+ # @param command_type [Symbol] the type of command to run for each tool.
12
+ # @param tools [Array<Tool>] the tools to run the commands for
13
+ # @param output: Reviewer.output [Output] the output channel to print results to
14
+ #
15
+ # @return [self]
10
16
  def initialize(command_type, tools, output: Reviewer.output)
11
17
  @command_type = command_type
12
18
  @tools = tools
@@ -14,17 +20,19 @@ module Reviewer
14
20
  @results = {}
15
21
  end
16
22
 
23
+ # Iterates over the tools in the batch to successfully run the commands. Also times the entire
24
+ # batch in order to provide a total execution time.
25
+ #
26
+ # @return [Results] the results summary for all commands run
17
27
  def run
18
28
  benchmark_batch do
19
- tools.each do |tool|
29
+ matching_tools.each do |tool|
30
+ # Create and execute a runner for the given tool, command type, and strategy
20
31
  runner = Runner.new(tool, command_type, strategy)
21
-
22
- # With multiple tools, run each one quietly.
23
- # Otherwise, with just one tool
24
32
  runner.run
25
33
 
26
- # Record the exit status
27
- capture_results(runner)
34
+ # Record the exit status for this tool
35
+ record_exit_status(runner)
28
36
 
29
37
  # If the tool fails, stop running other tools
30
38
  break unless runner.success?
@@ -34,10 +42,6 @@ module Reviewer
34
42
  results
35
43
  end
36
44
 
37
- def self.run(*args)
38
- new(*args).run
39
- end
40
-
41
45
  private
42
46
 
43
47
  def multiple_tools?
@@ -45,11 +49,22 @@ module Reviewer
45
49
  end
46
50
 
47
51
  def strategy
48
- multiple_tools? ? Runner::Strategies::Quiet : Runner::Strategies::Verbose
52
+ multiple_tools? ? Runner::Strategies::Captured : Runner::Strategies::Passthrough
49
53
  end
50
54
 
51
- def capture_results(runner)
52
- @results[runner.tool.key] = runner.exit_status
55
+ # Notes the exit status for the runner based on whether the runner was considered successful or
56
+ # not based on the configured `max_exit_status` for the tool. For example, some tools use exit
57
+ # status to convey significance. So even though it returns a non-zero exit status like 2, it
58
+ # can still be successful.
59
+ # @param runner [Runner] the instance of the runner that's being inspected
60
+ #
61
+ # @return [Integer] the adjusted exit status for the runner
62
+ def record_exit_status(runner)
63
+ # Since some tools can "succeed" with a positive exit status, the overall batch is only
64
+ # interested in subjective failure. So if the runner succeeded according to the tool's max
65
+ # exit status, it should record the tool's run as a success for the purposes of the larger
66
+ # batch success/failure
67
+ @results[runner.tool.key] = runner.success? ? 0 : runner.exit_status
53
68
  end
54
69
 
55
70
  # Records and prints the total runtime of a block
@@ -58,7 +73,19 @@ module Reviewer
58
73
  # @return [void] prints the elapsed time
59
74
  def benchmark_batch(&block)
60
75
  elapsed_time = Benchmark.realtime(&block)
61
- output.info "\nTotal Time ".white + "#{elapsed_time.round(1)}s".bold
76
+
77
+ # If there's failures, skip showing the total time to focus on the issues
78
+ return if @results.values.sum.positive?
79
+
80
+ output.batch_summary(results.size, elapsed_time)
81
+ end
82
+
83
+ # Returns the set of tools matching the provided command. So when formatting, if a tool does not
84
+ # have a format command, then it will be skipped.
85
+ #
86
+ # @return [Array<Tool>] the enabled tools that support the provided command
87
+ def matching_tools
88
+ tools.select { |tool| tool.settings.commands.key?(command_type) }
62
89
  end
63
90
  end
64
91
  end
@@ -7,6 +7,10 @@ module Reviewer
7
7
  class Env
8
8
  attr_reader :env_pairs
9
9
 
10
+ # Creates an instance of env variables for a tool to help generate the command string
11
+ # @param env_pairs [Hash] [description]
12
+ #
13
+ # @return [self]
10
14
  def initialize(env_pairs)
11
15
  @env_pairs = env_pairs
12
16
  end
@@ -3,18 +3,29 @@
3
3
  module Reviewer
4
4
  class Command
5
5
  class String
6
- # Assembles tool flag settings into a single string or array
6
+ # Translates tool flag settings from the tool's configuration values into a single string or
7
+ # array that can be used to generate the command string
7
8
  class Flags
8
9
  attr_reader :flag_pairs
9
10
 
11
+ # Creates an instance of command-string friendly flags
12
+ # @param flag_pairs [Hash] the flags (keys) and their values
13
+ #
14
+ # @return [self]
10
15
  def initialize(flag_pairs)
11
16
  @flag_pairs = flag_pairs
12
17
  end
13
18
 
19
+ # Creates a string-friendly format to use in a command
20
+ #
21
+ # @return [String] a string of flags that can be safely passed to a command
14
22
  def to_s
15
23
  to_a.join(' ')
16
24
  end
17
25
 
26
+ # Creates an array of all flag name/value pairs
27
+ #
28
+ # @return [Array<String>] array of all flag strings to use to when running the command
18
29
  def to_a
19
30
  flags = []
20
31
  flag_pairs.each { |key, value| flags << flag(key, value) }
@@ -2,20 +2,18 @@
2
2
 
3
3
  require_relative 'string/env'
4
4
  require_relative 'string/flags'
5
- require_relative 'string/verbosity'
6
5
 
7
6
  module Reviewer
8
7
  class Command
9
- # Assembles tool tool_settings into a usable command string for the command type and verbosity
8
+ # Assembles tool tool_settings into a usable command string for the command type
10
9
  class String
11
10
  include Conversions
12
11
 
13
- attr_reader :command_type, :tool_settings, :verbosity
12
+ attr_reader :command_type, :tool_settings
14
13
 
15
- def initialize(command_type, tool_settings:, verbosity: nil)
14
+ def initialize(command_type, tool_settings:)
16
15
  @command_type = command_type
17
16
  @tool_settings = tool_settings
18
- @verbosity = Verbosity(verbosity)
19
17
  end
20
18
 
21
19
  def to_s
@@ -29,11 +27,13 @@ module Reviewer
29
27
  [
30
28
  env_variables,
31
29
  body,
32
- flags,
33
- verbosity_options
30
+ flags
34
31
  ].compact
35
32
  end
36
33
 
34
+ # The string of environment variables built from a tool's configuration settings
35
+ #
36
+ # @return [String] the environment variable names and values concatened for the command
37
37
  def env_variables
38
38
  Env.new(tool_settings.env).to_s
39
39
  end
@@ -42,23 +42,17 @@ module Reviewer
42
42
  tool_settings.commands.fetch(command_type)
43
43
  end
44
44
 
45
+ # Gets the flags to be used in conjunction with the review command for a tool
46
+ # 1. The `review` commands are the only commands that use flags
47
+ # 2. If no flags are configured, this won't do anything
48
+ #
49
+ # @return [String] the concatenated list of flags to pass to the review command
45
50
  def flags
46
- # Flags to be used for `review` commands.
47
- # 1. The `review` commands are the only commands that use flags
48
- # 2. If no flags are configured, this won't do much
49
- #
50
- # Note: Since verbosity is handled separately, flags for 'quiet' are handled separately at a
51
- # lower level by design and excluded from this check. They are not included with the other
52
- # configured flags.
53
51
  return nil unless flags?
54
52
 
55
53
  Flags.new(tool_settings.flags).to_s
56
54
  end
57
55
 
58
- def verbosity_options
59
- Verbosity.new(tool_settings.quiet_option, level: verbosity.level).to_s
60
- end
61
-
62
56
  private
63
57
 
64
58
  # Determines whether the string needs flags added
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative 'command/string'
4
- require_relative 'command/verbosity'
5
4
 
6
5
  module Reviewer
7
6
  # The core funtionality to translate a tool, command type, and verbosity into a runnable command
@@ -10,19 +9,19 @@ module Reviewer
10
9
 
11
10
  SEED_SUBSTITUTION_VALUE = '$SEED'
12
11
 
13
- attr_reader :tool, :type
12
+ attr_reader :tool
13
+
14
+ attr_accessor :type
14
15
 
15
16
  # Creates an instance of the Command class to synthesize a command string using the tool,
16
17
  # command type, and verbosity.
17
18
  # @param tool [Tool, Symbol] a tool or tool key to use to look up the command and options
18
19
  # @param type [Symbol] the desired command type (:install, :prepare, :review, :format)
19
- # @param verbosity = Verbosity::TOTAL_SILENCE [Symbol] the desired verbosity for the command
20
20
  #
21
21
  # @return [Command] the intersection of a tool, command type, and verbosity
22
- def initialize(tool, type, verbosity = Verbosity::TOTAL_SILENCE)
22
+ def initialize(tool, type)
23
23
  @tool = Tool(tool)
24
24
  @type = type.to_sym
25
- @verbosity = Verbosity(verbosity)
26
25
  end
27
26
 
28
27
  # The final command string with all of the conditions appled
@@ -33,26 +32,6 @@ module Reviewer
33
32
  end
34
33
  alias to_s string
35
34
 
36
- # Getter for @verbosity. Since the setter is custom, the getter needs to be explicitly declared.
37
- # Otherwise, using `attr_accessor` and then overriding the setter muddies the waters.
38
- #
39
- # @return [Verbosity] the current verbosity setting for the command
40
- def verbosity # rubocop:disable Style/TrivialAccessors
41
- @verbosity
42
- end
43
-
44
- # Override verbosity assignment to clear the related memoized values when verbosity changes
45
- # @param verbosity [Verbosity, Symbol] the desired verbosity for the command
46
- #
47
- # @return [Verbosity] the updated verbosity level for the command
48
- def verbosity=(verbosity)
49
- # Unmemoize string since the verbosity has been changed
50
- @raw_string = nil
51
- @string = nil
52
-
53
- @verbosity = Verbosity(verbosity)
54
- end
55
-
56
35
  # Generates a seed that can be re-used across runs so that the results are consistent across
57
36
  # related runs for tools that would otherwise change the seed automatically every run.
58
37
  # Since not all tools will use the seed, there's no need to generate it in the initializer.
@@ -68,20 +47,16 @@ module Reviewer
68
47
  @seed
69
48
  end
70
49
 
71
- private
72
-
73
50
  # The raw command string before any substitutions. For example, since seeds need to remain
74
51
  # consistent from one run to the next, they're
75
52
  #
76
53
  # @return [type] [description]
77
54
  def raw_string
78
- @raw_string ||= String.new(
79
- type,
80
- tool_settings: tool.settings,
81
- verbosity: verbosity
82
- ).to_s
55
+ @raw_string ||= String.new(type, tool_settings: tool.settings).to_s
83
56
  end
84
57
 
58
+ private
59
+
85
60
  # The version of the command with the SEED_SUBSTITUTION_VALUE replaced
86
61
  #
87
62
  # @return [String] the command string with the SEED_SUBSTITUTION_VALUE replaced
@@ -4,6 +4,14 @@ require 'pathname'
4
4
 
5
5
  module Reviewer
6
6
  # Configuration values container for Reviewer
7
+ #
8
+ # @!attribute file
9
+ # @return [Pathname] the pathname for the primary configuraton file
10
+ # @!attribute history_file
11
+ # @return [Pathname] the pathname for the history file to store data across runs
12
+ #
13
+ # @author [garrettdimon]
14
+ #
7
15
  class Configuration
8
16
  DEFAULT_PATH = Dir.pwd.freeze
9
17
 
@@ -18,7 +26,6 @@ module Reviewer
18
26
  def initialize
19
27
  @file = Pathname(DEFAULT_CONFIG_LOCATION)
20
28
  @history_file = Pathname(DEFAULT_HISTORY_LOCATION)
21
- @printer = ::Reviewer::Printer.new
22
29
 
23
30
  # Future Configuration Options:
24
31
  # - seed_substitution_value(string): Currently a constant of `$SEED` in Reviewer::Command, but
@@ -12,16 +12,5 @@ module Reviewer
12
12
  end
13
13
  end
14
14
  module_function :Tool
15
-
16
- def Verbosity(value) # rubocop:disable Naming/MethodName
17
- case value
18
- when Command::Verbosity then value
19
- when Symbol then Command::Verbosity.new(value)
20
- when String then Command::Verbosity.new(value.to_sym)
21
- when Integer then Command::Verbosity.new(Command::Verbosity::LEVELS[value])
22
- else raise TypeError, "Cannot convert #{value} to Verbosity"
23
- end
24
- end
25
- module_function :Verbosity
26
15
  end
27
16
  end
@@ -50,7 +50,13 @@ module Reviewer
50
50
  #
51
51
  # @return [void] prints missing executable guidance
52
52
  def show_missing_executable_guidance
53
- output.missing_executable_guidance(command)
53
+ tool = command.tool
54
+ installation_command = Command.new(tool, :install).string if tool.installable?
55
+ install_link = tool.install_link
56
+
57
+ output.failure("Missing executable for '#{tool}'", command: command)
58
+ output.guidance('Try installing the tool:', installation_command)
59
+ output.guidance('Read the installation guidance:', install_link)
54
60
  end
55
61
 
56
62
  # Shows the recovery guidance for when a command generates an unrecoverable error
@@ -64,10 +70,8 @@ module Reviewer
64
70
  #
65
71
  # @return [void] prints syntax guidance
66
72
  def show_syntax_guidance
67
- output.syntax_guidance(
68
- ignore_link: command.tool.links[:ignore_syntax],
69
- disable_link: command.tool.links[:disable_syntax]
70
- )
73
+ output.guidance('Selectively Ignore a Rule:', command.tool.links[:ignore_syntax])
74
+ output.guidance('Fully Disable a Rule:', command.tool.links[:disable_syntax])
71
75
  end
72
76
  end
73
77
  end
@@ -3,15 +3,35 @@
3
3
  require 'yaml/store'
4
4
 
5
5
  module Reviewer
6
- # Provides an instance of a storage resource for persisting data across runs
6
+ # Provides an interface to a local storage resource for persisting data across runs. For example,
7
+ # it enables remembering when `prepare` commands were run for reviews so they can be run less
8
+ # frequently and thus improve performance.
9
+ #
10
+ # It also enables remembering seeds across runs. Eventually `rvw rerun` could reuse the seeds from
11
+ # the immediately preceding run to more easily facilitate fixing tests that are accidentally
12
+ # order-dependent. Or it could automatically record a list of seeds that led to failures.
13
+ #
14
+ # Long term, it could serve to record timing details across runs to provide insight to min, max,
15
+ # and means. Those times could then be used for reviewer to make more informed decisions about
16
+ # default behavior to ensure each run remains fast.
7
17
  class History
8
18
  attr_reader :file, :store
9
19
 
20
+ # Creates an instance of a YAML::Store-backed history file.
21
+ # @param file = Reviewer.configuration.history_file [Pathname] the history file to store data
22
+ #
23
+ # @return [History] an instance of history
10
24
  def initialize(file = Reviewer.configuration.history_file)
11
25
  @file = file
12
26
  @store = YAML::Store.new(file)
13
27
  end
14
28
 
29
+ # Saves a value to a given location in the history
30
+ # @param group [Symbol] the first-level key to use for saving the value--frequently a tool name
31
+ # @param attribute [Symbol] the second-level key to use for retrieving the value
32
+ # @param value [Primitive] any value that can be cleanly stored in YAML
33
+ #
34
+ # @return [Primitive] the value being stored
15
35
  def set(group, attribute, value)
16
36
  store.transaction do |s|
17
37
  s[group] = {} if s[group].nil?
@@ -19,18 +39,29 @@ module Reviewer
19
39
  end
20
40
  end
21
41
 
42
+ # Retrieves a stored value from the history file
43
+ # @param group [Symbol] the first-level key to use for retrieving the value
44
+ # @param attribute [Symbol] the second-level key to use for retrieving the value
45
+ #
46
+ # @return [Primitive] the value being stored
22
47
  def get(group, attribute)
23
48
  store.transaction do |s|
24
49
  s[group].nil? ? nil : s[group][attribute]
25
50
  end
26
51
  end
27
52
 
53
+ # Removes the existing history file.
54
+ #
55
+ # @return [void]
28
56
  def reset!
29
57
  return unless File.exist?(file)
30
58
 
31
59
  FileUtils.rm(file)
32
60
  end
33
61
 
62
+ # Convenience class method for removing the history file.
63
+ #
64
+ # @return [void]
34
65
  def self.reset!
35
66
  new.reset!
36
67
  end
@@ -17,6 +17,12 @@ module Reviewer
17
17
  stdout.strip.empty? ? [] : stdout.split("\n")
18
18
  end
19
19
 
20
+ # Gets the list of staged files
21
+ #
22
+ # @example Get the list of files
23
+ # staged.list #=> ['/Code/example.rb', '/Code/run.rb']
24
+ #
25
+ # @return [Array<String>] the array of staged filenames as strings
20
26
  def list
21
27
  @stdout, @stderr, @status = Open3.capture3(command)
22
28
  @exit_status = @status.exitstatus.to_i
@@ -24,10 +30,20 @@ module Reviewer
24
30
  @status.success? ? to_a : raise_command_line_error
25
31
  end
26
32
 
33
+ # Convenience method for retrieving the list of staged files since there's no parameters
34
+ # for an initializer.
35
+ #
36
+ # @example Get the list of files
37
+ # Reviewer::Keywords::Git::Staged.list #=> ['/Code/example.rb', '/Code/run.rb']
38
+ #
39
+ # @return [Array<String>] the array of staged filenames as strings
27
40
  def self.list
28
41
  new.list
29
42
  end
30
43
 
44
+ # Assembles the pieces of the command that gets the list of staged files
45
+ #
46
+ # @return [String] the full command to run to retrieve the list of staged files
31
47
  def command
32
48
  command_parts.join(' ')
33
49
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'io/console' # For determining console width/height
4
+
5
+ module Reviewer
6
+ class Output
7
+ # Wrapper to encapsulate some lower-level details of printing to $stdout
8
+ class Printer
9
+ attr_reader :stream
10
+
11
+ # Creates an instance of Output to print Reviewer activity and results to the console
12
+ def initialize(stream = $stdout)
13
+ @stream = stream.tap do |str|
14
+ # If the IO channel supports flushing the output immediately, then ensure it's enabled
15
+ str.sync = str.respond_to?(:sync=)
16
+ end
17
+ end
18
+
19
+ def print(style, content)
20
+ text(style, content)
21
+ end
22
+
23
+ def puts(style, content)
24
+ text(style, content)
25
+ stream.puts
26
+ end
27
+
28
+ def tty?
29
+ stream.tty?
30
+ end
31
+ alias style_enabled? tty?
32
+
33
+ private
34
+
35
+ def text(style, content)
36
+ if style_enabled?
37
+ stream.print Token.new(style, content).to_s
38
+ else
39
+ stream.print content
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end