broadway 0.0.3.5 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. data/README.markdown +155 -0
  2. data/lib/broadway.rb +64 -4
  3. data/lib/broadway/{core_ext.rb → ext.rb} +40 -6
  4. data/lib/broadway/filters/erb.rb +10 -0
  5. data/lib/broadway/filters/haml.rb +10 -0
  6. data/lib/broadway/filters/liquid.rb +14 -0
  7. data/lib/broadway/filters/markdown.rb +10 -0
  8. data/lib/broadway/filters/textile.rb +11 -0
  9. data/lib/broadway/migrators/blogger.rb +7 -0
  10. data/lib/broadway/migrators/wordpress.rb +7 -0
  11. data/lib/broadway/mixins/assetable.rb +48 -0
  12. data/lib/broadway/mixins/configurable.rb +32 -0
  13. data/lib/broadway/mixins/convertible.rb +25 -0
  14. data/lib/broadway/mixins/hierarchical.rb +61 -0
  15. data/lib/broadway/mixins/layoutable.rb +18 -0
  16. data/lib/broadway/mixins/pageable.rb +5 -0
  17. data/lib/broadway/mixins/processable.rb +44 -0
  18. data/lib/broadway/mixins/publishable.rb +36 -0
  19. data/lib/broadway/mixins/readable.rb +73 -0
  20. data/lib/broadway/mixins/resourceful.rb +27 -0
  21. data/lib/broadway/mixins/sluggable.rb +36 -0
  22. data/lib/broadway/mixins/sortable.rb +24 -0
  23. data/lib/broadway/mixins/taggable.rb +46 -0
  24. data/lib/broadway/mixins/themeable.rb +39 -0
  25. data/lib/broadway/processors/link.rb +45 -0
  26. data/lib/broadway/processors/post.rb +117 -0
  27. data/lib/broadway/processors/site.rb +77 -0
  28. data/lib/broadway/processors/tree.rb +121 -0
  29. data/lib/broadway/resources/asset.rb +28 -0
  30. data/lib/broadway/resources/configuration.rb +114 -0
  31. data/lib/broadway/resources/file.rb +88 -0
  32. data/lib/broadway/resources/layout.rb +28 -0
  33. data/lib/broadway/resources/link.rb +16 -0
  34. data/lib/broadway/resources/post.rb +63 -0
  35. data/lib/broadway/resources/site.rb +164 -0
  36. data/lib/broadway/resources/slug.rb +69 -0
  37. data/lib/broadway/sinatra/app.rb +21 -0
  38. data/lib/broadway/sinatra/helpers/collection_helper.rb +2 -1
  39. data/lib/broadway/sinatra/helpers/partial_helper.rb +5 -5
  40. data/lib/broadway/sinatra/helpers/text_helper.rb +5 -11
  41. data/lib/broadway/sinatra/processor.rb +84 -0
  42. data/lib/broadway/tasks.rb +1 -0
  43. data/lib/broadway/tasks/default.rake +85 -0
  44. metadata +46 -41
  45. data/README.textile +0 -306
  46. data/Rakefile +0 -85
  47. data/lib/broadway/api.rb +0 -51
  48. data/lib/broadway/asset.rb +0 -17
  49. data/lib/broadway/base.rb +0 -120
  50. data/lib/broadway/convertible.rb +0 -91
  51. data/lib/broadway/page.rb +0 -71
  52. data/lib/broadway/post.rb +0 -112
  53. data/lib/broadway/rails.rb +0 -3
  54. data/lib/broadway/resource.rb +0 -128
  55. data/lib/broadway/runner.rb +0 -62
  56. data/lib/broadway/sinatra.rb +0 -5
  57. data/lib/broadway/sinatra/base.rb +0 -90
  58. data/lib/broadway/sinatra/helpers.rb +0 -7
  59. data/lib/broadway/site.rb +0 -421
  60. data/lib/broadway/static_file.rb +0 -32
@@ -1,3 +0,0 @@
1
- module Broadway::Rails
2
-
3
- end
@@ -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
@@ -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
@@ -1,5 +0,0 @@
1
- module Broadway::Sinatra
2
-
3
- end
4
-
5
- require_local "sinatra/*"
@@ -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
@@ -1,7 +0,0 @@
1
- require_local "sinatra/helpers/*"
2
-
3
- helpers do
4
- files("sinatra/helpers/*") do |file|
5
- include "Broadway::Helpers::#{File.basename(file).split(".").first.camelize}".constantize
6
- end
7
- end
@@ -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