broadway 0.0.3.5 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.markdown +155 -0
- data/lib/broadway.rb +64 -4
- data/lib/broadway/{core_ext.rb → ext.rb} +40 -6
- data/lib/broadway/filters/erb.rb +10 -0
- data/lib/broadway/filters/haml.rb +10 -0
- data/lib/broadway/filters/liquid.rb +14 -0
- data/lib/broadway/filters/markdown.rb +10 -0
- data/lib/broadway/filters/textile.rb +11 -0
- data/lib/broadway/migrators/blogger.rb +7 -0
- data/lib/broadway/migrators/wordpress.rb +7 -0
- data/lib/broadway/mixins/assetable.rb +48 -0
- data/lib/broadway/mixins/configurable.rb +32 -0
- data/lib/broadway/mixins/convertible.rb +25 -0
- data/lib/broadway/mixins/hierarchical.rb +61 -0
- data/lib/broadway/mixins/layoutable.rb +18 -0
- data/lib/broadway/mixins/pageable.rb +5 -0
- data/lib/broadway/mixins/processable.rb +44 -0
- data/lib/broadway/mixins/publishable.rb +36 -0
- data/lib/broadway/mixins/readable.rb +73 -0
- data/lib/broadway/mixins/resourceful.rb +27 -0
- data/lib/broadway/mixins/sluggable.rb +36 -0
- data/lib/broadway/mixins/sortable.rb +24 -0
- data/lib/broadway/mixins/taggable.rb +46 -0
- data/lib/broadway/mixins/themeable.rb +39 -0
- data/lib/broadway/processors/link.rb +45 -0
- data/lib/broadway/processors/post.rb +117 -0
- data/lib/broadway/processors/site.rb +77 -0
- data/lib/broadway/processors/tree.rb +121 -0
- data/lib/broadway/resources/asset.rb +28 -0
- data/lib/broadway/resources/configuration.rb +114 -0
- data/lib/broadway/resources/file.rb +88 -0
- data/lib/broadway/resources/layout.rb +28 -0
- data/lib/broadway/resources/link.rb +16 -0
- data/lib/broadway/resources/post.rb +63 -0
- data/lib/broadway/resources/site.rb +164 -0
- data/lib/broadway/resources/slug.rb +69 -0
- data/lib/broadway/sinatra/app.rb +21 -0
- data/lib/broadway/sinatra/helpers/collection_helper.rb +2 -1
- data/lib/broadway/sinatra/helpers/partial_helper.rb +5 -5
- data/lib/broadway/sinatra/helpers/text_helper.rb +5 -11
- data/lib/broadway/sinatra/processor.rb +84 -0
- data/lib/broadway/tasks.rb +1 -0
- data/lib/broadway/tasks/default.rake +85 -0
- metadata +46 -41
- data/README.textile +0 -306
- data/Rakefile +0 -85
- data/lib/broadway/api.rb +0 -51
- data/lib/broadway/asset.rb +0 -17
- data/lib/broadway/base.rb +0 -120
- data/lib/broadway/convertible.rb +0 -91
- data/lib/broadway/page.rb +0 -71
- data/lib/broadway/post.rb +0 -112
- data/lib/broadway/rails.rb +0 -3
- data/lib/broadway/resource.rb +0 -128
- data/lib/broadway/runner.rb +0 -62
- data/lib/broadway/sinatra.rb +0 -5
- data/lib/broadway/sinatra/base.rb +0 -90
- data/lib/broadway/sinatra/helpers.rb +0 -7
- data/lib/broadway/site.rb +0 -421
- data/lib/broadway/static_file.rb +0 -32
data/lib/broadway/rails.rb
DELETED
data/lib/broadway/resource.rb
DELETED
@@ -1,128 +0,0 @@
|
|
1
|
-
module Broadway::Resource
|
2
|
-
|
3
|
-
def self.included(base)
|
4
|
-
base.class_eval do
|
5
|
-
include InstanceMethods
|
6
|
-
end
|
7
|
-
end
|
8
|
-
|
9
|
-
module InstanceMethods
|
10
|
-
attr_accessor :site, :position # order
|
11
|
-
# where the file is
|
12
|
-
attr_accessor :path, :dir, :basename, :ext, :title
|
13
|
-
attr_accessor :data, :content, :output
|
14
|
-
attr_accessor :date, :slug, :published, :categories, :asset
|
15
|
-
|
16
|
-
# Extract information from the post filename
|
17
|
-
# +name+ is the String filename of the post file
|
18
|
-
#
|
19
|
-
# Returns nothing
|
20
|
-
def process(options = {})
|
21
|
-
return if options.has_key?(:process) and options[:process] == false
|
22
|
-
|
23
|
-
self.read_yaml(path)
|
24
|
-
|
25
|
-
self.title = data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' ')
|
26
|
-
|
27
|
-
# If we've added a date and time to the yaml, use that instead of the filename date
|
28
|
-
# Means we'll sort correctly.
|
29
|
-
self.date = Time.parse(self.data["date"].to_s) if self.data.has_key?('date')
|
30
|
-
|
31
|
-
if self.data.has_key?('published') && self.data['published'] == false
|
32
|
-
self.published = false
|
33
|
-
else
|
34
|
-
self.published = true
|
35
|
-
end
|
36
|
-
|
37
|
-
if self.data.has_key?("asset")
|
38
|
-
data["asset"]["title"] ||= self.title
|
39
|
-
self.asset = ::Broadway::Asset.new(data["asset"])
|
40
|
-
end
|
41
|
-
|
42
|
-
self.tags = self.data["tags"]
|
43
|
-
self.tags ||= []
|
44
|
-
end
|
45
|
-
|
46
|
-
def children
|
47
|
-
key = self.categories.last
|
48
|
-
length = self.url.split("/").length
|
49
|
-
@children = site.find_pages_by_category(key) + site.find_posts_by_category(key)
|
50
|
-
@children.delete_if do |child|
|
51
|
-
(child.url == self.url) ||
|
52
|
-
(child.url.split("/").length <= length) ||
|
53
|
-
(child.is_a?(Broadway::Post) && child.categories != self.categories) ||
|
54
|
-
(child.is_a?(Broadway::Page) && child.categories[0..-2] != self.categories)
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
# The UID for this post (useful in feeds)
|
59
|
-
# e.g. /2008/11/05/my-awesome-post
|
60
|
-
#
|
61
|
-
# Returns <String>
|
62
|
-
def id
|
63
|
-
File.join(self.dir, self.slug)
|
64
|
-
end
|
65
|
-
|
66
|
-
def menu_title
|
67
|
-
self.data['menu_title'] || self.title
|
68
|
-
end
|
69
|
-
|
70
|
-
def tooltip
|
71
|
-
self.data["tooltip"]
|
72
|
-
end
|
73
|
-
|
74
|
-
def permalink
|
75
|
-
self.data['permalink']
|
76
|
-
end
|
77
|
-
|
78
|
-
def excerpt
|
79
|
-
self.data['excerpt']
|
80
|
-
end
|
81
|
-
|
82
|
-
def show_children?
|
83
|
-
return true if self.data.nil?
|
84
|
-
if self.data.has_key?("show_children")
|
85
|
-
return self.data["show_children"] == "true" || self.data["show_children"] == true
|
86
|
-
end
|
87
|
-
return true
|
88
|
-
end
|
89
|
-
|
90
|
-
def parent
|
91
|
-
url ? site.find_page_by_url(url[1..-1].split("/").first) : nil
|
92
|
-
end
|
93
|
-
|
94
|
-
def layout
|
95
|
-
self.data and self.data.has_key?("layout") ? self.data["layout"] : self.categories.first
|
96
|
-
end
|
97
|
-
|
98
|
-
def tags=(value)
|
99
|
-
@tags = case value
|
100
|
-
when Hash
|
101
|
-
value = value.each { |k, v| value[k] = tag_array(v) }
|
102
|
-
else
|
103
|
-
{"tags" => tag_array(value)}
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
# tags can be stored under multiple contexts
|
108
|
-
# default is "tags" (use "categories" or whatever else you want)
|
109
|
-
def tags(context = "tags")
|
110
|
-
@tags[context.to_s]
|
111
|
-
end
|
112
|
-
|
113
|
-
private
|
114
|
-
|
115
|
-
# helper method to convert tags to arrays
|
116
|
-
def tag_array(value)
|
117
|
-
result = case value
|
118
|
-
when String
|
119
|
-
value.split(",\s+")
|
120
|
-
when Array
|
121
|
-
value
|
122
|
-
end
|
123
|
-
result
|
124
|
-
end
|
125
|
-
|
126
|
-
end
|
127
|
-
|
128
|
-
end
|
data/lib/broadway/runner.rb
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
module Broadway
|
2
|
-
|
3
|
-
class Runner
|
4
|
-
|
5
|
-
attr_accessor :site, :content, :url
|
6
|
-
|
7
|
-
def initialize(site)
|
8
|
-
self.site = site
|
9
|
-
self.url = URI.parse(site.config[:url])
|
10
|
-
self.content = site.pages + site.posts
|
11
|
-
end
|
12
|
-
|
13
|
-
def run(&block)
|
14
|
-
versions = site.config[:theme_versions]
|
15
|
-
puts "Connecting to... #{url.scheme}://#{url.host}:#{url.port}"
|
16
|
-
item_url = nil
|
17
|
-
versions.each do |version|
|
18
|
-
prefix = version == "main" ? "" : version
|
19
|
-
begin
|
20
|
-
Net::HTTP.start(url.host, url.port) do |http|
|
21
|
-
self.content.each do |item|
|
22
|
-
if block_given?
|
23
|
-
item_url = yield(item)
|
24
|
-
else
|
25
|
-
if prefix == "touch"
|
26
|
-
item_url = "/touch#{item.url.gsub(".", "")}?layout=false"
|
27
|
-
else
|
28
|
-
item_url = ("/" + File.join(prefix, item.url.gsub(".", ""))).squeeze("/")
|
29
|
-
end
|
30
|
-
end
|
31
|
-
puts "Connecting to... #{url.scheme}://#{url.host}:#{url.port}#{item_url}"
|
32
|
-
item_dir = File.expand_path(File.join(site.config[:destination], prefix, item.url).squeeze("/"))
|
33
|
-
response = http.get(item_url)
|
34
|
-
# if the file doesn't exist or is not rendered correctly
|
35
|
-
next if error?(response, item_url, false)
|
36
|
-
FileUtils.mkdir_p(item_dir)
|
37
|
-
name = File.join(item_dir, "index.html").squeeze("/")
|
38
|
-
text = ""
|
39
|
-
text << response.body
|
40
|
-
puts "Writing... #{name}"
|
41
|
-
File.open(name, "w") {|f| f.write(text) }
|
42
|
-
end
|
43
|
-
end
|
44
|
-
rescue Exception => e
|
45
|
-
puts e.inspect
|
46
|
-
raise "Make sure you've started the server, run: 'ruby app.rb'"
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
def error?(response, path, whiny = true)
|
52
|
-
if !response.is_a?(Net::HTTPSuccess)
|
53
|
-
raise message if whiny
|
54
|
-
puts "Error at '#{path}': #{response.to_s}"
|
55
|
-
return true
|
56
|
-
end
|
57
|
-
false
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
data/lib/broadway/sinatra.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
libdir = File.dirname(__FILE__)
|
2
|
-
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
|
-
|
4
|
-
require "sinatra"
|
5
|
-
require "sinatra/base"
|
6
|
-
require "sinatra_more"
|
7
|
-
|
8
|
-
@site = site = Broadway.build
|
9
|
-
|
10
|
-
Sinatra::Application.instance_eval do
|
11
|
-
cattr_accessor :site
|
12
|
-
self.site = site
|
13
|
-
end
|
14
|
-
|
15
|
-
def site
|
16
|
-
@site
|
17
|
-
end
|
18
|
-
|
19
|
-
def default_locals(locals = {})
|
20
|
-
{
|
21
|
-
:environment => Sinatra::Application.environment,
|
22
|
-
:theme_version => "main"
|
23
|
-
}.merge(locals)
|
24
|
-
end
|
25
|
-
|
26
|
-
def default_options(options = {})
|
27
|
-
if params.has_key?("layout") and params["layout"] == false
|
28
|
-
options[:layout] = false
|
29
|
-
else
|
30
|
-
options[:layout] = !request.xhr?
|
31
|
-
end
|
32
|
-
options
|
33
|
-
end
|
34
|
-
|
35
|
-
def theme_path(version = "main")
|
36
|
-
File.join(site.config[:theme_path], site.config[:theme], version)
|
37
|
-
end
|
38
|
-
|
39
|
-
# "main" is the default, so remove it
|
40
|
-
def theme_versions
|
41
|
-
site.config[:theme_versions]
|
42
|
-
end
|
43
|
-
|
44
|
-
def pages
|
45
|
-
Dir.entries("public")
|
46
|
-
end
|
47
|
-
|
48
|
-
Sinatra::Application.class_eval do
|
49
|
-
register SinatraMore::MarkupPlugin
|
50
|
-
register SinatraMore::RoutingPlugin
|
51
|
-
end
|
52
|
-
|
53
|
-
def page_path(page, version)
|
54
|
-
content_path(page, version, "index")
|
55
|
-
end
|
56
|
-
|
57
|
-
def post_path(post, version)
|
58
|
-
content_path(post, version, "show")
|
59
|
-
end
|
60
|
-
|
61
|
-
def c(path)
|
62
|
-
result = site.config
|
63
|
-
path.to_s.split(".").each { |node| result = result[node.to_sym] if result }
|
64
|
-
result.nil? ? [] : result
|
65
|
-
end
|
66
|
-
|
67
|
-
def content_path(content, version, action)
|
68
|
-
# first check if it's in the posts directory (where textile files are)
|
69
|
-
path = File.join(content.dir, action).squeeze("/")
|
70
|
-
exists = false
|
71
|
-
extensions = %w(html haml erb html.haml html.erb)
|
72
|
-
target_path = File.join(site.config[:source], path)
|
73
|
-
extensions.each do |ext|
|
74
|
-
exists = true if File.exists?("#{target_path}.#{ext}")
|
75
|
-
end
|
76
|
-
return path if exists
|
77
|
-
# then check if it's in the theme directory
|
78
|
-
checked = [target_path]
|
79
|
-
path = target_path = File.join(theme_path(version), content.layout, action).squeeze("/")
|
80
|
-
exists = false
|
81
|
-
extensions.each do |ext|
|
82
|
-
exists = true if File.exists?(File.join(site.config[:source], "#{target_path}.#{ext}"))
|
83
|
-
end
|
84
|
-
return path if exists
|
85
|
-
return File.join(theme_path(version), "index") if content.url == "/"
|
86
|
-
checked << target_path
|
87
|
-
|
88
|
-
puts "Couldn't find any paths for #{content.class.to_s.downcase.split(":").last} '#{content.url}': #{checked.inspect}"
|
89
|
-
return nil
|
90
|
-
end
|
data/lib/broadway/site.rb
DELETED
@@ -1,421 +0,0 @@
|
|
1
|
-
module Broadway
|
2
|
-
|
3
|
-
class Site
|
4
|
-
attr_accessor :config, :layouts, :posts, :pages, :static_files, :categories, :exclude,
|
5
|
-
:source, :dest, :lsi, :pygments, :permalink_style, :tags, :tree
|
6
|
-
|
7
|
-
def self.generate
|
8
|
-
|
9
|
-
end
|
10
|
-
|
11
|
-
# Initialize the site
|
12
|
-
# +config+ is a Hash containing site configurations details
|
13
|
-
#
|
14
|
-
# Returns <Site>
|
15
|
-
def initialize(config)
|
16
|
-
merge_extra_config!(config)
|
17
|
-
|
18
|
-
config.recursive_symbolize_keys!
|
19
|
-
|
20
|
-
self.config = config.clone
|
21
|
-
|
22
|
-
self.source = config[:source]
|
23
|
-
self.dest = config[:destination]
|
24
|
-
self.lsi = config[:lsi]
|
25
|
-
self.pygments = config[:pygments]
|
26
|
-
self.permalink_style = config[:permalink].to_sym
|
27
|
-
self.exclude = config[:exclude] || []
|
28
|
-
self.tree = []
|
29
|
-
|
30
|
-
self.reset
|
31
|
-
self.setup
|
32
|
-
end
|
33
|
-
|
34
|
-
def merge_extra_config!(config)
|
35
|
-
locale = File.join(config[:locales], "#{config[:language]}.yml")
|
36
|
-
|
37
|
-
config.merge!(YAML.load_file(locale)) if File.exists?(locale)
|
38
|
-
|
39
|
-
|
40
|
-
config_dir = File.join(config[:source], "config")
|
41
|
-
return unless File.exists?(config_dir)
|
42
|
-
Dir.glob("#{config_dir}/**/*").each do |file|
|
43
|
-
next if File.directory?(file)
|
44
|
-
path = file.gsub(/config\//, "").split(".")[0..-2].join("")
|
45
|
-
ext = File.extname(file)
|
46
|
-
if ext =~ /yml/
|
47
|
-
data = YAML.load_file(file)
|
48
|
-
elsif ext =~ /xml/
|
49
|
-
data = parse_children Nokogiri::XML(IO.read(file)).children[0]
|
50
|
-
else
|
51
|
-
data = IO.read(file)
|
52
|
-
end
|
53
|
-
next unless data
|
54
|
-
name = path.split("/").first
|
55
|
-
if path =~ /\//
|
56
|
-
config[name] ||= {}
|
57
|
-
target = config[name]
|
58
|
-
name = path.split("/").last
|
59
|
-
else
|
60
|
-
target = config
|
61
|
-
end
|
62
|
-
if target.has_key?(name)
|
63
|
-
target[name].merge!(data)
|
64
|
-
else
|
65
|
-
target[name] = data
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
70
|
-
# first
|
71
|
-
def reset
|
72
|
-
self.layouts = {}
|
73
|
-
self.posts = []
|
74
|
-
self.pages = []
|
75
|
-
self.static_files = []
|
76
|
-
self.categories = Hash.new { |hash, key| hash[key] = [] }
|
77
|
-
self.tags = Hash.new { |hash, key| hash[key] = [] }
|
78
|
-
end
|
79
|
-
|
80
|
-
# second
|
81
|
-
# this just sets configuration variables on the dependencies, if necessary
|
82
|
-
def setup
|
83
|
-
# Check to see if LSI is enabled.
|
84
|
-
require 'classifier' if self.lsi
|
85
|
-
|
86
|
-
# Set the Markdown interpreter (and Maruku self.config, if necessary)
|
87
|
-
case self.config[:markdown]
|
88
|
-
when 'rdiscount'
|
89
|
-
begin
|
90
|
-
require 'rdiscount'
|
91
|
-
|
92
|
-
def markdown(content)
|
93
|
-
RDiscount.new(content).to_html
|
94
|
-
end
|
95
|
-
|
96
|
-
rescue LoadError
|
97
|
-
puts 'You must have the rdiscount gem installed first'
|
98
|
-
end
|
99
|
-
when 'maruku'
|
100
|
-
begin
|
101
|
-
require 'maruku'
|
102
|
-
|
103
|
-
def markdown(content)
|
104
|
-
Maruku.new(content).to_html
|
105
|
-
end
|
106
|
-
|
107
|
-
if self.config[:maruku][:use_divs]
|
108
|
-
require 'maruku/ext/div'
|
109
|
-
puts 'Maruku: Using extended syntax for div elements.'
|
110
|
-
end
|
111
|
-
|
112
|
-
if self.config[:maruku][:use_tex]
|
113
|
-
require 'maruku/ext/math'
|
114
|
-
puts "Maruku: Using LaTeX extension. Images in `#{self.config[:maruku][:png_dir]}`."
|
115
|
-
|
116
|
-
# Switch off MathML output
|
117
|
-
MaRuKu::Globals[:html_math_output_mathml] = false
|
118
|
-
MaRuKu::Globals[:html_math_engine] = 'none'
|
119
|
-
|
120
|
-
# Turn on math to PNG support with blahtex
|
121
|
-
# Resulting PNGs stored in `images/latex`
|
122
|
-
MaRuKu::Globals[:html_math_output_png] = true
|
123
|
-
MaRuKu::Globals[:html_png_engine] = self.config[:maruku][:png_engine]
|
124
|
-
MaRuKu::Globals[:html_png_dir] = self.config[:maruku][:png_dir]
|
125
|
-
MaRuKu::Globals[:html_png_url] = self.config[:maruku][:png_url]
|
126
|
-
end
|
127
|
-
rescue LoadError
|
128
|
-
puts "The maruku gem is required for markdown support!"
|
129
|
-
end
|
130
|
-
else
|
131
|
-
raise "Invalid Markdown processor: '#{self.config[:markdown]}' -- did you mean 'maruku' or 'rdiscount'?"
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def textile(content)
|
136
|
-
RedCloth.new(content).to_html
|
137
|
-
end
|
138
|
-
|
139
|
-
# third
|
140
|
-
# Do the actual work of processing the site and generating the
|
141
|
-
# real deal. Now has 4 phases; reset, read, render, write. This allows
|
142
|
-
# rendering to have full site payload available.
|
143
|
-
#
|
144
|
-
# Returns nothing
|
145
|
-
def process
|
146
|
-
build
|
147
|
-
generate
|
148
|
-
end
|
149
|
-
|
150
|
-
def build
|
151
|
-
puts "Building site: #{config[:source]} -> #{config[:destination]}"
|
152
|
-
self.reset
|
153
|
-
self.read
|
154
|
-
puts "Successfully built site, nothing was written."
|
155
|
-
end
|
156
|
-
|
157
|
-
def generate
|
158
|
-
puts "Generating site: #{config[:source]} -> #{config[:destination]}"
|
159
|
-
self.render
|
160
|
-
self.write
|
161
|
-
puts "Successfully generated site: #{config[:source]} -> #{config[:destination]}"
|
162
|
-
end
|
163
|
-
|
164
|
-
def read
|
165
|
-
self.read_layouts # existing implementation did this at top level only so preserved that
|
166
|
-
self.read_directories
|
167
|
-
end
|
168
|
-
|
169
|
-
# Read all the files in <source>/<dir>/_layouts and create a new Layout
|
170
|
-
# object with each one.
|
171
|
-
#
|
172
|
-
# Returns nothing
|
173
|
-
def read_layouts(dir = '')
|
174
|
-
base = File.join(self.source, dir, config[:layouts])
|
175
|
-
return unless File.exists?(base)
|
176
|
-
entries = []
|
177
|
-
Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
|
178
|
-
entries.each do |f|
|
179
|
-
name = f.split(".")[0..-2].join(".")
|
180
|
-
self.layouts[name] = Layout.new(self, base, f)
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
# Reads the directories and finds posts, pages and static files that will
|
185
|
-
# become part of the valid site according to the rules in +filter_entries+.
|
186
|
-
# The +dir+ String is a relative path used to call this method
|
187
|
-
# recursively as it descends through directories
|
188
|
-
#
|
189
|
-
# Returns nothing
|
190
|
-
def read_directories(dir = '')
|
191
|
-
base = File.join(self.source, dir).gsub(/\/$/, "")
|
192
|
-
lists = []
|
193
|
-
|
194
|
-
# RULES:
|
195
|
-
# Pages are only index.textile files, or static .html files (TODO)
|
196
|
-
# Posts are leaf nodes (name.textile files)
|
197
|
-
# Categories are the directory name split plus anything extra defined
|
198
|
-
# Pages and Posts can also be obtained via index.xml files
|
199
|
-
Dir.glob("#{base}/**/*").each do |path|
|
200
|
-
# removes junk, leaves us with .xml, .textile, .html
|
201
|
-
next if filtered?(path)
|
202
|
-
name = File.basename(path).split(".").first
|
203
|
-
ext = File.extname(path).gsub(".", "")
|
204
|
-
if name == "index"
|
205
|
-
if ext != "xml"
|
206
|
-
new_page(path)
|
207
|
-
end
|
208
|
-
elsif %w(textile markdown).include?(ext)
|
209
|
-
new_post(path)
|
210
|
-
else
|
211
|
-
self.static_files << StaticFile.new(:site => self, :path => path)
|
212
|
-
end
|
213
|
-
end
|
214
|
-
|
215
|
-
# lists are xml files we've collect, but we want to make sure we've
|
216
|
-
# created all the posts we need to beforehand
|
217
|
-
# this also sorts everything
|
218
|
-
list = File.join(config[:source], "index.xml")
|
219
|
-
lists << list if File.exists?(list)
|
220
|
-
lists.each do |path|
|
221
|
-
new_tree(path)
|
222
|
-
end
|
223
|
-
|
224
|
-
sort!
|
225
|
-
|
226
|
-
# finally, we have set all the initial variables on the
|
227
|
-
# pages and posts we need, now we can process them to find
|
228
|
-
# the content and generate the urls
|
229
|
-
self.pages.each do |page|
|
230
|
-
page.process
|
231
|
-
page.categories.each { |c| self.categories[c] << page }
|
232
|
-
page.tags.each { |c| self.tags[c] << page }
|
233
|
-
end
|
234
|
-
self.posts.each do |post|
|
235
|
-
post.process
|
236
|
-
post.categories.each { |c| self.categories[c] << post }
|
237
|
-
post.tags.each { |c| self.tags[c] << post }
|
238
|
-
end
|
239
|
-
end
|
240
|
-
|
241
|
-
def new_page(path, options = {})
|
242
|
-
return if path.nil? || path.empty?
|
243
|
-
page = Page.new(options.merge(:site => self, :path => path, :process => false))
|
244
|
-
self.pages << page
|
245
|
-
page
|
246
|
-
end
|
247
|
-
|
248
|
-
def new_post(path, options = {})
|
249
|
-
return if path.nil? || path.empty?
|
250
|
-
post = Post.new(options.merge(:site => self, :path => path, :process => false))
|
251
|
-
self.posts << post
|
252
|
-
post
|
253
|
-
end
|
254
|
-
|
255
|
-
def new_tree(path)
|
256
|
-
self.tree.concat parse_tree(Nokogiri::XML(IO.read(path)).root)
|
257
|
-
end
|
258
|
-
|
259
|
-
def parse_tree(parent)
|
260
|
-
result = []
|
261
|
-
return result if parent.nil? || parent.children.nil? || parent.children.empty?
|
262
|
-
parent.children.each_with_index do |child, index|
|
263
|
-
next unless child.elem?
|
264
|
-
content = child.children.empty? ? post_from_xml(child) : page_from_xml(child)
|
265
|
-
next unless content
|
266
|
-
if content.respond_to?(:children)
|
267
|
-
content.children.concat parse_tree(child)
|
268
|
-
end
|
269
|
-
content.position = index
|
270
|
-
result << content
|
271
|
-
end
|
272
|
-
result
|
273
|
-
end
|
274
|
-
|
275
|
-
# http://stackoverflow.com/questions/1769126/fastest-one-liner-way-to-get-xml-nodes-into-array-of-path-to-nodes-in-ruby
|
276
|
-
def node_list(elem, &proc)
|
277
|
-
return [] unless elem.class == Nokogiri::XML::Element
|
278
|
-
str = proc.call(elem)
|
279
|
-
[str] + elem.children.inject([]){|a,c| a+node_list(c,&proc)}.map{|e| "#{str}/#{e}"}
|
280
|
-
end
|
281
|
-
|
282
|
-
def post_from_xml(content)
|
283
|
-
# post
|
284
|
-
# 1. If a "src" is defined, then we should have already created the post
|
285
|
-
# 2. If no "src", then content is specified inline (or there is no content yet)
|
286
|
-
post = find_post_by_path(content["src"]) || find_post_by_url(content["url"])
|
287
|
-
path = (content["src"] || content["url"] || "").gsub(/^\//, "")
|
288
|
-
post ||= new_post(path)
|
289
|
-
return unless post
|
290
|
-
%w(title image excerpt menu_title tooltip show_children content layout).each do |key|
|
291
|
-
post.data[key] = content[key] if content.has_attribute?(key)
|
292
|
-
end
|
293
|
-
post
|
294
|
-
end
|
295
|
-
|
296
|
-
def page_from_xml(content)
|
297
|
-
# page
|
298
|
-
# 1. If a "src" is defined, then we should have already created the post
|
299
|
-
# 2. If no "src", then content is specified inline (or there is no content yet)
|
300
|
-
page = find_page_by_path(content["src"]) || find_page_by_url(content["url"])
|
301
|
-
path = (content["src"] || content["url"] || "").gsub(/^\//, "")
|
302
|
-
page ||= new_page(path)
|
303
|
-
return unless page
|
304
|
-
%w(title image excerpt menu_title tooltip show_children content layout).each do |key|
|
305
|
-
page.data[key] = content[key] if content.has_attribute?(key)
|
306
|
-
end
|
307
|
-
page
|
308
|
-
end
|
309
|
-
|
310
|
-
# sort all pages and posts
|
311
|
-
# determined by index.xml file (and later, for dates)
|
312
|
-
def sort!
|
313
|
-
sort_content(:page, self.pages)
|
314
|
-
sort_content(:post, self.posts)
|
315
|
-
end
|
316
|
-
|
317
|
-
def sort_content(type, contents)
|
318
|
-
|
319
|
-
method = type.to_s.pluralize
|
320
|
-
unnumbered = self.send(method).select { |content| content.position.nil? }
|
321
|
-
start_index = self.send(method).length - unnumbered.length
|
322
|
-
unnumbered.each_with_index { |content, index| content.position = start_index + index }
|
323
|
-
self.send(method).sort!
|
324
|
-
self.send(method)
|
325
|
-
end
|
326
|
-
|
327
|
-
# While in Jekyll this method renders the content explicitly,
|
328
|
-
# I'm using Sinatra, partly because I like having the flexibility
|
329
|
-
# of using their get/post methods, and partly because I don't have
|
330
|
-
# the time/desire to try to hack jekyll to use haml.
|
331
|
-
# I'd rather just start over
|
332
|
-
def render
|
333
|
-
Runner.new(self).run
|
334
|
-
end
|
335
|
-
|
336
|
-
# Write static files, pages and posts
|
337
|
-
#
|
338
|
-
# Returns nothing
|
339
|
-
def write
|
340
|
-
self.static_files.each do |sf|
|
341
|
-
sf.write(self.dest)
|
342
|
-
end
|
343
|
-
end
|
344
|
-
|
345
|
-
# Constructs a hash map of Posts indexed by the specified Post attribute
|
346
|
-
#
|
347
|
-
# Returns {post_attr => [<Post>]}
|
348
|
-
def post_attr_hash(post_attr)
|
349
|
-
# Build a hash map based on the specified post attribute ( post attr => array of posts )
|
350
|
-
# then sort each array in reverse order
|
351
|
-
hash = Hash.new { |hash, key| hash[key] = Array.new }
|
352
|
-
self.posts.each { |p| p.send(post_attr.to_sym).each { |t| hash[t] << p } }
|
353
|
-
hash.values.map { |sortme| sortme.sort! { |a, b| b <=> a} }
|
354
|
-
return hash
|
355
|
-
end
|
356
|
-
|
357
|
-
# The Hash payload containing site-wide data
|
358
|
-
#
|
359
|
-
# Returns {"site" => {"time" => <Time>,
|
360
|
-
# "posts" => [<Post>],
|
361
|
-
# "categories" => [<Post>]}
|
362
|
-
def site_payload
|
363
|
-
{"site" => self.config.merge({
|
364
|
-
"time" => Time.now,
|
365
|
-
"posts" => self.posts.sort { |a,b| b <=> a },
|
366
|
-
"categories" => post_attr_hash('categories'),
|
367
|
-
"tags" => post_attr_hash('tags')})}
|
368
|
-
end
|
369
|
-
|
370
|
-
# Filter out any files/directories that are hidden or backup files (start
|
371
|
-
# with "." or "#" or end with "~"), or contain site content (start with "_"),
|
372
|
-
# or are excluded in the site configuration, unless they are web server
|
373
|
-
# files such as '.htaccess'
|
374
|
-
def filtered?(path)
|
375
|
-
return true if File.directory?(path)
|
376
|
-
file = File.basename(path)
|
377
|
-
return true if ['.htaccess'].include?(file)
|
378
|
-
['.', '_', '#'].include?(file[0..0]) || file[-1..-1] == '~' || self.exclude.include?(file)
|
379
|
-
end
|
380
|
-
|
381
|
-
def find(type, method, value)
|
382
|
-
self.send(type.to_s.pluralize).select do |content|
|
383
|
-
if content.respond_to?(method)
|
384
|
-
content.send(method) == value
|
385
|
-
else
|
386
|
-
content.send(method.to_s.pluralize).include?(value)
|
387
|
-
end
|
388
|
-
end
|
389
|
-
end
|
390
|
-
|
391
|
-
def first(type, method, value)
|
392
|
-
find(type, method, value).first
|
393
|
-
end
|
394
|
-
|
395
|
-
%w(category url path tag title).each do |property|
|
396
|
-
%w(post page).each do |type|
|
397
|
-
define_method "find_#{type}_by_#{property}" do |value|
|
398
|
-
first(type.to_sym, property, value)
|
399
|
-
end
|
400
|
-
define_method "find_#{type.pluralize}_by_#{property}" do |value|
|
401
|
-
find(type.to_sym, property, value)
|
402
|
-
end
|
403
|
-
end
|
404
|
-
define_method "find_by_#{property}" do |value|
|
405
|
-
first(:page, property, value) || first(:post, property, value)
|
406
|
-
end
|
407
|
-
end
|
408
|
-
|
409
|
-
def page_count
|
410
|
-
self.pages ? self.pages.length : 0
|
411
|
-
end
|
412
|
-
|
413
|
-
def post_count
|
414
|
-
self.posts ? self.posts.length : 0
|
415
|
-
end
|
416
|
-
|
417
|
-
def inspect
|
418
|
-
"#<Broadway:Site @page_count=#{self.page_count.to_s} @post_count=#{self.post_count.to_s}>"
|
419
|
-
end
|
420
|
-
end
|
421
|
-
end
|