docrb-html 0.2.5 → 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.
Files changed (76) hide show
  1. checksums.yaml +4 -4
  2. data/.editorconfig +1 -1
  3. data/.rubocop.yml +32 -15
  4. data/Gemfile +11 -1
  5. data/Gemfile.lock +52 -2
  6. data/assets/breadcrumb.scss +0 -1
  7. data/assets/class_header.scss +1 -0
  8. data/assets/constant_display.scss +16 -0
  9. data/assets/doc_box.scss +34 -4
  10. data/assets/documentation_block.scss +29 -1
  11. data/assets/field_block.scss +46 -0
  12. data/assets/js/filtering.js +3 -3
  13. data/assets/links.scss +0 -2
  14. data/assets/method_argument.scss +2 -8
  15. data/assets/method_display.scss +4 -0
  16. data/assets/method_list.scss +40 -0
  17. data/assets/reference.scss +1 -1
  18. data/assets/style.scss +3 -0
  19. data/assets/svg.scss +37 -0
  20. data/assets/symbol.scss +1 -1
  21. data/assets/text_block.scss +9 -9
  22. data/bin/smoke +5 -0
  23. data/{renderer.gemspec → docrb-html.gemspec} +2 -0
  24. data/exe/docrb-html +1 -1
  25. data/lib/docrb-html.rb +165 -0
  26. data/lib/renderer/component/attribute.rb +9 -0
  27. data/lib/renderer/component/attribute_display.rb +17 -0
  28. data/lib/renderer/component/constant_display.rb +9 -0
  29. data/lib/renderer/component/doc_box.rb +26 -18
  30. data/lib/renderer/component/documentation_comment.rb +9 -0
  31. data/lib/renderer/component/field_block.rb +9 -0
  32. data/lib/renderer/component/method_argument.rb +19 -63
  33. data/lib/renderer/component/method_display.rb +1 -1
  34. data/lib/renderer/component/method_list.rb +1 -1
  35. data/lib/renderer/component/reference.rb +23 -12
  36. data/lib/renderer/component/text_block.rb +1 -7
  37. data/lib/renderer/component.rb +4 -4
  38. data/lib/renderer/core_extensions.rb +12 -1
  39. data/lib/renderer/entities/attribute.rb +17 -0
  40. data/lib/renderer/entities/attribute_definition.rb +23 -0
  41. data/lib/renderer/entities/base.rb +58 -0
  42. data/lib/renderer/entities/class.rb +17 -0
  43. data/lib/renderer/entities/container.rb +52 -0
  44. data/lib/renderer/entities/method.rb +18 -0
  45. data/lib/renderer/entities/method_argument.rb +27 -0
  46. data/lib/renderer/entities/method_definition.rb +25 -0
  47. data/lib/renderer/entities/module.rb +29 -0
  48. data/lib/renderer/entities/reference.rb +103 -0
  49. data/lib/renderer/entities/source_definition.rb +17 -0
  50. data/lib/renderer/entities.rb +30 -0
  51. data/lib/renderer/helpers.rb +77 -19
  52. data/lib/renderer/markdown.rb +62 -0
  53. data/lib/renderer/metadata.rb +10 -26
  54. data/lib/renderer/template.rb +4 -6
  55. data/lib/renderer/version.rb +1 -1
  56. data/script/makecomponent +1 -1
  57. data/templates/attribute.erb +10 -0
  58. data/templates/attribute_display.erb +22 -0
  59. data/templates/breadcrumb.erb +9 -9
  60. data/templates/class_header.erb +6 -7
  61. data/templates/component_list.erb +14 -8
  62. data/templates/constant_display.erb +13 -0
  63. data/templates/doc_box.erb +40 -31
  64. data/templates/documentation_block.erb +2 -11
  65. data/templates/documentation_comment.erb +23 -0
  66. data/templates/field_block.erb +15 -0
  67. data/templates/method_argument.erb +4 -4
  68. data/templates/method_display.erb +14 -21
  69. data/templates/method_list.erb +52 -8
  70. data/templates/reference.erb +10 -7
  71. data/templates/text_block.erb +13 -14
  72. metadata +60 -8
  73. data/lib/renderer/defs/specialized_object.rb +0 -172
  74. data/lib/renderer/defs/specialized_projection.rb +0 -31
  75. data/lib/renderer/defs.rb +0 -180
  76. data/lib/renderer.rb +0 -131
data/lib/docrb-html.rb ADDED
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ require "erb"
6
+ require "sassc"
7
+ require "nokogiri"
8
+ require "redcarpet"
9
+ require "rouge"
10
+ require "rouge/plugins/redcarpet"
11
+
12
+ require_relative "renderer/version"
13
+ require_relative "renderer/core_extensions"
14
+ require_relative "renderer/markdown"
15
+
16
+ class Renderer
17
+ ASSETS_PATH = Pathname.new(__dir__).join("../assets")
18
+ TEMPLATES_PATH = Pathname.new(__dir__).join("../templates")
19
+ STYLE_BASE = SassC::Engine.new(File.read(ASSETS_PATH.join("style.scss")),
20
+ style: :compressed,
21
+ load_paths: [ASSETS_PATH]).render
22
+ end
23
+
24
+ require_relative "renderer/template"
25
+ require_relative "renderer/component"
26
+ require_relative "renderer/helpers"
27
+ require_relative "renderer/page"
28
+ require_relative "renderer/metadata"
29
+ require_relative "renderer/entities"
30
+
31
+ class Renderer
32
+ def now = Time.now.strftime("%A, %-d %b %Y %H:%M:%S %Z")
33
+
34
+ def output_path(*args) = File.join(@output, *args.map(&:to_s))
35
+
36
+ def initialize(source, spec, output)
37
+ @output = output
38
+ @footer = Component::Footer.new(version: VERSION, updated_at: now)
39
+ @spec = spec
40
+ @source = source
41
+ Helpers.current_renderer = self
42
+ end
43
+
44
+ def git_url(loc)
45
+ url = @spec[:git_url]
46
+ tip = @spec[:git_tip]
47
+ filename = clean_file_path(loc)
48
+ "#{url}/blob/#{tip}#{filename}#L#{loc.line_start}"
49
+ end
50
+
51
+ def clean_file_path(definition) = definition.file_path.gsub(@spec[:git_root], "")
52
+
53
+ def make_outline
54
+ (@source.nodes.by_kind(:module) + @source.nodes.by_kind(:class))
55
+ .map { outline(_1) }
56
+ end
57
+
58
+ def outline(object, level = 0)
59
+ {
60
+ level:,
61
+ object:,
62
+ classes: object.classes.map { outline(_1, level + 1) },
63
+ modules: object.modules.map { outline(_1, level + 1) }
64
+ }
65
+ end
66
+
67
+ def render
68
+ project_header = Component::ProjectHeader.new(
69
+ name: @spec[:name],
70
+ description: @spec[:summary],
71
+ license: @spec[:license],
72
+ owner: Metadata.format_authors(@spec[:authors]),
73
+ links: Metadata.project_links(@spec)
74
+ )
75
+
76
+ index = Page.new(title: "#{@spec[:name]} - Docrb") do
77
+ readme = Component::Markdown.new(source: Markdown.render(@spec[:readme]))
78
+
79
+ [
80
+ project_header,
81
+ Component::TabBar.new(
82
+ selected_index: 0,
83
+ items: [
84
+ { name: "Readme", href: "/" },
85
+ { name: "Components", href: "/components.html" }
86
+ ]
87
+ ),
88
+ readme,
89
+ @footer
90
+ ]
91
+ end
92
+
93
+ components = Page.new(title: "Components - #{@spec[:name]} - Docrb") do
94
+ [
95
+ project_header,
96
+ Component::TabBar.new(
97
+ selected_index: 1,
98
+ items: [
99
+ { name: "Readme", href: "/" },
100
+ { name: "Components", href: "/components.html" }
101
+ ]
102
+ ),
103
+ Component::ComponentList.new(list: make_outline),
104
+ @footer
105
+ ]
106
+ end
107
+
108
+ pages(@source.nodes.by_kind(:class, :module))
109
+
110
+ FileUtils.mkdir_p @output
111
+
112
+ index.render_to(output_path("index.html"))
113
+ components.render_to(output_path("components.html"))
114
+ File.write(output_path("style.css"), STYLE_BASE)
115
+
116
+ copy_assets
117
+ end
118
+
119
+ def copy_assets
120
+ [
121
+ "js/filtering.js",
122
+ "favicon.ico"
123
+ ].each do |file|
124
+ File.write(output_path(File.basename(file)),
125
+ File.read(ASSETS_PATH.join(file)))
126
+ end
127
+ end
128
+
129
+ def pages(comps, parents = [])
130
+ comps.each do |comp|
131
+ title = "#{comp.name} - #{@spec[:name]} - Docrb"
132
+ page = Page.new(title:, level: parents.count) do
133
+ [
134
+ Component::ClassHeader.new(
135
+ type: comp.kind,
136
+ name: comp.name,
137
+ definitions: comp.defined_by.map do |by|
138
+ {
139
+ filename: File.basename(by.file_path),
140
+ git_url: git_url(by)
141
+ }
142
+ end
143
+ ),
144
+ Component::Breadcrumb.new(
145
+ project_name: @spec[:name],
146
+ items: (parents + [comp]).map.with_index do |p, idx|
147
+ { name: p.name, parents: parents[0...idx].map(&:name) }
148
+ end
149
+ ),
150
+ Component::DocBox.new(
151
+ item: comp,
152
+ meta: @spec
153
+ ),
154
+ @footer
155
+ ]
156
+ end
157
+
158
+ parent_dir = output_path(*parents.map(&:name))
159
+ FileUtils.mkdir_p(parent_dir)
160
+ page.render_to(File.join(parent_dir, "#{comp.name}.html"))
161
+
162
+ pages(comp.classes + comp.modules, parents + [comp])
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ class Component
5
+ class Attribute < Component
6
+ prop :item, :kind, :omit_type
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ class Component
5
+ class AttributeDisplay < Component
6
+ prop :item, :attr_type, :omit_link, :kind, :omit_type
7
+
8
+ def prepare
9
+ @attr_type = case item.type
10
+ when :accessor then "read/write"
11
+ when :reader then "read-only"
12
+ else "write-only"
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ class Component
5
+ class ConstantDisplay < Component
6
+ prop :item, :omit_link
7
+ end
8
+ end
9
+ end
@@ -3,32 +3,40 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class DocBox < Component
6
- prop :item, :meta, :defs, :has_class_docs, :has_class_details, :has_attrs,
7
- :has_class_methods, :has_instance_methods, :page_components,
8
- :defs, :sdefs, :attrs
6
+ prop :item, :meta, :has_class_docs, :has_class_details,
7
+ :instance_methods, :has_instance_methods,
8
+ :class_methods, :has_class_methods,
9
+ :has_instance_attributes, :instance_attributes,
10
+ :has_class_attributes, :class_attributes,
11
+ :has_constants, :constants,
12
+ :page_components
9
13
 
10
14
  def prepare
11
- @item = defs.specialized_projection.find_path(@item)
15
+ @has_class_docs = (item.doc && !item.doc.empty?) || false
16
+ @instance_methods = item.all_instance_methods
17
+ @class_methods = item.all_class_methods
18
+ @instance_attributes = item.kind == :class ? item.all_instance_attributes : []
19
+ @class_attributes = item.all_class_attributes
20
+ @constants = item.constants
12
21
 
13
- @has_class_docs = item[:doc] && !item[:doc].empty?
14
- @defs = item.defs || []
15
- @sdefs = item.sdefs || []
16
- @attrs = item.attributes || []
17
-
18
- @has_attrs = !@attrs.empty?
19
- @has_class_methods = !@sdefs.empty?
20
- @has_instance_methods = !@defs.empty?
22
+ @has_instance_attributes = !@instance_attributes.empty?
23
+ @has_class_attributes = !@class_attributes.empty?
24
+ @has_class_methods = !@class_methods.empty?
25
+ @has_instance_methods = !@instance_methods.empty?
21
26
  @has_class_details =
22
- !item[:inherits].nil? \
23
- || !item.fetch(:extends, []).empty? \
24
- || !item.fetch(:includes, []).empty? \
25
- || !item.fetch(:modules, []).empty? \
26
- || !item.fetch(:classes, []).empty?
27
+ (item.kind == :class && !item.inherits.nil?) \
28
+ || !item.extends.empty? \
29
+ || !item.includes.empty? \
30
+ || !item.modules.empty? \
31
+ || !item.classes.empty?
32
+ @has_constants = !@constants.empty?
27
33
 
28
34
  @page_components = {
29
35
  "class-documentation" => { enabled: has_class_docs, name: "Class Documentation" },
30
36
  "class-details" => { enabled: has_class_details, name: "Inheritance" },
31
- "attributes" => { enabled: has_attrs, name: "Attributes" },
37
+ "constants" => { enabled: has_constants, name: "Constants" },
38
+ "class-attributes" => { enabled: has_class_attributes, name: "Class Attributes" },
39
+ "instance-attributes" => { enabled: has_instance_attributes, name: "Instance Attributes" },
32
40
  "class-methods" => { enabled: has_class_methods, name: "Class Methods" },
33
41
  "instance-methods" => { enabled: has_instance_methods, name: "Instance Methods" }
34
42
  }
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ class Component
5
+ class DocumentationComment < Component
6
+ prop :doc, :class_name
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ class Component
5
+ class FieldBlock < Component
6
+ prop :fields
7
+ end
8
+ end
9
+ end
@@ -3,9 +3,14 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class MethodArgument < Component
6
- prop :type, :name, :value, :value_type, :computed
6
+ prop :type, :name, :value, :value_type, :computed, :arg
7
7
 
8
8
  def prepare
9
+ @type = @arg.kind
10
+ @name = @arg.name
11
+ @value = @arg.value&.source
12
+ @value_type = @arg.value_type
13
+
9
14
  @computed = {
10
15
  rest: rest_arg,
11
16
  name:,
@@ -15,17 +20,17 @@ class Renderer
15
20
  end
16
21
 
17
22
  def continuation_for_type
18
- if type == "kwarg" || type == "kwoptarg"
23
+ if arg.keyword? && !arg.rest?
19
24
  :colon
20
- elsif type&.index("opt")&.zero?
25
+ elsif arg.optional?
21
26
  :equal
22
27
  end
23
28
  end
24
29
 
25
30
  REST_ARG_BY_TYPE = {
26
- "kwrestarg" => :double,
27
- "restarg" => :single,
28
- "blockarg" => :block
31
+ kwrest: :double,
32
+ rest: :single,
33
+ block: :block
29
34
  }.freeze
30
35
 
31
36
  def rest_arg
@@ -36,71 +41,22 @@ class Renderer
36
41
  return if value_type.nil?
37
42
 
38
43
  case value_type
39
- when "sym"
44
+ when :symbol, :bool
40
45
  { kind: :symbol, value: }
41
- when "bool"
42
- { kind: :symbol, value: value.inspect }
43
- when "nil"
46
+ when :nil
44
47
  { kind: :symbol, value: "nil" }
45
- when "int"
48
+ when :number
46
49
  { kind: :number, value: }
47
- when "str"
50
+ when :string
48
51
  { kind: :string, value: }
49
- when "send"
50
- method_call_argument(value)
51
- when "const"
52
- const_value(value)
52
+ when :call
53
+ { kind: :call, value: }
54
+ when :const
55
+ { kind: :const, value: [value] }
53
56
  else
54
57
  { kind: :plain, value: }
55
58
  end
56
59
  end
57
-
58
- def method_call_argument(value)
59
- class_path = value[:target].map do |i|
60
- next { kind: :symbol, value: "self" } if i == "self"
61
-
62
- # TODO: Is this plain?
63
- [{ kind: :class_or_module, value: i }, { kind: :plain, value: "::" }]
64
- end.flatten
65
-
66
- class_path.pop
67
-
68
- {
69
- kind: :method_call_argument,
70
- value: [
71
- class_path,
72
- { kind: :plain, value: "." },
73
- { kind: :plain, value: value[:name] }
74
- ].flatten
75
- }
76
- end
77
-
78
- def const_value(value)
79
- class_path = value[:target].map do |i|
80
- # TODO: Is this plain?
81
- [{ kind: :class_or_module, value: i }, { kind: :continuation, double: true }]
82
- end.flatten
83
-
84
- class_path.pop
85
-
86
- if class_path.empty?
87
- return {
88
- kind: :const,
89
- value: [
90
- { kind: :class_or_module, value: value[:name] }
91
- ].flatten
92
- }
93
- end
94
-
95
- {
96
- kind: :const,
97
- value: [
98
- class_path,
99
- { kind: :continuation, double: true },
100
- { kind: :class_or_module, value: value[:name] }
101
- ].flatten
102
- }
103
- end
104
60
  end
105
61
  end
106
62
  end
@@ -3,7 +3,7 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class MethodDisplay < Component
6
- prop :visibility, :type, :name, :href, :args, :doc, :decoration, :short_type
6
+ prop :item, :omit_type, :parent
7
7
  end
8
8
  end
9
9
  end
@@ -3,7 +3,7 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class MethodList < Component
6
- prop :list
6
+ prop :list, :omit_type, :parent
7
7
  end
8
8
  end
9
9
  end
@@ -3,21 +3,32 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class Reference < Component
6
- prop :ref, :unresolved, :path
6
+ prop :unresolved, :path, :ref_type, :object, :value
7
7
 
8
8
  def prepare
9
- @unresolved = !ref[:ref_path]
10
- return if @unresolved
9
+ case @object
10
+ when Docrb::Parser::Reference
11
+ @unresolved = !@object.fulfilled?
12
+ @path = [@object.path.last]
13
+ return if @unresolved
11
14
 
12
- components = ref[:ref_path].dup
13
- @path = if ref[:ref_type] == "method"
14
- method_name = components.pop
15
- parent_name = components.pop
16
- components + ["#{parent_name}.html#{method_name}"]
17
- else
18
- last = components.pop
19
- (components + ["#{last}.html"]).compact
20
- end
15
+ @object = @object.dereference!
16
+ prepare
17
+ when Docrb::Parser::Container
18
+ @path = [@object.name]
19
+ @ref_type = @object.kind
20
+ parent = @object.parent
21
+ until parent.nil?
22
+ @path << parent.name
23
+ parent = parent.parent
24
+ end
25
+ @path << ""
26
+ @path.reverse!
27
+ else
28
+ @ref_type = :pure
29
+ @value = @object[:value]
30
+ @object = @object[:object]
31
+ end
21
32
  end
22
33
  end
23
34
  end
@@ -3,13 +3,7 @@
3
3
  class Renderer
4
4
  class Component
5
5
  class TextBlock < Component
6
- prop :list
7
-
8
- def prepare
9
- return unless !@list.nil? && !@list.is_a?(Array)
10
-
11
- @list = [{ type: "html", contents: @list }]
12
- end
6
+ prop :contents, :inline
13
7
  end
14
8
  end
15
9
  end
@@ -4,11 +4,11 @@ class Renderer
4
4
  class Component
5
5
  attr_accessor :id
6
6
 
7
- def self.prop(*names)
8
- attr_accessor(*names)
7
+ def self.prop(*)
8
+ attr_accessor(*)
9
9
 
10
10
  @props ||= []
11
- @props.append(*names)
11
+ @props.append(*)
12
12
  end
13
13
 
14
14
  class << self
@@ -38,7 +38,7 @@ class Renderer
38
38
 
39
39
  def render(&)
40
40
  prepare
41
- opts = props.map { [_1, send(_1)] }.to_h
41
+ opts = props.to_h { [_1, send(_1)] }
42
42
  opts[:id] = @id
43
43
  template.render(HELPERS, **opts, &)
44
44
  end
@@ -2,10 +2,21 @@
2
2
 
3
3
  class String
4
4
  def snakify
5
- gsub(/::/, "/")
5
+ gsub("::", "/")
6
6
  .gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
7
7
  .gsub(/([a-z\d])([A-Z])/, '\1_\2')
8
8
  .tr("-", "_")
9
9
  .downcase
10
10
  end
11
11
  end
12
+
13
+ class Object
14
+ def object_id_hex = "0x#{object_id.to_s(16).rjust(16, "0")}"
15
+
16
+ def self.memo(name, &)
17
+ define_method(name) do
18
+ @__memoized_variables__ ||= {}
19
+ @__memoized_variables__[name] ||= instance_exec(&)
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ module Entities
5
+ class Attribute
6
+ attr_accessor :name, :source, :definition, :overriding, :parent
7
+
8
+ def initialize(parent, name, model)
9
+ @name = name
10
+ @parent = parent
11
+ @source = model[:source] # TODO
12
+ @definition = AttributeDefinition.new(self, model[:definition])
13
+ @overriding = model[:overriding] # TODO
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ module Entities
5
+ class AttributeDefinition
6
+ attr_reader :defined_by, :doc, :name, :reader_visibility, :writer_visibility, :type, :parent
7
+
8
+ def initialize(parent, model)
9
+ @parent = parent
10
+ @defined_by = model[:defined_by]
11
+ @doc = model[:doc] # TODO
12
+ @name = model[:name]
13
+ @reader_visibility = model[:reader_visibility].to_sym
14
+ @writer_visibility = model[:writer_visibility].to_sym
15
+ @type = model[:type] # TODO
16
+ end
17
+
18
+ def accessor? = reader? && writer?
19
+ def reader? = reader_visibility == :public
20
+ def writer? = writer_visibility == :public
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,58 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ module Entities
5
+ class Base
6
+ attr_accessor :name, :parent, :references
7
+
8
+ def initialize(parent, name)
9
+ @parent = parent
10
+ @name = name
11
+ @references = []
12
+ end
13
+
14
+ def type = raise NotImplementedError, "Classes inheriting Entities::Base must implement #type"
15
+
16
+ def module? = type == :module
17
+
18
+ def class? = type == :class
19
+
20
+ def def? = type == :def
21
+
22
+ def static? = type == :sdef
23
+
24
+ def root
25
+ obj = self
26
+ loop do
27
+ return obj if obj.parent.nil?
28
+
29
+ obj = obj.parent
30
+ end
31
+
32
+ raise "Orphaned object"
33
+ end
34
+
35
+ def inspect = "#<#{self.class.name}:#{object_id_hex} #{name}>"
36
+
37
+ def to_s = inspect
38
+
39
+ def root? = module? && parent.nil?
40
+
41
+ def register_reference(ref)
42
+ @references << ref
43
+ end
44
+
45
+ def full_path
46
+ parents = []
47
+ parent = self.parent
48
+ until parent.root?
49
+ parents << parent
50
+ parent = parent.parent
51
+ end
52
+
53
+ parents.reverse!
54
+ parents << self
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Renderer
4
+ module Entities
5
+ class Class < Container
6
+ attr_accessor :attributes, :inherits
7
+
8
+ def initialize(parent, model)
9
+ super(parent, model)
10
+ @attributes = model.fetch(:attributes, {}).map { |k, v| Attribute.new(self, k.to_s, v) }
11
+ @inherits = init_reference(model[:inherits], :inherits)
12
+ end
13
+
14
+ def type = :class
15
+ end
16
+ end
17
+ end