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.
@@ -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
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module JekyllBaserowHeadlessCMS
4
+ VERSION = '0.1.0'
5
+ 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: []