github_markup 0.1.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/.gitignore +1 -0
- data/.kick +26 -0
- data/LICENSE +20 -0
- data/README.md +82 -0
- data/Rakefile +45 -0
- data/lib/github/commands/asciidoc2html +119 -0
- data/lib/github/commands/asciidocapi.py +240 -0
- data/lib/github/commands/rest2html +48 -0
- data/lib/github/markup.rb +60 -0
- data/lib/github/markup/rdoc.rb +20 -0
- data/lib/github/markup/version.rb +5 -0
- data/lib/github/markups.rb +28 -0
- data/test/markup_test.rb +28 -0
- data/test/markups/README.asciidoc +2 -0
- data/test/markups/README.asciidoc.html +13 -0
- data/test/markups/README.markdown +2 -0
- data/test/markups/README.markdown.html +5 -0
- data/test/markups/README.noformat +2 -0
- data/test/markups/README.noformat.html +2 -0
- data/test/markups/README.pod +7 -0
- data/test/markups/README.pod.html +10 -0
- data/test/markups/README.rdoc +2 -0
- data/test/markups/README.rdoc.html +8 -0
- data/test/markups/README.rst +3 -0
- data/test/markups/README.rst.html +7 -0
- data/test/markups/README.textile +2 -0
- data/test/markups/README.textile.html +4 -0
- data/test/markups/README.txt +2 -0
- data/test/markups/README.txt.html +2 -0
- metadata +84 -0
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.pyc
|
data/.kick
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# take control of the growl notifications
|
2
|
+
module GrowlHacks
|
3
|
+
def growl(type, subject, body, *args, &block)
|
4
|
+
case type
|
5
|
+
when Kicker::GROWL_NOTIFICATIONS[:succeeded]
|
6
|
+
puts subject = "Success"
|
7
|
+
body = body.split("\n").last
|
8
|
+
when Kicker::GROWL_NOTIFICATIONS[:failed]
|
9
|
+
subject = "Failure"
|
10
|
+
puts body
|
11
|
+
body = body.split("\n").last
|
12
|
+
else
|
13
|
+
return nil
|
14
|
+
end
|
15
|
+
super(type, subject, body, *args, &block)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
Kicker.send :extend, GrowlHacks
|
20
|
+
|
21
|
+
# no logging
|
22
|
+
Kicker::Utils.module_eval do
|
23
|
+
def log(message)
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 GitHub
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
GitHub Markup
|
2
|
+
=============
|
3
|
+
|
4
|
+
We use this library on GitHub when rendering your README or any other
|
5
|
+
rich text file.
|
6
|
+
|
7
|
+
Want to contribute? Great! There are two ways to add markups.
|
8
|
+
|
9
|
+
|
10
|
+
### Commands
|
11
|
+
|
12
|
+
If your markup is in a language other than Ruby, drop a translator
|
13
|
+
script in `lib/github/commands` which accepts input on STDIN and
|
14
|
+
returns HTML on STDOUT. See [rest2html][r2h] for an example.
|
15
|
+
|
16
|
+
Once your script is in place, edit `lib/github/markups.rb` and tell
|
17
|
+
GitHub Markup about it. Again we look to [rest2html][r2hc] for
|
18
|
+
guidance:
|
19
|
+
|
20
|
+
command(:rest2html, /rest|rst/)
|
21
|
+
|
22
|
+
Here we're telling GitHub Markup of the existence of a `rest2html`
|
23
|
+
command which should be used for any file ending in `rest` or
|
24
|
+
`rst`. Any regular expression will do.
|
25
|
+
|
26
|
+
Finally add your tests. Create a `README.extension` in `test/markups`
|
27
|
+
along with a `README.extension.html`. As you may imagine, the
|
28
|
+
`README.extension` should be your known input and the
|
29
|
+
`README.extension.html` should be the desired output.
|
30
|
+
|
31
|
+
Now run the tests: `rake`
|
32
|
+
|
33
|
+
If nothing complains, congratulations!
|
34
|
+
|
35
|
+
|
36
|
+
### Classes
|
37
|
+
|
38
|
+
If your markup can be translated using a Ruby library, that's
|
39
|
+
great. Check out Check `lib/github/markups.rb` for some
|
40
|
+
examples. Let's look at Markdown:
|
41
|
+
|
42
|
+
markup(:markdown, /md|mkdn?|markdown/) do |content|
|
43
|
+
Markdown.new(content).to_html
|
44
|
+
end
|
45
|
+
|
46
|
+
We give the `markup` method three bits of information: the name of the
|
47
|
+
file to `require`, a regular expression for extensions to match, and a
|
48
|
+
block to run with unformatted markup which should return HTML.
|
49
|
+
|
50
|
+
If you need to monkeypatch a RubyGem or something, check out the
|
51
|
+
included RDoc example.
|
52
|
+
|
53
|
+
Tests should be added in the same manner as described under the
|
54
|
+
`Commands` section.
|
55
|
+
|
56
|
+
|
57
|
+
Usage
|
58
|
+
-----
|
59
|
+
|
60
|
+
require 'github/markup'
|
61
|
+
GitHub::Markup.render('README.markdown', "* One\n* Two")
|
62
|
+
|
63
|
+
Or, more realistically:
|
64
|
+
|
65
|
+
require 'github/markup'
|
66
|
+
GitHub::Markup.render(file, File.read(file))
|
67
|
+
|
68
|
+
|
69
|
+
Contributing
|
70
|
+
------------
|
71
|
+
|
72
|
+
1. Fork it.
|
73
|
+
2. Create a branch
|
74
|
+
3. Commit your changes
|
75
|
+
4. Push to the branch
|
76
|
+
5. Create an [Issue][1] with a link to your branch
|
77
|
+
6. Enjoy a refreshing Diet Coke and wait
|
78
|
+
|
79
|
+
|
80
|
+
[r2h]: http://github.com/defunkt/github_markup/tree/master/lib/github/commands/rest2html
|
81
|
+
[r2hc]: http://github.com/defunkt/github_markup/tree/master/lib/github/markups.rb#L13
|
82
|
+
[1]: http://github.com/defunkt/github_markup/issues
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
task :default => :test
|
2
|
+
|
3
|
+
desc "Run tests"
|
4
|
+
task :test do
|
5
|
+
Dir['test/**/*_test.rb'].each { |file| require file }
|
6
|
+
end
|
7
|
+
|
8
|
+
desc "Kick it"
|
9
|
+
task :kick do
|
10
|
+
exec "kicker -e rake test lib"
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
require 'jeweler'
|
15
|
+
$LOAD_PATH.unshift 'lib'
|
16
|
+
require 'github/markup/version'
|
17
|
+
Jeweler::Tasks.new do |gemspec|
|
18
|
+
gemspec.name = "github_markup"
|
19
|
+
gemspec.summary = "The code we use to render README.your_favorite_markup"
|
20
|
+
gemspec.description = "The code we use to render README.your_favorite_markup"
|
21
|
+
gemspec.email = "chris@ozmm.org"
|
22
|
+
gemspec.homepage = "http://github.com/defunkt/github_markup"
|
23
|
+
gemspec.authors = ["Chris Wanstrath"]
|
24
|
+
gemspec.version = GitHub::Markup::Version
|
25
|
+
end
|
26
|
+
rescue LoadError
|
27
|
+
puts "Jeweler not available."
|
28
|
+
puts "Install it with: gem install jeweler"
|
29
|
+
end
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'sdoc_helpers'
|
33
|
+
rescue LoadError
|
34
|
+
puts "sdoc support not enabled. Please gem install sdoc-helpers."
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Push a new version to Gemcutter"
|
38
|
+
task :publish => [ :test, :gemspec, :build ] do
|
39
|
+
system "git tag v#{GitHub::Markup::Version}"
|
40
|
+
system "git push origin v#{GitHub::Markup::Version}"
|
41
|
+
system "git push origin master"
|
42
|
+
system "gem push pkg/github_markup-#{GitHub::Markup::Version}.gem"
|
43
|
+
system "git clean -fd"
|
44
|
+
#exec "rake pages"
|
45
|
+
end
|
@@ -0,0 +1,119 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
"""A small wrapper file for parsing AsciiDoc files at Github."""
|
4
|
+
|
5
|
+
__author__ = "Devin Weaver"
|
6
|
+
__copyright__ = "Copyright (C) 2009 Devin Weaver"
|
7
|
+
__license__ = "Public Domain"
|
8
|
+
__version__ = "0.1"
|
9
|
+
|
10
|
+
"""
|
11
|
+
github_asciidoc.py
|
12
|
+
------------------
|
13
|
+
|
14
|
+
This is a wrapper file for parsing AsciiDoc files at github. It wraps the
|
15
|
+
current AsciiDoc API.
|
16
|
+
|
17
|
+
AsciiDoc specifications suggest using the file extension of `.txt` however this
|
18
|
+
causes conflict because there is no way to determine if a text file is an
|
19
|
+
AsciiDoc or not without pre-processing the file. This gives us two simple
|
20
|
+
options:
|
21
|
+
|
22
|
+
1. **Parse all text files**. We could have all files ending in `.txt` or
|
23
|
+
``README.txt`` be parsed through AsciiDoc. It will print pretty text fine
|
24
|
+
even if it isn't formatted as such. However this could be *not what the user
|
25
|
+
expects*.
|
26
|
+
2. **Pick a unique extension**. We could pick a unique extension (i.e.
|
27
|
+
`.asciidoc`) to prevent clashing. Although not directly suggested by the
|
28
|
+
author of AsciiDoc there is no standard or practice to the contrary.
|
29
|
+
|
30
|
+
Option two is recommended by myself.
|
31
|
+
|
32
|
+
Requirements
|
33
|
+
~~~~~~~~~~~~
|
34
|
+
|
35
|
+
The AsciiDoc API comes in two parts. The first is the system installation of
|
36
|
+
AsciiDoc which has a simple install_. The second part is the API script. You
|
37
|
+
can either copy this to the current directory or the application's lib folder.
|
38
|
+
There is more information on the `API page`_
|
39
|
+
|
40
|
+
The `re` package is imported here for the purpose to accomplish E-Mail address
|
41
|
+
cloaking. AsciiDoc does not offer it's own cloaking algorithm like docutils
|
42
|
+
does. So I made a simple one here to do the same. **If the expense of regex's
|
43
|
+
is too high it can be easily commented out.**
|
44
|
+
|
45
|
+
.. tip::
|
46
|
+
AsciiDoc by default runs in *safe mode* which means it will not include
|
47
|
+
external files that are **not** in the same directory as the `infile`.
|
48
|
+
However since we use a StringIO through the API it should be based on the
|
49
|
+
current working directory.
|
50
|
+
|
51
|
+
.. _install: http://www.methods.co.nz/asciidoc/userguide.html
|
52
|
+
.. _API page: http://www.methods.co.nz/asciidoc/asciidocapi.html
|
53
|
+
"""
|
54
|
+
|
55
|
+
try:
|
56
|
+
import locale
|
57
|
+
locale.setlocale(locale.LC_ALL, '')
|
58
|
+
except:
|
59
|
+
pass
|
60
|
+
|
61
|
+
import sys
|
62
|
+
import cStringIO # faster then StringIO
|
63
|
+
from asciidocapi import AsciiDocAPI
|
64
|
+
from asciidocapi import AsciiDocError
|
65
|
+
import re # only needed to simulate cloak_email_addresses
|
66
|
+
|
67
|
+
def main():
|
68
|
+
"""
|
69
|
+
Parses the given AsciiDoc file or the redirected string input and returns
|
70
|
+
the HTML body.
|
71
|
+
|
72
|
+
Usage: asciidoc2html < README.rst
|
73
|
+
asciidoc2html README.rst
|
74
|
+
"""
|
75
|
+
try:
|
76
|
+
text = open(sys.argv[1], 'r').read()
|
77
|
+
except IOError: # given filename could not be found
|
78
|
+
return ''
|
79
|
+
except IndexError: # no filename given
|
80
|
+
text = sys.stdin.read()
|
81
|
+
|
82
|
+
infile = cStringIO.StringIO(text)
|
83
|
+
outfile = cStringIO.StringIO()
|
84
|
+
asciidoc = AsciiDocAPI()
|
85
|
+
asciidoc.options('-s')
|
86
|
+
|
87
|
+
try:
|
88
|
+
asciidoc.execute(infile, outfile, 'xhtml11')
|
89
|
+
except AsciiDocError, strerror:
|
90
|
+
str = "%s" % (strerror)
|
91
|
+
str = str.replace("&", "&") # Must be done first
|
92
|
+
str = str.replace("<", "%lt;")
|
93
|
+
str = str.replace(">", "%gt;")
|
94
|
+
outfile.write ("<blockquote><strong>AsciiDoc ERROR: %s</strong></blockquote>" % (str))
|
95
|
+
|
96
|
+
"""
|
97
|
+
Cloak email addresses
|
98
|
+
|
99
|
+
AsciiDoc API does not have a `cloak_email_addresses` option. We can do the
|
100
|
+
same with a set of regex but that can be expensive. Keep section commented
|
101
|
+
to disable. So ``abc@mail.example.com`` becomes:
|
102
|
+
|
103
|
+
-----------
|
104
|
+
<a class="reference" href="mailto:abc%40mail.example.org">
|
105
|
+
abc<span>@</span>mail<span>.</span>example<span>.</span>org</a>
|
106
|
+
-----------
|
107
|
+
"""
|
108
|
+
def mangleEmail(matches):
|
109
|
+
email1 = "%s%40%s" % (matches.group(1), matches.group(2))
|
110
|
+
email1 = email1.replace(".", ".")
|
111
|
+
email2 = "%s<span>@</span>%s" % (matches.group(1), matches.group(2))
|
112
|
+
email2 = email2.replace(".", "<span>.</span>")
|
113
|
+
return "<a class=\"reference\" href=\"mailto:%s\">%s</a>" % (email1, email2)
|
114
|
+
|
115
|
+
return re.sub(r'<a href="mailto:([^@]+)@([^@]+)">([^@]+)@([^@]+)</a>', mangleEmail, outfile.getvalue())
|
116
|
+
#return outfile.getvalue()
|
117
|
+
|
118
|
+
if __name__ == '__main__':
|
119
|
+
print main()
|
@@ -0,0 +1,240 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
"""
|
3
|
+
asciidocapi - AsciiDoc API wrapper class.
|
4
|
+
|
5
|
+
The AsciiDocAPI class provides an API for executing asciidoc. Minimal example
|
6
|
+
compiles `mydoc.txt` to `mydoc.html`:
|
7
|
+
|
8
|
+
import asciidocapi
|
9
|
+
asciidoc = asciidocapi.AsciiDocAPI()
|
10
|
+
asciidoc.execute('mydoc.txt')
|
11
|
+
|
12
|
+
- Full documentation in asciidocapi.txt.
|
13
|
+
- See the doctests below for more examples.
|
14
|
+
|
15
|
+
Doctests:
|
16
|
+
|
17
|
+
1. Check execution:
|
18
|
+
|
19
|
+
>>> import StringIO
|
20
|
+
>>> infile = StringIO.StringIO('Hello *{author}*')
|
21
|
+
>>> outfile = StringIO.StringIO()
|
22
|
+
>>> asciidoc = AsciiDocAPI()
|
23
|
+
>>> asciidoc.options('--no-header-footer')
|
24
|
+
>>> asciidoc.attributes['author'] = 'Joe Bloggs'
|
25
|
+
>>> asciidoc.execute(infile, outfile, backend='html4')
|
26
|
+
>>> print outfile.getvalue()
|
27
|
+
<p>Hello <strong>Joe Bloggs</strong></p>
|
28
|
+
|
29
|
+
>>> asciidoc.attributes['author'] = 'Bill Smith'
|
30
|
+
>>> infile = StringIO.StringIO('Hello _{author}_')
|
31
|
+
>>> outfile = StringIO.StringIO()
|
32
|
+
>>> asciidoc.execute(infile, outfile, backend='docbook')
|
33
|
+
>>> print outfile.getvalue()
|
34
|
+
<simpara>Hello <emphasis>Bill Smith</emphasis></simpara>
|
35
|
+
|
36
|
+
2. Check error handling:
|
37
|
+
|
38
|
+
>>> import StringIO
|
39
|
+
>>> asciidoc = AsciiDocAPI()
|
40
|
+
>>> infile = StringIO.StringIO('---------')
|
41
|
+
>>> outfile = StringIO.StringIO()
|
42
|
+
>>> asciidoc.execute(infile, outfile)
|
43
|
+
Traceback (most recent call last):
|
44
|
+
File "<stdin>", line 1, in <module>
|
45
|
+
File "asciidocapi.py", line 189, in execute
|
46
|
+
raise AsciiDocError(self.messages[-1])
|
47
|
+
AsciiDocError: ERROR: <stdin>: line 1: [blockdef-listing] missing closing delimiter
|
48
|
+
|
49
|
+
|
50
|
+
Copyright (C) 2009 Stuart Rackham. Free use of this software is granted
|
51
|
+
under the terms of the GNU General Public License (GPL).
|
52
|
+
|
53
|
+
"""
|
54
|
+
|
55
|
+
import sys,os,re
|
56
|
+
|
57
|
+
API_VERSION = '0.1.1'
|
58
|
+
MIN_ASCIIDOC_VERSION = '8.4.1' # Minimum acceptable AsciiDoc version.
|
59
|
+
|
60
|
+
|
61
|
+
def find_in_path(fname, path=None):
|
62
|
+
"""
|
63
|
+
Find file fname in paths. Return None if not found.
|
64
|
+
"""
|
65
|
+
if path is None:
|
66
|
+
path = os.environ.get('PATH', '')
|
67
|
+
for dir in path.split(os.pathsep):
|
68
|
+
fpath = os.path.join(dir, fname)
|
69
|
+
if os.path.isfile(fpath):
|
70
|
+
return fpath
|
71
|
+
else:
|
72
|
+
return None
|
73
|
+
|
74
|
+
|
75
|
+
class AsciiDocError(Exception):
|
76
|
+
pass
|
77
|
+
|
78
|
+
|
79
|
+
class Options(object):
|
80
|
+
"""
|
81
|
+
Stores asciidoc(1) command options.
|
82
|
+
"""
|
83
|
+
def __init__(self, values=[]):
|
84
|
+
self.values = values[:]
|
85
|
+
def __call__(self, name, value=None):
|
86
|
+
"""Shortcut for append method."""
|
87
|
+
self.append(name, value)
|
88
|
+
def append(self, name, value=None):
|
89
|
+
if type(value) in (int,float):
|
90
|
+
value = str(value)
|
91
|
+
self.values.append((name,value))
|
92
|
+
|
93
|
+
|
94
|
+
class Version(object):
|
95
|
+
"""
|
96
|
+
Parse and compare AsciiDoc version numbers. Instance attributes:
|
97
|
+
|
98
|
+
string: String version number '<major>.<minor>[.<micro>][suffix]'.
|
99
|
+
major: Integer major version number.
|
100
|
+
minor: Integer minor version number.
|
101
|
+
micro: Integer micro version number.
|
102
|
+
suffix: Suffix (begins with non-numeric character) is ignored when
|
103
|
+
comparing.
|
104
|
+
|
105
|
+
Doctest examples:
|
106
|
+
|
107
|
+
>>> Version('8.2.5') < Version('8.3 beta 1')
|
108
|
+
True
|
109
|
+
>>> Version('8.3.0') == Version('8.3. beta 1')
|
110
|
+
True
|
111
|
+
>>> Version('8.2.0') < Version('8.20')
|
112
|
+
True
|
113
|
+
>>> Version('8.20').major
|
114
|
+
8
|
115
|
+
>>> Version('8.20').minor
|
116
|
+
20
|
117
|
+
>>> Version('8.20').micro
|
118
|
+
0
|
119
|
+
>>> Version('8.20').suffix
|
120
|
+
''
|
121
|
+
>>> Version('8.20 beta 1').suffix
|
122
|
+
'beta 1'
|
123
|
+
|
124
|
+
"""
|
125
|
+
def __init__(self, version):
|
126
|
+
self.string = version
|
127
|
+
reo = re.match(r'^(\d+)\.(\d+)(\.(\d+))?\s*(.*?)\s*$', self.string)
|
128
|
+
if not reo:
|
129
|
+
raise ValueError('invalid version number: %s' % self.string)
|
130
|
+
groups = reo.groups()
|
131
|
+
self.major = int(groups[0])
|
132
|
+
self.minor = int(groups[1])
|
133
|
+
self.micro = int(groups[3] or '0')
|
134
|
+
self.suffix = groups[4] or ''
|
135
|
+
def __cmp__(self, other):
|
136
|
+
result = cmp(self.major, other.major)
|
137
|
+
if result == 0:
|
138
|
+
result = cmp(self.minor, other.minor)
|
139
|
+
if result == 0:
|
140
|
+
result = cmp(self.micro, other.micro)
|
141
|
+
return result
|
142
|
+
|
143
|
+
|
144
|
+
class AsciiDocAPI(object):
|
145
|
+
"""
|
146
|
+
AsciiDoc API class.
|
147
|
+
"""
|
148
|
+
def __init__(self, asciidoc_py=None):
|
149
|
+
"""
|
150
|
+
Locate and import asciidoc.py.
|
151
|
+
Initialize instance attributes.
|
152
|
+
"""
|
153
|
+
self.options = Options()
|
154
|
+
self.attributes = {}
|
155
|
+
self.messages = []
|
156
|
+
# Search for the asciidoc command file.
|
157
|
+
# Try ASCIIDOC_PY environment variable first.
|
158
|
+
cmd = os.environ.get('ASCIIDOC_PY')
|
159
|
+
if cmd:
|
160
|
+
if not os.path.isfile(cmd):
|
161
|
+
raise AsciiDocError('missing ASCIIDOC_PY file: %s' % cmd)
|
162
|
+
elif asciidoc_py:
|
163
|
+
# Next try path specified by caller.
|
164
|
+
cmd = asciidoc_py
|
165
|
+
if not os.path.isfile(cmd):
|
166
|
+
raise AsciiDocError('missing file: %s' % cmd)
|
167
|
+
else:
|
168
|
+
# Try shell search paths.
|
169
|
+
for fname in ['asciidoc.py','asciidoc.pyc','asciidoc']:
|
170
|
+
cmd = find_in_path(fname)
|
171
|
+
if cmd: break
|
172
|
+
else:
|
173
|
+
# Finally try current working directory.
|
174
|
+
for cmd in ['asciidoc.py','asciidoc.pyc','asciidoc']:
|
175
|
+
if os.path.isfile(cmd): break
|
176
|
+
else:
|
177
|
+
raise AsciiDocError('failed to locate asciidoc.py[c]')
|
178
|
+
cmd = os.path.realpath(cmd)
|
179
|
+
if os.path.splitext(cmd)[1] not in ['.py','.pyc']:
|
180
|
+
raise AsciiDocError('invalid Python module name: %s' % cmd)
|
181
|
+
sys.path.insert(0, os.path.dirname(cmd))
|
182
|
+
try:
|
183
|
+
try:
|
184
|
+
import asciidoc
|
185
|
+
except ImportError:
|
186
|
+
raise AsciiDocError('failed to import asciidoc')
|
187
|
+
finally:
|
188
|
+
del sys.path[0]
|
189
|
+
if Version(asciidoc.VERSION) < Version(MIN_ASCIIDOC_VERSION):
|
190
|
+
raise AsciiDocError(
|
191
|
+
'asciidocapi %s requires asciidoc %s or better'
|
192
|
+
% (API_VERSION, MIN_ASCIIDOC_VERSION))
|
193
|
+
self.asciidoc = asciidoc
|
194
|
+
self.cmd = cmd
|
195
|
+
|
196
|
+
def execute(self, infile, outfile=None, backend=None):
|
197
|
+
"""
|
198
|
+
Compile infile to outfile using backend format.
|
199
|
+
infile can outfile can be file path strings or file like objects.
|
200
|
+
"""
|
201
|
+
self.messages = []
|
202
|
+
opts = Options(self.options.values)
|
203
|
+
if outfile is not None:
|
204
|
+
opts('--out-file', outfile)
|
205
|
+
if backend is not None:
|
206
|
+
opts('--backend', backend)
|
207
|
+
for k,v in self.attributes.items():
|
208
|
+
if v == '' or k[-1] in '!@':
|
209
|
+
s = k
|
210
|
+
elif v is None: # A None value undefines the attribute.
|
211
|
+
s = k + '!'
|
212
|
+
else:
|
213
|
+
s = '%s=%s' % (k,v)
|
214
|
+
opts('--attribute', s)
|
215
|
+
args = [infile]
|
216
|
+
sys.path.insert(0, os.path.dirname(self.cmd))
|
217
|
+
try:
|
218
|
+
# The AsciiDoc command was designed to process source text then
|
219
|
+
# exit, there are globals and statics in asciidoc.py that have
|
220
|
+
# to be reinitialized before each run -- hence the reload.
|
221
|
+
reload(self.asciidoc)
|
222
|
+
finally:
|
223
|
+
del sys.path[0]
|
224
|
+
try:
|
225
|
+
try:
|
226
|
+
self.asciidoc.execute(self.cmd, opts.values, args)
|
227
|
+
finally:
|
228
|
+
self.messages = self.asciidoc.messages[:]
|
229
|
+
except SystemExit, e:
|
230
|
+
if e.code:
|
231
|
+
raise AsciiDocError(self.messages[-1])
|
232
|
+
|
233
|
+
|
234
|
+
if __name__ == "__main__":
|
235
|
+
"""
|
236
|
+
Run module doctests.
|
237
|
+
"""
|
238
|
+
import doctest
|
239
|
+
options = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS
|
240
|
+
doctest.testmod(optionflags=options)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
|
3
|
+
"""A small wrapper file for parsing ReST files at GitHub."""
|
4
|
+
|
5
|
+
__author__ = "Jannis Leidel"
|
6
|
+
__copyright__ = "Copyright (C) 2008 Jannis Leidel"
|
7
|
+
__license__ = "Public Domain"
|
8
|
+
__version__ = "0.1"
|
9
|
+
|
10
|
+
try:
|
11
|
+
import locale
|
12
|
+
locale.setlocale(locale.LC_ALL, '')
|
13
|
+
except:
|
14
|
+
pass
|
15
|
+
|
16
|
+
import sys
|
17
|
+
from docutils.core import publish_parts
|
18
|
+
from docutils.writers.html4css1 import Writer
|
19
|
+
|
20
|
+
SETTINGS = {
|
21
|
+
'cloak_email_addresses': True,
|
22
|
+
'file_insertion_enabled': False,
|
23
|
+
'raw_enabled': False,
|
24
|
+
'strip_comments': True,
|
25
|
+
}
|
26
|
+
|
27
|
+
def main():
|
28
|
+
"""
|
29
|
+
Parses the given ReST file or the redirected string input and returns the
|
30
|
+
HTML body.
|
31
|
+
|
32
|
+
Usage: rest2html < README.rst
|
33
|
+
rest2html README.rst
|
34
|
+
"""
|
35
|
+
try:
|
36
|
+
text = open(sys.argv[1], 'r').read()
|
37
|
+
except IOError: # given filename could not be found
|
38
|
+
return ''
|
39
|
+
except IndexError: # no filename given
|
40
|
+
text = sys.stdin.read()
|
41
|
+
|
42
|
+
parts = publish_parts(text, writer=Writer(), settings_overrides=SETTINGS)
|
43
|
+
if 'html_body' in parts:
|
44
|
+
return parts['html_body']
|
45
|
+
return ''
|
46
|
+
|
47
|
+
if __name__ == '__main__':
|
48
|
+
print main()
|
@@ -0,0 +1,60 @@
|
|
1
|
+
begin
|
2
|
+
require 'open3_detach'
|
3
|
+
rescue LoadError
|
4
|
+
require 'open3'
|
5
|
+
end
|
6
|
+
|
7
|
+
module GitHub
|
8
|
+
module Markup
|
9
|
+
extend self
|
10
|
+
@@markups = {}
|
11
|
+
|
12
|
+
def render(filename, content)
|
13
|
+
renderer(filename)[content] || content
|
14
|
+
end
|
15
|
+
|
16
|
+
def markup(file, pattern, &block)
|
17
|
+
require file.to_s
|
18
|
+
add_markup(pattern, &block)
|
19
|
+
rescue LoadError
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def command(command, regexp, &block)
|
24
|
+
command = command.to_s
|
25
|
+
if !File.exists?(command) && !command.include?('/')
|
26
|
+
command = File.dirname(__FILE__) + '/commands/' + command.to_s
|
27
|
+
end
|
28
|
+
|
29
|
+
add_markup(regexp) do |content|
|
30
|
+
rendered = execute(command, content)
|
31
|
+
block ? block.call(rendered) : rendered
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def add_markup(regexp, &block)
|
36
|
+
@@markups[regexp] = block
|
37
|
+
end
|
38
|
+
|
39
|
+
def renderer(filename)
|
40
|
+
@@markups.each do |key, value|
|
41
|
+
if Regexp.compile("(#{key})$") =~ filename
|
42
|
+
return value
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def execute(command, target)
|
48
|
+
out = ''
|
49
|
+
Open3.popen3(command) do |stdin, stdout, _|
|
50
|
+
stdin.puts target
|
51
|
+
stdin.close
|
52
|
+
out = stdout.read
|
53
|
+
end
|
54
|
+
out.gsub("\r", '')
|
55
|
+
end
|
56
|
+
|
57
|
+
# Define markups
|
58
|
+
instance_eval File.read(File.dirname(__FILE__) + '/markups.rb')
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'rdoc/generators/html_generator'
|
2
|
+
require 'ostruct'
|
3
|
+
|
4
|
+
module GitHub
|
5
|
+
module Markup
|
6
|
+
class RDoc
|
7
|
+
def initialize(content)
|
8
|
+
@content = content
|
9
|
+
end
|
10
|
+
|
11
|
+
def to_html
|
12
|
+
simple_markup = SM::SimpleMarkup.new
|
13
|
+
generator = Generators::HyperlinkHtml.new(nil, OpenStruct.new)
|
14
|
+
simple_markup.add_special(/((link:|https?:|mailto:|ftp:|www\.)\S+\w)/, :HYPERLINK)
|
15
|
+
simple_markup.add_special(/(((\{.*?\})|\b\S+?)\[\S+?\.\S+?\])/, :TIDYLINK)
|
16
|
+
simple_markup.convert(@content, generator)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
markup(:markdown, /md|mkdn?|markdown/) do |content|
|
2
|
+
Markdown.new(content).to_html
|
3
|
+
end
|
4
|
+
|
5
|
+
markup(:redcloth, /textile/) do |content|
|
6
|
+
RedCloth.new(content).to_html
|
7
|
+
end
|
8
|
+
|
9
|
+
markup('github/markup/rdoc', /rdoc/) do |content|
|
10
|
+
GitHub::Markup::RDoc.new(content).to_html
|
11
|
+
end
|
12
|
+
|
13
|
+
command(:rest2html, /rest|rst/)
|
14
|
+
|
15
|
+
command(:asciidoc2html, /asciidoc/)
|
16
|
+
|
17
|
+
# pod2html is nice enough to generate a full-on HTML document for us,
|
18
|
+
# so we return the favor by ripping out the good parts.
|
19
|
+
#
|
20
|
+
# Any block passed to `command` will be handed the command's STDOUT for
|
21
|
+
# post processing.
|
22
|
+
command("/usr/bin/env pod2html", /pod/) do |rendered|
|
23
|
+
if rendered =~ /<!-- INDEX BEGIN -->\s*(.+)\s*<!-- INDEX END -->/mi
|
24
|
+
`rm pod2htmd.tmp` if File.exists?('pod2htmd.tmp') rescue nil
|
25
|
+
`rm pod2htmi.tmp` if File.exists?('pod2htmi.tmp') rescue nil
|
26
|
+
$1
|
27
|
+
end
|
28
|
+
end
|
data/test/markup_test.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__) + "/../lib"
|
2
|
+
|
3
|
+
require 'github/markup'
|
4
|
+
|
5
|
+
def test_markup
|
6
|
+
passed = true
|
7
|
+
Dir['test/markups/README.*'].each do |readme|
|
8
|
+
next if readme =~ /html$/
|
9
|
+
markup = readme.split('.').last
|
10
|
+
|
11
|
+
expected = File.read("#{readme}.html")
|
12
|
+
actual = GitHub::Markup.render(readme, File.read(readme))
|
13
|
+
|
14
|
+
if expected == actual
|
15
|
+
puts "- #{markup}: OK"
|
16
|
+
else
|
17
|
+
passed = false
|
18
|
+
puts "- #{markup}: FAIL"
|
19
|
+
puts "#{markup} expected:", expected
|
20
|
+
puts "#{markup} actual:", actual
|
21
|
+
end
|
22
|
+
end
|
23
|
+
passed
|
24
|
+
end
|
25
|
+
|
26
|
+
at_exit do
|
27
|
+
exit test_markup ? 0 : 1
|
28
|
+
end
|
metadata
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: github_markup
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Wanstrath
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-02 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: The code we use to render README.your_favorite_markup
|
17
|
+
email: chris@ozmm.org
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- LICENSE
|
24
|
+
- README.md
|
25
|
+
files:
|
26
|
+
- .gitignore
|
27
|
+
- .kick
|
28
|
+
- LICENSE
|
29
|
+
- README.md
|
30
|
+
- Rakefile
|
31
|
+
- lib/github/commands/asciidoc2html
|
32
|
+
- lib/github/commands/asciidocapi.py
|
33
|
+
- lib/github/commands/rest2html
|
34
|
+
- lib/github/markup.rb
|
35
|
+
- lib/github/markup/rdoc.rb
|
36
|
+
- lib/github/markup/version.rb
|
37
|
+
- lib/github/markups.rb
|
38
|
+
- test/markup_test.rb
|
39
|
+
- test/markups/README.asciidoc
|
40
|
+
- test/markups/README.asciidoc.html
|
41
|
+
- test/markups/README.markdown
|
42
|
+
- test/markups/README.markdown.html
|
43
|
+
- test/markups/README.noformat
|
44
|
+
- test/markups/README.noformat.html
|
45
|
+
- test/markups/README.pod
|
46
|
+
- test/markups/README.pod.html
|
47
|
+
- test/markups/README.rdoc
|
48
|
+
- test/markups/README.rdoc.html
|
49
|
+
- test/markups/README.rst
|
50
|
+
- test/markups/README.rst.html
|
51
|
+
- test/markups/README.textile
|
52
|
+
- test/markups/README.textile.html
|
53
|
+
- test/markups/README.txt
|
54
|
+
- test/markups/README.txt.html
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/defunkt/github_markup
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: The code we use to render README.your_favorite_markup
|
83
|
+
test_files:
|
84
|
+
- test/markup_test.rb
|