/lib/yard/handlers/ruby/legacy/class_handler.rb

https://github.com/kou/yard · Ruby · 113 lines · 92 code · 11 blank · 10 comment · 19 complexity · 3448c518514d92df4cb05364cd95ed16 MD5 · raw file

  1. # frozen_string_literal: true
  2. # (see Ruby::ClassHandler)
  3. class YARD::Handlers::Ruby::Legacy::ClassHandler < YARD::Handlers::Ruby::Legacy::Base
  4. include YARD::Handlers::Ruby::StructHandlerMethods
  5. handles TkCLASS
  6. namespace_only
  7. process do
  8. if statement.tokens.to_s =~ /^class\s+(#{NAMESPACEMATCH})\s*(?:<\s*(.+)|\Z)/m
  9. classname = $1
  10. superclass_def = $2
  11. superclass = parse_superclass($2)
  12. classname = classname.gsub(/\s/, '')
  13. if superclass == "Struct"
  14. is_a_struct = true
  15. superclass = struct_superclass_name(superclass_def)
  16. create_struct_superclass(superclass, superclass_def)
  17. end
  18. undocsuper = superclass_def && superclass.nil?
  19. klass = register ClassObject.new(namespace, classname) do |o|
  20. o.superclass = superclass if superclass
  21. o.superclass.type = :class if o.superclass.is_a?(Proxy)
  22. end
  23. if is_a_struct
  24. parse_struct_subclass(klass, superclass_def)
  25. elsif klass
  26. create_attributes(klass, members_from_tags(klass))
  27. end
  28. parse_block(:namespace => klass)
  29. if undocsuper
  30. raise YARD::Parser::UndocumentableError, 'superclass (class was added without superclass)'
  31. end
  32. elsif statement.tokens.to_s =~ /^class\s*<<\s*([\w\:\s]+)/
  33. classname = $1.gsub(/\s/, '')
  34. proxy = Proxy.new(namespace, classname)
  35. # Allow constants to reference class names
  36. if ConstantObject === proxy
  37. if proxy.value =~ /\A#{NAMESPACEMATCH}\Z/
  38. proxy = Proxy.new(namespace, proxy.value)
  39. else
  40. raise YARD::Parser::UndocumentableError, "constant class reference '#{classname}'"
  41. end
  42. end
  43. if classname == "self"
  44. parse_block(:namespace => namespace, :scope => :class)
  45. elsif classname[0, 1] =~ /[A-Z]/
  46. register ClassObject.new(namespace, classname) if Proxy === proxy
  47. parse_block(:namespace => proxy, :scope => :class)
  48. else
  49. raise YARD::Parser::UndocumentableError, "class '#{classname}'"
  50. end
  51. else
  52. raise YARD::Parser::UndocumentableError, "class: #{statement.tokens}"
  53. end
  54. end
  55. private
  56. # Extracts the parameter list from the Struct.new declaration and returns it
  57. # formatted as a list of member names. Expects the user will have used symbols
  58. # to define the struct member names
  59. #
  60. # @param [String] superstring the string declaring the superclass
  61. # @return [Array<String>] a list of member names
  62. def extract_parameters(superstring)
  63. paramstring = superstring.match(/\A(O?Struct)\.new\((.*?)\)/)[2]
  64. paramstring.split(",").select {|x| x.strip[0, 1] == ":" }.map {|x| x.strip[1..-1] } # the 1..-1 chops the leading :
  65. end
  66. def create_struct_superclass(superclass, superclass_def)
  67. return if superclass == "Struct"
  68. the_super = register ClassObject.new(P("Struct"), superclass[8..-1]) do |o|
  69. o.superclass = "Struct"
  70. end
  71. parse_struct_subclass(the_super, superclass_def)
  72. the_super
  73. end
  74. def struct_superclass_name(superclass)
  75. match = superclass.match(/\A(Struct)\.new\((.*?)\)/)
  76. if match
  77. paramstring = match[2].split(",")
  78. first = paramstring.first.strip
  79. if first[0, 1] =~ /['"]/ && first[-1, 1] =~ /['"]/ && first !~ /\#\{/
  80. return "Struct::#{first[1..-2]}"
  81. end
  82. end
  83. "Struct"
  84. end
  85. def parse_struct_subclass(klass, superclass_def)
  86. # Bounce if there's no parens
  87. return unless superclass_def =~ /O?Struct\.new\((.*?)\)/
  88. members = extract_parameters(superclass_def)
  89. create_attributes(klass, members)
  90. end
  91. def parse_superclass(superclass)
  92. case superclass
  93. when /\A(#{NAMESPACEMATCH})(?:\s|\Z)/,
  94. /\A(Struct|OStruct)\.new/,
  95. /\ADelegateClass\((.+?)\)\s*\Z/,
  96. /\A(#{NAMESPACEMATCH})\(/
  97. $1
  98. when "self"
  99. namespace.path
  100. end
  101. end
  102. end