From e91752e2d2b6c631fd774823a10868c5d7e40d8a Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 10:25:21 -0500 Subject: Add cache directory as a customizable variable --- stack-core.el | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/stack-core.el b/stack-core.el index 9a535cd..3391d14 100644 --- a/stack-core.el +++ b/stack-core.el @@ -44,6 +44,10 @@ ;;; Constants and Customizable Options +(defcustom stack-cache-directory + (expand-file-name ".stackmode" user-emacs-directory) + "Directory containined cached files and precompiled filters.") + (defconst stack-core-api-version "2.2" "The current version of the API.") -- cgit v1.2.3 From 01307c43d70949b8bba63eaf3ddfd8a712e78875 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 10:39:07 -0500 Subject: Whitespace normalization in stack-filter.el --- stack-filter.el | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/stack-filter.el b/stack-filter.el index 1525a4b..75173a1 100644 --- a/stack-filter.el +++ b/stack-filter.el @@ -51,19 +51,20 @@ excluding those from EXCLUDE, using BASE as a base filter. INCLUDE and EXCLUDE must both be lists; BASE should be a symbol or string." (let ((keyword-arguments - `((include . ,(if include (mapconcat - #'stack-core-thing-as-string - include ";"))) - (exclude . ,(if exclude (mapconcat - #'stack-core-thing-as-string - exclude ";"))) - (base . ,(if base base))))) + `((include . ,(if include (mapconcat + #'stack-core-thing-as-string + include ";"))) + (exclude . ,(if exclude (mapconcat + #'stack-core-thing-as-string + exclude ";"))) + (base . ,(if base base))))) (let ((response (stack-core-make-request - "filter/create" - keyword-arguments))) + "filter/create" + keyword-arguments))) (url-hexify-string (cdr (assoc 'filter - (elt response 0))))))) + (elt response 0))))))) + (provide 'stack-filter) ;;; stack-filter.el ends here -- cgit v1.2.3 From 39777784204c3dd616dd21dc2207bf35cbf35eae Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 10:39:55 -0500 Subject: Modify directory-local variables everywhere Use this file for your own development environment. --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 4acb5ef..663dcbb 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ # Compiled Elisp *.elc /.cask/ +.dir-locals.el -- cgit v1.2.3 From d273e792fbc1cc7238e156e24099d25ed0b3c53e Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:13:41 -0500 Subject: Add function to retrieve cache files --- stack-core.el | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/stack-core.el b/stack-core.el index 3391d14..fde06a8 100644 --- a/stack-core.el +++ b/stack-core.el @@ -245,5 +245,14 @@ entire response as a complex alist." cons-cell)))) data)))) +(defun stack-cache-get-file (filename) + "Return a buffer for FILENAME from `stack-cache-directory'." + (let ((find-file-hook nil) + (file (expand-file-name filename stack-cache-directory))) + (unless (file-exists-p stack-cache-directory) + (mkdir stack-cache-directory)) + (unless (file-exists-p file) + (find-file-noselect file)))) + (provide 'stack-core) ;;; stack-core.el ends here -- cgit v1.2.3 From 434b71bb59ceea9c6cf93fcf3b6450b31426a260 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:49:49 -0500 Subject: Convenience function for retrieving cache files This should be sorted somewhere... Maybe stack-cache.el needs to be its own file. --- stack-core.el | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/stack-core.el b/stack-core.el index fde06a8..0d87f48 100644 --- a/stack-core.el +++ b/stack-core.el @@ -245,10 +245,14 @@ entire response as a complex alist." cons-cell)))) data)))) +(defun stack-cache-get-file-name (filename) + "Expands FILENAME in the context of `stack-cache-directory'." + (expand-file-name filename stack-cache-directory)) + (defun stack-cache-get-file (filename) "Return a buffer for FILENAME from `stack-cache-directory'." (let ((find-file-hook nil) - (file (expand-file-name filename stack-cache-directory))) + (file (stack-cache-get-file-name filename))) (unless (file-exists-p stack-cache-directory) (mkdir stack-cache-directory)) (unless (file-exists-p file) -- cgit v1.2.3 From a4adbdf59250807d5c1872df7f034029d6f44b08 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:50:42 -0500 Subject: Fix return when file already exists --- stack-core.el | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/stack-core.el b/stack-core.el index 0d87f48..f9b9669 100644 --- a/stack-core.el +++ b/stack-core.el @@ -255,8 +255,7 @@ entire response as a complex alist." (file (stack-cache-get-file-name filename))) (unless (file-exists-p stack-cache-directory) (mkdir stack-cache-directory)) - (unless (file-exists-p file) - (find-file-noselect file)))) + (find-file-noselect file))) (provide 'stack-core) ;;; stack-core.el ends here -- cgit v1.2.3 From d2a8e8e9c146c0cce084f7b4e6371a882374435e Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:51:39 -0500 Subject: Checkdoc file --- stack-filter.el | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/stack-filter.el b/stack-filter.el index 75173a1..0229ad1 100644 --- a/stack-filter.el +++ b/stack-filter.el @@ -34,7 +34,8 @@ (defvar stack-filter 'default - "The current filter. To customize the filter for the next call + "The current filter. +To customize the filter for the next call to `stack-core-make-request', let-bind this variable to the output of a call to `stack-core-compile-filter'. Be careful! If you're going to be using this new filter a lot, create a variable @@ -42,11 +43,11 @@ for it. Creation requests count against `stack-core-remaining-api-requests'!") -;;; Filter compilation +;;; Compilation +;;; TODO allow BASE to be a precompiled filter name (defun stack-filter-compile (&optional include exclude base) - "Compile a StackExchange filter including fields from INCLUDE, -excluding those from EXCLUDE, using BASE as a base filter. + "Compile INCLUDE and EXCLUDE into a filter derived from BASE. INCLUDE and EXCLUDE must both be lists; BASE should be a symbol or string." -- cgit v1.2.3 From 7d461a7d4f62d357a224741b9e808f1abada8d15 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:52:08 -0500 Subject: Introduce persistent storage for filters --- stack-filter.el | 39 +++++++++++++++++++++++++++++++++++++++ test/tests.el | 19 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/stack-filter.el b/stack-filter.el index 0229ad1..e5fb54d 100644 --- a/stack-filter.el +++ b/stack-filter.el @@ -32,6 +32,9 @@ ;;; Customizations +(defconst stack-filter-cache-file + "filters.el") + (defvar stack-filter 'default "The current filter. @@ -66,6 +69,42 @@ or string." (cdr (assoc 'filter (elt response 0))))))) + +;;; Storage and Retrieval + +(defun stack-filter-get (filter) + "Retrieve named FILTER from `stack-filter-cache-file'." + (with-current-buffer (stack-cache-get-file stack-filter-cache-file) + (or (cdr (ignore-errors (assoc filter (read (buffer-string))))) + nil))) + +(defun stack-filter-store (name filter) + "Store NAME as FILTER in `stack-filter-cache-file'. + +NAME should be a symbol and FILTER is a string as compiled by +`stack-filter-compile'." + (unless (symbolp name) + (error "Name must be a symbol: %S" name)) + (with-current-buffer (stack-cache-get-file stack-filter-cache-file) + (let* ((dict (or (ignore-errors + (read (buffer-string))) + nil)) + (entry (assoc name dict))) + (if entry (setf (cdr entry) filter) + (setq dict (cons (cons name filter) dict))) + (erase-buffer) + (insert (prin1-to-string dict)) + (save-buffer) + dict))) + +;;; TODO tail recursion should be made more efficient for the +;;; byte-compiler +(defun stack-filter-store-all (name-filter-alist) + (when name-filter-alist + (stack-filter-store + (caar name-filter-alist) + (cdar name-filter-alist)) + (stack-filter-store-all (cdr name-filter-alist)))) (provide 'stack-filter) ;;; stack-filter.el ends here diff --git a/test/tests.el b/test/tests.el index 44b0de0..fa60364 100644 --- a/test/tests.el +++ b/test/tests.el @@ -75,3 +75,22 @@ ((should-not-go)) ((1 . alpha) (2 . beta))] '(1 2 3))))) + +(ert-deftest test-filters () + ;; Ensure the file is empty + (ignore-errors + (delete-file + (stack-cache-get-file-name + stack-filter-cache-file))) + + (should-error (stack-filter-store "names must be symbols" + "this is a filter")) + ;; basic use + (should (equal '((test . "filter")) + (stack-filter-store 'test "filter"))) + ;; aggregation + (should (equal '((test2 . "filter2") (test . "filter")) + (stack-filter-store 'test2 "filter2"))) + ;; mutation + (should (equal '((test2 . "filter2") (test . "filter-test")) + (stack-filter-store 'test "filter-test")))) -- cgit v1.2.3 From 72f5d07097b3f1530c40379d52606d3bc6856938 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 11:54:59 -0500 Subject: Store compiled filter for question browsing --- stack-question.el | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/stack-question.el b/stack-question.el index 3902a39..eb5b8a3 100644 --- a/stack-question.el +++ b/stack-question.el @@ -28,9 +28,12 @@ (require 'stack-filter) (defvar stack-question-browse-filter - (stack-filter-compile nil + (stack-filter-compile + nil '(user.profile_image shallow_user.profile_image))) +(stack-filter-store 'question-browse stack-question-browse-filter) + (defun stack-question-get-questions (site &optional page) "Get the page PAGE of questions from SITE." (stack-core-make-request -- cgit v1.2.3 From 39a78ea40772a30678dcd947fc4b8735d356edfb Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 12:02:46 -0500 Subject: Fix test cases re file persistence Every time the test was getting run, it would create the file, but not delete it. This meant that tests were not idempotent and artifacts from previous tests were persisting into the next ones. Use `make-temp-file' to let-bind `stack-filter-cache-file' to a temporary file. It is guaranteed to be empty when the tests begin. --- test/tests.el | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/test/tests.el b/test/tests.el index fa60364..9c9438d 100644 --- a/test/tests.el +++ b/test/tests.el @@ -77,20 +77,19 @@ '(1 2 3))))) (ert-deftest test-filters () - ;; Ensure the file is empty - (ignore-errors + (let ((stack-filter-cache-file (make-temp-file "stack-test-"))) + (should-error (stack-filter-store "names must be symbols" + "this is a filter")) + ;; basic use + (should (equal '((test . "filter")) + (stack-filter-store 'test "filter"))) + ;; aggregation + (should (equal '((test2 . "filter2") (test . "filter")) + (stack-filter-store 'test2 "filter2"))) + ;; mutation + (should (equal '((test2 . "filter2") (test . "filter-test")) + (stack-filter-store 'test "filter-test"))) + ;; clean up (note: the file should exist) (delete-file (stack-cache-get-file-name - stack-filter-cache-file))) - - (should-error (stack-filter-store "names must be symbols" - "this is a filter")) - ;; basic use - (should (equal '((test . "filter")) - (stack-filter-store 'test "filter"))) - ;; aggregation - (should (equal '((test2 . "filter2") (test . "filter")) - (stack-filter-store 'test2 "filter2"))) - ;; mutation - (should (equal '((test2 . "filter2") (test . "filter-test")) - (stack-filter-store 'test "filter-test")))) + stack-filter-cache-file)))) -- cgit v1.2.3 From 13e7d2b851da90435bc33137925144074a28eb4c Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 12:06:17 -0500 Subject: Consolidate testing variables and print requests --- test/tests.el | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/tests.el b/test/tests.el index 9c9438d..f19d506 100644 --- a/test/tests.el +++ b/test/tests.el @@ -22,8 +22,10 @@ ;;; Tests -(setq stack-core-remaining-api-requests-message-threshold 50000) -(setq debug-on-error t) +(setq + stack-core-remaining-api-requests-message-threshold 50000 + debug-on-error t + stack-core-silent-requests nil) (require 'stack-core) (require 'stack-question) -- cgit v1.2.3 From 800b3af0e5a4cbfb385b2f8d925f5884bc71baad Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 13:02:14 -0500 Subject: Let-bind user-emacs-directory to cwd Useful for testing locally, since now you can simply blow away the `.stackmode' directory instead of searching for it in `.emacs.d'. At any rate, it keeps everything in the project tidy. --- .gitignore | 2 ++ test/tests.el | 17 +++++++++-------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 663dcbb..2585bf5 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,5 @@ *.elc /.cask/ .dir-locals.el +/.stackmode/ +/url/ diff --git a/test/tests.el b/test/tests.el index f19d506..bd0cb67 100644 --- a/test/tests.el +++ b/test/tests.el @@ -5,6 +5,8 @@ (if (string-prefix-p "stack-" (symbol-name symbol)) (unintern symbol))))) +;;; Tests + (defun stack-test-sample-data (method &optional directory) (with-current-buffer (find-file-noselect @@ -15,17 +17,16 @@ "'no-value" (buffer-string)))))) -(setq stack-test-data-questions - (stack-test-sample-data "questions") - stack-test-data-sites - (stack-test-sample-data "sites")) - -;;; Tests - (setq stack-core-remaining-api-requests-message-threshold 50000 debug-on-error t - stack-core-silent-requests nil) + stack-core-silent-requests nil + user-emacs-directory "." + + stack-test-data-questions + (stack-test-sample-data "questions") + stack-test-data-sites + (stack-test-sample-data "sites")) (require 'stack-core) (require 'stack-question) -- cgit v1.2.3 From 73a1b008211cc2ca548c30ff99b2950c99e53325 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 13:04:54 -0500 Subject: Let-bind stack-cache-directory instead There are several variables and functions that depend on it; simply binding the filter-cache file isn't enough. --- test/tests.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/tests.el b/test/tests.el index bd0cb67..9e1cff1 100644 --- a/test/tests.el +++ b/test/tests.el @@ -80,7 +80,7 @@ '(1 2 3))))) (ert-deftest test-filters () - (let ((stack-filter-cache-file (make-temp-file "stack-test-"))) + (let ((stack-cache-directory (make-temp-file "stack-test" t))) (should-error (stack-filter-store "names must be symbols" "this is a filter")) ;; basic use -- cgit v1.2.3 From f349fe5d764730a0a339045a59a46d53b9a33100 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 13:11:15 -0500 Subject: Implement and use `stack-cache-get' and `-set' --- stack-core.el | 30 +++++++++++++++++++++++------- stack-filter.el | 24 +++++++++--------------- 2 files changed, 32 insertions(+), 22 deletions(-) diff --git a/stack-core.el b/stack-core.el index f9b9669..c2aff15 100644 --- a/stack-core.el +++ b/stack-core.el @@ -249,13 +249,29 @@ entire response as a complex alist." "Expands FILENAME in the context of `stack-cache-directory'." (expand-file-name filename stack-cache-directory)) -(defun stack-cache-get-file (filename) - "Return a buffer for FILENAME from `stack-cache-directory'." - (let ((find-file-hook nil) - (file (stack-cache-get-file-name filename))) - (unless (file-exists-p stack-cache-directory) - (mkdir stack-cache-directory)) - (find-file-noselect file))) +(defun stack-cache-get (cache) + "Return the data within CACHE. + +As with `stack-cache-set', CACHE is a file name within the +context of `stack-cache-directory'." + (unless (file-exists-p stack-cache-directory) + (mkdir stack-cache-directory)) + (let ((file (stack-cache-get-file-name cache))) + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents (stack-cache-get-file-name cache)) + (read (buffer-string)))))) + +(defun stack-cache-set (cache data) + "Set the content of CACHE to DATA. + +As with `stack-cache-get', CACHE is a file name within the +context of `stack-cache-directory'." + (unless (file-exists-p stack-cache-directory) + (mkdir stack-cache-directory)) + (write-region (prin1-to-string data) nil + (stack-cache-get-file-name cache)) + data) (provide 'stack-core) ;;; stack-core.el ends here diff --git a/stack-filter.el b/stack-filter.el index e5fb54d..91c4875 100644 --- a/stack-filter.el +++ b/stack-filter.el @@ -74,9 +74,7 @@ or string." (defun stack-filter-get (filter) "Retrieve named FILTER from `stack-filter-cache-file'." - (with-current-buffer (stack-cache-get-file stack-filter-cache-file) - (or (cdr (ignore-errors (assoc filter (read (buffer-string))))) - nil))) + (cdr (assoc filter (stack-cache-get stack-filter-cache-file)))) (defun stack-filter-store (name filter) "Store NAME as FILTER in `stack-filter-cache-file'. @@ -85,17 +83,12 @@ NAME should be a symbol and FILTER is a string as compiled by `stack-filter-compile'." (unless (symbolp name) (error "Name must be a symbol: %S" name)) - (with-current-buffer (stack-cache-get-file stack-filter-cache-file) - (let* ((dict (or (ignore-errors - (read (buffer-string))) - nil)) - (entry (assoc name dict))) - (if entry (setf (cdr entry) filter) - (setq dict (cons (cons name filter) dict))) - (erase-buffer) - (insert (prin1-to-string dict)) - (save-buffer) - dict))) + (let* ((dict (stack-cache-get stack-filter-cache-file)) + (entry (assoc name dict))) + (if entry (setcdr entry filter) + (setq dict (cons (cons name filter) dict))) + + (stack-cache-set stack-filter-cache-file dict))) ;;; TODO tail recursion should be made more efficient for the ;;; byte-compiler @@ -104,7 +97,8 @@ NAME should be a symbol and FILTER is a string as compiled by (stack-filter-store (caar name-filter-alist) (cdar name-filter-alist)) - (stack-filter-store-all (cdr name-filter-alist)))) + (stack-filter-store-all + (cdr name-filter-alist)))) (provide 'stack-filter) ;;; stack-filter.el ends here -- cgit v1.2.3 From 7a58be5e2a0aeae2e170cc576befbb3a2e34878e Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 13:23:28 -0500 Subject: Fix get-sample-data and avoid `find-file-noselect' --- test/tests.el | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/test/tests.el b/test/tests.el index 9e1cff1..e097244 100644 --- a/test/tests.el +++ b/test/tests.el @@ -8,14 +8,13 @@ ;;; Tests (defun stack-test-sample-data (method &optional directory) - (with-current-buffer - (find-file-noselect - (concat "data-samples/" - (when directory (concat directory "/")) - method ".el")) - (eval (read (if (string-equal "" (buffer-string)) - "'no-value" - (buffer-string)))))) + (let ((file (concat "data-samples/" + (when directory (concat directory "/")) + method ".el"))) + (when (file-exists-p file) + (with-temp-buffer + (insert-file-contents file) + (read (buffer-string)))))) (setq stack-core-remaining-api-requests-message-threshold 50000 -- cgit v1.2.3 From e089914f96caf5e1a74acb75995634c34455a290 Mon Sep 17 00:00:00 2001 From: Sean Allred Date: Sun, 2 Nov 2014 13:40:46 -0500 Subject: Simplify filter-store-all by modifying *-store --- stack-filter.el | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/stack-filter.el b/stack-filter.el index 91c4875..4210549 100644 --- a/stack-filter.el +++ b/stack-filter.el @@ -76,29 +76,28 @@ or string." "Retrieve named FILTER from `stack-filter-cache-file'." (cdr (assoc filter (stack-cache-get stack-filter-cache-file)))) -(defun stack-filter-store (name filter) +(defun stack-filter-store (name &optional filter) "Store NAME as FILTER in `stack-filter-cache-file'. NAME should be a symbol and FILTER is a string as compiled by -`stack-filter-compile'." - (unless (symbolp name) - (error "Name must be a symbol: %S" name)) - (let* ((dict (stack-cache-get stack-filter-cache-file)) - (entry (assoc name dict))) - (if entry (setcdr entry filter) - (setq dict (cons (cons name filter) dict))) - - (stack-cache-set stack-filter-cache-file dict))) - -;;; TODO tail recursion should be made more efficient for the -;;; byte-compiler +`stack-filter-compile'. + +If NAME is a cons cell, (car NAME) is taken to be the actual NAME +and (cdr NAME) is taken to be the actual FILTER. In this case, +the second argument is simply ignored." + (let ((name (if (consp name) (car name) name)) + (filter (if (consp name) (cdr name) filter))) + (unless (symbolp name) + (error "Name must be a symbol: %S" name)) + (let* ((dict (stack-cache-get stack-filter-cache-file)) + (entry (assoc name dict))) + (if entry (setcdr entry filter) + (setq dict (cons (cons name filter) dict))) + + (stack-cache-set stack-filter-cache-file dict)))) + (defun stack-filter-store-all (name-filter-alist) - (when name-filter-alist - (stack-filter-store - (caar name-filter-alist) - (cdar name-filter-alist)) - (stack-filter-store-all - (cdr name-filter-alist)))) + (mapc #'stack-filter-store name-filter-alist)) (provide 'stack-filter) ;;; stack-filter.el ends here -- cgit v1.2.3