This file is indexed.

/usr/lib/ruby/vendor_ruby/fakeredis/sort_method.rb is in ruby-fakeredis 0.5.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 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
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Codes are mostly referenced from MockRedis' implementation.
module FakeRedis
  module SortMethod
    def sort(key, *redis_options_array)
      return [] unless key

      unless %w(list set zset).include? type(key)
        warn "Operation against a key holding the wrong kind of value: Expected list, set or zset at #{key}."
        raise Redis::CommandError.new("WRONGTYPE Operation against a key holding the wrong kind of value")
      end

      # redis_options is an array of format [BY pattern] [LIMIT offset count] [GET pattern [GET pattern ...]] [ASC|DESC] [ALPHA] [STORE destination]
      # Lets nibble it back into a hash
      options = extract_options_from(redis_options_array)

      # And now to actually do the work of this method

      projected = project(data[key], options[:by], options[:get])
      sorted    = sort_by(projected, options[:order])
      sliced    = slice(sorted, options[:limit])
      # We have to flatten it down as redis-rb adds back the array to the return value
      result = sliced.flatten(1)

      options[:store] ? rpush(options[:store], sliced) : sliced.flatten(1)
    end

    private

    ASCENDING_SORT  = Proc.new { |a, b| a.first <=> b.first }
    DESCENDING_SORT = Proc.new { |a, b| b.first <=> a.first }

    def extract_options_from(options_array)
      # Defaults
      options = {
        :limit => [],
        :order => "ASC",
        :get => []
      }

      if options_array.first == "BY"
        options_array.shift
        options[:by] = options_array.shift
      end

      if options_array.first == "LIMIT"
        options_array.shift
        options[:limit] = [options_array.shift, options_array.shift]
      end

      while options_array.first == "GET"
        options_array.shift
        options[:get] << options_array.shift
      end

      if %w(ASC DESC ALPHA).include?(options_array.first)
        options[:order] = options_array.shift
        options[:order] = "ASC" if options[:order] == "ALPHA"
      end

      if options_array.first == "STORE"
        options_array.shift
        options[:store] = options_array.shift
      end

      options
    end

    def project(enumerable, by, get_patterns)
      enumerable.map do |*elements|
        element = elements.flatten.first
        weight  = by ? lookup_from_pattern(by, element) : element
        value   = element

        if get_patterns.length > 0
          value = get_patterns.map do |pattern|
            pattern == "#" ? element : lookup_from_pattern(pattern, element)
          end
          value = value.first if value.length == 1
        end

        [weight, value]
      end
    end

    def sort_by(projected, direction)
      sorter =
        case direction.upcase
          when "DESC"
            DESCENDING_SORT
          when "ASC", "ALPHA"
            ASCENDING_SORT
          else
            raise "Invalid direction '#{direction}'"
        end

      projected.sort(&sorter).map(&:last)
    end

    def slice(sorted, limit)
      skip = limit.first || 0
      take = limit.last || sorted.length

      sorted[skip...(skip + take)] || sorted
    end

    def lookup_from_pattern(pattern, element)
      key = pattern.sub('*', element)

      if (hash_parts = key.split('->')).length > 1
        hget hash_parts.first, hash_parts.last
      else
        get key
      end
    end
  end
end