rtfdoc 0.1.3 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9526652b8cb796b8c425a270dbe24db31c72ac74dbc7b747dc467de1b45a9134
4
- data.tar.gz: 8a155561cd6a0a949a872618f09ef694405e4d013fa1a6089467cc5d3f933d9f
3
+ metadata.gz: 347b6ddeab271092954187a8bd379383ce42ab72f8391800e580f75e9c62fcf9
4
+ data.tar.gz: 69b43ac24ce63fd20cdf5359b7e672dc449fa11b234c837c6945ad0334dac4c5
5
5
  SHA512:
6
- metadata.gz: c67015811940e7cda515507725424bb9c7f9fc3b4c618cc86de0232d26762c29a522cdff08cf158f924fd4c7bd2a2693c64246184120b9b6eca5b670323df73f
7
- data.tar.gz: 3eea133c9c2f2672ae869c0afa73bdaa6c871cc012bbb9576f4e554d35080c1069163abad49c572c568607bb8bafa2d0b7e5c51711dc70219b34a1bdf8cf0b46
6
+ metadata.gz: f80c5696294bbd63d9ae9d0deec899288d1124840c805a9ff0d8f1884db15b9885223b84642791dc9d37fe917cb86155bf380576a327ad17745ea414873069b9
7
+ data.tar.gz: abf40c249d97fc2bc9c1fda9e5e06a266acd4539cd011569233c8f91ea9f6a1ee41719c17f12f08331805c5e160e1e8a1ddd88114bd33755f82f15c4b72672ed
@@ -7,6 +7,16 @@ require 'rtfdoc/version'
7
7
 
8
8
  module RTFDoc
9
9
  class AttributesComponent
10
+ # Needed because we can't call the same rendered within itself.
11
+ def self.private_renderer
12
+ @renderer ||= Redcarpet::Markdown.new(::RTFDoc::Renderer, {
13
+ underline: true,
14
+ space_after_headers: true,
15
+ fenced_code_blocks: true,
16
+ no_intra_emphasis: true
17
+ })
18
+ end
19
+
10
20
  def initialize(raw_attrs, title)
11
21
  @attributes = YAML.load(raw_attrs)
12
22
  @title = title
@@ -16,6 +26,10 @@ module RTFDoc
16
26
  class_eval <<-RUBY
17
27
  define_method(:output) { #{template.src} }
18
28
  RUBY
29
+
30
+ def to_html(text)
31
+ self.class.private_renderer.render(text) if text
32
+ end
19
33
  end
20
34
 
21
35
  class Renderer < Redcarpet::Render::Base
@@ -39,6 +53,10 @@ module RTFDoc
39
53
  "<p>#{text}</p>"
40
54
  end
41
55
 
56
+ def link(link, title, content)
57
+ %(<a title="#{title}" href="#{link}">#{content}</a>)
58
+ end
59
+
42
60
  def header(text, level)
43
61
  if level == 4
44
62
  %(<div class="header-table">#{text}</div>)
@@ -113,17 +131,49 @@ module RTFDoc
113
131
  class Template
114
132
  attr_reader :app_name, :page_title
115
133
 
116
- def initialize(sections, config)
117
- @content = sections.map(&:output).join
118
- @menu_content = sections.map(&:menu_output).join
134
+ def initialize(nodes, config)
135
+ @content = nodes.flat_map(&:output).join
136
+ # @menu_content = nodes.map(&:menu_output).join
119
137
  @app_name = config['app_name']
120
138
  @page_title = config['title']
139
+
140
+ generate_grouped_menu_content(nodes)
121
141
  end
122
142
 
123
143
  def output
124
144
  template = Erubi::Engine.new(File.read(File.expand_path('../src/index.html.erb', __dir__)))
125
145
  eval(template.src)
126
146
  end
147
+
148
+ private
149
+
150
+ # Transform a list of nodes into a list of groups. If all nodes already are groups, it will
151
+ # return the same list. Otherwise, it will build group from consecutives single resources.
152
+ def generate_grouped_menu_content(nodes)
153
+ i = 0
154
+ res = []
155
+
156
+ while i < nodes.length
157
+ node = nodes[i]
158
+ if node.is_a?(Group)
159
+ res << node
160
+ i += 1
161
+ else
162
+ inner = []
163
+ j = i
164
+ while node && !node.is_a?(Group)
165
+ inner << node
166
+ j += 1
167
+ node = nodes[j]
168
+ end
169
+
170
+ res << Group.new(nil, inner)
171
+ i = j
172
+ end
173
+ end
174
+
175
+ @menu_content = res.map(&:menu_output).join
176
+ end
127
177
  end
128
178
 
129
179
  module RenderAsSection
@@ -247,16 +297,18 @@ module RTFDoc
247
297
  end
248
298
 
249
299
  def generate_example(sections)
250
- endpoints = sections.reject { |s| s.name == 'desc' || s.name == 'object' }
300
+ endpoints = sections.reject { |s| s.is_a?(Scope) || s.name == 'desc' || s.name == 'object' }
251
301
  signatures = endpoints.each_with_object("") do |e, res|
252
302
  res << %(<div class="resource-sig">#{e.signature}</div>)
253
303
  end
304
+ scopes = sections.select { |s| s.is_a?(Scope) }.map!(&:generate_example).join("\n")
254
305
 
255
306
  @example = <<-HTML
256
307
  <div class="section-response">
257
308
  <div class="response-topbar">ENDPOINTS</div>
258
309
  <div class="section-endpoints">#{signatures}</div>
259
310
  </div>
311
+ #{scopes}
260
312
  HTML
261
313
  end
262
314
 
@@ -273,6 +325,25 @@ module RTFDoc
273
325
  desc = nil
274
326
 
275
327
  sections = endpoints.each_with_object([]) do |endpoint, res|
328
+ if endpoint.is_a?(Hash)
329
+ n, values = endpoint.each_pair.first
330
+ next unless n.start_with?('scope|')
331
+ dir_name = n.slice(6..-1)
332
+
333
+ scope_name = values['title'] || dir_name
334
+ scoped_endpoints = values['endpoints']
335
+
336
+ subsections = scoped_endpoints.each_with_object([]) do |e, r|
337
+ filename = paths.dig(dir_name, e)
338
+ next unless filename
339
+ content = File.read(filename)
340
+ r << Section.new(e, content, resource: name)
341
+ end
342
+
343
+ res << Scope.new(scope_name, subsections)
344
+ next res
345
+ end
346
+
276
347
  filename = paths[endpoint]
277
348
  next unless filename
278
349
 
@@ -300,7 +371,7 @@ module RTFDoc
300
371
  head, *tail = sections
301
372
  head.include_show_button = true
302
373
 
303
- inner = sections.map(&:output).join("\n")
374
+ inner = sections.flat_map(&:output).join("\n")
304
375
  %(<section class="head-section">#{inner}</section>)
305
376
  end
306
377
 
@@ -308,11 +379,80 @@ module RTFDoc
308
379
  head, *tail = sections
309
380
  <<-HTML
310
381
  <li data-anchor="#{name}">
311
- #{head.anchor(name.capitalize, class_list: 'expandable')}
382
+ #{head.anchor(human_name, class_list: 'expandable')}
312
383
  <ul>#{tail.map(&:menu_output).join}</ul>
313
384
  </li>
314
385
  HTML
315
386
  end
387
+
388
+ private
389
+
390
+ def human_name
391
+ name.tr('_', ' ').split.map!(&:capitalize).join(' ')
392
+ end
393
+ end
394
+
395
+ class Group
396
+ attr_reader :name, :resources
397
+
398
+ def initialize(name, resources, options = {})
399
+ @name = name
400
+ @resources = resources
401
+
402
+ sorted = true
403
+ sorted = options['sort'] if options.key?('sort')
404
+ @resources.sort! { |a, b| a.name <=> b.name } if sorted
405
+ end
406
+
407
+ def output
408
+ resources.map(&:output)
409
+ end
410
+
411
+ def menu_output
412
+ title = "<h5 class=\"nav-group-title\">#{name}</h5>" if name && name.length > 0
413
+
414
+ <<-HTML
415
+ <div class="sidebar-nav-group">
416
+ #{title}
417
+ <ul>#{resources.map(&:menu_output).join}</ul>
418
+ </div>
419
+ HTML
420
+ end
421
+ end
422
+
423
+ class Scope
424
+ attr_reader :name, :sections
425
+
426
+ def initialize(name, sections)
427
+ @name = name
428
+ @sections = sections
429
+ end
430
+
431
+ def output
432
+ sections.map(&:output)
433
+ end
434
+
435
+ def menu_output
436
+ <<-HTML
437
+ <li>
438
+ <div class="scope-title">#{name}</div>
439
+ <ul class="scoped">#{sections.map(&:menu_output).join}</ul>
440
+ </li>
441
+ HTML
442
+ end
443
+
444
+ def generate_example
445
+ signatures = sections.each_with_object("") do |s, res|
446
+ res << %(<div class="resource-sig">#{s.signature}</div>)
447
+ end
448
+
449
+ <<-HTML
450
+ <div class="section-response">
451
+ <div class="response-topbar">#{name} ENDPOINTS</div>
452
+ <div class="section-endpoints">#{signatures}</div>
453
+ </div>
454
+ HTML
455
+ end
316
456
  end
317
457
 
318
458
  class Generator
@@ -325,18 +465,8 @@ module RTFDoc
325
465
  end
326
466
 
327
467
  def run
328
- tree = build_content_tree
329
-
330
- nodes = config['resources'].map do |rs|
331
- if rs.is_a?(Hash)
332
- name, endpoints = rs.each_pair.first
333
- paths = tree[name]
334
- Resource.build(name, paths, endpoints: endpoints)
335
- else
336
- paths = tree[rs]
337
- paths.is_a?(Hash) ? Resource.build(rs, paths) : Section.new(rs, File.read(paths))
338
- end
339
- end
468
+ @tree = build_content_tree
469
+ nodes = build_nodes(config['resources'])
340
470
 
341
471
  out = File.new("#{Dir.tmpdir}/rtfdoc_output.html", 'w')
342
472
  out.write(Template.new(nodes, config).output)
@@ -345,6 +475,27 @@ module RTFDoc
345
475
 
346
476
  private
347
477
 
478
+ def build_nodes(ary, allow_groups: true)
479
+ ary.map do |rs|
480
+ if rs.is_a?(Hash)
481
+ name, values = rs.each_pair.first
482
+
483
+ if name.start_with?('group|')
484
+ raise 'Nested groups are not yet supported' if !allow_groups
485
+
486
+ group_name = values.key?('title') ? values['title'] : name.slice(6..-1)
487
+ Group.new(group_name, build_nodes(values['resources'], allow_groups: false), values)
488
+ else
489
+ paths = @tree[name]
490
+ Resource.build(name, paths, endpoints: values)
491
+ end
492
+ else
493
+ paths = @tree[rs]
494
+ paths.is_a?(Hash) ? Resource.build(rs, paths) : Section.new(rs, File.read(paths))
495
+ end
496
+ end
497
+ end
498
+
348
499
  def build_content_tree
349
500
  tree = {}
350
501
  slicer = (@content_dir.length + 1)..-1
@@ -1,3 +1,3 @@
1
1
  module RTFDoc
2
- VERSION = "0.1.3"
2
+ VERSION = "0.1.4"
3
3
  end
@@ -1,5 +1,4 @@
1
1
  <div class="section-list">
2
- <h5 class="section-list-title"><%= @title.capitalize %></h5>
3
2
  <ul>
4
3
  <% @attributes.each do |name, hash| %>
5
4
  <li class="list-item">
@@ -11,7 +10,7 @@
11
10
  <% end %>
12
11
  </div>
13
12
  <div class="list-item-description">
14
- <p><%= hash['desc'] %></p>
13
+ <%= to_html(hash['desc']) %>
15
14
  </div>
16
15
  <% if hash['children']&.any? %>
17
16
  <div class="section-list section-list-child">
@@ -34,7 +33,7 @@
34
33
  <div class="list-item-type"><%= child_hash['type'] %> </div>
35
34
  </div>
36
35
  <div class="list-item-description">
37
- <p><%= child_hash['desc'] %></p>
36
+ <%= to_html(child_hash['desc']) %>
38
37
  </div>
39
38
  </li>
40
39
  <% end %>
@@ -17,6 +17,7 @@
17
17
  --content-width: calc(100vw - var(--sidebar-width));
18
18
  --section-padding: 5vw;
19
19
  --example-width: 40vw;
20
+ --max-example-width: 33vw;
20
21
  --sp20: 20px;
21
22
  --sp24: 24px;
22
23
  --monospace-font: "SFMono-Regular", "Consolas", "Menlo", monospace;
@@ -30,6 +31,7 @@
30
31
  --table-font-size: 14px;
31
32
  --table-line-height: 18px;
32
33
  --code-font-size: 14px;
34
+ --code-block-font-size: 13px;
33
35
  }
34
36
 
35
37
  * {
@@ -80,13 +82,15 @@ body[data-theme="dark"] {
80
82
  --nav-color: var(--sail-gray300);
81
83
  --nav-hover-color: var(--sail-gray50);
82
84
  --nav-header-color: var(--sail-gray50);
83
- --brand-color: var(--sail-cyan500);
84
- --switch-background-color: #212d63;
85
+ --brand-color: #4db7e8;
86
+ --switch-background-color: #3d4eac;
85
87
  --code-strings: #98c379;
86
88
  --code-numbers: #56b6c2;
87
89
  --code-booleans: #d19a66;
88
90
  --child-list-title-color: var(--sail-gray500);
89
91
  --child-list-title-hover-color: var(--sail-gray100);
92
+ --scoped-endpoints-title-color: var(--sail-gray50);
93
+ --link-color: #4db7e8;
90
94
  }
91
95
 
92
96
  body[data-theme="light"] {
@@ -121,6 +125,18 @@ body[data-theme="light"] {
121
125
  --code--booleans: #e36209;
122
126
  --child-list-title-color: #586069;
123
127
  --child-list-title-hover-color: #24292e;
128
+ --scoped-endpoints-title-color: var(--sail-cyan500);
129
+ --link-color: var(--sail-cyan500);
130
+ }
131
+
132
+ a, a:visited {
133
+ color: var(--link-color);
134
+ text-decoration: none;
135
+ }
136
+
137
+ a:hover {
138
+ color: var(--nav-hover-color);
139
+ text-decoration: none;
124
140
  }
125
141
 
126
142
  #sidebar {
@@ -151,26 +167,69 @@ body[data-theme="light"] {
151
167
  }
152
168
 
153
169
  .sidebar-nav {
154
- margin-top: 16px; }
155
- .sidebar-nav a {
156
- text-decoration: none;
157
- color: var(--nav-color);
158
- transition: color .2s ease;
159
- font-size: var(--nav-font-size);
160
- font-weight: 500;
161
- display: block;
162
- padding: 5px 16px; }
163
- .sidebar-nav a:hover {
164
- color: var(--nav-hover-color); }
165
- .sidebar-nav a.current span {
166
- border-bottom: 2px solid var(--brand-color); }
167
- .sidebar-nav li > ul {
168
- display: none;
169
- padding-left: 16px;
170
- transition: all 2s ease; }
171
- .sidebar-nav li.expanded > ul,
172
- .sidebar-nav li.current > ul {
173
- display: block; }
170
+ margin-top: 16px;
171
+ width: 100%;
172
+ max-height: 570px;
173
+ overflow-y: scroll;
174
+ }
175
+
176
+ .sidebar-nav-group {
177
+ padding-bottom: 12px;
178
+
179
+ h5 {
180
+ text-transform: uppercase;
181
+ margin: 0;
182
+ padding: 16px 16px 8px 16px;
183
+ color: var(--title-color);
184
+ font-size: 13px;
185
+ font-weight: 600;
186
+ }
187
+ }
188
+
189
+ .sidebar-nav a {
190
+ text-decoration: none;
191
+ color: var(--nav-color);
192
+ transition: color .2s ease;
193
+ font-size: var(--nav-font-size);
194
+ font-weight: 500;
195
+ display: block;
196
+ padding: 5px 16px;
197
+ }
198
+
199
+ .sidebar-nav a:hover {
200
+ color: var(--nav-hover-color);
201
+ }
202
+
203
+ .sidebar-nav a.current span {
204
+ border-bottom: 2px solid var(--brand-color);
205
+ }
206
+
207
+ .sidebar-nav li > ul {
208
+ display: none;
209
+ padding-left: 16px;
210
+ transition: all 2s ease;
211
+ }
212
+
213
+ .sidebar-nav li.expanded {
214
+ margin-bottom: 10px;
215
+ }
216
+
217
+ .sidebar-nav li.expanded > ul,
218
+ .sidebar-nav li.current > ul {
219
+ display: block;
220
+ }
221
+
222
+ .sidebar-nav li > ul.scoped {
223
+ display: block;
224
+ }
225
+
226
+ .sidebar-nav .scope-title {
227
+ font-size: 13px;
228
+ color: var(--scoped-endpoints-title-color);
229
+ text-transform: uppercase;
230
+ font-weight: 400;
231
+ padding: 8px 0 3px 16px;
232
+ }
174
233
 
175
234
  /*Simple css to style it like a toggle switch*/
176
235
  .theme-switch-wrapper {
@@ -302,18 +361,21 @@ section > section .section-divider {
302
361
  .section-body {
303
362
  flex-shrink: 0;
304
363
  width: calc(var(--example-width) - var(--section-padding));
305
- margin-right: var(--section-padding); }
364
+ margin-right: var(--section-padding);
365
+
366
+ a { font-weight: 500; }
367
+ }
306
368
 
307
369
  .section-example {
308
370
  position: sticky;
309
371
  align-self: flex-start;
310
372
  top: 0;
311
373
  flex-grow: 1;
312
- max-width: 600px;
374
+ max-width: var(--max-example-width);
313
375
  padding-top: calc(40px + var(--sp20)); }
314
376
 
315
377
  p {
316
- margin-top: var(--sp20);
378
+ margin-top: 16px;
317
379
  font-size: var(--main-font-size);
318
380
  line-height: var(--main-line-height);
319
381
  font-weight: 400;
@@ -345,7 +407,10 @@ ul {
345
407
  border-radius: 8px;
346
408
  box-shadow: 0 0 0 1px var(--response-shadow-color);
347
409
  max-height: 83vh;
348
- overflow-y: auto;
410
+ overflow: hidden;
411
+ margin-bottom: 24px;
412
+
413
+ &:last-child { margin-bottom: 0 }
349
414
 
350
415
  .response-topbar {
351
416
  background: var(--block-header-background-color);
@@ -356,7 +421,7 @@ ul {
356
421
  border-radius: 8px 8px 0 0;
357
422
  text-transform: uppercase;
358
423
 
359
- .endpoint-def .method { width: auto }
424
+ .endpoint-def { overflow-y: auto; }
360
425
  }
361
426
 
362
427
  p {
@@ -372,9 +437,13 @@ ul {
372
437
  font-size: var(--code-font-size);
373
438
  font-weight: 400;
374
439
  line-height: 18px;
440
+ max-height: 75vh;
441
+ overflow: auto;
375
442
  }
376
443
  }
377
444
 
445
+
446
+
378
447
  .endpoint-def {
379
448
  display: flex;
380
449
  flex-direction: row;
@@ -386,7 +455,6 @@ ul {
386
455
 
387
456
  .method {
388
457
  text-align: right;
389
- width: 55px;
390
458
  margin-right: 7px;
391
459
  text-transform: uppercase;
392
460
  }
@@ -404,6 +472,7 @@ ul {
404
472
 
405
473
  .section-endpoints {
406
474
  padding: 12px 22px;
475
+ overflow-y: auto;
407
476
 
408
477
  a { text-decoration: none; }
409
478
  a:hover .method {
@@ -412,28 +481,27 @@ ul {
412
481
  a:hover .path {
413
482
  color: var(--endpoints-def-hover-color);
414
483
  }
415
- }
416
484
 
417
- h3 {
418
- margin-top: 20px;
419
- padding-top: 16px;
485
+ .resource-sig .method {
486
+ width: 55px;
487
+ min-width: 55px;
488
+ }
420
489
  }
421
490
 
422
- h3,
423
- .section-list .section-list-title {
491
+
492
+ h3 {
424
493
  font-size: 16px;
425
494
  font-weight: 500;
426
- padding-bottom: 12px;
495
+ padding: 24px 0 10px;
427
496
  border-bottom: 1px solid var(--divider-color);
428
497
  display: flex;
429
498
  align-items: baseline;
430
499
  color: var(--title-color);
431
500
  margin: 0;
432
- margin-top: var(--sp20);
433
- width: 100%; }
501
+ margin-top: 16px;
502
+ width: 100%;
503
+ }
434
504
 
435
- .section-list {
436
- margin-top: 32px; }
437
505
  .section-list .list-item {
438
506
  padding-top: 16px;
439
507
  padding-bottom: 15px;
@@ -544,12 +612,17 @@ p code {
544
612
  border: 1px solid var(--inline-code-border);
545
613
  padding: 2px 3px 0;
546
614
  margin: 0 1px;
547
- font-size: var(--code-font-size);
615
+ font-size: .9em;
548
616
  font-family: var(--monospace-font);
549
617
  font-weight: 500;
550
618
  background: var(--inline-code-background); }
551
619
 
552
620
  /** Code highlighting **/
621
+ pre code {
622
+ font-family: var(--monospace-font);
623
+ font-size: var(--code-block-font-size);
624
+ }
625
+
553
626
  pre code,
554
627
  pre code .w {
555
628
  color: var(--code-color); }
@@ -23,7 +23,7 @@
23
23
  </div>
24
24
  </div>
25
25
  <nav class="sidebar-nav" role="navigation">
26
- <ul><%= @menu_content %></ul>
26
+ <%= @menu_content %>
27
27
  </nav>
28
28
  </div>
29
29
  <div id="content" class="content-area">
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rtfdoc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - ccocchi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-08-17 00:00:00.000000000 Z
11
+ date: 2021-01-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: erubi