In Jekyll, how can I programmatically modify permalinks for pages? - ruby

In a Jekyll site with many pages (not blog posts), I want to tweak the permalink of each page programatically. I tried a Generator plugin, something like:
module MySite
class MySiteGenerator < Jekyll::Generator
def generate(site)
site.pages.each do |page|
page.data['permalink'] = '/foo' + page.url
# real world manipulation of course more complicated
end
end
end
end
But although this would run and set the page.data['permalink'] field, the output was still the same.
Is there something I'm doing wrong, or is there a different way entirely of doing this? Thanks!

It can be easier to override the page class with something like this :
module Jekyll
class Page
alias orig_permalink permalink
def permalink
permalink = orig_permalink
newPermalink = "foo/#{permalink}"
end
end
end
Not tested.

Related

Preprocessing markup files in jekyll

I would like to write a preprocessor that operates on a range of markup languages before they're processed into HTML by Jekyll. Ideally the user would simply create a file called _posts/xxyyzz.md.wmd, and Jekyll would preprocess it into xxyyzz.md using a plugin I provide, and then process that into HTML in the usual way.
It looks like Jekyll's Converter framework doesn't allow that, because the output_ext function is only given the final extension "wmd", preventing it from returning ".md" for ".md.wmd", ".textile" for ".textile.wmd", etc.
Is there a way to implement a chain of processing steps like this?
EDIT: grammar
Maybe you can try to use a Generator plugin that uses your wmd converter:
require "yourWmdConverter"
module Jekyll
class ConvertWmd < Jekyll::Generator
def initialize(config)
config['convert_wmd'] ||= true
end
def generate(site)
#site = site
site.posts.docs.each { |post| convertWmd post }
end
private
def convertWmd(post)
post.content = yourWmdConverter post.content
end
end
end

Two versions of each blog post in Jekyll

I need two versions of each of my posts in a very simple Jekyll setup: The public facing version and a barebones version with branding specifically for embedding.
I have one layout for each type:
post.html
post_embed.html
I could accomplish this just fine by making duplicates of each post file with different layouts in the front matter, but that's obviously a terrible way to do it. There must be a simpler solution, either at the level of the command line or in the front matter?
Update:
This SO question covers creating JSON files for each post. I really just need a generator to loop through each post, alter one value in the YAML front matter (embed_page=True) and feed it back to the same template. So each post is rendered twice, once with embed_page true and one with it false. Still don't have a full grasp of generators.
Here's my Jekyll plugin to accomplish this. It's probably absurdly inefficient, but I've been writing in Ruby for all of two days.
module Jekyll
# override write and destination functions to taking optional argument for pagename
class Post
def destination(dest, pagename)
# The url needs to be unescaped in order to preserve the correct filename
path = File.join(dest, CGI.unescape(self.url))
path = File.join(path, pagename) if template[/\.html$/].nil?
path
end
def write(dest, pagename="index.html")
path = destination(dest, pagename)
puts path
FileUtils.mkdir_p(File.dirname(path))
File.open(path, 'w') do |f|
f.write(self.output)
end
end
end
# the cleanup function was erasing our work
class Site
def cleanup
end
end
class EmbedPostGenerator < Generator
safe true
priority :low
def generate(site)
site.posts.each do |post|
if post.data["embeddable"]
post.data["is_embed"] = true
post.render(site.layouts, site.site_payload)
post.write(site.dest, "embed.html")
post.data["is_embed"] = false
end
end
end
end
end

What are the steps to getting this 'custom' permalink scheme in Jekyll?

I'm writing a Jekyll setup and I'd like to get my posts to have a permalink in the form: /2013/jan/something-something-in-january. I understand that it is impossible with vanilla permalinks to:
get the :month to be in text form or
get the :title to be dash delimited
I remember reading somewhere that I could achieve this by writing a plugin, but I'm not sure how. How can I do this?
I created a generator plugin:
module Jekyll
class PermalinkRewriter < Generator
safe true
priority :low
def generate(site)
# Until Jekyll allows me to use :slug, I have to resort to this
site.posts.each do |item|
item.data['permalink'] = '/' + item.slug + '/'
end
end
end
end

Can't get page data from Jekyll plugin

I'm trying to write a custom tag plugin for Jekyll that will output a hierarchical navigation tree of all the pages (not posts) on the site. I'm basically wanting a bunch nested <ul>'s with links (with the page title as the link text) to the pages with the current page noted by a certain CSS class.
I'm very inexperienced with ruby. I'm a PHP guy.
I figured I'd start just by trying to iterate through all the pages and output a one-dimensional list just to make sure I could at least do that. Here's what I have so far:
module Jekyll
class NavTree < Liquid::Tag
def initialize(tag_name, text, tokens)
super
end
def render(context)
site = context.registers[:site]
output = '<ul>'
site.pages.each do |page|
output += '<li>'+page.title+'</li>'
end
output += '<ul>'
output
end
end
end
Liquid::Template.register_tag('nav_tree', Jekyll::NavTree)
And I'm inserting it into my liquid template via {% nav_tree %}.
The problem is that the page variable in the code above doesn't have all the data that you'd expect. page.title is undefined and page.url is just the basename with a forward slash in front of it (e.g. for /a/b/c.html, it's just giving me /c.html).
What am I doing wrong?
Side note: I already tried doing this with pure Liquid markup, and I eventually gave up. I can easily iterate through site.pages just fine with Liquid, but I couldn't figure out a way to appropriately nest the lists.
Try:
module Jekyll
# Add accessor for directory
class Page
attr_reader :dir
end
class NavTree < Liquid::Tag
def initialize(tag_name, text, tokens)
super
end
def render(context)
site = context.registers[:site]
output = '<ul>'
site.pages.each do |page|
output += '<li>'+(page.data['title'] || page.url) +'</li>'
end
output += '<ul>'
output
end
end
end
Liquid::Template.register_tag('nav_tree', Jekyll::NavTree)
page.title is not always defined (example: atom.xml). You have to check if it is defined. Then you can take page.name or not process the entry...
def render(context)
site = context.registers[:site]
output = '<ul>'
site.pages.each do |page|
unless page.data['title'].nil?
t = page.data['title']
else
t = page.name
end
output += "<li>'+t+'</li>"
end
output += '<ul>'
output
end
Recently I faced a similar problem where the error "cannot convert nill into string" is just blowing my head. My config.yml file holds a line something like this " baseurl: /paradocs/jekyll/out/ " now thats for my local for a server i need to make that beseurl empty and the error starts to appear in build time so finally i have to made " baseurl: / " .. And that's did my job.

Call Sinatra erb from another class

I need to render a Sinatra erb template inside a class in my controller. I'm having issues calling this though. I've looked in the Sinatra rdocs and have come up with this:
Sinatra::Templates.erb :template_to_render
When I do this, I get the following error:
undefined method `erb' for Sinatra::Templates:Module
Is there a way to call this from another class?
To imitate rendering behavior of Sinatra controller in some other class (not controller) you can create module like this:
module ErbRender
include Sinatra::Templates
include Sinatra::Helpers
include Sinatra::ContentFor
def settings
#settings ||= begin
settings = Sinatra::Application.settings
settings.root = "#{ROOT}/app"
settings
end
end
def template_cache
#template_cache ||= Tilt::Cache.new
end
end
Here you may need to tune settings.root
Usage example:
class ArticleIndexingPostBody
include ErbRender
def get_body
erb :'amp/articles/show', layout: :'amp/layout'
end
end
This will properly render templates with layouts including content_for
why you don't require 'erb' and after use only erb
## You'll need to require erb in your app
require 'erb'
get '/' do
erb :index
end
You could have your class return the template name and render it in the main app.
Of course that's not exactly an answer (I don't have enough rep to add a comment with this account) and you're probably doing just that by now anyway...

Resources