post-modern 0.5.0

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.
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require 'rake'
2
+ require "rake/rdoctask"
3
+ require 'rake/gempackagetask'
4
+
5
+ spec = Gem::Specification.new do |s|
6
+ s.name = "post-modern"
7
+ s.authors = ["Lance Pollard"]
8
+ s.version = "0.5.0"
9
+ s.summary = "Write Haml in Markdown, Syntax Highlight with Pygments, and Pretty Print the output HTML, in Ruby."
10
+ s.description = "Write Haml in Markdown, Syntax Highlight with Pygments, and Pretty Print the output HTML, in Ruby."
11
+ s.homepage = "http://github.com/viatropos/post-modern"
12
+ s.license = "MIT"
13
+ s.email = "lancejpollard@gmail.com"
14
+ s.rubyforge_project = "post-modern"
15
+ s.platform = Gem::Platform::RUBY
16
+ s.files = %w(Rakefile) + Dir["{lib}/**/*"]
17
+ s.require_path = "lib"
18
+ end
19
+
20
+ Rake::GemPackageTask.new(spec) do |pkg|
21
+ pkg.gem_spec = spec
22
+ end
23
+
24
+ desc 'run unit tests'
25
+ task :test do
26
+ Dir["test/**/*"].each do |file|
27
+ next unless File.basename(file) =~ /test_/
28
+ next unless File.extname(file) == ".rb"
29
+ system "ruby #{file}"
30
+ end
31
+ end
32
+
33
+ desc "Create .gemspec file (useful for github)"
34
+ task :gemspec do
35
+ File.open("#{spec.name}.gemspec", "w") do |f|
36
+ f.puts spec.to_ruby
37
+ end
38
+ end
39
+
40
+ desc "Build the gem into the current directory"
41
+ task :gem => :gemspec do
42
+ `gem build #{spec.name}.gemspec`
43
+ end
44
+
45
+ desc "Publish gem to rubygems"
46
+ task :publish => [:package] do
47
+ %x[gem push #{spec.name}-#{spec.version}.gem]
48
+ end
49
+
50
+ desc "Print a list of the files to be put into the gem"
51
+ task :manifest do
52
+ File.open("Manifest", "w") do |f|
53
+ spec.files.each do |file|
54
+ f.puts file
55
+ end
56
+ end
57
+ end
58
+
59
+ desc "Install the gem locally"
60
+ task :install => [:package] do
61
+ File.mkdir("pkg") unless File.exists?("pkg")
62
+ command = "gem install pkg/#{spec.name}-#{spec.version} --no-ri --no-rdoc"
63
+ command = "sudo #{command}" if ENV["SUDO"] == true
64
+ sh %{#{command}}
65
+ end
66
+
67
+ desc "Generate the rdoc"
68
+ Rake::RDocTask.new do |rdoc|
69
+ files = ["README.md", "lib/**/*.rb"]
70
+ rdoc.rdoc_files.add(files)
71
+ rdoc.main = "README.md"
72
+ rdoc.title = spec.summary
73
+ end
74
+
75
+ task :yank do
76
+ `gem yank #{spec.name} -v #{spec.version}`
77
+ end
@@ -0,0 +1,13 @@
1
+ require 'rubygems'
2
+ require 'active_support'
3
+ require 'active_support/core_ext'
4
+
5
+ $:.unshift File.expand_path("..", __FILE__)
6
+
7
+ require 'post-modern/render'
8
+ require 'post-modern/read'
9
+
10
+ class PostModern
11
+ include PostModern::Read
12
+ include PostModern::Render
13
+ end
@@ -0,0 +1,10 @@
1
+ require 'post-modern'
2
+
3
+ module Haml::Filters::PostModern
4
+ include Haml::Filters::Base
5
+
6
+ def render(text)
7
+ #Redcarpet.new(text, :smart, :autolink, :fenced_code, :hard_wrap, :gh_blockcode).to_html
8
+ ::PostModern.render(text)
9
+ end
10
+ end
@@ -0,0 +1,47 @@
1
+ <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
2
+ <xsl:output method="html" indent="yes" encoding="ISO-8859-1"/>
3
+
4
+ <xsl:param name="indent-increment" select="' '"/>
5
+
6
+ <xsl:template name="newline">
7
+ <xsl:text disable-output-escaping="yes">
8
+ </xsl:text>
9
+ </xsl:template>
10
+
11
+ <xsl:template match="comment() | processing-instruction()">
12
+ <xsl:param name="indent" select="''"/>
13
+ <xsl:call-template name="newline"/>
14
+ <xsl:value-of select="$indent"/>
15
+ <xsl:copy />
16
+ </xsl:template>
17
+
18
+ <xsl:template match="text()">
19
+ <xsl:param name="indent" select="''"/>
20
+ <xsl:call-template name="newline"/>
21
+ <xsl:value-of select="$indent"/>
22
+ <xsl:value-of select="normalize-space(.)"/>
23
+ </xsl:template>
24
+
25
+ <xsl:template match="text()[normalize-space(.)='']"/>
26
+
27
+ <xsl:template match="*">
28
+ <xsl:param name="indent" select="''"/>
29
+ <xsl:call-template name="newline"/>
30
+ <xsl:value-of select="$indent"/>
31
+ <xsl:choose>
32
+ <xsl:when test="count(child::*) > 0">
33
+ <xsl:copy>
34
+ <xsl:copy-of select="@*"/>
35
+ <xsl:apply-templates select="*|text()">
36
+ <xsl:with-param name="indent" select="concat ($indent, $indent-increment)"/>
37
+ </xsl:apply-templates>
38
+ <xsl:call-template name="newline"/>
39
+ <xsl:value-of select="$indent"/>
40
+ </xsl:copy>
41
+ </xsl:when>
42
+ <xsl:otherwise>
43
+ <xsl:copy-of select="."/>
44
+ </xsl:otherwise>
45
+ </xsl:choose>
46
+ </xsl:template>
47
+ </xsl:stylesheet>
@@ -0,0 +1,71 @@
1
+ class PostModern
2
+ module Read
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def root_dir
7
+ @root_dir ||= Rails.root.join("app/documents")
8
+ end
9
+
10
+ def root_dir=(value)
11
+ @root_dir = value
12
+ end
13
+
14
+ def glob(path, &block)
15
+ Dir[path].collect do |file|
16
+ next if File.directory?(file) || file =~ /\.$/
17
+ post = Post.extract_from_file!(file)
18
+ yield post if block_given?
19
+ post
20
+ end.compact
21
+ end
22
+
23
+ def read(file, locals = {})
24
+ match, categories, date, slug, ext = *file.gsub("#{root_dir}/", "").match(/^(.+\/)*(?:(\d+-\d+-\d+)-)?(.*)(\.[^.]+)$/)
25
+ content = IO.read(file)
26
+
27
+ unless locals[:filters].present?
28
+ locals[:filters] = [:haml, :highlight, :pretty]
29
+ else
30
+ locals[:filters] = Array(locals[:filters])
31
+ end
32
+
33
+ if content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
34
+ data = YAML.load($1).symbolize_keys
35
+ content = content[($1.size + $2.size)..-1]
36
+ else
37
+ data = {}
38
+ end
39
+
40
+ data[:published_at] ||= date if date.present?
41
+ data[:format] = extract_format! ext.split(".").last
42
+ data[:title] ||= slug.titleize if slug.present?
43
+ data[:tags] = data[:tags].present? ? data[:tags].split(/,\s*/) : []
44
+ data[:keywords] = data[:keywords].present? ? data[:keywords].split(/,\s*/) : []
45
+ data[:slug] ||= slug
46
+ data[:file] = file.gsub("#{root_dir}/", "")
47
+ [:published_at, :edited_at].each do |date|
48
+ data[date] = Time.parse(data[date].to_s) if data[date].present?
49
+ end
50
+ data[:categories] = categories.split("/").select(&:present?) if categories
51
+ data[:published] = false unless data[:published].present?
52
+ data[:content] = render(content, data.merge(locals))
53
+
54
+ data
55
+ end
56
+
57
+ def extract_format!(ext)
58
+ case ext
59
+ when "md", "markdown", "mdown", "mkdown"
60
+ "markdown"
61
+ else
62
+ ext
63
+ end
64
+ end
65
+ end
66
+
67
+ module InstanceMethods
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,245 @@
1
+ require 'haml'
2
+ require 'albino'
3
+ require 'redcarpet'
4
+ require 'nokogiri'
5
+
6
+ #class Albino
7
+ # def initialize(target, lexer = :text, format = :html, encoding = self.class.default_encoding)
8
+ # @target = target
9
+ # @options = {:l => lexer, :f => format, :O => "encoding=#{encoding},style=colorful,linenos=table,anchorlinenos,lineanchors=LC" }
10
+ # @encoding = encoding
11
+ # end
12
+ #end
13
+
14
+ class PostModern
15
+ module Render
16
+ extend ActiveSupport::Concern
17
+
18
+ module ClassMethods
19
+ def render(content, locals = {})
20
+ # filter haml, scss
21
+ if locals[:filters] && Array(locals[:filters]).include?(:haml)
22
+ content.scan(/@@@ (\w+)\s*(.*)\n@@@/m).each do |lang, code|
23
+ content.gsub!($&, send("filter_#{lang}!", code, locals))
24
+ end
25
+ end
26
+
27
+ # convert to markdown
28
+ content = Redcarpet.new(content, :autolink, :fenced_code, :gh_blockcode).to_html
29
+
30
+ # filter code
31
+ if locals[:filters] && Array(locals[:filters]).include?(:highlight)
32
+ content = filter_code!(content, locals[:filters].include?(:pretty))
33
+ end
34
+
35
+ content
36
+ end
37
+
38
+ # can check prev/next elements to make it even prettier with correct "\n"
39
+ # do that in xslt.
40
+ def pretty_html(html)
41
+ return "" if html.nil?
42
+ raise "will throw segmentation error if not an Nokogiri::XML::Document" unless html.is_a?(Nokogiri::XML::Document)
43
+ pretty_printer = File.join(File.dirname(__FILE__), "pretty-print.xsl")
44
+ xsl = Nokogiri::XSLT(IO.read(pretty_printer))
45
+ xsl.transform(html).css("body").children.map do |child|
46
+ if child["class"].to_s.split(/\s+/).include?("data")
47
+ "\n" + html.css("##{child["id"]}").to_html.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "") + "\n"
48
+ else
49
+ child.to_html.gsub(/\t/, "").gsub(/^ /, "").gsub(/^\n/, "").gsub(/\n? $/, "\n")
50
+ end
51
+ end.join("\n").gsub(/\n+/m, "\n").gsub(/^\n/, "")
52
+ end
53
+
54
+ def pretty_html_with_xslt(html)
55
+ return "" if html.nil?
56
+ raise "will throw segmentation error if not an Nokogiri::XML::Document" unless html.is_a?(Nokogiri::XML::Document)
57
+ html.css("body").children.map do |child|
58
+ if child["class"].to_s.split(/\s+/).include?("data")
59
+ "\n" + html.css("##{child["id"]}").to_html.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "") + "\n"
60
+ else
61
+ child.to_html.gsub(/\t/, "").gsub(/^ /, "").gsub(/^\n/, "").gsub(/\n? $/, "\n")
62
+ end
63
+ end.join("\n").gsub(/\n+/m, "\n").gsub(/^\n/, "")
64
+ end
65
+
66
+ def filter_haml!(code, locals = {})
67
+ Haml::Engine.new(code).render(Object.new, locals).gsub(/^\n/, "").gsub(/\n$/, "")
68
+ end
69
+
70
+ def filter_code!(content, pretty = true)
71
+ html = Nokogiri::HTML(content)#::fragment(content, 'utf-8')
72
+ html.encoding = "utf-8"
73
+
74
+ index = 0
75
+
76
+ # code
77
+ all_lines = 0
78
+ html.css("pre").each do |code|
79
+ index += 1
80
+ liners = ""
81
+ source = ""
82
+ lines = code.text.strip.split("\n")
83
+ language = code["lang"].to_s
84
+ language = language.present? ? language : "text"
85
+ language = case language
86
+ when "yml"
87
+ :yaml
88
+ when "coffee", "coffee-script", "coffeescript"
89
+ :coffeescript
90
+ else # sass, scss, css, rbcon, irb
91
+ language.to_sym
92
+ end
93
+ total_lines = lines.size
94
+
95
+ total_lines.times do |ln|
96
+ liners += "<span id='L#{all_lines + ln+1}' rel='#LC#{all_lines + ln+1}'>#{ln+1}</span>\n"
97
+ end
98
+
99
+ lines.each_with_index do |line, i|
100
+ code_line = line.blank? ? line.to_s : Albino.new(line, language).to_s
101
+ code_line.gsub!(/<div class="highlight"><pre>|\n<\/pre>|\n<\/div>/,'')
102
+ #code_line.gsub!(/^([ ])+/){|m| "&nbsp;"*m.size}
103
+ code_line = "<br/>" if code_line.strip == ''
104
+ source += "<div class='line' id='LC#{all_lines + i+1}'>#{code_line}</div>"
105
+ end
106
+
107
+ all_lines += total_lines
108
+
109
+ liners_block = %{<td>
110
+ <pre class='line-numbers'>#{liners}</pre>
111
+ </td>}
112
+
113
+ source_block = %{<td>
114
+ <pre class='highlight'>#{source}</pre>
115
+ </td>}
116
+
117
+ output = %{
118
+ <table cellpadding='0' cellspacing='0'>
119
+ <tr>
120
+ #{liners_block}
121
+ #{source_block}
122
+ </tr>
123
+ </table>
124
+ }
125
+ code.name = "div"
126
+ code.set_attribute "class", "data type-#{language}"
127
+ code.set_attribute "id", "code-block-#{index}"
128
+ #code.inner_html = "<div class='highlight'><pre>#{source}</pre></div>"#output
129
+ code.inner_html = output#.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "")
130
+ end
131
+
132
+ if pretty
133
+ pretty_html(html)#.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "")
134
+ else
135
+ html.to_html.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "")
136
+ end
137
+ end
138
+
139
+ # pygmentize -f html -O style=colorful,linenos=table,anchorlinenos,lineanchors=LC -l ruby tmp.rb
140
+ def filter_code_faster_todo!(content, pretty = true)
141
+ index = 0
142
+
143
+ # code
144
+ all_lines = 0
145
+ content.scan(/\`{3}\s*(\w+)?\n*(?:([^\`]{3}*))\n*\`{3}/m).each do |lang, code|
146
+ match = $&
147
+ html = Nokogiri::HTML::fragment(code, "utf-8")#::fragment(content, 'utf-8')
148
+
149
+ index += 1
150
+ language = lang
151
+ language = language.present? ? language : "text"
152
+ language = case language
153
+ when "yml"
154
+ :yaml
155
+ when "coffee", "coffee-script", "coffeescript"
156
+ :coffeescript
157
+ else # sass, scss, css, rbcon, irb
158
+ language.to_sym
159
+ end
160
+
161
+ output = Albino.new(code.strip, language).to_s
162
+
163
+ output = "<div class='data type-#{language}' id='code-block-#{index}' lang='#{language}'>\n#{output}\n</div>"
164
+ content.gsub!(match, output)
165
+ end
166
+
167
+ content
168
+ end
169
+
170
+ def filter_code!(content, pretty = true)
171
+ html = Nokogiri::HTML(content)#::fragment(content, 'utf-8')
172
+ html.encoding = "utf-8"
173
+
174
+ # header
175
+ html.css("h1, h2, h3").each do |header|
176
+ header["id"] ||= header.text.to_s.strip.underscore.gsub("_", "-").squeeze("-")
177
+ end
178
+
179
+ index = 0
180
+
181
+ # code
182
+ all_lines = 0
183
+ html.css("pre").each do |code|
184
+ index += 1
185
+ liners = ""
186
+ source = ""
187
+ lines = code.text.strip.split("\n")
188
+ language = code["lang"].to_s
189
+ language = language.present? ? language : "text"
190
+ language = case language
191
+ when "yml"
192
+ :yaml
193
+ when "coffee", "coffee-script", "coffeescript"
194
+ :coffeescript
195
+ else # sass, scss, css, rbcon, irb
196
+ language.to_sym
197
+ end
198
+ total_lines = lines.size
199
+
200
+ total_lines.times do |ln|
201
+ liners += "<span id='L#{all_lines + ln+1}' rel='#LC#{all_lines + ln+1}'>#{ln+1}</span>\n"
202
+ end
203
+
204
+ lines.each_with_index do |line, i|
205
+ code_line = line.blank? ? line.to_s : Albino.new(line, language).to_s
206
+ code_line.gsub!(/<div class="highlight"><pre>|\n<\/pre>|\n<\/div>/,'')
207
+ #code_line.gsub!(/^([ ])+/){|m| "&nbsp;"*m.size}
208
+ code_line = "<br/>" if code_line.strip == ''
209
+ source += "<div class='line' id='LC#{all_lines + i+1}'>#{code_line}</div>"
210
+ end
211
+
212
+ all_lines += total_lines
213
+
214
+ liners_block = %{<td>
215
+ <pre class='line-numbers'>#{liners}</pre>
216
+ </td>}
217
+
218
+ source_block = %{<td>
219
+ <pre class='highlight'>#{source}</pre>
220
+ </td>}
221
+
222
+ output = %{
223
+ <table cellpadding='0' cellspacing='0'>
224
+ <tr>
225
+ #{liners_block}
226
+ #{source_block}
227
+ </tr>
228
+ </table>
229
+ }
230
+ code.name = "div"
231
+ code.set_attribute "class", "data type-#{language}"
232
+ code.set_attribute "id", "code-block-#{index}"
233
+ #code.inner_html = "<div class='highlight'><pre>#{source}</pre></div>"#output
234
+ code.inner_html = output#.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "")
235
+ end
236
+
237
+ if pretty
238
+ pretty_html(html)
239
+ else
240
+ html.to_html.gsub(/^([ ])+/){|m| "&nbsp;"*m.size}.gsub(/\n/, "")
241
+ end
242
+ end
243
+ end
244
+ end
245
+ end
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: post-modern
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.5.0
6
+ platform: ruby
7
+ authors:
8
+ - Lance Pollard
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-09-20 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: Write Haml in Markdown, Syntax Highlight with Pygments, and Pretty Print the output HTML, in Ruby.
17
+ email: lancejpollard@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - Rakefile
26
+ - lib/post-modern/haml/filters/post_modern.rb
27
+ - lib/post-modern/pretty-print.xsl
28
+ - lib/post-modern/read.rb
29
+ - lib/post-modern/render.rb
30
+ - lib/post-modern.rb
31
+ homepage: http://github.com/viatropos/post-modern
32
+ licenses:
33
+ - MIT
34
+ post_install_message:
35
+ rdoc_options: []
36
+
37
+ require_paths:
38
+ - lib
39
+ required_ruby_version: !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: "0"
45
+ required_rubygems_version: !ruby/object:Gem::Requirement
46
+ none: false
47
+ requirements:
48
+ - - ">="
49
+ - !ruby/object:Gem::Version
50
+ version: "0"
51
+ requirements: []
52
+
53
+ rubyforge_project: post-modern
54
+ rubygems_version: 1.8.10
55
+ signing_key:
56
+ specification_version: 3
57
+ summary: Write Haml in Markdown, Syntax Highlight with Pygments, and Pretty Print the output HTML, in Ruby.
58
+ test_files: []
59
+