Merge lp:~elachuni/ubuntu-webcatalog/convoy into lp:ubuntu-webcatalog

Proposed by Anthony Lenton
Status: Merged
Approved by: Anthony Lenton
Approved revision: 66
Merged at revision: 66
Proposed branch: lp:~elachuni/ubuntu-webcatalog/convoy
Merge into: lp:ubuntu-webcatalog
Prerequisite: lp:~elachuni/ubuntu-webcatalog/exhibit-widget
Diff against target: 209 lines (+68/-10)
10 files modified
.bzrignore (+1/-0)
django_project/config/main.cfg (+1/-1)
fabtasks/bootstrap.py (+2/-0)
src/webcatalog/templates/light/404.html (+1/-1)
src/webcatalog/templates/light/index.1col.html (+3/-4)
src/webcatalog/templates/webcatalog/application_detail.html (+1/-1)
src/webcatalog/templates/webcatalog/base.html (+1/-1)
src/webcatalog/tests/test_views.py (+34/-0)
src/webcatalog/urls.py (+1/-1)
src/webcatalog/views.py (+23/-1)
To merge this branch: bzr merge lp:~elachuni/ubuntu-webcatalog/convoy
Reviewer Review Type Date Requested Status
Michael Nelson (community) Approve
Review via email: mp+95963@code.launchpad.net

Commit message

Added a combo loader view using lp:convoy to load multiple css stylesheets or javascript files.

Description of the change

Overview
========
This branch adds a combo loader view using lp:convoy to load multiple css stylesheets or javascript files. It also plugs this into YUI so that all the javascript YUI pulls in is piled into a single request.

Details
=======
This improvement comes from a recent discussion on canonical-tech, the view is largely based on Sidnei's combo view for Django.
Using the combo loader all pages need at least three requests less (as the four stylesheets pulled in by base.html are
all requested together), and the application details page goes down from 34 requests to 8.

This change is part of a sequence of three MPs to add a nifty "exhibits" widget to the front page. The next branch will add the YUI3 carrousel widget.

To post a comment you must log in.
Revision history for this message
Michael Nelson (michael.nelson) wrote :
Download full text (10.3 KiB)

On Mon, Mar 5, 2012 at 7:13 PM, Anthony Lenton
<email address hidden> wrote:
> Anthony Lenton has proposed merging lp:~elachuni/ubuntu-webcatalog/convoy into lp:ubuntu-webcatalog with lp:~elachuni/ubuntu-webcatalog/exhibit-widget as a prerequisite.
>
> Requested reviews:
>  Canonical Consumer Applications Hackers (canonical-ca-hackers)
>
> For more details, see:
> https://code.launchpad.net/~elachuni/ubuntu-webcatalog/convoy/+merge/95963
>
> Overview
> ========
> This branch adds a combo loader view using lp:convoy to load multiple css stylesheets or javascript files.  It also plugs this into YUI so that all the javascript YUI pulls in is piled into a single request.

Excellent! All looks great... I had a few questions/thoughts below
inline that we may want to think about, but approving as is.

>
> Details
> =======
> This improvement comes from a recent discussion on canonical-tech, the view is largely based on Sidnei's combo view for Django.
> Using the combo loader all pages need at least three requests less (as the four stylesheets pulled in by base.html are
> all requested together), and the application details page goes down from 34 requests to 8.
>
> This change is part of a sequence of three MPs to add a nifty "exhibits" widget to the front page.  The next branch will add the YUI3 carrousel widget.
>
> --
> https://code.launchpad.net/~elachuni/ubuntu-webcatalog/convoy/+merge/95963
> You are subscribed to branch lp:ubuntu-webcatalog.
>
> === modified file '.bzrignore'
> --- .bzrignore  2011-09-15 10:53:40 +0000
> +++ .bzrignore  2012-03-05 18:12:18 +0000
> === modified file 'django_project/config/main.cfg'
> --- django_project/config/main.cfg      2012-03-05 18:12:17 +0000
> +++ django_project/config/main.cfg      2012-03-05 18:12:18 +0000
> @@ -50,7 +50,7 @@
>                               webcatalog.context_processors.google_analytics_id
>                               webcatalog.context_processors.user_agent
>
> -static_root = ./django_project/static/
> +static_root = ./src/webcatalog/static/

Hrm.. it sounds like we're not using this in the way it's meant to be
used... right, from memory we haven't started using the static files
support really. static_root should be point at where we want static
files collated when running the collectstatic management command:

https://docs.djangoproject.com/en/1.3/howto/static-files/#deploying-static-files-in-a-nutshell

Although maybe that's fine for development... might be worth us
reviewing the way we're handling these static files.

>  static_url = /assets/
>  admin_media_prefix = /assets/admin/
>
>
> === modified file 'fabtasks/bootstrap.py'
> --- fabtasks/bootstrap.py       2012-01-06 14:00:18 +0000
> +++ fabtasks/bootstrap.py       2012-03-05 18:12:18 +0000
> @@ -125,6 +125,8 @@
>     _symlink(
>         "branches/django-openid-auth/django_openid_auth",
>         "django_project/django_openid_auth")
> +    _get_or_pull_bzr_branch("lp:convoy", "convoy", revision=20)
> +    _symlink("branches/convoy/convoy", "django_project/convoy")

And it's available as a package to install for deploy? If so, we
should be updating the dependencies too right (that's a separate
branch now isn't it.. ...

Revision history for this message
Michael Nelson (michael.nelson) :
review: Approve
Revision history for this message
ISD Branch Mangler (isd-branches-mangler) wrote :

The prerequisite lp:~elachuni/ubuntu-webcatalog/exhibit-widget has not yet been merged into lp:ubuntu-webcatalog.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2011-09-15 10:53:40 +0000
3+++ .bzrignore 2012-03-05 18:12:18 +0000
4@@ -2,6 +2,7 @@
5 src/ubuntu_webcatalog.egg-info/
6 virtualenv/
7 django_project/django_openid_auth
8+django_project/convoy
9 django_project/local_settings.py
10 django_project/static/
11 django_project/media_root_dev/*
12
13=== modified file 'django_project/config/main.cfg'
14--- django_project/config/main.cfg 2012-03-05 18:12:17 +0000
15+++ django_project/config/main.cfg 2012-03-05 18:12:18 +0000
16@@ -50,7 +50,7 @@
17 webcatalog.context_processors.google_analytics_id
18 webcatalog.context_processors.user_agent
19
20-static_root = ./django_project/static/
21+static_root = ./src/webcatalog/static/
22 static_url = /assets/
23 admin_media_prefix = /assets/admin/
24
25
26=== modified file 'fabtasks/bootstrap.py'
27--- fabtasks/bootstrap.py 2012-01-06 14:00:18 +0000
28+++ fabtasks/bootstrap.py 2012-03-05 18:12:18 +0000
29@@ -125,6 +125,8 @@
30 _symlink(
31 "branches/django-openid-auth/django_openid_auth",
32 "django_project/django_openid_auth")
33+ _get_or_pull_bzr_branch("lp:convoy", "convoy", revision=20)
34+ _symlink("branches/convoy/convoy", "django_project/convoy")
35
36 def bootstrap():
37 virtualenv_create()
38
39=== modified file 'src/webcatalog/templates/light/404.html'
40--- src/webcatalog/templates/light/404.html 2011-07-04 11:52:26 +0000
41+++ src/webcatalog/templates/light/404.html 2012-03-05 18:12:18 +0000
42@@ -5,7 +5,7 @@
43 {% block header %}{% endblock %}
44
45 {% block head_extra %}
46- <link rel="stylesheet" type="text/css" href="{{ ASSET_ROOT }}/assets/light/css/404.css"/>
47+ <link rel="stylesheet" type="text/css" href="{% url wc-combo %}?light/css/reset.css&light/css/styles.css&light/css/404.css&light/css/forms.css"/>
48 {% endblock %}
49
50 {% block content_container %}
51
52=== modified file 'src/webcatalog/templates/light/index.1col.html'
53--- src/webcatalog/templates/light/index.1col.html 2012-01-06 14:24:49 +0000
54+++ src/webcatalog/templates/light/index.1col.html 2012-03-05 18:12:18 +0000
55@@ -3,10 +3,9 @@
56 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en" dir="ltr">
57 <head>
58 <title>{% block title %}{% endblock %}</title>
59- <link rel="stylesheet" type="text/css" href="/assets/light/css/reset.css"/>
60- <link rel="stylesheet" type="text/css" href="/assets/light/css/styles.css"/>
61- {% block head_extra %}{% endblock %}
62- <link rel="stylesheet" type="text/css" href="/assets/light/css/forms.css"/>
63+ {% block head_extra %}
64+ <link rel="stylesheet" type="text/css" href="{% url wc-combo %}?light/css/reset.css&light/css/styles.css&light/css/forms.css"/>
65+ {% endblock %}
66 </head>
67 <body>
68 <div id="container">
69
70=== modified file 'src/webcatalog/templates/webcatalog/application_detail.html'
71--- src/webcatalog/templates/webcatalog/application_detail.html 2011-09-16 09:47:19 +0000
72+++ src/webcatalog/templates/webcatalog/application_detail.html 2012-03-05 18:12:18 +0000
73@@ -8,7 +8,7 @@
74 {{ block.super }}
75 <script src="{{ STATIC_URL }}yui/3.4.0/build/yui/yui-min.js"></script>
76 <script>
77-YUI().use('io-base', 'node-base', function (Y) {
78+YUI({debug: false, combine: true, comboBase: '{% url wc-combo %}?', root: 'yui/3.4.0/build/'}).use('io-base', 'node-base', function (Y) {
79 function complete(id, obj){
80 var reviews_html = obj.responseText;
81 var reviews_div = Y.one('#reviews_placeholder');
82
83=== modified file 'src/webcatalog/templates/webcatalog/base.html'
84--- src/webcatalog/templates/webcatalog/base.html 2011-04-13 19:52:23 +0000
85+++ src/webcatalog/templates/webcatalog/base.html 2012-03-05 18:12:18 +0000
86@@ -3,7 +3,7 @@
87 {% block menus %}{% endblock %}
88
89 {% block head_extra %}
90- <link rel="stylesheet" type="text/css" href="{{ STATIC_URL }}css/webcatalog.css"/>
91+ <link rel="stylesheet" type="text/css" href="{% url wc-combo %}?light/css/reset.css&light/css/styles.css&css/webcatalog.css&light/css/forms.css"/>
92 {% endblock %}
93
94 {% block search %}
95
96=== modified file 'src/webcatalog/tests/test_views.py'
97--- src/webcatalog/tests/test_views.py 2012-03-05 18:12:17 +0000
98+++ src/webcatalog/tests/test_views.py 2012-03-05 18:12:18 +0000
99@@ -26,6 +26,7 @@
100
101 from django.conf import settings
102 from django.core.urlresolvers import reverse
103+from django.test import TestCase
104
105 from mock import patch
106 from rnrclient import ReviewDetails
107@@ -42,6 +43,7 @@
108 'IndexTestCase',
109 'OverviewTestCase',
110 'SearchTestCase',
111+ 'ComboViewTestCase',
112 ]
113
114
115@@ -638,3 +640,35 @@
116 response, 'webcatalog/application_review_list_snippet.html')
117 self.assertContains(response, 'review_summary1')
118 self.assertContains(response, 'review_summary2')
119+
120+
121+class ComboViewTestCase(TestCase):
122+ """Tests for ComboView."""
123+
124+ def test_no_fnames_in_request(self):
125+ """If there are no filenames in the request, a 404 is returned."""
126+ response = self.client.get(reverse('wc-combo'))
127+ self.assertEqual(404, response.status_code)
128+ self.assertEqual('text/plain', response['Content-Type'])
129+
130+ def test_content_type_for_js(self):
131+ """
132+ If the requested files are JS files, the response content-type will be
133+ set to 'text/javascript'.
134+ """
135+ response = self.client.get(reverse('wc-combo') + '?js/utils.js')
136+ self.assertEqual(200, response.status_code)
137+ self.assertEqual('text/javascript', response['Content-Type'])
138+ expected = '/* js/utils.js */\n/* [missing] */\n'
139+ self.assertEqual(expected, response.content)
140+
141+ def test_content_type_for_css(self):
142+ """
143+ If the requested files are CSS files, the response content-type will be
144+ set to 'text/css'.
145+ """
146+ response = self.client.get(reverse('wc-combo') + '?js/foo.css')
147+ self.assertEqual(200, response.status_code)
148+ expected = '/* js/foo.css */\n/* [missing] */\n'
149+ self.assertEqual('text/css', response['Content-Type'])
150+ self.assertEqual(expected, response.content)
151
152=== modified file 'src/webcatalog/urls.py'
153--- src/webcatalog/urls.py 2011-09-15 07:16:30 +0000
154+++ src/webcatalog/urls.py 2012-03-05 18:12:18 +0000
155@@ -45,7 +45,7 @@
156 'application_reviews', name="wc-package-reviews"),
157 url(r'^search/$', 'search', name="wc-search"),
158 url(r'^search/(?P<distro>[-.+\w]+)/$', 'search', name="wc-search"),
159+ url(r'^combo/$', 'combo_view', name='wc-combo'),
160
161 (r'^api/', include('webcatalog.api.urls')),
162-
163 )
164
165=== modified file 'src/webcatalog/views.py'
166--- src/webcatalog/views.py 2012-03-05 18:12:17 +0000
167+++ src/webcatalog/views.py 2012-03-05 18:12:18 +0000
168@@ -23,14 +23,16 @@
169 )
170
171 import operator
172+import os
173 from random import randint
174 from urllib import urlencode
175
176+from convoy.combo import combine_files, parse_qs
177 from django.conf import settings
178 from django.core.paginator import Paginator
179 from django.core.urlresolvers import reverse
180 from django.db.models import Q
181-from django.http import HttpResponseRedirect
182+from django.http import HttpResponseRedirect, HttpResponse
183 from django.shortcuts import (
184 get_object_or_404,
185 render_to_response,
186@@ -203,3 +205,23 @@
187 template = 'webcatalog/application_review_list.html'
188
189 return render_to_response(template, RequestContext(request, context))
190+
191+
192+def combo_view(request):
193+ """Handle a request for combining a set of files."""
194+ fnames = parse_qs(request.META.get("QUERY_STRING", ""))
195+ content_type = "text/plain"
196+
197+ if fnames:
198+ if fnames[0].endswith(".js"):
199+ content_type = "text/javascript"
200+ elif fnames[0].endswith(".css"):
201+ content_type = "text/css"
202+ content = combine_files(fnames, os.path.abspath(settings.STATIC_ROOT),
203+ resource_prefix=settings.MEDIA_URL, rewrite_urls=True)
204+ # We're turning the generator returned by combine_files into a string
205+ # here since GZipMiddleware would consume it if not. See Bug #822888.
206+ return HttpResponse(
207+ content_type=content_type, status=200,
208+ content="".join(content))
209+ return HttpResponse(content_type=content_type, status=404)

Subscribers

People subscribed via source and target branches