How to add a Liquid tag into YAML front matter in Jekyll? - yaml

I'm using a plugin to count page views for posts and pages based on Google Analytics. To display the page view count I'm using a Liquid tag {% pageview %}. Is there any way to add this data to YAML front matter, so it can be accessed in a list of popular posts on other pages by something like {{ page.views }}?
Here is the code for the Liquid tag in the plugin:
class PageViewTag < Liquid::Tag
def initialize(name, marker, token)
#params = Hash[*marker.split(/(?:: *)|(?:, *)/)]
super
end
def render(context)
site = context.environments.first['site']
if !site['page-view']
return ''
end
post = context.environments.first['post']
if post == nil
post = context.environments.first['page']
if post == nil
return ''
end
end
pv = post['_pv']
if pv == nil
return ''
end
html = pv.to_s.reverse.gsub(/...(?=.)/,"\\&\u2009").reverse
return html
end #render
end # PageViewTag
How can I instead of registering a Liquid tag merge this data to the data of the post (document in a collection)? And use via {{ page.views }}.

You can use a generator plugin to add some data['views'] to your posts or pages.

Here is the code for the plugin I made:
require 'jekyll'
module Jekyll
class PageviewsData < Jekyll::Generator
safe :true
priority :low
def generate(site)
# require ga-page-view plugin
if !site.config['page-view']
return
end
site.collections.each do |label, collection|
collection.docs.each { |doc|
pv = doc.data['_pv']
views = pv.to_s.reverse.gsub(/...(?=.)/,"\\&\u2009").reverse
doc.data.merge!('views' => views)
}
end
end
end
end

Related

Using Liquid custom tags to output multiple `span` elements`

I'm playing with Jekyll plugins and their ability to create custom tags and I've run into an issue extending my tag to accept a comma separated list.
I started with the following:
{% symbol R %}
And the matching plugin code:
module Jekyll
class Symbol < Liquid::Tag
def initialize(tag_name, text, tokens)
super
#text = text
end
def render(context)
"<span class='symbol-#{#text}'>#{#text}</span>"
end
end
end
Liquid::Template.register_tag('symbol', Jekyll::Symbol)
And the output on my page, as expected, is:
<span class="symbol-R">R</span>
What I'm trying to do now is amend this plugin so I can pass in a comma separated list, for example:
{% symbol R,G %}
I updated my plugin code to this:
module Jekyll
class Symbol < Liquid::Tag
def initialize(tag_name, text, tokens)
super
#text = text
end
def render(context)
symbol = #text.split(',').map(&:strip)
symbol.each do |item|
puts item # to test in terminal
"<span class='symbol-#{#text}'>#{#text}</span>"
end
end
end
end
Liquid::Template.register_tag('symbol', Jekyll::Symbol)
Terminal outputs correctly:
R
G
And my expectation was that I'd get the following on my page:
<span class="symbol-R">R</span><span class="symbol-G">G</span>
But all that shows up on the page is:
RG
Could I get some help in figuring this out? I feel like I'm super close... it's just the actual page rendering I'm clearly messing up on.
In Ruby your function will return the last expression that it executed. For you, this is the symbol.each do … end.
each returns various things, but what it does not do by itself is return the contents of its block. For that, you want map:
symbol.map do |item|
puts item # to test in terminal
"<span class='symbol-#{#text}'>#{#text}</span>"
end
Replace
symbol.each do |item|
puts item # to test in terminal
"<span class='symbol-#{#text}'>#{#text}</span>"
end
by
output = ""
symbol.each do |item|
output =+ "<span class='symbol-#{item}'>#{item}</span>"
end
output

undefined method each for nil class in nokigiri use

i am trying to fetch all links on the given link but it is giving me an error undefined method `each' for nil:NilClass
require 'nokogiri'
def find_links(link)
page = Nokogiri::HTML(open(link))
link_size = page.css('li')
(0..link_size.length).each do |index|
b = link_size[index]['href']
return b
end
end
find_links('http://code.tutsplus.com/tutorials/you-dont-know-anything-about-regular-expressions-a-complete-guide--net-7869').each do |url|
puts url
end
There are couple of issues in your code. Find explanation inline below:
def find_links(link)
page = Nokogiri::HTML(open(link))
link_size = page.css('li')
(0..link_size.length).each do |index|
b = link_size[index]['href'] # You are expecting to get 'href' on list item which is wrong.
return b # You shouldn't return from this block if you are expecting to get all the links. return from here will return from this method itself after first iteration.
# That's why you are getting nil error since link_size[index]['href'] doesn't have href attribute in first list item
end
end
Change your code to: (find explanations inline)
require 'nokogiri'
require 'open-uri'
def find_links(link)
page = Nokogiri::HTML(open(link))
# You want to iterate on anchor tags rather than list.
# See the use of `map`, it will return the array and since this is the last statement it will return from the method, giving all the links.
# .css('li a') will give all the anchor tags which have list item in it's parent chain.
page.css('li a').map { |x| x['href'] }
end
find_links('http://code.tutsplus.com/tutorials/you-dont-know-anything-about-regular-expressions-a-complete-guide--net-7869').each do |url|
puts url
end
require 'nokogiri'
require 'open-uri'
def find_links(link)
page = Nokogiri::HTML(open(link))
link_array = page.css('li')
(1..link_array.length).each do |f|
a=Array.new.push(page.css('li a')[f]['href'])
puts a
end
end
find_links('http://code.tutsplus.com/tutorials/you-dont-know-anything-about-regular-expressions-a-complete-guide--net-7869')

Calling one jekyll plugin from another

I'm writing a jekyll plugin to create a custom tag. It takes an argument and spits out a string of HTML. I've got it mostly working - I can pass it arguments and get back HTML based on those arguments. Great.
Here's what has me stumped: I want to include the render of another plugin as part of my own.
My aspirational plugin is jekyll_icon_list, the plugin I want to use is jekyll-inline-svg. Here's the (abbreviated) code:
require 'jekyll_icon_list/version'
require 'jekyll'
require 'jekyll-inline-svg'
module JekyllIconList
class IconList < Liquid::Tag
def initialize(tag_name, raw_args, tokens)
#raw_args = raw_args
#tokens = tokens
super
end
def parse_arguments(raw_args, settings)
# (Unrelated stuff)
end
def generate_image(icon, settings, context)
# (Unrelated stuff)
# Problem Here:
Liquid::Tag.parse(
'svg',
icon,
#tokens,
Liquid::ParseContext.new
).render(context)
end
def render(context)
# Builds my HTML, using generate_image in the process
end
end
end
Liquid::Template.register_tag('iconlist', JekyllIconList::IconList)
This doesn't throw any errors, but it also doesn't return anything at all.
Other things I've tried:
Jekyll::Tags::JekylInlineSvg.new(
returns a private method error. Jekyll doesn't want me making my own tags directly.
'{% svg #{icon} %}'
Returns exactly that literally with the icon substituted in; jekyll clearly doesn't parse the same file twice.
I'm trying to figure it out from Jekyll's source, but I'm not so practiced at reading source code and keep hitting dead ends. Can anyone point me in the right direction? Much appreciated.
Answering my own question:
def build_svg(icon_filename)
tag = "{% svg #{icon_filename} %}"
liquid_parse(tag)
end
def liquid_parse(input)
Liquid::Template.parse(input).render(#context)
end
Basically create a tiny template consisting of the tag you want to call, and hand it off to Liquid for parsing.
Below is the dirty way, which I used before I found the proper way:
Jekyll::Tags::JekyllInlineSvg.send(:new, 'svg', icon_filename, #tokens).render(context)
I found this question and answer, and while it's correct, I wanted to provide a full end-to-end example.
I wanted to wrap Jekyll Scholar's {% cite %} tags in my own content:
module Jekyll
class RenderTimeTag < Liquid::Tag
def initialize(tag_name, text, tokens)
super
#text = text
end
def build_cite(content, context)
tag = "{% cite #{content} %}"
return liquid_parse(tag, context)
end
def liquid_parse(input, context)
template = Liquid::Template.parse(input)
template.render(context)
end
def render(context)
citation = build_cite(#text, context)
# Yeah, I know this is bad HTML:
"<span tabindex=\"0\" class=\"citeblock\">#{citation}</span>"
end
end
end
Liquid::Template.register_tag('pretty_cite', Jekyll::RenderTimeTag)

How to render all Jekyll pages with a different layout?

I'm trying to create a Jekyll plugin, which should go through all posts and render them with a different layout. Can't figure out how to do that. That's what I have so far:
module Jekyll
class MyGenerator < Generator
priority :low
def generate(site)
site.posts.docs.each do |doc|
page = Page.new(site, site.source, File.dirname(doc.relative_path), doc.basename)
page.do_layout(
site.site_payload,
'post' => Layout.new(site, site.source, '_layouts/my.html')
)
page.write(?)
site.pages << page
end
end
end
end
This code doesn't work.
In my code below, I'm rendering all my pages a second time with the null layout. The resulting files all have the suffix "_BARE"
module Jekyll
class BareHtml < Page
def initialize(site, base, dest_dir, src_dir, page)
#site = site
#base = base
#dir = dest_dir
#dest_dir = dest_dir
#dest_name = page.basename
file_name = "#{page.basename}_BARE.html"
self.process(file_name)
self.read_yaml(base, page.path)
self.data['layout'] = nil ### <-- set the layout name here
end
end
class BareHtmlGenerator < Generator
safe true
priority :low
def generate(site)
# Converter for .md > .html
converter = site.find_converter_instance(Jekyll::Converters::Markdown)
dest = site.dest
src = site.source
# Create destination path
FileUtils.mkpath(dest) unless File.exists?(dest)
site_pages = site.pages.dup
site_pages.each do |page|
bare = BareHtml.new(site, site.source, dest, src, page)
bare.content = converter.convert(bare.content)
bare.render(site.layouts, site.site_payload)
bare.write(site.dest)
site.pages << bare
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.

Resources