bridgetown_directus 0.1.3 → 0.2.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 +4 -4
- data/CHANGELOG.md +10 -0
- data/README.md +67 -123
- data/Rakefile +1 -1
- data/bridgetown.automation.rb +50 -48
- data/bridgetown_directus.gemspec +2 -2
- data/example/bridgetown.config.yml +24 -0
- data/example/config/initializers.rb +34 -0
- data/lib/bridgetown_directus/builder.rb +105 -73
- data/lib/bridgetown_directus/client.rb +71 -0
- data/lib/bridgetown_directus/configuration.rb +82 -0
- data/lib/bridgetown_directus/data_mapper.rb +152 -0
- data/lib/bridgetown_directus/utils.rb +6 -1
- data/lib/bridgetown_directus/version.rb +1 -1
- data/lib/bridgetown_directus.rb +21 -19
- metadata +14 -14
- data/lib/bridgetown_directus/api_client.rb +0 -57
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dd3625cab0b39f5374ad7a3647e5b6e369bed783f05412cb38e15e8f10208908
|
4
|
+
data.tar.gz: a99f097216b2c9239188563a288b0df5752709782c29bfe15673edd73fd6c80d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d70b609eab2f9c4015c4e8898324fe046f8a62673c51d11057ec166372cceb7dfc3cba9793b1a37e3dd175f150d1d2dbc2c7c371427e740b61303630228793f0
|
7
|
+
data.tar.gz: 1dafe4be86342c4196ca93700035ad75f99b0a6a7d0ab78f0592f8ec0a76e51a223b7b63a7f46bf1b0568593b8322b0428f95e5329f38788c55d0a9b07907a9a
|
data/CHANGELOG.md
CHANGED
@@ -9,6 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
9
9
|
|
10
10
|
- ...
|
11
11
|
|
12
|
+
## [0.2.0] - 2025-04-16
|
13
|
+
|
14
|
+
- BREAKING: Simplified configuration—`resource_type` is no longer required. Use the Bridgetown collection name and layout instead.
|
15
|
+
- Automation now prompts for both Directus and Bridgetown collection names and sets up the initializer accordingly.
|
16
|
+
- Generated files are now flagged with `directus_generated: true` in front matter for safe cleanup.
|
17
|
+
- Only plugin-generated files (with this flag) are deleted during cleanup; user-authored files are preserved.
|
18
|
+
- Layouts for custom collections are now singular (e.g., `staff_member.erb`).
|
19
|
+
- README and example configuration updated for new conventions.
|
20
|
+
- Test suite updated for custom collections and file safety logic.
|
21
|
+
|
12
22
|
## [0.1.0] - 2024-09-27
|
13
23
|
|
14
24
|
- First version
|
data/README.md
CHANGED
@@ -2,16 +2,19 @@
|
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/bridgetown_directus)
|
4
4
|
|
5
|
-
This Bridgetown plugin integrates with [Directus](https://directus.io/),
|
5
|
+
This Bridgetown plugin integrates with [Directus](https://directus.io/), a flexible headless CMS. The plugin allows Bridgetown to pull content from a Directus API during the build process and generate static content in your site. It supports both single-language and multilingual content through Directus translations.
|
6
6
|
|
7
7
|
## Features
|
8
8
|
|
9
|
-
- Fetch
|
10
|
-
- Support for **
|
9
|
+
- Fetch content from **multiple Directus collections** during the build process
|
10
|
+
- Support for **flexible field mapping** and custom converters
|
11
|
+
- Support for **multilingual content** via Directus translations
|
12
|
+
- **Experimental**: Advanced **filtering, sorting, and pagination** options
|
13
|
+
- Simple configuration for any Bridgetown collection (posts, pages, or custom types)
|
11
14
|
|
12
15
|
## Installation
|
13
16
|
|
14
|
-
Before installing the plugin make sure you have an [Auth Token](https://docs.directus.io/reference/authentication.html#access-tokens) in your Directus instance.
|
17
|
+
Before installing the plugin, make sure you have an [Auth Token](https://docs.directus.io/reference/authentication.html#access-tokens) in your Directus instance.
|
15
18
|
|
16
19
|
### Recommended Installation (Bridgetown Automation)
|
17
20
|
|
@@ -21,11 +24,10 @@ Before installing the plugin make sure you have an [Auth Token](https://docs.dir
|
|
21
24
|
bin/bridgetown apply https://github.com/munkun-estudio/bridgetown_directus
|
22
25
|
```
|
23
26
|
|
24
|
-
|
25
|
-
-
|
26
|
-
-
|
27
|
-
-
|
28
|
-
- Configuring translatable fields (if translations enabled)
|
27
|
+
This will:
|
28
|
+
- Prompt for your Directus API URL, token, Directus collection name, and Bridgetown collection name
|
29
|
+
- Generate a minimal `config/initializers/bridgetown_directus.rb`
|
30
|
+
- All further customization is done in Ruby, not YAML
|
29
31
|
|
30
32
|
### Manual Installation
|
31
33
|
|
@@ -35,140 +37,82 @@ Before installing the plugin make sure you have an [Auth Token](https://docs.dir
|
|
35
37
|
bundle add "bridgetown_directus"
|
36
38
|
```
|
37
39
|
|
38
|
-
2. Run bundle install to install the gem.
|
39
|
-
3.
|
40
|
-
|
41
|
-
```ruby
|
42
|
-
init :"bridgetown_directus" do
|
43
|
-
api_url "https://your-directus-instance.com"
|
44
|
-
token ENV['DIRECTUS_AUTH_TOKEN'] || "your_token"
|
45
|
-
collection config.directus["collection"]
|
46
|
-
mappings config.directus["mappings"]
|
47
|
-
end
|
48
|
-
```
|
49
|
-
|
50
|
-
4. Configure your bridgetown.config.yml:
|
51
|
-
|
52
|
-
```yaml
|
53
|
-
directus:
|
54
|
-
collection: "posts"
|
55
|
-
mappings:
|
56
|
-
title: "title" # Required field
|
57
|
-
content: "body" # Required field
|
58
|
-
slug: "slug" # Optional, will be auto-generated if not provided
|
59
|
-
date: "date" # Optional, defaults to current date/time if not provided
|
60
|
-
category: "category" # Optional
|
61
|
-
excerpt: "excerpt" # Optional, defaults to content excerpt if not provided
|
62
|
-
image: "image" # Optional, URL for the image associated with the post
|
63
|
-
translations:
|
64
|
-
enabled: false # Set to true for multilingual support
|
65
|
-
fields: # Only required if translations are enabled
|
66
|
-
- title
|
67
|
-
- excerpt
|
68
|
-
- body
|
69
|
-
```
|
40
|
+
2. Run `bundle install` to install the gem.
|
41
|
+
3. Create `config/initializers/bridgetown_directus.rb` (see below for configuration).
|
70
42
|
|
71
43
|
## Configuration
|
72
44
|
|
73
|
-
###
|
74
|
-
|
75
|
-
You can configure the plugin either through environment variables or direct configuration:
|
76
|
-
|
77
|
-
1. Using environment variables:
|
78
|
-
|
79
|
-
```bash
|
80
|
-
export DIRECTUS_API_URL="https://your-directus-instance.com"
|
81
|
-
export DIRECTUS_AUTH_TOKEN="your-token"
|
82
|
-
```
|
83
|
-
|
84
|
-
2. Or through bridgetown.config.yml as shown in the installation section.
|
85
|
-
|
86
|
-
### Translations Configuration
|
87
|
-
|
88
|
-
To enable multilingual support:
|
89
|
-
|
90
|
-
1. In your bridgetown.config.yml, set translations.enabled to true:
|
91
|
-
|
92
|
-
```yaml
|
93
|
-
directus:
|
94
|
-
# ... other config ...
|
95
|
-
translations:
|
96
|
-
enabled: true
|
97
|
-
fields:
|
98
|
-
- title
|
99
|
-
- excerpt
|
100
|
-
- body
|
101
|
-
```
|
102
|
-
|
103
|
-
2. Ensure your Directus collection has translations enabled and configured for the specified fields.
|
104
|
-
|
105
|
-
3. The plugin will automatically:
|
106
|
-
|
107
|
-
- Generate posts for each available language
|
108
|
-
- Create appropriate URLs based on locale
|
109
|
-
- Handle fallback content if translations are missing
|
110
|
-
|
111
|
-
## Usage
|
112
|
-
|
113
|
-
Once the plugin is installed and configured, it will fetch posts from your Directus instance during each build. These posts will be generated as in-memory resources, meaning they are not written to disk but are treated as normal posts by Bridgetown.
|
45
|
+
### Minimal Example
|
114
46
|
|
115
|
-
|
47
|
+
```ruby
|
48
|
+
# config/initializers/bridgetown_directus.rb
|
49
|
+
init :bridgetown_directus do |directus|
|
50
|
+
directus.api_url = ENV["DIRECTUS_API_URL"] || "https://your-directus-instance.com"
|
51
|
+
directus.token = ENV["DIRECTUS_API_TOKEN"] || "your-token"
|
116
52
|
|
117
|
-
|
53
|
+
directus.register_collection(:posts) do |c|
|
54
|
+
c.endpoint = "posts"
|
55
|
+
c.layout = "post" # Use the singular layout for individual pages
|
56
|
+
# Minimal mapping (optional):
|
57
|
+
c.field :id, "id"
|
58
|
+
c.field :title, "title"
|
59
|
+
# To enable translations, uncomment and edit:
|
60
|
+
# c.enable_translations([:title, :content])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
```
|
118
64
|
|
119
|
-
|
65
|
+
For custom collections, create a layout file at `src/_layouts/[singular].erb` (e.g., `staff_member.erb`) to control the page rendering.
|
120
66
|
|
121
|
-
|
122
|
-
|
123
|
-
-
|
124
|
-
-
|
125
|
-
-
|
126
|
-
- **category**: Optional. The category for the post (Text field)
|
127
|
-
- **excerpt**: Optional. A short excerpt (Text field)
|
128
|
-
- **image**: Optional. An image associated with the post (File/Media field)
|
67
|
+
**By default, all Directus fields will be written to the front matter of generated Markdown files.**
|
68
|
+
You only need to declare fields with `c.field` if you want to:
|
69
|
+
- Rename a field in the output
|
70
|
+
- Transform/convert a field value (e.g., format a date, generate a slug, etc.)
|
71
|
+
- Set a default value if a field is missing
|
129
72
|
|
130
|
-
|
73
|
+
#### Example: Customizing a Field
|
131
74
|
|
132
|
-
|
75
|
+
```ruby
|
76
|
+
c.field :slug, "slug" do |value|
|
77
|
+
value || "staff_member-#{SecureRandom.hex(4)}"
|
78
|
+
end
|
79
|
+
```
|
133
80
|
|
134
|
-
|
81
|
+
### Translations
|
135
82
|
|
136
|
-
|
137
|
-
- In Directus, navigate to **Settings** > **Roles & Permissions**.
|
138
|
-
- Select the **Public** role (or create a custom role if needed).
|
139
|
-
- Under the **Collections** tab, locate the **directus_files** collection.
|
140
|
-
- Set the **read** permission to **enabled** so that the images can be accessed publicly.
|
83
|
+
To enable translations for specific fields, add this inside your collection block:
|
141
84
|
|
142
|
-
|
143
|
-
|
144
|
-
|
85
|
+
```ruby
|
86
|
+
c.enable_translations([:title, :content])
|
87
|
+
```
|
145
88
|
|
146
|
-
|
89
|
+
- You can list any field that exists in your Directus collection, even if it's not declared above with `c.field`.
|
90
|
+
- Only declare a field with `c.field` if you want to rename, transform, or set a default for it.
|
147
91
|
|
148
|
-
|
92
|
+
### File Generation & Cleanup
|
149
93
|
|
150
|
-
|
94
|
+
- **Generated files**: The plugin writes Markdown files to `src/_[bridgetown_collection]/` (e.g., `src/_staff_members/`).
|
95
|
+
- **Safety**: Only files with the `directus_generated: true` flag in their front matter are deleted during cleanup. User-authored files are never removed.
|
151
96
|
|
152
|
-
|
97
|
+
### Advanced Configuration
|
153
98
|
|
154
|
-
|
99
|
+
See the plugin source and inline documentation for advanced features such as:
|
100
|
+
- Multiple collections
|
101
|
+
- Custom layouts per collection
|
102
|
+
- Filtering, sorting, and pagination via `c.default_query` (**experimental**; not fully tested in production—see notes below)
|
103
|
+
- Selective field output
|
155
104
|
|
156
|
-
|
157
|
-
- [ ] Custom Field Mapping via DSL: Implement a DSL for more advanced field mapping.
|
158
|
-
- [ ] Asset Handling: Add functionality to download and manage images and other assets.
|
159
|
-
- [ ] Caching & Incremental Builds: Implement caching to improve build performance when fetching content.
|
160
|
-
- [ ] Draft Previews: Add support for previewing unpublished (draft) posts.
|
105
|
+
**Note:** Filtering, sorting, and pagination via `c.default_query` is experimental and not yet fully tested in real Bridgetown projects. Please report issues or contribute test cases if you use this feature!
|
161
106
|
|
162
|
-
|
107
|
+
### Migrating from 0.1.x
|
163
108
|
|
164
|
-
|
109
|
+
- **YAML config is no longer used.** All configuration is now in Ruby in `config/initializers/bridgetown_directus.rb`.
|
110
|
+
- Field mapping, transformation, and translations are handled in the initializer.
|
111
|
+
- All Directus fields are output by default; use `c.field` for customization.
|
112
|
+
- **Upgrading?** The `resource_type` option is no longer required. Use the Bridgetown collection name and layout instead. See the [CHANGELOG](CHANGELOG.md) for details.
|
165
113
|
|
166
|
-
|
114
|
+
---
|
167
115
|
|
168
|
-
|
116
|
+
For more details and advanced usage, see the [plugin README](https://github.com/Munkun-Estudio/bridgetown_directus).
|
169
117
|
|
170
|
-
|
171
|
-
2. Create a new branch (git checkout -b feature-branch)
|
172
|
-
3. Make your changes
|
173
|
-
4. Push to the branch (git push origin feature-branch)
|
174
|
-
5. Open a Pull Request
|
118
|
+
See [CHANGELOG.md](CHANGELOG.md) for upgrade notes and detailed changes.
|
data/Rakefile
CHANGED
data/bridgetown.automation.rb
CHANGED
@@ -2,61 +2,63 @@ say_status :directus, "Installing the bridgetown_directus plugin..."
|
|
2
2
|
|
3
3
|
# Prompt the user for Directus API URL and Auth Token
|
4
4
|
api_url = ask("What's your Directus instance URL? (Example: https://your-instance.example.com)")
|
5
|
-
auth_token = ask("What's your Directus API auth token? (Leave blank to use ENV['
|
6
|
-
|
7
|
-
|
8
|
-
# Ask if translations should be enabled with a default of 'n'
|
9
|
-
translations_enabled_input = ask("Do you want to enable translations? (y/n) default:", :yellow, default: "n")
|
10
|
-
translations_enabled = translations_enabled_input.strip.downcase.start_with?("y")
|
11
|
-
|
12
|
-
# Prepare the translations YAML block based on the user’s response
|
13
|
-
translations_yaml = if translations_enabled
|
14
|
-
translatable_fields_input = ask("List the translatable fields separated by commas (e.g., title, excerpt, content)")
|
15
|
-
translatable_fields = translatable_fields_input.split(',').map(&:strip)
|
16
|
-
|
17
|
-
" translations:\n enabled: true\n fields:\n#{translatable_fields.map { |field| " - #{field}" }.join("\n")}"
|
18
|
-
else
|
19
|
-
" translations:\n enabled: false"
|
20
|
-
end
|
5
|
+
auth_token = ask("What's your Directus API auth token? (Leave blank to use ENV['DIRECTUS_API_TOKEN'])")
|
6
|
+
directus_collection = ask("What's the Directus collection name (API endpoint/model)? (Example: posts)")
|
7
|
+
bridgetown_collection = ask("What's the Bridgetown collection name (used for folder and resource)? (Example: posts)")
|
21
8
|
|
22
9
|
# Add the bridgetown_directus gem
|
23
10
|
add_gem "bridgetown_directus"
|
24
11
|
|
25
|
-
# Add Directus configuration to config/initializers.rb
|
26
|
-
add_initializer :bridgetown_directus do
|
12
|
+
# Add minimal Directus configuration to config/initializers.rb in the idiomatic Bridgetown plugin style
|
13
|
+
add_initializer :bridgetown_directus do |directus|
|
27
14
|
<<~RUBY
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
15
|
+
# This block was generated by bridgetown_directus automation.
|
16
|
+
# All Directus configuration is now handled here (not in YAML).
|
17
|
+
# Set DIRECTUS_API_URL and DIRECTUS_API_TOKEN in your .env or shell environment.
|
18
|
+
#
|
19
|
+
# By default, ALL fields from Directus will be written to the front matter of generated Markdown files.
|
20
|
+
# You only need to declare fields here if you want to:
|
21
|
+
# - Rename a field in the output
|
22
|
+
# - Transform/convert a field value (e.g., format a date, generate a slug, etc.)
|
23
|
+
# - Set a default value if a field is missing
|
24
|
+
#
|
25
|
+
# Example: To customize or transform fields, use the c.field declaration:
|
26
|
+
# # require "securerandom" # Uncomment if you use SecureRandom in your mapping
|
27
|
+
# c.field :slug, "slug" do |value|
|
28
|
+
# value || "post-#{SecureRandom.hex(4)}"
|
29
|
+
# end
|
30
|
+
#
|
31
|
+
# ---
|
32
|
+
# TRANSLATIONS:
|
33
|
+
# To enable translations for specific fields, add this line inside your collection block:
|
34
|
+
# c.enable_translations([:title, :content])
|
35
|
+
# You can list any field that exists in your Directus collection, even if it's not declared above with c.field.
|
36
|
+
# Declaring a field with c.field is only required if you want to rename, transform, or set a default for it.
|
37
|
+
# ---
|
38
|
+
|
39
|
+
directus.api_url = ENV["DIRECTUS_API_URL"] || "#{api_url}"
|
40
|
+
directus.token = ENV["DIRECTUS_API_TOKEN"] || "#{auth_token}"
|
36
41
|
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
category: "category" # Optional
|
49
|
-
excerpt: "excerpt" # Optional, defaults to content excerpt if not provided
|
50
|
-
image: "image" # Optional, URL for the image associated with the post
|
51
|
-
#{translations_yaml}
|
52
|
-
YAML
|
42
|
+
directus.register_collection(:#{bridgetown_collection}) do |c|
|
43
|
+
c.endpoint = "#{directus_collection}"
|
44
|
+
c.layout = "#{bridgetown_collection.to_s.singularize}"
|
45
|
+
# Minimal mapping (optional):
|
46
|
+
c.field :id, "id"
|
47
|
+
c.field :title, "title"
|
48
|
+
# Add more c.field declarations above as needed for custom logic.
|
49
|
+
# To enable translations, uncomment and edit the following line:
|
50
|
+
# c.enable_translations([:title, :content])
|
51
|
+
end
|
52
|
+
RUBY
|
53
53
|
end
|
54
54
|
|
55
55
|
say_status :success, "Bridgetown Directus plugin has been installed!", :green
|
56
|
-
say_status :
|
57
|
-
|
58
|
-
|
56
|
+
say_status :directus, "Check config/initializers.rb for your Directus setup."
|
57
|
+
say_status :directus, "Check bridgetown.config.yml for your collection setup."
|
58
|
+
|
59
|
+
# Only remind the user to create a layout if it's a custom collection (not 'posts' or 'pages')
|
60
|
+
if !%w[posts pages].include?(bridgetown_collection.to_s)
|
61
|
+
say_status :directus, "Don't forget to create a layout file at src/_layouts/#{bridgetown_collection.to_s.singularize}.erb for your custom collection pages!"
|
59
62
|
end
|
60
|
-
|
61
|
-
say_status :directus, "For usage
|
62
|
-
say_status :directus, "https://github.com/Munkun-Estudio/bridgetown_directus/blob/main/README.md"
|
63
|
+
|
64
|
+
say_status :directus, "For advanced usage and field customization, see the README: https://github.com/Munkun-Estudio/bridgetown_directus"
|
data/bridgetown_directus.gemspec
CHANGED
@@ -24,7 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
|
25
25
|
spec.required_ruby_version = ">= 2.7.0"
|
26
26
|
|
27
|
-
spec.add_dependency "bridgetown", ">=
|
27
|
+
spec.add_dependency "bridgetown", ">= 2.0.0.beta4", "< 3.0"
|
28
28
|
spec.add_dependency "faraday", "~> 2.12"
|
29
29
|
|
30
30
|
spec.add_development_dependency "bundler", "~> 2.0"
|
@@ -32,7 +32,7 @@ Gem::Specification.new do |spec|
|
|
32
32
|
spec.add_development_dependency "rubocop-bridgetown", "~> 0.3"
|
33
33
|
spec.add_development_dependency "shoulda", "~> 3.0"
|
34
34
|
spec.add_development_dependency "minitest", "~> 5.0"
|
35
|
-
spec.add_development_dependency "minitest-profile", "~> 0.
|
35
|
+
spec.add_development_dependency "minitest-profile", "~> 0.0.2"
|
36
36
|
spec.add_development_dependency "minitest-reporters", "~> 1.0"
|
37
37
|
spec.add_development_dependency "webmock", "~> 3.0"
|
38
38
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Example Bridgetown configuration file for the enhanced Directus plugin
|
2
|
+
|
3
|
+
# Site settings
|
4
|
+
title: My Bridgetown Site with Directus
|
5
|
+
email: your-email@example.com
|
6
|
+
description: >-
|
7
|
+
A Bridgetown site powered by Directus headless CMS
|
8
|
+
baseurl: ""
|
9
|
+
url: ""
|
10
|
+
|
11
|
+
# Collections
|
12
|
+
collections:
|
13
|
+
posts:
|
14
|
+
output: true
|
15
|
+
permalink: /blog/:slug/
|
16
|
+
source: _posts
|
17
|
+
future: true
|
18
|
+
staff_members:
|
19
|
+
output: true
|
20
|
+
permalink: /staff_members/:slug/
|
21
|
+
source: _staff_members
|
22
|
+
sort_field: 'created_at'
|
23
|
+
sort_reverse: false
|
24
|
+
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Example Bridgetown Directus plugin initializer for v2+
|
4
|
+
#
|
5
|
+
# All Directus configuration is now handled here, NOT in bridgetown.config.yml.
|
6
|
+
# Use ENV variables for secrets and API credentials.
|
7
|
+
|
8
|
+
require "securerandom"
|
9
|
+
require "time"
|
10
|
+
|
11
|
+
init :bridgetown_directus do |directus|
|
12
|
+
# Set API credentials from environment variables
|
13
|
+
directus.api_url = ENV["DIRECTUS_API_URL"] || "https://your-directus-instance.com"
|
14
|
+
directus.token = ENV["DIRECTUS_API_TOKEN"] || "your-token-here"
|
15
|
+
|
16
|
+
# Example custom collection: staff_members
|
17
|
+
directus.register_collection(:staff_members) do |c|
|
18
|
+
c.endpoint = "staff_members"
|
19
|
+
c.layout = "staff_member"
|
20
|
+
c.field :id, "id"
|
21
|
+
c.field :title, "title"
|
22
|
+
# To enable translations, uncomment and edit:
|
23
|
+
# c.enable_translations([:title, :content])
|
24
|
+
# Add more fields as needed
|
25
|
+
end
|
26
|
+
|
27
|
+
# Example for posts (if needed)
|
28
|
+
directus.register_collection(:posts) do |c|
|
29
|
+
c.endpoint = "articles"
|
30
|
+
c.layout = "post"
|
31
|
+
c.field :title, "title"
|
32
|
+
c.field :content, "body"
|
33
|
+
end
|
34
|
+
end
|
@@ -1,103 +1,135 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
|
1
5
|
module BridgetownDirectus
|
2
6
|
class Builder < Bridgetown::Builder
|
3
7
|
def build
|
8
|
+
config = site.config.bridgetown_directus
|
4
9
|
return if site.ssr?
|
5
10
|
|
6
|
-
|
7
|
-
|
11
|
+
config.collections.each_value do |collection_config|
|
12
|
+
next unless [:posts, :pages, :custom_collection].include?(collection_config.resource_type)
|
8
13
|
|
9
|
-
|
10
|
-
|
11
|
-
|
14
|
+
process_collection(
|
15
|
+
client: Client.new(
|
16
|
+
api_url: config.api_url,
|
17
|
+
token: config.token
|
18
|
+
),
|
19
|
+
collection_config: collection_config
|
20
|
+
)
|
21
|
+
end
|
12
22
|
end
|
13
23
|
|
14
24
|
private
|
15
25
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
if posts_data.is_a?(Hash) && posts_data.key?("data") && posts_data["data"].is_a?(Array)
|
24
|
-
posts_array = posts_data["data"]
|
25
|
-
elsif posts_data.is_a?(Array)
|
26
|
-
posts_array = posts_data
|
26
|
+
# Determine the output directory for the given collection name
|
27
|
+
def collection_directory(collection_name)
|
28
|
+
case collection_name.to_s
|
29
|
+
when "posts"
|
30
|
+
File.join(site.source, "_posts")
|
31
|
+
when "pages"
|
32
|
+
File.join(site.source, "_pages")
|
27
33
|
else
|
28
|
-
|
34
|
+
File.join(site.source, "_#{collection_name}")
|
29
35
|
end
|
30
|
-
|
31
|
-
created_posts = 0
|
32
|
-
posts_array.each do |post|
|
33
|
-
if translations_enabled?
|
34
|
-
created_posts += create_translated_posts(post)
|
35
|
-
else
|
36
|
-
created_posts += create_single_post(post)
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
Utils.log_directus "Finished generating #{created_posts} posts."
|
41
36
|
end
|
42
37
|
|
43
|
-
|
44
|
-
|
38
|
+
# Write a Directus item as a Markdown file in the correct Bridgetown collection directory
|
39
|
+
def write_directus_file(item, collection_dir, layout = nil, api_url = nil)
|
40
|
+
require "fileutils"
|
41
|
+
FileUtils.mkdir_p(collection_dir)
|
42
|
+
slug = item["slug"] || item["id"].to_s
|
43
|
+
filename = build_filename(collection_dir, slug)
|
44
|
+
item = transform_item_fields(item, api_url, layout)
|
45
|
+
item["directus_generated"] = true # Add flag to front matter
|
46
|
+
content = item.delete("body") || ""
|
47
|
+
front_matter = generate_front_matter(item)
|
48
|
+
write_markdown_file(filename, front_matter, content)
|
45
49
|
end
|
46
50
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
51
|
+
def build_filename(collection_dir, slug)
|
52
|
+
File.join(collection_dir, "#{slug}.md")
|
53
|
+
end
|
50
54
|
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
content post["body"]
|
56
|
-
date post["date"] || Time.now.iso8601
|
57
|
-
category post["category"]
|
58
|
-
excerpt post["excerpt"]
|
59
|
-
image post["image"] ? "#{api_url}/assets/#{post['image']}" : nil
|
60
|
-
end
|
61
|
-
1
|
62
|
-
rescue => e
|
63
|
-
Utils.log_directus "Error creating post #{slug}: #{e.message}"
|
64
|
-
0
|
55
|
+
def transform_item_fields(item, api_url, layout)
|
56
|
+
item = item.dup
|
57
|
+
if item["image"] && api_url && !item["image"].to_s.start_with?("http://", "https://")
|
58
|
+
item["image"] = File.join(api_url, "assets", item["image"])
|
65
59
|
end
|
60
|
+
item["layout"] = layout if layout
|
61
|
+
item
|
66
62
|
end
|
67
63
|
|
68
|
-
def
|
69
|
-
|
70
|
-
|
64
|
+
def generate_front_matter(item)
|
65
|
+
yaml = item.to_yaml
|
66
|
+
yaml.sub(%r{^---\s*\n}, "") # Remove leading --- if present
|
67
|
+
end
|
71
68
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
next unless site.config["available_locales"].include?(bridgetown_locale)
|
69
|
+
def write_markdown_file(filename, front_matter, content)
|
70
|
+
File.write(filename, "---\n#{front_matter}---\n\n#{content}")
|
71
|
+
end
|
77
72
|
|
78
|
-
|
79
|
-
|
73
|
+
# Remove only plugin-generated Markdown files in the target directory before writing new ones
|
74
|
+
def clean_collection_directory(collection_dir)
|
75
|
+
require "yaml"
|
76
|
+
Dir.glob(File.join(collection_dir, "*.md")).each do |file|
|
77
|
+
fm = File.read(file)[%r{\A---.*?---}m]
|
78
|
+
File.delete(file) if fm && YAML.safe_load(fm)["directus_generated"]
|
79
|
+
rescue StandardError => e
|
80
|
+
warn "[BridgetownDirectus] Could not check/delete #{file}: #{e.message}"
|
81
|
+
end
|
82
|
+
end
|
80
83
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
def process_collection(client:, collection_config:)
|
85
|
+
endpoint = collection_config.endpoint || collection_config.name.to_s
|
86
|
+
begin
|
87
|
+
response = client.fetch_collection(endpoint, collection_config.default_query)
|
88
|
+
rescue StandardError => e
|
89
|
+
warn "Error fetching collection '#{endpoint}': #{e.message}"
|
90
|
+
return
|
91
|
+
end
|
92
|
+
collection_dir = collection_directory(collection_config.name)
|
93
|
+
clean_collection_directory(collection_dir)
|
94
|
+
api_url = site.config.bridgetown_directus.api_url
|
95
|
+
sanitized_response = sanitize_keys(response)
|
96
|
+
sanitized_response.each do |item|
|
97
|
+
write_directus_file(item, collection_dir, collection_config.layout, api_url)
|
98
|
+
end
|
99
|
+
end
|
93
100
|
|
94
|
-
|
95
|
-
|
96
|
-
|
101
|
+
# Recursively sanitize keys to avoid illegal instance variable names (Ruby 3.4+)
|
102
|
+
def sanitize_keys(obj)
|
103
|
+
case obj
|
104
|
+
when Hash
|
105
|
+
obj.each_with_object({}) do |(k, v), h|
|
106
|
+
safe_key = if %r{^\d}.match?(k.to_s)
|
107
|
+
"n_#{k}"
|
108
|
+
else
|
109
|
+
k
|
110
|
+
end
|
111
|
+
h[safe_key] = sanitize_keys(v)
|
97
112
|
end
|
113
|
+
when Array
|
114
|
+
obj.map { |v| sanitize_keys(v) }
|
115
|
+
else
|
116
|
+
obj
|
98
117
|
end
|
118
|
+
end
|
99
119
|
|
100
|
-
|
120
|
+
# Recursively log all keys to find problematic ones
|
121
|
+
def log_all_keys(obj, path = "")
|
122
|
+
case obj
|
123
|
+
when Hash
|
124
|
+
obj.each do |k, v|
|
125
|
+
# puts "[BridgetownDirectus DEBUG] Key at #{path}: #{k.inspect}" if %r{^\d}.match?(k.to_s)
|
126
|
+
log_all_keys(v, "#{path}/#{k}")
|
127
|
+
end
|
128
|
+
when Array
|
129
|
+
obj.each_with_index do |v, idx|
|
130
|
+
log_all_keys(v, "#{path}[#{idx}]")
|
131
|
+
end
|
132
|
+
end
|
101
133
|
end
|
102
134
|
end
|
103
135
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "faraday"
|
5
|
+
|
6
|
+
module BridgetownDirectus
|
7
|
+
# Client for interacting with the Directus API
|
8
|
+
class Client
|
9
|
+
attr_reader :api_url, :token
|
10
|
+
|
11
|
+
def initialize(api_url:, token:)
|
12
|
+
@api_url = api_url
|
13
|
+
@token = token
|
14
|
+
return unless @token.nil? || @api_url.nil?
|
15
|
+
|
16
|
+
raise StandardError, "Invalid Directus configuration: missing API token or URL"
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch_collection(collection, params = {})
|
20
|
+
response = connection.get("/items/#{collection}") do |req|
|
21
|
+
req.params.merge!(prepare_params(params))
|
22
|
+
end
|
23
|
+
handle_response(response)
|
24
|
+
end
|
25
|
+
|
26
|
+
def fetch_item(collection, id, params = {})
|
27
|
+
response = connection.get("/items/#{collection}/#{id}") do |req|
|
28
|
+
req.params.merge!(prepare_params(params))
|
29
|
+
end
|
30
|
+
handle_response(response)
|
31
|
+
end
|
32
|
+
|
33
|
+
def fetch_items_with_filter(collection, filter, params = {})
|
34
|
+
merged_params = params.merge(filter: filter)
|
35
|
+
fetch_collection(collection, merged_params)
|
36
|
+
end
|
37
|
+
|
38
|
+
def fetch_related_items(collection, id, relation, params = {})
|
39
|
+
response = connection.get("/items/#{collection}/#{id}/#{relation}") do |req|
|
40
|
+
req.params.merge!(prepare_params(params))
|
41
|
+
end
|
42
|
+
handle_response(response)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def connection
|
48
|
+
@connection ||= Faraday.new(url: @api_url) do |faraday|
|
49
|
+
faraday.headers["Authorization"] = "Bearer #{@token}"
|
50
|
+
faraday.headers["Content-Type"] = "application/json"
|
51
|
+
faraday.adapter Faraday.default_adapter
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def prepare_params(params)
|
56
|
+
params.transform_keys(&:to_s)
|
57
|
+
end
|
58
|
+
|
59
|
+
def handle_response(response)
|
60
|
+
unless response.success?
|
61
|
+
Utils.log_directus "Directus API error: #{response.status} - #{response.body}"
|
62
|
+
raise "Directus API error: #{response.status}"
|
63
|
+
end
|
64
|
+
json = JSON.parse(response.body)
|
65
|
+
json["data"] || []
|
66
|
+
rescue JSON::ParserError => e
|
67
|
+
Utils.log_directus "Failed to parse Directus response: #{e.message}"
|
68
|
+
raise
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BridgetownDirectus
|
4
|
+
# Configuration module for Bridgetown Directus plugin
|
5
|
+
class Configuration
|
6
|
+
attr_reader :collections
|
7
|
+
attr_accessor :api_url, :token
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@collections = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Register a new collection with the given name
|
14
|
+
# @param name [Symbol] The name of the collection
|
15
|
+
# @param block [Proc] Configuration block for the collection
|
16
|
+
# @return [CollectionConfig] The collection configuration
|
17
|
+
def register_collection(name, &block)
|
18
|
+
collection = CollectionConfig.new(name)
|
19
|
+
collection.instance_eval(&block) if block_given?
|
20
|
+
@collections[name] = collection
|
21
|
+
collection
|
22
|
+
end
|
23
|
+
|
24
|
+
# Find a collection by name
|
25
|
+
# @param name [Symbol] The name of the collection
|
26
|
+
# @return [CollectionConfig, nil] The collection configuration or nil if not found
|
27
|
+
def find_collection(name)
|
28
|
+
@collections[name]
|
29
|
+
end
|
30
|
+
|
31
|
+
# Collection configuration class
|
32
|
+
class CollectionConfig
|
33
|
+
attr_reader :name
|
34
|
+
|
35
|
+
# Initialize a new collection configuration
|
36
|
+
# @param name [Symbol] The name of the collection
|
37
|
+
def initialize(name)
|
38
|
+
@name = name
|
39
|
+
@fields = {}
|
40
|
+
@default_query = {}
|
41
|
+
@resource_type = :posts
|
42
|
+
@layout = "post"
|
43
|
+
@translations_enabled = false
|
44
|
+
@translatable_fields = []
|
45
|
+
@endpoint = nil
|
46
|
+
end
|
47
|
+
|
48
|
+
# Set up accessors for collection configuration properties
|
49
|
+
attr_accessor :endpoint, :fields, :default_query, :resource_type, :layout,
|
50
|
+
:translations_enabled, :translatable_fields
|
51
|
+
|
52
|
+
# Define a field mapping with optional converter
|
53
|
+
# @param bridgetown_field [Symbol] The field name in Bridgetown
|
54
|
+
# @param directus_field [String, Symbol] The field name in Directus
|
55
|
+
# @param converter [Proc, nil] Optional converter to transform the field value
|
56
|
+
# @return [void]
|
57
|
+
def field(bridgetown_field, directus_field, &converter)
|
58
|
+
@fields[bridgetown_field] = {
|
59
|
+
directus_field: directus_field.to_s,
|
60
|
+
converter: converter,
|
61
|
+
}
|
62
|
+
end
|
63
|
+
|
64
|
+
# Enable translations for this collection
|
65
|
+
# @param fields [Array<Symbol>] The fields that should be translated
|
66
|
+
# @return [void]
|
67
|
+
def enable_translations(fields = [])
|
68
|
+
@translations_enabled = true
|
69
|
+
@translatable_fields = fields
|
70
|
+
end
|
71
|
+
|
72
|
+
# Generate the resource path for a given item
|
73
|
+
# @param item [Hash] The data item from Directus
|
74
|
+
# @return [String] The resource path
|
75
|
+
def path(item)
|
76
|
+
# Default: /:resource_type/:slug/index.html
|
77
|
+
slug = item["slug"] || item[:slug] || item["id"] || item[:id]
|
78
|
+
"/#{resource_type}/#{slug}/index.html"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,152 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module BridgetownDirectus
|
4
|
+
# Data mapper for transforming Directus data into Bridgetown resources
|
5
|
+
class DataMapper
|
6
|
+
class << self
|
7
|
+
# Map Directus data to Bridgetown format based on collection configuration
|
8
|
+
# @param collection_config [CollectionConfig] The collection configuration
|
9
|
+
# @param data [Hash] The Directus data
|
10
|
+
# @return [Hash] The mapped data
|
11
|
+
def map(collection_config, data)
|
12
|
+
mapped_data = {}
|
13
|
+
|
14
|
+
collection_config.fields.each do |bridgetown_field, field_config|
|
15
|
+
if field_config.is_a?(Hash)
|
16
|
+
directus_field = field_config[:directus_field]
|
17
|
+
converter = field_config[:converter]
|
18
|
+
|
19
|
+
value = extract_value(data, directus_field)
|
20
|
+
|
21
|
+
# Apply converter if provided
|
22
|
+
value = converter.call(value) if converter.respond_to?(:call)
|
23
|
+
|
24
|
+
mapped_data[bridgetown_field] = value
|
25
|
+
else
|
26
|
+
# Support for simple string mapping for backward compatibility
|
27
|
+
directus_field = field_config.to_s
|
28
|
+
mapped_data[bridgetown_field] = extract_value(data, directus_field)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
mapped_data
|
33
|
+
end
|
34
|
+
|
35
|
+
# Map translated fields from Directus data
|
36
|
+
# @param collection_config [CollectionConfig] The collection configuration
|
37
|
+
# @param data [Hash] The Directus data
|
38
|
+
# @param locale [Symbol] The locale to map
|
39
|
+
# @return [Hash] The mapped data with translations
|
40
|
+
def map_translations(collection_config, data, locale)
|
41
|
+
# First map the base data
|
42
|
+
mapped_data = map(collection_config, data)
|
43
|
+
|
44
|
+
# If translations are enabled and the data has translations
|
45
|
+
return mapped_data unless collection_config.translations_enabled && data["translations"]
|
46
|
+
|
47
|
+
# Find the translation for the requested locale
|
48
|
+
translation = find_translation_for_locale(data["translations"], locale)
|
49
|
+
|
50
|
+
# Apply translations if found
|
51
|
+
if translation
|
52
|
+
apply_translations(collection_config, translation, mapped_data)
|
53
|
+
mapped_data[:locale] = locale
|
54
|
+
end
|
55
|
+
|
56
|
+
mapped_data
|
57
|
+
end
|
58
|
+
|
59
|
+
# Resolve relationships in the data
|
60
|
+
# @param client [Client] The Directus client
|
61
|
+
# @param collection_config [CollectionConfig] The collection configuration
|
62
|
+
# @param data [Hash] The mapped data
|
63
|
+
# @param relationships [Hash] Relationship configuration
|
64
|
+
# @return [Hash] The data with resolved relationships
|
65
|
+
def resolve_relationships(client, collection_config, data, relationships)
|
66
|
+
return data unless relationships
|
67
|
+
|
68
|
+
resolved_data = data.dup
|
69
|
+
|
70
|
+
relationships.each do |field, relationship_config|
|
71
|
+
relation_id = data[field]
|
72
|
+
next unless relation_id
|
73
|
+
|
74
|
+
related_collection = relationship_config[:collection]
|
75
|
+
related_fields = relationship_config[:fields] || "*"
|
76
|
+
|
77
|
+
# Fetch the related item
|
78
|
+
related_item = client.fetch_item(
|
79
|
+
related_collection,
|
80
|
+
relation_id,
|
81
|
+
{ fields: related_fields }
|
82
|
+
)
|
83
|
+
|
84
|
+
# Add the related data to the resolved data
|
85
|
+
if related_item && related_item["data"]
|
86
|
+
resolved_data["#{field}_data"] = related_item["data"]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
resolved_data
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
# Find translation for a specific locale
|
96
|
+
# @param translations [Array] Array of translation objects
|
97
|
+
# @param locale [Symbol] The locale to find
|
98
|
+
# @return [Hash, nil] The translation for the locale or nil if not found
|
99
|
+
def find_translation_for_locale(translations, locale)
|
100
|
+
translations.find do |t|
|
101
|
+
lang_code = t["languages_code"].to_s.split("-").first.downcase
|
102
|
+
lang_code == locale.to_s
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Apply translations to mapped data
|
107
|
+
# @param collection_config [CollectionConfig] The collection configuration
|
108
|
+
# @param translation [Hash] The translation data
|
109
|
+
# @param mapped_data [Hash] The mapped data to update
|
110
|
+
# @return [void]
|
111
|
+
def apply_translations(collection_config, translation, mapped_data)
|
112
|
+
collection_config.translatable_fields.each do |field|
|
113
|
+
# Get the Directus field name for this Bridgetown field
|
114
|
+
field_config = collection_config.fields[field]
|
115
|
+
|
116
|
+
directus_field = field_config.is_a?(Hash) ? field_config[:directus_field] : field_config.to_s
|
117
|
+
|
118
|
+
# Check if the translation has this field
|
119
|
+
next unless translation[directus_field]
|
120
|
+
|
121
|
+
value = translation[directus_field]
|
122
|
+
|
123
|
+
# Apply converter if provided
|
124
|
+
if field_config.is_a?(Hash) && field_config[:converter].respond_to?(:call)
|
125
|
+
value = field_config[:converter].call(value)
|
126
|
+
end
|
127
|
+
|
128
|
+
mapped_data[field] = value
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Extract a value from nested data using dot notation
|
133
|
+
# @param data [Hash] The data to extract from
|
134
|
+
# @param field [String] The field path (e.g., "user.profile.name")
|
135
|
+
# @return [Object] The extracted value
|
136
|
+
def extract_value(data, field)
|
137
|
+
return nil unless data
|
138
|
+
|
139
|
+
keys = field.to_s.split(".")
|
140
|
+
value = data
|
141
|
+
|
142
|
+
keys.each do |key|
|
143
|
+
return nil unless value.is_a?(Hash) && value.key?(key)
|
144
|
+
|
145
|
+
value = value[key]
|
146
|
+
end
|
147
|
+
|
148
|
+
value
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
@@ -3,7 +3,12 @@
|
|
3
3
|
module BridgetownDirectus
|
4
4
|
module Utils
|
5
5
|
def self.log_directus(message)
|
6
|
-
Bridgetown.logger
|
6
|
+
if defined?(Bridgetown) && Bridgetown.respond_to?(:logger)
|
7
|
+
Bridgetown.logger.info("Directus") { message }
|
8
|
+
elsif ENV["BRIDGETOWN_DIRECTUS_DEBUG"]
|
9
|
+
# Fallback for testing or when Bridgetown is not available
|
10
|
+
puts "[Directus] #{message}"
|
11
|
+
end
|
7
12
|
end
|
8
13
|
end
|
9
14
|
end
|
data/lib/bridgetown_directus.rb
CHANGED
@@ -2,36 +2,38 @@
|
|
2
2
|
|
3
3
|
require "bridgetown"
|
4
4
|
require_relative "bridgetown_directus/utils"
|
5
|
-
require_relative "bridgetown_directus/
|
5
|
+
require_relative "bridgetown_directus/client"
|
6
|
+
require_relative "bridgetown_directus/data_mapper"
|
7
|
+
require_relative "bridgetown_directus/configuration"
|
6
8
|
require_relative "bridgetown_directus/builder"
|
7
9
|
|
8
10
|
module BridgetownDirectus
|
9
11
|
# Bridgetown initializer for the plugin
|
10
|
-
Bridgetown.initializer :bridgetown_directus do |config
|
11
|
-
config.bridgetown_directus
|
12
|
-
config.bridgetown_directus
|
13
|
-
config.bridgetown_directus.token ||= token || ENV.fetch("DIRECTUS_API_TOKEN")
|
12
|
+
Bridgetown.initializer :bridgetown_directus do |config|
|
13
|
+
# Only assign config.bridgetown_directus if not already set
|
14
|
+
config.bridgetown_directus ||= Configuration.new
|
14
15
|
|
15
|
-
#
|
16
|
-
config.bridgetown_directus.
|
17
|
-
config.bridgetown_directus.
|
18
|
-
config.bridgetown_directus.translations ||= config.directus["translations"]
|
16
|
+
# Set up configuration directly (leave to user initializer if possible)
|
17
|
+
config.bridgetown_directus.api_url ||= ENV["DIRECTUS_API_URL"] || "[https://studio.munkun.com](https://studio.munkun.com)"
|
18
|
+
config.bridgetown_directus.token ||= ENV["DIRECTUS_TOKEN"] || "t1P6YstcUslmf-KJFbc6Kyg0bomMxkXY"
|
19
19
|
|
20
20
|
# Register the builder
|
21
21
|
config.builder BridgetownDirectus::Builder
|
22
|
+
end
|
23
|
+
|
24
|
+
class Configuration
|
25
|
+
attr_accessor :api_url, :token
|
26
|
+
attr_reader :collections
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
translatable_fields = config.bridgetown_directus.translations["fields"] || []
|
26
|
-
Bridgetown.logger.info "Directus translations enabled for fields: #{translatable_fields.join(', ')}"
|
27
|
-
else
|
28
|
-
Bridgetown.logger.info "Directus translations are disabled"
|
28
|
+
def initialize
|
29
|
+
@collections = {}
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
32
|
+
def register_collection(name, &block)
|
33
|
+
collection = CollectionConfig.new(name)
|
34
|
+
collection.instance_eval(&block) if block_given?
|
35
|
+
@collections[name] = collection
|
36
|
+
collection
|
35
37
|
end
|
36
38
|
end
|
37
39
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bridgetown_directus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Munkun
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-04-16 00:00:00.000000000 Z
|
12
11
|
dependencies:
|
13
12
|
- !ruby/object:Gem::Dependency
|
14
13
|
name: bridgetown
|
@@ -16,20 +15,20 @@ dependencies:
|
|
16
15
|
requirements:
|
17
16
|
- - ">="
|
18
17
|
- !ruby/object:Gem::Version
|
19
|
-
version:
|
18
|
+
version: 2.0.0.beta4
|
20
19
|
- - "<"
|
21
20
|
- !ruby/object:Gem::Version
|
22
|
-
version: '
|
21
|
+
version: '3.0'
|
23
22
|
type: :runtime
|
24
23
|
prerelease: false
|
25
24
|
version_requirements: !ruby/object:Gem::Requirement
|
26
25
|
requirements:
|
27
26
|
- - ">="
|
28
27
|
- !ruby/object:Gem::Version
|
29
|
-
version:
|
28
|
+
version: 2.0.0.beta4
|
30
29
|
- - "<"
|
31
30
|
- !ruby/object:Gem::Version
|
32
|
-
version: '
|
31
|
+
version: '3.0'
|
33
32
|
- !ruby/object:Gem::Dependency
|
34
33
|
name: faraday
|
35
34
|
requirement: !ruby/object:Gem::Requirement
|
@@ -120,14 +119,14 @@ dependencies:
|
|
120
119
|
requirements:
|
121
120
|
- - "~>"
|
122
121
|
- !ruby/object:Gem::Version
|
123
|
-
version:
|
122
|
+
version: 0.0.2
|
124
123
|
type: :development
|
125
124
|
prerelease: false
|
126
125
|
version_requirements: !ruby/object:Gem::Requirement
|
127
126
|
requirements:
|
128
127
|
- - "~>"
|
129
128
|
- !ruby/object:Gem::Version
|
130
|
-
version:
|
129
|
+
version: 0.0.2
|
131
130
|
- !ruby/object:Gem::Dependency
|
132
131
|
name: minitest-reporters
|
133
132
|
requirement: !ruby/object:Gem::Requirement
|
@@ -156,7 +155,6 @@ dependencies:
|
|
156
155
|
- - "~>"
|
157
156
|
- !ruby/object:Gem::Version
|
158
157
|
version: '3.0'
|
159
|
-
description:
|
160
158
|
email: development@munkun.com
|
161
159
|
executables: []
|
162
160
|
extensions: []
|
@@ -171,9 +169,13 @@ files:
|
|
171
169
|
- Rakefile
|
172
170
|
- bridgetown.automation.rb
|
173
171
|
- bridgetown_directus.gemspec
|
172
|
+
- example/bridgetown.config.yml
|
173
|
+
- example/config/initializers.rb
|
174
174
|
- lib/bridgetown_directus.rb
|
175
|
-
- lib/bridgetown_directus/api_client.rb
|
176
175
|
- lib/bridgetown_directus/builder.rb
|
176
|
+
- lib/bridgetown_directus/client.rb
|
177
|
+
- lib/bridgetown_directus/configuration.rb
|
178
|
+
- lib/bridgetown_directus/data_mapper.rb
|
177
179
|
- lib/bridgetown_directus/utils.rb
|
178
180
|
- lib/bridgetown_directus/version.rb
|
179
181
|
- package.json
|
@@ -185,7 +187,6 @@ metadata:
|
|
185
187
|
bug_tracker_uri: https://github.com/munkun-estudio/bridgetown_directus/issues
|
186
188
|
changelog_uri: https://github.com/munkun-estudio/bridgetown_directus/releases
|
187
189
|
homepage_uri: https://github.com/munkun-estudio/bridgetown_directus
|
188
|
-
post_install_message:
|
189
190
|
rdoc_options: []
|
190
191
|
require_paths:
|
191
192
|
- lib
|
@@ -200,8 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
201
|
- !ruby/object:Gem::Version
|
201
202
|
version: '0'
|
202
203
|
requirements: []
|
203
|
-
rubygems_version: 3.
|
204
|
-
signing_key:
|
204
|
+
rubygems_version: 3.6.2
|
205
205
|
specification_version: 4
|
206
206
|
summary: Use Directus as headless CMS for Bridgetown
|
207
207
|
test_files: []
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module BridgetownDirectus
|
4
|
-
class APIClient
|
5
|
-
def initialize(site)
|
6
|
-
@site = site
|
7
|
-
@api_url = site.config.bridgetown_directus.api_url
|
8
|
-
@api_token = site.config.bridgetown_directus.token
|
9
|
-
|
10
|
-
raise StandardError, "Invalid Directus configuration: missing API token or URL" if @api_token.nil? || @api_url.nil?
|
11
|
-
end
|
12
|
-
|
13
|
-
# Main method to fetch posts
|
14
|
-
def fetch_posts
|
15
|
-
Utils.log_directus "Request URL: #{@api_url}/items/#{@site.config.bridgetown_directus.collection}"
|
16
|
-
|
17
|
-
response = connection.get("/items/#{@site.config.bridgetown_directus.collection}") do |req|
|
18
|
-
req.params['filter'] = { status: { _eq: "published" } }.to_json
|
19
|
-
req.params['fields'] = '*,translations.*'
|
20
|
-
end
|
21
|
-
|
22
|
-
if response.success?
|
23
|
-
JSON.parse(response.body) # Return the parsed posts
|
24
|
-
elsif response.status == 401
|
25
|
-
raise RuntimeError, "Unauthorized access to Directus API"
|
26
|
-
else
|
27
|
-
raise "Error fetching posts: #{response.status} - #{response.body}"
|
28
|
-
end
|
29
|
-
rescue Faraday::TimeoutError
|
30
|
-
raise Faraday::TimeoutError, "The request to fetch posts timed out"
|
31
|
-
rescue JSON::ParserError
|
32
|
-
raise JSON::ParserError, "The response from Directus was not valid JSON"
|
33
|
-
end
|
34
|
-
|
35
|
-
# Setup Faraday connection with authorization headers
|
36
|
-
def connection
|
37
|
-
Faraday.new(url: @api_url) do |faraday|
|
38
|
-
faraday.options.timeout = 5
|
39
|
-
faraday.options.open_timeout = 2
|
40
|
-
faraday.headers['Authorization'] = "Bearer #{@api_token}"
|
41
|
-
faraday.headers['Content-Type'] = 'application/json'
|
42
|
-
faraday.adapter Faraday.default_adapter
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
# New method for validating the data structure
|
47
|
-
private def validate_posts_data(posts_data)
|
48
|
-
if posts_data.is_a?(Hash) && posts_data.key?("data") && posts_data["data"].is_a?(Array)
|
49
|
-
posts_data["data"]
|
50
|
-
elsif posts_data.is_a?(Array)
|
51
|
-
posts_data
|
52
|
-
else
|
53
|
-
raise "Invalid posts data structure: #{posts_data.inspect}"
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|