BigW Consortium Gitlab

attributes_parser.rb 2.89 KB
Newer Older
Robert Speicher committed
1 2 3 4
module Gitlab
  module Git
    # Class for parsing Git attribute files and extracting the attributes for
    # file patterns.
5 6 7 8 9 10 11
    class AttributesParser
      def initialize(attributes_data)
        @data = attributes_data || ""

        if @data.is_a?(File)
          @patterns = parse_file
        end
Robert Speicher committed
12 13 14 15
      end

      # Returns all the Git attributes for the given path.
      #
16
      # file_path - A path to a file for which to get the attributes.
Robert Speicher committed
17 18
      #
      # Returns a Hash.
19 20
      def attributes(file_path)
        absolute_path = File.join('/', file_path)
Robert Speicher committed
21 22

        patterns.each do |pattern, attrs|
23
          return attrs if File.fnmatch?(pattern, absolute_path)
Robert Speicher committed
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84
        end

        {}
      end

      # Returns a Hash containing the file patterns and their attributes.
      def patterns
        @patterns ||= parse_file
      end

      # Parses an attribute string.
      #
      # These strings can be in the following formats:
      #
      #     text      # => { "text" => true }
      #     -text     # => { "text" => false }
      #     key=value # => { "key" => "value" }
      #
      # string - The string to parse.
      #
      # Returns a Hash containing the attributes and their values.
      def parse_attributes(string)
        values = {}
        dash = '-'
        equal = '='
        binary = 'binary'

        string.split(/\s+/).each do |chunk|
          # Data such as "foo = bar" should be treated as "foo" and "bar" being
          # separate boolean attributes.
          next if chunk == equal

          key = chunk

          # Input: "-foo"
          if chunk.start_with?(dash)
            key = chunk.byteslice(1, chunk.length - 1)
            value = false

          # Input: "foo=bar"
          elsif chunk.include?(equal)
            key, value = chunk.split(equal, 2)

          # Input: "foo"
          else
            value = true
          end

          values[key] = value

          # When the "binary" option is set the "diff" option should be set to
          # the inverse. If "diff" is later set it should overwrite the
          # automatically set value.
          values['diff'] = false if key == binary && value
        end

        values
      end

      # Iterates over every line in the attributes file.
      def each_line
85 86
        @data.each_line do |line|
          break unless line.valid_encoding?
Robert Speicher committed
87

88
          yield line.strip
Robert Speicher committed
89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
        end
      end

      private

      # Parses the Git attributes file.
      def parse_file
        pairs = []
        comment = '#'

        each_line do |line|
          next if line.start_with?(comment) || line.empty?

          pattern, attrs = line.split(/\s+/, 2)

          parsed = attrs ? parse_attributes(attrs) : {}

106 107
          absolute_pattern = File.join('/', pattern)
          pairs << [absolute_pattern, parsed]
Robert Speicher committed
108 109 110 111 112 113 114 115
        end

        # Newer entries take precedence over older entries.
        pairs.reverse.to_h
      end
    end
  end
end