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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 20a2c70d35267d05960e810047bdef045d63b903
4
+ data.tar.gz: 553ea900ca9ae8dc4010f33173c2cca0f90b81dd
5
+ SHA512:
6
+ metadata.gz: 059a83a1e06aa97cc8ab2c0bd9e6021f5d5ccc4f6f94088bf75fe3bbe38dea738c5920aab2c1189f550c902e2e19f0e69dde5980a3e483e2a7cfc8aecfb572d7
7
+ data.tar.gz: f6e16bea89780b765c77e7702a09cdf45f75aa677c9a1b545168c2e94db3834ddeeb806b26f93f3c3d50f502952fe4af4e122e5a92e6d70e0111ae8dec0f3c32
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - "1.9.3"
4
+ - "2.0.0"
5
+ script bundle exec rake
data/Rakefile CHANGED
@@ -1,13 +1,11 @@
1
- require 'pathname'
2
- require 'colorize'
1
+ require 'rake/testtask'
3
2
 
4
3
  task default: [:test]
5
4
 
6
- task :test do
7
- Pathname.glob("test/test_*.rb") do |path|
8
- puts "\nExecuting #{path.basename.to_s.magenta}"
9
- ruby path
10
- end
5
+ Rake::TestTask.new do |t|
6
+ t.libs << 'lib/carmine_contraption'
7
+ t.test_files = FileList['test/lib/carmine_contraption/test_*.rb']
8
+ t.verbose = true
11
9
  end
12
10
 
13
11
  task :build_gem do
data/Readme.md CHANGED
@@ -18,7 +18,7 @@ Currently, Carmine Contraption requires ssh access to a web server and a Git rep
18
18
  I have published Carmine Contraption as a [gem on RubyGems.org](https://rubygems.org/gems/CarmineContraption). Just use `gem install CarmineContraption` to test it out.
19
19
 
20
20
  ### Content Folder Structure
21
- This is the tree that you write in. More specifically, the drafts folder. `content` is known as `source` within the bowels of Carmine Contraption.
21
+ This is the tree that you write in. More specifically, the drafts folder. `content` is known as `source` within the bowels of Carmine Contraption. `formats` and `templates` contain layout and styling for posts. `formats` contain html templates for displaying individual posts. `templates` contain templates for displaying collections of posts.
22
22
 
23
23
  content
24
24
  |
@@ -28,11 +28,29 @@ This is the tree that you write in. More specifically, the drafts folder. `conte
28
28
  | |
29
29
  | |-- another_draft.md
30
30
  |
31
+ |-- formats
32
+ | |
33
+ | |-- article.html.erb
34
+ | |
35
+ | |-- link.html.erb
36
+ | |
37
+ | |-- raw.md.erb
38
+ | |
39
+ | |-- rss.xml.erb
40
+ |
31
41
  |-- posts
32
- |
33
- |-- 20120721-on-metaprogramming.html
34
- |
35
- |-- 20120801-example-post.md
42
+ | |
43
+ | |-- 20120721-on-metaprogramming.html
44
+ | |
45
+ | |-- 20120801-example-post.md
46
+ |
47
+ |-- templates
48
+ |
49
+ |-- default.html.erb
50
+ |
51
+ |-- landing_page.html.erb
52
+ |
53
+ |-- rss.xml.erb
36
54
 
37
55
  ### Server Folder Structure
38
56
  This layout is the living, breathing incarnation of your content. `www` is known as `destination` to Carmine Contraption. I have only listed the directories that Carmine Contraption will generate and modify. Other useful directories you may consider including are `css` and `js`.
@@ -79,13 +97,19 @@ The header tags can be placed in any order. Once Carmine Contraption encounters
79
97
 
80
98
  To mark a post as ready to publish add `publish-now` as a line anywhere in the file. This line will be replaced with the Publication timestamp.
81
99
 
100
+ ### Formatters and Templates
101
+ Formatters and templates are very similar. Both are [ERB](http://ruby-doc.org/stdlib-1.9.3/libdoc/erb/rdoc/ERB.html) files. Both are given one variable for parsing, `context`. The main distinction is the expected use case. Formatters are meant for styling/laying out a single post, while templates represent the overall structure of the website. To handle the different purposes the contents of `context` are slightly different: formatters are given an instance of `CarmineContraption::Post`, while templates are passed a string containing a collection of formatted posts.
102
+
82
103
  ## Contributing
83
- ### Build Status
84
- All statuses provided by [Gitlab CI](https://github.com/gitlabhq/gitlab-ci).
104
+ To suggest a feature or report a bug create a [new issue](https://github.com/rampantmonkey/carmine_contraption/issues/new).
105
+
106
+ Pull requests will also be considered.
107
+
108
+ ### Status
85
109
 
86
- - Master &rarr; ![master](http://ci.rampantmonkey.com/projects/1/status?ref=master)
87
- - Dev &rarr; ![dev](http://ci.rampantmonkey.com/projects/1/status?ref=dev)
88
- - Testing &rarr; ![testing](http://ci.rampantmonkey.com/projects/1/status?ref=testing)
110
+ [![Build Status](https://travis-ci.org/rampantmonkey/carmine_contraption.png?branch=master)](https://travis-ci.org/rampantmonkey/carmine_contraption)
111
+ [![Code Climate](https://codeclimate.com/github/rampantmonkey/carmine_contraption.png)](https://codeclimate.com/github/rampantmonkey/carmine_contraption)
112
+ [![Gem Version](https://badge.fury.io/rb/CarmineContraption.png)](http://badge.fury.io/rb/CarmineContraption)
89
113
 
90
114
  ## License
91
115
  Carmine Contraption is licensed under [The MIT License](http://opensource.org/licenses/MIT).
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.platform = Gem::Platform::RUBY
9
9
  s.authors = ["Casey Robinson"]
10
10
  s.email = ["kc@rampantmonkey.com"]
11
- s.homepage = ""
11
+ s.homepage = "https://github.com/rampantmonkey/carmine_contraption"
12
12
  s.summary = "Static Website Generator"
13
13
  s.description = "Longer and more detailed version of summary..."
14
14
 
@@ -0,0 +1,34 @@
1
+ require 'carmine_contraption/black_hole.rb'
2
+ require 'carmine_contraption/error.rb'
3
+ require 'carmine_contraption/formatter.rb'
4
+ require 'carmine_contraption/index.rb'
5
+ require 'carmine_contraption/media.rb'
6
+ require 'carmine_contraption/options.rb'
7
+ require 'carmine_contraption/post.rb'
8
+ require 'carmine_contraption/runner.rb'
9
+ require 'carmine_contraption/uploader.rb'
10
+ require 'carmine_contraption/version.rb'
11
+ require 'carmine_contraption/writer.rb'
12
+
13
+ require 'yaml'
14
+
15
+ module CarmineContraption
16
+ def Maybe value
17
+ value.nil? ? BlackHole.new : value
18
+ end
19
+
20
+ at_exit do
21
+ if $!
22
+ puts "Something bad happened. See `/tmp/crash.log` for details"
23
+ open('/tmp/crash.log', 'a') do |log|
24
+ error = { timestamp: Time.now,
25
+ message: $!.message,
26
+ backtrace: $!.backtrace
27
+ }
28
+ YAML.dump error, log
29
+ end
30
+ exit!
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,12 @@
1
+ require_relative '../carmine_contraption'
2
+
3
+ module CarmineContraption
4
+ class BlackHole
5
+ def method_missing *args, &block
6
+ self
7
+ end
8
+
9
+ def nil?; true; end
10
+ end
11
+ end
12
+
@@ -0,0 +1,5 @@
1
+ module CarmineContraption
2
+ class Error < StandardError; end
3
+
4
+ class MissingFormat < Error; end
5
+ end
@@ -0,0 +1,34 @@
1
+ require 'time'
2
+ require 'erb'
3
+
4
+ module CarmineContraption
5
+ class Formatter
6
+ attr_reader :directory
7
+
8
+ def initialize formatter_directory='./'
9
+ @directory = Pathname.new(formatter_directory).expand_path
10
+ find_formats
11
+ end
12
+
13
+ def format context, method
14
+ renderer = ERB.new formats_hash.fetch(method) { raise MissingFormat, "formatter #{method} not found" }.read
15
+ renderer.result binding
16
+ end
17
+
18
+ def formats
19
+ @formats.keys
20
+ end
21
+
22
+ private
23
+ def find_formats
24
+ tmp = {}
25
+ Pathname.glob(@directory+"*") { |path| tmp[path.basename.to_s.split('.')[0].to_sym] = path }
26
+ @formats = tmp
27
+ end
28
+
29
+ def formats_hash
30
+ @formats
31
+ end
32
+
33
+ end
34
+ end
@@ -1,141 +1,82 @@
1
- require_relative 'month_transform.rb'
2
- require_relative 'post'
1
+ require_relative '../carmine_contraption'
3
2
  require 'pathname'
4
3
  require 'time'
5
4
  require 'erb'
6
5
  require 'colorize'
6
+ require 'date'
7
7
 
8
8
  module CarmineContraption
9
9
  class Index
10
- include MonthTransform
11
- attr_reader :yearly_archives, :monthly_archives, :tag_archives
10
+ attr_reader :base, :destination, :all_posts
12
11
 
13
- def initialize source, destination, template, override=false
14
- @source = source
12
+ def initialize base_path, destination
13
+ @base = base_path
14
+ @source = base_path + "posts"
15
15
  @destination = destination
16
- raise "Template not found." unless template.file?
17
- @template = template
18
- @override = override
16
+ find_all_posts
19
17
  end
20
18
 
21
- def build_monthly_archives
22
- @monthly_archives = {}
23
- collect_archives { |the_post| (@monthly_archives[the_post.year + the_post.month] ||=[]) << the_post}
19
+ def yearly_archives
20
+ build_yearly_archives unless @yearly
21
+ @yearly
24
22
  end
25
23
 
26
- def build_tag_archives
27
- @tag_archives = {}
28
- collect_archives { |the_post| the_post.tags.each { |t| (@tag_archives[t.gsub(' ', '_').downcase] ||=[]) << the_post } }
24
+ def monthly_archives
25
+ build_monthly_archives unless @monthly
26
+ @monthly
29
27
  end
30
28
 
31
- def build_yearly_archives
32
- @yearly_archives = {}
33
- collect_archives { |the_post| (@yearly_archives[the_post.year] ||= []) << the_post}
29
+ def tag_archives
30
+ build_tag_archives unless @tagly
31
+ @tagly
34
32
  end
35
33
 
36
- def collect_archives
37
- @all_posts.each do |a|
38
- yield a
39
- end
40
- end
41
-
42
- def find_all_posts
43
- @all_paths = []
44
- Pathname.glob(@source+"*.html") { |path| @all_paths << path}
45
- Pathname.glob(@source+"*.md") { |path| @all_paths << path}
46
- @all_posts = []
47
- @all_paths.each do |path|
48
- @all_posts << Post.from_file(path, path.extname)
49
- end
50
- @all_posts.sort!
34
+ def build_date_from_key k
35
+ d = Date.new k[0..3].to_i, k[4..5].to_i
51
36
  end
52
37
 
53
- def write_file file, content='', supress_message=false
54
- puts "Rendering #{file}".blue unless supress_message
55
- renderer = ERB.new(@template.read)
56
- file.open('w:UTF-8') { |f| f.write renderer.result(binding) }
38
+ def longest_tag
39
+ find_length_of_most_used_tag
57
40
  end
58
41
 
59
- def write_individual_posts
60
- @all_posts.each do |the_post|
61
- path = @destination + the_post.year + the_post.month
62
- path.mkpath
63
- path += "#{the_post.guid}.html"
64
- write_file path, the_post.formatted_output unless ( path.exist? and !@override )
42
+ private
43
+ def build_monthly_archives
44
+ @monthly = {}
45
+ collect_archives { |the_post| (@monthly[the_post.published.strftime "%Y%m"] ||=[]) << the_post}
65
46
  end
66
- end
67
47
 
68
- def write_main_page
69
- content = ""
70
- @all_posts[-8..-1].reverse.each do |the_post|
71
- content << the_post.formatted_output
72
- end
73
- content << %Q{<section class="archive_title">\n<h2>Archives</h2>\n<section class="archives">\n}
74
- @monthly_archives.keys.reverse.each do |k|
75
- content << %Q{<a href="/#{k[0..3]}/#{k[4..5]}/" class="nohover">#{month_alpha_long k[4..5]} #{k[0..3]}</a>\n}
48
+ def build_tag_archives
49
+ @tagly = {}
50
+ collect_archives { |the_post| the_post.tags.each { |t| (@tagly[t.gsub(' ', '_').downcase] ||=[]) << the_post } }
76
51
  end
77
- content << %Q{</section>\n</section>\n}
78
- write_file @destination + "recent.html", content
79
- end
80
52
 
81
- def write_monthly_archives
82
- @monthly_archives.keys.each do |k|
83
- path = @destination + k[0..3] + k[4..5]
84
- path.mkpath
85
- content = "<h1 class=\"archive\">ARCHIVE FOR '<emph>#{(month_alpha_long k[4..5]).upcase} #{k[0..3]}</emph>'</h1>\n<hr class=\"subtleSep\" />"
86
- @monthly_archives[k].each { |the_post| content << the_post.formatted_output }
87
- write_file path+"index.html", content, true
53
+ def build_yearly_archives
54
+ @yearly = {}
55
+ collect_archives { |the_post| (@yearly[the_post.published.strftime "%Y"] ||= []) << the_post}
88
56
  end
89
- end
90
57
 
91
- def write_rss
92
- content = %Q(<?xml version="1.0" encoding="utf-8" ?>\n<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">\n<channel>\n<title>Rampant Monkey</title>\n<link>http://rampantmonkey.com</link>\n<description>Rampant Monkey - Kitchen Sink</description>\n<language>en-us</language>\n<atom:link href="http://rampantmonkey.com/rss.xml" rel="self" type="application/rss+xml" /><lastBuildDate>#{Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z")}</lastBuildDate>\n)
93
- @all_posts.reverse.each { |the_post| content += the_post.formatted_output 'rss' }
94
- content += %Q(</channel>\n</rss>)
95
- feed_path = @destination + "rss.xml"
96
- feed_path.open('w:UTF-8') { |f| f.write(content) }
97
- rss_base_path = @destination+"rss"
98
- rss_base_path.mkpath
99
- @tag_archives.keys.each do |k|
100
- content = %Q(<?xml version="1.0" encoding="utf-8" ?>\n<rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom">\n<channel>\n<title>Rampant Monkey</title>\n<link>http://rampantmonkey.com</link>\n<description>Rampant Monkey - #{k}</description>\n<language>en-us</language>\n<atom:link href="http://rampantmonkey.com/rss/#{k}.xml" rel="self" type="application/rss+xml" /><lastBuildDate>#{Time.now.strftime("%a, %d %b %Y %H:%M:%S %Z")}</lastBuildDate>\n)
101
- @tag_archives[k].reverse.each { |the_post| content += the_post.formatted_output 'rss' }
102
- content += %Q(</channel>\n</rss>)
103
- (rss_base_path + "#{k}.xml").open('w:UTF-8') { |f| f.write(content) }
104
- end
105
- puts "Updating RSS feed".blue
106
- end
107
58
 
108
- def write_tag_archives
109
- path = @destination + "tags"
110
- path.mkpath
111
- @tag_archives.keys.each do |k|
112
- content = "<h1 class=\"archive\">ARCHIVE FOR '<emph>#{k.upcase.gsub('_', ' ')}</emph>' CATEGORY</h1>\n<hr class=\"subtleSep\" />"
113
- @tag_archives[k].reverse.each { |the_post| content << the_post.formatted_output }
114
- write_file path + "#{k.gsub(' ', '_').downcase}.html", content, true
59
+ def find_all_posts
60
+ @all_paths = []
61
+ Pathname.glob(@source+"*.html") { |path| @all_paths << path}
62
+ Pathname.glob(@source+"*.md") { |path| @all_paths << path}
63
+ @all_posts = []
64
+ @all_paths.each do |path|
65
+ @all_posts << Post.from_file(path, path.extname)
66
+ end
67
+ @all_posts.sort!
115
68
  end
116
- end
117
69
 
118
- def write_tag_cloud
119
- path = @destination + "tags" + "index.html"
120
- content = "<h1 class=\"archive\">TAG CLOUD</h1>\n<hr class=\"subtleSep\" />"
121
- content += "<section class=\"tag_cloud \">\n"
122
- max = @tag_archives.max_by { |k, v| v.length }[-1].length
123
- @tag_archives.sort_by{|k,v| k}.each do |k, v|
124
- size = (1+v.length.to_f/max) * 0.8
125
- content += "<span style=\"font-size: #{size}em;\"><a href=\"tags/#{k.gsub(' ', '_').downcase}.html\">#{k.gsub('_', ' ')}</a></span>\n"
70
+
71
+ def collect_archives
72
+ @all_posts.each do |a|
73
+ yield a
74
+ end
126
75
  end
127
- content += "</section>\n"
128
- write_file path, content
129
- end
130
76
 
131
- def write_yearly_archives
132
- @yearly_archives.keys.each do |k|
133
- path = @destination + k
134
- path.mkpath
135
- content = "<h1 class=\"archive\">ARCHIVE FOR '<emph>#{k}</emph>'</h1>\n<hr class=\"subtleSep\" />"
136
- @yearly_archives[k].each { |the_post| content << the_post.formatted_output }
137
- write_file path+"index.html", content, true
77
+ def find_length_of_most_used_tag
78
+ tag_archives.max_by { |k,v| v.length }[-1].length
138
79
  end
139
- end
80
+
140
81
  end
141
82
  end
@@ -15,7 +15,7 @@ module CarmineContraption
15
15
  end
16
16
 
17
17
  def remote_path
18
- values[:remote_path]
18
+ values[:remote_path] + name.to_s
19
19
  end
20
20
 
21
21
  def update args={}
@@ -2,19 +2,14 @@
2
2
 
3
3
  require 'redcarpet'
4
4
  require 'colorize'
5
+ require 'date'
5
6
 
6
- require_relative 'formatters'
7
- require_relative 'month_transform'
8
- require_relative 'media'
7
+ require_relative '../carmine_contraption'
9
8
 
10
9
  module CarmineContraption
11
10
  class Post
12
- attr_reader :title, :content, :extra, :guid, :summary, :published, :status, :type, :tags
13
-
14
- include MonthTransform
15
11
 
16
12
  class << self
17
- include MonthTransform
18
13
 
19
14
  def from_file path, extension=".html"
20
15
  guid = File.basename(path, extension)
@@ -35,46 +30,39 @@ module CarmineContraption
35
30
  if line =~ /^\s/
36
31
  new_post = new_post.update(content: raw_content[(i+1)..-1].join("\n"))
37
32
  break
38
- end
39
- l = line.split ': '
40
- if l.length == 1
41
- new_post = new_post.update(title: (ignore_separator l[0])) unless new_post.title
42
33
  else
43
- case l[0].downcase
44
- when /type/
45
- new_post = new_post.update(type: l[1])
46
- when /published/
47
- new_post = new_post.update published: (convert_date l[1])
48
- when /tags/
49
- new_post = new_post.update tags: (parse_tags l[1])
50
- when /extra/
51
- new_post = new_post.update extra: (rejoin_string l)
52
- when /summary/
53
- new_post = new_post.update summary: (rejoin_string l)
54
- end
34
+ new_post = process_header_line line, new_post
55
35
  end
56
36
  end
57
37
  new_post
58
38
  end
59
39
 
60
- def ignore_separator line
61
- line unless line =~ /^=+/
40
+ def process_header_line line, new_post
41
+ l = line.split ': '
42
+ if l.length == 1
43
+ new_post = new_post.update(title: (ignore_separator l[0])) unless new_post.title
44
+ else
45
+ new_post = new_post.update(header_switch l[0].downcase.to_sym, l)
46
+ end
47
+ new_post
62
48
  end
63
49
 
64
- def convert_date date
65
- if date =~ /^\D/
66
- published = date.split(' ')
67
- tmp = []
68
- tmp[0] = "#{published[3]}-#{month_arabic published[2]}-#{published[1]}"
69
- tmp[1] = published[4]
70
- tmp[2] = '-0400'
71
- tmp[2] = '-0500' if published[-1] == 'EST'
72
- tmp.join(' ')
50
+ def header_switch sym, split_line
51
+ case sym
52
+ when :published
53
+ split_line[1] = split_line[1].gsub(/<\/p>/, '')
54
+ { published: (DateTime.parse split_line[1]) }
55
+ when :tags
56
+ { tags: (parse_tags split_line[1]) }
73
57
  else
74
- date
58
+ { sym => rejoin_string(split_line) }
75
59
  end
76
60
  end
77
61
 
62
+ def ignore_separator line
63
+ line unless line =~ /^=+/
64
+ end
65
+
78
66
  def find_media content, path
79
67
  a = []
80
68
  content.scan(/<img src="([^"]*)"/) do |match|
@@ -114,66 +102,33 @@ module CarmineContraption
114
102
  guid: '',
115
103
  summary: '',
116
104
  published: '',
105
+ path: nil,
117
106
  status: '',
118
107
  type: '',
119
108
  tags: [],
120
109
  media: [],
121
- original_path: ''}
122
- @defaults.merge!(opts)
123
- @title = @defaults[:title]
124
- @content = @defaults[:content]
125
- @extra = @defaults[:extra]
126
- @guid = @defaults[:guid]
127
- @summary = @defaults[:summary]
128
- @published = @defaults[:published]
129
- @status = @defaults[:status]
130
- @type = @defaults[:type]
131
- @tags = @defaults[:tags]
110
+ original_path: ''}.merge!(opts)
132
111
  end
133
112
 
134
- def update opts={}
135
- Post.new(@defaults.merge opts)
136
- end
137
-
138
- def year
139
- @published[0..3]
113
+ def method_missing method, *args, &block
114
+ @defaults.fetch(method) { super }
140
115
  end
141
116
 
142
- def month
143
- @published[5..6]
144
- end
145
-
146
- def day
147
- @published[8..9]
117
+ def update opts={}
118
+ Post.new(@defaults.merge opts)
148
119
  end
149
120
 
150
121
  def <=> other
151
122
  published <=> other.published
152
123
  end
153
124
 
154
- def media
155
- @defaults[:media]
156
- end
157
-
158
- def original_path
159
- @defaults[:original_path]
160
- end
161
-
162
- def formatted_output method='type'
125
+ def formatted_output formatter, method=nil
163
126
  insert_remote_media_links
164
- formatter = CarmineContraption::RawFormatter.new
165
- case method
166
- when 'type'
167
- case @type.downcase
168
- when /article/
169
- formatter = CarmineContraption::ArticleFormatter.new
170
- when /link/
171
- formatter = CarmineContraption::LinkFormatter.new
172
- end
173
- when 'rss'
174
- formatter = CarmineContraption::RssFormatter.new
127
+ if method
128
+ formatter.format self, method
129
+ else
130
+ formatter.format self, type.chomp.downcase.to_sym
175
131
  end
176
- formatter.format_post self
177
132
  end
178
133
 
179
134
  private