1##
2# A Module include in a class with \#include
3
4class RDoc::Include < RDoc::CodeObject
5
6  ##
7  # Name of included module
8
9  attr_accessor :name
10
11  ##
12  # Creates a new Include for +name+ with +comment+
13
14  def initialize(name, comment)
15    super()
16    @name = name
17    self.comment = comment
18    @module = nil # cache for module if found
19  end
20
21  ##
22  # Includes are sorted by name
23
24  def <=> other
25    return unless self.class === other
26
27    name <=> other.name
28  end
29
30  def == other # :nodoc:
31    self.class === other and @name == other.name
32  end
33
34  alias eql? ==
35
36  ##
37  # Full name based on #module
38
39  def full_name
40    m = self.module
41    RDoc::ClassModule === m ? m.full_name : @name
42  end
43
44  def hash # :nodoc:
45    [@name, self.module].hash
46  end
47
48  def inspect # :nodoc:
49    "#<%s:0x%x %s.include %s>" % [
50      self.class,
51      object_id,
52      parent_name, @name,
53    ]
54  end
55
56  ##
57  # Attempts to locate the included module object.  Returns the name if not
58  # known.
59  #
60  # The scoping rules of Ruby to resolve the name of an included module are:
61  # - first look into the children of the current context;
62  # - if not found, look into the children of included modules,
63  #   in reverse inclusion order;
64  # - if still not found, go up the hierarchy of names.
65  #
66  # This method has <code>O(n!)</code> behavior when the module calling
67  # include is referencing nonexistent modules.  Avoid calling #module until
68  # after all the files are parsed.  This behavior is due to ruby's constant
69  # lookup behavior.
70  #
71  # As of the beginning of October, 2011, no gem includes nonexistent modules.
72
73  def module
74    return @module if @module
75
76    # search the current context
77    return @name unless parent
78    full_name = parent.child_name(@name)
79    @module = @store.modules_hash[full_name]
80    return @module if @module
81    return @name if @name =~ /^::/
82
83    # search the includes before this one, in reverse order
84    searched = parent.includes.take_while { |i| i != self }.reverse
85    searched.each do |i|
86      inc = i.module
87      next if String === inc
88      full_name = inc.child_name(@name)
89      @module = @store.modules_hash[full_name]
90      return @module if @module
91    end
92
93    # go up the hierarchy of names
94    up = parent.parent
95    while up
96      full_name = up.child_name(@name)
97      @module = @store.modules_hash[full_name]
98      return @module if @module
99      up = up.parent
100    end
101
102    @name
103  end
104
105  ##
106  # Sets the store for this class or module and its contained code objects.
107
108  def store= store
109    super
110
111    @file = @store.add_file @file.full_name if @file
112  end
113
114  def to_s # :nodoc:
115    "include #@name in: #{parent}"
116  end
117
118end
119
120