chusaku 0.3.2 → 0.6.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.
- checksums.yaml +4 -4
- data/.github/workflows/linting.yml +18 -0
- data/.github/workflows/testing.yml +17 -0
- data/.rubocop.yml +14 -0
- data/README.md +3 -4
- data/chusaku.gemspec +10 -10
- data/lib/chusaku/cli.rb +34 -37
- data/lib/chusaku/parser.rb +26 -18
- data/lib/chusaku/routes.rb +45 -23
- data/lib/chusaku/version.rb +1 -1
- data/lib/chusaku.rb +141 -45
- metadata +21 -21
- data/.circleci/config.yml +0 -29
- data/.codeclimate.yml +0 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 46b0340d8c2c5cde79a47703b564be4bec987b2fed0e9561ce658227ac3a2076
|
4
|
+
data.tar.gz: 8d2384684ad0c72d2f763446177b16d97d76f2c84c95bf4a7b5ff8e08cf32c23
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8e8d607f077c5de894d5fd70989d595fda9f4fab5d5111eeedb4acfa526d4f94cc7cb64abc64b42ac2968ef9e7f16bbf413a4329b46e919f0b46d8e9bc5c527
|
7
|
+
data.tar.gz: 72a674b922b87067ea2c3276f111603ee377a25bff277862187aa879b11ac83909877749b6b0cd5ff3315429e95b482da211f54b74b460eff129015780734e6c
|
@@ -0,0 +1,18 @@
|
|
1
|
+
name: Linting
|
2
|
+
on: [pull_request]
|
3
|
+
jobs:
|
4
|
+
rubocop:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
steps:
|
7
|
+
- uses: actions/checkout@v2
|
8
|
+
- name: Set up Ruby
|
9
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
10
|
+
with:
|
11
|
+
ruby-version: 3.0.0
|
12
|
+
bundler-cache: true
|
13
|
+
- name: Run RuboCop
|
14
|
+
uses: reviewdog/action-rubocop@v2
|
15
|
+
with:
|
16
|
+
rubocop_version: gemfile
|
17
|
+
github_token: ${{ secrets.github_token }}
|
18
|
+
reporter: github-pr-review
|
@@ -0,0 +1,17 @@
|
|
1
|
+
name: Testing
|
2
|
+
on: [pull_request]
|
3
|
+
jobs:
|
4
|
+
test:
|
5
|
+
runs-on: ubuntu-latest
|
6
|
+
strategy:
|
7
|
+
matrix:
|
8
|
+
ruby-version: ['2.6', '2.7', '3.0']
|
9
|
+
steps:
|
10
|
+
- uses: actions/checkout@v2
|
11
|
+
- name: Set up Ruby
|
12
|
+
uses: ruby/setup-ruby@473e4d8fe5dd94ee328fdfca9f8c9c7afc9dae5e
|
13
|
+
with:
|
14
|
+
ruby-version: ${{ matrix.ruby-version }}
|
15
|
+
bundler-cache: true
|
16
|
+
- name: Run tests
|
17
|
+
run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -1,12 +1,23 @@
|
|
1
1
|
require:
|
2
2
|
- rubocop-performance
|
3
3
|
|
4
|
+
inherit_mode:
|
5
|
+
merge:
|
6
|
+
- Exclude
|
7
|
+
|
4
8
|
AllCops:
|
9
|
+
NewCops: enable
|
5
10
|
Exclude:
|
6
11
|
- 'bin/**/*'
|
7
12
|
- 'test/mock/app/**/*'
|
8
13
|
- 'test/mock/examples/**/*'
|
9
14
|
|
15
|
+
Gemspec/RequiredRubyVersion:
|
16
|
+
Enabled: false
|
17
|
+
|
18
|
+
Layout/LineLength:
|
19
|
+
Max: 120
|
20
|
+
|
10
21
|
Metrics/AbcSize:
|
11
22
|
Exclude:
|
12
23
|
- 'test/**/*'
|
@@ -16,6 +27,9 @@ Metrics/MethodLength:
|
|
16
27
|
Exclude:
|
17
28
|
- 'test/**/*'
|
18
29
|
|
30
|
+
Metrics/ModuleLength:
|
31
|
+
Max: 250
|
32
|
+
|
19
33
|
Style/ClassAndModuleChildren:
|
20
34
|
Exclude:
|
21
35
|
- 'test/**/*'
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Chusaku
|
2
2
|
|
3
|
-
|
4
|
-
|-----|----|---------|
|
5
|
-
|[](https://badge.fury.io/rb/chusaku)|[](https://circleci.com/gh/nshki/chusaku)|[](https://codeclimate.com/github/nshki/chusaku/maintainability)
|
3
|
+
[](https://badge.fury.io/rb/chusaku)
|
6
4
|
|
7
5
|
Add comments above your Rails actions that look like:
|
8
6
|
|
@@ -54,9 +52,10 @@ Chusaku has some flags available for use as well:
|
|
54
52
|
```
|
55
53
|
$ bundle exec chusaku --help
|
56
54
|
Usage: chusaku [options]
|
55
|
+
--dry-run Run without file modifications
|
57
56
|
--exit-with-error-on-annotation
|
58
57
|
Fail if any file was annotated
|
59
|
-
--
|
58
|
+
--verbose Print all annotations
|
60
59
|
-v, --version Show Chusaku version number and quit
|
61
60
|
-h, --help Show this help message and quit
|
62
61
|
```
|
data/chusaku.gemspec
CHANGED
@@ -8,7 +8,7 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.name = 'chusaku'
|
9
9
|
spec.version = Chusaku::VERSION
|
10
10
|
spec.authors = ['Nishiki Liu']
|
11
|
-
spec.email = ['nishiki
|
11
|
+
spec.email = ['nishiki@hey.com']
|
12
12
|
|
13
13
|
spec.summary = 'Annotate your Rails controllers with route info.'
|
14
14
|
spec.description = 'Annotate your Rails controllers with route info.'
|
@@ -26,7 +26,7 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.metadata['changelog_uri'] = spec.homepage
|
27
27
|
else
|
28
28
|
raise 'RubyGems 2.0 or newer is required to protect against ' \
|
29
|
-
|
29
|
+
'public gem pushes.'
|
30
30
|
end
|
31
31
|
|
32
32
|
# Specify which files should be added to the gem when it is released.
|
@@ -37,15 +37,15 @@ Gem::Specification.new do |spec|
|
|
37
37
|
f.match(%r{^(test|spec|features)/})
|
38
38
|
end
|
39
39
|
end
|
40
|
-
spec.bindir
|
41
|
-
spec.executables
|
40
|
+
spec.bindir = 'bin'
|
41
|
+
spec.executables = 'chusaku'
|
42
42
|
spec.require_paths = ['lib']
|
43
43
|
|
44
|
-
spec.add_development_dependency 'bundler', '~> 2.
|
45
|
-
spec.add_development_dependency 'minitest', '~> 5.
|
46
|
-
spec.add_development_dependency 'rake', '~>
|
47
|
-
spec.add_development_dependency 'rubocop', '~>
|
48
|
-
spec.add_development_dependency 'rubocop-performance', '~> 1.
|
44
|
+
spec.add_development_dependency 'bundler', '~> 2.2'
|
45
|
+
spec.add_development_dependency 'minitest', '~> 5.14'
|
46
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
47
|
+
spec.add_development_dependency 'rubocop', '~> 1.7'
|
48
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.9'
|
49
49
|
|
50
|
-
spec.add_dependency '
|
50
|
+
spec.add_dependency 'railties', '>= 3.0'
|
51
51
|
end
|
data/lib/chusaku/cli.rb
CHANGED
@@ -14,17 +14,17 @@ module Chusaku
|
|
14
14
|
Finished = Class.new(RuntimeError)
|
15
15
|
NotARailsProject = Class.new(RuntimeError)
|
16
16
|
|
17
|
-
# Initializes a new instance of Chusaku::CLI
|
17
|
+
# Initializes a new instance of `Chusaku::CLI`.
|
18
18
|
#
|
19
|
-
# @return
|
19
|
+
# @return [Chusaku::CLI] Instance of this class
|
20
20
|
def initialize
|
21
21
|
@options = {}
|
22
22
|
end
|
23
23
|
|
24
24
|
# Parse CLI flags, if any, and handle applicable behaviors.
|
25
25
|
#
|
26
|
-
# @param
|
27
|
-
# @return
|
26
|
+
# @param args [Array<String>] CLI arguments
|
27
|
+
# @return [Integer] 0 on success, 1 on error
|
28
28
|
def call(args = ARGV)
|
29
29
|
optparser.parse!(args)
|
30
30
|
check_for_rails_project
|
@@ -40,8 +40,8 @@ module Chusaku
|
|
40
40
|
|
41
41
|
# Raises exception if Rails project cannot be detected.
|
42
42
|
#
|
43
|
-
# @raise
|
44
|
-
# @return
|
43
|
+
# @raise [Chusaku::CLI::NotARailsProject] Exception if not Rails project
|
44
|
+
# @return [void]
|
45
45
|
def check_for_rails_project
|
46
46
|
has_controllers = File.directory?('./app/controllers')
|
47
47
|
has_rakefile = File.exist?('./Rakefile')
|
@@ -50,53 +50,54 @@ module Chusaku
|
|
50
50
|
|
51
51
|
# Returns an instance of OptionParser with supported flags.
|
52
52
|
#
|
53
|
-
# @return
|
53
|
+
# @return [OptionParser] Preconfigured OptionParser instance
|
54
54
|
def optparser
|
55
55
|
OptionParser.new do |opts|
|
56
56
|
opts.banner = 'Usage: chusaku [options]'
|
57
|
-
add_error_on_annotation_flag(opts)
|
58
57
|
add_dry_run_flag(opts)
|
58
|
+
add_error_on_annotation_flag(opts)
|
59
|
+
add_verbose_flag(opts)
|
59
60
|
add_version_flag(opts)
|
60
61
|
add_help_flag(opts)
|
61
62
|
end
|
62
63
|
end
|
63
64
|
|
65
|
+
# Adds `--dry-run` flag.
|
66
|
+
#
|
67
|
+
# @param opts [OptionParser] OptionParser instance
|
68
|
+
# @return [void]
|
69
|
+
def add_dry_run_flag(opts)
|
70
|
+
opts.on('--dry-run', 'Run without file modifications') do
|
71
|
+
@options[:dry] = true
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
64
75
|
# Adds `--exit-with-error-on-annotation` flag.
|
65
76
|
#
|
66
|
-
# @param
|
67
|
-
# @return
|
77
|
+
# @param opts [OptionParser] OptionParser instance
|
78
|
+
# @return [void]
|
68
79
|
def add_error_on_annotation_flag(opts)
|
69
|
-
opts.on(
|
70
|
-
'--exit-with-error-on-annotation',
|
71
|
-
'Fail if any file was annotated'
|
72
|
-
) do
|
80
|
+
opts.on('--exit-with-error-on-annotation', 'Fail if any file was annotated') do
|
73
81
|
@options[:error_on_annotation] = true
|
74
82
|
end
|
75
83
|
end
|
76
84
|
|
77
|
-
# Adds `--
|
85
|
+
# Adds `--verbose` flag.
|
78
86
|
#
|
79
|
-
# @param
|
80
|
-
# @return
|
81
|
-
def
|
82
|
-
opts.on(
|
83
|
-
|
84
|
-
'Run without file modifications'
|
85
|
-
) do
|
86
|
-
@options[:dry] = true
|
87
|
+
# @param opts [OptionParser] OptionParser instance
|
88
|
+
# @return [void]
|
89
|
+
def add_verbose_flag(opts)
|
90
|
+
opts.on('--verbose', 'Print all annotations') do
|
91
|
+
@options[:verbose] = true
|
87
92
|
end
|
88
93
|
end
|
89
94
|
|
90
95
|
# Adds `--version` flag.
|
91
96
|
#
|
92
|
-
# @param
|
93
|
-
# @return
|
97
|
+
# @param opts [OptionParser] OptionParser instance
|
98
|
+
# @return [void]
|
94
99
|
def add_version_flag(opts)
|
95
|
-
opts.on(
|
96
|
-
'-v',
|
97
|
-
'--version',
|
98
|
-
'Show Chusaku version number and quit'
|
99
|
-
) do
|
100
|
+
opts.on('-v', '--version', 'Show Chusaku version number and quit') do
|
100
101
|
puts(Chusaku::VERSION)
|
101
102
|
raise Finished
|
102
103
|
end
|
@@ -104,14 +105,10 @@ module Chusaku
|
|
104
105
|
|
105
106
|
# Adds `--help` flag.
|
106
107
|
#
|
107
|
-
# @param
|
108
|
-
# @return
|
108
|
+
# @param opts [OptionParser] OptionParser instance
|
109
|
+
# @return [void]
|
109
110
|
def add_help_flag(opts)
|
110
|
-
opts.on(
|
111
|
-
'-h',
|
112
|
-
'--help',
|
113
|
-
'Show this help message and quit'
|
114
|
-
) do
|
111
|
+
opts.on('-h', '--help', 'Show this help message and quit') do
|
115
112
|
puts(opts)
|
116
113
|
raise Finished
|
117
114
|
end
|
data/lib/chusaku/parser.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
module Chusaku
|
4
4
|
# Handles parsing a file and groups its lines into categories.
|
5
5
|
module Parser
|
6
|
+
# Primary method to call.
|
7
|
+
#
|
6
8
|
# Example output:
|
7
9
|
#
|
8
10
|
# {
|
@@ -11,45 +13,49 @@ module Chusaku
|
|
11
13
|
# {
|
12
14
|
# type: :code,
|
13
15
|
# body: 'class Foo\n',
|
14
|
-
# action: nil
|
16
|
+
# action: nil,
|
17
|
+
# line_number: 1
|
15
18
|
# },
|
16
19
|
# {
|
17
20
|
# type: :comment,
|
18
21
|
# body: ' # Bar\n # Baz\n',
|
19
|
-
# action: nil
|
22
|
+
# action: nil,
|
23
|
+
# line_number: 2
|
20
24
|
# },
|
21
25
|
# {
|
22
26
|
# type: :action,
|
23
27
|
# body: ' def action_name; end\n',
|
24
|
-
# action: 'action_name'
|
28
|
+
# action: 'action_name',
|
29
|
+
# line_number: 4
|
25
30
|
# }
|
26
31
|
# {
|
27
32
|
# type: :code,
|
28
33
|
# body: 'end # vanilla is the best flavor\n',
|
29
|
-
# action: nil
|
34
|
+
# action: nil,
|
35
|
+
# line_number: 5
|
30
36
|
# }
|
31
37
|
# ]
|
32
38
|
# }
|
33
39
|
#
|
34
|
-
# @param
|
35
|
-
# @param
|
36
|
-
# @return
|
40
|
+
# @param path [String] File path to parse
|
41
|
+
# @param actions [Array<String>] List of valid actions for this route
|
42
|
+
# @return [Hash] { content => String, groups => Array<Hash> }
|
37
43
|
def self.call(path:, actions:)
|
38
44
|
groups = []
|
39
45
|
group = {}
|
40
46
|
content = IO.read(path)
|
41
47
|
|
42
|
-
content.each_line do |line|
|
48
|
+
content.each_line.with_index do |line, index|
|
43
49
|
parsed_line = parse_line(line: line, actions: actions)
|
44
50
|
|
45
|
-
if group[:type]
|
51
|
+
if group[:type] == parsed_line[:type]
|
52
|
+
# Same group. Push the current line into the current group.
|
53
|
+
group[:body] += line
|
54
|
+
else
|
46
55
|
# Now looking at a new group. Push the current group onto the array
|
47
56
|
# and start a new one.
|
48
57
|
groups.push(group) unless group.empty?
|
49
|
-
group = parsed_line
|
50
|
-
else
|
51
|
-
# Same group. Push the current line into the current group.
|
52
|
-
group[:body] += line
|
58
|
+
group = parsed_line.merge(line_number: index + 1)
|
53
59
|
end
|
54
60
|
end
|
55
61
|
|
@@ -58,20 +64,22 @@ module Chusaku
|
|
58
64
|
{ content: content, groups: groups }
|
59
65
|
end
|
60
66
|
|
61
|
-
# Given a line and actions, returns the line's type
|
67
|
+
# Given a line and actions, returns the line's type.
|
68
|
+
#
|
69
|
+
# A type can be one of:
|
62
70
|
#
|
63
71
|
# 1. comment - A line that is entirely commented. Lines that have trailing
|
64
72
|
# comments do not fall under this category.
|
65
73
|
# 2. action - A line that contains an action definition.
|
66
74
|
# 3. code - Anything else.
|
67
75
|
#
|
68
|
-
#
|
76
|
+
# Returns a Hash in the form:
|
69
77
|
#
|
70
78
|
# { type: :action, body: 'def foo', action: 'foo' }
|
71
79
|
#
|
72
|
-
# @param
|
73
|
-
# @param
|
74
|
-
# @return
|
80
|
+
# @param line [String] A line of a file
|
81
|
+
# @param actions [Array<String>] List of valid actions for this route
|
82
|
+
# @return [Hash] { type => Symbol, body => String, action => String }
|
75
83
|
def self.parse_line(line:, actions:)
|
76
84
|
comment_match = /^\s*#.*$/.match(line)
|
77
85
|
def_match = /^\s*def\s+(\w*)\s*\w*.*$/.match(line)
|
data/lib/chusaku/routes.rb
CHANGED
@@ -4,6 +4,8 @@ module Chusaku
|
|
4
4
|
# Handles extracting information about the Rails project's routes.
|
5
5
|
class Routes
|
6
6
|
class << self
|
7
|
+
# Primary method to call.
|
8
|
+
#
|
7
9
|
# Example output:
|
8
10
|
#
|
9
11
|
# {
|
@@ -23,19 +25,21 @@ module Chusaku
|
|
23
25
|
# }
|
24
26
|
# }
|
25
27
|
#
|
26
|
-
# @return
|
28
|
+
# @return [Hash] Routes hash
|
27
29
|
def call
|
28
30
|
routes = {}
|
29
31
|
|
30
32
|
Rails.application.routes.routes.each do |route|
|
31
|
-
controller, action =
|
33
|
+
controller, action, defaults = extract_data_from(route)
|
32
34
|
routes[controller] ||= {}
|
33
35
|
routes[controller][action] ||= []
|
34
36
|
|
35
|
-
|
36
|
-
|
37
|
-
routes
|
38
|
-
|
37
|
+
add_info_for \
|
38
|
+
route: route,
|
39
|
+
routes: routes,
|
40
|
+
controller: controller,
|
41
|
+
action: action,
|
42
|
+
defaults: defaults
|
39
43
|
end
|
40
44
|
|
41
45
|
backfill_routes(routes)
|
@@ -43,38 +47,56 @@ module Chusaku
|
|
43
47
|
|
44
48
|
private
|
45
49
|
|
50
|
+
# Adds formatted route info for the given param combination.
|
51
|
+
#
|
52
|
+
# @param route [Hash] Route info
|
53
|
+
# @param routes [Hash] Collection of all route info
|
54
|
+
# @param controller [String] Controller key
|
55
|
+
# @param action [String] Action key
|
56
|
+
# @param defaults [Hash] Default parameters for route
|
57
|
+
# @return [void]
|
58
|
+
def add_info_for(route:, routes:, controller:, action:, defaults:)
|
59
|
+
verbs_for(route).each do |verb|
|
60
|
+
routes[controller][action]
|
61
|
+
.push(format(route: route, verb: verb, defaults: defaults))
|
62
|
+
routes[controller][action].uniq!
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
46
66
|
# Extract the HTTP verbs for a Rails route. Required for older versions of
|
47
67
|
# Rails that return regular expressions for a route verb which sometimes
|
48
68
|
# contains multiple verbs.
|
49
69
|
#
|
50
|
-
# @param
|
51
|
-
# @return
|
70
|
+
# @param route [ActionDispatch::Journey::Route] Route given by Rails
|
71
|
+
# @return [Array<String>] List of HTTP verbs for the given route
|
52
72
|
def verbs_for(route)
|
53
73
|
route_verb = route.verb.to_s
|
54
74
|
|
55
|
-
[
|
75
|
+
%w[GET POST PUT PATCH DELETE].select do |verb|
|
56
76
|
route_verb.include?(verb)
|
57
77
|
end
|
58
78
|
end
|
59
79
|
|
60
80
|
# Formats information for a given route.
|
61
81
|
#
|
62
|
-
# @param
|
63
|
-
# @param
|
64
|
-
# @
|
65
|
-
|
82
|
+
# @param route [ActionDispatch::Journey::Route] Route given by Rails
|
83
|
+
# @param verb [String] HTTP verb
|
84
|
+
# @param defaults [Hash] Default parameters for route
|
85
|
+
# @return [Hash] { verb => String, path => String, name => String }
|
86
|
+
def format(route:, verb:, defaults:)
|
66
87
|
{
|
67
88
|
verb: verb,
|
68
89
|
path: route.path.spec.to_s.gsub('(.:format)', ''),
|
69
|
-
name: route.name
|
90
|
+
name: route.name,
|
91
|
+
defaults: defaults
|
70
92
|
}
|
71
93
|
end
|
72
94
|
|
73
95
|
# Given a routes hash, backfill entries that aren't already filled by
|
74
96
|
# `Rails.application.routes`.
|
75
97
|
#
|
76
|
-
# @param
|
77
|
-
# @return
|
98
|
+
# @param routes [Hash] Routes hash generated by this class
|
99
|
+
# @return [Hash] Backfilled routes hash
|
78
100
|
def backfill_routes(routes)
|
79
101
|
paths = {}
|
80
102
|
|
@@ -92,14 +114,14 @@ module Chusaku
|
|
92
114
|
|
93
115
|
# Given a route, extract the controller and action strings.
|
94
116
|
#
|
95
|
-
# @param
|
96
|
-
# @return
|
97
|
-
def
|
98
|
-
defaults = route.defaults
|
99
|
-
controller = defaults
|
100
|
-
action = defaults
|
117
|
+
# @param route [ActionDispatch::Journey::Route] Route instance
|
118
|
+
# @return [Array<Object>] (String, String, Hash)
|
119
|
+
def extract_data_from(route)
|
120
|
+
defaults = route.defaults.dup
|
121
|
+
controller = defaults.delete(:controller)
|
122
|
+
action = defaults.delete(:action)
|
101
123
|
|
102
|
-
[controller, action]
|
124
|
+
[controller, action, defaults]
|
103
125
|
end
|
104
126
|
end
|
105
127
|
end
|
data/lib/chusaku/version.rb
CHANGED
data/lib/chusaku.rb
CHANGED
@@ -15,16 +15,16 @@ module Chusaku
|
|
15
15
|
# # ...
|
16
16
|
# end
|
17
17
|
#
|
18
|
-
# @param
|
19
|
-
# @return
|
18
|
+
# @param flags [Hash] CLI flags
|
19
|
+
# @return [Integer] 0 on success, 1 on error
|
20
20
|
def call(flags = {})
|
21
21
|
@flags = flags
|
22
22
|
@routes = Chusaku::Routes.call
|
23
|
-
@
|
23
|
+
@changes = []
|
24
24
|
controllers_pattern = 'app/controllers/**/*_controller.rb'
|
25
25
|
|
26
26
|
Dir.glob(Rails.root.join(controllers_pattern)).each do |path|
|
27
|
-
controller = %r{controllers
|
27
|
+
controller = %r{controllers/(.*)_controller\.rb}.match(path)[1]
|
28
28
|
actions = @routes[controller]
|
29
29
|
next if actions.nil?
|
30
30
|
|
@@ -38,35 +38,63 @@ module Chusaku
|
|
38
38
|
|
39
39
|
# Adds annotations to the given file.
|
40
40
|
#
|
41
|
-
# @param
|
42
|
-
# @param
|
43
|
-
# @param
|
44
|
-
# @return
|
41
|
+
# @param path [String] Path to file
|
42
|
+
# @param controller [String] Controller name
|
43
|
+
# @param actions [Array<String>] List of valid actions for the controller
|
44
|
+
# @return [void]
|
45
45
|
def annotate_file(path:, controller:, actions:)
|
46
46
|
parsed_file = Chusaku::Parser.call(path: path, actions: actions)
|
47
47
|
parsed_file[:groups].each_cons(2) do |prev, curr|
|
48
|
-
|
48
|
+
record_change(group: prev, type: :clean, path: path)
|
49
49
|
next unless curr[:type] == :action
|
50
50
|
|
51
51
|
route_data = @routes[controller][curr[:action]]
|
52
52
|
next unless route_data.any?
|
53
53
|
|
54
|
-
|
54
|
+
record_change(group: curr, type: :annotate, route_data: route_data, path: path)
|
55
55
|
end
|
56
56
|
|
57
57
|
write_to_file(path: path, parsed_file: parsed_file)
|
58
58
|
end
|
59
59
|
|
60
|
+
# Clean or annotate a group and track the group as changed if applicable.
|
61
|
+
#
|
62
|
+
# @param group [Hash] { type => Symbol, body => String }
|
63
|
+
# @param type [Symbol] [:clean, :annotate]
|
64
|
+
# @param path [String] File path
|
65
|
+
# @param route_data [Array<Hash>] [{
|
66
|
+
# verb: String,
|
67
|
+
# path: String,
|
68
|
+
# name: String }]
|
69
|
+
# @return [void]
|
70
|
+
def record_change(group:, type:, path:, route_data: [])
|
71
|
+
old_body = group[:body]
|
72
|
+
|
73
|
+
case type
|
74
|
+
when :clean
|
75
|
+
clean_group(group)
|
76
|
+
when :annotate
|
77
|
+
annotate_group(group: group, route_data: route_data)
|
78
|
+
end
|
79
|
+
return if old_body == group[:body]
|
80
|
+
|
81
|
+
@changes.push \
|
82
|
+
old_body: old_body,
|
83
|
+
new_body: group[:body],
|
84
|
+
path: path,
|
85
|
+
line_number: group[:line_number]
|
86
|
+
end
|
87
|
+
|
60
88
|
# Given a parsed group, clean out its contents.
|
61
89
|
#
|
62
|
-
# @param
|
90
|
+
# @param group [Hash] { type => Symbol, body => String }
|
63
91
|
# @return {void}
|
64
92
|
def clean_group(group)
|
65
93
|
return unless group[:type] == :comment
|
66
94
|
|
67
95
|
group[:body] = group[:body].gsub(/^\s*#\s*@route.*$\n/, '')
|
68
96
|
group[:body] =
|
69
|
-
group[:body].gsub(%r{^\s*# (GET|POST|PATCH
|
97
|
+
group[:body].gsub(%r{^\s*# (GET|POST|PATCH/PUT|DELETE) /\S+$\n}, '')
|
70
98
|
end
|
71
99
|
|
72
100
|
# Add an annotation to the given group given by Chusaku::Parser that looks
|
@@ -74,57 +102,125 @@ module Chusaku
|
|
74
102
|
#
|
75
103
|
# @route GET /waterlilies/:id (waterlilies)
|
76
104
|
#
|
77
|
-
# @param
|
78
|
-
# @param
|
79
|
-
# @return
|
105
|
+
# @param group [Hash] Parsed content given by Chusaku::Parser
|
106
|
+
# @param route_data [Hash] Individual route data given by Chusaku::Routes
|
107
|
+
# @return [void]
|
80
108
|
def annotate_group(group:, route_data:)
|
81
109
|
whitespace = /^(\s*).*$/.match(group[:body])[1]
|
82
110
|
route_data.reverse_each do |datum|
|
83
|
-
|
84
|
-
annotation = "@route #{datum[:verb]} #{datum[:path]}"
|
85
|
-
annotation += " (#{name})" unless name.nil?
|
86
|
-
comment = "#{whitespace}# #{annotation}\n"
|
111
|
+
comment = "#{whitespace}# #{annotate_route(**datum)}\n"
|
87
112
|
group[:body] = comment + group[:body]
|
88
113
|
end
|
89
114
|
end
|
90
115
|
|
116
|
+
# Generate route annotation.
|
117
|
+
#
|
118
|
+
# @param verb [String] HTTP verb for route
|
119
|
+
# @param path [String] Rails path for route
|
120
|
+
# @param name [String] Name used in route helpers
|
121
|
+
# @param defaults [Hash] Default parameters for route
|
122
|
+
# @return [String] "@route <verb> <path> {<defaults>} (<name>)"
|
123
|
+
def annotate_route(verb:, path:, name:, defaults:)
|
124
|
+
annotation = "@route #{verb} #{path}"
|
125
|
+
if defaults&.any?
|
126
|
+
defaults_str =
|
127
|
+
defaults
|
128
|
+
.map { |key, value| "#{key}: #{value.inspect}" }
|
129
|
+
.join(', ')
|
130
|
+
annotation += " {#{defaults_str}}"
|
131
|
+
end
|
132
|
+
annotation += " (#{name})" unless name.nil?
|
133
|
+
annotation
|
134
|
+
end
|
135
|
+
|
91
136
|
# Write annotated content to a file if it differs from the original.
|
92
137
|
#
|
93
|
-
# @param
|
94
|
-
# @param
|
95
|
-
# @return
|
138
|
+
# @param path [String] File path to write to
|
139
|
+
# @param parsed_file [Hash] Hash mutated by {#annotate_group}
|
140
|
+
# @return [void]
|
96
141
|
def write_to_file(path:, parsed_file:)
|
97
|
-
|
98
|
-
return
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
142
|
+
new_content = new_content_for(parsed_file)
|
143
|
+
return if parsed_file[:content] == new_content
|
144
|
+
|
145
|
+
!@flags.include?(:dry) && perform_write(path: path, content: new_content)
|
146
|
+
end
|
147
|
+
|
148
|
+
# Extracts the new file content for the given parsed file.
|
149
|
+
#
|
150
|
+
# @param parsed_file [Hash] { groups => Array<Hash> }
|
151
|
+
# @return [String] New file content
|
152
|
+
def new_content_for(parsed_file)
|
153
|
+
parsed_file[:groups].map { |pf| pf[:body] }.join
|
154
|
+
end
|
155
|
+
|
156
|
+
# Wraps the write operation. Needed to clearly distinguish whether it's a
|
157
|
+
# write in the test suite or a write in actual use.
|
158
|
+
#
|
159
|
+
# @param path [String] File path
|
160
|
+
# @param content [String] File content
|
161
|
+
# @return [void]
|
162
|
+
def perform_write(path:, content:)
|
163
|
+
File.open(path, file_mode) do |file|
|
164
|
+
if file.respond_to?(:test_write)
|
165
|
+
file.test_write(content, path)
|
166
|
+
else
|
167
|
+
file.write(content)
|
111
168
|
end
|
112
169
|
end
|
170
|
+
end
|
113
171
|
|
114
|
-
|
172
|
+
# When running the test suite, we want to make sure we're not overwriting
|
173
|
+
# any files. `r` mode ensures that, and `w` is used for actual usage.
|
174
|
+
#
|
175
|
+
# @return [String] 'r' or 'w'
|
176
|
+
def file_mode
|
177
|
+
File.instance_methods.include?(:test_write) ? 'r' : 'w'
|
115
178
|
end
|
116
179
|
|
117
180
|
# Output results to user.
|
118
181
|
#
|
119
|
-
# @return
|
182
|
+
# @return [Integer] 0 for success, 1 for error
|
120
183
|
def output_results
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
184
|
+
puts(output_copy)
|
185
|
+
exit_code = 0
|
186
|
+
exit_code = 1 if @changes.any? && @flags.include?(:error_on_annotation)
|
187
|
+
exit_code
|
188
|
+
end
|
189
|
+
|
190
|
+
# Determines the copy to be used in the program output.
|
191
|
+
#
|
192
|
+
# @return [String] Copy to be outputted to user
|
193
|
+
def output_copy
|
194
|
+
return 'Nothing to annotate.' if @changes.empty?
|
195
|
+
|
196
|
+
copy = changes_copy
|
197
|
+
copy += "\nChusaku has finished running."
|
198
|
+
copy += "\nThis was a dry run so no files were changed." if @flags.include?(:dry)
|
199
|
+
copy += "\nExited with status code 1." if @flags.include?(:error_on_annotation)
|
200
|
+
copy
|
201
|
+
end
|
202
|
+
|
203
|
+
# Returns the copy for recorded changes if `--verbose` flag is passed.
|
204
|
+
#
|
205
|
+
# @return [String] Copy of recorded changes
|
206
|
+
def changes_copy
|
207
|
+
return '' unless @flags.include?(:verbose)
|
208
|
+
|
209
|
+
@changes.map do |change|
|
210
|
+
<<~CHANGE_OUTPUT
|
211
|
+
[#{change[:path]}:#{change[:line_number]}]
|
212
|
+
|
213
|
+
Before:
|
214
|
+
```ruby
|
215
|
+
#{change[:old_body].chomp}
|
216
|
+
```
|
217
|
+
|
218
|
+
After:
|
219
|
+
```ruby
|
220
|
+
#{change[:new_body].chomp}
|
221
|
+
```
|
222
|
+
CHANGE_OUTPUT
|
223
|
+
end.join("\n")
|
128
224
|
end
|
129
225
|
end
|
130
226
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: chusaku
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nishiki Liu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2021-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -16,94 +16,94 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - "~>"
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '2.
|
19
|
+
version: '2.2'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
24
|
- - "~>"
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '2.
|
26
|
+
version: '2.2'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
31
|
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '5.
|
33
|
+
version: '5.14'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '5.
|
40
|
+
version: '5.14'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '13.0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '13.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: rubocop
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
61
|
+
version: '1.7'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '
|
68
|
+
version: '1.7'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
70
|
name: rubocop-performance
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
73
|
- - "~>"
|
74
74
|
- !ruby/object:Gem::Version
|
75
|
-
version: '1.
|
75
|
+
version: '1.9'
|
76
76
|
type: :development
|
77
77
|
prerelease: false
|
78
78
|
version_requirements: !ruby/object:Gem::Requirement
|
79
79
|
requirements:
|
80
80
|
- - "~>"
|
81
81
|
- !ruby/object:Gem::Version
|
82
|
-
version: '1.
|
82
|
+
version: '1.9'
|
83
83
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
84
|
+
name: railties
|
85
85
|
requirement: !ruby/object:Gem::Requirement
|
86
86
|
requirements:
|
87
|
-
- - "
|
87
|
+
- - ">="
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: '
|
89
|
+
version: '3.0'
|
90
90
|
type: :runtime
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
|
-
- - "
|
94
|
+
- - ">="
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
96
|
+
version: '3.0'
|
97
97
|
description: Annotate your Rails controllers with route info.
|
98
98
|
email:
|
99
|
-
- nishiki
|
99
|
+
- nishiki@hey.com
|
100
100
|
executables:
|
101
101
|
- chusaku
|
102
102
|
extensions: []
|
103
103
|
extra_rdoc_files: []
|
104
104
|
files:
|
105
|
-
- ".
|
106
|
-
- ".
|
105
|
+
- ".github/workflows/linting.yml"
|
106
|
+
- ".github/workflows/testing.yml"
|
107
107
|
- ".gitignore"
|
108
108
|
- ".rubocop.yml"
|
109
109
|
- Gemfile
|
@@ -142,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
142
142
|
- !ruby/object:Gem::Version
|
143
143
|
version: '0'
|
144
144
|
requirements: []
|
145
|
-
rubygems_version: 3.1.
|
145
|
+
rubygems_version: 3.1.6
|
146
146
|
signing_key:
|
147
147
|
specification_version: 4
|
148
148
|
summary: Annotate your Rails controllers with route info.
|
data/.circleci/config.yml
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
version: 2
|
2
|
-
jobs:
|
3
|
-
build:
|
4
|
-
working_directory: ~/chusaku
|
5
|
-
docker:
|
6
|
-
- image: circleci/ruby
|
7
|
-
steps:
|
8
|
-
- checkout
|
9
|
-
- run:
|
10
|
-
name: Install Bundler
|
11
|
-
command: |
|
12
|
-
gem install bundler
|
13
|
-
- restore_cache:
|
14
|
-
keys:
|
15
|
-
- dep-v1-{{ .Branch }}-{{ checksum "chusaku.gemspec" }}
|
16
|
-
- dep-v1-{{ .Branch }}
|
17
|
-
- dep-v1-
|
18
|
-
- run:
|
19
|
-
name: Install dependencies
|
20
|
-
command: |
|
21
|
-
bundle install --path vendor/bundle
|
22
|
-
- save_cache:
|
23
|
-
key: dep-v1-{{ .Branch }}-{{ checksum "chusaku.gemspec" }}
|
24
|
-
paths:
|
25
|
-
- vendor/bundle
|
26
|
-
- run:
|
27
|
-
name: Run tests
|
28
|
-
command: |
|
29
|
-
bundle exec rake test
|
data/.codeclimate.yml
DELETED