Skip to content

Instantly share code, notes, and snippets.

@ratbeard
Created July 15, 2011 15:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ratbeard/1084877 to your computer and use it in GitHub Desktop.
Save ratbeard/1084877 to your computer and use it in GitHub Desktop.
Middleman feature for generated pages based off entries in a data yaml file
<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>
# 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
---
- 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment