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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f2976318f89e26e31f77c54e1cc338812ac6a107ec033d53c077fd4eb1759be6
4
- data.tar.gz: 8413d9353d6115929985f3d6275396abeb25d7b9df834ccd0d4e19fd834ef574
3
+ metadata.gz: 76adb49d62bcaeab5291f4b09b12318ea5e80dc7c4b77ff7bbfee697732a953f
4
+ data.tar.gz: 8434d642b81b5d4cb67b7f9852adef2da679c24f7c9ea287aba903653b9703ee
5
5
  SHA512:
6
- metadata.gz: 44c12d0f6c0f970cd3a0e5c76cd14b18d8bfbf16f7d39e8789e4c09c87a8273a8b3e288de865533c7a39b81fe1dd51088a1ddc7c33edb0325e12a676153d5563
7
- data.tar.gz: 5e01aeaa4eea9099c47445c88db326ff4ceb5d028088bbd6c2a328f21673c17d80c93bc8fda0c4135e76956f1439a26df758985a23bfe895d5f3f2ff14eb3982
6
+ metadata.gz: 7b5880ed576e31a7f80a970389dc559e81ec932005716122de67a5f2c25586f82e674e6bdf1aba100d13a5bc7cba41df458b6e439c825684fa51ef11f77c6337
7
+ data.tar.gz: fa2ed22e8e87762f4577f4e45765f22c1e2d8139eb8d02f18682f7859ac543bfad8977b545645717a5f6acb2fd75f1eac02de6697982de6eebc02cedd595c68c
@@ -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/**/*'
@@ -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.liu@gmail.com']
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 = 'bin'
41
- spec.executables = 'chusaku'
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'
@@ -15,8 +15,8 @@ module Chusaku
15
15
  # # ...
16
16
  # end
17
17
  #
18
- # @param {Hash} flags - CLI flags
19
- # @return {Integer} - 0 on success, 1 on error
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 {String} path - Path to file
42
- # @param {String} controller - Controller name
43
- # @param {Array<String>} actions - List of valid actions for the controller
44
- # @return {void}
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 {Hash} group - { type: Symbol, body: String }
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 {Hash} group - Parsed content given by Chusaku::Parser
78
- # @param {Hash} route_data - Individual route data given by Chusaku::Routes
79
- # @return {void}
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
- name = datum[:name]
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 {String} path - File path to write to
94
- # @param {Hash} parsed_file - Hash mutated by `annotate_group`
95
- # @return {void}
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
- content = parsed_file[:groups].map { |pf| pf[:body] }.join
98
- return unless parsed_file[:content] != content
99
-
100
- unless @flags.include?(:dry)
101
- # When running the test suite, we want to make sure we're not
102
- # overwriting any files. `r` mode ensures that.
103
- mode = File.instance_methods.include?(:test_write) ? 'r' : 'w'
104
-
105
- File.open(path, mode) do |file|
106
- if file.respond_to?(:test_write)
107
- file.test_write(content, path)
108
- else
109
- file.write(content)
110
- end
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
- @annotated_paths.push(path)
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 {Integer} - 0 for success, 1 for error
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(', ')}")
@@ -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 {Chusaku::CLI} - Instance of this class
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 {Array<String>} args - CLI arguments
27
- # @return {Integer} - 0 on success, 1 on error
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 {Chusaku::CLI::NotARailsProject} - Exception if not Rails project
44
- # @return {void}
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 {OptionParser} - Preconfigured OptionParser instance
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 {OptionParser} opts - OptionParser instance
67
- # @return {void}
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 {OptionParser} opts - OptionParser instance
80
- # @return {void}
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 {OptionParser} opts - OptionParser instance
93
- # @return {void}
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 {OptionParser} opts - OptionParser instance
108
- # @return {void}
107
+ # @param opts [OptionParser] OptionParser instance
108
+ # @return [void]
109
109
  def add_help_flag(opts)
110
110
  opts.on(
111
111
  '-h',
@@ -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 {String} path - File path to parse
35
- # @param {Array<String>} actions - List of valid actions for this route
36
- # @return {Hash} - { content: String, groups: 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> }
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
- # And give back a Hash in the form:
72
+ # Returns a Hash in the form:
69
73
  #
70
74
  # { type: :action, body: 'def foo', action: 'foo' }
71
75
  #
72
- # @param {String} line - A line of a file
73
- # @param {Array<String>} actions - List of valid actions for this route
74
- # @return {Hash} - { type: Symbol, body: String, action: String }
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)
@@ -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 {Hash} - Routes hash
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 = extract_controller_and_action_from(route)
33
+ controller, action, defaults = extract_data_from(route)
32
34
  routes[controller] ||= {}
33
35
  routes[controller][action] ||= []
34
36
 
35
- verbs_for(route).each do |verb|
36
- routes[controller][action].push(format(route: route, verb: verb))
37
- routes[controller][action].uniq!
38
- end
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 {ActionDispatch::Journey::Route} route - Route given by Rails
51
- # @return {Array<String>} - List of HTTP verbs for the given route
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
- ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'].select do |verb|
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 {ActionDispatch::Journey::Route} route - Route given by Rails
63
- # @param {String} verb - HTTP verb
64
- # @return {Hash} - { verb: String, path: String, name: String }
65
- def format(route:, verb:)
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 {Hash} routes - Routes hash generated by this class
77
- # @return {Hash} - Backfilled routes hash
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 {ActionDispatch::Journey::Route} route - Route instance
96
- # @return {Array<String>} - [String, String]
97
- def extract_controller_and_action_from(route)
98
- defaults = route.defaults
99
- controller = defaults[:controller]
100
- action = defaults[:action]
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Chusaku
4
- VERSION = '0.3.2'
4
+ VERSION = '0.4.0'
5
5
  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.3.2
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-07-09 00:00:00.000000000 Z
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.liu@gmail.com
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.2
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.