python-testing-style-guide
This document is a draft style guide for writing Python tests (and Django tests). It is meant to be expanded and refined over time.
Many of the recommendations stand in stark contrast to conventions for good code. This is deliberate: tests are not regular code and have a much different set of risks and rewards.
If you're curious why a particular practice is recommended, get someone to defend or explain it (there's a chance it's wrong and needs to be updated). That said, a lot of these recommendations come from specific mistakes and scars earned over many years of work, so don't be surprised if they're passionately defended.
Prerequisites
PEP8 first
Start with PEP8 and strive to understand why it makes the recommendations it does. We like nearly all of it except for the Maximum Line Length recommendation.
Lint in your editor
Configure the Python linting plugin for your editor and use it every time without exception.
Prefer imperfect tests to no tests
Tests should (almost) always accompany code we write professionally. We're fortunate to work in an environment where testing is actively encouraged, so don't waste that priveledge. We get away with all sorts of other efficiencies and laziness because of our sincere dedication to pragmatic and practical testing.
The practice of testing
Tox
Every package should have setup instructions for a developer in the README
and should be testable using tox
after setup. Use 100% test success to
confirm your setup is complete.
Master should be 100%
Strive to never push a failing test to master. If you want to push failing tests to another branch, that's fine (in fact often encouraged).
Run tests often
We run our tests continuously with Jenkins (every morning and after every commit), but you still need to run them locally before pushing code.
Use TDD if it makes sense
Some people do TDD often, sometimes, or rarely. Figure out when it's an effective habit for you.
Pay attention to coverage
Remember the QA team
We are fortunate in that we have a smart and effective QA team. Some functionality simply cannot be effectively tested, which can feel frustrating to a developer. Before spending too much time on a set of tests of limited benefit, consider whether the issue should be
Delete dead or misleading tests aggressively
Do not be shy about removing tests as soon as they start to become false or unhelpful.
Focus on tests during code reviews
Reproduce before testing
For regressions, you should always make sure you can reproduce the issue in a browser as a typical user before writing a single line of code (test or otherwise). After you can reproduce it, try to get a failing test to reliably reproduce it (if possible). Only then is it a good time to start writing the fix.
Always "Click the thing"
No developer should ever rely on tests alone. Before resolving an issue, make sure you have walked through it at least once in a browser as a typical user.
Write regression tests whenever possible
Fail tests first
How do you know if it is actually testing anything if the assert
never failed?
Write descriptive failure messages
When tests fail they should tell you exactly why.
Yes:
response = self.client.post('/api/v1/data/', data=SAMPLE)
assert response.status_code == status.HTTP_201_CREATED, "expected HTTP_201, got HTTP_{} data: {}".format(response.status_code, response.data)
This results in a message which explains exactly what was expected and why the test failed:
> assert response.status_code == status.HTTP_201_CREATED, "expected HTTP_201, got HTTP_{} data: {}".format(response.status_code, response.data)
E AssertionError: expected HTTP_201, got HTTP_400 data: {'name': [u'This field is required.']}
E assert 400 == 201
E + where 400 = <rest_framework.response.Response object at 0x1070bbb10>.status_code
E + and 201 = status.HTTP_201_CREATED
No:
response = self.client.post('/api/v1/data/', data=SAMPLE)
assert response.status_code == status.HTTP_201_CREATED
This results in a message which isn't very helpful in diagnosing the test failure:
> assert response.status_code == status.HTTP_201_CREATED
E AssertionError: assert 201 == 400
E + where 400 = <rest_framework.response.Response object at 0x1070bbb10>.status_code
E + and 201 = status.HTTP_201_CREATED
Prefer fewer asserts per test
Read through the "One Assert per Test" section of Robert Martin's Clean Code. In fact, read the entire chapter
Structure
Prefer small tests
Separate tests into different files
Tests are generally structured to mirror the file layout of the modules they are testing. It is OK to group tests for small modules or to separate targeted tests for a single module across many files.
Follow import conventions
Imports should be grouped in the following order (extends PEP8 rules):
- Python standard library imports
- Public library imports
- Django imports
- Testing library imports
- Internal libary imports
- Local application imports
Each import line must be in alphabetical order inside its group.
import datetime
import inspect
import logging
import uuid
import os
from lxml import etree
from lxml.html import document_fromstring, html5parser
from django.contrib.auth import get_user_model
from django.core.urlresolvers import reverse
from django.test import TestCase
from django.test.client import Client
from django.views.defaults import server_error
import mock
from nose import SkipTest
from nose.tools import assert_equals, assert_in
import nest.models
from nest.tests.test_helper import create_document
from entice.models import UserSubject
from heron.decorators import anyone_allowed
from heron.models import Trial
from heron.urls import urlpatterns
from tools.urls import traverse_urls
Write to be read
Our tests are often the first thing other developers read to try to understand our code. They'll also often be the first thing we read when something is on fire and we need to fix a bug. Strive for readability in tests.
Always write a docstring
Every single test should have a human-readable docstring. The docstring should follow the pattern of the rest of the module.
See the PEP guidelines for more info on docstring conventions.
Use "should" in every docstring
Tests are often best when they focus on what behavior "should" happen in terms of important actors in the system. A good mental trick to keep tests in this style is to use "should" in every docstring.
In addition, try to use a known actor or object as the subject of the docstring.
Keep the number of words before should (or "should not") to a minimum to improve clarity.
'''The Manage Users page should show a button to revoke access to the site for each User'''
'''The Manage Users page should show Users with obscured emails if they are associated with a demo Account'''
'''A Tutorial should be able to exist in multiple Groups'''
'''A European User should not be required to enter a state or province'''
Prefer descriptive variables
A reader may have no idea what is happening inside your test. Help them by binding to descriptive variables.
Yes:
reset_url = reverse('django.contrib.auth.views.password_reset_confirm', kwargs={'uidb36': self.user.id, 'token': token})
absolute_reset_url = "http://{}.{}{}'.format(self.account.subdomain, settings.BASE_SITE, reset_url)
expected = "Set your personal password: {}".format(absolute_reset_url)
self.assertOutboxContainsBody(expected)
No:
self.assertOutboxContainsBody(
'Set your personal password: http://{subdomain}.{base_site}{url}'.format(
subdomain=self.account.subdomain, base_site=settings.BASE_SITE,
url=reverse('django.contrib.auth.views.password_reset_confirm',
kwargs={'uidb36': self.user.id, 'token': expected})))
Expectations
Always bind an expected
value
We always bind an expected
value because then the reader has no doubt of what
is happening inside our asserts. Conventions like this allow us to focus on
what's different about the code instead of being distracted by eccentricities
of each implementation.
 This can improve readability ("What does this JSON response actually look like in practice?")
and ensure that tests don't accidentally test nothing.
Yes:
expected = ["2001-09-01",
"2002-12-30",
"2003-08-20",
"2003-08-29",
]
self.assertEquals(expected, dates)
No:
expected = [lesson.started for lesson in self.lessons] # Oops, self.lessons was empty and I just tested nothing
self.assertEquals(expected, dates)
Things that people will yell about
Never print
Use logging
instead of print
in your code.
Learn how to make the logger work for you rather than against you.
This should appear in every one of your files:
import logging
log = logging.getLogger(__name__)
Unusual testing conventions
Repeat yourself
Django
Avoid fixtures
Use CSS selectors
More people can read and write CSS selectors than XPath or alternatives.
Prefer extremely targeted functional tests
Use t-
CSS classes
Every single test that relies on specific HTML markup must use/add a CSS class starting with t-
to the required HTML elements.
This declarative style allows the HTML and CSS to be refactored without impacting tests (even extremely specific ones).
num_revoke_buttons = len(nodes.cssselect(".t-user-table .t-revoke"))
Consider templatetags
Templatetags are often much easier to test (especially in isolation) than the equivalent functional test.
Testing tools and techniques
Use mocks with care
Enjoy assertRaises
How to avoid traps
Really think about boundary values
Test the obvious positive and negative cases separately
Introduce some randomness
We introduce randomness to our tests to make sure we're handling a range of inputs sanely.
The two most common patterns for introducing randomness are using uuid.uuid4()
for string values
and random.randint()
.
self.epub = nest.models.EpubArchive.objects.create(identifier=str(uuid.uuid4()))
bit = self.epub.htmlfile_set.create(filename="first.html", virtual_pages=random.randint(1, 100))
how_many = random.randint(10, 100)
for i in range(how_many):
...
Warning: Randomness means that some test failures will become hard to reproduce. This is usually an acceptable cost.
Introduce some Unicode
When setting up expected values or string inputs, try to remember to include strange Unicode characters.
expected = [u"R",
u"å",
u"n",
u"d",
u"☃",
u"ɯ"]
Resources
- General, but excellent guidance on writing unit tests (language agnostic): Ch. 9 "Unit Tests" from Clean Code
- A gentle introduction to the feel of testing (Django focus): Test-Driven Web Development with Python
- The classic description of TDD (Java examples, oh well): Test Driven Development: By Example
- Another classic focused on explaining how testing works in the real world (more Java examples, oh well): Test Driven Development: By Example
- Recent take on testing lessons from a mature project: Testing Django Projects at Scale, a PyCon CA 2013 video
- Thoughts on mocking versus faking: Stop mocking, start testing, a writeup with links to a PyCon 2012 video
- A presentation to convince you to start testing: Getting Started Testing your Python


--- layout: default title: About header: Hi, I'm Owen 👋 description: Owen Williams is a UX Writer, developer and marketing strategist in Victoria, British Columbia, Canada. permalink: /about/ image: "/images/card.png" seo: type: Person ---
I'm a technologist with experience wearing different hats, as a people lead, writer and developer.
I've been in the trenches as a developer and writer, so I know the tech jargon that matters, from Node to AWS.
Today, I'm a Product Design Manager at Stripe in Victoria, British Columbia, leading a multi-discipline UX team building tools developers love.
Before Stripe and Shopify, I ran a freelance UX strategy and consulting business.
It started with Charged in 2013, a weekly newsletter delivered to 15,000 people, and grew into helping others with UX strategy for more than three years.
I live in Victoria, British Columbia, with my partner and our fluffy scottish-straight cat, Mika.
Before we moved to Canada in 2019, we lived in Amsterdam, The Netherlands for 5 years, but I grew up in Wellington, New Zealand (we invented the flat white).
When I'm not working, I love to read, bake, and hike. I'm a little obsessed with finding new music, too.
I'm an avid Twitter user, and joined the service in 2008. I didn't get my short username until much later, but that's a story for in person.


All about the OBF
The Open Bioinformatics Foundation is a non-profit, volunteer-run group for Open Source software development and Open Science within the biological research community.
The latest happenings in the OBF Community
Travel Fellowships
The OBF Travel Fellowship program aims at increasing diverse participation at events promoting open source bioinformatics software development and open science in the biological research community.


Hilary McReynolds
Hilary McReynolds
I am the instructor of this course. I am originally from Canada, and have lived in the United States
and England as well. I came to the UK in 2011 for graduate school, and then decided to stay here
for the foreseeable future. I have worked as a Computer Science Teacher, Mathematics Teacher and a
Software Engineer here in London.
A little about my Software skills
- General web development (HTML / CSS / Javascript...)
- Web development with React framework
- Full stack Java development


Hi Dennis,
Thanks for ur Rsponse, However my problem was solved. I recompiled the
Xsd2jibx java files and created new jars using the Jibx version i had.
On 1/25/07, Dennis Sosnoski <[EMAIL PROTECTED]> wrote:
Hi Rohan,
The Xsd2Jibx tool is very outdated and no longer maintained. You'll need
to download an old version of JiBX (try jibx-1.0RC1, since that's from
the same timeframe as the last Xsd2Jibx release) to make this work.
I am actively working on a replacement, and hope to have it available
next month.
- Dennis
Dennis M. Sosnoski
SOA and Web Services in Java
Training and Consulting
http://www.sosnoski.com - http://www.sosnoski.co.nz
Seattle, WA +1-425-939-0576 - Wellington, NZ +64-4-298-6117
Rohan wrote:
> Hi ppl,
> I am getting the following exception whenever i am trying to
> run the *xsd2jibx* jar.
>
>
> log4j:WARN No appenders could be found for logger
> (org.jibx.xsd2jibx.FileXsdResolver).
>
> log4j:WARN Please initialize the log4j system properly.
>
> _
>
> org.jibx.xsd2jibx.GeneratorException
>
> _: unable to unmarshall
>
> Root cause:
>
> _org.jibx.runtime.JiBXException_: Unable to access binding information
> for class org.jibx.xsd2jibx.XsdSchema
>
> Make sure the binding has been compiled
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:380_)
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:254_)
>
> at org.jibx.xsd2jibx.Generator.execute(
>
> _Generator.java:170_)
>
> at org.jibx.xsd2jibx.Generate.generate(
>
> _Generate.java:131_)
>
> at org.jibx.xsd2jibx.Generate.main(
>
> _Generate.java:181_)
>
> Caused by: Unable to access binding information for class
> org.jibx.xsd2jibx.XsdSchema
>
> Make sure the binding has been compiled
>
> _
>
> java.lang.NoSuchFieldException
>
> _: JiBX_bindingList
>
> at java.lang.Class.getDeclaredField(
>
> _Class.java:1208_)
>
> at org.jibx.runtime.BindingDirectory.getBindingList(
>
> _BindingDirectory.java:70_)
>
> at org.jibx.runtime.BindingDirectory.getFactory(
>
> _BindingDirectory.java:189_)
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:376_)
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:254_)
>
> at org.jibx.xsd2jibx.Generator.execute(
>
> _Generator.java:170_)
>
> at org.jibx.xsd2jibx.Generate.generate(
>
> _Generate.java:131_)
>
> at org.jibx.xsd2jibx.Generate.main(
>
> _Generate.java:181_)
>
> Cause: Unable to access binding information for class
> org.jibx.xsd2jibx.XsdSchema
>
> Make sure the binding has been compiled
>
> _
>
> java.lang.NoSuchFieldException
>
> _: JiBX_bindingList
>
> at java.lang.Class.getDeclaredField(
>
> _Class.java:1208_)
>
> at org.jibx.runtime.BindingDirectory.getBindingList(
>
> _BindingDirectory.java:70_)
>
> at org.jibx.runtime.BindingDirectory.getFactory(
>
> _BindingDirectory.java:189_)
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:376_)
>
> at org.jibx.xsd2jibx.XsdSchema.unmarshall(
>
> _XsdSchema.java:254_)
>
> at org.jibx.xsd2jibx.Generator.execute(
>
> _Generator.java:170_)
>
> at org.jibx.xsd2jibx.Generate.generate(
>
> _Generate.java:131_)
>
> at org.jibx.xsd2jibx.Generate.main(
>
> _Generate.java:181_)
> Can anyone of u please help me resolve this exception
>
> Thanks,
> Rohan
>
> ------------------------------------------------------------------------
>
>
-------------------------------------------------------------------------
> Take Surveys. Earn Cash. Influence the Future of IT
> Join SourceForge.net's Techsay panel and you'll get the chance to share
your
> opinions on IT & business topics through brief surveys - and earn cash
>
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
> ------------------------------------------------------------------------
>
> _______________________________________________
> jibx-users mailing list
><EMAIL_ADDRESS>> https://lists.sourceforge.net/lists/listinfo/jibx-users
>
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share
your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
jibx-users mailing list
<EMAIL_ADDRESS>https://lists.sourceforge.net/lists/listinfo/jibx-users
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
jibx-users mailing list
<EMAIL_ADDRESS>https://lists.sourceforge.net/lists/listinfo/jibx-users


layout: post title: “NS-58 Launch Announcement” date: 2016-09-07 00:00:00 -0400 categories: NS-58
Fall semester has arrived! The first launch of the semester is scheduled to take place on September 17, around 8:00 AM from Clear Springs, MD, weather permitting.
The current payload lineup is Helios, an altitude control system, Command Module, the main tracking and telemetry system, Bach’s Box, the weather payload, IRENE, a radiation-measuring payload, SCORCH / MARS, two cut-down payloads, DataPigeon, a droppable data carrier, and Cloud360, a cloud-centric CapTech payload.
Join Us! Balloon Launch Details
When:
Saturday, September 17, 2016
Where:
Clear Spring Elementary School 12627 Broadfording Rd Clear Spring, Maryland 21722
Questions?
Contact Dr. Mary Bowden
Email<EMAIL_ADDRESS>Phone:<PHONE_NUMBER>
Live Updates
You can follow our live tweetup of the launch day here.
You can also track us on the APRS website using UMD’s callsign: W3EAX-8.
The NearSpace High Altitude Balloon Team thanks the Maryland Space Grant for its continued support and effort to make our program possible.


--- layout: documentation title: ICMPv6InfoMessageContentType ---
Elements associated with ICMPv6 informational messages (as opposed to ICMP v6 error messages).
| Field Name | Type | Description |
|---|---|---|
| Identifier0..1 | HexBinaryObjectPropertyType |
16-bit identifier. Combined with the sequence number, called the "quench" for echo reply and echo request. |
| Sequence_Number0..1 | HexBinaryObjectPropertyType |
16-bit sequence number. The identifier and sequence number can be used by the client to match the reply with the request that caused the reply. |


A hackathon is an overnight coding event in which teams spend the weekend “hacking” together to create an awesome project like a game, website, or app! Hackathons are a great way to learn more about coding, meet new people, and build something cool. Check out Dave Fontenot's post on "What is a hackathon?" to learn more!
Then you’ve come to the right place! hackBCA is open to students of all levels and beginners are encouraged to come. Hackathons provide an awesome opportunity to dive into programming. At hackBCA, workshops and mentors will help you get started with the basics of programming and you’ll leave our hackathon having learned a lot.
You should bring your laptop or desktop, school ID, phone, charging cables, and any hardware you plan on using. Power strips, personal hygiene products, and an extra set of clothes are also recommended. Mainly, bring your awesome self!
It’s free! And food will be provided free of cost. We will consider sending a bus to your area with this form. Unfortunately, we will not be reimbursing any other travel arrangements for this year's event.
Any student in high school can attend! You do not have to attend BCA or be from the US. If you need help convincing your parents, contact us at<EMAIL_ADDRESS>You can work on a project individually or with a team. You can come as a team, or form a team at the event. For more information on teams, check out our rules.
Nope! Please don’t start your hack until after the kickoff ceremony if you plan to submit it for judging.
You must submit your hack by 11:30 AM on Sunday, but you can still work on them after 11:30 AM. We will be sending out the details of where to submit your hack on Sunday.


The answer to each puzzle is a state name, so only letters will be used. To start, guess any letter. If you choose a correct letter, it will appear in the puzzle. If the letter you choose is not used in the puzzle, your remaining guesses will decrease by one. You will win by guessing all of the correct letters before the remaining guesses teaches zero.


SessionCredentials
class SessionCredentials
Methods
__construct(array $params, SenderCredentials $senderCreds)
Initializes the class with the given parameters.
string
getSessionId()
Get Intacct session ID
getEndpoint()
Get endpoint
getSenderCredentials()
Get sender credentials
string
getCurrentCompanyId()
Get session's current company ID
string
getCurrentUserId()
Get session's current user ID
bool
getCurrentUserIsExternal()
Get session's current user is external or not
MockHandler
getMockHandler()
Get mock handler for unit testing
LoggerInterface
getLogger()
No description
getLogMessageFormat()
No description
int
getLogLevel()
No description
Details
at line line 84
__construct(array $params, SenderCredentials $senderCreds)
Initializes the class with the given parameters.


001/* 002 * Copyright 2008 Les Hazlewood 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.apache.shiro.session.mgt; 017 018import org.apache.shiro.session.InvalidSessionException; 019 020import java.util.Collection; 021import java.util.Date; 022 023/** 024 * A {@code Native} session manager is one that manages sessions natively - that is, it is directly responsible 025 * for the creation, persistence and removal of {@link org.apache.shiro.session.Session Session} instances and their 026 * lifecycles. 027 * 028 * @since 1.0 029 */ 030public interface NativeSessionManager extends SessionManager { 031 032 /** 033 * Returns the time the associated {@code Session} started (was created). 034 * 035 * @param key the session key to use to look up the target session. 036 * @return the time the specified {@code Session} started (was created). 037 * @see org.apache.shiro.session.Session#getStartTimestamp() 038 */ 039 Date getStartTimestamp(SessionKey key); 040 041 /** 042 * Returns the time the associated {@code Session} last interacted with the system. 043 * 044 * @param key the session key to use to look up the target session. 045 * @return time the session last accessed the system 046 * @see org.apache.shiro.session.Session#getLastAccessTime() 047 * @see org.apache.shiro.session.Session#touch() 048 */ 049 Date getLastAccessTime(SessionKey key); 050 051 /** 052 * Returns {@code true} if the associated session is valid (it exists and is not stopped nor expired), 053 * {@code false} otherwise. 054 * 055 * @param key the session key to use to look up the target session. 056 * @return {@code true} if the session is valid (exists and is not stopped or expired), {@code false} otherwise. 057 */ 058 boolean isValid(SessionKey key); 059 060 /** 061 * Returns quietly if the associated session is valid (it exists and is not stopped or expired) or throws 062 * an {@link org.apache.shiro.session.InvalidSessionException} indicating that the session id is invalid. This 063 * might be preferred to be used instead of {@link #isValid} since any exception thrown will definitively explain 064 * the reason for invalidation. 065 * 066 * @param key the session key to use to look up the target session. 067 * @throws org.apache.shiro.session.InvalidSessionException 068 * if the session id is invalid (it does not exist or it is stopped or expired). 069 */ 070 void checkValid(SessionKey key) throws InvalidSessionException; 071 072 /** 073 * Returns the time in milliseconds that the associated session may remain idle before expiring. 074 *
 075 *
A negative return value means the session will never expire.
 076 *
A non-negative return value (0 or greater) means the session expiration will occur if idle for that 077 * length of time.
 078 *
 079 * 080 * @param key the session key to use to look up the target session. 081 * @return the time in milliseconds that the associated session may remain idle before expiring. 082 * @throws org.apache.shiro.session.InvalidSessionException 083 * if the session has been stopped or expired prior to calling this method. 084 */ 085 long getTimeout(SessionKey key) throws InvalidSessionException; 086 087 /** 088 * Sets the time in milliseconds that the associated session may remain idle before expiring. 089 *
 090 *
A negative return value means the session will never expire.
 091 *
A non-negative return value (0 or greater) means the session expiration will occur if idle for that 092 * length of time.
 093 *
 094 * 095 * @param key the session key to use to look up the target session. 096 * @param maxIdleTimeInMillis the time in milliseconds that the associated session may remain idle before expiring. 097 * @throws org.apache.shiro.session.InvalidSessionException 098 * if the session has been stopped or expired prior to calling this method. 099 */ 100 void setTimeout(SessionKey key, long maxIdleTimeInMillis) throws InvalidSessionException; 101 102 /** 103 * Updates the last accessed time of the session identified by sessionId. This 104 * can be used to explicitly ensure that a session does not time out. 105 * 106 * @param key the session key to use to look up the target session. 107 * @throws org.apache.shiro.session.InvalidSessionException 108 * if the session has been stopped or expired prior to calling this method. 109 * @see org.apache.shiro.session.Session#touch 110 */ 111 void touch(SessionKey key) throws InvalidSessionException; 112 113 /** 114 * Returns the host name or IP string of the host where the session was started, if known. If 115 * no host name or IP was specified when starting the session, this method returns {@code null} 116 * 117 * @param key the session key to use to look up the target session. 118 * @return the host name or ip address of the host where the session originated, if known. If unknown, 119 * this method returns {@code null}. 120 */ 121 String getHost(SessionKey key); 122 123 /** 124 * Explicitly stops the associated session, thereby releasing all of its resources. 125 * 126 * @param key the session key to use to look up the target session. 127 * @throws InvalidSessionException if the session has stopped or expired prior to calling this method. 128 * @see org.apache.shiro.session.Session#stop 129 */ 130 void stop(SessionKey key) throws InvalidSessionException; 131 132 /** 133 * Returns all attribute keys maintained by the target session or an empty collection if there are no attributes. 134 * 135 * @param sessionKey the session key to use to look up the target session. 136 * @return all attribute keys maintained by the target session or an empty collection if there are no attributes. 137 * @throws InvalidSessionException if the associated session has stopped or expired prior to calling this method. 138 * @see org.apache.shiro.session.Session#getAttributeKeys() 139 */ 140 Collection<Object> getAttributeKeys(SessionKey sessionKey); 141 142 /** 143 * Returns the object bound to the associated session identified by the specified attribute key. If there 144 * is no object bound under the attribute key for the given session, {@code null} is returned. 145 * 146 * @param sessionKey session key to use to look up the target session. 147 * @param attributeKey the unique name of the object bound to the associated session 148 * @return the object bound under the {@code attributeKey} or {@code null} if there is no object bound. 149 * @throws InvalidSessionException if the specified session has stopped or expired prior to calling this method. 150 * @see org.apache.shiro.session.Session#getAttribute(Object key) 151 */ 152 Object getAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException; 153 154 /** 155 * Binds the specified {@code value} to the associated session uniquely identified by the {@code attributeKey}. 156 * If there is already a session attribute bound under the {@code attributeKey}, that existing object will be 157 * replaced by the new {@code value}. 158 *
 159 * If the {@code value} parameter is null, it has the same effect as if the 160 * {@link #removeAttribute(SessionKey sessionKey, Object attributeKey)} method was called. 161 * 162 * @param sessionKey the session key to use to look up the target session. 163 * @param attributeKey the key under which the {@code value} object will be bound in this session 164 * @param value the object to bind in this session. 165 * @throws InvalidSessionException if the specified session has stopped or expired prior to calling this method. 166 * @see org.apache.shiro.session.Session#setAttribute(Object key, Object value) 167 */ 168 void setAttribute(SessionKey sessionKey, Object attributeKey, Object value) throws InvalidSessionException; 169 170 /** 171 * Removes (unbinds) the object bound to associated {@code Session} under the given {@code attributeKey}. 172 * 173 * @param sessionKey session key to use to look up the target session. 174 * @param attributeKey the key uniquely identifying the object to remove 175 * @return the object removed or {@code null} if there was no object bound under the specified {@code attributeKey}. 176 * @throws InvalidSessionException if the specified session has stopped or expired prior to calling this method. 177 * @see org.apache.shiro.session.Session#removeAttribute(Object key) 178 */ 179 Object removeAttribute(SessionKey sessionKey, Object attributeKey) throws InvalidSessionException; 180 181}


You grow extra arms! While this spell lasts, carried items do not count against your hand size, and for 1 movement point you may pick up any or all items in your square, including 1 treasure. Picking up a tresure does not end your turn, but you cannot carry more than 1 treasure at a time.
When punching, your punch affects all wizards and creatures within your reach.


If ever there were a spring day so perfect,
so uplifted by a warm intermittent breeze
that it made you want to throw
open all the windows in the house
and unlatch the door to the canary's cage,
indeed, rip the little door from its jamb,
a day when the cool brick paths
and the garden bursting with peonies
seemed so etched in sunlight
that you felt like taking
a hammer to the glass paperweight
on the living room end table,
releasing the inhabitants
from their snow-covered cottage
so they could walk out,
holding hands and squinting
into this larger dome of blue and white,
well, today is just that kind of day.


The theses from the 2015 graduates of the BFA Graphic Design at the KABK, The Hague. Find out more about their graduation projects at ‘While you were sleeping’.
Coaching: Marjan Brandsma, Eric Schrijver, Dirk Vis.
{body}The theses from the 2015 graduates of the BFA Graphic Design at the KABK, The Hague. Find out more about their graduation projects at ‘While you were sleeping’.
Coaching: Marjan Brandsma, Eric Schrijver, Dirk Vis.
{body}


This simple app helps you learn the layout of the piano keyboard and guitar/bass fretboard and how they relate to each other. Press any piano key or tap a guitar string on any fret and you will see the corresponding key/fret on another instrument.
For guitar/bass it also shows where the same note on other strings is located.
Click/tap once to highlight the note and click/tap it again to turn off. The same notes have the same color but notes in higher octaves have higher lightness.
This app is open-source, made by axln. For more info, please visit the app's page at GitHub.


"Three Spaniards? Adventures of those three bloody-minded soladoes?"
"Duck him again; and keep ducking him."
"Am I the same man that helped kill this whale? Doesn't the devil live for ever; who ever heard that the devil was dead? Did you ever see any parson a wearing mourning for the devil? And if the devil has a latch-key to get into the admiral's cabin, don't you suppose he can crawl into a porthole? Tell me that, Mr. Flask?"
"Do you see that mainmast there?" pointing to the ship; "well, that's the figure one; now take all the hoops in the Pequod's hold, and string along in a row with that mast, for oughts, do you see; well, that wouldn't begin to be Fedallah's age. Nor all the coopers in creation couldn't show hoops enough to make oughts enough."
"But see here, Stubb, I thought you a little boasted just now, that you meant to give Fedallah a sea-toss, if you got a good chance. Now, if he's so old as all those hoops of yours come to, and if he is going to live for ever, what good will it do to pitch him overboard—tell me that?
"No: never saw such a book; heard of it, though. But now, tell me, Stubb, do you suppose that that devil you was speaking of just now, was the same you say is now on board the Pequod?"


{% extends "layout.html" %} {% block page_title %} Example - Forms {% endblock %} {% block content %} {% include "includes/breadcrumb_v0_2.html" %} About your *benefits* Over the previous eight years have you taken benefits from your defined contrbutions schemes (DCS) or have a statement by your sceheme administrators confirming your benefits have been accessed flexibly? Yes No {% endblock %}
Over the previous eight years have you taken benefits from your defined contrbutions schemes (DCS) or have a statement by your sceheme administrators confirming your benefits have been accessed flexibly?


Intro
Proven excellence in the development of strong rapport with students, colleagues and administrators. Experienced lecturer at university and conferences with a knack for communicating audiences of all levels. Committed researcher with strong mathematical skills to develop and implement projects.
Core Skills
- Technology Enhanced Learning & Distance Education
- Nanometrology & Qualitative Methods & Data analysis
- Energy and Environmental Management
- Quality Control & Design of Experiments & Taguchi Method
- Artificial Intelligence & Fuzzy Logic
- Standardization (ESOs&ISO)
- Management System Modeling & Optimization
- Ansys, Matlab, iGrafx, n5-Solutions, CAD-CAM Systems
- Python (beginner)
Education
PhD in Mechanical Engineering, 2009-2013 Vienna University of Technology, Vienna (Austria) Dissertation: Energy Management Based Model in Advanced Manufacturing Industry Enabled by Nanotechnology Research of Interest:- High Precision Measurement (Nanometrology Laboratory)
- Nanotechnology
- Integrated Management Systems (Energy, Environment & Quality Management)
- Finite Element Method (FEM) Analysis (Ansys)
- Energy System Analysis
- Mechanical design, software development and control of a motor drive & optimization (Matlab)
- Energy Systems


Promise based HTTP client for the browser and node.js
| Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | Latest ✔ | 11 ✔ |
Using npm:
$ npm install axios
Using bower:
$ bower install axios
Using yarn:
$ yarn add axios
Using cdn:
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
Performing a GET
request
const axios = require('axios');
// Make a request for a user with a given ID
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.finally(function () {
// always executed
});
// Optionally the request above could also be done as
axios.get('/user', {
params: {
ID: 12345
}
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
})
.then(function () {
// always executed
});
// Want to use async/await? Add the `async` keyword to your outer function/method.
async function getUser() {
try {
const response = await axios.get('/user?ID=12345');
console.log(response);
} catch (error) {
console.error(error);
}
}
NOTE:
async/await
is part of ECMAScript 2017 and is not supported in Internet Explorer and older browsers, so use with caution.
Performing a POST
request
axios.post('/user', {
firstName: 'Fred',
lastName: 'Flintstone'
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});
Performing multiple concurrent requests
function getUserAccount() {
return axios.get('/user/12345');
}
function getUserPermissions() {
return axios.get('/user/12345/permissions');
}
axios.all([getUserAccount(), getUserPermissions()])
.then(axios.spread(function (acct, perms) {
// Both requests are now complete
}));
Requests can be made by passing the relevant config to axios
.
// Send a POST request
axios({
method: 'post',
url: '/user/12345',
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});
// GET request for remote image
axios({
method: 'get',
url: 'http://bit.ly/2mTM3nY',
responseType: 'stream'
})
.then(function (response) {
response.data.pipe(fs.createWriteStream('ada_lovelace.jpg'))
});
// Send a GET request (default method)
axios('/user/12345');
For convenience aliases have been provided for all supported request methods.
When using the alias methods url
, method
, and data
properties don't need to be specified in config.
Helper functions for dealing with concurrent requests.
You can create a new instance of axios with a custom config.
const instance = axios.create({
baseURL: 'https://some-domain.com/api/',
timeout: 1000,
headers: {'X-Custom-Header': 'foobar'}
});
The available instance methods are listed below. The specified config will be merged with the instance config.
These are the available config options for making requests. Only the url
is required. Requests will default to GET
if method
is not specified.
{
// `url` is the server URL that will be used for the request
url: '/user',
// `method` is the request method to be used when making the request
method: 'get', // default
// `baseURL` will be prepended to `url` unless `url` is absolute.
// It can be convenient to set `baseURL` for an instance of axios to pass relative URLs
// to methods of that instance.
baseURL: 'https://some-domain.com/api/',
// `transformRequest` allows changes to the request data before it is sent to the server
// This is only applicable for request methods 'PUT', 'POST', 'PATCH' and 'DELETE'
// The last function in the array must return a string or an instance of Buffer, ArrayBuffer,
// FormData or Stream
// You may modify the headers object.
transformRequest: [function (data, headers) {
// Do whatever you want to transform the data
return data;
}],
// `transformResponse` allows changes to the response data to be made before
// it is passed to then/catch
transformResponse: [function (data) {
// Do whatever you want to transform the data
return data;
}],
// `headers` are custom headers to be sent
headers: {'X-Requested-With': 'XMLHttpRequest'},
// `params` are the URL parameters to be sent with the request
// Must be a plain object or a URLSearchParams object
params: {
ID: 12345
},
// `paramsSerializer` is an optional function in charge of serializing `params`
// (e.g. https://www.npmjs.com/package/qs, http://api.jquery.com/jquery.param/)
paramsSerializer: function (params) {
return Qs.stringify(params, {arrayFormat: 'brackets'})
},
// `data` is the data to be sent as the request body
// Only applicable for request methods 'PUT', 'POST', and 'PATCH'
// When no `transformRequest` is set, must be of one of the following types:
// - string, plain object, ArrayBuffer, ArrayBufferView, URLSearchParams
// - Browser only: FormData, File, Blob
// - Node only: Stream, Buffer
data: {
firstName: 'Fred'
},
// `timeout` specifies the number of milliseconds before the request times out.
// If the request takes longer than `timeout`, the request will be aborted.
timeout: 1000, // default is `0` (no timeout)
// `withCredentials` indicates whether or not cross-site Access-Control requests
// should be made using credentials
withCredentials: false, // default
// `adapter` allows custom handling of requests which makes testing easier.
// Return a promise and supply a valid response (see lib/adapters/README.md).
adapter: function (config) {
/* ... */
},
// `auth` indicates that HTTP Basic auth should be used, and supplies credentials.
// This will set an `Authorization` header, overwriting any existing
// `Authorization` custom headers you have set using `headers`.
// Please note that only HTTP Basic auth is configurable through this parameter.
// For Bearer tokens and such, use `Authorization` custom headers instead.
auth: {
username: 'janedoe',
password: 's00pers3cret'
},
// `responseType` indicates the type of data that the server will respond with
// options are: 'arraybuffer', 'document', 'json', 'text', 'stream'
// browser only: 'blob'
responseType: 'json', // default
// `responseEncoding` indicates encoding to use for decoding responses
// Note: Ignored for `responseType` of 'stream' or client-side requests
responseEncoding: 'utf8', // default
// `xsrfCookieName` is the name of the cookie to use as a value for xsrf token
xsrfCookieName: 'XSRF-TOKEN', // default
// `xsrfHeaderName` is the name of the http header that carries the xsrf token value
xsrfHeaderName: 'X-XSRF-TOKEN', // default
// `onUploadProgress` allows handling of progress events for uploads
onUploadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// `onDownloadProgress` allows handling of progress events for downloads
onDownloadProgress: function (progressEvent) {
// Do whatever you want with the native progress event
},
// `maxContentLength` defines the max size of the http response content in bytes allowed
maxContentLength: 2000,
// `validateStatus` defines whether to resolve or reject the promise for a given
// HTTP response status code. If `validateStatus` returns `true` (or is set to `null`
// or `undefined`), the promise will be resolved; otherwise, the promise will be
// rejected.
validateStatus: function (status) {
return status >= 200 && status < 300; // default
},
// `maxRedirects` defines the maximum number of redirects to follow in node.js.
// If set to 0, no redirects will be followed.
maxRedirects: 5, // default
// `socketPath` defines a UNIX Socket to be used in node.js.
// e.g. '/var/run/docker.sock' to send requests to the docker daemon.
// Only either `socketPath` or `proxy` can be specified.
// If both are specified, `socketPath` is used.
socketPath: null, // default
// `httpAgent` and `httpsAgent` define a custom agent to be used when performing http
// and https requests, respectively, in node.js. This allows options to be added like
// `keepAlive` that are not enabled by default.
httpAgent: new http.Agent({ keepAlive: true }),
httpsAgent: new https.Agent({ keepAlive: true }),
// 'proxy' defines the hostname and port of the proxy server.
// You can also define your proxy using the conventional `http_proxy` and
// `https_proxy` environment variables. If you are using environment variables
// for your proxy configuration, you can also define a `no_proxy` environment
// variable as a comma-separated list of domains that should not be proxied.
// Use `false` to disable proxies, ignoring environment variables.
// `auth` indicates that HTTP Basic auth should be used to connect to the proxy, and
// supplies credentials.
// This will set an `Proxy-Authorization` header, overwriting any existing
// `Proxy-Authorization` custom headers you have set using `headers`.
proxy: {
host: '<IP_ADDRESS>',
port: 9000,
auth: {
username: 'mikeymike',
password: 'rapunz3l'
}
},
// `cancelToken` specifies a cancel token that can be used to cancel the request
// (see Cancellation section below for details)
cancelToken: new CancelToken(function (cancel) {
})
}
The response for a request contains the following information.
{
// `data` is the response that was provided by the server
data: {},
// `status` is the HTTP status code from the server response
status: 200,
// `statusText` is the HTTP status message from the server response
statusText: 'OK',
// `headers` the headers that the server responded with
// All header names are lower cased
headers: {},
// `config` is the config that was provided to `axios` for the request
config: {},
// `request` is the request that generated this response
// It is the last ClientRequest instance in node.js (in redirects)
// and an XMLHttpRequest instance the browser
request: {}
}
When using then
, you will receive the response as follows:
axios.get('/user/12345')
.then(function (response) {
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
});
When using catch
, or passing a rejection callback as second parameter of then
, the response will be available through the error
object as explained in the Handling Errors section.
You can specify config defaults that will be applied to every request.
axios.defaults.baseURL = 'https://api.example.com';
axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
// Set config defaults when creating the instance
const instance = axios.create({
baseURL: 'https://api.example.com'
});
// Alter defaults after instance has been created
instance.defaults.headers.common['Authorization'] = AUTH_TOKEN;
Config will be merged with an order of precedence. The order is library defaults found in lib/defaults.js, then defaults
property of the instance, and finally config
argument for the request. The latter will take precedence over the former. Here's an example.
// Create an instance using the config defaults provided by the library
// At this point the timeout config value is `0` as is the default for the library
const instance = axios.create();
// Override timeout default for the library
// Now all requests using this instance will wait 2.5 seconds before timing out
instance.defaults.timeout = 2500;
// Override timeout for this request as it's known to take a long time
instance.get('/longRequest', {
timeout: 5000
});
You can intercept requests or responses before they are handled by then
or catch
.
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
If you may need to remove an interceptor later you can.
const myInterceptor = axios.interceptors.request.use(function () {/*...*/});
axios.interceptors.request.eject(myInterceptor);
You can add interceptors to a custom instance of axios.
const instance = axios.create();
instance.interceptors.request.use(function () {/*...*/});
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
You can define a custom HTTP status code error range using the validateStatus
config option.
axios.get('/user/12345', {
validateStatus: function (status) {
return status < 500; // Reject only if the status code is greater than or equal to 500
}
})
You can cancel a request using a cancel token.
The axios cancel token API is based on the withdrawn cancelable promises proposal.
You can create a cancel token using the CancelToken.source
factory as shown below:
const CancelToken = axios.CancelToken;
const source = CancelToken.source();
axios.get('/user/12345', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled', thrown.message);
} else {
// handle error
}
});
axios.post('/user/12345', {
name: 'new name'
}, {
cancelToken: source.token
})
// cancel the request (the message parameter is optional)
source.cancel('Operation canceled by the user.');
You can also create a cancel token by passing an executor function to the CancelToken
constructor:
const CancelToken = axios.CancelToken;
let cancel;
axios.get('/user/12345', {
cancelToken: new CancelToken(function executor(c) {
// An executor function receives a cancel function as a parameter
cancel = c;
})
});
// cancel the request
cancel();
Note: you can cancel several requests with the same cancel token.
By default, axios serializes JavaScript objects to JSON
. To send data in the application/x-www-form-urlencoded
format instead, you can use one of the following options.
In a browser, you can use the URLSearchParams
API as follows:
const params = new URLSearchParams();
params.append('param1', 'value1');
params.append('param2', 'value2');
axios.post('/foo', params);
Note that
URLSearchParams
is not supported by all browsers (see caniuse.com), but there is a polyfill available (make sure to polyfill the global environment).
Alternatively, you can encode data using the qs
library:
const qs = require('qs');
axios.post('/foo', qs.stringify({ 'bar': 123 }));
Or in another way (ES6),
import qs from 'qs';
const data = { 'bar': 123 };
const options = {
method: 'POST',
headers: { 'content-type': 'application/x-www-form-urlencoded' },
data: qs.stringify(data),
url,
};
axios(options);
In node.js, you can use the querystring
module as follows:
const querystring = require('querystring');
axios.post('http://something.com/', querystring.stringify({ foo: 'bar' }));
You can also use the qs
library.
The qs
library is preferable if you need to stringify nested objects, as the querystring
method has known issues with that use case (https://github.com/nodejs/node-v0.x-archive/issues/1665).
Until axios reaches a 1.0
release, breaking changes will be released with a new minor version. For example 0.5.1
, and 0.5.4
will have the same API, but 0.6.0
will have breaking changes.
axios depends on a native ES6 Promise implementation to be supported. If your environment doesn't support ES6 Promises, you can polyfill.
axios includes TypeScript definitions.
import axios from 'axios';
axios.get('/user?ID=12345');
axios is heavily inspired by the $http service provided in Angular. Ultimately axios is an effort to provide a standalone $http
-like service for use outside of Angular.
MIT


SSH keys are a necessity for Python development when you are working with Git, connecting to remote servers and automating your deployments. Let's walk through how to generate SSH key pairs, which contain both a public and a private key within a single pair, on Ubuntu Linux.
Open up a new terminal window in Ubuntu like we see in the following screenshot.
The ssh-keygen
command provides an interactive command line interface for
generating both the public and private keys. Invoke ssh-keygen
with the
following -t
and -b
arguments to ensure we get a 4096 bit RSA key.
Optionally, you can also specify your email address with -C
(otherwise
one will be generated off your current Linux account):
ssh-keygen -t rsa -b 4096 -C<EMAIL_ADDRESS>The first prompt you will see asks where to save the key. However, there are actually two files that will be generated: the public key and the private key.
Generating public/private rsa key pair.
Enter file in which to save the key (/home/matt/.ssh/id_rsa):
This prompt refers to the private key and whatever you enter will also
generate a second file for the public key that has the same name and .pub
appended.
If you already have a key, you should specify a new filename. I use many SSH keys so I typically name them "test-deploy", "prod-deploy", "ci-server" along with a unique project name. Naming is one of those hard computer science problems, so take some time to come up with a system that works for you and the development team you work with!
Next you will see a prompt for an optional passphrase:
Enter passphrase (empty for no passphrase):
Whether or not you want a passphrase depends on how you will use the key. The system will ask you for the passphrase whenever you use the SSH key (although you can use ssh-agent to store the passphrase after you activate it on a system the first time) so it is more secure. However, if you are automating deployments with a continuous integration server like Jenkins then you will not want a passphrase.
Be aware that it is impossible to recover a passphrase if it is lost. Keep that passphrase safe and secure because otherwise a completely new key would have to be generated.
Enter the passphrase (or just press enter to not have a passphrase) twice. You'll see some output like the following:
Your identification has been saved in /home/matt/.ssh/prod_deploy.
Your public key has been saved in /home/matt/.ssh/prod_deploy.pub.
The key fingerprint is:
SHA256:xoCWgk40XfM5mruZQNCVoBKXZ4d0gn09ivVENacb7xw matt@ubuntu
The key's randomart image is:
+---[RSA 2048]----+
|.oo*==oo..o . |
|.+*.*** = + |
|o+.++=.B .o |
|+ .o. +oo + |
| . . o S . E |
| . .. o . |
| . . o |
| . + |
| + |
+----[SHA256]-----+
Your SSH key is now generated and ready to use!
Now that you have your public and private keys, I recommend setting up a Python development environment with one of the following tutorials so you can start coding:
Additional ssh-keygen
command resources:
Questions? Contact me via Twitter @fullstackpython or @mattmakai. I'm also on GitHub with the username mattmakai.
Something wrong with this post? Fork this page's source on GitHub.


It seems like the awareness of this concept of neuroplasticity might provide a different mindset when encountering difficulty in a challenging task. This understanding might just be the motivation needed to push through. A accelerationist approaching to brain. Whereas growth mindset is the belief that our brains have the abilities to constantly grow and evolve. Therefore, instead of regarding current capabilities are fixed and 'the way it is', growth mindset entails holding a more generative attitude towards difficult tasks.
Having been diagnosed with ADHD, I feel like there are times that I use the diagnoses as an excuse and barrier to justify not being organized, not fulfilling promises or the impulsive decision to just give up. The principals of neuroplasticity gives me a different/new way of coping with life with this disorder. A continuous process of new beginnings and growth. Perhaps what would be useful would be staying with tasks that I don't normally do or perceive as difficult/impossible. In the context of ADHD this could be training for my attention, reading and general organization.


My voyage to Aragon
Chapter 1. On the
Occasion of her Ladyshipís Sixth Birthday
Once upon a time, a little princess called Anna sat at
her parentsí feet, and wondered what it was all about. Turning six, that
is. What did it mean to be six?
Well, she was about to find out everything she needed
to know about being six. Right in front of her was the most strangely dressed
man she had ever seen Ė with a big pointy hat of red, yellow and green, and
funny looking shoes with bells on his toes, and he had with him a beautiful
gold guitar!
Little Anna stared at him, and he began to
speak. "Dear Anna, I am Theodore" he said, "and I am your
humble troubadour."
And so the story beganÖ
I have a story for you Anna , for your very special
day.  Itís the story of what happened when a little princess went
to Aragon.
 Itís a story of weird things and wild beasts, and it will
tell all you need to know about all the things you do not know.
Now hereís what happened when Anna went to
Aragon.
It was a very special day, and the King and Queen were
very proud. They couldnít believe their little girl had already turned six!
But now it was time for her to leave the castle, and leave behind all her games
and toys, and set out to learn about the world.
The King would not hear of his daughter growing up to
be a silly lady. It was time for little princess Anna to go to school.
Anna chose a little pony named Sage to carry her on her journey, and they got
him ready. Little Sage had a bubble bath, and four brand new shoes. They
lifted little Anna onto the saddle, and music played, and everyone began to
cheer. When Anna rode out of the castle gates on little
Sage, his hooves going clippity-clop on the cobblestones, the King filled with
pride, and the Queen waved her hanky and cried big, silvery tears.
Chapter 2. The Trouble Begins
Anna and Sage rode along the winding lanes,
across meadows full or wild flowers, and crossed little stone bridges over
babbling brooks. They passed the time making up songs for each other,
singing happily all the way.
They followed the road into the dark close trees of
the forest, and the air grew cold and damp. Anna shivered, and
wished she were back among her friends at the castle, playing in the
sunshine. Growing up is hard, she thought.
They reached a crossroads, but there was no
sign. "Which way do we go?"  You always know whatís best."
Sage snorted and shook his head, and swished his tail.
"I really do not know," he said, "and I do not know how to find
out."
Just then a fox stepped gingerly out of the forest
undergrowth. He was a very fine fox, a real gentleman, dressed in a fine
suit of blue velvet with bright gold buttons. His shoes were shiny and clean,
and Sage thought that was very strange for someone whoís just been walking round
in the damp forest.
"Allow me to introduce myself", he said,
"My name is Esprit, and I am very pleased to meet you. I see you are
unsure which way to go. Allow me to help you Ė I know every road in the
forest!"
Sage snorted again, "Hmph!" he thought to
himself. "I donít like this fox one little bit. Everyone knows you
canít trust a fox!" Sage was sure the fox was up to something.
"And anyway, how did he know we were lost?"
The fox smoothed his fine, bushy tail, and then he
said "Now there is just one thing, little princess. I have a little
problem of my own, and I need your help. Perhaps you can help me, before
I help you."
"In the forest lives a little mouse, and though
heís small, heís very brave.
I need your help with that."
Sage whispered to Anna , "So now we know what
this fox is all about. Why would a fox need help to get his lunch back from a
little mouse? This is not safe, Anna dear, we must take our chances on
the roads, and not follow him into the dark woods."
Anna grew cross with Sage. "Now Sage,"
she said sternly, "donít be mean. This kind fox needs our help, and
we must give it.  Esprit looked sideways at Sage and said
to Anna "and donít listen to your silly pony. Heís not seen
much of the world, and he does not have your wisdom."
 ĎWeíll
see whoís wise, you crafty fox! " he said, shaking his head and swishing
his tail. "You may think youíre clever, but youíre no match for me! "
Sage turned his head and spoke to Anna , "Now Anna dear, I must
object, itís not wise to follow this sly fox. But if you say that it must
be so, then into the forest we will go. "
Anna nodded. She felt somehow she had to help
Esprit, but she too was unsure what they would find as they followed the fox
into the dark, damp, cold forest.
Chapter 3. The Mouse and the
Cheese
Anna, Sage, and Esprit walked through the forest,
picking their way through the bushes, fallen trees, and moss. Sage
stepped carefully, trying to keep his shiny new shoes clean, and Anna
rode, ducking under the low branches and hanging vines. Esprit
strode ahead, knowing which way to go even though there was no path. Anna
began to worry that they would never find their way back. Sage
began to worry if there was no mouse at all, and they were on their way to
becoming dinner for the fox and his pals.
"Is it much further Esprit?" Anna
asked, her voice a little shaky with fear.
ďWe should go back!" Sage muttered.
ďItís only a mouse!" said the fox, "and
look, there he is!"

"So," Anna thought, "this little
mouse did steal the foxís lunch. We must do something, because stealing
just isnít right!"
Anna spoke first, and she spoke straight to the mouse.
"Mouse, sir, please return the cheese."
The mouse crossed his little arms, and shook his head.
Anna went on, this time a little more firmly, and a
little more angry. "Mouse, you are not being fair.  You should stick to nuts and seeds, and leave
fine foods to fine gentlemen, like Esprit."
Well, mouse considered himself to be a very fine
gentleman indeed. He did not like to hear Anna tell him that he was not
good enough for a piece of cheese!   Just as
mice are for cats, cheese is for mice. Everyone knows that! Iím no common
city rat; Iím a country gent, with country tastes. Fine wine, and cheese,
are what I like, and this piece of cheese is mine alone!"
But as he shouted, and opened his mouth, the piece of
cheese fell to the forest floor. The mouse roared on for several minutes, while
the fox gobbled up the cheese, licked his lips, and smiled.
When the mouse saw what he had done, he flew into a
terrible rage. He stomped off back to his treetop house, went inside, and
slammed the little door so hard the whole tree shook, and leaves tumbled down
onto Anna and Sage.
Anna thought for a moment about what had happened, and
she watched fox delicately wipe the corners of his mouth with a fine silk
handkerchief. She had gotten foxís cheese back through trickery, yet
foxes were meant to be the crafty ones. She found herself a little angry
that Esprit had eaten the whole piece of cheese so quickly all by himself, when
he must have known Anna would be hungry.
Esprit leaned against the tree, and patted his tummy,
and said "So, princess Anna, tell me now, what have you learned from the
Fox, the Mouse, and what used to be a piece of cheese?"
Anna rubbed her chin, and scratched her head.
"I learned a thing or two about you," she said, "and next time
Iíll ask for my share! But most of all I learned that temper never leads to
good, and anger only leads to loss. No matter what is said to me, no matter
what anger others try to cause, I know I will be better off to show them
patience, and to turn the other cheek."
Chapter 4. Anna Gets Hungry
Esprit the fox had a full belly now, and was ready to
set off, and Sage hoped heíd take them back to the road and show them the
way. But Anna, having missed out on her share of cheese, was hungry, and
it was time for her supper anyway.
"Dear Esprit, now we have helped you in your
problem and it is time for you to help us with ours." she said. "But
first, I am so very hungry, and you have eaten; could you find some supper for
the two of us?"
Esprit did not seem at all concerned about taking them
back to the road. "I know a crane who lives nearby," he said,
"and she is a wonderful cook. I suggest we stop there and see if she
would be kind enough to offer us a meal. Soon it will be dark, after
dinner I wonder if you two would be so kind as to stay the night with me in my
den Ė I promise to make you very comfortable, and then I will help you resume your
journey in the morning."
Sage was anxious to be getting on with their journey
to Aragon, but even he was hungry, and it was getting late. He did not
know the way back to the road, and although he did not trust Esprit, even Sage
knew it would be wiser to get some dinner and some sleep. He would try to
persuade Esprit and Anna to hurry back to the road in the morning.
Esprit led them deeper into the woods, until they came
to a small pond, thick and green with weeds and lily pads. They could
hear bullfrogs croaking, and knew that darkness was not far away. On the
edge of the pond stood a small but lovely house, with flowers all around, and
pretty blue curtains in the windows. Esprit strode up to the front door,
and rapped firmly on the door with his knuckles.
After nearly a whole minute, the door slowly opened,
and a great long beak poked out. "Whoís there?" it croaked. "Who
could be visiting at so late an hour?"
"Dear Mrs. Crane," said the fox, "it is
Esprit, and I have some very noble friends with me, all the way from the
castle. They are hungry from their journey, and you are an excellent cook;
would you be so kind as to allow them to share your supper?"
The crane stepped outside, and looked at Anna and
Sage. "Well," she said, "your friends look plenty
well-to-do to me.
Anna answered, "Please, Mrs. Crane, we did not
plan for this detour. We have not brought with us enough for supper, and
we are very hungry indeed. We will not ask for much, a bowl of soup would
be enough."
The crane was clearly not happy about feeding the
guests, and after much fussing, she finally agreed.
Anna and Sage looked at each other. They were
not used to such poor hospitality, but they were hungry, so they politely said
"thank you" and went inside. They sat down at the table, while
the crane prepared some soup, muttering all the time under her breath.
The soup did smell fantastic, and the crane began to
serve it up. Esprit waved his paw to show that he didnít wish any (he had
filled up on cheese, after all), but Anna and Sage eagerly awaited their
dinner. But Mrs. Crane had other plans. She poured the soup into
great tall vases, and set them at the table. She began to greedily slurp
her soup, sticking her great long beak into the neck of her vase.
Sage looked at the vase. He tried to fit his
little nose into the neck of the vase, but even his tiny muzzle was too
wide. He tried this way, and that way, but try as he might, he couldnít
reach the soup.
Anna didnít do much better. She stuck her spoon into
the neck of the vase, and tried to reach the soup. But she couldnít spoon
up the soup, and when she brought out her spoon, all that she had were a few
drops at the very end.
Anna and Sage were going crazy Ė they were so hungry,
and the soup was so near, but they just couldnít get to it. Esprit
watched their struggles, and laughed a little to himself. Mrs. Crane
pretended not to notice the frustration her guests were feeling, and carried on
slurping her soup. When she had finished, she looked at Sage and Anna,
and grew quite cross indeed.
"Whatís the matter, is my soup not good enough
for you?" she asked.
"No," said Anna, "it smells wonderful,
itís just that Ė" and Mrs. Crane interrupted. "Well, never
mind, then," she said as she cleared away the vases, "all the more
for me".
Esprit stood up and spoke. "Well, thatís supper
then, letís be on our way before it gets dark." Anna and Sage got up from
their seats with tears in their eyes. Esprit stopped, and spoke to
the crane.
"Say, Mrs. Crane," he said. "You have
been most generous and kind." Sage let out a very angry cough at that
remark, and Esprit waved his paw at Sage as if to say "be quiet".
Esprit continued, "So that we may return your kindness, why donít you join
us for breakfast in the morning?"
Mrs. Crane looked carefully at Esprit. Like
Sage, she knew that foxes were seldom to be trusted. But there was
nothing Mrs. Crane loved more than food, especially when it was someone
elseís. She agreed, and they said their good nights to meet again at
Espritís house in the morning.
Anna and Sage had only just risen the next day, when
there was a loud banging on Espritís front door. Esprit was making
porridge, and the smell of warm oats and cinnamon had Anna and Sage fidgeting
excitedly at the table, still hungry from the night before. They did not want
to see Mrs. Crane again, but Esprit had invited them, and it was his house, so
they would just have to put up with her.
Esprit opened the door, and Mrs. Crane strode in.
"Whereís breakfast then?" she said. "Iíve come miles and I am so
very hungry."
 She tucked the napkin around her
neck, and clacked her beak. "Hurry up!" she squawked, "Iím
starving!"
Esprit brought the porridge to the table. He had
made it thick and rich with lots of honey, so you could almost eat it with a
fork. The porridge filled up the plates almost to the rim. Anna and
Sage thought it a little unusual to serve porridge on a plate, but they were so
hungry they didnít care. Anna picked up her spoon and tucked into her breakfast,
and Sageís little velvet muzzle nibbled hurriedly on his porridge. The
crane, however, couldnít eat. Her beak was so long and narrow that she
couldnít reach the porridge. Her beak just bounced off the plates,
splattering porridge everywhere.
"I canít eat like this!" she cried,
"Canít you see my bill wonít reach?"
Fox chuckled a little. "Whatís the matter,
Mrs. Crane?" he asked. "Is my breakfast not good enough for
you?" Anna and Sage looked first at Esprit, then at the crane, and
smiled.
Mrs. Crane didnít think it was very funny. She
stood up, looked at everyone, and waggled her wing at them all. "Well I
never!" she said, and she stomped out, and took off into the air, with the
napkin still around her neck.
Esprit sat down at Mrs. Craneís place, picked up a
spoon, and began to eat. "So, Anna ", he said, "learned
anything today?"
Anna nodded, and smiled. "I certainly did,
Mr. Fox." she replied. "I learned that how you are to others is how
they will be to you. I must show kindness if I wish others to be kind to
me."
Chapter 5. The journey continues
Anna and Sage were really
anxious to get on with their journey. Their time with Esprit had been
interesting, but Anna had to get to school. She was afraid that her
mother would worry if she found out they hadnít arrived in Aragon yet.
Sage, too, had had just about enough of Mr. Fox and all his little games.
"Itís time to go",
Esprit said, as if he could read their minds. "Iíll take you back to the
road and set you on your way. Itís a long walk, though, so weíd better
get going."
Anna climbed aboard Sage,
Esprit fastened the shiny gold buttons on his coat, and off they went.
They walked slowly on, and the sun streamed through the treetops and made
pretty patterns on the forest floor. Squirrels ran among the branches,
and brightly colored birds fluttered through the leaves.
They traveled miles through
the woods, listening to the birds sing and the squirrels chatter. Anna
began to sing, and Sage smiled as he listened to her beautiful voice.
Neither one noticed that they seemed to be traveling much farther than if they
had just gone back the way theyíd come.
Esprit looked back over his
shoulder at the two of them and smiled. "Everyone okay?" he
asked.
"Oh yes!" cried
Anna. "It is so very beautiful here. The sunbeams, the birdsong, the
bluebells Ė Iíve never seen anything so beautiful! I could stay here
forever!"
They walked on and on, and
soon it began to grow dark. Still neither Anna nor Sage noticed how far
they had traveled, the forest was so beautiful. Soon they walked out into
a meadow, underneath the night sky, their path lit only by moonbeams.
"Oh Sage, look!"
Anna gasped. "Look at the stars; have you ever seen anything so
beautiful?" Sage looked up as he walked along. "No, Anna , I have
not," he replied. "They are truly beautiful. Why, oh wise
fox, do you not care to take notice of such a beautiful place? You are
very fortunate to live here, yet you donít bother to give it even a
glance!"
Esprit shrugged his shoulders,
and plodded on, picking his way along the path.  Anna said, still gazing at the sky. "The stars will laugh at
you, for not bothering to notice how beautiful they are!".
"That may be, Anna "
Esprit replied, "that may be."
Just then, the fox leapt over
a huge ditch that crossed the path. Anna and Sage were so busy gazing at
the stars, and teasing Esprit that they never saw it, and down they tumbled
into it! Sage and Anna fell into a heap at the bottom, up to their
middles in murky, muddy water!
"Oh no!" cried Anna,
and "Ouch!" cried Sage, and they untangled themselves from the weeds
and vines, and climbed out of the ditch. Esprit looked at them, and
chuckled, as they tried to shake off the water and the mud that covered them
both.
"If the stars werenít
laughing before," said Esprit, "they are surely laughing now!"
Anna scowled at Esprit.
"It isnít funny!" she shouted angrily. "Oh come now, Anna
", he replied "take a minute and tell me what you have learned."
Anna stopped and looked at
Esprit, then looked at Sage, still covered in mud. She looked at the path
where they had come, and at the deep, murky ditch. "I learned that
sometimes you can look at the stars," she said, "but you also have to
keep your eyes on the road. You can dream, but you also have to keep your
feet on the ground."
Chapter 6. The truth comes out
It would be fair to say that both Anna and Sage had
had quite enough of Espritís lessons, and they really wanted to get back to the
road. And as soon as they thought it, they stepped out of the forest onto
the road.
"Now, Mr. Fox" Anna said sternly, "it
is time for you to tell us which way to go!" They turned and looked back
at Esprit, but he wasnít there. Instead, there stood a beautiful fairy,
with long golden hair, a flowing white dress, and a magic wand that sparkled in
the moonlight.
"Where did he go?" cried Anna. "Whatís
happened to him?"
"It was me all along," the fairy said
softly. "I am your teacher, but you had to learn for yourself. Now
tell me what you learned."
Anna tried to remember everything that had happened,
and each of the adventures they had with the crafty fox. She tried to
remember what she learned from each one.
"First, I learned that temper never solves
anything, and never leads to happiness. I learned that if others are
unkind, I mustnít grow angry, but I should smile and
remain calm.
"Second, I learned that meanness given will be
meanness returned. If I wish kindness from others, I must show them
kindness first."
"Last, I learned that there is a time to gaze in
wonderment at the world around, and a time to keep your eyes on the road under
your feet. It is joyful to dream, but you must also keep your feet on the
ground."
"You learned your lessons well, little
Anna," the fairy said.
"But I have to go to Aragon to go to
school," Anna cried. "My father and mother said so!"
"Little Anna," said the fairy, "these
are the forests of Aragon. That is why there is no sign at these crossroads Ė
you donít need to know which way to go, because you are already here. But your
lessons are finished now, until next year. Go home to your parents,
and tell them what you learned."
Anna thought for a moment. Sage shook his head,
as if that would help him understand. Anna smiled, and said "Thank
you, pretty fairy, for all that you have taught me. I will never forget
these lessons, and I will live by them for ever more."
"See that you do, little Anna," the fairy
replied. "See that you do, and you will grow to be a wise and kind queen
one day." And with that she spun around, and vanished in a cloud of fairy
dust.
Anna and Sage turned around, and set off on the road
back to the castle. Anna did remember her lessons, and so she grew to
become a beautiful queen, loved by all her kingdom for evermore.
Back to the partyÖ
And with that, the troubadour finished his tale, and
bowed to little princess Anna. Everyone in the great hall watched to see
if she liked it. Slowly, a smile formed on Annaís face, and soon she
glowed with happiness. The hall erupted into cheers and applause.
"Thank you! Thank you!" cried little Anna.
"There is no better gift. Your story told me all I need to know, to
become a wise and caring queen one day!". She looked up at her
mother and the queen smiled down at her. Suddenly Anna knew, this was not
just a story, this was a tale of how her mother became the queen and how she
became so loved by the entire kingdom.
Anna knew then that this story was a special gift. She
knew that if she learned these lessons she would grow to be just like her
mother, and that she, too, would live happily, ever after.


Preparation time
less than 30 mins
Cooking time
10 to 30 mins
Serves
Serves 4
This delicious Thai salad is a great recipe for preparing in advance if you have people over – keep the noodles, herbs, prawn and crab mixture separately and assemble at the last minute.
For the dressing, add the palm sugar and fish sauce to a small bowl and mix well to dissolve the sugar. Add the lime juice and set aside.
For the crispy noodles, heat the oil for deep-fat frying in a deep fryer, wok or large saucepan to 180-190C/250F-375F. When it is ready, a small cube of bread should become crisp and golden-brown and float to the top in a few seconds. Cook the vermicelli - do this in batches. Fry until puffed up and turning a pale golden-brown colour. Carefully remove using a slotted spoon and drain on a plate lined with kitchen paper.
Add three tablespoons of vegetable oil to a wok or saucepan. Fry the Thai shallots (or banana shallot) until crisp. Remove from the pan and set aside to drain on kitchen paper.
Add another tablespoon of vegetable oil to the wok. When hot, add the prawns and stir or toss until slightly opaque.
Add the ginger, garlic, and galangal and fry for a couple of minutes. Stir until the prawns are cooked – no longer than a minute. Transfer into a large bowl with the crisp shallots.
Add the spring onions, bean sprouts, lime leaves, and crabmeat. Pour in the dressing and the herbs. Mix well, but gently as you do not want to bruise the herbs - use your hands for this.
Layer the salad up in a large serving bowl, by putting a layer of crisp noodles on the bottom and a layer of prawn and crab mixture on top, then repeat until you have used up all the ingredients. Garnish with slithers of chilli and lime wedges and serve straight away.


Pre-processing is an ephemeral activity that takes place at read time, and none of the controls are ever seen by Prolog. It's purpose is to tailor a program for a specific environment, facilitate code sharing, and to provide succinct denotations (and hiding) of more complex code. It is based on the syntax and semantics of the C preprocessor. Preprocessor expressions are written in C functional style, not Prolog relational style.
For example, consider this small program that has macros defined for tracing and rigorous error checking that are used during debug mode, but are disabled (set to the Prolog no-op true) when not debugging. It also illustrates use of the predefined macros for date, time and line numbers.
#define DEBUG #ifdef DEBUG #define TRACE(X) writeq(trace - X), nl #define CHECK(X,MSG) ( X -> true; throw(check(MSG, __LINE__)) ) #else #define TRACE(X) true #define CHECK(X,MSG) true #endif main :- write('Build date': __DATE__), nl, write('Build time': __TIME__), nl, catch(go, ERR, error(ERR)). error(check(MESSAGE, LINE)) :- write('Error':MESSAGE), tab(2), write('on line':LINE), nl. go :- A = 2, TRACE(`calling square with A` = A), CHECK(integer(A), `argument to square not an integer`), square(A, AA), write(answer:AA), nl, B = hi, TRACE(`calling square with B` = B), CHECK(integer(B), `argument to square not an integer`), square(B, BB), write(answer:BB), nl. square(A, AA) :- AA is A * A.
Running this program yields ths result:
Build date: 4/15/2002 Build time:17:13:24 trace - `calling square with A` = 2 answer:4 trace - `calling square with B` = hi Error:argument to square not an integer on line:29
Expanded macro definitions are read by Prolog and must be valid Prolog syntax, so the use of extant C macros beyond simple values requires careful consideration.
A preprocessor control directive is denoted by '#' in column one:
#if, #ifdef, #ifndef, #else, #elif, #endif, #define, #undef, #error
Controls can be block commented out.
This defines a global macro which will be stored
by Prolog. There can only be one macro with a given name.
A macro is strictly text replacement, expanded at load time so the arguments
are text, not values, and there is no other environment. A macro body should
not contain text that denotes free Prolog variables, ie. variables that are
not the names of it's own arguments or the names of other macros, unless it
is defining a self-contained clause to be asserted.
It consists of two parts, the definiend (head) and the definition (body). The
definiend has a name and parameters, all of which must conform to Prolog syntax
for variables; but they are neither, they are just names. There is a limit of
15 parameters. Each argument takes up size+1 space and the total space available
is only 64. This helps to ensure that a definiend does not need more than one
line of text.
The rest of the line following the definiend is the definition, which shall
be text, possibly containing references to parameter names. The definition is
delimited by a line feed symbol, but may employ a backslash symbol to shield
a linefeed from view. Then both the backslash symbol and the linefeed are elided.
Thus, the definition is stored as a single line of text with no particular limit
on size.
A macro reference is a token which is the name of a defined macro,
followed by its arguments (if any). It is never seen by Prolog. When a macro reference
is encountered at load time it is replaced by the macro definition, which itself
is expanded by replacing parameter references by arguments, and the result is
expected to be a fragment of syntactically acceptable Prolog.
There is no particular limit to the size of a macro argument but it must be
balanced with respect to quotes and parentheses.
There is no syntactic reason for the definition itself to be balanced, but an
unbalanced macro would be highly context dependent.
Some macros are pre-defined because they are are 'dynamic'. Their semantics
are not constant text but depend upon hidden code at expansion time.
The following macros are pre-defined:
Macro Definition __DATE__ expansion date __TIME__ expansion time __LINE__ line number
Copyright ©1987-2011 Amzi! inc. All Rights Reserved. Amzi! is a registered trademark and Logic Server is a trademark of Amzi! inc.


{% include "common/includes/carousel.html" with carousel_items=self.carousel_items.all only %}
{% endblock %}
{% block content %}
{% include "common/includes/intro.html" with intro=self.intro only %}
{% if self.image %}
{# Artist image - first store image as a variable in order to construct an image tag with a class applied #}
{% image self.image fill-300x200-c50 as photo %}
{% endif %}
{% if self.body %}
{% include "common/includes/body.html" with body=self.body only %}


--- title: Building frontend code with Ant excerpt: Recently I've been checking various ways for optimising frontend code. In Professional JavaScript for Web Developers (2nd Edition), Nicholas C. Zakas speaks about best practices for deploying websites in production (among other good things). date: '2010-04-11 17:43:26 +0200' date_gmt: '2010-04-11 15:43:26 +0200' categories: - Webdev tags: - Webdev - Ant ---
Recently I've been checking various ways for optimising frontend code. In Professional JavaScript for Web Developers (2nd Edition), Nicholas C. Zakas speaks about best practices for deploying websites in production (among other good things). Having a solid build process for deployment saves both time and bandwidth, and helps automating repetitive tasks. There are plenty of online resources about the subject, but Julien Lecomte's Building Web Applications With Apache Ant explains really well what possibilities Ant offers for frontend code build process (see also Setting up a JavaScript Build Process). For more details, see Apache Ant Manual.
Based on the advice from those articles, I've put together a basic Ant script which concatenates and minifies both JS and CSS files during the build process. This could be used as a base template for frontend production code.
On this site, I use Minify PHP5 app on the server side to automatically combine and minify multiple CSS and Javascript files every time they change. However, I wanted to define a structured build process that I can customise and reuse for any website I work on.
Here's what the script does (for details, see comments in the code):
apply
task) and add '-min' to the file name.You need JDK, Apache Ant and YUI Compressor to run this. Now, when I set this up on Windows, I got the following error:
Unable to locate tools.jar. Expected to find it in C:\Program Files\Java\jre6\lib\tools.jar
If you get that too, just copy tools.jar from earlier Java version and you're done. When everything is set up, go to Ant dir (where the build.xml file lives):
cd C:\Ant
Then build the project (by default, it uses build.xml):
ant
Here's the build.xml I've created:
{% highlight html %}{% endhighlight %}JS/CSS production build for myProject Build Date: ${TODAY} Build Date: ${TODAY} Done! Done! Done! Done!
Obviously, there are lots of other things you can do with Ant. There's really nothing new in this script, but it combines ideas from several articles mentioned above so I thought I'd share it here (download build.xml from GitHub). I hope it can be useful for you - I know for me it will! ;)
Update 28 March, 2011: uploaded this build.xml file to GitHub.


OMEX Archive Search Engine
Match all of the following
Match any of the following
Search
Has Mediator Participant
Contain reaction
Contain species
Contain protein
Apply to taxon
Include compartment
Include protein
Include protein as reactant
Include protein as product
Results
{% for model in models %}
{{model}}
{% endfor %}


Tyler Cain
Home
Home
menu
Current Work.
Card Title
TrackRx
Deployed Link
Github Repo
Photo Searcher
Deployed Link
Github Repo
Weather Forecast App
Deployed Link
Github Repo
Daily Planner
Deployed Link
Github Repo
Fitness Tracker
Deployed Link
Github Repo
Finance App
Deployed Link
Github Repo
View my resume.
Thanks for being here.


Homepage
Kunwar Shaanjeet Singh Grover
Reach me:
<EMAIL_ADDRESS>github
resume
reading list
:pensive:
{% for link, title in pages %}
{{title}}
{% endfor %}


NYT Story Criticizing Gabbard's White Pantsuit May Be the Most Ridiculous Article Ever Printed
by Leah BarkoukisThe New York Times published an article devoted exclusively to criticizing Rep. Tulsi Gabbard for wearing a white pantsuit. The arguments in the piece are insane enough, but to make matters worse, the same author wrote for the same paper during the last election cycle about how great it was that Hillary Clinton wore white pantsuits.
Gabbard, the author wrote, seems much less interested in representing a symbol of suffragists, but more to “tap into another tradition, latent in the public memory: the mythical white knight, riding in to save us all from yet another “regime change war.”
It gets better than that, as Glenn Greenwald documents.
"Her white suits are not the white suits of Ms. Clinton, nor even the white of Ms. Williamson, whose early appearances in the shade often seemed tied to her wellness gospel and ideas of renewal and rebirth. Rather, they are the white of avenging angels and flaming swords, of somewhat combative righteousness (also cult leaders). And that kind of association, though it can be weirdly compelling, is also not really community building. It sets someone apart, rather than joining others together. It has connotations of the fringe, rather than the center."
It goes without saying that the piece was widely ridiculed.


ZoologyEnroll BotanyEnroll MicrochemistryEnroll MedicineEnroll EcologyEnroll MicrobiologyEnroll Molecular BiologyEnroll Evolutionary BiologyEnroll Cell BiologyEnroll Marine BiologyEnroll