[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/taxy e09dc38 32/39: Add: (taxy-mapc*) And musicy.el exa
From: |
ELPA Syncer |
Subject: |
[elpa] externals/taxy e09dc38 32/39: Add: (taxy-mapc*) And musicy.el example |
Date: |
Fri, 27 Aug 2021 10:57:35 -0400 (EDT) |
branch: externals/taxy
commit e09dc38a5786b3de2a70437f232968b644f32e13
Author: Adam Porter <adam@alphapapa.net>
Commit: Adam Porter <adam@alphapapa.net>
Add: (taxy-mapc*) And musicy.el example
---
README.org | 49 +++++++++++---------
examples/README.org | 26 +++++++++++
examples/musicy.el | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++++
images/musicy.png | Bin 0 -> 28010 bytes
taxy.el | 11 +++++
taxy.info | 77 +++++++++++++++++++++++++-------
6 files changed, 252 insertions(+), 37 deletions(-)
diff --git a/README.org b/README.org
index 4624c6f..168a687 100644
--- a/README.org
+++ b/README.org
@@ -23,37 +23,17 @@ Helpful features include:
:END:
:CONTENTS:
- [[#examples][Examples]]
+ - [[#example-applications][Example applications]]
- [[#usage][Usage]]
- [[#dynamic-taxys][Dynamic taxys]]
- [[#reusable-taxys][Reusable taxys]]
- [[#threading-macros][Threading macros]]
+ - [[#modifying-filled-taxys][Modifying filled taxys]]
- [[#magit-section][Magit section]]
- [[#changelog][Changelog]]
- [[#development][Development]]
:END:
-# * Installation
-# :PROPERTIES:
-# :TOC: :depth 0
-# :END:
-#
-# ** MELPA
-#
-# If you installed from MELPA, you're done. Just run one of the commands
below.
-#
-# ** Manual
-#
-# Install these required packages:
-#
-# + =foo=
-# + =bar=
-#
-# Then put this file in your load-path, and put this in your init file:
-#
-# #+BEGIN_SRC elisp
-# (require 'taxy)
-# #+END_SRC
-
* Examples
Let's imagine a silly taxonomy of numbers below 100:
@@ -220,6 +200,10 @@ That's better:
("B" "C" "D" "F" "G" "H" "J" "K" "L" "M" "N" "N" "P" "Q" "R" "S" "T" "V"
"W" "X" "Y" "Z"))))
#+END_SRC
+** Example applications
+
+Some example applications may be found in the
[[file:examples/README.org][examples directory]].
+
* Usage
:PROPERTIES:
:TOC: :include descendants :depth 1
@@ -228,6 +212,7 @@ That's better:
- [[#dynamic-taxys][Dynamic taxys]]
- [[#reusable-taxys][Reusable taxys]]
- [[#threading-macros][Threading macros]]
+- [[#modifying-filled-taxys][Modifying filled taxys]]
- [[#magit-section][Magit section]]
:END:
@@ -458,6 +443,26 @@ If you happen to like macros, ~taxy~ works well with
threading (i.e. ~thread-las
taxy-plain)
#+END_SRC
+** Modifying filled taxys
+
+Sometimes it's necessary to modify a taxy after filling it with objects, e.g.
to sort the objects and/or the sub-taxys. For this, use the function
~taxy-mapc-taxys~ (a.k.a. ~taxy-mapc*~). For example, in the sample
application [[file:examples/musicy.el][musicy.el]], the taxys and their objects
are sorted after filling, like so:
+
+#+BEGIN_SRC elisp
+ (defun musicy-files (files)
+ (thread-last musicy-taxy
+ taxy-emptied
+ (taxy-fill files)
+ (taxy-mapc* (lambda (taxy)
+ ;; Sort sub-taxys by their name.
+ (setf (taxy-taxys taxy)
+ (cl-sort (taxy-taxys taxy) #'string<
+ :key #'taxy-name))
+ ;; Sort sub-taxys' objects by name.
+ (setf (taxy-objects taxy)
+ (cl-sort (taxy-objects taxy) #'string<))))
+ taxy-magit-section-pp))
+#+END_SRC
+
** Magit section
Showing a =taxy= with =magit-section= is very easy:
diff --git a/examples/README.org b/examples/README.org
new file mode 100644
index 0000000..06d7325
--- /dev/null
+++ b/examples/README.org
@@ -0,0 +1,26 @@
+#+TITLE: Taxy Examples
+
+Some example applcations using ~taxy~.
+
+* Musicy
+
+[[file:musicy.el][Musicy]] displays a music library in a ~magit-section~
buffer. Use it like:
+
+#+BEGIN_SRC elisp
+ (require 'musicy)
+
+ (musicy "~/Music")
+#+END_SRC
+
+Since it calls the =mediainfo= program on every file, it can be slow on large
music libraries, so you might want to test it on only a subset of them, like:
+
+#+BEGIN_SRC elisp
+ (musicy-files
+ (seq-take (directory-files-recursively
+ "~/Music" (rx "." (or "mp3" "ogg") eos))
+ 100))
+#+END_SRC
+
+The resulting buffer shows tracks organized by genre, then artist, then year,
then album, then track name:
+
+[[../images/musicy.png]]
diff --git a/examples/musicy.el b/examples/musicy.el
new file mode 100644
index 0000000..be74fcd
--- /dev/null
+++ b/examples/musicy.el
@@ -0,0 +1,126 @@
+;;; musicy.el --- View a music library in a useful taxonomy -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Adam Porter
+
+;; Author: Adam Porter <adam@alphapapa.net>
+;; Keywords: convenience
+
+;; 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 <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;; This is a sample application using `taxi'. It uses the "mediainfo"
+;; program to get info about audio files, but any function could be
+;; swapped into its place (e.g. one that retrieved data from MPD).
+
+;;; Code:
+
+;;;; Requirements
+
+(require 'cl-lib)
+
+(require 'taxy)
+(require 'taxy-magit-section)
+
+;; Used to avoid repeated calls to "mediainfo" for the same file.
+(require 'memoize)
+
+;;;; Variables
+
+(defvar musicy-taxy
+ (cl-labels ((call-proc (process &rest args)
+ "Return results of running PROCESS with ARGS."
+ (declare (indent defun))
+ (with-temp-buffer
+ (if (zerop (apply #'call-process process nil t nil
+ args))
+ (buffer-substring-no-properties (point-min)
(point-max))
+ (warn "mediainfo failed for: %S" args))))
+ (mediainfo (file)
+ (call-proc "mediainfo" file))
+ (mediainfo-attr (attr file)
+ (if-let ((info (musicy-mediainfo file)))
+ (when (string-match
+ (rx-to-string `(seq (eval ,attr) (1+
blank) ":" (1+ blank) (group (1+ nonl))))
+ info)
+ (match-string 1 info))
+ (format "No info for file: %S" file)))
+ (genre (file)
+ (or (mediainfo-attr "Genre" file)
+ "[unknown genre]"))
+ (year (file)
+ (or (when-let (date (or (mediainfo-attr "Recorded date"
file)
+ (mediainfo-attr "Original/Released
date" file)
+ (mediainfo-attr "Year" file)))
+ (when (string-match (rx (group (1+ digit))) date)
+ (match-string 1 date)))
+ "[unknown year]"))
+ (artist (file)
+ (mediainfo-attr "Performer" file))
+ (album (file)
+ (mediainfo-attr "Album" file))
+ (track-name (file)
+ (mediainfo-attr "Track name" file))
+ (track-number (file)
+ (mediainfo-attr "Track name/Position" file))
+ (track-string (file)
+ (concat
+ (pcase (track-number file)
+ ((or "-1" 'nil) nil)
+ (number (format "%s: " number)))
+ (track-name file))))
+ (make-taxy
+ :name "Musicy"
+ :taxys (list (make-taxy
+ :name "Genres"
+ :take (apply-partially #'taxy-take-keyed*
+ (list #'genre #'artist #'year
#'album #'track-string)))))))
+
+;;;; Customization
+
+
+;;;; Commands
+
+(defun musicy (directory)
+ (interactive (list (read-directory-name "Directory of music files: ")))
+ (let ((files (directory-files-recursively
+ directory (rx "." (or "mp3" "ogg") eos))))
+ (musicy-files files)))
+
+(defun musicy-files (files)
+ (thread-last musicy-taxy
+ taxy-emptied
+ (taxy-fill files)
+ (taxy-mapc* (lambda (taxy)
+ (setf (taxy-taxys taxy)
+ (cl-sort (taxy-taxys taxy) #'string<
+ :key #'taxy-name))
+ (setf (taxy-objects taxy)
+ (cl-sort (taxy-objects taxy) #'string<))))
+ ;; taxy-plain
+ taxy-magit-section-pp))
+
+;;;; Functions
+
+(defmemoize musicy-mediainfo (file)
+ (with-temp-buffer
+ (if (zerop (call-process "mediainfo" nil t nil file))
+ (buffer-substring-no-properties (point-min) (point-max))
+ (warn "mediainfo failed for: %S" file))))
+
+;;;; Footer
+
+(provide 'musicy)
+
+;;; musicy.el ends here
diff --git a/images/musicy.png b/images/musicy.png
new file mode 100644
index 0000000..8b2af29
Binary files /dev/null and b/images/musicy.png differ
diff --git a/taxy.el b/taxy.el
index 335f7dd..6ba04a5 100644
--- a/taxy.el
+++ b/taxy.el
@@ -116,6 +116,17 @@ replace objects with a more useful form after
classification."
(defalias 'taxy-mapcar #'taxy-mapcar-objects)
+(defun taxy-mapc-taxys (fn taxy)
+ "Return TAXY having applied FN to it and its descendants.
+Does not copy TAXY. Destructively modifies TAXY, if FN does."
+ (declare (indent defun))
+ (funcall fn taxy)
+ (cl-loop for sub-taxy in-ref (taxy-taxys taxy)
+ do (setf sub-taxy (taxy-mapc-taxys fn sub-taxy)))
+ taxy)
+
+(defalias 'taxy-mapc* #'taxy-mapc-taxys)
+
(cl-defun taxy-take-keyed (key-fn object taxy &key (key-name-fn #'identity))
"Take OBJECT into TAXY, adding new taxys dynamically.
Places OBJECT into a taxy in TAXY for the value returned by
diff --git a/taxy.info b/taxy.info
index 43ef1bc..ccb6d37 100644
--- a/taxy.info
+++ b/taxy.info
@@ -21,12 +21,17 @@ taxy.el
— The Detailed Node Listing —
+Examples
+
+* Example applications::
+
Usage
* Dynamic taxys::
* Reusable taxys::
* Threading macros::
+* Modifying filled taxys::
* Magit section::
Dynamic taxys
@@ -210,13 +215,26 @@ subset of the taxys’ slots, suitable for display.
("Consonants" "Well, if they aren't a vowel..."
("B" "C" "D" "F" "G" "H" "J" "K" "L" "M" "N" "N" "P" "Q" "R" "S" "T"
"V" "W" "X" "Y" "Z"))))
+* Menu:
+
+* Example applications::
+
+
+File: README.info, Node: Example applications, Up: Examples
+
+1.1 Example applications
+========================
+
+Some example applications may be found in the examples directory
+(examples/README.org).
+
File: README.info, Node: Usage, Next: Changelog, Prev: Examples, Up: Top
2 Usage
*******
- • • • •
+ • • • • •
A taxy is defined with the ‘make-taxy’ constructor, like:
(make-taxy :name "Numbery"
@@ -257,6 +275,7 @@ replace objects in a taxy with, e.g. a more useful
representation.
* Dynamic taxys::
* Reusable taxys::
* Threading macros::
+* Modifying filled taxys::
* Magit section::
@@ -477,7 +496,7 @@ replace the room structs with useful representations for
display:
("#matrix-dev:matrix.org" "!jxlRxnrZCsjpjDubDX:matrix.org")))))))
-File: README.info, Node: Threading macros, Next: Magit section, Prev:
Reusable taxys, Up: Usage
+File: README.info, Node: Threading macros, Next: Modifying filled taxys,
Prev: Reusable taxys, Up: Usage
2.3 Threading macros
====================
@@ -494,9 +513,35 @@ If you happen to like macros, ‘taxy’ works well with
threading (i.e.
taxy-plain)
-File: README.info, Node: Magit section, Prev: Threading macros, Up: Usage
+File: README.info, Node: Modifying filled taxys, Next: Magit section, Prev:
Threading macros, Up: Usage
+
+2.4 Modifying filled taxys
+==========================
+
+Sometimes it’s necessary to modify a taxy after filling it with objects,
+e.g. to sort the objects and/or the sub-taxys. For this, use the
+function ‘taxy-mapc-taxys’ (a.k.a. ‘taxy-mapc*’). For example, in the
+sample application musicy.el (examples/musicy.el), the taxys and their
+objects are sorted after filling, like so:
+
+ (defun musicy-files (files)
+ (thread-last musicy-taxy
+ taxy-emptied
+ (taxy-fill files)
+ (taxy-mapc* (lambda (taxy)
+ ;; Sort sub-taxys by their name.
+ (setf (taxy-taxys taxy)
+ (cl-sort (taxy-taxys taxy) #'string<
+ :key #'taxy-name))
+ ;; Sort sub-taxys' objects by name.
+ (setf (taxy-objects taxy)
+ (cl-sort (taxy-objects taxy) #'string<))))
+ taxy-magit-section-pp))
+
+
+File: README.info, Node: Magit section, Prev: Modifying filled taxys, Up:
Usage
-2.4 Magit section
+2.5 Magit section
=================
Showing a taxy with magit-section is very easy:
@@ -552,17 +597,19 @@ GPLv3
Tag Table:
Node: Top218
-Node: Examples1101
-Node: Usage9001
-Node: Dynamic taxys11087
-Node: Multi-level dynamic taxys13505
-Node: Reusable taxys15668
-Node: Threading macros19837
-Node: Magit section20367
-Node: Changelog20962
-Node: 01-pre21100
-Node: Development21194
-Node: License21365
+Node: Examples1164
+Node: Example applications9099
+Node: Usage9304
+Node: Dynamic taxys11421
+Node: Multi-level dynamic taxys13839
+Node: Reusable taxys16002
+Node: Threading macros20171
+Node: Modifying filled taxys20710
+Node: Magit section21803
+Node: Changelog22404
+Node: 01-pre22542
+Node: Development22636
+Node: License22807
End Tag Table
- [elpa] externals/taxy dbad5b9 01/39: Let there be taxonomy!, (continued)
- [elpa] externals/taxy dbad5b9 01/39: Let there be taxonomy!, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 8797141 02/39: Dynamic, consuming, and non-consuming, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 6c5b7c7 07/39: Add: taxy-apply, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 02217c4 11/39: Docs: Add comments, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 408a32b 09/39: Docs: Tidy, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 09724dd 10/39: Add: taxy-take-keyed, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 9d44dba 13/39: Change: taxy-apply -> taxy-map, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 6eff6ae 18/39: Docs: Put Contents first, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 0a57638 17/39: Docs: Mention threading macros, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 0dce844 16/39: Docs: Add example of incremental filling, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy e09dc38 32/39: Add: (taxy-mapc*) And musicy.el example,
ELPA Syncer <=
- [elpa] externals/taxy bd28836 24/39: Meta: Headers, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy f0b926f 34/39: Add: Diredy example, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 21e6fa6 22/39: Docs: Commentary, header, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy e56ae33 26/39: Meta: .gitignore, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 15eb9b7 31/39: Docs: Tidy, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 01e956a 04/39: Rename function, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 657823b 03/39: Docs: Tidy example, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy f115919 06/39: Add: taxy-copy and reverse args to taxy-fill, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy a94ab1c 05/39: Tidy, ELPA Syncer, 2021/08/27
- [elpa] externals/taxy 751e9d7 08/39: Show reusable taxys, ELPA Syncer, 2021/08/27