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