Monthly Archives: November 2016

Epiphany Queries

Python Epiphany collection classes can be queried in a way that is reminiscent of SQL WHERE clauses, except with the full expressive power of Python. Let’s create a deities collection class and a corresponding deity entity class to set up a demonstration.


class deities(entities):
    pass

class deity(entity):
    def __init__(self, name, gender):
        self.name = name
        self.gender = gender

Here, deities inherits from entities making it a collection class. deity objects will obviously be appended to this collection class. Each deity object will store the deities name and gender.

Let’s start by using our classes to build up a pantheon of the major Greek gods and goddesses.


pantheon = deities()
pantheon  +=  deity('Aphrodite',   'female')
pantheon  +=  deity('Apollo',      'male')
pantheon  +=  deity('Ares',        'male')
pantheon  +=  deity('Artemis',     'female')
pantheon  +=  deity('Athena',      'female')
pantheon  +=  deity('Demeter',     'female')
pantheon  +=  deity('Hades',       'male')
pantheon  +=  deity('Hephaestus',  'male')
pantheon  +=  deity('Hera',        'female')
pantheon  +=  deity('Hermes',      'male')
pantheon  +=  deity('Hestia',      'female')
pantheon  +=  deity('Poseidon',    'male')
pantheon  +=  deity('Zeus',        'male')

Now, using the pantheon collection object, lets create gods and goddesses collections by querying the pantheon collection.


gods = pantheon.where(lambda x: x.gender == 'male')
goddesses = pantheon.where(lambda x: x.gender == 'female')

assert isinstance(gods, entities)
assert isinstance(goddesses, entities)

assert gods.count == 7
assert goddesses.count == 6

The first line uses the where method to collect all the deities that are male. The second line does the same for female deities. A lambda object is used as the query, though any Python callable, such as a function, could be used. Lambdas have the advantage of being terse.

Later we can assert that the where method returns a generic entities class . Ideally, where would have created and returned a deities class in this example. However, that feature hasn’t been implemented yet.

Finally, we make use of the queries to observe that there are 7 male deities and 6 female deities in our original pantheon collection.

In the above queries, only one property, gender, was tested for equality. However, we can make the test as expressive as any Python expression can be. Let’s say we want to see how many male deities are in our pantheon collection whose name starts with the letter ‘A’.


gods = pantheon.where(lambda x: x.name[0] == 'A' and x.gender == 'male')
assert gods.count == 2
assert gods[0].name == 'Apollo'
assert gods[1].name == 'Ares'

Here we see it’s no problem to use Python’s string indexing as well as the and operator to create a slightly more complex query. Of course, very complicated queries with complex nested Boolean expressions can be used as well. We can see that two deities are returned: the first being ‘Apollo’ and the second being ‘Ares’.

Disabled submit buttons in Chrome

There is an interesting difference in the way Chrome handles disabled submit buttons that is worth keeping in mind when developing for different browsers.

disabled submit

When the user clicks a button — particularly a submit button — it is often useful to disable the button so the user can’t accidentally click it multiple times. Multiple clicks would lead to undesirable results, such as a blog post being submitted twice or, worse, a sales order being executed more than once.

Let’s consider an HTML form called my-form with a button named submit-button.


<form id="my-form">
    <!--Form elements would go here-->
    <button id="submit-button" type="submit">Submit</button>
</form>

The following JavaScript (which relies on jQuery) will work on non-Chrome browsers.

     
$('#submit-button').on('click', function (e) {
    $(this).attr('disabled', 'disabled');
});

This event is triggered when the submit-button is clicked. It sets the button’s disabled attribute. Afterwards, the browser will submit the form.

To get the above code to work in all browsers, make the following changes.

     
$('#submit-button').on('click', function (e) {
    e.preventDefault();
    $(this).attr('disabled', 'disabled');
    $('#my-form').submit();
});

The first line will disable the browsers default behavior (e.preventDefault();) — which is to submit the form. Then we disable the button like we did before. Finally, the form is submitted imperatively by the JavaScript.