Skip to content

Commit d0027b5

Browse files
authored
Merge pull request #141 from bhagany/paginate
Fix #63 - Paginate
2 parents 48b733d + 750e25c commit d0027b5

5 files changed

Lines changed: 349 additions & 52 deletions

File tree

examples/blog/build.boot

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
[hiccup "1.0.5"]
66
[pandeiro/boot-http "0.6.3-SNAPSHOT"]])
77

8-
(require '[io.perun :refer :all]
8+
(require '[clojure.string :as str]
9+
'[io.perun :refer :all]
910
'[io.perun.example.index :as index-view]
1011
'[io.perun.example.post :as post-view]
1112
'[pandeiro.boot-http :refer [serve]])
@@ -25,6 +26,20 @@
2526
(gravatar :source-key :author-email :target-key :author-gravatar)
2627
(render :renderer 'io.perun.example.post/render)
2728
(collection :renderer 'io.perun.example.index/render :page "index.html")
29+
(paginate :renderer 'io.perun.example.paginate/render)
30+
(assortment :renderer 'io.perun.example.assortment/render
31+
:grouper (fn [entries]
32+
(->> entries
33+
(mapcat (fn [entry]
34+
(if-let [kws (:keywords entry)]
35+
(map #(-> [% entry]) (str/split kws #"\s*,\s*"))
36+
[])))
37+
(reduce (fn [result [kw entry]]
38+
(let [path (str kw ".html")]
39+
(-> result
40+
(update-in [path :entries] conj entry)
41+
(assoc-in [path :entry :keyword] kw))))
42+
{}))))
2843
(static :renderer 'io.perun.example.about/render :page "about.html")
2944
(inject-scripts :scripts #{"start.js"})
3045
(sitemap)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(ns io.perun.example.assortment
2+
(:require [hiccup.page :refer [html5]]))
3+
4+
(defn render [{global-meta :meta posts :entries entry :entry}]
5+
(html5 {:lang "en" :itemtype "http://schema.org/Blog"}
6+
[:head
7+
[:title (str (:site-title global-meta) "|" (:keyword entry))]
8+
[:meta {:charset "utf-8"}]
9+
[:meta {:http-equiv "X-UA-Compatible" :content "IE=edge,chrome=1"}]
10+
[:meta {:name "viewport" :content "width=device-width, initial-scale=1.0, user-scalable=no"}]]
11+
[:body
12+
[:h1 (str "Page " (:page entry))]
13+
[:ul.items.columns.small-12
14+
(for [post posts]
15+
[:li (:title post)])]]))
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(ns io.perun.example.paginate
2+
(:require [hiccup.page :refer [html5]]))
3+
4+
(defn render [{global-meta :meta posts :entries entry :entry}]
5+
(html5 {:lang "en" :itemtype "http://schema.org/Blog"}
6+
[:head
7+
[:title (str (:site-title global-meta) "|" (:tag entry))]
8+
[:meta {:charset "utf-8"}]
9+
[:meta {:http-equiv "X-UA-Compatible" :content "IE=edge,chrome=1"}]
10+
[:meta {:name "viewport" :content "width=device-width, initial-scale=1.0, user-scalable=no"}]]
11+
[:body
12+
[:h1 (str "Page " (:page entry))]
13+
[:ul.items.columns.small-12
14+
(for [post posts]
15+
[:li (:title post)])]]))

src/io/perun.clj

Lines changed: 126 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -753,16 +753,15 @@
753753
paths (grouper (filter-meta-by-ext fileset options))]
754754
(if (seq paths)
755755
(reduce
756-
(fn [result [path {:keys [entries group-meta]}]]
756+
(fn [result [path {:keys [entry entries]}]]
757757
(let [sorted (->> entries
758758
(sort-by sortby comparator)
759759
(map #(assoc % :content (->> (:path %)
760760
(boot/tmp-get fileset)
761761
boot/tmp-file
762762
slurp))))
763763
new-path (perun/create-filepath out-dir path)
764-
new-entry (assoc group-meta :out-dir out-dir)]
765-
(perun/report-info task-name (str "rendered " task-name " " path))
764+
new-entry (assoc entry :out-dir out-dir)]
766765
(assoc result new-path {:meta global-meta
767766
:entry new-entry
768767
:entries (vec sorted)})))
@@ -772,11 +771,84 @@
772771
(perun/report-info task-name (str task-name " found nothing to render"))
773772
[]))))
774773

775-
(def ^:private +collection-defaults+
774+
(defn assortment-pre-wrap
775+
"Handles common assortment task orchestration
776+
777+
`task-name` is used for log messages. `tracer` is a keyword that gets added
778+
to the `:io.perun/trace` metadata. `grouper` is a function that takes a seq
779+
of entries and returns a map of paths to render data (see docstring for
780+
`assortment` for more info)
781+
782+
Returns a boot `with-pre-wrap` result"
783+
[{:keys [task-name tracer grouper options]}]
784+
(cond (not (fn? (:comparator options)))
785+
(u/fail (str task-name " task :comparator option should implement Fn\n"))
786+
(not (ifn? (:filterer options)))
787+
(u/fail (str task-name " task :filterer option value should implement IFn\n"))
788+
(not (ifn? (:sortby options)))
789+
(u/fail (str task-name " task :sortby option value should implement IFn\n"))
790+
(not (ifn? grouper))
791+
(u/fail (str task-name " task :grouper option value should implement IFn\n"))
792+
:else
793+
(let [;; Make sure task-level metadata gets added to each entry
794+
meta-grouper (fn [entries]
795+
(->> entries
796+
grouper
797+
(map (fn [[path data]]
798+
[path (update-in data [:entry] #(merge (:meta options) %))]))
799+
(into {})))
800+
options (assoc options :grouper meta-grouper)]
801+
(render-pre-wrap {:task-name task-name
802+
:render-paths-fn (partial grouped-paths task-name)
803+
:options options
804+
:tracer tracer}))))
805+
806+
(def ^:private +assortment-defaults+
776807
{:out-dir "public"
777808
:filterer identity
778809
:extensions [".html"]
779810
:sortby (fn [file] (:date-published file))
811+
:comparator (fn [i1 i2] (compare i2 i1))
812+
:grouper #(-> {"index.html" {:entries %}})})
813+
814+
(deftask assortment
815+
"Render multiple collections
816+
The symbol supplied as `renderer` should resolve to a function
817+
which will be called with a map containing the following keys:
818+
- `:meta`, global perun metadata
819+
- `:entry`, the metadata for this collection
820+
- `:entries`, all entries
821+
822+
The `grouper` function will be called with a seq containing the
823+
entries to be grouped, and it should return a map with keys that
824+
are filenames and values that are maps with the keys:
825+
- `:entries`: the entries for each collection
826+
- `:entry`: (optional) page metadata for this collection
827+
828+
Entries can optionally be filtered by supplying a function
829+
to the `filterer` option.
830+
831+
The `sortby` function can be used for ordering entries before rendering."
832+
[o out-dir OUTDIR str "the output directory"
833+
r renderer RENDERER sym "page renderer (fully qualified symbol resolving to a function)"
834+
g grouper GROUPER code "group posts function, keys are filenames, values are to-be-rendered entries"
835+
_ filterer FILTER code "predicate to use for selecting entries (default: `identity`)"
836+
e extensions EXTENSIONS [str] "extensions of files to include"
837+
s sortby SORTBY code "sort entries by function"
838+
c comparator COMPARATOR code "sort by comparator function"
839+
m meta META edn "metadata to set on each collection entry"]
840+
(let [grouper (or grouper #(-> {"index.html" {:entries %}}))
841+
options (merge +assortment-defaults+ (dissoc *opts* :grouper))]
842+
(assortment-pre-wrap {:task-name "assortment"
843+
:tracer :io.perun/assortment
844+
:grouper grouper
845+
:options options})))
846+
847+
(def ^:private +collection-defaults+
848+
{:out-dir "public"
849+
:filterer identity
850+
:extensions [".html"]
851+
:sortby :date-published
780852
:comparator (fn [i1 i2] (compare i2 i1))})
781853

782854
(deftask collection
@@ -790,51 +862,69 @@
790862
Entries can optionally be filtered by supplying a function
791863
to the `filterer` option.
792864
793-
The `sortby` and `groupby` functions can be used for ordering entries
865+
The `sortby` function can be used for ordering entries
794866
before rendering as well as rendering groups of entries to different pages."
795867
[o out-dir OUTDIR str "the output directory"
796868
r renderer RENDERER sym "page renderer (fully qualified symbol resolving to a function)"
797869
_ filterer FILTER code "predicate to use for selecting entries (default: `identity`)"
798870
e extensions EXTENSIONS [str] "extensions of files to include"
799871
s sortby SORTBY code "sort entries by function"
800-
g groupby GROUPBY code "group posts by function, keys are filenames, values are to-be-rendered entries"
801872
c comparator COMPARATOR code "sort by comparator function"
802873
p page PAGE str "collection result page path"
803874
m meta META edn "metadata to set on each collection entry"]
804-
(let [options (merge +collection-defaults+
805-
(dissoc *opts* :page)
806-
(if page
807-
{:grouper #(-> {page {:entries %
808-
:group-meta meta}})}
809-
(if groupby
810-
{:grouper #(->> %
811-
(group-by groupby)
812-
(map (fn [[page entries]]
813-
[page {:entries entries
814-
:group-meta meta}]))
815-
(into {}))}
816-
{:grouper #(-> {"index.html" {:entries %
817-
:group-meta meta}})})))]
818-
(cond (not (fn? (:comparator options)))
819-
(u/fail "collection task :comparator option should implement Fn\n")
820-
(not (ifn? (:filterer options)))
821-
(u/fail "collection task :filterer option value should implement IFn\n")
822-
(and (:page options) groupby)
823-
(u/fail "using the :page option will render any :groupby option setting effectless\n")
824-
(and (:groupby options) (not (ifn? (:groupby options))))
825-
(u/fail "collection task :groupby option value should implement IFn\n")
826-
(not (ifn? (:sortby options)))
827-
(u/fail "collection task :sortby option value should implement IFn\n")
828-
:else
829-
(let [collection-paths (partial grouped-paths "collection")]
830-
(render-pre-wrap {:task-name"collection"
831-
:render-paths-fn collection-paths
832-
:options options
833-
:tracer :io.perun/collection})))))
875+
(let [p (or page "index.html")]
876+
(assortment-pre-wrap {:task-name "collection"
877+
:tracer :io.perun/collection
878+
:grouper #(-> {p {:entries %}})
879+
:options (merge +collection-defaults+ (dissoc *opts* :page))})))
834880

835881
(def +inject-scripts-defaults+
836882
{:extensions [".html"]})
837883

884+
(def ^:private +paginate-defaults+
885+
{:out-dir "public"
886+
:prefix "page-"
887+
:page-size 10
888+
:filterer identity
889+
:extensions [".html"]
890+
:sortby (fn [file] (:date-published file))
891+
:comparator (fn [i1 i2] (compare i2 i1))})
892+
893+
(deftask paginate
894+
"Render multiple collections
895+
The symbol supplied as `renderer` should resolve to a function
896+
which will be called with a map containing the following keys:
897+
- `:meta`, global perun metadata
898+
- `:entry`, the metadata for this collection
899+
- `:entries`, all entries
900+
901+
Entries can optionally be filtered by supplying a function
902+
to the `filterer` option.
903+
904+
The `sortby` function can be used for ordering entries before rendering."
905+
[o out-dir OUTDIR str "the output directory"
906+
f prefix PREFIX str "the prefix for each html file, eg prefix-1.html, prefix-2.html (default: `\"page-\"`)"
907+
p page-size PAGESIZE int "the number of entries to include in each page (default: `10`)"
908+
r renderer RENDERER sym "page renderer (fully qualified symbol resolving to a function)"
909+
_ filterer FILTER code "predicate to use for selecting entries (default: `identity`)"
910+
e extensions EXTENSIONS [str] "extensions of files to include"
911+
s sortby SORTBY code "sort entries by function"
912+
c comparator COMPARATOR code "sort by comparator function"
913+
m meta META edn "metadata to set on each collection entry"]
914+
(let [{:keys [sortby comparator page-size prefix] :as options} (merge +paginate-defaults+ *opts*)
915+
grouper (fn [entries]
916+
(->> entries
917+
(sort-by sortby comparator)
918+
(partition-all page-size)
919+
(map-indexed #(-> [(str prefix (inc %1) ".html")
920+
{:entry {:page (inc %1)}
921+
:entries %2}]))
922+
(into {})))]
923+
(assortment-pre-wrap {:task-name "paginate"
924+
:tracer :io.perun/paginate
925+
:grouper grouper
926+
:options options})))
927+
838928
(deftask inject-scripts
839929
"Inject JavaScript scripts into html files.
840930
Use either filter to include only files matching or remove to

0 commit comments

Comments
 (0)