CarmineContraption 0.1.0 → 0.1.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.
- 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
|