What changed in version 5.0¶
Upgrading¶
To upgrade a production system, install the new system in a new virtualenv, then migrate your database:
reahl migratedb etc
Backwards-incompatible changes¶
Since this version is a major version update it is not backwards-compatible with previous versions. Everything that was deprecated in older versions is removed, and some new backwards-incompatible changes have been added.
- @exposed
Previously, if you marked a method with
exposed
, its returnedFieldIndex
did not take into account possible methods higher up in the inheritance hierarchy. This has been changed -exposed
now always calls each method higher up in a hierarchy, thus the resultantFieldIndex
now contains allField
s added by overridden methods in the hierarchy.- Python 2 support
Since Python 2 is now officially retired, this release drops support for Python < 3.5.
- ButtonInput
ButtonInput
now creates its own layout upon construction. Instead of setting your ownLayout
on an already-createdButtonInput
, use one or more ofButtonInput
's new keyword arguments upon construction: style, outline, size, active, wide or text_wrap- Unique input names and IDs
The name of an
Input
is derived from the name of itsField
. PreviouslyInput
names used to be adapted automatically so as to prevent name clashes between differentInput
s on aForm
. This is no longer the case: you now have to explicitly disambiguate an Input that has a name clash with another by passing it aField
with a modified name (seewith_discriminator()
).Every Input is also now generated with a name and an ID that include the
Form
's ID and hence are unique on the entire page.- Changes to nested_transaction
nested_transaction()
used to yield the transaction object. It now yields aTransactionVeto
object which can be used to force the transaction to be committed or not by settingshould_commit
to True or False.- Moved
HTML5Page
HTML5Page
used to be inreahl.web.bootstrap.ui
, but now resides inreahl.web.bootstrap.page
due to unavoidable dependency issues. You need to change your code to import it from the new module.- Project metadata changes
In a .reahlproject file, the runtime dependencies of a project used to be declared at the top-level with a
`<deps purpose="run">`
tag. The format of this file has now changed. Each released minor version of the project is now listed in a separate`<version>`
tag, and the runtime dependencies are now stated inside the`version`
tag for which those dependencies hold. In keepoing with this change,Migration
s no longer have a`version`
attribute to indicate which version they are for. See XML reference for .reahlproject.
Changing contents in response to a changing Input¶
The major goal of this release was to incorporate more javascript logic into pages, but still hide that behind Python.
We’ve done that by adding the refresh_widget keyword argument of PrimitiveInput()
. When constructing a
PrimitiveInput
you can pass an HTMLWidget
as the refresh_widget
of the PrimitiveInput
. When the PrimitiveInput
is changed, it will
trigger a refresh of its refresh_widget. As before, it is necessary
to call enable_refresh()
on such an HTMLWidget
for this to work.
Two examples were added to explain usage of this feature:
Optimistic locking¶
This release adds functionality that guards against one user overwriting changes made concurrently to the same data by another user.
Consider, for example, the following sequence of events:
user A opens page X
user B opens page X
user A changes input on a form on page X, and clicks on a
ButtonInput
that submits itthe database is changed as a result of user A’s changes in such a way that page X would not render the same anymore
yet, user B still has the old page X open, and now makes similar changes on that page and clicks on a
ButtonInput
that submits the info
Without intervention in the above scenario user B’s changes might overwrite those of user A or the application could break - depending on how the code was written.
Reahl now computes a hash of all the input values on a form on a page. When the page is submitted, this hash is sent back to the server which recomputes the hash. If any differences are picked up, the user is shown an error message explaining that someone else has changed the same data and given the chance to refresh the values and try again.
This mechanism can also be customised to:
For more info, see the HOWTO:
Error pages¶
Previously if an application encountered an unexpected exception, it would return an HTTP 5xx error code to the browser, which typically displays an unhelpful, unattractive error page.
In this release introduces the concept of a default_error_view.
The effect of this is that error messages can be rendered within the general look, feel and layout of your application. This happens by default, but the mechanism can also be customised at several different levels of your application.
For more info, see the HOWTO:
Database schema evolution¶
Previously, if you had a database with schema for a particular version of Reahl components (and your own), you could migrate said schema (and your data) to the next new version of Reahl components. For example, if you were using Reahl 3.0, you could upgrade to 3.1.
You could not do an upgrade that jumped versions. For example if your app’s version 1.0 depended on Reahl 3.0, you could not migrate successfully to a version 2.0 of your app which used Reahl 5.0.
The database migration machinery in this version was extensively overhauled to be able to handle any upgrade scenario we could imagine. (This includes scenarios where, for example, dependencies of components change amongst different versions of a component.)
Each Reahl component still only carries knowledge of its own
Migration
s so that a diverse set of components can be used together
in the same database without knowledge of one another. Migration
s
are still written as before.
What has changed is what metadata is kept of a project. Instead of only stating a project’s current version and its dependencies, you now have to list all released versions of the form major.minor. For each such version also state its dependencies and migrations.
Migration
s do not have a version class attribute anymore.
For more info, see the tutorial example:
Widget changes¶
- Table footers
reahl.web.ui.Table.with_data()
andreahl.web.bootstrap.tables.Table.with_data()
gained a footer_items keyword argument. This allows one to supply content for the table footer.- Domain exceptions and exception Alerts
DomainException
was changed to hold onto a list of possible error messages via its new detail_messages keyword argument. This is used, for example, byValidationException
where aValidationException
signals that there were validation errors and its detail_messages provide a list of all the specific validation failures.If an exception is present on a
Form
you should display it. Previously displaying its message in anAlert
would have been enough, but with the list of detail_messages now included, one should really display that list as well - and with it all styled better.To do this
add_alert_for_domain_exception()
was added toreahl.web.bootstrap.forms.FormLayout
. This provides an easy way to add an error message in cases where there is an exception present on aForm
. TheAlert
added byadd_alert_for_domain_exception()
also includes a way for a user to react to the case where it is detected that another user made concurrent changes to underlying data.- Simple FormLayout
reahl.web.ui.FormLayout
was added to provide similar functionality toreahl.web.bootstrap.forms.FormLayout
on a lower level. This is especially used in tests.
A more expressive and composable XPath¶
XPath
has been changed significantly to make it more useful and expressive in tests.
You can now construct an XPath
by chaining and composition. For
example, you can find a div with a specific css class like this:
XPath.div().including_class('myclass')
XPath
instances can be further composed in terms of one another:
XPath.button_labelled('Save').inside_of(XPath.div().including_class('myclass'))
A more Ajax-friendly DriverBrowser¶
When one generally tests an application, it is to be expected that user actions could trigger ajax refreshes or generally refer to elements that aren’t visible on the page yet - but that have to be waited for to appear.
Test code that continually triggers such events and waits for the results can obfuscate the intent of a test.
For this reason several DriverBrowser
methods have been changed to
automatically do “the right thing” in such circumstances.
Methods like type()
and click()
, for example, now always trigger a
blur event on the PrimitiveInput
targeted and then wait for any
ajax that might have been triggered in response to finish before
returning.
This default behaviour can be overridden using keyword arguments where appropriate.
Commandline tools¶
- Creating a new project
You can now start a new project by checking out one of our examples, but with a different name. In such checked-out code module, package, and various other names are renamed appropriately. (See reahl example -h)
- Help with configuration
You can also create a fresh new configuration directory with configuration based on having answered a few questions interactively. (See reahl createconfig -h)
- Hosting static files
Sometimes you need to host static files directly via a proxy such as nginx. You can now get to all those static files by running reahl exportstatic. (See reahl exportstatic -h)
Docker instead of Vagrant¶
Maintaining our Vagrant boxes for development became a bit cumbersome. With this release we have switched to using a Docker image for that purpose.
Downloading the Docker dev image is the quickest way to get started with Reahl because in it Reahl is installed in a clean venv ready for use.
The Docker development image is not meant for production use. It comes with Reahl as well as a bunch of development tools installed.
More info on using it can be found in: Development environment setup.
ReahlWSGIApplication start_on_first_request¶
When running your app under uwsgi, the uWSGI server first imports your application and then forks several worker processes. If you start your application at module level (so that it is started after uwsgi imported your application) your application is also already connected to its database…and the forked processes inherit these established connections.
Such database connections are not thread-safe and cannot be shared between processes.
The start_on_first_request=True of ReahlWSGIApplication
ensures that your application
need not be started when created. Instead, it will be started when it receives its first
request and avoid this problem:
import os
from reahl.web.fw import ReahlWSGIApplication
application = ReahlWSGIApplication.from_directory('%s/etc' % os.path.expanduser('~'), start_on_first_request=True)
Updated dependencies¶
Some included thirdparty JavaScript and CSS libraries were updated:
JQuery to 3.5.1
Bootstrap to 4.5.2
JQueryUI to 1.12.1 - but our distribution includes only the widget factory with :focusable and :tabbable, nothing else.
JQuery.validate was updated to 1.19.1 (and patched).
JQuery.form to 4.2.2
JQuery.blockUI to 2.70.0
js.cookie to 1.4.1
Popper to 1.16
holder to 2.9.7
Unchanged:
JQuery BBQ 1.3pre (patched).
JQuery-form 4.2.2.
HTML5shiv to 3.7.3
The versions of some external dependencies were updated:
alembic to 0.9.6
Babel to 2.8
beautifulsoup4 to 4.6
docutils to 0.14
lxml to 4.2
mysqlclient to 1.3
Pillow to 2.5
ply to 3.8
prompt_toolkit to 2.0.10
psycopg2-binary to 2.7
Pygments to 2.1.0
python-dateutil to 2.8
selenium to 2.42
setuptools-git to 1.1
SQLAlchemy to 1.2.0
twine to 1.15.0
tzlocal to 2.0.0
watchdog to 0.8.3
WebOb to 1.4
wheel to 0.34.0
wrapt to 1.10.2