Update content
This commit is contained in:
parent
a743be105b
commit
d4d5a179c7
3246 changed files with 108862 additions and 108287 deletions
|
@ -34,17 +34,17 @@
|
|||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
"value": "2025-04-21T01:22:04+00:00"
|
||||
}
|
||||
],
|
||||
"imported": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
"value": "2025-04-21T01:22:05+00:00"
|
||||
}
|
||||
],
|
||||
"next": [
|
||||
{
|
||||
"value": "1969-12-31T23:59:59+00:00"
|
||||
"value": "2025-04-21T13:22:05+00:00"
|
||||
}
|
||||
],
|
||||
"queued": [
|
||||
|
@ -54,7 +54,7 @@
|
|||
],
|
||||
"source": [
|
||||
{
|
||||
"value": "public:\/\/feeds\/daily2_1.xml"
|
||||
"value": "https:\/\/www.oliverdavies.uk\/rss\/daily2.xml"
|
||||
}
|
||||
],
|
||||
"config": [
|
||||
|
@ -67,7 +67,7 @@
|
|||
],
|
||||
"item_count": [
|
||||
{
|
||||
"value": 808
|
||||
"value": 811
|
||||
}
|
||||
]
|
||||
}
|
11356
content/meta/index.json
11356
content/meta/index.json
File diff suppressed because it is too large
Load diff
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0077d24d-3555-4798-8463-0b47e8aa3a67"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Why I write automated tests"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/14\/why-i-write-tests",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>In February 2012, I saw a tweet from Tim Millwood asking if anyone wanted to maintain or co-maintain a Drupal module called <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">Override Node Options<\/a>.<\/p>\n\n<p>It had more than 9,200 active installations at that time, with versions for Drupal 5, 6 and 7.<\/p>\n\n<p>I said yes and became the module\u2019s maintainer.<\/p>\n\n<p>The module now has versions for Drupal 7, 8 and 9, with (at the latest count, according to Drupal.org) 32,292 active installations - which makes it currently the 197th most installed module.<\/p>\n\n<p>There have been two main things that come to mind with this module, related to automated testing.<\/p>\n\n<p>Before I become the maintainer, a feature request had been created, along with a large patch file, to add some new permissions to the module. There were some large merge conflicts that stopped me from just committing the changes but I was able to fix them manually and, because the tests still passed, ensure that the original functionality still worked. There weren\u2019t tests for the new permissions but I committed the patch and added the tests later.<\/p>\n\n<p>Without the tests to ensure that the original functionality still worked, I probably wouldn\u2019t have committed the patch and would have just closed the issue.<\/p>\n\n<p>More recently, a friend and ex-colleague and I decided to refactor some of the module's code.<\/p>\n\n<p>We wanted to split the <code>override_node_options.module<\/code> file so that each override was in its own file and its own class. This would make them easier to edit and maintain, and if anyone wanted to add a new one, they\u2019d just need to create a new file for it and add it to the list of overrides.<\/p>\n\n<p>Without the tests ensuring that the module still worked after the refactor, we probably wouldn\u2019t have done it as it was used on over 30,000 sites that I didn't want to break.<\/p>\n\n<p>When I was learning about testing, I was working on projects where I was writing the code during the day and the tests in the evening on my own time.<\/p>\n\n<p>I remember once when my manual testing had been fine, but when writing the test, I found that I\u2019d used an incorrect permission name in the code that was causing the test to fail. This was a bug that, rather than waiting for a QA Engineer or the client to discover and report, I was able to fix it locally before I'd even committed the code.<\/p>\n\n<p>I also worked on an event booking and management website, where we had code responsible for calculating the number of available spaces for an event based on orders, determining the correct price based on the customer's status and the time until the event, creating voucher codes for new members and event leaders, and bulk messaging event attendees. All of the custom functionality was covered by automated tests.<\/p>\n\n<p>The great thing about testing is that it gives you confidence that everything still works how you expect - not only when you wrote the code, but also in the future.<\/p>\n\n<p>I've talked about this, and how to get started with automated testing in Drupal, in a presentation called <a href=\"http:\/\/localhost:8000\/presentations\/tdd-test-driven-drupal\">TDD - Test-Driven Drupal<\/a>. If you want to find out more, the slides and a video recording are embedded there.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>In February 2012, I saw a tweet from Tim Millwood asking if anyone wanted to maintain or co-maintain a Drupal module called <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">Override Node Options<\/a>.<\/p>\n\n<p>It had more than 9,200 active installations at that time, with versions for Drupal 5, 6 and 7.<\/p>\n\n<p>I said yes and became the module\u2019s maintainer.<\/p>\n\n<p>The module now has versions for Drupal 7, 8 and 9, with (at the latest count, according to Drupal.org) 32,292 active installations - which makes it currently the 197th most installed module.<\/p>\n\n<p>There have been two main things that come to mind with this module, related to automated testing.<\/p>\n\n<p>Before I become the maintainer, a feature request had been created, along with a large patch file, to add some new permissions to the module. There were some large merge conflicts that stopped me from just committing the changes but I was able to fix them manually and, because the tests still passed, ensure that the original functionality still worked. There weren\u2019t tests for the new permissions but I committed the patch and added the tests later.<\/p>\n\n<p>Without the tests to ensure that the original functionality still worked, I probably wouldn\u2019t have committed the patch and would have just closed the issue.<\/p>\n\n<p>More recently, a friend and ex-colleague and I decided to refactor some of the module's code.<\/p>\n\n<p>We wanted to split the <code>override_node_options.module<\/code> file so that each override was in its own file and its own class. This would make them easier to edit and maintain, and if anyone wanted to add a new one, they\u2019d just need to create a new file for it and add it to the list of overrides.<\/p>\n\n<p>Without the tests ensuring that the module still worked after the refactor, we probably wouldn\u2019t have done it as it was used on over 30,000 sites that I didn't want to break.<\/p>\n\n<p>When I was learning about testing, I was working on projects where I was writing the code during the day and the tests in the evening on my own time.<\/p>\n\n<p>I remember once when my manual testing had been fine, but when writing the test, I found that I\u2019d used an incorrect permission name in the code that was causing the test to fail. This was a bug that, rather than waiting for a QA Engineer or the client to discover and report, I was able to fix it locally before I'd even committed the code.<\/p>\n\n<p>I also worked on an event booking and management website, where we had code responsible for calculating the number of available spaces for an event based on orders, determining the correct price based on the customer's status and the time until the event, creating voucher codes for new members and event leaders, and bulk messaging event attendees. All of the custom functionality was covered by automated tests.<\/p>\n\n<p>The great thing about testing is that it gives you confidence that everything still works how you expect - not only when you wrote the code, but also in the future.<\/p>\n\n<p>I've talked about this, and how to get started with automated testing in Drupal, in a presentation called <a href=\"http:\/\/localhost:8000\/presentations\/tdd-test-driven-drupal\">TDD - Test-Driven Drupal<\/a>. If you want to find out more, the slides and a video recording are embedded there.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:07+00:00",
|
||||
"guid": null,
|
||||
"hash": "55096628413f9bbe1be1adce212ab46c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0086c29b-9324-4d72-bdc0-c2a75c2aaca5.json
Normal file
100
content/node.0086c29b-9324-4d72-bdc0-c2a75c2aaca5.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0086c29b-9324-4d72-bdc0-c2a75c2aaca5"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Building fonts with Nix"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-04-16T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/04\/16\/fonts",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I recently started <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">using Nix to build my PDF presentation slides<\/a> that <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">I create with rst2pdf<\/a>.<\/p>\n\n<p>I removed the custom build script that was generating the PDF files and moved that code into a Nix derivation.<\/p>\n\n<p>Now I can run <code>nix build .#test-driven-drupal<\/code> and it will generate the slides for that talk.<\/p>\n\n<p>As well as the files specific to each presentation, I also have a derivation for shared assets that apply to all talks - i.e. the stylesheets and fonts that are embedded within the PDF.<\/p>\n\n<p>The font files were stored in the repository but I wanted to remove them and use the font files available in nixpkgs.<\/p>\n\n<p>After some small changes, <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\/commit\/e24d2df83f04e492151b1a1f4901490ce76ffd45\">I was able to do it<\/a> and remove the font files from my repository.<\/p>\n\n<p>The Inter font is available in nixpkgs, but it downloads an <code>InterVariable.ttf<\/code> font that rst2pdf didn't know how to change the font weight for, so I made my own derivation of the static Inter font based on the releases from GitHub.<\/p>\n\n<p>I'm happy that I was able to achieve this, as my repository is leaner and I'm continuing to find new and interesting uses for Nix in my workflows.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I recently started <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">using Nix to build my PDF presentation slides<\/a> that <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">I create with rst2pdf<\/a>.<\/p>\n\n<p>I removed the custom build script that was generating the PDF files and moved that code into a Nix derivation.<\/p>\n\n<p>Now I can run <code>nix build .#test-driven-drupal<\/code> and it will generate the slides for that talk.<\/p>\n\n<p>As well as the files specific to each presentation, I also have a derivation for shared assets that apply to all talks - i.e. the stylesheets and fonts that are embedded within the PDF.<\/p>\n\n<p>The font files were stored in the repository but I wanted to remove them and use the font files available in nixpkgs.<\/p>\n\n<p>After some small changes, <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\/commit\/e24d2df83f04e492151b1a1f4901490ce76ffd45\">I was able to do it<\/a> and remove the font files from my repository.<\/p>\n\n<p>The Inter font is available in nixpkgs, but it downloads an <code>InterVariable.ttf<\/code> font that rst2pdf didn't know how to change the font weight for, so I made my own derivation of the static Inter font based on the releases from GitHub.<\/p>\n\n<p>I'm happy that I was able to achieve this, as my repository is leaner and I'm continuing to find new and interesting uses for Nix in my workflows.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:17+00:00",
|
||||
"guid": null,
|
||||
"hash": "663165d347672170986b3ba5d0f0ac13",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.008a122d-b219-46fa-8774-999d1e56e18b.json
Normal file
100
content/node.008a122d-b219-46fa-8774-999d1e56e18b.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "008a122d-b219-46fa-8774-999d1e56e18b"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Consistency is key\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-04-18T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/04\/18\/consistency-is-key",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>A side effect of <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/03\/04\/why-i-built-a-tool-to-generate-configuration-files\">using a tool to generate build configuration files<\/a> with templates is the consistency that it introduces.<\/p>\n\n<p>The majority of my projects use a PHP-FPM or PHP CLI container. In my Docker Compose file, the service was mostly named <code>php<\/code> but sometimes it was <code>php-fpm<\/code>. In the templated file, it's always named <code>php<\/code>.<\/p>\n\n<p>Some projects would use <code>mysql<\/code> or <code>mariadb<\/code> for the database service and <code>nginx<\/code> or <code>caddy<\/code> depending on which server was being used. These are now always <code>database<\/code> and <code>web<\/code> respectively.<\/p>\n\n<p>As well as being easier to switch between projects and not having to think about which names are used in each codebase, it's also much easier to write tools and automation when the names are consistent.<\/p>\n\n<p>For example, I'd always write a long-ish command to import a database file - reading and unzipping it, and importing it by connecting to the database running in its container. The command would essentially be the same with slight changes based on that project - such as the database service name.<\/p>\n\n<p>Now the command is the same for all projects, and I can automate it by writing a script that works on any project meaning I no longer need to write the long command at all.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>A side effect of <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/03\/04\/why-i-built-a-tool-to-generate-configuration-files\">using a tool to generate build configuration files<\/a> with templates is the consistency that it introduces.<\/p>\n\n<p>The majority of my projects use a PHP-FPM or PHP CLI container. In my Docker Compose file, the service was mostly named <code>php<\/code> but sometimes it was <code>php-fpm<\/code>. In the templated file, it's always named <code>php<\/code>.<\/p>\n\n<p>Some projects would use <code>mysql<\/code> or <code>mariadb<\/code> for the database service and <code>nginx<\/code> or <code>caddy<\/code> depending on which server was being used. These are now always <code>database<\/code> and <code>web<\/code> respectively.<\/p>\n\n<p>As well as being easier to switch between projects and not having to think about which names are used in each codebase, it's also much easier to write tools and automation when the names are consistent.<\/p>\n\n<p>For example, I'd always write a long-ish command to import a database file - reading and unzipping it, and importing it by connecting to the database running in its container. The command would essentially be the same with slight changes based on that project - such as the database service name.<\/p>\n\n<p>Now the command is the same for all projects, and I can automate it by writing a script that works on any project meaning I no longer need to write the long command at all.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "aacb745c094cf5a9f668c64b15000f49",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.00e8b0fc-a551-47e4-b0df-b568beada250.json
Normal file
100
content/node.00e8b0fc-a551-47e4-b0df-b568beada250.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "00e8b0fc-a551-47e4-b0df-b568beada250"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Legacy code is anything older than..."
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-03-22T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/03\/22\/legacy",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>How do you define legacy code?<\/p>\n\n<p>Code that was written by someone else?<\/p>\n\n<p>Code that doesn't have tests?<\/p>\n\n<p>Any code that has been released to production?<\/p>\n\n<p>Code that's more than a day old?<\/p>\n\n<p>In a talk I recently watched, the speaker suggested that any code written more than thirty minutes ago is legacy code.<\/p>\n\n<p>Once you've written some code and left it for half an hour, you need to re-read it to remember and re-learn what the code does and decide how you want to implement your next requirement.<\/p>\n\n<p>This is the same approach for code that was written longer ago or written by someone else.<\/p>\n\n<p>What do you think?<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>How do you define legacy code?<\/p>\n\n<p>Code that was written by someone else?<\/p>\n\n<p>Code that doesn't have tests?<\/p>\n\n<p>Any code that has been released to production?<\/p>\n\n<p>Code that's more than a day old?<\/p>\n\n<p>In a talk I recently watched, the speaker suggested that any code written more than thirty minutes ago is legacy code.<\/p>\n\n<p>Once you've written some code and left it for half an hour, you need to re-read it to remember and re-learn what the code does and decide how you want to implement your next requirement.<\/p>\n\n<p>This is the same approach for code that was written longer ago or written by someone else.<\/p>\n\n<p>What do you think?<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:17+00:00",
|
||||
"guid": null,
|
||||
"hash": "9983c859dafd94ec45282b7438b9cb59",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
102
content/node.00e9d07f-479d-4a4d-8a13-ad6cf44c0454.json
Normal file
102
content/node.00e9d07f-479d-4a4d-8a13-ad6cf44c0454.json
Normal file
|
@ -0,0 +1,102 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "00e9d07f-479d-4a4d-8a13-ad6cf44c0454"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "page",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "a6dfb70f-21fc-46e0-aa8c-45c5d0cef75f"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-19T00:16:39+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Examples"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-04-18T23:50:11+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-19T00:16:39+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/examples",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "<p>Here are links to example websites and code repositories I have created for talks, presentations and demonstrations:<\/p><ul><li><a href=\"https:\/\/rebuilding-bartik.oliverdavies.uk\/\">Rebuilding Bartik<\/a><\/li><li><a href=\"https:\/\/rebuilding-acquia.oliverdavies.uk\">Rebuilding Acquia<\/a><\/li><li><a href=\"https:\/\/wp-tailwind.oliverdavies.uk\">WordPress' 2019 theme<\/a><\/li><li><a href=\"https:\/\/rebuilding-symfony.oliverdavies.uk\">Rebuilding Symfony<\/a><\/li><li><a href=\"https:\/\/rebuilding-bristol-js.oliverdavies.uk\">Rebuilding Bristol JS<\/a><\/li><li><a href=\"https:\/\/phpsw-sculpin-demo.oliverdavies.uk\">PHPSW Sculpin demo (website)<\/a><\/li><li><a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/phpsw-sculpin-demo\">PHPSW Sculpin demo (code)<\/a><\/li><\/ul>",
|
||||
"format": "basic_html",
|
||||
"processed": "<p>Here are links to example websites and code repositories I have created for talks, presentations and demonstrations:<\/p><ul><li><a href=\"https:\/\/rebuilding-bartik.oliverdavies.uk\/\">Rebuilding Bartik<\/a><\/li><li><a href=\"https:\/\/rebuilding-acquia.oliverdavies.uk\">Rebuilding Acquia<\/a><\/li><li><a href=\"https:\/\/wp-tailwind.oliverdavies.uk\">WordPress' 2019 theme<\/a><\/li><li><a href=\"https:\/\/rebuilding-symfony.oliverdavies.uk\">Rebuilding Symfony<\/a><\/li><li><a href=\"https:\/\/rebuilding-bristol-js.oliverdavies.uk\">Rebuilding Bristol JS<\/a><\/li><li><a href=\"https:\/\/phpsw-sculpin-demo.oliverdavies.uk\">PHPSW Sculpin demo (website)<\/a><\/li><li><a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/phpsw-sculpin-demo\">PHPSW Sculpin demo (code)<\/a><\/li><\/ul>",
|
||||
"summary": ""
|
||||
}
|
||||
],
|
||||
"field_seo_analysis": [
|
||||
{
|
||||
"status": "0",
|
||||
"focus_keyword": "",
|
||||
"title": null,
|
||||
"description": null
|
||||
}
|
||||
],
|
||||
"field_seo_description": [],
|
||||
"field_seo_image": [],
|
||||
"field_seo_title": []
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "010cb9ae-6877-48ab-aa77-7c26d067c9b8"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Committing CI artifacts"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-07-03T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/07\/03\/committing-ci-artifacts",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>One of the main uses for <a href=\"http:\/\/localhost:8000\/daily\/2024\/07\/02\/ci-not-ci-pipeline\">a CI pipeline<\/a> is to build artifacts for your application, such as installing your dependencies using Composer or npm, or using build tools to perform tasks such as building your CSS and JavaScript assets.<\/p>\n\n<p>Performing these tasks in a CI pipeline means the resulting files can be ignored from your code repository and not committed - making your commits smaller and easier to review, and less likely for you to encounter merge conflicts.<\/p>\n\n<p>The alternative approach is to not use a CI pipline and to perform the tasks manually and commit them to your repository.<\/p>\n\n<p>This introduces a separate set of challenges, but people like having the files in their repository and not worrying about failures in their pipeline.<\/p>\n\n<p>Which do you prefer?<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>One of the main uses for <a href=\"http:\/\/localhost:8000\/daily\/2024\/07\/02\/ci-not-ci-pipeline\">a CI pipeline<\/a> is to build artifacts for your application, such as installing your dependencies using Composer or npm, or using build tools to perform tasks such as building your CSS and JavaScript assets.<\/p>\n\n<p>Performing these tasks in a CI pipeline means the resulting files can be ignored from your code repository and not committed - making your commits smaller and easier to review, and less likely for you to encounter merge conflicts.<\/p>\n\n<p>The alternative approach is to not use a CI pipline and to perform the tasks manually and commit them to your repository.<\/p>\n\n<p>This introduces a separate set of challenges, but people like having the files in their repository and not worrying about failures in their pipeline.<\/p>\n\n<p>Which do you prefer?<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "9b83d5f29fb969f33b4f82824db6eee3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0126db35-8bee-48eb-9586-b2084be1c852"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "I wrote a Neovim plugin"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-13T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/13\/i-wrote-a-neovim-plugin",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I enjoy writing and working with open-source software, starting back to when I started working with PHP and Drupal in 2007.<\/p>\n\n<p>Since then, I've written and maintained a number of Drupal modules and themes, PHP libraries, npm packages, Ansible roles and Docker images - all of which are available on my GitHub and Drupal.org pages.<\/p>\n\n<p>Just over a year ago, <a href=\"\/blog\/going-full-vim\">I switched to using Neovim full-time<\/a> for my development and DevOps work, and last week, I wrote my first Neovim plugin, written in Lua.<\/p>\n\n<p>I've used Lua to configure Neovim but this is the first time that I've written and open-sourced a standalone Neovim plugin.<\/p>\n\n<p>It's called <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">toggle-checkbox.nvim<\/a> and is used toggle checkboxes in Markdown files - something that I use frequently for to-do lists.<\/p>\n\n<p>For example, this a simple list containing both checked and unchecked checkboxes:<\/p>\n\n<pre><code class=\"markdown\">- [x] A completed task\n- [ ] An incomplete task\n<\/code><\/pre>\n\n<p>To toggle a checkbox, the <code>x<\/code> character needs to be either added or removed, depending on whether we're checking or unchecking it.<\/p>\n\n<p>This is done by calling the <code>toggle()<\/code> function within the plugin.<\/p>\n\n<p>In my Neovim configuration, I've added a keymap to do this:<\/p>\n\n<pre><code class=\"lua\">vim.keymap.set(\n \"n\",\n \"<leader>tt\",\n \"require('toggle-checkbox').toggle()\"\n)\n<\/code><\/pre>\n\n<p>This means that I can use the same keymap by running <code><leader>tt<\/code> to check or uncheck a checkbox. I could use Vim's replace mode to do this, but I really wanted to have one keymap that I could use for both.<\/p>\n\n<p>As it's my first Neovim plugin, I decided to keep it simple.<\/p>\n\n<p>The main <code>toggle-checkbox.lua<\/code> file is currently only 41 lines of code, and whilst there is an existing Vim plugin that I could have used, I was excited to write my own plugin for Neovim, to start contributing to the Neovim ecosystem, and add a Neovim plugin to my portfolio of open-source projects.<\/p>\n\n<p>You can view the plugin at <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">https:\/\/github.com\/opdavies\/toggle-checkbox.nvim<\/a>, as well as my Neovim configuration (which is also written in Lua) as part of <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/tree\/main\/roles\/neovim\/files\">my Dotfiles repository<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I enjoy writing and working with open-source software, starting back to when I started working with PHP and Drupal in 2007.<\/p>\n\n<p>Since then, I've written and maintained a number of Drupal modules and themes, PHP libraries, npm packages, Ansible roles and Docker images - all of which are available on my GitHub and Drupal.org pages.<\/p>\n\n<p>Just over a year ago, <a href=\"\/blog\/going-full-vim\">I switched to using Neovim full-time<\/a> for my development and DevOps work, and last week, I wrote my first Neovim plugin, written in Lua.<\/p>\n\n<p>I've used Lua to configure Neovim but this is the first time that I've written and open-sourced a standalone Neovim plugin.<\/p>\n\n<p>It's called <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">toggle-checkbox.nvim<\/a> and is used toggle checkboxes in Markdown files - something that I use frequently for to-do lists.<\/p>\n\n<p>For example, this a simple list containing both checked and unchecked checkboxes:<\/p>\n\n<pre><code class=\"markdown\">- [x] A completed task\n- [ ] An incomplete task\n<\/code><\/pre>\n\n<p>To toggle a checkbox, the <code>x<\/code> character needs to be either added or removed, depending on whether we're checking or unchecking it.<\/p>\n\n<p>This is done by calling the <code>toggle()<\/code> function within the plugin.<\/p>\n\n<p>In my Neovim configuration, I've added a keymap to do this:<\/p>\n\n<pre><code class=\"lua\">vim.keymap.set(\n \"n\",\n \"<leader>tt\",\n \"require('toggle-checkbox').toggle()\"\n)\n<\/code><\/pre>\n\n<p>This means that I can use the same keymap by running <code><leader>tt<\/code> to check or uncheck a checkbox. I could use Vim's replace mode to do this, but I really wanted to have one keymap that I could use for both.<\/p>\n\n<p>As it's my first Neovim plugin, I decided to keep it simple.<\/p>\n\n<p>The main <code>toggle-checkbox.lua<\/code> file is currently only 41 lines of code, and whilst there is an existing Vim plugin that I could have used, I was excited to write my own plugin for Neovim, to start contributing to the Neovim ecosystem, and add a Neovim plugin to my portfolio of open-source projects.<\/p>\n\n<p>You can view the plugin at <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">https:\/\/github.com\/opdavies\/toggle-checkbox.nvim<\/a>, as well as my Neovim configuration (which is also written in Lua) as part of <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/tree\/main\/roles\/neovim\/files\">my Dotfiles repository<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:07+00:00",
|
||||
"guid": null,
|
||||
"hash": "da50f1968f98fdd321596a74d98b2ec4",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.018a674d-e084-4947-b3d9-9459fb4b5e43.json
Normal file
100
content/node.018a674d-e084-4947-b3d9-9459fb4b5e43.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "018a674d-e084-4947-b3d9-9459fb4b5e43"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Finding the best test base\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-27T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/27\/finding-the-best-test-base",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As well as different base classes for types of tests - i.e. functional, kernel and unit - there are other test base classes within those that can be used to simplify things.<\/p>\n\n<p>For example, if we have this test:<\/p>\n\n<pre><code class=\"language-php\"><?php\n\nnamespace Drupal\\Tests\\example\\Kernel;\n\nuse Drupal\\KernelTests\\KernelTestBase;\nuse Drupal\\Tests\\node\\Traits\\NodeCreationTrait;\nuse Drupal\\Tests\\user\\Traits\\UserCreationTrait;\n\nclass ExampleTest extends KernelTestBase {\n\n use NodeCreationTrait;\n use UserCreationTrait;\n\n protected static $modules = [\n 'node',\n 'user',\n ];\n\n public function setUp(): void {\n parent::setUp();\n\n $this->installEntitySchema(entity_type_id: 'node');\n $this->installEntitySchema(entity_type_id: 'user');\n }\n\n public function testExample(): void {\n $user = $this->createUser();\n\n $article = $this->createNode([\n 'title' => 'Test article',\n 'uid' => $user,\n ]);\n\n self::assertSame('1', $article->getOwnerId());\n }\n\n}\n<\/code><\/pre>\n\n<p>Both creation traits must be imported, the <code>node<\/code> and <code>user<\/code> modules must be enabled, and the entity tables must be installed.<\/p>\n\n<p>When writing a lot of tests, this can result in duplication and more complex tests that take longer to write.<\/p>\n\n<p>This can be simplified using <code>EntityKernelTestBase<\/code> instead of <code>KernelTestBase<\/code>:<\/p>\n\n<pre><code class=\"language-php\"><?php\n\nuse Drupal\\KernelTests\\Core\\Entity\\EntityKernelTestBase;\nuse Drupal\\Tests\\node\\Traits\\NodeCreationTrait;\n\nclass ExampleTest extends EntityKernelTestBase {\n\n use NodeCreationTrait;\n\n protected static $modules = [\n 'node',\n ];\n\n public function testExample(): void {\n $user = $this->createUser();\n\n $article = $this->createNode([\n 'title' => 'Test article',\n 'uid' => $user,\n ]);\n\n self::assertSame('1', $article->getOwnerId());\n }\n\n}\n<\/code><\/pre>\n\n<p>The class is simpler, fewer modules must be specified, and the entity schemas are automatically installed.<\/p>\n\n<p>As well as the core modules, some contrib modules also provide their own base test cases.<\/p>\n\n<p>If you're using the Webform module, you may want to use <code>WebformAccessTestBase<\/code> instead of the standard <code>UnitTestCase<\/code>.<\/p>\n\n<p>It's definitely worth spending some time looking at what base test cases are available and which are the best ones to use for your own tests.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As well as different base classes for types of tests - i.e. functional, kernel and unit - there are other test base classes within those that can be used to simplify things.<\/p>\n\n<p>For example, if we have this test:<\/p>\n\n<pre><code class=\"language-php\"><?php\n\nnamespace Drupal\\Tests\\example\\Kernel;\n\nuse Drupal\\KernelTests\\KernelTestBase;\nuse Drupal\\Tests\\node\\Traits\\NodeCreationTrait;\nuse Drupal\\Tests\\user\\Traits\\UserCreationTrait;\n\nclass ExampleTest extends KernelTestBase {\n\n use NodeCreationTrait;\n use UserCreationTrait;\n\n protected static $modules = [\n 'node',\n 'user',\n ];\n\n public function setUp(): void {\n parent::setUp();\n\n $this->installEntitySchema(entity_type_id: 'node');\n $this->installEntitySchema(entity_type_id: 'user');\n }\n\n public function testExample(): void {\n $user = $this->createUser();\n\n $article = $this->createNode([\n 'title' => 'Test article',\n 'uid' => $user,\n ]);\n\n self::assertSame('1', $article->getOwnerId());\n }\n\n}\n<\/code><\/pre>\n\n<p>Both creation traits must be imported, the <code>node<\/code> and <code>user<\/code> modules must be enabled, and the entity tables must be installed.<\/p>\n\n<p>When writing a lot of tests, this can result in duplication and more complex tests that take longer to write.<\/p>\n\n<p>This can be simplified using <code>EntityKernelTestBase<\/code> instead of <code>KernelTestBase<\/code>:<\/p>\n\n<pre><code class=\"language-php\"><?php\n\nuse Drupal\\KernelTests\\Core\\Entity\\EntityKernelTestBase;\nuse Drupal\\Tests\\node\\Traits\\NodeCreationTrait;\n\nclass ExampleTest extends EntityKernelTestBase {\n\n use NodeCreationTrait;\n\n protected static $modules = [\n 'node',\n ];\n\n public function testExample(): void {\n $user = $this->createUser();\n\n $article = $this->createNode([\n 'title' => 'Test article',\n 'uid' => $user,\n ]);\n\n self::assertSame('1', $article->getOwnerId());\n }\n\n}\n<\/code><\/pre>\n\n<p>The class is simpler, fewer modules must be specified, and the entity schemas are automatically installed.<\/p>\n\n<p>As well as the core modules, some contrib modules also provide their own base test cases.<\/p>\n\n<p>If you're using the Webform module, you may want to use <code>WebformAccessTestBase<\/code> instead of the standard <code>UnitTestCase<\/code>.<\/p>\n\n<p>It's definitely worth spending some time looking at what base test cases are available and which are the best ones to use for your own tests.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:42+00:00",
|
||||
"guid": null,
|
||||
"hash": "550456dfd70a84795f8c61ba84691a1d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.01b112a4-915a-4518-a75d-f9af67d2b119.json
Normal file
100
content/node.01b112a4-915a-4518-a75d-f9af67d2b119.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "01b112a4-915a-4518-a75d-f9af67d2b119"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:40+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Just..."
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-12-31T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:40+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/12\/31\/just",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>If you have a request that starts with \"Just\", it's likely a red flag!<\/p>\n\n<p>Why is it being made?<\/p>\n\n<p>What problem is it going to solve?<\/p>\n\n<p>Whose problem does it solve?<\/p>\n\n<p>Is the problem time-sensitive?<\/p>\n\n<p>What value does it add?<\/p>\n\n<p>Is it the best solution to fix the problem?<\/p>\n\n<p>Is it the best solution to fix the problem right now?<\/p>\n\n<p>To know this, we need to know why it's needed and it \"just\" needs doing.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>If you have a request that starts with \"Just\", it's likely a red flag!<\/p>\n\n<p>Why is it being made?<\/p>\n\n<p>What problem is it going to solve?<\/p>\n\n<p>Whose problem does it solve?<\/p>\n\n<p>Is the problem time-sensitive?<\/p>\n\n<p>What value does it add?<\/p>\n\n<p>Is it the best solution to fix the problem?<\/p>\n\n<p>Is it the best solution to fix the problem right now?<\/p>\n\n<p>To know this, we need to know why it's needed and it \"just\" needs doing.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:40+00:00",
|
||||
"guid": null,
|
||||
"hash": "4f25a7d5fdd045a4a767afa851b0d6f0",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.01ba9676-20ed-4ba2-80de-5caf92ba7407.json
Normal file
100
content/node.01ba9676-20ed-4ba2-80de-5caf92ba7407.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "01ba9676-20ed-4ba2-80de-5caf92ba7407"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Why \"no build\" is appealing"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-10-28T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/10\/28\/why-no-build-is-appealing",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>You're or a new\u00a0team member are\u00a0onboarded to a project to make some CSS changes.<\/p>\n\n<p>The project uses Sass, Less or another preprocessor.<\/p>\n\n<p>Not knowing this, the generated CSS files are changed instead of the Sass files.<\/p>\n\n<p>The changes are committed and pushed, but because they weren't added to the Sass files, they will be lost when the\u00a0CSS files are re-generated in the future.<\/p>\n\n<p>Or, if the Sass and CSS files become too out of sync, everyone will be too worried about losing changes to use the Sass files, so they will be abandoned\u00a0and not used.<\/p>\n\n<p>What if you change the\u00a0correct files but don't know the magic command to generate the assets for that project?<\/p>\n\n<p>I've seen things like this happen on numerous projects and is an example of why the \"no build\" approach is appealing.<\/p>\n\n<p>Especially with enhancements to CSS, such as custom properties (variables) and nesting, a lot of the functionality from preprocessors can now be done with normal CSS.<\/p>\n\n<p>This means the front-end build steps can be simplified\u00a0or removed entirely, reducing the technical overhead and dependency cost, and it's easier for new Developers to get started.<\/p>\n\n<p>Win, win!<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>You're or a new team member are onboarded to a project to make some CSS changes.<\/p>\n\n<p>The project uses Sass, Less or another preprocessor.<\/p>\n\n<p>Not knowing this, the generated CSS files are changed instead of the Sass files.<\/p>\n\n<p>The changes are committed and pushed, but because they weren't added to the Sass files, they will be lost when the CSS files are re-generated in the future.<\/p>\n\n<p>Or, if the Sass and CSS files become too out of sync, everyone will be too worried about losing changes to use the Sass files, so they will be abandoned and not used.<\/p>\n\n<p>What if you change the correct files but don't know the magic command to generate the assets for that project?<\/p>\n\n<p>I've seen things like this happen on numerous projects and is an example of why the \"no build\" approach is appealing.<\/p>\n\n<p>Especially with enhancements to CSS, such as custom properties (variables) and nesting, a lot of the functionality from preprocessors can now be done with normal CSS.<\/p>\n\n<p>This means the front-end build steps can be simplified or removed entirely, reducing the technical overhead and dependency cost, and it's easier for new Developers to get started.<\/p>\n\n<p>Win, win!<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:25+00:00",
|
||||
"guid": null,
|
||||
"hash": "247a1b12455fa21f13c5bcd13b69f5c7",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "01c4184d-7c73-479f-b005-18264ea50c2e"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Deployments or releases\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-06-21T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/06\/21\/deployments-or-releases",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>\"Deployments\" and \"releases\" are often used interchangeably but mean different things.<\/p>\n\n<p>A deployment moves a change from one place to another, such as some updated code from a staging environment to production.<\/p>\n\n<p>A release is when the change made is available to users.<\/p>\n\n<p>They can happen at the same time, or you can use feature flags to separate them, deploying the code in advance, and the change is only released (or unreleased) by toggling the feature flag.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>\"Deployments\" and \"releases\" are often used interchangeably but mean different things.<\/p>\n\n<p>A deployment moves a change from one place to another, such as some updated code from a staging environment to production.<\/p>\n\n<p>A release is when the change made is available to users.<\/p>\n\n<p>They can happen at the same time, or you can use feature flags to separate them, deploying the code in advance, and the change is only released (or unreleased) by toggling the feature flag.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:03+00:00",
|
||||
"guid": null,
|
||||
"hash": "b418347cf60efb3abda4a93b884ed151",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "023d16f6-0ebc-4c81-91e6-90458fbbf9c1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:04+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Tailwind: why I prefer to extract HTML components\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-02-20T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:04+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/02\/20\/tailwind-why-i-prefer-to-extract-html-components",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Tailwind offers the @apply directive that you can use to extract components in your CSS by applying the styles the classes would have added.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"css\">\/* Input *\/\n\n.btn {\n @apply inline-block rounded-3xl bg-blue-500 px-8 py-3 text-lg text-white hover:bg-blue-800 focus:bg-blue-800;\n}\n\n\/* Output *\/\n\n.btn {\n --tw-bg-opacity: 1;\n --tw-text-opacity: 1;\n background-color: rgb(59 130 246 \/ var(--tw-bg-opacity));\n border-radius: 1.5rem;\n color: rgb(255 255 255 \/ var(--tw-text-opacity));\n display: inline-block;\n font-size: 1.125rem;\n line-height: 1.75rem;\n padding-bottom: 0.75rem;\n padding-left: 2rem;\n padding-right: 2rem;\n padding-top: 0.75rem;\n}\n\n.btn:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(30 64 175 \/ var(--tw-bg-opacity));\n}\n\n.btn:focus {\n --tw-bg-opacity: 1;\n background-color: rgb(30 64 175 \/ var(--tw-bg-opacity));\n}\n<\/code><\/pre>\n\n<p>Whilst this works and reduced duplication, I prefer to handle this within my HTML instead.<\/p>\n\n<p>All templating engines I've used have ways to loop over or map through items and including separate files such as separate partials or different components.<\/p>\n\n<p>The main benefit of this is that you get the HTML structure of the component and not just the styling. If you extract a .btn component, it may depend on a span or other element within it to display correctly but as this isn't obvious, it may be missed when writing an implementation of it in HTML - this can't happen when working inside a loop or importing a canonical version.<\/p>\n\n<p>Also, by working just in the HTML markup, we continue to get the lower cognitive load and speed benefits that we're used to when styling with utility classes rather than switching between multiple files.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Tailwind offers the @apply directive that you can use to extract components in your CSS by applying the styles the classes would have added.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"css\">\/* Input *\/\n\n.btn {\n @apply inline-block rounded-3xl bg-blue-500 px-8 py-3 text-lg text-white hover:bg-blue-800 focus:bg-blue-800;\n}\n\n\/* Output *\/\n\n.btn {\n --tw-bg-opacity: 1;\n --tw-text-opacity: 1;\n background-color: rgb(59 130 246 \/ var(--tw-bg-opacity));\n border-radius: 1.5rem;\n color: rgb(255 255 255 \/ var(--tw-text-opacity));\n display: inline-block;\n font-size: 1.125rem;\n line-height: 1.75rem;\n padding-bottom: 0.75rem;\n padding-left: 2rem;\n padding-right: 2rem;\n padding-top: 0.75rem;\n}\n\n.btn:hover {\n --tw-bg-opacity: 1;\n background-color: rgb(30 64 175 \/ var(--tw-bg-opacity));\n}\n\n.btn:focus {\n --tw-bg-opacity: 1;\n background-color: rgb(30 64 175 \/ var(--tw-bg-opacity));\n}\n<\/code><\/pre>\n\n<p>Whilst this works and reduced duplication, I prefer to handle this within my HTML instead.<\/p>\n\n<p>All templating engines I've used have ways to loop over or map through items and including separate files such as separate partials or different components.<\/p>\n\n<p>The main benefit of this is that you get the HTML structure of the component and not just the styling. If you extract a .btn component, it may depend on a span or other element within it to display correctly but as this isn't obvious, it may be missed when writing an implementation of it in HTML - this can't happen when working inside a loop or importing a canonical version.<\/p>\n\n<p>Also, by working just in the HTML markup, we continue to get the lower cognitive load and speed benefits that we're used to when styling with utility classes rather than switching between multiple files.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:04+00:00",
|
||||
"guid": null,
|
||||
"hash": "de94dc72634b64860368d368dd1b936a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "025d8016-8a75-4a90-8442-be30f05baad8"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "PHP types and assertions\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-08-20T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/08\/20\/php-types-and-assertions",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Following yesterday's email about input validation, guard clauses and assertion libraries, these can be used to compliment PHP's native types and checking.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"language-php\">function createJourney(string $from, string $to, int $duration): void {\n var_dump($from, $to, $duration);\n}\n<\/code><\/pre>\n\n<p>In this code, each parameter has a type, but there's no validation on the values.<\/p>\n\n<p>If I run this:<\/p>\n\n<pre><code class=\"language-plain\">createJourney('', '', -10);\n<\/code><\/pre>\n\n<p>I would get this output:<\/p>\n\n<pre><code class=\"language-plain\">string(0) \"\"\nstring(0) \"\"\nint(-10)\n<\/code><\/pre>\n\n<p>This is probably not what you want.<\/p>\n\n<p>I expect <code>$to<\/code> and <code>$from<\/code> to be not empty and the duration to be greater than zero.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>I can use an assertion library or throw my own Exceptions if the values pass the type checks but aren't what I need.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"language-php\">function createJourney(string $from, string $to, int $duration): void {\n Assert::stringNotEmpty($from);\n Assert::stringNotEmpty($to);\n Assert::positiveInteger($duration);\n\n var_dump($from, $to, $duration);\n}\n<\/code><\/pre>\n\n<p>Now, if an empty string or negative duration is passed - in my implementation or test code - an Exception will be thrown.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Following yesterday's email about input validation, guard clauses and assertion libraries, these can be used to compliment PHP's native types and checking.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"language-php\">function createJourney(string $from, string $to, int $duration): void {\n var_dump($from, $to, $duration);\n}\n<\/code><\/pre>\n\n<p>In this code, each parameter has a type, but there's no validation on the values.<\/p>\n\n<p>If I run this:<\/p>\n\n<pre><code class=\"language-plain\">createJourney('', '', -10);\n<\/code><\/pre>\n\n<p>I would get this output:<\/p>\n\n<pre><code class=\"language-plain\">string(0) \"\"\nstring(0) \"\"\nint(-10)\n<\/code><\/pre>\n\n<p>This is probably not what you want.<\/p>\n\n<p>I expect <code>$to<\/code> and <code>$from<\/code> to be not empty and the duration to be greater than zero.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>I can use an assertion library or throw my own Exceptions if the values pass the type checks but aren't what I need.<\/p>\n\n<p>For example:<\/p>\n\n<pre><code class=\"language-php\">function createJourney(string $from, string $to, int $duration): void {\n Assert::stringNotEmpty($from);\n Assert::stringNotEmpty($to);\n Assert::positiveInteger($duration);\n\n var_dump($from, $to, $duration);\n}\n<\/code><\/pre>\n\n<p>Now, if an empty string or negative duration is passed - in my implementation or test code - an Exception will be thrown.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "7a9179502a50a3f6f3443cdebef1a615",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.027ebf68-13b3-4ae5-bd71-08e0f42f7898.json
Normal file
100
content/node.027ebf68-13b3-4ae5-bd71-08e0f42f7898.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "027ebf68-13b3-4ae5-bd71-08e0f42f7898"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Ask questions"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-05-30T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/05\/30\/ask-questions",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As a Software Developer, it's common to hear that you need to ask questions to progress or work efficiently.<\/p>\n\n<p>You shouldn't just ask technical questions, though.<\/p>\n\n<p>Also ask questions like:<\/p>\n\n<ul>\n<li>What do we want to achieve by delivering this feature?<\/li>\n<li>How do we know if it's a success (or not)?<\/li>\n<li>What business value does this add and who will benefit?<\/li>\n<li>Why do this now? Why not wait a few months or not do it at all?<\/li>\n<li>Is there an existing solution for this?<\/li>\n<li>Can we achieve the same or a similar result in a different way?<\/li>\n<\/ul>\n\n<p>As well as knowing how to do something, it's important to know why it needs to be done.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As a Software Developer, it's common to hear that you need to ask questions to progress or work efficiently.<\/p>\n\n<p>You shouldn't just ask technical questions, though.<\/p>\n\n<p>Also ask questions like:<\/p>\n\n<ul>\n<li>What do we want to achieve by delivering this feature?<\/li>\n<li>How do we know if it's a success (or not)?<\/li>\n<li>What business value does this add and who will benefit?<\/li>\n<li>Why do this now? Why not wait a few months or not do it at all?<\/li>\n<li>Is there an existing solution for this?<\/li>\n<li>Can we achieve the same or a similar result in a different way?<\/li>\n<\/ul>\n\n<p>As well as knowing how to do something, it's important to know why it needs to be done.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:31+00:00",
|
||||
"guid": null,
|
||||
"hash": "f0c3d6e951782cfb763d893c57d2dced",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "02be26ba-44c0-4be5-b05d-327c1387edc9"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Using Nix for local application development"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-11-30T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/11\/30\/using-nix-for-local-application-development",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Instead of using tools like Docker or nvm to manage dependencies for your projects, you can use Nix instead.<\/p>\n\n<p>Creating a Nix shell or flake for each project with its dependencies will install everything without needing containers and with the benefit of everything being locked to specific versions, <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/12\/why-consistency-and-reproducibility-are-important\">making environments reproducible<\/a>.<\/p>\n\n<p>If you need a specific version of PHP or node for a project, it will be available and different versions can be used for other projects.<\/p>\n\n<p>And if you need services like MySQL and you're not using NixOS, you can also use devenv to manage services, tasks and processes for each project.<\/p>\n\n<p>For me, Nix and devenv have <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/11\/could-nix-and-devenv-replace-docker-compose\">replaced Docker and Docker Compose<\/a> on my development projects.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Instead of using tools like Docker or nvm to manage dependencies for your projects, you can use Nix instead.<\/p>\n\n<p>Creating a Nix shell or flake for each project with its dependencies will install everything without needing containers and with the benefit of everything being locked to specific versions, <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/12\/why-consistency-and-reproducibility-are-important\">making environments reproducible<\/a>.<\/p>\n\n<p>If you need a specific version of PHP or node for a project, it will be available and different versions can be used for other projects.<\/p>\n\n<p>And if you need services like MySQL and you're not using NixOS, you can also use devenv to manage services, tasks and processes for each project.<\/p>\n\n<p>For me, Nix and devenv have <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/11\/could-nix-and-devenv-replace-docker-compose\">replaced Docker and Docker Compose<\/a> on my development projects.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:57+00:00",
|
||||
"guid": null,
|
||||
"hash": "29f50807932d09cb8473fe0be543a5d1",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.02db45dc-db1a-4d95-a449-6fe4c7884c3e.json
Normal file
100
content/node.02db45dc-db1a-4d95-a449-6fe4c7884c3e.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "02db45dc-db1a-4d95-a449-6fe4c7884c3e"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:38+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't put business logic in templates"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-01-10T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:38+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/01\/10\/dont-put-business-logic-in-templates",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Here is some code from my website:<\/p>\n\n<p><img src=\"https:\/\/www.oliverdavies.uk\/assets\/images\/talk-count-code.png\" alt=\"A screenshot of the code that calculates the number of talks I've given.\" \/><\/p>\n\n<p>If you want, you can also <a href=\"https:\/\/raw.githubusercontent.com\/opdavies\/oliverdavies.uk\/main\/source\/_pages\/presentations.md\">view it on GitHub<\/a>.<\/p>\n\n<p>It is business logic responsible for counting the number of talks I've given at different events so I can display it on my Talks page.<\/p>\n\n<p>It starts at zero, loops over each talk, and increments the talk count if the event is the current day or a past date.<\/p>\n\n<p>It's only used in a single place, so the same logic isn't duplicated elsewhere.<\/p>\n\n<p>But it's in the page's Twig template.<\/p>\n\n<p>It has no test coverage.<\/p>\n\n<p>If I need to change or refactor it, I'd need to test it again manually.<\/p>\n\n<p>Don't do this.<\/p>\n\n<h2 id=\"so%2C-what-should-i-do%3F\">So, what should I do?<\/h2>\n\n<p>It's OK to put simple presentational logic, such as looping over a list or whether to show or hide a value within a template, but not complex business logic.<\/p>\n\n<p>Business logic should be separated and executed elsewhere. The values should be passed to the template to be rendered.<\/p>\n\n<p>This makes the business logic easier to test as you can test the logic itself and determine the value passed to the template is correct without being concerned about the templating engine.<\/p>\n\n<p>In an application, you may need to output a value to a template and a terminal. You'd have one source of truth, such as a Service, Action or Command class that calculates the value before passing it to the appropriate output.<\/p>\n\n<p>Once the logic is separated, you only need to test it once.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In a previous version of my website, I did this by creating a custom Twig function.<\/p>\n\n<p>It was as simple as adding <code><\/code> to the template.<\/p>\n\n<p>All the logic was moved from the template to my custom extension.<\/p>\n\n<p>The logic was separated.<\/p>\n\n<p>It had tests.<\/p>\n\n<p>This is the approach I'd take to achieve the same result for a client application.<\/p>\n\n<p>For a client, I want to run a test suite and be confident my logic works as expected - now and in the future.<\/p>\n\n<p>For myself, and for calculating something simple, this is fine.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Here is some code from my website:<\/p>\n\n<p><img src=\"https:\/\/www.oliverdavies.uk\/assets\/images\/talk-count-code.png\" alt=\"A screenshot of the code that calculates the number of talks I've given.\"><\/p>\n\n<p>If you want, you can also <a href=\"https:\/\/raw.githubusercontent.com\/opdavies\/oliverdavies.uk\/main\/source\/_pages\/presentations.md\">view it on GitHub<\/a>.<\/p>\n\n<p>It is business logic responsible for counting the number of talks I've given at different events so I can display it on my Talks page.<\/p>\n\n<p>It starts at zero, loops over each talk, and increments the talk count if the event is the current day or a past date.<\/p>\n\n<p>It's only used in a single place, so the same logic isn't duplicated elsewhere.<\/p>\n\n<p>But it's in the page's Twig template.<\/p>\n\n<p>It has no test coverage.<\/p>\n\n<p>If I need to change or refactor it, I'd need to test it again manually.<\/p>\n\n<p>Don't do this.<\/p>\n\n<h2 id=\"so%2C-what-should-i-do%3F\">So, what should I do?<\/h2>\n\n<p>It's OK to put simple presentational logic, such as looping over a list or whether to show or hide a value within a template, but not complex business logic.<\/p>\n\n<p>Business logic should be separated and executed elsewhere. The values should be passed to the template to be rendered.<\/p>\n\n<p>This makes the business logic easier to test as you can test the logic itself and determine the value passed to the template is correct without being concerned about the templating engine.<\/p>\n\n<p>In an application, you may need to output a value to a template and a terminal. You'd have one source of truth, such as a Service, Action or Command class that calculates the value before passing it to the appropriate output.<\/p>\n\n<p>Once the logic is separated, you only need to test it once.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In a previous version of my website, I did this by creating a custom Twig function.<\/p>\n\n<p>It was as simple as adding <code><\/code> to the template.<\/p>\n\n<p>All the logic was moved from the template to my custom extension.<\/p>\n\n<p>The logic was separated.<\/p>\n\n<p>It had tests.<\/p>\n\n<p>This is the approach I'd take to achieve the same result for a client application.<\/p>\n\n<p>For a client, I want to run a test suite and be confident my logic works as expected - now and in the future.<\/p>\n\n<p>For myself, and for calculating something simple, this is fine.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:38+00:00",
|
||||
"guid": null,
|
||||
"hash": "a75b22468e6abeb329819fa5a65c2bc1",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.02eee27f-311e-45da-b022-0deaf156a6f0.json
Normal file
100
content/node.02eee27f-311e-45da-b022-0deaf156a6f0.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "02eee27f-311e-45da-b022-0deaf156a6f0"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Who's Travis?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-23T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/23\/travis",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I remember seeing a talk or listening to a podcast during which they mentioned Travis, as in the CI pipeline tool, and some of the things they were using it for.<\/p>\n\n<p>They also said that their client thought Travis was a person on their team.<\/p>\n\n<p>In some ways, having a CI pipeline performing automated checks and tests is like having another team member.<\/p>\n\n<p>Instead of waiting for a person to manually check code styles, run tests and spot potential bugs, why not automate them and run them in the CI pipeline every time code changes are pushed?<\/p>\n\n<p>You'll get results sooner than waiting for a person do check the code by hand and, arguably, more consistent results.<\/p>\n\n<p>Then, if you do peer code reviews, you know the basics are already done and the reviewer can focus on other things.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I remember seeing a talk or listening to a podcast during which they mentioned Travis, as in the CI pipeline tool, and some of the things they were using it for.<\/p>\n\n<p>They also said that their client thought Travis was a person on their team.<\/p>\n\n<p>In some ways, having a CI pipeline performing automated checks and tests is like having another team member.<\/p>\n\n<p>Instead of waiting for a person to manually check code styles, run tests and spot potential bugs, why not automate them and run them in the CI pipeline every time code changes are pushed?<\/p>\n\n<p>You'll get results sooner than waiting for a person do check the code by hand and, arguably, more consistent results.<\/p>\n\n<p>Then, if you do peer code reviews, you know the basics are already done and the reviewer can focus on other things.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:21+00:00",
|
||||
"guid": null,
|
||||
"hash": "58c9da441a16db434c006de482ddb439",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.031b38e7-5589-4f0a-9eaf-18621a4b0ea3.json
Normal file
100
content/node.031b38e7-5589-4f0a-9eaf-18621a4b0ea3.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "031b38e7-5589-4f0a-9eaf-18621a4b0ea3"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "PHPUnit does more than unit testing\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-13T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/13\/phpunit-does-more-than-unit-testing",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As the name suggests, <a href=\"http:\/\/phpunit.de\">PHPUnit<\/a> is a unit testing framework for PHP.<\/p>\n\n<p>It's the defacto PHP testing framework used by the major PHP projects, such as Drupal, Symfony, Laravel.<\/p>\n\n<p>But unit testing isn't all that PHPUnit can do.<\/p>\n\n<p>These frameworks extend PHPUnit to give it more functionality.<\/p>\n\n<p>Drupal allows you to run functional\/browser tests to make requests to pages, check the response codes and content, as well as integration\/kernel tests to test real services instead of relying on mocking within unit tests.<\/p>\n\n<p>Symfony does the same with its web and kernel test cases.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>If you use a framework and want to more than unit testing, intsead of including another testing framework, try using some of the PHPUnit extensions that are provided by the framework.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As the name suggests, <a href=\"http:\/\/phpunit.de\">PHPUnit<\/a> is a unit testing framework for PHP.<\/p>\n\n<p>It's the defacto PHP testing framework used by the major PHP projects, such as Drupal, Symfony, Laravel.<\/p>\n\n<p>But unit testing isn't all that PHPUnit can do.<\/p>\n\n<p>These frameworks extend PHPUnit to give it more functionality.<\/p>\n\n<p>Drupal allows you to run functional\/browser tests to make requests to pages, check the response codes and content, as well as integration\/kernel tests to test real services instead of relying on mocking within unit tests.<\/p>\n\n<p>Symfony does the same with its web and kernel test cases.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>If you use a framework and want to more than unit testing, intsead of including another testing framework, try using some of the PHPUnit extensions that are provided by the framework.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:42+00:00",
|
||||
"guid": null,
|
||||
"hash": "ebb678e6bfb989f0b14c1f494c92a1af",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0354447e-bafe-46fe-8064-c458c6619d68"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Prove the concept\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-26T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/26\/prove-the-concept",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>When starting a new task, find the simplest way to prove the concept.<\/p>\n\n<p>Investigate upfront and evaluate potential approaches.<\/p>\n\n<p>What's the smallest or quickest thing you could do to validate an idea?<\/p>\n\n<p>It could be a small script that you can run and verify something works before moving it to its correct place within your application or creating a first implementation with hard-coded data that you refactor once you've proven the concept.<\/p>\n\n<p>If you can't, you'll know it won't work without investing a large amount of time and you can move on to the next potential approach.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>When starting a new task, find the simplest way to prove the concept.<\/p>\n\n<p>Investigate upfront and evaluate potential approaches.<\/p>\n\n<p>What's the smallest or quickest thing you could do to validate an idea?<\/p>\n\n<p>It could be a small script that you can run and verify something works before moving it to its correct place within your application or creating a first implementation with hard-coded data that you refactor once you've proven the concept.<\/p>\n\n<p>If you can't, you'll know it won't work without investing a large amount of time and you can move on to the next potential approach.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "53531d5906d1f9a7bfd86d494bd38724",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.03a28574-fa89-455a-8e83-81a276c0d658.json
Normal file
100
content/node.03a28574-fa89-455a-8e83-81a276c0d658.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03a28574-fa89-455a-8e83-81a276c0d658"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Code reviews are about the code, not code style"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-10-30T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/10\/30\/code-reviews-are-about-the-code-not-code-style",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>If you do code reviews, they should be about reviewing the code and not about the style of the code.<\/p>\n\n<p>There shouldn't be comments about whether tabs or spaces are used, how many spaces are on each line, where the braces are, or whether there should be semicolons.<\/p>\n\n<p>A code style should be defined upfront and checking code against it can be done automatically with tools such as phpcs or Prettier.<\/p>\n\n<p>You don't need to wait for a human to review the code style - automated tools can be run locally or in a CI pipeline and provide feedback much faster.<\/p>\n\n<p>And if people aren't reviewing the code style, they can focus on reviewing the code itself.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>If you do code reviews, they should be about reviewing the code and not about the style of the code.<\/p>\n\n<p>There shouldn't be comments about whether tabs or spaces are used, how many spaces are on each line, where the braces are, or whether there should be semicolons.<\/p>\n\n<p>A code style should be defined upfront and checking code against it can be done automatically with tools such as phpcs or Prettier.<\/p>\n\n<p>You don't need to wait for a human to review the code style - automated tools can be run locally or in a CI pipeline and provide feedback much faster.<\/p>\n\n<p>And if people aren't reviewing the code style, they can focus on reviewing the code itself.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:25+00:00",
|
||||
"guid": null,
|
||||
"hash": "9fe14e0414ae452819b91e9a2abc8403",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03a9c0db-a315-44e7-83fd-81527afb0d65"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Coding defensively and considering the unhappy path"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-02-21T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/02\/21\/coding-defensively-and-considering-the-unhappy-path",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Last week, whilst speaking about Sculpin at PHP South West, I showed an example from a client project I'd built with Sculpin.<\/p>\n\n<p>The project was for a local gym and fitness centre, and I was demonstrating how I'd created the timetable page and kept the data separate from the HTML and Twig markup so the client could easily administer it (they edit the files on GitHub, which triggers a rebuild of the website).<\/p>\n\n<p>Each class should have a name and start and end times and be on a specified day.<\/p>\n\n<p>But what if the client didn't include a name or start or end time?<\/p>\n\n<p>What if they put a number or boolean instead of a string?<\/p>\n\n<p>What if there are no classes on a particular day?<\/p>\n\n<p>You don't know how people will use your software, so it's best to be defensive - validate and verify things before rendering them and prevent the page or whole application from breaking if something isn't as you expected or assumed.<\/p>\n\n<p>We also can't assume the \"happy path\" will always be correct.<\/p>\n\n<p>What if there aren't any classes? Do we put an empty message on the timetable or not show that day?<\/p>\n\n<h2 id=\"another-scenario\">Another scenario<\/h2>\n\n<p>If you were retrieving or posting data to an API endpoint, would you assume it was successful or verify the response code is what you expected?<\/p>\n\n<p>If you get the data, do you check if it's in the correct format and something you can use?<\/p>\n\n<p>If not, do you verify other actions in your application haven't run and it's not in an invalid state?<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In PHP, a common approach is to use the <code>assert()<\/code> function, as we saw in <a href=\"http:\/\/localhost:8000\/daily\/2024\/02\/20\/which-level-is-right-for-you\">yesterday's email<\/a>.<\/p>\n\n<p>Then, write automated tests that don't test just the happy path but also the unhappy paths.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Last week, whilst speaking about Sculpin at PHP South West, I showed an example from a client project I'd built with Sculpin.<\/p>\n\n<p>The project was for a local gym and fitness centre, and I was demonstrating how I'd created the timetable page and kept the data separate from the HTML and Twig markup so the client could easily administer it (they edit the files on GitHub, which triggers a rebuild of the website).<\/p>\n\n<p>Each class should have a name and start and end times and be on a specified day.<\/p>\n\n<p>But what if the client didn't include a name or start or end time?<\/p>\n\n<p>What if they put a number or boolean instead of a string?<\/p>\n\n<p>What if there are no classes on a particular day?<\/p>\n\n<p>You don't know how people will use your software, so it's best to be defensive - validate and verify things before rendering them and prevent the page or whole application from breaking if something isn't as you expected or assumed.<\/p>\n\n<p>We also can't assume the \"happy path\" will always be correct.<\/p>\n\n<p>What if there aren't any classes? Do we put an empty message on the timetable or not show that day?<\/p>\n\n<h2 id=\"another-scenario\">Another scenario<\/h2>\n\n<p>If you were retrieving or posting data to an API endpoint, would you assume it was successful or verify the response code is what you expected?<\/p>\n\n<p>If you get the data, do you check if it's in the correct format and something you can use?<\/p>\n\n<p>If not, do you verify other actions in your application haven't run and it's not in an invalid state?<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In PHP, a common approach is to use the <code>assert()<\/code> function, as we saw in <a href=\"http:\/\/localhost:8000\/daily\/2024\/02\/20\/which-level-is-right-for-you\">yesterday's email<\/a>.<\/p>\n\n<p>Then, write automated tests that don't test just the happy path but also the unhappy paths.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "a94b36c1ef2e928071f7d01827c7163b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03ab2265-f728-4e06-8aa0-be4f05a41526"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "It's only a bad situation if you fail to learn from it\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-06-22T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/06\/22\/fail-to-lear",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I heard this on a non-tech podcast but it applies to tech too:<\/p>\n\n<blockquote>\n <p>It's only a bad situation if you fail to learn from it<\/p>\n<\/blockquote>\n\n<p>Whether its a missed deadline, a failed deployment, a production outage or pushing a bug to production, there are opportunities to learn and improve.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I heard this on a non-tech podcast but it applies to tech too:<\/p>\n\n<blockquote>\n <p>It's only a bad situation if you fail to learn from it<\/p>\n<\/blockquote>\n\n<p>Whether its a missed deadline, a failed deployment, a production outage or pushing a bug to production, there are opportunities to learn and improve.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:03+00:00",
|
||||
"guid": null,
|
||||
"hash": "1d08162ec930721f889a571b47fde853",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.03c5cbcb-f7f6-49a1-b064-6a9a79b34b53.json
Normal file
100
content/node.03c5cbcb-f7f6-49a1-b064-6a9a79b34b53.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03c5cbcb-f7f6-49a1-b064-6a9a79b34b53"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Why do people still use Git Flow?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-02-25T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/02\/25\/why-do-people-still-use-git-flow",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p><a href=\"https:\/\/www.oliverdavies.uk\/presentations\/git-flow\">The first conference talk I gave<\/a> was at DrupalCamp London 2014.<\/p>\n\n<p>The talk was about Git Flow - a popular branching scheme I was using to manage a project, where there are different branches for development and production versions of the software and separate branches for features, releases and hotfixes.<\/p>\n\n<p>It's been a standard approach as I've worked on different projects and teams, usually with pull or merge requests and code reviews.<\/p>\n\n<p>But why do people still use it?<\/p>\n\n<p>I was conducting technical interviews recently and every candidate explained how they used Git Flow or something inspired by it, and GitHub and GitLab have their own simplified versions.<\/p>\n\n<p>Vincent Driessen wrote the original Git Flow blog post in January 2010, though he updated it in March 2020 to add this note of reflection:<\/p>\n\n<blockquote>\n <p>This model was conceived in 2010, now more than 10 years ago, and not very long after Git itself came into being. In those 10 years, git-flow (the branching model laid out in this article) has become hugely popular in many a software team to the point where people have started treating it like a standard of sorts \u2014 but unfortunately also as a dogma or panacea.<\/p>\n \n <p>During those 10 years, Git itself has taken the world by a storm, and the most popular type of software that is being developed with Git is shifting more towards web apps \u2014 at least in my filter bubble. Web apps are typically continuously delivered, not rolled back, and you don't have to support multiple versions of the software running in the wild.<\/p>\n \n <p>This is not the class of software that I had in mind when I wrote the blog post 10 years ago. If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.<\/p>\n \n <p>If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years. In that case, please read on.<\/p>\n \n <p>To conclude, always remember that panaceas don't exist. Consider your own context. Don't be hating. Decide for yourself.<\/p>\n<\/blockquote>\n\n<p>Since adopting trunk-based development and continuous integration and delivery, I've worked faster and avoided merge conflicts, which wasn't the case before, even when I was the only one working on a codebase.<\/p>\n\n<p>It's a simpler workflow.<\/p>\n\n<p>I don't need to think about whether this branch is a feature or a hotfix, and I've rarely seen release and hotfix branches used as described in the original blog post.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Do what works best for you and your team, but don't adopt something because it's the \"standard\" approach.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p><a href=\"https:\/\/www.oliverdavies.uk\/presentations\/git-flow\">The first conference talk I gave<\/a> was at DrupalCamp London 2014.<\/p>\n\n<p>The talk was about Git Flow - a popular branching scheme I was using to manage a project, where there are different branches for development and production versions of the software and separate branches for features, releases and hotfixes.<\/p>\n\n<p>It's been a standard approach as I've worked on different projects and teams, usually with pull or merge requests and code reviews.<\/p>\n\n<p>But why do people still use it?<\/p>\n\n<p>I was conducting technical interviews recently and every candidate explained how they used Git Flow or something inspired by it, and GitHub and GitLab have their own simplified versions.<\/p>\n\n<p>Vincent Driessen wrote the original Git Flow blog post in January 2010, though he updated it in March 2020 to add this note of reflection:<\/p>\n\n<blockquote>\n <p>This model was conceived in 2010, now more than 10 years ago, and not very long after Git itself came into being. In those 10 years, git-flow (the branching model laid out in this article) has become hugely popular in many a software team to the point where people have started treating it like a standard of sorts \u2014 but unfortunately also as a dogma or panacea.<\/p>\n \n <p>During those 10 years, Git itself has taken the world by a storm, and the most popular type of software that is being developed with Git is shifting more towards web apps \u2014 at least in my filter bubble. Web apps are typically continuously delivered, not rolled back, and you don't have to support multiple versions of the software running in the wild.<\/p>\n \n <p>This is not the class of software that I had in mind when I wrote the blog post 10 years ago. If your team is doing continuous delivery of software, I would suggest to adopt a much simpler workflow (like GitHub flow) instead of trying to shoehorn git-flow into your team.<\/p>\n \n <p>If, however, you are building software that is explicitly versioned, or if you need to support multiple versions of your software in the wild, then git-flow may still be as good of a fit to your team as it has been to people in the last 10 years. In that case, please read on.<\/p>\n \n <p>To conclude, always remember that panaceas don't exist. Consider your own context. Don't be hating. Decide for yourself.<\/p>\n<\/blockquote>\n\n<p>Since adopting trunk-based development and continuous integration and delivery, I've worked faster and avoided merge conflicts, which wasn't the case before, even when I was the only one working on a codebase.<\/p>\n\n<p>It's a simpler workflow.<\/p>\n\n<p>I don't need to think about whether this branch is a feature or a hotfix, and I've rarely seen release and hotfix branches used as described in the original blog post.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Do what works best for you and your team, but don't adopt something because it's the \"standard\" approach.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:36+00:00",
|
||||
"guid": null,
|
||||
"hash": "9b0cfa53767098dc8873acdefd4d5eba",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03cc008a-1b6a-48f6-9b60-735cb82830e7"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Countdown to Drupal 11"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-06-29T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/06\/29\/countdown-to-drupal-11",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2024\/06\/23\/drupal-10-3-released\">With Drupal 10.3 released<\/a>, Drupal 11 will be the next major version.<\/p>\n\n<p>There will be a Drupal 10.4, which will be the first minor maintenance version and supported until Drupal 12's release in 2026.<\/p>\n\n<p>According to <a href=\"https:\/\/www.drupal.org\/about\/core\/policies\/core-release-cycles\/schedule#current\">the current development cycle<\/a>, Drupal 11 will be released in a month's time on the 29th of July, with 11.1 in December alongside 10.4.<\/p>\n\n<p>Drupal 7 will be unsupported as of January 2025.<\/p>\n\n<p>With Drupal 10 websites needing to be upgraded to 10.3 before 11, it gives people the chance to test their applications on the latest Drupal 10 before upgrading to 11 - a refined version of 10 without the deprecated code.<\/p>\n\n<p>It also gives module and theme maintainers the opportunity to make their projects compatible before making any breaking changes, meaning upgrades should be more stable as the main changes can be done beforehand.<\/p>\n\n<p>I'm looking forward to upgrading my applications and projects to Drupal 11, and seeing what's to come in 11.1 and beyond.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2024\/06\/23\/drupal-10-3-released\">With Drupal 10.3 released<\/a>, Drupal 11 will be the next major version.<\/p>\n\n<p>There will be a Drupal 10.4, which will be the first minor maintenance version and supported until Drupal 12's release in 2026.<\/p>\n\n<p>According to <a href=\"https:\/\/www.drupal.org\/about\/core\/policies\/core-release-cycles\/schedule#current\">the current development cycle<\/a>, Drupal 11 will be released in a month's time on the 29th of July, with 11.1 in December alongside 10.4.<\/p>\n\n<p>Drupal 7 will be unsupported as of January 2025.<\/p>\n\n<p>With Drupal 10 websites needing to be upgraded to 10.3 before 11, it gives people the chance to test their applications on the latest Drupal 10 before upgrading to 11 - a refined version of 10 without the deprecated code.<\/p>\n\n<p>It also gives module and theme maintainers the opportunity to make their projects compatible before making any breaking changes, meaning upgrades should be more stable as the main changes can be done beforehand.<\/p>\n\n<p>I'm looking forward to upgrading my applications and projects to Drupal 11, and seeing what's to come in 11.1 and beyond.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "e6345f399d28ab0955c18f1528e29428",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.03ce1ec7-c326-4e2a-94c9-a12efb9dfa89.json
Normal file
100
content/node.03ce1ec7-c326-4e2a-94c9-a12efb9dfa89.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03ce1ec7-c326-4e2a-94c9-a12efb9dfa89"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "How easily can you move changes between environments?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-12-24T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/12\/24\/moving-changes",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Regardless of <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/12\/23\/how-many-environments-do-you-need\">how many environments your application has<\/a>, you need to be able to move changes between them reliably.<\/p>\n\n<p>You don't want to configure each environment and make every change by hand.<\/p>\n\n<p>You want to automate this as much as possible so your changes are the same every time.<\/p>\n\n<p>In Drupal 7, the Features module was used to export changes once and apply them again using a <code>features revert<\/code> command - although its original use case was to extract reusable features for different applications.<\/p>\n\n<p>I've also written a lot of update of update hooks, like <code>mymodule_update_8001<\/code> to apply changes when database updates are applied.<\/p>\n\n<p>Since Drupal 8, we've had configuration management - a first-class way to export and import configuration changes - which I think was one of the best additions to Drupal 8, and something not available in some other CMSes, frameworks and applications.<\/p>\n\n<p>There's an ecosystem around configuration management, including Config Split for per-environment configurations and Config Ignore to ignore sensitive information or changes you don't want to manage via imported configuration.<\/p>\n\n<p>I recently worked on a project where we didn't have a CI pipeline running configuration imports on each change and things were very difficult to manage. Once that was in place, though, things were much easier, more consistent and changes were quicker to release.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Regardless of <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/12\/23\/how-many-environments-do-you-need\">how many environments your application has<\/a>, you need to be able to move changes between them reliably.<\/p>\n\n<p>You don't want to configure each environment and make every change by hand.<\/p>\n\n<p>You want to automate this as much as possible so your changes are the same every time.<\/p>\n\n<p>In Drupal 7, the Features module was used to export changes once and apply them again using a <code>features revert<\/code> command - although its original use case was to extract reusable features for different applications.<\/p>\n\n<p>I've also written a lot of update of update hooks, like <code>mymodule_update_8001<\/code> to apply changes when database updates are applied.<\/p>\n\n<p>Since Drupal 8, we've had configuration management - a first-class way to export and import configuration changes - which I think was one of the best additions to Drupal 8, and something not available in some other CMSes, frameworks and applications.<\/p>\n\n<p>There's an ecosystem around configuration management, including Config Split for per-environment configurations and Config Ignore to ignore sensitive information or changes you don't want to manage via imported configuration.<\/p>\n\n<p>I recently worked on a project where we didn't have a CI pipeline running configuration imports on each change and things were very difficult to manage. Once that was in place, though, things were much easier, more consistent and changes were quicker to release.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:23+00:00",
|
||||
"guid": null,
|
||||
"hash": "edb67464b9188fe49c1cef46dfa04ac8",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.03ec00a2-aea4-4454-bbe9-413f4c865596.json
Normal file
100
content/node.03ec00a2-aea4-4454-bbe9-413f4c865596.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03ec00a2-aea4-4454-bbe9-413f4c865596"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Static websites are easy to backup"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-03-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/03\/14\/backup",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As well as being <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simple to deploy<\/a>, static websites are easy to backup and, if needed, restore.<\/p>\n\n<p>I backup several static websites from my server using rsync - the same command I use to deploy them.<\/p>\n\n<p>rsync is fast as it only downloads files that have changed, so backing up several websites only takes a few seconds.<\/p>\n\n<p>There are are no databases to worry about - all I need to do is backup the static files themselves.<\/p>\n\n<p>Running the backups is also easy.<\/p>\n\n<p>I have a scheduled cron job that downloads everything from the <code>\/var\/www\/vhosts<\/code> directory on my server and creates a local copy.<\/p>\n\n<p>If I need to restore from a backup or migrate to a different server, I just run the appropriate rsync command to re-upload them - the same as how I deployed them originally.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As well as being <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simple to deploy<\/a>, static websites are easy to backup and, if needed, restore.<\/p>\n\n<p>I backup several static websites from my server using rsync - the same command I use to deploy them.<\/p>\n\n<p>rsync is fast as it only downloads files that have changed, so backing up several websites only takes a few seconds.<\/p>\n\n<p>There are are no databases to worry about - all I need to do is backup the static files themselves.<\/p>\n\n<p>Running the backups is also easy.<\/p>\n\n<p>I have a scheduled cron job that downloads everything from the <code>\/var\/www\/vhosts<\/code> directory on my server and creates a local copy.<\/p>\n\n<p>If I need to restore from a backup or migrate to a different server, I just run the appropriate rsync command to re-upload them - the same as how I deployed them originally.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:17+00:00",
|
||||
"guid": null,
|
||||
"hash": "cad56e3254ecbb16885c1f741f0776f9",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.03f8c551-dc44-4dde-ab7b-771c6244e9ac.json
Normal file
100
content/node.03f8c551-dc44-4dde-ab7b-771c6244e9ac.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "03f8c551-dc44-4dde-ab7b-771c6244e9ac"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Useful Git configuration"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-09-19T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/09\/19\/useful-git-configuration",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Here are some snippets from my Git configuration file.<\/p>\n\n<p>These days, I use a much simpler workflow and configuration since doing more trunk-based development, but in general, I rebase instead of merging by default, and prefer to use fast-forward merges that doesn't create a merge commit.<\/p>\n\n<p><code>branch.autosetuprebase = always<\/code> and <code>pull.rebase = true<\/code> configure Git to always rebase instead of pull. It does this for all branches, though I might override this for <code>main<\/code> branches.<\/p>\n\n<p><code>pull.ff = only<\/code> and <code>merge.ff = only<\/code> prevents creating a merge commit and will prevent the merge if it would create one. If I needed to override this, I could by using the <code>--no-ff<\/code> option on the command line.<\/p>\n\n<p>I use <code>checkout.defaultRemote = origin<\/code> to ensure that the <code>origin<\/code> remote is used if I have multiple remotes configured, and <code>push.default = upstream<\/code> to set the default remote to push to.<\/p>\n\n<p><code>merge.autoStash<\/code> allows for running merges on a dirty worktree by automatically creating and re-applying a stash of the changes, and <code>fetch.prune<\/code> will automatically prune branches on fetch - keeping things tidy.<\/p>\n\n<p>I also have and use a number of aliases.<\/p>\n\n<p>Some like <code>pl = pull<\/code> and <code>ps = push<\/code> are shorter versions of existing commands, and some like <code>aa = add --all<\/code>, <code>fixup = commit --fixup<\/code> and some additional arguments to commands.<\/p>\n\n<p>I also have some like <code>current-branch = rev-parse --abbrev-ref HEAD<\/code> and <code>worktrees = worktree list<\/code> which add simple additional commands, and some like <code>repush = !git pull --rebase && git push<\/code> which use execute shell commands to execute more complex commands or combine multiple commands.<\/p>\n\n<p>This is a snapshot of my Git configuration. The <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/blob\/7e935b12c09358adad480a566988b9cbfaf5999e\/roles\/git\/files\/.gitconfig\">full version is on GitHub<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Here are some snippets from my Git configuration file.<\/p>\n\n<p>These days, I use a much simpler workflow and configuration since doing more trunk-based development, but in general, I rebase instead of merging by default, and prefer to use fast-forward merges that doesn't create a merge commit.<\/p>\n\n<p><code>branch.autosetuprebase = always<\/code> and <code>pull.rebase = true<\/code> configure Git to always rebase instead of pull. It does this for all branches, though I might override this for <code>main<\/code> branches.<\/p>\n\n<p><code>pull.ff = only<\/code> and <code>merge.ff = only<\/code> prevents creating a merge commit and will prevent the merge if it would create one. If I needed to override this, I could by using the <code>--no-ff<\/code> option on the command line.<\/p>\n\n<p>I use <code>checkout.defaultRemote = origin<\/code> to ensure that the <code>origin<\/code> remote is used if I have multiple remotes configured, and <code>push.default = upstream<\/code> to set the default remote to push to.<\/p>\n\n<p><code>merge.autoStash<\/code> allows for running merges on a dirty worktree by automatically creating and re-applying a stash of the changes, and <code>fetch.prune<\/code> will automatically prune branches on fetch - keeping things tidy.<\/p>\n\n<p>I also have and use a number of aliases.<\/p>\n\n<p>Some like <code>pl = pull<\/code> and <code>ps = push<\/code> are shorter versions of existing commands, and some like <code>aa = add --all<\/code>, <code>fixup = commit --fixup<\/code> and some additional arguments to commands.<\/p>\n\n<p>I also have some like <code>current-branch = rev-parse --abbrev-ref HEAD<\/code> and <code>worktrees = worktree list<\/code> which add simple additional commands, and some like <code>repush = !git pull --rebase && git push<\/code> which use execute shell commands to execute more complex commands or combine multiple commands.<\/p>\n\n<p>This is a snapshot of my Git configuration. The <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/blob\/7e935b12c09358adad480a566988b9cbfaf5999e\/roles\/git\/files\/.gitconfig\">full version is on GitHub<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "c064efff8a7c26a4e05fee32b076e0b2",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "047d366e-35b4-4569-ac29-2cebe56989fb"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "More code, more problems\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-22T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/22\/more-code-more-problems",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>The more code you have, the more potential problems you have.<\/p>\n\n<p>More code means more opportunities for bugs in your software.<\/p>\n\n<p>There's more code to maintain and more chance of encountering breaking changes as you update between major software versions of your project's dependencies, such as a CMS or framework.<\/p>\n\n<p>If you can keep your amount of code to a minimum and reduce the maintenance overhead, you are less likely to experience issues.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>The more code you have, the more potential problems you have.<\/p>\n\n<p>More code means more opportunities for bugs in your software.<\/p>\n\n<p>There's more code to maintain and more chance of encountering breaking changes as you update between major software versions of your project's dependencies, such as a CMS or framework.<\/p>\n\n<p>If you can keep your amount of code to a minimum and reduce the maintenance overhead, you are less likely to experience issues.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "137f467c7894dcf4d534ab6c7572567a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0495b46d-90e9-49a1-9448-51f0737474a8"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Technical debt isn't always bad"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-10-02T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/10\/02\/technical-debt-isn-t-always-bad",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2024\/10\/01\/not-all-legacy-code-is-technical-debt\">Technical debt is usually referred to negatively<\/a>, but technical debt isn't always bad.<\/p>\n\n<p>The key thing is to know when and why you're taking on technical debt and when it will be addressed.<\/p>\n\n<p>If you have a goal or deadline to meet, you may decide to take on technical debt to release a feature sooner or a simpler version is released now and a more complex version will come later.<\/p>\n\n<p>I've done this on multi-site Drupal projects before, where I've hard-coded a background image URL as part of a minimum-viable version and made it changeable only when it needed to be - i.e. when the second website was introduced.<\/p>\n\n<p>For the initial version, that approach was good enough and meant we could move forward.<\/p>\n\n<p>The client and I decided to take on this technical debit in advance so we could release it sooner, and we knew when and how we were going to address it and pay it back.<\/p>\n\n<p>This was a good situation, not a bad one.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2024\/10\/01\/not-all-legacy-code-is-technical-debt\">Technical debt is usually referred to negatively<\/a>, but technical debt isn't always bad.<\/p>\n\n<p>The key thing is to know when and why you're taking on technical debt and when it will be addressed.<\/p>\n\n<p>If you have a goal or deadline to meet, you may decide to take on technical debt to release a feature sooner or a simpler version is released now and a more complex version will come later.<\/p>\n\n<p>I've done this on multi-site Drupal projects before, where I've hard-coded a background image URL as part of a minimum-viable version and made it changeable only when it needed to be - i.e. when the second website was introduced.<\/p>\n\n<p>For the initial version, that approach was good enough and meant we could move forward.<\/p>\n\n<p>The client and I decided to take on this technical debit in advance so we could release it sooner, and we knew when and how we were going to address it and pay it back.<\/p>\n\n<p>This was a good situation, not a bad one.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "1ece063b605c1351451d9f4379ee3bd6",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "04bac412-3008-4d80-88a4-2fb466e77d30"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Things take as long as they take"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-01-07T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/01\/07\/things-take-as-long-as-they-take",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Today, I saw a post that asked the question:<\/p>\n\n<blockquote>\n <p>Have you ever spent three days on an issue that should have taken 2 hours?<\/p>\n<\/blockquote>\n\n<p>My thought was, \"Who said it should have taken two hours?\".<\/p>\n\n<p>In my experience, tasks take as long as they take.<\/p>\n\n<p>Even something that seems simple can end up being something complex.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Today, I saw a post that asked the question:<\/p>\n\n<blockquote>\n <p>Have you ever spent three days on an issue that should have taken 2 hours?<\/p>\n<\/blockquote>\n\n<p>My thought was, \"Who said it should have taken two hours?\".<\/p>\n\n<p>In my experience, tasks take as long as they take.<\/p>\n\n<p>Even something that seems simple can end up being something complex.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "c80c0a0daeda9ac2879e1a8706bfad5e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0554df36-c8d8-4383-8afc-da3370b7b8ae"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't inject too many dependencies\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-09-12T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/09\/12\/dont-inject-too-many-dependencies",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>While dependency injection is a good practice - i.e., passing dependencies into a class, usually via a constructor method - you want to be aware of how many dependencies you inject into each class.<\/p>\n\n<p>There's no hard and fast rule, but I usually notice when I get to three dependencies and rarely inject more than four or five into a class.<\/p>\n\n<p>Having too many dependencies suggests that the class is doing too much and has too many responsibilities and that another class may be needed.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Having smaller and simpler classes makes them easier to read, maintain and review. Ideally, each class should only have one responsibility, so it adheres to the Single Responsibility Principle (the \"S\" in SOLID).<\/p>\n\n<p>Creating classes is cheap, so why not split one large and difficult-to-maintain class with many dependencies into multiple smaller and easier-to-work-with ones?<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>While dependency injection is a good practice - i.e., passing dependencies into a class, usually via a constructor method - you want to be aware of how many dependencies you inject into each class.<\/p>\n\n<p>There's no hard and fast rule, but I usually notice when I get to three dependencies and rarely inject more than four or five into a class.<\/p>\n\n<p>Having too many dependencies suggests that the class is doing too much and has too many responsibilities and that another class may be needed.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Having smaller and simpler classes makes them easier to read, maintain and review. Ideally, each class should only have one responsibility, so it adheres to the Single Responsibility Principle (the \"S\" in SOLID).<\/p>\n\n<p>Creating classes is cheap, so why not split one large and difficult-to-maintain class with many dependencies into multiple smaller and easier-to-work-with ones?<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "b40abd231dda53a740949b3e5d5ac434",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0564c6eb-6ed8-4390-942d-ba901c038998"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Violinist, render arrays and feature flags"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-09-13T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/09\/13\/violinist-render-arrays-and-feature-flags",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>This week, I spoke with Eirik Morland again on the <a href=\"http:\/\/localhost:8000\/podcast\">Beyond Blocks podcast<\/a> about recent improvements to violinist.io, such as team\/multi-user subscriptions.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/21-eirik-morland-violinist-2\">Listen to the episode now<\/a>.<\/p>\n\n<p>I was great to speak to Eirik again and for him to be the first returning guest on the podcast.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/8-eirik-morland-violinist\">Listen to the first episode with Eirik<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>This week, I spoke with Eirik Morland again on the <a href=\"http:\/\/localhost:8000\/podcast\">Beyond Blocks podcast<\/a> about recent improvements to violinist.io, such as team\/multi-user subscriptions.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/21-eirik-morland-violinist-2\">Listen to the episode now<\/a>.<\/p>\n\n<p>I was great to speak to Eirik again and for him to be the first returning guest on the podcast.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/8-eirik-morland-violinist\">Listen to the first episode with Eirik<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "fbad09b906f5606d9d39c07e3a6f7cb0",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0588b7ef-d687-4eea-a55f-c2e0bb556a2c"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:59+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Dead or done"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-06-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:59+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/06\/14\/dead-or-done",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Yesterday, I wrote about <a href=\"http:\/\/localhost:8000\/daily\/2024\/06\/13\/vetting-third-party-open-source-software\">some things I look for when evaluating open-source projects<\/a>.<\/p>\n\n<p>One thing I said was \"When was the most recent commit and release?\".<\/p>\n\n<p>If a project hasn't had many recent commits, it could be outdated or no longer supported.<\/p>\n\n<p>Alternatively, it could be considered feature complete and not getting new features, and only getting bug fixes and maintenance updates.<\/p>\n\n<p>I see this a lot with Vim plugins that were written several years ago and are now minimally maintained and updated, but getting no new features.<\/p>\n\n<p>This happens in the Drupal space, too, when people wrote a module for a project which they have since completed, or no longer work with that client or for that company.<\/p>\n\n<p>If there are at least commits for security compatibility, such as new versions of PHP or node, that's a sign the project is in a maintenance phase.<\/p>\n\n<p>If there are no recent commits, the project could be dead and I'd carefully consider if you want to add or use it.<\/p>\n\n<p>Something that could help is if maintainers are explicit about what state their project is in.<\/p>\n\n<p>Add a note to the README.md or CONTRIBUTING.md file saying if the project is feature complete or what the maintenance state is.<\/p>\n\n<p>If the project is no longer maintained, you can also document it and potentially archive the repository too to show that it will no longer be updated and to avoid confusion.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Yesterday, I wrote about <a href=\"http:\/\/localhost:8000\/daily\/2024\/06\/13\/vetting-third-party-open-source-software\">some things I look for when evaluating open-source projects<\/a>.<\/p>\n\n<p>One thing I said was \"When was the most recent commit and release?\".<\/p>\n\n<p>If a project hasn't had many recent commits, it could be outdated or no longer supported.<\/p>\n\n<p>Alternatively, it could be considered feature complete and not getting new features, and only getting bug fixes and maintenance updates.<\/p>\n\n<p>I see this a lot with Vim plugins that were written several years ago and are now minimally maintained and updated, but getting no new features.<\/p>\n\n<p>This happens in the Drupal space, too, when people wrote a module for a project which they have since completed, or no longer work with that client or for that company.<\/p>\n\n<p>If there are at least commits for security compatibility, such as new versions of PHP or node, that's a sign the project is in a maintenance phase.<\/p>\n\n<p>If there are no recent commits, the project could be dead and I'd carefully consider if you want to add or use it.<\/p>\n\n<p>Something that could help is if maintainers are explicit about what state their project is in.<\/p>\n\n<p>Add a note to the README.md or CONTRIBUTING.md file saying if the project is feature complete or what the maintenance state is.<\/p>\n\n<p>If the project is no longer maintained, you can also document it and potentially archive the repository too to show that it will no longer be updated and to avoid confusion.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:59+00:00",
|
||||
"guid": null,
|
||||
"hash": "06646d3c892a58bf29e20d2b84896a7c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.05952537-6bb6-408b-a417-73e91d310190.json
Normal file
100
content/node.05952537-6bb6-408b-a417-73e91d310190.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "05952537-6bb6-408b-a417-73e91d310190"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:38+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Tim Lehnen and the Drupal Association"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-02-06T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:38+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/02\/06\/tim-lehnen-and-the-drupal-association",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Last week on the Beyond Blocks podcast, I spoke with Tim Lehnen - CTO and former colleague at the Drupal Association.<\/p>\n\n<p>We discussed what the Drupal Association is and what it does, how companies and individuals can contribute and support the Association, some recent and upcoming improvements to Drupal.org, Drupal 7's end-of-life, and more.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/podcast\/9-tim-lehnen\">Listen now<\/a><\/p>\n\n<p>This Friday, I'm releasing an episode with Ryan Weaver from SymfonyCasts.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Last week on the Beyond Blocks podcast, I spoke with Tim Lehnen - CTO and former colleague at the Drupal Association.<\/p>\n\n<p>We discussed what the Drupal Association is and what it does, how companies and individuals can contribute and support the Association, some recent and upcoming improvements to Drupal.org, Drupal 7's end-of-life, and more.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/podcast\/9-tim-lehnen\">Listen now<\/a><\/p>\n\n<p>This Friday, I'm releasing an episode with Ryan Weaver from SymfonyCasts.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:38+00:00",
|
||||
"guid": null,
|
||||
"hash": "2a00495abcb2b4fb956be9ee3bf4582e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.05c37db1-17f0-4be2-8fb6-2a2535aac23d.json
Normal file
100
content/node.05c37db1-17f0-4be2-8fb6-2a2535aac23d.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "05c37db1-17f0-4be2-8fb6-2a2535aac23d"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:46+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Test to save your job\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-09-24T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:46+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/09\/24\/test-to-save-your-job",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I've recently been going through my YouTube \"Watch Later\" list and watching (or rewatching) videos of conference and meetup talks that I saved to watch later.<\/p>\n\n<p>Today's talk was by Matt Stauffer at one of the previous Laracon conferences.<\/p>\n\n<p>I've quoted Matt previously when explaining what to test on applications. The answer: \"The thing you'd lose your job for if it broke\".<\/p>\n\n<p>In this talk, Matt has a slide that goes further into this, titled \"Test to save your job\".<\/p>\n\n<h2 id=\"what-matt-said\">What Matt said<\/h2>\n\n<p>The best place to start your tests is by asking yourself: \"What part of this app, if broken, would make me worried for my job?\"<\/p>\n\n<ul>\n<li>What's most likely to break?<\/li>\n<li>What do I have the least control over?<\/li>\n<li>What are we about to refactor?<\/li>\n<li>What would make my clients stress out?<\/li>\n<li>What would make me stressed out?<\/li>\n<\/ul>\n\n<h2 id=\"what-about-you%3F\">What about you?<\/h2>\n\n<p>Do you have any other ways to decide what code to test? Reply and let me know, as I'd love to know.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I've recently been going through my YouTube \"Watch Later\" list and watching (or rewatching) videos of conference and meetup talks that I saved to watch later.<\/p>\n\n<p>Today's talk was by Matt Stauffer at one of the previous Laracon conferences.<\/p>\n\n<p>I've quoted Matt previously when explaining what to test on applications. The answer: \"The thing you'd lose your job for if it broke\".<\/p>\n\n<p>In this talk, Matt has a slide that goes further into this, titled \"Test to save your job\".<\/p>\n\n<h2 id=\"what-matt-said\">What Matt said<\/h2>\n\n<p>The best place to start your tests is by asking yourself: \"What part of this app, if broken, would make me worried for my job?\"<\/p>\n\n<ul>\n<li>What's most likely to break?<\/li>\n<li>What do I have the least control over?<\/li>\n<li>What are we about to refactor?<\/li>\n<li>What would make my clients stress out?<\/li>\n<li>What would make me stressed out?<\/li>\n<\/ul>\n\n<h2 id=\"what-about-you%3F\">What about you?<\/h2>\n\n<p>Do you have any other ways to decide what code to test? Reply and let me know, as I'd love to know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:46+00:00",
|
||||
"guid": null,
|
||||
"hash": "c2095c4a1278959cfff849fb4f819ace",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0679084c-8c6d-4212-bd7b-5679bc0ac064"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Test-driven development makes you more productive\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-15T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/15\/test-driven-development-makes-you-more-productive",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I think that test-driven development (TDD) makes you productive.<\/p>\n\n<p>Firstly, you save time by not needing to switch from your code to a terminal or browser to test it.<\/p>\n\n<p>But, just as importantly, TDD reduces procrastination. It's much clearer to see what the next steps are.<\/p>\n\n<p>You're either thinking and designing your code when writing a failing test or fixing the test failures in the implementation code to get the test to pass. You can focus on each failure and message separately and get them to pass instead of thinking about the whole feature or the rest of the application.<\/p>\n\n<p>Once you have a working test, you can focus on refactoring any code or moving on to writing the next assertion or the next test.<\/p>\n\n<p>I think that achieving small tasks with short feedback loops using test-driven development makes it much easier to remain productive and focussed.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I think that test-driven development (TDD) makes you productive.<\/p>\n\n<p>Firstly, you save time by not needing to switch from your code to a terminal or browser to test it.<\/p>\n\n<p>But, just as importantly, TDD reduces procrastination. It's much clearer to see what the next steps are.<\/p>\n\n<p>You're either thinking and designing your code when writing a failing test or fixing the test failures in the implementation code to get the test to pass. You can focus on each failure and message separately and get them to pass instead of thinking about the whole feature or the rest of the application.<\/p>\n\n<p>Once you have a working test, you can focus on refactoring any code or moving on to writing the next assertion or the next test.<\/p>\n\n<p>I think that achieving small tasks with short feedback loops using test-driven development makes it much easier to remain productive and focussed.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "d8c18f8667771c2497ae722e5849f6fc",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.067acf87-db7d-430d-8b8a-e38beb26f511.json
Normal file
100
content/node.067acf87-db7d-430d-8b8a-e38beb26f511.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "067acf87-db7d-430d-8b8a-e38beb26f511"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Why I prefer integration tests to unit tests\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/14\/why-i-prefer-integration-tests-to-unit-tests",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>With unit tests, you need to mock <strong>everything<\/strong>.<\/p>\n\n<p>If what you're testing has a dependency, you need to create and use a mock version.<\/p>\n\n<p>If the mock has its own dependencies, you need to mock those, too.<\/p>\n\n<p>I've written unit tests with mocks and accidentally only tested the mocks instead of the functionality I intended.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>With integration tests, you can use and test the real dependencies.<\/p>\n\n<p>Whilst they may be slightly slower to run, I prefer the simpler setup, less need for mocks, and knowing the real services work - not just the mocked versions.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>With unit tests, you need to mock <strong>everything<\/strong>.<\/p>\n\n<p>If what you're testing has a dependency, you need to create and use a mock version.<\/p>\n\n<p>If the mock has its own dependencies, you need to mock those, too.<\/p>\n\n<p>I've written unit tests with mocks and accidentally only tested the mocks instead of the functionality I intended.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>With integration tests, you can use and test the real dependencies.<\/p>\n\n<p>Whilst they may be slightly slower to run, I prefer the simpler setup, less need for mocks, and knowing the real services work - not just the mocked versions.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:42+00:00",
|
||||
"guid": null,
|
||||
"hash": "b38408f033b23dc55649c0a05a3af78b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "06873fa4-9ca1-4ac9-acfe-213853600b12"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Tidy then push"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-02-11T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/02\/11\/tidy",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As I said <a href=\"http:\/\/localhost:8000\/daily\/2025\/02\/10\/refactoring\">in yesterday's email<\/a>, sometimes you change your mind whilst working on something.<\/p>\n\n<p>Maybe you change your approach and have a commit that supersedes an earlier one, fix a typo, or find a bug and need to revert a commit.<\/p>\n\n<p>If you're pushing your changes to a branch for review, I suggest using <code>git rebase<\/code> to clean up your commits.<\/p>\n\n<p>You can squash the typo fix into the commit that introduced the typo, or remove the original implementation that you later moved away from.<\/p>\n\n<p>Whilst there is an option to squash all the commits when merging, I don't like it and prefer people to tidy their commits before pushing.<\/p>\n\n<p>This means the commits are easier to review and you can keep the original commit history and all the context within the messages instead of a generic <code>Merge commit..<\/code> message.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As I said <a href=\"http:\/\/localhost:8000\/daily\/2025\/02\/10\/refactoring\">in yesterday's email<\/a>, sometimes you change your mind whilst working on something.<\/p>\n\n<p>Maybe you change your approach and have a commit that supersedes an earlier one, fix a typo, or find a bug and need to revert a commit.<\/p>\n\n<p>If you're pushing your changes to a branch for review, I suggest using <code>git rebase<\/code> to clean up your commits.<\/p>\n\n<p>You can squash the typo fix into the commit that introduced the typo, or remove the original implementation that you later moved away from.<\/p>\n\n<p>Whilst there is an option to squash all the commits when merging, I don't like it and prefer people to tidy their commits before pushing.<\/p>\n\n<p>This means the commits are easier to review and you can keep the original commit history and all the context within the messages instead of a generic <code>Merge commit..<\/code> message.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "7cd7f74b4b05c11ac108a9166e5bf214",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "06f241bc-f1fc-41a4-a385-506d80f150eb"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Why I prefer types\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-09-20T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/09\/20\/why-i-prefer-types",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Whether it's PHP or JavaScript\/TypeScript, I prefer type declarations in my code.<\/p>\n\n<p>As well as benefits like auto-completion in your IDE or text editor and being able to more effectively statically analyse the code, to me, the code is more readable and easier to understand with the types included.<\/p>\n\n<p>It's more to read, but I can do so easily and immediately know what a function expects as function arguments and what it will return.<\/p>\n\n<p>Here's the code from my previous email on types from a few days ago, with and without the types declared:<\/p>\n\n<pre><code class=\"js\">add(...numbers) {\n \/\/ ...\n}\n\nsubtract(...numbers) {\n \/\/ ...\n}\n\nadd(...numbers: number[]): number {\n \/\/ ...\n}\n\nsubtract(...numbers: number[]): number {\n \/\/ ...\n}\n<\/code><\/pre>\n\n<p>Without types, I can infer what the function accepts and returns, but that's based on my assumption, which could be incorrect.<\/p>\n\n<p>What if <code>numbers<\/code> was an array of strings of numbers - e.g. <code>['one', 'two', 'three']<\/code> - and what if instead of returning the result, it stored it in state to return from a different method like <code>equals()<\/code> or <code>calculate()<\/code>?<\/p>\n\n<p>With the type declarations included, I don't need to presume, infer or make best guesses.<\/p>\n\n<p>It's clear from just reading the code.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Whether it's PHP or JavaScript\/TypeScript, I prefer type declarations in my code.<\/p>\n\n<p>As well as benefits like auto-completion in your IDE or text editor and being able to more effectively statically analyse the code, to me, the code is more readable and easier to understand with the types included.<\/p>\n\n<p>It's more to read, but I can do so easily and immediately know what a function expects as function arguments and what it will return.<\/p>\n\n<p>Here's the code from my previous email on types from a few days ago, with and without the types declared:<\/p>\n\n<pre><code class=\"js\">add(...numbers) {\n \/\/ ...\n}\n\nsubtract(...numbers) {\n \/\/ ...\n}\n\nadd(...numbers: number[]): number {\n \/\/ ...\n}\n\nsubtract(...numbers: number[]): number {\n \/\/ ...\n}\n<\/code><\/pre>\n\n<p>Without types, I can infer what the function accepts and returns, but that's based on my assumption, which could be incorrect.<\/p>\n\n<p>What if <code>numbers<\/code> was an array of strings of numbers - e.g. <code>['one', 'two', 'three']<\/code> - and what if instead of returning the result, it stored it in state to return from a different method like <code>equals()<\/code> or <code>calculate()<\/code>?<\/p>\n\n<p>With the type declarations included, I don't need to presume, infer or make best guesses.<\/p>\n\n<p>It's clear from just reading the code.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "506ce77f679ad7914294ef3ac3d0201c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.075872d9-7af9-438a-960f-09e72e209e1f.json
Normal file
100
content/node.075872d9-7af9-438a-960f-09e72e209e1f.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "075872d9-7af9-438a-960f-09e72e209e1f"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Anyone can use open source software"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-02T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/02\/anyone",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Another great thing about open source software is anyone can use it, contribute to it or provide services for it.<\/p>\n\n<p>You don't need to be a large company like Acquia or Red Hat.<\/p>\n\n<p>I was a self-taught solo hobbyist Developer when I started building a website for my local Tae Kwon-Do club in 2007 with PHP.<\/p>\n\n<p>In 2008, I started learning Drupal, built my own website and started doing freelance Drupal development and consulting work.<\/p>\n\n<p>I became a full-time Drupal Developer in 2010 and haven't looked back since.<\/p>\n\n<p>I was able to this for free using open source software.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Another great thing about open source software is anyone can use it, contribute to it or provide services for it.<\/p>\n\n<p>You don't need to be a large company like Acquia or Red Hat.<\/p>\n\n<p>I was a self-taught solo hobbyist Developer when I started building a website for my local Tae Kwon-Do club in 2007 with PHP.<\/p>\n\n<p>In 2008, I started learning Drupal, built my own website and started doing freelance Drupal development and consulting work.<\/p>\n\n<p>I became a full-time Drupal Developer in 2010 and haven't looked back since.<\/p>\n\n<p>I was able to this for free using open source software.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:21+00:00",
|
||||
"guid": null,
|
||||
"hash": "3523efa30025b9ea53133ab91ddb3086",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "077780fd-8480-426c-80b9-d54befef06b1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Avoiding over-mocking\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-16T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/16\/avoiding-over-mocking",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>In unit tests, and sometimes in kernel tests, you need to mock the dependencies you aren't testing, but you can over-mock and only be testing the mocks and not the code you want to test.<\/p>\n\n<p>Here's an example (thanks, ChatGPT, for the code).<\/p>\n\n<h2 id=\"the-class-to-be-tested-myclass.php\">The Class to be tested (MyClass.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\nclass MyClass {\n\n public function __construct(\n private DependencyInterface $dependency\n ) {\n }\n\n public function doSomething() {\n $result = $this->dependency->performAction();\n\n return \"Result: \" . $result;\n }\n}\n<\/code><\/pre>\n\n<h2 id=\"dependency-interface-dependencyinterface.php\">Dependency Interface (DependencyInterface.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\ninterface DependencyInterface {\n\n public function performAction();\n\n}\n<\/code><\/pre>\n\n<h2 id=\"a-test-class-that-ends-up-testing-mocks-myclasstest.php\">A test class that ends up testing mocks (MyClassTest.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass MyClassTest extends TestCase {\n\n public function testDoSomething() {\n \/\/ Creating a mock of the DependencyInterface.\n $dependencyMock = $this->createMock(DependencyInterface::class);\n\n \/\/ Setting up the mock to return a specific value.\n $dependencyMock->expects($this->once())\n ->method('performAction')\n ->willReturn('Mocked result');\n\n \/\/ Creating an instance of MyClass with the mock.\n $myClass = new MyClass($dependencyMock);\n\n \/\/ Calling the method to be tested.\n $result = $myClass->doSomething();\n\n \/\/ Asserting that the result matches the expected value.\n $this->assertEquals('Result: Mocked result', $result);\n }\n\n}\n<\/code><\/pre>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In this example, the test creates a mock for the <code>DependencyInterface<\/code> and sets up an expectation that the performAction method will be called once, returning a specific value.<\/p>\n\n<p>The test then calls the <code>doSomething<\/code> method on <code>MyClass<\/code> and asserts that the result is as expected.<\/p>\n\n<p>The issue with this test is that it's not testing the actual behaviour of <code>MyClass<\/code>.<\/p>\n\n<p>It's only testing that the mock is configured correctly.<\/p>\n\n<p>If the real implementation of <code>MyClass<\/code> has a bug, this test won't catch it.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>In unit tests, and sometimes in kernel tests, you need to mock the dependencies you aren't testing, but you can over-mock and only be testing the mocks and not the code you want to test.<\/p>\n\n<p>Here's an example (thanks, ChatGPT, for the code).<\/p>\n\n<h2 id=\"the-class-to-be-tested-myclass.php\">The Class to be tested (MyClass.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\nclass MyClass {\n\n public function __construct(\n private DependencyInterface $dependency\n ) {\n }\n\n public function doSomething() {\n $result = $this->dependency->performAction();\n\n return \"Result: \" . $result;\n }\n}\n<\/code><\/pre>\n\n<h2 id=\"dependency-interface-dependencyinterface.php\">Dependency Interface (DependencyInterface.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\ninterface DependencyInterface {\n\n public function performAction();\n\n}\n<\/code><\/pre>\n\n<h2 id=\"a-test-class-that-ends-up-testing-mocks-myclasstest.php\">A test class that ends up testing mocks (MyClassTest.php)<\/h2>\n\n<pre><code class=\"language-php\"><?php\n\nuse PHPUnit\\Framework\\TestCase;\n\nclass MyClassTest extends TestCase {\n\n public function testDoSomething() {\n \/\/ Creating a mock of the DependencyInterface.\n $dependencyMock = $this->createMock(DependencyInterface::class);\n\n \/\/ Setting up the mock to return a specific value.\n $dependencyMock->expects($this->once())\n ->method('performAction')\n ->willReturn('Mocked result');\n\n \/\/ Creating an instance of MyClass with the mock.\n $myClass = new MyClass($dependencyMock);\n\n \/\/ Calling the method to be tested.\n $result = $myClass->doSomething();\n\n \/\/ Asserting that the result matches the expected value.\n $this->assertEquals('Result: Mocked result', $result);\n }\n\n}\n<\/code><\/pre>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In this example, the test creates a mock for the <code>DependencyInterface<\/code> and sets up an expectation that the performAction method will be called once, returning a specific value.<\/p>\n\n<p>The test then calls the <code>doSomething<\/code> method on <code>MyClass<\/code> and asserts that the result is as expected.<\/p>\n\n<p>The issue with this test is that it's not testing the actual behaviour of <code>MyClass<\/code>.<\/p>\n\n<p>It's only testing that the mock is configured correctly.<\/p>\n\n<p>If the real implementation of <code>MyClass<\/code> has a bug, this test won't catch it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:01+00:00",
|
||||
"guid": null,
|
||||
"hash": "db65f35f5c7767e34b717942905448f4",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "079c994b-172d-4f47-9217-5edf00858228"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Should you deploy on a Friday?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-06-20T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/06\/20\/should-you-deploy-on-a-friday",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>There's a common saying about not deploying changes on a Friday to prevent outages or issues before the weekend.<\/p>\n\n<p>I've also seen this where people won't deploy after a particular time of the day as it's too close to the evening.<\/p>\n\n<h2 id=\"when-did-you-last-deploy%3F\">When did you last deploy?<\/h2>\n\n<p>The longer it's been since the last deployment, the risker each deployment is.<\/p>\n\n<p>If there are weeks or months of changes, it will be risky regardless of which day it is.<\/p>\n\n<p>If your last deployment was an afternoon, deploying a small change the following morning will be low risk, even on a Thursday and Friday.<\/p>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n\n<p>If you're nervous about deploying on a Friday, I think you need to aim for smaller and more frequent deployments to minimise the risk.<\/p>\n\n<p>The issue isn't when you're deploying. You likely need to do so more often.<\/p>\n\n<p>If there is an issue after a large release, it will take more time to debug or roll back compared to a small release which is easier to find and fix the problem or revert that single change.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>There's a common saying about not deploying changes on a Friday to prevent outages or issues before the weekend.<\/p>\n\n<p>I've also seen this where people won't deploy after a particular time of the day as it's too close to the evening.<\/p>\n\n<h2 id=\"when-did-you-last-deploy%3F\">When did you last deploy?<\/h2>\n\n<p>The longer it's been since the last deployment, the risker each deployment is.<\/p>\n\n<p>If there are weeks or months of changes, it will be risky regardless of which day it is.<\/p>\n\n<p>If your last deployment was an afternoon, deploying a small change the following morning will be low risk, even on a Thursday and Friday.<\/p>\n\n<h2 id=\"conclusion\">Conclusion<\/h2>\n\n<p>If you're nervous about deploying on a Friday, I think you need to aim for smaller and more frequent deployments to minimise the risk.<\/p>\n\n<p>The issue isn't when you're deploying. You likely need to do so more often.<\/p>\n\n<p>If there is an issue after a large release, it will take more time to debug or roll back compared to a small release which is easier to find and fix the problem or revert that single change.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:03+00:00",
|
||||
"guid": null,
|
||||
"hash": "531214c785675f0543e778a343d5a677",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.07cb61ba-669e-446c-a2e4-3ac4f4c6e0c2.json
Normal file
100
content/node.07cb61ba-669e-446c-a2e4-3ac4f4c6e0c2.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "07cb61ba-669e-446c-a2e4-3ac4f4c6e0c2"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:44+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Is code coverage an objective or guideline?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-02T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:44+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/02\/is-code-coverage-an-objective-or-guideline",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Many development teams and projects use code coverage - e.g. how many lines of code are covered by automated tests - as an objective, and saying it must be 100% or another percentage.<\/p>\n\n<p>But is this an effective metric?<\/p>\n\n<p>In the same way as deleting failing tests to fix a pipeline, a code coverage amount can be faked.<\/p>\n\n<p>With this in mind, what if, instead of setting an objective such as 100% code coverage, you used it as a guideline?<\/p>\n\n<p>If you're working on a legacy project, what if you set a minimum code coverage amount as a guideline to ensure any new code has tests by not dropping under that level?<\/p>\n\n<p>Would that be better than saying every line of code needs to be covered?<\/p>\n\n<p>Code coverage is something I'm thinking of using more, so I want to know what you think.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Many development teams and projects use code coverage - e.g. how many lines of code are covered by automated tests - as an objective, and saying it must be 100% or another percentage.<\/p>\n\n<p>But is this an effective metric?<\/p>\n\n<p>In the same way as deleting failing tests to fix a pipeline, a code coverage amount can be faked.<\/p>\n\n<p>With this in mind, what if, instead of setting an objective such as 100% code coverage, you used it as a guideline?<\/p>\n\n<p>If you're working on a legacy project, what if you set a minimum code coverage amount as a guideline to ensure any new code has tests by not dropping under that level?<\/p>\n\n<p>Would that be better than saying every line of code needs to be covered?<\/p>\n\n<p>Code coverage is something I'm thinking of using more, so I want to know what you think.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:44+00:00",
|
||||
"guid": null,
|
||||
"hash": "37779b8be562eca580a1d3eb2ead9f83",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.07ddcb2f-b45d-4aa6-889a-ca9b0cb0ac82.json
Normal file
100
content/node.07ddcb2f-b45d-4aa6-889a-ca9b0cb0ac82.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "07ddcb2f-b45d-4aa6-889a-ca9b0cb0ac82"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:04+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "What are Git hooks and why are they useful?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-16T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:04+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/16\/what-are-git-hooks-why-are-they-useful",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>In yesterday's email, I mentioned Git hooks but didn't go into any detail. So, what are they?<\/p>\n\n<p>Git hooks are Bash scripts that you add to your repository that are executed when certain events happen, such as before a commit is made or before a push to a remote.<\/p>\n\n<p>By default, the script files need to be within the <code>.git\/hooks<\/code> directory, have executable permissions, and be named to exactly match the name of the hook - e.g. <code>pre-push<\/code> - with no file extension.<\/p>\n\n<p>If it returns an error exit code then the process is stopped and the action doesn't complete.<\/p>\n\n<p>This is useful if, for example, you or your team use a specified format for commit messages and you want to prevent the commit if the message doesn't match the requirements.<\/p>\n\n<p>But, the main benefit that I get from Git hooks if from the <code>pre-push<\/code> hook.<\/p>\n\n<p>I use it to run a subset of the checks that are run within project's CI pipeline to limit failures in the CI tool and fix simple errors before I push the code.<\/p>\n\n<p>Typically, these are the quicker tasks such as ensuring the Docker image builds, running linting and static analysis, validating lock files, and some of the automated tests if they don't take too long to run.<\/p>\n\n<p>If a build is going to fail because of something simply like a linting error, then I'd rather find that out and fix it locally rather than waiting for a CI tool to fail.<\/p>\n\n<p>Also, if you're utilising trunk-based development and continuous integration where team members are pushing changes regularly, then you want to keep the pipeline in a passing, deployable state as much as possible and prevent disruption.<\/p>\n\n<p>But what have Git hooks got to do with the \"run\" file?<\/p>\n\n<p>Firstly, I like to keep the scripts as minimal as possible and move the majority of the code into functions within the <code>run<\/code> file. This means that the scripts are only responsible for running functions like <code>.\/run test:commit<\/code> and returning the appropriate exit code, but also means that it's easy to iterate and test them locally without making fake commits or trying to push them to your actual remote repository (and hoping that they don't get pushed).<\/p>\n\n<p>Secondly, I like to simplify the setup of Git hooks with their own functions.<\/p>\n\n<p>For security reasons, the <code>.git\/hooks<\/code> directory cannot be committed and pushed to your remote so they need to be enabled per user within their own clone of the repository.<\/p>\n\n<p>A common workaround is to put the scripts in a directory like <code>.githooks<\/code> and either symlink them to where Git expects them to be, or to use the <code>core.hooksPath<\/code> configuration option and change where Git is going to look.<\/p>\n\n<p>I like to lower the barrier for any team members by creating <code>git-hooks:on<\/code> and <code>git-hooks:off<\/code> functions which either set or unset the <code>core.hooksPath<\/code>. If someone wants to enable the Git hooks then they only need to run one of those commands rather than having to remember the name of the configuration option or manually creating or removing symlinks.<\/p>\n\n<p>There are other Git hooks that can be used but just using <code>pre-commit<\/code> and <code>pre-push<\/code> has saved me and teams that I've worked on both Developer time and build minutes, provides quicker feedback and fewer disruptions in our build pipelines, and I like how simple it can be by creating custom functions in a <code>run<\/code> file.<\/p>\n\n<p>Lastly, I've created <a href=\"https:\/\/github.com\/opdavies\/git-hooks-scratch\">https:\/\/github.com\/opdavies\/git-hooks-scratch<\/a> as an example with a minimal <code>run<\/code> file and some example hooks.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>In yesterday's email, I mentioned Git hooks but didn't go into any detail. So, what are they?<\/p>\n\n<p>Git hooks are Bash scripts that you add to your repository that are executed when certain events happen, such as before a commit is made or before a push to a remote.<\/p>\n\n<p>By default, the script files need to be within the <code>.git\/hooks<\/code> directory, have executable permissions, and be named to exactly match the name of the hook - e.g. <code>pre-push<\/code> - with no file extension.<\/p>\n\n<p>If it returns an error exit code then the process is stopped and the action doesn't complete.<\/p>\n\n<p>This is useful if, for example, you or your team use a specified format for commit messages and you want to prevent the commit if the message doesn't match the requirements.<\/p>\n\n<p>But, the main benefit that I get from Git hooks if from the <code>pre-push<\/code> hook.<\/p>\n\n<p>I use it to run a subset of the checks that are run within project's CI pipeline to limit failures in the CI tool and fix simple errors before I push the code.<\/p>\n\n<p>Typically, these are the quicker tasks such as ensuring the Docker image builds, running linting and static analysis, validating lock files, and some of the automated tests if they don't take too long to run.<\/p>\n\n<p>If a build is going to fail because of something simply like a linting error, then I'd rather find that out and fix it locally rather than waiting for a CI tool to fail.<\/p>\n\n<p>Also, if you're utilising trunk-based development and continuous integration where team members are pushing changes regularly, then you want to keep the pipeline in a passing, deployable state as much as possible and prevent disruption.<\/p>\n\n<p>But what have Git hooks got to do with the \"run\" file?<\/p>\n\n<p>Firstly, I like to keep the scripts as minimal as possible and move the majority of the code into functions within the <code>run<\/code> file. This means that the scripts are only responsible for running functions like <code>.\/run test:commit<\/code> and returning the appropriate exit code, but also means that it's easy to iterate and test them locally without making fake commits or trying to push them to your actual remote repository (and hoping that they don't get pushed).<\/p>\n\n<p>Secondly, I like to simplify the setup of Git hooks with their own functions.<\/p>\n\n<p>For security reasons, the <code>.git\/hooks<\/code> directory cannot be committed and pushed to your remote so they need to be enabled per user within their own clone of the repository.<\/p>\n\n<p>A common workaround is to put the scripts in a directory like <code>.githooks<\/code> and either symlink them to where Git expects them to be, or to use the <code>core.hooksPath<\/code> configuration option and change where Git is going to look.<\/p>\n\n<p>I like to lower the barrier for any team members by creating <code>git-hooks:on<\/code> and <code>git-hooks:off<\/code> functions which either set or unset the <code>core.hooksPath<\/code>. If someone wants to enable the Git hooks then they only need to run one of those commands rather than having to remember the name of the configuration option or manually creating or removing symlinks.<\/p>\n\n<p>There are other Git hooks that can be used but just using <code>pre-commit<\/code> and <code>pre-push<\/code> has saved me and teams that I've worked on both Developer time and build minutes, provides quicker feedback and fewer disruptions in our build pipelines, and I like how simple it can be by creating custom functions in a <code>run<\/code> file.<\/p>\n\n<p>Lastly, I've created <a href=\"https:\/\/github.com\/opdavies\/git-hooks-scratch\">https:\/\/github.com\/opdavies\/git-hooks-scratch<\/a> as an example with a minimal <code>run<\/code> file and some example hooks.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:04+00:00",
|
||||
"guid": null,
|
||||
"hash": "2dc52bfbe73534ab29285ff16486431f",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "07f773b8-2626-470e-b97e-aec79cd9ddeb"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Using open source software to build open source software"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-12-08T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/12\/08\/building-open-source",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As well as using <a href=\"http:\/\/localhost:8000\/daily\/2024\/12\/07\/open-source\">free and open source software for my homelab<\/a>, I develop software applications for clients using open source software, such as PHP, Drupal, Symfony and Sculpin.<\/p>\n\n<p>I also use open source software to do this.<\/p>\n\n<p>I use <a href=\"http:\/\/localhost:8000\/daily\/2024\/09\/08\/my-laptop-died\">NixOS as the operating system on my laptop<\/a>.<\/p>\n\n<p>I use Alacritty as my terminal emulator and tmux as a terminal multiplexer to have different sessions, windows and tabs for each project.<\/p>\n\n<p>I write my code in Neovim and use various additional plugins.<\/p>\n\n<p>I use Git for source control and tools like PHPStan, PHPUnit and Pest to run checks on my code.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/30\/using-nix-for-local-application-development\">I use Nix and devenv<\/a> to run the applications locally and host them on Linux servers running Nginx.<\/p>\n\n<p>I also use OBS, Kdenlive and GIMP to record and edit my content, which are all also open source projects.<\/p>\n\n<p>I run an open source business, using open source tools and projects and contribute to and <a href=\"http:\/\/localhost:8000\/daily\/2024\/03\/09\/override-node-options-40624-drupal-websites\">maintain my own open source software projects<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As well as using <a href=\"http:\/\/localhost:8000\/daily\/2024\/12\/07\/open-source\">free and open source software for my homelab<\/a>, I develop software applications for clients using open source software, such as PHP, Drupal, Symfony and Sculpin.<\/p>\n\n<p>I also use open source software to do this.<\/p>\n\n<p>I use <a href=\"http:\/\/localhost:8000\/daily\/2024\/09\/08\/my-laptop-died\">NixOS as the operating system on my laptop<\/a>.<\/p>\n\n<p>I use Alacritty as my terminal emulator and tmux as a terminal multiplexer to have different sessions, windows and tabs for each project.<\/p>\n\n<p>I write my code in Neovim and use various additional plugins.<\/p>\n\n<p>I use Git for source control and tools like PHPStan, PHPUnit and Pest to run checks on my code.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/30\/using-nix-for-local-application-development\">I use Nix and devenv<\/a> to run the applications locally and host them on Linux servers running Nginx.<\/p>\n\n<p>I also use OBS, Kdenlive and GIMP to record and edit my content, which are all also open source projects.<\/p>\n\n<p>I run an open source business, using open source tools and projects and contribute to and <a href=\"http:\/\/localhost:8000\/daily\/2024\/03\/09\/override-node-options-40624-drupal-websites\">maintain my own open source software projects<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:57+00:00",
|
||||
"guid": null,
|
||||
"hash": "2408bd8dcaa6ea4bd93b2e65f0fac540",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.08578f5f-e9e2-4b3b-87e6-facee5b087af.json
Normal file
100
content/node.08578f5f-e9e2-4b3b-87e6-facee5b087af.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "08578f5f-e9e2-4b3b-87e6-facee5b087af"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:19+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Should you have a separate front-end for your Drupal website?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-27T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:19+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/27\/separate-front-end",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>A few years ago, \"decoupled\" or \"headless\" Drupal was a popular approach, leveraging Drupal's built-in JSON:API module to expose its data via an API which can be consumed by a separate front-end application.<\/p>\n\n<p>The front-end application would retrieve the data from Drupal via the API and generate the appropriate HTML.<\/p>\n\n<p>It's an approach I've used in the past and <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/decoupling-drupal-vuejs\">spoken about at conferences<\/a>, but it comes with pros and cons.<\/p>\n\n<p>In theory, as the Drupal (or back-end application) and front-end are completely separate, there can be two separate and independent teams working on them.<\/p>\n\n<p>This adds overhead and complexity and I've found that one team will commonly be blocking the other instead of both being able to work in parallel.<\/p>\n\n<p>As I said yesterday, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/01\/26\/layout-builder\">previewing content in Drupal can be an issue<\/a> - particularly with a decoupled approach which needs a front-end to be rebuilt before the changes can be seen.<\/p>\n\n<p>If you have a separate front-end, you'll need to create everything from scratch, such as writing accessible HTML markup and othe standard features that would normally be provided by Drupal and, because you've got two separate front-end and back-end applications, you've got twice the amount of maintenance.<\/p>\n\n<p>You could also be excluding yourself from any new features that will be available in future versions of Drupal or Drupal CMS, such as the new Experience Builder.<\/p>\n\n<p>Whilst decoupled\/headless builds are a viable option and can work well in some situations, it's not something I recommend often.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>A few years ago, \"decoupled\" or \"headless\" Drupal was a popular approach, leveraging Drupal's built-in JSON:API module to expose its data via an API which can be consumed by a separate front-end application.<\/p>\n\n<p>The front-end application would retrieve the data from Drupal via the API and generate the appropriate HTML.<\/p>\n\n<p>It's an approach I've used in the past and <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/decoupling-drupal-vuejs\">spoken about at conferences<\/a>, but it comes with pros and cons.<\/p>\n\n<p>In theory, as the Drupal (or back-end application) and front-end are completely separate, there can be two separate and independent teams working on them.<\/p>\n\n<p>This adds overhead and complexity and I've found that one team will commonly be blocking the other instead of both being able to work in parallel.<\/p>\n\n<p>As I said yesterday, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/01\/26\/layout-builder\">previewing content in Drupal can be an issue<\/a> - particularly with a decoupled approach which needs a front-end to be rebuilt before the changes can be seen.<\/p>\n\n<p>If you have a separate front-end, you'll need to create everything from scratch, such as writing accessible HTML markup and othe standard features that would normally be provided by Drupal and, because you've got two separate front-end and back-end applications, you've got twice the amount of maintenance.<\/p>\n\n<p>You could also be excluding yourself from any new features that will be available in future versions of Drupal or Drupal CMS, such as the new Experience Builder.<\/p>\n\n<p>Whilst decoupled\/headless builds are a viable option and can work well in some situations, it's not something I recommend often.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:19+00:00",
|
||||
"guid": null,
|
||||
"hash": "780bda0044c53e6e45fb49f2037ac82e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "08598367-d6b0-45e5-a073-fbf23115417b"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Too many choices?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-17T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/17\/too-many-choices",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I've recently considered moving my infrastructure automation code from Pulumi to Terraform.<\/p>\n\n<p>One of Pulumi's features is that you can write your automation in a programming language instead of a domain-specific language (DSL) with Terraform.<\/p>\n\n<p>As a Developer, this seems appealing, but it poses an important question - which programming language should you use?<\/p>\n\n<p>I've written and re-written Pulumi code in TypeScript and Python and experimented with Go to see which feels best for me.<\/p>\n\n<p>If one of these were my primary language, it would be a no-brainer.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>When I go into my automation repository, I want to write my code as quickly and simply as possible. I don't want to be thinking about how to write it or what language would be best to write it in.<\/p>\n\n<p>Whilst I'd have to learn another DSL for Terraform, it would simplify my options by removing that choice for me, but also if I write automation code and hand it over to a client.<\/p>\n\n<p>It's like taking my children to a restaurant.<\/p>\n\n<p>They'll get overwhelmed if there are too many options on the menu. If we limit the options or order for them, they won't.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I've recently considered moving my infrastructure automation code from Pulumi to Terraform.<\/p>\n\n<p>One of Pulumi's features is that you can write your automation in a programming language instead of a domain-specific language (DSL) with Terraform.<\/p>\n\n<p>As a Developer, this seems appealing, but it poses an important question - which programming language should you use?<\/p>\n\n<p>I've written and re-written Pulumi code in TypeScript and Python and experimented with Go to see which feels best for me.<\/p>\n\n<p>If one of these were my primary language, it would be a no-brainer.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>When I go into my automation repository, I want to write my code as quickly and simply as possible. I don't want to be thinking about how to write it or what language would be best to write it in.<\/p>\n\n<p>Whilst I'd have to learn another DSL for Terraform, it would simplify my options by removing that choice for me, but also if I write automation code and hand it over to a client.<\/p>\n\n<p>It's like taking my children to a restaurant.<\/p>\n\n<p>They'll get overwhelmed if there are too many options on the menu. If we limit the options or order for them, they won't.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "960ab3763a91c3a533d2f22da91bd8fd",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.087fc4ed-53af-4f0c-9935-a808c2c84361.json
Normal file
100
content/node.087fc4ed-53af-4f0c-9935-a808c2c84361.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "087fc4ed-53af-4f0c-9935-a808c2c84361"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't put HTML in your body field"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-05-24T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/05\/24\/dont-put-html-in-your-body-field",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I often see Drupal projects where people have put raw HTML code into their body or other rich-text fields.<\/p>\n\n<p>Whilst it can be useful for short-term prototyping, I don't think it should be done for content that will be reused or kept for a period of time.<\/p>\n\n<p>If you have structured HTML code in multiple nodes, you can't make changes without editing each instance.<\/p>\n\n<p>What if you need to fix a bug and have hundreds or thousands of instances?<\/p>\n\n<p>If you have inline styles, they won't be updated or affected by changes to your stylesheets, such as changing colour or font family.<\/p>\n\n<p>Instead, create first-class components that use templates that are easier to change and maintain and have a single source of truth that adheres to your design system.<\/p>\n\n<p>In Drupal, use structured data in fields with Paragraphs or Layout Builder and build the templates around them.<\/p>\n\n<p>This makes it easier to maintain and also for people to add and edit content.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I often see Drupal projects where people have put raw HTML code into their body or other rich-text fields.<\/p>\n\n<p>Whilst it can be useful for short-term prototyping, I don't think it should be done for content that will be reused or kept for a period of time.<\/p>\n\n<p>If you have structured HTML code in multiple nodes, you can't make changes without editing each instance.<\/p>\n\n<p>What if you need to fix a bug and have hundreds or thousands of instances?<\/p>\n\n<p>If you have inline styles, they won't be updated or affected by changes to your stylesheets, such as changing colour or font family.<\/p>\n\n<p>Instead, create first-class components that use templates that are easier to change and maintain and have a single source of truth that adheres to your design system.<\/p>\n\n<p>In Drupal, use structured data in fields with Paragraphs or Layout Builder and build the templates around them.<\/p>\n\n<p>This makes it easier to maintain and also for people to add and edit content.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:31+00:00",
|
||||
"guid": null,
|
||||
"hash": "c81fcb760babf4b27ac37f42d606aade",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "08a03029-d231-45f2-91b3-0a4611156b0c"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:06+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Refactoring to value objects"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-10-03T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:06+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/10\/03\/refactoring-value-objects",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Here's a snippet of some Drupal code that I wrote last week. It's responsible for converting an array of nodes into a Collection of one of it's field values.<\/p>\n\n<pre><code class=\"language-php\">return Collection::make($stationNodes)\n ->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString())\n ->values();\n<\/code><\/pre>\n\n<p>There are two issues with this code.<\/p>\n\n<p>First, whilst I'm implicitly saying that it accepts a certain type of node, because of the <code>NodeInterface<\/code> typehint this could accept any type of node. If that node doesn't have the required field, the code will error - but I'd like to know sooner if an incorrect type of node is passed and make it explicit that only a certain type of node can be used.<\/p>\n\n<p>Second, the code for getting the field values is quite verbose and is potentially repeated in other places within the codebase. I'd like to have a simple way to access these field values that I can reuse anywhere else. If the logic for getting these particular field values changes, then I'd only need to change it in one place.<\/p>\n\n<h2 id=\"introducing-a-value-object\">Introducing a value object<\/h2>\n\n<p>This is the value object that I created.<\/p>\n\n<p>It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.<\/p>\n\n<p>I've added a helper method to get the field value, encapsulating that logic in a reusable function whilst making the code easier to read and its intent clearer.<\/p>\n\n<pre><code class=\"language-php\">namespace Drupal\\mymodule\\ValueObject;\n\nuse Drupal\\node\\NodeInterface;\n\nfinal class Station implements StationInterface {\n\n private NodeInterface $node;\n\n private function __construct(NodeInterface $node) {\n if ($node->bundle() != 'station') {\n throw new \\InvalidArgumentException();\n }\n\n $this->node = $node;\n }\n\n public function getStationCode(): string {\n return $this->node->get('field_station_code')->getString();\n }\n\n public static function fromNode(NodeInterface $node): self {\n return new self($node);\n }\n\n}\n<\/code><\/pre>\n\n<h2 id=\"refactoring-to-use-the-value-object\">Refactoring to use the value object<\/h2>\n\n<p>This is what my code now looks like:<\/p>\n\n<pre><code class=\"language-php\">return Collection::make($stationNodes)\n ->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node))\n ->map(fn (StationInterface $station): string => $station->getStationCode())\n ->values();\n<\/code><\/pre>\n\n<h1><<<<<<< HEAD:website\/source\/_daily_emails\/2022-10-03.md<\/h1>\n\n<blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <p>b9cea6d (chore: replace Sculpin with Astro):website\/src\/pages\/daily-emails\/2022-10-03.md\n I've added an additional <code>map<\/code> to convert the nodes to the value object, but the second map can now use the new typehint - ensuring better type safety and also giving us auto-completion in IDEs and text editors. If an incorrect node type is passed in, then the Exception will be thrown and a much clearer error message will be shown.<\/p>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n<\/blockquote>\n\n<p>Finally, I can use the helper method to get the field value, encapsulating the logic within the value object and making it intention clearer and easier to read.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Here's a snippet of some Drupal code that I wrote last week. It's responsible for converting an array of nodes into a Collection of one of it's field values.<\/p>\n\n<pre><code class=\"language-php\">return Collection::make($stationNodes)\n ->map(fn (NodeInterface $station): string => $station->get('field_station_code')->getString())\n ->values();\n<\/code><\/pre>\n\n<p>There are two issues with this code.<\/p>\n\n<p>First, whilst I'm implicitly saying that it accepts a certain type of node, because of the <code>NodeInterface<\/code> typehint this could accept any type of node. If that node doesn't have the required field, the code will error - but I'd like to know sooner if an incorrect type of node is passed and make it explicit that only a certain type of node can be used.<\/p>\n\n<p>Second, the code for getting the field values is quite verbose and is potentially repeated in other places within the codebase. I'd like to have a simple way to access these field values that I can reuse anywhere else. If the logic for getting these particular field values changes, then I'd only need to change it in one place.<\/p>\n\n<h2 id=\"introducing-a-value-object\">Introducing a value object<\/h2>\n\n<p>This is the value object that I created.<\/p>\n\n<p>It accepts the original node but checks to ensure that the node is the correct type. If not, an Exception is thrown.<\/p>\n\n<p>I've added a helper method to get the field value, encapsulating that logic in a reusable function whilst making the code easier to read and its intent clearer.<\/p>\n\n<pre><code class=\"language-php\">namespace Drupal\\mymodule\\ValueObject;\n\nuse Drupal\\node\\NodeInterface;\n\nfinal class Station implements StationInterface {\n\n private NodeInterface $node;\n\n private function __construct(NodeInterface $node) {\n if ($node->bundle() != 'station') {\n throw new \\InvalidArgumentException();\n }\n\n $this->node = $node;\n }\n\n public function getStationCode(): string {\n return $this->node->get('field_station_code')->getString();\n }\n\n public static function fromNode(NodeInterface $node): self {\n return new self($node);\n }\n\n}\n<\/code><\/pre>\n\n<h2 id=\"refactoring-to-use-the-value-object\">Refactoring to use the value object<\/h2>\n\n<p>This is what my code now looks like:<\/p>\n\n<pre><code class=\"language-php\">return Collection::make($stationNodes)\n ->map(fn (NodeInterface $node): StationInterface => Station::fromNode($node))\n ->map(fn (StationInterface $station): string => $station->getStationCode())\n ->values();\n<\/code><\/pre>\n\n<h1><<<<<<< HEAD:website\/source\/_daily_emails\/2022-10-03.md<\/h1>\n\n<blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <blockquote>\n <p>b9cea6d (chore: replace Sculpin with Astro):website\/src\/pages\/daily-emails\/2022-10-03.md\n I've added an additional <code>map<\/code> to convert the nodes to the value object, but the second map can now use the new typehint - ensuring better type safety and also giving us auto-completion in IDEs and text editors. If an incorrect node type is passed in, then the Exception will be thrown and a much clearer error message will be shown.<\/p>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n <\/blockquote>\n<\/blockquote>\n\n<p>Finally, I can use the helper method to get the field value, encapsulating the logic within the value object and making it intention clearer and easier to read.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:06+00:00",
|
||||
"guid": null,
|
||||
"hash": "7ee7cb63765e8fc082bb197e047a1b09",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "08c0fcef-6c95-4e5a-91cd-a196e8027d2a"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:59+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Paying it forward"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-04-09T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:59+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/04\/09\/paying-it-forward",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As well as building applications with PHP and Drupal, I spend a lot of time helping others and \"paying it forward\".<\/p>\n\n<p>I write and contribute to open-source software and offer free online pair programming sessions to work on open-source projects.<\/p>\n\n<p>I speak at conferences and meetups.<\/p>\n\n<p>I create content, including videos and coding live streams.<\/p>\n\n<p>I mentor at in-person events, such as DrupalCon, and for bootcamps, such as School of Code.<\/p>\n\n<p>I've organised events, such as PHP South Wales and DrupalCamp Bristol, and reviewed session submissions for DrupalCon.<\/p>\n\n<p>But I wouldn't have been able to do this without others doing the same when I was learning and getting into software development.<\/p>\n\n<p>In July 2008, when I was evaluating technologies, I posted a question on a forum (I was learning HTML, PHP and MySQL then).<\/p>\n\n<p>Someone replied and recommended using Drupal, which is how I learned about the project.<\/p>\n\n<p>If they hadn't done that, I may not be in the position I am now.<\/p>\n\n<p>I might not have found Drupal, contributed to it, worked for the Drupal Association, or spoken at DrupalCon.<\/p>\n\n<p>Now, it's my turn to pay it forward.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As well as building applications with PHP and Drupal, I spend a lot of time helping others and \"paying it forward\".<\/p>\n\n<p>I write and contribute to open-source software and offer free online pair programming sessions to work on open-source projects.<\/p>\n\n<p>I speak at conferences and meetups.<\/p>\n\n<p>I create content, including videos and coding live streams.<\/p>\n\n<p>I mentor at in-person events, such as DrupalCon, and for bootcamps, such as School of Code.<\/p>\n\n<p>I've organised events, such as PHP South Wales and DrupalCamp Bristol, and reviewed session submissions for DrupalCon.<\/p>\n\n<p>But I wouldn't have been able to do this without others doing the same when I was learning and getting into software development.<\/p>\n\n<p>In July 2008, when I was evaluating technologies, I posted a question on a forum (I was learning HTML, PHP and MySQL then).<\/p>\n\n<p>Someone replied and recommended using Drupal, which is how I learned about the project.<\/p>\n\n<p>If they hadn't done that, I may not be in the position I am now.<\/p>\n\n<p>I might not have found Drupal, contributed to it, worked for the Drupal Association, or spoken at DrupalCon.<\/p>\n\n<p>Now, it's my turn to pay it forward.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:59+00:00",
|
||||
"guid": null,
|
||||
"hash": "43ead52317b471703708839250e11a67",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09509576-3a6d-47a9-9127-6b01be234771"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Drupal and the Open Web"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-02-05T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/02\/05\/open",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>A few days ago, I said that <a href=\"http:\/\/localhost:8000\/daily\/2025\/02\/02\/simple\">I still like to use RSS<\/a> to consume a lot of online content.<\/p>\n\n<p>Unfortunately, a lot of websites either don't have RSS or Atom feeds, or they aren't easy to find without viewing the website's source code.<\/p>\n\n<p>The ability for people to publish and easily consume other people's content is a main principle of the open web, instead of relying on other websites and social media.<\/p>\n\n<p>Drupal is one of the projects <a href=\"https:\/\/www.drupal.org\/association\/open-web-manifesto\">that make an open web possible<\/a>.<\/p>\n\n<p>It's easy to create an RSS feed of your articles, events or any other content on your Drupal website for others to consume.<\/p>\n\n<p>I also love that Drupal.org itself does this by publishing RSS feeds for project releases and various other content - not just news or blog posts.<\/p>\n\n<p>Because of this, I can subscribe to the feeds I want from any website that creates them and I can consume it how and when I want.<\/p>\n\n<p>Long live the open web!<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>A few days ago, I said that <a href=\"http:\/\/localhost:8000\/daily\/2025\/02\/02\/simple\">I still like to use RSS<\/a> to consume a lot of online content.<\/p>\n\n<p>Unfortunately, a lot of websites either don't have RSS or Atom feeds, or they aren't easy to find without viewing the website's source code.<\/p>\n\n<p>The ability for people to publish and easily consume other people's content is a main principle of the open web, instead of relying on other websites and social media.<\/p>\n\n<p>Drupal is one of the projects <a href=\"https:\/\/www.drupal.org\/association\/open-web-manifesto\">that make an open web possible<\/a>.<\/p>\n\n<p>It's easy to create an RSS feed of your articles, events or any other content on your Drupal website for others to consume.<\/p>\n\n<p>I also love that Drupal.org itself does this by publishing RSS feeds for project releases and various other content - not just news or blog posts.<\/p>\n\n<p>Because of this, I can subscribe to the feeds I want from any website that creates them and I can consume it how and when I want.<\/p>\n\n<p>Long live the open web!<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "96e23c320d92785e0b18c25bec3e0d3c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.09a0d65c-0169-4e0a-b890-fdf03dc66035.json
Normal file
100
content/node.09a0d65c-0169-4e0a-b890-fdf03dc66035.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09a0d65c-0169-4e0a-b890-fdf03dc66035"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Drupal 10.3 released"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-06-23T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:31+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/06\/23\/drupal-10-3-released",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Last week, Drupal 10.3 was released.<\/p>\n\n<p>It's the final feature release of Drupal 10, with Drupal 11 to be released this year.<\/p>\n\n<p>As it's a minor release, it provides improvements and new functionality and does not break backward compatibility (BC) for public APIs, so it should be easy to upgrade from Drupal 10.2.<\/p>\n\n<p>An interesting change is that you <strong>need to upgrade to Drupal 10.3 prior to upgrading to Drupal 11<\/strong>. This is similar to Symfony's upgrade cycle and will make upgrading to Drupal 11, which will be refined version of Drupal 10 with a very similar public API, much easier.<\/p>\n\n<h2 id=\"what-about-drupal-10.4\">What about Drupal 10.4<\/h2>\n\n<p>There will still be a Drupal 10.4 release in December, and will be the first maintenance minor version, receiving limited forward-compatible and security fixes only, and will be supported until the release of Drupal 12 in 2026.<\/p>\n\n<p>It's great to see the project evolve, and I'm looking forward to upgrading my projects to Drupal 10.3 and then 11 in the near future.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Last week, Drupal 10.3 was released.<\/p>\n\n<p>It's the final feature release of Drupal 10, with Drupal 11 to be released this year.<\/p>\n\n<p>As it's a minor release, it provides improvements and new functionality and does not break backward compatibility (BC) for public APIs, so it should be easy to upgrade from Drupal 10.2.<\/p>\n\n<p>An interesting change is that you <strong>need to upgrade to Drupal 10.3 prior to upgrading to Drupal 11<\/strong>. This is similar to Symfony's upgrade cycle and will make upgrading to Drupal 11, which will be refined version of Drupal 10 with a very similar public API, much easier.<\/p>\n\n<h2 id=\"what-about-drupal-10.4\">What about Drupal 10.4<\/h2>\n\n<p>There will still be a Drupal 10.4 release in December, and will be the first maintenance minor version, receiving limited forward-compatible and security fixes only, and will be supported until the release of Drupal 12 in 2026.<\/p>\n\n<p>It's great to see the project evolve, and I'm looking forward to upgrading my projects to Drupal 10.3 and then 11 in the near future.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:31+00:00",
|
||||
"guid": null,
|
||||
"hash": "9ab0b186e8be83ec48f221f864037954",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09c1755f-a3b9-44a5-a17f-5daacfe71967"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "What are Git hooks and why are they useful?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-16T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/16\/what-are-git-hooks-why-are-they-useful",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>In yesterday's email, I mentioned Git hooks but didn't go into any detail. So, what are they?<\/p>\n\n<p>Git hooks are Bash scripts that you add to your repository that are executed when certain events happen, such as before a commit is made or before a push to a remote.<\/p>\n\n<p>By default, the script files need to be within the <code>.git\/hooks<\/code> directory, have executable permissions, and be named to exactly match the name of the hook - e.g. <code>pre-push<\/code> - with no file extension.<\/p>\n\n<p>If it returns an error exit code then the process is stopped and the action doesn't complete.<\/p>\n\n<p>This is useful if, for example, you or your team use a specified format for commit messages and you want to prevent the commit if the message doesn't match the requirements.<\/p>\n\n<p>But, the main benefit that I get from Git hooks if from the <code>pre-push<\/code> hook.<\/p>\n\n<p>I use it to run a subset of the checks that are run within project's CI pipeline to limit failures in the CI tool and fix simple errors before I push the code.<\/p>\n\n<p>Typically, these are the quicker tasks such as ensuring the Docker image builds, running linting and static analysis, validating lock files, and some of the automated tests if they don't take too long to run.<\/p>\n\n<p>If a build is going to fail because of something simply like a linting error, then I'd rather find that out and fix it locally rather than waiting for a CI tool to fail.<\/p>\n\n<p>Also, if you're utilising trunk-based development and continuous integration where team members are pushing changes regularly, then you want to keep the pipeline in a passing, deployable state as much as possible and prevent disruption.<\/p>\n\n<p>But what have Git hooks got to do with the \"run\" file?<\/p>\n\n<p>Firstly, I like to keep the scripts as minimal as possible and move the majority of the code into functions within the <code>run<\/code> file. This means that the scripts are only responsible for running functions like <code>.\/run test:commit<\/code> and returning the appropriate exit code, but also means that it's easy to iterate and test them locally without making fake commits or trying to push them to your actual remote repository (and hoping that they don't get pushed).<\/p>\n\n<p>Secondly, I like to simplify the setup of Git hooks with their own functions.<\/p>\n\n<p>For security reasons, the <code>.git\/hooks<\/code> directory cannot be committed and pushed to your remote so they need to be enabled per user within their own clone of the repository.<\/p>\n\n<p>A common workaround is to put the scripts in a directory like <code>.githooks<\/code> and either symlink them to where Git expects them to be, or to use the <code>core.hooksPath<\/code> configuration option and change where Git is going to look.<\/p>\n\n<p>I like to lower the barrier for any team members by creating <code>git-hooks:on<\/code> and <code>git-hooks:off<\/code> functions which either set or unset the <code>core.hooksPath<\/code>. If someone wants to enable the Git hooks then they only need to run one of those commands rather than having to remember the name of the configuration option or manually creating or removing symlinks.<\/p>\n\n<p>There are other Git hooks that can be used but just using <code>pre-commit<\/code> and <code>pre-push<\/code> has saved me and teams that I've worked on both Developer time and build minutes, provides quicker feedback and fewer disruptions in our build pipelines, and I like how simple it can be by creating custom functions in a <code>run<\/code> file.<\/p>\n\n<p>Lastly, I've created <a href=\"https:\/\/github.com\/opdavies\/git-hooks-scratch\">https:\/\/github.com\/opdavies\/git-hooks-scratch<\/a> as an example with a minimal <code>run<\/code> file and some example hooks.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>In yesterday's email, I mentioned Git hooks but didn't go into any detail. So, what are they?<\/p>\n\n<p>Git hooks are Bash scripts that you add to your repository that are executed when certain events happen, such as before a commit is made or before a push to a remote.<\/p>\n\n<p>By default, the script files need to be within the <code>.git\/hooks<\/code> directory, have executable permissions, and be named to exactly match the name of the hook - e.g. <code>pre-push<\/code> - with no file extension.<\/p>\n\n<p>If it returns an error exit code then the process is stopped and the action doesn't complete.<\/p>\n\n<p>This is useful if, for example, you or your team use a specified format for commit messages and you want to prevent the commit if the message doesn't match the requirements.<\/p>\n\n<p>But, the main benefit that I get from Git hooks if from the <code>pre-push<\/code> hook.<\/p>\n\n<p>I use it to run a subset of the checks that are run within project's CI pipeline to limit failures in the CI tool and fix simple errors before I push the code.<\/p>\n\n<p>Typically, these are the quicker tasks such as ensuring the Docker image builds, running linting and static analysis, validating lock files, and some of the automated tests if they don't take too long to run.<\/p>\n\n<p>If a build is going to fail because of something simply like a linting error, then I'd rather find that out and fix it locally rather than waiting for a CI tool to fail.<\/p>\n\n<p>Also, if you're utilising trunk-based development and continuous integration where team members are pushing changes regularly, then you want to keep the pipeline in a passing, deployable state as much as possible and prevent disruption.<\/p>\n\n<p>But what have Git hooks got to do with the \"run\" file?<\/p>\n\n<p>Firstly, I like to keep the scripts as minimal as possible and move the majority of the code into functions within the <code>run<\/code> file. This means that the scripts are only responsible for running functions like <code>.\/run test:commit<\/code> and returning the appropriate exit code, but also means that it's easy to iterate and test them locally without making fake commits or trying to push them to your actual remote repository (and hoping that they don't get pushed).<\/p>\n\n<p>Secondly, I like to simplify the setup of Git hooks with their own functions.<\/p>\n\n<p>For security reasons, the <code>.git\/hooks<\/code> directory cannot be committed and pushed to your remote so they need to be enabled per user within their own clone of the repository.<\/p>\n\n<p>A common workaround is to put the scripts in a directory like <code>.githooks<\/code> and either symlink them to where Git expects them to be, or to use the <code>core.hooksPath<\/code> configuration option and change where Git is going to look.<\/p>\n\n<p>I like to lower the barrier for any team members by creating <code>git-hooks:on<\/code> and <code>git-hooks:off<\/code> functions which either set or unset the <code>core.hooksPath<\/code>. If someone wants to enable the Git hooks then they only need to run one of those commands rather than having to remember the name of the configuration option or manually creating or removing symlinks.<\/p>\n\n<p>There are other Git hooks that can be used but just using <code>pre-commit<\/code> and <code>pre-push<\/code> has saved me and teams that I've worked on both Developer time and build minutes, provides quicker feedback and fewer disruptions in our build pipelines, and I like how simple it can be by creating custom functions in a <code>run<\/code> file.<\/p>\n\n<p>Lastly, I've created <a href=\"https:\/\/github.com\/opdavies\/git-hooks-scratch\">https:\/\/github.com\/opdavies\/git-hooks-scratch<\/a> as an example with a minimal <code>run<\/code> file and some example hooks.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:07+00:00",
|
||||
"guid": null,
|
||||
"hash": "2dc52bfbe73534ab29285ff16486431f",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.09ccfce7-e7be-4c14-85a1-5919afdf5a50.json
Normal file
100
content/node.09ccfce7-e7be-4c14-85a1-5919afdf5a50.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09ccfce7-e7be-4c14-85a1-5919afdf5a50"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:04+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "I wrote a Neovim plugin"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-13T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:04+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/13\/i-wrote-a-neovim-plugin",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I enjoy writing and working with open-source software, starting back to when I started working with PHP and Drupal in 2007.<\/p>\n\n<p>Since then, I've written and maintained a number of Drupal modules and themes, PHP libraries, npm packages, Ansible roles and Docker images - all of which are available on my GitHub and Drupal.org pages.<\/p>\n\n<p>Just over a year ago, <a href=\"\/blog\/going-full-vim\">I switched to using Neovim full-time<\/a> for my development and DevOps work, and last week, I wrote my first Neovim plugin, written in Lua.<\/p>\n\n<p>I've used Lua to configure Neovim but this is the first time that I've written and open-sourced a standalone Neovim plugin.<\/p>\n\n<p>It's called <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">toggle-checkbox.nvim<\/a> and is used toggle checkboxes in Markdown files - something that I use frequently for to-do lists.<\/p>\n\n<p>For example, this a simple list containing both checked and unchecked checkboxes:<\/p>\n\n<pre><code class=\"markdown\">- [x] A completed task\n- [ ] An incomplete task\n<\/code><\/pre>\n\n<p>To toggle a checkbox, the <code>x<\/code> character needs to be either added or removed, depending on whether we're checking or unchecking it.<\/p>\n\n<p>This is done by calling the <code>toggle()<\/code> function within the plugin.<\/p>\n\n<p>In my Neovim configuration, I've added a keymap to do this:<\/p>\n\n<pre><code class=\"lua\">vim.keymap.set(\n \"n\",\n \"<leader>tt\",\n \"require('toggle-checkbox').toggle()\"\n)\n<\/code><\/pre>\n\n<p>This means that I can use the same keymap by running <code><leader>tt<\/code> to check or uncheck a checkbox. I could use Vim's replace mode to do this, but I really wanted to have one keymap that I could use for both.<\/p>\n\n<p>As it's my first Neovim plugin, I decided to keep it simple.<\/p>\n\n<p>The main <code>toggle-checkbox.lua<\/code> file is currently only 41 lines of code, and whilst there is an existing Vim plugin that I could have used, I was excited to write my own plugin for Neovim, to start contributing to the Neovim ecosystem, and add a Neovim plugin to my portfolio of open-source projects.<\/p>\n\n<p>You can view the plugin at <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">https:\/\/github.com\/opdavies\/toggle-checkbox.nvim<\/a>, as well as my Neovim configuration (which is also written in Lua) as part of <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/tree\/main\/roles\/neovim\/files\">my Dotfiles repository<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I enjoy writing and working with open-source software, starting back to when I started working with PHP and Drupal in 2007.<\/p>\n\n<p>Since then, I've written and maintained a number of Drupal modules and themes, PHP libraries, npm packages, Ansible roles and Docker images - all of which are available on my GitHub and Drupal.org pages.<\/p>\n\n<p>Just over a year ago, <a href=\"\/blog\/going-full-vim\">I switched to using Neovim full-time<\/a> for my development and DevOps work, and last week, I wrote my first Neovim plugin, written in Lua.<\/p>\n\n<p>I've used Lua to configure Neovim but this is the first time that I've written and open-sourced a standalone Neovim plugin.<\/p>\n\n<p>It's called <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">toggle-checkbox.nvim<\/a> and is used toggle checkboxes in Markdown files - something that I use frequently for to-do lists.<\/p>\n\n<p>For example, this a simple list containing both checked and unchecked checkboxes:<\/p>\n\n<pre><code class=\"markdown\">- [x] A completed task\n- [ ] An incomplete task\n<\/code><\/pre>\n\n<p>To toggle a checkbox, the <code>x<\/code> character needs to be either added or removed, depending on whether we're checking or unchecking it.<\/p>\n\n<p>This is done by calling the <code>toggle()<\/code> function within the plugin.<\/p>\n\n<p>In my Neovim configuration, I've added a keymap to do this:<\/p>\n\n<pre><code class=\"lua\">vim.keymap.set(\n \"n\",\n \"<leader>tt\",\n \"require('toggle-checkbox').toggle()\"\n)\n<\/code><\/pre>\n\n<p>This means that I can use the same keymap by running <code><leader>tt<\/code> to check or uncheck a checkbox. I could use Vim's replace mode to do this, but I really wanted to have one keymap that I could use for both.<\/p>\n\n<p>As it's my first Neovim plugin, I decided to keep it simple.<\/p>\n\n<p>The main <code>toggle-checkbox.lua<\/code> file is currently only 41 lines of code, and whilst there is an existing Vim plugin that I could have used, I was excited to write my own plugin for Neovim, to start contributing to the Neovim ecosystem, and add a Neovim plugin to my portfolio of open-source projects.<\/p>\n\n<p>You can view the plugin at <a href=\"https:\/\/github.com\/opdavies\/toggle-checkbox.nvim\">https:\/\/github.com\/opdavies\/toggle-checkbox.nvim<\/a>, as well as my Neovim configuration (which is also written in Lua) as part of <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/tree\/main\/roles\/neovim\/files\">my Dotfiles repository<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:04+00:00",
|
||||
"guid": null,
|
||||
"hash": "da50f1968f98fdd321596a74d98b2ec4",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.09daddb1-1715-4428-84b4-19653faf4744.json
Normal file
100
content/node.09daddb1-1715-4428-84b4-19653faf4744.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09daddb1-1715-4428-84b4-19653faf4744"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Upgrading my Drupal example project to Drupal 10\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-02-17T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/02\/17\/upgrading-my-drupal-example-project-to-drupal-10",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Today I upgraded my <a href=\"https:\/\/github.com\/opdavies\/docker-examples\/tree\/main\/drupal\">Drupal Docker example<\/a> to Drupal 10.<\/p>\n\n<p>Admittedly, it's a simple project, but upgrading from Drupal 9 to 10 was a straightforward process.<\/p>\n\n<p>I had to remove the Examples module as there's no Drupal 10 compatible version yet, but updating to Drupal 10 only needed me to change the version constraints in the <code>composer.json<\/code> file and run the <code>composer update<\/code> command.<\/p>\n\n<p>After completing a number of Drupal 7 upgrade projects which involve migrating content, rewriting custom modules, and rebuilding themes, it's great to be able to upgrade between major modern versions with a few simple commands.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Today I upgraded my <a href=\"https:\/\/github.com\/opdavies\/docker-examples\/tree\/main\/drupal\">Drupal Docker example<\/a> to Drupal 10.<\/p>\n\n<p>Admittedly, it's a simple project, but upgrading from Drupal 9 to 10 was a straightforward process.<\/p>\n\n<p>I had to remove the Examples module as there's no Drupal 10 compatible version yet, but updating to Drupal 10 only needed me to change the version constraints in the <code>composer.json<\/code> file and run the <code>composer update<\/code> command.<\/p>\n\n<p>After completing a number of Drupal 7 upgrade projects which involve migrating content, rewriting custom modules, and rebuilding themes, it's great to be able to upgrade between major modern versions with a few simple commands.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "7648009a474298b1a14504ba7a413f7e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.09dc2986-dec6-4d38-bef1-f591579307f1.json
Normal file
100
content/node.09dc2986-dec6-4d38-bef1-f591579307f1.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09dc2986-dec6-4d38-bef1-f591579307f1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "To squash or not to squash\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-01-25T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/01\/25\/to-squash-or-not-to-squash",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>When reviewing a pull or merge request, tools like GitHub and GitHub offer the option to squash the commits before merging.<\/p>\n\n<p>If the request had twenty commits, they'd be combined into a single commit before being merged.<\/p>\n\n<p>But should you do it?<\/p>\n\n<p>The answer will be \"it depends\" based on the project or team, but I'm personally not a fan of squashing commits.<\/p>\n\n<p>Even though I commit small changes often, I put quite a bit of effort into <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/01\/24\/small-commits-and-good-commit-messges\">crafting commits and writing detailed commit messages<\/a> that capture the reason for each change. If the commits are squashed, either the messages will be combined into one extra-long commit message or I've seen them be deleted completely.<\/p>\n\n<p>One large commit message would be very difficult to read and connect specific messages with their changes, and deleting the commit body would lose the history completely and waste the time it took to write the messages and craft the commits. It may be available within the pull or merge request page but there's no guarantee that you'll continue to use the same repository hosting service in the future.<\/p>\n\n<p>One large commit would also be difficult to debug if there was an error. If the whole feature was added in a single commit, tools like <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/01\/23\/debugging-with-git-bisect\">git bisect<\/a> would no longer work and a single commit couldn't be simply reverted if it contained a bug.<\/p>\n\n<p>I prefer to keep the original small commits and instead prefer to use rebasing and only fast-forward merges to avoid merge commits and keep a simple, linear history in my Git log, and be able to easily read, find and, if needed, fix the code that's been committed.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>When reviewing a pull or merge request, tools like GitHub and GitHub offer the option to squash the commits before merging.<\/p>\n\n<p>If the request had twenty commits, they'd be combined into a single commit before being merged.<\/p>\n\n<p>But should you do it?<\/p>\n\n<p>The answer will be \"it depends\" based on the project or team, but I'm personally not a fan of squashing commits.<\/p>\n\n<p>Even though I commit small changes often, I put quite a bit of effort into <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/01\/24\/small-commits-and-good-commit-messges\">crafting commits and writing detailed commit messages<\/a> that capture the reason for each change. If the commits are squashed, either the messages will be combined into one extra-long commit message or I've seen them be deleted completely.<\/p>\n\n<p>One large commit message would be very difficult to read and connect specific messages with their changes, and deleting the commit body would lose the history completely and waste the time it took to write the messages and craft the commits. It may be available within the pull or merge request page but there's no guarantee that you'll continue to use the same repository hosting service in the future.<\/p>\n\n<p>One large commit would also be difficult to debug if there was an error. If the whole feature was added in a single commit, tools like <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/01\/23\/debugging-with-git-bisect\">git bisect<\/a> would no longer work and a single commit couldn't be simply reverted if it contained a bug.<\/p>\n\n<p>I prefer to keep the original small commits and instead prefer to use rebasing and only fast-forward merges to avoid merge commits and keep a simple, linear history in my Git log, and be able to easily read, find and, if needed, fix the code that's been committed.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "54447bfd288cd6e71d4b06bd0646502d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.09fbc3fd-af16-4114-91f7-eda41dc8d1be.json
Normal file
100
content/node.09fbc3fd-af16-4114-91f7-eda41dc8d1be.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "09fbc3fd-af16-4114-91f7-eda41dc8d1be"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Should I wait to upgrade from Drupal 7?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-29T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/29\/should-i-wait-to-upgrade-from-drupal-7",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>It was announced at DrupalCon that Drupal 7 support was being extended one final time until January 2025.<\/p>\n\n<p>But if you have a Drupal 7 website, does that mean you should wait to start upgrading it?<\/p>\n\n<p>I recommend starting the process as soon as possible.<\/p>\n\n<p>Even though Drupal core support is extended, I've looked at projects that use modules marked as unsupported by their maintainers for some time as they focus on versions for Drupal 8, 9 or 10.<\/p>\n\n<p>In that case, those modules will have no new features, bug fixes or security updates, although Drupal core support has been extended.<\/p>\n\n<p>You may have a lot of custom code that needs to be ported to Drupal 10 or a complex data structure that needs to be migrated, These things will take time, so it's best not to leave it until the last minute.<\/p>\n\n<p>If you're stuck on Drupal 7, book an <a href=\"https:\/\/www.oliverdavies.uk\/call\">upgrade consultation call<\/a> with me or purchase an <a href=\"https:\/\/www.oliverdavies.uk\/drupal7\">upgrade roadmap for your project<\/a> and I'll get you unstuck.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>It was announced at DrupalCon that Drupal 7 support was being extended one final time until January 2025.<\/p>\n\n<p>But if you have a Drupal 7 website, does that mean you should wait to start upgrading it?<\/p>\n\n<p>I recommend starting the process as soon as possible.<\/p>\n\n<p>Even though Drupal core support is extended, I've looked at projects that use modules marked as unsupported by their maintainers for some time as they focus on versions for Drupal 8, 9 or 10.<\/p>\n\n<p>In that case, those modules will have no new features, bug fixes or security updates, although Drupal core support has been extended.<\/p>\n\n<p>You may have a lot of custom code that needs to be ported to Drupal 10 or a complex data structure that needs to be migrated, These things will take time, so it's best not to leave it until the last minute.<\/p>\n\n<p>If you're stuck on Drupal 7, book an <a href=\"https:\/\/www.oliverdavies.uk\/call\">upgrade consultation call<\/a> with me or purchase an <a href=\"https:\/\/www.oliverdavies.uk\/drupal7\">upgrade roadmap for your project<\/a> and I'll get you unstuck.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:52+00:00",
|
||||
"guid": null,
|
||||
"hash": "49f172d2f1fab5d643988eb42ef695ee",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0a5fa15a-2d8a-4a91-ac71-45c758897f96"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Static websites are easy to build"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-03-12T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/03\/12\/easy",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Static websites are the easiest way to build websites.<\/p>\n\n<p>You create an index.html file, type some words, open the file in a browser and you'll see the words you entered.<\/p>\n\n<p>You built a website!<\/p>\n\n<p>Then you can create any more pages you need and style it with CSS.<\/p>\n\n<p>This how I built my first website, for a Tae Kwon-Do school I used to train at.<\/p>\n\n<p>This worked great, but at some point, becomes hard to scale.<\/p>\n\n<p>What if you want to add a new link to your navigation menu? You'd need to update each HTML page separately.<\/p>\n\n<p>At this point, I started to learn about PHP and MySQL, and then Drupal.<\/p>\n\n<p>Static site generators like Sculpin, Jekyll and Hugo also fix this problem.<\/p>\n\n<p>They allow you to write HTML files with a template language like Twig and use includes, loops and conditions to make your files easier to create and maintain with a language and tools you're familiar with.<\/p>\n\n<p>It still generates a static website with HTML files, but in a more maintainable way.<\/p>\n\n<p>As a PHP Developer, I like Sculpin but also like Tome to export a Drupal website to static HTML.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Static websites are the easiest way to build websites.<\/p>\n\n<p>You create an index.html file, type some words, open the file in a browser and you'll see the words you entered.<\/p>\n\n<p>You built a website!<\/p>\n\n<p>Then you can create any more pages you need and style it with CSS.<\/p>\n\n<p>This how I built my first website, for a Tae Kwon-Do school I used to train at.<\/p>\n\n<p>This worked great, but at some point, becomes hard to scale.<\/p>\n\n<p>What if you want to add a new link to your navigation menu? You'd need to update each HTML page separately.<\/p>\n\n<p>At this point, I started to learn about PHP and MySQL, and then Drupal.<\/p>\n\n<p>Static site generators like Sculpin, Jekyll and Hugo also fix this problem.<\/p>\n\n<p>They allow you to write HTML files with a template language like Twig and use includes, loops and conditions to make your files easier to create and maintain with a language and tools you're familiar with.<\/p>\n\n<p>It still generates a static website with HTML files, but in a more maintainable way.<\/p>\n\n<p>As a PHP Developer, I like Sculpin but also like Tome to export a Drupal website to static HTML.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "898a4ca4ca6f3d15e2c342efb0420bee",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0a6387c9-4696-4406-9a6d-57fa292831fa.json
Normal file
100
content/node.0a6387c9-4696-4406-9a6d-57fa292831fa.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0a6387c9-4696-4406-9a6d-57fa292831fa"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't make assumptions"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-08T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:21+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/08\/don-t-make-assumptions",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I was recently writing code for a project and found myself making assumptions about what I was writing.<\/p>\n\n<p>I was creating my own requirements.<\/p>\n\n<p>Something no-one asked for.<\/p>\n\n<p>I was assuming a value was always going to be a certain number of digits long.<\/p>\n\n<p>I was writing code that verified this was true or throw an Exception.<\/p>\n\n<p>Until I found out that that one of the values wasn't the same length as the others.<\/p>\n\n<p>This could be an error in the data or it could correct.<\/p>\n\n<p>No-one told me the lengths were always going to be the same.<\/p>\n\n<p>So why was I checking it?<\/p>\n\n<p>Why was I adding bugs to the code?<\/p>\n\n<p>I've reverted the code that checks the length of the value and gone to find clarification.<\/p>\n\n<p>If it's an issue, it'll be fixed in the source data.<\/p>\n\n<p>If the lengths should all be the same, I'll potentially re-add the check.<\/p>\n\n<p>Until I'm sure, I'll only write what's needed to deliver the feature and stop adding my own requirements and assumptions.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I was recently writing code for a project and found myself making assumptions about what I was writing.<\/p>\n\n<p>I was creating my own requirements.<\/p>\n\n<p>Something no-one asked for.<\/p>\n\n<p>I was assuming a value was always going to be a certain number of digits long.<\/p>\n\n<p>I was writing code that verified this was true or throw an Exception.<\/p>\n\n<p>Until I found out that that one of the values wasn't the same length as the others.<\/p>\n\n<p>This could be an error in the data or it could correct.<\/p>\n\n<p>No-one told me the lengths were always going to be the same.<\/p>\n\n<p>So why was I checking it?<\/p>\n\n<p>Why was I adding bugs to the code?<\/p>\n\n<p>I've reverted the code that checks the length of the value and gone to find clarification.<\/p>\n\n<p>If it's an issue, it'll be fixed in the source data.<\/p>\n\n<p>If the lengths should all be the same, I'll potentially re-add the check.<\/p>\n\n<p>Until I'm sure, I'll only write what's needed to deliver the feature and stop adding my own requirements and assumptions.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:21+00:00",
|
||||
"guid": null,
|
||||
"hash": "2ddc207d69aba210aead45269c83988b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0a900cea-9ccd-448a-9740-6e11f7577bb2.json
Normal file
100
content/node.0a900cea-9ccd-448a-9740-6e11f7577bb2.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0a900cea-9ccd-448a-9740-6e11f7577bb2"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Laravel Prompts and framework-agnostic tools\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-08-04T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/08\/04\/laravel-prompts",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Although I wouldn't consider myself a \"Laravel Developer\", I enjoy watching the talks from the Laravel conferences, most recently Laracon US, that took place last week.<\/p>\n\n<p>I like to learn from other communities and adopt relevant practices or tools within my projects. I did this with Laravel Collections, which I use on nearly every project.<\/p>\n\n<h2 id=\"laravel-prompts\">Laravel Prompts<\/h2>\n\n<p>I watched Jess Archer's talk on the new Laravel Prompts and was happy when she said it worked with plain PHP projects, not just Laravel.<\/p>\n\n<p>This means I can potentially use it with Drupal and Drush or Symfony Console applications, too, such as my build configuration file generator.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>I like tools I can use across multiple technologies and prefer framework-agnostic tools to specific ones.<\/p>\n\n<p>Then, if I'm working on a different project, I can use the tools I already know instead of learning something different just for that.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Although I wouldn't consider myself a \"Laravel Developer\", I enjoy watching the talks from the Laravel conferences, most recently Laracon US, that took place last week.<\/p>\n\n<p>I like to learn from other communities and adopt relevant practices or tools within my projects. I did this with Laravel Collections, which I use on nearly every project.<\/p>\n\n<h2 id=\"laravel-prompts\">Laravel Prompts<\/h2>\n\n<p>I watched Jess Archer's talk on the new Laravel Prompts and was happy when she said it worked with plain PHP projects, not just Laravel.<\/p>\n\n<p>This means I can potentially use it with Drupal and Drush or Symfony Console applications, too, such as my build configuration file generator.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>I like tools I can use across multiple technologies and prefer framework-agnostic tools to specific ones.<\/p>\n\n<p>Then, if I'm working on a different project, I can use the tools I already know instead of learning something different just for that.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:52+00:00",
|
||||
"guid": null,
|
||||
"hash": "37e18d6b3d8c61b4ed2592c0d3ede8c2",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0aa0a89f-2980-4351-9f9c-345e85c019eb"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Write once, manage forever"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-01-26T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/01\/26\/write-once-manage-forever",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I built my <a href=\"http:\/\/localhost:8000\/build-config\">Build Configs<\/a> tool because I only wanted to write a file once and re-use it instead of writing it over again or copying and pasting between projects.<\/p>\n\n<p>Having standardised templates for different languages and project types, I can easily set up new projects and get them running in a few minutes.<\/p>\n\n<p>If I need to add a feature or fix a bug, I can do it once in the Build Configs tool and easily regenerate the configuration files for all projects, and they'll all get the updated files.<\/p>\n\n<p>There's no \"I need to copy this feature from project A or this bug fix from project B.\" when starting on project C.<\/p>\n\n<p>As I only work on fixed-price engagements, it's in my interest to be able to create and maintain projects in a fast and efficient manner.<\/p>\n\n<p>The Build Configs tool enables me to do that.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I built my <a href=\"http:\/\/localhost:8000\/build-config\">Build Configs<\/a> tool because I only wanted to write a file once and re-use it instead of writing it over again or copying and pasting between projects.<\/p>\n\n<p>Having standardised templates for different languages and project types, I can easily set up new projects and get them running in a few minutes.<\/p>\n\n<p>If I need to add a feature or fix a bug, I can do it once in the Build Configs tool and easily regenerate the configuration files for all projects, and they'll all get the updated files.<\/p>\n\n<p>There's no \"I need to copy this feature from project A or this bug fix from project B.\" when starting on project C.<\/p>\n\n<p>As I only work on fixed-price engagements, it's in my interest to be able to create and maintain projects in a fast and efficient manner.<\/p>\n\n<p>The Build Configs tool enables me to do that.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "43e93c9e3c74111834d02bcc44647f25",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ab1e627-0821-4382-b31c-d46b9419e169"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "When should you run your tests?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-10-23T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/10\/23\/when-should-run-your-tests",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>After my talk at DrupalCon, I was asked when you should run your tests.<\/p>\n\n<p>Of course, if you're doing test-driven development, you have to run the tests as you work on them and have the red, green, refactor cycle as you work on the feature or bug fix.<\/p>\n\n<p>If you're not doing TDD, I'd recommend running the whole test suite before you push your code to know it works before a peer review or to an environment for quality assurance or user-acceptance testing.<\/p>\n\n<h2 id=\"what-about-ci-pipelines%3F\">What about CI pipelines?<\/h2>\n\n<p>As well as running tests manually, I'd add them to a CI pipeline, such as GitHub Actions, GitLab CI or Bitbucket Pipelines.<\/p>\n\n<p>There, tasks can be run automatically each time a commit is pushed, so you don't need to rely on them being run manually.<\/p>\n\n<p>If you're doing trunk-based development, you want the CI pipeline to run on every push to prevent regressions and ensure the tests continue to pass.<\/p>\n\n<p>If you're working with feature branches and doing code review, run the tests as part of the merge or pull request so you know everything works as expected before the code is reviewed.<\/p>\n\n<p>This answers the main \"Does it work?\" question, and allows the reviewer to focus on reviewing the code and suggesting improvements.<\/p>\n\n<p>If the CI pipeline in the merge or pull request fails, it needs to be fixed before submitting it for review as there's no need to review the code before it changes to fix the pipeline.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>After my talk at DrupalCon, I was asked when you should run your tests.<\/p>\n\n<p>Of course, if you're doing test-driven development, you have to run the tests as you work on them and have the red, green, refactor cycle as you work on the feature or bug fix.<\/p>\n\n<p>If you're not doing TDD, I'd recommend running the whole test suite before you push your code to know it works before a peer review or to an environment for quality assurance or user-acceptance testing.<\/p>\n\n<h2 id=\"what-about-ci-pipelines%3F\">What about CI pipelines?<\/h2>\n\n<p>As well as running tests manually, I'd add them to a CI pipeline, such as GitHub Actions, GitLab CI or Bitbucket Pipelines.<\/p>\n\n<p>There, tasks can be run automatically each time a commit is pushed, so you don't need to rely on them being run manually.<\/p>\n\n<p>If you're doing trunk-based development, you want the CI pipeline to run on every push to prevent regressions and ensure the tests continue to pass.<\/p>\n\n<p>If you're working with feature branches and doing code review, run the tests as part of the merge or pull request so you know everything works as expected before the code is reviewed.<\/p>\n\n<p>This answers the main \"Does it work?\" question, and allows the reviewer to focus on reviewing the code and suggesting improvements.<\/p>\n\n<p>If the CI pipeline in the merge or pull request fails, it needs to be fixed before submitting it for review as there's no need to review the code before it changes to fix the pipeline.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:01+00:00",
|
||||
"guid": null,
|
||||
"hash": "1f2cab06d1412951184ea5c3b5d74edb",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0acd55fb-39a2-400b-bc1b-2acf5896b63d"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Reproducible or repeatable"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-20T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/20\/reproducible",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2025\/01\/19\/minimum-viable-development-environment\">In yesterday's email<\/a>, I showed how I've been using Nix and flake files to build reproducible and shareable development environments for Drupal applications.<\/p>\n\n<p>The reason it's reproducible is the <code>flake.lock<\/code> file.<\/p>\n\n<p>Similar to <code>composer.lock<\/code> or <code>package-lock.json<\/code>, it captures the exact versions of the packages installed from the nixpkgs repository.<\/p>\n\n<p>This file, along with <code>flake.nix<\/code>, can be committed alongside the application code and anyone with Nix installed can run <code>nix develop<\/code> to get a shell with the same packages and dependencies.<\/p>\n\n<p>This isn't the same as other solutions, where you add something like <code>FROM php:8.2<\/code> but, because there's no lockfile, there's no guarantee the same package versions will be installed so there could be mismatches that cause errors.<\/p>\n\n<p>With <code>flake.lock<\/code>, the environment isn't just repeatable - it's completely reproducible.<\/p>\n\n<p>Locally, in a CI pipeline or in production.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p><a href=\"http:\/\/localhost:8000\/daily\/2025\/01\/19\/minimum-viable-development-environment\">In yesterday's email<\/a>, I showed how I've been using Nix and flake files to build reproducible and shareable development environments for Drupal applications.<\/p>\n\n<p>The reason it's reproducible is the <code>flake.lock<\/code> file.<\/p>\n\n<p>Similar to <code>composer.lock<\/code> or <code>package-lock.json<\/code>, it captures the exact versions of the packages installed from the nixpkgs repository.<\/p>\n\n<p>This file, along with <code>flake.nix<\/code>, can be committed alongside the application code and anyone with Nix installed can run <code>nix develop<\/code> to get a shell with the same packages and dependencies.<\/p>\n\n<p>This isn't the same as other solutions, where you add something like <code>FROM php:8.2<\/code> but, because there's no lockfile, there's no guarantee the same package versions will be installed so there could be mismatches that cause errors.<\/p>\n\n<p>With <code>flake.lock<\/code>, the environment isn't just repeatable - it's completely reproducible.<\/p>\n\n<p>Locally, in a CI pipeline or in production.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "180481b0b90c165143857e50b30bbb77",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0af916e1-e99f-43fa-b9b0-6472eabf31f1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "What if there was no open-source software\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-06-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:03+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/06\/14\/what-if-there-was-no-open-source-software",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I was listening to a podcast today, and the question was mentioned - \"What if there was no open-source software?\".<\/p>\n\n<p>As a self-taught Developer who has worked with open-source technologies and become an expert in Drupal - an open-source content management system - this would have had a big effect.<\/p>\n\n<p>If there were no open-source frameworks or CMSes like Drupal, Symfony, Laravel or Vue.js, Developers would need to write everything from scratch, and companies would need to pay for the extra time.<\/p>\n\n<p>There would be no reusable knowledge as Developers move to different companies as everything would be written in-house.<\/p>\n\n<p>There would be no communities and events like conferences and meetups for open-source technologies.<\/p>\n\n<p>As well as frameworks, what about languages like PHP that are open-sourced? Would companies also need to write and maintain their own programming languages?<\/p>\n\n<p>What about Linux, which I use every day for my desktop environment and servers, and other tools like Neovim, PHPStan, PHPUnit and Pest that are all open-source?<\/p>\n\n<p>Even as someone who contributes to and sponsors open-source projects and their Developers, it's still a shock to think how things would work without open-source software.<\/p>\n\n<p>If you use open-source software, please consider sponsoring, supporting or contributing to the projects you use and depend on.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I was listening to a podcast today, and the question was mentioned - \"What if there was no open-source software?\".<\/p>\n\n<p>As a self-taught Developer who has worked with open-source technologies and become an expert in Drupal - an open-source content management system - this would have had a big effect.<\/p>\n\n<p>If there were no open-source frameworks or CMSes like Drupal, Symfony, Laravel or Vue.js, Developers would need to write everything from scratch, and companies would need to pay for the extra time.<\/p>\n\n<p>There would be no reusable knowledge as Developers move to different companies as everything would be written in-house.<\/p>\n\n<p>There would be no communities and events like conferences and meetups for open-source technologies.<\/p>\n\n<p>As well as frameworks, what about languages like PHP that are open-sourced? Would companies also need to write and maintain their own programming languages?<\/p>\n\n<p>What about Linux, which I use every day for my desktop environment and servers, and other tools like Neovim, PHPStan, PHPUnit and Pest that are all open-source?<\/p>\n\n<p>Even as someone who contributes to and sponsors open-source projects and their Developers, it's still a shock to think how things would work without open-source software.<\/p>\n\n<p>If you use open-source software, please consider sponsoring, supporting or contributing to the projects you use and depend on.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:03+00:00",
|
||||
"guid": null,
|
||||
"hash": "c70e6f1af7689e9704999c27d288916d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0b2c61c6-d125-44fc-8dd4-d5657f48af12.json
Normal file
100
content/node.0b2c61c6-d125-44fc-8dd4-d5657f48af12.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b2c61c6-d125-44fc-8dd4-d5657f48af12"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Coding defensively, and Implicit vs explicit coding"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-10-09T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/10\/09\/coding-defensively-implicit-explicit",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/10\/08\/first-impressions-astro\">being introduced to Astro<\/a> in Simon's most recent Pro Tailwind workshop, something else that we discussed was implicit vs explicit coding, and coding defensively.<\/p>\n\n<p>For example, if you had this code:<\/p>\n\n<pre><code class=\"javascript\">const sizeClasses = {\n small: 'px-3 py-1 text-sm',\n medium: 'px-5 py-2',\n large: 'px-7 py-2.5 text-lg',\n}\n\nconst shapeClasses = {\n square: '',\n rounded: 'rounded',\n pill: 'rounded-full',\n}\n<\/code><\/pre>\n\n<p>Both the <code>medium<\/code> size and <code>square<\/code> shape have an implicit value.<\/p>\n\n<p>The <code>small<\/code> size has a text size class of <code>text-sm<\/code> and the <code>large<\/code> size has <code>text-lg<\/code>. As there isn't a text size added for <code>medium<\/code>, it is implicitly <code>text-base<\/code> - the default text size.<\/p>\n\n<p>Likewise, the <code>rounded<\/code> shape has a class of <code>rounded<\/code> and the <code>pill<\/code> shape has <code>rounded-full<\/code>. As a square button doesn't have any rounding, it has an empty string but it is implicitly <code>rounded-none<\/code> - the default border radius value.<\/p>\n\n<p>If we were to code this explicitly, <code>text-base<\/code> and <code>rounded-none<\/code> would be added to their respective size and shape classes.<\/p>\n\n<p>It's mostly personal preference, but explicitly adding the additional classes could potentially future-proof the components if there was a situation where the text size or border radius was being overridden.<\/p>\n\n<p>It also makes it more obvious to anyone reading the code that these values are being set, rather than them needing to make that assumption - assuming that they're aware of the default values at all.<\/p>\n\n<p>It's similar to having this example PHP code:<\/p>\n\n<pre><code class=\"language-php\">function __invoke(string $type, int $limit): void {};\n<\/code><\/pre>\n\n<p>Whilst I'm using type hints for the parameters to ensure that the values are a string and an integer respectively, it's also safe to assume that the type shouldn't be an empty string, so do we check for that?<\/p>\n\n<p>I'd also suggest that the limit shouldn't be a negative integer, so we'd want to check that the value is not less than zero, or if zero isn't being used as an \"all\" value, then we'd want to check that the limit is greater than one.<\/p>\n\n<p>In this case, the type hints add some explicitness to the parameters, but checking for these additional conditions adds another defensive layer to the code - forcing it to return earlier with an explicit error message rather than causing a vaguer error and elsewhere in the application.<\/p>\n\n<p>Personally, I like to be explicit and code defensively, making sure that I try and cover as many edge cases as possible and writing test cases for them.<\/p>\n\n<p>Coming back to the Tailwind example, the majority of us decided to add in extra classes after the exercise and it was an interesting discussion and part of the workshop.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/10\/08\/first-impressions-astro\">being introduced to Astro<\/a> in Simon's most recent Pro Tailwind workshop, something else that we discussed was implicit vs explicit coding, and coding defensively.<\/p>\n\n<p>For example, if you had this code:<\/p>\n\n<pre><code class=\"javascript\">const sizeClasses = {\n small: 'px-3 py-1 text-sm',\n medium: 'px-5 py-2',\n large: 'px-7 py-2.5 text-lg',\n}\n\nconst shapeClasses = {\n square: '',\n rounded: 'rounded',\n pill: 'rounded-full',\n}\n<\/code><\/pre>\n\n<p>Both the <code>medium<\/code> size and <code>square<\/code> shape have an implicit value.<\/p>\n\n<p>The <code>small<\/code> size has a text size class of <code>text-sm<\/code> and the <code>large<\/code> size has <code>text-lg<\/code>. As there isn't a text size added for <code>medium<\/code>, it is implicitly <code>text-base<\/code> - the default text size.<\/p>\n\n<p>Likewise, the <code>rounded<\/code> shape has a class of <code>rounded<\/code> and the <code>pill<\/code> shape has <code>rounded-full<\/code>. As a square button doesn't have any rounding, it has an empty string but it is implicitly <code>rounded-none<\/code> - the default border radius value.<\/p>\n\n<p>If we were to code this explicitly, <code>text-base<\/code> and <code>rounded-none<\/code> would be added to their respective size and shape classes.<\/p>\n\n<p>It's mostly personal preference, but explicitly adding the additional classes could potentially future-proof the components if there was a situation where the text size or border radius was being overridden.<\/p>\n\n<p>It also makes it more obvious to anyone reading the code that these values are being set, rather than them needing to make that assumption - assuming that they're aware of the default values at all.<\/p>\n\n<p>It's similar to having this example PHP code:<\/p>\n\n<pre><code class=\"language-php\">function __invoke(string $type, int $limit): void {};\n<\/code><\/pre>\n\n<p>Whilst I'm using type hints for the parameters to ensure that the values are a string and an integer respectively, it's also safe to assume that the type shouldn't be an empty string, so do we check for that?<\/p>\n\n<p>I'd also suggest that the limit shouldn't be a negative integer, so we'd want to check that the value is not less than zero, or if zero isn't being used as an \"all\" value, then we'd want to check that the limit is greater than one.<\/p>\n\n<p>In this case, the type hints add some explicitness to the parameters, but checking for these additional conditions adds another defensive layer to the code - forcing it to return earlier with an explicit error message rather than causing a vaguer error and elsewhere in the application.<\/p>\n\n<p>Personally, I like to be explicit and code defensively, making sure that I try and cover as many edge cases as possible and writing test cases for them.<\/p>\n\n<p>Coming back to the Tailwind example, the majority of us decided to add in extra classes after the exercise and it was an interesting discussion and part of the workshop.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "2396dbcb354eade3105e541370b34bef",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0b3627d0-77e9-4ed5-888b-3360997cdbc8.json
Normal file
100
content/node.0b3627d0-77e9-4ed5-888b-3360997cdbc8.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b3627d0-77e9-4ed5-888b-3360997cdbc8"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Not all legacy code is technical debt"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-10-01T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/10\/01\/not-all-legacy-code-is-technical-debt",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Technical debt is an overused term with people often referring to any legacy code, outdated dependencies or bugs as technical debt.<\/p>\n\n<p>Technical debt specifically refers to the future cost when a short-term solution is selected over a more flexible or efficient one.<\/p>\n\n<p>For example, hard-coding something for now and making it dynamic later.<\/p>\n\n<p>It's not any code the current Developers didn't write or no longer want to support.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Technical debt is an overused term with people often referring to any legacy code, outdated dependencies or bugs as technical debt.<\/p>\n\n<p>Technical debt specifically refers to the future cost when a short-term solution is selected over a more flexible or efficient one.<\/p>\n\n<p>For example, hard-coding something for now and making it dynamic later.<\/p>\n\n<p>It's not any code the current Developers didn't write or no longer want to support.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:25+00:00",
|
||||
"guid": null,
|
||||
"hash": "238cdb2a724852497414bcfd508b5a90",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0b39583b-3864-44db-8c8c-7956133eddb4.json
Normal file
100
content/node.0b39583b-3864-44db-8c8c-7956133eddb4.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b39583b-3864-44db-8c8c-7956133eddb4"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Visual testing and Diffy with Yuri Gerasymov"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-03-10T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/03\/10\/visual-testing-and-diffy-with-yuri-gerasymov",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>This week on the Beyond Blocks podcast, I'm joined by Yuri Gerasymov to discuss Diffy visual regression testing.<\/p>\n\n<h2 id=\"talking-points\">Talking Points<\/h2>\n\n<ul>\n<li>What is visual regression testing?<\/li>\n<li>How do you deal with false positives?<\/li>\n<li>Different use cases for visual regression testing.<\/li>\n<li>Automatic updates.<\/li>\n<li>Scheduling content.<\/li>\n<li>Visual regression testing in CI.<\/li>\n<li>Diffy in WordPress.<\/li>\n<li>What's a baseline?<\/li>\n<li>Initial setup and onboarding feedback.<\/li>\n<li>Testing dark mode?<\/li>\n<li>Component testing with Storybook and Fractal?<\/li>\n<li>Testing local environments.<\/li>\n<li>Testing as authenticated users.<\/li>\n<li>The roadmap for Diffy.<\/li>\n<\/ul>\n\n<h2 id=\"quotable-quotes\">Quotable Quotes<\/h2>\n\n<ul>\n<li>We help development teams to have less visual bugs in their website. We take screenshots of the pages and compare them so you can see what changed and how. (YG)<\/li>\n<li>We built tools for you to mock the content. You provide selectors for the elements with the content of the article and we'll replace it with lorem ipsum text so it will be exactly the same across multiple environments. (YG)<\/li>\n<li>I can still write an assertion to check the text is on the page or not, but it won't confirm it's in the correct place. (OD)<\/li>\n<li>Having a tool checking for changes on a regular basis instead of only after a deployment would be very useful. (OD)<\/li>\n<li>So, you could have a tool like Violinst automatically creating pull requests and Diffy checking those PRs, so the two could work together? (OD)<\/li>\n<li>With visual testing, it's very easy to get started. (YG)<\/li>\n<li>Visual testing is great for showing your client your work. (YG)<\/li>\n<\/ul>\n\n<p>I learned a lot during this conversation and have added visual regression testing to my testing toolbox for working on projects.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/podcast\/14-yuri-gerasymov-diffy\">Listen to the episode<\/a><\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>This week on the Beyond Blocks podcast, I'm joined by Yuri Gerasymov to discuss Diffy visual regression testing.<\/p>\n\n<h2 id=\"talking-points\">Talking Points<\/h2>\n\n<ul>\n<li>What is visual regression testing?<\/li>\n<li>How do you deal with false positives?<\/li>\n<li>Different use cases for visual regression testing.<\/li>\n<li>Automatic updates.<\/li>\n<li>Scheduling content.<\/li>\n<li>Visual regression testing in CI.<\/li>\n<li>Diffy in WordPress.<\/li>\n<li>What's a baseline?<\/li>\n<li>Initial setup and onboarding feedback.<\/li>\n<li>Testing dark mode?<\/li>\n<li>Component testing with Storybook and Fractal?<\/li>\n<li>Testing local environments.<\/li>\n<li>Testing as authenticated users.<\/li>\n<li>The roadmap for Diffy.<\/li>\n<\/ul>\n\n<h2 id=\"quotable-quotes\">Quotable Quotes<\/h2>\n\n<ul>\n<li>We help development teams to have less visual bugs in their website. We take screenshots of the pages and compare them so you can see what changed and how. (YG)<\/li>\n<li>We built tools for you to mock the content. You provide selectors for the elements with the content of the article and we'll replace it with lorem ipsum text so it will be exactly the same across multiple environments. (YG)<\/li>\n<li>I can still write an assertion to check the text is on the page or not, but it won't confirm it's in the correct place. (OD)<\/li>\n<li>Having a tool checking for changes on a regular basis instead of only after a deployment would be very useful. (OD)<\/li>\n<li>So, you could have a tool like Violinst automatically creating pull requests and Diffy checking those PRs, so the two could work together? (OD)<\/li>\n<li>With visual testing, it's very easy to get started. (YG)<\/li>\n<li>Visual testing is great for showing your client your work. (YG)<\/li>\n<\/ul>\n\n<p>I learned a lot during this conversation and have added visual regression testing to my testing toolbox for working on projects.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/podcast\/14-yuri-gerasymov-diffy\">Listen to the episode<\/a><\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:36+00:00",
|
||||
"guid": null,
|
||||
"hash": "f767e648d91ab4e42156d03ff67f7f6c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b3d3cba-7f34-428f-9ccd-414eb68f14df"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "How much would it cost to build Drupal?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-03-03T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/03\/03\/cost",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "3b2f48d28cdda255b884d631a4a170ad",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b453792-d771-4d1b-a852-5837c850ef8a"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:05+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Are sprints incompatible with Continuous Deployment?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-11-08T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:05+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/11\/08\/are-sprints-incompatible-with-continuous-deployment",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>It's been common for me whilst working on software projects to have work organised into sprints or cycles - a period, usually between 1 and 3 weeks, where the team is working on stories and tasks for that project.<\/p>\n\n<p>In my experience, those changes are usually released at the end of that cycle. But it seems that's not always the case; see <a href=\"https:\/\/scrumdictionary.com\/term\/release-sprint\">release sprints<\/a>:<\/p>\n\n<blockquote>\n <p>A specialised sprint whose purpose is to release deliverable results; it contains stories specific to release activities and finishing undone work. A release sprint usually contains no additional development.<\/p>\n<\/blockquote>\n\n<p>If we worked in two-week cycles and released at the end of each one, it would be at least two weeks before a change could be deployed to production. But what if we wanted to follow continuous deployment and release more frequently? Maybe daily or hourly?<\/p>\n\n<p>Instead of waiting for a release sprint, if we released multiple times within a single sprint, how would this fit into or affect the process?<\/p>\n\n<p>Does the release cycle need to be tightly coupled to the sprint cycle or can they be separate and independent of each other?<\/p>\n\n<p>I've worked on projects - including a current one - where I've done multiple releases in a sprint, so of course, it can be done from a technical perspective, but how do we get the best from both processes - whether they work together or separately?<\/p>\n\n<p>This is something that I'm going to continue to experiment with, iterate on, and learn more about going forward.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>It's been common for me whilst working on software projects to have work organised into sprints or cycles - a period, usually between 1 and 3 weeks, where the team is working on stories and tasks for that project.<\/p>\n\n<p>In my experience, those changes are usually released at the end of that cycle. But it seems that's not always the case; see <a href=\"https:\/\/scrumdictionary.com\/term\/release-sprint\">release sprints<\/a>:<\/p>\n\n<blockquote>\n <p>A specialised sprint whose purpose is to release deliverable results; it contains stories specific to release activities and finishing undone work. A release sprint usually contains no additional development.<\/p>\n<\/blockquote>\n\n<p>If we worked in two-week cycles and released at the end of each one, it would be at least two weeks before a change could be deployed to production. But what if we wanted to follow continuous deployment and release more frequently? Maybe daily or hourly?<\/p>\n\n<p>Instead of waiting for a release sprint, if we released multiple times within a single sprint, how would this fit into or affect the process?<\/p>\n\n<p>Does the release cycle need to be tightly coupled to the sprint cycle or can they be separate and independent of each other?<\/p>\n\n<p>I've worked on projects - including a current one - where I've done multiple releases in a sprint, so of course, it can be done from a technical perspective, but how do we get the best from both processes - whether they work together or separately?<\/p>\n\n<p>This is something that I'm going to continue to experiment with, iterate on, and learn more about going forward.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:05+00:00",
|
||||
"guid": null,
|
||||
"hash": "efd0ec8ddb3fb7929febf9e8017a8f84",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0b4f562a-f1d9-47b0-92f3-ee4ca8b7fd71.json
Normal file
100
content/node.0b4f562a-f1d9-47b0-92f3-ee4ca8b7fd71.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b4f562a-f1d9-47b0-92f3-ee4ca8b7fd71"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:27+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "You need tests to refactor safely"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-09-01T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:27+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/09\/01\/you-need-tests-to-refactor-safely",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p><a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/08\/31\/make-it-work-then-make-it-good\">Once you have working code<\/a>, you can refactor it to make it better.<\/p>\n\n<p>You can rename variables, extract new functions or classes, ensure the code is styled and formatted correctly - anything to make the code easier to read, understand or maintain.<\/p>\n\n<p>But, the key thing is the code still needs to work.<\/p>\n\n<p>You don't want to break working code and introduce a regression, even if it is messy or difficult to read.<\/p>\n\n<p>The best way I know to approach this is by having automated tests covering this code, and ensure they are working and passing before starting to refactor.<\/p>\n\n<p>Then you can run the tests again and ensure they still pass.<\/p>\n\n<p>If they pass, the code still works and the refactoring was successful.<\/p>\n\n<p>If not, the code is broken and you need to revert the changes and start again.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p><a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/08\/31\/make-it-work-then-make-it-good\">Once you have working code<\/a>, you can refactor it to make it better.<\/p>\n\n<p>You can rename variables, extract new functions or classes, ensure the code is styled and formatted correctly - anything to make the code easier to read, understand or maintain.<\/p>\n\n<p>But, the key thing is the code still needs to work.<\/p>\n\n<p>You don't want to break working code and introduce a regression, even if it is messy or difficult to read.<\/p>\n\n<p>The best way I know to approach this is by having automated tests covering this code, and ensure they are working and passing before starting to refactor.<\/p>\n\n<p>Then you can run the tests again and ensure they still pass.<\/p>\n\n<p>If they pass, the code still works and the refactoring was successful.<\/p>\n\n<p>If not, the code is broken and you need to revert the changes and start again.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:27+00:00",
|
||||
"guid": null,
|
||||
"hash": "c4f9c5744c27b541d231076231201427",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b69e069-315d-42af-a4c0-b0714764ab83"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "nix is like nvm, but for everything"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-04-15T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/04\/15\/nix-nvm",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I was recently explaining and demonstrating Nix and direnv to a colleague and showing how, when I moved into a directory, new packages or different versions of packages became available.<\/p>\n\n<p>If I left the directory, I was reverted back to my global packages and versions.<\/p>\n\n<p>In this demonstration, I was showing how I can have different versions of PHP and node for a particular project - replacing a lot of what I'd previously used tools like Vagrant and Docker for.<\/p>\n\n<p>I came up with a comparison between Nix and nvm - the node version manager - a tool that allows you to install multiple versions of nodejs and switch between them.<\/p>\n\n<p>Using Nix and direnv is more seamless, but it works for everything.<\/p>\n\n<p>I'm able to switch versions of PHP, MySQL, MariaDB, PostgreSQL or anything else I need with Nix.<\/p>\n\n<p>Not just node, and without needing containers.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I was recently explaining and demonstrating Nix and direnv to a colleague and showing how, when I moved into a directory, new packages or different versions of packages became available.<\/p>\n\n<p>If I left the directory, I was reverted back to my global packages and versions.<\/p>\n\n<p>In this demonstration, I was showing how I can have different versions of PHP and node for a particular project - replacing a lot of what I'd previously used tools like Vagrant and Docker for.<\/p>\n\n<p>I came up with a comparison between Nix and nvm - the node version manager - a tool that allows you to install multiple versions of nodejs and switch between them.<\/p>\n\n<p>Using Nix and direnv is more seamless, but it works for everything.<\/p>\n\n<p>I'm able to switch versions of PHP, MySQL, MariaDB, PostgreSQL or anything else I need with Nix.<\/p>\n\n<p>Not just node, and without needing containers.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "1f1938498491ff40c88d4e5b99fc617b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0b6d2f8b-2aac-4ed4-aedb-ae8eb2f139f1.json
Normal file
100
content/node.0b6d2f8b-2aac-4ed4-aedb-ae8eb2f139f1.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0b6d2f8b-2aac-4ed4-aedb-ae8eb2f139f1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Releasing a Drupal module template"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-09-22T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/09\/22\/releasing-drupal-module-template",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Today, I an the idea to create a reusable template for new Drupal modules, based on how I like to build modules and how I've shown others to do so in my Drupal testing workshop.<\/p>\n\n<p>So I did, and released it for free <a href=\"https:\/\/github.com\/opdavies\/drupal-module-template\">on my GitHub account<\/a>.<\/p>\n\n<p>Like my Tailwind CSS starter theme on Drupal.org, it's not intended to be added as a module directly, but something that can be cloned and used as a base for people's own modules.<\/p>\n\n<p>It includes an example route and Controller that load a basic page, and has a test to ensure that the page exists and loads correctly.<\/p>\n\n<p>The Controller is defined as a service and uses autowiring to automatically inject the its dependencies, the same as in my workshop example code.<\/p>\n\n<p>It's the initial release so it's rough around the edges still. I'll use it tomorrow to create a new module and document the steps to add to the README as well as other pieces of documentation.<\/p>\n\n<p>If you're creating a new Drupal module and try it out, start a discussion on the GitHub repository or <a href=\"https:\/\/twitter.com\/opdavies\">let me know on Twitter<\/a>. If you have questions, create a discussion or just reply to this email and I'll get back to you.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Today, I an the idea to create a reusable template for new Drupal modules, based on how I like to build modules and how I've shown others to do so in my Drupal testing workshop.<\/p>\n\n<p>So I did, and released it for free <a href=\"https:\/\/github.com\/opdavies\/drupal-module-template\">on my GitHub account<\/a>.<\/p>\n\n<p>Like my Tailwind CSS starter theme on Drupal.org, it's not intended to be added as a module directly, but something that can be cloned and used as a base for people's own modules.<\/p>\n\n<p>It includes an example route and Controller that load a basic page, and has a test to ensure that the page exists and loads correctly.<\/p>\n\n<p>The Controller is defined as a service and uses autowiring to automatically inject the its dependencies, the same as in my workshop example code.<\/p>\n\n<p>It's the initial release so it's rough around the edges still. I'll use it tomorrow to create a new module and document the steps to add to the README as well as other pieces of documentation.<\/p>\n\n<p>If you're creating a new Drupal module and try it out, start a discussion on the GitHub repository or <a href=\"https:\/\/twitter.com\/opdavies\">let me know on Twitter<\/a>. If you have questions, create a discussion or just reply to this email and I'll get back to you.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "70de2df26133c67db29db3a24a4e7e6a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0bb328d6-219a-4e8e-8823-8331674eda84"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:04+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Debugging with git bisect\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-01-23T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:04+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/01\/23\/debugging-with-git-bisect",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Last week, I had to debug a regression in a codebase.<\/p>\n\n<p>Something was working at the last release but is now broken.<\/p>\n\n<p>There have been around 20 commits to the mainline branch since the last release, and the first step to fixing the issue is to determine which commit caused the regression.<\/p>\n\n<p>Git has a great tool for this - <code>git bisect<\/code>.<\/p>\n\n<p>You tell Git what the last known working commit was, such as the tag of the last release, and it will start to split the commits and prompt you to tell it whether the commit is good or bad.<\/p>\n\n<p>If there are 20 commits, it may pick commit number 10, and based on whether the commit is good or bad, it may pick commit 5 or 15.<\/p>\n\n<p>Based on your answers, Git will then tell you which the first bad commit is.<\/p>\n\n<p>Even better, if it's something that you can script or is covered with an automated test, <code>git bisect<\/code> can run a command for you and find the failure automatically rather than a human needing to check manually.<\/p>\n\n<p>Once you've found the commit that breaks, you can view it and find and fix the bug.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Last week, I had to debug a regression in a codebase.<\/p>\n\n<p>Something was working at the last release but is now broken.<\/p>\n\n<p>There have been around 20 commits to the mainline branch since the last release, and the first step to fixing the issue is to determine which commit caused the regression.<\/p>\n\n<p>Git has a great tool for this - <code>git bisect<\/code>.<\/p>\n\n<p>You tell Git what the last known working commit was, such as the tag of the last release, and it will start to split the commits and prompt you to tell it whether the commit is good or bad.<\/p>\n\n<p>If there are 20 commits, it may pick commit number 10, and based on whether the commit is good or bad, it may pick commit 5 or 15.<\/p>\n\n<p>Based on your answers, Git will then tell you which the first bad commit is.<\/p>\n\n<p>Even better, if it's something that you can script or is covered with an automated test, <code>git bisect<\/code> can run a command for you and find the failure automatically rather than a human needing to check manually.<\/p>\n\n<p>Once you've found the commit that breaks, you can view it and find and fix the bug.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:04+00:00",
|
||||
"guid": null,
|
||||
"hash": "6fbd09cf55994352dfe3e9a6fae9be71",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0bbaccac-20db-4baf-87fd-e72a3a38aca7.json
Normal file
100
content/node.0bbaccac-20db-4baf-87fd-e72a3a38aca7.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0bbaccac-20db-4baf-87fd-e72a3a38aca7"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Sculpin from Scratch"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-12-04T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/12\/04\/sculpin-from-scratch",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Over the next few weeks, I'm going to create another email course, this time about building websites with the <a href=\"https:\/\/sculpin.io\">Sculpin static site generator<\/a>.<\/p>\n\n<p>I've been using Sculpin since 2015 when <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/test-drive-twig-with-sculpin\">I used it to learn Twig<\/a> before Drupal 8's release and I use it to generate my website.<\/p>\n\n<p>I've recently been <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-static-websites-sculpin\">speaking about it at user groups<\/a> like PHP South West, BrumPHP and PHP Berkshire.<\/p>\n\n<p>Similar to my <a href=\"https:\/\/www.oliverdavies.uk\/atdc\">automated testing email course<\/a>, this will send a lesson every day straight to your inbox and will show how to build a website with Sculpin from scratch.<\/p>\n\n<p>If you want to be added when it's ready or have questions, reply to this email and let me know.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Over the next few weeks, I'm going to create another email course, this time about building websites with the <a href=\"https:\/\/sculpin.io\">Sculpin static site generator<\/a>.<\/p>\n\n<p>I've been using Sculpin since 2015 when <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/test-drive-twig-with-sculpin\">I used it to learn Twig<\/a> before Drupal 8's release and I use it to generate my website.<\/p>\n\n<p>I've recently been <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-static-websites-sculpin\">speaking about it at user groups<\/a> like PHP South West, BrumPHP and PHP Berkshire.<\/p>\n\n<p>Similar to my <a href=\"https:\/\/www.oliverdavies.uk\/atdc\">automated testing email course<\/a>, this will send a lesson every day straight to your inbox and will show how to build a website with Sculpin from scratch.<\/p>\n\n<p>If you want to be added when it's ready or have questions, reply to this email and let me know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:23+00:00",
|
||||
"guid": null,
|
||||
"hash": "6b905a72c2e1be12adf25aea58ea044c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0bc4eeae-809a-4e74-9a7c-67f5574a5d17.json
Normal file
100
content/node.0bc4eeae-809a-4e74-9a7c-67f5574a5d17.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0bc4eeae-809a-4e74-9a7c-67f5574a5d17"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Upgrading from Drupal 9 is easier\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-31T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/31\/upgrading-from-drupal-9-is-easier",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Upgrading from Drupal 7 to 8, 9 or 10 is a large task.<\/p>\n\n<p>You must create a new empty site, migrate your configuration and data, manually recreate anything else and rewrite any custom modules and themes.<\/p>\n\n<p>Since Drupal 8, things have been different.<\/p>\n\n<p>You can upgrade your site in place.<\/p>\n\n<p>No large code rewrite or data migration.<\/p>\n\n<p>Many contributed modules support multiple major Drupal versions simultaneously, so they may not need upgrading.<\/p>\n\n<p>Others may need small changes to remove deprecated code or be compatible with breaking changes, and some tools can automate some or all of the changes.<\/p>\n\n<p>The same applies to upgrading from Drupal 9 to 10, which will be the same for Drupal 11 next year.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Upgrading from Drupal 7 to 8, 9 or 10 is a large task.<\/p>\n\n<p>You must create a new empty site, migrate your configuration and data, manually recreate anything else and rewrite any custom modules and themes.<\/p>\n\n<p>Since Drupal 8, things have been different.<\/p>\n\n<p>You can upgrade your site in place.<\/p>\n\n<p>No large code rewrite or data migration.<\/p>\n\n<p>Many contributed modules support multiple major Drupal versions simultaneously, so they may not need upgrading.<\/p>\n\n<p>Others may need small changes to remove deprecated code or be compatible with breaking changes, and some tools can automate some or all of the changes.<\/p>\n\n<p>The same applies to upgrading from Drupal 9 to 10, which will be the same for Drupal 11 next year.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:52+00:00",
|
||||
"guid": null,
|
||||
"hash": "062455710dfbfed24f6c650fa013d504",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0bd8d075-e41f-4ba5-9412-c142576418f3"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Testing is all about confidence\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-24T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/24\/testing-is-all-about-confidence",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Testing - manual or automated - is about building confidence.<\/p>\n\n<p>If we deploy this change or release this feature, are we confident it will work as expected and not cause regressions elsewhere?<\/p>\n\n<p>What if someone asked you on a scale between one and ten?<\/p>\n\n<p>From an automated perspective, have you written enough tests for the feature to be confident it works?<\/p>\n\n<p>If you're fixing a bug, do you have a test that reproduces the bug that was originally failing but now passing since you've added the fix?<\/p>\n\n<p>Do the tests have enough assertions, and have you covered enough use cases and scenarios?<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>You can utilise code coverage metrics, but no hard rule says that the feature will work once x percentage is covered. Something with 100% coverage can still contain bugs.<\/p>\n\n<p>For me, it's about the answer to the question:<\/p>\n\n<p>If we deploy this change, how confident are you that it will work as expected?<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Testing - manual or automated - is about building confidence.<\/p>\n\n<p>If we deploy this change or release this feature, are we confident it will work as expected and not cause regressions elsewhere?<\/p>\n\n<p>What if someone asked you on a scale between one and ten?<\/p>\n\n<p>From an automated perspective, have you written enough tests for the feature to be confident it works?<\/p>\n\n<p>If you're fixing a bug, do you have a test that reproduces the bug that was originally failing but now passing since you've added the fix?<\/p>\n\n<p>Do the tests have enough assertions, and have you covered enough use cases and scenarios?<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>You can utilise code coverage metrics, but no hard rule says that the feature will work once x percentage is covered. Something with 100% coverage can still contain bugs.<\/p>\n\n<p>For me, it's about the answer to the question:<\/p>\n\n<p>If we deploy this change, how confident are you that it will work as expected?<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "60c550711c51c2b739051e4640eba954",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0bde3c8b-1a55-4f36-9a2e-ec5834771482"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:05+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Creating a small proof-of-concept application in an afternoon\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-11-11T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:05+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/11\/12\/creating-small-proof-of-concept-application-afternoon",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>This morning, I was asked a \u201cCould you build\u2026\u201d question.<\/p>\n\n<p>It was an idea mentioned a short while ago and involves a simple, interactive form on the front end that sends requests to a public API, filters the results from the response and displays them to the user.<\/p>\n\n<p>I\u2019d probably want to hide the API request behind a service responsible for interacting with the API and filtering the results - ensuring that the API could be switched with something else later if needed.<\/p>\n\n<p>This afternoon, I built a small proof-of-concept application with Vue.js and TypeScript.<\/p>\n\n<p>There\u2019s no API, or service retrieving real-time results. All of the data is hard-coded within the App component, as well as the code that filters, sorts and returns the results.<\/p>\n\n<p>The results are shown by adding a <code><pre><\/pre><\/code> to the page, with a <code><pre><\/pre><\/code> to show the input data.<\/p>\n\n<p>There isn\u2019t even any styling, with just some basic horizontal rules to split the page - similar to <a href=\"https:\/\/twitter.com\/taylorotwell\/status\/1203356860818087944\">these screenshots from Taylor Otwell<\/a> of some work-in-progress versions of Vapor and Nova.<\/p>\n\n<p>A working proof of concept, or a \"spike\", answers the initial \"Can we build...\" question. It can be shown to a client or other stakeholders, act as a starting point for discussions and requirements gathering and then be turned into user stories. It also allows the Developers to validate their initial thoughts and experiment with different approaches.<\/p>\n\n<p>If the spike is successful, the idea can then be moved forward and implemented in a full way, otherwise, it can be stopped with a minimal amount of effort and time.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>This morning, I was asked a \u201cCould you build\u2026\u201d question.<\/p>\n\n<p>It was an idea mentioned a short while ago and involves a simple, interactive form on the front end that sends requests to a public API, filters the results from the response and displays them to the user.<\/p>\n\n<p>I\u2019d probably want to hide the API request behind a service responsible for interacting with the API and filtering the results - ensuring that the API could be switched with something else later if needed.<\/p>\n\n<p>This afternoon, I built a small proof-of-concept application with Vue.js and TypeScript.<\/p>\n\n<p>There\u2019s no API, or service retrieving real-time results. All of the data is hard-coded within the App component, as well as the code that filters, sorts and returns the results.<\/p>\n\n<p>The results are shown by adding a <code><pre><\/pre><\/code> to the page, with a <code><pre><\/pre><\/code> to show the input data.<\/p>\n\n<p>There isn\u2019t even any styling, with just some basic horizontal rules to split the page - similar to <a href=\"https:\/\/twitter.com\/taylorotwell\/status\/1203356860818087944\">these screenshots from Taylor Otwell<\/a> of some work-in-progress versions of Vapor and Nova.<\/p>\n\n<p>A working proof of concept, or a \"spike\", answers the initial \"Can we build...\" question. It can be shown to a client or other stakeholders, act as a starting point for discussions and requirements gathering and then be turned into user stories. It also allows the Developers to validate their initial thoughts and experiment with different approaches.<\/p>\n\n<p>If the spike is successful, the idea can then be moved forward and implemented in a full way, otherwise, it can be stopped with a minimal amount of effort and time.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:05+00:00",
|
||||
"guid": null,
|
||||
"hash": "f378ea534b4141dc8290191f8941df2c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0c2bb4b6-9a7e-4a4c-99d7-76dfbb8a8a2b.json
Normal file
100
content/node.0c2bb4b6-9a7e-4a4c-99d7-76dfbb8a8a2b.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0c2bb4b6-9a7e-4a4c-99d7-76dfbb8a8a2b"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "De-jargoning Drupal"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-09-18T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:25+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/09\/18\/de-jargoning-drupal",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>This week, I learned there is a Drupalisms Working Group - a group focused on de-jargoning Drupal and making it easier for newcomers to Drupal by removing some of the Drupal-specific language.<\/p>\n\n<p>From <a href=\"https:\/\/www.drupal.org\/association\/blog\/de-jargoning-drupal-working-with-the-community-to-open-up-drupals-terminology\">the introductory blog post<\/a>:<\/p>\n\n<blockquote>\n <p>If you\u2019re familiar with Drupal, you will have learned its language. You will be familiar with words like Views, Blocks and Paragraphs, and you will appreciate their respective features and functions. But for those new to Drupal, getting to grips with what words mean can mean a steep learning curve.<\/p>\n<\/blockquote>\n\n<p>Drupalisms is something I've discussed on a few episodes of Beyond Blocks, including <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/21-eirik-morland-violinist-2\">the most recent episode and the seonc with Eirik Morland<\/a>.<\/p>\n\n<p>I didn't realise there were BoF sessions about this at DrupalCon Lille last year, so I'm hoping there will be more next week in Barcelona.<\/p>\n\n<p>Anything that helps Drupal easier to use and adopt is a good thing.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>This week, I learned there is a Drupalisms Working Group - a group focused on de-jargoning Drupal and making it easier for newcomers to Drupal by removing some of the Drupal-specific language.<\/p>\n\n<p>From <a href=\"https:\/\/www.drupal.org\/association\/blog\/de-jargoning-drupal-working-with-the-community-to-open-up-drupals-terminology\">the introductory blog post<\/a>:<\/p>\n\n<blockquote>\n <p>If you\u2019re familiar with Drupal, you will have learned its language. You will be familiar with words like Views, Blocks and Paragraphs, and you will appreciate their respective features and functions. But for those new to Drupal, getting to grips with what words mean can mean a steep learning curve.<\/p>\n<\/blockquote>\n\n<p>Drupalisms is something I've discussed on a few episodes of Beyond Blocks, including <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/21-eirik-morland-violinist-2\">the most recent episode and the seonc with Eirik Morland<\/a>.<\/p>\n\n<p>I didn't realise there were BoF sessions about this at DrupalCon Lille last year, so I'm hoping there will be more next week in Barcelona.<\/p>\n\n<p>Anything that helps Drupal easier to use and adopt is a good thing.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:25+00:00",
|
||||
"guid": null,
|
||||
"hash": "e8fab9a9c586506079edcd257eb1d672",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0c459aee-d009-4cd4-9fa6-f6a856a23b6b.json
Normal file
100
content/node.0c459aee-d009-4cd4-9fa6-f6a856a23b6b.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0c459aee-d009-4cd4-9fa6-f6a856a23b6b"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "The contribution-first workflow\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-12-01T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:42+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/12\/01\/the-contribution-first-workflow",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I've worked on many software projects with a lot of custom code.<\/p>\n\n<p>Not all the code is specific to that client or project, and often, code is identified as it can be extracted from the project and open-sourced as a Drupal module, PHP or JavaScript library, or Tailwind CSS plugin.<\/p>\n\n<p>Usually, the code is written as custom code initially, with the best intentions to revisit it once the project is complete and open-source it.<\/p>\n\n<p>But this rarely happens, as there's always the next sprint or project waiting.<\/p>\n\n<p>It takes too long to extract the code as it usually needs to be tidied or refactored beforehand.<\/p>\n\n<p>It may have been written with the client or project name within the code, which needs changing.<\/p>\n\n<p>My suggestion is to avoid this step.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Instead of writing it as custom code and hopefully extracting it later, start it as a separate module, library or plugin, and use Composer or npm to add it to your project as another dependency.<\/p>\n\n<p>Whilst it's a slightly smaller overhead, it's better and less risky than rewriting or refactoring code later and it's already open-sourced.<\/p>\n\n<p>Plus, you may get some issues, testing and improvements from others along the way.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I've worked on many software projects with a lot of custom code.<\/p>\n\n<p>Not all the code is specific to that client or project, and often, code is identified as it can be extracted from the project and open-sourced as a Drupal module, PHP or JavaScript library, or Tailwind CSS plugin.<\/p>\n\n<p>Usually, the code is written as custom code initially, with the best intentions to revisit it once the project is complete and open-source it.<\/p>\n\n<p>But this rarely happens, as there's always the next sprint or project waiting.<\/p>\n\n<p>It takes too long to extract the code as it usually needs to be tidied or refactored beforehand.<\/p>\n\n<p>It may have been written with the client or project name within the code, which needs changing.<\/p>\n\n<p>My suggestion is to avoid this step.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Instead of writing it as custom code and hopefully extracting it later, start it as a separate module, library or plugin, and use Composer or npm to add it to your project as another dependency.<\/p>\n\n<p>Whilst it's a slightly smaller overhead, it's better and less risky than rewriting or refactoring code later and it's already open-sourced.<\/p>\n\n<p>Plus, you may get some issues, testing and improvements from others along the way.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:42+00:00",
|
||||
"guid": null,
|
||||
"hash": "1489f86d52518013ecedf7b402c2c07e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0c92ae14-fa57-4daf-a5f9-20b1354346fa"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Should I wait to upgrade from Drupal 7?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-29T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/29\/should-i-wait-to-upgrade-from-drupal-7",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>It was announced at DrupalCon that Drupal 7 support was being extended one final time until January 2025.<\/p>\n\n<p>But if you have a Drupal 7 website, does that mean you should wait to start upgrading it?<\/p>\n\n<p>I recommend starting the process as soon as possible.<\/p>\n\n<p>Even though Drupal core support is extended, I've looked at projects that use modules marked as unsupported by their maintainers for some time as they focus on versions for Drupal 8, 9 or 10.<\/p>\n\n<p>In that case, those modules will have no new features, bug fixes or security updates, although Drupal core support has been extended.<\/p>\n\n<p>You may have a lot of custom code that needs to be ported to Drupal 10 or a complex data structure that needs to be migrated, These things will take time, so it's best not to leave it until the last minute.<\/p>\n\n<p>If you're stuck on Drupal 7, book an <a href=\"http:\/\/localhost:8000\/call\">upgrade consultation call<\/a> with me or purchase an <a href=\"http:\/\/localhost:8000\/drupal7\">upgrade roadmap for your project<\/a> and I'll get you unstuck.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>It was announced at DrupalCon that Drupal 7 support was being extended one final time until January 2025.<\/p>\n\n<p>But if you have a Drupal 7 website, does that mean you should wait to start upgrading it?<\/p>\n\n<p>I recommend starting the process as soon as possible.<\/p>\n\n<p>Even though Drupal core support is extended, I've looked at projects that use modules marked as unsupported by their maintainers for some time as they focus on versions for Drupal 8, 9 or 10.<\/p>\n\n<p>In that case, those modules will have no new features, bug fixes or security updates, although Drupal core support has been extended.<\/p>\n\n<p>You may have a lot of custom code that needs to be ported to Drupal 10 or a complex data structure that needs to be migrated, These things will take time, so it's best not to leave it until the last minute.<\/p>\n\n<p>If you're stuck on Drupal 7, book an <a href=\"http:\/\/localhost:8000\/call\">upgrade consultation call<\/a> with me or purchase an <a href=\"http:\/\/localhost:8000\/drupal7\">upgrade roadmap for your project<\/a> and I'll get you unstuck.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "591efe5fb3861d0c68d3b83fffa1da68",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0c9750c0-7050-4f7e-ac67-b896554df0f1"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "People read more code than they write"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-08-07T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/08\/07\/people-read-more-code-than-they-write",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Which do you do more?<\/p>\n\n<p>Read code or write code?<\/p>\n\n<p>If you include colleagues' code, logs and CI pipeline output, open-source software, books, courses, tutorials, examples, videos, live streams, meetup and conference talks and blog posts (just to think of some), you definitely read more code than you write.<\/p>\n\n<p>Which is why it's important to <a href=\"http:\/\/localhost:8000\/daily\/2024\/08\/06\/computers-dont-care\">optimise for readability<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Which do you do more?<\/p>\n\n<p>Read code or write code?<\/p>\n\n<p>If you include colleagues' code, logs and CI pipeline output, open-source software, books, courses, tutorials, examples, videos, live streams, meetup and conference talks and blog posts (just to think of some), you definitely read more code than you write.<\/p>\n\n<p>Which is why it's important to <a href=\"http:\/\/localhost:8000\/daily\/2024\/08\/06\/computers-dont-care\">optimise for readability<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "b71f49a1681732644d719090945fd2d3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ce24277-65f4-49a0-8325-c7fce30d93b5"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Drupal Commerce: not just for selling t-shirts and hats"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-03-19T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/03\/19\/drupal-commerce-not-just-for-selling-t-shirts-and-hats",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I recently had Ryan Szrama as a guest on the <a href=\"http:\/\/localhost:8000\/podcast\/13-ryan-szrama-centarro\">Beyond Blocks podcast<\/a>.<\/p>\n\n<p>Ryan is the CEO of Centarro - the company behind Drupal Commerce, the eCommerce platform built on the Drupal CMS.<\/p>\n\n<p>I've used Drupal Commerce for a number of projects since it was released in 2011, as well as Ubercart before that.<\/p>\n\n<p>One of the major things I like about it is its flexibility.<\/p>\n\n<p>The Commerce Kickstart distribution is a great way to create a demo Drupal Commerce project that shows a typical eCommerce store selling everything from books to hats, furniture and inflatable flamingos.<\/p>\n\n<p>I've used Drupal Commerce for these typical scenarios but also for some non-typical ones.<\/p>\n\n<p>I created a multi-site Drupal Commerce store for a gadget insurance company, dealing with many products and product variations. I built a custom Vue.js form that created an order with the required items before passing customers to a Drupal Commerce checkout flow.<\/p>\n\n<p>I created a yearly photography competition website that photographers can enter by purchasing a product and uploading their photographs to the order. I built custom judging functionality, which allows jurors to score each entry and the site owner to see the totals and which submission won the competition.<\/p>\n\n<p>I created an events management and booking website where each event was a product with different variations based on the different prices - early bird, regular and last minute. Each event had a maximum number of places and, potentially, a waitlist.<\/p>\n\n<p>This website also included a loyalty scheme for event organisers and attendees, who received coupons after organising or attending a certain number of events.<\/p>\n\n<p>Drupal Commerce can do a lot and isn't just selling t-shirts, hats, books or furniture.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I recently had Ryan Szrama as a guest on the <a href=\"http:\/\/localhost:8000\/podcast\/13-ryan-szrama-centarro\">Beyond Blocks podcast<\/a>.<\/p>\n\n<p>Ryan is the CEO of Centarro - the company behind Drupal Commerce, the eCommerce platform built on the Drupal CMS.<\/p>\n\n<p>I've used Drupal Commerce for a number of projects since it was released in 2011, as well as Ubercart before that.<\/p>\n\n<p>One of the major things I like about it is its flexibility.<\/p>\n\n<p>The Commerce Kickstart distribution is a great way to create a demo Drupal Commerce project that shows a typical eCommerce store selling everything from books to hats, furniture and inflatable flamingos.<\/p>\n\n<p>I've used Drupal Commerce for these typical scenarios but also for some non-typical ones.<\/p>\n\n<p>I created a multi-site Drupal Commerce store for a gadget insurance company, dealing with many products and product variations. I built a custom Vue.js form that created an order with the required items before passing customers to a Drupal Commerce checkout flow.<\/p>\n\n<p>I created a yearly photography competition website that photographers can enter by purchasing a product and uploading their photographs to the order. I built custom judging functionality, which allows jurors to score each entry and the site owner to see the totals and which submission won the competition.<\/p>\n\n<p>I created an events management and booking website where each event was a product with different variations based on the different prices - early bird, regular and last minute. Each event had a maximum number of places and, potentially, a waitlist.<\/p>\n\n<p>This website also included a loyalty scheme for event organisers and attendees, who received coupons after organising or attending a certain number of events.<\/p>\n\n<p>Drupal Commerce can do a lot and isn't just selling t-shirts, hats, books or furniture.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "8974a98575ca24f1c5bc317715f49365",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0cee44e0-f233-406c-ab11-250a71c03c48"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "You can do utility-first CSS with Sass"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-07-09T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/07\/09\/you-can-do-utility-first-css-with-sass",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Yesterday, I said that <a href=\"http:\/\/localhost:8000\/daily\/2024\/07\/08\/back-to-sass-and-traditional-css\">I'm working on a Sass project with no utility or atomic styles<\/a>.<\/p>\n\n<p>But, the two aren't mutually exclusive.<\/p>\n\n<p>You can do both.<\/p>\n\n<p>You can write your own utility classes, like <code>flex<\/code>, <code>font-bold<\/code> or <code>text-red<\/code> in Sass or plain CSS.<\/p>\n\n<p>You can use a framework like Tailwind CSS, but you don't need to.<\/p>\n\n<p>In some projects, with existing stylesheets and usually other frameworks, you can't add anothe full framework without having unintended consequences.<\/p>\n\n<p>Usually, if I want to introduce utility classes to an existing project, I start by writing my own that are inspired by a framework such as Tailwind CSS and maybe refactor to the framework later once the concept has been introduced and the codebase is able to work with it.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Yesterday, I said that <a href=\"http:\/\/localhost:8000\/daily\/2024\/07\/08\/back-to-sass-and-traditional-css\">I'm working on a Sass project with no utility or atomic styles<\/a>.<\/p>\n\n<p>But, the two aren't mutually exclusive.<\/p>\n\n<p>You can do both.<\/p>\n\n<p>You can write your own utility classes, like <code>flex<\/code>, <code>font-bold<\/code> or <code>text-red<\/code> in Sass or plain CSS.<\/p>\n\n<p>You can use a framework like Tailwind CSS, but you don't need to.<\/p>\n\n<p>In some projects, with existing stylesheets and usually other frameworks, you can't add anothe full framework without having unintended consequences.<\/p>\n\n<p>Usually, if I want to introduce utility classes to an existing project, I start by writing my own that are inspired by a framework such as Tailwind CSS and maybe refactor to the framework later once the concept has been introduced and the codebase is able to work with it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "b32818061edabf585a8d041c5fc9739b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0d033a94-9e45-4d39-a15a-825678128782"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "An interesting thing I spotted about the Override Node Options module"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-11-16T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:57+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/11\/16\/an-interesting-thing-i-spotted-about-the-override-node-options-module",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Before <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/13\/speaking-at-the-drupal-london-meetup\">my remote talk for the Drupal London meetup<\/a>, I'm updating the usage statistics for <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">the Override Node Options module<\/a> - one of the modules I maintain on Drupal.org.<\/p>\n\n<p>In my slides for DrupalCamp Belgium, I showed the usage figures from October 2023, which showed 38,096 installations and it being the 173rd most installed module.<\/p>\n\n<p>This week, the number of installations has slightly increased to 38,223.<\/p>\n\n<p>What's interesting is that whilst the number of installations has been consistent, there are a lot less Drupal 7 websites using the module and a lot more Drupal 8+ sites using it.<\/p>\n\n<h2 id=\"october-2023\">October 2023<\/h2>\n\n<ul>\n<li>5.x-1.x: 1<\/li>\n<li>6.x-1.x: 297<\/li>\n<li>7.x-1.x: 13,717<\/li>\n<li>8.x-2.x: 24,081<\/li>\n<li>Total: 38,096<\/li>\n<\/ul>\n\n<h2 id=\"november-2024\">November 2024<\/h2>\n\n<ul>\n<li>5.x-1.x: 4<\/li>\n<li>6.x-1.x: 202<\/li>\n<li>7.x-1.x: 10,429<\/li>\n<li>8.x-2.x: 27,588<\/li>\n<li>Total: 38,223<\/li>\n<\/ul>\n\n<p>Assuming these numbers are correct, this makes me feel very positive and happy about the adoption of newer versions of Drupal and that people are upgrading their D7 websites to Drupal 10 or 11.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Before <a href=\"http:\/\/localhost:8000\/daily\/2024\/11\/13\/speaking-at-the-drupal-london-meetup\">my remote talk for the Drupal London meetup<\/a>, I'm updating the usage statistics for <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">the Override Node Options module<\/a> - one of the modules I maintain on Drupal.org.<\/p>\n\n<p>In my slides for DrupalCamp Belgium, I showed the usage figures from October 2023, which showed 38,096 installations and it being the 173rd most installed module.<\/p>\n\n<p>This week, the number of installations has slightly increased to 38,223.<\/p>\n\n<p>What's interesting is that whilst the number of installations has been consistent, there are a lot less Drupal 7 websites using the module and a lot more Drupal 8+ sites using it.<\/p>\n\n<h2 id=\"october-2023\">October 2023<\/h2>\n\n<ul>\n<li>5.x-1.x: 1<\/li>\n<li>6.x-1.x: 297<\/li>\n<li>7.x-1.x: 13,717<\/li>\n<li>8.x-2.x: 24,081<\/li>\n<li>Total: 38,096<\/li>\n<\/ul>\n\n<h2 id=\"november-2024\">November 2024<\/h2>\n\n<ul>\n<li>5.x-1.x: 4<\/li>\n<li>6.x-1.x: 202<\/li>\n<li>7.x-1.x: 10,429<\/li>\n<li>8.x-2.x: 27,588<\/li>\n<li>Total: 38,223<\/li>\n<\/ul>\n\n<p>Assuming these numbers are correct, this makes me feel very positive and happy about the adoption of newer versions of Drupal and that people are upgrading their D7 websites to Drupal 10 or 11.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:57+00:00",
|
||||
"guid": null,
|
||||
"hash": "f0490b5fa42d0058d6f97d5177d6648d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0d710a10-9959-4e55-b1c6-c5c41c20d492"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "It depends\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-11-07T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:01+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/11\/07\/it-depends",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Usually, in software development, there isn't always a definitive black-and-white answer to a question or situation.<\/p>\n\n<p>Most of the time, the answer is \"it depends\".<\/p>\n\n<p>How you approach a problem depends on context.<\/p>\n\n<p>How long do you have?<\/p>\n\n<p>Are you working on the final version or a prototype or minimum-viable product?<\/p>\n\n<p>Should you use a contributed module or write one yourself?<\/p>\n\n<p>What if an existing module hasn't been updated for some time or doesn't have tests or other quality checks included?<\/p>\n\n<p>Do you write custom CSS or use a framework like Tailwind CSS or Bootstrap?<\/p>\n\n<p>Should this project be written in this framework or CMS, or would a different one be better suited?<\/p>\n\n<h2 id=\"here%27s-the-thing...\">Here's the thing...<\/h2>\n\n<p>There are usually multiple approaches to achieve the same result.<\/p>\n\n<p>Decisions will depend on a combination of various factors. In a different situation, the answer could be different.<\/p>\n\n<p>This doesn't make any solution outright wrong.<\/p>\n\n<p>It was right given the situation.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Usually, in software development, there isn't always a definitive black-and-white answer to a question or situation.<\/p>\n\n<p>Most of the time, the answer is \"it depends\".<\/p>\n\n<p>How you approach a problem depends on context.<\/p>\n\n<p>How long do you have?<\/p>\n\n<p>Are you working on the final version or a prototype or minimum-viable product?<\/p>\n\n<p>Should you use a contributed module or write one yourself?<\/p>\n\n<p>What if an existing module hasn't been updated for some time or doesn't have tests or other quality checks included?<\/p>\n\n<p>Do you write custom CSS or use a framework like Tailwind CSS or Bootstrap?<\/p>\n\n<p>Should this project be written in this framework or CMS, or would a different one be better suited?<\/p>\n\n<h2 id=\"here%27s-the-thing...\">Here's the thing...<\/h2>\n\n<p>There are usually multiple approaches to achieve the same result.<\/p>\n\n<p>Decisions will depend on a combination of various factors. In a different situation, the answer could be different.<\/p>\n\n<p>This doesn't make any solution outright wrong.<\/p>\n\n<p>It was right given the situation.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:01+00:00",
|
||||
"guid": null,
|
||||
"hash": "f40826212e5c798e718edd8ad155d3c0",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0d8ce62f-3e06-4e92-bc18-b0b4995f378b.json
Normal file
100
content/node.0d8ce62f-3e06-4e92-bc18-b0b4995f378b.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0d8ce62f-3e06-4e92-bc18-b0b4995f378b"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "drush deploy"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-11-18T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/11\/18\/drush-deploy",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>This week I learned about the <a href=\"https:\/\/www.drush.org\/13.x\/deploycommand\">drush deploy command<\/a>.<\/p>\n\n<p>It's been available since Drush 10.3 and combines several commands together in an attempt to \"standardise how Drupal deployments work\".<\/p>\n\n<p>These commands are:<\/p>\n\n<pre><code class=\"shell\">drush updatedb\ndrush config:import\ndrush cache:rebuild\ndrush deploy:hook\n<\/code><\/pre>\n\n<p>Usually, I run these commands separately but grouping them and standardising makes a lot of sense.<\/p>\n\n<p><code>drush deploy:hook<\/code> is also very interesting and allows for adding deploy hooks similar to update hooks in .install files.<\/p>\n\n<p>I'm looking forward to updating my deployment pipelines to use this and making them simpler and easier to maintain.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>This week I learned about the <a href=\"https:\/\/www.drush.org\/13.x\/deploycommand\">drush deploy command<\/a>.<\/p>\n\n<p>It's been available since Drush 10.3 and combines several commands together in an attempt to \"standardise how Drupal deployments work\".<\/p>\n\n<p>These commands are:<\/p>\n\n<pre><code class=\"shell\">drush updatedb\ndrush config:import\ndrush cache:rebuild\ndrush deploy:hook\n<\/code><\/pre>\n\n<p>Usually, I run these commands separately but grouping them and standardising makes a lot of sense.<\/p>\n\n<p><code>drush deploy:hook<\/code> is also very interesting and allows for adding deploy hooks similar to update hooks in .install files.<\/p>\n\n<p>I'm looking forward to updating my deployment pipelines to use this and making them simpler and easier to maintain.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:23+00:00",
|
||||
"guid": null,
|
||||
"hash": "4720dadf7849019a7ca4401ddec374be",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0dafdc61-c714-4581-8485-e6b9e8888629"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Bootcamps, communities and first Developer jobs"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-06-30T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:58+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/06\/30\/bootcamps--communities-and-first-developer-jobs",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Tomorrow is the first day of cohort 17 of School of Code - a free 16-week software development bootcamp - and I'm happy to be mentoring a student again for my third time.<\/p>\n\n<p>I also mentored the winning team at a hack day event in January and <a href=\"http:\/\/localhost:8000\/presentations\/communities-contribution\">spoke at the TechConnect meetup<\/a> last year.<\/p>\n\n<p>Last week, I spoke with George Gordon - a recent School of Code graduate who I met at the hack day and later at another meetup I was speaking at.<\/p>\n\n<p>We discussed his experience with coding bootcamps, getting into the software industry and involved with community events and how he found his first Developer job!<\/p>\n\n<p>It will be released soon on <a href=\"http:\/\/localhost:8000\/podcast\">the Beyond Blocks podcast page<\/a>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Tomorrow is the first day of cohort 17 of School of Code - a free 16-week software development bootcamp - and I'm happy to be mentoring a student again for my third time.<\/p>\n\n<p>I also mentored the winning team at a hack day event in January and <a href=\"http:\/\/localhost:8000\/presentations\/communities-contribution\">spoke at the TechConnect meetup<\/a> last year.<\/p>\n\n<p>Last week, I spoke with George Gordon - a recent School of Code graduate who I met at the hack day and later at another meetup I was speaking at.<\/p>\n\n<p>We discussed his experience with coding bootcamps, getting into the software industry and involved with community events and how he found his first Developer job!<\/p>\n\n<p>It will be released soon on <a href=\"http:\/\/localhost:8000\/podcast\">the Beyond Blocks podcast page<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:58+00:00",
|
||||
"guid": null,
|
||||
"hash": "e106e4056daf94f25e5ee0b03bcef5a9",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0db14d77-200d-4ea6-a1ff-76df07df7864.json
Normal file
100
content/node.0db14d77-200d-4ea6-a1ff-76df07df7864.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0db14d77-200d-4ea6-a1ff-76df07df7864"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Override Node Options is used on 40,624 Drupal websites"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-03-09T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:36+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/03\/09\/override-node-options-40624-drupal-websites",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>In my <a href=\"https:\/\/www.oliverdavies.uk\/talks\/tdd-test-driven-drupal\">Test-Driven Drupal talk slides<\/a>, I show a screenshot of a tweet from February 2012 by Tim Millwood asking if anyone wanted to maintain the Override Node Options module.<\/p>\n\n<p>I said yes and took over maintainership.<\/p>\n\n<p>In April 2012, the module was installed on 9,212 Drupal websites - just over 7,000 Drupal 6 websites and just over 2,000 Drupal 7 websites.<\/p>\n\n<p>Before DrupalCon Lille, the module was used on 38,096 websites and was 173rd in the most used modules.<\/p>\n\n<p>As of the 3rd of March, the Override Node Options module is used on 40,624 Drupal websites and is up to the 169th most used module.<\/p>\n\n<p>That's pretty cool!<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>In my <a href=\"https:\/\/www.oliverdavies.uk\/talks\/tdd-test-driven-drupal\">Test-Driven Drupal talk slides<\/a>, I show a screenshot of a tweet from February 2012 by Tim Millwood asking if anyone wanted to maintain the Override Node Options module.<\/p>\n\n<p>I said yes and took over maintainership.<\/p>\n\n<p>In April 2012, the module was installed on 9,212 Drupal websites - just over 7,000 Drupal 6 websites and just over 2,000 Drupal 7 websites.<\/p>\n\n<p>Before DrupalCon Lille, the module was used on 38,096 websites and was 173rd in the most used modules.<\/p>\n\n<p>As of the 3rd of March, the Override Node Options module is used on 40,624 Drupal websites and is up to the 169th most used module.<\/p>\n\n<p>That's pretty cool!<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:36+00:00",
|
||||
"guid": null,
|
||||
"hash": "ee421350e08042bf1dc38655f1c7358b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0e278570-ecee-419f-83c5-f26596d2c521"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Just use curl"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-01-10T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/01\/10\/curl",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I don't use complicated or bloated applications to test HTTP requests and API endpoints.<\/p>\n\n<p>I just use <code>curl<\/code> on my command line.<\/p>\n\n<p>For example, if I want to query the Drupal.org API for my user information, I can run <code>curl https:\/\/www.drupal.org\/api-d7\/user.json?uid=381388<\/code> and see the response.<\/p>\n\n<p>To see the request and response headers, status code, SSL certificate information and more, I can run <code>curl -v<\/code> to run it in verbose mode.<\/p>\n\n<p>If the response returns JSON, I can use <code>jq<\/code> to format the results.<\/p>\n\n<p>If I need to make a POST request, I can use <code>-X POST<\/code>, I can use <code>--data<\/code> or <code>--json<\/code> to send data and <code>--header<\/code> to send any required headers.<\/p>\n\n<p>curl is great program with so many options and no AI bloat, complicated UIs or paid plans.<\/p>\n\n<p>Want to see what else it can do?<\/p>\n\n<p>Just open your terminal and type <code>man curl<\/code>.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I don't use complicated or bloated applications to test HTTP requests and API endpoints.<\/p>\n\n<p>I just use <code>curl<\/code> on my command line.<\/p>\n\n<p>For example, if I want to query the Drupal.org API for my user information, I can run <code>curl https:\/\/www.drupal.org\/api-d7\/user.json?uid=381388<\/code> and see the response.<\/p>\n\n<p>To see the request and response headers, status code, SSL certificate information and more, I can run <code>curl -v<\/code> to run it in verbose mode.<\/p>\n\n<p>If the response returns JSON, I can use <code>jq<\/code> to format the results.<\/p>\n\n<p>If I need to make a POST request, I can use <code>-X POST<\/code>, I can use <code>--data<\/code> or <code>--json<\/code> to send data and <code>--header<\/code> to send any required headers.<\/p>\n\n<p>curl is great program with so many options and no AI bloat, complicated UIs or paid plans.<\/p>\n\n<p>Want to see what else it can do?<\/p>\n\n<p>Just open your terminal and type <code>man curl<\/code>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "071392af7e184fa125a44531711fdfde",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0e9497b7-171a-4509-910b-40d18d62e134"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Drupalisms and de-jargoning Drupal"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-02-14T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:56+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/02\/14\/drupalisms",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>After the topic of Drupalisms and Drupal jargon came up in previous podcast episodes, I was happy to speak with Emma Horrell and Luke McCormick.<\/p>\n\n<p>We discussed the work happening in Drupal and Drupal CMS to de-jargon Drupal, the Drupalisms working group.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/27-drupalisms\">Listen to the episode here<\/a>.<\/p>\n\n<p>If you want to be a guest on the <a href=\"http:\/\/localhost:8000\/podcast\">Beyond Blocks podcast<\/a>, reply and let me know.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>After the topic of Drupalisms and Drupal jargon came up in previous podcast episodes, I was happy to speak with Emma Horrell and Luke McCormick.<\/p>\n\n<p>We discussed the work happening in Drupal and Drupal CMS to de-jargon Drupal, the Drupalisms working group.<\/p>\n\n<p><a href=\"http:\/\/localhost:8000\/podcast\/27-drupalisms\">Listen to the episode here<\/a>.<\/p>\n\n<p>If you want to be a guest on the <a href=\"http:\/\/localhost:8000\/podcast\">Beyond Blocks podcast<\/a>, reply and let me know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:12:56+00:00",
|
||||
"guid": null,
|
||||
"hash": "bffebd32dd75bb3cc31ae5a5a69bff5d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ea40f58-e645-4187-996b-3e45c1550d6a"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "One more \"run\" command, for Git worktrees"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-08-17T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:07+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/08\/17\/one-more-run-command-git-worktrees",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Here's another <code>run<\/code> file example, this time relating to Git worktrees...<\/p>\n\n<p>One project that I work on is a multilingual Drupal application that needs to work in both English and Welsh. As I'm cloning a fresh version today, I'm doing it as a bare repository so I can use worktrees.<\/p>\n\n<p>To work on it locally, just like in production, I need to use a different URL for each language so that Drupal can identify it and load the correct content and configuration.<\/p>\n\n<p>For fixed environments like production or staging, the URLs are set in configuration files, but for ad-hoc environments such as local worktrees, I thought that the best approach was to override them as needed per worktree using Drush (a Drupal CLI tool).<\/p>\n\n<p>I could do this manually each time or I could automate it in a <code>run<\/code> command. :)<\/p>\n\n<p>Here's the function that I came up with:<\/p>\n\n<pre><code class=\"bash\">function drupal:set-urls-for-worktree {\n # Set the site URLs based on the current Git worktree name.\n local worktree_name=\"$(basename $PWD)\"\n\n local cy_url=\"cy-projectname-${worktree_name}.docker.localhost\"\n local en_url=\"projectname-${worktree_name}.docker.localhost\"\n\n # Update the URLs.\n drush config:set language.negotiation url.domains.cy -y $cy_url\n drush config:set language.negotiation url.domains.en -y $en_url\n\n # Display the domains configuration to ensure that they were set correctly.\n drush config:get language.negotiation url.domains\n}\n<\/code><\/pre>\n\n<p>It builds the worktree URL for each language based on the directory name, executes the configuration change, and finally displays the updated configuration so I can confirm that it's been set correctly.<\/p>\n\n<p>This is a good example of why I like using <code>run<\/code> files and how I use them to automate and simplify parts of my workflow.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Here's another <code>run<\/code> file example, this time relating to Git worktrees...<\/p>\n\n<p>One project that I work on is a multilingual Drupal application that needs to work in both English and Welsh. As I'm cloning a fresh version today, I'm doing it as a bare repository so I can use worktrees.<\/p>\n\n<p>To work on it locally, just like in production, I need to use a different URL for each language so that Drupal can identify it and load the correct content and configuration.<\/p>\n\n<p>For fixed environments like production or staging, the URLs are set in configuration files, but for ad-hoc environments such as local worktrees, I thought that the best approach was to override them as needed per worktree using Drush (a Drupal CLI tool).<\/p>\n\n<p>I could do this manually each time or I could automate it in a <code>run<\/code> command. :)<\/p>\n\n<p>Here's the function that I came up with:<\/p>\n\n<pre><code class=\"bash\">function drupal:set-urls-for-worktree {\n # Set the site URLs based on the current Git worktree name.\n local worktree_name=\"$(basename $PWD)\"\n\n local cy_url=\"cy-projectname-${worktree_name}.docker.localhost\"\n local en_url=\"projectname-${worktree_name}.docker.localhost\"\n\n # Update the URLs.\n drush config:set language.negotiation url.domains.cy -y $cy_url\n drush config:set language.negotiation url.domains.en -y $en_url\n\n # Display the domains configuration to ensure that they were set correctly.\n drush config:get language.negotiation url.domains\n}\n<\/code><\/pre>\n\n<p>It builds the worktree URL for each language based on the directory name, executes the configuration change, and finally displays the updated configuration so I can confirm that it's been set correctly.<\/p>\n\n<p>This is a good example of why I like using <code>run<\/code> files and how I use them to automate and simplify parts of my workflow.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:07+00:00",
|
||||
"guid": null,
|
||||
"hash": "1d0346a7d6dd5cac3b65b148563e58b2",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0eac413c-fb28-4640-ad04-9d4a52399677.json
Normal file
100
content/node.0eac413c-fb28-4640-ad04-9d4a52399677.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0eac413c-fb28-4640-ad04-9d4a52399677"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "How much would it cost to build Drupal?"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-03-03T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:17+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2025\/03\/03\/cost",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:17+00:00",
|
||||
"guid": null,
|
||||
"hash": "3b2f48d28cdda255b884d631a4a170ad",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -1,100 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ec2ce4a-6ab7-4edb-b23a-48d91780da7c"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't put business logic in templates"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-01-10T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-16T14:13:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/01\/10\/dont-put-business-logic-in-templates",
|
||||
"langcode": "en"
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Here is some code from my website:<\/p>\n\n<p><img src=\"http:\/\/localhost:8000\/assets\/images\/talk-count-code.png\" alt=\"A screenshot of the code that calculates the number of talks I've given.\" \/><\/p>\n\n<p>If you want, you can also <a href=\"https:\/\/raw.githubusercontent.com\/opdavies\/oliverdavies.uk\/main\/source\/_pages\/presentations.md\">view it on GitHub<\/a>.<\/p>\n\n<p>It is business logic responsible for counting the number of talks I've given at different events so I can display it on my Talks page.<\/p>\n\n<p>It starts at zero, loops over each talk, and increments the talk count if the event is the current day or a past date.<\/p>\n\n<p>It's only used in a single place, so the same logic isn't duplicated elsewhere.<\/p>\n\n<p>But it's in the page's Twig template.<\/p>\n\n<p>It has no test coverage.<\/p>\n\n<p>If I need to change or refactor it, I'd need to test it again manually.<\/p>\n\n<p>Don't do this.<\/p>\n\n<h2 id=\"so%2C-what-should-i-do%3F\">So, what should I do?<\/h2>\n\n<p>It's OK to put simple presentational logic, such as looping over a list or whether to show or hide a value within a template, but not complex business logic.<\/p>\n\n<p>Business logic should be separated and executed elsewhere. The values should be passed to the template to be rendered.<\/p>\n\n<p>This makes the business logic easier to test as you can test the logic itself and determine the value passed to the template is correct without being concerned about the templating engine.<\/p>\n\n<p>In an application, you may need to output a value to a template and a terminal. You'd have one source of truth, such as a Service, Action or Command class that calculates the value before passing it to the appropriate output.<\/p>\n\n<p>Once the logic is separated, you only need to test it once.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In a previous version of my website, I did this by creating a custom Twig function.<\/p>\n\n<p>It was as simple as adding <code><\/code> to the template.<\/p>\n\n<p>All the logic was moved from the template to my custom extension.<\/p>\n\n<p>The logic was separated.<\/p>\n\n<p>It had tests.<\/p>\n\n<p>This is the approach I'd take to achieve the same result for a client application.<\/p>\n\n<p>For a client, I want to run a test suite and be confident my logic works as expected - now and in the future.<\/p>\n\n<p>For myself, and for calculating something simple, this is fine.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Here is some code from my website:<\/p>\n\n<p><img src=\"http:\/\/localhost:8000\/assets\/images\/talk-count-code.png\" alt=\"A screenshot of the code that calculates the number of talks I've given.\"><\/p>\n\n<p>If you want, you can also <a href=\"https:\/\/raw.githubusercontent.com\/opdavies\/oliverdavies.uk\/main\/source\/_pages\/presentations.md\">view it on GitHub<\/a>.<\/p>\n\n<p>It is business logic responsible for counting the number of talks I've given at different events so I can display it on my Talks page.<\/p>\n\n<p>It starts at zero, loops over each talk, and increments the talk count if the event is the current day or a past date.<\/p>\n\n<p>It's only used in a single place, so the same logic isn't duplicated elsewhere.<\/p>\n\n<p>But it's in the page's Twig template.<\/p>\n\n<p>It has no test coverage.<\/p>\n\n<p>If I need to change or refactor it, I'd need to test it again manually.<\/p>\n\n<p>Don't do this.<\/p>\n\n<h2 id=\"so%2C-what-should-i-do%3F\">So, what should I do?<\/h2>\n\n<p>It's OK to put simple presentational logic, such as looping over a list or whether to show or hide a value within a template, but not complex business logic.<\/p>\n\n<p>Business logic should be separated and executed elsewhere. The values should be passed to the template to be rendered.<\/p>\n\n<p>This makes the business logic easier to test as you can test the logic itself and determine the value passed to the template is correct without being concerned about the templating engine.<\/p>\n\n<p>In an application, you may need to output a value to a template and a terminal. You'd have one source of truth, such as a Service, Action or Command class that calculates the value before passing it to the appropriate output.<\/p>\n\n<p>Once the logic is separated, you only need to test it once.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>In a previous version of my website, I did this by creating a custom Twig function.<\/p>\n\n<p>It was as simple as adding <code><\/code> to the template.<\/p>\n\n<p>All the logic was moved from the template to my custom extension.<\/p>\n\n<p>The logic was separated.<\/p>\n\n<p>It had tests.<\/p>\n\n<p>This is the approach I'd take to achieve the same result for a client application.<\/p>\n\n<p>For a client, I want to run a test suite and be confident my logic works as expected - now and in the future.<\/p>\n\n<p>For myself, and for calculating something simple, this is fine.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-16T14:13:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "6661315587de91080c79443d9409cf7c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0ed52f6c-937e-49c8-a8fe-a2fbf26d2085.json
Normal file
100
content/node.0ed52f6c-937e-49c8-a8fe-a2fbf26d2085.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ed52f6c-937e-49c8-a8fe-a2fbf26d2085"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "A month of daily emails"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2022-09-12T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:02+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2022\/09\/12\/month-daily-emails",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>It\u2019s already been a month since I started my email list and writing daily emails.<\/p>\n\n<p>Since then, I\u2019ve written emails on various development and workflow-based topics, including Drupal, Git, Docker, Neovim, Ansible and Tailwind CSS.<\/p>\n\n<p>The first email was written on Thursday the 12th of August and after initially wondering whether I should start on the upcoming Monday, or how often to post, I decided to jump in with both feet and wrote the first daily post that day. The first few weren't actually emailed as I waited to see if I could sustain writing a daily post (I was just posting them to my website), but after a few days, I set up the email list and started sending the posts.<\/p>\n\n<p>I can confirm what <a href=\"https:\/\/jonathanstark.com\">Jonathan Stark<\/a> and <a href=\"https:\/\/jhall.io\">Jonathan Hall<\/a> have said - that it's easier to write daily and that you start to see topic ideas everywhere. I started with a list of between 20 and 25 ideas and still have most of them as I've pivoted on a day's topic based on an article or tweet that I saw, some code that I'd written, or some approach that I took.<\/p>\n\n<p>If you're considering starting a daily email list, I'd recommend it.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>It\u2019s already been a month since I started my email list and writing daily emails.<\/p>\n\n<p>Since then, I\u2019ve written emails on various development and workflow-based topics, including Drupal, Git, Docker, Neovim, Ansible and Tailwind CSS.<\/p>\n\n<p>The first email was written on Thursday the 12th of August and after initially wondering whether I should start on the upcoming Monday, or how often to post, I decided to jump in with both feet and wrote the first daily post that day. The first few weren't actually emailed as I waited to see if I could sustain writing a daily post (I was just posting them to my website), but after a few days, I set up the email list and started sending the posts.<\/p>\n\n<p>I can confirm what <a href=\"https:\/\/jonathanstark.com\">Jonathan Stark<\/a> and <a href=\"https:\/\/jhall.io\">Jonathan Hall<\/a> have said - that it's easier to write daily and that you start to see topic ideas everywhere. I started with a list of between 20 and 25 ideas and still have most of them as I've pivoted on a day's topic based on an article or tweet that I saw, some code that I'd written, or some approach that I took.<\/p>\n\n<p>If you're considering starting a daily email list, I'd recommend it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:02+00:00",
|
||||
"guid": null,
|
||||
"hash": "01eb9599a4993e6a38dff0ff03338270",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0ef00c69-d59a-4b10-ba58-e1b1fe3d6ba3.json
Normal file
100
content/node.0ef00c69-d59a-4b10-ba58-e1b1fe3d6ba3.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ef00c69-d59a-4b10-ba58-e1b1fe3d6ba3"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:50+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Vim is my lightsaber\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-08-09T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:50+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/08\/09\/vim-is-my-lightsaber",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>\"Vim is my lightsaber\" is the final chapter of Jess Archer's Neovim course on Laracasts.<\/p>\n\n<p>In this summary, Jess explains that some Star Wars characters have lightsabers customised to their preferences and fighting style.<\/p>\n\n<p>Vim is her lightsaber - her personalised tool to develop software. It works how she wants it to, with the features she needs.<\/p>\n\n<p>I've written before about mastering your tools and thought this was a nice spin on it.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>\"Vim is my lightsaber\" is the final chapter of Jess Archer's Neovim course on Laracasts.<\/p>\n\n<p>In this summary, Jess explains that some Star Wars characters have lightsabers customised to their preferences and fighting style.<\/p>\n\n<p>Vim is her lightsaber - her personalised tool to develop software. It works how she wants it to, with the features she needs.<\/p>\n\n<p>I've written before about mastering your tools and thought this was a nice spin on it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "cab67b9a1e9754bab69a7eb6e764fc33",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0ef26663-fde8-4bf7-a9ad-eb14ca5d5eea.json
Normal file
100
content/node.0ef26663-fde8-4bf7-a9ad-eb14ca5d5eea.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0ef26663-fde8-4bf7-a9ad-eb14ca5d5eea"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Don't use arbitrary values in Tailwind CSS\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-01-02T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:22:00+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/01\/02\/dont-use-arbitrary-values-in-tailwind-css",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>It's been almost five years since I gave the first version of my \"<a href=\"https:\/\/www.oliverdavies.uk\/talks\/taking-flight-with-tailwind-css\">Taking Flight with Tailwind CSS<\/a>\" talk at the Drupal Bristol meetup in January 2018.<\/p>\n\n<p>It's a talk that I've updated every time I've given it, showing new rebuilt example UIs and the new features in the framework, as well as tweaking content like installation steps for different audiences.<\/p>\n\n<p>As I prepare for the Norfolk Developers Conference (nordevcon) next month, I'm updating the talk again.<\/p>\n\n<p>One feature that wasn't in the last version of the talk is arbitrary values in class names. This, I think, is an antipattern and something that I avoid using.<\/p>\n\n<p>Whilst you could always add arbitrary values in custom CSS, one of my original reasons for liking and adopting Tailwind CSS was that there was a predefined list of classes to choose from.<\/p>\n\n<p>A set list of colours, text sizes, margins and padding, for example, meant that UIs looked more consistent and the generated CSS was smaller.<\/p>\n\n<p>If you need an additional value or to replace the existing values, you can do so within your <code>tailwind.config.js<\/code> file, and Tailwind will generate classes accordingly.<\/p>\n\n<p>Whilst writing custom CSS, inline styles, and arbitrary values are all possible, I'd suggest using the configuration file, generating the classes that you need, and sticking to them as much as you can and enjoying the consistency and benefits that brings.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>It's been almost five years since I gave the first version of my \"<a href=\"https:\/\/www.oliverdavies.uk\/talks\/taking-flight-with-tailwind-css\">Taking Flight with Tailwind CSS<\/a>\" talk at the Drupal Bristol meetup in January 2018.<\/p>\n\n<p>It's a talk that I've updated every time I've given it, showing new rebuilt example UIs and the new features in the framework, as well as tweaking content like installation steps for different audiences.<\/p>\n\n<p>As I prepare for the Norfolk Developers Conference (nordevcon) next month, I'm updating the talk again.<\/p>\n\n<p>One feature that wasn't in the last version of the talk is arbitrary values in class names. This, I think, is an antipattern and something that I avoid using.<\/p>\n\n<p>Whilst you could always add arbitrary values in custom CSS, one of my original reasons for liking and adopting Tailwind CSS was that there was a predefined list of classes to choose from.<\/p>\n\n<p>A set list of colours, text sizes, margins and padding, for example, meant that UIs looked more consistent and the generated CSS was smaller.<\/p>\n\n<p>If you need an additional value or to replace the existing values, you can do so within your <code>tailwind.config.js<\/code> file, and Tailwind will generate classes accordingly.<\/p>\n\n<p>Whilst writing custom CSS, inline styles, and arbitrary values are all possible, I'd suggest using the configuration file, generating the classes that you need, and sticking to them as much as you can and enjoying the consistency and benefits that brings.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:22:00+00:00",
|
||||
"guid": null,
|
||||
"hash": "6922005e460c87b7b1c677699162005b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0f083367-b75e-4f01-b1ef-706ff8669d70.json
Normal file
100
content/node.0f083367-b75e-4f01-b1ef-706ff8669d70.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0f083367-b75e-4f01-b1ef-706ff8669d70"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Too many choices?\n"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2023-07-17T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:52+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2023\/07\/17\/too-many-choices",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>I've recently considered moving my infrastructure automation code from Pulumi to Terraform.<\/p>\n\n<p>One of Pulumi's features is that you can write your automation in a programming language instead of a domain-specific language (DSL) with Terraform.<\/p>\n\n<p>As a Developer, this seems appealing, but it poses an important question - which programming language should you use?<\/p>\n\n<p>I've written and re-written Pulumi code in TypeScript and Python and experimented with Go to see which feels best for me.<\/p>\n\n<p>If one of these were my primary language, it would be a no-brainer.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>When I go into my automation repository, I want to write my code as quickly and simply as possible. I don't want to be thinking about how to write it or what language would be best to write it in.<\/p>\n\n<p>Whilst I'd have to learn another DSL for Terraform, it would simplify my options by removing that choice for me, but also if I write automation code and hand it over to a client.<\/p>\n\n<p>It's like taking my children to a restaurant.<\/p>\n\n<p>They'll get overwhelmed if there are too many options on the menu. If we limit the options or order for them, they won't.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>I've recently considered moving my infrastructure automation code from Pulumi to Terraform.<\/p>\n\n<p>One of Pulumi's features is that you can write your automation in a programming language instead of a domain-specific language (DSL) with Terraform.<\/p>\n\n<p>As a Developer, this seems appealing, but it poses an important question - which programming language should you use?<\/p>\n\n<p>I've written and re-written Pulumi code in TypeScript and Python and experimented with Go to see which feels best for me.<\/p>\n\n<p>If one of these were my primary language, it would be a no-brainer.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>When I go into my automation repository, I want to write my code as quickly and simply as possible. I don't want to be thinking about how to write it or what language would be best to write it in.<\/p>\n\n<p>Whilst I'd have to learn another DSL for Terraform, it would simplify my options by removing that choice for me, but also if I write automation code and hand it over to a client.<\/p>\n\n<p>It's like taking my children to a restaurant.<\/p>\n\n<p>They'll get overwhelmed if there are too many options on the menu. If we limit the options or order for them, they won't.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:52+00:00",
|
||||
"guid": null,
|
||||
"hash": "960ab3763a91c3a533d2f22da91bd8fd",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
100
content/node.0f46a1a3-60ab-4b97-8437-8715674fa310.json
Normal file
100
content/node.0f46a1a3-60ab-4b97-8437-8715674fa310.json
Normal file
|
@ -0,0 +1,100 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "0f46a1a3-60ab-4b97-8437-8715674fa310"
|
||||
}
|
||||
],
|
||||
"langcode": [
|
||||
{
|
||||
"value": "en"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_email",
|
||||
"target_type": "node_type",
|
||||
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
|
||||
}
|
||||
],
|
||||
"revision_timestamp": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"revision_uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"revision_log": [],
|
||||
"status": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Override Node Options and Drupal 11"
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2024-12-02T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-04-21T01:21:23+00:00"
|
||||
}
|
||||
],
|
||||
"promote": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"sticky": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"default_langcode": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"revision_translation_affected": [
|
||||
{
|
||||
"value": true
|
||||
}
|
||||
],
|
||||
"path": [
|
||||
{
|
||||
"alias": "\/daily\/2024\/12\/02\/override-node-options-and-drupal-11",
|
||||
"langcode": null
|
||||
}
|
||||
],
|
||||
"body": [
|
||||
{
|
||||
"value": "\n <p>Last week, I released a new version of the <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">Override Node Options module<\/a> - version 8.x-2.9.<\/p>\n\n<p>This version makes the module compatible with Drupal 11 and, as there are no breaking changes, it's still compatible with Drupal 9 and 10.<\/p>\n\n<p>It's great to see the module used on many Drupal 8+ websites and distributions such as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/11\/17\/override-node-options-used-by-localgov-drupal\">LocalGov Drupal<\/a>.<\/p>\n\n<p>Whilst <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/11\/16\/an-interesting-thing-i-spotted-about-the-override-node-options-module\">the overall number of installations has been consistent<\/a>, the number of Drupal 7 installations has decreased whilst the Drupal 8+ version installations have increased.<\/p>\n\n<p>With a Drupal 11-compatible version now available, I hope it continues to increase.<\/p>\n\n ",
|
||||
"format": "full_html",
|
||||
"processed": "\n <p>Last week, I released a new version of the <a href=\"https:\/\/www.drupal.org\/project\/override_node_options\">Override Node Options module<\/a> - version 8.x-2.9.<\/p>\n\n<p>This version makes the module compatible with Drupal 11 and, as there are no breaking changes, it's still compatible with Drupal 9 and 10.<\/p>\n\n<p>It's great to see the module used on many Drupal 8+ websites and distributions such as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/11\/17\/override-node-options-used-by-localgov-drupal\">LocalGov Drupal<\/a>.<\/p>\n\n<p>Whilst <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/11\/16\/an-interesting-thing-i-spotted-about-the-override-node-options-module\">the overall number of installations has been consistent<\/a>, the number of Drupal 7 installations has decreased whilst the Drupal 8+ version installations have increased.<\/p>\n\n<p>With a Drupal 11-compatible version now available, I hope it continues to increase.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "2025-04-21T01:21:23+00:00",
|
||||
"guid": null,
|
||||
"hash": "b386cfa88fcc78b52277d0eb4bc4f76d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue