clayoven 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []