CarmineContraption 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +5 -0
- data/Rakefile +5 -7
- data/Readme.md +34 -10
- data/carmine_contraption.gemspec +1 -1
- data/lib/carmine_contraption.rb +34 -0
- data/lib/carmine_contraption/black_hole.rb +12 -0
- data/lib/carmine_contraption/error.rb +5 -0
- data/lib/carmine_contraption/formatter.rb +34 -0
- data/lib/carmine_contraption/index.rb +47 -106
- data/lib/carmine_contraption/media.rb +1 -1
- data/lib/carmine_contraption/post.rb +34 -79
- data/lib/carmine_contraption/runner.rb +86 -86
- data/lib/carmine_contraption/version.rb +1 -1
- data/lib/carmine_contraption/writer.rb +132 -0
- data/test/data/formats/raw.html.erb +8 -0
- data/test/lib/carmine_contraption/test_error.rb +19 -0
- data/test/lib/carmine_contraption/test_formatter.rb +59 -0
- data/test/lib/carmine_contraption/test_index.rb +4 -0
- data/test/{test_media.rb → lib/carmine_contraption/test_media.rb} +1 -3
- data/test/{test_options.rb → lib/carmine_contraption/test_options.rb} +1 -4
- data/test/{test_post.rb → lib/carmine_contraption/test_post.rb} +2 -37
- data/test/lib/carmine_contraption/test_runner.rb +4 -0
- data/test/{test_uploader.rb → lib/carmine_contraption/test_uploader.rb} +1 -5
- data/test/test_helper.rb +5 -0
- metadata +36 -40
- data/lib/carmine_contraption/formatters.rb +0 -64
- data/lib/carmine_contraption/month_transform.rb +0 -95
- data/test/test_index.rb +0 -6
- data/test/test_runner.rb +0 -6
@@ -1,6 +1,4 @@
|
|
1
|
-
require_relative '
|
2
|
-
require_relative 'index'
|
3
|
-
require_relative 'uploader'
|
1
|
+
require_relative '../carmine_contraption'
|
4
2
|
|
5
3
|
require 'pathname'
|
6
4
|
require 'colorize'
|
@@ -12,107 +10,109 @@ module CarmineContraption
|
|
12
10
|
@source = @options.source
|
13
11
|
@destination = @options.destination
|
14
12
|
@override = @options.override
|
15
|
-
@template = Pathname.new('lib/templates/default.html.erb')
|
16
13
|
@input_extensions = [".md", ".html"]
|
17
14
|
@drafts_path = @source + 'drafts'
|
18
15
|
@posts_path = @source + 'posts'
|
19
16
|
end
|
20
17
|
|
21
|
-
def
|
22
|
-
@
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
puts "Publishing draft: #{posted_filename}".yellow
|
39
|
-
(@posts_path+posted_filename).open('w') {|f| f.write(post)}
|
40
|
-
`rm #{i}`
|
41
|
-
`lib/update_repo.sh #{@source} #{posted_filename}` if @options.update_repository
|
42
|
-
up = Uploader.new({access_key_id: @options.s3_access_key_id,
|
43
|
-
secret_access_key: @options.s3_secret_access_key},
|
44
|
-
@options.s3_target_bucket)
|
45
|
-
the_post = Post.from_file((@posts_path+posted_filename).to_s, input_extension)
|
46
|
-
tmp = []
|
47
|
-
the_post.media.each do |m|
|
48
|
-
puts "Uploading #{m.name}...".green
|
49
|
-
remote_name = "#{the_post.year}/#{the_post.month}/#{m.name}"
|
50
|
-
up.upload (m.update remote_path: remote_name)
|
51
|
-
tmp << (m.update remote_path: "http://#{@options.s3_target_bucket}/#{remote_name}")
|
18
|
+
def run
|
19
|
+
find_new_posts if @options.new_post_check
|
20
|
+
if @options.build_targets.has_value?(true)
|
21
|
+
@the_index = Index.new @source, @destination
|
22
|
+
@the_writer = Writer.new @the_index, @override
|
23
|
+
@options.build_targets.each { |k, v| send "build_#{k}" if v }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def find_new_posts
|
29
|
+
@input_extensions.each do |input_extension|
|
30
|
+
Pathname.glob(@drafts_path + "*#{input_extension}") do |i|
|
31
|
+
post = IO.read i
|
32
|
+
if is_draft_ready? post
|
33
|
+
post, posted_filename = timestamp_post post, i.basename
|
34
|
+
move_draft post, posted_filename, i
|
52
35
|
end
|
53
|
-
the_post = the_post.update(media: tmp)
|
54
|
-
(@posts_path+posted_filename).open('w') { |f| f.write(the_post.formatted_output '') }
|
55
36
|
end
|
56
37
|
end
|
57
38
|
end
|
58
|
-
end
|
59
39
|
|
60
|
-
|
61
|
-
|
62
|
-
@input_extensions.each do |input_extension|
|
63
|
-
Pathname.glob(@posts_path + "*#{input_extension}") { | path | all_paths << path }
|
40
|
+
def is_draft_ready? post
|
41
|
+
/^publish-now\s*$/.match post.downcase
|
64
42
|
end
|
65
|
-
most_recent = all_paths.sort.last
|
66
|
-
most_recent_extension = most_recent.extname
|
67
|
-
the_post = Post.from_file most_recent, most_recent_extension
|
68
|
-
landing_template = Pathname.new('lib/templates/landing_page.html')
|
69
|
-
output = landing_template.open('r').read
|
70
|
-
output.gsub!(/\#\{title\}/, the_post.title)
|
71
|
-
output.gsub!(/\#\{summary\}/, the_post.summary)
|
72
|
-
link = "#{the_post.year}/#{the_post.month}/#{the_post.guid}.html"
|
73
|
-
output.gsub!(/\#\{link\}/, link)
|
74
|
-
(@destination + "index.html").open('w:UTF-8') {|f| f.write(output) }
|
75
|
-
puts "Rendering #{@destination}index.html".blue
|
76
|
-
end
|
77
43
|
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
44
|
+
def timestamp_post post, filename
|
45
|
+
t = Time.now
|
46
|
+
post.gsub!(/^publish-now\s*$/, "Published: #{t.strftime "%a, %d %b %Y %H:%M:%S %Z"}\n")
|
47
|
+
posted_filename = "#{t.strftime('%Y%m%d')}-#{filename}"
|
48
|
+
return post, posted_filename
|
49
|
+
end
|
82
50
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
51
|
+
def move_draft post, filename, draft_name
|
52
|
+
puts "Publishing draft: #{filename}".yellow
|
53
|
+
(@posts_path+filename).open('w') {|f| f.write(post)}
|
54
|
+
`rm #{draft_name}`
|
55
|
+
`lib/update_repo.sh #{@source} #{filename}` if @options.update_repository
|
56
|
+
the_post = Post.from_file((@posts_path+filename).to_s, File.extname(draft_name))
|
57
|
+
the_post = upload_media the_post
|
58
|
+
(@posts_path+filename).open('w') { |f| f.write(the_post.formatted_output Formatter.new(@source+"formats"), :raw) }
|
59
|
+
end
|
87
60
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
61
|
+
def upload_media post
|
62
|
+
up = Uploader.new({access_key_id: @options.s3_access_key_id,
|
63
|
+
secret_access_key: @options.s3_secret_access_key},
|
64
|
+
@options.s3_target_bucket)
|
65
|
+
post.update(media: post.media.map do |m|
|
66
|
+
puts "Uploading #{m.name}...".green
|
67
|
+
remote_path = post.published.strftime "%Y/%m/"
|
68
|
+
up.upload (m.update remote_path: remote_path)
|
69
|
+
m.update remote_path: "http://#{@options.s3_target_bucket}/#{remote_path}"
|
70
|
+
end
|
71
|
+
)
|
72
|
+
end
|
93
73
|
|
94
|
-
|
95
|
-
|
96
|
-
|
74
|
+
def build_landing
|
75
|
+
all_paths = []
|
76
|
+
@input_extensions.each do |input_extension|
|
77
|
+
Pathname.glob(@posts_path + "*#{input_extension}") { | path | all_paths << path }
|
78
|
+
end
|
79
|
+
most_recent = all_paths.sort.last
|
80
|
+
most_recent_extension = most_recent.extname
|
81
|
+
the_post = Post.from_file most_recent, most_recent_extension
|
82
|
+
landing_template = Pathname.new('lib/templates/landing_page.html')
|
83
|
+
output = landing_template.open('r').read
|
84
|
+
output.gsub!(/\#\{title\}/, the_post.title)
|
85
|
+
output.gsub!(/\#\{summary\}/, the_post.summary)
|
86
|
+
link = "#{the_post.published.strftime "%Y/%m/"}#{the_post.guid}.html"
|
87
|
+
output.gsub!(/\#\{link\}/, link)
|
88
|
+
(@destination + "index.html").open('w:UTF-8') {|f| f.write(output) }
|
89
|
+
puts "Rendering #{@destination}index.html".blue
|
90
|
+
end
|
97
91
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
@the_index.write_main_page
|
102
|
-
end
|
92
|
+
def build_monthly
|
93
|
+
@the_writer.write_monthly_archives
|
94
|
+
end
|
103
95
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
end
|
96
|
+
def build_yearly
|
97
|
+
@the_writer.write_yearly_archives
|
98
|
+
end
|
108
99
|
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
@the_index = Index.new @posts_path, @destination, @template, @override
|
113
|
-
@the_index.find_all_posts
|
114
|
-
@options.build_targets.each { |k, v| send "build_#{k}" if v }
|
100
|
+
def build_tag
|
101
|
+
@the_writer.write_tag_archives
|
102
|
+
@the_writer.write_tag_cloud
|
115
103
|
end
|
116
|
-
|
104
|
+
|
105
|
+
def build_individual
|
106
|
+
@the_writer.write_individual_posts
|
107
|
+
end
|
108
|
+
|
109
|
+
def build_index
|
110
|
+
@the_writer.write_main_page
|
111
|
+
end
|
112
|
+
|
113
|
+
def build_rss
|
114
|
+
@the_writer.write_rss
|
115
|
+
end
|
116
|
+
|
117
117
|
end
|
118
118
|
end
|
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative '../carmine_contraption'
|
2
|
+
|
3
|
+
module CarmineContraption
|
4
|
+
class Writer
|
5
|
+
def initialize the_index, override
|
6
|
+
@index = the_index
|
7
|
+
@templater = Formatter.new(the_index.base + "templates")
|
8
|
+
@formatter = Formatter.new(the_index.base + "formats")
|
9
|
+
@override = override
|
10
|
+
end
|
11
|
+
|
12
|
+
def write_rss
|
13
|
+
puts "Updating RSS feed".blue
|
14
|
+
content = format_post_collection all_posts.reverse, :rss
|
15
|
+
feed_path = destination + "rss.xml"
|
16
|
+
write_file feed_path, content, false, :rss
|
17
|
+
end
|
18
|
+
|
19
|
+
def write_individual_posts
|
20
|
+
all_posts.each do |the_post|
|
21
|
+
path = post_path the_post
|
22
|
+
write_file path, the_post.formatted_output(@formatter) unless ( path.exist? and !@override )
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def write_main_page
|
27
|
+
content = ""
|
28
|
+
content << format_post_collection(all_posts[-8..-1].reverse)
|
29
|
+
content << create_archive_links
|
30
|
+
write_file destination + "recent.html", content
|
31
|
+
end
|
32
|
+
|
33
|
+
def write_monthly_archives
|
34
|
+
monthly_archives.each_key do |k|
|
35
|
+
d = build_date_from_key k
|
36
|
+
path = destination + d.year.to_s + d.month.to_s
|
37
|
+
path.mkpath
|
38
|
+
content = format_archive_for_month d, k
|
39
|
+
write_file path+"index.html", content, true
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def write_tag_archives
|
44
|
+
path = destination + "tags"
|
45
|
+
path.mkpath
|
46
|
+
tag_archives.keys.each { |k| write_tag_archive k, path }
|
47
|
+
write_tag_feeds
|
48
|
+
end
|
49
|
+
|
50
|
+
def write_yearly_archives
|
51
|
+
yearly_archives.keys.each do |k|
|
52
|
+
path = destination + k
|
53
|
+
path.mkpath
|
54
|
+
content = archive_heading "ARCHIVE FOR '<emph>#{k}</emph>'"
|
55
|
+
content << format_post_collection(yearly_archives[k])
|
56
|
+
write_file path+"index.html", content, true
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def write_tag_cloud
|
61
|
+
path = destination + "tags" + "index.html"
|
62
|
+
content = archive_heading "TAG_CLOUD"
|
63
|
+
content += "<section class=\"tag_cloud \">\n"
|
64
|
+
max = longest_tag
|
65
|
+
tag_archives.sort_by{|k,v| k}.each do |k, v|
|
66
|
+
content << scale_tag(k, v.length, max)
|
67
|
+
end
|
68
|
+
content += "</section>\n"
|
69
|
+
write_file path, content
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
def method_missing method, *args, &block
|
74
|
+
return @index.send method, *args, &block if @index.respond_to? method
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
def post_path the_post
|
79
|
+
path = destination + the_post.published.strftime("%Y") + the_post.published.strftime("%m")
|
80
|
+
path.mkpath
|
81
|
+
path += "#{the_post.guid}.html"
|
82
|
+
end
|
83
|
+
|
84
|
+
def format_post_collection posts, formatting_method=nil
|
85
|
+
posts.inject("") { |c, post| c << post.formatted_output(@formatter, formatting_method) }
|
86
|
+
end
|
87
|
+
|
88
|
+
def scale_tag k, tag_length, max
|
89
|
+
size = (1+tag_length.to_f/max) * 0.8
|
90
|
+
%Q{<span style="font-size: #{size}em;"><a href="tags/#{k.gsub(' ', '_').downcase}.html">#{k.gsub('_', ' ')}</a></span>\n}
|
91
|
+
end
|
92
|
+
|
93
|
+
def write_tag_archive k, path
|
94
|
+
content = archive_heading "ARCHIVE FOR '<emph>#{k.upcase.gsub '_', ' ' }</emph>'"
|
95
|
+
content << format_post_collection(tag_archives[k].reverse)
|
96
|
+
write_file path + "#{k.gsub(' ', '_').downcase}.html", content, true
|
97
|
+
end
|
98
|
+
|
99
|
+
def write_file file, content='', supress_message=false, method=:default
|
100
|
+
puts "Rendering #{file}".blue unless supress_message
|
101
|
+
file.open('w:UTF-8') { |f| f.write @templater.format(content, method) }
|
102
|
+
end
|
103
|
+
|
104
|
+
def write_tag_feeds
|
105
|
+
rss_base_path = destination+"rss"
|
106
|
+
rss_base_path.mkpath
|
107
|
+
tag_archives.keys.each do |k|
|
108
|
+
content = format_post_collection tag_archives[k].reverse, :rss
|
109
|
+
write_file (rss_base_path + "#{k}.xml"), content, true, :rss
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def create_archive_links
|
114
|
+
content = %Q{<section class="archive_title">\n<h2>Archives</h2>\n<section class="archives">\n}
|
115
|
+
content << monthly_archives.keys.reverse.map do |k|
|
116
|
+
d = build_date_from_key k
|
117
|
+
%Q{<a href="/#{d.year}/#{d.month}/" class="nohover">#{d.strftime "%b"} #{d.year}</a>\n}
|
118
|
+
end .join("\n")
|
119
|
+
content << %Q{</section>\n</section>\n}
|
120
|
+
end
|
121
|
+
|
122
|
+
|
123
|
+
def format_archive_for_month m, k
|
124
|
+
content = archive_heading "ARCHIVE FOR '<emph>#{m.strftime "%^b"} #{m.year}</emph>'"
|
125
|
+
content << format_post_collection(monthly_archives[k])
|
126
|
+
end
|
127
|
+
|
128
|
+
def archive_heading text
|
129
|
+
"<h1 class=\"archive\">#{text}</h1>\n<hr class=\"subtleSep\" />\n"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
module CarmineContraption
|
4
|
+
class TestError < Test::Unit::TestCase
|
5
|
+
context "Error Hierarchy" do
|
6
|
+
should "be subclasssed from standard error" do
|
7
|
+
assert Error.ancestors.include? StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
should "be namespaced" do
|
11
|
+
assert Error.name.include? "::"
|
12
|
+
end
|
13
|
+
|
14
|
+
should "have CarmineContraption namespace" do
|
15
|
+
assert_equal Error.name.to_s.split("::").first, "CarmineContraption"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require_relative '../../test_helper'
|
2
|
+
|
3
|
+
module CarmineContraption
|
4
|
+
class TestFormatter < Test::Unit::TestCase
|
5
|
+
context "basics" do
|
6
|
+
should "exist" do
|
7
|
+
assert_not_nil Formatter.new
|
8
|
+
end
|
9
|
+
|
10
|
+
should "find formats" do
|
11
|
+
f = Formatter.new "test/data/formats"
|
12
|
+
f.formats.each do |t|
|
13
|
+
assert [:raw].include?(t), "#{t} not expected"
|
14
|
+
end
|
15
|
+
assert_equal 1, f.formats.size
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "format method" do
|
20
|
+
setup do
|
21
|
+
@f = Formatter.new "test/data/formats"
|
22
|
+
path = 'test/data/posts/20121209-test.md'
|
23
|
+
@post = Post.from_file(path)
|
24
|
+
@post_content = IO.read(path)
|
25
|
+
end
|
26
|
+
|
27
|
+
should "raise error about missing format" do
|
28
|
+
assert_raise MissingFormat do
|
29
|
+
@f.format @post, :a
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
should "not raise error when given valid format" do
|
34
|
+
assert_nothing_raised MissingFormat do
|
35
|
+
@f.format @post, :raw
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
should "return formatted post" do
|
40
|
+
expected = "Testing... Testing...\n\n"
|
41
|
+
expected << "======================\n"
|
42
|
+
expected << "Type: Article\n\n"
|
43
|
+
expected << "Published: 2012-12-09T19:37:58-05:00\n"
|
44
|
+
expected << "Tags: test\n"
|
45
|
+
expected << "Summary: Testing.\n\n\n"
|
46
|
+
expected << "Testing whether the markdown support works.\n\n\n\n"
|
47
|
+
expected << "Code sample\n\n"
|
48
|
+
expected << " #include<iostream>\n\n\n\n"
|
49
|
+
expected << " int main(){\n\n"
|
50
|
+
expected << " std::cout << \"Hello World.\\n\";\n\n"
|
51
|
+
expected << " return 0;\n\n"
|
52
|
+
expected << " }\n\n\n\n"
|
53
|
+
expected << "Some __bold text__.\n\n"
|
54
|
+
assert_equal expected, @f.format(@post, :raw)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
end
|