CarmineContraption 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,4 @@
1
- require_relative 'options'
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 check_for_new_posts
22
- @input_extensions.each do |input_extension|
23
- Pathname.glob(@drafts_path + "*#{input_extension}") do |i|
24
- post = ''
25
- posted_filename = i.basename
26
- move_completed_draft = false
27
- File.foreach(i) do |line|
28
- if line.downcase =~ /publish-now/
29
- move_completed_draft = true
30
- t = Time.now
31
- post += 'Published: ' + t.strftime("%a, %d %b %Y %H:%M:%S %Z") + "\n"
32
- posted_filename = "#{t.strftime('%Y%m%d')}-#{posted_filename}"
33
- else
34
- post += line
35
- end
36
- end
37
- if move_completed_draft
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
- def build_landing
61
- all_paths = []
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
- def build_monthly
79
- @the_index.build_monthly_archives
80
- @the_index.write_monthly_archives
81
- end
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
- def build_yearly
84
- @the_index.build_yearly_archives
85
- @the_index.write_yearly_archives
86
- end
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
- def build_tag
89
- @the_index.build_tag_archives
90
- @the_index.write_tag_archives
91
- @the_index.write_tag_cloud
92
- end
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
- def build_individual
95
- @the_index.write_individual_posts
96
- end
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
- def build_index
99
- @the_index.build_yearly_archives
100
- @the_index.build_monthly_archives
101
- @the_index.write_main_page
102
- end
92
+ def build_monthly
93
+ @the_writer.write_monthly_archives
94
+ end
103
95
 
104
- def build_rss
105
- @the_index.build_tag_archives
106
- @the_index.write_rss
107
- end
96
+ def build_yearly
97
+ @the_writer.write_yearly_archives
98
+ end
108
99
 
109
- def run
110
- check_for_new_posts if @options.new_post_check
111
- if @options.build_targets.has_value?(true)
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
- end
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
@@ -1,3 +1,3 @@
1
1
  module CarmineContraption
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  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,8 @@
1
+ <%= context.title %>
2
+ <%= "="*context.title.length %>
3
+ Type: <%= context.type %>
4
+ Published: <%= context.published %>
5
+ Tags: <%= context.tags.join ', ' %>
6
+ Summary: <%= context.summary %>
7
+
8
+ <%= context.content %>
@@ -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
@@ -0,0 +1,4 @@
1
+ require_relative '../../test_helper'
2
+
3
+ class TestIndex < Test::Unit::TestCase
4
+ end
@@ -1,6 +1,4 @@
1
- require 'test/unit'
2
- require 'shoulda'
3
- require_relative '../lib/carmine_contraption/media'
1
+ require_relative '../../test_helper'
4
2
 
5
3
  module CarmineContraption
6
4
  class TestMedia < Test::Unit::TestCase
@@ -1,7 +1,4 @@
1
- require 'test/unit'
2
- require 'shoulda'
3
- require 'pathname'
4
- require_relative '../lib/carmine_contraption/options'
1
+ require_relative '../../test_helper'
5
2
 
6
3
  class TestOptions < Test::Unit::TestCase
7
4