chusaku 0.1.4 → 0.4.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/.codeclimate.yml +4 -0
- data/.rubocop.yml +17 -17
- data/README.md +30 -3
- data/bin/chusaku +3 -6
- data/chusaku.gemspec +6 -4
- data/lib/chusaku.rb +145 -74
- data/lib/chusaku/cli.rb +120 -0
- data/lib/chusaku/parser.rb +40 -23
- data/lib/chusaku/routes.rb +96 -43
- data/lib/chusaku/version.rb +1 -1
- metadata +36 -6
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 76adb49d62bcaeab5291f4b09b12318ea5e80dc7c4b77ff7bbfee697732a953f
|
4
|
+
data.tar.gz: 8434d642b81b5d4cb67b7f9852adef2da679c24f7c9ea287aba903653b9703ee
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7b5880ed576e31a7f80a970389dc559e81ec932005716122de67a5f2c25586f82e674e6bdf1aba100d13a5bc7cba41df458b6e439c825684fa51ef11f77c6337
|
7
|
+
data.tar.gz: fa2ed22e8e87762f4577f4e45765f22c1e2d8139eb8d02f18682f7859ac543bfad8977b545645717a5f6acb2fd75f1eac02de6697982de6eebc02cedd595c68c
|
data/.codeclimate.yml
ADDED
data/.rubocop.yml
CHANGED
@@ -1,27 +1,27 @@
|
|
1
|
+
require:
|
2
|
+
- rubocop-performance
|
3
|
+
|
1
4
|
AllCops:
|
2
5
|
Exclude:
|
3
6
|
- 'bin/**/*'
|
7
|
+
- 'test/mock/app/**/*'
|
8
|
+
- 'test/mock/examples/**/*'
|
4
9
|
|
5
|
-
Layout/
|
6
|
-
|
10
|
+
Layout/LineLength:
|
11
|
+
Max: 120
|
7
12
|
|
8
13
|
Metrics/AbcSize:
|
9
|
-
|
14
|
+
Exclude:
|
15
|
+
- 'test/**/*'
|
10
16
|
|
11
17
|
Metrics/MethodLength:
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
Enabled: false
|
16
|
-
|
17
|
-
Style/ClassVars:
|
18
|
-
Enabled: false
|
19
|
-
|
20
|
-
Style/CommentedKeyword:
|
21
|
-
Enabled: false
|
18
|
+
Max: 25
|
19
|
+
Exclude:
|
20
|
+
- 'test/**/*'
|
22
21
|
|
23
|
-
|
24
|
-
|
22
|
+
Metrics/ModuleLength:
|
23
|
+
Max: 250
|
25
24
|
|
26
|
-
Style/
|
27
|
-
|
25
|
+
Style/ClassAndModuleChildren:
|
26
|
+
Exclude:
|
27
|
+
- 'test/**/*'
|
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# Chusaku
|
2
2
|
|
3
|
-
|
|
4
|
-
|
5
|
-
|[](https://circleci.com/gh/nshki/chusaku)|[](https://badge.fury.io/rb/chusaku)|[](https://circleci.com/gh/nshki/chusaku)|[](https://codeclimate.com/github/nshki/chusaku/maintainability)
|
6
6
|
|
7
7
|
Add comments above your Rails actions that look like:
|
8
8
|
|
@@ -49,6 +49,33 @@ From the root of your Rails application, run:
|
|
49
49
|
$ bundle exec chusaku
|
50
50
|
```
|
51
51
|
|
52
|
+
Chusaku has some flags available for use as well:
|
53
|
+
|
54
|
+
```
|
55
|
+
$ bundle exec chusaku --help
|
56
|
+
Usage: chusaku [options]
|
57
|
+
--exit-with-error-on-annotation
|
58
|
+
Fail if any file was annotated
|
59
|
+
--dry-run Run without file modifications
|
60
|
+
-v, --version Show Chusaku version number and quit
|
61
|
+
-h, --help Show this help message and quit
|
62
|
+
```
|
63
|
+
|
64
|
+
|
65
|
+
## Pre-commit Hook
|
66
|
+
|
67
|
+
Here's an example setup that you could use for automating Chusaku as a Git hook
|
68
|
+
with the [Lefthook](https://github.com/Arkweid/lefthook) gem.
|
69
|
+
|
70
|
+
```yaml
|
71
|
+
pre-commit:
|
72
|
+
commands:
|
73
|
+
chusaku:
|
74
|
+
run: eval "! git diff --staged --name-only | grep -q 'routes.rb' && exit 0 || bundle exec chusaku --exit-with-error-on-annotation"
|
75
|
+
```
|
76
|
+
|
77
|
+
This example config only runs Chusaku if `routes.rb` was modified.
|
78
|
+
|
52
79
|
|
53
80
|
## Development
|
54
81
|
|
data/bin/chusaku
CHANGED
@@ -1,9 +1,5 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
unless File.directory?('./app/controllers') && File.exist?('./Rakefile')
|
4
|
-
abort 'Please run chusaku from the root of your project.'
|
5
|
-
end
|
6
|
-
|
7
3
|
require 'rubygems'
|
8
4
|
|
9
5
|
begin
|
@@ -18,5 +14,6 @@ begin
|
|
18
14
|
rescue StandardError
|
19
15
|
end
|
20
16
|
|
21
|
-
require 'chusaku'
|
22
|
-
|
17
|
+
require 'chusaku/cli'
|
18
|
+
|
19
|
+
exit Chusaku::CLI.new.call
|
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.'
|
@@ -37,13 +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
44
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
45
45
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
46
|
-
spec.add_development_dependency 'rake', '~>
|
46
|
+
spec.add_development_dependency 'rake', '~> 12.3'
|
47
|
+
spec.add_development_dependency 'rubocop', '~> 0.77'
|
48
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.5'
|
47
49
|
|
48
50
|
spec.add_dependency 'rails', '> 2.0'
|
49
51
|
end
|
data/lib/chusaku.rb
CHANGED
@@ -4,92 +4,163 @@ require 'chusaku/version'
|
|
4
4
|
require 'chusaku/parser'
|
5
5
|
require 'chusaku/routes'
|
6
6
|
|
7
|
+
# Handles core functionality of annotating projects.
|
7
8
|
module Chusaku
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
prev[:body] = prev[:body].gsub(/^\s*#\s*@route.*$\n/, '')
|
33
|
-
end
|
9
|
+
class << self
|
10
|
+
# The main method to run Chusaku. Annotate all actions in a Rails project as
|
11
|
+
# follows:
|
12
|
+
#
|
13
|
+
# # @route GET /waterlilies/:id (waterlilies)
|
14
|
+
# def show
|
15
|
+
# # ...
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# @param flags [Hash] CLI flags
|
19
|
+
# @return [Integer] 0 on success, 1 on error
|
20
|
+
def call(flags = {})
|
21
|
+
@flags = flags
|
22
|
+
@routes = Chusaku::Routes.call
|
23
|
+
@annotated_paths = []
|
24
|
+
controllers_pattern = 'app/controllers/**/*_controller.rb'
|
25
|
+
|
26
|
+
Dir.glob(Rails.root.join(controllers_pattern)).each do |path|
|
27
|
+
controller = %r{controllers\/(.*)_controller\.rb}.match(path)[1]
|
28
|
+
actions = @routes[controller]
|
29
|
+
next if actions.nil?
|
30
|
+
|
31
|
+
annotate_file(path: path, controller: controller, actions: actions.keys)
|
32
|
+
end
|
34
33
|
|
35
|
-
|
34
|
+
output_results
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
# Adds annotations to the given file.
|
40
|
+
#
|
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
|
+
def annotate_file(path:, controller:, actions:)
|
46
|
+
parsed_file = Chusaku::Parser.call(path: path, actions: actions)
|
47
|
+
parsed_file[:groups].each_cons(2) do |prev, curr|
|
48
|
+
clean_group(prev)
|
36
49
|
next unless curr[:type] == :action
|
37
50
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
# Add annotations.
|
44
|
-
whitespace = /^(\s*).*$/.match(curr[:body])[1]
|
45
|
-
data.reverse.each do |datum|
|
46
|
-
annotation = annotate(datum)
|
47
|
-
comment = "#{whitespace}# #{annotation}\n"
|
48
|
-
curr[:body] = comment + curr[:body]
|
49
|
-
end
|
51
|
+
route_data = @routes[controller][curr[:action]]
|
52
|
+
next unless route_data.any?
|
53
|
+
|
54
|
+
annotate_group(group: curr, route_data: route_data)
|
50
55
|
end
|
51
56
|
|
52
|
-
|
53
|
-
parsed_content = parsed_file.map { |pf| pf[:body] }
|
54
|
-
write(path, parsed_content.join)
|
57
|
+
write_to_file(path: path, parsed_file: parsed_file)
|
55
58
|
end
|
56
59
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
60
|
+
# Given a parsed group, clean out its contents.
|
61
|
+
#
|
62
|
+
# @param group [Hash] { type => Symbol, body => String }
|
63
|
+
# @return {void}
|
64
|
+
def clean_group(group)
|
65
|
+
return unless group[:type] == :comment
|
66
|
+
|
67
|
+
group[:body] = group[:body].gsub(/^\s*#\s*@route.*$\n/, '')
|
68
|
+
group[:body] =
|
69
|
+
group[:body].gsub(%r{^\s*# (GET|POST|PATCH\/PUT|DELETE) \/\S+$\n}, '')
|
62
70
|
end
|
63
|
-
end
|
64
71
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
# Add an annotation to the given group given by Chusaku::Parser that looks
|
73
|
+
# like:
|
74
|
+
#
|
75
|
+
# @route GET /waterlilies/:id (waterlilies)
|
76
|
+
#
|
77
|
+
# @param group [Hash] Parsed content given by Chusaku::Parser
|
78
|
+
# @param route_data [Hash] Individual route data given by Chusaku::Routes
|
79
|
+
# @return [void]
|
80
|
+
def annotate_group(group:, route_data:)
|
81
|
+
whitespace = /^(\s*).*$/.match(group[:body])[1]
|
82
|
+
route_data.reverse_each do |datum|
|
83
|
+
comment = "#{whitespace}# #{annotate_route(**datum)}\n"
|
84
|
+
group[:body] = comment + group[:body]
|
77
85
|
end
|
78
86
|
end
|
79
|
-
end
|
80
87
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
verb
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
88
|
+
# Generate route annotation.
|
89
|
+
#
|
90
|
+
# @param verb [String] HTTP verb for route
|
91
|
+
# @param path [String] Rails path for route
|
92
|
+
# @param name [String] Name used in route helpers
|
93
|
+
# @param defaults [Hash] Default parameters for route
|
94
|
+
# @return [String] "@route <verb> <path> {<defaults>} (<name>)"
|
95
|
+
def annotate_route(verb:, path:, name:, defaults:)
|
96
|
+
annotation = "@route #{verb} #{path}"
|
97
|
+
if defaults&.any?
|
98
|
+
defaults_str =
|
99
|
+
defaults
|
100
|
+
.map { |key, value| "#{key}: #{value.inspect}" }
|
101
|
+
.join(', ')
|
102
|
+
annotation += " {#{defaults_str}}"
|
103
|
+
end
|
104
|
+
annotation += " (#{name})" unless name.nil?
|
105
|
+
annotation
|
106
|
+
end
|
107
|
+
|
108
|
+
# Write annotated content to a file if it differs from the original.
|
109
|
+
#
|
110
|
+
# @param path [String] File path to write to
|
111
|
+
# @param parsed_file [Hash] Hash mutated by {#annotate_group}
|
112
|
+
# @return [void]
|
113
|
+
def write_to_file(path:, parsed_file:)
|
114
|
+
new_content = new_content_for(parsed_file)
|
115
|
+
return unless parsed_file[:content] != new_content
|
116
|
+
|
117
|
+
!@flags.include?(:dry) && perform_write(path: path, content: new_content)
|
118
|
+
@annotated_paths.push(path)
|
119
|
+
end
|
120
|
+
|
121
|
+
# Extracts the new file content for the given parsed file.
|
122
|
+
#
|
123
|
+
# @param parsed_file [Hash] { groups => Array<Hash> }
|
124
|
+
# @return [String] New file content
|
125
|
+
def new_content_for(parsed_file)
|
126
|
+
parsed_file[:groups].map { |pf| pf[:body] }.join
|
127
|
+
end
|
128
|
+
|
129
|
+
# Wraps the write operation. Needed to clearly distinguish whether it's a
|
130
|
+
# write in the test suite or a write in actual use.
|
131
|
+
#
|
132
|
+
# @param path [String] File path
|
133
|
+
# @param content [String] File content
|
134
|
+
# @return [void]
|
135
|
+
def perform_write(path:, content:)
|
136
|
+
File.open(path, file_mode) do |file|
|
137
|
+
if file.respond_to?(:test_write)
|
138
|
+
file.test_write(content, path)
|
139
|
+
else
|
140
|
+
file.write(content)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
# When running the test suite, we want to make sure we're not overwriting
|
146
|
+
# any files. `r` mode ensures that, and `w` is used for actual usage.
|
147
|
+
#
|
148
|
+
# @return [String] 'r' or 'w'
|
149
|
+
def file_mode
|
150
|
+
File.instance_methods.include?(:test_write) ? 'r' : 'w'
|
151
|
+
end
|
152
|
+
|
153
|
+
# Output results to user.
|
154
|
+
#
|
155
|
+
# @return [Integer] 0 for success, 1 for error
|
156
|
+
def output_results
|
157
|
+
if @annotated_paths.any?
|
158
|
+
puts("Annotated #{@annotated_paths.join(', ')}")
|
159
|
+
@flags.include?(:error_on_annotation) ? 1 : 0
|
160
|
+
else
|
161
|
+
puts('Nothing to annotate')
|
162
|
+
0
|
163
|
+
end
|
164
|
+
end
|
94
165
|
end
|
95
166
|
end
|
data/lib/chusaku/cli.rb
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'optparse'
|
4
|
+
require 'chusaku'
|
5
|
+
|
6
|
+
module Chusaku
|
7
|
+
# Enables flags for the `chusaku` executable.
|
8
|
+
class CLI
|
9
|
+
attr_reader :options
|
10
|
+
|
11
|
+
STATUS_SUCCESS = 0
|
12
|
+
STATUS_ERROR = 1
|
13
|
+
|
14
|
+
Finished = Class.new(RuntimeError)
|
15
|
+
NotARailsProject = Class.new(RuntimeError)
|
16
|
+
|
17
|
+
# Initializes a new instance of `Chusaku::CLI`.
|
18
|
+
#
|
19
|
+
# @return [Chusaku::CLI] Instance of this class
|
20
|
+
def initialize
|
21
|
+
@options = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
# Parse CLI flags, if any, and handle applicable behaviors.
|
25
|
+
#
|
26
|
+
# @param args [Array<String>] CLI arguments
|
27
|
+
# @return [Integer] 0 on success, 1 on error
|
28
|
+
def call(args = ARGV)
|
29
|
+
optparser.parse!(args)
|
30
|
+
check_for_rails_project
|
31
|
+
Chusaku.call(options)
|
32
|
+
rescue NotARailsProject
|
33
|
+
warn('Please run chusaku from the root of your project.')
|
34
|
+
STATUS_ERROR
|
35
|
+
rescue Finished
|
36
|
+
STATUS_SUCCESS
|
37
|
+
end
|
38
|
+
|
39
|
+
private
|
40
|
+
|
41
|
+
# Raises exception if Rails project cannot be detected.
|
42
|
+
#
|
43
|
+
# @raise [Chusaku::CLI::NotARailsProject] Exception if not Rails project
|
44
|
+
# @return [void]
|
45
|
+
def check_for_rails_project
|
46
|
+
has_controllers = File.directory?('./app/controllers')
|
47
|
+
has_rakefile = File.exist?('./Rakefile')
|
48
|
+
raise NotARailsProject unless has_controllers && has_rakefile
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns an instance of OptionParser with supported flags.
|
52
|
+
#
|
53
|
+
# @return [OptionParser] Preconfigured OptionParser instance
|
54
|
+
def optparser
|
55
|
+
OptionParser.new do |opts|
|
56
|
+
opts.banner = 'Usage: chusaku [options]'
|
57
|
+
add_error_on_annotation_flag(opts)
|
58
|
+
add_dry_run_flag(opts)
|
59
|
+
add_version_flag(opts)
|
60
|
+
add_help_flag(opts)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
# Adds `--exit-with-error-on-annotation` flag.
|
65
|
+
#
|
66
|
+
# @param opts [OptionParser] OptionParser instance
|
67
|
+
# @return [void]
|
68
|
+
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
|
73
|
+
@options[:error_on_annotation] = true
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# Adds `--dry-run` flag.
|
78
|
+
#
|
79
|
+
# @param opts [OptionParser] OptionParser instance
|
80
|
+
# @return [void]
|
81
|
+
def add_dry_run_flag(opts)
|
82
|
+
opts.on(
|
83
|
+
'--dry-run',
|
84
|
+
'Run without file modifications'
|
85
|
+
) do
|
86
|
+
@options[:dry] = true
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Adds `--version` flag.
|
91
|
+
#
|
92
|
+
# @param opts [OptionParser] OptionParser instance
|
93
|
+
# @return [void]
|
94
|
+
def add_version_flag(opts)
|
95
|
+
opts.on(
|
96
|
+
'-v',
|
97
|
+
'--version',
|
98
|
+
'Show Chusaku version number and quit'
|
99
|
+
) do
|
100
|
+
puts(Chusaku::VERSION)
|
101
|
+
raise Finished
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Adds `--help` flag.
|
106
|
+
#
|
107
|
+
# @param opts [OptionParser] OptionParser instance
|
108
|
+
# @return [void]
|
109
|
+
def add_help_flag(opts)
|
110
|
+
opts.on(
|
111
|
+
'-h',
|
112
|
+
'--help',
|
113
|
+
'Show this help message and quit'
|
114
|
+
) do
|
115
|
+
puts(opts)
|
116
|
+
raise Finished
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
data/lib/chusaku/parser.rb
CHANGED
@@ -1,32 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Chusaku
|
4
|
+
# Handles parsing a file and groups its lines into categories.
|
4
5
|
module Parser
|
5
|
-
#
|
6
|
+
# Primary method to call.
|
6
7
|
#
|
7
8
|
# Example output:
|
8
9
|
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
19
|
-
#
|
20
|
-
#
|
10
|
+
# {
|
11
|
+
# content: <Original file content>,
|
12
|
+
# groups: [
|
13
|
+
# {
|
14
|
+
# type: :code,
|
15
|
+
# body: 'class Foo\n',
|
16
|
+
# action: nil
|
17
|
+
# },
|
18
|
+
# {
|
19
|
+
# type: :comment,
|
20
|
+
# body: ' # Bar\n # Baz\n',
|
21
|
+
# action: nil
|
22
|
+
# },
|
23
|
+
# {
|
24
|
+
# type: :action,
|
25
|
+
# body: ' def action_name; end\n',
|
26
|
+
# action: 'action_name'
|
27
|
+
# }
|
28
|
+
# {
|
29
|
+
# type: :code,
|
30
|
+
# body: 'end # vanilla is the best flavor\n',
|
31
|
+
# action: nil
|
32
|
+
# }
|
33
|
+
# ]
|
34
|
+
# }
|
21
35
|
#
|
22
|
-
# @param
|
23
|
-
# @param
|
24
|
-
# @return {Array<Hash>}
|
36
|
+
# @param path [String] File path to parse
|
37
|
+
# @param actions [Array<String>] List of valid actions for this route
|
38
|
+
# @return [Hash] { content => String, groups => Array<Hash> }
|
25
39
|
def self.call(path:, actions:)
|
26
40
|
groups = []
|
27
41
|
group = {}
|
42
|
+
content = IO.read(path)
|
28
43
|
|
29
|
-
|
44
|
+
content.each_line do |line|
|
30
45
|
parsed_line = parse_line(line: line, actions: actions)
|
31
46
|
|
32
47
|
if group[:type] != parsed_line[:type]
|
@@ -42,23 +57,25 @@ module Chusaku
|
|
42
57
|
|
43
58
|
# Push the last group onto the array and return.
|
44
59
|
groups.push(group)
|
45
|
-
groups
|
60
|
+
{ content: content, groups: groups }
|
46
61
|
end
|
47
62
|
|
48
|
-
# Given a line and actions, returns the line's type
|
63
|
+
# Given a line and actions, returns the line's type.
|
64
|
+
#
|
65
|
+
# A type can be one of:
|
49
66
|
#
|
50
67
|
# 1. comment - A line that is entirely commented. Lines that have trailing
|
51
68
|
# comments do not fall under this category.
|
52
69
|
# 2. action - A line that contains an action definition.
|
53
70
|
# 3. code - Anything else.
|
54
71
|
#
|
55
|
-
#
|
72
|
+
# Returns a Hash in the form:
|
56
73
|
#
|
57
74
|
# { type: :action, body: 'def foo', action: 'foo' }
|
58
75
|
#
|
59
|
-
# @param
|
60
|
-
# @param
|
61
|
-
# @return {
|
76
|
+
# @param line [String] A line of a file
|
77
|
+
# @param actions [Array<String>] List of valid actions for this route
|
78
|
+
# @return [Hash] { type => Symbol, body => String, action => String }
|
62
79
|
def self.parse_line(line:, actions:)
|
63
80
|
comment_match = /^\s*#.*$/.match(line)
|
64
81
|
def_match = /^\s*def\s+(\w*)\s*\w*.*$/.match(line)
|
data/lib/chusaku/routes.rb
CHANGED
@@ -1,67 +1,107 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module Chusaku
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
4
|
+
# Handles extracting information about the Rails project's routes.
|
5
|
+
class Routes
|
6
|
+
class << self
|
7
|
+
# Primary method to call.
|
8
|
+
#
|
9
|
+
# Example output:
|
10
|
+
#
|
11
|
+
# {
|
12
|
+
# 'users' => {
|
13
|
+
# 'edit' => [
|
14
|
+
# { verb: 'GET', path: '/users/:id', name: 'edit_user' }
|
15
|
+
# ],
|
16
|
+
# 'update' => [
|
17
|
+
# { verb: 'PATCH', path: '/users', name: 'edit_user' },
|
18
|
+
# { verb: 'PUT', path: '/users', name: 'edit_user' }
|
19
|
+
# ]
|
20
|
+
# },
|
21
|
+
# 'empanadas' => {
|
22
|
+
# 'create' => [
|
23
|
+
# { verb: 'POST', path: '/empanadas', name: nil }
|
24
|
+
# ]
|
25
|
+
# }
|
26
|
+
# }
|
27
|
+
#
|
28
|
+
# @return [Hash] Routes hash
|
29
|
+
def call
|
30
|
+
routes = {}
|
27
31
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
+
Rails.application.routes.routes.each do |route|
|
33
|
+
controller, action, defaults = extract_data_from(route)
|
34
|
+
routes[controller] ||= {}
|
35
|
+
routes[controller][action] ||= []
|
36
|
+
|
37
|
+
add_info_for \
|
38
|
+
route: route,
|
39
|
+
routes: routes,
|
40
|
+
controller: controller,
|
41
|
+
action: action,
|
42
|
+
defaults: defaults
|
43
|
+
end
|
32
44
|
|
33
|
-
routes
|
34
|
-
routes[controller][action] ||= []
|
35
|
-
routes[controller][action].push(format_route(route))
|
45
|
+
backfill_routes(routes)
|
36
46
|
end
|
37
47
|
|
38
|
-
|
39
|
-
end
|
48
|
+
private
|
40
49
|
|
41
|
-
|
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
|
42
65
|
|
43
|
-
# Extract
|
66
|
+
# Extract the HTTP verbs for a Rails route. Required for older versions of
|
67
|
+
# Rails that return regular expressions for a route verb which sometimes
|
68
|
+
# contains multiple verbs.
|
44
69
|
#
|
45
|
-
# @param
|
46
|
-
# @return
|
47
|
-
def
|
70
|
+
# @param route [ActionDispatch::Journey::Route] Route given by Rails
|
71
|
+
# @return [Array<String>] List of HTTP verbs for the given route
|
72
|
+
def verbs_for(route)
|
73
|
+
route_verb = route.verb.to_s
|
74
|
+
|
75
|
+
%w[GET POST PUT PATCH DELETE].select do |verb|
|
76
|
+
route_verb.include?(verb)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# Formats information for a given route.
|
81
|
+
#
|
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:)
|
48
87
|
{
|
49
|
-
verb:
|
88
|
+
verb: verb,
|
50
89
|
path: route.path.spec.to_s.gsub('(.:format)', ''),
|
51
|
-
name: route.name
|
90
|
+
name: route.name,
|
91
|
+
defaults: defaults
|
52
92
|
}
|
53
93
|
end
|
54
94
|
|
55
95
|
# Given a routes hash, backfill entries that aren't already filled by
|
56
96
|
# `Rails.application.routes`.
|
57
97
|
#
|
58
|
-
# @param
|
59
|
-
# @return
|
60
|
-
def
|
98
|
+
# @param routes [Hash] Routes hash generated by this class
|
99
|
+
# @return [Hash] Backfilled routes hash
|
100
|
+
def backfill_routes(routes)
|
61
101
|
paths = {}
|
62
102
|
|
63
103
|
routes.each do |_controller, actions|
|
64
|
-
actions.each do |
|
104
|
+
actions.each do |_action, data|
|
65
105
|
data.each do |datum|
|
66
106
|
paths[datum[:path]] ||= datum[:name]
|
67
107
|
datum[:name] ||= paths[datum[:path]]
|
@@ -71,5 +111,18 @@ module Chusaku
|
|
71
111
|
|
72
112
|
routes
|
73
113
|
end
|
114
|
+
|
115
|
+
# Given a route, extract the controller and action strings.
|
116
|
+
#
|
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)
|
123
|
+
|
124
|
+
[controller, action, defaults]
|
125
|
+
end
|
126
|
+
end
|
74
127
|
end
|
75
128
|
end
|
data/lib/chusaku/version.rb
CHANGED
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.4.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: 2020-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -44,14 +44,42 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '12.3'
|
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: '12.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.77'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.77'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-performance
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.5'
|
55
83
|
- !ruby/object:Gem::Dependency
|
56
84
|
name: rails
|
57
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -68,13 +96,14 @@ dependencies:
|
|
68
96
|
version: '2.0'
|
69
97
|
description: Annotate your Rails controllers with route info.
|
70
98
|
email:
|
71
|
-
- nishiki
|
99
|
+
- nishiki@hey.com
|
72
100
|
executables:
|
73
101
|
- chusaku
|
74
102
|
extensions: []
|
75
103
|
extra_rdoc_files: []
|
76
104
|
files:
|
77
105
|
- ".circleci/config.yml"
|
106
|
+
- ".codeclimate.yml"
|
78
107
|
- ".gitignore"
|
79
108
|
- ".rubocop.yml"
|
80
109
|
- Gemfile
|
@@ -86,6 +115,7 @@ files:
|
|
86
115
|
- bin/setup
|
87
116
|
- chusaku.gemspec
|
88
117
|
- lib/chusaku.rb
|
118
|
+
- lib/chusaku/cli.rb
|
89
119
|
- lib/chusaku/parser.rb
|
90
120
|
- lib/chusaku/routes.rb
|
91
121
|
- lib/chusaku/version.rb
|
@@ -112,7 +142,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
142
|
- !ruby/object:Gem::Version
|
113
143
|
version: '0'
|
114
144
|
requirements: []
|
115
|
-
rubygems_version: 3.
|
145
|
+
rubygems_version: 3.1.4
|
116
146
|
signing_key:
|
117
147
|
specification_version: 4
|
118
148
|
summary: Annotate your Rails controllers with route info.
|