broadway 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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