1require 'cgi' 2 3## 4# A Context is something that can hold modules, classes, methods, attributes, 5# aliases, requires, and includes. Classes, modules, and files are all 6# Contexts. 7 8class RDoc::Context < RDoc::CodeObject 9 10 include Comparable 11 12 ## 13 # Types of methods 14 15 TYPES = %w[class instance] 16 17 ## 18 # If a context has these titles it will be sorted in this order. 19 20 TOMDOC_TITLES = [nil, 'Public', 'Internal', 'Deprecated'] # :nodoc: 21 TOMDOC_TITLES_SORT = TOMDOC_TITLES.sort_by { |title| title.to_s } # :nodoc: 22 23 ## 24 # Class/module aliases 25 26 attr_reader :aliases 27 28 ## 29 # All attr* methods 30 31 attr_reader :attributes 32 33 ## 34 # Block params to be used in the next MethodAttr parsed under this context 35 36 attr_accessor :block_params 37 38 ## 39 # Constants defined 40 41 attr_reader :constants 42 43 ## 44 # Sets the current documentation section of documentation 45 46 attr_writer :current_section 47 48 ## 49 # Files this context is found in 50 51 attr_reader :in_files 52 53 ## 54 # Modules this context includes 55 56 attr_reader :includes 57 58 ## 59 # Modules this context is extended with 60 61 attr_reader :extends 62 63 ## 64 # Methods defined in this context 65 66 attr_reader :method_list 67 68 ## 69 # Name of this class excluding namespace. See also full_name 70 71 attr_reader :name 72 73 ## 74 # Files this context requires 75 76 attr_reader :requires 77 78 ## 79 # Use this section for the next method, attribute or constant added. 80 81 attr_accessor :temporary_section 82 83 ## 84 # Hash <tt>old_name => [aliases]</tt>, for aliases 85 # that haven't (yet) been resolved to a method/attribute. 86 # (Not to be confused with the aliases of the context.) 87 88 attr_accessor :unmatched_alias_lists 89 90 ## 91 # Aliases that could not be resolved. 92 93 attr_reader :external_aliases 94 95 ## 96 # Current visibility of this context 97 98 attr_accessor :visibility 99 100 ## 101 # Hash of registered methods. Attributes are also registered here, 102 # twice if they are RW. 103 104 attr_reader :methods_hash 105 106 ## 107 # Params to be used in the next MethodAttr parsed under this context 108 109 attr_accessor :params 110 111 ## 112 # Hash of registered constants. 113 114 attr_reader :constants_hash 115 116 ## 117 # Creates an unnamed empty context with public current visibility 118 119 def initialize 120 super 121 122 @in_files = [] 123 124 @name ||= "unknown" 125 @parent = nil 126 @visibility = :public 127 128 @current_section = Section.new self, nil, nil 129 @sections = { nil => @current_section } 130 @temporary_section = nil 131 132 @classes = {} 133 @modules = {} 134 135 initialize_methods_etc 136 end 137 138 ## 139 # Sets the defaults for methods and so-forth 140 141 def initialize_methods_etc 142 @method_list = [] 143 @attributes = [] 144 @aliases = [] 145 @requires = [] 146 @includes = [] 147 @extends = [] 148 @constants = [] 149 @external_aliases = [] 150 151 # This Hash maps a method name to a list of unmatched aliases (aliases of 152 # a method not yet encountered). 153 @unmatched_alias_lists = {} 154 155 @methods_hash = {} 156 @constants_hash = {} 157 158 @params = nil 159 160 @store ||= nil 161 end 162 163 ## 164 # Contexts are sorted by full_name 165 166 def <=>(other) 167 full_name <=> other.full_name 168 end 169 170 ## 171 # Adds +an_alias+ that is automatically resolved 172 173 def add_alias an_alias 174 return an_alias unless @document_self 175 176 method_attr = find_method(an_alias.old_name, an_alias.singleton) || 177 find_attribute(an_alias.old_name, an_alias.singleton) 178 179 if method_attr then 180 method_attr.add_alias an_alias, self 181 else 182 add_to @external_aliases, an_alias 183 unmatched_alias_list = 184 @unmatched_alias_lists[an_alias.pretty_old_name] ||= [] 185 unmatched_alias_list.push an_alias 186 end 187 188 an_alias 189 end 190 191 ## 192 # Adds +attribute+ if not already there. If it is (as method(s) or attribute), 193 # updates the comment if it was empty. 194 # 195 # The attribute is registered only if it defines a new method. 196 # For instance, <tt>attr_reader :foo</tt> will not be registered 197 # if method +foo+ exists, but <tt>attr_accessor :foo</tt> will be registered 198 # if method +foo+ exists, but <tt>foo=</tt> does not. 199 200 def add_attribute attribute 201 return attribute unless @document_self 202 203 # mainly to check for redefinition of an attribute as a method 204 # TODO find a policy for 'attr_reader :foo' + 'def foo=()' 205 register = false 206 207 key = nil 208 209 if attribute.rw.index 'R' then 210 key = attribute.pretty_name 211 known = @methods_hash[key] 212 213 if known then 214 known.comment = attribute.comment if known.comment.empty? 215 elsif registered = @methods_hash[attribute.pretty_name << '='] and 216 RDoc::Attr === registered then 217 registered.rw = 'RW' 218 else 219 @methods_hash[key] = attribute 220 register = true 221 end 222 end 223 224 if attribute.rw.index 'W' then 225 key = attribute.pretty_name << '=' 226 known = @methods_hash[key] 227 228 if known then 229 known.comment = attribute.comment if known.comment.empty? 230 elsif registered = @methods_hash[attribute.pretty_name] and 231 RDoc::Attr === registered then 232 registered.rw = 'RW' 233 else 234 @methods_hash[key] = attribute 235 register = true 236 end 237 end 238 239 if register then 240 attribute.visibility = @visibility 241 add_to @attributes, attribute 242 resolve_aliases attribute 243 end 244 245 attribute 246 end 247 248 ## 249 # Adds a class named +given_name+ with +superclass+. 250 # 251 # Both +given_name+ and +superclass+ may contain '::', and are 252 # interpreted relative to the +self+ context. This allows handling correctly 253 # examples like these: 254 # class RDoc::Gauntlet < Gauntlet 255 # module Mod 256 # class Object # implies < ::Object 257 # class SubObject < Object # this is _not_ ::Object 258 # 259 # Given <tt>class Container::Item</tt> RDoc assumes +Container+ is a module 260 # unless it later sees <tt>class Container</tt>. +add_class+ automatically 261 # upgrades +given_name+ to a class in this case. 262 263 def add_class class_type, given_name, superclass = '::Object' 264 # superclass +nil+ is passed by the C parser in the following cases: 265 # - registering Object in 1.8 (correct) 266 # - registering BasicObject in 1.9 (correct) 267 # - registering RubyVM in 1.9 in iseq.c (incorrect: < Object in vm.c) 268 # 269 # If we later find a superclass for a registered class with a nil 270 # superclass, we must honor it. 271 272 # find the name & enclosing context 273 if given_name =~ /^:+(\w+)$/ then 274 full_name = $1 275 enclosing = top_level 276 name = full_name.split(/:+/).last 277 else 278 full_name = child_name given_name 279 280 if full_name =~ /^(.+)::(\w+)$/ then 281 name = $2 282 ename = $1 283 enclosing = @store.classes_hash[ename] || @store.modules_hash[ename] 284 # HACK: crashes in actionpack/lib/action_view/helpers/form_helper.rb (metaprogramming) 285 unless enclosing then 286 # try the given name at top level (will work for the above example) 287 enclosing = @store.classes_hash[given_name] || 288 @store.modules_hash[given_name] 289 return enclosing if enclosing 290 # not found: create the parent(s) 291 names = ename.split('::') 292 enclosing = self 293 names.each do |n| 294 enclosing = enclosing.classes_hash[n] || 295 enclosing.modules_hash[n] || 296 enclosing.add_module(RDoc::NormalModule, n) 297 end 298 end 299 else 300 name = full_name 301 enclosing = self 302 end 303 end 304 305 # fix up superclass 306 superclass = nil if full_name == 'BasicObject' 307 superclass = nil if full_name == 'Object' and defined?(::BasicObject) 308 superclass = '::BasicObject' if 309 defined?(::BasicObject) and full_name == 'Object' 310 311 # find the superclass full name 312 if superclass then 313 if superclass =~ /^:+/ then 314 superclass = $' #' 315 else 316 if superclass =~ /^(\w+):+(.+)$/ then 317 suffix = $2 318 mod = find_module_named($1) 319 superclass = mod.full_name + '::' + suffix if mod 320 else 321 mod = find_module_named(superclass) 322 superclass = mod.full_name if mod 323 end 324 end 325 326 # did we believe it was a module? 327 mod = @store.modules_hash.delete superclass 328 329 upgrade_to_class mod, RDoc::NormalClass, mod.parent if mod 330 331 # e.g., Object < Object 332 superclass = nil if superclass == full_name 333 end 334 335 klass = @store.classes_hash[full_name] 336 337 if klass then 338 # if TopLevel, it may not be registered in the classes: 339 enclosing.classes_hash[name] = klass 340 341 # update the superclass if needed 342 if superclass then 343 existing = klass.superclass 344 existing = existing.full_name unless existing.is_a?(String) if existing 345 if existing.nil? || 346 (existing == 'Object' && superclass != 'Object') then 347 klass.superclass = superclass 348 end 349 end 350 else 351 # this is a new class 352 mod = @store.modules_hash.delete full_name 353 354 if mod then 355 klass = upgrade_to_class mod, RDoc::NormalClass, enclosing 356 357 klass.superclass = superclass unless superclass.nil? 358 else 359 klass = class_type.new name, superclass 360 361 enclosing.add_class_or_module(klass, enclosing.classes_hash, 362 @store.classes_hash) 363 end 364 end 365 366 klass.parent = self 367 368 klass 369 end 370 371 ## 372 # Adds the class or module +mod+ to the modules or 373 # classes Hash +self_hash+, and to +all_hash+ (either 374 # <tt>TopLevel::modules_hash</tt> or <tt>TopLevel::classes_hash</tt>), 375 # unless #done_documenting is +true+. Sets the #parent of +mod+ 376 # to +self+, and its #section to #current_section. Returns +mod+. 377 378 def add_class_or_module mod, self_hash, all_hash 379 mod.section = current_section # TODO declaring context? something is 380 # wrong here... 381 mod.parent = self 382 mod.store = @store 383 384 unless @done_documenting then 385 self_hash[mod.name] = mod 386 # this must be done AFTER adding mod to its parent, so that the full 387 # name is correct: 388 all_hash[mod.full_name] = mod 389 end 390 391 mod 392 end 393 394 ## 395 # Adds +constant+ if not already there. If it is, updates the comment, 396 # value and/or is_alias_for of the known constant if they were empty/nil. 397 398 def add_constant constant 399 return constant unless @document_self 400 401 # HACK: avoid duplicate 'PI' & 'E' in math.c (1.8.7 source code) 402 # (this is a #ifdef: should be handled by the C parser) 403 known = @constants_hash[constant.name] 404 405 if known then 406 known.comment = constant.comment if known.comment.empty? 407 408 known.value = constant.value if 409 known.value.nil? or known.value.strip.empty? 410 411 known.is_alias_for ||= constant.is_alias_for 412 else 413 @constants_hash[constant.name] = constant 414 add_to @constants, constant 415 end 416 417 constant 418 end 419 420 ## 421 # Adds included module +include+ which should be an RDoc::Include 422 423 def add_include include 424 add_to @includes, include 425 426 include 427 end 428 429 ## 430 # Adds extension module +ext+ which should be an RDoc::Extend 431 432 def add_extend ext 433 add_to @extends, ext 434 435 ext 436 end 437 438 ## 439 # Adds +method+ if not already there. If it is (as method or attribute), 440 # updates the comment if it was empty. 441 442 def add_method method 443 return method unless @document_self 444 445 # HACK: avoid duplicate 'new' in io.c & struct.c (1.8.7 source code) 446 key = method.pretty_name 447 known = @methods_hash[key] 448 449 if known then 450 if @store then # otherwise we are loading 451 known.comment = method.comment if known.comment.empty? 452 previously = ", previously in #{known.file}" unless 453 method.file == known.file 454 @store.rdoc.options.warn \ 455 "Duplicate method #{known.full_name} in #{method.file}#{previously}" 456 end 457 else 458 @methods_hash[key] = method 459 method.visibility = @visibility 460 add_to @method_list, method 461 resolve_aliases method 462 end 463 464 method 465 end 466 467 ## 468 # Adds a module named +name+. If RDoc already knows +name+ is a class then 469 # that class is returned instead. See also #add_class. 470 471 def add_module(class_type, name) 472 mod = @classes[name] || @modules[name] 473 return mod if mod 474 475 full_name = child_name name 476 mod = @store.modules_hash[full_name] || class_type.new(name) 477 478 add_class_or_module mod, @modules, @store.modules_hash 479 end 480 481 ## 482 # Adds an alias from +from+ (a class or module) to +name+ which was defined 483 # in +file+. 484 485 def add_module_alias from, name, file 486 return from if @done_documenting 487 488 to_name = child_name name 489 490 # if we already know this name, don't register an alias: 491 # see the metaprogramming in lib/active_support/basic_object.rb, 492 # where we already know BasicObject is a class when we find 493 # BasicObject = BlankSlate 494 return from if @store.find_class_or_module to_name 495 496 to = from.dup 497 to.name = name 498 to.full_name = nil 499 500 if to.module? then 501 @store.modules_hash[to_name] = to 502 @modules[name] = to 503 else 504 @store.classes_hash[to_name] = to 505 @classes[name] = to 506 end 507 508 # Registers a constant for this alias. The constant value and comment 509 # will be updated later, when the Ruby parser adds the constant 510 const = RDoc::Constant.new name, nil, to.comment 511 const.record_location file 512 const.is_alias_for = from 513 add_constant const 514 515 to 516 end 517 518 ## 519 # Adds +require+ to this context's top level 520 521 def add_require(require) 522 return require unless @document_self 523 524 if RDoc::TopLevel === self then 525 add_to @requires, require 526 else 527 parent.add_require require 528 end 529 end 530 531 ## 532 # Returns a section with +title+, creating it if it doesn't already exist. 533 # +comment+ will be appended to the section's comment. 534 # 535 # A section with a +title+ of +nil+ will return the default section. 536 # 537 # See also RDoc::Context::Section 538 539 def add_section title, comment = nil 540 if section = @sections[title] then 541 section.add_comment comment if comment 542 else 543 section = Section.new self, title, comment 544 @sections[title] = section 545 end 546 547 section 548 end 549 550 ## 551 # Adds +thing+ to the collection +array+ 552 553 def add_to array, thing 554 array << thing if @document_self 555 556 thing.parent = self 557 thing.store = @store if @store 558 thing.section = current_section 559 end 560 561 ## 562 # Is there any content? 563 # 564 # This means any of: comment, aliases, methods, attributes, external 565 # aliases, require, constant. 566 # 567 # Includes and extends are also checked unless <tt>includes == false</tt>. 568 569 def any_content(includes = true) 570 @any_content ||= !( 571 @comment.empty? && 572 @method_list.empty? && 573 @attributes.empty? && 574 @aliases.empty? && 575 @external_aliases.empty? && 576 @requires.empty? && 577 @constants.empty? 578 ) 579 @any_content || (includes && !(@includes + @extends).empty? ) 580 end 581 582 ## 583 # Creates the full name for a child with +name+ 584 585 def child_name name 586 if name =~ /^:+/ 587 $' #' 588 elsif RDoc::TopLevel === self then 589 name 590 else 591 "#{self.full_name}::#{name}" 592 end 593 end 594 595 ## 596 # Class attributes 597 598 def class_attributes 599 @class_attributes ||= attributes.select { |a| a.singleton } 600 end 601 602 ## 603 # Class methods 604 605 def class_method_list 606 @class_method_list ||= method_list.select { |a| a.singleton } 607 end 608 609 ## 610 # Array of classes in this context 611 612 def classes 613 @classes.values 614 end 615 616 ## 617 # All classes and modules in this namespace 618 619 def classes_and_modules 620 classes + modules 621 end 622 623 ## 624 # Hash of classes keyed by class name 625 626 def classes_hash 627 @classes 628 end 629 630 ## 631 # The current documentation section that new items will be added to. If 632 # temporary_section is available it will be used. 633 634 def current_section 635 if section = @temporary_section then 636 @temporary_section = nil 637 else 638 section = @current_section 639 end 640 641 section 642 end 643 644 ## 645 # Is part of this thing was defined in +file+? 646 647 def defined_in?(file) 648 @in_files.include?(file) 649 end 650 651 def display(method_attr) # :nodoc: 652 if method_attr.is_a? RDoc::Attr 653 "#{method_attr.definition} #{method_attr.pretty_name}" 654 else 655 "method #{method_attr.pretty_name}" 656 end 657 end 658 659 ## 660 # Iterator for ancestors for duck-typing. Does nothing. See 661 # RDoc::ClassModule#each_ancestor. 662 # 663 # This method exists to make it easy to work with Context subclasses that 664 # aren't part of RDoc. 665 666 def each_ancestor # :nodoc: 667 end 668 669 ## 670 # Iterator for attributes 671 672 def each_attribute # :yields: attribute 673 @attributes.each { |a| yield a } 674 end 675 676 ## 677 # Iterator for classes and modules 678 679 def each_classmodule(&block) # :yields: module 680 classes_and_modules.sort.each(&block) 681 end 682 683 ## 684 # Iterator for constants 685 686 def each_constant # :yields: constant 687 @constants.each {|c| yield c} 688 end 689 690 ## 691 # Iterator for included modules 692 693 def each_include # :yields: include 694 @includes.each do |i| yield i end 695 end 696 697 ## 698 # Iterator for extension modules 699 700 def each_extend # :yields: extend 701 @extends.each do |e| yield e end 702 end 703 704 ## 705 # Iterator for methods 706 707 def each_method # :yields: method 708 return enum_for __method__ unless block_given? 709 710 @method_list.sort.each { |m| yield m } 711 end 712 713 ## 714 # Iterator for each section's contents sorted by title. The +section+, the 715 # section's +constants+ and the sections +attributes+ are yielded. The 716 # +constants+ and +attributes+ collections are sorted. 717 # 718 # To retrieve methods in a section use #methods_by_type with the optional 719 # +section+ parameter. 720 # 721 # NOTE: Do not edit collections yielded by this method 722 723 def each_section # :yields: section, constants, attributes 724 return enum_for __method__ unless block_given? 725 726 constants = @constants.group_by do |constant| constant.section end 727 attributes = @attributes.group_by do |attribute| attribute.section end 728 729 constants.default = [] 730 attributes.default = [] 731 732 sort_sections.each do |section| 733 yield section, constants[section].sort, attributes[section].sort 734 end 735 end 736 737 ## 738 # Finds an attribute +name+ with singleton value +singleton+. 739 740 def find_attribute(name, singleton) 741 name = $1 if name =~ /^(.*)=$/ 742 @attributes.find { |a| a.name == name && a.singleton == singleton } 743 end 744 745 ## 746 # Finds an attribute with +name+ in this context 747 748 def find_attribute_named(name) 749 case name 750 when /\A#/ then 751 find_attribute name[1..-1], false 752 when /\A::/ then 753 find_attribute name[2..-1], true 754 else 755 @attributes.find { |a| a.name == name } 756 end 757 end 758 759 ## 760 # Finds a class method with +name+ in this context 761 762 def find_class_method_named(name) 763 @method_list.find { |meth| meth.singleton && meth.name == name } 764 end 765 766 ## 767 # Finds a constant with +name+ in this context 768 769 def find_constant_named(name) 770 @constants.find {|m| m.name == name} 771 end 772 773 ## 774 # Find a module at a higher scope 775 776 def find_enclosing_module_named(name) 777 parent && parent.find_module_named(name) 778 end 779 780 ## 781 # Finds an external alias +name+ with singleton value +singleton+. 782 783 def find_external_alias(name, singleton) 784 @external_aliases.find { |m| m.name == name && m.singleton == singleton } 785 end 786 787 ## 788 # Finds an external alias with +name+ in this context 789 790 def find_external_alias_named(name) 791 case name 792 when /\A#/ then 793 find_external_alias name[1..-1], false 794 when /\A::/ then 795 find_external_alias name[2..-1], true 796 else 797 @external_aliases.find { |a| a.name == name } 798 end 799 end 800 801 ## 802 # Finds a file with +name+ in this context 803 804 def find_file_named name 805 @store.find_file_named name 806 end 807 808 ## 809 # Finds an instance method with +name+ in this context 810 811 def find_instance_method_named(name) 812 @method_list.find { |meth| !meth.singleton && meth.name == name } 813 end 814 815 ## 816 # Finds a method, constant, attribute, external alias, module or file 817 # named +symbol+ in this context. 818 819 def find_local_symbol(symbol) 820 find_method_named(symbol) or 821 find_constant_named(symbol) or 822 find_attribute_named(symbol) or 823 find_external_alias_named(symbol) or 824 find_module_named(symbol) or 825 find_file_named(symbol) 826 end 827 828 ## 829 # Finds a method named +name+ with singleton value +singleton+. 830 831 def find_method(name, singleton) 832 @method_list.find { |m| m.name == name && m.singleton == singleton } 833 end 834 835 ## 836 # Finds a instance or module method with +name+ in this context 837 838 def find_method_named(name) 839 case name 840 when /\A#/ then 841 find_method name[1..-1], false 842 when /\A::/ then 843 find_method name[2..-1], true 844 else 845 @method_list.find { |meth| meth.name == name } 846 end 847 end 848 849 ## 850 # Find a module with +name+ using ruby's scoping rules 851 852 def find_module_named(name) 853 res = @modules[name] || @classes[name] 854 return res if res 855 return self if self.name == name 856 find_enclosing_module_named name 857 end 858 859 ## 860 # Look up +symbol+, first as a module, then as a local symbol. 861 862 def find_symbol(symbol) 863 find_symbol_module(symbol) || find_local_symbol(symbol) 864 end 865 866 ## 867 # Look up a module named +symbol+. 868 869 def find_symbol_module(symbol) 870 result = nil 871 872 # look for a class or module 'symbol' 873 case symbol 874 when /^::/ then 875 result = @store.find_class_or_module symbol 876 when /^(\w+):+(.+)$/ 877 suffix = $2 878 top = $1 879 searched = self 880 while searched do 881 mod = searched.find_module_named(top) 882 break unless mod 883 result = @store.find_class_or_module "#{mod.full_name}::#{suffix}" 884 break if result || searched.is_a?(RDoc::TopLevel) 885 searched = searched.parent 886 end 887 else 888 searched = self 889 while searched do 890 result = searched.find_module_named(symbol) 891 break if result || searched.is_a?(RDoc::TopLevel) 892 searched = searched.parent 893 end 894 end 895 896 result 897 end 898 899 ## 900 # The full name for this context. This method is overridden by subclasses. 901 902 def full_name 903 '(unknown)' 904 end 905 906 ## 907 # Does this context and its methods and constants all have documentation? 908 # 909 # (Yes, fully documented doesn't mean everything.) 910 911 def fully_documented? 912 documented? and 913 attributes.all? { |a| a.documented? } and 914 method_list.all? { |m| m.documented? } and 915 constants.all? { |c| c.documented? } 916 end 917 918 ## 919 # URL for this with a +prefix+ 920 921 def http_url(prefix) 922 path = name_for_path 923 path = path.gsub(/<<\s*(\w*)/, 'from-\1') if path =~ /<</ 924 path = [prefix] + path.split('::') 925 926 File.join(*path.compact) + '.html' 927 end 928 929 ## 930 # Instance attributes 931 932 def instance_attributes 933 @instance_attributes ||= attributes.reject { |a| a.singleton } 934 end 935 936 ## 937 # Instance methods 938 #-- 939 # TODO rename to instance_methods 940 941 def instance_method_list 942 @instance_method_list ||= method_list.reject { |a| a.singleton } 943 end 944 945 ## 946 # Breaks method_list into a nested hash by type (<tt>'class'</tt> or 947 # <tt>'instance'</tt>) and visibility (+:public+, +:protected+, +:private+). 948 # 949 # If +section+ is provided only methods in that RDoc::Context::Section will 950 # be returned. 951 952 def methods_by_type section = nil 953 methods = {} 954 955 TYPES.each do |type| 956 visibilities = {} 957 RDoc::VISIBILITIES.each do |vis| 958 visibilities[vis] = [] 959 end 960 961 methods[type] = visibilities 962 end 963 964 each_method do |method| 965 next if section and not method.section == section 966 methods[method.type][method.visibility] << method 967 end 968 969 methods 970 end 971 972 ## 973 # Yields AnyMethod and Attr entries matching the list of names in +methods+. 974 975 def methods_matching(methods, singleton = false, &block) 976 (@method_list + @attributes).each do |m| 977 yield m if methods.include?(m.name) and m.singleton == singleton 978 end 979 980 each_ancestor do |parent| 981 parent.methods_matching(methods, singleton, &block) 982 end 983 end 984 985 ## 986 # Array of modules in this context 987 988 def modules 989 @modules.values 990 end 991 992 ## 993 # Hash of modules keyed by module name 994 995 def modules_hash 996 @modules 997 end 998 999 ## 1000 # Name to use to generate the url. 1001 # <tt>#full_name</tt> by default. 1002 1003 def name_for_path 1004 full_name 1005 end 1006 1007 ## 1008 # Changes the visibility for new methods to +visibility+ 1009 1010 def ongoing_visibility=(visibility) 1011 @visibility = visibility 1012 end 1013 1014 ## 1015 # Record +top_level+ as a file +self+ is in. 1016 1017 def record_location(top_level) 1018 @in_files << top_level unless @in_files.include?(top_level) 1019 end 1020 1021 ## 1022 # Should we remove this context from the documentation? 1023 # 1024 # The answer is yes if: 1025 # * #received_nodoc is +true+ 1026 # * #any_content is +false+ (not counting includes) 1027 # * All #includes are modules (not a string), and their module has 1028 # <tt>#remove_from_documentation? == true</tt> 1029 # * All classes and modules have <tt>#remove_from_documentation? == true</tt> 1030 1031 def remove_from_documentation? 1032 @remove_from_documentation ||= 1033 @received_nodoc && 1034 !any_content(false) && 1035 @includes.all? { |i| !i.module.is_a?(String) && i.module.remove_from_documentation? } && 1036 classes_and_modules.all? { |cm| cm.remove_from_documentation? } 1037 end 1038 1039 ## 1040 # Removes methods and attributes with a visibility less than +min_visibility+. 1041 #-- 1042 # TODO mark the visibility of attributes in the template (if not public?) 1043 1044 def remove_invisible(min_visibility) 1045 return if min_visibility == :private 1046 remove_invisible_in @method_list, min_visibility 1047 remove_invisible_in @attributes, min_visibility 1048 end 1049 1050 ## 1051 # Only called when min_visibility == :public or :private 1052 1053 def remove_invisible_in array, min_visibility # :nodoc: 1054 if min_visibility == :public then 1055 array.reject! { |e| 1056 e.visibility != :public and not e.force_documentation 1057 } 1058 else 1059 array.reject! { |e| 1060 e.visibility == :private and not e.force_documentation 1061 } 1062 end 1063 end 1064 1065 ## 1066 # Tries to resolve unmatched aliases when a method or attribute has just 1067 # been added. 1068 1069 def resolve_aliases added 1070 # resolve any pending unmatched aliases 1071 key = added.pretty_name 1072 unmatched_alias_list = @unmatched_alias_lists[key] 1073 return unless unmatched_alias_list 1074 unmatched_alias_list.each do |unmatched_alias| 1075 added.add_alias unmatched_alias, self 1076 @external_aliases.delete unmatched_alias 1077 end 1078 @unmatched_alias_lists.delete key 1079 end 1080 1081 ## 1082 # Returns RDoc::Context::Section objects referenced in this context for use 1083 # in a table of contents. 1084 1085 def section_contents 1086 used_sections = {} 1087 1088 each_method do |method| 1089 next unless method.display? 1090 1091 used_sections[method.section] = true 1092 end 1093 1094 # order found sections 1095 sections = sort_sections.select do |section| 1096 used_sections[section] 1097 end 1098 1099 # only the default section is used 1100 return [] if 1101 sections.length == 1 and not sections.first.title 1102 1103 sections 1104 end 1105 1106 ## 1107 # Sections in this context 1108 1109 def sections 1110 @sections.values 1111 end 1112 1113 def sections_hash # :nodoc: 1114 @sections 1115 end 1116 1117 ## 1118 # Sets the current section to a section with +title+. See also #add_section 1119 1120 def set_current_section title, comment 1121 @current_section = add_section title, comment 1122 end 1123 1124 ## 1125 # Given an array +methods+ of method names, set the visibility of each to 1126 # +visibility+ 1127 1128 def set_visibility_for(methods, visibility, singleton = false) 1129 methods_matching methods, singleton do |m| 1130 m.visibility = visibility 1131 end 1132 end 1133 1134 ## 1135 # Sorts sections alphabetically (default) or in TomDoc fashion (none, 1136 # Public, Internal, Deprecated) 1137 1138 def sort_sections 1139 titles = @sections.map { |title, _| title } 1140 1141 if titles.length > 1 and 1142 TOMDOC_TITLES_SORT == 1143 (titles | TOMDOC_TITLES).sort_by { |title| title.to_s } then 1144 @sections.values_at(*TOMDOC_TITLES).compact 1145 else 1146 @sections.sort_by { |title, _| 1147 title.to_s 1148 }.map { |_, section| 1149 section 1150 } 1151 end 1152 end 1153 1154 def to_s # :nodoc: 1155 "#{self.class.name} #{self.full_name}" 1156 end 1157 1158 ## 1159 # Return the TopLevel that owns us 1160 #-- 1161 # FIXME we can be 'owned' by several TopLevel (see #record_location & 1162 # #in_files) 1163 1164 def top_level 1165 return @top_level if defined? @top_level 1166 @top_level = self 1167 @top_level = @top_level.parent until RDoc::TopLevel === @top_level 1168 @top_level 1169 end 1170 1171 ## 1172 # Upgrades NormalModule +mod+ in +enclosing+ to a +class_type+ 1173 1174 def upgrade_to_class mod, class_type, enclosing 1175 enclosing.modules_hash.delete mod.name 1176 1177 klass = RDoc::ClassModule.from_module class_type, mod 1178 klass.store = @store 1179 1180 # if it was there, then we keep it even if done_documenting 1181 @store.classes_hash[mod.full_name] = klass 1182 enclosing.classes_hash[mod.name] = klass 1183 1184 klass 1185 end 1186 1187 autoload :Section, 'rdoc/context/section' 1188 1189end 1190 1191