perun 2017-12-10

@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:

(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)))))

nice!

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

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.

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

Will start from the above

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

that sounds great!

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?

@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

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

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

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

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

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

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

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

@bhagany oh that's cool

so maybe I should chain the two somehow

nice! looking

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

ah, no sorry -

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

it's pretty specific to my particular usage

oh ok gotcha

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

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

(it takes the fileset)

(and I just corrected that namespace, sorry)

np πŸ˜„

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

there is metadata on each entry

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

ah that, right

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

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

yep that was the plan πŸ˜„

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

sounds good πŸ™‚

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)))))

looks great!

I actually need dedupication there πŸ˜„

ahhhh. right

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

I don't think it does...

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

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

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

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

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

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

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

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

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

ah, you're on the right track

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

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

uhm...

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

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

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

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

yes, right

πŸ‘ 1

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

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

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

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

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*

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*

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

oh well

unless I only display :title and :headline

I'm not sure I follow

uhm ok let me try first your suggestion

checking those tasks..

okay, let me know if you have any questions

thanks a lot πŸ˜„ really appreaciate!

πŸ‘ 1

@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

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

I’m confused about the file replacement not happening though

yeah me too...I am investigating

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

yes so :content now contains the whole html

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

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

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

ok now I see why that works πŸ˜„

@bhagany sorry if I bombard

You’re fine, go ahead :)

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

I see a string containing hiccup

After which task?

now I do:

(perun/render :out-dir posts-dir :renderer ' :filterer post?)
   (recent-posts)
   (all-tags)
   (perun/render :out-dir posts-dir :renderer ')
  

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

so I was expecting hiccup in there...

oh wait

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

is it converted to string?

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

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

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

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

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

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

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

in the :content key?

that's really odd...

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
     "<!-- Post -->"
     [: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) "")}]]]
       (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)]
     "<!-- End Post -->"]))

(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 (->> [(: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
      "<!-- Sidebar -->"
      (base/sidebar all-tags latest-posts active-tags)
      "<!-- End Sidebar -->"])))

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

oh, that works

I missed that part

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

yep in render-content

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

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

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

so :content just needs to be a string

let me try one thing

@bhagany fun!

(ns 
  (: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
    "<!-- Post -->"
    [: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) "")}]]]
      (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)]
    "<!-- End Post -->"]))

(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 (->> [(: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
      "<!-- Sidebar -->"
      (base/sidebar all-tags latest-posts active-tags)
      "<!-- End Sidebar -->"])))

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

yes pr-str outputs clojure data

I feel better to have data there πŸ˜„

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

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

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

other question...sometimes yaml errors out with:

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                                         ^"]

ah maybe a character that means something