├── .gitignore ├── Makefile ├── README.markdown ├── app.assets ├── app.py ├── app.yaml ├── assets.manifest ├── assets ├── bootstrap │ ├── css │ │ ├── bootstrap-responsive.css │ │ └── bootstrap.css │ ├── img │ │ ├── glyphicons-halflings-white.png │ │ └── glyphicons-halflings.png │ └── js │ │ └── bootstrap.js ├── embed.css ├── index.js ├── jquery.js └── prettify │ ├── prettify.css │ └── prettify.js ├── gist_it ├── __init__.py └── appengine.py ├── jinja2-assets ├── gist.jinja.html ├── gist.jinja.js ├── index.jinja.html └── test.jinja.html ├── pyl ├── jinja2 │ ├── __init__.py │ ├── _debugsupport.c │ ├── _markupsafe │ │ ├── __init__.py │ │ ├── _bundle.py │ │ ├── _constants.py │ │ ├── _native.py │ │ └── tests.py │ ├── _stringdefs.py │ ├── bccache.py │ ├── compiler.py │ ├── constants.py │ ├── debug.py │ ├── defaults.py │ ├── environment.py │ ├── exceptions.py │ ├── ext.py │ ├── filters.py │ ├── lexer.py │ ├── loaders.py │ ├── meta.py │ ├── nodes.py │ ├── optimizer.py │ ├── parser.py │ ├── runtime.py │ ├── sandbox.py │ ├── tests.py │ ├── testsuite │ │ ├── __init__.py │ │ ├── api.py │ │ ├── core_tags.py │ │ ├── debug.py │ │ ├── doctests.py │ │ ├── ext.py │ │ ├── filters.py │ │ ├── imports.py │ │ ├── inheritance.py │ │ ├── lexnparse.py │ │ ├── loader.py │ │ ├── regression.py │ │ ├── res │ │ │ ├── __init__.py │ │ │ └── templates │ │ │ │ ├── broken.html │ │ │ │ ├── foo │ │ │ │ └── test.html │ │ │ │ ├── syntaxerror.html │ │ │ │ └── test.html │ │ ├── security.py │ │ ├── tests.py │ │ └── utils.py │ ├── utils.py │ └── visitor.py └── versioned_memcache │ ├── __init__.py │ └── memcache.py ├── synopsis.markdown ├── t ├── 01-basic.t ├── Makefile.PL └── cpanm ├── t0 ├── 01-basic.t └── render-gist-it-example └── test ├── test_cgi_escape.py ├── test_footer.py ├── test_gist_it.py ├── test_highlight.py ├── test_slice.py └── test_style.py /.gitignore: -------------------------------------------------------------------------------- 1 | /t/Makefile 2 | /t/p5 3 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: test t crunch 2 | 3 | test: 4 | unit2 discover -v test 5 | @echo 6 | @echo "For remote testing with perl, run the following:" 7 | @echo " dev_appserver.py ." 8 | @echo " $(MAKE) t" 9 | 10 | t: 11 | mkdir -p t/p5 12 | perl t/cpanm -l t/p5 --installdeps ./t 13 | prove t/ 14 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | # gist-it 2 | 3 | An AppEngine app to embed files from a github repository like a gist 4 | 5 | http://github.com/robertkrimen/gist-it 6 | 7 | ### Try it 8 | 9 | You can try it at [gist-it.appspot.com](http://gist-it.appspot.com) 10 | 11 | ### Usage 12 | 13 | Launch a working example with: 14 | 15 | dev_appserver.py . 16 | 17 | The gisting handler is located at ```app.py/dispatch_gist_it``` 18 | 19 | Associated the handler with a path is pretty easy, for example: 20 | 21 | wsgi_application = webapp.WSGIApplication( [ 22 | ( r'(.*)', dispatch_gist_it ), 23 | ], debug=True ) 24 | 25 | ### Testing 26 | 27 | To run python unit2 tests: 28 | 29 | make test 30 | 31 | To run perl tests (against a running server): 32 | 33 | make t 34 | 35 | ### Author 36 | 37 | Robert Krimen (robertkrimen@gmail.com) 38 | 39 | * http://github.com/robertkrimen 40 | * http://search.cpan.org/~rokr/?R=D 41 | -------------------------------------------------------------------------------- /app.assets: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env perl 2 | 3 | use Modern::Perl; 4 | 5 | sub { 6 | my ( $collate ) = @_; 7 | 8 | $collate->import( <<_END_ ); 9 | jquery 10 | prettify 11 | _END_ 12 | 13 | #jquery-ui 14 | #zeroclipboard 15 | #css3buttons 16 | #tipsy 17 | 18 | $collate->write( 'assets' ); 19 | 20 | return; 21 | 22 | $collate->write( 'root-assets/_', compressed => 1, 23 | detach => [qw[ 24 | jquery-ui/base 25 | css3buttons 26 | zeroclipboard 27 | ]], 28 | rewrite => [ 29 | '/assets/_', 30 | [ 'appengine/index.html', 'appengine/index_.html' ], 31 | ] 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | import logging 3 | import new 4 | import os 5 | import pickle 6 | import cgi 7 | import sys 8 | import traceback 9 | import urlparse 10 | import types 11 | import wsgiref.handlers 12 | import re 13 | import posixpath 14 | import urllib 15 | 16 | from google.appengine.api import urlfetch 17 | from google.appengine.ext import db 18 | from google.appengine.ext import webapp 19 | from google.appengine.ext.webapp.util import run_wsgi_app 20 | from google.appengine.ext.webapp import template 21 | 22 | _LOCAL_ = os.environ[ 'SERVER_SOFTWARE' ].startswith( 'Development' ) 23 | _DEBUG_ = False or _LOCAL_ 24 | _CACHE_ = False 25 | 26 | sys.path.append( 'pyl' ) 27 | 28 | import jinja2 as jinja2_ 29 | jinja2 = jinja2_.Environment( loader=jinja2_.FileSystemLoader( 'jinja2-assets' ) ) 30 | 31 | from gist_it import appengine as gist_it_appengine 32 | gist_it_appengine.jinja2 = jinja2 33 | 34 | class RequestHandler( webapp.RequestHandler ): 35 | 36 | def url_for( self, *arguments ): 37 | parse = urlparse.urlparse( self.request.url ) 38 | path = '' 39 | if len( arguments ): 40 | path = posixpath.join( *arguments ) 41 | return str( urlparse.urlunparse( ( parse.scheme, parse.netloc, path, '', '', '' ) ) ) 42 | 43 | def render_template( self, template_name, **arguments ): 44 | self.response.out.write( jinja2.get_template( template_name ).render( dispatch=self, **arguments ) ) 45 | 46 | class dispatch_index( RequestHandler ): 47 | def get( self ): 48 | self.render_template( 'index.jinja.html' ) 49 | 50 | class dispatch_test( RequestHandler ): 51 | def get( self ): 52 | return gist_it_appengine.dispatch_test( self ) 53 | 54 | class dispatch_test0( RequestHandler ): 55 | def get( self ): 56 | return gist_it_appengine.dispatch_test0( self ) 57 | 58 | class dispatch_gist_it( RequestHandler ): 59 | def get( self, location ): 60 | return gist_it_appengine.dispatch_gist_it( self, location ) 61 | 62 | wsgi_application = webapp.WSGIApplication( [ 63 | ( r'/', dispatch_index ), 64 | ( r'/test', dispatch_test ), 65 | ( r'/test0', dispatch_test0 ), 66 | ( r'/xyzzy/(.*)', dispatch_gist_it ), 67 | ( r'(.*)', dispatch_gist_it ), 68 | ], debug=_DEBUG_ ) 69 | 70 | def main(): 71 | run_wsgi_app( wsgi_application ) 72 | 73 | if __name__ == '__main__': 74 | main() 75 | -------------------------------------------------------------------------------- /app.yaml: -------------------------------------------------------------------------------- 1 | application: 0 2 | version: 1 3 | 4 | runtime: python 5 | api_version: 1 6 | 7 | handlers: 8 | - url: /assets 9 | static_dir: assets 10 | expiration: 1d 11 | 12 | - url: .* 13 | script: app.py 14 | -------------------------------------------------------------------------------- /assets.manifest: -------------------------------------------------------------------------------- 1 | # fetchett 2 | 3 | #google-code-prettify 4 | bootstrap 5 | jquery 6 | -------------------------------------------------------------------------------- /assets/bootstrap/img/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/assets/bootstrap/img/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /assets/bootstrap/img/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/robertkrimen/gist-it/e4e67336df783ae4626fc73805a1fd52bc299012/assets/bootstrap/img/glyphicons-halflings.png -------------------------------------------------------------------------------- /assets/embed.css: -------------------------------------------------------------------------------- 1 | .gist-it-gist div { 2 | padding: 0; 3 | margin: 0; 4 | } 5 | 6 | .gist-it-gist .gist-file { 7 | border: 1px solid #DEDEDE; 8 | font-family: Consolas,"Liberation Mono",Courier,monospace; 9 | margin-bottom: 1em; 10 | } 11 | 12 | .gist-it-gist .gist-file .gist-meta { 13 | background-color: #EAEAEA; 14 | color: #666666; 15 | font-family: Helvetica,arial,freesans,clean,sans-serif; 16 | font-size: 85%; 17 | overflow: hidden; 18 | padding: 0.5em; 19 | } 20 | 21 | .gist-it-gist .gist-file .gist-meta a { 22 | color: #336699; 23 | } 24 | 25 | .gist-it-gist .gist-file .gist-meta a:visited { 26 | color: #773377; 27 | } 28 | 29 | .gist-it-gist .gist-file .gist-data { 30 | background-color: #F8F8FF; 31 | border-bottom: 1px solid #DDDDDD; 32 | font-size: 100%; 33 | overflow: auto; 34 | word-wrap: normal; 35 | } 36 | 37 | .gist-it-gist .gist-file .gist-data pre { 38 | background: none repeat scroll 0 0 transparent !important; 39 | border: medium none !important; 40 | font-family: Consolas,"Liberation Mono",Courier,monospace; 41 | margin: 0 !important; 42 | padding: 0 !important; 43 | } 44 | -------------------------------------------------------------------------------- /assets/index.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | $( '#try-it' ).click(function(){ 3 | $( '#try-it-modal' ).modal( 'show' ); 4 | return false; 5 | }); 6 | 7 | $modal = $( '#try-it-modal' ); 8 | $input = $modal.find('input'); 9 | $script = $modal.find('.script'); 10 | $output = $modal.find('.output'); 11 | 12 | $modal.find('form').on('submit', function(){ 13 | load(); 14 | }); 15 | $modal.bind('show', function(){ 16 | if (!$output.children().length) { 17 | load(); 18 | } 19 | }); 20 | $modal.bind('shown', function(){ 21 | $input.focus().select(); 22 | }); 23 | 24 | function load(){ 25 | var url = $input.val(); 26 | $output.text(""); 27 | if (url === "") { 28 | $output.addClass('alert alert-warning').text("Enter a GitHub URL"); 29 | return; 30 | } 31 | url = GIST_IT_BASE + '/' + url; 32 | $script.empty().append( 33 | $( '
' ).text('') 34 | ); 35 | $output.removeClass('alert alert-warning alert-error').text("Loading..."); 36 | prettyPrint(); 37 | $.getJSON(url, { 'test': 'json' }, function(data){ 38 | $output.html(data.html); 39 | prettyPrint(); 40 | }).error(function(response){ 41 | $output.addClass('alert alert-error').text(response.responseText); 42 | }); 43 | } 44 | }); 45 | -------------------------------------------------------------------------------- /assets/prettify/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} -------------------------------------------------------------------------------- /assets/prettify/prettify.js: -------------------------------------------------------------------------------- 1 | !function(){var p=null;window.PR_SHOULD_USE_CONTINUATION=!0; 2 | (function(){function S(b){function c(e){var b=e.charCodeAt(0);if(b!==92)return b;var a=e.charAt(1);return(b=q[a])?b:"0"<=a&&a<="7"?parseInt(e.substring(1),8):a==="u"||a==="x"?parseInt(e.substring(2),16):e.charCodeAt(1)}function g(e){if(e<32)return(e<16?"\\x0":"\\x")+e.toString(16);e=String.fromCharCode(e);return e==="\\"||e==="-"||e==="]"||e==="^"?"\\"+e:e}function d(e){var b=e.substring(1,e.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),e=[],a= 3 | b[0]==="^",d=["["];a&&d.push("^");for(var a=a?1:0,f=b.length;a# Embed the file robertkrimen/gist-it-example/example.js 40 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js"></script> 41 | 42 | # Embed without a footer 43 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js?footer=0"></script> 44 | 45 | # Show only the first and second line 46 | <script src="http://gist-it.appspot.com/github/robertkrimen/gist-it-example/blob/master/example.js?slice=0:1"></script> 47 |48 | 49 | 50 |
function Xyzzy() { 55 | return "Nothing happens"; 56 | }57 | 58 |
gist-it lets you take a file from your GitHub repository and embed it into any webpage, just like a gist
77 |Syntax highlighting by google-code-prettify
78 | 79 |Specify the slice
parameter to show only a particular portion of the file:
slice=1: | 85 |Show from the second line on (skip the first line) | 86 |
slice=0:-1 | 89 |Show the first line up to and including the second-to-last line | 90 |
slice=24:101 | 93 |Show lines 24 through 101 | 94 |
slice=0 | 97 |Show only the first line of the file | 98 |
Use the footer
parameter to control the appearance of the footer:
105 | footer=no 106 | footer=0 107 | |
108 | Hide the footer | 109 |
footer=minimal | 112 |Show the footer without this leading part: This Gist brought to you by gist-it. |
113 |
http://github.com/robertkrimen/gist-it
118 | 119 |{{ item[0] }}
An example github gist
23 | 24 |{{ user.username|e }}
{{ caller(user) }}apo
Hello {{ name }}!
{% endmacro %}' 114 | '{{ say_hello("") }}') 115 | escaped_out = 'Hello <blink>foo</blink>!
' 116 | assert t.render() == escaped_out 117 | assert unicode(t.module) == escaped_out 118 | assert escape(t.module) == escaped_out 119 | assert t.module.say_hello('') == escaped_out 120 | assert escape(t.module.say_hello('')) == escaped_out 121 | 122 | def test_attr_filter(self): 123 | env = SandboxedEnvironment() 124 | tmpl = env.from_string('{{ cls|attr("__subclasses__")() }}') 125 | self.assert_raises(SecurityError, tmpl.render, cls=int) 126 | 127 | def test_binary_operator_intercepting(self): 128 | def disable_op(left, right): 129 | raise TemplateRuntimeError('that operator so does not work') 130 | for expr, ctx, rv in ('1 + 2', {}, '3'), ('a + 2', {'a': 2}, '4'): 131 | env = SandboxedEnvironment() 132 | env.binop_table['+'] = disable_op 133 | t = env.from_string('{{ %s }}' % expr) 134 | assert t.render(ctx) == rv 135 | env.intercepted_binops = frozenset(['+']) 136 | t = env.from_string('{{ %s }}' % expr) 137 | try: 138 | t.render(ctx) 139 | except TemplateRuntimeError, e: 140 | pass 141 | else: 142 | self.fail('expected runtime error') 143 | 144 | def test_unary_operator_intercepting(self): 145 | def disable_op(arg): 146 | raise TemplateRuntimeError('that operator so does not work') 147 | for expr, ctx, rv in ('-1', {}, '-1'), ('-a', {'a': 2}, '-2'): 148 | env = SandboxedEnvironment() 149 | env.unop_table['-'] = disable_op 150 | t = env.from_string('{{ %s }}' % expr) 151 | assert t.render(ctx) == rv 152 | env.intercepted_unops = frozenset(['-']) 153 | t = env.from_string('{{ %s }}' % expr) 154 | try: 155 | t.render(ctx) 156 | except TemplateRuntimeError, e: 157 | pass 158 | else: 159 | self.fail('expected runtime error') 160 | 161 | 162 | def suite(): 163 | suite = unittest.TestSuite() 164 | suite.addTest(unittest.makeSuite(SandboxTestCase)) 165 | return suite 166 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/tests.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.tests 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Who tests the tests? 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import unittest 12 | from jinja2.testsuite import JinjaTestCase 13 | 14 | from jinja2 import Markup, Environment 15 | 16 | env = Environment() 17 | 18 | 19 | class TestsTestCase(JinjaTestCase): 20 | 21 | def test_defined(self): 22 | tmpl = env.from_string('{{ missing is defined }}|{{ true is defined }}') 23 | assert tmpl.render() == 'False|True' 24 | 25 | def test_even(self): 26 | tmpl = env.from_string('''{{ 1 is even }}|{{ 2 is even }}''') 27 | assert tmpl.render() == 'False|True' 28 | 29 | def test_odd(self): 30 | tmpl = env.from_string('''{{ 1 is odd }}|{{ 2 is odd }}''') 31 | assert tmpl.render() == 'True|False' 32 | 33 | def test_lower(self): 34 | tmpl = env.from_string('''{{ "foo" is lower }}|{{ "FOO" is lower }}''') 35 | assert tmpl.render() == 'True|False' 36 | 37 | def test_typechecks(self): 38 | tmpl = env.from_string(''' 39 | {{ 42 is undefined }} 40 | {{ 42 is defined }} 41 | {{ 42 is none }} 42 | {{ none is none }} 43 | {{ 42 is number }} 44 | {{ 42 is string }} 45 | {{ "foo" is string }} 46 | {{ "foo" is sequence }} 47 | {{ [1] is sequence }} 48 | {{ range is callable }} 49 | {{ 42 is callable }} 50 | {{ range(5) is iterable }} 51 | ''') 52 | assert tmpl.render().split() == [ 53 | 'False', 'True', 'False', 'True', 'True', 'False', 54 | 'True', 'True', 'True', 'True', 'False', 'True' 55 | ] 56 | 57 | def test_sequence(self): 58 | tmpl = env.from_string( 59 | '{{ [1, 2, 3] is sequence }}|' 60 | '{{ "foo" is sequence }}|' 61 | '{{ 42 is sequence }}' 62 | ) 63 | assert tmpl.render() == 'True|True|False' 64 | 65 | def test_upper(self): 66 | tmpl = env.from_string('{{ "FOO" is upper }}|{{ "foo" is upper }}') 67 | assert tmpl.render() == 'True|False' 68 | 69 | def test_sameas(self): 70 | tmpl = env.from_string('{{ foo is sameas false }}|' 71 | '{{ 0 is sameas false }}') 72 | assert tmpl.render(foo=False) == 'True|False' 73 | 74 | def test_no_paren_for_arg1(self): 75 | tmpl = env.from_string('{{ foo is sameas none }}') 76 | assert tmpl.render(foo=None) == 'True' 77 | 78 | def test_escaped(self): 79 | env = Environment(autoescape=True) 80 | tmpl = env.from_string('{{ x is escaped }}|{{ y is escaped }}') 81 | assert tmpl.render(x='foo', y=Markup('foo')) == 'False|True' 82 | 83 | 84 | def suite(): 85 | suite = unittest.TestSuite() 86 | suite.addTest(unittest.makeSuite(TestsTestCase)) 87 | return suite 88 | -------------------------------------------------------------------------------- /pyl/jinja2/testsuite/utils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | jinja2.testsuite.utils 4 | ~~~~~~~~~~~~~~~~~~~~~~ 5 | 6 | Tests utilities jinja uses. 7 | 8 | :copyright: (c) 2010 by the Jinja Team. 9 | :license: BSD, see LICENSE for more details. 10 | """ 11 | import gc 12 | import unittest 13 | 14 | import pickle 15 | 16 | from jinja2.testsuite import JinjaTestCase 17 | 18 | from jinja2.utils import LRUCache, escape, object_type_repr 19 | 20 | 21 | class LRUCacheTestCase(JinjaTestCase): 22 | 23 | def test_simple(self): 24 | d = LRUCache(3) 25 | d["a"] = 1 26 | d["b"] = 2 27 | d["c"] = 3 28 | d["a"] 29 | d["d"] = 4 30 | assert len(d) == 3 31 | assert 'a' in d and 'c' in d and 'd' in d and 'b' not in d 32 | 33 | def test_pickleable(self): 34 | cache = LRUCache(2) 35 | cache["foo"] = 42 36 | cache["bar"] = 23 37 | cache["foo"] 38 | 39 | for protocol in range(3): 40 | copy = pickle.loads(pickle.dumps(cache, protocol)) 41 | assert copy.capacity == cache.capacity 42 | assert copy._mapping == cache._mapping 43 | assert copy._queue == cache._queue 44 | 45 | 46 | class HelpersTestCase(JinjaTestCase): 47 | 48 | def test_object_type_repr(self): 49 | class X(object): 50 | pass 51 | self.assert_equal(object_type_repr(42), 'int object') 52 | self.assert_equal(object_type_repr([]), 'list object') 53 | self.assert_equal(object_type_repr(X()), 54 | 'jinja2.testsuite.utils.X object') 55 | self.assert_equal(object_type_repr(None), 'None') 56 | self.assert_equal(object_type_repr(Ellipsis), 'Ellipsis') 57 | 58 | 59 | class MarkupLeakTestCase(JinjaTestCase): 60 | 61 | def test_markup_leaks(self): 62 | counts = set() 63 | for count in xrange(20): 64 | for item in xrange(1000): 65 | escape("foo") 66 | escape("function Xyzzy() {\n return "Nothing happens";\n}\n\E! ); 49 | $test->body_like( qr{\Q