Skip to content

Commit

Permalink
v2.1.0 (#108)
Browse files Browse the repository at this point in the history
- Change type hint on `view_to_component` callable to have `request` argument be optional.
- More DRY way of async view rendering.
- Have docs demonstrate how to use Django-IDOM with `channels>=4.0.0`.
- Concatenate some examples on the `view_to_component` docs
- Add note to docs about potential information exposure via `view_to_component` when using `compatibility=True`.
- Clean up docs on running tests
  • Loading branch information
Archmonger authored Nov 2, 2022
1 parent 09d11f0 commit 5cf5640
Show file tree
Hide file tree
Showing 22 changed files with 288 additions and 182 deletions.
24 changes: 20 additions & 4 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,28 @@ Using the following categories, list your changes in this order:

- Nothing (yet)

## [2.0.1]- 2022-10-18
## [2.1.0] - 2022-11-01

## Changed

- Minimum `channels` version is now `4.0.0`.

### Fixed

- Change type hint on `view_to_component` callable to have `request` argument be optional.
- Change type hint on `view_to_component` to represent it as a decorator with paranthesis (ex `@view_to_component(compatibility=True)`)

## Security

- Add note to docs about potential information exposure via `view_to_component` when using `compatibility=True`.

## [2.0.1] - 2022-10-18

### Fixed

- Ability to use `key=...` parameter on all prefabricated components
- Ability to use `key=...` parameter on all prefabricated components.

## [2.0.0]- 2022-10-17
## [2.0.0] - 2022-10-17

### Added

Expand Down Expand Up @@ -155,7 +170,8 @@ Using the following categories, list your changes in this order:

- Support for IDOM within the Django

[unreleased]: https://github.com/idom-team/django-idom/compare/2.0.1...HEAD
[unreleased]: https://github.com/idom-team/django-idom/compare/2.1.0...HEAD
[2.1.0]: https://github.com/idom-team/django-idom/compare/2.0.1...2.1.0
[2.0.1]: https://github.com/idom-team/django-idom/compare/2.0.0...2.0.1
[2.0.0]: https://github.com/idom-team/django-idom/compare/1.2.0...2.0.0
[1.2.0]: https://github.com/idom-team/django-idom/compare/1.1.0...1.2.0
Expand Down
23 changes: 17 additions & 6 deletions docs/src/contribute/running-tests.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,26 @@
This repo uses [Nox](https://nox.thea.codes/en/stable/) to run scripts which can be found in `noxfile.py`. For a full test of available scripts run `nox -l`. To run the full test suite simple execute:
This repo uses [Nox](https://nox.thea.codes/en/stable/) to run tests. For a full test of available scripts run `nox -l`.

If you plan to run tests, you'll need to install the following dependencies first:

- [Python 3.8+](https://www.python.org/downloads/)
- [Git](https://git-scm.com/downloads)

Once done, you should clone this repository:

```bash
git clone https://github.com/idom-team/django-idom.git
cd django-idom
pip install -r ./requirements/test-run.txt --upgrade
```

Then, by running the command below you can run the full test suite:

```
nox -s test
```

If you do not want to run the tests in the background:
Or, if you want to run the tests in the foreground:

```
nox -s test -- --headed
```

!!! warning "Most tests will not run on Windows"

Due to [bugs within Django Channels](https://github.com/django/channels/issues/1207), functional tests are not run on Windows. In order for Windows users to test Django-IDOM functionality, you will need to run tests via [Windows Subsystem for Linux](https://code.visualstudio.com/docs/remote/wsl).
121 changes: 82 additions & 39 deletions docs/src/features/components.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

## View To Component

Convert any Django view into a IDOM component by usng this decorator. Compatible with sync/async [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/).
Convert any Django view into a IDOM component by usng this decorator. Compatible with [Function Based Views](https://docs.djangoproject.com/en/dev/topics/http/views/) and [Class Based Views](https://docs.djangoproject.com/en/dev/topics/class-based-views/). Views can be sync or async.

=== "components.py"

Expand Down Expand Up @@ -41,11 +41,42 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
| --- | --- |
| `_ViewComponentConstructor` | A function that takes `request: HttpRequest | None, *args: Any, key: Key | None, **kwargs: Any` and returns an IDOM component. |

??? warning "Existing limitations"
??? Warning "Potential information exposure when using `compatibility = True`"

When using `compatibility` mode, IDOM automatically exposes a URL to your view.

It is your responsibility to ensure priveledged information is not leaked via this method.

This can be done via directly writing conditionals into your view, or by adding decorators such as [user_passes_test](https://docs.djangoproject.com/en/dev/topics/auth/default/#django.contrib.auth.decorators.user_passes_test) to your views prior to using `view_to_component`.

=== "Function Based View"

```python
...

@view_to_component(compatibility=True)
@user_passes_test(lambda u: u.is_superuser)
def example_view(request):
...
```

=== "Class Based View"

```python
...

@view_to_component(compatibility=True)
@method_decorator(user_passes_test(lambda u: u.is_superuser), name="dispatch")
class ExampleView(TemplateView):
...
```

??? info "Existing limitations"

There are currently several limitations of using `view_to_component` that may be resolved in a future version of `django_idom`.

- Requires manual intervention to change request methods beyond `GET`.
- IDOM events cannot conveniently be attached to converted view HTML.
- Does not currently load any HTML contained with a `<head>` tag
- Has no option to automatically intercept local anchor link (ex. `#!html <a href='example/'></a>`) click events

Expand Down Expand Up @@ -77,87 +108,95 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible

??? question "How do I transform views from external libraries?"

=== "components.py"
In order to convert external views, you can utilize `view_to_component` as a function, rather than a decorator.

In order to convert external views, you can utilize `view_to_component` as a function, rather than a decorator.
=== "components.py"

```python linenums="1"
from idom import component, html
from django.http import HttpResponse
from django_idom.components import view_to_component
from some_library.views import example_view

converted_view = view_to_component(example_view)
example_vtc = view_to_component(example_view)

@component
def my_component():
return html.div(
converted_view(),
example_vtc(),
)
```

??? question "How do I provide `args` and `kwargs` to a view?"
??? question "How do I provide `request`, `args`, and `kwargs` to a view?"

You can use the `args` and `kwargs` parameters to provide positional and keyworded arguments to a view.
<font size="4">**`Request`**</font>

You can use the `request` parameter to provide the view a custom request object.

=== "components.py"

```python linenums="1"
from idom import component, html
from django.http import HttpResponse
from django.http import HttpResponse, HttpRequest
from django_idom.components import view_to_component

example_request = HttpRequest()
example_request.method = "PUT"

@view_to_component
def hello_world_view(request, arg1, arg2, key1=None, key2=None):
return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}")
def hello_world_view(request):
return HttpResponse(f"Hello World! {request.method}")

@component
def my_component():
return html.div(
hello_world_view(
None, # Your request object (optional)
"value_1",
"value_2",
key1="abc",
key2="123",
example_request,
),
)
```

??? question "How do I provide a custom `request` object to a view?"
---

You can use the `request` parameter to provide the view a custom request object.
<font size="4">**`args` and `kwargs`**</font>

You can use the `args` and `kwargs` parameters to provide positional and keyworded arguments to a view.

=== "components.py"

```python linenums="1"
from idom import component, html
from django.http import HttpResponse, HttpRequest
from django.http import HttpResponse
from django_idom.components import view_to_component

example_request = HttpRequest()
example_request.method = "PUT"

@view_to_component
def hello_world_view(request):
return HttpResponse(f"Hello World! {request.method}")
def hello_world_view(request, arg1, arg2, key1=None, key2=None):
return HttpResponse(f"Hello World! {arg1} {arg2} {key1} {key2}")

@component
def my_component():
return html.div(
hello_world_view(
example_request,
None, # Your request object (optional)
"value_1",
"value_2",
key1="abc",
key2="123",
),
)
```

??? question "What is `compatibility` mode?"
??? question "How do I use `strict_parseing`, `compatibility`, and `transforms`?"

For views that rely on HTTP responses other than `GET` (such as `PUT`, `POST`, `PATCH`, etc), you should consider using compatibility mode to render your view within an iframe.
<font size="4">**`strict_parsing`**</font>

Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments do not apply to compatibility mode.
By default, an exception will be generated if your view's HTML does not perfectly adhere to HTML5.

Please note that by default the iframe is unstyled, and thus won't look pretty until you add some CSS.
However, there are some circumstances where you may not have control over the original HTML, so you may be unable to fix it. Or you may be relying on non-standard HTML tags such as `#!html <my-tag> Hello World </my-tag>`.

In these scenarios, you may want to rely on best-fit parsing by setting the `strict_parsing` parameter to `False`.

Note that best-fit parsing is designed to be similar to how web browsers would handle non-standard or broken HTML.

=== "components.py"

Expand All @@ -166,9 +205,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
from django.http import HttpResponse
from django_idom.components import view_to_component

@view_to_component(compatibility=True)
@view_to_component(strict_parsing=False)
def hello_world_view(request):
return HttpResponse("Hello World!")
return HttpResponse("<my-tag> Hello World </my-tag>")

@component
def my_component():
Expand All @@ -177,13 +216,17 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
)
```

??? question "What is `strict_parsing`?"

By default, an exception will be generated if your view's HTML does not perfectly adhere to HTML5.

However, there are some circumstances where you may not have control over the original HTML, so you may be unable to fix it. Or you may be relying on non-standard HTML tags such as `#!html <my-tag> Hello World </my-tag>`.
---

In these scenarios, you may want to rely on best-fit parsing by setting the `strict_parsing` parameter to `False`.
<font size="4">**`compatibility`**</font>

For views that rely on HTTP responses other than `GET` (such as `PUT`, `POST`, `PATCH`, etc), you should consider using compatibility mode to render your view within an iframe.

Any view can be rendered within compatibility mode. However, the `transforms`, `strict_parsing`, `request`, `args`, and `kwargs` arguments do not apply to compatibility mode.

Please note that by default the iframe is unstyled, and thus won't look pretty until you add some CSS.

=== "components.py"

Expand All @@ -192,9 +235,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
from django.http import HttpResponse
from django_idom.components import view_to_component

@view_to_component(strict_parsing=False)
@view_to_component(compatibility=True)
def hello_world_view(request):
return HttpResponse("<my-tag> Hello World </my-tag>")
return HttpResponse("Hello World!")

@component
def my_component():
Expand All @@ -203,9 +246,9 @@ Convert any Django view into a IDOM component by usng this decorator. Compatible
)
```

Note that best-fit parsing is very similar to how web browsers will handle broken HTML.
---

??? question "What is `transforms`?"
<font size="4">**`transforms`**</font>

After your view has been turned into [VDOM](https://idom-docs.herokuapp.com/docs/reference/specifications.html#vdom) (python dictionaries), `view_to_component` will call your `transforms` functions on every VDOM node.

Expand Down
12 changes: 6 additions & 6 deletions docs/src/features/hooks.md
Original file line number Diff line number Diff line change
Expand Up @@ -324,12 +324,6 @@ You can expect this hook to provide strings such as `/idom/my_path`.
return html.div(my_location)
```

??? info "This hook's behavior will be changed in a future update"

This hook will be updated to return the browser's currently active path. This change will come in alongside IDOM URL routing support.

Check out [idom-team/idom-router#2](https://github.com/idom-team/idom-router/issues/2) for more information.

??? example "See Interface"

<font size="4">**Parameters**</font>
Expand All @@ -342,6 +336,12 @@ You can expect this hook to provide strings such as `/idom/my_path`.
| --- | --- |
| `Location` | A object containing the current URL's `pathname` and `search` query. |

??? info "This hook's behavior will be changed in a future update"

This hook will be updated to return the browser's currently active path. This change will come in alongside IDOM URL routing support.

Check out [idom-team/idom-router#2](https://github.com/idom-team/idom-router/issues/2) for more information.

## Use Origin

This is a shortcut that returns the Websocket's `origin`.
Expand Down
2 changes: 1 addition & 1 deletion docs/src/getting-started/learn-more.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ Additionally, the vast majority of tutorials/guides you find for React can be ap

| Learn More |
| --- |
| [Django-IDOM Advanced Usage](../features/components.md){ .md-button } [IDOM Hooks, Events, and More](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html){ .md-button } [Ask Questions on GitHub Discussions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } |
| [Django-IDOM Advanced Usage](../features/components.md){ .md-button } [IDOM Core Documentation](https://idom-docs.herokuapp.com/docs/guides/creating-interfaces/index.html){ .md-button } [Ask Questions on GitHub Discussions](https://github.com/idom-team/idom/discussions){ .md-button .md-button--primary } |
4 changes: 2 additions & 2 deletions docs/src/installation/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,15 @@ In your settings you'll need to add `django_idom` to [`INSTALLED_APPS`](https://

Django-IDOM requires ASGI in order to use Websockets.

If you haven't enabled ASGI on your **Django project** yet, you'll need to add `channels` to `INSTALLED_APPS` and set your `ASGI_APPLICATION` variable.
If you haven't enabled ASGI on your **Django project** yet, you'll need to install `channels[daphne]`, add `daphne` to `INSTALLED_APPS`, then set your `ASGI_APPLICATION` variable.

Read the [Django Channels Docs](https://channels.readthedocs.io/en/stable/installation.html) for more info.

=== "settings.py"

```python linenums="1"
INSTALLED_APPS = [
"channels",
"daphne",
...
]
ASGI_APPLICATION = "example_project.asgi.application"
Expand Down
2 changes: 1 addition & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ def test_suite(session: Session) -> None:
def test_types(session: Session) -> None:
install_requirements_file(session, "check-types")
install_requirements_file(session, "pkg-deps")
session.run("mypy", "--show-error-codes", "src/django_idom")
session.run("mypy", "--show-error-codes", "src/django_idom", "tests/test_app")


@nox.session
Expand Down
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,4 @@ ignore_missing_imports = true
warn_unused_configs = true
warn_redundant_casts = true
warn_unused_ignores = true
check_untyped_defs = true
2 changes: 1 addition & 1 deletion requirements/pkg-deps.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
channels >=3.0.0
channels >=4.0.0
idom >=0.40.2, <0.41.0
aiofile >=3.0
typing_extensions
2 changes: 1 addition & 1 deletion src/django_idom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from django_idom.websocket.paths import IDOM_WEBSOCKET_PATH


__version__ = "2.0.1"
__version__ = "2.1.0"
__all__ = [
"IDOM_WEBSOCKET_PATH",
"IdomWebsocket",
Expand Down
Loading

0 comments on commit 5cf5640

Please sign in to comment.