Remove custom Twig extensions

Go back to calculating the talk count inline and using a macro to get
the number of years experience.
This commit is contained in:
Oliver Davies 2024-11-29 22:36:18 +00:00
parent c4ac4a8d3f
commit 94a4190be6
14 changed files with 1659 additions and 1871 deletions

View file

@ -10,11 +10,3 @@ sculpin_content_types:
permalink: /blog/:basename/ permalink: /blog/:basename/
presentations: presentations:
permalink: /presentations/:basename/ permalink: /presentations/:basename/
services:
App\Experience\ExperienceTwigExtension:
tags:
- {name: twig.extension}
App\Presentations\PresentationTwigExtension:
tags:
- {name: twig.extension}

View file

@ -8,18 +8,5 @@
"allow-plugins": { "allow-plugins": {
"sculpin/sculpin-theme-composer-plugin": true "sculpin/sculpin-theme-composer-plugin": true
} }
},
"autoload": {
"psr-4": {
"App\\": "src/"
}
},
"autoload-dev": {
"psr-4": {
"Tests\\": "tests/"
}
},
"require-dev": {
"phpunit/phpunit": "^11.1"
} }
} }

3267
composer.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,3 +1,5 @@
{% import "_macros" as macros %}
<div> <div>
<h2 class="text-xl font-bold">About me</h2> <h2 class="text-xl font-bold">About me</h2>
@ -8,7 +10,7 @@
</div> </div>
<div class="{{ site.prose_classes }}"> <div class="{{ site.prose_classes }}">
<p>I'm an Acquia-certified Drupal Triple Expert with {{ get_years_of_experience() }} years of experience, an open-source software maintainer and Drupal core contributor, <a href="/presentations">public speaker</a>, <a href="{{ site.youtube.channel.url }}/streams">live streamer</a>, and host of the <a href="/podcast">Beyond Blocks podcast</a>.</p> <p>I'm an Acquia-certified Drupal Triple Expert with {{ macros.yearsOfExperience }} years of experience, an open-source software maintainer and Drupal core contributor, <a href="/presentations">public speaker</a>, <a href="{{ site.youtube.channel.url }}/streams">live streamer</a>, and host of the <a href="/podcast">Beyond Blocks podcast</a>.</p>
</div> </div>
</div> </div>
</div> </div>

View file

@ -0,0 +1,3 @@
{% macro yearsOfExperience() %}
{{ today|date('Y') - 2007 }}
{% endmacro %}

View file

@ -22,6 +22,8 @@ faqs:
{% block content %} {% block content %}
{% import "_macros" as macros %}
{# <h2>Who is this for?</h2> #} {# <h2>Who is this for?</h2> #}
{# Pain #} {# Pain #}
@ -30,7 +32,7 @@ faqs:
{# Fix #} {# Fix #}
<p>As a professional Software Developer and Consultant with {{ get_years_of_experience() }} years of Drupal and PHP experience, I have a lot of knowledge that I use to help customers and their projects.</p> <p>As a professional Software Developer and Consultant with {{ macros.yearsOfExperience }} years of Drupal and PHP experience, I have a lot of knowledge that I use to help customers and their projects.</p>
{# 1st call to action #} {# 1st call to action #}
@ -90,7 +92,7 @@ faqs:
<h2>Who am I?</h2> <h2>Who am I?</h2>
<ul> <ul>
<li>I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience.</li> <li>I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.</li>
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li> <li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li> <li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li> <li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>

View file

@ -5,6 +5,8 @@ button:
url: https://buy.stripe.com/aEU4h0gBc4ro0p27sz url: https://buy.stripe.com/aEU4h0gBc4ro0p27sz
--- ---
{% import "_macros" as macros %}
{# Pain #} {# Pain #}
Drupal 7 will be unsupported on the **5th of January 2025**. Drupal 7 will be unsupported on the **5th of January 2025**.
@ -68,7 +70,7 @@ An upgrade roadmap is a personalised audit of your Drupal website and includes d
## Who am I? ## Who am I?
* I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience. * I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.
* I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org. * I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.
* I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites. * I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.
* I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups. * I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.

View file

@ -6,6 +6,8 @@ link: https://savvycal.com/opdavies/pair
{% block content %} {% block content %}
{% import "_macros" as macros %}
{# Pain #} {# Pain #}
<p>Are you stuck adding a new feature or fixing a bug?</p> <p>Are you stuck adding a new feature or fixing a bug?</p>
@ -48,7 +50,7 @@ link: https://savvycal.com/opdavies/pair
<h2>Who am I?</h2> <h2>Who am I?</h2>
<ul> <ul>
<li>I'm an Acquia-certified Drupal expert with {{ get_years_of_experience() }} years of professional development experience.</li> <li>I'm an Acquia-certified Drupal expert with {{ macros.yearsOfExperience }} years of professional development experience.</li>
<li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li> <li>I'm a former Drupal Association employee who was responsible for improving and maintaining Drupal.org.</li>
<li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li> <li>I'm a Drupal core contributor and maintain numerous Drupal projects, including the Override Node Options module, which is used on over 38,000 websites.</li>
<li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li> <li>I'm a multiple-time DrupalCon speaker who regularly presents talks and workshops at conferences and meetups.</li>

View file

@ -3,7 +3,10 @@ title: Presentations
use: [presentations] use: [presentations]
--- ---
Since September 2012, I have given {{ get_presentation_count(data.presentations) }} public talks and workshops at various conferences and meetups, in-person and remotely, on topics including PHP, Drupal, automated testing, Git, CSS, and systems administration. {% set today = 'today'|date('U') %}
{% set presentation_count = data.presentations|reduce((count, presentation) => count + (presentation.events|filter(e => e.date|date('U') < today)|length), 0) %}
<p>Since September 2012, I have given {{ presentation_count }} public talks and workshops at various conferences and meetups, in-person and remotely, on topics including PHP, Drupal, automated testing, Git, CSS, and systems administration.</p>
{% for talk in data.presentations|sort((a, b) => a.events|last.date|date('U') > b.events|last.date|date('U') ? -1 : 1) %} {% for talk in data.presentations|sort((a, b) => a.events|last.date|date('U') > b.events|last.date|date('U') ? -1 : 1) %}
<article> <article>

View file

@ -2,13 +2,15 @@
title: Press Info title: Press Info
--- ---
{% import "_macros" as macros %}
The following information is provided as a cut-and-paste resource for conference organisers, media professionals, podcast hosts, and other interested parties. The following information is provided as a cut-and-paste resource for conference organisers, media professionals, podcast hosts, and other interested parties.
Please feel free to use anything here as-is without checking with me first. If you have additional questions, you can <a href="mailto:{{ site.email }}">email me directly</a>. Please feel free to use anything here as-is without checking with me first. If you have additional questions, you can <a href="mailto:{{ site.email }}">email me directly</a>.
## Short Bio ## Short Bio
Oliver is a Software Developer and Drupal expert with {{ get_years_of_experience() }} years experience. He specialises in code quality, automated testing and test-driven development. Oliver is a Software Developer and Drupal expert with {{ macros.yearsOfExperience }} years experience. He specialises in code quality, automated testing and test-driven development.
## Sample Topics ## Sample Topics

View file

@ -2,9 +2,11 @@
title: Speaker Information title: Speaker Information
--- ---
{% import "_macros" as macros %}
## Bio ## Bio
Oliver is a Software Developer and Drupal Expert with {{ get_years_of_experience() }} years of experience. As well as consulting on large Drupal projects, Oliver helps Drupal Developers learn automated testing and test-driven development via a free email course and paid coaching and workshops. He regularly contributes to open-source software projects, including Drupal core. Oliver is a Software Developer and Drupal Expert with {{ macros.yearsOfExperience }} years of experience. As well as consulting on large Drupal projects, Oliver helps Drupal Developers learn automated testing and test-driven development via a free email course and paid coaching and workshops. He regularly contributes to open-source software projects, including Drupal core.
## Photos ## Photos

View file

@ -1,29 +0,0 @@
<?php
namespace App\Experience;
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class ExperienceTwigExtension extends AbstractExtension
{
private static $startYear = 2007;
public function getFunctions(): array
{
return [
new TwigFunction('get_years_of_experience', [$this, 'getYearsOfExperience']),
];
}
public function getName(): string
{
return 'experience';
}
public function getYearsOfExperience(): int
{
return (new \DateTimeImmutable())->format('Y') - self::$startYear;
}
}

View file

@ -1,38 +0,0 @@
<?php
namespace App\Presentations;
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
use Twig\Extension\AbstractExtension;
use Twig\TwigFunction;
class PresentationTwigExtension extends AbstractExtension
{
public function getFunctions(): array
{
return [
new TwigFunction('get_presentation_count', [$this, 'getPresentationCount']),
];
}
public function getName(): string
{
return 'presentations';
}
public function getPresentationCount(array $presentations): int
{
$today = (new \DateTime('today'))->getTimestamp();
return collect($presentations)
->flatMap(fn (ProxySourceItem $presentation) => $presentation->data()->get('events'))
->filter(
function (array $event) use ($today): bool {
assert(array_key_exists(array: $event, key: 'date'));
return $event['date'] < $today;
}
)
->count();
}
}

View file

@ -1,141 +0,0 @@
<?php
namespace Tests\Presentations;
use App\Presentations\PresentationTwigExtension;
use Dflydev\DotAccessConfiguration\Configuration;
use PHPUnit\Framework\TestCase;
use Sculpin\Contrib\ProxySourceCollection\ProxySourceItem;
class PresentationTwigExtensionTest extends TestCase
{
private PresentationTwigExtension $extension;
public function setUp(): void
{
$this->extension = new PresentationTwigExtension();
}
public function testNoPastEvents(): void
{
$presentation = $this->createPresentation(
events: [
['date' => (new \DateTime('+1 days'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 0, presentations: [$presentation]);
}
public function testSinglePastEvent(): void
{
$presentationA = $this->createPresentation(
events: [
['date' => (new \DateTime('+1 days'))->getTimestamp()],
],
);
$presentationB = $this->createPresentation(
events: [
['date' => (new \DateTime('-3 days'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 1, presentations: [$presentationA, $presentationB]);
}
public function testSinglePresentationWithMultiplePastEvents(): void
{
$presentation = $this->createPresentation(
events: [
['date' => (new \DateTime('-1 days'))->getTimestamp()],
['date' => (new \DateTime('-1 week'))->getTimestamp()],
['date' => (new \DateTime('-1 year'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 3, presentations: [$presentation]);
}
public function testSinglePresentationWithMultiplePastAndFutureEvents(): void
{
$presentation = $this->createPresentation(
events: [
['date' => (new \DateTime('+1 day'))->getTimestamp()],
['date' => (new \DateTime('-1 day'))->getTimestamp()],
['date' => (new \DateTime('-1 week'))->getTimestamp()],
['date' => (new \DateTime('+1 year'))->getTimestamp()],
['date' => (new \DateTime('-1 year'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 3, presentations: [$presentation]);
}
public function testMultiplePastEvents(): void
{
$presentationA = $this->createPresentation(
events: [
['date' => (new \DateTime('-1 days'))->getTimestamp()],
['date' => (new \DateTime('+1 days'))->getTimestamp()],
],
);
$presentationB = $this->createPresentation(
events: [
['date' => (new \DateTime('-3 days'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 2, presentations: [$presentationA, $presentationB]);
}
public function testTheCurrentDayIsNotCounted(): void
{
$presentationA = $this->createPresentation(
events: [
['date' => (new \DateTime('yesterday'))->getTimestamp()],
['date' => (new \DateTime('today'))->getTimestamp()],
],
);
$presentationB = $this->createPresentation(
events: [
['date' => (new \DateTime('today'))->getTimestamp()],
],
);
$presentationC = $this->createPresentation(
events: [
['date' => (new \DateTime('yesterday'))->getTimestamp()],
],
);
$this->assertPresentationCount(expectedCount: 2, presentations: [$presentationA, $presentationB, $presentationC]);
}
/**
* Assert the extension uses the correct number of presentations.
*/
private function assertPresentationCount(int $expectedCount, array $presentations): void
{
self::assertSame(
actual: $this->extension->getPresentationCount($presentations),
expected: $expectedCount,
);
}
/**
* Create a mock presentation with a list of events.
*/
private function createPresentation(array $events): ProxySourceItem
{
$configuration = $this->createMock(Configuration::class);
$configuration->method('get')->with($this->identicalTo('events'))->willReturn($events);
$presentation = $this->createMock(ProxySourceItem::class);
$presentation->method('data')->willReturn($configuration);
return $presentation;
}
}