rtfdoc 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
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