Website/lib/gemsmith.rb

280 lines
6.7 KiB
Ruby

require 'yaml'
require 'redcarpet'
require 'erb'
require 'pipeline.rb'
class Array
def where
clone.delete_if { |i| not yield i }
end
end
module GemSmith
def reset_gemsmith()
$gemsmith = {
active: nil,
in_path: nil,
out_path: nil,
static: [],
collections: {},
assets: {}
}
end
def load_site(in_path, out_path)
if File.symlink? in_path then
$stderr.puts "Warning: Skipping symlink #{f}"
[]
elsif File.directory? in_path then
full_out_dir = File.join($gemsmith[:out_path], out_path)
directory "#{full_out_dir}" => [File.dirname("#{full_out_dir}")]
Dir["#{in_path}/*"].map do |f|
out_f = File.join(out_path, File.basename(f))
load_site(f, out_f)
end.flatten(1)
else
[{
modified: File.mtime(in_path),
in_path: in_path,
dependencies: [in_path, File.dirname(File.join($gemsmith[:out_path], out_path))],
out_path: out_path,
rel_dir: File.dirname(out_path),
stream: Pipeline.new(in_path),
"title" => File.basename(out_path, ".*").gsub(/[_-]/, " ").capitalize
}]
end
end
def for_files(pattern)
tmp = $gemsmith[:active]
$gemsmith[:active] =
$gemsmith[:active].where {|f| pattern =~ f[:out_path] }
yield
$gemsmith[:active] = tmp
end
def exclude_files(pattern)
tmp = $gemsmith[:active]
$gemsmith[:active] =
$gemsmith[:active].where {|f| not pattern =~ f[:out_path] }
yield
$gemsmith[:active] = tmp
end
def site(site_name, params = {})
reset_gemsmith()
in_path = params.fetch(:src, "src")
out_path = params.fetch(:out, File.join("build", site_name.to_s))
$gemsmith[:in_path] = in_path
$gemsmith[:out_path] = out_path
$gemsmith[:active] = load_site(in_path, "")
directory out_path
task site_name => [ in_path, out_path ]
yield
$gemsmith[:active].each { |f|
file File.join(out_path, f[:out_path]) => f[:dependencies] do
error = nil;
File.open(File.join(out_path, f[:out_path]), "w+") { |out|
begin
f[:stream].pipe_to(out)
rescue => e
error = e
end
}
unless error.nil?
File.unlink(File.join(out_path, f[:out_path]))
raise error
end
end
task site_name => File.join(out_path, f[:out_path])
}
unless $gemsmith[:static].empty?
task site_name => "#{site_name}_static"
static_assets = $gemsmith[:static]
target_dirs = static_assets.map { |a,b| File.join(out_path, b) }
target_dirs.each { |d| directory d }
target_dirs.push out_path
task "#{site_name}_static" => target_dirs do
static_assets.each { |in_path, out_in_dir|
system("rsync -a #{in_path}/* #{File.join(out_path, out_in_dir)}")
}
end
end
end
def apply
raise "Applying effects to pages outside of a site block" if $gemsmith[:active].nil?
$gemsmith[:active].each { |f| yield f }
end
def extract_date_from_filename
apply { |f|
d = /(\d\d\d\d)-(\d\d?)-(\d\d?)/.match File.basename(f[:in_path])
raise "Expecting date in the name of '#{File.basename f[:in_path]}'" if d.nil?
f[:date] = Time.new(d[1].to_i, d[2].to_i, d[3].to_i)
}
end
def create_collection(name)
curr = nil
collection = []
$gemsmith[:active].each { |f|
f["#{name}_prev".to_sym] = curr
f["#{name}_next".to_sym] = nil
curr["#{name}_next".to_sym] = f unless curr.nil?
curr = f
collection.push(f)
}
$gemsmith[:collections][name] = collection
end
def get_collection(name)
$gemsmith[:collections][name]
end
def extract_headers
apply { |f|
yaml = ["---\n"]
pos = nil
f[:stream].consume { |fp|
if /^---$/ =~ fp.readline then
l = fp.readline
until /^---$/ =~ l do
yaml.push l
l = fp.readline
end
pos = fp.pos
end
}
unless pos.nil?
begin
f.merge!(YAML.load(yaml.join))
rescue => e
raise "Error parsing YAML ('#{f[:in_path]}'):\n#{e}\n#{yaml.join}---"
end
f[:stream].skip_to(pos)
end
}
end
def render_markdown(renderer = Redcarpet::Render::HTML.new(), options = {})
markdown = Redcarpet::Markdown.new(renderer, options)
apply { |f|
f[:out_path] = File.join(File.dirname(f[:out_path]), "#{File.basename(f[:out_path], ".*")}.html")
f[:stream].transform_all { |body|
markdown.render(body)
}
}
end
def render_erb
apply { |f|
f[:out_path] = File.join(File.dirname(f[:out_path]), "#{File.basename(f[:out_path], ".*")}.html")
f[:stream].transform_all { |body|
b = binding()
f.each { |k,v| b.local_variable_set(k, v) }
$gemsmith[:now_rendering] = File.join(f[:rel_dir], File.basename(f[:out_path]))
ERB.new(body).result(b)
}
}
end
def apply_template(default_template)
erb = Hash.new { |h,k|
h[k] = ERB.new(File.open(k) { |fp| fp.read })
}
apply { |f|
template = f.fetch(:template, default_template)
f[:dependencies].push(template)
f[:stream].transform_all { |body|
b = binding()
f.each { |k,v| b.local_variable_set(k, v) }
$gemsmith[:now_rendering] = File.join(f[:rel_dir], File.basename(f[:out_path]))
erb[template].result(b)
}
}
end
def add_dependency(dep)
$gemsmith[:active].each { |f| f[:dependencies].push dep }
end
def add_assets(assets, params = {})
in_dir = params.fetch(:source, "assets")
out_dir = params.fetch(:dir, "assets")
assets.each { |a|
$gemsmith[:active] += load_site(File.join(in_dir, a), File.join(out_dir, a))
$gemsmith[:assets][a] = File.join(out_dir, a)
}
end
def add_file(in_path, out_path)
$gemsmith[:active].push({
dependencies: [in_path, File.join($gemsmith[:out_path], File.dirname(out_path))],
out_path: out_path,
in_path: in_path,
rel_dir: File.dirname(out_path),
stream: Pipeline.new(in_path)
})
end
def add_dir(in_path, out_path)
if File.directory? in_path
directory File.join($gemsmith[:out_path], out_path) =>
File.join($gemsmith[:out_path], File.dirname(out_path))
Dir["#{in_path}/*"].each { |f|
add_dir(f, File.join(out_path, File.basename(f)))
}
else
add_file(in_path, out_path)
end
end
def add_static(in_path, out_path)
$gemsmith[:static].push([in_path,out_path])
end
def add_file_string(out_path, data)
$gemsmith[:active].push({
dependencies: [],
out_path: out_path,
rel_dir: File.dirname(out_path),
stream: Pipeline.new(string: data)
})
end
def root_path(file = nil)
context = $gemsmith[:now_rendering].split(/#{File::SEPARATOR}/)
context.pop
context.shift if context[0] == ""
return file if context.empty?
ret = File.join( *([".."] * context.length))
return File.join(ret, file) unless file.nil?
return ret
end
def asset_path(asset)
target = $gemsmith[:assets][asset]
return "???" if target.nil?
root_path(target)
end
def all_outputs
$gemsmith[:active].map { |x| x[:out_path] }
end
def metadata_for(path)
$gemsmith[:active].find { |x| x[:out_path] == path }
end
end