jekyll-baserow-headless-cms 0.1.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 +7 -0
- data/.claude/settings.json +12 -0
- data/.mcp.json +68 -0
- data/.rspec +3 -0
- data/.rubocop.yml +87 -0
- data/CHANGELOG.md +38 -0
- data/Gemfile +10 -0
- data/LICENSE +21 -0
- data/README.md +291 -0
- data/Rakefile +26 -0
- data/docs/EXAMPLES_AND_CONFIGURATION.md +407 -0
- data/docs/architecture.excalidraw +3979 -0
- data/docs/architecture.png +0 -0
- data/docs/architecture.svg +5 -0
- data/docs/templates/github-actions_baserow-sync.yml +61 -0
- data/lib/jekyll-baserow-headless-cms.rb +14 -0
- data/lib/jekyll_baserow_headless_cms/baserow_client.rb +106 -0
- data/lib/jekyll_baserow_headless_cms/data_organizers.rb +249 -0
- data/lib/jekyll_baserow_headless_cms/field_extractors.rb +182 -0
- data/lib/jekyll_baserow_headless_cms/generator.rb +178 -0
- data/lib/jekyll_baserow_headless_cms/version.rb +5 -0
- metadata +189 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module JekyllBaserowHeadlessCMS
|
|
7
|
+
# Jekyll Generator that fetches data from Baserow tables
|
|
8
|
+
class Generator < Jekyll::Generator
|
|
9
|
+
safe true
|
|
10
|
+
priority :highest
|
|
11
|
+
|
|
12
|
+
def generate(site)
|
|
13
|
+
@site = site
|
|
14
|
+
@config = site.config['baserow'] || {}
|
|
15
|
+
@collections_config = @config['collections'] || {}
|
|
16
|
+
|
|
17
|
+
unless @config['enabled'] != false
|
|
18
|
+
Jekyll.logger.info 'BaserowCMS:', 'Plugin disabled in configuration'
|
|
19
|
+
return
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
unless ENV['BASEROW_TOKEN']
|
|
23
|
+
Jekyll.logger.info 'BaserowCMS:', 'No BASEROW_TOKEN found, using collections fallback'
|
|
24
|
+
use_all_collections_fallback
|
|
25
|
+
return
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
Jekyll.logger.info 'BaserowCMS:', 'Fetching data from Baserow API...'
|
|
29
|
+
|
|
30
|
+
begin
|
|
31
|
+
@client = BaserowClient.new(ENV.fetch('BASEROW_TOKEN', nil), base_url: ENV.fetch('BASEROW_API_URL', nil))
|
|
32
|
+
|
|
33
|
+
@collections_config.each do |collection_name, collection_config|
|
|
34
|
+
fetch_collection_data(collection_name, collection_config)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
Jekyll.logger.info 'BaserowCMS:', 'All data fetched successfully'
|
|
38
|
+
rescue StandardError => e
|
|
39
|
+
Jekyll.logger.error 'BaserowCMS:', "Error fetching data: #{e.message}"
|
|
40
|
+
Jekyll.logger.warn 'BaserowCMS:', 'Falling back to collections'
|
|
41
|
+
use_all_collections_fallback
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Fetch data for a single collection
|
|
48
|
+
def fetch_collection_data(collection_name, config)
|
|
49
|
+
env_var = config['table_env']
|
|
50
|
+
data_file = config['data_file']
|
|
51
|
+
|
|
52
|
+
table_id = ENV.fetch(env_var, nil)
|
|
53
|
+
if table_id.nil? || table_id.empty? || table_id.start_with?('example_')
|
|
54
|
+
Jekyll.logger.info 'BaserowCMS:', "No #{env_var} found, using fallback for #{collection_name}"
|
|
55
|
+
use_collection_fallback(collection_name, config)
|
|
56
|
+
return
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
begin
|
|
60
|
+
baserow_data = @client.query_table(table_id)
|
|
61
|
+
organized_data = DataOrganizers.organize(baserow_data, config)
|
|
62
|
+
|
|
63
|
+
if DataOrganizers.data_present?(organized_data)
|
|
64
|
+
data_key = data_file.sub('.yml', '').sub('.yaml', '')
|
|
65
|
+
@site.data[data_key] = organized_data
|
|
66
|
+
create_data_file(organized_data, data_file, collection_name)
|
|
67
|
+
|
|
68
|
+
count = organized_data.is_a?(Hash) ? organized_data.size : organized_data.length
|
|
69
|
+
Jekyll.logger.info 'BaserowCMS:', "#{collection_name} fetched (#{count} items)"
|
|
70
|
+
else
|
|
71
|
+
Jekyll.logger.warn 'BaserowCMS:', "No data found for #{collection_name}, using fallback"
|
|
72
|
+
use_collection_fallback(collection_name, config)
|
|
73
|
+
end
|
|
74
|
+
rescue StandardError => e
|
|
75
|
+
Jekyll.logger.error 'BaserowCMS:', "Error fetching #{collection_name}: #{e.message}"
|
|
76
|
+
use_collection_fallback(collection_name, config)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Create a YAML data file
|
|
81
|
+
def create_data_file(data, file_name, collection_name)
|
|
82
|
+
data_dir = File.join(@site.source, '_data')
|
|
83
|
+
FileUtils.mkdir_p(data_dir)
|
|
84
|
+
|
|
85
|
+
data_file = File.join(data_dir, file_name)
|
|
86
|
+
new_content = data.to_yaml
|
|
87
|
+
|
|
88
|
+
# Skip if content unchanged
|
|
89
|
+
if File.exist?(data_file)
|
|
90
|
+
existing_content = File.read(data_file)
|
|
91
|
+
yaml_start = existing_content.index("---\n")
|
|
92
|
+
if yaml_start
|
|
93
|
+
existing_yaml = existing_content[yaml_start..]
|
|
94
|
+
if existing_yaml.strip == new_content.strip
|
|
95
|
+
Jekyll.logger.info 'BaserowCMS:', "#{collection_name} data unchanged, skipping"
|
|
96
|
+
return
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
File.open(data_file, 'w') do |file|
|
|
102
|
+
file.write("# #{collection_name.capitalize} data imported from Baserow\n")
|
|
103
|
+
file.write("# Auto-generated by jekyll-baserow-headless-cms - Do not edit manually\n")
|
|
104
|
+
file.write("# Last updated: #{Time.now.strftime('%Y-%m-%d %H:%M:%S')}\n\n")
|
|
105
|
+
file.write(new_content)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
Jekyll.logger.info 'BaserowCMS:', "#{collection_name} written to _data/#{file_name}"
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Use fallback for all collections
|
|
112
|
+
def use_all_collections_fallback
|
|
113
|
+
@collections_config.each do |collection_name, config|
|
|
114
|
+
use_collection_fallback(collection_name, config)
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Use Jekyll collections as fallback
|
|
119
|
+
def use_collection_fallback(collection_name, config)
|
|
120
|
+
Jekyll.logger.info 'BaserowCMS:', "Using fallback for #{collection_name}"
|
|
121
|
+
|
|
122
|
+
data_file = config['data_file']
|
|
123
|
+
data_key = data_file.sub('.yml', '').sub('.yaml', '')
|
|
124
|
+
fields_config = config['fields'] || []
|
|
125
|
+
|
|
126
|
+
# Convert Jekyll collection to Baserow-like row format
|
|
127
|
+
mock_data = { 'results' => [] }
|
|
128
|
+
|
|
129
|
+
@site.collections[collection_name]&.docs&.each_with_index do |doc, index|
|
|
130
|
+
row = { 'id' => "collection_#{index}" }
|
|
131
|
+
row.merge!(convert_doc_to_fields(doc.data, fields_config))
|
|
132
|
+
mock_data['results'] << row
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
organized_data = DataOrganizers.organize(mock_data, config)
|
|
136
|
+
@site.data[data_key] = organized_data
|
|
137
|
+
create_data_file(organized_data, data_file, collection_name)
|
|
138
|
+
|
|
139
|
+
count = organized_data.is_a?(Hash) ? organized_data.size : organized_data.length
|
|
140
|
+
Jekyll.logger.info 'BaserowCMS:', "#{collection_name} fallback applied (#{count} items)"
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Convert Jekyll document data to Baserow-like row fields
|
|
144
|
+
def convert_doc_to_fields(data, fields_config)
|
|
145
|
+
fields = {}
|
|
146
|
+
|
|
147
|
+
fields_config.each do |field_config|
|
|
148
|
+
field_name = field_config['name']
|
|
149
|
+
field_type = field_config['type']
|
|
150
|
+
field_key = field_config['key'] || FieldExtractors.normalize_key(field_name)
|
|
151
|
+
|
|
152
|
+
value = data[field_key] || data[field_name.downcase] || data[field_name]
|
|
153
|
+
next if value.nil?
|
|
154
|
+
|
|
155
|
+
fields[field_name] = convert_value_to_baserow_field(value, field_type)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
fields
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Convert a value to a Baserow field value shape
|
|
162
|
+
def convert_value_to_baserow_field(value, field_type)
|
|
163
|
+
case field_type
|
|
164
|
+
when 'number', 'rating', 'count'
|
|
165
|
+
value.to_i
|
|
166
|
+
when 'boolean'
|
|
167
|
+
!!value
|
|
168
|
+
when 'single_select'
|
|
169
|
+
{ 'id' => nil, 'value' => value.to_s, 'color' => nil }
|
|
170
|
+
when 'multi_select'
|
|
171
|
+
items = value.is_a?(Array) ? value : [value]
|
|
172
|
+
items.map { |v| { 'id' => nil, 'value' => v.to_s, 'color' => nil } }
|
|
173
|
+
else
|
|
174
|
+
value.to_s
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: jekyll-baserow-headless-cms
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Maxime Lenne
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-08-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: jekyll
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '3.7'
|
|
20
|
+
- - "<"
|
|
21
|
+
- !ruby/object:Gem::Version
|
|
22
|
+
version: '5.0'
|
|
23
|
+
type: :runtime
|
|
24
|
+
prerelease: false
|
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
26
|
+
requirements:
|
|
27
|
+
- - ">="
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '3.7'
|
|
30
|
+
- - "<"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '5.0'
|
|
33
|
+
- !ruby/object:Gem::Dependency
|
|
34
|
+
name: bundler
|
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '2.0'
|
|
40
|
+
type: :development
|
|
41
|
+
prerelease: false
|
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.0'
|
|
47
|
+
- !ruby/object:Gem::Dependency
|
|
48
|
+
name: rake
|
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '13.0'
|
|
54
|
+
type: :development
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '13.0'
|
|
61
|
+
- !ruby/object:Gem::Dependency
|
|
62
|
+
name: rspec
|
|
63
|
+
requirement: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '3.12'
|
|
68
|
+
type: :development
|
|
69
|
+
prerelease: false
|
|
70
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '3.12'
|
|
75
|
+
- !ruby/object:Gem::Dependency
|
|
76
|
+
name: rubocop
|
|
77
|
+
requirement: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '1.50'
|
|
82
|
+
type: :development
|
|
83
|
+
prerelease: false
|
|
84
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '1.50'
|
|
89
|
+
- !ruby/object:Gem::Dependency
|
|
90
|
+
name: rubocop-rspec
|
|
91
|
+
requirement: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '3.0'
|
|
96
|
+
type: :development
|
|
97
|
+
prerelease: false
|
|
98
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
99
|
+
requirements:
|
|
100
|
+
- - "~>"
|
|
101
|
+
- !ruby/object:Gem::Version
|
|
102
|
+
version: '3.0'
|
|
103
|
+
- !ruby/object:Gem::Dependency
|
|
104
|
+
name: simplecov
|
|
105
|
+
requirement: !ruby/object:Gem::Requirement
|
|
106
|
+
requirements:
|
|
107
|
+
- - "~>"
|
|
108
|
+
- !ruby/object:Gem::Version
|
|
109
|
+
version: '0.22'
|
|
110
|
+
type: :development
|
|
111
|
+
prerelease: false
|
|
112
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - "~>"
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0.22'
|
|
117
|
+
- !ruby/object:Gem::Dependency
|
|
118
|
+
name: webmock
|
|
119
|
+
requirement: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - "~>"
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '3.18'
|
|
124
|
+
type: :development
|
|
125
|
+
prerelease: false
|
|
126
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
127
|
+
requirements:
|
|
128
|
+
- - "~>"
|
|
129
|
+
- !ruby/object:Gem::Version
|
|
130
|
+
version: '3.18'
|
|
131
|
+
description: A configurable Jekyll plugin that fetches content from Baserow tables
|
|
132
|
+
and makes it available as Jekyll data files. Supports multiple collections, various
|
|
133
|
+
field types, and automatic fallback to Jekyll collections.
|
|
134
|
+
email:
|
|
135
|
+
- hello@maxime-lenne.fr
|
|
136
|
+
executables: []
|
|
137
|
+
extensions: []
|
|
138
|
+
extra_rdoc_files: []
|
|
139
|
+
files:
|
|
140
|
+
- ".claude/settings.json"
|
|
141
|
+
- ".mcp.json"
|
|
142
|
+
- ".rspec"
|
|
143
|
+
- ".rubocop.yml"
|
|
144
|
+
- CHANGELOG.md
|
|
145
|
+
- Gemfile
|
|
146
|
+
- LICENSE
|
|
147
|
+
- README.md
|
|
148
|
+
- Rakefile
|
|
149
|
+
- docs/EXAMPLES_AND_CONFIGURATION.md
|
|
150
|
+
- docs/architecture.excalidraw
|
|
151
|
+
- docs/architecture.png
|
|
152
|
+
- docs/architecture.svg
|
|
153
|
+
- docs/templates/github-actions_baserow-sync.yml
|
|
154
|
+
- lib/jekyll-baserow-headless-cms.rb
|
|
155
|
+
- lib/jekyll_baserow_headless_cms/baserow_client.rb
|
|
156
|
+
- lib/jekyll_baserow_headless_cms/data_organizers.rb
|
|
157
|
+
- lib/jekyll_baserow_headless_cms/field_extractors.rb
|
|
158
|
+
- lib/jekyll_baserow_headless_cms/generator.rb
|
|
159
|
+
- lib/jekyll_baserow_headless_cms/version.rb
|
|
160
|
+
homepage: https://github.com/maxime-lenne/jekyll-baserow-headless-cms
|
|
161
|
+
licenses:
|
|
162
|
+
- MIT
|
|
163
|
+
metadata:
|
|
164
|
+
homepage_uri: https://github.com/maxime-lenne/jekyll-baserow-headless-cms
|
|
165
|
+
source_code_uri: https://github.com/maxime-lenne/jekyll-baserow-headless-cms
|
|
166
|
+
changelog_uri: https://github.com/maxime-lenne/jekyll-baserow-headless-cms/blob/main/CHANGELOG.md
|
|
167
|
+
bug_tracker_uri: https://github.com/maxime-lenne/jekyll-baserow-headless-cms/issues
|
|
168
|
+
documentation_uri: https://github.com/maxime-lenne/jekyll-baserow-headless-cms#readme
|
|
169
|
+
rubygems_mfa_required: 'true'
|
|
170
|
+
post_install_message:
|
|
171
|
+
rdoc_options: []
|
|
172
|
+
require_paths:
|
|
173
|
+
- lib
|
|
174
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
175
|
+
requirements:
|
|
176
|
+
- - ">="
|
|
177
|
+
- !ruby/object:Gem::Version
|
|
178
|
+
version: 2.7.0
|
|
179
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
180
|
+
requirements:
|
|
181
|
+
- - ">="
|
|
182
|
+
- !ruby/object:Gem::Version
|
|
183
|
+
version: '0'
|
|
184
|
+
requirements: []
|
|
185
|
+
rubygems_version: 3.4.19
|
|
186
|
+
signing_key:
|
|
187
|
+
specification_version: 4
|
|
188
|
+
summary: Jekyll plugin to sync content from Baserow tables
|
|
189
|
+
test_files: []
|