summaryrefslogtreecommitdiff
path: root/lib/bundler/vendor/thor-0.19.1/lib/thor/core_ext/ordered_hash.rb
blob: 7e80672a07a1f5c2a8ebec6006583ebb748793c7 (plain)
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
class Bundler::Thor
  module CoreExt #:nodoc:
    if RUBY_VERSION >= "1.9"
      class OrderedHash < ::Hash
      end
    else
      # This class is based on the Ruby 1.9 ordered hashes.
      #
      # It keeps the semantics and most of the efficiency of normal hashes
      # while also keeping track of the order in which elements were set.
      #
      class OrderedHash #:nodoc:
        include Enumerable

        Node = Struct.new(:key, :value, :next, :prev)

        def initialize
          @hash = {}
        end

        def [](key)
          @hash[key] && @hash[key].value
        end

        def []=(key, value)
          if node = @hash[key] # rubocop:disable AssignmentInCondition
            node.value = value
          else
            node = Node.new(key, value)

            if !defined?(@first) || @first.nil?
              @first = @last = node
            else
              node.prev = @last
              @last.next = node
              @last = node
            end
          end

          @hash[key] = node
          value
        end

        def delete(key)
          if node = @hash[key] # rubocop:disable AssignmentInCondition
            prev_node = node.prev
            next_node = node.next

            next_node.prev = prev_node if next_node
            prev_node.next = next_node if prev_node

            @first = next_node if @first == node
            @last = prev_node  if @last  == node

            value = node.value
          end

          @hash.delete(key)
          value
        end

        def keys
          map { |k, v| k }
        end

        def values
          map { |k, v| v }
        end

        def each
          return unless defined?(@first) && @first
          yield [@first.key, @first.value]
          node = @first
          yield [node.key, node.value] while node = node.next # rubocop:disable AssignmentInCondition
          self
        end

        def merge(other)
          hash = self.class.new

          each do |key, value|
            hash[key] = value
          end

          other.each do |key, value|
            hash[key] = value
          end

          hash
        end

        def empty?
          @hash.empty?
        end
      end
    end
  end
end