Remove the feeds module
This commit is contained in:
parent
acfd1cca8d
commit
0c98be96eb
851 changed files with 840 additions and 10348 deletions
|
@ -22,7 +22,6 @@
|
|||
"drupal/core-project-message": "^11.1",
|
||||
"drupal/core-recommended": "^11.1",
|
||||
"drupal/drupal_cms_seo_tools": "^1.1",
|
||||
"drupal/feeds": "^3.0",
|
||||
"drupal/markdown_easy": "^1.0",
|
||||
"drupal/paragraphs": "^1.19",
|
||||
"drupal/pathauto": "^1.13",
|
||||
|
|
312
composer.lock
generated
312
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "377f681cb29972bd5a9a2bf00efd10f7",
|
||||
"content-hash": "7befc6549cf5eab7ceaa7aa2bca95acd",
|
||||
"packages": [
|
||||
{
|
||||
"name": "asm89/stack-cors",
|
||||
|
@ -2152,116 +2152,6 @@
|
|||
"source": "https://git.drupalcode.org/project/entity_reference_revisions"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "drupal/feeds",
|
||||
"version": "3.0.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://git.drupalcode.org/project/feeds.git",
|
||||
"reference": "8.x-3.0"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://ftp.drupal.org/files/projects/feeds-8.x-3.0.zip",
|
||||
"reference": "8.x-3.0",
|
||||
"shasum": "d6cbfdcc6ece690fe3b20dd62285eea91113089a"
|
||||
},
|
||||
"require": {
|
||||
"drupal/core": "^10.1 || ^11",
|
||||
"laminas/laminas-feed": "^2.22"
|
||||
},
|
||||
"require-dev": {
|
||||
"drupal/pathauto": "^1.0",
|
||||
"megachriz/drupalbook": "^1.0 || ^2.0"
|
||||
},
|
||||
"type": "drupal-module",
|
||||
"extra": {
|
||||
"drupal": {
|
||||
"version": "8.x-3.0",
|
||||
"datestamp": "1736951425",
|
||||
"security-coverage": {
|
||||
"status": "covered",
|
||||
"message": "Covered by Drupal's security advisory policy"
|
||||
}
|
||||
},
|
||||
"drush": {
|
||||
"services": {
|
||||
"drush.services.yml": ">=9"
|
||||
}
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packages.drupal.org/8/downloads",
|
||||
"license": [
|
||||
"GPL-2.0-or-later"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "alex_b",
|
||||
"homepage": "https://www.drupal.org/user/53995"
|
||||
},
|
||||
{
|
||||
"name": "dave reid",
|
||||
"homepage": "https://www.drupal.org/user/53892"
|
||||
},
|
||||
{
|
||||
"name": "e2thex",
|
||||
"homepage": "https://www.drupal.org/user/189123"
|
||||
},
|
||||
{
|
||||
"name": "febbraro",
|
||||
"homepage": "https://www.drupal.org/user/43670"
|
||||
},
|
||||
{
|
||||
"name": "franz",
|
||||
"homepage": "https://www.drupal.org/user/581844"
|
||||
},
|
||||
{
|
||||
"name": "Ian Ward",
|
||||
"homepage": "https://www.drupal.org/user/4736"
|
||||
},
|
||||
{
|
||||
"name": "jmiccolis",
|
||||
"homepage": "https://www.drupal.org/user/31731"
|
||||
},
|
||||
{
|
||||
"name": "joelpittet",
|
||||
"homepage": "https://www.drupal.org/user/160302"
|
||||
},
|
||||
{
|
||||
"name": "kking",
|
||||
"homepage": "https://www.drupal.org/user/24399"
|
||||
},
|
||||
{
|
||||
"name": "megachriz",
|
||||
"homepage": "https://www.drupal.org/user/654114"
|
||||
},
|
||||
{
|
||||
"name": "tobby",
|
||||
"homepage": "https://www.drupal.org/user/154797"
|
||||
},
|
||||
{
|
||||
"name": "twistor",
|
||||
"homepage": "https://www.drupal.org/user/473738"
|
||||
},
|
||||
{
|
||||
"name": "Will White",
|
||||
"homepage": "https://www.drupal.org/user/32237"
|
||||
},
|
||||
{
|
||||
"name": "yhahn",
|
||||
"homepage": "https://www.drupal.org/user/264833"
|
||||
}
|
||||
],
|
||||
"description": "Aggregates RSS/Atom/RDF feeds, imports CSV files and more.",
|
||||
"homepage": "https://www.drupal.org/project/feeds",
|
||||
"keywords": [
|
||||
"Drupal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://git.drupalcode.org/project/feeds",
|
||||
"issues": "https://www.drupal.org/project/issues/feeds"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "drupal/field_group",
|
||||
"version": "3.6.0",
|
||||
|
@ -4243,206 +4133,6 @@
|
|||
],
|
||||
"time": "2025-03-27T12:30:47+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-escaper",
|
||||
"version": "2.17.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laminas/laminas-escaper.git",
|
||||
"reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-escaper/zipball/df1ef9503299a8e3920079a16263b578eaf7c3ba",
|
||||
"reference": "df1ef9503299a8e3920079a16263b578eaf7c3ba",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-ctype": "*",
|
||||
"ext-mbstring": "*",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"conflict": {
|
||||
"zendframework/zend-escaper": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"infection/infection": "^0.29.8",
|
||||
"laminas/laminas-coding-standard": "~3.0.1",
|
||||
"phpunit/phpunit": "^10.5.45",
|
||||
"psalm/plugin-phpunit": "^0.19.2",
|
||||
"vimeo/psalm": "^6.6.2"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laminas\\Escaper\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "Securely and safely escape HTML, HTML attributes, JavaScript, CSS, and URLs",
|
||||
"homepage": "https://laminas.dev",
|
||||
"keywords": [
|
||||
"escaper",
|
||||
"laminas"
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://laminas.dev/chat",
|
||||
"docs": "https://docs.laminas.dev/laminas-escaper/",
|
||||
"forum": "https://discourse.laminas.dev",
|
||||
"issues": "https://github.com/laminas/laminas-escaper/issues",
|
||||
"rss": "https://github.com/laminas/laminas-escaper/releases.atom",
|
||||
"source": "https://github.com/laminas/laminas-escaper"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://funding.communitybridge.org/projects/laminas-project",
|
||||
"type": "community_bridge"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-06T19:29:36+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-feed",
|
||||
"version": "2.23.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laminas/laminas-feed.git",
|
||||
"reference": "7c6755695cdca18f983d8a116bfa6a55439b1a94"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-feed/zipball/7c6755695cdca18f983d8a116bfa6a55439b1a94",
|
||||
"reference": "7c6755695cdca18f983d8a116bfa6a55439b1a94",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-dom": "*",
|
||||
"ext-libxml": "*",
|
||||
"laminas/laminas-escaper": "^2.9",
|
||||
"laminas/laminas-stdlib": "^3.6",
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"conflict": {
|
||||
"laminas/laminas-servicemanager": "<3.3",
|
||||
"zendframework/zend-feed": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laminas/laminas-cache": "^2.13.2 || ^3.12",
|
||||
"laminas/laminas-cache-storage-adapter-memory": "^1.1.0 || ^2.3",
|
||||
"laminas/laminas-coding-standard": "~2.5.0",
|
||||
"laminas/laminas-db": "^2.18",
|
||||
"laminas/laminas-http": "^2.19",
|
||||
"laminas/laminas-servicemanager": "^3.22.1",
|
||||
"laminas/laminas-validator": "^2.46",
|
||||
"phpunit/phpunit": "^10.5.5",
|
||||
"psalm/plugin-phpunit": "^0.19.0",
|
||||
"psr/http-message": "^2.0",
|
||||
"vimeo/psalm": "^5.18.0"
|
||||
},
|
||||
"suggest": {
|
||||
"laminas/laminas-cache": "Laminas\\Cache component, for optionally caching feeds between requests",
|
||||
"laminas/laminas-db": "Laminas\\Db component, for use with PubSubHubbub",
|
||||
"laminas/laminas-http": "Laminas\\Http for PubSubHubbub, and optionally for use with Laminas\\Feed\\Reader",
|
||||
"laminas/laminas-servicemanager": "Laminas\\ServiceManager component, for easily extending ExtensionManager implementations",
|
||||
"laminas/laminas-validator": "Laminas\\Validator component, for validating email addresses used in Atom feeds and entries when using the Writer subcomponent",
|
||||
"psr/http-message": "PSR-7 ^1.0.1, if you wish to use Laminas\\Feed\\Reader\\Http\\Psr7ResponseDecorator"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laminas\\Feed\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "provides functionality for creating and consuming RSS and Atom feeds",
|
||||
"homepage": "https://laminas.dev",
|
||||
"keywords": [
|
||||
"atom",
|
||||
"feed",
|
||||
"laminas",
|
||||
"rss"
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://laminas.dev/chat",
|
||||
"docs": "https://docs.laminas.dev/laminas-feed/",
|
||||
"forum": "https://discourse.laminas.dev",
|
||||
"issues": "https://github.com/laminas/laminas-feed/issues",
|
||||
"rss": "https://github.com/laminas/laminas-feed/releases.atom",
|
||||
"source": "https://github.com/laminas/laminas-feed"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://funding.communitybridge.org/projects/laminas-project",
|
||||
"type": "community_bridge"
|
||||
}
|
||||
],
|
||||
"time": "2025-05-13T09:35:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laminas/laminas-stdlib",
|
||||
"version": "3.20.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/laminas/laminas-stdlib.git",
|
||||
"reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/laminas/laminas-stdlib/zipball/8974a1213be42c3e2f70b2c27b17f910291ab2f4",
|
||||
"reference": "8974a1213be42c3e2f70b2c27b17f910291ab2f4",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "~8.1.0 || ~8.2.0 || ~8.3.0 || ~8.4.0"
|
||||
},
|
||||
"conflict": {
|
||||
"zendframework/zend-stdlib": "*"
|
||||
},
|
||||
"require-dev": {
|
||||
"laminas/laminas-coding-standard": "^3.0",
|
||||
"phpbench/phpbench": "^1.3.1",
|
||||
"phpunit/phpunit": "^10.5.38",
|
||||
"psalm/plugin-phpunit": "^0.19.0",
|
||||
"vimeo/psalm": "^5.26.1"
|
||||
},
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
"Laminas\\Stdlib\\": "src/"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"description": "SPL extensions, array utilities, error handlers, and more",
|
||||
"homepage": "https://laminas.dev",
|
||||
"keywords": [
|
||||
"laminas",
|
||||
"stdlib"
|
||||
],
|
||||
"support": {
|
||||
"chat": "https://laminas.dev/chat",
|
||||
"docs": "https://docs.laminas.dev/laminas-stdlib/",
|
||||
"forum": "https://discourse.laminas.dev",
|
||||
"issues": "https://github.com/laminas/laminas-stdlib/issues",
|
||||
"rss": "https://github.com/laminas/laminas-stdlib/releases.atom",
|
||||
"source": "https://github.com/laminas/laminas-stdlib"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://funding.communitybridge.org/projects/laminas-project",
|
||||
"type": "community_bridge"
|
||||
}
|
||||
],
|
||||
"time": "2024-10-29T13:46:07+00:00"
|
||||
},
|
||||
{
|
||||
"name": "laravel/prompts",
|
||||
"version": "v0.3.5",
|
||||
|
|
|
@ -4,7 +4,6 @@ status: true
|
|||
dependencies:
|
||||
config:
|
||||
- field.field.node.daily_email.body
|
||||
- field.field.node.daily_email.feeds_item
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- path
|
||||
|
@ -80,5 +79,9 @@ content:
|
|||
size: 60
|
||||
placeholder: ''
|
||||
third_party_settings: { }
|
||||
hidden:
|
||||
feeds_item: true
|
||||
url_redirects:
|
||||
weight: 50
|
||||
region: content
|
||||
settings: { }
|
||||
third_party_settings: { }
|
||||
hidden: { }
|
||||
|
|
|
@ -4,7 +4,6 @@ status: true
|
|||
dependencies:
|
||||
config:
|
||||
- field.field.node.daily_email.body
|
||||
- field.field.node.daily_email.feeds_item
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- text
|
||||
|
@ -22,5 +21,4 @@ content:
|
|||
weight: 0
|
||||
region: content
|
||||
hidden:
|
||||
feeds_item: true
|
||||
links: true
|
||||
|
|
|
@ -5,7 +5,6 @@ dependencies:
|
|||
config:
|
||||
- core.entity_view_mode.node.rss
|
||||
- field.field.node.daily_email.body
|
||||
- field.field.node.daily_email.feeds_item
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- layout_builder
|
||||
|
@ -28,5 +27,4 @@ content:
|
|||
weight: 0
|
||||
region: content
|
||||
hidden:
|
||||
feeds_item: true
|
||||
links: true
|
||||
|
|
|
@ -5,7 +5,6 @@ dependencies:
|
|||
config:
|
||||
- core.entity_view_mode.node.teaser
|
||||
- field.field.node.daily_email.body
|
||||
- field.field.node.daily_email.feeds_item
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- text
|
||||
|
@ -24,5 +23,4 @@ content:
|
|||
weight: 0
|
||||
region: content
|
||||
hidden:
|
||||
feeds_item: true
|
||||
links: true
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
uuid: 5d7c008f-6f2d-4055-9cbc-caeb6aa1ce79
|
||||
langcode: en
|
||||
status: false
|
||||
dependencies:
|
||||
module:
|
||||
- feeds
|
||||
_core:
|
||||
default_config_hash: 8I9Vm3L0dLCdctKfCjpw5vLwMetyaJzxbWNMzb2V7-I
|
||||
id: feeds_feed.full
|
||||
label: 'Full feed'
|
||||
description: null
|
||||
targetEntityType: feeds_feed
|
||||
cache: true
|
|
@ -26,7 +26,6 @@ module:
|
|||
eca_user: 0
|
||||
editor: 0
|
||||
entity_reference_revisions: 0
|
||||
feeds: 0
|
||||
field: 0
|
||||
field_group: 0
|
||||
field_ui: 0
|
||||
|
|
|
@ -1,69 +0,0 @@
|
|||
uuid: bbef79e9-3f62-4d02-87f7-42aa5dc29f04
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.field.node.daily_email.body
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- node
|
||||
label: 'Daily emails'
|
||||
id: daily_emails
|
||||
description: ''
|
||||
help: ''
|
||||
import_period: -1
|
||||
fetcher: http
|
||||
fetcher_configuration:
|
||||
auto_detect_feeds: false
|
||||
use_pubsubhubbub: false
|
||||
always_download: false
|
||||
fallback_hub: ''
|
||||
request_timeout: 30
|
||||
parser: syndication
|
||||
parser_configuration: { }
|
||||
processor: 'entity:node'
|
||||
processor_configuration:
|
||||
values:
|
||||
type: daily_email
|
||||
langcode: en
|
||||
insert_new: 1
|
||||
update_existing: 0
|
||||
update_non_existent: _keep
|
||||
skip_hash_check: false
|
||||
skip_validation: false
|
||||
skip_validation_types: { }
|
||||
authorize: true
|
||||
revision: false
|
||||
expire: -1
|
||||
owner_feed_author: false
|
||||
owner_id: 1
|
||||
custom_sources: { }
|
||||
mappings:
|
||||
-
|
||||
target: title
|
||||
map:
|
||||
value: title
|
||||
settings:
|
||||
language: null
|
||||
unique: { }
|
||||
-
|
||||
target: path
|
||||
map:
|
||||
alias: url
|
||||
settings:
|
||||
language: null
|
||||
-
|
||||
target: created
|
||||
map:
|
||||
value: timestamp
|
||||
settings:
|
||||
language: null
|
||||
timezone: UTC
|
||||
-
|
||||
target: body
|
||||
map:
|
||||
value: content
|
||||
summary: ''
|
||||
settings:
|
||||
language: ''
|
||||
format: full_html
|
|
@ -1,3 +0,0 @@
|
|||
_core:
|
||||
default_config_hash: HinPVeSuQPC-ststYO0ZlaHvrrLfgh3rC008LYb446k
|
||||
lock_timeout: 43200
|
|
@ -1,23 +0,0 @@
|
|||
uuid: ce374884-abf4-4e9e-880e-c2bd504da762
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- field.storage.node.feeds_item
|
||||
- node.type.daily_email
|
||||
module:
|
||||
- feeds
|
||||
id: node.daily_email.feeds_item
|
||||
field_name: feeds_item
|
||||
entity_type: node
|
||||
bundle: daily_email
|
||||
label: 'Feeds item'
|
||||
description: ''
|
||||
required: false
|
||||
translatable: true
|
||||
default_value: { }
|
||||
default_value_callback: ''
|
||||
settings:
|
||||
handler: 'default:feeds_feed'
|
||||
handler_settings: { }
|
||||
field_type: feeds_item
|
|
@ -1,20 +0,0 @@
|
|||
uuid: 9d2bdd56-8c87-4985-93ad-ead8cefc98c5
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- feeds
|
||||
- node
|
||||
id: node.feeds_item
|
||||
field_name: feeds_item
|
||||
entity_type: node
|
||||
type: feeds_item
|
||||
settings:
|
||||
target_type: feeds_feed
|
||||
module: feeds
|
||||
locked: false
|
||||
cardinality: -1
|
||||
translatable: false
|
||||
indexes: { }
|
||||
persist_with_no_fields: false
|
||||
custom_storage: false
|
|
@ -1,13 +0,0 @@
|
|||
uuid: 37be311a-3330-4c13-8d20-319f62d340f6
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- feeds
|
||||
_core:
|
||||
default_config_hash: Ur7RuR5ew0bsKEMwJUA7ZY0oY1mfXrSzcw4Tlt8FMJ0
|
||||
id: feeds_feed_clear_action
|
||||
label: 'Delete imported items of selected feeds'
|
||||
type: feeds_feed
|
||||
plugin: feeds_feed_clear_action
|
||||
configuration: { }
|
|
@ -1,13 +0,0 @@
|
|||
uuid: 4cfa8fe9-2d83-4211-a1a6-4ddd664799df
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- feeds
|
||||
_core:
|
||||
default_config_hash: yZW4ELH2L51kf2mGKd7f62uozGH-jFuWz1k9abACq1U
|
||||
id: feeds_feed_delete_action
|
||||
label: 'Delete selected feeds'
|
||||
type: feeds_feed
|
||||
plugin: feeds_feed_delete_action
|
||||
configuration: { }
|
|
@ -1,13 +0,0 @@
|
|||
uuid: 8f41e4fb-e97d-4799-ba3a-431d16261b07
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
module:
|
||||
- feeds
|
||||
_core:
|
||||
default_config_hash: aQm30ToKvnhu77M_7zjucDLw0bxkaz5u3LDhByjaH8A
|
||||
id: feeds_feed_import_action
|
||||
label: 'Import selected feeds'
|
||||
type: feeds_feed
|
||||
plugin: feeds_feed_import_action
|
||||
configuration: { }
|
|
@ -1,737 +0,0 @@
|
|||
uuid: 7b32bdd2-6eee-49e0-ac5c-decae7ef386a
|
||||
langcode: en
|
||||
status: true
|
||||
dependencies:
|
||||
config:
|
||||
- system.menu.admin
|
||||
module:
|
||||
- feeds
|
||||
- user
|
||||
_core:
|
||||
default_config_hash: RbZ1xJz3BHNcfMQFvqYZv7O41f32c4ERI3DiwNKyss4
|
||||
id: feeds_feed
|
||||
label: Feeds
|
||||
module: views
|
||||
description: ''
|
||||
tag: ''
|
||||
base_table: feeds_feed
|
||||
base_field: fid
|
||||
display:
|
||||
default:
|
||||
display_plugin: default
|
||||
id: default
|
||||
display_title: Master
|
||||
position: 0
|
||||
display_options:
|
||||
access:
|
||||
type: perm
|
||||
options:
|
||||
perm: 'access feed overview'
|
||||
cache:
|
||||
type: none
|
||||
options: { }
|
||||
query:
|
||||
type: views_query
|
||||
options:
|
||||
disable_sql_rewrite: false
|
||||
distinct: false
|
||||
replica: false
|
||||
query_comment: ''
|
||||
query_tags: { }
|
||||
exposed_form:
|
||||
type: basic
|
||||
options:
|
||||
submit_button: Apply
|
||||
reset_button: false
|
||||
reset_button_label: Reset
|
||||
exposed_sorts_label: 'Sort by'
|
||||
expose_sort_order: true
|
||||
sort_asc_label: Asc
|
||||
sort_desc_label: Desc
|
||||
pager:
|
||||
type: full
|
||||
options:
|
||||
items_per_page: 50
|
||||
offset: 0
|
||||
id: 0
|
||||
total_pages: null
|
||||
tags:
|
||||
previous: '‹ previous'
|
||||
next: 'next ›'
|
||||
first: '« first'
|
||||
last: 'last »'
|
||||
expose:
|
||||
items_per_page: false
|
||||
items_per_page_label: 'Items per page'
|
||||
items_per_page_options: '5, 10, 25, 50'
|
||||
items_per_page_options_all: false
|
||||
items_per_page_options_all_label: '- All -'
|
||||
offset: false
|
||||
offset_label: Offset
|
||||
quantity: 9
|
||||
style:
|
||||
type: table
|
||||
options:
|
||||
grouping: { }
|
||||
row_class: ''
|
||||
default_row_class: true
|
||||
override: true
|
||||
sticky: false
|
||||
caption: ''
|
||||
summary: ''
|
||||
description: ''
|
||||
columns:
|
||||
feeds_feed_bulk_form: feeds_feed_bulk_form
|
||||
title: title
|
||||
type: type
|
||||
name: name
|
||||
changed: changed
|
||||
imported: imported
|
||||
operations: operations
|
||||
info:
|
||||
feeds_feed_bulk_form:
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
title:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
type:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
name:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
changed:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
imported:
|
||||
sortable: true
|
||||
default_sort_order: asc
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
operations:
|
||||
align: ''
|
||||
separator: ''
|
||||
empty_column: false
|
||||
responsive: ''
|
||||
default: '-1'
|
||||
empty_table: true
|
||||
row:
|
||||
type: fields
|
||||
fields:
|
||||
feeds_feed_bulk_form:
|
||||
id: feeds_feed_bulk_form
|
||||
table: feeds_feed
|
||||
field: feeds_feed_bulk_form
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: 'Bulk update'
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
action_title: 'With selection'
|
||||
include_exclude: exclude
|
||||
selected_actions: { }
|
||||
entity_type: feeds_feed
|
||||
plugin_id: bulk_form
|
||||
title:
|
||||
id: title
|
||||
table: feeds_feed
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Title
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: string
|
||||
settings:
|
||||
link_to_entity: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: null
|
||||
entity_field: title
|
||||
plugin_id: field
|
||||
type:
|
||||
id: type
|
||||
table: feeds_feed
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Type
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: target_id
|
||||
type: entity_reference_label
|
||||
settings:
|
||||
link: false
|
||||
group_column: target_id
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: feeds_feed
|
||||
entity_field: type
|
||||
plugin_id: field
|
||||
name:
|
||||
id: name
|
||||
table: users_field_data
|
||||
field: name
|
||||
relationship: uid
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Author
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: user_name
|
||||
settings:
|
||||
link_to_entity: true
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: user
|
||||
entity_field: name
|
||||
plugin_id: field
|
||||
changed:
|
||||
id: changed
|
||||
table: feeds_feed
|
||||
field: changed
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Updated
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: timestamp
|
||||
settings:
|
||||
date_format: short
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: feeds_feed
|
||||
entity_field: changed
|
||||
plugin_id: field
|
||||
imported:
|
||||
id: imported
|
||||
table: feeds_feed
|
||||
field: imported
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Imported
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
click_sort_column: value
|
||||
type: timestamp
|
||||
settings:
|
||||
date_format: short
|
||||
custom_date_format: ''
|
||||
timezone: ''
|
||||
group_column: value
|
||||
group_columns: { }
|
||||
group_rows: true
|
||||
delta_limit: 0
|
||||
delta_offset: 0
|
||||
delta_reversed: false
|
||||
delta_first_last: false
|
||||
multi_type: separator
|
||||
separator: ', '
|
||||
field_api_classes: false
|
||||
entity_type: feeds_feed
|
||||
entity_field: imported
|
||||
plugin_id: field
|
||||
operations:
|
||||
id: operations
|
||||
table: feeds_feed
|
||||
field: operations
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
label: Operations
|
||||
exclude: false
|
||||
alter:
|
||||
alter_text: false
|
||||
text: ''
|
||||
make_link: false
|
||||
path: ''
|
||||
absolute: false
|
||||
external: false
|
||||
replace_spaces: false
|
||||
path_case: none
|
||||
trim_whitespace: false
|
||||
alt: ''
|
||||
rel: ''
|
||||
link_class: ''
|
||||
prefix: ''
|
||||
suffix: ''
|
||||
target: ''
|
||||
nl2br: false
|
||||
max_length: 0
|
||||
word_boundary: true
|
||||
ellipsis: true
|
||||
more_link: false
|
||||
more_link_text: ''
|
||||
more_link_path: ''
|
||||
strip_tags: false
|
||||
trim: false
|
||||
preserve_tags: ''
|
||||
html: false
|
||||
element_type: ''
|
||||
element_class: ''
|
||||
element_label_type: ''
|
||||
element_label_class: ''
|
||||
element_label_colon: true
|
||||
element_wrapper_type: ''
|
||||
element_wrapper_class: ''
|
||||
element_default_classes: true
|
||||
empty: ''
|
||||
hide_empty: false
|
||||
empty_zero: false
|
||||
hide_alter_empty: true
|
||||
destination: true
|
||||
entity_type: feeds_feed
|
||||
plugin_id: entity_operations
|
||||
filters:
|
||||
title:
|
||||
id: title
|
||||
table: feeds_feed
|
||||
field: title
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: contains
|
||||
value: ''
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: title_op
|
||||
label: Title
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: title_op
|
||||
identifier: title
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
entity_type: feeds_feed
|
||||
entity_field: title
|
||||
plugin_id: string
|
||||
type:
|
||||
id: type
|
||||
table: feeds_feed
|
||||
field: type
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
operator: in
|
||||
value: { }
|
||||
group: 1
|
||||
exposed: true
|
||||
expose:
|
||||
operator_id: type_op
|
||||
label: Type
|
||||
description: ''
|
||||
use_operator: false
|
||||
operator: type_op
|
||||
identifier: type
|
||||
required: false
|
||||
remember: false
|
||||
multiple: false
|
||||
remember_roles:
|
||||
authenticated: authenticated
|
||||
anonymous: '0'
|
||||
reduce: false
|
||||
is_grouped: false
|
||||
group_info:
|
||||
label: ''
|
||||
description: ''
|
||||
identifier: ''
|
||||
optional: true
|
||||
widget: select
|
||||
multiple: false
|
||||
remember: false
|
||||
default_group: All
|
||||
default_group_multiple: { }
|
||||
group_items: { }
|
||||
entity_type: feeds_feed
|
||||
entity_field: type
|
||||
plugin_id: bundle
|
||||
sorts:
|
||||
changed:
|
||||
id: changed
|
||||
table: feeds_feed
|
||||
field: changed
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
order: DESC
|
||||
exposed: false
|
||||
expose:
|
||||
label: ''
|
||||
granularity: second
|
||||
entity_type: feeds_feed
|
||||
entity_field: changed
|
||||
plugin_id: date
|
||||
title: Feeds
|
||||
header: { }
|
||||
footer: { }
|
||||
empty:
|
||||
result:
|
||||
id: result
|
||||
table: views
|
||||
field: result
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: ''
|
||||
empty: true
|
||||
content: 'There are no feeds yet.'
|
||||
plugin_id: result
|
||||
relationships:
|
||||
uid:
|
||||
id: uid
|
||||
table: feeds_feed
|
||||
field: uid
|
||||
relationship: none
|
||||
group_type: group
|
||||
admin_label: Author
|
||||
required: false
|
||||
entity_type: feeds_feed
|
||||
entity_field: uid
|
||||
plugin_id: standard
|
||||
arguments: { }
|
||||
display_extenders: { }
|
||||
filter_groups:
|
||||
operator: AND
|
||||
groups:
|
||||
1: AND
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user.permissions
|
||||
tags: { }
|
||||
page_1:
|
||||
display_plugin: page
|
||||
id: page_1
|
||||
display_title: Page
|
||||
position: 1
|
||||
display_options:
|
||||
display_extenders: { }
|
||||
path: admin/content/feed
|
||||
menu:
|
||||
type: normal
|
||||
title: Feeds
|
||||
description: ''
|
||||
expanded: false
|
||||
parent: system.admin_content
|
||||
weight: 0
|
||||
context: '0'
|
||||
menu_name: admin
|
||||
cache_metadata:
|
||||
max-age: 0
|
||||
contexts:
|
||||
- 'languages:language_content'
|
||||
- 'languages:language_interface'
|
||||
- url
|
||||
- url.query_args
|
||||
- user.permissions
|
||||
tags: { }
|
|
@ -1,73 +0,0 @@
|
|||
{
|
||||
"uuid": [
|
||||
{
|
||||
"value": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
],
|
||||
"type": [
|
||||
{
|
||||
"target_id": "daily_emails",
|
||||
"target_type": "feeds_feed_type",
|
||||
"target_uuid": "bbef79e9-3f62-4d02-87f7-42aa5dc29f04"
|
||||
}
|
||||
],
|
||||
"title": [
|
||||
{
|
||||
"value": "Daily emails"
|
||||
}
|
||||
],
|
||||
"uid": [
|
||||
{
|
||||
"target_type": "user",
|
||||
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
|
||||
}
|
||||
],
|
||||
"status": [
|
||||
{
|
||||
"value": false
|
||||
}
|
||||
],
|
||||
"created": [
|
||||
{
|
||||
"value": "2025-04-16T14:12:30+00:00"
|
||||
}
|
||||
],
|
||||
"changed": [
|
||||
{
|
||||
"value": "2025-05-13T23:02:18+00:00"
|
||||
}
|
||||
],
|
||||
"imported": [
|
||||
{
|
||||
"value": "2025-05-11T09:01:00+00:00"
|
||||
}
|
||||
],
|
||||
"next": [
|
||||
{
|
||||
"value": "1969-12-31T23:59:59+00:00"
|
||||
}
|
||||
],
|
||||
"queued": [
|
||||
{
|
||||
"value": "1970-01-01T00:00:00+00:00"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
{
|
||||
"value": "https:\/\/www.oliverdavies.uk\/rss\/daily2.xml"
|
||||
}
|
||||
],
|
||||
"config": [
|
||||
{
|
||||
"fetcher": {
|
||||
"fid": "1",
|
||||
"usage_id": "728512ba-6198-4260-9eff-4e30b02d8ec6"
|
||||
}
|
||||
}
|
||||
],
|
||||
"item_count": [
|
||||
{
|
||||
"value": 0
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A common mistake I see with new Developers and Development teams when starting with automated testing is to split development time and testing into separate tasks.<\/p>\n\n<p>They'll say when quoting a client or in an estimation session that the development time will take x hours and writing tests will take y hours.<\/p>\n\n<p>This is something I've always avoided.<\/p>\n\n<p>When broken out this way, it implies the automated tests are optional when they should be an integral part of the development process.<\/p>\n\n<p>They aren't a separate task that can be removed or skipped to save time or money.<\/p>\n\n<p>If I take my car to a garage, I don't ask them how long it will take to repair and how long to test what they've done.<\/p>\n\n<p>I want to know how much the whole task will cost and how long it will take.<\/p>\n\n<p>I assume they automatically test and verify the work they do and follow their industry's standard and best practices.<\/p>\n\n<p>I'm doing the same when developing software.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "71938847a402f6b5af7c2e34bca063d3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Test cases are usually split into two or three sections - \"Arrange, Act, Assert\" or \"Given, When, Then\".<\/p>\n\n<p>If the test has a prerequisite, such as some users or content to be created or in a given state, you create the required environment for the test. With unit tests, this would include mocking any dependencies you need to use.<\/p>\n\n<p>Then you perform an action on the subject under test and, finally, assert that the system is in the desired state.<\/p>\n\n<p>Maybe a user was pending initially, and they're active after running a command or a cron task. You can assert the initial state of the user as well as the final state to ensure the action did what it was supposed to do.<\/p>\n\n<h2 id=\"option-1\">Option 1<\/h2>\n\n<p>To help me get started, I'll sometimes write a test like this with placeholders to separate the test into its separate stages:<\/p>\n\n<pre><code class=\"language-php\">\/** @test *\/\nfunction should_activate_a_pending_user(): void {\n \/\/ Arrange.\n\n \/\/ Act.\n\n \/\/ Assert.\n}\n<\/code><\/pre>\n\n<p>This makes me think about the different stages and what each might need to contain.<\/p>\n\n<h2 id=\"option-2\">Option 2<\/h2>\n\n<p>Or I might write it out in the \"Given, When, Then\" format:<\/p>\n\n<pre><code class=\"language-php\">\/** @test *\/\nfunction should_activate_a_pending_user(): void {\n \/\/ Given I have a user.\n \/\/ And the user is pending.\n\n \/\/ When I run the user update command.\n\n \/\/ Then the user should no longer be 'pending'.\n \/\/ And the user status should be 'active'.\n}\n<\/code><\/pre>\n\n<p>This takes a little longer to write but feels more familiar if you're used to a behavioural testing framework like Behat.<\/p>\n\n<p>Either way, sometimes, I'll remove the comments once I've written the code around them or leave them to provide additional context.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "a1a228d3231ffab8895ec0cbd78f0b1d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A side effect of <a href=\"http:\/\/default\/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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "aacb745c094cf5a9f668c64b15000f49",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Something I see on many Developers' websites and CVs is percentages or levels of how well they know certain tools and frameworks.<\/p>\n\n<p>Things like \"80% of PHP\" or \"Advanced in HTML and CSS\".<\/p>\n\n<p>But how do you quantify that?<\/p>\n\n<p>Do people alter their percentages accordingly if a new feature is added to a language or framework?<\/p>\n\n<p>Or, instead of trying to show how much you understand, focus on what problems you can solve with those tools and how you can provide value to customers or employers instead of what tools you'd use to do it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3bf8b7e44f82bf7d6ad5ce1fda692ae6",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A common reason why environments aren't updated and get out of sync is because it's a time-consuming or complex task.<\/p>\n\n<p>The process should be simple to run, quick, reliable and reproducible.<\/p>\n\n<p>It's the same as deploying a change to a staging or production environment.<\/p>\n\n<p>You want the same result on every time on every environment.<\/p>\n\n<p>You want every environment - including <a href=\"http:\/\/default\/daily\/2024\/12\/25\/localhost\">local development environments<\/a> to be as consistent as possible to minimise bugs and errors.<\/p>\n\n<p>To do this, I automate things to make them as simple as possible.<\/p>\n\n<p>I use <a href=\"http:\/\/default\/daily\/2022\/08\/15\/using-run-file-simplify-project-tasks\">run files<\/a> with commands to import databases, perform updates and run pre-update and post-update tasks.<\/p>\n\n<p>I use tools like Nix and <a href=\"http:\/\/default\/daily\/2024\/11\/11\/could-nix-and-devenv-replace-docker-compose\">devenv<\/a> to create identical and reproducible environments.<\/p>\n\n<p>The simpler and quicker is it, the more it can and will be done.<\/p>\n\n<p>You can also use automation to perform long or complex tasks outside of working hours such as sanitising and importing large databases.<\/p>\n\n<p>The more you can automate, the better.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "957251b7baf05530d91b2a9731016317",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I started learning software and web development with HTML and CSS in 2007. Then I started with PHP and soon after started to learn Drupal 6 (or maybe 5).<\/p>\n\n<p>A lot of Developers' first programming language was PHP and maybe used earlier versions of Drupal before moving to another CMS, framework or language.<\/p>\n\n<p>They still remember, though, how things were when they used it and aren't aware of the advancements and improvements that have been made.<\/p>\n\n<p>The PHP language itself has improved significantly in recent versions with new features and much better peformance.<\/p>\n\n<p>Drupal is more powerful with a lot more available in core compared to when I started to use it and, since adopting modern PHP approaches and third-party code in Drupal 8, writing custom Drupal modules is different with fewer Drupalisms and more industry-standard approaches.<\/p>\n\n<p>If there's a tool you haven't tried for a while, maybe take another look and see if it's improved since you last used it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3baa147faae6a26ac886bb26940c8f43",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>PHPStan is a static analysis tool for PHP.<\/p>\n\n<p>It finds potential issues in PHP code without needing to run it, so Developers can find and resolve potential issues sooner.<\/p>\n\n<p>I use it on all my projects including existing ones I've inherited.<\/p>\n\n<p>But how can you add a static analysis tool to a codebase without getting a lot of errors from the existing code?<\/p>\n\n<p>PHPStan has different levels of strictness.<\/p>\n\n<p>Level 0 is the least strict and each level adds more rules and strictness, resulting in more errors.<\/p>\n\n<p>Most of the time, people will start by running PHPStan on level 0, fixing any errors and committing the changes.<\/p>\n\n<p>Then repeat the process as many times as needed until you reach the level you want to achieve.<\/p>\n\n<p>I don't think this is the right approach.<\/p>\n\n<p>This could mean that you need to edit the same files multiple times as you work through the levels.<\/p>\n\n<p>There's also a period of time where Developers can still write suboptimal code whilst you work your way up to your desired level.<\/p>\n\n<p>Another approach is to use a feature of PHPStan called the baseline.<\/p>\n\n<p>The baseline is a way of capturing and saving all the existing errors up to the selected level so they are no longer reported.<\/p>\n\n<p>If you did this for an existing project, it would return no errors as everything would be included in the baseline.<\/p>\n\n<p>Once you decide what level you want your project to run, you can start as soon as the baseline is generated and without needing to change files multiple times.<\/p>\n\n<p>Instead of spending time working through the levels one at a time, commit some time to pruning the baseline and reducing the errors in it.<\/p>\n\n<p>This I think is a better approach and how I add PHPStan to existing codebases.<\/p>\n\n<p>To learn more about static analysis and PHPStan, listen to <a href=\"http:\/\/default\/podcast\/22-dave-liddament\">episode 22 of the Beyond Blocks podcast<\/a> with Dave Liddament.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "9a7b258b69b97c51304c0aa8d6b87263",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Before I started to use [Tailwind CSS][0], I used CSS preprocessors like Less and Sass to add features like variables and nesting to my CSS files.<\/p>\n\n<p>Stylesheets would be written in .scss, .sass or .less files and processed to create the stylesheets that would be used by browsers.<\/p>\n\n<p>But, with the recent improvements to CSS, do we still need these preprocessors?<\/p>\n\n<p>Here's a very small example of some CSS that just works:<\/p>\n\n<pre><code class=\"css\">:root {\n --color-primary: red;\n --color-secondary: green;\n}\n\na {\n color: var(--color-primary);\n\n &:hover, &:focus {\n color: var(--color-secondary);\n }\n}\n<\/code><\/pre>\n\n<p>It looks like a Sass file, but it's native CSS.<\/p>\n\n<p>It has variables (a.k.a. custom properties) and nesting, which I think are the most used features from preprocessors.<\/p>\n\n<p>But there's no additional build step to generate the end stylesheet. I can use this stylesheet as it is - making it easier to work on and less confusing for new Developers.<\/p>\n\n<p>If I'm not using Tailwind CSS or atomic styles, writing plain CSS files is the approach I'd use.<\/p>\n\n<p>No preprocessors needed.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "cdd9ab25b0351a9082e1580898e5ecbc",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "60c550711c51c2b739051e4640eba954",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,6 +87,5 @@
|
|||
"processed": "<p>There's nothing more frustrating for me than seeing an error I've seen before and not remembering how I fixed it last time.<\/p><p>I try to remember and search for the error message, just to find and read the same articles and posts again or watch the same videos.<\/p><p>I wrote my first blog post on my website in 2010 to document my learning for myself and to share with others.<\/p><p>I've also been a keen note taker, using tools like Evernote and others to take notes and write documentation for myself to refer to in the future.<\/p><p>These days, I just <a href=\"http:\/\/default\/daily\/2024\/11\/10\/write-plain-text-files\">write plain text files<\/a> using <a href=\"https:\/\/github.com\/nickjj\/notes\">Nick Janetakis' notes program<\/a>, which I've modified slightly <a href=\"http:\/\/default\/daily\/2025\/04\/21\/patch\">by patching it<\/a> to create daily notes instead of monthly ones. <\/p><p>They're fast to write, easy to search and available offline if I'm traveling or away from my computer.<\/p><p>I much prefer being able to search my notes and find what I'm looking for or, if it's a post that I've written publicly, searching online and finding my own answer.<\/p><p>Whether you're a new or experienced Developer, you're always learning new things, so write them down for yourself and, if you want, write publicly and share your learnings with others.<\/p>",
|
||||
"summary": ""
|
||||
}
|
||||
],
|
||||
"feeds_item": []
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I recently <a href=\"https:\/\/x.com\/StefanGalescu\/status\/1798421063355007017\">saw this post on X<\/a>, talking about an open-source project.<\/p>\n\n<p>This was the part that I want to highlight:<\/p>\n\n<blockquote>\n <p>I was also impressed by the decisions documents. More projects should implement this concept.<\/p>\n<\/blockquote>\n\n<p>Looking at the repository, it contains records of 36 decisions made on the project between May 2023 and April 2024.<\/p>\n\n<p>Decision records provide useful information to new people to a project, whether you want to contribute to an open-source project or it's a new client or work codebase.<\/p>\n\n<p>The code and project will likely continue once you've finished working on it, so capturing decisions for future Developers is key.<\/p>\n\n<p>I've often joined projects and wondered why or how something was done, and can only guess as the decision wasn't recorded or is somewhere not available to me, such as an old wiki or ticketing system.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>I agree with the poster.<\/p>\n\n<p>More people should record the decisions made on their project, whether it's open-source or not.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "bc11c3125e2e228f16b76f91b749c764",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"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=\"http:\/\/default\/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=\"http:\/\/default\/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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "54447bfd288cd6e71d4b06bd0646502d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>In yesterday's email, I mentioned that deprecating code allows it to be backwards compatible - but what does that mean?<\/p>\n\n<p>If I were to remove a function like <code>drupal_set_message()<\/code> that is used code elsewhere in an application, the code would no longer work and would break.<\/p>\n\n<p>As a module or library maintainer, I don't want to cause applications to break by making backward incompatible changes (a \"BC break\" or a \"breaking change\").<\/p>\n\n<p>Maintaining backward compatibility means that people who use the code can update to the latest version without breakages, and if they use any deprecated code, they know to update it to be compatible with future versions.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "18e7b8e123a0c299bb44600a75b56d9e",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Today, I was speaking with a colleague about configuring Git, which led to a conversation about dotfiles repositories - somewhere where you version the changes to your configuration files and, usually, create symlinks to their expected locations.<\/p>\n\n<p>Afterwards, I realised the first commit to <a href=\"https:\/\/github.com\/opdavies\/dotfiles\">my personal dotfiles repository<\/a> was over eight years ago, in July 2015.<\/p>\n\n<p>What started as a repository to put my own Git configuration has undergone various changes since the tools I use changed, as well as the tools to manage the files themselves.<\/p>\n\n<p>In 2021, I switched my <code>.vimrc<\/code> configuration file to an <code>init.vim<\/code> and switched to Neovim full-time and, most recently, almost a year ago, I started to use the Nix package manager and later adopted NixOS as my primary Linux distribution.<\/p>\n\n<p>My complete NixOS and Home Manager configurations are within my <code>dotfiles<\/code> repository, and the configuration for tools, including Neovim, tmux and Git.<\/p>\n\n<p>I wonder what the repository will look like in another eight years...<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "b663fb9c5e0257d2fdece24454ad3bfd",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Today, I've been at an event run by a local animal rescue charity. It's one that we attend often as my children like to enter the dog show, but this year, I've also sponsored one of the categories.<\/p>\n\n<p>As well as organising the PHP South Wales user group, I'm also now a sponsor - donating books and elePHPant plushies for raffle prizes and paying the group's Meetup.com subscription costs.<\/p>\n\n<p>Giving back and supporting open-source maintainers and content creators is a big priority of mine. If I use some open-source software or find that someone's Twitch or YouTube channel is useful, if that person or organisation is on GitHub or Patron, then I'll sponsor them, or I'll subscribe to their channel.<\/p>\n\n<p>If I find a useful blog post or video, I'll add a comment or link to it on Twitter, thanking them and letting them know that it helped me.<\/p>\n\n<p>Especially if it's something that I've used within my projects, it makes sense to support it and it's maintainers, so that they keep working on and improving the software, continue streaming, and keep writing blog posts and recording videos for me to learn from.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "891d018e5d9b5fa68288878004365cce",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>How do you validate an idea from a client or stakeholder?<\/p>\n\n<p>Unlike a minimum viable product, which is the smallest amount of functionality for a feature to be released, a proof of concept is an initial investigation into whether the idea is viable.<\/p>\n\n<p>What's the least amount of development you can do to prove the concept?<\/p>\n\n<p>Can you create something using a smaller data set or with simplified functionality?<\/p>\n\n<p>What can you build within a minimal timeframe so, if the idea isn't used, too much time wasn't invested in it, but still verifies it?<\/p>\n\n<p>Do you need to write any code or do any development at all?<\/p>\n\n<p>All we need to do is find out if the concept could work.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "f671ea961bdda4e275277b04dc159c79",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>The main programming languages I write are PHP and JavaScript.<\/p>\n\n<p>Both offer types where, as well as declaring a parameter or property, you can define what type it is - whether it's a string, array, boolean, etc.<\/p>\n\n<p>Adding types is optional.<\/p>\n\n<p>You don't need to add types to your PHP code, and you can choose to write plain JavaScript instead of TypeScript.<\/p>\n\n<p>Some people prefer simpler or cleaner code or less \"visual debt\".<\/p>\n\n<p>I like the extra clarity that types add.<\/p>\n\n<p>I like to be able to read some code and immediately know what types things should be.<\/p>\n\n<p>I like the clearer errors and messages if a different type is given than was expected.<\/p>\n\n<p>Tools like PHPStan know more about my code and give better recommendations than if I don't add types.<\/p>\n\n<p>I like the better autocompletion I get when writing code that has types.<\/p>\n\n<p>I like types, but I also like the flexibility of whether or not to add them and for Developers and development teams to make their own decisions based on their preferences.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "c50e363614371738c421f8a32ee763f3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p><a href=\"http:\/\/default\/daily\/2023\/11\/03\/why-your-company-should-contribute-to-open-source-software\">Yesterday's email<\/a> explained why your company should contribute to open-source software, but why should you contribute as an individual?<\/p>\n\n<p>Most of the same reasons apply, such as gaining experience and improved knowledge from contributing.<\/p>\n\n<p>As an individual, you can build your own reputation and personal brand.<\/p>\n\n<p>You'll get exposure from contributions and involvement with initiatives, such as the Drupal admin UI improvements and other core initiatives, which look great on your CV and LinkedIn profile.<\/p>\n\n<p>This could lead to better career opportunities and potential projects.<\/p>\n\n<p>I've had paid development work directly from my open-source code contributions, as well as public speaking and event organising, so I can vouch for this.<\/p>\n\n<p>Like companies, if you make money from open-source software - either a salary or from paid projects or courses - it's in your interest to contribute so the software you use is maintained and improved so it's the best it can be.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "0f37079942ad6ce63ae32e8ea7c57c79",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Does your team have a \"No deploy Friday\" policy?<\/p>\n\n<p>What about not deploying after a certain time in the afternoon?<\/p>\n\n<p>These approaches are attempts to minimise risk when deploying.<\/p>\n\n<p>If there is an issue, will someone be available during the evening or weekend to resolve it?<\/p>\n\n<p>To me, this indicates the deployment process is too complicated, possibly due to a lack of automation, or deployments aren't happening frequently enough.<\/p>\n\n<p>Having a <a href=\"http:\/\/default\/daily\/2025\/01\/30\/gatekeeper\">robust and passing CI pipeline<\/a> that runs automated checks and tests is crucial to know the code is deployable.<\/p>\n\n<p><a href=\"http:\/\/default\/daily\/2023\/09\/28\/feature-flags-enable-continuous-integration\">Feature flags are a great way<\/a> to separate deploying code from releasing changes to users, which means you don't need to avoid pushing some code until the change is complete. It can be done incrementally and released over several deployments.<\/p>\n\n<p>Too much time between deployments is a smell.<\/p>\n\n<p>The more time there is between a deployment and the larger the changeset, the riskier the deployment will be.<\/p>\n\n<p>There is more to go wrong and it'll be harder to diagnose and resolve any issues.<\/p>\n\n<p>I always advocate for many smaller releases than larger less frequent ones.<\/p>\n\n<p>Ideally, a production release every day - even if the changes are small or everything is hidden behind feature flags.<\/p>\n\n<p>Deploying on Friday is easy if you last deployed on Thursday.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "021827be7bf3f083020a0f2ff11066d8",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p><a href=\"http:\/\/default\/daily\/2025\/01\/13\/patches\">Applying patch files<\/a> is a common way to customise and extend open source software, and how we used to submit changes to Drupal before issue forks and merge requests were added to Drupal.org.<\/p>\n\n<p>Some software, such as dwm and st from suckless.org are released as minimal versions that you patch to add features to.<\/p>\n\n<p>If you find a line of code that you want to add, edit or delete, a patch file describes the changes so you can re-apply them whenever the source file changes.<\/p>\n\n<p>Patching offers unlimited customisation and flexibility.<\/p>\n\n<p>Whatever changes you want to make, you can.<\/p>\n\n<p>The downside is you need to maintain any patches you've written.<\/p>\n\n<p>If a change is made that causes your patch to no longer apply, you'll need to update the patch.<\/p>\n\n<p>There are some patches I commonly apply to Drupal projects, but I'll try to either contribute the changes back to the Drupal so I no longer need the patch or make the change in a custom module.<\/p>\n\n<p>Sometimes, though, <a href=\"http:\/\/default\/daily\/2025\/01\/14\/patching-drupal\">patching is the only option<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "e1882c079f5b933697958ec2ac91a1b7",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>My website is built with Sculpin - a static site generator written in PHP.<\/p>\n\n<p>It uses a some of the same Symfony components as Drupal, uses Twig for templating and YAML for configuration, and has similar features like content types and taxonomies for structuring content.<\/p>\n\n<p>When I first created my website it was on Drupal 6 and upgraded to Drupal 7 before I started to take an interest in static site generators and later using Jekyll, Sculpin and Astro (and Sculpin, again).<\/p>\n\n<p>I enjoyed learning Sculpin and took it as an opportunity to learn Twig before Drupal 8, which I spoke about in <a href=\"http:\/\/default\/presentations\/test-drive-twig-with-sculpin\">the first Sculpin talk I gave<\/a>, at DrupalCamp North in July 2015.<\/p>\n\n<p>I had three Git repositories, the current Sculpin one, the Astro version, and the original Sculpin version with its first commit in March 2015 - a few months before DrupalCamp North.<\/p>\n\n<p>Static site generators keep the files in text files intead of a database, so I was wondering if it was possible to merge the repositories together and combine the information whilst keeping the same commit history so existing tags and contribtions would still apply to the original commits.<\/p>\n\n<p>In short, I was able to do it by adding the old repositories as additional remotes and using the <code>--allow-unrelated-histories<\/code> <a href=\"https:\/\/git-scm.com\/docs\/git-merge#Documentation\/git-merge.txt---allow-unrelated-histories\">option for git merge<\/a>.<\/p>\n\n<p>After fixing some minor merge conflicts, everything was merged successfully and I have [one repository containing 5,272 all commits][2], going back to 2015.<\/p>\n\n<p>This makes it older than <a href=\"http:\/\/default\/daily\/2023\/08\/08\/8-years-of-dotfiles\">my dotfiles repository<\/a>, which I started in July 2015.<\/p>\n\n<p>Similar to <a href=\"http:\/\/default\/daily\/2024\/07\/31\/why-i-use-linux\">why I use Linux<\/a>, I believe in owning your own content rather than relying on third-party platforms, so having all my content and history in one repository is great.<\/p>\n\n<p>And I learned something new about Git at the same time.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "cc353a3343b4a9ed6dc01850a09c8f76",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've recently started as a Mentor for the School of Code, working with a student on their cohort that started last week. Whilst the Bootcamp is focussed on JavaScript and node, and is currently looking at front-end development and using APIs, we've already started talking about different back-end technologies and frameworks.<\/p>\n\n<p>Something that I've believed for a while is that software development is about solving problems and different languages and frameworks are tools to use to solve the problem and achieve the desired result. The programming fundamentals that he's learning now can be applied to different languages and some may be better depending on the problem that needs to be solved.<\/p>\n\n<p>I've also said when speaking with a client this week that development is about adding business value.<\/p>\n\n<p>Whilst there is some value to doing security updates and maintenance to keep an application running and secure, I'd much rather be working on tasks that directly add value for the client like some of the tasks I've done for them recently like improving eCommerce conversions and adding new payment methods or revenue streams.<\/p>\n\n<p>It's easier for the client to justify and see the results of the work, it improves the experience for their customers or users, and it's more interesting and fulfilling for me.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "7256a5bb0e6800a548d88e2979344646",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p><a href=\"http:\/\/default\/podcast\/2-alternate-realities\">This week's episode<\/a> of the Beyond Blocks podcast is live, where I speak with Panagiotis Moutsopoulos (vensires on Drupal.org) - Drupal Backend Developer at E-Sepia.<\/p>\n\n<p>We discuss his first time DrupalCon and, more specifically, his session \"Drupal's Alternate Realities\" - a \"Birds of a Feather\" (BoF) session presenting some history, but mainly the different ways to tackle a problem in Drupal using different methodologies.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "4ddf140ca0555c2028d2bb2db394282f",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>There are three things you can do with legacy code.<\/p>\n\n<p>Refactor it, remove it or replace it.<\/p>\n\n<p>You can refactor it to make it easier to maintain and change in the future.<\/p>\n\n<p>If it's not needed, you can remove it.<\/p>\n\n<p>If it's not used, there's no need to keep it.<\/p>\n\n<p>Or you can replace it with something else.<\/p>\n\n<p>There isn't one right answer and the correct approach will depend on the situation and objective.<\/p>\n\n<p>In a future email, I'll give some examples of how I refactor legacy code.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "a3ea19c23705f4574c9b1455cc2169bc",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>As well as <a href=\"http:\/\/default\/daily\/2024\/11\/27\/nix-as-an-operating-system\">my laptop configuration<\/a>, <a href=\"http:\/\/default\/daily\/2024\/11\/30\/using-nix-for-local-application-development\">local development environments<\/a> and <a href=\"http:\/\/default\/daily\/2024\/11\/28\/running-nixos-in-the-cloud\">production server<\/a>, I've also been using Nix for something else recently.<\/p>\n\n<p>Setting up and configuring a Homelab on an old laptop.<\/p>\n\n<p>I've been able to install and configure services like Jellyfin for playing video files, Immich for photo hosting and management, Gitea as my own Git server, Vaultwarden for securely storing my passwords and Traefik as a reverse proxy.<\/p>\n\n<p>Each of these was very easy to configure with only a few lines of the Nix language and avoided a heavy use of Docker which seems common in most Homelab setups.<\/p>\n\n<p>Next, I'd like to add home automation with Home Assistant and explore running a local DNS server within my network.<\/p>\n\n<p>I'm looking forward to these, and seeing what else I can add to this setup using Nix and NixOS.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "ddffb71cbb4982c0c4d9ca78fecdcd4c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>After yesterday's post on why I prefer using Git on the command line rather than using a GUI tool, today I thought that I'd post about how I've configured Git.<\/p>\n\n<p>First, I rarely ever run the <code>git<\/code> command - I usually run a <code>g<\/code> function that I've created within my zsh configuration.<\/p>\n\n<p>Rather than being an simple alias, it's a shell function that will run <code>git status -sb<\/code> to show the current status of the repository if there are no additional arguments. If there are, such as when running <code>g add<\/code>, then this is executed as a normal Git command. (This is something that I first saw from Thoughtbot, if I remember correctly).<\/p>\n\n<h2 id=\"using-.gitconfig\">Using .gitconfig<\/h2>\n\n<p>The main part of my configuration is within Git's <code>~\/.gitconfig<\/code> file, where I can configure Git to work how I want.<\/p>\n\n<p>For example, I like to avoid merge conflicts, so I always want to use fast-forward merges whilst pulling and also to rebase by default. I can do this by adding <code>ff = only<\/code> and <code>rebase = true<\/code> to the <code>[pull]<\/code> section of my <code>~\/.gitconfig<\/code> file.<\/p>\n\n<p>I can do this manually, or running <code>git config --global pull.rebase true<\/code> will set the option but also update the file automatically.<\/p>\n\n<p>Some of the tweaks that I've made are to only allow fast-forward merges by adding <code>merge.ff = only<\/code>, automatically squash commits when rebasing by setting <code>rebase.autosquash = true<\/code>, and automatically pruning branches by adding <code>fetch.prune = true<\/code>.<\/p>\n\n<h3 id=\"simple-aliases\">Simple aliases<\/h3>\n\n<p>Another way that I configure Git is using aliases, which are also within the <code>~\/.gitconfig<\/code> file.<\/p>\n\n<p>For example, if I ran <code>git config --global alias.b \"branch\"<\/code>, then running <code>git b<\/code> would just run <code>git branch<\/code> which shortens the command and saves some time and keystrokes.<\/p>\n\n<p>I have similar one- or two letter \"short\" aliases for pushing and pulling code, and some that also set some additional arguments such as <code>aa<\/code> for <code>add --all<\/code> and <code>worktrees<\/code> for <code>worktree list<\/code>.<\/p>\n\n<h3 id=\"more-complicated-aliases\">More complicated aliases<\/h3>\n\n<p>Aliases can be more complex if needed by prefixing it with a <code>!<\/code>, meaning that it executes it as a shell command.<\/p>\n\n<p>This means that I can have <code>repush = !git pull --rebase && git push<\/code> to chain two separate Git commands and combine them into one, and <code>ureset = !git reset --hard $(git upstream)<\/code> which executes the full command, including another alias as part of it.<\/p>\n\n<p>I also have <code>issues = !gh issue list --web<\/code> and <code>pulls = !gh pr list --web<\/code> to open the current repository's GitHub issues or pull requests respectively, which can be done as it's not limited to just running <code>git<\/code> commands.<\/p>\n\n<h3 id=\"custom-functions\">Custom functions<\/h3>\n\n<p>Finally, if an alias is getting too long or complex, then it can extracted to it's own file.<\/p>\n\n<p>Any executable file within your <code>$PATH<\/code> that starts with <code>git-<\/code> will automatically become a Git command.<\/p>\n\n<p>One example that I have is <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/blob\/2b20cd1e59ae3b1fa81074077e855cbdfa02f146\/bin\/bin\/git-cm\">git-cm<\/a> which, similar to the <code>g<\/code> function`, is a bash script that checks for any arguments passed to it and runs a slightly different command. It achieves the same thing as if it were an alias, but it does make it easier to write and maintain as it's in a separate file.<\/p>\n\n<p>These are just some examples. If you want to see my entire configuration, then check out <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/tree\/2b20cd1e59ae3b1fa81074077e855cbdfa02f146\/roles\/git\/files\">my dotfiles repository on GitHub<\/a>.<\/p>\n\n<p>How have you configured Git for your workflow? Reply to this email and let me know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "4d3892ad51836bdd33e47e7043615d75",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "137f467c7894dcf4d534ab6c7572567a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Code review with pull and merge requests is great for open-source but not for development teams or soloists.<\/p>\n\n<p>On an open-source project, the code changes are most likely being submitted by someone you don't know and don't work with regularly, so having a step to review the code prior to merging it and decide if you want to take on the responsibility of maintaining it is a big decision.<\/p>\n\n<p>On a development team, you work closely with the person submitting the change request and you have a shared responsibility and ownership of the code being added. The person isn't going to submit their change and not be seen again.<\/p>\n\n<p>It takes time for code to be reviewed, which means it takes longer for the change to be released to users.<\/p>\n\n<p>If you're a soloist, are you going to submit a request for you to review your own code?<\/p>\n\n<p>If you don't need to do code review on your team, do you need to create feature or topic branches?<\/p>\n\n<p>I'd suggest sticking to one canonical branch and doing trunk-based development instead.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "54fdebe3f9556a6d18a8f24c72f72652",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>This week on the <a href=\"http:\/\/default\/podcast\">Beyond Blocks podcast<\/a>, I'm joined by Dan Leech - a PHP Developer and open-source project creator.<\/p>\n\n<p>He and I recently gave talks at the PHP South West meetup, where Dan introduced a new project - PHP-TUI - for building terminal user interfaces (TUIs) with PHP.<\/p>\n\n<p>I use one of Dan's other open-source projects - Phpactor - within Neovim, and he also presented at PHP South Wales about PHPBench, so it was great to discuss and learn more about these in this episode.<\/p>\n\n<p><a href=\"http:\/\/default\/podcast\/6-dan-leech-php-tui\">Listen to the episode now<\/a>, and I'll be back with more in the New Year.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3cc8c054a1675b2ab1a1ec63dd5f49ad",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've noticed a lot of Developers recently adopting SQLite for their database and I wonder why this is.<\/p>\n\n<p>Laravel changed their default database to SQLite for local development.<\/p>\n\n<p>It simplifies the development environment as there's no need for a separate database like MySQL or MariaDB but, if you'll be using one of those in production, won't that cause more issues when you migrate your local application?<\/p>\n\n<p>Drupal supports using SQLite, but, other than for <a href=\"http:\/\/default\/atdc\">my automated testing course<\/a>, or when running automated tests, I've always used a MySQL or MariaDB database.<\/p>\n\n<p>Maybe this is something to keep an eye on and potentially use more for some scenarios in the future.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "91a7ebf19cb056c110911fc721129f1a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>With most things, it's easy to see where an issue is. There's only one way to build a car or a mobile phone, for example.<\/p>\n\n<p>With other things, such as software, it's not as clear.<\/p>\n\n<p>There are usually multiple ways to achieve the same result, but not all implementations are equal.<\/p>\n\n<p>It's important to understand if an issue is caused by the software itself or how it's been implemented.<\/p>\n\n<p>Some approaches may be quicker, better for end-user experience or more performant.<\/p>\n\n<p>Each has its own advantages and disadvantages.<\/p>\n\n<p>What was the correct solution then may not be the best one now.<\/p>\n\n<p>Priorities may have changed or new approaches could be available.<\/p>\n\n<p>You know more now about the problem that's being solved than you did at the time.<\/p>\n\n<p>One of the original discarded solutions could now be the best option, or a new option that wasn't previously available that solves the problem in a different way.<\/p>\n\n<p>The software may be capable of achieving what you want - it may just need to be configured in a different way.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "838a5e92a344c70d064478815bc8dd16",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A quote I heard from DrupalCon, made by my former colleague Tim Lehnen - CTO at the Drupal Association - when talking about contribution:<\/p>\n\n<blockquote>\n <p>Contribution is what makes Drupal thrive.<\/p>\n \n <p>Drupal is built by people.<\/p>\n \n <p>No one entity, no one person builds Drupal.<\/p>\n \n <p>It's you who builds Drupal.<\/p>\n<\/blockquote>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "957b36c5f9d9b70a87a9a60d1f31f9ab",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>As well as <a href=\"http:\/\/default\/daily\/2024\/06\/03\/writing-comments-first\">writing comments first<\/a>, when writing tests, I sometimes like to write my tests backwards and start by writing the assertions first.<\/p>\n\n<p>I know what I want to assert in the test, so it's an easy place to start.<\/p>\n\n<p>I'll run it, see the error, fix it and continue working backwards.<\/p>\n\n<p>For example, I could start with this:<\/p>\n\n<pre><code class=\"php\">public function testOnlyPostNodesAreShown(): void {\n $assert = $this->assertSession();\n $assert->pageTextContains('Post one');\n $assert->pageTextContains('Post two');\n $assert->pageTextNotContains('This is not a post');\n}\n<\/code><\/pre>\n\n<p>This test will fail when I run it, but it makes me think about what I need to do to fix the error and how to do so in the best way.<\/p>\n\n<p>In this case, I need to make a request to the page that should render the text:<\/p>\n\n<pre><code class=\"php\">public function testOnlyPostNodesAreShown(): void {\n $this->drupalGet('\/blog');\n\n $assert = $this->assertSession();\n $assert->pageTextContains('Post one');\n $assert->pageTextContains('Post two');\n $assert->pageTextNotContains('This is not a post');\n}\n<\/code><\/pre>\n\n<p>This will still fail, as I also need to create the required posts:<\/p>\n\n<pre><code class=\"php\">public function testOnlyPostNodesAreShown(): void {\n PostBuilder::create()->setTitle('Post one')->getPost();\n PostBuilder::create()->setTitle('Post two')->getPost();\n\n $this->createNode([\n 'title' => 'This is not a post',\n 'type' => 'page',\n ]);\n\n $this->drupalGet('\/blog');\n\n $assert = $this->assertSession();\n $assert->pageTextContains('Post one');\n $assert->pageTextContains('Post two');\n $assert->pageTextNotContains('This is not a post');\n}\n<\/code><\/pre>\n\n<p>Now the test passes.<\/p>\n\n<p>Doing test-driven development keeps my code clean and minimal, and I find this approach keeps my test clean, too.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3bdc81d1ff8f05f8b1beb3069dfdd370",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Last week, it was announced that Newport City Council's (my local council) website is now running on LocalGov Drupal - a distribution for council websites.<\/p>\n\n<p>This increases the total to 44 councils in the UK and Ireland.<\/p>\n\n<p>Newport is the first Welsh council to use it and the first LocalGov website to be bilingual, serving content in both English and Welsh.<\/p>\n\n<p>It's great to see Drupal adoption continue to grow in Wales and within the Welsh public sector, and LocalGov being adopted by more councils across the UK and Ireland.<\/p>\n\n<p>I think LocalGov Drupal is a great project and one <a href=\"https:\/\/github.com\/localgovdrupal\/localgov_alert_banner\/pull\/225\">I've contributed to previously<\/a>, and I plan to do more in the future.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "2955d4cb562bc0a9ba0456bc040c629b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>It's shown in the examples of the <a href=\"http:\/\/default\/daily\/2023\/11\/24\/are-conventional-commits-worth-it\">conventional commits specification<\/a> as part of the optional footer data.<\/p>\n\n<p>But is it useful?<\/p>\n\n<p>It can be if your issue tracker is linked to your Git repository and you can click the issue ID in a commit message and see the issue.<\/p>\n\n<p>But, how often do teams change issue-tracking software or the project is passed to a different company that uses a different issue tracker?<\/p>\n\n<p>That makes the issue IDs that reference the old IDs useless as no one has access to the issues it references.<\/p>\n\n<p>I'd recommend putting as much information in the commit message itself and not relying on it being in an external source, like an issue tracker.<\/p>\n\n<p>The Git log and commit messages will remain even if a different issue tracker is used, or a different team starts working on the project, and that additional information isn't lost.<\/p>\n\n<p>I'm not against putting the issue ID in the commit message but don't do it instead of writing a descriptive commit message.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "1029a860138fe970164e891ad1617d01",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "f40826212e5c798e718edd8ad155d3c0",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I took a bit of time off from these emails whilst I was preparing for the first PHP Stoke event which happened last week in Stoke-on-Trent.<\/p>\n\n<p>It was a great event with around 35 attendees and two other speakers as well as myself.<\/p>\n\n<p>The <a href=\"http:\/\/default\/presentations\/things-you-should-know-about-php\">latest version of my slides are online<\/a> as well <a href=\"http:\/\/default\/things-about-php\">some updated resources<\/a>.<\/p>\n\n<p>My next talk will be at the Norfolk Developers conference next month where I'll be presenting an updated version of <a href=\"http:\/\/default\/presentations\/taking-flight-with-tailwind-css\">Taking Flight with Tailwind CSS<\/a>.<\/p>\n\n<p>If you have a topic idea for a talk or would like me to speak at your meetup or conference, please get in touch.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "c6186e7dbf6c19f8512dc083f70d7840",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>\"Frequency reduces difficulty\" is a phrase I heard on a podcast today, though I have heard it before.<\/p>\n\n<p>It's a better way of saying, \"If it hurts, do it more often\".<\/p>\n\n<p>If software deployments and releases are large, risky, and contain bugs, break them into smaller, less-risky deployments and release them more often.<\/p>\n\n<p>If you get a lot of merge conflicts in Git, merge your code more frequently. Make your topic branches as short-lived as possible, or try trunk-based development and avoid topic branches by default.<\/p>\n\n<p>If writing tests is difficult and takes time, persevere. It will get easier the more you do it, and you'll save time in the future by releasing fewer bugs and being able to refactor safely.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "48855aeeae21e4c8f58c4898ddd9fea4",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>The Override Node Options module that I said yesterday is used on 35,000 websites isn't a complicated module.<\/p>\n\n<p>It doesn't add a lot of features.<\/p>\n\n<p>It solves a problem by adding additional permissions to Drupal that site administrators can assign to user roles and not need to give a more global permission like \"administer nodes\".<\/p>\n\n<p>The problem being solved is allowing users to set node options, like the created date or published status, in a secure way.<\/p>\n\n<p>If it wasn't solving a problem, it wouldn't be used on 35,000 Drupal websites.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "1c6afa84bf0230b6fc71cdc42239c6e2",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>One of my favorite features that was introducted in PHP 8 was promoted constructor properties.<\/p>\n\n<p>If I'm passing arguments into a constructor, I can declare a visibility and it will be promoted to a property on the class.<\/p>\n\n<p>Here's an example of a value of a data transfer object that accepts a sort code and account number as strings:<\/p>\n\n<pre><code class=\"language-php\">class AccountDetails {\n\n public function __construct(\n public string $accountNumber,\n public string $sortCode,\n ) {}\n\n}\n<\/code><\/pre>\n\n<p>Without promoted constructor properties, I'd need to create the properties and assign them manually, and I'd have this:<\/p>\n\n<pre><code class=\"language-php\">class AccountDetails {\n\n public string $accountNumber;\n\n public string $sortCode;\n\n public function __construct(\n string $accountNumber,\n string $sortCode,\n ) {\n $this->accountNumber = $accountNumber;\n $this->sortCode = $sortCode;\n }\n\n}\n<\/code><\/pre>\n\n<p>Whilst text editors and IDEs can create the properties automatically, I prefer this as it's less code, more readable and easier to understand.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "6e7b86e1b92a964e048c10b4a1bb65da",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've been using Drupal since 2008 and built the first version of my website with Drupal 6 before updating it to Drupal 7.<\/p>\n\n<p>Around that time, I discovered static site generators and built the next version of my website with Jekyll.<\/p>\n\n<p>I liked how I could write content in simple plain files, export them to HTML and upload them to a server.<\/p>\n\n<p>Static websites are fast and secure.<\/p>\n\n<p>There was no need for PHP or a database. Just a simple web server like Apache or Nginx that can serve static files.<\/p>\n\n<p>I later switched to Sculpin, a static site generator written with PHP, which has a lot of similarities with Drupal.<\/p>\n\n<p>But what if you want the power of Drupal with the benefits of a static website?<\/p>\n\n<p><a href=\"https:\/\/www.drupal.org\/project\/tome\">Tome<\/a> is a module that turns Drupal into a static site generator.<\/p>\n\n<p>Drupal works the same locally with access to all the usual core functionality and contrib modules and themes from Drupal.org, but you export everything to static files - the same as using Jekyll or Sculpin.<\/p>\n\n<p>Sam Mortenson, the creator of the Tome module discussed it on <a href=\"http:\/\/default\/podcast\/19-sam-mortenson\">our episode of the Beyond Blocks podcast<\/a> where we also talked about single file components.<\/p>\n\n<p>If you want the power of a flexible content management system and the performance and security benefits of a static website, Tome gives you the best of both worlds.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "2b68ef78311dde7624d8a9f3bf2aecd5",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I started developing websites in HTML and CSS in 2007 before adopting PHP and Drupal in 2008.<\/p>\n\n<p>Since then, I've used Drupal 6, 7, 8, 9 and 10 to deliver applications for clients, agencies and in-house teams.<\/p>\n\n<p>I also use Symfony and Laravel, as well as JavaScript, TypeScript and Vue.js.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>There isn't one outright \"best\" programming language, CMS or framework.<\/p>\n\n<p>It depends on the project and its requirements as well as the knowledge and experience of the team working on it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "6878d25dc442270921c029ddd00724c8",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>As with client applications, I recommend releasing new versions of open-source software as often as possible.<\/p>\n\n<p>This makes it easier for users to update to newer versions as they can easily see the changes, and less risky as it's easier to roll back or diagnose the problem if there is an issue.<\/p>\n\n<p>If you've added a feature or fixed a bug, fewer people will likely use it if it's not within a released version of your project and sat on a development branch or waiting for a tagged release. This means they're not benefiting from the changes and getting value from them.<\/p>\n\n<p>You can add feature flags to hide work-in-progress features that you don't want people to use yet, but you don't want to block yourself from releasing a new version until it's complete.<\/p>\n\n<p>Regular releases encourage smaller and simpler changes rather than larger and more complex - and potentially breaking - changes.<\/p>\n\n<p>I like to have a release day each month to release new versions of my open-source projects, which works well for me.<\/p>\n\n<p>I have a recurring task on my To Do list to review my projects and tag and release any new versions that are needed.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "2a66b63393fa72ffccd1e378ef840bca",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Yesterday I replied to <a href=\"https:\/\/x.com\/ianmiell\/status\/1304103008242991111\">a post on X<\/a>:<\/p>\n\n<blockquote>\n <p>I have worked on many teams that use CI tooling and call their process CI, but I have never seen CI actually done as defined on Wikipedia:<\/p>\n \n <p>\"CI is the practice of merging all developers' working copies to a shared mainline several times a day\"<\/p>\n<\/blockquote>\n\n<p><a href=\"http:\/\/default\/blog\/continuous-integration-vs-continuous-integration\">I've written about this before<\/a> and I think the term CI or CI\/CD is one of the most misused or misleading in software development.<\/p>\n\n<p>CI, or continuous integration, is, as the post days, the process of everyone merging their changes at least once, or usually several, times a day.<\/p>\n\n<p>It isn't something that is configured or created - it's a process.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>You can do CI without a CI pipeline and vice versa.<\/p>\n\n<p>You can have a CI pipeline but not do continuous delivery or deployment.<\/p>\n\n<p>What most people think of as CI or CI\/CD is a set of automated checks that run when code is updated - usually on a feature or topic branch.<\/p>\n\n<p>Whilst important, it's not \"CI\".<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "44876b1c656f7fa3e4611f4f47e7c361",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Last Friday and Saturday, I was happy to attend and present two sessions at DrupalCamp Ghent in Belgium.<\/p>\n\n<p>I was able to present talks on automated testing and Tailwind CSS, which were my 99th and 100th talks, respectively!<\/p>\n\n<p>The sessions were streamed live, or the slides and previous recordings <a href=\"http:\/\/default\/dcg\">are on my website<\/a>.<\/p>\n\n<p>It was a great event and it's always nice to see and meet new people within the Drupal community, to see new places and share knowledge by giving presentations.<\/p>\n\n<p>Reply and let me know if you have any feedback on the sessions or if you'd like me to present a talk or workshop at your event.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "666722719a2759d76794a08b3c2837fa",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>When giving talks and workshops or coaching on automated testing and test-driven development, some people may not have written tests before and aren't familiar with the structure or know where to begin.<\/p>\n\n<p>In the workshops I ran for DrupalCamp London and DrupalCamp NYC, I wanted to cover this first before writing any implementation code.<\/p>\n\n<p>Where do you put a test class, and what does it contain?<\/p>\n\n<p>How do you run the tests, and how can you make it pass or fail?<\/p>\n\n<h2 id=\"what-we-did\">What we did<\/h2>\n\n<p>To start, we wrote a test for existing functionality within Drupal core - anonymous users can visit the front page.<\/p>\n\n<p>This is the whole test:<\/p>\n\n<pre><code class=\"language-php\"><?php\n\nnamespace Drupal\\Tests\\my_module\\Functional;\n\nuse Drupal\\Tests\\BrowserTestBase;\nuse Symfony\\Component\\HttpFoundation\\Response;\n\nclass MyModuleTest extends BrowserTestBase {\n\n protected $defaultTheme = 'stark';\n\n \/** @test *\/\n public function the_front_page_loads_for_anonymous_users() {\n $this->drupalGet('<front>');\n\n $this->assertResponse(Response::HTTP_OK);\n }\n\n}\n<\/code><\/pre>\n\n<p>This is a test someone can write, run and see the test pass.<\/p>\n\n<p>They can then experiment by changing the values to make the test fail in different ways.<\/p>\n\n<h2 id=\"what-next%3F\">What next?<\/h2>\n\n<p>Then, we tested anonymous users cannot access the administration pages, which is also already the case in Drupal core, and then authenticated users with the correct permissions could access them.<\/p>\n\n<p>People were getting the idea by now, and we moved on to writing and testing some of our own code.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "af41a18ab3e2e252ab86e7243333ad9f",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A common cause of not refactoring code is a lack of test coverage.<\/p>\n\n<p>Why change some code if it works? What if we break it whilst refactoring it and introduce a regression?<\/p>\n\n<p>If the code has accompanying automated tests, this can't happen.<\/p>\n\n<p>The tests should be passing before starting a refactor and should be passing once the refactor is complete.<\/p>\n\n<p>If the tests fail, the refactor was unsuccessful and should be reverted.<\/p>\n\n<p>If you have a CI pipeline, a failing test should break the pipeline and prevent any further steps if you're doing continuous delivery or deployment whilst signalling to the Developer their refactor was unsuccessful and the pipeline needs to be fixed.<\/p>\n\n<p>Without tests, the code could be broken during the refactor, and we wouldn't know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "aa56bdd02fef215659f339a51ad72312",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>If <a href=\"http:\/\/default\/daily\/2025\/02\/18\/conflicts\">feature branches cause conflicts<\/a>, what is the alternative?<\/p>\n\n<p>Don't branch.<\/p>\n\n<p>Keep all changes on a single branch and avoid merge conflicts between branches by not having branches.<\/p>\n\n<p>But how can you avoid releasing changes before they are ready?<\/p>\n\n<p>You can have a local branch with your changes that you don't push and rebase locally, but then <a href=\"http:\/\/default\/daily\/2025\/02\/17\/ci-cd\">you're not doing continuous integration<\/a> and you're just as likely to get conflicts and have incompatible code.<\/p>\n\n<p>One of the main benefits of trunk-based development, where everything is on a single branch, is that everyone's work always works together.<\/p>\n\n<p>The better option is to use feature toggles (aka feature flags).<\/p>\n\n<p>Wrapping code in feature toggles separates deploying the code from releasing the feature.<\/p>\n\n<p>The code can be released, but the feature isn't active until the toggle is enabled.<\/p>\n\n<p>This means everyone can work on the same branch and continuously deploy changes whilst being selective about the features that are visible to the end users.<\/p>\n\n<p>If you have multiple environments, the same code can be used with different toggles enabled to allow for testing different features.<\/p>\n\n<p>Then, once the feature is ready to be enabled, there is no deployment needed.<\/p>\n\n<p>The code is already there - it just needs to be enabled.<\/p>\n\n<p>For Drupal, there is a <a href=\"https:\/\/www.drupal.org\/project\/feature_toggle\">Feature Toggle module<\/a> and <a href=\"https:\/\/www.drupal.org\/project\/feature_toggle_twig\">an extension that I wrote<\/a> that make it easy to use and manage feature toggles by just logging into the admin UI and selecting the active toggles you want.<\/p>\n\n<p>And, if an issue is discovered, it can also be easily disabled <a href=\"http:\/\/default\/daily\/2025\/02\/19\/back-or-forward\">without needing to roll back to the previous version<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "118c906a95ca0d083c79dec04035d210",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Instead of <a href=\"http:\/\/default\/daily\/2024\/05\/31\/putting-glue-on-pizza\">using AI to autocomplete code<\/a> or <a href=\"http:\/\/default\/daily\/2024\/10\/08\/ai-in-drupal\">perform tasks within your Drupal website<\/a>, I've seen people using AI as a virtual pair programming partner.<\/p>\n\n<p>Similar to using a search engine or finding results on websites like Stack Overflow, but also being able to ask follow-up questions and for clarification as part of a conversation.<\/p>\n\n<p>You're still relying on its answers and advice to be correct, but using AI as a learning tool seems like a sensible approach if you don't have a non-AI pairing partner available.<\/p>\n\n<p>Just make sure to validate and verify whatever it gives you.<\/p>\n\n<p>Once you've committed it, you're responsible for it.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "e41c8f0126dd0649815f7d2f25e91bbc",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Following the announcement at DrupalCon, the official announcement has been released regarding Drupal 7's end-of-life extension:<\/p>\n\n<blockquote>\n <p>Today, we are officially announcing that Drupal 7 will reach its end of life on January 5, 2025.<\/p>\n \n <p>With this final extension, the Drupal Security Team is also adjusting the level of support provided.<\/p>\n \n <p>This will be the final extension.<\/p>\n<\/blockquote>\n\n<p>If you're involved with one of the ~400,000 Drupal 7 websites (according to https:\/\/www.drupal.org\/project\/usage\/drupal), what does the additional year and a half of Drupal 7 mean to you?<\/p>\n\n<p>Will you use the time to find the budget to upgrade, assemble a migration plan, port any missing contributed modules or refactor your custom modules or theme to make it easier to upgrade?<\/p>\n\n<p>Is it different knowing it\u2019s the final extension compared to the previous time when it wasn\u2019t known if it would be extended again?<\/p>\n\n<p>If you're stuck on Drupal 7 or staying on it into 2024 or 2025, what's stopping you from upgrading?<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3d0846676f872cb7c1f67b9afcb995ac",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I recently saw a social media post that said something like \"Don't add blank lines within functions. They take up space and don't add anything\".<\/p>\n\n<p>I may have misremembered the exact wording, but that was the gist of it.<\/p>\n\n<p>I like to add blank lines within my functions to create and add separation between logical blocks of code.<\/p>\n\n<p>Whilst this doesn't add anything for a computer parsing and serving the code, or a CI pipeline performing checks, it makes the code easier for people to read and understand which, it my opinion, adds its own value - even if the functionality is the same.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "d138c01e7e0e6b18b39148606ce5111a",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A major addition to my Git workflow has been the ability to interactively add hunks of code to be committed.<\/p>\n\n<p>There's <code>git add -i<\/code> to interactively add, though I usually go straight to <code>git add -p<\/code> to use <code>patch<\/code>.<\/p>\n\n<p>This will ask you to confirm if you want to add each hunk to the commit (a.k.a. the staging area) or not.<\/p>\n\n<p>For example, here's the prompt I get whilst working on the post for this email:<\/p>\n\n<pre><code class=\"shell\">diff --git a\/source\/_daily_emails\/2024-05-06.md b\/source\/_daily_emails\/2024-05-06.md\nindex 42fe48f..ef36a2b 100644\n--- a\/source\/_daily_emails\/2024-05-06.md\n+++ b\/source\/_daily_emails\/2024-05-06.md\n@@ -4,10 +4,12 @@ date: 2024-05-06\n permalink: daily\/2024\/05\/06\/interactive-staging\n tags:\n - software-development\n- # - drupal\n- # - php\n- # - podcast\n+ - git\n cta: ~\n snippet: |\n TODO\n ---\n+\n+A major addition to my Git workflow has been the ability to interactively add hunks of code to be committed.\n+\n+There's `git add -i` to interactively add, though I usually go straight to `git add -p` to use `patch`.\n+There's `git add -i` to interactively add, though I usually go straight to `git add -p` to use `patch`.\n(1\/1) Stage this hunk [y,n,q,a,d,s,e,?]?\n<\/code><\/pre>\n\n<p>I can add the whole hunk, split it into smaller hunks, add all the hunks in the file or ignore this hunk and later hunks in the file.<\/p>\n\n<p>Then the process is repeated for any following hunks.<\/p>\n\n<p>This means I can add the relevant hunks to craft the commit I want and I can keep my commits small and meaningful, and easy to revert if there is an issue.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "4f660d322ca2ed3306e781c6c10053a1",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've recently decided I'm going to open source <a href=\"http:\/\/default\/presentations\/building-build-configs\">Build Configs tool<\/a> that I use to generate build configuration files for Drupal, Sculpin and Fractal projects.<\/p>\n\n<p>Inspired by <a href=\"http:\/\/default\/presentations\/working-with-workspace\">Workspace<\/a> and others, and based on previous versions of similar tools - most recently by <a href=\"https:\/\/github.com\/ALT-F4-LLC\/build-configs\">TheAltF4Stream's project with the same name<\/a> (which is written in Rust and supports different template types) - I've been using this tool to manage configuration files for various personal, client and open-source projects.<\/p>\n\n<p>Before I open-source it, there are some changes I'd like to make, such as renaming some template types and updating the format and keys within the configuration file.<\/p>\n\n<p>Changes to the configuration file would be a breaking change and, whilst it's only me using it, I want my other projects to keep working and for me to continue supporting the prior versions - at least for now, so I want to make sure any changes are backward compatible.<\/p>\n\n<h2 id=\"how-it-works\">How it works<\/h2>\n\n<p>There are four steps performed when generating files for a project:<\/p>\n\n<ul>\n<li>Create a final configuration object from the project's configuration file as well as any defaults.<\/li>\n<li>Validate the final configuration.<\/li>\n<li>Create a list of files to generate.<\/li>\n<li>Generate the files.<\/li>\n<\/ul>\n\n<p>If I change <code>sculpin<\/code> to <code>sculpin-site<\/code> in a configuration file, for example, it will fail the validation step.<\/p>\n\n<p>But, I have an opportunity within the first step to perform any normalisation that's needed and to provide a compatibility layer - such as changing <code>sculpin-site<\/code>, which is an invalid value, to <code>sculpin<\/code>.<\/p>\n\n<p>I also renamed <code>symfony<\/code> to <code>symfony-cli<\/code> by performing the same step.<\/p>\n\n<p>This means the validation step will receive valid data that it can use and the new changes have been encapsulated within a single step of the process. I haven't needed to change any code elsewhere.<\/p>\n\n<p>I can also add deprecation warnings if legacy values are used.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Similar to feature flags, this is temporary code that will later be removed when I'm ready to remove the compatibility layer, similar to how <code>drupal_set_message()<\/code> was deprecated and changed to use the <code>Messenger<\/code> service before being removed in Drupal 9.<\/p>\n\n<p>In the future, I can refactor the internal logic to use a different approach and when I'm ready, eventually remove the compatibility layer and tag a new major version with the breaking changes.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "a38ce17b89e3700c2d33876324be5747",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I started using the Nix package manager <a href=\"https:\/\/github.com\/opdavies\/dotfiles.nix\/commit\/af1d8d37\">in September 2022<\/a> as a replacement for Ansible.<\/p>\n\n<p>Since then, I've switched to daily-driving NixOS as my main operating system and Home Manager to manage my dotfiles (configuration files for managing application settings, like Neovim, tmux and Alacritty).<\/p>\n\n<p>A benefit I didn't initially think of was that now I can write all my configuration files in the Nix language.<\/p>\n\n<p>I don't need to write YAML, TOML, INI or JSON configuration files. Nix and Home Manager will convert it for me.<\/p>\n\n<p>For example, see <a href=\"https:\/\/github.com\/opdavies\/dotfiles\/blob\/172b7c9ca61d2dd6ffdc967af9102b1ca24edd81\/nix\/modules\/home-manager\/git.nix#L25\">my Git configuration written in Nix<\/a> which is converted and written to .gitignore.<\/p>\n\n<p>And, if there isn't a built-in module for what I need, there are functions like <code>toJSON<\/code> that will convert Nix code to JSON that I can write to a file.<\/p>\n\n<p>This is also great if a program changes its configuration file language, which Alacritty did recently.<\/p>\n\n<p>They changed from YAML to TOML and I didn't need to change anything.<\/p>\n\n<p>Nix has become my one configuration language to rule them all.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "afe58adbc88026446b4472112f8ea2a7",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Computers don't care how code is written or formatted, but humans do.<\/p>\n\n<p>A computer will parse and execute your code whether it's written on a single line or multiple.<\/p>\n\n<p>So, don't optimise for computers.<\/p>\n\n<p>Optimise your code for humans.<\/p>\n\n<p>People read more code than they write, so write your code in a readable way so it's easy for people to read and understand - not the computer.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "00288ea28683ef7e8d770a0109344500",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Surprisingly, it's been two and a half years since I last did a live coding stream.<\/p>\n\n<p>As well as talk recordings and demos, I did a few live streams back then, working on the \"Test-Driven Drupal\" project and submitting a merge request to Drupal core.<\/p>\n\n<p>It's been something that I'd like to get back into and pick up again, and I plan on doing that within the next few weeks.<\/p>\n\n<p>I have a new freelance project due to start in December but getting back into streaming seems like a good way to make sure that I put aside time for open-source and side projects, as well as for writing longer form blog posts and hopefully starting to prepare more meetup and conference talks again.<\/p>\n\n<p>I'll be streaming again on my YouTube channel, so if you'd like to be notified when I do, <a href=\"https:\/\/www.youtube.com\/channel\/UCkeK0qF9HHUPQH_fvn4ghqQ?sub_confirmation=1\">please subscribe<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "ed9692196aea3236ab447bca233fd4bc",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <h2 id=\"what-is-a-changelog%3F\">What is a Changelog?<\/h2>\n\n<p>A Changelog is a file that documents changes made to a codebase.<\/p>\n\n<p>In its simplest form, it's a plain text file within a code repository, or it can be written in Markdown or reStructuredText or kept within a separate tool like Confluence or Sharepoint. Regardless of where it's kept, the main thing is the content.<\/p>\n\n<p>It's not a copy of the Git log. It's a summary of the changes to be read by humans and for them to see what's changed, not a list of Git commits.<\/p>\n\n<p>If you're considering using my open-source package, you can see what changes I've released, when, and what changes are due to release.<\/p>\n\n<h2 id=\"how-is-it-structured%3F\">How is it structured?<\/h2>\n\n<p>Each version should have a heading specifying the version number and\/or release date and a list of changes grouped by their type - whether something was added, changed, deprecated, removed, etc. This works well if you use conventional commits!<\/p>\n\n<p>I like to follow a format called <a href=\"https:\/\/keepachangelog.com\">Keep a Changelog<\/a>.<\/p>\n\n<p>The headings link to a diff so you can see all of the commits and changes made to the code, and there's an <code>Unreleased<\/code> section that shows commits that have yet to be tagged and released.<\/p>\n\n<p>If you need to present changes to a review or approval board, having an easy-to-read list of changes, separated by their type, is a much clearer format than a list of Git commits.<\/p>\n\n<h2 id=\"when-should-it-be-updated%3F\">When should it be updated?<\/h2>\n\n<p>I recommend continually updating the Changelog rather than leaving it to just before a deployment. It's quick to add it to the <code>Unreleased<\/code> section as part of the commit and update the headings later.<\/p>\n\n<h2 id=\"can-i-see-an-example%3F\">Can I see an example?<\/h2>\n\n<p>Sure. See the one I added to the <a href=\"https:\/\/github.com\/opdavies\/national-rail-enquiries-feed-parser\/blob\/main\/CHANGELOG.md\">National Rail Enquiries feed parser library<\/a> I've been working on or the <a href=\"https:\/\/git.drupalcode.org\/project\/tailwindcss\/-\/blob\/5.x\/CHANGELOG.md\">Tailwind CSS starter kit theme for Drupal<\/a>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "e760bef0560b6cb431513a24b2eb0212",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>In some of my first emails to this list, I wrote about <code>just<\/code> and <code>run<\/code> files.<\/p>\n\n<p>Both allow you to write project-specific aliases and commands. For example, instead of running <code>docker compose exec php phpunit<\/code>, you run <code>just test<\/code> or <code>run test<\/code>.<\/p>\n\n<p>While <code>just<\/code> uses a simple Makefile-like syntax, it requires its own program, so it needs to be installed.<\/p>\n\n<p>While I have it installed locally, I've had instances where CI pipelines have failed because they can't download and install <code>just<\/code> and not because of an error in my code.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>A <code>run<\/code> script is a file of Bash functions.<\/p>\n\n<p>Because it's written in Bash, it runs anywhere without installing additional dependencies.<\/p>\n\n<p>If a CI pipeline fails, which is less often, it's due to a failure within my code and not because of a download error.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "ef1660e036d0cc4b520bdd563fad7f86",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>With Drupal 7's end-of-life date of the 5th of January 2025 quickly approaching, I've recently seen again a number of companies offering support for Drupal 7 after its end-of-life date.<\/p>\n\n<p>I've seen the same in corporate IT environments where they're running versions of software post their EOL date, so it's not only Drupal 7, but I wonder if this is a good thing?<\/p>\n\n<p>Is this deterring companies from upgrading if they know this is an option, or should everyone upgrade and we can move forward from Drupal 7 and other end-of-life software?<\/p>\n\n<p>According to <a href=\"https:\/\/www.drupal.org\/project\/usage\/drupal\">https:\/\/www.drupal.org\/project\/usage\/drupal<\/a>, there are still 281,000 active Drupal 7 installations.<\/p>\n\n<p>It's considerably less than before - this time last year, it was over 380,000 installations - but it's still a lot of Drupal 7 being used.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "e3227fd692a43f0f5a3b4f9afed9df94",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A common misunderstanding for new Developers is that Git and GitHub are the same thing, but they aren't.<\/p>\n\n<p>Git is decentralised, so doesn't rely on using external repositories on services like GitHub, GitLab or Bitbucket.<\/p>\n\n<p>You can run <code>git init<\/code> and use it locally without pushing to any remote services.<\/p>\n\n<p>These services also add extra terminology, such as forks, syncing and pull or merge requests which aren't part of Git itself.<\/p>\n\n<p>This can cause confusion, which is why <a href=\"http:\/\/default\/daily\/2022\/08\/23\/git-gui-command-line\">I think it's important to learn Git itself<\/a> instead of relying on external services or desktop apps.<\/p>\n\n<p>And, if you're going to use a remote repository, consider something like Gitea, which you can host yourself and keep control of your data.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "22be20101f09fc0d4e10cd3c6b867b76",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Today, I made my <a href=\"https:\/\/github.com\/opdavies\/drupal-module-template\">Drupal module template<\/a> compatible with Drupal 10.<\/p>\n\n<p>Because Drupal 10 is developed from Drupal 9, the changes between the two and the changes required are minimal.<\/p>\n\n<p>In this case, I only needed to change the <code>core_version_requirement<\/code> in <code>drupal_module_template.info.yml<\/code> from <code>^9<\/code> to <code>^9 || ^10<\/code> - meaning the template works for both Drupal 9 and 10, so there's no need for different versions.<\/p>\n\n<p>If it used any deprecated code that was removed before Drupal 10, I'd have needed to update it to use the Drupal 10-compliant code.<\/p>\n\n<p>Updating to new major Drupal versions is much easier than it used to be!<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "d9902d3f03d57f8eef7e67d29133f05f",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've been a Drupal and Linux user for a long time, as well as using command-line tools and utilities like Neovim and tmux.<\/p>\n\n<p>Since September 2022, I've used NixOS as my daily operating system on my computers.<\/p>\n\n<p>Based on similar programs, in particular by ThePrimeagen, I wrote and use a program to automatically open tmux sessions and run initial commands for any project in my Code directory.<\/p>\n\n<p>In October, he released <a href=\"https:\/\/github.com\/ThePrimeagen\/tmux-sessionizer\">tmux-sessionizer as its own package<\/a>.<\/p>\n\n<p>There is a <code>tmux-sessionizer<\/code> already in nixpkgs, but <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/nix-config\/src\/commit\/28d75ce6b85e0852aa3f8454bab4f2206ad7e50a\/pkgs\/tmux-sessionizer\/default.nix\">I wrote my own derivation<\/a> to use Prime's.<\/p>\n\n<p>There was also this problem in the code:<\/p>\n\n<pre><code class=\"bash\"># If someone wants to make this extensible, i'll accept\n# PR\nselected=$(find ~\/ ~\/personal ~\/personal\/dev\/env\/.config -mindepth 1 -maxdepth 1 -type d | fzf)\n<\/code><\/pre>\n\n<p>These directories don't match what I use on my computers.<\/p>\n\n<p>People have submitted pull requests, but these haven't yet been merged.<\/p>\n\n<p>Either I change my directory structure or I can't use it.<\/p>\n\n<p>But, in my derivation, I can apply my own patch files to make any changes I want - such as changing the directories to ones I'd want to use - making it usable for me.<\/p>\n\n<p>I can also apply other people's patches, the same as I'm doing with dwm.<\/p>\n\n<p>Someone has created a pull request to make the directories dynamic based on a configuration file.<\/p>\n\n<p>I can apply these changes as a patch to my configuration and remove my custom one.<\/p>\n\n<p>I can make any other changes I want or need to.<\/p>\n\n<p>The possibilities are endless.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "8a79fcd4efce6727224214430cc26505",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"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": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "f378ea534b4141dc8290191f8941df2c",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,6 +87,5 @@
|
|||
"processed": "<p>I've been surprised to still recently find software development agencies using their own proprietary content management systems and frameworks.<\/p><p>With so many available open source options, like Drupal and Symfony, why would people write their own?<\/p><p>Why spend the time and effort to build basic functionality like user registration and login when existing tools already solve that problem?<\/p><p>Why ignore the thousands of open source modules, themes, plugins, packages and libraries that are available?<\/p><p>Instead, leverage the tools that already exist and focus on solving domain-specific issues and writing code that's genuinely specific to the project.<\/p><p>Then you can contribute some of the saved time back to the project and make it better for yourself and others in the future and ready for the next project.<\/p>",
|
||||
"summary": ""
|
||||
}
|
||||
],
|
||||
"feeds_item": []
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I've built Drupal websites for 18 years and have worked with versions as old as Drupal 5.<\/p>\n\n<p>A common issue for people using the CMS has been the editorial experience when adding and editing content.<\/p>\n\n<p>Once logged in, to add or edit a node, they need to go to a separate administration screen to enter or change the content.<\/p>\n\n<p>The administration page usually looks nothing like what the page will look like once published, with the user only seeing large forms and different styles, or a different theme altogether.<\/p>\n\n<p>There is a page preview option, but this only works once the forms have been completed and requires the user to be able to \"see\" how the page will look beforehand and know what content will appear where.<\/p>\n\n<p>This has become trickier with the use of Paragraphs and entity reference fields, where the editor only sees the title of the content they want to reference but not what they will see or how it will look.<\/p>\n\n<p>As well as being difficult to create content, it also makes it difficult to edit it.<\/p>\n\n<h2 id=\"layout-builder\">Layout Builder<\/h2>\n\n<p>This is why I like Drupal's Layout Builder module.<\/p>\n\n<p>Originally released as a contributed module, it has been part of Drupal core since 8.7 and allows editors to be able to create complex pages using a drag and drop interface and instantly see how they look.<\/p>\n\n<p>There's now an ecosystem of Layout Builder-related modules, <a href=\"https:\/\/www.drupal.org\/project\/layout_builder_extra_templates\">including one I wrote<\/a>, that add more functionality to the Layout Builder, such as adding more template suggestions, restricting the available blocks, or adding inline styles - making it even more powerful.<\/p>\n\n<p>If you have a Drupal site and haven't tried Layout Builder, you can incrementally opt in and only enable it for certain content types. You don't need to go all in straight away.<\/p>\n\n<p>But, I think that once you've tried it, you won't be able to go back.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "cba5205b68a6ba553b9b34ef661692d2",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Before adding a new feature or change to a codebase, ask if it's really needed and consider its long-term implications.<\/p>\n\n<p>Code is easy to write, but needs to be maintained as newer language or framework features are added or have breaking changes.<\/p>\n\n<p>Something I've added recently to Build Configs was an option to use an <a href=\"http:\/\/default\/daily\/2024\/01\/27\/gitignore-inclusive-or-exclusive\">inclusive or exclusive .gitignore file<\/a>.<\/p>\n\n<p>Whilst it's only adding an if condition based on a value, it adds a separate path in my code and both need to be maintained.<\/p>\n\n<p>I've been thinking of adding <code>just<\/code> again to some projects instead of a <code>run<\/code> file, which would add separate files that need to be maintained and kept up-to-date with each other so both offer the same features.<\/p>\n\n<p>Is this something I want to maintain going forward? Does it add enough value to justify its maintenance?<\/p>\n\n<p>Different to a feature flag, which usually has a known lifespan, this could need be maintained for the whole lifespan of the application.<\/p>\n\n<p>On a client project, this could be having two sets of buttons with rounded and square corners.<\/p>\n\n<p>Do we need both?<\/p>\n\n<p>It could be the positioning of a title in a header. Fewer options mean there is less code to write and maintain.<\/p>\n\n<p>In a Drupal project, each choice could mean adding a different field, taxonomy term, or content or block type to achieve the desired result.<\/p>\n\n<p>The more we can achieve with fewer options means the application will be easier to maintain and work on in the future.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "b412bcd2d4532d309472dae5c82e8109",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I recently had <a href=\"http:\/\/default\/podcast\/27-drupalisms\">my first podcast episode with two guests<\/a>, where I discussed Drupal terminology and Drupalisms with Emma Horrell and Luke McCormick.<\/p>\n\n<p>It was a great episode, but there was something I needed to do before I could release it.<\/p>\n\n<p>Before I could release the episode, I needed to update my website to show both guest names.<\/p>\n\n<p>The other 26 episodes only had a single guest per episode, and my podcast pages were built to only show the first guest name.<\/p>\n\n<p>When building the pages for the <a href=\"http:\/\/default\/podcast\/1-retrofit\">first episode with Matt Glaman<\/a>, I only needed to show a single guest name.<\/p>\n\n<p>There was no need to show multiple guest names until I had an episode with multiple guests.<\/p>\n\n<p>I wrote the simplest code to achieve the requirements I had at the time.<\/p>\n\n<p>If I wrote for two guests but never had an episode with two guests, my website would be bloated and have functionality that wasn't needed or used.<\/p>\n\n<p>Now my requirements have changed.<\/p>\n\n<p>I can have an episode with one or two guests, but not three or more guests.<\/p>\n\n<p>If I have an episode with more than two guests, I'll write that functionality then.<\/p>\n\n<p>But not before.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "6d7fff29a7d69d2505863bd126857e30",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Last week, I asked <a href=\"http:\/\/default\/daily\/2024\/05\/15\/should-you-include-issue-ids-in-your-commit-messages\">whether you should include issue IDs in commit messages<\/a>.<\/p>\n\n<p>Another thing I like to reference in a commit message is the commit ID (or SHA) of a related commit.<\/p>\n\n<p>For example, when I run <code>git log<\/code> in my website repository, I see commits like this:<\/p>\n\n<pre><code class=\"plain\">commit 0c91825c16217d0fe7eff4ea100a67550051c4a9\nAuthor: Oliver Davies <oliver@oliverdavies.dev>\nDate: Sat May 11 15:32:07 2024 +0200\n\n Create a cached talk counter\n\n Create a cached version of the talk counter service that returns a\n cached result of the talk count for that day.\n\n This uses the Decorator design pattern to decorate the existing\n `TalkCounter` service and works as they both implement the same\n `TalkCounterInterface`.\n<\/code><\/pre>\n\n<p>The sha for this commit is <code>0c91825c16217d0fe7eff4ea100a67550051c4a9<\/code>.<\/p>\n\n<p>If I was to make another commit that was related to this one, I can include this commit sha in my new commit message.<\/p>\n\n<p>I also don't need to include the entire thing - only enough for it to be unique (usually five or six characters).<\/p>\n\n<p>Once pushed, the commit IDs should never change, so this will be a permanent reference to the first commit.<\/p>\n\n<p>Helpfully, websites like GitHub, GitLab and Bitbucket will identify it as a commit sha and make it clickable so you can easily navigate to the referenced commit.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "0fa771c1556974be2eeabf42a87e75c4",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Today, I've been following a conversation on Twitter and reading a blog post about Drupal naming and removing the version number from promotional materials.<\/p>\n\n<p>In the post, the author recommends stopping using the version number and \"just say Drupal\" and referring to Drupal 7 and older as \"Legacy Drupal\".<\/p>\n\n<p>Here's a quote from the article that stood out to me:<\/p>\n\n<blockquote>\n <p>This is no longer true! We have successfully eliminated the need for organizations to leave Drupal because \u201cit\u2019s cheaper to just rebuild it in Wordpress than upgrade from 8 to 9 or from 9 to 10.\u201d We\u2019ve made it easier for our clients to spend less on major upgrades, we should be proud!<\/p>\n<\/blockquote>\n\n<p>Another suggestion was to also use the term \"Modern Drupal\" for anything since Drupal 8.<\/p>\n\n<p>I've been doing this or calling it \"Drupal 8+\" on streams and podcast episodes, but I like this approach and I'm going to standardise on this, help change the perception of Drupal and that large rebuilds are no longer needed to upgrade as they were between the older legacy versions.<\/p>\n\n<p><a href=\"https:\/\/ten7.com\/blog\/post\/just-say-drupal\">Read the article<\/a> for more information about this.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "e9efe0920d1be12d60386b0f70dfbd4b",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Note: The numbers within this post are taken from my <a href=\"http:\/\/default\/presentations\/tdd-test-driven-drupal\">Test-Driven Drupal talk<\/a>, in which I also talk about this.<\/p>\n\n<p>My first commit to the 7.x-1.x branch of the Override Node Options module was in March 2012. According to Drupal.org, the module was used on 9,212 websites then.<\/p>\n\n<p>As well as the 7.x-1.x branch, there's the 8.x-2.x branch which supports Drupal 9 and 10, and previously Drupal 8.<\/p>\n\n<p>The most recent statistics show the module is currently used on 34,981 websites and is consistently around 35,000.<\/p>\n\n<h2 id=\"what-does-that-mean%3F\">What does that mean?<\/h2>\n\n<p>The module is considered feature complete, but I'm not ruling out any new additions.<\/p>\n\n<p>The main thing is ensuring that any changes don't break 35,000 websites!<\/p>\n\n<p>I do this by relying on the module's automated test suite and ensuring that tests are added for any features or bugs and that the tests are passing before any new release.<\/p>\n\n<h2 id=\"this-has-worked-well\">This has worked well<\/h2>\n\n<p>A few years ago, I committed a feature request to both versions. While it didn't include additional tests, I verified the existing functionality worked after resolving a large merge conflict by ensuring the original tests passed.<\/p>\n\n<p>More recently, a colleague and I refactored the module and split each override into its own class, making adding and maintaining overrides easier.<\/p>\n\n<p>Because the tests were still passing, we knew our refactor was successful and not causing regressions.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>Having automated tests and ensuring they're always passing has allowed me to add features and refactor code that I wouldn't have done or had the confidence to do otherwise.<\/p>\n\n<p>It's great to have a popular module, but on the other hand, I don't want to break 35,000 websites which makes the tests invaluable.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "ae450dbfadd5cdf2b2b209019173a073",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I was recently working with a client and in-house development team who were working in a Git Flow-like way with different branches for each sprint and feature..<\/p>\n\n<p>Each sprint branch has its own environment that can be reviewed and tested.<\/p>\n\n<p>Once the sprint is complete, the sprint branch is merged into the mainline branch.<\/p>\n\n<p>Due to the number of branches and environments, it was hard to tell what was where.<\/p>\n\n<p>If they wanted to test a particular change, it could be in various places depending on which branches had been merged and had been deployed.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>My suggestion was to simplify things by limiting the number of branches and environments.<\/p>\n\n<p>If they used trunk-based development on a single branch, it would be much easier to find a change and less likely for there to be differences or regressions.<\/p>\n\n<p>If they used feature flags, they could control features by toggling them on and off as needed, or enable features for users with a certain role, in a group or a particular email domain.<\/p>\n\n<p>There would also be less overhead and process, allowing the team to focus on delivery and not working which branch their change was on or which environment they need to test.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "4c40524b5ba4570cdb941cb4e59e0508",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p><code>just<\/code> compared to <code>make<\/code> is something that was asked during my PHP London talk, and whilst they are similar, <code>just<\/code> has differences for me that explains why I use it:<\/p>\n\n<h2 id=\"tabs-or-spaces\">Tabs or spaces<\/h2>\n\n<p>A Makefile needs to use tabs. Justfiles are more flexible and work with tabs or any number of spaces.<\/p>\n\n<h2 id=\".phony\">.PHONY<\/h2>\n\n<p>With a Makefile, you need to declare some targets as \"phony\". I believe that this is for targets that don't generate artifact files with that name, so as I'm not compiling and building files with <code>make<\/code>, this is redundant and adds visual noise.<\/p>\n\n<h2 id=\"passing-arguments\">Passing arguments<\/h2>\n\n<p>This is how a <code>composer<\/code> target looks like in a Makefile:<\/p>\n\n<pre><code class=\"make\">composer:\n docker compose exec php composer\n<\/code><\/pre>\n\n<p>With this, I'd expect to be able to pass arguments to it - e.g. <code>make composer info drupal\/core<\/code>.<\/p>\n\n<p>But, instead of seeing the expected output, I get an error: <code>make: *** No rule to make target 'info'. Stop.<\/code>.<\/p>\n\n<p>This is what I'd need to do to pass arguments to the <code>composer<\/code> target:<\/p>\n\n<pre><code class=\"make\">composer:\n docker compose exec php composer $(COMPOSER_ARGS)\n<\/code><\/pre>\n\n<p>Now I can run <code>make composer COMPOSER_ARGS=\"info drupal\/core\"<\/code> and see what I was expecting but the syntax isn't what I'd want.<\/p>\n\n<p><code>just<\/code>, on the other hand, allows for defining parameters to its recipes:<\/p>\n\n<pre><code class=\"language-yaml\">composer *args:\n docker compose exec php composer \n<\/code><\/pre>\n\n<p>Here, I can create as many named parameters as needed and use them in the recipe with the syntax that I wanted - <code>just composer info drupal\/core<\/code>.<\/p>\n\n<p>I can think of a few others but this is is the main reason why I moved from <code>make<\/code> and later adopted <code>just<\/code>.<\/p>\n\n<p><code>just<\/code>, for me, gives the flexibilty that I need whilst using a simple and familiar syntax but without some of the confusing and complicated behaviours of <code>make<\/code>.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "0747677b2ea7269056a31e78221a91aa",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p><a href=\"http:\/\/default\/daily\/2023\/01\/02\/dont-use-arbitrary-values-in-tailwind-css\">I generally don't use aritrary classes with Tailwind CSS<\/a>.<\/p>\n\n<p>But, they are powerful, and I do use them in some situations.<\/p>\n\n<p>A few months ago, <a href=\"https:\/\/x.com\/opdavies\/status\/1755332703308652730\">I posted a screenshot of a timeline component<\/a> I've built for a client project.<\/p>\n\n<p>Today, I needed to fix an issue with the first and last \"paths\" as they were stretching further than they should.<\/p>\n\n<p>This is the class I added to fix the problem:<\/p>\n\n<blockquote>\n <p>mr-[calc(50%<em>-_calc(var(--path-width)<\/em>\/_2))]<\/p>\n<\/blockquote>\n\n<p>It adds an arbitrary amount of right margin, which is 50% of the container minus half of the path width, which is the <code>--path-width<\/code> variable.<\/p>\n\n<p>This class uses the <code>calc<\/code> function as well as <code>var<\/code> to determine the correct margin to apply, whilst keeping the code adaptable in case the path width changes.<\/p>\n\n<p>And, as this is a value that's only used in this component, there's no benefit to writing this in a stylesheet - making an arbitrary value was a good option.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "bb3e2c953c9fc62ba7bde1aa86085a2d",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>I recently saw a social media website taking over users' handles - renaming them so they can have the original.<\/p>\n\n<p>There were similar issues a few years ago when a website put a paywall in front of articles written by its users.<\/p>\n\n<p>I've written articles and tutorials for companies I've worked at that no longer exist as the companies have been acquired.<\/p>\n\n<p>This is why I like to own and control my content. I still have my versions if a website shuts down or changes its settings.<\/p>\n\n<p>There are a lot of options, including Drupal, that can help with this. It's easy to set up a new website and start publishing your own content.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "674a5bd2088bccaaabc0533c88ba62f8",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Something I've said recently when mentoring bootcamp students and working with Junior Developers is that there isn't a perfect solution to each problem, and there are multiple ways to achieve the same result.<\/p>\n\n<p>Similarly, there isn't a perfect tool for every situation.<\/p>\n\n<p>You can use different tools and get the same outcome.<\/p>\n\n<p>In some of my early emails, I talked about <code>run<\/code> files and <code>just<\/code> - two ways to write project-specific commands or aliases that simplify complex commands or group multiple commands together.<\/p>\n\n<p>Both do the same thing, but each have pros and cons.<\/p>\n\n<p><code>run<\/code> files are files written in Bash, which are more verbose but can run anywhere.<\/p>\n\n<p>just is its own program that needs to be installed for it to be available, so Developers will need to have it installed, and it will need to be added to a CI pipeline to be available.<\/p>\n\n<p>I've had CI pipelines fail because they can't download just, and not because of anything in the application code.<\/p>\n\n<p>I like the simplicity of justfiles, though, and that the files are simpler.<\/p>\n\n<h2 id=\"here%27s-the-thing\">Here's the thing<\/h2>\n\n<p>If there isn't a perfect solution, approach, or tool, it comes down to the pros and cons of each. Which option is best for you, your project, or your team?<\/p>\n\n<p>There isn't a \"one size fits all\" solution that works for everyone all the time.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "3579f9c1128a7333aacc2788d9b130b3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>On a current project, I've decided to use a component library as the first place to do front-end development.<\/p>\n\n<p>I'm using <a href=\"https:\/\/fractal.build\">Fractal<\/a> as I can use Twig for templates. As Drupal also uses Twig templates, I have more reusabilty between the components in Fractal and Drupal compared to converting them from a different templating language like Handlebars or Nunjucks.<\/p>\n\n<p>Rather than developing directly within the custom Drupal theme, I've been creating new components and pages initially within Fractal.<\/p>\n\n<p>I have been able to create new components quickly and easily with the views uing Twig templates and inject data to it using a context file - a YAML file for each component that contains data that is injected automatically into the view.<\/p>\n\n<p>This meant that I've been able to develop new components from scratch without needing to set up content types or paragraphs within Drupal, validate and confirm my data model, and present the templates to the client for review in Fractal. If a change is needed, it's quick to do.<\/p>\n\n<p>I've also moved my asset generation step into Fractal. No CSS or JavaScript is being compiled within the Drupal theme, it is created within Fractal and copied over with the Twig templates.<\/p>\n\n<p>In most cases, I've been able to copy the Twig templates into Drupal and replace the static context data with dynamic data from Drupal without needing to make any further changes.<\/p>\n\n<p>In a couple of situations, I've needed to change my implementation slightly when moving a template into Drupal, so in this workflow, I've made the changes in Fractal and re-exported them to keep things in sync between the two systems.<\/p>\n\n<p>In situations where there is existing markup and\/or styles from the Drupal side, I've copied those into Fractal so that they match before adding the additional styling and any markup changes.<\/p>\n\n<p>In general, I like the approach as it gives me more flexibility upfront to make changes before needing to configure Drupal. I can see how things could get out of sync between the two systems, but hopefully having the assets compiled in Fractal and needing to copy them into Drupal will keep things synced up.<\/p>\n\n<p>I don't think that I'd use this approach for all projects, but for this one, where I'm working with multiple themes and will need to later add different variants of pages and components, it's worked well so far.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "51b7b1bae4312428d8b9e7a4c5ac11ea",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>A few years ago, decoupled or headless Drupal - where Drupal is used as an API with a separate front-end - was very popular with some large Drupal agencies and clients using it.<\/p>\n\n<p><a href=\"http:\/\/default\/talks\/decoupling-drupal-vuejs\">I gave a conference talk<\/a> about how to use Drupal as a backend application for a Vue.js frontend.<\/p>\n\n<p>Recently, though, I haven't heard much about it.<\/p>\n\n<p>Some previous advocates have moved back to the more traditional approach.<\/p>\n\n<p>Interestingly, though, I did hear quite a bit about it at DrupalCon and about Drupal and Next.js in particular.<\/p>\n\n<p>I want to try using Drupal with Astro in the future and see what the landscape is like now.<\/p>\n\n<h2 id=\"what-about-you%3F\">What about you?<\/h2>\n\n<p>Do you use Drupal in a decoupled or headless way? If so, what do you use for the frontend?<\/p>\n\n<p>If not, or if you've moved back to a more traditional approach, why?<\/p>\n\n<p>Reply and let me know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "dbf68a40071443f9943fde754f0bddb3",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>The version number of a release is a key indicator of whether it\u2019s compatible with existing code.<\/p>\n\n<p>Semantic versioning is a popular approach used by Drupal core, and many contributed modules, themes and distributions.<\/p>\n\n<p>It uses version numbers like 1.0.0 to show the major, minor and patch versions.<\/p>\n\n<p>If the second or third number changes, e.g. 1.1.0 or 1.0.1, the release contains new backwards-compatible features or fixes, so it\u2019s safe to update.<\/p>\n\n<p>If the first number changes, e.g. 2.0.0, the release is not backwards compatible and contains breaking changes that you\u2019ll need to review and update your code accordingly.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "c787c2846b67f55a45525ab52acd36c1",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>In the most recent episode of the Beyond Blocks podcast, I spoke with J\u00fcrgen Haas about Drupal's ECA module.<\/p>\n\n<p>ECA stands for Events, Conditions and Actions and is a module I was aware of, but haven't used before - so I was great to learn more about it.<\/p>\n\n<p>I also got to meet J\u00fcrgen at DrupalCon in Barcelona, as well as several other podcast guests, which was fantastic.<\/p>\n\n<p><a href=\"http:\/\/default\/podcast\/23-jurgen-haas-eca\">Listen to the episode now<\/a>.<\/p>\n\n<p>If you'd like to be a guest on the podcast or want to suggest a topic, reply and let me know!<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "cf37bf626cc98ddb264219b228efc723",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>Yesterday, I wanted to make a breaking change to my <a href=\"http:\/\/default\/daily\/2023\/03\/04\/why-i-built-a-tool-to-generate-configuration-files\">build-configs project<\/a> - changing the default database credentials that are used by Docker Compose.<\/p>\n\n<p>As I have several projects based on generated files by the tool, changing the values could cause issues in those projects in the future and this is something that I wanted to avoid.<\/p>\n\n<h2 id=\"what-did-i-do%3F\">What did I do?<\/h2>\n\n<p>To avoid this issue and needing to update all of my projects at once, I added a feature flag to the <code>build.yaml<\/code> file so I can opt-in to this feature on a per-project basis.<\/p>\n\n<p>If a project, like my <a href=\"https:\/\/github.com\/opdavies\/docker-example-drupal\">Drupal<\/a> and <a href=\"https:\/\/github.com\/opdavies\/docker-example-drupal-localgov\">LocalGov Drupal<\/a> Docker examples, are opted in, its files will get the new credentials. If not, it will continue to use the original ones.<\/p>\n\n<p><a href=\"https:\/\/github.com\/opdavies\/docker-example-drupal\/commit\/3f496168d5c32f9706970519023b431ee02c4b19\">In this commit<\/a>, you'll see where I enabled the feature flag and committed the resulting change.<\/p>\n\n<h2 id=\"what-does-this-achieve%3F\">What does this achieve?<\/h2>\n\n<p>I can continue to work on existing projects without them breaking, and migrate projects one at a time by using the feature flag instead of needing to do them all once.<\/p>\n\n<p>Once all active projects have been migrated or completed, the feature flag can be removed and I can refactor and simplify the code - removing the feature flag and the legacy values.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "12cfe483b3ce1a78d111407418450b62",
|
||||
"target_type": "feeds_feed",
|
||||
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -87,14 +87,5 @@
|
|||
"processed": "\n <p>When creating a custom module, where do you put your business logic?<\/p>\n\n<p>You want to keep classes like Controllers and Commands simple and move any reusable logic into separate files.<\/p>\n\n<p>Usually, this means 'Service' classes, but another approach I like is to use 'Action' classes.<\/p>\n\n<h2 id=\"what-is-an-action-class%3F\">What is an Action class?<\/h2>\n\n<p>An Action is a PHP class representing a single action that must be performed.<\/p>\n\n<p>It usually contains a single method with a descriptive name summarising the task, such as <code>GetAccessToken<\/code>.<\/p>\n\n<p>This differs from a generic service like <code>ApiService<\/code> with multiple methods like <code>getAccessToken()<\/code>.<\/p>\n\n<h2 id=\"using-action-classes\">Using Action classes<\/h2>\n\n<p>I'll register Action classes in the service container to use dependency injection and autowiring and easily inject the Action into other classes that need it, like Controllers and Commands.<\/p>\n\n<p>If you need multiple implementations, multiple actions can implement the same Interface and make them swappable, such as having <code>GetAccessToken<\/code> and <code>GetAndCacheAccessToken<\/code> implement the same <code>GetsAccessToken<\/code> interface.<\/p>\n\n<p>That also enables using design patterns like Decorators with Actions.<\/p>\n\n<h2 id=\"why-i-like-actions\">Why I like Actions<\/h2>\n\n<p>I like more readable and meaningful class names and prefer working with multiple simpler classes than those with fewer complex ones.<\/p>\n\n<p>I like leveraging design patterns I'm used to, such as the Decorator pattern, by having common interfaces and contracts.<\/p>\n\n<p>I like that if I need to add another implementation, I can add it without changing the existing code, so it follows the SOLID principles.<\/p>\n\n<h2 id=\"what-about-you%3F\">What about you?<\/h2>\n\n<p>Do you use Action classes in your code, or do you use Services or something else?<\/p>\n\n<p>Reply to this email and let me know.<\/p>\n\n ",
|
||||
"summary": null
|
||||
}
|
||||
],
|
||||
"feeds_item": [
|
||||
{
|
||||
"imported": "1970-01-01T00:32:50+00:00",
|
||||
"guid": null,
|
||||
"hash": "f43fe44d7a6115a84e3ad56bda9fb804",
|
||||
"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