Filtering compilation based on Metadata

119 views
Skip to first unread message

da...@dpwright.com

unread,
Aug 16, 2014, 5:24:03 AM8/16/14
to hak...@googlegroups.com
Hello,

I'm trying to implement a feature whereby a particular file may or may not be compiled based on its Metadata.  I can see that Rules has access to metadata through its MonadMetadata implementation, but I need to pass an Identifier to it.  I have used `match` to get the initial list of files which may or may not have the metadata in question.  I can think of a few ways to approach the issue:

1) If there was some way, within Rules, to run some sort of "filter" that'd be ideal so I could just write something like `match "posts/*" $ filter hasRequiredMetadata $ do ...`
2) Failing that, I could move the logic into the Compiler instead.  Returning the empty string doesn't really work because it results in an empty file rather than no file at all, and I don't want to return an error either, because this isn't an error, so I would need some way to drop out of the Compiler without generating any output.
3) If I had access to `Hakyll.Core.Rules.Internal` I could maybe write a custom `Rules` akin to `match`, but with the filtering.  This isn't exposed though.
4) I could use `getMatches` to get a list of all Identifiers matching my glob, and then loop over them, checking the identifier and manually running `rulesExtraDependencies` and `create` to manually generate each page individually (rather than using `Routes`).

Currently, (4) above is the only one I can figure out how to do, but it seems a bit heavy-handed.

Is there a better way to do this which I haven't thought of?  A quick google throws up some tutorials for adding a "published: true" style metadata field, which is basically the functionality I'm looking for, but they're all aimed at Hakyll 3 as far as I can tell.

Any hints greatly appreciated!

-Dani.

nichola...@gmail.com

unread,
Sep 16, 2014, 10:37:04 PM9/16/14
to hak...@googlegroups.com, da...@dpwright.com
I'd like to do essentially the same thing! I found a way but it requires modifying Hakyll. I opened a pull request:

https://github.com/jaspervdj/hakyll/pull/300

For the record, I'm using this to cross-publish my blog posts across multiple subblog "streams." This means each post I write can end up in one or all of my subblogs by specifying them in a metadata field. E.g.:

  forM_ subblogNames (\sb ->                                                                                                                                                                 
    matchMetadata "content/posts/*" (isSubblog sb) $ version sb $ do
    ...


I'd be glad to find a way to do this without needing to hack Hakyll, but a heavy-handed approach like the one Dani imagines as (4) is all I have been able to devise.

Nick

Kyle Marek-Spartz

unread,
Sep 30, 2014, 9:28:13 PM9/30/14
to hak...@googlegroups.com, hak...@googlegroups.com, da...@dpwright.com
Not sure it applies exactly here, but for HaskellMN we created two different feeds, one with a tweetable version of the article and one with the full article.


I created a different post context which uses the description from the metadata instead of the body of the post as the description in the tweet context:


Also, for my site, I created two different feeds, one with the 10 most recent and one with everything (for Google indexing purposes):


Here I extracted the feed compiler and then fed it a filter depending on which feed I wanted to make:

Some combination of the two approaches may give you what you need without modifying Hakyll itself.

Kyle Marek-Spartz
 
 
 
 
 
On Sep 16, 2014, 9:37:03 PM, (null) <nichola...@gmail.com> wrote:

Martin Hilscher

unread,
Oct 1, 2014, 4:28:05 AM10/1/14
to hak...@googlegroups.com
Hi,

I might be completely mistaking and this might not be helpful to you at
all, but I solve a similar problem differently.
I have a section on my blog where I list all talks I held. This list is
generated by looking at the Metadata of the file in question if that
contains "type: talk" it goes into the list (and the corresponding
archive).

Have a look if you like:

https://github.com/xinitrc/xinitrc.de/blob/master/site.hs#L209

this should be the line containing the filterByType with the convenience
method filterTalks right after that.

In addition you can put this method in a chain with e.g. recentFirst
like here.

https://github.com/xinitrc/xinitrc.de/blob/master/site.hs#L162

This works without hacking Hakyll. So it might be the solution to what
you wanted.

Best regards,

Martin
> --
> You received this message because you are subscribed to the Google
> Groups "hakyll" group.
> To unsubscribe from this group and stop receiving emails from it, send
> an email to hakyll+un...@googlegroups.com.
> For more options, visit https://groups.google.com/d/optout.


---

Email is like Tetris, you lose eventually.

Nicholas McAvoy

unread,
Oct 5, 2014, 2:55:15 PM10/5/14
to hak...@googlegroups.com
Thanks to Kyle and Martin for looking at this problem and replying!

Strategies like the ones you mention do get me part of the way to my
goal. For instance, I think I wrote the exact same function as Martin's
`filterByType` except I call it `onlyItemsForSubblog`:

https://github.com/ohbadiah/nickmcavoy-dotcom/blob/cross-subblog/blog/src/Nick/Subblogs.hs#L18

I also think it's really interesting how Kyle creates a Compiler
directly from Context.

However, I think all of that only gets me aggregate views of the posts
from manipulating an entire list of items, as in my `processSubblogIndex`:

https://github.com/ohbadiah/nickmcavoy-dotcom/blob/cross-subblog/blog/src/Main.hs#L241

Ultimately though I think I still need something other than `create` and
`match` for compiling each blog post itself to versions in multiple
subblogs. The plan is to map each file that matches a glob to multiple
compiled output locations based on Metadata. `create` won't do this
because it doesn't glob; you just specify the output route. `match` will
I believe map each globbed file to exactly one compiled output location,
but not more or less than one.

My little `matchMetadata` maps each globbed file to either zero or one
output locations, which combined with forM_ gives me the one-to-many
functionality I seek:

https://github.com/ohbadiah/nickmcavoy-dotcom/blob/cross-subblog/blog/src/Main.hs#L104

With further thought I think one way I might be able to do this without
modifying Hakyll is to leverage `create` rather than `match:` I could
generate the list of output routes for each blog post for each of its
subblogs and then `create` Rules for each item in those lists. It might
not be too much worse than the links list I figured out how to put in
Context this weekend:

https://github.com/ohbadiah/nickmcavoy-dotcom/blob/cross-subblog/blog/src/Nick/Subblogs.hs#L28

So maybe that would work, but the glory of open source software is there
isn't anything stopping me from building and using a version of Hakyll
with my `matchMetadata` in the short term.

Finally, I should note that this is rather a lot of gymnastics in order
to support behavior just a little bit different from the Categories that
are already baked in to Hakyll. That's the fun of hacking around,
though; whatever user experience I wish to have as a blogger, that's the
one that I can build as a hacker!

Thanks again to both of you for your help. Let me know if I'm missing
something.

Nick
Reply all
Reply to author
Forward
0 new messages