jekyll-sqlite 0.1.5 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbf075a895e90cae8f3b5d69299089ad2f2381b9ba255348b7f517c6ef03f8a0
4
- data.tar.gz: 920c6c9292ead41d747e88a8cb6ab5e5a1a85d54d19781d3b675a316a90c3e98
3
+ metadata.gz: 85c5327f559510f9baa0eef20116398123e50254da7d75924c8d08e8883c7505
4
+ data.tar.gz: 29329e963af9d41a2059fc91f2a56088f8a5e038ae890985028bec086c5a253d
5
5
  SHA512:
6
- metadata.gz: 769762e5786644f055002eae18dd443e12989242f24f5ce79cbd03dcea235bf73cc7e6b4c6a70b30dac024d03ca03e84fbe5f16ef5cdf9289cd930b8ccbeedf4
7
- data.tar.gz: 0c950eca035285304de7900e780b41e769340083b9966c2a01df02a4b10330bc8917cbd63be53f84852ceb1e8dfb7f7d4845c73c89afa0fa8f4719c8dfad82fb
6
+ metadata.gz: f96eb487a5177930b82a19ef0ca5916f51bb4f6f89bfac8317d00dbb7be6ecf6700660765a13d5b5f34b4a6a3244658ced390d4b284ec0ccbf0d97cb6229af7d
7
+ data.tar.gz: e74fb0ee889375285a718879f72cfea1a2c69899846ea62fd44d325b0154c65b8d8070ead3c33f48d458fcffe985864e7ec2dc665ed1a5639e9c156075ff62c9
data/.rubocop.yml CHANGED
@@ -1,6 +1,6 @@
1
- require: rubocop-rake
1
+ plugins: rubocop-rake
2
2
  AllCops:
3
- TargetRubyVersion: 3.0
3
+ TargetRubyVersion: 3.2
4
4
  NewCops: enable
5
5
  Exclude:
6
6
  - "node_modules/**/*"
@@ -8,6 +8,7 @@ AllCops:
8
8
  - "vendor/**/*"
9
9
  - ".git/**/*"
10
10
  - "test/_plugins/jekyll_sqlite_generator.rb"
11
+ - "docs/**/*"
11
12
  Style/StringLiterals:
12
13
  Enabled: true
13
14
  EnforcedStyle: double_quotes
@@ -20,4 +21,6 @@ Layout/LineLength:
20
21
  Max: 120
21
22
 
22
23
  Metrics/AbcSize:
23
- Max: 15
24
+ Max: 20
25
+ Metrics/MethodLength:
26
+ Max: 100
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
+ ---
2
+ title: Changelog
3
+ nav_order: "ZZZ"
4
+ ---
5
+
6
+ All notable changes to this project will be documented in this file.
7
+
8
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
9
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
10
+
1
11
  ## [Unreleased]
2
12
 
13
+ ## [0.2.0] - 2025-08-23
14
+ - Multiple-levels of nesting is now supported
15
+ - Slight performance improvement by keeping database open for entire plugin run
16
+
17
+ ## [0.1.6] - 2025-07-31
18
+ - Drop Ruby 3.1
19
+ - sqlite3 requirement to `2.7.3`
20
+
21
+ ## [0.1.5] - 2024-07-31
22
+ - sqlite3 requirement changed to `1.6`
23
+
3
24
  ## [0.1.4] - 2024-07-17
4
25
  - Per-page queries are now supported via a `sqlite` config block in the front matter.
5
26
  - Documents support for existing site data being used within queries.
data/Gemfile CHANGED
@@ -6,12 +6,9 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  # These are development dependencies
9
- gem "jekyll", "~> 4.0"
10
- gem "rake", "~> 13.0"
11
- gem "rubocop", "~> 1.21"
12
- gem "rubocop-rake", "~> 0.6.0"
9
+ gem "jekyll", "~> 4.4", ">= 4.4.1"
10
+ gem "rake", "~> 13.3"
11
+ gem "rubocop", "~> 1.80"
12
+ gem "rubocop-rake", "~> 0.7"
13
13
 
14
- # Ruby 3.4 preparedness
15
- gem "base64", "~> 0.2.0"
16
- gem "bigdecimal", "~> 3.1"
17
- gem "csv", "~> 3.3"
14
+ gem "logger", "~> 1.7"
data/README.md CHANGED
@@ -5,140 +5,23 @@ A Jekyll generator plugin to lets you use SQLite database instead of data files
5
5
  It supports site-level queries, per-page queries, and prepared queries that can
6
6
  use existing data (possibly generated via more queries) as parameters.
7
7
 
8
- [![Continuous Integration](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml/badge.svg)](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml) [![Gem Version](https://badge.fury.io/rb/jekyll-sqlite.svg)](https://badge.fury.io/rb/jekyll-sqlite)
9
-
10
- ## Installation
11
-
12
- Add this line to your site's `Gemfile`:
13
-
14
- ```ruby
15
- gem 'jekyll-sqlite'
16
- ```
17
-
18
- And then add this line to your site's `_config.yml`:
19
-
20
- ```yml
21
- plugins:
22
- - jekyll_sqlite
23
- ```
24
-
25
- :warning: If you are using Jekyll < 3.5.0 use the `gems` key instead of `plugins`.
26
-
27
- ## Usage
28
-
29
- Update your `_config.yml` to define your data sources with your SQLite database. Please see
30
- the `test` directory for a functional example with the [Northwind database](https://github.com/jpwhite3/northwind-SQLite3).
31
-
32
- ```yml
33
- ...
34
- sqlite:
35
- - data: customers
36
- file: *db
37
- query: SELECT * from Customers
38
- ```
39
-
40
- Then, you can use the `site.data` attributes accordingly:
41
-
42
- ```liquid
43
- {{ site.data.customers | jsonify }}
44
- ```
45
-
46
- ## Prepared Queries
47
-
48
- This plugin supports prepared queries with parameter binding. This lets you
49
- use existing data from a previous query, or some other source (such as
50
- `site.data.*` or `page.*`) as a parameter in your query.
51
-
52
- Say you have a YAML file defining your items (`data/books.yaml`):
53
-
54
- ```yaml
55
- - id: 31323952-2708-42dc-a995-6006a23cbf00
56
- name: Time Travel with a Rubber Band
57
- - id: 5c8e67a0-d490-4743-b5b8-8e67bd1f95a2
58
- name: The Art of Cache Invalidation
59
- ```
60
- and the prices for the items in your SQLite database, the following configuration will enrich the `items` array with the price:
61
-
62
- ```yaml
63
- sql:
64
- - data: items.books
65
- query: SELECT price, author FROM pricing WHERE id =:id
66
- db: books.db
67
- ```
68
- This would allow the following Liquid loop to be written:
8
+ The primary usecase is to **avoid Liquid Hell**, wherein you're left mangling
9
+ multiple data sources from CSV/JSON/YAML files using liquid templating by
10
+ saving temporary variables, creating maps, and so on. SQL is a decent language
11
+ for reshaping datasets - supporting joins, filters, and aggregations. So this
12
+ allows you to use SQL for reshaping your data, and then use liquid
13
+ for what it was meant for - presentation and templating.
69
14
 
70
- ```liquid
71
- {% for item in site.data.items %}
72
- {{item.meta.price}}, {{item.meta.author}}
73
- {% endfor %}
74
- ```
75
-
76
- ## Per Page Queries
77
-
78
- The exact same syntax can be used on a per-page basis to generate data within
79
- each page. This is helpful for keeping page-specific queries within the page
80
- itself. Here's an example:
81
-
82
- ```yaml
83
- ---
84
- FeaturedSupplierID: 2
85
- sqlite:
86
- - data: suppliers
87
- file: "_db/northwind.db"
88
- query: "SELECT CompanyName, SupplierID FROM suppliers ORDER BY SupplierID"
89
- - data: suppliers.products
90
- # This is a prepared query, where SupplierID is coming from the previous query.
91
- file: "_db/northwind.db"
92
- query: "SELECT ProductName, CategoryID,UnitPrice FROM products WHERE SupplierID = :SupplierID"
93
- # :FeaturedSupplierID is picked up automatically from the page frontmatter.
94
- - data: FeaturedSupplier
95
- file: "_db/northwind.db"
96
- query: "SELECT * SupplierID = :FeaturedSupplierID"
97
- ---
98
- {{page.suppliers|jsonify}}
99
- ```
100
-
101
- This will generate a `page.suppliers` array with all the suppliers, and a `page.FeaturedSupplier` object with the details of the featured supplier.
102
-
103
- Each supplier will have a `products` array with all the products for that supplier.
104
-
105
- ## Generating Pages
106
-
107
- It works well with the `datapage_gen` plugin:
108
-
109
- See the [datapage_gen](https://github.com/avillafiorita/jekyll-datapage_gen) docs for more details.
110
-
111
- Here's a sample configuration:
112
-
113
- ```yaml
114
- sqlite:
115
- restaurants:
116
- file: _db/reviews.db
117
- sql: SELECT id, name, last_review_date > 1672531200 as active, address FROM restaurants;
118
- page_gen:
119
- - data: restaurants
120
- template: restaurant
121
- name: id
122
- title: name
123
- filter: active
124
- ```
125
-
126
- This will automatically generate a file for each restaurant
127
- restaurants/#{id}.html file with the layout `_layouts/restaurant.html` and page.id, page.name, page.active set and page.title set to restaurant name
128
-
129
- Note that the `datapage_gen` plugin will run _after_ the `jekyll-sqlite` plugin,
130
- if you generate any pages with per-page queries, these queries will not execute.
131
-
132
- ## Development
133
-
134
- After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
15
+ [![Continuous Integration](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml/badge.svg)](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml) [![Gem Version](https://badge.fury.io/rb/jekyll-sqlite.svg)](https://badge.fury.io/rb/jekyll-sqlite)
135
16
 
136
- To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
17
+ Documentation is now maintained at <https://captnemo.in/jekyll-sqlite/>.
137
18
 
138
19
  ## Contributing
139
20
 
140
21
  Bug reports and pull requests are welcome on GitHub at https://github.com/captn3m0/jekyll-sqlite. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/captn3m0/jekyll-sqlite/blob/main/CODE_OF_CONDUCT.md).
141
22
 
23
+ Note that only maintained versions of [Jekyll](https://endoflife.date/jekyll) and [Ruby](https://endoflife.date/ruby) are supported.
24
+
142
25
  ## Code of Conduct
143
26
 
144
27
  Everyone interacting in the Jekyll::Sqlite project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/captn3m0/jekyll-sqlite/blob/main/CODE_OF_CONDUCT.md).
data/Rakefile CHANGED
@@ -19,7 +19,6 @@ def query_db(query)
19
19
  end
20
20
 
21
21
  # rubocop:disable Metrics/AbcSize
22
- # rubocop:disable Metrics/MethodLength
23
22
  def validate_json
24
23
  file = "_site/data.json"
25
24
  data = JSON.parse(File.read(file))
@@ -54,9 +53,25 @@ def validate_page_json
54
53
  fs = query_db("SELECT * FROM Suppliers WHERE SupplierID = 6")
55
54
  assert read_data["focusSupplier"][0] == fs, "Focus Supplier doesn't match"
56
55
  end
57
- # rubocop:enable Metrics/AbcSize
58
- # rubocop:enable Metrics/MethodLength
59
56
 
57
+ def validate_employees_json
58
+ file = "_site/employees.json"
59
+ regions = JSON.parse(File.read(file))
60
+ assert regions.size == 4
61
+ assert regions[0]["RegionID"] == 1, "First RegionID should be 1"
62
+ assert regions[0]["RegionDescription"] == "Eastern", "First Region is Eastern"
63
+ assert regions[-1]["RegionID"] == 4, "Four zotal Regions"
64
+ assert regions[0]["territories"].size == 19, "There should be 19 territories in Eastern"
65
+ assert regions[0]["territories"][0]["TerritoryID"] == "01730", "First TerritoryID should be 1"
66
+ assert regions[0]["territories"][0]["TerritoryDescription"] == "Bedford", "First TerritoryID should be Bedford"
67
+ bedford = regions[0]["territories"][0]
68
+ assert bedford == {
69
+ "TerritoryID" => "01730",
70
+ "TerritoryDescription" => "Bedford",
71
+ "EmployeeIDs" => [{ "EmployeeID" => 2, "FirstName" => "Andrew", "LastName" => "Fuller" }]
72
+ }, "Bedford should have Andrew Fuller"
73
+ end
74
+ # rubocop:enable Metrics/AbcSize
60
75
  task default: :rubocop
61
76
 
62
77
  desc "Build Test Site"
@@ -65,4 +80,5 @@ task :test do
65
80
  Jekyll::Site.new(Jekyll.configuration).process
66
81
  validate_json
67
82
  validate_page_json
83
+ validate_employees_json
68
84
  end
data/docs/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ _site
2
+ .sass-cache
3
+ .jekyll-cache
4
+ .jekyll-metadata
5
+ vendor
data/docs/404.html ADDED
@@ -0,0 +1,8 @@
1
+ ---
2
+ permalink: /404.html
3
+ layout: page
4
+ ---
5
+ <h1>404</h1>
6
+
7
+ <p><strong>Page not found :(</strong></p>
8
+ <p>The requested page could not be found.</p>
data/docs/CHANGELOG.md ADDED
@@ -0,0 +1 @@
1
+ ../CHANGELOG.md
data/docs/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+ gem "jekyll", "~> 4.4.1"
3
+ gem "just-the-docs"
4
+
5
+ gem "logger", "~> 1.7"
data/docs/_config.yml ADDED
@@ -0,0 +1,44 @@
1
+ theme: just-the-docs
2
+ title: Jekyll SQLite
3
+ description: >-
4
+ generator plugin to lets you use SQLite database instead of data files as a
5
+ data source. It lets you easily create APIs and websites from a SQLite
6
+ database, by linking together a database file, your template, and the relevant
7
+ queries.
8
+ baseurl: "/jekyll-sqlite"
9
+ url: "https://captnemo.in"
10
+ github_username: captn3m0
11
+ # https://just-the-docs.com/docs/configuration/
12
+ favicon_ico: https://jekyllrb.com/favicon.ico
13
+ aux_links:
14
+ GitHub:
15
+ - "https://github.com/captn3m0/jekyll-sqlite/"
16
+ RubyGems:
17
+ - "https://rubygems.org/gems/jekyll-sqlite"
18
+ gh_edit_link: true
19
+ gh_edit_link_text: "Edit this page on GitHub."
20
+ gh_edit_repository: "https://github.com/captn3m0/jekyll-sqlite" # the github URL for your repo
21
+ gh_edit_branch: "main"
22
+ gh_edit_source: docs
23
+ gh_edit_view_mode: "edit"
24
+
25
+ callouts:
26
+ demo:
27
+ color: green
28
+ opacity: 0.5
29
+ note:
30
+ color: yellow
31
+ opacity: 0.3
32
+ # https://jekyllrb.com/docs/configuration/front-matter-defaults/
33
+ defaults:
34
+ - scope:
35
+ path: ""
36
+ values:
37
+ layout: default
38
+ encoding: utf-8
39
+ markdown: kramdown
40
+ strict_front_matter: true
41
+ # Silence Saas deprecation warnings, to be removed after this is fixed in just-the-docs
42
+ sass:
43
+ quiet_deps: true # https://github.com/just-the-docs/just-the-docs/issues/1541
44
+ silence_deprecations: ['import']
data/docs/demo.md ADDED
@@ -0,0 +1,29 @@
1
+ ---
2
+ title: Demo
3
+ permalink: /demo.html
4
+ ---
5
+
6
+ 🏁 A fully-functional demo website that uses this plugin is available at
7
+ [northwind.captnemo.in](https://northwind.captnemo.in). The source code for
8
+ the demo is available at [captn3m0/northwind](https://github.com/captn3m0/northwind).
9
+ You can find more details at the demo page.
10
+
11
+ Here is a screenshot:
12
+
13
+ ![Screenshot of https://northwind.captnemo.in/products.html](img/northwind-1.jpg)
14
+
15
+ It relies on all features of the plugin, along with using `jekyll-datapage_gen`
16
+ plugin to generate individual pages for each data item.
17
+
18
+ 1. A [per-page query](usage/#per-page-queries) is used on the restock page to generate list of
19
+ products that need to be restocked. [source](https://github.com/captn3m0/northwind/blob/main/restock.md?plain=1)
20
+ 2. Customers, Orders, Products, Categories are set as global data items
21
+ in [config.yml](https://github.com/captn3m0/northwind/blob/main/_config.yml)
22
+ 3. `site.data.categories[*].products` is filled using a parameterised query
23
+ in [`config.yml`](https://github.com/captn3m0/northwind/blob/main/_config.yml#L47-L49)
24
+ 4. Featured Product and Employee of the Month, shown on homepage are set by a query
25
+ in `config.yml`, but the query parameters are set in [`_data`](https://github.com/captn3m0/northwind/tree/main/_data)
26
+ directory as YML files.
27
+ 5. The datapage plugin config generates a page for every product and customer.
28
+
29
+ The database is a trimmed-version of the northwind database from https://github.com/jpwhite3/northwind-SQLite3.
Binary file
Binary file
data/docs/index.md ADDED
@@ -0,0 +1,26 @@
1
+ ---
2
+ title: Home
3
+ nav_order: 0
4
+ ---
5
+
6
+ A Jekyll generator plugin to lets you use SQLite databases instead of [Data Files][df] as a
7
+ data source. It lets you easily create APIs and websites from a SQLite
8
+ database, by linking together a database file, your template, and the relevant
9
+ queries.
10
+
11
+ Jekyll's Data Files are great, but they are limited to YAML/JSON/TSV/CSV file
12
+ formats - this plugin gives you another option: SQLite databases.
13
+
14
+ It supports site-level queries, per-page queries, and prepared queries that can
15
+ use existing data (possibly generated via more queries) as parameters.
16
+
17
+ The primary usecase is to **avoid Liquid Hell**, wherein you're left mangling
18
+ multiple data sources from CSV/JSON/YAML files using liquid templating by
19
+ saving temporary variables, creating maps, and so on. SQL is a decent language
20
+ for reshaping datasets - supporting joins, filters, and aggregations. So this
21
+ allows you to use SQL for reshaping your data, and then use liquid
22
+ for what it was meant for - presentation and templating.
23
+
24
+ [![Continuous Integration](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml/badge.svg)](https://github.com/captn3m0/jekyll-sqlite/actions/workflows/main.yml) [![Gem Version](https://badge.fury.io/rb/jekyll-sqlite.svg)](https://badge.fury.io/rb/jekyll-sqlite)
25
+
26
+ [df]: https://jekyllrb.com/docs/datafiles/ "Data Files at Jekyll Docs site"
data/docs/install.md ADDED
@@ -0,0 +1,23 @@
1
+ ---
2
+ title: Installation
3
+ ---
4
+
5
+ Add this line to your site's `Gemfile`:
6
+
7
+ ```ruby
8
+ gem 'jekyll-sqlite'
9
+ ```
10
+
11
+ And then add this line to your site's `_config.yml`:
12
+
13
+ ```yml
14
+ plugins:
15
+ - jekyll_sqlite
16
+ ```
17
+
18
+ See [Usage](/jekyll-sqlite/usage/) for next steps.
19
+
20
+ ---
21
+
22
+ Note that only supported versions of [Ruby](https://endoflife.date/ruby)
23
+ and [Jekyll](https://endoflife.date/ruby) are supported.
@@ -0,0 +1,73 @@
1
+ ---
2
+ title: Using with Datapage Plugin
3
+ parent: Usage
4
+ nav_order: 2
5
+ ---
6
+
7
+ The Jekyll [Datapage Generator](https://github.com/avillafiorita/jekyll-datapage_gen)
8
+ plugin allows you to specify data files for which we want to
9
+ generate one page per record.
10
+
11
+ You can use it alongside this plugin to generate data from a SQLite database,
12
+ and generate a page per row of your resultset.
13
+
14
+ This is how a simple configuration would look:
15
+
16
+ ```yaml
17
+ # for the sqlite plugin
18
+ sqlite:
19
+ - data: restaurants
20
+ file: _db/reviews.db
21
+ query: SELECT id, name, last_review_date > 1672531200 as active, address FROM restaurants;
22
+
23
+ # for the datapage_gen plugin
24
+ page_gen:
25
+ - data: restaurants
26
+ # The layout used for each generated page _layouts/restaurant.html
27
+ template: restaurant
28
+ page_data_prefix: restaurants
29
+ name: id
30
+ title: name
31
+ filter: active
32
+ ```
33
+
34
+ This will automatically generate a file for each restaurant `restaurants/#
35
+ {id}.html` file with the layout `_layouts/restaurant.html` and page.id,
36
+ page.name, page.active set and page.title set to restaurant name. Query data is
37
+ accessed in the page template via `{%raw%}{{ page.restaurants.address }}{%endraw%}` - the
38
+ namespace set in `page_data_prefix`.
39
+
40
+ Note that the `datapage_gen` plugin will run _after_ the `jekyll-sqlite` plugin, if you generate any pages with per-page queries, these queries will not execute.
41
+
42
+ ## Demo Example
43
+
44
+ The following example comes from the [Demo](../demo/).
45
+
46
+ The following datapage configuration in `_config.yml`:
47
+
48
+ ```yml
49
+ page_gen:
50
+ - data: products
51
+ template: product # _layouts/product.html
52
+ page_data_prefix: product
53
+ title: ProductName
54
+ name: ProductID
55
+ extension: html
56
+ ```
57
+
58
+ will generate a page for each product in the `site.data.products` array.
59
+
60
+ In order to get data into the array, we can use:
61
+
62
+ ```yml
63
+ sqlite:
64
+ - data: products
65
+ file: _db/northwind.db
66
+ query: SELECT * from Products
67
+ ```
68
+
69
+ Here's a screenshot of how it looks:
70
+
71
+ ![Product Page](../img/northwind-2.jpg)
72
+
73
+ See it in action at <https://northwind.captnemo.in/products/1.html>
@@ -0,0 +1,54 @@
1
+ ---
2
+ title: Dynamic DB File
3
+ parent: Usage
4
+ nav_order: 3
5
+ ---
6
+
7
+ If you want to select the database filename via an environment variable,
8
+ you can use the following options as a workaround:
9
+
10
+ ## Using `envsubst` from GNU gettext
11
+
12
+ You can install it via [brew](https://formulae.brew.sh/formula/gettext)
13
+ or [on linux](https://repology.org/project/gettext/versions).
14
+
15
+ First, define your database filename,
16
+ and create a new configuration template file.
17
+
18
+ ```sh
19
+ export JEKYLL_DB=events-blr.db
20
+ cp _config.yml _config.txt
21
+ ```
22
+
23
+ Then, use the database name in your new ocnfig file
24
+
25
+ ```yaml
26
+ sqlite:
27
+ data: events
28
+ file: $JEKYLL_DB
29
+ query: SELECT * FROM events
30
+ ```
31
+
32
+ Then, use `envsubst` to generate the `_config.yml`
33
+
34
+ ```bash
35
+ envsubst < "_config.txt" > "_config.yml"
36
+ ```
37
+
38
+ ## Using multiple configuration files
39
+
40
+ You can define your `sqlite` parameter multiple times
41
+ across multiple files, using different database filenames
42
+
43
+ For eg, `_config-blr.yml` could only include:
44
+
45
+ ```
46
+ sqlite:
47
+ data: events
48
+ file: events-blr.db
49
+ query: SELECT * FROM events
50
+ ```
51
+
52
+ And you can run jekyll using `jekyll --config _config.yml,_config-blr.yml`
53
+
54
+ However, this requires duplicating your query across multiple files.
@@ -0,0 +1,20 @@
1
+ ---
2
+ title: Usage
3
+ has_toc: false
4
+ permalink: /usage/
5
+ ---
6
+ Update your `_config.yml` to define your data sources with your SQLite database.
7
+
8
+ ```yml
9
+ ...
10
+ sqlite:
11
+ - data: customers
12
+ file: *db
13
+ query: SELECT * from Customers
14
+ ```
15
+
16
+ Then, you can use the `site.data` attributes accordingly:
17
+
18
+ ```liquid{%raw%}
19
+ {{ site.data.customers | jsonify }}{%endraw%}
20
+ ```
@@ -0,0 +1,44 @@
1
+ ---
2
+ title: Nested Queries
3
+ parent: Usage
4
+ nav_order: 4
5
+ ---
6
+ Starting from `0.2.0`, queries can be nested infinitely.
7
+
8
+ The following configuration is used in the [demo](../demo/):
9
+
10
+ ```yaml
11
+ sqlite:
12
+ - data: regions
13
+ file: *db
14
+ query: |
15
+ SELECT RegionID, RegionDescription FROM Regions
16
+ ORDER BY RegionID
17
+
18
+ - data: regions.territories
19
+ file: *db
20
+ query: |
21
+ SELECT TerritoryID, TerritoryDescription FROM Territories
22
+ WHERE RegionID = :RegionID
23
+ ORDER BY TerritoryDescription
24
+
25
+ - data: regions.territories.EmployeeIDs
26
+ file: *db
27
+ query: |
28
+ SELECT T.EmployeeID as EmployeeID, FirstName,LastName
29
+ FROM EmployeeTerritories T,Employees
30
+ WHERE T.TerritoryID = :TerritoryID
31
+ AND T.EmployeeID = Employees.EmployeeID
32
+ ```
33
+
34
+ The first query generates `site.data.regions` as a list. The second query
35
+ sets territories inside each of the regions, and the third query
36
+ sets the list of employees inside each territory.
37
+
38
+ {: .note }
39
+ > Per Page Query
40
+ >
41
+ > On the Demo website, you can see the result at
42
+ > [the regions page](https://northwind.captnemo.in/regions.html)
43
+ > where each region is broken into territories, with the name
44
+ > of the employee under each region.
@@ -0,0 +1,41 @@
1
+ ---
2
+ title: Per-page Queries
3
+ nav_order: 0
4
+ parent: Usage
5
+ ---
6
+
7
+ The exact same syntax can be used on a per-page basis to generate data within
8
+ each page. This is helpful for keeping page-specific queries within the page
9
+ itself. Here's an example:
10
+
11
+ ```yaml
12
+ ---
13
+ FeaturedSupplierID: 2
14
+ sqlite:
15
+ - data: suppliers
16
+ file: "_db/northwind.db"
17
+ query: "SELECT CompanyName, SupplierID FROM suppliers ORDER BY SupplierID"
18
+ - data: suppliers.products
19
+ # This is a prepared query, where SupplierID is coming from the previous query.
20
+ file: "_db/northwind.db"
21
+ query: "SELECT ProductName, CategoryID,UnitPrice FROM products WHERE SupplierID = :SupplierID"
22
+ # :FeaturedSupplierID is picked up automatically from the page frontmatter.
23
+ - data: FeaturedSupplier
24
+ file: "_db/northwind.db"
25
+ query: "SELECT * SupplierID = :FeaturedSupplierID"
26
+ ---
27
+ {%raw%}{{page.suppliers|jsonify}}{%endraw%}
28
+ ```
29
+
30
+ This will generate a `page.suppliers` array with all the suppliers, and a `page.FeaturedSupplier` object with the details of the featured supplier.
31
+
32
+ Each supplier will have a `products` array with all the products for that supplier.
33
+
34
+ {: .note }
35
+ > Per Page Query
36
+ >
37
+ > On the Demo website, a per-page query
38
+ > is used on the restock page to generate
39
+ > list of products that need to be restocked. You can see the
40
+ > [source](https://github.com/captn3m0/northwind/blob/main/restock.md?plain=1)
41
+ > and the [resulting page](https://northwind.captnemo.in/restock.html)
@@ -0,0 +1,32 @@
1
+ ---
2
+ title: Parametrized Queries
3
+ parent: Usage
4
+ nav_order: 1
5
+ ---
6
+ This plugin supports prepared queries with parameter binding. This lets you
7
+ use existing data from a previous query, or some other source (such as
8
+ `site.data.*` or `page.*`) as a parameter in your query.
9
+
10
+ Say you have a YAML file defining your items (`data/books.yaml`):
11
+
12
+ ```yaml
13
+ - id: 31323952-2708-42dc-a995-6006a23cbf00
14
+ name: Time Travel with a Rubber Band
15
+ - id: 5c8e67a0-d490-4743-b5b8-8e67bd1f95a2
16
+ name: The Art of Cache Invalidation
17
+ ```
18
+ and the prices for the items in your SQLite database, the following configuration will enrich the `items` array with the price:
19
+
20
+ ```yaml
21
+ sql:
22
+ - data: items.books
23
+ file: books.db
24
+ query: SELECT price, author FROM pricing WHERE id =:id
25
+ ```
26
+ This would allow the following Liquid loop to be written:
27
+
28
+ ```liquid{%raw%}
29
+ {% for item in site.data.items %}
30
+ {{item.meta.price}}, {{item.meta.author}}
31
+ {% endfor %}{%endraw%}
32
+ ```
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
11
11
 
12
12
  spec.summary = "A Jekyll plugin to use SQLite databases as a data source."
13
13
  spec.homepage = "https://github.com/captn3m0/jekyll-sqlite"
14
- spec.required_ruby_version = ">= 3.0.0"
14
+ spec.required_ruby_version = ">= 3.2.0"
15
15
 
16
16
  spec.metadata["homepage_uri"] = spec.homepage
17
17
  spec.metadata["source_code_uri"] = spec.homepage
@@ -27,7 +27,6 @@ Gem::Specification.new do |spec|
27
27
  spec.bindir = "exe"
28
28
  spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
29
29
  spec.require_paths = ["lib"]
30
-
31
- spec.add_dependency "sqlite3", "~> 2.0"
30
+ spec.add_dependency "sqlite3", "~> 2.7.3"
32
31
  spec.metadata["rubygems_mfa_required"] = "true"
33
32
  end
@@ -8,22 +8,40 @@ module JekyllSQlite
8
8
  # Set to high to be higher than the Jekyll Datapages Plugin
9
9
  priority :high
10
10
 
11
- ##
12
- # Split the given key using dots and return the last part
13
- # customers.order -> order
14
- def get_tip(name)
15
- name.split(".")[-1]
11
+ def get_database(file)
12
+ return @db[file] if @db.key?(file)
13
+
14
+ @db[file] = SQLite3::Database.new file, readonly: true
15
+ end
16
+
17
+ def close_all_databases
18
+ @db.each_value(&:close)
16
19
  end
17
20
 
18
21
  ##
19
- # Get the root of where we are generating the data
20
- def get_root(root, db_name)
21
- db_name.split(".")[0..-2].each do |p|
22
- root = root[p]
23
- rescue KeyError
24
- raise "Jekyll SQLite: Invalid root. #{p} not found while iterating to #{db_name}"
22
+ # Recursively attach query results to nested data structures
23
+ # Supports arbitrary levels of nesting (e.g., regions.territories.EmployeeIDs)
24
+ # Handles both arrays and hashes at each level
25
+ def attach_nested_data(root, path_segments, db, query)
26
+ return 0 if path_segments.empty?
27
+
28
+ if path_segments.size == 1
29
+ key = path_segments.first
30
+ db.prepare(query) do |stmt|
31
+ _prepare_query(stmt, get_bind_params(root))
32
+ root[key] = stmt.execute.to_a
33
+ end
34
+ return root[key].size
35
+ end
36
+
37
+ first, *remaining = path_segments
38
+ current_level = root[first]
39
+
40
+ if current_level.is_a?(Array)
41
+ current_level.sum { |item| attach_nested_data(item, remaining, db, query) }
42
+ else
43
+ attach_nested_data(current_level, remaining, db, query)
25
44
  end
26
- root
27
45
  end
28
46
 
29
47
  ##
@@ -38,38 +56,9 @@ module JekyllSQlite
38
56
  end
39
57
  end
40
58
 
41
- ##
42
- # Internal function to generate data given
43
- # root: a Hash-Like root object (site.data, site.data.*, page.data)
44
- # key: string as the key to use to attach the data to the root
45
- # db: SQLite3 Database object to execute the query on
46
- # query: string containing the query to execute
47
- # Sets root[db_name] = ResultSet of the query, as an array
48
- # Returns the count of the result set
49
- def _gen_data(root, key, db, query)
50
- db.prepare(query) do |stmt|
51
- _prepare_query stmt, get_bind_params(root)
52
- root[key] = stmt.execute.to_a
53
- end
54
- root[key].count
55
- end
56
-
57
- ##
58
- # Calls _gen_data for the given root
59
- # iterates through the array if root is an array
60
- def gen_data(root, ...)
61
- if root.is_a? Array
62
- # call gen_data for each item in the array
63
- # and return the sum of all the counts
64
- root.map { |item| gen_data(item, ...) }.sum
65
- else
66
- _gen_data(root, ...)
67
- end
68
- end
69
-
70
59
  ##
71
60
  # Validate given configuration object
72
- def validate_config(config)
61
+ def valid_config?(config)
73
62
  return false unless config.is_a? Hash
74
63
  return false unless config.key?("query")
75
64
  return false unless File.exist?(config["file"])
@@ -86,31 +75,16 @@ module JekyllSQlite
86
75
  dict.select { |_key, value| !value.is_a?(Array) && !value.is_a?(Hash) }
87
76
  end
88
77
 
89
- ##
90
- # Given a configuration, generate the data
91
- # and attach it to the given data_root
92
78
  def generate_data_from_config(root, config)
93
79
  key = config["data"]
94
80
  query = config["query"]
95
81
  file = config["file"]
96
- SQLite3::Database.new file, readonly: true do |db|
97
- db.results_as_hash = config.fetch("results_as_hash", true)
98
82
 
99
- branch = get_root(root, key)
100
- tip = get_tip(config["data"])
101
-
102
- count = gen_data(branch, tip, db, query)
103
- Jekyll.logger.info "Jekyll SQLite:", "Loaded #{key}. Count=#{count}"
104
- end
105
- end
106
-
107
- ##
108
- # Iterate through all the pages in the site
109
- # and generate the data from the configuration
110
- def gen_pages(site)
111
- site.pages.each do |page|
112
- gen(page.data, page)
113
- end
83
+ db = get_database(file)
84
+ db.results_as_hash = config.fetch("results_as_hash", true)
85
+ path_segments = key.split(".")
86
+ count = attach_nested_data(root, path_segments, db, query)
87
+ Jekyll.logger.info "Jekyll SQLite:", "Loaded #{key}. Count=#{count}"
114
88
  end
115
89
 
116
90
  ##
@@ -120,9 +94,8 @@ module JekyllSQlite
120
94
  # Root is either site.data or page.data
121
95
  # and config_holder is either site.config or page itself.
122
96
  def gen(root, config_holder)
123
- sqlite_configs = config_holder["sqlite"] || []
124
- sqlite_configs.each do |config|
125
- unless validate_config(config)
97
+ (config_holder["sqlite"] || []).each do |config|
98
+ unless valid_config?(config)
126
99
  Jekyll.logger.error "Jekyll SQLite:", "Invalid Configuration. Skipping"
127
100
  next
128
101
  end
@@ -133,8 +106,13 @@ module JekyllSQlite
133
106
  ##
134
107
  # Entrpoint to the generator, called by Jekyll
135
108
  def generate(site)
109
+ @db = {}
136
110
  gen(site.data, site.config)
137
- gen_pages(site)
111
+ site.pages.each do |page|
112
+ gen(page.data, page)
113
+ end
114
+ ensure
115
+ close_all_databases
138
116
  end
139
117
  end
140
118
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Sqlite
5
- VERSION = "0.1.5"
5
+ VERSION = "0.2.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll-sqlite
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nemo
8
- autorequire:
9
8
  bindir: exe
10
9
  cert_chain: []
11
- date: 2024-07-31 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: sqlite3
@@ -16,15 +15,14 @@ dependencies:
16
15
  requirements:
17
16
  - - "~>"
18
17
  - !ruby/object:Gem::Version
19
- version: '2.0'
18
+ version: 2.7.3
20
19
  type: :runtime
21
20
  prerelease: false
22
21
  version_requirements: !ruby/object:Gem::Requirement
23
22
  requirements:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
- version: '2.0'
27
- description:
25
+ version: 2.7.3
28
26
  email:
29
27
  - jekyll-sqlite@captnemo.in
30
28
  executables: []
@@ -37,6 +35,22 @@ files:
37
35
  - Gemfile
38
36
  - README.md
39
37
  - Rakefile
38
+ - docs/.gitignore
39
+ - docs/404.html
40
+ - docs/CHANGELOG.md
41
+ - docs/Gemfile
42
+ - docs/_config.yml
43
+ - docs/demo.md
44
+ - docs/img/northwind-1.jpg
45
+ - docs/img/northwind-2.jpg
46
+ - docs/index.md
47
+ - docs/install.md
48
+ - docs/usage/datapage.md
49
+ - docs/usage/dynamic.md
50
+ - docs/usage/index.md
51
+ - docs/usage/nested.md
52
+ - docs/usage/per-page.md
53
+ - docs/usage/writing-queries.md
40
54
  - jekyll-sqlite.gemspec
41
55
  - lib/jekyll-sqlite/generator.rb
42
56
  - lib/jekyll-sqlite/version.rb
@@ -49,7 +63,6 @@ metadata:
49
63
  source_code_uri: https://github.com/captn3m0/jekyll-sqlite
50
64
  changelog_uri: https://github.com/captn3m0/jekyll-sqlite/blob/master/CHANGELOG.md
51
65
  rubygems_mfa_required: 'true'
52
- post_install_message:
53
66
  rdoc_options: []
54
67
  require_paths:
55
68
  - lib
@@ -57,15 +70,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
70
  requirements:
58
71
  - - ">="
59
72
  - !ruby/object:Gem::Version
60
- version: 3.0.0
73
+ version: 3.2.0
61
74
  required_rubygems_version: !ruby/object:Gem::Requirement
62
75
  requirements:
63
76
  - - ">="
64
77
  - !ruby/object:Gem::Version
65
78
  version: '0'
66
79
  requirements: []
67
- rubygems_version: 3.5.11
68
- signing_key:
80
+ rubygems_version: 3.6.9
69
81
  specification_version: 4
70
82
  summary: A Jekyll plugin to use SQLite databases as a data source.
71
83
  test_files: []