lib/chusaku.rb in chusaku-0.3.2 vs lib/chusaku.rb in chusaku-0.4.0
- old
+ new
@@ -13,12 +13,12 @@
# # @route GET /waterlilies/:id (waterlilies)
# def show
# # ...
# end
#
- # @param {Hash} flags - CLI flags
- # @return {Integer} - 0 on success, 1 on error
+ # @param flags [Hash] CLI flags
+ # @return [Integer] 0 on success, 1 on error
def call(flags = {})
@flags = flags
@routes = Chusaku::Routes.call
@annotated_paths = []
controllers_pattern = 'app/controllers/**/*_controller.rb'
@@ -36,14 +36,14 @@
private
# Adds annotations to the given file.
#
- # @param {String} path - Path to file
- # @param {String} controller - Controller name
- # @param {Array<String>} actions - List of valid actions for the controller
- # @return {void}
+ # @param path [String] Path to file
+ # @param controller [String] Controller name
+ # @param actions [Array<String>] List of valid actions for the controller
+ # @return [void]
def annotate_file(path:, controller:, actions:)
parsed_file = Chusaku::Parser.call(path: path, actions: actions)
parsed_file[:groups].each_cons(2) do |prev, curr|
clean_group(prev)
next unless curr[:type] == :action
@@ -57,11 +57,11 @@
write_to_file(path: path, parsed_file: parsed_file)
end
# Given a parsed group, clean out its contents.
#
- # @param {Hash} group - { type: Symbol, body: String }
+ # @param group [Hash] { type => Symbol, body => String }
# @return {void}
def clean_group(group)
return unless group[:type] == :comment
group[:body] = group[:body].gsub(/^\s*#\s*@route.*$\n/, '')
@@ -72,52 +72,88 @@
# Add an annotation to the given group given by Chusaku::Parser that looks
# like:
#
# @route GET /waterlilies/:id (waterlilies)
#
- # @param {Hash} group - Parsed content given by Chusaku::Parser
- # @param {Hash} route_data - Individual route data given by Chusaku::Routes
- # @return {void}
+ # @param group [Hash] Parsed content given by Chusaku::Parser
+ # @param route_data [Hash] Individual route data given by Chusaku::Routes
+ # @return [void]
def annotate_group(group:, route_data:)
whitespace = /^(\s*).*$/.match(group[:body])[1]
route_data.reverse_each do |datum|
- name = datum[:name]
- annotation = "@route #{datum[:verb]} #{datum[:path]}"
- annotation += " (#{name})" unless name.nil?
- comment = "#{whitespace}# #{annotation}\n"
+ comment = "#{whitespace}# #{annotate_route(**datum)}\n"
group[:body] = comment + group[:body]
end
end
+ # Generate route annotation.
+ #
+ # @param verb [String] HTTP verb for route
+ # @param path [String] Rails path for route
+ # @param name [String] Name used in route helpers
+ # @param defaults [Hash] Default parameters for route
+ # @return [String] "@route <verb> <path> {<defaults>} (<name>)"
+ def annotate_route(verb:, path:, name:, defaults:)
+ annotation = "@route #{verb} #{path}"
+ if defaults&.any?
+ defaults_str =
+ defaults
+ .map { |key, value| "#{key}: #{value.inspect}" }
+ .join(', ')
+ annotation += " {#{defaults_str}}"
+ end
+ annotation += " (#{name})" unless name.nil?
+ annotation
+ end
+
# Write annotated content to a file if it differs from the original.
#
- # @param {String} path - File path to write to
- # @param {Hash} parsed_file - Hash mutated by `annotate_group`
- # @return {void}
+ # @param path [String] File path to write to
+ # @param parsed_file [Hash] Hash mutated by {#annotate_group}
+ # @return [void]
def write_to_file(path:, parsed_file:)
- content = parsed_file[:groups].map { |pf| pf[:body] }.join
- return unless parsed_file[:content] != content
+ new_content = new_content_for(parsed_file)
+ return unless parsed_file[:content] != new_content
- unless @flags.include?(:dry)
- # When running the test suite, we want to make sure we're not
- # overwriting any files. `r` mode ensures that.
- mode = File.instance_methods.include?(:test_write) ? 'r' : 'w'
+ !@flags.include?(:dry) && perform_write(path: path, content: new_content)
+ @annotated_paths.push(path)
+ end
- File.open(path, mode) do |file|
- if file.respond_to?(:test_write)
- file.test_write(content, path)
- else
- file.write(content)
- end
+ # Extracts the new file content for the given parsed file.
+ #
+ # @param parsed_file [Hash] { groups => Array<Hash> }
+ # @return [String] New file content
+ def new_content_for(parsed_file)
+ parsed_file[:groups].map { |pf| pf[:body] }.join
+ end
+
+ # Wraps the write operation. Needed to clearly distinguish whether it's a
+ # write in the test suite or a write in actual use.
+ #
+ # @param path [String] File path
+ # @param content [String] File content
+ # @return [void]
+ def perform_write(path:, content:)
+ File.open(path, file_mode) do |file|
+ if file.respond_to?(:test_write)
+ file.test_write(content, path)
+ else
+ file.write(content)
end
end
+ end
- @annotated_paths.push(path)
+ # When running the test suite, we want to make sure we're not overwriting
+ # any files. `r` mode ensures that, and `w` is used for actual usage.
+ #
+ # @return [String] 'r' or 'w'
+ def file_mode
+ File.instance_methods.include?(:test_write) ? 'r' : 'w'
end
# Output results to user.
#
- # @return {Integer} - 0 for success, 1 for error
+ # @return [Integer] 0 for success, 1 for error
def output_results
if @annotated_paths.any?
puts("Annotated #{@annotated_paths.join(', ')}")
@flags.include?(:error_on_annotation) ? 1 : 0
else