diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 0000000..c870634 --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,39 @@ +name: CI + +on: + push: + branches: [ "main" ] + pull_request: + branches: [ "*" ] + +jobs: + test: + runs-on: ubuntu-latest + environment: Continuous Integration + strategy: + matrix: + emacs_version: + # 28.1 and 28.2 should be tested, but they are not working for reasons + # I haven't figured out yet, and I haven't been able to test manually + # for other reasons I haven't figured out yet. + - 29.1 + - 29.2 + steps: + - name: Set up Emacs + uses: jcs090218/setup-emacs@master + with: + version: ${{matrix.emacs_version}} + + - name: Install Eldev + uses: emacs-eldev/setup-eldev@v1 + + - name: Check out the source code + uses: actions/checkout@v4 + + - name: Lint the project + run: | + eldev -p -dtT lint + + - name: Test the project + run: | + eldev -p -dtT test diff --git a/Eldev b/Eldev new file mode 100644 index 0000000..f55d1a3 --- /dev/null +++ b/Eldev @@ -0,0 +1,13 @@ + ; -*- mode: emacs-lisp; lexical-binding: t -*- + +(eldev-use-package-archive 'melpa) +(eldev-use-plugin 'maintainer) +(eldev-add-extra-dependencies 'test '(:package emacsql)) +(eldev-add-extra-dependencies 'test '(:package kv)) +(eldev-add-extra-dependencies 'emacs '(:package emacsql)) +(eldev-add-extra-dependencies 'emacs '(:package kv)) + +;; To both deal with emacsql and built-in sqlite in various versions, we need to +;; weird things that package linting doesn't like. So we disable this specific +;; kind of linting. +(add-to-list 'eldev-lint-disabled 'package) diff --git a/triples-backups.el b/triples-backups.el index e382f5f..de69353 100644 --- a/triples-backups.el +++ b/triples-backups.el @@ -19,16 +19,18 @@ ;; along with GNU Emacs. If not, see . ;;; Commentary: -;; This provides backup functionality. The information about how and when to do +;; This provides backup functionality. The information about how and when to do ;; backups lives in the database itself, on a special entity `database'. (require 'triples) +;;; Code: + (defun triples-backups-setup (db num-to-keep strategy) "Set DB's backup strategy. -NUM-TO-KEEP is the number of backup files to keep. Older ones are -removed. STRATEGY is a symbol that corresponds to a function -`triples-backups-strategy-STRATEGY'. This function must always be +NUM-TO-KEEP is the number of backup files to keep. Older ones +are removed. STRATEGY is a symbol that corresponds to a function +`triples-backups-strategy-STRATEGY'. This function must always be loaded before any client of this db calls `triples-backups-maybe-backup', so adding your own may not always be appropriate." @@ -40,8 +42,10 @@ be appropriate." :strategy strategy :last-update-time (time-convert (current-time) 'integer)))) (defun triples-backups-configuration (db) - "Returns the backup configuration set by `triples-backups-setup'. -If no one has ever run that on this database, `nil' is returned." + "Return the backup configuration set by `triples-backups-setup'. +If no one has ever run that on this database, nil is returned. + +DB is the database to get the configuration for." (triples-get-type db 'database 'backup)) (defun triples-backups-last-update-time (db) @@ -57,7 +61,7 @@ default to the standard triple database given in (strategy-func (intern (format "triples-backups-strategy-%s" (plist-get backup-info :strategy))))) (unless backup-info - (error "`triples-backups-setup' needs to be called on this database before trying to back up.")) + (error "`triples-backups-setup' needs to be called on this database before trying to back up")) (unless (fboundp strategy-func) (display-warning 'triples diff --git a/triples-upgrade.el b/triples-upgrade.el index 4a144c0..62d2621 100644 --- a/triples-upgrade.el +++ b/triples-upgrade.el @@ -20,7 +20,7 @@ ;;; Commentary: ;; Occasionally, changes in the triples library are not backwards-compatible, -;; and require upgrading the database. This file contains functions to do those +;; and require upgrading the database. This file contains functions to do those ;; ugprades, along with instructions an how and when to use them. ;;; Code: @@ -32,10 +32,10 @@ (defun triples-upgrade-to-0.3 (db) "Upgrade the DB to version 0.3. This will convert all stringified integers stored with sqlite to -actual integers. On emacs version before 29, it will not do +actual integers. On Emacs version before 29, it will not do anything, since only the built-in sqlite data needs upgrading. Callers should force a backup to happen before calling this, -with `(triples-backup db file most-positive-fixnum)'. +by calling triples-backup with `most-positive-fixnum'. This function only handles the case where users transition from emacsql to sqlite, it is assumed that users don't transition from @@ -83,4 +83,5 @@ be correct by default." (message "Upgraded all stringified integers in triple database to actual integers")))) (provide 'triples-upgrade) -;; triples-upgrade ends here + +;;; triples-upgrade.el ends here diff --git a/triples.el b/triples.el index 31db47a..2295e98 100644 --- a/triples.el +++ b/triples.el @@ -22,11 +22,11 @@ ;;; Commentary: ;; Triples is a library implementing a data storage based on the idea of -;; triples: subject, predicate, objects, plus some extra metadata. This data +;; triples: subject, predicate, objects, plus some extra metadata. This data ;; structure provides a way to store data according to an extensible schema, and ;; provide an API offering two-way links between all information stored. ;; -;; This package requires either emacs 29 or the emacsql package to be installed. +;; This package requires either Emacs 29 or the emacsql package to be installed. (require 'cl-lib) @@ -48,18 +48,19 @@ 'builtin 'emacsql) "The interface to sqlite to use. -Either `builtin' or `emacsql'. Defaults to builtin when -available. Builtin is available when the version is Emacs 29 or +Either `builtin' or `emacsql'. Defaults to builtin when +available. Builtin is available when the version is Emacs 29 or greater, and emacsql is usable when the `emacsql' package is installed.") (defconst triples-sqlite-executable "sqlite3" - "If using emacs 29 builtin sqlite, this specifices the executable. + "If using Emacs 29 builtin sqlite, this specifices the executable. It is invoked to make backups.") (defconst triples-default-database-filename (locate-user-emacs-file "triples.db") - "The default filename triples database. If no database is -specified, this file is used.") + "The default filename triples database. + +If no database is specified, this file is used.") (defmacro triples-with-transaction (db &rest body) "Create a transaction using DB, executing BODY. @@ -101,7 +102,7 @@ If FILE is nil, use `triples-default-database-filename'." ('builtin (and (fboundp 'sqlite-available-p) (sqlite-available-p))) ('emacsql (require 'emacsql nil t))) - (error "The triples package requires either Emacs 29 or the emacsql package to be installed.")) + (error "The triples package requires either Emacs 29 or the emacsql package to be installed")) (let ((file (or file triples-default-database-filename))) (pcase triples-sqlite-interface ('builtin (let* ((db (sqlite-open file))) @@ -154,19 +155,19 @@ The first argument is unused, but later may be used to specify the running database. This uses the same backup location and names as configured in -variables such as `backup-directory-alist'. Due to the fact that +variables such as `backup-directory-alist'. Due to the fact that the database is never opened as a buffer, normal backups will not work, therefore this function must be called instead. Th DB argument is currently unused, but may be used in the future -if emacs's native sqlite gains a backup feature. +if Emacs's native sqlite gains a backup feature. FILENAME can be nil, if so `triples-default-database-filename' will be used. This also will clear excess backup files, according to NUM-TO-KEEP, which specifies how many backup files at max should -exist at any time. Older backups are the ones that are deleted." +exist at any time. Older backups are the ones that are deleted." (let ((filename (expand-file-name (or filename triples-default-database-filename)))) (call-process (pcase triples-sqlite-interface ('builtin triples-sqlite-executable) @@ -190,10 +191,11 @@ exist at any time. Older backups are the ones that are deleted." (intern (format ":%s" sym))) (defun triples-standardize-val (val) - "If VAL is a string, return it as enclosed in quotes + "If VAL is a string, return it as enclosed in quotes. + This is done to have compatibility with the way emacsql stores -values. Turn a symbol into a string as well, but not a quoted -one, because sqlite cannot handle symbols. Integers do not need +values. Turn a symbol into a string as well, but not a quoted +one, because sqlite cannot handle symbols. Integers do not need to be stringified." ;; Do not print control characters escaped - we want to get things out exactly ;; as we put them in. @@ -218,7 +220,7 @@ the string itself is wrapped in quotes." "Insert triple to DB: SUBJECT, PREDICATE, OBJECT with PROPERTIES. This is a SQL replace operation, because we don't want any duplicates; if the triple is the same, it has to differ at least -with PROPERTIES. This is a low-level function that bypasses our +with PROPERTIES. This is a low-level function that bypasses our normal schema checks, so should not be called from client programs." (unless (symbolp predicate) (error "Predicates in triples must always be symbols")) @@ -243,8 +245,8 @@ normal schema checks, so should not be called from client programs." (defun triples--emacsql-andify (wc) "In emacsql where clause WC, insert `:and' between query elements. -Returns the new list with the added `:and.'s. The first element -MUST be there `:where' clause. This does reverse the clause +Returns the new list with the added `:and.'s. The first element +MUST be there `:where' clause. This does reverse the clause elements, but it shouldn't matter." (cons (car wc) ;; the :where clause (let ((clauses (cdr wc)) @@ -257,7 +259,10 @@ elements, but it shouldn't matter." (defun triples-db-delete (db &optional subject predicate object properties) "Delete triples matching SUBJECT, PREDICATE, OBJECT, PROPERTIES. -If any of these are nil, they will not selected for. If you set + +DB is the database to delete from. + +If any of these are nil, they will not selected for. If you set all to nil, everything will be deleted, so be careful!" (pcase triples-sqlite-interface ('builtin (sqlite-execute @@ -290,7 +295,9 @@ all to nil, everything will be deleted, so be careful!" (seq-filter #'identity (list subject predicate object properties))))))) (defun triples-db-delete-subject-predicate-prefix (db subject pred-prefix) - "Delete triples matching SUBJECT and predicates with PRED-PREFIX." + "Delete triples matching SUBJECT and predicates with PRED-PREFIX. + +DB is the database to delete from." (unless (symbolp pred-prefix) (error "Predicates in triples must always be symbols")) (pcase triples-sqlite-interface @@ -301,11 +308,14 @@ all to nil, everything will be deleted, so be careful!" subject (format "%s/%%" (triples--decolon pred-prefix)))))) (defun triples-db-select-pred-op (db pred op val &optional properties) - "Select triples matching predicates with PRED having OP relation to VAL. -OP is a comparison operator, and VAL is the value to compare. OP, -the comparison operator, is a symbol for a standard numerical -comparison such as `=', `!=', `>', or, when `val' is a strings, -`like'. All alphabetic comparison is case insensitive. + "Select matching predicates with PRED having OP relation to VAL. + +DB is the database to select from. + +OP is a comparison operator, and VAL is the value to compare. It +is a symbol for a standard numerical comparison such as `=', +`!=', `>', or, when `val' is a strings, `like'. All alphabetic +comparison is case insensitive. If PROPERTIES is given, triples must match the given properties." (unless (symbolp pred) @@ -344,7 +354,7 @@ If PROPERTIES is given, triples must match the given properties." pred val properties))))) (defun triples-db-select-pred-prefix (db subject pred-prefix) - "Return rows matching SUBJECT and PRED-PREFIX." + "Return rows in DB matching SUBJECT and PRED-PREFIX." (pcase triples-sqlite-interface ('builtin (mapcar (lambda (row) (mapcar #'triples-standardize-result row)) (sqlite-select db "SELECT * FROM triples WHERE subject = ? AND predicate LIKE ?" @@ -355,8 +365,11 @@ If PROPERTIES is given, triples must match the given properties." (defun triples-db-select (db &optional subject predicate object properties selector) "Return rows matching SUBJECT, PREDICATE, OBJECT, PROPERTIES. + +DB is the database to select from. + If any of these are nil, they are not included in the select -statement. The SELECTOR is list of symbols subject, precicate, +statement. The SELECTOR is list of symbols subject, precicate, object, properties to retrieve or nil for *." (pcase triples-sqlite-interface ('builtin (mapcar (lambda (row) (mapcar #'triples-standardize-result row)) @@ -404,7 +417,7 @@ object, properties to retrieve or nil for *." Any references to OLD-SUBJECT as an object are also replaced. This will throw an error if there is an existing subject NEW-SUBJECT with at least one equal property (such as type -markers). But if there are no commonalities, the OLD-SUBJECT is +markers). But if there are no commonalities, the OLD-SUBJECT is merged into NEW-SUBJECT." (pcase triples-sqlite-interface ('builtin @@ -521,7 +534,7 @@ them." pprops)))))))) (defun triples-remove-schema-type (db type) - "This removes the schema for TYPE in DB, and all associated data." + "Remove the schema for TYPE in DB, and all associated data." (triples-with-transaction db (let ((subjects (triples-subjects-of-type db type))) @@ -561,13 +574,15 @@ PROPERTIES is a plist of properties, without TYPE prefixes." (triples--add db op))) (defmacro triples--eval-when-fboundp (sym form) - "Delay macroexpansion to runtime if SYM is not yet `fboundp'." + "Delay macroexpansion to runtime if SYM is not yet `fboundp'. +FORM is the code to delay." (declare (indent 1) (debug (symbolp form))) (if (fboundp sym) form `(eval ',form t))) (defun triples--with-transaction (db body-fun) + "Wrap BODY-FUN in a transaction for DB." (pcase triples-sqlite-interface ('builtin (condition-case err (progn @@ -584,7 +599,7 @@ PROPERTIES is a plist of properties, without TYPE prefixes." (defun triples-set-types (db subject &rest combined-props) "Set all data for types in COMBINED-PROPS in DB for SUBJECT. COMBINED-PROPS is a plist which takes combined properties such as -:named/name and their values. All other data related to the types +:named/name and their values. All other data related to the types given in the COMBINED-PROPS will be removed." (let ((type-to-plist (make-hash-table))) (triples--plist-mapc @@ -759,7 +774,10 @@ FN must take two arguments: the key and the value." triple type (type-of (nth 2 triple))))) (defun triples-verify-base/virtual-reversed-compliant (_ triple) - "Virtual reversed properties shouldn't be set manually, so are never compliant." + "Reject any TRIPLE with a virtual reversed property. + +Virtual reversed properties shouldn't be set manually, so are +never compliant." (error "Invalid triple found: %s, should not be setting a `base/virtual-reversed' property" triple))