docrb 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,178 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- class BaseContainer
6
- # Module Computations implements utility methods to handle hierarchy and
7
- # nesting.
8
- module Computations
9
- # Recursively computes all dependants of the receiver.
10
- # This method expands all references into concrete representations.
11
- def compute_dependants
12
- return if @compute_dependants
13
-
14
- @compute_dependants = true
15
-
16
- classes.each(&:compute_dependants)
17
- modules.each(&:compute_dependants)
18
-
19
- extends.each do |ref|
20
- resolve_ref(ref)&.compute_dependants
21
- end
22
- includes.each do |ref|
23
- resolve_ref(ref)&.compute_dependants
24
- end
25
-
26
- return unless (parent_name = @inherits)
27
-
28
- @parent_class = resolve(parent_name)
29
- return unless @parent_class
30
-
31
- @parent_class.compute_dependants
32
- end
33
-
34
- # Returns a Hash containing all methods for the container, along with
35
- # inherited and included ones.
36
- def merged_instance_methods
37
- return @merged_instance_methods if @merged_instance_methods
38
-
39
- compute_dependants
40
-
41
- methods = {}
42
- @parent_class&.merged_instance_methods&.each do |k, v|
43
- methods[k] = { source: :inheritance, definition: v }
44
- end
45
-
46
- includes.each do |ref|
47
- next unless (container = resolve_container(ref))
48
-
49
- container.merged_instance_methods.each do |k, v|
50
- methods[k] = { source: :inclusion, definition: v, overriding: methods[k] }
51
- end
52
- end
53
-
54
- defs.each do |m|
55
- methods[m.name] = { source: :source, definition: m, overriding: methods[m.name] }
56
- end
57
-
58
- methods
59
- end
60
-
61
- # Returns a hash containing all class methods for the container, along
62
- # with inherited and extended ones.
63
- def merged_class_methods
64
- return @merged_class_methods if @merged_class_methods
65
-
66
- compute_dependants
67
-
68
- methods = {}
69
- @parent_class&.merged_class_methods&.each do |k, v|
70
- methods[k] = { source: :inheritance, definition: v }
71
- end
72
-
73
- includes.each do |ref|
74
- next unless (container = resolve_container(ref))
75
-
76
- container.merged_class_methods.each do |k, v|
77
- methods[k] = { source: :extension, definition: v, overriding: methods[k] }
78
- end
79
- end
80
-
81
- sdefs.each do |m|
82
- methods[m.name] = { source: :source, definition: m, overriding: methods[m.name] }
83
- end
84
-
85
- methods
86
- end
87
-
88
- # Returns a hash containing all attributes for the container, along with
89
- # inherited ones.
90
- def merged_attributes
91
- return @merged_attributes if @merged_attributes
92
-
93
- compute_dependants
94
-
95
- attrs = {}
96
- @parent_class&.merged_attributes&.each do |k, v|
97
- attrs[k] = { source: :inheritance, definition: v }
98
- end
99
-
100
- attributes.each do |m|
101
- attrs[m.name] = { source: :source, definition: m, overriding: attrs[m.name] }
102
- end
103
-
104
- attrs
105
- end
106
-
107
- # Deprecated: Use #merged_instance_methods.
108
- def all_defs
109
- return @all_defs if @all_defs
110
-
111
- compute_dependants
112
-
113
- methods = {}
114
- @parent_class&.all_defs&.each do |k, v|
115
- methods[k] = v
116
- end
117
-
118
- includes.each do |ref|
119
- resolve_ref(ref)&.all_defs&.each do |met|
120
- if methods.key? met.name
121
- methods[key].override! met
122
- next
123
- end
124
-
125
- methods[met.name] = met
126
- end
127
- end
128
-
129
- defs.each do |met|
130
- if methods.key? met.name
131
- methods[met.name].override! met
132
- next
133
- end
134
-
135
- methods[met.name] = met
136
- end
137
-
138
- @all_defs = methods
139
- end
140
-
141
- # Deprecated: Use #merged_class_methods.
142
- def all_sdefs
143
- return @all_sdefs if @all_sdefs
144
-
145
- compute_dependants
146
-
147
- methods = {}
148
-
149
- @parent_class&.all_sdefs&.each do |k, v|
150
- methods[k] = v
151
- end
152
-
153
- extends.each do |ref|
154
- resolve_ref(ref)&.all_defs&.each do |met|
155
- if methods.key? met.name
156
- methods[met.name].override! met
157
- next
158
- end
159
-
160
- methods[met.name] = met
161
- end
162
- end
163
-
164
- @sdefs.each do |met|
165
- if methods.key? met.name
166
- methods[met.name].override! met
167
- next
168
- end
169
-
170
- methods[met.name] = met
171
- end
172
-
173
- @all_sdefs = methods
174
- end
175
- end
176
- end
177
- end
178
- end
@@ -1,123 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # BaseContainer represents a container for methods, classes, modules and
6
- # attributes.
7
- class BaseContainer < Resolvable
8
- require_relative "./base_container/computations"
9
- include Computations
10
-
11
- attr_accessor :extends, :includes, :classes, :modules, :defs, :sdefs,
12
- :type, :name, :defined_by, :parent, :doc
13
-
14
- # Initialises a new BaseContainer instance with a provided parent,
15
- # filename and represented object.
16
- def initialize(parent, filename, obj)
17
- super()
18
- @parent = parent
19
- @type = obj[:type]
20
- @name = obj[:name]
21
- @extends = []
22
- @includes = []
23
- @classes = ObjectContainer.new(self, DocClass)
24
- @modules = ObjectContainer.new(self, DocModule)
25
- @defs = ObjectContainer.new(self, DocMethod)
26
- @sdefs = ObjectContainer.new(self, DocMethod)
27
- @defined_by = ObjectContainer.new(nil, FileRef)
28
- append(filename, obj)
29
- end
30
-
31
- # Appends a new object defined by the provided file to this container
32
- def <<(filename, obj)
33
- append(filename, obj)
34
- end
35
-
36
- # Appends a new class defined by the provided file to this container
37
- def append_class(filename, cls)
38
- @classes.push filename, cls
39
- end
40
-
41
- def inspect
42
- ext = "#{extends.length} extend#{extends.length == 1 ? "" : "s"}"
43
- inc = "#{includes.length} include#{includes.length == 1 ? "" : "s"}"
44
- cls = "#{classes.length} class#{classes.length == 1 ? "" : "es"}"
45
- mod = "#{modules.length} module#{modules.length == 1 ? "" : "s"}"
46
- de = "#{defs.length} def#{defs.length == 1 ? "" : "s"}"
47
- sde = "#{sdefs.length} sdef#{sdefs.length == 1 ? "" : "s"}"
48
-
49
- "#<#{self.class.name}:#{format("0x%08x",
50
- object_id * 2)} #{@type} #{@name} #{ext}, #{inc}, #{cls}, #{mod}, #{de}, #{sde}>"
51
- end
52
-
53
- # Attempts to append a given object defined in a provided filename.
54
- # Raises ArgumentError in case the object can't be added to the container
55
- # due to type contraints.
56
- #
57
- # filename - Filename defining the object being appended
58
- # obj - The object definition to be appended to this container.
59
- def append(filename, obj)
60
- raise ArgumentError, "cannot append obj of type #{obj[:type]} into #{@name} (#{@type})" if obj[:type] != @type
61
-
62
- raise ArgumentError, "cannot append obj named #{obj[:name]} into #{@name} (#{@type})" if obj[:name] != @name
63
-
64
- unless (doc = obj[:doc]).nil?
65
- @defined_by.push(filename, obj)
66
- @doc = doc
67
- end
68
-
69
- obj.fetch(:classes, []).each { |c| @classes.push(filename, c) }
70
- obj.fetch(:modules, []).each { |m| @modules.push(filename, m) }
71
- obj.fetch(:extend, []).each { |e| @extends << e }
72
- obj.fetch(:include, []).each { |i| @includes << i }
73
- obj.fetch(:methods, []).each do |met|
74
- if met[:type] == :def
75
- @defs.push(filename, met)
76
- else
77
- @sdefs.push(filename, met)
78
- end
79
- end
80
-
81
- @inherits = obj.fetch(:inherits, nil) if obj[:type] == :class
82
-
83
- appended(filename, obj)
84
- end
85
-
86
- # Courtesy method. This method is called whenever a new object is
87
- # appended to the container. Inheriting classes can override it to perform
88
- # further operations on the appended object.
89
- def appended(filename, obj); end
90
-
91
- # Intenral: Recursively unpacks a given element by checking its
92
- # :definition and :overriding keys.
93
- #
94
- # Returns the updated element.
95
- def unpack(elem)
96
- return unless elem
97
-
98
- elem.tap do |e|
99
- inner = e[:definition]
100
- e[:definition] = inner.is_a?(Hash) ? unpack(inner) : inner.to_h
101
- e[:overriding] = unpack(e[:overriding])
102
- end
103
- end
104
-
105
- # Returns a Hash representation of the current container along with its
106
- # children.
107
- def to_h
108
- {
109
- type:,
110
- name:,
111
- doc: DocBlocks.prepare(doc, parent: self),
112
- defined_by: defined_by.map(&:to_h),
113
- defs: merged_instance_methods.transform_values { |v| unpack(v) },
114
- sdefs: merged_class_methods.transform_values { |v| unpack(v) },
115
- classes: classes.map(&:to_h),
116
- modules: modules.map(&:to_h),
117
- includes: includes.map(&:to_h),
118
- extends: extends.map(&:to_h)
119
- }
120
- end
121
- end
122
- end
123
- end
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # DocAttribute represents a single attribute defined on a class.
6
- class DocAttribute < Resolvable
7
- attr_reader :parent, :defined_by, :name, :docs, :type, :writer_visibility,
8
- :reader_visibility
9
-
10
- def initialize(parent, filename, obj)
11
- super()
12
- @parent = parent
13
- @defined_by = [filename]
14
- @name = obj[:name]
15
- @docs = obj[:docs]
16
- @type = nil
17
- @writer_visibility = obj[:writer_visibility]
18
- @reader_visibility = obj[:reader_visibility]
19
- end
20
-
21
- # Marks the current attribute as an accessor.
22
- #
23
- # Returns the attribute's instance for chaining.
24
- def accessor!
25
- @type = :accessor
26
- self
27
- end
28
-
29
- # Marks the current attribute as an reader.
30
- #
31
- # Returns the attribute's instance for chaining.
32
- def reader!
33
- @type = :reader
34
- self
35
- end
36
-
37
- # Marks the current attribute as an writer.
38
- #
39
- # Returns the attribute's instance for chaining.
40
- def writer!
41
- @type = :writer
42
- self
43
- end
44
-
45
- # Returns a Hash representing this attribute
46
- def to_h
47
- {
48
- defined_by:,
49
- name:,
50
- docs:,
51
- type:,
52
- writer_visibility:,
53
- reader_visibility:
54
- }
55
- end
56
- end
57
- end
58
- end
@@ -1,111 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # DocBlocks provides utilities for annotating and formatting documentation
6
- # blocks.
7
- class DocBlocks
8
- # Internal: Transforms a provided reference on a given parent by a
9
- # ttempting to resolve it into a specific object.
10
- #
11
- # ref - Reference to be resolved
12
- # parent - The reference's parent
13
- #
14
- # Returns the reference itself by augmenting it with :ref_type and
15
- # :ref_path information.
16
- def self.process_reference(ref, parent)
17
- return process_method_reference(ref, parent) if ref[:ref_type] == :method
18
- return ref if ref[:ref_type] != :ambiguous
19
-
20
- resolved = parent.resolve_ref(ref)
21
- if resolved.nil?
22
- ref[:ref_type] = :not_found
23
- return ref
24
- end
25
- ref[:ref_type] = resolved.type
26
- ref[:ref_path] = resolved.path
27
- ref
28
- end
29
-
30
- def self.process_method_reference(ref, parent)
31
- if (resolved = parent.resolve_ref(ref))
32
- ref[:ref_path] = resolved.path
33
- end
34
- ref
35
- end
36
-
37
- # Internal: Processes a identifier on a provided parent. Returns an
38
- # augmented reference with extra location information whenever it can
39
- # be resolved.
40
- #
41
- # id - Identifier to be processed
42
- # parent - Parent on which the identifier appeared.
43
- def self.process_identifier(id, parent)
44
- resolved = parent.resolve(id[:contents].to_sym)
45
- if resolved.nil?
46
- puts "Unresolved: #{id[:contents]} on #{parent.path}"
47
- return {
48
- type: :span,
49
- contents: Markdown.inline("`#{id[:contents]}`")
50
- }
51
- end
52
- {
53
- type: :ref,
54
- ref_type: resolved.type,
55
- ref_path: resolved.path,
56
- contents: id[:contents]
57
- }
58
- end
59
-
60
- # Internal: Formats a given TextBlock object by expanding its contents
61
- # into markdown annotations and attempting to resolve references and
62
- # identifiers.
63
- #
64
- # Returns the updated block list.
65
- def self.format_text_block(block, parent)
66
- unless block[:contents].is_a? Array
67
- block[:contents] = Markdown.inline(block[:contents])
68
- return block
69
- end
70
- block[:contents].map! do |c|
71
- case c[:type]
72
- when :span
73
- c[:contents] = Markdown.inline(c[:contents])
74
- when :ref
75
- c = process_reference(c, parent)
76
- when :camelcase_identifier
77
- c = process_identifier(c, parent)
78
- end
79
- c
80
- end
81
- block
82
- end
83
-
84
- # Public: Updates a given documentation block by augmenting markdown
85
- # elements, references, fields and identifiers.
86
- #
87
- # doc - Documentation block to be processed
88
- # parent - The parent container to which the block belongs to.
89
- #
90
- # Returns the updated block.
91
- def self.prepare(doc, parent: nil)
92
- return unless doc
93
-
94
- doc[:contents].map! do |block|
95
- case block[:type]
96
- when :text_block
97
- format_text_block(block, parent)
98
- when :code_example
99
- block[:contents] = Markdown.render_source(block[:contents])
100
- when :field_block
101
- block[:contents] = block[:contents].transform_values { |v| format_text_block(v, parent) }
102
- else
103
- puts "Skipped block with type #{block[:type]}"
104
- end
105
- block
106
- end
107
- doc
108
- end
109
- end
110
- end
111
- end
@@ -1,43 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # DocClass represents a documented class
6
- class DocClass < BaseContainer
7
- attr_accessor :inherits, :attributes
8
-
9
- # Initializes a new instance with the provided parent, file, and object.
10
- #
11
- # parent - The parent container holding this class definition.
12
- # filename - The filename defining this class.
13
- # obj - The parsed class data.
14
- def initialize(parent, filename, obj)
15
- @inherits = nil
16
- @attributes = ObjectContainer.new(self, DocAttribute, always_append: true)
17
- super
18
- end
19
-
20
- def appended(filename, obj)
21
- obj.fetch(:attr_accessor, []).each do |att|
22
- @attributes.push(filename, att).accessor!
23
- end
24
-
25
- obj.fetch(:attr_reader, []).each do |att|
26
- @attributes.push(filename, att).reader!
27
- end
28
-
29
- obj.fetch(:attr_writer, []).each do |att|
30
- @attributes.push(filename, att).writer!
31
- end
32
- end
33
-
34
- # Returns a Hash representation of this class definition
35
- def to_h
36
- super.to_h.merge({
37
- inherits:,
38
- attributes: merged_attributes.transform_values { |v| unpack(v) }
39
- })
40
- end
41
- end
42
- end
43
- end
@@ -1,66 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # DocMethod represents a documented method
6
- class DocMethod < Resolvable
7
- attr_reader :parent, :type, :name, :args, :doc, :visibility,
8
- :overriden_by, :defined_by
9
-
10
- # Initializes a new DocMethod instance with a given parent, path, and
11
- # object.
12
- #
13
- # parent - The object holding the represented method
14
- # filename - The filename on which the method is defined on
15
- # obj - The method definition itself
16
- def initialize(parent, filename, obj)
17
- super()
18
- @parent = parent
19
- @type = obj[:type]
20
- @name = obj[:name]
21
- @args = obj[:args]
22
- @doc = obj[:doc]
23
- @visibility = obj[:visibility]
24
- @overriden_by = nil
25
- @defined_by = FileRef.new(nil, filename, obj)
26
- end
27
-
28
- # Public: Marks this method as being overriden by a provided object in
29
- # a given filename. This method initializes a new DocMethod instance using
30
- # this instance's parent, the provided filename and object, and invokes
31
- # #override! on it.
32
- #
33
- # filename - The filename defining the override for this method
34
- # obj - The object representing the override for this method
35
- def override(filename, obj)
36
- override! DocMethod.new(@parent, filename, obj)
37
- end
38
-
39
- # Public: Marks this method as being overriden by the provided method
40
- #
41
- # method - DocMethod object overriding this method's implementation
42
- def override!(method)
43
- @overriden_by = method
44
- end
45
-
46
- def inspect
47
- type = @type == :def ? "method" : "singleton method"
48
- overriden = overriden_by.nil? ? "" : " overriden"
49
- "#<DocMethod:#{format("0x%08x", object_id * 2)} #{visibility} #{type} #{name}#{overriden}>"
50
- end
51
-
52
- # Public: Returns a Hash representation of this method
53
- def to_h
54
- {
55
- type:,
56
- name:,
57
- args:,
58
- doc: DocBlocks.prepare(doc, parent: self),
59
- visibility:,
60
- overriden_by:,
61
- defined_by: defined_by&.to_h
62
- }
63
- end
64
- end
65
- end
66
- end
@@ -1,9 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # DocModule represents a documented Module object
6
- class DocModule < BaseContainer
7
- end
8
- end
9
- end
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # FileRef represents a file reference. This class is used to represent
6
- # definition metadata of objects in files. Instances of this class
7
- # indicates filenames and boundaries for a represented object.
8
- class FileRef
9
- attr_reader :filename, :start_at, :end_at
10
-
11
- # Initializes a new FileRef instance.
12
- #
13
- # _parent - Unused.
14
- # filename - Path to the file being referenced
15
- # obj - The object being referenced in the provided filename
16
- #
17
- def initialize(_parent, filename, obj)
18
- @filename = filename
19
- @start_at, @end_at = obj.values_at(:start_at, :end_at)
20
- end
21
-
22
- # Public: Returns the source code portion of the represented reference
23
- def ruby_source
24
- f = File.read(filename).split("\n")[start_at - 1..end_at - 1]
25
- f.join("\n")
26
- end
27
-
28
- # Public: Returns the reference's Hash representation
29
- def to_h
30
- source = ruby_source
31
- {
32
- filename:,
33
- start_at:,
34
- end_at:,
35
- source:,
36
- markdown_source: Markdown.render_source(source)
37
- }
38
- end
39
- end
40
- end
41
- end
@@ -1,68 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Docrb
4
- class DocCompiler
5
- # ObjectContainer implements utilities for representing a container of
6
- # objects with a common type.
7
- class ObjectContainer
8
- extend Forwardable
9
-
10
- attr_reader :objects
11
-
12
- # Initializes a new container with a given parent, containing objects with
13
- # a provided class and options.
14
- #
15
- # parent - The container's parent
16
- # cls - The class of represented items
17
- # **opts - Options list. Currently, only `always_append` is supported,
18
- # which indicates that all objects provided to the instance must
19
- # be appended regardless of their type.
20
- def initialize(parent, cls, **opts)
21
- @cls = cls
22
- @objects = []
23
- @opts = opts
24
- @parent = parent
25
- end
26
-
27
- def_delegators :@objects, :length, :each, :map, :find, :filter, :first, :[], :to_json
28
-
29
- # Pushes a new object into the container. May raise ArgumentError in case
30
- # the object being pushed is not compatible with the container's base
31
- # object.
32
- #
33
- # filename - Name of the file which defines the object being pushed
34
- # obj - The object being pushed
35
- #
36
- # Returns the new instance representing the pushed object.
37
- def push(filename, obj)
38
- if @cls.method_defined?(:name) &&
39
- (instance = @objects.find { |o| o.name == obj[:name] }) &&
40
- !@opts.fetch(:always_append, false)
41
-
42
- return instance.merge(filename, obj) if instance.respond_to? :merge
43
- return instance.override(filename, obj) if instance.respond_to? :override
44
- return instance.append(filename, obj) if instance.respond_to? :append
45
-
46
- raise ArgumentError, "cannot handle existing object #{obj[:name]} for container of type #{@cls.name}"
47
- end
48
-
49
- inst = @cls.new(@parent, filename, obj)
50
- @objects << inst
51
- inst
52
- end
53
-
54
- def inspect
55
- opts = []
56
- opts << "always_append" if @opts[:always_append]
57
- count = if @objects.count.zero?
58
- "empty"
59
- else
60
- "#{@objects.length} object#{@objects.length == 1 ? "" : "s"}"
61
- end
62
- obj_id = format("0x%08x", object_id * 2)
63
- opts_repr = opts.empty? ? "" : " #{opts.join(", ")}"
64
- "#<ObjectContainer:#{obj_id} containing #{@cls.name}, #{count}#{opts_repr}>"
65
- end
66
- end
67
- end
68
- end