post-modern 0.5.0

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