public
Created

Middleman feature for generated pages based off entries in a data yaml file

  • Download Gist
_article.html.erb
HTML+ERB
1 2 3 4 5 6 7 8 9 10
<link rel="stylesheet" href="/stylesheets/news/detail.css" />
 
<section>
<div class="module central content">
<div class="post">
<h2><%= article.title %></h2>
<%= article.content %>
</div>
</div>
</section>
data_pages.rb
Ruby
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
# DataPages allow you to create generated pages for middleman (i.e. there is no actual
# template file backing up the page).
#
# It requires a yaml data file that consists of an array of entities, each of which must
# have a `slug` property, which is used for the url of that page. If a `title` property
# exists, it will set it for `page.data.title`.
#
# Sample config
#
# # Custom Feature to allow generated pages like: '/news/title-of-article', based off news.yml
# require 'lib/data_pages'
# activate DataPages
# data_page \
# "/news/:slug", # sinatra route
# :data => 'news', # name of data yaml file
# :template => 'news/_article.html.erb', # template to render for each entity
# :name => :article, # local variable name to reference inside the template
# :build_path => 'news/' # when generating the pages with mm-build, prefix slug with this (to match the route above)
#
module DataPages
class << self
def registered(app)
app.extend ClassMethods
end
alias :included :registered
end
 
 
module ClassMethods
def data_page(url, options={})
# Capture app for use inside route handler to add page data
app = self
# Store the data_page info for use by mm-build
set :data_page_list, [] unless respond_to? :data_page_list
self.data_page_list << {:url => url}.merge(options.dup)
 
# Read parameters
template = options.delete(:template) || raise("data_page needs a :template parameter")
data_key = options.delete(:data) || raise("data_page needs a :data parameter")
entity_name = options.delete(:name) || raise("data_page needs a :name parameter")
 
# Copied from core_extension/routing.rb
url = url.gsub(%r{#{settings.index_file}$}, "")
url = url.gsub(%r{(\/)$}, "") if url.length > 1
paths = [url]
paths << "#{url}/" if url.length > 1 && url.split("/").last.split('.').length <= 1
# Tweak, add starting root slash
paths << "/#{path_to_index(url)}"
#puts "!!! making route", *paths
paths.each do |p|
get(p) do
# My logic
slug = URI.escape(params[:slug].chomp(".html"))
 
entities = data.send(data_key)
 
# Find the entity for this requests slug.
# Need to double escape slug, since escape returns '%E64' when the url
# might be '%e64'
unless entity = entities.find {|e| URI.escape(URI.unescape(e.slug)) == slug }
#require 'ruby-debug'; debugger
$stderr.puts "Couldnt find slug #{slug.inspect}"
pass
end
 
# Set page title if entity has that attribute
if title = entity.title
app.data_content("page", {:title => title})
end
 
locals = {}
locals[entity_name] = entity
 
# Copied from base.rb#process_request
old_layout = settings.layout
settings.set :layout, options[:layout] if !options[:layout].nil?
 
layout = if settings.layout
if options[:layout] == false || request.path_info =~ /\.(css|js)$/
false
else
settings.fetch_layout_path(settings.layout).to_sym
end
else
false
end
render_options = { :layout => layout }
render_options[:layout_engine] = options[:layout_engine] if options.has_key? :layout_engine
# Tweak:
#result = render(request.path_info, render_options, options[:locals])
result = render(template, render_options, locals)
settings.set :layout, old_layout
if result
content_type mime_type(File.extname(request.path_info)), :charset => 'utf-8'
status 200
body result
else
status 404
end
end
end
end
 
end
end
 
class Middleman::Builder
# Copy paste override to add `rescue source.to_s`
def tilt_template(source, *args, &block)
config = args.last.is_a?(Hash) ? args.pop : {}
destination = args.first || source
source = File.expand_path(find_in_source_paths(source.to_s)) rescue source.to_s
context = instance_eval('binding')
 
@@rack_test ||= ::Rack::Test::Session.new(::Rack::MockSession.new(SHARED_SERVER))
 
create_file destination, nil, config do
# The default render just requests the page over Rack and writes the response
request_path = destination.sub(/^#{SHARED_SERVER.build_dir}/, "")
@@rack_test.get(request_path)
@@rack_test.last_response.body
end
end
remove_invocation :tilt_template
 
# For each registered data_page, iterate through each entry in the yaml file
# and generate the page for that entity.
# I'm not sure why this generates each page twice.
def build_data_pages
return unless SHARED_SERVER.respond_to?(:data_page_list)
 
SHARED_SERVER.data_page_list.each do |data_page|
instance = SHARED_SERVER.allocate
data = Middleman::CoreExtensions::Data::DataObject.new(instance)
entities = data.send(data_page[:data])
 
entities.each do |entity|
source = 'news/' + entity.slug
destination = "build/#{data_page[:build_path]}#{entity.slug}/index.html"
tilt_template(source, destination, {:force => true})
end
end
end
end
news.yml
YAML
1 2 3 4 5 6 7 8 9
---
- title: "Todays update"
content: "Today was a good day."
slug: what-a-great-day
author: space150
- title: "Hot Sauce"
content: "Cholula is the best, IMO YMMV."
slug: sooo-hot
author: space150

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.