module Chusaku # Handles parsing a file and groups its lines into categories. module Parser # Primary method to call. # # Example output: # # { # content: , # groups: [ # { # type: :code, # body: 'class Foo\n', # action: nil, # line_number: 1 # }, # { # type: :comment, # body: ' # Bar\n # Baz\n', # action: nil, # line_number: 2 # }, # { # type: :action, # body: ' def action_name; end\n', # action: 'action_name', # line_number: 4 # } # { # type: :code, # body: 'end # vanilla is the best flavor\n', # action: nil, # line_number: 5 # } # ] # } # # @param path [String] File path to parse # @param actions [Array] List of valid actions for this route # @return [Hash] { content => String, groups => Array } def self.call(path:, actions:) groups = [] group = {} content = IO.read(path) content.each_line.with_index do |line, index| parsed_line = parse_line(line: line, actions: actions) if group[:type] == parsed_line[:type] # Same group. Push the current line into the current group. group[:body] += line else # Now looking at a new group. Push the current group onto the array # and start a new one. groups.push(group) unless group.empty? group = parsed_line.merge(line_number: index + 1) end end # Push the last group onto the array and return. groups.push(group) {content: content, groups: groups} end # Given a line and actions, returns the line's type. # # A type can be one of: # # 1. comment - A line that is entirely commented. Lines that have trailing # comments do not fall under this category. # 2. action - A line that contains an action definition. # 3. code - Anything else. # # Returns a Hash in the form: # # { type: :action, body: 'def foo', action: 'foo' } # # @param line [String] A line of a file # @param actions [Array] List of valid actions for this route # @return [Hash] { type => Symbol, body => String, action => String } def self.parse_line(line:, actions:) comment_match = /^\s*#.*$/.match(line) def_match = /^\s*def\s+(\w*)\s*\w*.*$/.match(line) if !comment_match.nil? {type: :comment, body: line, action: nil} elsif !def_match.nil? && actions.include?(def_match[1]) {type: :action, body: line, action: def_match[1]} else {type: :code, body: line, action: nil} end end end end