broadway 0.0.1

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.
@@ -0,0 +1,121 @@
1
+ def files(path, from = __FILE__, &block)
2
+ Dir.glob(File.join(File.dirname(from), path)) {|file| yield file}
3
+ end
4
+
5
+ def require_local(path, from = __FILE__)
6
+ files(path, from) {|file| require file}
7
+ end
8
+
9
+ require "rubygems"
10
+ require 'redcloth'
11
+ require 'nokogiri'
12
+ require 'active_support'
13
+ require 'action_mailer'
14
+ require 'action_view'
15
+ require 'action_view/base'
16
+
17
+ # core
18
+ require 'fileutils'
19
+ require 'time'
20
+ require 'yaml'
21
+ require 'nokogiri'
22
+ require 'cgi'
23
+ require 'sinatra'
24
+ require 'sinatra/base'
25
+ require 'sinatra_more'
26
+ require 'haml'
27
+
28
+ # stdlib
29
+
30
+ # 3rd party
31
+ require 'redcloth'
32
+
33
+ $:.push(File.dirname(__FILE__))
34
+ require 'core_ext'
35
+ require 'convertible'
36
+ require 'resource'
37
+ require 'site'
38
+ require 'asset'
39
+ require 'page'
40
+ require 'post'
41
+ require 'static_file'
42
+ require 'runner'
43
+ require "helpers"
44
+
45
+ module Broadway
46
+ VERSION = "0.0.1"
47
+ # Default options. Overriden by values in _config.yml or command-line opts.
48
+ # (Strings rather symbols used for compatability with YAML)
49
+ DEFAULTS = {
50
+ :auto => false,
51
+ :server => false,
52
+ :server_port => 4000,
53
+
54
+ :source => '.',
55
+ :destination => File.join('..', '_posts'),
56
+
57
+ :lsi => false,
58
+ :pygments => false,
59
+ :markdown => 'maruku',
60
+ :permalink => 'pretty',
61
+ :url_type => 'relative',
62
+ :url => 'http://localhost:4567',
63
+ :language => 'en-US',
64
+
65
+ :maruku => {
66
+ :use_tex => false,
67
+ :use_divs => false,
68
+ :png_engine => 'blahtex',
69
+ :png_dir => 'images/latex',
70
+ :png_url => '/images/latex'
71
+ },
72
+
73
+ :layouts => "_layouts",
74
+ :posts => "",#"_posts"
75
+ :posts_include => [".textile", ".markdown"]
76
+ }
77
+
78
+ # Generate a Broadway configuration Hash by merging the default options
79
+ # with anything in _config.yml, and adding the given options on top
80
+ # +override+ is a Hash of config directives
81
+ #
82
+ # Returns Hash
83
+ def self.configuration(override)
84
+ # _config.yml may override default source location, but until
85
+ # then, we need to know where to look for _config.yml
86
+ source = override[:source] || Broadway::DEFAULTS[:source]
87
+
88
+ # Get configuration from <source>/_config.yml
89
+ config_file = File.join(source, '_config.yml')
90
+ begin
91
+ config = YAML.load_file(config_file)
92
+ raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
93
+ $stdout.puts "Configuration from #{config_file}"
94
+ rescue => err
95
+ $stderr.puts "WARNING: Could not read configuration. Using defaults (and options)."
96
+ $stderr.puts "\t" + err.to_s
97
+ config = {}
98
+ end
99
+
100
+ # Merge DEFAULTS < _config.yml < override
101
+ Broadway::DEFAULTS.deep_merge(config).deep_merge(override)
102
+ end
103
+
104
+ def self.build(options = {})
105
+ self.process(:build, options)
106
+ end
107
+
108
+ def self.generate(options = {})
109
+ self.process(:process, options)
110
+ end
111
+
112
+ def self.process(method, options = {})
113
+ options = Broadway.configuration(options)
114
+
115
+ site = Broadway::Site.new(options)
116
+
117
+ site.send method
118
+
119
+ site
120
+ end
121
+ end
@@ -0,0 +1,89 @@
1
+ # Convertible provides methods for converting a pagelike item
2
+ # from a certain type of markup into actual content
3
+ #
4
+ # Requires
5
+ # self.site -> Jekyll::Site
6
+ # self.content=
7
+ # self.data=
8
+ # self.ext=
9
+ # self.output=
10
+ require 'liquid'
11
+ module Broadway
12
+ module Convertible
13
+ # Return the contents as a string
14
+ def to_s
15
+ self.content || ''
16
+ end
17
+
18
+ # Read the YAML frontmatter
19
+ # +base+ is the String path to the dir containing the file
20
+ # +name+ is the String filename of the file
21
+ # You can also get the content from xml, so this might not need to run
22
+ # Returns nothing
23
+ def read_yaml(path)
24
+ return if !File.exists?(path) || File.directory?(path)
25
+
26
+ self.content = IO.read(path)
27
+
28
+ if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
29
+ self.content = self.content[($1.size + $2.size)..-1]
30
+
31
+ self.data.merge!(YAML.load($1))
32
+ end
33
+ end
34
+
35
+ # Transform the contents based on the file extension.
36
+ #
37
+ # Returns nothing
38
+ def transform
39
+ case self.content_type
40
+ when 'textile'
41
+ self.ext = ".html"
42
+ self.content = self.site.textile(self.content)
43
+ when 'markdown'
44
+ self.ext = ".html"
45
+ self.content = self.site.markdown(self.content)
46
+ end
47
+ end
48
+
49
+ # Determine which formatting engine to use based on this convertible's
50
+ # extension
51
+ #
52
+ # Returns one of :textile, :markdown or :unknown
53
+ def content_type
54
+ case self.ext[1..-1]
55
+ when /textile/i
56
+ return 'textile'
57
+ when /markdown/i, /mkdn/i, /md/i, /mkd/i
58
+ return 'markdown'
59
+ end
60
+ return 'unknown'
61
+ end
62
+
63
+ # Add any necessary layouts to this convertible document
64
+ # +layouts+ is a Hash of {"name" => "layout"}
65
+ # +site_payload+ is the site payload hash
66
+ #
67
+ # Returns nothing
68
+ def do_layout(payload, layouts)
69
+ info = { :filters => [], :registers => { :site => self.site } }
70
+
71
+ # render and transform content (this becomes the final content of the object)
72
+ payload["content_type"] = self.content_type
73
+ self.content = Liquid::Template.parse(self.content).render(payload, info)
74
+ self.transform
75
+
76
+ # output keeps track of what will finally be written
77
+ self.output = self.content
78
+
79
+ # recursively render layouts
80
+ layout = layouts[self.data["layout"]]
81
+ while layout
82
+ payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
83
+ self.output = Liquid::Template.parse(layout.content).render(payload, info)
84
+
85
+ layout = layouts[layout.data["layout"]]
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,93 @@
1
+ class Hash
2
+ def recursive_symbolize_keys!
3
+ self.symbolize_keys!
4
+ self.values.each do |v|
5
+ if v.is_a? Hash
6
+ v.recursive_symbolize_keys!
7
+ elsif v.is_a? Array
8
+ v.recursive_symbolize_keys!
9
+ end
10
+ end
11
+ self
12
+ end
13
+ end
14
+
15
+ class Array
16
+ def recursive_symbolize_keys!
17
+ self.each do |item|
18
+ if item.is_a? Hash
19
+ item.recursive_symbolize_keys!
20
+ elsif item.is_a? Array
21
+ item.recursive_symbolize_keys!
22
+ end
23
+ end
24
+ end
25
+
26
+ # http://snippets.dzone.com/posts/show/3486
27
+ def chunk(pieces)
28
+ q, r = length.divmod(pieces)
29
+ (0..pieces).map { |i| i * q + [r, i].min }.enum_cons(2).map { |a, b| slice(a...b) }
30
+ end
31
+
32
+ def / len
33
+ a = []
34
+ each_with_index do |x,i|
35
+ a << [] if i % len == 0
36
+ a.last << x
37
+ end
38
+ a
39
+ end
40
+ end
41
+
42
+ class Hash
43
+ # Merges self with another hash, recursively.
44
+ #
45
+ # This code was lovingly stolen from some random gem:
46
+ # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
47
+ #
48
+ # Thanks to whoever made it.
49
+ def deep_merge(hash)
50
+ target = dup
51
+
52
+ hash.keys.each do |key|
53
+ if hash[key].is_a? Hash and self[key].is_a? Hash
54
+ target[key] = target[key].deep_merge(hash[key])
55
+ next
56
+ end
57
+
58
+ target[key] = hash[key]
59
+ end
60
+
61
+ target
62
+ end
63
+
64
+ # Read array from the supplied hash favouring the singular key
65
+ # and then the plural key, and handling any nil entries.
66
+ # +hash+ the hash to read from
67
+ # +singular_key+ the singular key
68
+ # +plural_key+ the singular key
69
+ #
70
+ # Returns an array
71
+ def pluralized_array(singular_key, plural_key)
72
+ hash = self
73
+ if hash.has_key?(singular_key)
74
+ array = [hash[singular_key]] if hash[singular_key]
75
+ elsif hash.has_key?(plural_key)
76
+ case hash[plural_key]
77
+ when String
78
+ array = hash[plural_key].split
79
+ when Array
80
+ array = hash[plural_key].compact
81
+ end
82
+ end
83
+ array || []
84
+ end
85
+ end
86
+
87
+ # Thanks, ActiveSupport!
88
+ class Date
89
+ # Converts datetime to an appropriate format for use in XML
90
+ def xmlschema
91
+ strftime("%Y-%m-%dT%H:%M:%S%Z")
92
+ end if RUBY_VERSION < '1.9'
93
+ end
@@ -0,0 +1,7 @@
1
+ require_local "helpers/*"
2
+
3
+ helpers do
4
+ files("helpers/*") do |file|
5
+ include "Broadway::Helpers::#{File.basename(file).split(".").first.camelize}".constantize
6
+ end
7
+ end
@@ -0,0 +1,152 @@
1
+ module Broadway
2
+ module Helpers
3
+ module CollectionHelper
4
+
5
+ # comment from http://openmonkey.com/articles/2009/02/a-cycle-helper-for-sinatra
6
+ # You can then call it like this:
7
+ # .user_badge{ :class => cycle('normal', 'normal', 'last') }
8
+ def cycle(*states)
9
+ states[@_cycle = ((@_cycle || -1) + 1) % states.size]
10
+ end
11
+
12
+ def grid_for(array, options = {}, &block)
13
+ return "" if array.empty?
14
+ columns = options[:columns] || 3
15
+ (array / columns).each do |row|
16
+ yield row
17
+ end
18
+ end
19
+
20
+ def row_for(array, options = {}, &block)
21
+ return "" if array.empty?
22
+ array.each_with_index do |node, i|
23
+ attributes = options.has_key?(:li_attributes) ? options[:li_attributes] : {}
24
+ attributes[:class] ||= ""
25
+ if i == 0 and options.has_key?(:first)
26
+ attributes[:class] << "#{options[:first]}"
27
+ elsif i == array.length - 1 and options.has_key?(:last)
28
+ attributes[:class] << "#{options[:last]}"
29
+ end
30
+ haml_tag :li, attributes do
31
+ if block_given?
32
+ yield node, i
33
+ else
34
+ haml_concat node.title
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ def menu_for(item, options = {}, &block)
41
+ if item.is_a?(Site)
42
+ menu_tag(item.tree, options, &block)
43
+ elsif item.is_a?(Page)
44
+ menu_tag(item.children, options, &block)
45
+ elsif item.is_a?(Post)
46
+ ""
47
+ else
48
+ menu_tag(item, options, &block)
49
+ end
50
+ end
51
+
52
+ # - menu_tag(c(:menu)) do |attributes, node|
53
+ # - attributes[:class] = "one two"
54
+ # - node[:title]
55
+ def menu_tag(array, options = {}, &block)
56
+ return "" if array.empty?
57
+ menu_attributes = nil
58
+ if options.has_key?(:menu_attributes)
59
+ menu_attributes = options[:menu_attributes]
60
+ options.delete(:menu_attributes)
61
+ end
62
+ haml_tag :ul, menu_attributes do
63
+ array.each_with_index do |node, i|
64
+ attributes = options.has_key?(:li_attributes) ? options[:li_attributes] : {}
65
+ attributes[:class] ||= ""
66
+ if i == 0 and options.has_key?(:first)
67
+ attributes[:class] << " #{options[:first]}"
68
+ elsif i == array.length - 1 and options.has_key?(:last)
69
+ attributes[:class] << " #{options[:last]}"
70
+ end
71
+ haml_tag :li, attributes do
72
+ if block_given?
73
+ yield node
74
+ else
75
+ haml_concat node.title
76
+ end
77
+ next if (options.has_key?(:show_children) and options[:show_children] == false)
78
+ next unless (node.respond_to?(:children) and node.show_children?)
79
+
80
+ menu_tag(node.children, options, &block)
81
+ end
82
+ end
83
+ end
84
+ end
85
+
86
+ def breadcrumb_for(item, options = {}, &block)
87
+ options[:from] ||= "/" + item.url.gsub(/^\//, "").split("/").first
88
+ options[:to] ||= item.url
89
+ breadcrumb(options[:from], options, &block)
90
+ end
91
+
92
+ def breadcrumb(current_url, options, &block)
93
+ item = site.find_by_url(current_url)
94
+ return "" unless item
95
+ haml_tag :a, options.merge(:href => current_url) do
96
+ if block_given?
97
+ yield item
98
+ else
99
+ haml_concat item.title
100
+ end
101
+ end
102
+ if options[:to] != current_url
103
+ haml_concat options[:delimiter] || " » "
104
+ next_node = options[:to].gsub(/#{current_url}\/?/, "").split("/").first
105
+ breadcrumb("#{current_url}/#{next_node}", options, &block)
106
+ end
107
+ end
108
+
109
+ def find_index_dir(dir)
110
+ return dir if File.exists?(File.join(dir, "index.haml"))
111
+ return dir == "." ? dir : find_index_dir(File.dirname(dir))
112
+ end
113
+
114
+ # TODO
115
+ def load_tree
116
+ root = File.expand_path("public")
117
+ dir = params[:dir]
118
+ result = "<ul class=\"jqueryFileTree\" style=\"display: none;\">"
119
+ begin
120
+ path = File.expand_path(File.join(root, dir)).untaint
121
+ if (path.split("/").length >= root.split("/").length)
122
+ current_dir = Dir.pwd
123
+ Dir.chdir(File.expand_path(path).untaint);
124
+ #loop through all directories
125
+ Dir.glob("*") {
126
+ |x|
127
+ if not File.directory?(x.untaint) then next end
128
+ result << "<li class=\"directory collapsed\"><a href=\"#\" rel=\"#{dir}#{x}/\">#{x}</a></li>";
129
+ }
130
+
131
+ #loop through all files
132
+ Dir.glob("*") {
133
+ |x|
134
+ if not File.file?(x.untaint) then next end
135
+ ext = File.extname(x)[1..-1]
136
+ result << "<li class=\"file ext_#{ext}\"><a href=\"#\" rel=\"#{dir}#{x}\">#{x}</a></li>"
137
+ }
138
+ else
139
+ #only happens when someone tries to go outside your root directory...
140
+ result << "You are way out of your league"
141
+ end
142
+ rescue
143
+ result << "Internal Error"
144
+ end
145
+ Dir.chdir(current_dir)
146
+ result << "</ul>"
147
+ result
148
+ end
149
+
150
+ end
151
+ end
152
+ end