Middleman: referencing URL stored in a data file f

2019-07-03 23:46发布

问题:

For my Middleman-built website I have stored links and other information about all pages in a data file.

data/pages.yaml:

pageA:
  link: /some-long-url-subject-to-change.html
  name: PageA name
  info: Some other related info

Then, in my HAML template (source/test.haml), I can print relative path to pageA with = data.pages.pageA.link.

Now, I want to use markdown syntax to reference that page by its name (pageA).

Example (source/test.html.haml):

.info
    :markdown
        This is some text with a [manual link](https://google.com) to somewhere. 
        This is another text with a [data-referenced link](pageA) to that page.

In the same way as first "manual link" links to Google, I would like second link to use relative path stored in data file to create a link. One solution that I see to solve this problem would be to replace (pageA) text with evaluation of = data.pages.pageA.link prior to it being rendered by markdown.

I assume this would be possible by creating custom helper, but I can't quite nail it.


My attempt at solution

I tried to write a custom helper to replace (pageA) text with evaluation of = data.pages.pageA.link prior to it being rendered by markdown.

I was able to replace specific text (pageA) with information from data and I was also able to write more generic case, which replaces all data references with explicit text of typical data reference. But I can't get to replace data.pages.pageA.link in generic case for evaluation of = data.pages.pageA.link.

My helper:

# Replace specific text with information from ``data/pages.yaml``
specific = text.gsub("pageA",data.pages.pageA.link)
# Generic case: using explicit text
generic = text.gsub(/\]\((.*?)\)/,'](data.pages.\1.link)')
# Generic case: trying to use variable name, but getting explicit text
generic = text.gsub(/\]\((.*?)\)/,'](#{data.pages.\1.link})')

Usage of helper in my test.html.haml:

= myhelper("This is another text with a [data-referenced link](pageA) to that page.")

Printing specific variable gives me what I want (/some-long-url-subject-to-change.html). But printing generic variable results in plain text, instead of information from data file.

It is possible that I am lacking some basic Ruby knowledge and solution is indeed very simple.

回答1:

It looks as if you are trying to write your own templating system, which is probably not the best idea. First, you'll need to write a regex that properly parses Markdown and finds link notation. Then you need to eval the string you pulled to get the value from your data. And finally you'll need to substitute the placeholder with that value. I more or less got that working with this code in config.rb:

helpers do
  def myhelper(text)
    page=/\]\((.*?)\)/.match(text)[1]
    link=eval( "data.pages.#{page}.link" )
    text.gsub(page,link)
  end
end

But please don't use this code! It's fragile and error-prone. Worse, there's a much simpler way to do what you are trying to do. Replace your test.haml with test.haml.erb:

.info
    :markdown
        This is some text with a [manual link](https://google.com) to somewhere. 
        This is another text with a [data-referenced link](<%= data.pages.pageA.link %>) to that page.

= myhelper("This is another text with a [data-referenced link](pageA) to that page.")

The .erb extension tells Middleman to treat the file as an ERB template. In particular, it'll evaluate whatever is between <%= and %> as Ruby code. That means you can skip using a messy helper method. As a bonus, you don't need to write more code to get the other bits of data:

  • <%= data.pages.pageA.name %> => 'PageA name'
  • <%= data.pages.pageA.info %> => 'Some other related info'

If you need to do some more complex processing, such as iterating over a list, you can do that right in the template itself. It's easier to read and maintain that way.

I also included a call to the helper method as contrast. It's a lot less clear what's going on than just using the existing templating system. Plus, you are bound to forget to wrap all the relevant strings in the method and end up with broken links down the road.

As Adam suggested, you might be even better off using Markdown directly and skipping Haml.



回答2:

I'm not sure exactly what you are encountering here. The following works on my end:

Assuming your data file data/pages.yaml contains

pageA:
  link: /some-long-url-subject-to-change.html
  name: PageA name
  info: Some other related info

and your template test.haml.md.erb looks like

---
title: Test
---

This is another text with a [<%= data.pages.pageA.name %>](<%= data.pages.pageA.link %>) to that page.

the output should be as follows, with PageA name being the clickable link.

This is another text with a PageA name to that page.

It seems like you are running into issues with the markdown interpretation. Try adjusting the template filename from test.haml to test.haml.md.erb.

This construction tells Middleman to first interpret the ERB bits, then the Markdown bits, and finally the HAML (or plain HTML if not using HAML).