dss_tech_docs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (151) hide show
  1. checksums.yaml +7 -0
  2. data/.editorconfig +29 -0
  3. data/.gitignore +37 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +11 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +9 -0
  8. data/CHANGELOG.md +274 -0
  9. data/CONTRIBUTING.md +9 -0
  10. data/Gemfile +4 -0
  11. data/LICENCE +21 -0
  12. data/README.md +89 -0
  13. data/Rakefile +13 -0
  14. data/docs/configuration.md +205 -0
  15. data/docs/core-layout-without-sidebar.png +0 -0
  16. data/docs/core-layout.png +0 -0
  17. data/docs/expired-page.png +0 -0
  18. data/docs/frontmatter.md +145 -0
  19. data/docs/last-reviewed-only.png +0 -0
  20. data/docs/last-reviewed-only.svg +1 -0
  21. data/docs/layout-layout.png +0 -0
  22. data/docs/not-expired-page.png +0 -0
  23. data/docs/page-expiry.md +85 -0
  24. data/dss_tech_docs.gemspec +46 -0
  25. data/example/.ruby-version +1 -0
  26. data/example/Gemfile +3 -0
  27. data/example/config.rb +9 -0
  28. data/example/config/hide-expiry.yml +51 -0
  29. data/example/config/tech-docs.yml +50 -0
  30. data/example/source/api-path.html.md +7 -0
  31. data/example/source/api-reference.html.md +5 -0
  32. data/example/source/child-of-expired-page.html.md +8 -0
  33. data/example/source/core-layout-without-sidebar.html.md.erb +7 -0
  34. data/example/source/core-layout.html.md.erb +12 -0
  35. data/example/source/expired-page-with-owner.html.md +10 -0
  36. data/example/source/expired-page.html.md +9 -0
  37. data/example/source/headings.html.md +11 -0
  38. data/example/source/index.html.md.erb +19 -0
  39. data/example/source/javascripts/application.js +1 -0
  40. data/example/source/not-expired-page.html.md +9 -0
  41. data/example/source/pets.yml +109 -0
  42. data/example/source/stylesheets/print.css.scss +3 -0
  43. data/example/source/stylesheets/screen-old-ie.css.scss +4 -0
  44. data/example/source/stylesheets/screen.css.scss +1 -0
  45. data/example/source/templates/proxy_template.html.md +8 -0
  46. data/lib/assets/javascripts/_analytics.js +58 -0
  47. data/lib/assets/javascripts/_govuk/modules.js +57 -0
  48. data/lib/assets/javascripts/_modules/anchored-headings.js +18 -0
  49. data/lib/assets/javascripts/_modules/collapsible-navigation.js +95 -0
  50. data/lib/assets/javascripts/_modules/in-page-navigation.js +132 -0
  51. data/lib/assets/javascripts/_modules/navigation.js +34 -0
  52. data/lib/assets/javascripts/_modules/page-expiry.js +15 -0
  53. data/lib/assets/javascripts/_modules/search.js +367 -0
  54. data/lib/assets/javascripts/_modules/table-of-contents.js +111 -0
  55. data/lib/assets/javascripts/_start-modules.js +13 -0
  56. data/lib/assets/javascripts/_vendor/fixedsticky.js +194 -0
  57. data/lib/assets/javascripts/_vendor/jquery.js +5 -0
  58. data/lib/assets/javascripts/_vendor/jquery.mark.js +1081 -0
  59. data/lib/assets/javascripts/_vendor/lodash.js +613 -0
  60. data/lib/assets/javascripts/_vendor/modernizr.js +3 -0
  61. data/lib/assets/javascripts/govuk_tech_docs.js +10 -0
  62. data/lib/assets/stylesheets/_accessibility.scss +9 -0
  63. data/lib/assets/stylesheets/_core.scss +71 -0
  64. data/lib/assets/stylesheets/_fonts.scss +29 -0
  65. data/lib/assets/stylesheets/_govuk_tech_docs.scss +2 -0
  66. data/lib/assets/stylesheets/_syntax-highlighting.scss +196 -0
  67. data/lib/assets/stylesheets/_variables.scss +12 -0
  68. data/lib/assets/stylesheets/govuk_frontend_toolkit/_colours.scss +2 -0
  69. data/lib/assets/stylesheets/govuk_frontend_toolkit/_conditionals.scss +81 -0
  70. data/lib/assets/stylesheets/govuk_frontend_toolkit/_css3.scss +90 -0
  71. data/lib/assets/stylesheets/govuk_frontend_toolkit/_device-pixels.scss +10 -0
  72. data/lib/assets/stylesheets/govuk_frontend_toolkit/_font_stack.scss +19 -0
  73. data/lib/assets/stylesheets/govuk_frontend_toolkit/_grid_layout.scss +136 -0
  74. data/lib/assets/stylesheets/govuk_frontend_toolkit/_helpers.scss +16 -0
  75. data/lib/assets/stylesheets/govuk_frontend_toolkit/_measurements.scss +14 -0
  76. data/lib/assets/stylesheets/govuk_frontend_toolkit/_shims.scss +55 -0
  77. data/lib/assets/stylesheets/govuk_frontend_toolkit/_typography.scss +249 -0
  78. data/lib/assets/stylesheets/govuk_frontend_toolkit/_url-helpers.scss +16 -0
  79. data/lib/assets/stylesheets/govuk_frontend_toolkit/colours/_organisation.scss +103 -0
  80. data/lib/assets/stylesheets/govuk_frontend_toolkit/colours/_palette.scss +77 -0
  81. data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_alpha-beta.scss +66 -0
  82. data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_breadcrumbs.scss +53 -0
  83. data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_buttons.scss +141 -0
  84. data/lib/assets/stylesheets/govuk_frontend_toolkit/design-patterns/_media-player.scss +242 -0
  85. data/lib/assets/stylesheets/modules/_anchored-heading.scss +54 -0
  86. data/lib/assets/stylesheets/modules/_app-pane.scss +64 -0
  87. data/lib/assets/stylesheets/modules/_collapsible.scss +52 -0
  88. data/lib/assets/stylesheets/modules/_contribution-banner.scss +22 -0
  89. data/lib/assets/stylesheets/modules/_footer.scss +130 -0
  90. data/lib/assets/stylesheets/modules/_govuk-logo.scss +47 -0
  91. data/lib/assets/stylesheets/modules/_header.scss +290 -0
  92. data/lib/assets/stylesheets/modules/_page-review.scss +35 -0
  93. data/lib/assets/stylesheets/modules/_phase-banner.scss +22 -0
  94. data/lib/assets/stylesheets/modules/_search.scss +137 -0
  95. data/lib/assets/stylesheets/modules/_skip-link.scss +31 -0
  96. data/lib/assets/stylesheets/modules/_technical-documentation.scss +241 -0
  97. data/lib/assets/stylesheets/modules/_toc.scss +216 -0
  98. data/lib/assets/stylesheets/modules/_warning-text.scss +73 -0
  99. data/lib/assets/stylesheets/palette/_syntax-highlighting.scss +23 -0
  100. data/lib/assets/stylesheets/utilities/_fonts.scss +29 -0
  101. data/lib/assets/stylesheets/utilities/_printable.scss +13 -0
  102. data/lib/assets/stylesheets/vendor/_fixedsticky.scss +22 -0
  103. data/lib/dss_tech_docs.rb +121 -0
  104. data/lib/govuk_tech_docs/api_reference/api_reference_extension.rb +101 -0
  105. data/lib/govuk_tech_docs/api_reference/api_reference_renderer.rb +279 -0
  106. data/lib/govuk_tech_docs/api_reference/templates/api_reference_full.html.erb +19 -0
  107. data/lib/govuk_tech_docs/api_reference/templates/operation.html.erb +11 -0
  108. data/lib/govuk_tech_docs/api_reference/templates/parameters.html.erb +28 -0
  109. data/lib/govuk_tech_docs/api_reference/templates/path.html.erb +4 -0
  110. data/lib/govuk_tech_docs/api_reference/templates/responses.html.erb +33 -0
  111. data/lib/govuk_tech_docs/api_reference/templates/schema.html.erb +29 -0
  112. data/lib/govuk_tech_docs/contribution_banner.rb +62 -0
  113. data/lib/govuk_tech_docs/meta_tags.rb +67 -0
  114. data/lib/govuk_tech_docs/page_review.rb +52 -0
  115. data/lib/govuk_tech_docs/pages.rb +32 -0
  116. data/lib/govuk_tech_docs/redirects.rb +39 -0
  117. data/lib/govuk_tech_docs/table_of_contents/heading.rb +30 -0
  118. data/lib/govuk_tech_docs/table_of_contents/heading_tree.rb +27 -0
  119. data/lib/govuk_tech_docs/table_of_contents/heading_tree_builder.rb +41 -0
  120. data/lib/govuk_tech_docs/table_of_contents/heading_tree_renderer.rb +46 -0
  121. data/lib/govuk_tech_docs/table_of_contents/headings_builder.rb +39 -0
  122. data/lib/govuk_tech_docs/table_of_contents/helpers.rb +79 -0
  123. data/lib/govuk_tech_docs/tech_docs_html_renderer.rb +34 -0
  124. data/lib/govuk_tech_docs/unique_identifier_extension.rb +13 -0
  125. data/lib/govuk_tech_docs/unique_identifier_generator.rb +72 -0
  126. data/lib/govuk_tech_docs/version.rb +3 -0
  127. data/lib/govuk_tech_docs/warning_text_extension.rb +23 -0
  128. data/lib/source/api/pages.json.erb +1 -0
  129. data/lib/source/favicon.ico +0 -0
  130. data/lib/source/images/anchored-heading-icon-2x.png +0 -0
  131. data/lib/source/images/anchored-heading-icon.png +0 -0
  132. data/lib/source/images/gov.uk_logotype_crown-2x.png +0 -0
  133. data/lib/source/images/gov.uk_logotype_crown.png +0 -0
  134. data/lib/source/images/gov.uk_logotype_crown_invert_trans.png +0 -0
  135. data/lib/source/images/govuk-crest-2x.png +0 -0
  136. data/lib/source/images/govuk-crest.png +0 -0
  137. data/lib/source/images/govuk-icn-close.png +0 -0
  138. data/lib/source/images/govuk-icn-close@2x.png +0 -0
  139. data/lib/source/images/govuk-icn-numbered-list.png +0 -0
  140. data/lib/source/images/govuk-icn-numbered-list@2x.png +0 -0
  141. data/lib/source/images/open-government-licence.png +0 -0
  142. data/lib/source/images/open-government-licence_2x.png +0 -0
  143. data/lib/source/images/search-result-caret.svg +13 -0
  144. data/lib/source/layouts/_analytics.erb +15 -0
  145. data/lib/source/layouts/_footer.erb +10 -0
  146. data/lib/source/layouts/_header.erb +44 -0
  147. data/lib/source/layouts/_page_review.erb +22 -0
  148. data/lib/source/layouts/_search.erb +16 -0
  149. data/lib/source/layouts/core.erb +82 -0
  150. data/lib/source/layouts/layout.erb +18 -0
  151. metadata +474 -0
@@ -0,0 +1,19 @@
1
+ <h1 id="<%= info.title.parameterize %>"><%= info.title %> v<%= info.version %></h1>
2
+ <%= markdown(info.description) %>
3
+
4
+ <% unless servers.empty? %>
5
+ <h2 id="servers">Servers</h2>
6
+ <div id="server-list">
7
+ <% servers.each do |server| %>
8
+ <% if server.description %>
9
+ <p><strong><%= server.description %></strong></p>
10
+ <% end %>
11
+ <a href="<%= server.url %>"><%= server.url %></a>
12
+ <% end %>
13
+ </div>
14
+ <% end %>
15
+
16
+ <%= paths %>
17
+
18
+ <h2 id="schemas">Schemas</h2>
19
+ <%= schemas %>
@@ -0,0 +1,11 @@
1
+ <h3 id="<%= id %>"><%= key %></h3>
2
+ <% if operation.summary %>
3
+ <p><em><%= operation.summary %></em></p>
4
+ <% end %>
5
+ <% if operation.description %>
6
+ <p><%= markdown(operation.description) %></p>
7
+ <% end %>
8
+
9
+ <%= parameters %>
10
+
11
+ <%= responses %>
@@ -0,0 +1,28 @@
1
+ <% if parameters.any? %>
2
+ <h4 id="<%= id %>">Parameters</h4>
3
+ <table>
4
+ <thead>
5
+ <tr><th>Parameter</th><th>In</th><th>Type</th><th>Required</th><th>Description</th></tr>
6
+ </thead>
7
+ <tbody>
8
+ <% parameters.each do |parameter| %>
9
+ <tr>
10
+ <td><%= parameter.name %></td>
11
+ <td><%= parameter.in %></td>
12
+ <td><%= parameter.schema.type %></td>
13
+ <td><%= parameter.required? %></td>
14
+ <td><%= markdown(parameter.description) %>
15
+ <% if parameter.schema.enum %>
16
+ <p>Available items:</p>
17
+ <ul>
18
+ <% parameter.schema.enum.each do |item| %>
19
+ <li><%= item %></li>
20
+ <% end %>
21
+ </ul>
22
+ <% end %>
23
+ </td>
24
+ </tr>
25
+ <% end %>
26
+ </tbody>
27
+ </table>
28
+ <% end %>
@@ -0,0 +1,4 @@
1
+ <% if text %>
2
+ <h2 id="<%= id %>"><%= text %></h2>
3
+ <% end %>
4
+ <%= operations %>
@@ -0,0 +1,33 @@
1
+ <% if responses.any? %>
2
+ <h4 id="<%= id %>">Responses</h4>
3
+ <table>
4
+ <thead>
5
+ <tr><th>Status</th><th>Description</th><th>Schema</th></tr>
6
+ </thead>
7
+ <tbody>
8
+ <% responses.each do |key,response| %>
9
+ <tr>
10
+ <td><%= key %></td>
11
+ <td>
12
+ <%= markdown(response.description) %>
13
+ <% if response.content['application/json']
14
+ if response.content['application/json']["example"]
15
+ request_body = json_prettyprint(response.content['application/json']["example"])
16
+ else
17
+ request_body = json_output(response.content['application/json'].schema)
18
+ end
19
+ end %>
20
+ <% if !request_body.blank? %>
21
+ <pre><code><%= request_body %></code></pre>
22
+ <% end %>
23
+ </td>
24
+ <td>
25
+ <%= if response.content['application/json']
26
+ get_schema_link(response.content['application/json'].schema)
27
+ end %>
28
+ </td>
29
+ </tr>
30
+ <% end %>
31
+ </tbody>
32
+ </table>
33
+ <% end %>
@@ -0,0 +1,29 @@
1
+ <h3 id="<%= id = 'schema-' + title; id.parameterize %>"><%= title %></h3>
2
+ <%= markdown(schema.description) %>
3
+ <% if properties.any? %>
4
+ <table>
5
+ <thead>
6
+ <tr><th>Name</th><th>Type</th><th>Required</th><th>Description</th><th>Schema</th></tr>
7
+ </thead>
8
+ <tbody>
9
+ <% properties.each do |property| %>
10
+ <tr>
11
+ <td><%= property[0] %></td>
12
+ <td><%= property[1].type %></td>
13
+ <td><%= property[1].required.present? %></td>
14
+ <td><%= markdown(property[1].description) %></td>
15
+ <td>
16
+ <%=
17
+ schema = property[1]
18
+ # If property is an array, check the items property for a reference.
19
+ if property[1].type == 'array'
20
+ schema = property[1]['items']
21
+ end
22
+ # Only print a link if it's a referenced object.
23
+ get_schema_link(schema) if schema.node_context.referenced_by.to_s.include? '#/components/schemas' and !schema.node_context.source_location.to_s.include? '/properties/' %>
24
+ </td>
25
+ </tr>
26
+ <% end %>
27
+ </tbody>
28
+ </table>
29
+ <% end %>
@@ -0,0 +1,62 @@
1
+ module GovukTechDocs
2
+ # Helper included
3
+ module ContributionBanner
4
+ def source_urls
5
+ SourceUrls.new(current_page, config)
6
+ end
7
+ end
8
+
9
+ class SourceUrls
10
+ attr_reader :current_page, :config
11
+
12
+ def initialize(current_page, config)
13
+ @current_page = current_page
14
+ @config = config
15
+ end
16
+
17
+ def view_source_url
18
+ override_from_page || source_from_yaml_file || source_from_file
19
+ end
20
+
21
+ def report_issue_url
22
+ url = config[:source_urls]&.[](:report_issue_url)
23
+
24
+ if url.nil?
25
+ "#{repo_url}/issues/new?labels=bug&title=Re: '#{current_page.data.title}'&body=Problem with '#{current_page.data.title}' (#{config[:tech_docs][:host]}#{current_page.url})"
26
+ else
27
+ "#{url}?subject=Re: '#{current_page.data.title}'&body=Problem with '#{current_page.data.title}' (#{config[:tech_docs][:host]}#{current_page.url})"
28
+ end
29
+ end
30
+
31
+ def repo_url
32
+ "https://github.com/#{config[:tech_docs][:github_repo]}"
33
+ end
34
+
35
+ def repo_branch
36
+ config[:tech_docs][:github_branch] || 'master'
37
+ end
38
+
39
+ private
40
+
41
+ # If a `page` local exists, see if it has a `source_url`. This is used by the
42
+ # pages that are created by the proxy system because they can't use frontmatter
43
+ def override_from_page
44
+ locals.key?(:page) ? locals[:page].try(:source_url) : false
45
+ end
46
+
47
+ # In the frontmatter we can specify a `source_url`. Use this if the actual
48
+ # source of the page is in another GitHub repo.
49
+ def source_from_yaml_file
50
+ current_page.data.source_url
51
+ end
52
+
53
+ # As the last fallback link to the source file in this repository.
54
+ def source_from_file
55
+ "#{repo_url}/blob/#{repo_branch}/source/#{current_page.file_descriptor[:relative_path]}"
56
+ end
57
+
58
+ def locals
59
+ current_page.metadata[:locals]
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,67 @@
1
+ module GovukTechDocs
2
+ class MetaTags
3
+ def initialize(config, current_page)
4
+ @config = config
5
+ @current_page = current_page
6
+ end
7
+
8
+ def tags
9
+ all_tags = {
10
+ 'description' => page_description,
11
+ 'og:description' => page_description,
12
+ 'og:image' => page_image,
13
+ 'og:site_name' => site_name,
14
+ 'og:title' => page_title,
15
+ 'og:type' => 'object',
16
+ 'og:url' => canonical_url,
17
+ 'twitter:card' => 'summary',
18
+ 'twitter:domain' => URI.parse(host).host,
19
+ 'twitter:image' => page_image,
20
+ 'twitter:title' => browser_title,
21
+ 'twitter:url' => canonical_url,
22
+ }
23
+
24
+ Hash[all_tags.select { |_k, v| v }]
25
+ end
26
+
27
+ def browser_title
28
+ [page_title, site_name].select(&:present?).uniq.join(' | ')
29
+ end
30
+
31
+ def canonical_url
32
+ "#{host}#{current_page.url}"
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :config, :current_page
38
+
39
+ def page_image
40
+ "#{host}/images/govuk-large.png"
41
+ end
42
+
43
+ def site_name
44
+ config[:tech_docs][:full_service_name] || config[:tech_docs][:service_name]
45
+ end
46
+
47
+ def page_description
48
+ locals[:description] || frontmatter.description
49
+ end
50
+
51
+ def page_title
52
+ locals[:title] || frontmatter.title
53
+ end
54
+
55
+ def host
56
+ config[:tech_docs][:host].to_s
57
+ end
58
+
59
+ def locals
60
+ current_page.metadata[:locals]
61
+ end
62
+
63
+ def frontmatter
64
+ current_page.data
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,52 @@
1
+ module GovukTechDocs
2
+ class PageReview
3
+ attr_reader :page
4
+
5
+ def initialize(page, config = {})
6
+ @page = page
7
+ @config = config
8
+ end
9
+
10
+ def review_by
11
+ return unless last_reviewed_on
12
+
13
+ @review_by ||= Chronic.parse(
14
+ "in #{page.data.review_in}",
15
+ now: last_reviewed_on.to_time
16
+ ).to_date
17
+ end
18
+
19
+ def under_review?
20
+ page.data.review_in.present?
21
+ end
22
+
23
+ def last_reviewed_on
24
+ page.data.last_reviewed_on
25
+ end
26
+
27
+ def owner_slack
28
+ page.data.owner_slack || default_owner_slack
29
+ end
30
+
31
+ def owner_slack_url
32
+ return "" unless owner_slack_workspace
33
+ # Slack URLs don't have the # (channels) or @ (usernames)
34
+ slack_identifier = owner_slack.to_s.delete('#').delete('@')
35
+ "https://#{owner_slack_workspace}.slack.com/messages/#{slack_identifier}"
36
+ end
37
+
38
+ def show_expiry?
39
+ @config[:tech_docs].fetch(:show_expiry, true)
40
+ end
41
+
42
+ private
43
+
44
+ def default_owner_slack
45
+ @config[:tech_docs][:default_owner_slack]
46
+ end
47
+
48
+ def owner_slack_workspace
49
+ @config[:tech_docs][:owner_slack_workspace]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,32 @@
1
+ module GovukTechDocs
2
+ class Pages
3
+ attr_reader :sitemap
4
+
5
+ def initialize(sitemap, config)
6
+ @sitemap = sitemap
7
+ @config = config
8
+ end
9
+
10
+ def to_json
11
+ as_json.to_json
12
+ end
13
+
14
+ private
15
+
16
+ def as_json
17
+ pages.map do |page|
18
+ review = PageReview.new(page, @config)
19
+ {
20
+ title: page.data.title,
21
+ url: "#{@config[:tech_docs][:host]}#{page.url}",
22
+ review_by: review.review_by,
23
+ owner_slack: review.owner_slack,
24
+ }
25
+ end
26
+ end
27
+
28
+ def pages
29
+ sitemap.resources.select { |page| page.data.title }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,39 @@
1
+ module GovukTechDocs
2
+ class Redirects
3
+ LEADING_SLASH = %r[\A\/]
4
+
5
+ def initialize(context)
6
+ @context = context
7
+ end
8
+
9
+ def redirects
10
+ all_redirects = redirects_from_config + redirects_from_frontmatter
11
+
12
+ all_redirects.map do |from, to|
13
+ # Middleman needs paths without leading slashes
14
+ [from.sub(LEADING_SLASH, ''), to: to.sub(LEADING_SLASH, '')]
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :context
21
+
22
+ def redirects_from_config
23
+ context.config[:tech_docs][:redirects].to_a
24
+ end
25
+
26
+ def redirects_from_frontmatter
27
+ reds = []
28
+ context.sitemap.resources.each do |page|
29
+ next unless page.data.old_paths
30
+
31
+ page.data.old_paths.each do |old_path|
32
+ reds << [old_path, page.path]
33
+ end
34
+ end
35
+
36
+ reds
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,30 @@
1
+ module GovukTechDocs
2
+ module TableOfContents
3
+ class Heading
4
+ def initialize(element_name:, text:, attributes:, page_url: '')
5
+ @element_name = element_name
6
+ @text = text
7
+ @attributes = attributes
8
+ @page_url = page_url
9
+ end
10
+
11
+ def size
12
+ @element_name.scan(/h(\d)/) && $1 && Integer($1)
13
+ end
14
+
15
+ def href
16
+ @page_url + '#' + @attributes['id']
17
+ end
18
+
19
+ def title
20
+ @text
21
+ end
22
+
23
+ def ==(other)
24
+ @element_name == other.instance_variable_get("@element_name") &&
25
+ @text == other.instance_variable_get("@text") &&
26
+ @attributes == other.instance_variable_get("@attributes")
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,27 @@
1
+ module GovukTechDocs
2
+ module TableOfContents
3
+ class HeadingTree
4
+ attr_accessor :parent, :heading, :children
5
+
6
+ def initialize(parent: nil, heading: nil, children: [])
7
+ @parent = parent
8
+ @heading = heading
9
+ @children = children
10
+ end
11
+
12
+ def depth
13
+ if parent
14
+ parent.depth + 1
15
+ else
16
+ 1
17
+ end
18
+ end
19
+
20
+ def ==(other)
21
+ heading == other.heading &&
22
+ children.length == other.children.length &&
23
+ children.map.with_index { |child, index| child == other.children[index] }.all?
24
+ end
25
+ end
26
+ end
27
+ end