yard 0.6.8 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of yard might be problematic. Click here for more details.

Files changed (224) hide show
  1. data/.yardopts +1 -0
  2. data/ChangeLog +723 -0
  3. data/README.md +16 -6
  4. data/docs/CodeObjects.md +10 -16
  5. data/docs/GettingStarted.md +232 -32
  6. data/docs/Glossary.md +1 -2
  7. data/docs/Handlers.md +10 -16
  8. data/docs/Overview.md +14 -13
  9. data/docs/Parser.md +13 -22
  10. data/docs/Tags.md +209 -16
  11. data/docs/Templates.md +237 -26
  12. data/docs/WhatsNew.md +178 -2
  13. data/lib/yard.rb +13 -10
  14. data/lib/yard/autoload.rb +22 -18
  15. data/lib/yard/cli/command.rb +13 -12
  16. data/lib/yard/cli/command_parser.rb +20 -19
  17. data/lib/yard/cli/config.rb +19 -19
  18. data/lib/yard/cli/diff.rb +46 -21
  19. data/lib/yard/cli/gems.rb +11 -11
  20. data/lib/yard/cli/graph.rb +13 -13
  21. data/lib/yard/cli/help.rb +1 -1
  22. data/lib/yard/cli/list.rb +22 -0
  23. data/lib/yard/cli/server.rb +17 -17
  24. data/lib/yard/cli/stats.rb +32 -32
  25. data/lib/yard/cli/yardoc.rb +181 -135
  26. data/lib/yard/cli/yri.rb +29 -29
  27. data/lib/yard/code_objects/base.rb +101 -101
  28. data/lib/yard/code_objects/class_object.rb +20 -20
  29. data/lib/yard/code_objects/constant_object.rb +1 -1
  30. data/lib/yard/code_objects/extended_method_object.rb +5 -5
  31. data/lib/yard/code_objects/extra_file_object.rb +89 -0
  32. data/lib/yard/code_objects/macro_object.rb +215 -0
  33. data/lib/yard/code_objects/method_object.rb +30 -30
  34. data/lib/yard/code_objects/module_object.rb +1 -1
  35. data/lib/yard/code_objects/namespace_object.rb +39 -39
  36. data/lib/yard/code_objects/proxy.rb +38 -38
  37. data/lib/yard/code_objects/root_object.rb +1 -1
  38. data/lib/yard/config.rb +40 -40
  39. data/lib/yard/core_ext/array.rb +2 -2
  40. data/lib/yard/core_ext/file.rb +11 -11
  41. data/lib/yard/core_ext/insertion.rb +10 -10
  42. data/lib/yard/core_ext/module.rb +2 -2
  43. data/lib/yard/core_ext/string.rb +2 -2
  44. data/lib/yard/core_ext/symbol_hash.rb +14 -14
  45. data/lib/yard/docstring.rb +122 -54
  46. data/lib/yard/globals.rb +2 -2
  47. data/lib/yard/handlers/base.rb +216 -127
  48. data/lib/yard/handlers/processor.rb +65 -27
  49. data/lib/yard/handlers/ruby/alias_handler.rb +6 -3
  50. data/lib/yard/handlers/ruby/attribute_handler.rb +7 -6
  51. data/lib/yard/handlers/ruby/base.rb +50 -31
  52. data/lib/yard/handlers/ruby/class_condition_handler.rb +11 -11
  53. data/lib/yard/handlers/ruby/class_handler.rb +10 -10
  54. data/lib/yard/handlers/ruby/class_variable_handler.rb +3 -3
  55. data/lib/yard/handlers/ruby/constant_handler.rb +7 -7
  56. data/lib/yard/handlers/ruby/exception_handler.rb +2 -2
  57. data/lib/yard/handlers/ruby/extend_handler.rb +1 -1
  58. data/lib/yard/handlers/ruby/legacy/alias_handler.rb +8 -5
  59. data/lib/yard/handlers/ruby/legacy/attribute_handler.rb +6 -5
  60. data/lib/yard/handlers/ruby/legacy/base.rb +42 -27
  61. data/lib/yard/handlers/ruby/legacy/class_condition_handler.rb +9 -9
  62. data/lib/yard/handlers/ruby/legacy/class_handler.rb +13 -12
  63. data/lib/yard/handlers/ruby/legacy/class_variable_handler.rb +3 -6
  64. data/lib/yard/handlers/ruby/legacy/constant_handler.rb +5 -8
  65. data/lib/yard/handlers/ruby/legacy/exception_handler.rb +1 -1
  66. data/lib/yard/handlers/ruby/legacy/extend_handler.rb +1 -0
  67. data/lib/yard/handlers/ruby/legacy/macro_handler.rb +40 -0
  68. data/lib/yard/handlers/ruby/legacy/method_handler.rb +10 -10
  69. data/lib/yard/handlers/ruby/legacy/mixin_handler.rb +4 -3
  70. data/lib/yard/handlers/ruby/legacy/module_handler.rb +2 -1
  71. data/lib/yard/handlers/ruby/legacy/private_constant_handler.rb +4 -4
  72. data/lib/yard/handlers/ruby/legacy/visibility_handler.rb +2 -1
  73. data/lib/yard/handlers/ruby/legacy/yield_handler.rb +3 -3
  74. data/lib/yard/handlers/ruby/macro_handler.rb +41 -0
  75. data/lib/yard/handlers/ruby/macro_handler_methods.rb +130 -0
  76. data/lib/yard/handlers/ruby/method_condition_handler.rb +1 -1
  77. data/lib/yard/handlers/ruby/method_handler.rb +13 -13
  78. data/lib/yard/handlers/ruby/mixin_handler.rb +4 -4
  79. data/lib/yard/handlers/ruby/module_handler.rb +2 -1
  80. data/lib/yard/handlers/ruby/private_constant_handler.rb +4 -4
  81. data/lib/yard/handlers/ruby/struct_handler_methods.rb +11 -11
  82. data/lib/yard/handlers/ruby/visibility_handler.rb +1 -1
  83. data/lib/yard/handlers/ruby/yield_handler.rb +5 -5
  84. data/lib/yard/logging.rb +11 -11
  85. data/lib/yard/parser/base.rb +8 -8
  86. data/lib/yard/parser/c_parser.rb +42 -33
  87. data/lib/yard/parser/ruby/ast_node.rb +62 -61
  88. data/lib/yard/parser/ruby/legacy/ruby_lex.rb +66 -66
  89. data/lib/yard/parser/ruby/legacy/ruby_parser.rb +4 -4
  90. data/lib/yard/parser/ruby/legacy/statement.rb +11 -11
  91. data/lib/yard/parser/ruby/legacy/statement_list.rb +15 -15
  92. data/lib/yard/parser/ruby/legacy/token_list.rb +9 -9
  93. data/lib/yard/parser/ruby/ruby_parser.rb +51 -37
  94. data/lib/yard/parser/source_parser.rb +271 -46
  95. data/lib/yard/rake/yardoc_task.rb +18 -17
  96. data/lib/yard/registry.rb +64 -64
  97. data/lib/yard/registry_store.rb +34 -34
  98. data/lib/yard/rubygems/backports.rb +8 -0
  99. data/lib/yard/rubygems/backports/LICENSE.txt +57 -0
  100. data/lib/yard/rubygems/backports/MIT.txt +20 -0
  101. data/lib/yard/rubygems/backports/gem.rb +8 -0
  102. data/lib/yard/rubygems/backports/source_index.rb +353 -0
  103. data/lib/yard/rubygems/specification.rb +2 -2
  104. data/lib/yard/serializers/base.rb +20 -20
  105. data/lib/yard/serializers/file_system_serializer.rb +28 -24
  106. data/lib/yard/serializers/process_serializer.rb +3 -3
  107. data/lib/yard/serializers/stdout_serializer.rb +6 -6
  108. data/lib/yard/serializers/yardoc_serializer.rb +17 -17
  109. data/lib/yard/server/adapter.rb +12 -12
  110. data/lib/yard/server/commands/base.rb +26 -26
  111. data/lib/yard/server/commands/display_file_command.rb +3 -2
  112. data/lib/yard/server/commands/display_object_command.rb +5 -5
  113. data/lib/yard/server/commands/frames_command.rb +1 -1
  114. data/lib/yard/server/commands/library_command.rb +7 -7
  115. data/lib/yard/server/commands/library_index_command.rb +2 -2
  116. data/lib/yard/server/commands/list_command.rb +8 -8
  117. data/lib/yard/server/commands/search_command.rb +8 -8
  118. data/lib/yard/server/commands/static_file_command.rb +3 -3
  119. data/lib/yard/server/doc_server_helper.rb +6 -3
  120. data/lib/yard/server/doc_server_serializer.rb +1 -1
  121. data/lib/yard/server/library_version.rb +45 -45
  122. data/lib/yard/server/rack_adapter.rb +10 -10
  123. data/lib/yard/server/router.rb +28 -28
  124. data/lib/yard/server/static_caching.rb +5 -5
  125. data/lib/yard/server/templates/default/fulldoc/html/css/custom.css +3 -3
  126. data/lib/yard/server/templates/default/fulldoc/html/js/live.js +1 -1
  127. data/lib/yard/server/templates/default/layout/html/breadcrumb.erb +2 -2
  128. data/lib/yard/server/templates/default/layout/html/headers.erb +13 -8
  129. data/lib/yard/server/templates/default/layout/html/setup.rb +7 -0
  130. data/lib/yard/server/templates/doc_server/full_list/html/full_list.erb +2 -2
  131. data/lib/yard/server/templates/doc_server/full_list/html/setup.rb +14 -4
  132. data/lib/yard/server/templates/doc_server/library_list/html/contents.erb +2 -2
  133. data/lib/yard/server/templates/doc_server/library_list/html/headers.erb +2 -2
  134. data/lib/yard/server/templates/doc_server/processing/html/processing.erb +1 -1
  135. data/lib/yard/server/templates/doc_server/search/html/search.erb +1 -1
  136. data/lib/yard/server/webrick_adapter.rb +2 -2
  137. data/lib/yard/tags/default_factory.rb +19 -19
  138. data/lib/yard/tags/default_tag.rb +1 -1
  139. data/lib/yard/tags/library.rb +68 -63
  140. data/lib/yard/tags/option_tag.rb +1 -1
  141. data/lib/yard/tags/overload_tag.rb +9 -9
  142. data/lib/yard/tags/ref_tag_list.rb +2 -2
  143. data/lib/yard/tags/tag.rb +7 -7
  144. data/lib/yard/templates/engine.rb +31 -31
  145. data/lib/yard/templates/erb_cache.rb +1 -1
  146. data/lib/yard/templates/helpers/base_helper.rb +46 -32
  147. data/lib/yard/templates/helpers/filter_helper.rb +2 -2
  148. data/lib/yard/templates/helpers/html_helper.rb +120 -81
  149. data/lib/yard/templates/helpers/html_syntax_highlight_helper.rb +4 -4
  150. data/lib/yard/templates/helpers/markup/rdoc_markup.rb +9 -9
  151. data/lib/yard/templates/helpers/markup_helper.rb +37 -30
  152. data/lib/yard/templates/helpers/method_helper.rb +7 -7
  153. data/lib/yard/templates/helpers/text_helper.rb +7 -7
  154. data/lib/yard/templates/helpers/uml_helper.rb +3 -3
  155. data/lib/yard/templates/section.rb +14 -14
  156. data/lib/yard/templates/template.rb +54 -54
  157. data/lib/yard/verifier.rb +27 -27
  158. data/spec/cli/list_spec.rb +8 -0
  159. data/spec/cli/yardoc_spec.rb +58 -10
  160. data/spec/code_objects/extra_file_object_spec.rb +132 -0
  161. data/spec/code_objects/macro_object_spec.rb +154 -0
  162. data/spec/docstring_spec.rb +90 -0
  163. data/spec/handlers/base_spec.rb +22 -0
  164. data/spec/handlers/examples/macro_handler_001.rb.txt +73 -0
  165. data/spec/handlers/examples/method_handler_001.rb.txt +17 -0
  166. data/spec/handlers/macro_handler_spec.rb +140 -0
  167. data/spec/handlers/method_handler_spec.rb +28 -0
  168. data/spec/handlers/processor_spec.rb +4 -0
  169. data/spec/handlers/spec_helper.rb +1 -1
  170. data/spec/parser/c_parser_spec.rb +47 -16
  171. data/spec/parser/examples/extrafile.c.txt +8 -0
  172. data/spec/parser/examples/multifile.c.txt +6 -0
  173. data/spec/parser/ruby/ruby_parser_spec.rb +5 -0
  174. data/spec/parser/source_parser_spec.rb +235 -0
  175. data/spec/rake/yardoc_task_spec.rb +22 -17
  176. data/spec/serializers/file_system_serializer_spec.rb +6 -0
  177. data/spec/server/commands/library_command_spec.rb +39 -0
  178. data/spec/spec_helper.rb +14 -0
  179. data/spec/templates/examples/method001.html +6 -6
  180. data/spec/templates/examples/method002.html +4 -4
  181. data/spec/templates/examples/method003.html +10 -10
  182. data/spec/templates/examples/method005.html +2 -2
  183. data/spec/templates/examples/module001.dot +2 -0
  184. data/spec/templates/examples/module001.html +76 -37
  185. data/spec/templates/examples/module001.txt +1 -1
  186. data/spec/templates/helpers/base_helper_spec.rb +7 -2
  187. data/spec/templates/helpers/html_helper_spec.rb +49 -5
  188. data/spec/templates/helpers/markup_helper_spec.rb +9 -8
  189. data/spec/templates/module_spec.rb +7 -0
  190. data/spec/templates/onefile_spec.rb +47 -0
  191. data/templates/default/fulldoc/html/css/style.css +7 -5
  192. data/templates/default/fulldoc/html/full_list.erb +13 -10
  193. data/templates/default/fulldoc/html/full_list_files.erb +1 -1
  194. data/templates/default/fulldoc/html/js/app.js +16 -14
  195. data/templates/default/fulldoc/html/js/full_list.js +7 -6
  196. data/templates/default/fulldoc/html/setup.rb +78 -17
  197. data/templates/default/layout/html/files.erb +1 -1
  198. data/templates/default/layout/html/headers.erb +11 -7
  199. data/templates/default/layout/html/search.erb +4 -4
  200. data/templates/default/layout/html/setup.rb +28 -8
  201. data/templates/default/module/html/inherited_attributes.erb +17 -0
  202. data/templates/default/module/setup.rb +1 -1
  203. data/templates/default/onefile/html/files.erb +2 -2
  204. data/templates/default/onefile/html/layout.erb +1 -1
  205. data/templates/default/onefile/html/setup.rb +7 -5
  206. data/templates/default/tags/html/option.erb +1 -1
  207. data/templates/default/tags/html/tag.erb +3 -3
  208. data/templates/guide/class/html/setup.rb +1 -0
  209. data/templates/guide/docstring/html/setup.rb +1 -0
  210. data/templates/guide/fulldoc/html/css/style.css +91 -0
  211. data/templates/guide/fulldoc/html/js/app.js +33 -0
  212. data/templates/guide/fulldoc/html/setup.rb +54 -0
  213. data/templates/guide/layout/html/layout.erb +81 -0
  214. data/templates/guide/layout/html/setup.rb +24 -0
  215. data/templates/guide/method/html/header.erb +18 -0
  216. data/templates/guide/method/html/setup.rb +21 -0
  217. data/templates/guide/module/html/header.erb +7 -0
  218. data/templates/guide/module/html/method_list.erb +5 -0
  219. data/templates/guide/module/html/setup.rb +26 -0
  220. data/templates/guide/tags/html/setup.rb +8 -0
  221. metadata +40 -7
  222. data/lib/yard/handlers/ruby/legacy/process_handler.rb +0 -13
  223. data/lib/yard/handlers/ruby/process_handler.rb +0 -18
  224. data/spec/handlers/process_handler_spec.rb +0 -17
@@ -5,9 +5,9 @@ module YARD::CodeObjects
5
5
  # The {ClassObject} that this class object inherits from in Ruby source.
6
6
  # @return [ClassObject] a class object that is the superclass of this one
7
7
  attr_reader :superclass
8
-
8
+
9
9
  # Creates a new class object in +namespace+ with +name+
10
- #
10
+ #
11
11
  # @see Base.new
12
12
  def initialize(namespace, name, *args, &block)
13
13
  super
@@ -25,16 +25,16 @@ module YARD::CodeObjects
25
25
  end
26
26
  end
27
27
  end
28
-
28
+
29
29
  # Whether or not the class is a Ruby Exception
30
- #
30
+ #
31
31
  # @return [Boolean] whether the object represents a Ruby exception
32
32
  def is_exception?
33
33
  inheritance_tree.reverse.any? {|o| BUILTIN_EXCEPTIONS_HASH.has_key? o.path }
34
34
  end
35
-
35
+
36
36
  # Returns the inheritance tree of the object including self.
37
- #
37
+ #
38
38
  # @param [Boolean] include_mods whether or not to include mixins in the
39
39
  # inheritance tree.
40
40
  # @return [Array<NamespaceObject>] the list of code objects that make up
@@ -50,10 +50,10 @@ module YARD::CodeObjects
50
50
  m.inheritance_tree(include_mods)
51
51
  end.flatten.uniq
52
52
  end
53
-
53
+
54
54
  # Returns the list of methods matching the options hash. Returns
55
55
  # all methods if hash is empty.
56
- #
56
+ #
57
57
  # @param [Hash] opts the options hash to match
58
58
  # @option opts [Boolean] :inherited (true) whether inherited methods should be
59
59
  # included in the list
@@ -62,16 +62,16 @@ module YARD::CodeObjects
62
62
  # @return [Array<MethodObject>] the list of methods that matched
63
63
  def meths(opts = {})
64
64
  opts = SymbolHash[:inherited => true].update(opts)
65
- list = super(opts)
65
+ list = super(opts)
66
66
  list += inherited_meths(opts).reject do |o|
67
67
  next(false) if opts[:all]
68
68
  list.find {|o2| o2.name == o.name && o2.scope == o.scope }
69
69
  end if opts[:inherited]
70
70
  list
71
71
  end
72
-
72
+
73
73
  # Returns only the methods that were inherited.
74
- #
74
+ #
75
75
  # @return [Array<MethodObject>] the list of inherited method objects
76
76
  def inherited_meths(opts = {})
77
77
  inheritance_tree[1..-1].inject([]) do |list, superclass|
@@ -86,9 +86,9 @@ module YARD::CodeObjects
86
86
  end
87
87
  end
88
88
  end
89
-
89
+
90
90
  # Returns the list of constants matching the options hash.
91
- #
91
+ #
92
92
  # @param [Hash] opts the options hash to match
93
93
  # @option opts [Boolean] :inherited (true) whether inherited constant should be
94
94
  # included in the list
@@ -99,9 +99,9 @@ module YARD::CodeObjects
99
99
  opts = SymbolHash[:inherited => true].update(opts)
100
100
  super(opts) + (opts[:inherited] ? inherited_constants : [])
101
101
  end
102
-
102
+
103
103
  # Returns only the constants that were inherited.
104
- #
104
+ #
105
105
  # @return [Array<ConstantObject>] the list of inherited constant objects
106
106
  def inherited_constants
107
107
  inheritance_tree[1..-1].inject([]) do |list, superclass|
@@ -114,11 +114,11 @@ module YARD::CodeObjects
114
114
  end
115
115
  end
116
116
  end
117
-
117
+
118
118
  # Sets the superclass of the object
119
- #
119
+ #
120
120
  # @param [Base, Proxy, String, Symbol, nil] object the superclass value
121
- # @return [void]
121
+ # @return [void]
122
122
  def superclass=(object)
123
123
  case object
124
124
  when Base, Proxy, NilClass
@@ -126,13 +126,13 @@ module YARD::CodeObjects
126
126
  when String, Symbol
127
127
  @superclass = Proxy.new(namespace, object)
128
128
  else
129
- raise ArgumentError, "superclass must be CodeObject, Proxy, String or Symbol"
129
+ raise ArgumentError, "superclass must be CodeObject, Proxy, String or Symbol"
130
130
  end
131
131
 
132
132
  if name == @superclass.name && namespace != YARD::Registry.root && !object.is_a?(Base)
133
133
  @superclass = Proxy.new(namespace.namespace, object)
134
134
  end
135
-
135
+
136
136
  if @superclass == self
137
137
  msg = "superclass #{@superclass.inspect} cannot be the same as the declared class #{self.inspect}"
138
138
  @superclass = P("::Object")
@@ -5,7 +5,7 @@ module YARD::CodeObjects
5
5
  # The source code representing the constant's value
6
6
  # @return [String] the value the constant is set to
7
7
  attr_reader :value
8
-
8
+
9
9
  def value=(value)
10
10
  @value = format_source(value)
11
11
  end
@@ -1,20 +1,20 @@
1
1
  module YARD::CodeObjects
2
2
  # Represents an instance method of a module that was mixed into the class
3
3
  # scope of another namespace.
4
- #
4
+ #
5
5
  # @see MethodObject
6
6
  class ExtendedMethodObject
7
7
  instance_methods.each {|m| undef_method(m) unless m =~ /^__/ || m.to_sym == :object_id }
8
-
8
+
9
9
  # @return [Symbol] always +:class+
10
10
  def scope; :class end
11
-
11
+
12
12
  # Sets up a delegate for {MethodObject} obj.
13
- #
13
+ #
14
14
  # @param [MethodObject] obj the instance method to treat as a mixed in
15
15
  # class method on another namespace.
16
16
  def initialize(obj) @del = obj end
17
-
17
+
18
18
  # Sends all methods to the {MethodObject} assigned in {#initialize}
19
19
  # @see #initialize
20
20
  # @see MethodObject
@@ -0,0 +1,89 @@
1
+ module YARD::CodeObjects
2
+ # An ExtraFileObject represents an extra documentation file (README or other
3
+ # file). It is not strictly a CodeObject (does not inherit from `Base`) although
4
+ # it implements `path`, `name` and `type`, and therefore should be structurally
5
+ # compatible with most CodeObject interfaces.
6
+ class ExtraFileObject
7
+ attr_accessor :filename
8
+ attr_accessor :attributes
9
+ attr_accessor :name
10
+ attr_accessor :contents
11
+
12
+ # Creates a new extra file object.
13
+ # @param [String] filename the location on disk of the file
14
+ # @param [String] contents the file contents. If not set, the contents
15
+ # will be read from disk using the +filename+.
16
+ def initialize(filename, contents = nil)
17
+ self.filename = filename
18
+ self.name = File.basename(filename).gsub(/\.[^.]+$/, '')
19
+ self.attributes = SymbolHash.new(false)
20
+ parse_contents(contents || File.read(@filename))
21
+ end
22
+
23
+ alias path name
24
+
25
+ def title
26
+ attributes[:title] || name
27
+ end
28
+
29
+ def inspect
30
+ "#<yardoc #{type} #{filename} attrs=#{attributes.inspect}>"
31
+ end
32
+ alias to_s inspect
33
+
34
+ def type; 'extra_file' end
35
+
36
+ def ==(other)
37
+ return false unless self.class === other
38
+ other.filename == filename
39
+ end
40
+ alias eql? ==
41
+ alias equal? ==
42
+ def hash; filename.hash end
43
+
44
+ private
45
+
46
+ # @param [String] data the file contents
47
+ def parse_contents(data)
48
+ retried = false
49
+ cut_index = 0
50
+ data = data.split("\n")
51
+ data.each_with_index do |line, index|
52
+ case line
53
+ when /^#!(\S+)\s*$/
54
+ if index == 0
55
+ attributes[:markup] = $1
56
+ else
57
+ cut_index = index
58
+ break
59
+ end
60
+ when /^\s*#\s*@(\S+)\s*(.+?)\s*$/
61
+ attributes[$1] = $2
62
+ else
63
+ cut_index = index
64
+ break
65
+ end
66
+ end
67
+ data = data[cut_index..-1] if cut_index > 0
68
+ self.contents = data.join("\n")
69
+
70
+ if contents.respond_to?(:force_encoding) && attributes[:encoding]
71
+ begin
72
+ contents.force_encoding(attributes[:encoding])
73
+ rescue ArgumentError
74
+ log.warn "Invalid encoding `#{attributes[:encoding]}' in #{filename}"
75
+ end
76
+ end
77
+ rescue ArgumentError => e
78
+ if retried && e.message =~ /invalid byte sequence/
79
+ # This should never happen.
80
+ log.warn "Could not read #{filename}, #{e.message}. You probably want to set `--charset`."
81
+ self.contents = ''
82
+ return
83
+ end
84
+ data.force_encoding('binary') if data.respond_to?(:force_encoding)
85
+ retried = true
86
+ retry
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,215 @@
1
+ module YARD
2
+ module CodeObjects
3
+ # A MacroObject represents a docstring defined through +@macro NAME+ and can be
4
+ # reused by specifying the tag +@macro NAME+. You can also provide the
5
+ # +attached+ type flag to the macro definition to have it attached to the
6
+ # specific DSL method so it will be implicitly reused.
7
+ #
8
+ # Macros are fully described in the {file:docs/Tags.md#macros Tags Overview}
9
+ # document.
10
+ #
11
+ # @example Creating a basic named macro
12
+ # # @macro prop
13
+ # # @method $1(${3-})
14
+ # # @return [$2] the value of the $0
15
+ # property :foo, String, :a, :b
16
+ #
17
+ # # @macro prop
18
+ # property :bar, Numeric, :value
19
+ #
20
+ # @example Creating a macro that is attached to the method call
21
+ # # @macro [attach] prop2
22
+ # # @method $1(value)
23
+ # property :foo
24
+ #
25
+ # # Extra data added to docstring
26
+ # property :bar
27
+ class MacroObject < Base
28
+ MACRO_MATCH = /(\\)?\$(?:\{(-?\d+|\*)(-)?(-?\d+)?\}|(-?\d+|\*))/
29
+
30
+ class << self
31
+ # Creates a new macro and fills in the relevant properties.
32
+ # @param [String] macro_name the name of the macro, must be unique.
33
+ # @param [String] data the data the macro should expand when re-used
34
+ # @param [CodeObjects::Base] method_object an object to attach this
35
+ # macro to. If supplied, {#attached?} will be true
36
+ # @return [MacroObject] the newly created object
37
+ def create(macro_name, data, method_object = nil)
38
+ obj = new(:root, macro_name)
39
+ obj.macro_data = data
40
+ obj.method_object = method_object
41
+ obj
42
+ end
43
+
44
+ # Finds a macro using +macro_name+
45
+ # @return [MacroObject] if a macro is found
46
+ # @return [nil] if there is no registered macro by that name
47
+ def find(macro_name)
48
+ Registry.at('.macro.' + macro_name.to_s)
49
+ end
50
+
51
+ # Parses a given docstring and determines if the macro is "new" or
52
+ # not. If the macro has $variable names or if it has a @macro tag
53
+ # with the [new] or [attached] flag, it is considered new.
54
+ #
55
+ # If a new macro is found, the macro is created and registered. Otherwise
56
+ # the macro name is searched and returned. If a macro is not found,
57
+ # nil is returned.
58
+ #
59
+ # @param [CodeObjects::Base] method_object an optional method to attach
60
+ # the macro to. Only used if the macro is being created, otherwise
61
+ # this argument is ignored.
62
+ # @return [MacroObject] the newly created or existing macro, depending
63
+ # on whether the @macro tag was a new tag or not.
64
+ # @return [nil] if the +data+ has no macro tag or if the macro is
65
+ # not new and no macro by the macro name is found.
66
+ def find_or_create(data, method_object = nil)
67
+ docstring = Docstring === data ? data : Docstring.new(data)
68
+ return unless docstring.tag(:macro)
69
+ return unless name = macro_name(docstring)
70
+ if new_macro?(docstring)
71
+ method_object = nil unless attached_macro?(docstring, method_object)
72
+ create(name, macro_data(docstring), method_object)
73
+ else
74
+ find(name)
75
+ end
76
+ end
77
+ alias create_docstring find_or_create
78
+
79
+ # Expands +macro_data+ using the interpolation parameters.
80
+ #
81
+ # Interpolation rules:
82
+ # * $0, $1, $2, ... = the Nth parameter in +call_params+
83
+ # * $& = the full statement source (excluding block)
84
+ # * Also supports $\{N-M} ranges, as well as negative indexes on N or M
85
+ # * Use \$ to escape the variable name in a macro.
86
+ #
87
+ # @macro [new] macro.expand
88
+ # @param [Array<String>] call_params the method name and parameters
89
+ # to the method call. These arguments will fill \$0-N
90
+ # @param [String] full_source the full source line (excluding block)
91
+ # interpolated as \$&
92
+ # @param [String] block_source Currently unused. Will support
93
+ # interpolating the block data as a variable.
94
+ # @return [String] the expanded macro data
95
+ # @param [String] macro_data the macro data to expand (taken from {#macro_data})
96
+ def expand(macro_data, call_params = [], full_source = '', block_source = '')
97
+ macro_data = macro_data.all if macro_data.is_a?(Docstring)
98
+ macro_data.gsub(MACRO_MATCH) do
99
+ escape, first, last, rng = $1, $2 || $5, $4, $3 ? true : false
100
+ next $&[1..-1] if escape
101
+ if first == '*'
102
+ last ? $& : full_source
103
+ else
104
+ first_i = first.to_i
105
+ last_i = (last ? last.to_i : call_params.size)
106
+ last_i = first_i unless rng
107
+ params = call_params[first_i..last_i]
108
+ params ? params.join(", ") : ''
109
+ end
110
+ end
111
+ end
112
+
113
+ # Applies a macro on a docstring by creating any macro data inside of
114
+ # the docstring first. Equivalent to calling {find_or_create} and {apply_macro}
115
+ # on the new macro object.
116
+ #
117
+ # @param [Docstring] docstring the docstring to create a macro out of
118
+ # @macro macro.expand
119
+ # @see find_or_create
120
+ def apply(docstring, call_params = [], full_source = '', block_source = '', method_object = nil)
121
+ macro = find_or_create(docstring, method_object)
122
+ apply_macro(macro, docstring, call_params, full_source, block_source)
123
+ end
124
+
125
+ # Applies a macro to a docstring, interpolating the macro's data on the
126
+ # docstring and appending any extra local docstring data that was in
127
+ # the original +docstring+ object.
128
+ #
129
+ # @param [MacroObject] macro the macro object
130
+ # @macro macro.expand
131
+ def apply_macro(macro, docstring, call_params = [], full_source = '', block_source = '')
132
+ docstring = Docstring.new(docstring) unless Docstring === docstring
133
+ data = []
134
+ data << macro.expand(call_params, full_source, block_source) if macro
135
+ if !macro && new_macro?(docstring)
136
+ data << expand(macro_data(docstring), call_params, full_source, block_source)
137
+ end
138
+ data << nonmacro_data(docstring)
139
+ data.join("\n").strip
140
+ end
141
+
142
+ private
143
+
144
+ def new_macro?(docstring)
145
+ if docstring.tag(:macro)
146
+ if types = docstring.tag(:macro).types
147
+ return true if types.include?('new') || types.include?('attach')
148
+ end
149
+ if docstring.all =~ MACRO_MATCH
150
+ return true
151
+ end
152
+ end
153
+ false
154
+ end
155
+
156
+ def attached_macro?(docstring, method_object)
157
+ return false if method_object.nil?
158
+ return false if docstring.tag(:macro).types.nil?
159
+ docstring.tag(:macro).types.include?('attach')
160
+ end
161
+
162
+ def macro_name(docstring)
163
+ docstring.tag(:macro).name
164
+ end
165
+
166
+ def macro_data(docstring)
167
+ new_docstring = docstring.dup
168
+ new_docstring.delete_tags(:macro)
169
+ tag_text = docstring.tag(:macro).text
170
+ if !tag_text || tag_text.strip.empty?
171
+ new_docstring.to_raw.strip
172
+ else
173
+ tag_text
174
+ end
175
+ end
176
+
177
+ def nonmacro_data(docstring)
178
+ if new_macro?(docstring)
179
+ text = docstring.tag(:macro).text
180
+ return '' if !text || text.strip.empty?
181
+ end
182
+ new_docstring = docstring.dup
183
+ new_docstring.delete_tags(:macro)
184
+ new_docstring.to_raw
185
+ end
186
+ end
187
+
188
+ # @return [String] the macro data stored on the object
189
+ attr_accessor :macro_data
190
+
191
+ # @return [CodeObjects::Base] the method object that this macro is
192
+ # attached to.
193
+ attr_accessor :method_object
194
+
195
+ # @return [Boolean] whether this macro is attached to a method
196
+ def attached?; method_object ? true : false end
197
+ def path; '.macro.' + name.to_s end
198
+ def sep; '.' end
199
+
200
+ # Expands the macro using
201
+ # @param [Array<String>] call_params a list of tokens that are passed
202
+ # to the method call
203
+ # @param [String] full_source the full method call (not including the block)
204
+ # @param [String] block_source the source passed in the block of the method
205
+ # call, if there is a block.
206
+ # @example Expanding a Macro
207
+ # macro.expand(%w(property foo bar), 'property :foo, :bar', '') #=>
208
+ # "...macro data interpolating this line of code..."
209
+ # @see expand
210
+ def expand(call_params = [], full_source = '', block_source = '')
211
+ self.class.expand(macro_data, call_params, full_source, block_source)
212
+ end
213
+ end
214
+ end
215
+ end