added capacity for pulling static pages from multiple directory source trees.
[leap_website.git] / lib / menu.rb
1 #
2 # A navigation menu class
3 #
4
5 class Menu
6   attr_accessor :parent
7   attr_accessor :children
8   attr_accessor :name
9
10   #
11   # load the menu.txt file and build the in-memory menu array
12   #
13   def load(menu_file_path)
14     File.open(menu_file_path) do |file|
15       parse_menu(file)
16     end
17   end
18
19   def initialize(name, parent=nil)
20     self.name = name
21     self.parent = parent
22     self.children = []
23   end
24
25   ##
26   ## public methods
27   ##
28
29   #
30   # returns the menu under the item that matches item_name.
31   #
32   def submenu(item_name=nil)
33     if item_name
34       self.children.detect {|child| child.name == item_name} || Menu.new
35     else
36       self.children
37     end
38   end
39
40   #
41   # returns path from root to this leaf as an array
42   #
43   def path
44     @path ||= begin
45       if parent == nil
46         []
47       else
48         parent.path + [name]
49       end
50     end
51   end
52
53   def each(&block)
54     children.each(&block)
55   end
56
57   def size
58     children.size
59   end
60
61   #
62   # returns true if menu's path starts with +path_prefix+
63   #
64   def path_starts_with?(path_prefix)
65     array_starts_with?(path, path_prefix)
66   end
67
68   def path_prefix_of?(full_path)
69     array_starts_with?(full_path, path)
70   end
71
72   #
73   # returns true if this menu item is the terminus menu item for path.
74   # (meaning that there are no children that match more path segments)
75   #
76   def leaf_for_path?(path)
77     return false unless path_prefix_of?(path)
78     next_path_segment = (path - self.path).first
79     return false if next_path_segment.nil?
80     return !children.detect {|i| i.name == next_path_segment}
81   end
82
83   def inspect(indent=0)
84     lines = []
85     lines << '  '*indent + '- ' + self.name
86     self.children.each do |child|
87       lines << child.inspect(indent+1)
88     end
89     lines.join("\n")
90   end
91
92   #
93   # private & protected methods
94   #
95
96   protected
97
98   def add_child(name)
99     self.children << Menu.new(name, self)
100   end
101
102   private
103
104   def array_starts_with?(big_array, small_array)
105     small_array.length.times do |i|
106       if small_array[i] != big_array[i]
107         return false
108       end
109     end
110     return true
111   end
112
113
114   def parse_menu(file)
115     while true
116       item = file.readline
117       if item.strip.chars.any? && item !~ /^\s*#/
118         depth = item.scan("  ").size
119         last_menu_at_depth(depth).add_child(item.strip)
120       end
121     end
122   rescue EOFError
123     # done loading
124   end
125
126   #
127   # returns the last list of children at the specified depth
128   #
129   def last_menu_at_depth(depth)
130     menu = self
131     depth.times { menu = menu.children.last }
132     menu
133   end
134
135 end