From b31379af34f67a7f34c1bdd7f28451eb6ddde209 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Wed, 3 Dec 2014 00:43:06 -0500 Subject: [PATCH 01/11] Provide framework for revamped caching system Begins to address #88. --- sx-cache.el | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/sx-cache.el b/sx-cache.el index e68397dd..49ee3c7b 100644 --- a/sx-cache.el +++ b/sx-cache.el @@ -34,6 +34,47 @@ :type 'directory :group 'sx) +(defvar sx-cache--cache-volatility-alist + nil + "Known caches and their states. +The list consists of cons cells (CACHE . STATE). STATE is a +vector [VARIABLE VOLATILE READ]: + + VARIABLE symbol for variable + VOLATILE t if volatile + READ t if variable's value has been read from disk") + +(defun sx-cache-register (cache variable isvolatile) + "Register CACHE with VARIABLE and mark with ISVOLATILE. +See `sx-cache--cache-volatility-alist'." + (add-to-list 'sx-cache--cache-volatility-alist + (cons cache (vector variable isvolatile nil)))) + +(defun sx-cache--variable-value (cache) + "Returns the value of the variable bound to CACHE. +See `sx-cache--variable-name'." + (symbol-value (sx-cache--variable-name cache))) + +(defun sx-cache--variable-name (cache) + "Return the variable name CACHE is bound to." + (elt (sx-cache--state cache) 0)) + +(defun sx-cache--volatile-p (cache) + "Return t when CACHE is volatile." + (elt (sx-cache--state cache) 1)) + +(defun sx-cache--read-p (cache) + "Return t when CACHE has been read from disk." + (elt (sx-cache--state cache) 2)) + +(defun sx-cache--state (cache) + "Ensure CACHE is registered and return its state. +See `sx-cache--cache-volatility-alist'. + +If CACHE is not registered, `unknown-cache' is signaled." + (or (cdr (assoc cache sx-cache--cache-volatility-alist)) + (signal 'unknown-cache cache))) + (defun sx-cache--ensure-sx-cache-directory-exists () "Ensure `sx-cache-directory' exists." (unless (file-exists-p sx-cache-directory) From 14f74080c24c404cc3dfafdc2736e6f9fb0f4ada Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 30 Jan 2015 01:01:56 -0600 Subject: [PATCH 02/11] Use stash.el library This commit will break Travis for this branch. stash.el has yet to be released. It will be released to MELPA and then to ELPA pending paperwork (at which point it will be removed from MELPA). --- sx-cache.el | 153 +++++++++++++++------------------------------------- sx.el | 2 +- 2 files changed, 43 insertions(+), 112 deletions(-) diff --git a/sx-cache.el b/sx-cache.el index 089247c2..7654faf3 100644 --- a/sx-cache.el +++ b/sx-cache.el @@ -26,129 +26,60 @@ ;; `(sx-cache-get 'pkg)' ;; ;; This symbol is then converted into a filename within -;; `sx-cache-directory' using `sx-cache-get-file-name'. -;; -;; Currently, the cache is written at every `sx-cache-set', but this -;; write will eventually be done by some write-all function which will -;; be set on an idle timer. +;; `sx-cache-directory'. ;;; Code: +(require 'stash) + (defcustom sx-cache-directory (locate-user-emacs-file ".sx") "Directory containing cached data." :type 'directory :group 'sx) -(defvar sx-cache--cache-volatility-alist - nil - "Known caches and their states. -The list consists of cons cells (CACHE . STATE). STATE is a -vector [VARIABLE VOLATILE READ]: - - VARIABLE symbol for variable - VOLATILE t if volatile - READ t if variable's value has been read from disk") - -(defun sx-cache-register (cache variable isvolatile) - "Register CACHE with VARIABLE and mark with ISVOLATILE. -See `sx-cache--cache-volatility-alist'." - (add-to-list 'sx-cache--cache-volatility-alist - (cons cache (vector variable isvolatile nil)))) - -(defun sx-cache--variable-value (cache) - "Returns the value of the variable bound to CACHE. -See `sx-cache--variable-name'." - (symbol-value (sx-cache--variable-name cache))) - -(defun sx-cache--variable-name (cache) - "Return the variable name CACHE is bound to." - (elt (sx-cache--state cache) 0)) - -(defun sx-cache--volatile-p (cache) - "Return t when CACHE is volatile." - (elt (sx-cache--state cache) 1)) - -(defun sx-cache--read-p (cache) - "Return t when CACHE has been read from disk." - (elt (sx-cache--state cache) 2)) - -(defun sx-cache--state (cache) - "Ensure CACHE is registered and return its state. -See `sx-cache--cache-volatility-alist'. - -If CACHE is not registered, `unknown-cache' is signaled." - (or (cdr (assoc cache sx-cache--cache-volatility-alist)) - (signal 'unknown-cache cache))) - -(defun sx-cache--ensure-sx-cache-directory-exists () - "Ensure `sx-cache-directory' exists." - (unless (file-exists-p sx-cache-directory) - (mkdir sx-cache-directory))) - -(defun sx-cache-get-file-name (filename) - "Expand FILENAME in the context of `sx-cache-directory'." - (expand-file-name - (concat (symbol-name filename) ".el") - sx-cache-directory)) - -(defun sx-cache-get (cache &optional form) - "Return the data within CACHE. -If CACHE does not exist, use `sx-cache-set' to set CACHE to the -result of evaluating FORM. - -CACHE is resolved to a file name by `sx-cache-get-file-name'." - (sx-cache--ensure-sx-cache-directory-exists) - (let ((file (sx-cache-get-file-name cache))) - ;; If the file exists, return the data it contains - (if (file-exists-p file) - (with-temp-buffer - (insert-file-contents (sx-cache-get-file-name cache)) - (read (buffer-string))) - ;; Otherwise, set CACHE to the evaluation of FORM. - ;; `sx-cache-set' returns the data that CACHE was set to. - (sx-cache-set cache (eval form))))) - -(defun sx-cache-set (cache data) - "Set the content of CACHE to DATA and save. -DATA will be written as returned by `prin1'. - -CACHE is resolved to a file name by `sx-cache-get-file-name'." - (sx-cache--ensure-sx-cache-directory-exists) - (let (print-length print-level) - (write-region (prin1-to-string data) nil - (sx-cache-get-file-name cache))) - data) - -(defun sx-cache--invalidate (cache &optional vars init-method) - "Set cache CACHE to nil. - -VARS is a list of variables to unbind to ensure cache is cleared. -If INIT-METHOD is defined, call it after all invalidation to -re-initialize the cache." - (let ((file (sx-cache-get-file-name cache))) - (delete-file file)) - (mapc #'makunbound vars) - (when init-method - (funcall init-method))) - -(defun sx-cache-invalidate-all (&optional save-auth) - "Invalidate all caches using `sx-cache--invalidate'. -Afterwards reinitialize caches using `sx-initialize'. If -SAVE-AUTH is non-nil, do not clear AUTH cache. +(defcustom sx-cache-write-delay 5 + "Idle delay in seconds to write cache data." + :type 'integer + :group 'sx) -Interactively only clear AUTH cache if prefix arg was given. +(defvar sx-cache--list nil + "List of cache variables.") + +(defmacro sx-cache-new (variable &optional default) + `(progn + (stash-new ',variable + ,(expand-file-name + (concat (symbol-name variable) ".el") + sx-cache-directory) + ,default + ,sx-cache-write-delay) + (add-to-list 'sx-cache--list ,variable))) + +(defun sx-cache-read (cache) + "Return the data within CACHE." + (stash-load cache)) + +(defun sx-cache-set (cache data &optional immediate) + "Set the content of CACHE to DATA. +If IMMEDIATE is non-nil, immediately write to disk." + (stash-set cache data immediate)) + +(defun sx-cache--invalidate (cache) + "Invalidate CACHE." + (stash-reset cache)) + +(defun sx-cache-invalidate-all (&rest except) + "Invalidate all caches using `sx-cache--invalidate'. +Afterwards reinitialize caches using `sx-initialize'. All those +cache symbols in EXCEPT are spared. Interactively, this includes +only `auth'. Note: This will also remove read/unread status of questions as well as delete the list of hidden questions." - (interactive (list (not current-prefix-arg))) - (let* ((default-directory sx-cache-directory) - (caches (file-expand-wildcards "*.el"))) - (when save-auth - (setq caches (cl-remove-if (lambda (x) - (string= x "auth.el")) caches))) - (lwarn 'sx :debug "Invalidating: %S" caches) - (mapc #'delete-file caches) - (sx-initialize 'force))) + (interactive (list (unless current-prefix-arg '(auth)))) + (mapc #'sx-cache--invalidate + (cl-set-difference sx-cache--list except)) + (sx-initialize 'force)) (provide 'sx-cache) ;;; sx-cache.el ends here diff --git a/sx.el b/sx.el index 26151b37..37bfe5af 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "1.0")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by From 7fd24861c45569f25e8b2186fd561d3c191449d2 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Fri, 30 Jan 2015 19:33:22 -0600 Subject: [PATCH 03/11] Bump stash version down to released --- sx.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx.el b/sx.el index 37bfe5af..57c89902 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "1.0")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.2")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by From 4ac91ac8a3502f6b726726a2194bb8e977247324 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 01:15:43 -0600 Subject: [PATCH 04/11] Use stash.el This obsoletes sx-cache. --- sx-auth.el | 24 ++++++++++---------- sx-favorites.el | 16 ++++++------- sx-filter.el | 24 +++++++++----------- sx-load.el | 1 - sx-method.el | 14 +++++++----- sx-networks.el | 20 ++++++++--------- sx-question.el | 60 ++++++++++++++++++++++++------------------------- sx-request.el | 30 ++++++++++++------------- sx-site.el | 17 +++++++++----- sx.el | 21 ++++++++--------- sx.org | 2 -- 11 files changed, 110 insertions(+), 119 deletions(-) diff --git a/sx-auth.el b/sx-auth.el index cba310d0..1164e051 100644 --- a/sx-auth.el +++ b/sx-auth.el @@ -31,6 +31,7 @@ (require 'sx) (require 'sx-request) (require 'sx-cache) +(require 'stash) (defconst sx-auth-root "https://stackexchange.com/oauth/dialog") @@ -38,14 +39,16 @@ "http://vermiculus.github.io/sx.el/auth/auth.htm") (defconst sx-auth-client-id "3291") -(defvar sx-auth-access-token - nil + +(defstash sx-auth-access-token + "auth.el" sx nil "Your access token. This is needed to use your account to write questions, make comments, and read your inbox. Do not alter this unless you know what you are doing! This variable is set with `sx-auth-authenticate'.") +(sx-init-variable sx-auth-access-token) (defconst sx-auth-method-auth '((me . t) @@ -74,12 +77,10 @@ This variable is set with `sx-auth-authenticate'.") render upvote (unanswered my-tags))) - "List of methods that require auth. -Methods are of the form \(METHOD . SUBMETHODS) where SUBMETHODS - is \(METHOD METHOD METHOD ...). - -If all SUBMETHODS require auth or there are no submethods, form -will be \(METHOD . t)") + "List of methods that require authentication. +Methods are of the form \(METHOD SUBMETHOD...). If all +SUBMETHODS require authentication or there are no submethods, +form will be \(METHOD . t)") (defconst sx-auth-filter-auth '(question.upvoted @@ -133,10 +134,9 @@ parsed and displayed prominently on the page)." ",")))) (browse-url url) (read-string "Enter the access token displayed on the webpage: "))) - (if (string-equal "" sx-auth-access-token) - (progn (setq sx-auth-access-token nil) - (error "You must enter this code to use this client fully")) - (sx-cache-set 'auth `((access_token . ,sx-auth-access-token))))) + (when (string-equal "" sx-auth-access-token) + (progn (setq sx-auth-access-token nil) + (error "You must enter this code to use this client fully")))) (defun sx-auth--method-p (method &optional submethod) "Check if METHOD is one that may require authentication. diff --git a/sx-favorites.el b/sx-favorites.el index 444df29d..567fb640 100644 --- a/sx-favorites.el +++ b/sx-favorites.el @@ -25,16 +25,16 @@ ;;; Code: (require 'sx-method) -(require 'sx-cache) (require 'sx-site) (require 'sx-networks) (require 'sx-filter) +(require 'stash) (defconst sx-favorite-list-filter (sx-filter-from-nil (question.question_id))) -(defvar sx-favorites--user-favorite-list nil +(defstash sx-favorites--user-favorite-list "favorites.el" sx nil "Alist of questions favorited by the user. Each element has the form (SITE FAVORITE-LIST). And each element in FAVORITE-LIST is the numerical QUESTION_ID.") @@ -42,9 +42,8 @@ in FAVORITE-LIST is the numerical QUESTION_ID.") (defun sx-favorites--initialize () "Ensure question-favorites cache is available. Added as hook to initialization." - (or (setq sx-favorites--user-favorite-list - (sx-cache-get 'question-favorites)) - (sx-favorites-update))) + (or (stash-load 'sx-favorites--user-favorite-list) + (sx-favorites-update))) ;; ;; Append to ensure `sx-network--initialize' is run before it. ;; This is removed for now because it performs a lot of API calls and ;; was never used. @@ -62,13 +61,12 @@ Added as hook to initialization." "Update list of starred QUESTION_IDs for SITE. Writes list to cache QUESTION-FAVORITES." (let* ((favs (sx-favorites--retrieve-favorites site)) - (site-cell (assoc site - sx-favorites--user-favorite-list)) + (site-cell (assoc site sx-favorites--user-favorite-list)) (fav-cell (mapcar #'cdar favs))) (if site-cell (setcdr site-cell fav-cell) - (push (cons site fav-cell) sx-favorites--user-favorite-list)) - (sx-cache-set 'question-favorites sx-favorites--user-favorite-list))) + (push (cons site fav-cell) + sx-favorites--user-favorite-list)))) (defun sx-favorites-update () "Update all sites retrieved from `sx-network--user-sites'." diff --git a/sx-filter.el b/sx-filter.el index 1ccf611e..08c4d532 100644 --- a/sx-filter.el +++ b/sx-filter.el @@ -29,20 +29,17 @@ ;;; Dependencies (require 'sx) -(require 'sx-cache) (require 'sx-request) +(require 'stash) - -;;; Customizations - -(defvar sx--filter-alist - (sx-cache-get 'filter) - "An alist of known filters. See `sx-filter-compile'. +(defstash sx--filter-alist "filter.el" sx nil + "An alist of known filters. See `sx-filter-compile'. Structure: - (((INCLUDE EXCLUDE BASE ) . \"compiled filter \") - ((INCLUDE2 EXCLUDE2 BASE2) . \"compiled filter2\") - ...)") + (((INCLUDE EXCLUDE BASE) . \"compiled filter\")...)") + + +;;; Customizations ;;; Creation @@ -94,13 +91,12 @@ Returns the compiled filter as a string." If the filter data exists in `sx--filter-alist', that value will be returned. Otherwise, compile INCLUDE, EXCLUDE, and BASE into a filter with `sx-filter-compile' and push the association onto -`sx--filter-alist'. Re-cache the alist with `sx-cache-set' and -return the compiled filter." +`sx--filter-alist'. Return the compiled filter." (or (cdr (assoc (list include exclude base) sx--filter-alist)) (let ((filter (sx-filter-compile include exclude base))) (when filter - (push (cons (list include exclude base) filter) sx--filter-alist) - (sx-cache-set 'filter sx--filter-alist) + (push (cons (list include exclude base) filter) + sx--filter-alist) filter)))) diff --git a/sx-load.el b/sx-load.el index 003f965a..aa741d19 100644 --- a/sx-load.el +++ b/sx-load.el @@ -26,7 +26,6 @@ sx-auth sx-button sx-babel - sx-cache sx-compose sx-encoding sx-favorites diff --git a/sx-method.el b/sx-method.el index 9d61e601..1a9dc288 100644 --- a/sx-method.el +++ b/sx-method.el @@ -92,8 +92,7 @@ See `sx-request-make' and `sx-request-all-items'. Return the entire response as a complex alist." (declare (indent 1)) - (let ((access-token (sx-cache-get 'auth)) - (method-auth (sx-auth--method-p method submethod)) + (let ((method-auth (sx-auth--method-p method submethod)) (filter-auth (sx-auth--filter-p filter)) (full-method (concat (format "%s" method) (when id @@ -111,14 +110,17 @@ Return the entire response as a complex alist." (cond ((eq get-all t) #'sx-request-all-stop-when-no-more) (t get-all)))) - (lwarn "sx-call-method" :debug "A: %S T: %S. M: %S,%s. F: %S" (equal 'warn auth) - access-token method-auth full-method filter-auth) - (unless access-token + (lwarn "sx-call-method" + :debug "A: %S T: %S. M: %S,%s. F: %S" + (eq 'warn auth) sx-auth-access-token + method-auth full-method filter-auth) + (unless sx-auth-access-token (cond ;; 1. Need auth and warn user (interactive use) ((and method-auth (equal 'warn auth)) (sx-user-error - "This request requires authentication. Please run `M-x sx-authenticate' and try again.")) + "This request requires authentication. \ +Please run `M-x sx-authenticate' and try again.")) ;; 2. Need auth to populate UI, cannot provide subset ((and method-auth auth) (setq call 'sx-request-fallback)) diff --git a/sx-networks.el b/sx-networks.el index 45eaf05f..e281fac2 100644 --- a/sx-networks.el +++ b/sx-networks.el @@ -25,9 +25,9 @@ ;;; Code: (require 'sx-method) -(require 'sx-cache) (require 'sx-site) (require 'sx-filter) +(require 'stash) (defconst sx-network--user-filter (sx-filter-from-nil @@ -48,8 +48,8 @@ (defun sx-network--get-associated () "Retrieve cached information for network user. If cache is not available, retrieve current data." - (or (and (setq sx-network--user-information (sx-cache-get 'network-user) - sx-network--user-sites + (or (and sx-network--user-information + (setq sx-network--user-sites (sx-network--map-site-url-to-site-api))) (sx-network--update))) @@ -57,12 +57,12 @@ If cache is not available, retrieve current data." "Update user information. Sets cache and then uses `sx-network--get-associated' to update the variables." - (sx-cache-set 'network-user - (sx-method-call 'me - :submethod 'associated - :keywords '((types . (main_site meta_site))) - :filter sx-network--user-filter - :auth t)) + (setq network-user + (sx-method-call 'me + :submethod 'associated + :keywords '((types . (main_site meta_site))) + :filter sx-network--user-filter + :auth t)) (sx-network--get-associated)) (defun sx-network--initialize () @@ -88,7 +88,7 @@ list of sites the user is active on." (cdr (assoc u-site sites-info))))) sx-network--user-information))) -(defvar sx-network--user-information nil +(defstash sx-network--user-information "network-user.el" sx nil "User information for the various sites.") (defvar sx-network--user-sites nil diff --git a/sx-question.el b/sx-question.el index 1df49006..1121d3d9 100644 --- a/sx-question.el +++ b/sx-question.el @@ -28,6 +28,7 @@ (require 'sx) (require 'sx-filter) (require 'sx-method) +(require 'stash) (defun sx-question-get-questions (site &optional page keywords submethod) "Get SITE questions. Return page PAGE (the first if nil). @@ -96,7 +97,8 @@ for the post." ;;; Question Properties ;;;; Read/unread -(defvar sx-question--user-read-list nil +(defstash sx-question--user-read-list + "read-questions.el" sx nil "Alist of questions read by the user. Each element has the form @@ -106,13 +108,14 @@ Each element has the form where each element in QUESTION-LIST has the form (QUESTION_ID . LAST-VIEWED-DATE).") +(sx-init-variable sx-question--user-read-list) (defun sx-question--ensure-read-list (site) "Ensure `sx-question--user-read-list' has been read from cache. If no cache exists for it, initialize one with SITE." (unless sx-question--user-read-list (setq sx-question--user-read-list - (sx-cache-get 'read-questions `'((,site)))))) + (list (list site))))) (defun sx-question--read-p (question) "Non-nil if QUESTION has been read since last updated. @@ -129,39 +132,36 @@ See `sx-question--user-read-list'." Returns nil if question (in its current state) was already marked read, i.e., if it was `sx-question--read-p'. See `sx-question--user-read-list'." - (prog1 - (sx-assoc-let question - (sx-question--ensure-read-list .site_par) - (let ((site-cell (assoc .site_par sx-question--user-read-list)) - (q-cell (cons .question_id .last_activity_date)) - cell) - (cond - ;; First question from this site. - ((null site-cell) - (push (list .site_par q-cell) sx-question--user-read-list)) - ;; Question already present. - ((setq cell (assoc .question_id site-cell)) - ;; Current version is newer than cached version. - (when (or (not (numberp (cdr cell))) - (> .last_activity_date (cdr cell))) - (setcdr cell .last_activity_date))) - ;; Question wasn't present. - (t - (sx-sorted-insert-skip-first - q-cell site-cell (lambda (x y) (> (car x) (car y)))))))) - ;; Save the results. - ;; @TODO This causes a small lag on `j' and `k' as the list gets - ;; large. Should we do this on a timer? - (sx-cache-set 'read-questions sx-question--user-read-list))) + (sx-assoc-let question + (sx-question--ensure-read-list .site_par) + (let ((site-cell (assoc .site_par sx-question--user-read-list)) + (q-cell (cons .question_id .last_activity_date)) + cell) + (cond + ;; First question from this site. + ((null site-cell) + (push (list .site_par q-cell) sx-question--user-read-list)) + ;; Question already present. + ((setq cell (assoc .question_id site-cell)) + ;; Current version is newer than cached version. + (when (or (not (numberp (cdr cell))) + (> .last_activity_date (cdr cell))) + (setcdr cell .last_activity_date))) + ;; Question wasn't present. + (t + (sx-sorted-insert-skip-first + q-cell site-cell (lambda (x y) (> (car x) (car y))))))))) ;;;; Hidden -(defvar sx-question--user-hidden-list nil +(defstash sx-question--user-hidden-list + "hidden-questions.el" sx nil "Alist of questions hidden by the user. Each element has the form (SITE QUESTION_ID QUESTION_ID ...)") +(sx-init-variable sx-question--user-hidden-list) (defun sx-question--ensure-hidden-list (site) "Ensure the `sx-question--user-hidden-list' has been read from cache. @@ -169,7 +169,7 @@ Each element has the form If no cache exists for it, initialize one with SITE." (unless sx-question--user-hidden-list (setq sx-question--user-hidden-list - (sx-cache-get 'hidden-questions `'((,site)))))) + (list (list site))))) (defun sx-question--hidden-p (question) "Non-nil if QUESTION has been hidden." @@ -190,9 +190,7 @@ If no cache exists for it, initialize one with SITE." ;; Not first question and question wasn't present. ;; Add it in, but make sure it's sorted (just in case we ;; decide to rely on it later). - (sx-sorted-insert-skip-first .question_id site-cell >)) - ;; Save the results. - (sx-cache-set 'hidden-questions sx-question--user-hidden-list))))) + (sx-sorted-insert-skip-first .question_id site-cell >)))))) ;;;; Other data diff --git a/sx-request.el b/sx-request.el index 8f672ecf..ae296ab7 100644 --- a/sx-request.el +++ b/sx-request.el @@ -258,22 +258,20 @@ false, use the symbol `false'. Each element is processed with `sx--thing-as-string'." ;; Add API key to list of arguments, this allows for increased quota ;; automatically. - (let ((api-key (cons "key" sx-request-api-key)) - (auth (car (sx-cache-get 'auth)))) - (push api-key alist) - (when auth - (push auth alist)) - (mapconcat - (lambda (pair) - (concat - (sx--thing-as-string (car pair)) - "=" - (sx--thing-as-string (cdr pair) kv-sep t))) - (delq nil (mapcar - (lambda (pair) - (when (cdr pair) pair)) - alist)) - "&"))) + (push (cons "key" sx-request-api-key) alist) + (when (bound-and-true-p sx-auth-access-token) + (push (cons 'access_token sx-auth-access-token) alist)) + (mapconcat + (lambda (pair) + (concat + (sx--thing-as-string (car pair)) + "=" + (sx--thing-as-string (cdr pair) kv-sep t))) + (delq nil (mapcar + (lambda (pair) + (when (cdr pair) pair)) + alist)) + "&")) ;;; Response Processors diff --git a/sx-site.el b/sx-site.el index 4dac8e62..e758e46d 100644 --- a/sx-site.el +++ b/sx-site.el @@ -25,8 +25,13 @@ ;;; Code: (require 'sx-method) -(require 'sx-cache) (require 'sx-filter) +(require 'stash) + +(defstash sx-site-list + "sites.el" sx nil + "List of sites.") +(sx-init-variable sx-site-list) (defconst sx-site-browse-filter (sx-filter-from-nil @@ -41,11 +46,11 @@ (defun sx-site--get-site-list () "Return all sites with `sx-site-browse-filter'." - (sx-cache-get - 'site-list - '(sx-method-call 'sites - :pagesize 999 - :filter sx-site-browse-filter))) + (unless sx-site-list + (setq sx-site-list + (sx-method-call 'sites + :pagesize 999 + :filter sx-site-browse-filter)))) (defcustom sx-site-favorites nil diff --git a/sx.el b/sx.el index 57c89902..c0d4c02c 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.2")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ ;;; Code: (require 'tabulated-list) +(require 'stash) (defconst sx-version "0.1" "Version of the `sx' package.") @@ -36,6 +37,8 @@ :tag "SX" :group 'applications) +(defapp sx 60) + ;;; User commands (defun sx-version () @@ -426,19 +429,13 @@ Run after `sx-init--internal-hook'." This is used internally to set initial values for variables such as filters.") -(defmacro sx-init-variable (variable value &optional setter) - "Set VARIABLE to VALUE using SETTER. -SETTER should be a function of two arguments. If SETTER is nil, -`set' is used." - (eval - `(add-hook - 'sx-init--internal-hook - (lambda () - (,(or setter #'setq) ,variable ,value)))) - nil) +(defmacro sx-init-variable (variable) + "Set up VARIABLE to be loaded from disk on init." + `(add-hook 'sx-init--internal-hook + (lambda () (stash-load ,variable)))) (defvar sx-initialized nil - "Nil if sx hasn't been initialized yet. + "Nil if SX hasn't been initialized yet. If it has, holds the time at which initialization happened.") (defun sx-initialize (&optional force) diff --git a/sx.org b/sx.org index e206cc24..1e5dd550 100644 --- a/sx.org +++ b/sx.org @@ -163,8 +163,6 @@ structure. This list is very loosely ordered form low to high-level. - ~sx-time.el~ :: Similar to ~sx.el~, but only contains a few time-related functions. - ~sx-filter.el~ :: Handles retrieval of filters. -- ~sx-cache.el~ :: Saves and restores persistent data between - sessions. - ~sx-button.el~ :: Defines all button types used throughout the package. Currently used only by ~sx-question-print.el~. From e4ca0e86a18638b37f9c796e91d9bd6ee64c0f3b Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 01:28:37 -0600 Subject: [PATCH 05/11] Bump stash.el version number Ensure application data is written before Emacs is killed. --- sx.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx.el b/sx.el index c0d4c02c..50fdbc09 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3.1")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by From 53afa2e51882a5dc0a77e6ea21588eb8c7e7cc85 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 01:40:12 -0600 Subject: [PATCH 06/11] Bump stash.el version --- sx.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx.el b/sx.el index 50fdbc09..b88e28de 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3.1")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3.2")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by From 2ad7edcdd0593c397a758bf0aac3693797328fb1 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 01:40:26 -0600 Subject: [PATCH 07/11] Use cask exec for tests --- .travis.yml | 2 +- Cask | 2 ++ Makefile | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index 067fa62c..0a25f08c 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,7 +26,7 @@ before_install: - cask --version script: - - emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit + - cask exec emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit notifications: webhooks: diff --git a/Cask b/Cask index a055f125..c5aa89e5 100644 --- a/Cask +++ b/Cask @@ -1,3 +1,5 @@ +; -*- mode: emacs-lisp -*- + (source gnu) (source melpa-stable) diff --git a/Makefile b/Makefile index e04c1b0a..e61fb41c 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ $(VERSIONS) :: clean evm use emacs-24.$@-bin emacs --version cask install - emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit + cask exec emacs --batch -L . -l ert -l test/tests.el -f ert-run-tests-batch-and-exit clean: rm -rf .sx/ From a7de950b09ec8e59ca0443116c813a52362e60c8 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 01:40:59 -0600 Subject: [PATCH 08/11] Remove sx-cache.el --- sx-cache.el | 89 ----------------------------------------------------- 1 file changed, 89 deletions(-) delete mode 100644 sx-cache.el diff --git a/sx-cache.el b/sx-cache.el deleted file mode 100644 index 7654faf3..00000000 --- a/sx-cache.el +++ /dev/null @@ -1,89 +0,0 @@ -;;; sx-cache.el --- caching -*- lexical-binding: t; -*- - -;; Copyright (C) 2014 Sean Allred - -;; Author: Sean Allred - -;; This program is free software; you can redistribute it and/or modify -;; it under the terms of the GNU General Public License as published by -;; the Free Software Foundation, either version 3 of the License, or -;; (at your option) any later version. - -;; This program is distributed in the hope that it will be useful, -;; but WITHOUT ANY WARRANTY; without even the implied warranty of -;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -;; GNU General Public License for more details. - -;; You should have received a copy of the GNU General Public License -;; along with this program. If not, see . - -;;; Commentary: - -;; This file handles the cache system. All caches are retrieved and -;; set using symbols. The symbol should be the sub-package that is -;; using the cache. For example, `sx-pkg' would use -;; -;; `(sx-cache-get 'pkg)' -;; -;; This symbol is then converted into a filename within -;; `sx-cache-directory'. - -;;; Code: - -(require 'stash) - -(defcustom sx-cache-directory (locate-user-emacs-file ".sx") - "Directory containing cached data." - :type 'directory - :group 'sx) - -(defcustom sx-cache-write-delay 5 - "Idle delay in seconds to write cache data." - :type 'integer - :group 'sx) - -(defvar sx-cache--list nil - "List of cache variables.") - -(defmacro sx-cache-new (variable &optional default) - `(progn - (stash-new ',variable - ,(expand-file-name - (concat (symbol-name variable) ".el") - sx-cache-directory) - ,default - ,sx-cache-write-delay) - (add-to-list 'sx-cache--list ,variable))) - -(defun sx-cache-read (cache) - "Return the data within CACHE." - (stash-load cache)) - -(defun sx-cache-set (cache data &optional immediate) - "Set the content of CACHE to DATA. -If IMMEDIATE is non-nil, immediately write to disk." - (stash-set cache data immediate)) - -(defun sx-cache--invalidate (cache) - "Invalidate CACHE." - (stash-reset cache)) - -(defun sx-cache-invalidate-all (&rest except) - "Invalidate all caches using `sx-cache--invalidate'. -Afterwards reinitialize caches using `sx-initialize'. All those -cache symbols in EXCEPT are spared. Interactively, this includes -only `auth'. - -Note: This will also remove read/unread status of questions as well -as delete the list of hidden questions." - (interactive (list (unless current-prefix-arg '(auth)))) - (mapc #'sx-cache--invalidate - (cl-set-difference sx-cache--list except)) - (sx-initialize 'force)) - -(provide 'sx-cache) -;;; sx-cache.el ends here - -;; Local Variables: -;; indent-tabs-mode: nil -;; End: From 1368912b1dacb633a2c124d7606000ef86440733 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 08:53:02 -0600 Subject: [PATCH 09/11] Prune leftover require --- sx-auth.el | 1 - 1 file changed, 1 deletion(-) diff --git a/sx-auth.el b/sx-auth.el index 1164e051..bb52d0d2 100644 --- a/sx-auth.el +++ b/sx-auth.el @@ -30,7 +30,6 @@ (require 'sx) (require 'sx-request) -(require 'sx-cache) (require 'stash) (defconst sx-auth-root From f9a47e8aa0406f02448f38c184d6e8302795c318 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 09:50:50 -0600 Subject: [PATCH 10/11] Fix variable name --- sx-networks.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sx-networks.el b/sx-networks.el index e281fac2..5a3928e2 100644 --- a/sx-networks.el +++ b/sx-networks.el @@ -57,7 +57,7 @@ If cache is not available, retrieve current data." "Update user information. Sets cache and then uses `sx-network--get-associated' to update the variables." - (setq network-user + (setq sx-network--user-information (sx-method-call 'me :submethod 'associated :keywords '((types . (main_site meta_site))) From 676ecdcd4e45c8b13957695c2afdf6e0a7bc07d3 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 1 Feb 2015 09:50:57 -0600 Subject: [PATCH 11/11] Use stash.el 0.4 (updated interface) Hopefully this will also resolve those issues Travis has been having downloading the right version... --- sx.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/sx.el b/sx.el index b88e28de..5d9d5e5b 100644 --- a/sx.el +++ b/sx.el @@ -6,7 +6,7 @@ ;; URL: https://github.com/vermiculus/sx.el/ ;; Version: 0.1 ;; Keywords: help, hypermedia, tools -;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.3.2")) +;; Package-Requires: ((emacs "24.1") (cl-lib "0.5") (json "1.3") (markdown-mode "2.0") (let-alist "1.0.3") (stash "0.4")) ;; This program is free software; you can redistribute it and/or modify ;; it under the terms of the GNU General Public License as published by @@ -37,7 +37,7 @@ :tag "SX" :group 'applications) -(defapp sx 60) +(defstashapp sx 60) ;;; User commands