Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Unite Template and Renderer classes #94

Merged
merged 6 commits into from
Dec 13, 2016
Merged

Conversation

quis
Copy link
Member

@quis quis commented Dec 1, 2016

So this is the work that I wanted to do as part of #84 but wasn’t sure whether it was worth the effort. But @leohemsted’s comments on that PR got me thinking again, and I had 2 uninterrupted hours on the train this morning, so here we are.


This is a big refactor. It’s addressing three issues which were making the existing code bloated and overly complicated:

  1. The interaction between the Template and Renderer classes was still weird, because an instance of Renderer had effectively become a method of a Template instance

  2. The Renderer classes themselves weren’t really proper classes, they were basically just functions that took a template and turned it into a string, but architected in a slightly odd way. There are people who consider a class that only has __init__ and __call__ methods an anti-pattern (see Jack Diedrich’s talk Stop writing classes

  3. There were methods on the Template class (eg content_count) which were very specific to one kind of template (eg a text message).

This commit makes three main changes to address these issues:

  1. Makes Template into an abstract-ish base class, and makes the various Renderers into Templates which inherit from the base template class (ie HTMLEmailTemplate instead of a renderer called HTMLEmail).

  2. Moves the functionality of the renderers to __str__ methods on template instances. This is a better model of what they do (turn templates into strings).

  3. Methods that are only relevant to one type of template (eg text messages) now live only on that type of template.

There is one thing that’s still slightly weird about this. Service-level things on a template (eg the text message sender name) aren’t set through an __init__ method any more because there’s no appropriate place for one to exist.

Instead they are set directly on the instance (eg template.sms_sender = 'Service name'). This means doing some slightly funny jiggery pokery around default and required ‘arguments’, which
would be more intuitive with an __init__. But I think the tradeoff is worth it for the simpler code elsewhere.

super() considered super!

Also, add subject property back onto certain templates

It’s really ugly having to do this sort of thing all over the API:

subject=str(Field(plain_text_email.subject, notification.personalisation))

But it doesn’t really belong in the base Template class because not all templates have subjects.

So this commit re-adds subjects (and the highlighting/replacing thereof) to those templates which do have subject lines.

So the API code can just change to:

subject=template.subject

@quis quis force-pushed the refactor-template-renderer branch 10 times, most recently from 4ebfa41 to 8dfc4d8 Compare December 6, 2016 11:36
The renderers are going to become part of the `Template` class, so
there are going to be more tests of the various types of template than
just the ones in this file.

But I don’t wanna merge them all into one file because they’ll be
testing different things, and it could get very long.
Just so it doesn’t get lost amongst all the other template stuff.
@quis quis force-pushed the refactor-template-renderer branch from 8dfc4d8 to dafc322 Compare December 6, 2016 16:58
This is a big refactor. It’s addressing three issues which were making
the existing code bloated and overly complicated:

1. The interaction between the `Template` and `Renderer` classes was
   still weird, because an instance of `Renderer` had effectively become
   a method of a `Template` instance

2. The `Renderer` classes themselves weren’t really proper classes, they
   were bsically just functions that took a template and turned it into
   a string, but architected in a slightly odd way. There are people who
   consider a class that only has `__init__` and `__call__` methods an
   anti-pattern [1]

3. There were methods on the `Template` class (eg `content_count`) which
   were very specific to one kind of template (eg a text message).

This commit makes two main changes to address these issues:

1. Makes `Template` into an abstract-ish base class, and makes the
   various `Renderer`s into `Template`s which inherit from the base
   template class (ie `HTMLEmailTemplate` instead of a renderer called
   `HTMLEmail`).

2. Moves the functionality of the renderers to `__str__` methods on
   template instances. This is a better model of what they do (turn
   templates into strings).

3. Methods that are only relevant to one type of template (eg text
   messages) now live only on that type of template.

---

1. Stop writing classes – Jack Diedrich:
   https://www.youtube.com/watch?v=o9pEzgHorH0
quis added a commit to alphagov/notifications-admin that referenced this pull request Dec 9, 2016
quis added a commit to alphagov/notifications-admin that referenced this pull request Dec 9, 2016
It’s really ugly having to do this sort of thing all over the API:
```python
subject=str(Field(plain_text_email.subject, notification.personalisation))
```

But it doesn’t really belong in the base `Template` class because not
all templates have subjects.

So this commit re-adds subjects (and the highlighting/replacing thereof)
to those templates which do have subject lines.

So the API code can just change to:

```python
subject=template.subject
```
quis added a commit to alphagov/notifications-api that referenced this pull request Dec 9, 2016
Includes:

- [ ] alphagov/notifications-utils#94 (breaking
      changes which are responsible for all the changes to the API in
      this PR)
Copy link
Contributor

@leohemsted leohemsted left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(unrelated to current PR but there's some unreachable code in recipients.py#L269)

tested locally with admin and api checked out to respective PRs. seems mostly reasonable. code wise seems fine but there's too much code change here for me to critique layout without getting a headache ;)

but it doesn't honour sms placeholders exceeding max length and firetext throws a 400 at us

raise TypeError("The template needs to have a template type of 'sms'")
return get_sms_fragment_count(self.content_count)
def content_count(self):
return len(str(self).encode(self.encoding))

@property
def content_too_long(self):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused property? it certainly errors

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, the API was doing this comparison itself. Removed in: 7997fc7

'subject': self.subject,
'from_name': self.from_name,
'from_address': self.from_address,
'recipient': Field("((email address))", template.get('values', {}), with_brackets=False),
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

think this should be self._template? it's currently crashing when i try and send an email on admin app, so make sure there's a test for this

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

self.values. I think I’d fixed this already and squashed it out but not pushed it or something ¯_(ツ)_/¯

quis added a commit to alphagov/notifications-api that referenced this pull request Dec 12, 2016
Includes:

- [ ] alphagov/notifications-utils#94 (breaking
      changes which are responsible for all the changes to the API in
      this PR)
This method was actually broken by recent changes, and the API wasn’t
using it anyway – it was just doing the comparison itself.

This requires a slight change to `RecipientCSV` to also do the
comparison itself.
Significant, breaking changes:

- changes the interface to the `Template` class, including removal of
  the `drop_values` argument for all template types
- removes all `Renderer` classes and moves them to methods of subclasses
  of the `Template` class
@quis
Copy link
Member Author

quis commented Dec 13, 2016

Addresses unreachable code in a separate PR: #101

@quis quis merged commit ac20f7e into master Dec 13, 2016
@quis quis deleted the refactor-template-renderer branch December 13, 2016 10:30
quis added a commit to alphagov/notifications-admin that referenced this pull request Dec 13, 2016
quis added a commit to alphagov/notifications-api that referenced this pull request Dec 13, 2016
Includes:

- [ ] alphagov/notifications-utils#94 (breaking
      changes which are responsible for all the changes to the API in
      this PR)
quis added a commit to alphagov/notifications-api that referenced this pull request Dec 13, 2016
Includes:

- [x] alphagov/notifications-utils#94 (breaking
      changes which are responsible for all the changes to the API in
      this PR)

The test for `get_sms_fragment_count` has been removed because this
method is already tested in utils here:

https://github.com/alphagov/notifications-utils/blob/ac20f7e99ece4935fccf39ed9d5c0fb40e45bfbc/tests/test_base_template.py#L140-L159
quis added a commit to alphagov/notifications-admin that referenced this pull request Dec 14, 2016
quis added a commit to alphagov/notifications-admin that referenced this pull request Dec 14, 2016
andyhd pushed a commit to crossgovernmentservices/notifications-admin that referenced this pull request Feb 22, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants