Skip to content

Commit

Permalink
Add more detailed output with --verbose flag (#25)
Browse files Browse the repository at this point in the history
* Keep track of group line numbers in Parser

* Add `line_number` in Parser doc

* Add `--verbose` flag

* Print verbose changes with flag

* Update README

* Make output a little prettier to read
  • Loading branch information
nshki authored Oct 30, 2021
1 parent c911cc4 commit 58e93d6
Show file tree
Hide file tree
Showing 7 changed files with 138 additions and 60 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,10 @@ Chusaku has some flags available for use as well:
```
$ bundle exec chusaku --help
Usage: chusaku [options]
--dry-run Run without file modifications
--exit-with-error-on-annotation
Fail if any file was annotated
--dry-run Run without file modifications
--verbose Print all annotations
-v, --version Show Chusaku version number and quit
-h, --help Show this help message and quit
```
Expand Down
88 changes: 63 additions & 25 deletions lib/chusaku.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class << self
def call(flags = {})
@flags = flags
@routes = Chusaku::Routes.call
@annotated_paths = []
@changes = []
controllers_pattern = 'app/controllers/**/*_controller.rb'

Dir.glob(Rails.root.join(controllers_pattern)).each do |path|
Expand All @@ -45,18 +45,46 @@ def call(flags = {})
def annotate_file(path:, controller:, actions:)
parsed_file = Chusaku::Parser.call(path: path, actions: actions)
parsed_file[:groups].each_cons(2) do |prev, curr|
clean_group(prev)
record_change(group: prev, type: :clean, path: path)
next unless curr[:type] == :action

route_data = @routes[controller][curr[:action]]
next unless route_data.any?

annotate_group(group: curr, route_data: route_data)
record_change(group: curr, type: :annotate, route_data: route_data, path: path)
end

write_to_file(path: path, parsed_file: parsed_file)
end

# Clean or annotate a group and track the group as changed if applicable.
#
# @param group [Hash] { type => Symbol, body => String }
# @param type [Symbol] [:clean, :annotate]
# @param path [String] File path
# @param route_data [Array<Hash>] [{
# verb: String,
# path: String,
# name: String }]
# @return [void]
def record_change(group:, type:, path:, route_data: [])
old_body = group[:body]

case type
when :clean
clean_group(group)
when :annotate
annotate_group(group: group, route_data: route_data)
end
return if old_body == group[:body]

@changes.push \
old_body: old_body,
new_body: group[:body],
path: path,
line_number: group[:line_number]
end

# Given a parsed group, clean out its contents.
#
# @param group [Hash] { type => Symbol, body => String }
Expand Down Expand Up @@ -112,10 +140,9 @@ def annotate_route(verb:, path:, name:, defaults:)
# @return [void]
def write_to_file(path:, parsed_file:)
new_content = new_content_for(parsed_file)
return unless parsed_file[:content] != new_content
return if parsed_file[:content] == new_content

!@flags.include?(:dry) && perform_write(path: path, content: new_content)
@annotated_paths.push(path)
end

# Extracts the new file content for the given parsed file.
Expand Down Expand Up @@ -156,33 +183,44 @@ def file_mode
def output_results
puts(output_copy)
exit_code = 0
exit_code = 1 if @annotated_paths.any? && @flags.include?(:error_on_annotation)
exit_code = 1 if @changes.any? && @flags.include?(:error_on_annotation)
exit_code
end

# Determines the copy to be used in the program output.
#
# @return [String] Copy to be outputted to user
def output_copy
return 'Nothing to annotate.' if @annotated_paths.empty?

annotated_paths = @annotated_paths.join(', ')
dry_run = @flags.include?(:dry)
error_on_annotation = @flags.include?(:error_on_annotation)

if dry_run && error_on_annotation
<<~COPY
Annotations missing in the following files: #{annotated_paths}
Run `chusaku` to annotate them. Exiting with status code 1.
COPY
elsif dry_run
"The following files would be annotated without `--dry-run`: #{annotated_paths}"
elsif error_on_annotation
"Annotated #{annotated_paths}.\n\nExiting with status code 1."
else
"Annotated #{annotated_paths}."
end
return 'Nothing to annotate.' if @changes.empty?

copy = changes_copy
copy += "\nChusaku has finished running."
copy += "\nThis was a dry run so no files were changed." if @flags.include?(:dry)
copy += "\nExited with status code 1." if @flags.include?(:error_on_annotation)
copy
end

# Returns the copy for recorded changes if `--verbose` flag is passed.
#
# @return [String] Copy of recorded changes
def changes_copy
return '' unless @flags.include?(:verbose)

@changes.map do |change|
<<~CHANGE_OUTPUT
[#{change[:path]}:#{change[:line_number]}]
Before:
```ruby
#{change[:old_body].chomp}
```
After:
```ruby
#{change[:new_body].chomp}
```
CHANGE_OUTPUT
end.join("\n")
end
end
end
41 changes: 19 additions & 22 deletions lib/chusaku/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,36 +54,41 @@ def check_for_rails_project
def optparser
OptionParser.new do |opts|
opts.banner = 'Usage: chusaku [options]'
add_error_on_annotation_flag(opts)
add_dry_run_flag(opts)
add_error_on_annotation_flag(opts)
add_verbose_flag(opts)
add_version_flag(opts)
add_help_flag(opts)
end
end

# Adds `--dry-run` flag.
#
# @param opts [OptionParser] OptionParser instance
# @return [void]
def add_dry_run_flag(opts)
opts.on('--dry-run', 'Run without file modifications') do
@options[:dry] = true
end
end

# Adds `--exit-with-error-on-annotation` flag.
#
# @param opts [OptionParser] OptionParser instance
# @return [void]
def add_error_on_annotation_flag(opts)
opts.on(
'--exit-with-error-on-annotation',
'Fail if any file was annotated'
) do
opts.on('--exit-with-error-on-annotation', 'Fail if any file was annotated') do
@options[:error_on_annotation] = true
end
end

# Adds `--dry-run` flag.
# Adds `--verbose` flag.
#
# @param opts [OptionParser] OptionParser instance
# @return [void]
def add_dry_run_flag(opts)
opts.on(
'--dry-run',
'Run without file modifications'
) do
@options[:dry] = true
def add_verbose_flag(opts)
opts.on('--verbose', 'Print all annotations') do
@options[:verbose] = true
end
end

Expand All @@ -92,11 +97,7 @@ def add_dry_run_flag(opts)
# @param opts [OptionParser] OptionParser instance
# @return [void]
def add_version_flag(opts)
opts.on(
'-v',
'--version',
'Show Chusaku version number and quit'
) do
opts.on('-v', '--version', 'Show Chusaku version number and quit') do
puts(Chusaku::VERSION)
raise Finished
end
Expand All @@ -107,11 +108,7 @@ def add_version_flag(opts)
# @param opts [OptionParser] OptionParser instance
# @return [void]
def add_help_flag(opts)
opts.on(
'-h',
'--help',
'Show this help message and quit'
) do
opts.on('-h', '--help', 'Show this help message and quit') do
puts(opts)
raise Finished
end
Expand Down
16 changes: 10 additions & 6 deletions lib/chusaku/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,22 +13,26 @@ module Parser
# {
# type: :code,
# body: 'class Foo\n',
# action: nil
# action: nil,
# line_number: 1
# },
# {
# type: :comment,
# body: ' # Bar\n # Baz\n',
# action: nil
# action: nil,
# line_number: 2
# },
# {
# type: :action,
# body: ' def action_name; end\n',
# action: 'action_name'
# action: 'action_name',
# line_number: 4
# }
# {
# type: :code,
# body: 'end # vanilla is the best flavor\n',
# action: nil
# action: nil,
# line_number: 5
# }
# ]
# }
Expand All @@ -41,7 +45,7 @@ def self.call(path:, actions:)
group = {}
content = IO.read(path)

content.each_line do |line|
content.each_line.with_index do |line, index|
parsed_line = parse_line(line: line, actions: actions)

if group[:type] == parsed_line[:type]
Expand All @@ -51,7 +55,7 @@ def self.call(path:, actions:)
# Now looking at a new group. Push the current group onto the array
# and start a new one.
groups.push(group) unless group.empty?
group = parsed_line
group = parsed_line.merge(line_number: index + 1)
end
end

Expand Down
37 changes: 31 additions & 6 deletions test/chusaku_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ def test_dry_run_flag

assert_equal(0, exit_code)
assert_empty(File.written_files)
assert_includes(out, 'would be annotated')
assert_includes(out, 'This was a dry run so no files were changed.')
end

def test_exit_with_error_on_annotation_flag
Expand All @@ -22,7 +22,7 @@ def test_exit_with_error_on_annotation_flag

assert_equal(1, exit_code)
assert_equal(2, File.written_files.count)
assert_includes(out, 'status code 1')
assert_includes(out, 'Exited with status code 1.')
end

def test_dry_run_and_exit_with_error_flag
Expand All @@ -33,21 +33,46 @@ def test_dry_run_and_exit_with_error_flag

assert_equal(1, exit_code)
assert_empty(File.written_files)
assert_includes(out, 'Annotations missing')
assert_includes(out, 'status code 1')
assert_includes(out, 'This was a dry run so no files were changed.')
assert_includes(out, 'Exited with status code 1.')
end

def test_verbose_flag
File.reset_mock
exit_code = 0

out, _err = capture_io { exit_code = Chusaku.call(verbose: true) }

assert_equal(0, exit_code)
assert_includes \
out,
<<~CHANGES_COPY
[test/mock/app/controllers/api/tacos_controller.rb:4]
Before:
```ruby
def show; end
```
After:
```ruby
# @route GET / (root)
# @route GET /api/tacos/:id (taco)
def show; end
```
CHANGES_COPY
end

def test_mock_app
File.reset_mock
exit_code = 0

out, _err = capture_io { exit_code = Chusaku.call }
capture_io { exit_code = Chusaku.call }
files = File.written_files
base_path = 'test/mock/app/controllers'

assert_equal(0, exit_code)
assert(2, files.count)
assert_includes(out, 'Annotated')
refute_includes(files, "#{base_path}/api/burritos_controller.rb")

expected =
Expand Down
10 changes: 10 additions & 0 deletions test/cli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,14 @@ def test_exit_with_error_on_annotation_flag
assert_equal({ error_on_annotation: true }, cli.options)
end
end

def test_verbose_flag
cli = Chusaku::CLI.new
cli.stub(:check_for_rails_project, nil) do
capture_io do
assert_equal(0, cli.call(['--verbose']))
end
assert_equal({ verbose: true }, cli.options)
end
end
end
3 changes: 3 additions & 0 deletions test/parser_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ def test_example_file
assert_equal \
[nil, nil, nil, 'foo', nil],
(result[:groups].map { |r| r[:action] })
assert_equal \
[1, 2, 4, 6, 7],
(result[:groups].map { |r| r[:line_number] })
end

def test_empty_file
Expand Down

0 comments on commit 58e93d6

Please sign in to comment.