chusaku 0.3.2 → 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/.rubocop.yml +6 -0
- data/chusaku.gemspec +3 -3
- data/lib/chusaku.rb +69 -33
- data/lib/chusaku/cli.rb +15 -15
- data/lib/chusaku/parser.rb +12 -8
- data/lib/chusaku/routes.rb +45 -23
- data/lib/chusaku/version.rb +1 -1
- metadata +4 -4
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/.rubocop.yml
CHANGED
@@ -7,6 +7,9 @@ AllCops:
|
|
7
7
|
- 'test/mock/app/**/*'
|
8
8
|
- 'test/mock/examples/**/*'
|
9
9
|
|
10
|
+
Layout/LineLength:
|
11
|
+
Max: 120
|
12
|
+
|
10
13
|
Metrics/AbcSize:
|
11
14
|
Exclude:
|
12
15
|
- 'test/**/*'
|
@@ -16,6 +19,9 @@ Metrics/MethodLength:
|
|
16
19
|
Exclude:
|
17
20
|
- 'test/**/*'
|
18
21
|
|
22
|
+
Metrics/ModuleLength:
|
23
|
+
Max: 250
|
24
|
+
|
19
25
|
Style/ClassAndModuleChildren:
|
20
26
|
Exclude:
|
21
27
|
- 'test/**/*'
|
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,8 +37,8 @@ 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'
|
data/lib/chusaku.rb
CHANGED
@@ -15,8 +15,8 @@ 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
|
@@ -38,10 +38,10 @@ 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|
|
@@ -59,7 +59,7 @@ module Chusaku
|
|
59
59
|
|
60
60
|
# Given a parsed group, clean out its contents.
|
61
61
|
#
|
62
|
-
# @param
|
62
|
+
# @param group [Hash] { type => Symbol, body => String }
|
63
63
|
# @return {void}
|
64
64
|
def clean_group(group)
|
65
65
|
return unless group[:type] == :comment
|
@@ -74,49 +74,85 @@ module Chusaku
|
|
74
74
|
#
|
75
75
|
# @route GET /waterlilies/:id (waterlilies)
|
76
76
|
#
|
77
|
-
# @param
|
78
|
-
# @param
|
79
|
-
# @return
|
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
80
|
def annotate_group(group:, route_data:)
|
81
81
|
whitespace = /^(\s*).*$/.match(group[:body])[1]
|
82
82
|
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"
|
83
|
+
comment = "#{whitespace}# #{annotate_route(**datum)}\n"
|
87
84
|
group[:body] = comment + group[:body]
|
88
85
|
end
|
89
86
|
end
|
90
87
|
|
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
|
+
|
91
108
|
# Write annotated content to a file if it differs from the original.
|
92
109
|
#
|
93
|
-
# @param
|
94
|
-
# @param
|
95
|
-
# @return
|
110
|
+
# @param path [String] File path to write to
|
111
|
+
# @param parsed_file [Hash] Hash mutated by {#annotate_group}
|
112
|
+
# @return [void]
|
96
113
|
def write_to_file(path:, parsed_file:)
|
97
|
-
|
98
|
-
return unless parsed_file[:content] !=
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
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)
|
111
141
|
end
|
112
142
|
end
|
143
|
+
end
|
113
144
|
|
114
|
-
|
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'
|
115
151
|
end
|
116
152
|
|
117
153
|
# Output results to user.
|
118
154
|
#
|
119
|
-
# @return
|
155
|
+
# @return [Integer] 0 for success, 1 for error
|
120
156
|
def output_results
|
121
157
|
if @annotated_paths.any?
|
122
158
|
puts("Annotated #{@annotated_paths.join(', ')}")
|
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,7 +50,7 @@ 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]'
|
@@ -63,8 +63,8 @@ module Chusaku
|
|
63
63
|
|
64
64
|
# Adds `--exit-with-error-on-annotation` flag.
|
65
65
|
#
|
66
|
-
# @param
|
67
|
-
# @return
|
66
|
+
# @param opts [OptionParser] OptionParser instance
|
67
|
+
# @return [void]
|
68
68
|
def add_error_on_annotation_flag(opts)
|
69
69
|
opts.on(
|
70
70
|
'--exit-with-error-on-annotation',
|
@@ -76,8 +76,8 @@ module Chusaku
|
|
76
76
|
|
77
77
|
# Adds `--dry-run` flag.
|
78
78
|
#
|
79
|
-
# @param
|
80
|
-
# @return
|
79
|
+
# @param opts [OptionParser] OptionParser instance
|
80
|
+
# @return [void]
|
81
81
|
def add_dry_run_flag(opts)
|
82
82
|
opts.on(
|
83
83
|
'--dry-run',
|
@@ -89,8 +89,8 @@ module Chusaku
|
|
89
89
|
|
90
90
|
# Adds `--version` flag.
|
91
91
|
#
|
92
|
-
# @param
|
93
|
-
# @return
|
92
|
+
# @param opts [OptionParser] OptionParser instance
|
93
|
+
# @return [void]
|
94
94
|
def add_version_flag(opts)
|
95
95
|
opts.on(
|
96
96
|
'-v',
|
@@ -104,8 +104,8 @@ module Chusaku
|
|
104
104
|
|
105
105
|
# Adds `--help` flag.
|
106
106
|
#
|
107
|
-
# @param
|
108
|
-
# @return
|
107
|
+
# @param opts [OptionParser] OptionParser instance
|
108
|
+
# @return [void]
|
109
109
|
def add_help_flag(opts)
|
110
110
|
opts.on(
|
111
111
|
'-h',
|
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
|
# {
|
@@ -31,9 +33,9 @@ module Chusaku
|
|
31
33
|
# ]
|
32
34
|
# }
|
33
35
|
#
|
34
|
-
# @param
|
35
|
-
# @param
|
36
|
-
# @return
|
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> }
|
37
39
|
def self.call(path:, actions:)
|
38
40
|
groups = []
|
39
41
|
group = {}
|
@@ -58,20 +60,22 @@ module Chusaku
|
|
58
60
|
{ content: content, groups: groups }
|
59
61
|
end
|
60
62
|
|
61
|
-
# 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:
|
62
66
|
#
|
63
67
|
# 1. comment - A line that is entirely commented. Lines that have trailing
|
64
68
|
# comments do not fall under this category.
|
65
69
|
# 2. action - A line that contains an action definition.
|
66
70
|
# 3. code - Anything else.
|
67
71
|
#
|
68
|
-
#
|
72
|
+
# Returns a Hash in the form:
|
69
73
|
#
|
70
74
|
# { type: :action, body: 'def foo', action: 'foo' }
|
71
75
|
#
|
72
|
-
# @param
|
73
|
-
# @param
|
74
|
-
# @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 }
|
75
79
|
def self.parse_line(line:, actions:)
|
76
80
|
comment_match = /^\s*#.*$/.match(line)
|
77
81
|
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
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: 2020-
|
11
|
+
date: 2020-12-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -96,7 +96,7 @@ dependencies:
|
|
96
96
|
version: '2.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: []
|
@@ -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.4
|
146
146
|
signing_key:
|
147
147
|
specification_version: 4
|
148
148
|
summary: Annotate your Rails controllers with route info.
|