lib/hx.rb in hx-0.3.2 vs lib/hx.rb in hx-0.3.3
- old
+ new
@@ -1,8 +1,8 @@
# hx - A very small website generator.
#
-# Copyright (c) 2009 MenTaLguY <mental@rydia.net>
+# Copyright (c) 2009-2010 MenTaLguY <mental@rydia.net>
#
# Permission is hereby granted, free of charge, to any person obtaining
# a copy of this software and associated documentation files (the
# "Software"), to deal in the Software without restriction, including
# without limitation the rights to use, copy, modify, merge, publish,
@@ -19,21 +19,14 @@
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-require 'cgi'
require 'rubygems'
-require 'ostruct'
require 'set'
-require 'date'
-require 'time'
-require 'fileutils'
require 'pathname'
require 'yaml'
-require 'liquid'
-require 'redcloth'
module Hx
class NoSuchEntryError < RuntimeError
end
@@ -124,21 +117,10 @@
def initialize(*sources)
@sources = sources
end
- def edit_entry(path, prototype=nil)
- @sources.each do |source|
- begin
- source.edit_entry(path, prototype) { |text| yield text }
- break
- rescue EditingNotSupportedError
- end
- end
- self
- end
-
def each_entry
seen = Set[]
@sources.each do |source|
source.each_entry do |path, entry|
yield path, entry unless seen.include? path
@@ -220,14 +202,15 @@
self
end
def each_entry
unless @entries
- @entries = []
+ entries = []
@source.each_entry do |path, entry|
- @entries << [path, entry]
+ entries << [path, entry]
end
+ @entries = entries
end
@entries.each do |path, entry|
yield path, entry.dup
end
self
@@ -487,263 +470,20 @@
end
end
class FileBuilder
def initialize(output_dir)
- @output_dir = output_dir
+ @output_dir = Pathname.new(output_dir)
end
def build_file(path, entry)
- filename = File.join(@output_dir, path)
- dirname = File.dirname(filename)
- FileUtils.mkdir_p dirname
- File.open(filename, "wb") do |stream|
+ filename = @output_dir + path
+ dirname = filename.parent
+ dirname.mkpath()
+ filename.open("wb") do |stream|
stream.write entry['content'].to_s
end
- end
-end
-
-module Backend
-
-class Hobix
- include Source
-
- def initialize(source, options)
- @entry_dir = Hx.get_pathname(options, :entry_dir)
- end
-
- def yaml_repr(value)
- YAML.parse(YAML.dump(value))
- end
- private :yaml_repr
-
- def edit_entry(path, prototype=nil)
- entry_filename = @entry_dir + "#{path}.yaml"
- begin
- text = entry_filename.read
- previous_mtime = entry_filename.mtime
- rescue Errno::ENOENT
- raise NoSuchEntryError, path unless prototype
- prototype = prototype.dup
- prototype['content'] = (prototype['content'] || "").dup
- content = prototype['content']
- def content.to_yaml_style ; :literal ; end
- native = YAML::DomainType.new('hobix.com,2004', 'entry', prototype)
- text = YAML.dump(native)
- previous_mtime = nil
- end
- text = yield text
- repr = YAML.parse(text)
- keys = {}
- repr.value.each_key { |key| keys[key.value] = key }
- %w(created updated).each { |name| keys[name] ||= yaml_repr(name) }
- update_time = Time.now
- update_time_repr = yaml_repr(update_time)
- previous_mtime ||= update_time
- previous_mtime_repr = yaml_repr(previous_mtime)
- repr.add(keys['created'], previous_mtime_repr) unless repr['created']
- repr.add(keys['updated'], update_time_repr)
- entry_filename.parent.mkpath()
- entry_filename.open('w') { |stream| stream << repr.emit }
self
end
-
- def each_entry
- Pathname.glob(@entry_dir + '**/*.yaml') do |entry_filename|
- path = entry_filename.relative_path_from(@entry_dir).to_s
- path.sub!(/\.yaml$/, '')
- entry = entry_filename.open('r') do |stream|
- YAML.load(stream).value
- end
- entry['updated'] ||= entry_filename.mtime
- entry['created'] ||= entry['updated']
- yield path, entry
- end
- self
- end
-end
-
-end
-
-module Listing
-
-class RecursiveIndex
- include Source
-
- def self.new(source, options)
- listing = super(source, options)
- if options.has_key? :limit
- listing = Limit.new(listing, :limit => options[:limit])
- end
- if options.has_key? :page_size
- listing = Paginate.new(listing, :page_size => options[:page_size])
- end
- listing
- end
-
- def initialize(source, options)
- @source = source
- end
-
- def each_entry
- indexes = Hash.new { |h,k| h[k] = {'items' => []} }
- @source.each_entry do |path, entry|
- components = path.split("/")
- until components.empty?
- components.pop
- index_path = (components + ["index"]).join("/")
- index = indexes[index_path]
- index['items'] << {'path' => path, 'entry' => entry}
- if entry['modified'] and
- (not index['modified'] or entry['modified'] > index['modified'])
- index['modified'] = entry['modified']
- end
- end
- end
- indexes.each do |path, entry|
- yield path, entry
- end
- self
- end
-end
-
-class Paginate
- include Source
-
- def initialize(source, options)
- @source = source
- @page_size = options[:page_size]
- end
-
- def each_entry
- @source.each_entry do |index_path, index_entry|
- items = index_entry['items'] || []
- if items.empty?
- index_entry = index_entry.dup
- index_entry['pages'] = [index_entry]
- index_entry['page_index'] = 0
- yield index_path, index_entry
- else
- pages = []
- n_pages = (items.size + @page_size - 1) / @page_size
- for num in 0...n_pages
- page_items = items[@page_size * num, @page_size]
- entry = index_entry.dup
- entry['items'] = page_items
- entry['prev_page'] = "#{num}"
- entry['next_page'] = "#{num+2}"
- entry['pages'] = pages
- entry['page_index'] = num
- pages << {'path' => "#{index_path}/#{num+1}", 'entry' => entry}
- end
- pages[0]['path'] = index_path
- pages[0]['entry'].delete('prev_page')
- if pages.size > 1
- index_name = index_path.split('/').last
- pages[0]['entry']['next_page'] = "#{index_name}/2"
- pages[1]['entry']['prev_page'] = "../#{index_name}"
- end
- pages[-1]['entry'].delete('next_page')
- pages.each do |page|
- yield page['path'], page['entry']
- end
- end
- end
- self
- end
-end
-
-class Limit
- include Source
-
- def initialize(source, options)
- @source = source
- @limit = options[:limit]
- end
-
- def each_entry
- @source.each_entry do |path, entry|
- if entry['items']
- trimmed_entry = entry.dup
- trimmed_entry['items'] = entry['items'][0...@limit]
- else
- trimmed_entry = entry
- end
- yield path, trimmed_entry
- end
- self
- end
-end
-
-end
-
-module Output
-
-class LiquidTemplate
- include Source
-
- module TextFilters
- def textilize(input)
- RedCloth.new(input).to_html
- end
-
- def escape_url(input)
- CGI.escape(input)
- end
-
- def escape_xml(input)
- CGI.escapeHTML(input)
- end
-
- def path_to_url(input, base_url)
- "#{base_url}#{input}"
- end
-
- def handleize(input)
- "id_#{input.to_s.gsub(/[^A-Za-z0-9]/, '_')}"
- end
-
- def xsd_datetime(input)
- input = Time.parse(input) unless Time === input
- input.xmlschema
- end
- end
-
- def initialize(source, options)
- @source = source
- @options = {}
- for key, value in options
- @options[key.to_s] = value
- end
- template_dir = Hx.get_pathname(options, :template_dir)
- # global, so all LiquidTemplate instances kind of have to agree on the
- # same template directory for things to work right
- Liquid::Template.file_system = Liquid::LocalFileSystem.new(template_dir)
- Liquid::Template.register_filter(TextFilters)
- template_file = template_dir + options[:template]
- @template = template_file.open('r') { |s| Liquid::Template.parse(s.read) }
- @extension = options[:extension]
- end
-
- def each_entry
- @source.each_entry do |path, entry|
- unless @extension.nil?
- output_path = "#{path}.#{@extension}"
- else
- output_path = path
- end
- output_entry = entry.dup
- output_entry['content'] = @template.render(
- 'now' => Time.now,
- 'options' => @options,
- 'path' => path,
- 'entry' => entry
- )
- yield output_path, output_entry
- end
- self
- end
-end
-
end
end