Carlos Daniel Ruvalcaba Valenzuela avatar Carlos Daniel Ruvalcaba Valenzuela committed 65b00ab

Initial revision based on tgext.less and tgext.scss

Comments (0)

Files changed (6)

+About tgext.coffeescript
+------------------------------
+
+`CoffeeScript <http://coffeescript.org/>`_ is a little language that compiles to JavaScript. Underneath all those
+awkward braces and semicolons, JavaScript has always had a gorgeous object model at its heart.
+`CoffeeScript <http://coffeescript.org/>`_ is an attempt to expose the good parts of JavaScript in a simple way.
+
+`tgext.coffeescript <https://bitbucket.org/_amol_/tgext.coffeescript>`_ is a middleware aimed at making
+`TurboGears2 <http://www.turbogears.org>`_ development easier, tgext.coffeescript converts regular coffee
+files to javascript using the official CoffeeScript compiler.
+
+tgext.coffeescript is based on `tgext.scss <https://bitbucket.org/_amol_/tgext.scss>`_ by Alessandro Molina and is
+under the same license (MIT).
+
+Installing
+-------------------------------
+
+tgext.coffeescript can be installed both from pypi or from bitbucket::
+
+    easy_install tgext.coffeescript
+
+You will also need to install the CoffeeScript compiler, for instructions on this check their website.
+
+`CoffeeScript <http://coffeescript.org/>`_
+
+
+Enabling tgext.coffeescript
+----------------------------------
+
+Using tgext.coffeescript is really simple, you edit your `config/middeware.py` and just after
+the `#Wrap your base TurboGears 2 application with custom middleware here` comment wrap
+`app` with `CoffeeScriptMiddleware`::
+
+    from tgext.coffeescript import CoffeeScriptMiddleware
+
+    make_base_app = base_config.setup_tg_wsgi_app(load_environment)
+
+    def make_app(global_conf, full_stack=True, **app_conf):
+        app = make_base_app(global_conf, full_stack=True, **app_conf)
+
+        # Wrap your base TurboGears 2 application with custom middleware here
+        app = CoffeeScriptMiddleware(app)
+
+        return app
+
+Now you just have to put your .coffee files inside *public/javascript* and they will be served as JavaScript.
+
+Compiler options
+----------------------------------
+
+Currently tgext.coffeescript turns the --bare compiler option by default, this means that the resulting javascript
+output will not be wrapper under a top-level function, if you want to reverse this behaviour you can tell the
+middleware to disable it::
+
+    app = CoffeeScriptMiddleware(app, bare=False)
+
+This will disable the bare options for all files served with tgext.coffeescript, more granular control of this option
+may be available later depending on the need for it.
+[egg_info]
+tag_build = dev
+tag_svn_revision = true
+from setuptools import setup, find_packages
+import sys, os
+
+version = '0.1'
+
+here = os.path.abspath(os.path.dirname(__file__))
+try:
+    README = open(os.path.join(here, 'README.rst')).read()
+except IOError:
+    README = ''
+
+setup(name='tgext.coffeescript',
+      version=version,
+      description="CoffeeScript middleware for TurboGears2",
+      long_description=README,
+      classifiers=[
+        "Environment :: Web Environment",
+        "Topic :: Software Development :: Libraries :: Python Modules",
+        "License :: OSI Approved :: MIT License",
+        "Framework :: TurboGears"
+        ],
+      keywords='turbogears2.extension CoffeeScript WSGI',
+      author='Carlos Daniel Ruvalcaba Valenzuela',
+      author_email='clsdaniel@gmail.com',
+      url='http://bitbucket.org/clsdaniel/tgext.coffeescript',
+      license='MIT',
+      packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
+      namespace_packages=['tgext'],
+      include_package_data=True,
+      package_data = {'':['*.html', '*.js', '*.css', '*.png', '*.gif']},
+      zip_safe=False,
+      install_requires=[
+        "TurboGears2 >= 2.0b7",
+      ],
+      entry_points="""
+      # -*- Entry points: -*-
+      """,
+      )

tgext/__init__.py

+# See http://peak.telecommunity.com/DevCenter/setuptools#namespace-packages
+try:
+    __import__('pkg_resources').declare_namespace(__name__)
+except ImportError:
+    from pkgutil import extend_path
+    __path__ = extend_path(__path__, __name__)
+
Add a comment to this file

tgext/coffeescript/__init__.py

Empty file added.

tgext/coffeescript/middleware.py

+import subprocess
+import os
+from tg import config
+from webob.exc import status_map
+from webob.response import Response
+
+class CoffeeScriptMiddleware(object):
+    def __init__(self, app, bare=True):
+        self.app = app
+        self.cache = {}
+        self.static_files_path = os.path.abspath(config['pylons.paths']['static_files'])
+        self.bare = True
+
+        try:
+            coffee = subprocess.Popen(['coffee', '--version'], stdout=subprocess.PIPE)
+        except OSError:
+            pass
+
+        out, err = coffee.communicate()
+
+        if "CoffeeScript" in out:
+            self.method = "subprocess"
+
+    def compile_coffee(self, fullpath):
+        if self.bare:
+            args = ['coffee', '-bp', fullpath]
+        else:
+            ['coffee', '-p', fullpath]
+
+        coffee = subprocess.Popen(args, stdout=subprocess.PIPE)
+        out, err = coffee.communicate()
+
+        # TODO: Handle returned error code from compiler
+        if err:
+            return ""
+
+        return out
+
+    def compile(self, fullpath):
+        if self.method == "subprocess":
+            return self.compile_coffee(fullpath)
+        return ""
+
+    def __call__(self, environment, start_response):
+        path = environment.get('PATH_INFO')
+
+        if not path.endswith('.coffee'):
+            return self.app(environment, start_response)
+
+        # Check if the file exists, if it doesn't send 404 response
+        full_path = os.path.join(self.static_files_path, path[1:])
+        if not os.path.exists(full_path):
+            return status_map[404]()(environment, start_response)
+
+        # Generate etag key (file modified time)
+        etag_key = '"%s"' % os.stat(full_path).st_mtime
+        none_match = environment.get('HTTP_IF_NONE_MATCH')
+
+        if none_match and etag_key == none_match:
+            start_response('304 Not Modified', (('ETag', etag_key),))
+            return ['']
+
+        cached = self.cache.get(path)
+        if not cached or cached['etag_key'] != etag_key:
+            cached = dict(content=self.compile(full_path), etag_key=etag_key)
+            self.cache[path] = cached
+
+        response = Response()
+        response.content_type = "text/javascript"
+        response.headers['ETag'] = etag_key
+        response.body = cached['content']
+
+        return response(environment, start_response)
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.