ajb (owner)

Revisions

gist: 55955 Download_button fork
public
Public Clone URL: git://gist.github.com/55955.git
Ruby
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
117
118
119
120
121
122
123
124
125
126
# A collection is a list (or an array, if you will) of symbols. This class is inspired by
# Ada 95's Enumeration Types, although the only relevant components of which are essentially just 'an array that wraps.'
# This class maintains an internal pointer, which points to the current symbol in the collection.
# Author: Allyn Bauer <allyn.bauer@gmail.com>
# v1.2 3 February 2009
#
class Collection
  # Items is a list or an Array of the items this method will contain. These items are not changeable.
  def initialize(*items)
    if items.first.is_a? Array # if items is an array
      @collection = *items
    else
      @collection = items
    end
    
    @collection.collect! { |item| item.to_sym }
    @collection.freeze # Collection does not allow changes to its data
    # the only real reason for this is not wanting to handle pointer issues that could result from modification
    
    @pointer = 0 # Our internal pointer
    
  rescue NoMethodError => exc # A collection maintains symbols.
    raise ArgumentError.new("Cannot instantiate Collection because an item can't be made into a symbol.\n#{exc}")
  end
 
  # Advances the pointer and returns the element that is amt from the current, wrapping.
  def next(amt = 1)
    @pointer = up(amt)
    current
  end
  
  # Reduces the pointer and return the element that is amt before the current, wrapping.
  def prev(amt = 1)
    @pointer = down(amt)
    current
  end
  
  # Return the item that is before object
  def predecessor_of(object)
    action_of(object, :down)
  end
  
  # Return the item that is after object
  def successor_of(object)
    action_of(object, :up)
  end
  
  # Sets the internal pointer to the position provided, or the position of the object provided
  # If the provided input is invalid, @pointer is reset
  def set_to(pos)
    if index?(pos)
      @pointer = wrap(pos)
    elsif @collection.include?(pos)
      @pointer = index(pos)
    else
      @pointer = 0
    end
    current
  end
  
  # Returns the current pointer location as an integer
  def pos
    @pointer
  end
  
  # Return the current element without advancing the pointer
  def current
    item_at pos
  end
  
  # The item at position
  def item_at(position)
    @collection[position]
  end
  
  def index(object)
    @collection.index(object.to_sym)
  end
  
  # Reset the internal pointer to the beginning.
  def reset
    @pointer = 0
  end
  
  def to_s
    @collection.collect { |x| x == current ? "(#{x})" : x }.join(" ")
  end
  
  def to_a
    @collection
  end
  
  protected
  
  # If we can't find a method here, give it to the array.
  def method_missing(*args, &block)
    @collection.send(*args, &block)
  end
  
  private
  
  def index?(item)
    item.is_a? Fixnum or item.is_a? Integer
  end
  
  def action_of(object, direction)
    if index?(object)
      item_at(send(direction, 1, object))
    else
      item_at(send(direction, 1, @collection.index(object)))
    end
  end
  
  # Return an wrapped integer, which is @pointer++ or from++
  def up(amt = 1, from = @pointer)
    wrap(from + amt)
  end
  
  def down(amt = 1, from = @pointer)
    wrap(from - amt)
  end
  
  def wrap(int)
    int % @collection.length
  end
end