class Blufin::ScannerJs

Constants

DATA_TYPES

Public Class Methods

scan(path) click to toggle source

Scan Javascript code. @return void

# File lib/scan/scanner_js.rb, line 9
def self.scan(path)

    # Check path exists.
    Blufin::Terminal::error("Path does not exist: #{Blufin::Terminal::format_invalid(path)}") unless Blufin::Files::path_exists(path)

    @path   = path
    @data   = {}
    @errors = []

    # Get all file(s) in path.
    Blufin::Files::get_files_in_dir(path, 'js').each { |file| scan_file(file) }

    return @data, @errors

end

Private Class Methods

extract_type(string, allow_void = false) click to toggle source

Attempts to extract 'string' from something like '{string}'. Will throw Error if anything isn't right. @return string

# File lib/scan/scanner_js.rb, line 176
def self.extract_type(string, allow_void = false)
    types = DATA_TYPES
    types << 'void' if allow_void
    type          = string.gsub(/^\{/, '').gsub(/\}$/, '')
    types_compare = type =~ /\|/ ? type.split('|') : [type]
    types_compare.each do |type_inner|
        raise RuntimeError, "Unrecognized data type: #{type_inner}" unless types.include?(type_inner.downcase)
        raise RuntimeError, "Data types must be lowercase, found: #{type_inner}" if type_inner.downcase != 'array' && type_inner != type_inner.downcase
    end
    type
end
parse_comment(line) click to toggle source

Parses comments. @return void

# File lib/scan/scanner_js.rb, line 96
def self.parse_comment(line)
    if line =~ /^\s*\/\*+\s*.*$/
        # /** OR *
        return
    elsif line =~ /^\s*\/+\s*\w.*$/
        # // some comment
        return
    elsif line =~ /^\s*\*\/\s*$/
        # */
        return
    elsif line =~ /^\s*\*+\s*[A-Za-z0-9].*$/
        # * some comment
        @docs[:description] = [] if @docs[:description].nil?
        @docs[:description] << line.gsub(/^\s*\*\s*/, '').strip
    elsif line =~ /^\s*\*+\s*@param\s*\{(\w|\*)+\}*.+$/
        # * @param {string} parent - The parent key.
        ls          = line.gsub(/^\s*\*+\s*@param\s+/, '').split(' ')
        type        = extract_type(ls[0])
        param_name  = ls[1]
        param_name  = param_name.strip.gsub(/^\[/, '').gsub(/]$/, '')
        hyphen      = ls[2]
        description = ls.drop(3).join(' ')
        raise RuntimeError, "Duplicate parameter: #{param_name}" if @docs[:params].is_a?(Hash) && @docs[:params].has_key?(param_name)
        raise RuntimeError, "Expected hyphen after parameter name. IE: {#{type}} #{param_name} - ..." if ls.length > 2 && hyphen != '-'
        @docs[:params]                           = {} if @docs[:params].nil?
        @docs[:params][param_name]               = {}
        @docs[:params][param_name][:type]        = type
        @docs[:params][param_name][:description] = description
    elsif line =~ /^\s*\*+\s*@returns\s+\{(\w|\*)+\}.*$/
        # * @returns {boolean}
        raise RuntimeError, 'Multiple return statement(s).' if @docs.has_key?(:return)
        ls             = line.gsub(/^\s*\*+\s*@returns\s+/, '').split(' ')
        @docs[:return] = nil
        @docs[:return] = extract_type(ls[0], true) if ls.any?
    elsif line.strip == '*'
        return
    else
        raise RuntimeError, 'Something is wrong with this line, it failed to parse.'
    end
end
parse_method_definition(line) click to toggle source

Parses the 1st line of a method and extracts information about the parameters. @return void

# File lib/scan/scanner_js.rb, line 139
def self.parse_method_definition(line)
    method_name    = line.strip.gsub(/\(.*$/, '')
    method_private = method_name =~ /^_/ ? true : false
    raise RuntimeError, "Duplicate method: #{method_name}" if @data[@file_short][:methods].keys.include?(method_name)
    matches = line.match(/\(.*\)/)
    raise RuntimeError, 'Unable to extract method parameters.' unless matches.length == 1
    params = matches[0].strip.gsub(/^\(/, '').gsub(/\)$/, '')
    params = params.split(',')
    params.each do |param|
        param.strip!
        if param =~ /\w+\s*=\s*\w+/
            ps = param.split('=')
            raise RuntimeError, "Expected exactly one equal (=) sign, got: #{ps.length - 1}" unless ps.length == 2
            param_extracted = ps[0].strip
            raise RuntimeError, "Duplicate parameter: #{param_extracted}" if @params.has_key?(param_extracted)
            @params[param_extracted] = {:default => ps[1].strip}
        elsif param =~ /\w+/
            raise RuntimeError, "Duplicate parameter: #{param}" if @params.has_key?(param)
            @params[param] = {}
        else
            raise RuntimeError, "Unrecognized parameter: #{param}"
        end
    end
    @docs                                     = {:params => {}} unless @docs.is_a?(Hash)
    @docs[:params]                            = {} unless @docs[:params].is_a?(Hash)
    @data[@file_short][:methods][method_name] = {
        :line        => @line,
        :line_number => @line_number,
        :docs        => @docs,
        :params      => @params,
        :private     => method_private
    }
end
scan_file(file) click to toggle source

Scans a file. @return void

# File lib/scan/scanner_js.rb, line 29
def self.scan_file(file)

    @file                        = file
    @file_short                  = Blufin::Strings::remove_surrounding_slashes(file.gsub(@path, ''))
    @data[@file_short]           = {}
    @data[@file_short][:methods] = {}
    @data[@file_short][:file]    = file

    @line          = nil
    @line_number   = 0
    @params        = nil
    @docs          = nil
    @docs_active   = false
    @method_active = false

    Blufin::Files::read_file(file).each do |line|

        begin

            @line        = line.gsub("\n", '')
            @line_number += 1

            if @line =~ /^\s*\/+\s*\w.*$/ # // some comment
                parse_comment(@line)
            elsif @line =~ /^\s*\/\*+\s*.*$/ # /** OR *
                @docs        = {}
                @docs_active = true
            elsif @line =~ /^\s{4}\w+\s*(:\s*function)?\(.*\)\s*\{\s*(\}|\},)?\s*$/
                # clear() {}
                # clear() {},
                # clear(key) {
                # clear(key, value) {
                # clear(key, value = null) {
                # set: function() {
                # set: function(key) {
                # set: function(key, value) {
                # set: function(key, value = null) {
                @method_active = true
                @params        = {}
                # Extract method data.
                parse_method_definition(@line)
                @docs = nil
            end

            # If we're inside a comment.
            parse_comment(@line) if @docs_active

            # Break out of comment ( ... */)
            @docs_active   = false if @line =~ /^.*\*+\/\s*$/

            # Break out of method ( ... } OR }, )
            @method_active = false if @line =~ /^\s{4}(\}|\},)\s*$/ || @line =~ /^\s{4}\w+\s*(:\s*function)?\(.*\)\s*\{\s*(\}|\},)\s*$/

        rescue RuntimeError => e
            @errors << Blufin::ScannerError.new(file, @line, @line_number, e.message)
            next
        end

    end

    # Make sure we have at least one method.
    @errors << Blufin::ScannerError.new(file, nil, nil, "No methods/functions found in file: #{file}") unless @data[@file_short][:methods].length > 0

end