trifle-docs 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bfae89cd75902acfa8a8b3c23e082e9820e6af13f05a8f69c0e955ae0fe65f56
4
- data.tar.gz: db34713f816c8ad860930a85ce198654fcb1adbe91716a308db3a8b11c941fb9
3
+ metadata.gz: 9f51e6c6b892eaca5b790d652ccb970146c9e98b624cf3353c84af3ffb95ed4c
4
+ data.tar.gz: f5058c9fa39c81a635db554fabffaab5535f57a76a078138319df0e72732cd49
5
5
  SHA512:
6
- metadata.gz: ec86cf47abf1b3d8534ae6fce439d353e694fb443c37ac59753e6ca18f7f72330b007e1bdcc1390f0f6b87e3ea89b41ef41db85816059eceb54716bb49ef6d43
7
- data.tar.gz: c2775f303aec6dfa94a2071fc1722a932d996d907aeb767f58269ee8060d86d5b49b94c42ce67c28a7060175e365434756b446f3b64896ed02e434d825bfc40e
6
+ metadata.gz: 48ce9ec16b882be46f90825f0e12bb13c783fa16e5b22f5eae27551418a5e58d112a321496fdf46810474c68de5f8933661931a696b6378bc836ffd7a8e74192
7
+ data.tar.gz: 9721a213e1f2cef167e0adf4ed18025b0740ba6594e26f6f0f326e4e6412631f5957447f503cd2ca45c4ce4e2df5efb497a15a8aaf2943a29b246a2c373869a8
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- trifle-docs (0.6.0)
4
+ trifle-docs (0.7.0)
5
5
  redcarpet
6
6
  rouge
7
7
  sinatra
@@ -27,9 +27,7 @@ module Trifle
27
27
 
28
28
  get '/llms.txt' do
29
29
  meta = Trifle::Docs.meta(url: 'llms.txt')
30
- if meta && meta['type'] == 'file'
31
- return send_file(meta['path'])
32
- end
30
+ return send_file(meta['path']) if meta && meta['type'] == 'file'
33
31
 
34
32
  content = Trifle::Docs::Helper::Llms.homepage_markdown
35
33
  halt(404, 'Not Found') if content.nil?
@@ -40,9 +38,7 @@ module Trifle
40
38
 
41
39
  get '/llms-full.txt' do
42
40
  meta = Trifle::Docs.meta(url: 'llms-full.txt')
43
- if meta && meta['type'] == 'file'
44
- return send_file(meta['path'])
45
- end
41
+ return send_file(meta['path']) if meta && meta['type'] == 'file'
46
42
 
47
43
  content = Trifle::Docs::Helper::Llms.full_markdown
48
44
  halt(404, 'Not Found') if content.nil? || content.strip.empty?
@@ -51,32 +47,70 @@ module Trifle
51
47
  content
52
48
  end
53
49
 
50
+ get '/sitemap.xml' do
51
+ meta = Trifle::Docs.meta(url: 'sitemap.xml')
52
+ return send_file(meta['path']) if meta && meta['type'] == 'file'
53
+
54
+ render_generated_sitemap
55
+ end
56
+
54
57
  get '/*' do
58
+ handle_request(params, request)
59
+ end
60
+
61
+ def markdown_requested?(request, params)
62
+ return true if params['format'].to_s.downcase == 'md'
63
+
64
+ accept = request.env['HTTP_ACCEPT'].to_s
65
+ accept.include?('text/markdown')
66
+ end
67
+
68
+ def render_markdown?(meta, request, params)
69
+ return false if meta['type'] == 'file'
70
+
71
+ markdown_requested?(request, params) ||
72
+ Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent)
73
+ end
74
+
75
+ def handle_request(params, request)
55
76
  url = params['splat'].first.chomp('/')
56
77
  meta = Trifle::Docs.meta(url: url)
57
78
  halt(404, 'Not Found') if meta.nil?
58
79
 
59
- if Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent) && meta['type'] != 'file'
60
- content_type 'text/markdown'
61
- return Trifle::Docs::Helper::MarkdownLayout.render(
62
- meta: meta,
63
- raw_content: Trifle::Docs.raw_content(url: url),
64
- sitemap: Trifle::Docs.sitemap
65
- )
66
- end
67
-
68
- if meta['type'] == 'file'
69
- send_file meta['path']
70
- else
71
- erb (meta['template'] || 'page').to_sym, {}, {
72
- sitemap: Trifle::Docs.sitemap,
73
- collection: Trifle::Docs.collection(url: url),
74
- content: Trifle::Docs.content(url: url),
75
- meta: meta,
76
- url: url
77
- }
78
- end
80
+ return render_markdown(meta, url) if render_markdown?(meta, request, params)
81
+ return send_file(meta['path']) if meta['type'] == 'file'
82
+
83
+ render_html(meta, url)
84
+ end
85
+
86
+ def render_markdown(meta, url)
87
+ content_type 'text/markdown'
88
+ Trifle::Docs::Helper::MarkdownLayout.render(
89
+ meta: meta,
90
+ raw_content: Trifle::Docs.raw_content(url: url),
91
+ sitemap: Trifle::Docs.sitemap
92
+ )
93
+ end
94
+
95
+ def render_html(meta, url)
96
+ erb (meta['template'] || 'page').to_sym, {}, {
97
+ sitemap: Trifle::Docs.sitemap,
98
+ collection: Trifle::Docs.collection(url: url),
99
+ content: Trifle::Docs.content(url: url),
100
+ meta: meta,
101
+ url: url
102
+ }
79
103
  end
104
+
105
+ def render_generated_sitemap
106
+ content = Trifle::Docs::Helper::Sitemap.xml
107
+ halt(404, 'Not Found') if content.nil? || content.strip.empty?
108
+
109
+ content_type 'application/xml'
110
+ content
111
+ end
112
+
113
+ private :handle_request
80
114
  end
81
115
  end
82
116
  end
@@ -19,79 +19,35 @@ if Object.const_defined?('Rails')
19
19
  root to: 'page#show'
20
20
  get 'llms.txt', to: 'page#llms'
21
21
  get 'llms-full.txt', to: 'page#llms_full'
22
+ get 'sitemap.xml', to: 'page#sitemap'
22
23
  get 'search', to: 'page#search'
23
24
  get '*url', to: 'page#show'
24
25
  end
25
26
  end
26
27
  end
27
28
 
28
- class PageController < ActionController::Base
29
- layout :docs_layout
30
-
31
- def configuration
32
- params[:configuration] || Trifle::Docs.default
33
- end
29
+ module PageControllerHelpers
30
+ private
34
31
 
35
- def docs_layout
36
- "layouts/trifle/docs/#{configuration.layout}"
37
- end
38
-
39
- def show # rubocop:disable Metrics/AbcSize
40
- url = [params[:url], params[:format]].compact.join('.')
32
+ def render_llms(url, allow_empty: false)
41
33
  meta = Trifle::Docs.meta(url: url, config: configuration)
42
- render_not_found and return if meta.nil?
43
-
44
- if Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent) && meta['type'] != 'file'
45
- render_markdown(url: url, meta: meta)
46
- return
47
- end
48
- render_file(meta: meta) and return if meta['type'] == 'file'
49
-
50
- render_content(url: url, meta: meta)
51
- end
52
-
53
- def search
54
- results = Trifle::Docs.search(query: params[:query], scope: params[:scope])
55
-
56
- render 'search', locals: {
57
- results: results,
58
- query: params[:query],
59
- scope: params[:scope],
60
- sitemap: Trifle::Docs.sitemap,
61
- meta: { description: 'Search' }
62
- }
63
- end
64
-
65
- def llms
66
- meta = Trifle::Docs.meta(url: 'llms.txt', config: configuration)
67
- if meta && meta['type'] == 'file'
68
- send_file(meta['path'])
69
- return
70
- end
34
+ return send_file(meta['path']) if meta && meta['type'] == 'file'
71
35
 
72
- content = Trifle::Docs::Helper::Llms.homepage_markdown(config: configuration)
73
- if content.nil?
74
- render_not_found
75
- return
76
- end
36
+ content = yield
37
+ return render_not_found if content.nil?
38
+ return render_not_found if !allow_empty && content.strip.empty?
77
39
 
78
40
  render plain: content, content_type: 'text/markdown'
79
41
  end
80
42
 
81
- def llms_full
82
- meta = Trifle::Docs.meta(url: 'llms-full.txt', config: configuration)
83
- if meta && meta['type'] == 'file'
84
- send_file(meta['path'])
85
- return
86
- end
43
+ def render_sitemap(url)
44
+ meta = Trifle::Docs.meta(url: url, config: configuration)
45
+ return send_file(meta['path']) if meta && meta['type'] == 'file'
87
46
 
88
- content = Trifle::Docs::Helper::Llms.full_markdown(config: configuration)
89
- if content.nil? || content.strip.empty?
90
- render_not_found
91
- return
92
- end
47
+ content = yield
48
+ return render_not_found if content.nil? || content.strip.empty?
93
49
 
94
- render plain: content, content_type: 'text/markdown'
50
+ render plain: content, content_type: 'application/xml'
95
51
  end
96
52
 
97
53
  def render_not_found
@@ -119,6 +75,93 @@ if Object.const_defined?('Rails')
119
75
  sitemap: Trifle::Docs.sitemap(config: configuration)
120
76
  ), content_type: 'text/markdown'
121
77
  end
78
+
79
+ def fetch_meta(url)
80
+ Trifle::Docs.meta(url: url, config: configuration)
81
+ end
82
+
83
+ def render_for_meta(meta, url, wants_md, request)
84
+ return render_markdown(url: url, meta: meta) if render_markdown?(meta, wants_md, request)
85
+ return render_file(meta: meta) if file_meta?(meta)
86
+
87
+ render_content(url: url, meta: meta)
88
+ end
89
+
90
+ def file_meta?(meta)
91
+ meta['type'] == 'file'
92
+ end
93
+
94
+ def resolve_url(params)
95
+ raw_url = params[:url]
96
+ format = params[:format]
97
+ wants_md = markdown_requested?(format, request)
98
+ url = wants_md ? raw_url : [raw_url, format].compact.join('.')
99
+ [url, wants_md]
100
+ end
101
+
102
+ def render_markdown?(meta, wants_md, request)
103
+ return false if meta['type'] == 'file'
104
+
105
+ wants_md || Trifle::Docs::Helper::AiDetection.ai_scraper?(request.user_agent)
106
+ end
107
+
108
+ def markdown_requested?(format, request)
109
+ return true if format.to_s.downcase == 'md'
110
+
111
+ request.headers['Accept'].to_s.include?('text/markdown')
112
+ end
113
+ end
114
+
115
+ class PageController < ActionController::Base
116
+ include PageControllerHelpers
117
+
118
+ layout :docs_layout
119
+
120
+ def configuration
121
+ params[:configuration] || Trifle::Docs.default
122
+ end
123
+
124
+ def docs_layout
125
+ "layouts/trifle/docs/#{configuration.layout}"
126
+ end
127
+
128
+ def show
129
+ url, wants_md = resolve_url(params)
130
+ meta = fetch_meta(url)
131
+ return render_not_found if meta.nil?
132
+
133
+ render_for_meta(meta, url, wants_md, request)
134
+ end
135
+
136
+ def search
137
+ results = Trifle::Docs.search(query: params[:query], scope: params[:scope])
138
+
139
+ render 'search', locals: {
140
+ results: results,
141
+ query: params[:query],
142
+ scope: params[:scope],
143
+ sitemap: Trifle::Docs.sitemap,
144
+ meta: { description: 'Search' }
145
+ }
146
+ end
147
+
148
+ def llms
149
+ render_llms('llms.txt', allow_empty: true) do
150
+ Trifle::Docs::Helper::Llms.homepage_markdown(config: configuration)
151
+ end
152
+ end
153
+
154
+ def llms_full
155
+ render_llms('llms-full.txt') do
156
+ Trifle::Docs::Helper::Llms.full_markdown(config: configuration)
157
+ end
158
+ end
159
+
160
+ def sitemap
161
+ render_sitemap('sitemap.xml') do
162
+ Trifle::Docs::Helper::Sitemap.xml(config: configuration)
163
+ end
164
+ end
122
165
  end
123
166
  end
124
167
  end
@@ -49,20 +49,25 @@ module Trifle
49
49
  end
50
50
 
51
51
  def format_page(meta:, url:, raw_content:)
52
- title = meta['title'] || Trifle::Docs::Helper::MarkdownLayout.derive_title_from_url(url)
53
- page_url = meta['url'] || "/#{url}"
54
-
55
- lines = []
56
- lines << "# #{title}"
57
- lines << ''
58
- lines << "Source: #{page_url}"
59
- lines << ''
52
+ lines = page_header(meta: meta, url: url)
60
53
  lines << raw_content.to_s.strip
61
54
  lines << ''
62
55
  lines << '---'
63
56
 
64
57
  lines.join("\n")
65
58
  end
59
+
60
+ def page_header(meta:, url:)
61
+ title = meta['title'] || Trifle::Docs::Helper::MarkdownLayout.derive_title_from_url(url)
62
+ page_url = meta['url'] || "/#{url}"
63
+
64
+ [
65
+ "# #{title}",
66
+ '',
67
+ "Source: #{page_url}",
68
+ ''
69
+ ]
70
+ end
66
71
  end
67
72
  end
68
73
  end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'cgi'
4
+ require 'time'
5
+
6
+ module Trifle
7
+ module Docs
8
+ module Helper
9
+ module Sitemap
10
+ module_function
11
+
12
+ def xml(config: nil)
13
+ sitemap = Trifle::Docs.sitemap(config: config)
14
+ urls = sitemap_urls(sitemap)
15
+ return nil if urls.empty?
16
+
17
+ build_document(urls)
18
+ end
19
+
20
+ def flatten_sitemap(node, path = [])
21
+ return [] unless node.is_a?(Hash)
22
+
23
+ entries = []
24
+ meta = node['_meta']
25
+ entries << { url: path.join('/'), meta: meta } if meta
26
+
27
+ node.keys.reject { |key| key == '_meta' }.sort.each do |key|
28
+ entries.concat(flatten_sitemap(node[key], path + [key]))
29
+ end
30
+
31
+ entries
32
+ end
33
+
34
+ def sitemap_urls(sitemap)
35
+ entries = flatten_sitemap(sitemap)
36
+ entries.filter_map do |entry|
37
+ meta = entry[:meta]
38
+ next if meta.nil? || meta['type'] == 'file'
39
+
40
+ build_url_entry(entry[:url], meta)
41
+ end
42
+ end
43
+
44
+ def build_document(urls)
45
+ [
46
+ %(<?xml version="1.0" encoding="UTF-8"?>),
47
+ %(<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">),
48
+ urls.join("\n"),
49
+ %(</urlset>)
50
+ ].join("\n")
51
+ end
52
+
53
+ def build_url_entry(url, meta)
54
+ loc = normalize_loc(url, meta)
55
+ lastmod = format_lastmod(meta['updated_at'])
56
+ lastmod_tag = lastmod ? "<lastmod>#{lastmod}</lastmod>" : ''
57
+
58
+ "<url><loc>#{loc}</loc>#{lastmod_tag}</url>"
59
+ end
60
+
61
+ def normalize_loc(url, meta)
62
+ loc = meta['url']
63
+ loc = "/#{url}" if loc.nil? || loc.empty?
64
+ loc = '/' if loc == '//'
65
+ CGI.escapeHTML(loc)
66
+ end
67
+
68
+ def format_lastmod(value)
69
+ return nil if value.nil?
70
+
71
+ return value.utc.iso8601 if value.respond_to?(:utc) && value.respond_to?(:iso8601)
72
+ return value.iso8601 if value.respond_to?(:iso8601)
73
+
74
+ nil
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Trifle
4
4
  module Docs
5
- VERSION = '0.6.0'
5
+ VERSION = '0.7.0'
6
6
  end
7
7
  end
data/lib/trifle/docs.rb CHANGED
@@ -5,6 +5,7 @@ require_relative 'docs/helper/tree'
5
5
  require_relative 'docs/helper/markdown_layout'
6
6
  require_relative 'docs/helper/ai_detection'
7
7
  require_relative 'docs/helper/llms'
8
+ require_relative 'docs/helper/sitemap'
8
9
  require_relative 'docs/harvester'
9
10
  require_relative 'docs/harvester/file'
10
11
  require_relative 'docs/harvester/markdown'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: trifle-docs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jozef Vaclavik
@@ -203,6 +203,7 @@ files:
203
203
  - lib/trifle/docs/helper/ai_detection.rb
204
204
  - lib/trifle/docs/helper/llms.rb
205
205
  - lib/trifle/docs/helper/markdown_layout.rb
206
+ - lib/trifle/docs/helper/sitemap.rb
206
207
  - lib/trifle/docs/helper/tree.rb
207
208
  - lib/trifle/docs/operations/collection.rb
208
209
  - lib/trifle/docs/operations/content.rb