woody 0.3.4 → 0.4.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/bin/woody +14 -9
- data/lib/woody/compiler.rb +76 -68
- data/lib/woody/deployer.rb +17 -15
- data/lib/woody/episode.rb +20 -44
- data/lib/woody/generator.rb +9 -8
- data/lib/woody/post.rb +63 -0
- data/lib/woody/version.rb +2 -2
- data/lib/woody.rb +41 -15
- data/templates/feed.xml +16 -15
- data/templates/index.html +3 -3
- data/templates/layout.html +4 -3
- data/templates/post.html +19 -0
- data/woody.gemspec +2 -0
- metadata +36 -3
- data/templates/episode.html +0 -16
data/bin/woody
CHANGED
@@ -10,12 +10,17 @@ program :description, 'Podcast static site generator'
|
|
10
10
|
|
11
11
|
default_command :compile
|
12
12
|
|
13
|
+
$dir = "."
|
14
|
+
global_option '--dir', '--dir path/to/site', "Specifies path to Woody site directory, if not the current directory" do |dir|
|
15
|
+
$dir = dir
|
16
|
+
end
|
17
|
+
|
13
18
|
command :compile do |c|
|
14
19
|
c.description = "Compiles the site"
|
15
20
|
c.option "--no-add", "Don't ask to add new metadata"
|
16
21
|
c.action do |args, options|
|
17
|
-
Woody.
|
18
|
-
|
22
|
+
site = Woody.new($dir)
|
23
|
+
site.compile(options)
|
19
24
|
end
|
20
25
|
end
|
21
26
|
alias_command :c, :compile
|
@@ -23,8 +28,8 @@ alias_command :c, :compile
|
|
23
28
|
command :deploy do |c|
|
24
29
|
c.description = "Deploys the site"
|
25
30
|
c.action do |args, options|
|
26
|
-
Woody.
|
27
|
-
|
31
|
+
site = Woody.new($dir)
|
32
|
+
site.deploy
|
28
33
|
end
|
29
34
|
end
|
30
35
|
alias_command :d, :deploy
|
@@ -33,9 +38,9 @@ command :cd do |c|
|
|
33
38
|
c.description = "Compiles then deploys the site"
|
34
39
|
c.option "--no-add", "Don't ask to add new metadata"
|
35
40
|
c.action do |args, options|
|
36
|
-
Woody.
|
37
|
-
|
38
|
-
|
41
|
+
site = Woody.new($dir)
|
42
|
+
site.compile(options)
|
43
|
+
site.deploy
|
39
44
|
end
|
40
45
|
end
|
41
46
|
|
@@ -54,8 +59,8 @@ end
|
|
54
59
|
command :'update templates' do |c|
|
55
60
|
c.description = "Sets template files to current default. DESTRUCTIVE!"
|
56
61
|
c.action do |args, options|
|
57
|
-
Woody.
|
58
|
-
|
62
|
+
site = Woody.new($dir)
|
63
|
+
site.update_templates
|
59
64
|
end
|
60
65
|
end
|
61
66
|
alias_command :'templates update', :'update templates'
|
data/lib/woody/compiler.rb
CHANGED
@@ -1,38 +1,48 @@
|
|
1
|
-
|
1
|
+
require 'preamble'
|
2
|
+
|
3
|
+
class Woody
|
2
4
|
# Handles functions related to compiling the Woody site
|
3
5
|
module Compiler
|
4
|
-
@@touchedfiles = []
|
5
6
|
|
6
7
|
# Compiles the Woody site
|
7
|
-
def
|
8
|
+
def compile(options = nil)
|
8
9
|
puts "Compiling..."
|
9
|
-
meta = YAML.load_file("content/metadata.yml")
|
10
|
+
meta = YAML.load_file(dir("content/metadata.yml"))
|
10
11
|
|
11
12
|
# instantiate the metadata hash so shit doesn't explode in our faces
|
12
|
-
|
13
|
-
if meta == false
|
14
|
-
meta = Hash.new
|
15
|
-
end
|
13
|
+
meta = Hash.new if meta == false
|
16
14
|
|
17
|
-
|
18
|
-
filesfound
|
19
|
-
Dir.glob('content/*.mp3') do |file|
|
20
|
-
filename = file[8..-1]
|
15
|
+
posts = Array.new
|
16
|
+
filesfound = Array.new
|
17
|
+
Dir.glob(dir('content/*.mp3')) do |file|
|
18
|
+
filename = undir(file)[8..-1]
|
21
19
|
unless meta == false or meta[filename].nil?
|
22
20
|
# Episode metadata already stored
|
23
|
-
|
21
|
+
posts << Episode.new_from_meta(self, filename, meta[filename])
|
24
22
|
filesfound << filename
|
25
23
|
else
|
26
24
|
# No episode metadata stored for this yet
|
27
|
-
unless options.no_add == false # Seemingly backwards, I know...
|
28
|
-
prompt_metadata(meta,
|
25
|
+
unless options.nil? or options.no_add == false # Seemingly backwards, I know...
|
26
|
+
prompt_metadata(meta, posts, filesfound, filename)
|
29
27
|
else
|
30
28
|
puts "Warning: found media file #{filename} but no metadata. Will not be published."
|
31
29
|
end
|
32
30
|
end
|
33
31
|
end
|
34
32
|
|
35
|
-
|
33
|
+
# Process blog posts
|
34
|
+
Dir.glob(dir('content/posts/*')) do |file|
|
35
|
+
filename = undir(file)[8..-1]
|
36
|
+
data = Preamble.load(file)
|
37
|
+
m = data[0]
|
38
|
+
body = data[1]
|
39
|
+
|
40
|
+
posts << Post.new(self, filename, m['title'], m['subtitle'], body, Date.parse(m['date'].to_s), m['tags'])
|
41
|
+
end
|
42
|
+
|
43
|
+
posts.sort_by! do |post|
|
44
|
+
post.date
|
45
|
+
end.reverse!
|
36
46
|
|
37
47
|
# Check for files in meta but not found
|
38
48
|
unless meta.empty?
|
@@ -43,40 +53,41 @@ module Woody
|
|
43
53
|
end
|
44
54
|
|
45
55
|
# Make sure necessary directories exist
|
46
|
-
FileUtils.mkdir_p('output/assets') unless File.directory?('output/assets')
|
47
|
-
FileUtils.mkdir_p('output/assets/mp3') unless File.directory?('output/assets/mp3')
|
48
|
-
FileUtils.mkdir_p('output/
|
56
|
+
FileUtils.mkdir_p(dir 'output/assets') unless File.directory?(dir 'output/assets')
|
57
|
+
FileUtils.mkdir_p(dir 'output/assets/mp3') unless File.directory?(dir 'output/assets/mp3')
|
58
|
+
FileUtils.mkdir_p(dir 'output/post') unless File.directory?(dir 'output/post')
|
49
59
|
|
50
60
|
# Copy over (TODO: and process) MP3 files
|
51
|
-
|
52
|
-
|
61
|
+
posts.each do |post|
|
62
|
+
next unless post.has_file?
|
63
|
+
copy_file_to_output dir(File.join("content", post.filename)), File.join("assets/mp3", post.compiledname)
|
53
64
|
end
|
54
65
|
|
55
66
|
# Copy over assets
|
56
67
|
copy_assets
|
57
68
|
|
58
69
|
# Update index.html
|
59
|
-
layout = File.read('templates/layout.html')
|
70
|
+
layout = File.read(dir 'templates/layout.html')
|
60
71
|
layout = Erubis::Eruby.new(layout)
|
61
72
|
|
62
|
-
index_compiled = layout.result(config:
|
63
|
-
index = Erubis::Eruby.new(File.read('templates/index.html'))
|
64
|
-
index.result(config:
|
65
|
-
ep = Erubis::Eruby.new(File.read('templates/
|
66
|
-
ep.result(config:
|
73
|
+
index_compiled = layout.result(config: @config) do
|
74
|
+
index = Erubis::Eruby.new(File.read(dir 'templates/index.html'))
|
75
|
+
index.result(config: @config, posts: posts) do |post|
|
76
|
+
ep = Erubis::Eruby.new(File.read(dir 'templates/post.html'))
|
77
|
+
ep.result(config: @config, posts: posts, post: post)
|
67
78
|
end
|
68
79
|
end
|
69
80
|
write_output_file('index.html') {|f| f.write(index_compiled) }
|
70
81
|
|
71
|
-
# Update
|
72
|
-
|
73
|
-
layout = File.read('templates/layout.html')
|
82
|
+
# Update post pages
|
83
|
+
posts.each do |post|
|
84
|
+
layout = File.read(dir 'templates/layout.html')
|
74
85
|
layout = Erubis::Eruby.new(layout)
|
75
|
-
|
76
|
-
ep = Erubis::Eruby.new(File.read('templates/
|
77
|
-
ep.result(config:
|
86
|
+
post_compiled = layout.result(config: @config) do
|
87
|
+
ep = Erubis::Eruby.new(File.read(dir 'templates/post.html'))
|
88
|
+
ep.result(config: @config, posts: posts, post: post)
|
78
89
|
end
|
79
|
-
write_output_file(
|
90
|
+
write_output_file(post.path!) {|f| f.write(post_compiled) }
|
80
91
|
end
|
81
92
|
|
82
93
|
# Copy over iTunes.png
|
@@ -87,44 +98,24 @@ module Woody
|
|
87
98
|
end
|
88
99
|
|
89
100
|
# Update (iTunes) RSS
|
90
|
-
|
101
|
+
@config['itunes']['explicit'] = "no" if @config['itunes']['explicit'].nil?
|
91
102
|
feedxml = File.read File.join($source_root, "feed.xml") # Use feed.xml template in gem, not in site's template folder.
|
92
103
|
feed = Erubis::Eruby.new(feedxml)
|
93
|
-
feed_compiled = feed.result(config:
|
104
|
+
feed_compiled = feed.result(config: @config, posts: posts)
|
94
105
|
write_output_file("feed.xml") {|f| f.write(feed_compiled) }
|
95
106
|
|
96
107
|
|
97
|
-
|
98
|
-
# TODO: Update General RSS
|
99
|
-
|
100
|
-
|
101
108
|
# Purge left over files
|
102
109
|
purge_output
|
103
110
|
|
104
111
|
# Remove all empty directories from the output
|
105
|
-
Dir['output/**/*'].select { |d| File.directory? d }.select { |d| (Dir.entries(d) - %w[ . .. ]).empty? }.each { |d| Dir.rmdir d }
|
112
|
+
Dir[dir 'output/**/*'].select { |d| File.directory? d }.select { |d| (Dir.entries(d) - %w[ . .. ]).empty? }.each { |d| Dir.rmdir d }
|
106
113
|
end
|
107
114
|
|
108
115
|
private
|
109
116
|
|
110
|
-
# Safely copies files to the output directory
|
111
|
-
def self.copy_file_to_output(source, destination)
|
112
|
-
d = File.join("output", destination)
|
113
|
-
FileUtils.copy source, d
|
114
|
-
@@touchedfiles << d
|
115
|
-
end
|
116
|
-
|
117
|
-
# Safely writes files to the output directory
|
118
|
-
def self.write_output_file(filename, &block)
|
119
|
-
file = File.join("output", filename)
|
120
|
-
File.open(file, 'w') do |f|
|
121
|
-
yield f
|
122
|
-
end
|
123
|
-
@@touchedfiles << file
|
124
|
-
end
|
125
|
-
|
126
117
|
# Prompts for metadata for new media files
|
127
|
-
def
|
118
|
+
def prompt_metadata(meta, posts, filesfound, filename)
|
128
119
|
puts "Found new media file: #{filename}"
|
129
120
|
if agree("Add metadata for this file? ")
|
130
121
|
m = Hash.new
|
@@ -136,7 +127,7 @@ module Woody
|
|
136
127
|
m['explicit'] = agree "Explicit content?: "
|
137
128
|
|
138
129
|
meta[filename] = m
|
139
|
-
|
130
|
+
posts << Episode.new(self, filename, m['title'], Date.parse(m['date'].to_s), m['synopsis'], m['subtitle'], m['tags'], m['explicit'])
|
140
131
|
filesfound << filename
|
141
132
|
|
142
133
|
write_meta meta
|
@@ -146,27 +137,44 @@ module Woody
|
|
146
137
|
end
|
147
138
|
|
148
139
|
# Writes the metadata file
|
149
|
-
def
|
150
|
-
File.open(
|
140
|
+
def write_meta(meta)
|
141
|
+
File.open(dir('content/metadata.yml'), 'w' ) do |out|
|
151
142
|
YAML.dump meta, out
|
152
143
|
end
|
153
144
|
end
|
154
145
|
|
146
|
+
# Safely copies files to the output directory
|
147
|
+
def copy_file_to_output(source, destination)
|
148
|
+
d = dir(File.join("output", destination))
|
149
|
+
FileUtils.copy source, d
|
150
|
+
@touchedfiles << undir(d)
|
151
|
+
end
|
152
|
+
|
153
|
+
# Safely writes files to the output directory
|
154
|
+
def write_output_file(filename, &block)
|
155
|
+
file = dir(File.join("output", filename))
|
156
|
+
File.open(file, 'w') do |f|
|
157
|
+
yield f
|
158
|
+
end
|
159
|
+
@touchedfiles << undir(file)
|
160
|
+
end
|
161
|
+
|
162
|
+
|
155
163
|
# Copies custom assets to output
|
156
|
-
def
|
157
|
-
Dir.glob "templates/assets/**/*" do |item|
|
164
|
+
def copy_assets
|
165
|
+
Dir.glob dir("templates/assets/**/*") do |item|
|
158
166
|
next if File.directory? item
|
159
|
-
d = item
|
167
|
+
d = undir item
|
168
|
+
d = d[10..-1] # Cut off "templates/" at beginning
|
160
169
|
copy_file_to_output item, d
|
161
170
|
end
|
162
171
|
end
|
163
172
|
|
164
|
-
|
165
173
|
# Deletes old files from the site's output directory
|
166
|
-
def
|
167
|
-
Dir.glob "output/**/*" do |item|
|
174
|
+
def purge_output
|
175
|
+
Dir.glob dir("output/**/*") do |item|
|
168
176
|
next if File.directory? item
|
169
|
-
File.delete item unless
|
177
|
+
File.delete item unless @touchedfiles.include? undir(item)
|
170
178
|
end
|
171
179
|
end
|
172
180
|
|
data/lib/woody/deployer.rb
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
|
1
|
+
class Woody
|
2
2
|
# Handles functions relating to deploying the Woody site
|
3
3
|
module Deployer
|
4
|
-
@@touchedfiles = []
|
5
4
|
# Deploys the Woody site to S3
|
6
|
-
def
|
5
|
+
def deploy
|
7
6
|
puts "Deploying..."
|
8
7
|
|
9
|
-
Dir.glob "output/**/*" do |item|
|
8
|
+
Dir.glob dir("output/**/*") do |item|
|
10
9
|
next if File.directory? item
|
10
|
+
item = undir item
|
11
11
|
name = item[7..-1] # Remove "output/"
|
12
12
|
next if name == "index.html" # These *must* be left until last
|
13
13
|
next if name == "feed.xml"
|
@@ -26,17 +26,17 @@ module Woody
|
|
26
26
|
|
27
27
|
# Deletes old objects from the S3 bucket
|
28
28
|
# @param [Array] touchedfiles specifies the S3 objects to keep
|
29
|
-
def
|
30
|
-
bucket = AWS::S3::Bucket.find
|
31
|
-
prefix =
|
29
|
+
def purge_bucket
|
30
|
+
bucket = AWS::S3::Bucket.find @bucketname
|
31
|
+
prefix = @config['s3']['prefix']
|
32
32
|
if prefix.nil?
|
33
33
|
bucket.objects.each do |object|
|
34
|
-
object.delete unless
|
34
|
+
object.delete unless @s3touchedobjects.include? object.key
|
35
35
|
end
|
36
36
|
else
|
37
37
|
bucket.objects.each do |object|
|
38
38
|
if object.key.start_with? prefix # If using a prefix, don't delete anything outside of that 'subdirectory'
|
39
|
-
object.delete unless
|
39
|
+
object.delete unless @s3touchedobjects.include? object.key
|
40
40
|
end
|
41
41
|
end
|
42
42
|
end
|
@@ -49,18 +49,20 @@ module Woody
|
|
49
49
|
# Prints notices to STDOUT
|
50
50
|
# @param [String] objectname specifies the S3 object's key/name
|
51
51
|
# @param [String] filepath specifies the path to the file to upload
|
52
|
-
def
|
53
|
-
prefix =
|
52
|
+
def upload(objectname, filepath)
|
53
|
+
prefix = @config['s3']['prefix']
|
54
54
|
unless prefix.nil?
|
55
55
|
objectname = File.join(prefix, objectname)
|
56
56
|
end
|
57
57
|
|
58
|
+
filepath = dir(filepath)
|
59
|
+
|
58
60
|
# Generate hash of file
|
59
61
|
hash = filehash filepath
|
60
|
-
|
62
|
+
@s3touchedobjects << objectname
|
61
63
|
# Get hash of version already uploaded, if available.
|
62
64
|
begin
|
63
|
-
object = AWS::S3::S3Object.find objectname,
|
65
|
+
object = AWS::S3::S3Object.find objectname, @bucketname
|
64
66
|
oldhash = object.metadata['hash']
|
65
67
|
rescue AWS::S3::NoSuchKey
|
66
68
|
# File not uploaded yet
|
@@ -69,7 +71,7 @@ module Woody
|
|
69
71
|
unless hash == oldhash
|
70
72
|
# Don't reupload if file hasn't changed
|
71
73
|
puts "#{objectname}: Uploading"
|
72
|
-
AWS::S3::S3Object.store(objectname, open(filepath),
|
74
|
+
AWS::S3::S3Object.store(objectname, open(filepath), @bucketname, access: :public_read, 'x-amz-meta-hash' => hash)
|
73
75
|
else
|
74
76
|
puts "#{objectname}: Not uploading, hasn't changed since last time."
|
75
77
|
end
|
@@ -80,7 +82,7 @@ module Woody
|
|
80
82
|
# Stored in S3 object metadata (x-amz-meta-hash) and used to avoid re-uploading unchanged files
|
81
83
|
# @param [String] filepath path to file
|
82
84
|
# @return [String] hash of file
|
83
|
-
def
|
85
|
+
def filehash(filepath)
|
84
86
|
sha1 = Digest::SHA1.new
|
85
87
|
File.open(filepath) do|file|
|
86
88
|
buffer = ''
|
data/lib/woody/episode.rb
CHANGED
@@ -1,57 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
1
|
+
require "woody/post"
|
2
|
+
|
3
|
+
class Woody
|
4
|
+
# Represents an episode of the podcast. Inherits from Post.
|
5
|
+
class Episode < Post
|
4
6
|
# Creates a new Episode object from segment of metadata.yml
|
5
7
|
# @param [String] filename specifies the name of the MP3 file
|
6
8
|
# @param [Hash] meta is the relevant part of metadata.yml
|
7
9
|
# @return [Episode] the new Episode object
|
8
|
-
def self.new_from_meta(filename, meta)
|
9
|
-
return Episode.new(filename, meta['title'], Date.parse(meta['date'].to_s), meta['synopsis'], meta['subtitle'], meta['tags'], meta['explicit'])
|
10
|
+
def self.new_from_meta(site, filename, meta)
|
11
|
+
return Episode.new(site, filename, meta['title'], Date.parse(meta['date'].to_s), meta['synopsis'], meta['subtitle'], meta['tags'], meta['explicit'])
|
10
12
|
end
|
11
13
|
|
12
14
|
# Creates a new Episode object
|
13
15
|
# @param [String] filename specifies the name of the MP3 file
|
14
16
|
# @param [String] title specifies the episode's title
|
15
17
|
# @param [Date] date specifies the episode's published date
|
16
|
-
# @param [String]
|
18
|
+
# @param [String] body specifies the episode's synopsis/body
|
17
19
|
# @param [String] subtitle specifies the episode's subtitle
|
18
20
|
# @param [Array] tags specifies the episode's tags - each element is a String
|
19
21
|
# @return [Episode] the new Episode object
|
20
|
-
def initialize(filename, title, date,
|
21
|
-
|
22
|
-
@title = title
|
23
|
-
@date = date
|
24
|
-
@synopsis = synopsis
|
25
|
-
@subtitle = subtitle
|
26
|
-
@tags = tags
|
22
|
+
def initialize(site, filename, title, date, raw_body, subtitle = nil, tags = [], explicit = false)
|
23
|
+
super site, filename, title, subtitle, raw_body, date, tags
|
27
24
|
@explicit = explicit
|
28
|
-
@compiledname = @filename.gsub(/[^0-9A-Za-z .]/, '').gsub(' ', '_')
|
25
|
+
@compiledname = @filename.gsub(/[^0-9A-Za-z ._]/, '').gsub(' ', '_')
|
29
26
|
end
|
30
27
|
|
31
|
-
attr_accessor :
|
32
|
-
|
33
|
-
# @return the episode's page URL where possible, otherwise false
|
34
|
-
def url
|
35
|
-
return "#{$config['urlbase']}#{path!}" unless path! == false
|
36
|
-
return false
|
37
|
-
end
|
28
|
+
attr_accessor :explicit
|
38
29
|
|
39
|
-
# @return the episode's page path! where possible, otherwise false. Does not take prefix in to account.
|
40
|
-
def path!(leader=true)
|
41
|
-
return "#{leader ? "/" : ""}episode/#{@compiledname[0..-5]}.html" unless @compiledname.nil?
|
42
|
-
return false
|
43
|
-
end
|
44
|
-
|
45
|
-
# @return the episode's page path! where possible, otherwise false. Includes the site prefix if enabled.
|
46
|
-
def path(leader=true)
|
47
|
-
prefix = $config['s3']['prefix']
|
48
|
-
return "#{leader ? "/" : ""}#{prefix.nil? ? "" : prefix + "/" }episode/#{@compiledname[0..-5]}.html" unless @compiledname.nil?
|
49
|
-
return false
|
50
|
-
end
|
51
30
|
|
52
31
|
# @return the episode's media file URL where possible, otherwise false
|
53
32
|
def file_url
|
54
|
-
return "#{
|
33
|
+
return "#{@site.config['urlbase']}#{file_path!}" unless file_path! == false
|
55
34
|
return false
|
56
35
|
end
|
57
36
|
|
@@ -63,23 +42,19 @@ module Woody
|
|
63
42
|
|
64
43
|
# @return the episode's media file path! where possible, otherwise false. Includes site prefix if enabled.
|
65
44
|
def file_path(leader=true)
|
66
|
-
prefix =
|
45
|
+
prefix = @site.config['s3']['prefix']
|
67
46
|
return "#{leader ? "/" : ""}#{prefix.nil? ? "" : prefix + "/" }assets/mp3/#{@compiledname}" unless @compiledname.nil?
|
68
47
|
return false
|
69
48
|
end
|
70
49
|
|
71
50
|
|
72
|
-
# @return [String] a comma separated list of tags, or nil if no tags
|
73
|
-
def keywords
|
74
|
-
@tags.join ', ' unless @tags.nil? or @tags.empty?
|
75
|
-
end
|
76
51
|
|
77
52
|
# @return [Integer] the size of the episodes media file in bytes
|
78
53
|
def size
|
79
|
-
File.size File.join("content", filename)
|
54
|
+
File.size @site.dir(File.join("content", filename))
|
80
55
|
end
|
81
56
|
|
82
|
-
# @return [String] 'yes' if explicit content, otherwise
|
57
|
+
# @return [String] 'yes' if explicit content, otherwise 'no'
|
83
58
|
def explicit_string
|
84
59
|
@explicit ? 'yes' : 'no'
|
85
60
|
end
|
@@ -88,7 +63,7 @@ module Woody
|
|
88
63
|
def duration
|
89
64
|
return @duration unless @duration.nil?
|
90
65
|
length = 0
|
91
|
-
Mp3Info.open(File.join("content", filename)) do |mp3|
|
66
|
+
Mp3Info.open(@site.dir(File.join("content", filename))) do |mp3|
|
92
67
|
length = mp3.length
|
93
68
|
end
|
94
69
|
@duration = Time.at(length).gmtime.strftime('%R:%S') # Should work up to 24 hours
|
@@ -97,8 +72,9 @@ module Woody
|
|
97
72
|
end
|
98
73
|
end
|
99
74
|
|
100
|
-
|
101
|
-
|
75
|
+
|
76
|
+
def has_file?
|
77
|
+
true
|
102
78
|
end
|
103
79
|
|
104
80
|
end
|
data/lib/woody/generator.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
class Woody
|
2
2
|
# Handles functions related to generating Woody sites and updating them and their data stores
|
3
3
|
module Generator
|
4
4
|
# Generates a blank skeleton Woody site
|
@@ -17,7 +17,7 @@ module Woody
|
|
17
17
|
cdir_p("#{name}/templates")
|
18
18
|
cpy_t("layout.html", "#{name}/templates/layout.html")
|
19
19
|
cpy_t("index.html", "#{name}/templates/index.html")
|
20
|
-
cpy_t("
|
20
|
+
cpy_t("post.html", "#{name}/templates/post.html")
|
21
21
|
|
22
22
|
cdir_p("#{name}/templates/assets")
|
23
23
|
cpy_t("stylesheet.css", "#{name}/templates/assets/stylesheet.css")
|
@@ -25,23 +25,24 @@ module Woody
|
|
25
25
|
cdir_p("#{name}/content")
|
26
26
|
cpy_t("metadata.yml", "#{name}/content/metadata.yml")
|
27
27
|
cpy_t("iTunes.png", "#{name}/content/iTunes.png")
|
28
|
+
cdir_p("#{name}/content/posts")
|
28
29
|
|
29
30
|
cdir_p("#{name}/output")
|
30
31
|
cdir_p("#{name}/output/assets")
|
31
32
|
cdir_p("#{name}/output/assets/mp3")
|
32
|
-
cdir_p("#{name}/output/
|
33
|
+
cdir_p("#{name}/output/post")
|
33
34
|
|
34
35
|
puts "Done!"
|
35
36
|
puts "Now, do `cd #{name}` then edit the config file, woody-config.yml."
|
36
37
|
end
|
37
38
|
|
38
39
|
# Replaces the templates in the Woody site with the gem's current default ones
|
39
|
-
def self.update_templates
|
40
|
+
def self.update_templates(site)
|
40
41
|
puts "Updating templates..."
|
41
|
-
cpy_t("layout.html", "templates/layout.html")
|
42
|
-
cpy_t("index.html", "templates/index.html")
|
43
|
-
cpy_t("
|
44
|
-
cpy_t("stylesheet.css", "templates/assets/stylesheet.css")
|
42
|
+
cpy_t("layout.html", site.dir("templates/layout.html"))
|
43
|
+
cpy_t("index.html", site.dir("templates/index.html"))
|
44
|
+
cpy_t("post.html", site.dir("templates/post.html"))
|
45
|
+
cpy_t("stylesheet.css", site.dir("templates/assets/stylesheet.css"))
|
45
46
|
puts "Done! Thanks for updating :)"
|
46
47
|
end
|
47
48
|
|
data/lib/woody/post.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
|
3
|
+
class Woody
|
4
|
+
# Represents a post
|
5
|
+
class Post
|
6
|
+
# Creates a new Post object
|
7
|
+
# @param [String] filename specifies the name of the post file
|
8
|
+
# @param [String] title specifies the Post's title
|
9
|
+
# @param [String] subtitle specifies the Post's subtitle
|
10
|
+
# @param [String] body specifies the Post's body
|
11
|
+
# @param [Date] date specifies the Post's published date
|
12
|
+
# @param [Array] tags specifies the Post's tags - each element is a String
|
13
|
+
# @return [Post] the new Post object
|
14
|
+
def initialize(site, filename, title, subtitle, raw_body, date, tags = [], compiledname = nil)
|
15
|
+
@site = site
|
16
|
+
@filename = filename
|
17
|
+
@title = title
|
18
|
+
@subtitle = subtitle
|
19
|
+
@raw_body = raw_body
|
20
|
+
@date = date
|
21
|
+
@tags = tags.nil? ? [] : tags
|
22
|
+
@compiledname = @filename[6..-1].gsub(/[^0-9A-Za-z ._]/, '').gsub(' ', '_')
|
23
|
+
end
|
24
|
+
attr_accessor :filename, :title, :subtitle, :raw_body, :date, :tags, :compiledname
|
25
|
+
|
26
|
+
def body(regenerate = false)
|
27
|
+
return @body unless @body.nil? or regenerate
|
28
|
+
return @body = Kramdown::Document.new(@raw_body).to_html
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return the Page's page URL where possible, otherwise false
|
32
|
+
def url
|
33
|
+
return "#{@site.config['urlbase']}#{path!}" unless path! == false
|
34
|
+
return false
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return the Page's page path where possible, otherwise false. Does not take prefix in to account.
|
38
|
+
def path!(leader=true)
|
39
|
+
return "#{leader ? "/" : ""}post/#{@compiledname.chomp(File.extname(@compiledname))}.html" unless @compiledname.nil?
|
40
|
+
return false
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return the Page's page path where possible, otherwise false. Includes the site prefix if enabled.
|
44
|
+
def path(leader=true)
|
45
|
+
prefix = @site.config['s3']['prefix']
|
46
|
+
return "#{leader ? "/" : ""}#{prefix.nil? ? "" : prefix + "/" }post/#{@compiledname.chomp(File.extname(@compiledname))}.html" unless @compiledname.nil?
|
47
|
+
return false
|
48
|
+
end
|
49
|
+
|
50
|
+
# @return [String] a comma separated list of tags, or nil if no tags
|
51
|
+
def keywords
|
52
|
+
@tags.join ', ' unless @tags.nil? or @tags.empty?
|
53
|
+
end
|
54
|
+
|
55
|
+
def has_file?
|
56
|
+
false
|
57
|
+
end
|
58
|
+
|
59
|
+
def <=> (other)
|
60
|
+
other.date <=> self.date
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/woody/version.rb
CHANGED
data/lib/woody.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require "woody/version"
|
2
|
+
require "woody/post"
|
2
3
|
require "woody/episode"
|
3
4
|
require "woody/compiler"
|
4
5
|
require "woody/deployer"
|
@@ -19,34 +20,45 @@ require 'mp3info'
|
|
19
20
|
$VERBOSE = oldverbosity
|
20
21
|
|
21
22
|
# Woody podcast static site generator
|
22
|
-
|
23
|
+
class Woody
|
24
|
+
include Generator
|
25
|
+
include Compiler
|
26
|
+
include Deployer
|
27
|
+
|
28
|
+
|
23
29
|
# Path of template directory inside gem
|
24
30
|
$source_root = File.expand_path("../../templates", __FILE__)
|
25
31
|
|
26
32
|
# Load configuration and connect to S3
|
27
|
-
def
|
33
|
+
def initialize(directory = ".")
|
34
|
+
@directory = directory
|
35
|
+
@touchedfiles = []
|
36
|
+
@s3touchedobjects = []
|
37
|
+
|
38
|
+
|
39
|
+
|
28
40
|
begin
|
29
|
-
|
41
|
+
@config = YAML.load_file(dir("woody-config.yml"))
|
30
42
|
rescue Errno::ENOENT
|
31
43
|
puts "This doesn't look like a valid Woody site directory!"
|
32
44
|
exit!
|
33
45
|
end
|
34
46
|
|
35
47
|
# Strip trailing slash from urlbase, if present.
|
36
|
-
if
|
37
|
-
|
48
|
+
if @config['urlbase'].end_with? "/"
|
49
|
+
@config['urlbase'] = @config['urlbase'][0..-2]
|
38
50
|
end
|
39
51
|
|
40
|
-
if
|
41
|
-
prefix =
|
52
|
+
if @config['distributiontype'] == "s3"
|
53
|
+
prefix = @config['s3']['prefix']
|
42
54
|
unless prefix.nil?
|
43
|
-
|
55
|
+
@config['urlbase'] = @config['urlbase'] + "/" + prefix
|
44
56
|
end
|
45
57
|
end
|
46
58
|
|
47
|
-
|
48
|
-
:access_key_id =>
|
49
|
-
:secret_access_key =>
|
59
|
+
s3options = {
|
60
|
+
:access_key_id => @config['s3']['accesskey']['id'],
|
61
|
+
:secret_access_key => @config['s3']['accesskey']['secret']
|
50
62
|
}
|
51
63
|
|
52
64
|
unless ENV['http_proxy'].nil?
|
@@ -56,14 +68,28 @@ module Woody
|
|
56
68
|
p[:port] = uri.port
|
57
69
|
p[:user] = uri.user unless uri.user.nil?
|
58
70
|
p[:password] = uri.password unless uri.password.nil?
|
59
|
-
|
71
|
+
s3options[:proxy] = p
|
60
72
|
end
|
61
73
|
|
62
|
-
AWS::S3::Base.establish_connection!(
|
63
|
-
AWS::S3::DEFAULT_HOST.replace
|
64
|
-
|
74
|
+
AWS::S3::Base.establish_connection!(s3options)
|
75
|
+
AWS::S3::DEFAULT_HOST.replace @config['s3']['hostname']
|
76
|
+
@bucketname = @config['s3']['bucket']
|
65
77
|
end
|
66
78
|
|
79
|
+
attr_accessor :config
|
80
|
+
attr_reader :directory
|
81
|
+
|
82
|
+
def dir(dir="")
|
83
|
+
File.expand_path(File.join(@directory, dir))
|
84
|
+
end
|
85
|
+
|
86
|
+
def undir(string)
|
87
|
+
string[dir.length+1..-1]
|
88
|
+
end
|
89
|
+
|
90
|
+
def update_templates
|
91
|
+
Generator::update_templates(self)
|
92
|
+
end
|
67
93
|
end
|
68
94
|
|
69
95
|
|
data/templates/feed.xml
CHANGED
@@ -17,27 +17,28 @@
|
|
17
17
|
<itunes:email><%= config['itunes']['owner']['email'] %></itunes:email>
|
18
18
|
</itunes:owner>
|
19
19
|
|
20
|
-
<itunes:image href="<%=
|
21
|
-
<itunes:category text="<%=
|
20
|
+
<itunes:image href="<%= config['urlbase'] + "/assets/iTunes.png" %>"/>
|
21
|
+
<itunes:category text="<%= config['itunes']['category'] %>"/>
|
22
22
|
|
23
|
-
<atom:link href="<%=
|
23
|
+
<atom:link href="<%= config['urlbase'] %>/feed.xml" rel="self" type="application/rss+xml" />
|
24
24
|
|
25
25
|
|
26
|
-
<%-
|
26
|
+
<%- posts.each do |post| %>
|
27
|
+
<%- next unless post.has_file? %>
|
27
28
|
<item>
|
28
|
-
<title><%=
|
29
|
-
<itunes:subtitle><%=
|
30
|
-
<itunes:summary><%=
|
31
|
-
<pubDate><%=
|
32
|
-
<itunes:duration><%=
|
33
|
-
<itunes:keywords><%=
|
34
|
-
<itunes:explicit><%=
|
35
|
-
<description><%=
|
36
|
-
<enclosure url="<%=
|
37
|
-
<guid><%=
|
29
|
+
<title><%= post.title %></title>
|
30
|
+
<itunes:subtitle><%= post.subtitle %></itunes:subtitle>
|
31
|
+
<itunes:summary><%= post.body %></itunes:summary>
|
32
|
+
<pubDate><%= post.date.rfc2822 %></pubDate>
|
33
|
+
<itunes:duration><%= post.duration %></itunes:duration>
|
34
|
+
<itunes:keywords><%= post.keywords %></itunes:keywords>
|
35
|
+
<itunes:explicit><%= post.explicit_string %></itunes:explicit>
|
36
|
+
<description><%= post.body %></description>
|
37
|
+
<enclosure url="<%= post.file_url %>" length="<%= post.size %>" type="audio/mpeg"/>
|
38
|
+
<guid><%= post.url %></guid>
|
38
39
|
<category>Podcasts</category>
|
39
40
|
</item>
|
40
|
-
|
41
|
+
<% end %>
|
41
42
|
|
42
43
|
</channel>
|
43
44
|
</rss>
|
data/templates/index.html
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
<h2>Episodes:</h2>
|
2
|
-
<%= link_to "RSS Feed", "#{
|
3
|
-
<%
|
2
|
+
<%= link_to "RSS Feed", "#{ config['urlbase'] }/feed.xml" %>
|
3
|
+
<% posts.each do |post| %>
|
4
4
|
<div class="episode">
|
5
|
-
<%= yield
|
5
|
+
<%= yield post %>
|
6
6
|
</div>
|
7
7
|
<% end %>
|
data/templates/layout.html
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html>
|
3
3
|
<head>
|
4
|
-
<
|
4
|
+
<meta charset="UTF-8" />
|
5
|
+
<title><%= config['title'] %></title>
|
5
6
|
<%= generator_meta_tag %>
|
6
7
|
<link rel="stylesheet" type="text/css" href="/assets/stylesheet.css" />
|
7
8
|
</head>
|
8
9
|
<body>
|
9
|
-
<h1><%= link_to
|
10
|
-
<h2><%=
|
10
|
+
<h1><%= link_to config['title'], config['urlbase'] %></h1>
|
11
|
+
<h2><%= config['subtitle'] %></h2>
|
11
12
|
<hr />
|
12
13
|
<%= yield %>
|
13
14
|
</body>
|
data/templates/post.html
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
<h3><%= link_to post.title, post.path %></h3>
|
2
|
+
<h4><%= post.subtitle %></h4>
|
3
|
+
|
4
|
+
<%- if post.has_file? %>
|
5
|
+
Player goes here
|
6
|
+
|
7
|
+
<a href="<%= post.file_path %>">Download MP3</a>
|
8
|
+
<% end %>
|
9
|
+
|
10
|
+
<div class="synopsis body">
|
11
|
+
<%= post.body %>
|
12
|
+
</div>
|
13
|
+
|
14
|
+
<div class="tags">
|
15
|
+
<strong>Tags:</strong>
|
16
|
+
<% post.tags.each do |tag| %>
|
17
|
+
<span class="tag"><%= tag %> </span>
|
18
|
+
<% end %>
|
19
|
+
</div>
|
data/woody.gemspec
CHANGED
@@ -22,6 +22,8 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_runtime_dependency 'aws-s3'
|
23
23
|
gem.add_runtime_dependency 'commander'
|
24
24
|
gem.add_runtime_dependency 'highline'
|
25
|
+
gem.add_runtime_dependency 'preamble'
|
26
|
+
gem.add_runtime_dependency 'kramdown'
|
25
27
|
|
26
28
|
# gem.post_install_message = "This update modifies default templates. Please run `woody update templates` in your site directory to update them. Warning: this will destroy any modifications to your templates."
|
27
29
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: woody
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-12-
|
12
|
+
date: 2012-12-15 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: erubis
|
@@ -91,6 +91,38 @@ dependencies:
|
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: preamble
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: kramdown
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
94
126
|
description: Woody
|
95
127
|
email:
|
96
128
|
- david@davidr.me
|
@@ -110,13 +142,14 @@ files:
|
|
110
142
|
- lib/woody/deployer.rb
|
111
143
|
- lib/woody/episode.rb
|
112
144
|
- lib/woody/generator.rb
|
145
|
+
- lib/woody/post.rb
|
113
146
|
- lib/woody/version.rb
|
114
|
-
- templates/episode.html
|
115
147
|
- templates/feed.xml
|
116
148
|
- templates/iTunes.png
|
117
149
|
- templates/index.html
|
118
150
|
- templates/layout.html
|
119
151
|
- templates/metadata.yml
|
152
|
+
- templates/post.html
|
120
153
|
- templates/stylesheet.css
|
121
154
|
- templates/woody-config.yml
|
122
155
|
- woody.gemspec
|
data/templates/episode.html
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
<h3><%= link_to episode.title, episode.path %></h3>
|
2
|
-
<h4><%= episode.subtitle %></h4>
|
3
|
-
Player goes here
|
4
|
-
|
5
|
-
<a href="<%= episode.file_path %>">Download MP3</a>
|
6
|
-
|
7
|
-
<div class="synopsis">
|
8
|
-
<%= episode.synopsis %>
|
9
|
-
</div>
|
10
|
-
|
11
|
-
<div class="tags">
|
12
|
-
<strong>Tags:</strong>
|
13
|
-
<% episode.tags.each do |tag| %>
|
14
|
-
<span class="tag"><%= tag %> </span>
|
15
|
-
<% end %>
|
16
|
-
</div>
|