perun

Discuss perun static site generator
bhagany 2017-12-10T18:31:47.000056Z

@richiardiandrea I have finally had some time today for looking at these things. Your second question is easier (adding things to global metadata), so I'm going to start with that. Here's a task I've written to include recent posts in global metadata:

bhagany 2017-12-10T18:31:51.000081Z

(deftask recent-posts
  "Adds the `n` most recent posts to global metadata as `:recent-posts`"
  [n num-posts NUMPOSTS int "The number of posts to store"]
  (with-pre-wrap fileset
    (let [options (merge +recent-posts-defaults+ *opts*)
          global-meta (pm/get-global-meta fileset)
          recent (->> (pm/get-meta fileset)
                      (filter post?)
                      (sort-by :date-published #(compare %2 %1))
                      (take (:num-posts options)))]
      (perun/report-info "recent-posts" "added %s posts to metadata" (count recent))
      (pm/set-global-meta fileset (assoc global-meta :recent-posts recent)))))

richiardiandrea 2017-12-10T18:32:37.000059Z

nice!

richiardiandrea 2017-12-10T18:33:32.000043Z

How to generalize that now :) because this can apply to any "global" properties you want to pass around in all website's pages

bhagany 2017-12-10T18:33:32.000151Z

A few thoughts - I think you should be able to modify this to get tags from the entries instead. Also, I think maybe that your use-case should be an automatic feature of the tags task.

richiardiandrea 2017-12-10T18:34:16.000048Z

Yep basically I need a task that let me append to global starting from the entries (no filtering)

richiardiandrea 2017-12-10T18:34:23.000101Z

Will start from the above

richiardiandrea 2017-12-10T18:34:36.000019Z

Maybe contribute back if I like what I am doing :)

bhagany 2017-12-10T18:34:41.000100Z

that sounds great!

bhagany 2017-12-10T18:37:17.000061Z

for your other question about paginating the results of assortment - it is possible to write a grouper function that would do what you want, but it is not as easy out of the box as I'd like. I'm still thinking about how best to improve assortment, but in the meantime, would an example of a grouper function help?

richiardiandrea 2017-12-10T18:38:32.000072Z

@bhagany I think I will not paginate in the first version so low priority ;) I have a grouper function that does that..was just wondering whether I was missing something

bhagany 2017-12-10T18:38:55.000058Z

okay, sounds good - you aren't missing anything πŸ™‚

richiardiandrea 2017-12-10T18:41:24.000031Z

It was such a nice experience to work with perun especially using Deraen's boot-livereload

richiardiandrea 2017-12-10T18:41:55.000090Z

The only thing is that I am compiling less at every watch so it is kind of a slower refreshing experience

bhagany 2017-12-10T18:42:39.000074Z

ah, hrm. it compiles even without a css change?

bhagany 2017-12-10T18:43:17.000093Z

I think perun should be able to help with that, if you wouldn't mind posting that less task

richiardiandrea 2017-12-10T18:44:58.000001Z

Yes no well, I rolled my own less task because I am using the js less compiler..but I haven't implemented the check for previously changed files

bhagany 2017-12-10T18:46:41.000091Z

okay, you may want to look into using io.perun.content-task - it is basically an abstraction of the file-watching pattern that you just need to plug a rendering function into

richiardiandrea 2017-12-10T18:47:43.000043Z

@bhagany oh that's cool

richiardiandrea 2017-12-10T18:47:48.000024Z

the task is this: https://github.com/degree9/boot-css/pull/1

richiardiandrea 2017-12-10T18:48:02.000011Z

so maybe I should chain the two somehow

bhagany 2017-12-10T18:48:28.000058Z

nice! looking

richiardiandrea 2017-12-10T18:51:35.000006Z

@bhagany in the above, is post? a perun function?

bhagany 2017-12-10T18:51:54.000102Z

ah, no sorry -

bhagany 2017-12-10T18:51:56.000114Z

(defn post?
  [{:keys [path]}]
  (.startsWith path "public/posts/"))

bhagany 2017-12-10T18:52:16.000057Z

it's pretty specific to my particular usage

richiardiandrea 2017-12-10T18:52:27.000093Z

oh ok gotcha

richiardiandrea 2017-12-10T18:53:18.000093Z

can some meta on the fileset obj can be used as well? do we have any available?

bhagany 2017-12-10T18:53:54.000059Z

yep, you can call io.perun.meta/get-global-meta in any task to get the current global metadata

bhagany 2017-12-10T18:54:16.000046Z

(it takes the fileset)

bhagany 2017-12-10T18:56:08.000072Z

(and I just corrected that namespace, sorry)

richiardiandrea 2017-12-10T18:57:22.000084Z

np πŸ˜„

richiardiandrea 2017-12-10T18:57:57.000082Z

so there is no metadata on each individual entry of the fileset, only "global" am I right?

bhagany 2017-12-10T19:02:44.000117Z

there is metadata on each entry

bhagany 2017-12-10T19:03:19.000118Z

my post? filter is using the :path key of that metadata, on each entry, for instance

richiardiandrea 2017-12-10T19:03:55.000014Z

ah that, right

richiardiandrea 2017-12-10T19:04:26.000107Z

so if I had a :post entry I can just read that one

bhagany 2017-12-10T19:05:27.000147Z

yeah, assuming you've put it there by some means, like metadata at the top of a markdown file, or meta arguments passed to perun tasks

richiardiandrea 2017-12-10T19:05:54.000010Z

yep that was the plan πŸ˜„

richiardiandrea 2017-12-10T19:06:30.000047Z

but I guess I will stick to your task for now, I have a folder for the posts as well

bhagany 2017-12-10T19:06:46.000064Z

sounds good πŸ™‚

richiardiandrea 2017-12-10T19:09:37.000039Z

and here you go, here is the other simple one:

(deftask all-tags
  "Adds all the found tags as :all-tags global-meta"
  []
  (with-pre-wrap fileset
    (let [options (merge +recent-posts-defaults+ *opts*)
          global-meta (pmeta/get-global-meta fileset)
          all-tags (->> (pmeta/get-meta fileset)
                        (filter post?)
                        (mapcat :tags)
                        (vec))]
      (pcore/report-info "all-tags" "added %s to metadata" all-tags)
      (pmeta/set-global-meta fileset (assoc global-meta :all-tags all-tags)))))

bhagany 2017-12-10T19:10:00.000060Z

looks great!

richiardiandrea 2017-12-10T19:16:03.000047Z

I actually need dedupication there πŸ˜„

bhagany 2017-12-10T19:16:12.000100Z

ahhhh. right

bhagany 2017-12-10T19:21:27.000110Z

hrm... does lessc report the files it writes?

richiardiandrea 2017-12-10T19:21:46.000065Z

I don't think it does...

bhagany 2017-12-10T19:22:03.000099Z

okay, I'll see if I can work around that

richiardiandrea 2017-12-10T19:22:13.000004Z

I was checking content-task and I saw that it needs more things in there...

bhagany 2017-12-10T19:24:15.000140Z

yeah, my intuition says that you'd be able to use other things that perun defines for those other arguments, but I'm not absolutely sure

richiardiandrea 2017-12-10T19:28:27.000088Z

also the all-tags seems not to work because it needs to happen after rendering the posts...

bhagany 2017-12-10T19:29:23.000014Z

ah... yes

richiardiandrea 2017-12-10T19:29:30.000006Z

uhm, actually even :recent-posts does not display in the individual posts

bhagany 2017-12-10T19:30:08.000151Z

yeah, my needs allow me to call recent-posts after the initial rendering

bhagany 2017-12-10T19:30:49.000031Z

but I'm assuming you need the tags on each page, in a menu or sidebar of some sort?

richiardiandrea 2017-12-10T19:31:38.000063Z

yep

bhagany 2017-12-10T19:31:58.000025Z

okay, I have a solution for you. well, actually, an example

richiardiandrea 2017-12-10T19:32:02.000066Z

I tried just now to render twice, I get a frankestain, but I see the tags and the posts

bhagany 2017-12-10T19:32:22.000073Z

ah, you're on the right track

bhagany 2017-12-10T19:32:23.000081Z

one sec

1
bhagany 2017-12-10T19:33:43.000068Z

I'm calling render twice there, but I'm using two different render functions

bhagany 2017-12-10T19:34:05.000018Z

the second one basically wraps the content produced by the first one

richiardiandrea 2017-12-10T19:35:04.000105Z

uhm...

bhagany 2017-12-10T19:35:04.000148Z

in your case, you could do the initial render of the content that's unique to each page, then collect metadata, then render each again

richiardiandrea 2017-12-10T19:36:21.000081Z

let me bend my mind for a sec, not sure I get this πŸ˜„

bhagany 2017-12-10T19:36:38.000105Z

no worries, I'm here to help πŸ™‚

richiardiandrea 2017-12-10T19:37:31.000042Z

so my second renderer would render only the menu/sidebar?

bhagany 2017-12-10T19:37:38.000020Z

yes, right

πŸ‘ 1
bhagany 2017-12-10T19:40:21.000076Z

if that doesn't appeal to you, I think there's another way too, if you'd like to hear it

richiardiandrea 2017-12-10T19:41:03.000062Z

I'd like to, just to see if it is less code change

bhagany 2017-12-10T19:41:34.000086Z

okay. is the metadata you need to generate your sidebar at the head of your markdown files?

richiardiandrea 2017-12-10T19:41:57.000062Z

yes, basically :tags + :recent-posts for now

bhagany 2017-12-10T19:43:33.000037Z

okay, if you take a look at the markdown task, you'll see that it's the composition of two simpler tasks: yaml-metadata and markdown*

richiardiandrea 2017-12-10T19:44:12.000016Z

ah!

bhagany 2017-12-10T19:44:19.000074Z

that would allow you to split up the work that markdown does, so that metadata is collected, then you can run your custom tasks, and then markdown*

richiardiandrea 2017-12-10T19:44:49.000094Z

yep that would work for :tags, not for :recent-posts if I understand correctly

richiardiandrea 2017-12-10T19:44:57.000019Z

oh well

richiardiandrea 2017-12-10T19:45:11.000041Z

unless I only display :title and :headline

bhagany 2017-12-10T19:45:24.000016Z

I'm not sure I follow

richiardiandrea 2017-12-10T19:45:57.000082Z

uhm ok let me try first your suggestion

richiardiandrea 2017-12-10T19:46:12.000032Z

checking those tasks..

bhagany 2017-12-10T19:46:29.000046Z

okay, let me know if you have any questions

richiardiandrea 2017-12-10T19:46:41.000123Z

thanks a lot πŸ˜„ really appreaciate!

πŸ‘ 1
richiardiandrea 2017-12-10T20:07:21.000035Z

@bhagany that first solution does not seem to work for me...I basically call a page function already...which wraps...but moving the sidebar is not enough, the content also changes...rendering twice I thought would replace the file but for some reason it does not

bhagany 2017-12-10T20:09:04.000154Z

Ah if the content also changes, then yeah, that wouldn’t work

bhagany 2017-12-10T20:09:17.000086Z

I’m confused about the file replacement not happening though

richiardiandrea 2017-12-10T20:09:42.000105Z

yeah me too...I am investigating

richiardiandrea 2017-12-10T20:16:26.000064Z

it looks like the rendered the second time around has the content of the first render

richiardiandrea 2017-12-10T20:20:27.000026Z

yes so :content now contains the whole html

bhagany 2017-12-10T20:20:47.000089Z

Ah, yes, sorry I wasn’t clearer about that

bhagany 2017-12-10T20:21:08.000072Z

It’s going to progressively build up the html, from the inside out

bhagany 2017-12-10T20:21:46.000078Z

You can take a look at the rendering functions for http://nicerthantriton.com to see

richiardiandrea 2017-12-10T20:22:12.000080Z

ok now I see why that works πŸ˜„

richiardiandrea 2017-12-10T20:46:10.000037Z

@bhagany sorry if I bombard

bhagany 2017-12-10T20:46:26.000104Z

You’re fine, go ahead :)

richiardiandrea 2017-12-10T20:46:30.000050Z

but it is not hmtl I see now in :content πŸ˜„

richiardiandrea 2017-12-10T20:46:43.000012Z

I see a string containing hiccup

bhagany 2017-12-10T20:46:51.000082Z

After which task?

richiardiandrea 2017-12-10T20:47:12.000131Z

now I do:

(perun/render :out-dir posts-dir :renderer '<http://com.andrearichiardi.blog.post/render-content|com.andrearichiardi.blog.post/render-content> :filterer post?)
   (recent-posts)
   (all-tags)
   (perun/render :out-dir posts-dir :renderer '<http://com.andrearichiardi.blog.post/render-page|com.andrearichiardi.blog.post/render-page>)
  

richiardiandrea 2017-12-10T20:49:02.000021Z

i see you are doing an into the second time in page

richiardiandrea 2017-12-10T20:49:19.000031Z

so I was expecting hiccup in there...

richiardiandrea 2017-12-10T20:50:37.000078Z

oh wait

richiardiandrea 2017-12-10T20:50:59.000065Z

so the first time :content is set to whatever the first render returns

richiardiandrea 2017-12-10T20:51:06.000010Z

is it converted to string?

bhagany 2017-12-10T20:51:31.000151Z

yes, I think it'll be the file contents as a string

bhagany 2017-12-10T20:51:40.000035Z

I'm trying to understand why I have that into though...

richiardiandrea 2017-12-10T20:52:28.000008Z

yeah it looks like your return html5 stuff from post...

bhagany 2017-12-10T20:53:15.000020Z

I think maybe that's just leftover from an earlier iteration of this code...

bhagany 2017-12-10T20:53:45.000096Z

before I modified render to work this way, I used function composition to achieve what I wanted, and in that case, I think that content was a hiccup data structure

bhagany 2017-12-10T20:53:57.000022Z

and now that it's a string it kind of accidentally works

richiardiandrea 2017-12-10T20:54:14.000025Z

if my first renderer wrap thing with hiccup.core/html5, then I receive hiccup as data in the second render

bhagany 2017-12-10T20:54:34.000106Z

in the :content key?

richiardiandrea 2017-12-10T20:54:39.000089Z

yep

bhagany 2017-12-10T20:54:44.000007Z

that's really odd...

richiardiandrea 2017-12-10T20:55:05.000145Z

so this works:

(defn render-content [{global-meta :meta entries :entries entry :entry}]
  (html5 ;; I should not need this, maybe perun bug?
    [:div.twelve.wide.column
     "&lt;!-- Post --&gt;"
     [:div.ui.right.floated.basic.segment
      [:div.ui.compact.segments
       [:div.ui.segment
        [:a.ui.medium.image {:href (:canonical-url entry)}
         [:img {:src (or (:image entry) "<http://p-hold.com/300/250>")}]]]
       (when-let [sponsor-img (:sponsor-image entry)]
         [:div.ui.right.aligned.basic.secondary.segment
          [:div "Sponsored by"
           [:a.ui.tiny.image {:href (:sponsor-url entry)}
            [:img {:src sponsor-img}]]]])]]
     [:div (:content entry)]
     "&lt;!-- End Post --&gt;"]))

(defn render-page [{global-meta :meta entries :entries entry :entry}]
  (let [all-tags (or (:all-tags entry) (into #{} (mapcat :tags) entries))
        latest-posts (or (:latest-posts entry) (take 3 entries))
        active-tags (-&gt;&gt; [(:tag entry)] (keep identity) (into #{}))]
    (base/page
     global-meta
     [:div.ui.centered.main.stackable.reverse.grid.container {:role "content"}
      (:content entry) ;; will receive the html from render-content above
      "&lt;!-- Sidebar --&gt;"
      (base/sidebar all-tags latest-posts active-tags)
      "&lt;!-- End Sidebar --&gt;"])))

bhagany 2017-12-10T20:55:13.000015Z

thanks, was just going to ask for that πŸ™‚

bhagany 2017-12-10T20:56:30.000036Z

oh, that works

bhagany 2017-12-10T20:56:39.000106Z

I missed that part

bhagany 2017-12-10T20:56:57.000008Z

so yes, that's what I would expect - you were expecting not to have to call html5?

richiardiandrea 2017-12-10T20:57:10.000008Z

yep in render-content

bhagany 2017-12-10T20:57:50.000048Z

okay, yeah, render isn't tied to any particular html renderer, so it's up to you to figure out how you want to return html

bhagany 2017-12-10T20:58:13.000049Z

but the return of your render function needs to be the string that you want written to the file

bhagany 2017-12-10T20:58:27.000091Z

(also, it doesn't have to be html)

richiardiandrea 2017-12-10T20:58:35.000099Z

lhm

richiardiandrea 2017-12-10T20:58:45.000027Z

so :content just needs to be a string

bhagany 2017-12-10T20:58:55.000044Z

yes, right

richiardiandrea 2017-12-10T20:59:05.000002Z

let me try one thing

richiardiandrea 2017-12-10T20:59:56.000083Z

@bhagany fun!

(ns <http://com.andrearichiardi.blog.post|com.andrearichiardi.blog.post>
  (:require [clojure.edn :as edn]
            [com.andrearichiardi.blog.base :as base]))

(defn render-content [{global-meta :meta entries :entries entry :entry}]
  (pr-str
   [:div.twelve.wide.column
    "&lt;!-- Post --&gt;"
    [:div.ui.right.floated.basic.segment
     [:div.ui.compact.segments
      [:div.ui.segment
       [:a.ui.medium.image {:href (:canonical-url entry)}
        [:img {:src (or (:image entry) "<http://p-hold.com/300/250>")}]]]
      (when-let [sponsor-img (:sponsor-image entry)]
        [:div.ui.right.aligned.basic.secondary.segment
         [:div "Sponsored by"
          [:a.ui.tiny.image {:href (:sponsor-url entry)}
           [:img {:src sponsor-img}]]]])]]
    [:div (:content entry)]
    "&lt;!-- End Post --&gt;"]))

(defn render-page [{global-meta :meta entries :entries entry :entry}]
  (let [all-tags (or (:all-tags entry) (into #{} (mapcat :tags) entries))
        latest-posts (or (:latest-posts entry) (take 3 entries))
        active-tags (-&gt;&gt; [(:tag entry)] (keep identity) (into #{}))]
    (base/page
     global-meta
     [:div.ui.centered.main.stackable.reverse.grid.container {:role "content"}
      (edn/read-string (:content entry)) ;; will receive the html from render-content above
      "&lt;!-- Sidebar --&gt;"
      (base/sidebar all-tags latest-posts active-tags)
      "&lt;!-- End Sidebar --&gt;"])))

bhagany 2017-12-10T21:00:38.000084Z

hehehehehe, so that outputs the hiccup as a string, then?

richiardiandrea 2017-12-10T21:00:54.000058Z

yes pr-str outputs clojure data

bhagany 2017-12-10T21:00:58.000109Z

nice

richiardiandrea 2017-12-10T21:01:29.000026Z

I feel better to have data there πŸ˜„

richiardiandrea 2017-12-10T21:02:26.000175Z

this could be a topic for my new blog πŸ˜„

bhagany 2017-12-10T21:02:59.000005Z

it's interesting, it will definitely be more flexible for subsequent renders

richiardiandrea 2017-12-10T21:04:58.000030Z

you could serialize and pass a function over πŸ˜„

richiardiandrea 2017-12-10T21:15:42.000014Z

other question...sometimes yaml errors out with:

richiardiandrea 2017-12-10T21:15:54.000015Z

ll; mapping values are not allowed here;  in 'string', line 4, column 87:
                                                  ...  in a GitHub blog? The answer is: of course you can! At the end  ... 
                                                                                      ^
        problem: "mapping values are not allowed here"
    problemMark: #object[org.yaml.snakeyaml.error.Mark 0x5cf45f80 " in 'string', line 4, column 87:\n     ...  in a GitHub blog? The answer is: of course you can! At the end  ... \n                                         ^"]

richiardiandrea 2017-12-10T21:16:16.000043Z

ah maybe a character that means something