clayoven 0.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,24 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $:.unshift File.join(File.dirname(__FILE__), *%w{ .. lib })
4
+
5
+ require 'clayoven'
6
+
7
+ case ARGV[0]
8
+ when "httpd"
9
+ Httpd.start
10
+ when "imapd"
11
+ while 1
12
+ mails = Imapd.poll
13
+ if not mails.empty?
14
+ Core.main
15
+ mails.each { |mail|
16
+ `git add .`
17
+ puts `git commit -a -m "#{mail.filename}: new post\n\n#{mail.date}\n#{mail.msgid}"`
18
+ }
19
+ end
20
+ sleep 1800
21
+ end
22
+ else
23
+ Core.main
24
+ end
@@ -0,0 +1,106 @@
1
+ $:.unshift File.dirname(__FILE__)
2
+
3
+ require 'slim'
4
+ require 'clayoven/config'
5
+ require 'clayoven/claytext'
6
+ require 'clayoven/httpd'
7
+ require 'clayoven/imapd'
8
+
9
+ def when_introduced(filename)
10
+ timestamp = `git log --reverse --pretty="%at" #{filename} 2>/dev/null | head -n 1`.strip
11
+ if timestamp == ""
12
+ Time.now
13
+ else
14
+ Time.at(timestamp.to_i)
15
+ end
16
+ end
17
+
18
+ module Core
19
+ class Page
20
+ attr_accessor :filename, :permalink, :timestamp, :title, :topic, :body,
21
+ :paragraphs, :target, :indexfill, :topics
22
+
23
+ def render(topics)
24
+ @topics = topics
25
+ @paragraphs = ClayText.process! @body
26
+ Slim::Engine.set_default_options pretty: true, sort_attrs: false
27
+ rendered = Slim::Template.new { IO.read("design/template.slim") }.render(self)
28
+ File.open(@target, mode="w") { |targetio|
29
+ nbytes = targetio.write(rendered)
30
+ puts "[GEN] #{@target} (#{nbytes} bytes out)"
31
+ }
32
+ end
33
+ end
34
+
35
+ class IndexPage < Page
36
+ def initialize(filename)
37
+ @filename = filename
38
+ if @filename == "index"
39
+ @permalink = @filename
40
+ else
41
+ @permalink = filename.split(".index")[0]
42
+ end
43
+ @topic = @permalink
44
+ @target = "#{@permalink}.html"
45
+ @timestamp = when_introduced @filename
46
+ end
47
+ end
48
+
49
+ class ContentPage < Page
50
+ attr_accessor :pub_date
51
+
52
+ def initialize(filename)
53
+ @filename = filename
54
+ @topic, @permalink = @filename.split(":", 2)
55
+ @target = "#{@permalink}.html"
56
+ @indexfill = nil
57
+ @timestamp = when_introduced @filename
58
+ end
59
+ end
60
+
61
+ def self.main
62
+ if not File.exists? "index"
63
+ puts "error: index file not found; aborting"
64
+ exit 1
65
+ end
66
+
67
+ config = ConfigData.new
68
+ all_files = (Dir.entries(".") -
69
+ [".", "..", ".clayoven", "design"]).reject { |entry|
70
+ config.ignore.any? { |pattern| %r{#{pattern}} =~ entry }
71
+ }
72
+
73
+ if not Dir.entries("design").include? "template.slim"
74
+ puts "error: design/template.slim file not found; aborting"
75
+ exit 1
76
+ end
77
+
78
+ index_files = ["index"] + all_files.select { |file| /\.index$/ =~ file }
79
+ content_files = all_files - index_files
80
+ topics = index_files.map { |file| file.split(".index")[0] }.uniq
81
+
82
+ # Next, look for stray files
83
+ (content_files.reject { |file| topics.include? (file.split(":", 2)[0]) })
84
+ .each { |stray_entry|
85
+ content_files = content_files - [stray_entry]
86
+ puts "warning: #{stray_entry} is a stray file or directory; ignored"
87
+ }
88
+
89
+ index_pages = index_files.map { |filename| IndexPage.new(filename) }
90
+ content_pages = content_files.map { |filename| ContentPage.new(filename) }
91
+
92
+ # First, fill in all the page attributes
93
+ (index_pages + content_pages).each { |page|
94
+ page.title, page.body = (IO.read page.filename).split("\n\n", 2)
95
+ }
96
+
97
+ # Compute the indexfill for indexes
98
+ topics.each { |topic|
99
+ topic_index = index_pages.select { |page| page.topic == topic }[0]
100
+ topic_index.indexfill = content_pages.select { |page|
101
+ page.topic == topic }.sort { |a, b| b.timestamp <=> a.timestamp }
102
+ }
103
+
104
+ (index_pages + content_pages).each { |page| page.render topics }
105
+ end
106
+ end
@@ -0,0 +1,80 @@
1
+ class Paragraph
2
+ attr_accessor :content, :first, :type
3
+
4
+ def initialize(content)
5
+ @content = content
6
+ @first = false
7
+ @type = :plain
8
+ end
9
+
10
+ def is_first?
11
+ @first
12
+ end
13
+ end
14
+
15
+ module ClayText
16
+ def self.mark_emailquote!(paragraph)
17
+ paragraph.type = :emailquote
18
+ end
19
+
20
+ def self.mark_codeblock!(paragraph)
21
+ paragraph.type = :codeblock
22
+ end
23
+
24
+ def self.anchor_footerlinks!(paragraph)
25
+ paragraph.content.gsub!(%r{^(\[\d+\]:) (.*://(.*))}) {
26
+ "#{$1} <a href=\"#{$2}\">#{$3[0, 64]}#{%{...} if $3.length > 67}</a>"
27
+ }
28
+ paragraph.type = :footer
29
+ end
30
+
31
+ def self.process!(body)
32
+ htmlescape_rules = {
33
+ "&" => "&amp;",
34
+ "\"" => "&quot;",
35
+ "'" => "&#39;",
36
+ "<" => "&lt;",
37
+ ">" => "&gt;"
38
+ }.freeze
39
+
40
+ paragraph_types = [:plain, :emailquote, :codeblock, :header, :footer]
41
+ paragraph_rules = {
42
+ Proc.new { |line| line.start_with? "&gt; " } => method(:mark_emailquote!),
43
+ Proc.new { |line| line.start_with? " " } => method(:mark_codeblock!),
44
+ Proc.new { |line| /^\[\d+\]: / =~ line } => method(:anchor_footerlinks!)
45
+ }.freeze
46
+
47
+ # First, htmlescape the body text
48
+ body.gsub!(/[&"'<>]/, htmlescape_rules)
49
+
50
+ paragraphs = []
51
+ body.split("\n\n").each { |content|
52
+ paragraphs << Paragraph.new(content)
53
+ }
54
+
55
+ paragraphs[0].first = true
56
+ if paragraphs[0].content.start_with? "(" and
57
+ paragraphs[0].content.end_with? ")"
58
+ paragraphs[0].type = :header
59
+ end
60
+
61
+ # Paragraph-level processing
62
+ paragraphs.each { |paragraph|
63
+ paragraph_rules.each { |proc_match, callback|
64
+ if paragraph.content.lines.all? &proc_match
65
+ callback.call paragraph
66
+ end
67
+ }
68
+ }
69
+
70
+ # Generate is_*? methods for Paragraph
71
+ Paragraph.class_eval {
72
+ paragraph_types.each { |type|
73
+ define_method("is_#{type.to_s}?") { @type == type }
74
+ }
75
+ }
76
+
77
+ body = paragraphs.map(&:content).join("\n\n")
78
+ paragraphs
79
+ end
80
+ end
@@ -0,0 +1,24 @@
1
+ require 'yaml'
2
+
3
+ class ConfigData
4
+ attr_accessor :rootpath, :rcpath, :ignorepath, :rc, :ignore
5
+
6
+ def initialize
7
+ @rootpath = ".clayoven"
8
+ @rcpath = File.expand_path "~/.clayovenrc"
9
+ @ignorepath = "#{rootpath}/ignore"
10
+ @ignore = ["\\.html$", "~$", "^.\#", "^\#.*\#$",
11
+ "^\\.git$", "^\\.gitignore$", "^\\.htaccess$"]
12
+ @rc = nil
13
+
14
+ Dir.mkdir @rootpath if not Dir.exists? @rootpath
15
+ if File.exists? @ignorepath
16
+ @ignore = IO.read(@ignorepath).split("\n")
17
+ else
18
+ File.open(@ignorepath, "w") { |ignoreio|
19
+ ignoreio.write @ignore.join("\n") }
20
+ puts "[NOTE] #{@ignorepath} populated with sane defaults"
21
+ end
22
+ @rc = YAML.load_file @rcpath if File.exists? @rcpath
23
+ end
24
+ end
@@ -0,0 +1,24 @@
1
+ require 'webrick'
2
+
3
+ module Httpd
4
+ def self.start
5
+ port = 8000
6
+ callback = Proc.new { |req, res|
7
+ if %r{^/$} =~ req.path_info
8
+ res.set_redirect WEBrick::HTTPStatus::Found, "index.html"
9
+ end
10
+ if %r{^/([^.]*)$} =~ req.path_info
11
+ res.set_redirect WEBrick::HTTPStatus::Found, "#{$1}.html"
12
+ end
13
+ }
14
+
15
+ server = WEBrick::HTTPServer.new(:Port => port,
16
+ :RequestCallback => callback,
17
+ :DocumentRoot => Dir.pwd)
18
+
19
+ puts "clayoven serving at: http://localhost:#{port}"
20
+
21
+ trap(:INT) { server.shutdown }
22
+ server.start
23
+ end
24
+ end
@@ -0,0 +1,46 @@
1
+ require 'net/imap'
2
+ require_relative 'config'
3
+
4
+ class Mail
5
+ attr_accessor :filename, :date, :msgid
6
+
7
+ def initialize(filename, date, msgid)
8
+ @filename = filename
9
+ @date = date
10
+ @msgid = msgid
11
+ end
12
+ end
13
+
14
+ module Imapd
15
+ def self.poll
16
+ config = ConfigData.new
17
+ if not config.rc
18
+ puts "error: #{config.rcpath} not found; aborting"
19
+ exit 1
20
+ end
21
+ mails = []
22
+ server = Net::IMAP.new(config.rc["server"],
23
+ {:port => config.rc["port"], :ssl => config.rc["ssl"]})
24
+ trap(:INT) { exit 1 }
25
+ server.login config.rc["username"], config.rc["password"]
26
+ puts "[NOTE] LOGIN successful"
27
+ server.examine "INBOX"
28
+ server.search(["ALL"]).each { |id|
29
+ message = server.fetch(id, ["ENVELOPE", "RFC822.TEXT"])[0]
30
+ if message.attr["ENVELOPE"].sender[0].mailbox == "artagnon" and
31
+ message.attr["ENVELOPE"].sender[0].host == "gmail.com" and
32
+ message.attr["ENVELOPE"].sender[0].name == "Ramkumar Ramachandra"
33
+ date = message.attr["ENVELOPE"].date
34
+ msgid = message.attr["ENVELOPE"].message_id
35
+ title, filename = message.attr["ENVELOPE"].subject.split(" # ")
36
+ next if File.exists? filename
37
+ File.open(filename, "w") { |targetio|
38
+ targetio.write([title, message.attr["RFC822.TEXT"].delete("\r")].join "\n\n")
39
+ }
40
+ mails << Mail.new(filename, date, msgid)
41
+ end
42
+ }
43
+ server.disconnect
44
+ mails
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,68 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clayoven
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Ramkumar Ramachandra
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-17 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: slim
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.3.6
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.3.6
30
+ description: clayoven is a modern website generator with a traditional design
31
+ email: artagnon@gmail.com
32
+ executables:
33
+ - clayoven
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/clayoven.rb
38
+ - lib/clayoven/config.rb
39
+ - lib/clayoven/claytext.rb
40
+ - lib/clayoven/httpd.rb
41
+ - lib/clayoven/imapd.rb
42
+ - bin/clayoven
43
+ homepage: https://github.com/artagnon/clayoven
44
+ licenses:
45
+ - MIT
46
+ post_install_message: clayoven installed! Run `clayoven` or `clayoven (httpd|imapd)`
47
+ rdoc_options: []
48
+ require_paths:
49
+ - lib
50
+ required_ruby_version: !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 1.9.2
56
+ required_rubygems_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ requirements: []
63
+ rubyforge_project: clayoven
64
+ rubygems_version: 1.8.23
65
+ signing_key:
66
+ specification_version: 3
67
+ summary: Modern website generator with a traditional design
68
+ test_files: []