When making dynamic pages with local data, is it possible to add dynamic content to the frontmatter?

Hey Everyone, first of all, thank you for MIddleman!! This is the best ruby project tool I have discovered in a long time. I wish I had found it 6 months ago when I was working on a project that would have been perfect for Middleman.

On my current site, I am generating dynamic pages based on a local data file. I would love to be able to use dynamic data within the frontmatter for these pages. Things like page title, meta description, open graph image, canonical url, etc. I understand that frontmatter is YAML, that’s why it is great, and you cannot put dynamic data in a traditional YAML file, but is it possible in a Middleman template?

Thanks in advance for your help and this awesome project!!

As far as I’ve been able to figure this out, the YAML frontmatter is read into two places:

current_page.data – that can not be changed.

current_page.metadata[:page] – that can.

So you should be able to place/change your data there. (For example an open graph image) and then use it in your template or helper functions.

Unfortunately, with the same data in two places, you don’t know which set other developers will use. For example, the blog extension template gets the title from current_page.data, so you can’t change the title (unless you modify the extensions template).

Thanks @tommysundstrom for the reply.

What I ended up doing was creating a new layout file specific to my content, let’s call them projects. Inside the projects layout, I was able to directly reference the local data variable and skip defining page specific, dynamic frontmatter.

In config.rb:

data.work.projects.each do |project|
  proxy "/#{project.slug}.html", "/project.html", :locals => { :project => project }, :ignore => true
end

This creates all of my project pages from my local data file. (This is awesome, by the way!!).

Then at the top of my project template, I defined the layout in the frontmatter. (note: this is the only frontmatter definition I used in this case):

---
layout: "projects"
---

Finally, in my projects layout file, I am able to reference my local data directly:

%title project['title']
%meta{content: project['meta_description'], name: "description"}/
%meta{content: project['title'], property: "og:title"}/
%meta{content: project['canonical'], property: "og:url"}/
%meta{content: project['image'], property: "og:image"}/
%meta{content: project['meta_description'], property: "og:description"}/
%link{href: project['canonical'], rel: "canonical"}/

Works exactly as expected.

If anyone has a cleaner solution, I would love to know about it!!

Yup, that’s the current way it works, but I’m fixing data for 4.0 so there will only be one place.

@mindtonic I was hoping you might be able to elaborate a little more on your method, as I am trying to wrap my mind around it. What I am trying to do is remove all the <meta> info from the layout.erb template and specify custom <meta> info on each page, either on the page directly or on a config file and have it inserted with a tag. I am fairly new to this and my Ruby experience is still in noob status, so any help would be awesome.

Thank you,
Chuck

Exemple on how you can add a meta tag from frontmatter: How to: Controlling which image Facebook shows when your article is shared

@tommysundstrom Thank you for the link, but I feel like a fish out of water not being able to figure this out. There’s the layout.erb page that is the primary template for the pages.html.erb pages, which also includes the <head> area that is applied to all the sites pages by default.

Much like being able specifiy the page title by

---
title: Page Title
--- 

I would like to specify description and keywords on a per page basis, essentially overriding anything that may be default in the layout.erb page.

Thank you for your help. I hope I am not the only one trying to figure this out and the results from this thread will help many.

Thank you again.

I’m not familiar with erb, but it should be something like this

---
description: Text with description for this page
---
<head>
  ...
  <% if current_page.metadata[:page].try(:[], 'description') %>
    <meta name="description" content="<%= current_page.metadata[:page]['description'] %>">
  <% end %>
  ...
</head>

@tommysundstrom Rocking brotha!! That worked!! Thank you, again.

Or more simply:

---
description: Text with description for this page
---
<head>
  ...
  <% if current_page.data.description %>
    <meta name="description" content="<%= current_page.data.description %>">
  <% end %>
  ...
</head>

You only need current_page.metadata[:page] for data added by extensions, etc. For frontmatter data, current_page.data will work fine.

3 Likes

@bhollis That worked great! Thank you so much.

@mindtonic can you go into more detail about how you generate the canonical link? I’m also trying to generate canonical links for my pages but I feel like I’m in the movie Inception thinking about how to output the full URL of a current_page into a partial called header.

Are you setting a variable for the domain in your config.rb or project template?

The key to this process is in the data/projects.yml file. Here is an excerpt:

projects:
  -
    title: Awesome Project 1
    slug: project_1
    images:
      - awesomeproject1.png
    meta_description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    description: |
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique ut quam non fermentum. In a sodales mi. Ut non ullamcorper ante, sit amet sagittis diam. Ut turpis orci, malesuada a lacus eu, fringilla faucibus felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam nec est at nibh mattis mattis eget sit amet leo. Cras tristique vestibulum nisi. Fusce iaculis venenatis metus, vitae luctus nunc blandit eget. Integer et nunc sed augue varius varius eu et elit. Suspendisse eu nisl venenatis, vehicula mi eu, rhoncus mauris. Integer magna diam, sodales a volutpat vel, cursus posuere nisi.
  -
    title: Awesome Project 2
    slug: project_2
    images:
      - awesomeproject2.png
      - awesomeproject2_mobile.png
      - awesomeproject2_detail.png
    meta_description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
    description: |
      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique ut quam non fermentum. In a sodales mi. Ut non ullamcorper ante, sit amet sagittis diam. Ut turpis orci, malesuada a lacus eu, fringilla faucibus felis. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam nec est at nibh mattis mattis eget sit amet leo. Cras tristique vestibulum nisi. Fusce iaculis venenatis metus, vitae luctus nunc blandit eget. Integer et nunc sed augue varius varius eu et elit. Suspendisse eu nisl venenatis, vehicula mi eu, rhoncus mauris. Integer magna diam, sodales a volutpat vel, cursus posuere nisi.

In this file I am able to define as many meta attributes as I need. One important note: Because this is my own work, I can safely assume that all attributes will always be present, therefore I do not check for attribute existence in the hash.

I am using traditional slug style URLs. When config.rb loops through all of the records in projects.yml, it assigns the url with the slug. It also points to the specific layout that I am using and passes the attribute has in as a local variable:

data.work.projects.each do |project|
  proxy "/#{project.slug}.html", "/project.html", :locals => { :project => project }, :ignore => true
end

I then apply the values of the attribute hash to the meta tags in the head section of the layout file like this excerpt:

%title Middleman Rocks! | #{project['title']}
%meta{content: project['meta_description'], name: "description"}/
%meta{content: "Middleman Rocks! | #{project['title']}", property: "og:title"}/
%meta{content: "http://middlemanrocks.com/#{project['slug']}", property: "og:url"}/
%meta{content: "http://middlemanrocks.com/img/#{project.images.first}", property: "og:image"}/
%meta{content: project['meta_description'], property: "og:description"}/
%link{href: "http://middlemanrocks.com/#{project['slug']}", rel: "canonical"}/

This could easily be extracted into a helper, I just didn’t need to DRY it up for this project.

Regarding Canonical URL’s, because this is a simple site, I use the generated slug for each page as the Canonical URL. It would also be an option to add a cannonical_url attribute to the YAML file and have that populate the meta tag.

Hope this helps!

1 Like

Yes, this helped clarified what I trying to achieve. Since I’m already using middleman-search_engine_sitemap my config.rb has a set :url_root, 'http://example.com'. So I just reused that in my partial _link.erb as

<link href="<%= settings.url_root %><%= current_page.url %>" rel="canonical" />

Good information thanks for sharing
vmware