jekyll-sqlite 0.1.5 → 0.2.1

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: b968c4b7df9bfd1fbe127a29743b8a9ddc823b7db6438975621b63e7307e26f7
4
+ data.tar.gz: 8906f3acfec3fedf578755563953d2c5563f49cac45a4bb24b77d32ba2899d97
5
5
  SHA512:
6
- metadata.gz: 769762e5786644f055002eae18dd443e12989242f24f5ce79cbd03dcea235bf73cc7e6b4c6a70b30dac024d03ca03e84fbe5f16ef5cdf9289cd930b8ccbeedf4
7
- data.tar.gz: 0c950eca035285304de7900e780b41e769340083b9966c2a01df02a4b10330bc8917cbd63be53f84852ceb1e8dfb7f7d4845c73c89afa0fa8f4719c8dfad82fb
6
+ metadata.gz: a9d02570d9db93418a10958a727917b2c37f56a5d4995185e9dfed857c7fabc2315c51229b59b9ec9c193de4dd5184bea067bbb16cfcb245096f167ec70af476
7
+ data.tar.gz: 53599208a2c848d8ca4cce454184d2da3d434f20f952d7b882a5b68c852934746885d586ed98544e2db9428ff9d018fb597ae17dc1420384d69f449b811fddd4
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,29 @@
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.1] - 2026-01-03
14
+ - Only passes named parameters in SQLite query. Requires sqlite3-ruby 2.9.0
15
+
16
+ ## [0.2.0] - 2025-08-23
17
+ - Multiple-levels of nesting is now supported
18
+ - Slight performance improvement by keeping database open for entire plugin run
19
+
20
+ ## [0.1.6] - 2025-07-31
21
+ - Drop Ruby 3.1
22
+ - sqlite3 requirement to `2.7.3`
23
+
24
+ ## [0.1.5] - 2024-07-31
25
+ - sqlite3 requirement changed to `1.6`
26
+
3
27
  ## [0.1.4] - 2024-07-17
4
28
  - Per-page queries are now supported via a `sqlite` config block in the front matter.
5
29
  - Documents support for existing site data being used within queries.
@@ -12,4 +36,4 @@
12
36
 
13
37
  ## [0.1.0] - 2023-05-08
14
38
 
15
- - Initial release
39
+ - Initial release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,46 @@
1
+ ---
2
+ title: Contribution Guide
3
+ ---
4
+
5
+ # Contributing to jekyll-sqlite
6
+
7
+ **This document is a work-in-progress.**
8
+
9
+ This doc is a short introduction on how to modify and maintain the sqlite3-ruby gem.
10
+
11
+ ## Making a Release
12
+
13
+ 0. Update `version.rb`
14
+ 0. Update CHANGELOG.md
15
+ 1. Run `bundle exec rake rubocop` to lint
16
+ 2. Commit + push
17
+ 3. If build passes, tag and push the tag.
18
+
19
+ Gem publication on rubygems automatically happens via GitHub Actions.
20
+
21
+ ## Running Tests
22
+
23
+ `bundle exec rake test`
24
+
25
+ ## Test Infrastructure
26
+
27
+ The tests are maintained in `test` directory as a separate jekyll website.
28
+ The site is built inside `Rakefile`, and it uses JSON output files as templates.
29
+
30
+ These JSON output files can then be used for testing the plugin.
31
+
32
+ ## Rubocop
33
+
34
+ Linting is mandatory to pass the CI.
35
+
36
+ ## Docs
37
+
38
+ Docs are maintained in docs/ directory as a separate Jekyll site that uses
39
+ just-the-docs theme. A few markdown files are symlinked inside docs so that
40
+ they get published to the website as well.
41
+
42
+ ## Demo
43
+
44
+ The demo is maintained separately on another repo, but the expectation is that
45
+ all important features are used in the demo. If you contribute such a change
46
+ that adds a new feature, please update the demo as well.
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"
13
-
14
- # Ruby 3.4 preparedness
15
- gem "base64", "~> 0.2.0"
16
- gem "bigdecimal", "~> 3.1"
17
- gem "csv", "~> 3.3"
9
+ gem "erb", "~> 6.0"
10
+ gem "jekyll", "~> 4.4", ">= 4.4.1"
11
+ gem "logger", "~> 1.7"
12
+ gem "rake", "~> 13.3"
13
+ gem "rubocop", "~> 1.80"
14
+ gem "rubocop-rake", "~> 0.7"
data/LICENSE ADDED
@@ -0,0 +1,8 @@
1
+ The MIT License (MIT)
2
+ Copyright (c) Nemo
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
5
+
6
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
7
+
8
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
@@ -0,0 +1 @@
1
+ ../CONTRIBUTING.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,30 @@
1
+ ---
2
+ title: Demo
3
+ ---
4
+
5
+ 🏁 A fully-functional demo website that uses this plugin is available at
6
+ [northwind.captnemo.in](https://northwind.captnemo.in). The source code for
7
+ the demo is available at [captn3m0/northwind](https://github.com/captn3m0/northwind).
8
+ You can find more details at the demo page.
9
+
10
+ Here is a screenshot:
11
+
12
+ ![Screenshot of https://northwind.captnemo.in/products.html](img/northwind-1.jpg)
13
+
14
+ It relies on all features of the plugin, along with using `jekyll-datapage_gen`
15
+ plugin to generate individual pages for each data item.
16
+
17
+ 1. A [per-page query](usage/#per-page-queries) is used on the restock page to generate list of
18
+ products that need to be restocked. [source](https://github.com/captn3m0/northwind/blob/main/restock.md?plain=1)
19
+ 2. Customers, Orders, Products, Categories are set as global data items
20
+ in [config.yml](https://github.com/captn3m0/northwind/blob/main/_config.yml)
21
+ 3. `site.data.categories[*].products` is filled using a parameterised query
22
+ in [`config.yml`](https://github.com/captn3m0/northwind/blob/main/_config.yml#L47-L49)
23
+ 4. Featured Product and Employee of the Month, shown on homepage are set by a query
24
+ in `config.yml`, but the query parameters are set in [`_data`](https://github.com/captn3m0/northwind/tree/main/_data)
25
+ directory as YML files.
26
+ 5. The datapage plugin config generates a page for every product and customer.
27
+ 6. A multi-level nested query is used to generate a list of employees. See [Nested Query]({% link usage/nested.md %}) in docs.
28
+ 7. A permalink is set for all the customers by creating a permalink attribute in the select query: `SELECT ... as permalink`. Since we are setting a top-level attribute in the final page, it cannot be set alongside `page_data_prefix` in the datapage_gen configuration. See [this commit](https://github.com/captn3m0/northwind/commit/3d70d6a81be34af5f1ebbcfa5da09a170f2ee9ff) for the implementation. I'd suggest only using this when the `dir + name/name_expr` configuration in the `datapage` plugin fall short.
29
+
30
+ The database is a trimmed-version of the northwind database from <https://github.com/jpwhite3/northwind-SQLite3>.
data/docs/help.md ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ title: Help
3
+ ---
4
+
5
+ Need help? You can file a new issue on GitHub at
6
+ <https://github.com/captn3m0/jekyll-sqlite/issues/new>.
7
+
8
+ This project is intended to be a safe, welcoming space for collaboration, and
9
+ contributors are expected to adhere to the [code of conduct][coc].
10
+
11
+ Note that only maintained versions of [Jekyll](https://endoflife.date/jekyll)
12
+ and [Ruby](https://endoflife.date/ruby) are supported.
13
+
14
+ [coc]: https://github.com/captn3m0/jekyll-sqlite/blob/main/CODE_OF_CONDUCT.md
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://rubygems.org/gems/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]({% link demo.md %}).
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]({% link demo.md %}):
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.9.0"
32
31
  spec.metadata["rubygems_mfa_required"] = "true"
33
32
  end
@@ -8,68 +8,59 @@ 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
16
15
  end
17
16
 
18
- ##
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}"
25
- end
26
- root
17
+ def close_all_databases
18
+ @db.each_value(&:close)
27
19
  end
28
20
 
29
21
  ##
30
- # Prepare the query by binding the parameters
31
- # Since we don't know if the query needs them
32
- # we ignore all errors about "no such bind parameter"
33
- def _prepare_query(stmt, params)
34
- params.each do |key, value|
35
- stmt.bind_param key, value
36
- rescue StandardError => e
37
- raise e unless e.message.include? "no such bind parameter"
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
38
35
  end
39
- end
40
36
 
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
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)
53
44
  end
54
- root[key].count
55
45
  end
56
46
 
57
47
  ##
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, ...)
48
+ # Prepare the query by binding the parameters
49
+ # Since we don't know if the query needs them
50
+ # we ignore all errors about "no such bind parameter"
51
+ def _prepare_query(stmt, params)
52
+ stmt.named_params.each do |key|
53
+ val = params[key]
54
+ unless [Integer, String, Float, SQLite3::Blob, nil].include? val.class
55
+ Jekyll.logger.error "#{key} type is #{val.class} in query: #{stmt.get_sql}"
56
+ end
57
+ stmt.bind_param key, params[key]
67
58
  end
68
59
  end
69
60
 
70
61
  ##
71
62
  # Validate given configuration object
72
- def validate_config(config)
63
+ def valid_config?(config)
73
64
  return false unless config.is_a? Hash
74
65
  return false unless config.key?("query")
75
66
  return false unless File.exist?(config["file"])
@@ -86,31 +77,16 @@ module JekyllSQlite
86
77
  dict.select { |_key, value| !value.is_a?(Array) && !value.is_a?(Hash) }
87
78
  end
88
79
 
89
- ##
90
- # Given a configuration, generate the data
91
- # and attach it to the given data_root
92
80
  def generate_data_from_config(root, config)
93
81
  key = config["data"]
94
82
  query = config["query"]
95
83
  file = config["file"]
96
- SQLite3::Database.new file, readonly: true do |db|
97
- db.results_as_hash = config.fetch("results_as_hash", true)
98
84
 
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
85
+ db = get_database(file)
86
+ db.results_as_hash = config.fetch("results_as_hash", true)
87
+ path_segments = key.split(".")
88
+ count = attach_nested_data(root, path_segments, db, query)
89
+ Jekyll.logger.info "Jekyll SQLite:", "Loaded #{key}. Count=#{count}"
114
90
  end
115
91
 
116
92
  ##
@@ -120,9 +96,8 @@ module JekyllSQlite
120
96
  # Root is either site.data or page.data
121
97
  # and config_holder is either site.config or page itself.
122
98
  def gen(root, config_holder)
123
- sqlite_configs = config_holder["sqlite"] || []
124
- sqlite_configs.each do |config|
125
- unless validate_config(config)
99
+ (config_holder["sqlite"] || []).each do |config|
100
+ unless valid_config?(config)
126
101
  Jekyll.logger.error "Jekyll SQLite:", "Invalid Configuration. Skipping"
127
102
  next
128
103
  end
@@ -133,8 +108,13 @@ module JekyllSQlite
133
108
  ##
134
109
  # Entrpoint to the generator, called by Jekyll
135
110
  def generate(site)
111
+ @db = {}
136
112
  gen(site.data, site.config)
137
- gen_pages(site)
113
+ site.pages.each do |page|
114
+ gen(page.data, page)
115
+ end
116
+ ensure
117
+ close_all_databases
138
118
  end
139
119
  end
140
120
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Jekyll
4
4
  module Sqlite
5
- VERSION = "0.1.5"
5
+ VERSION = "0.2.1"
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.1
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.9.0
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.9.0
28
26
  email:
29
27
  - jekyll-sqlite@captnemo.in
30
28
  executables: []
@@ -34,9 +32,29 @@ files:
34
32
  - ".rubocop.yml"
35
33
  - CHANGELOG.md
36
34
  - CODE_OF_CONDUCT.md
35
+ - CONTRIBUTING.md
37
36
  - Gemfile
37
+ - LICENSE
38
38
  - README.md
39
39
  - Rakefile
40
+ - docs/.gitignore
41
+ - docs/404.html
42
+ - docs/CHANGELOG.md
43
+ - docs/CONTRIBUTING.md
44
+ - docs/Gemfile
45
+ - docs/_config.yml
46
+ - docs/demo.md
47
+ - docs/help.md
48
+ - docs/img/northwind-1.jpg
49
+ - docs/img/northwind-2.jpg
50
+ - docs/index.md
51
+ - docs/install.md
52
+ - docs/usage/datapage.md
53
+ - docs/usage/dynamic.md
54
+ - docs/usage/index.md
55
+ - docs/usage/nested.md
56
+ - docs/usage/per-page.md
57
+ - docs/usage/writing-queries.md
40
58
  - jekyll-sqlite.gemspec
41
59
  - lib/jekyll-sqlite/generator.rb
42
60
  - lib/jekyll-sqlite/version.rb
@@ -49,7 +67,6 @@ metadata:
49
67
  source_code_uri: https://github.com/captn3m0/jekyll-sqlite
50
68
  changelog_uri: https://github.com/captn3m0/jekyll-sqlite/blob/master/CHANGELOG.md
51
69
  rubygems_mfa_required: 'true'
52
- post_install_message:
53
70
  rdoc_options: []
54
71
  require_paths:
55
72
  - lib
@@ -57,15 +74,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
57
74
  requirements:
58
75
  - - ">="
59
76
  - !ruby/object:Gem::Version
60
- version: 3.0.0
77
+ version: 3.2.0
61
78
  required_rubygems_version: !ruby/object:Gem::Requirement
62
79
  requirements:
63
80
  - - ">="
64
81
  - !ruby/object:Gem::Version
65
82
  version: '0'
66
83
  requirements: []
67
- rubygems_version: 3.5.11
68
- signing_key:
84
+ rubygems_version: 3.6.9
69
85
  specification_version: 4
70
86
  summary: A Jekyll plugin to use SQLite databases as a data source.
71
87
  test_files: []