Skip to content
This repository has been archived by the owner on Apr 4, 2018. It is now read-only.

Commit

Permalink
Stop hard-error on file-not-found; add resolution for shared assets (…
Browse files Browse the repository at this point in the history
…things in Static) in the Python apps
  • Loading branch information
fidothe committed Apr 18, 2011
1 parent 89bd3ea commit f33ce7f
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 9 deletions.
9 changes: 7 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,15 +41,20 @@ in `settings.py` add cdn_helpers to your list of installed apps:
'cdn_helpers'
)

in your local-development `local_settings.py`:

APP_DEPLOYMENT_ENV = 'local' # (disables CDN url generation)
CDN_HOSTS = []

in the appropriate `local_settings.py`:

APP_DEPLOYMENT_ENV = 'dev' # (or staging, production)
CDN_HOSTS = ['cdn1', 'cdn2'] # (as appropriate for the environment)

SHARED_PUBLIC_ROOT = "/path/to/shared/assets" # (i.e. where Static's public dir is)

in templates use the `asset_url` helper:

<img src="{% asset_url "/images/thing.png" %}">
<img src="{% asset_url thing.image.src %}">
<img src="{% asset_url thing.image.url %}">

The app provides the `cdn_css` command for `manage.py`. This uses the Ruby script process_css to do the heavy lifting, but relies on the `MEDIA_ROOT` and `MEDIA_URL` settings in `settings.py` to tell where to look and how to construct the URLs.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added fixtures/images/thing.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
26 changes: 20 additions & 6 deletions py/cdn_helpers/templatetags/asset_url.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,10 @@ def __init__(self, settings = None):
def process_url(self, url):
"""Process url and return a suitable URL for the app's environment"""
if hasattr(self.settings, 'APP_DEPLOYMENT_ENV') and self.settings.APP_DEPLOYMENT_ENV != 'local':
sha1 = self.generate_sha1(self.file_path_from_url(url))
return self.compose_url(url, sha1)
sha1 = self.fetch_sha1(self.file_path_from_url(url), self.generate_sha1)
if sha1 != None:
url = self.compose_path(url, sha1)
return self.compose_url(url)
else:
return url

Expand All @@ -36,22 +38,34 @@ def file_path_from_url(self, url):
media_url = '%s/' % self.settings.MEDIA_URL.lstrip('/').rstrip('/')
if stripped_url.find(media_url) == 0:
url = stripped_url[len(media_url) - 1:]
return os.path.normpath(os.path.join(self.settings.MEDIA_ROOT, url.lstrip('/')))
local_path = os.path.normpath(os.path.join(self.settings.MEDIA_ROOT, url.lstrip('/')))
if os.path.exists(local_path):
return local_path
if hasattr(self.settings, 'SHARED_PUBLIC_ROOT'):
shared_path = os.path.normpath(os.path.join(self.settings.SHARED_PUBLIC_ROOT, url.lstrip('/')))
if os.path.exists(shared_path):
return shared_path
return None

def generate_sha1(self, file_path):
"""Generates and return sha1 for the file at file_path"""
# import hashlib
if file_path == None:
return None
hash_func = hashlib.sha1()
with open(file_path) as f:
hash_func.update(f.read())

return hash_func.hexdigest()

def compose_path(self, path, sha1):
"""Stitch path and hash together"""
return path + '/' + sha1[0:8] + os.path.splitext(path)[1]

def compose_url(self, path, sha1):
"""Stitch path and hash together into a CDN url"""
def compose_url(self, uri_path):
"""Stitch hashed path into a CDN url"""
# import random, os
host = random.choice(self.settings.CDN_HOSTS)
uri_path = path + '/' + sha1[0:8] + os.path.splitext(path)[1]
return urlparse.urlunparse(('http', host, uri_path, '', '', ''))

def fetch_sha1(self, path, sha1_generator):
Expand Down
42 changes: 41 additions & 1 deletion test/test_cdn_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ def test_sha1_calculation(self):
self.asset_url.generate_sha1(os.path.join(fixtures_dir, 'h5.png')),
'daf4a94f8d2cc9f564a6f958d1915ea9346d09f1'
)

def test_sha1_calculation_returns_None_if_passed_None(self):
self.assertEqual(
self.asset_url.generate_sha1(None),
None
)

def test_url_generation(self):
self.django_settings['CDN_HOSTS'] = ['dev1.host.com']

self.assertEqual(
self.asset_url.compose_url('/images/h5.png', 'daf4a94f8d2cc9f564a6f958d1915ea9346d09f1'),
self.asset_url.compose_url('/images/h5.png/daf4a94f.png'),
'http://dev1.host.com/images/h5.png/daf4a94f.png'
)

Expand Down Expand Up @@ -85,6 +91,34 @@ def test_correctly_locates_a_file_when_media_url_is_set(self):
os.path.normpath(os.path.join(fixtures_dir, 'images/thing.png'))
)

def test_correctly_locates_a_shared_file_if_SHARED_PUBLIC_ROOT_is_set(self):
self.django_settings['MEDIA_ROOT'] = fixtures_dir
self.django_settings['MEDIA_URL'] = '/media_url/'
self.django_settings['SHARED_PUBLIC_ROOT'] = os.path.join(fixtures_dir, 'files_from_static')

self.assertEqual(
self.asset_url.file_path_from_url('/media_url/images/answers-advisory-highlight.png'),
os.path.normpath(os.path.join(fixtures_dir, 'files_from_static', 'images/answers-advisory-highlight.png'))
)

def test_returns_None_for_a_shared_file_if_SHARED_PUBLIC_ROOT_is_not_set(self):
self.django_settings['MEDIA_ROOT'] = fixtures_dir
self.django_settings['MEDIA_URL'] = '/media_url/'

self.assertEqual(
self.asset_url.file_path_from_url('/media_url/images/answers-advisory-highlight.png'),
None
)

def test_returns_None_if_file_cannot_be_located(self):
self.django_settings['MEDIA_ROOT'] = fixtures_dir
self.django_settings['MEDIA_URL'] = '/media_url/'

self.assertEqual(
self.asset_url.file_path_from_url('/media_url/unreal.css'),
None
)

class TestAssetUrlFromCDN(unittest.TestCase):
def setUp(self):
self.django_settings = MockSettings()
Expand All @@ -96,6 +130,12 @@ def test_cdn_url_created(self):
self.django_settings['CDN_HOSTS'] = ['cdn.host.com']
self.assertEqual(self.asset_url.process_url('/h5.png'), 'http://cdn.host.com/h5.png/daf4a94f.png')

def test_cdn_url_returns_unhashed_url_if_file_not_found(self):
self.django_settings['MEDIA_ROOT'] = fixtures_dir
self.django_settings['APP_DEPLOYMENT_ENV'] = 'dev'
self.django_settings['CDN_HOSTS'] = ['cdn.host.com']
self.assertEqual(self.asset_url.process_url('/h51.png'), 'http://cdn.host.com/h51.png')


if __name__ == '__main__':
unittest.main()

0 comments on commit f33ce7f

Please sign in to comment.