snippeteer 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +7 -0
- data/README.markdown +46 -0
- data/bin/snippeteer +46 -0
- data/lib/snippeteer.rb +147 -0
- data/snippeteer.gemspec +23 -0
- data/test/test.rb +70 -0
- metadata +51 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 762100a898939f8f15917f878a8d74808b014e72
|
4
|
+
data.tar.gz: 4d5724e1217213c8785f24651056c5059c529233
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2f1fe5b7d58d13b55c6efe6093d32f76ad9e683de6dfa0831bf682ba2822f3255b38633fb49b692e8c6f9345fa62ab32aa4805a5d611433b859a70db14c8c6c
|
7
|
+
data.tar.gz: e7544e3e86a77d7ded34f91506de0c2da8b504c5ea3e9cfef7afa3cb60f8f660536397e8fe7519c24c45e371630049d8eb8d2d492d080dc1aa8b14e094fd0526
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2013 feivel jellyfish
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.markdown
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
# snippeteer
|
2
|
+
code snippet extractor & runner for Jekyll blog posts. test your examples with almost no effort!
|
3
|
+
|
4
|
+
```shell
|
5
|
+
$ cat post.markdown
|
6
|
+
herp derp blog post
|
7
|
+
|
8
|
+
{% highlight c %}
|
9
|
+
int herp;
|
10
|
+
{% endhighlight %}
|
11
|
+
|
12
|
+
{% highlight python %}
|
13
|
+
puts('derpity')
|
14
|
+
{% endhighlight %}
|
15
|
+
|
16
|
+
{% highlight c %}
|
17
|
+
double derp = herp;
|
18
|
+
{% endhighlight %}
|
19
|
+
|
20
|
+
$ snippeteer snip post.markdown
|
21
|
+
wrote post.markdown.c
|
22
|
+
wrote post.markdown.py
|
23
|
+
$ cat post.markdown.c
|
24
|
+
int herp;
|
25
|
+
double derp = herp;
|
26
|
+
```
|
27
|
+
|
28
|
+
`snippeteer` also knows how to run code in some languages directly:
|
29
|
+
|
30
|
+
```shell
|
31
|
+
$ snippeteer run post.markdown
|
32
|
+
derpity
|
33
|
+
```
|
34
|
+
|
35
|
+
## extending
|
36
|
+
|
37
|
+
`snippeteer` can be extended to recognize new languages:
|
38
|
+
|
39
|
+
```shell
|
40
|
+
$ cat snip_ext.rb
|
41
|
+
class Snippeteer::Lang
|
42
|
+
register "trendy", ".tr", "runtrendy"
|
43
|
+
end
|
44
|
+
$ snippeteer --load snip_ext.rb run trendy-lang-takes-hacker-news-by-storm.markdown
|
45
|
+
```
|
46
|
+
|
data/bin/snippeteer
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'snippeteer'
|
4
|
+
require 'commander/import'
|
5
|
+
|
6
|
+
program :version, Snippeteer::VERSION
|
7
|
+
program :description, 'Code snippet extractor for Jekyll posts.'
|
8
|
+
|
9
|
+
default_scanners = [Snippeteer::LiquidHighlightScanner]
|
10
|
+
|
11
|
+
global_option '--load CONFIGS', Array, "Load CONFIGS before executing." do |configs|
|
12
|
+
configs.each {|cfg| load cfg}
|
13
|
+
end
|
14
|
+
|
15
|
+
command :snip do |c|
|
16
|
+
c.syntax = 'snippeteer snip [FILES ...]'
|
17
|
+
c.description = 'Extract snippets from files.'
|
18
|
+
c.action do |files, options|
|
19
|
+
files.each do |f|
|
20
|
+
Snippeteer::Writer.new(f, default_scanners).write_sources.values.each do |fn|
|
21
|
+
puts "wrote #{fn}"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
command :run do |c|
|
28
|
+
c.syntax = 'snippeteer run [FILES ...]'
|
29
|
+
c.description = 'Run snippets from files'
|
30
|
+
c.option '--noclean', "Don't remove source files after running them."
|
31
|
+
c.action do |files, opts|
|
32
|
+
run, ok = [], true
|
33
|
+
files.each do |f|
|
34
|
+
puts opts.no_clean
|
35
|
+
run += Snippeteer::Writer.new(f, default_scanners).run_sources(!opts.noclean)
|
36
|
+
end
|
37
|
+
run.each do |filename, ran_ok|
|
38
|
+
if not ran_ok
|
39
|
+
$stderr.puts "# warning: execution of #{filename} finished with non-zero exit status"
|
40
|
+
ok = false
|
41
|
+
end
|
42
|
+
end
|
43
|
+
exit 1 if not ok
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
data/lib/snippeteer.rb
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
module Snippeteer
|
2
|
+
VERSION = '0.1.0'
|
3
|
+
# Container for code tagged with language metadata.
|
4
|
+
class Snippet < Struct.new(:lang, :code); end
|
5
|
+
|
6
|
+
# Filesystem interface for the document reader. Reads files
|
7
|
+
# and writes/runs extracted snippets.
|
8
|
+
class Writer
|
9
|
+
attr_reader :path, :scanners
|
10
|
+
|
11
|
+
# Takes a filename and a list of scanners. Scanners are expected to have
|
12
|
+
# a `scan' method that takes a string and returns a list of Snippets.
|
13
|
+
def initialize(path, scanners)
|
14
|
+
@path, @scanners = path, scanners
|
15
|
+
end
|
16
|
+
|
17
|
+
# Write extracted snippets to files according to their language. Returns
|
18
|
+
# a hash mapping language to filename.
|
19
|
+
def write_sources
|
20
|
+
basename = File.basename path
|
21
|
+
sources.each_with_object({}) do |snip, written|
|
22
|
+
lang, src = snip
|
23
|
+
srcname = basename + lang.ext
|
24
|
+
File.open(srcname, 'w') {|srcfile| srcfile.write src}
|
25
|
+
written[lang] = srcname
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Write extracted snippets, then execute them if an execution command is
|
30
|
+
# given for their language. By default, removes the written files after
|
31
|
+
# execution. Returns a list of filename, exit status pairs.
|
32
|
+
def run_sources(cleanup = true)
|
33
|
+
srcs = write_sources.each_with_object([]) do |written, run|
|
34
|
+
lang, srcfile = written
|
35
|
+
if lang.exec
|
36
|
+
run << [srcfile, system("#{lang.exec} #{srcfile}")]
|
37
|
+
FileUtils.rm srcfile if cleanup
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def sources
|
43
|
+
Reader.new(File.read(@path), @scanners).sources
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Document reader. Extracts and aggregates snippets from data.
|
48
|
+
class Reader
|
49
|
+
attr_reader :data, :scanners
|
50
|
+
def initialize(data, scanners)
|
51
|
+
@data, @scanners = data, scanners
|
52
|
+
end
|
53
|
+
|
54
|
+
def snippets
|
55
|
+
scanners.reduce([]) {|snips, scanner| snips + scanner.scan(data)}
|
56
|
+
end
|
57
|
+
|
58
|
+
def sources
|
59
|
+
snippets.each_with_object({}) do |snip, srcs|
|
60
|
+
srcs[snip.lang] ||= ""
|
61
|
+
srcs[snip.lang] << snip.code
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Container & registry for language information.
|
67
|
+
class Lang < Struct.new(:ext, :exec)
|
68
|
+
UNKNOWN = new ".txt"
|
69
|
+
@registry = {}
|
70
|
+
|
71
|
+
class << self
|
72
|
+
# Define a new (or redefine an old) language, with
|
73
|
+
# filename extension and optional execution command.
|
74
|
+
def register(name, ext, exec = nil)
|
75
|
+
@registry[name] = new(ext, exec)
|
76
|
+
end
|
77
|
+
|
78
|
+
def find(lang)
|
79
|
+
if found = @registry.keys.find {|m| m == lang}
|
80
|
+
@registry[found]
|
81
|
+
else
|
82
|
+
UNKNOWN
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# here's a super incomplete list of languages
|
88
|
+
register "haskell", ".hs", "runhaskell"
|
89
|
+
register "ruby", ".rb", "ruby"
|
90
|
+
register "python", ".py", "python"
|
91
|
+
register "c", ".c"
|
92
|
+
register "c++", ".c++"
|
93
|
+
register "php", ".php", "php -f"
|
94
|
+
register "perl", ".pl", "perl"
|
95
|
+
register "javascript", ".js"
|
96
|
+
register "coffeescript", ".coffee", "coffee"
|
97
|
+
register "clojure", ".clj", "clojure"
|
98
|
+
register "clojurescript", ".cljs"
|
99
|
+
register "erlang", ".erl"
|
100
|
+
register "lisp", ".lisp", "sbcl --script" # arbitrary + non-portable choice
|
101
|
+
register "shell", ".sh", "sh"
|
102
|
+
register "java", ".java"
|
103
|
+
register "lua", ".lua", "lua"
|
104
|
+
register "scheme", ".scm"
|
105
|
+
register "go", ".go"
|
106
|
+
register "ocaml", ".ml"
|
107
|
+
end
|
108
|
+
|
109
|
+
# A _very_ simple parser for Jekyll pages that tries to extract code from
|
110
|
+
# Liquid highlight tags and partition it by language. Should just work as
|
111
|
+
# long as you don't give it nested highlight blocks or something like that.
|
112
|
+
class LiquidHighlightScanner
|
113
|
+
HIGHLIGHT = /^\s*{%\s*highlight\s+(\w+).*%}\s*$/
|
114
|
+
ENDHIGHLIGHT = /^\s*{%\s*endhighlight\s*%}\s*$/
|
115
|
+
|
116
|
+
def self.scan(doc)
|
117
|
+
new(doc).scan
|
118
|
+
end
|
119
|
+
|
120
|
+
def initialize(doc)
|
121
|
+
@lines = doc.lines.each
|
122
|
+
@snips = []
|
123
|
+
end
|
124
|
+
|
125
|
+
def scan
|
126
|
+
unhighlit
|
127
|
+
rescue StopIteration
|
128
|
+
@snips
|
129
|
+
end
|
130
|
+
|
131
|
+
private
|
132
|
+
def highlit(lang)
|
133
|
+
snip = Snippet.new lang, ""
|
134
|
+
until (l = @lines.next) =~ ENDHIGHLIGHT
|
135
|
+
snip.code += l
|
136
|
+
end
|
137
|
+
@snips << snip
|
138
|
+
unhighlit
|
139
|
+
end
|
140
|
+
|
141
|
+
def unhighlit
|
142
|
+
until (l = @lines.next) =~ HIGHLIGHT; end
|
143
|
+
highlit Lang.find $1
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
data/snippeteer.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
$LOAD_PATH << File.expand_path("../lib", __FILE__)
|
2
|
+
require 'snippeteer'
|
3
|
+
require 'rake'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'snippeteer'
|
7
|
+
spec.version = Snippeteer::VERSION
|
8
|
+
spec.author = 'feivel jellyfish'
|
9
|
+
spec.email = 'walpurgisriot@gmail.com'
|
10
|
+
spec.files = FileList['snippeteer.gemspec',
|
11
|
+
'README.markdown',
|
12
|
+
'LICENSE',
|
13
|
+
'lib/*',
|
14
|
+
'bin/*']
|
15
|
+
spec.test_files = FileList['test/**/*']
|
16
|
+
spec.executables = ["snippeteer"]
|
17
|
+
spec.bindir = 'bin'
|
18
|
+
spec.license = 'MIT/X11'
|
19
|
+
spec.homepage = 'http://github.com/walpurgisriot/jekyll-snippeteer'
|
20
|
+
spec.summary = 'Code snippet extractor & runner.'
|
21
|
+
spec.description = 'Code snippet extractor & runner for Jekyll posts.'
|
22
|
+
end
|
23
|
+
|
data/test/test.rb
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'test/unit'
|
3
|
+
require_relative '../lib/snippeteer'
|
4
|
+
|
5
|
+
POST = <<EOF
|
6
|
+
hai this hurr is my blog
|
7
|
+
i have written sum coed:
|
8
|
+
{% highlight c %}
|
9
|
+
int num = 2;
|
10
|
+
{% endhighlight%}
|
11
|
+
|
12
|
+
{%highlight ruby%}
|
13
|
+
nums = [1,2,3] # lol
|
14
|
+
{% endhighlight %}
|
15
|
+
|
16
|
+
{% highlight c %}
|
17
|
+
const char *myTxt = "i am writin c";
|
18
|
+
// lol i dont get y C needs semmicolons?
|
19
|
+
{% endhighlight %}
|
20
|
+
|
21
|
+
<a href="mailto:the_code_kid93@yahoo.com">HIRE ME</a>
|
22
|
+
EOF
|
23
|
+
|
24
|
+
POST.freeze
|
25
|
+
|
26
|
+
|
27
|
+
class LangTest < Test::Unit::TestCase
|
28
|
+
def test_find
|
29
|
+
assert_equal Snippeteer::Lang.find("haskell"), Snippeteer::Lang.new(".hs", "runhaskell")
|
30
|
+
assert_equal Snippeteer::Lang.find("ruby"), Snippeteer::Lang.new(".rb", "ruby")
|
31
|
+
assert_equal Snippeteer::Lang.find("blargysploo"), Snippeteer::Lang::UNKNOWN
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_register
|
35
|
+
# modifying entries
|
36
|
+
Snippeteer::Lang.register "ruby", ".rb", "jruby"
|
37
|
+
assert_equal Snippeteer::Lang.find("ruby").exec, "jruby"
|
38
|
+
|
39
|
+
# creating new entries
|
40
|
+
Snippeteer::Lang.register "made_up_lang", ".mul"
|
41
|
+
assert_equal Snippeteer::Lang.find("made_up_lang"), Snippeteer::Lang.new(".mul", nil)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class ScannerTest < Test::Unit::TestCase
|
46
|
+
def test_scan
|
47
|
+
results = Snippeteer::LiquidHighlightScanner.scan POST
|
48
|
+
assert_equal results.size, 3
|
49
|
+
assert_equal results.select {|r| r.lang.ext == ".c"}.size, 2
|
50
|
+
assert_equal results.select {|r| r.lang.ext == ".rb"}.size, 1
|
51
|
+
|
52
|
+
text = results.map(&:code).join
|
53
|
+
assert !(text =~ /HIRE ME/)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
class ReaderTest < Test::Unit::TestCase
|
58
|
+
def lang(l)
|
59
|
+
Snippeteer::Lang.find l
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_sources
|
63
|
+
srcs = Snippeteer::Reader.new(POST, [Snippeteer::LiquidHighlightScanner]).sources
|
64
|
+
assert_equal srcs.size, 2
|
65
|
+
assert srcs.has_key? lang "ruby"
|
66
|
+
assert srcs.has_key? lang "c"
|
67
|
+
assert_equal srcs[lang "c"].lines.size, 3
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
metadata
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: snippeteer
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- feivel jellyfish
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-12-05 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Code snippet extractor & runner for Jekyll posts.
|
14
|
+
email: walpurgisriot@gmail.com
|
15
|
+
executables:
|
16
|
+
- snippeteer
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- snippeteer.gemspec
|
21
|
+
- README.markdown
|
22
|
+
- LICENSE
|
23
|
+
- lib/snippeteer.rb
|
24
|
+
- bin/snippeteer
|
25
|
+
- test/test.rb
|
26
|
+
homepage: http://github.com/walpurgisriot/jekyll-snippeteer
|
27
|
+
licenses:
|
28
|
+
- MIT/X11
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - '>='
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.0.14
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Code snippet extractor & runner.
|
50
|
+
test_files:
|
51
|
+
- test/test.rb
|