Automated dev commit

This commit is contained in:
Oliver Davies 2025-05-02 09:08:59 +01:00
parent fb3f325d09
commit d689be14f2
89 changed files with 5840 additions and 18 deletions

View file

@ -2,9 +2,9 @@ _core:
default_config_hash: jtno5biznHZbrIgKwzq-ze-7XaQxLCGe6PeUOR7bRiQ
cache:
page:
max_age: 900
max_age: 0
css:
preprocess: true
preprocess: false
gzip: true
fast_404:
enabled: true
@ -12,5 +12,5 @@ fast_404:
exclude_paths: '/\/(?:styles|imagecache)\//'
html: '<!DOCTYPE html><html><head><title>404 Not Found</title></head><body><h1>Not Found</h1><p>The requested URL "@path" was not found on this server.</p></body></html>'
js:
preprocess: true
preprocess: false
gzip: true

View file

@ -0,0 +1,194 @@
uuid: de364a16-6052-402e-b7c0-52c243c8bfcf
langcode: en
status: true
dependencies:
config:
- node.type.daily_email
module:
- node
id: daily_email_rss_feed
label: 'Daily email RSS feed'
module: views
description: ''
tag: ''
base_table: node_field_data
base_field: nid
display:
default:
id: default
display_title: Default
display_plugin: default
position: 0
display_options:
fields:
title:
id: title
table: node_field_data
field: title
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: title
plugin_id: field
label: ''
exclude: false
alter:
alter_text: false
make_link: false
absolute: false
word_boundary: false
ellipsis: false
strip_tags: false
trim: false
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
pager:
type: some
options:
offset: 0
items_per_page: 1
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
access:
type: none
options: { }
cache:
type: tag
options: { }
empty: { }
sorts:
created:
id: created
table: node_field_data
field: created
relationship: none
group_type: group
admin_label: ''
entity_type: node
entity_field: created
plugin_id: date
order: DESC
expose:
label: ''
field_identifier: ''
exposed: false
granularity: second
arguments: { }
filters:
status:
id: status
table: node_field_data
field: status
entity_type: node
entity_field: status
plugin_id: boolean
value: '1'
group: 1
expose:
operator: ''
type:
id: type
table: node_field_data
field: type
entity_type: node
entity_field: type
plugin_id: bundle
value:
daily_email: daily_email
style:
type: default
options:
grouping: { }
row_class: ''
default_row_class: true
uses_fields: false
row:
type: fields
options:
default_field_elements: true
inline: { }
separator: ''
hide_empty: false
query:
type: views_query
options:
query_comment: ''
disable_sql_rewrite: false
distinct: false
replica: false
query_tags: { }
relationships: { }
header: { }
footer: { }
display_extenders: { }
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
tags: { }
feed_1:
id: feed_1
display_title: Feed
display_plugin: feed
position: 1
display_options:
pager:
type: some
options:
offset: 0
items_per_page: 1
style:
type: rss
options:
uses_fields: false
description: 'A daily email newsletter about software development, DevOps, community, and open source.'
row:
type: node_rss
options:
relationship: none
view_mode: default
display_extenders: { }
path: rss/daily.xml
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- 'user.node_grants:view'
tags: { }

View file

@ -34,17 +34,17 @@
],
"changed": [
{
"value": "2025-04-29T13:41:02+00:00"
"value": "2025-05-02T07:25:00+00:00"
}
],
"imported": [
{
"value": "2025-04-29T13:41:02+00:00"
"value": "2025-05-02T07:25:00+00:00"
}
],
"next": [
{
"value": "2025-04-30T01:41:02+00:00"
"value": "2025-05-02T19:25:00+00:00"
}
],
"queued": [
@ -67,7 +67,7 @@
],
"item_count": [
{
"value": 811
"value": 853
}
]
}

View file

@ -3388,14 +3388,174 @@
"paragraph.e89e2456-b6b5-4c5f-922f-9f34f6e77e68",
"paragraph.2f7f892b-75ff-48a0-9034-b9af048efd84"
],
"paragraph.e8a7c3c3-c07a-4545-8387-ab03b8f7f47a": [],
"paragraph.373fc0f2-717e-4fea-ae97-ec2c674834fd": [],
"paragraph.8828e7e1-466a-4086-a79a-e120899f0784": [],
"paragraph.34fea7ca-2458-448e-8991-495d69ee6836": [],
"paragraph.08be1735-e111-4a35-a270-2c56640f689f": [],
"paragraph.81b18409-0f9d-49de-a8ff-c75c7c4852c0": [],
"paragraph.e89e2456-b6b5-4c5f-922f-9f34f6e77e68": [],
"paragraph.2f7f892b-75ff-48a0-9034-b9af048efd84": [],
"node.6e6b15bc-6319-4392-ac07-7c38fb66e6a4": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.6900edff-5340-44ce-b328-9f3d24ab673b": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.43617141-40c9-4462-b130-60ee7697ab9d": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.d130088a-0c2c-4dfb-b65d-3b2ee8d6239c": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.0dc3cee6-40bf-4f95-8d25-4c32e95e7462": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.72729ed3-73ab-4e13-9115-fbf6d8ca6b08": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.6d520198-e187-44dd-b434-034f0f74d5c2": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.5cc406b1-f4ca-49c0-a296-d352086fc7f4": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.c4ee2105-bde9-49ad-89e2-2c06c215bdbb": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.0710efa0-c7f4-4254-afab-7c7a4f5b6959": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.2a8452d0-735c-41de-88ef-a9cc81f4035f": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.dc67f83a-6499-4b35-a040-3c7f89549cc7": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.00204e36-4f2d-4b4a-bcf6-c0140209e835": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.b09ccd35-261d-4fa5-99cc-52f781c52e87": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.3cbded61-45d4-46f7-95a0-a0ebb3da9847": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.641d773f-97cb-4da8-93e9-69df3c89261c": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.231b6fcb-38a4-4461-b0d4-63ab4c31806c": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.ecf2d8ff-f99c-4faf-899d-8e80e1b74287": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.03653911-19e8-4f60-8baa-5165b8380d29": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.197ba205-f2c2-4c14-8d71-ac01bf602861": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.cac7e909-60cc-48f1-9535-b1b3ea3347ad": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.d52128e8-0927-4232-8c77-e9c0d60578ca": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.e3631745-3190-45d0-b893-7e5315faeba3": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.d96c36ac-6f81-4a25-a5d1-b62c846680d1": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.3fa16a10-1230-498f-ad80-4cab3ae96a28": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.98e87c30-8dd5-4d80-aed3-53439673de97": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.4403292d-2b01-4681-afb2-0ab2113694a6": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.ed274b9c-e558-4ccb-a0e3-845fd4252c85": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.e3d951af-bba8-45f4-b249-c7e0f73e2a47": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.737d4134-6074-4498-b8d3-f51d7b71d006": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.ffcfe425-1787-4842-bbbf-0362a655a830": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.af5c2fcc-7f6c-4363-943c-f23dcdab51ca": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.f21de9a2-c741-4a12-9109-c7b5a452dccb": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.efea11de-28be-4cb0-9bad-9b04322c2126": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.9e166002-410b-4261-aac8-e47dbe1c04d8": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.d51a4843-892e-4d52-81ca-66c2af097478": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.bed02c02-476f-4f2f-8d21-0c8c45edb128": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.3d15dfe9-c530-40d6-8923-f5d7e7b3018b": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.c6ef991b-2262-4942-b1f9-34c8a3afced4": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.8c006484-b85e-4eb7-a2c2-f0094a331712": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.09407211-6afb-4f28-95b7-5a7c3f9235b8": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"node.dee73953-50c6-4afb-9c3d-09c53a5fd415": [
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849",
"feeds_feed.90c85284-7ca8-4074-9178-97ff8384fe76"
],
"path_alias.ac3001e1-433d-40c7-bb9d-a4fb9e8d0544": [
"node.63032bf4-8a96-4e14-93b2-92f5b057184e"
],
@ -5955,9 +6115,143 @@
"path_alias.1a615955-1c23-4677-9839-1e29d2260179": [
"node.f9b17889-2531-4e1e-b120-a0f0e27fed83"
],
"path_alias.c0415faf-ca06-4f19-a31a-c7c1b3cf1e3f": [
"node.6e6b15bc-6319-4392-ac07-7c38fb66e6a4"
],
"path_alias.d395f86e-2911-4b1d-a49c-ca3dd1c32083": [
"node.6900edff-5340-44ce-b328-9f3d24ab673b"
],
"path_alias.0bc43805-583f-45cc-9604-2cb7a000af0b": [
"node.43617141-40c9-4462-b130-60ee7697ab9d"
],
"path_alias.aa4704f0-c8d1-4554-a133-fe2fca90c142": [
"node.d130088a-0c2c-4dfb-b65d-3b2ee8d6239c"
],
"path_alias.6898a61e-17b6-4103-9293-840d7a012850": [
"node.0dc3cee6-40bf-4f95-8d25-4c32e95e7462"
],
"path_alias.a7edd40d-18da-4a9b-ba0a-a8190cc6d104": [
"node.72729ed3-73ab-4e13-9115-fbf6d8ca6b08"
],
"path_alias.2dff4b65-61ed-46e9-94c9-71d479303461": [
"node.6d520198-e187-44dd-b434-034f0f74d5c2"
],
"path_alias.a0821366-eddb-4182-9987-d6d0caf0559a": [
"node.5cc406b1-f4ca-49c0-a296-d352086fc7f4"
],
"path_alias.dbd11573-f435-42c5-9fb2-eeff934fa7e1": [
"node.c4ee2105-bde9-49ad-89e2-2c06c215bdbb"
],
"path_alias.45376d45-20cf-41ca-84ab-8b50e6828666": [
"node.0710efa0-c7f4-4254-afab-7c7a4f5b6959"
],
"path_alias.04fe45c5-c54e-4110-9d33-b9681b32f3b5": [
"node.2a8452d0-735c-41de-88ef-a9cc81f4035f"
],
"path_alias.dc5689bd-8f0d-4c07-9a50-0bbc35a52c8f": [
"node.dc67f83a-6499-4b35-a040-3c7f89549cc7"
],
"path_alias.1e93c591-9fdc-415d-9ddd-ffe9ce2005f9": [
"node.00204e36-4f2d-4b4a-bcf6-c0140209e835"
],
"path_alias.82fb019d-185d-4b8f-a3ad-ed260b5c60e2": [
"node.b09ccd35-261d-4fa5-99cc-52f781c52e87"
],
"path_alias.cde23a5c-26c9-4cb1-9dc6-3de19ec7856f": [
"node.3cbded61-45d4-46f7-95a0-a0ebb3da9847"
],
"path_alias.a635bfb6-ac56-4a3e-87bb-e7487942d5a7": [
"node.641d773f-97cb-4da8-93e9-69df3c89261c"
],
"path_alias.3aad1a8d-456a-4f50-9dbd-c1073580e74e": [
"node.231b6fcb-38a4-4461-b0d4-63ab4c31806c"
],
"path_alias.1274b1fe-43b6-4277-bb7e-cfc25e8786ce": [
"node.ecf2d8ff-f99c-4faf-899d-8e80e1b74287"
],
"path_alias.ac2378ff-1de4-442b-9018-54c9c8322961": [
"node.03653911-19e8-4f60-8baa-5165b8380d29"
],
"path_alias.b0482047-f38c-41f4-9020-e5447d48ee1d": [
"node.197ba205-f2c2-4c14-8d71-ac01bf602861"
],
"path_alias.fcfbfce9-4005-4385-9904-527459c0193b": [
"node.cac7e909-60cc-48f1-9535-b1b3ea3347ad"
],
"path_alias.e2818d79-08ab-43de-a46a-611692fb21ad": [
"node.d52128e8-0927-4232-8c77-e9c0d60578ca"
],
"path_alias.b7ca03cd-c00c-404e-8efb-9c043c6c896d": [
"node.e3631745-3190-45d0-b893-7e5315faeba3"
],
"path_alias.8cdf0c15-fa61-4f6c-beba-6e2750a43add": [
"node.d96c36ac-6f81-4a25-a5d1-b62c846680d1"
],
"path_alias.a703bebc-ef85-4b26-9aca-8923ec103114": [
"node.3fa16a10-1230-498f-ad80-4cab3ae96a28"
],
"path_alias.66bb38d3-d7b4-43e4-875f-25afb877d1ce": [
"node.98e87c30-8dd5-4d80-aed3-53439673de97"
],
"path_alias.58d1c76c-54a4-4a93-8637-766e5bb2e598": [
"node.4403292d-2b01-4681-afb2-0ab2113694a6"
],
"path_alias.749b2e45-4c6f-4413-99a8-8bdb7295a36b": [
"node.ed274b9c-e558-4ccb-a0e3-845fd4252c85"
],
"path_alias.1ce9a885-2b61-4222-b340-08b54135eb2f": [
"node.e3d951af-bba8-45f4-b249-c7e0f73e2a47"
],
"path_alias.4b5dab9d-c0b7-47df-b2de-8f46fc45cb9c": [
"node.737d4134-6074-4498-b8d3-f51d7b71d006"
],
"path_alias.be97582e-4563-4f43-a6f2-c0a61b3832ce": [
"node.ffcfe425-1787-4842-bbbf-0362a655a830"
],
"path_alias.1e2d3a71-a014-477a-864c-d9c816b1c7a6": [
"node.af5c2fcc-7f6c-4363-943c-f23dcdab51ca"
],
"path_alias.0d5d3954-a5ea-4230-9508-c69a31e739d6": [
"node.f21de9a2-c741-4a12-9109-c7b5a452dccb"
],
"path_alias.3515d35d-aae2-46e9-9f2d-19482dd90011": [
"node.efea11de-28be-4cb0-9bad-9b04322c2126"
],
"path_alias.4071b156-9da4-4f65-a6ab-e390d9e955a0": [
"node.9e166002-410b-4261-aac8-e47dbe1c04d8"
],
"path_alias.d69bb36d-01d6-4dde-8b03-c01808447a14": [
"node.d51a4843-892e-4d52-81ca-66c2af097478"
],
"path_alias.fe4be376-f835-439b-9d6a-e281d8c8e6bc": [
"node.bed02c02-476f-4f2f-8d21-0c8c45edb128"
],
"path_alias.903ec0e9-3c6a-4279-b40f-620441e7d816": [
"node.3d15dfe9-c530-40d6-8923-f5d7e7b3018b"
],
"path_alias.450e40e9-6a07-4c26-a4fb-80f415e8af57": [
"node.c6ef991b-2262-4942-b1f9-34c8a3afced4"
],
"path_alias.7ffe966e-9311-4627-a058-e120afdfe341": [
"node.8c006484-b85e-4eb7-a2c2-f0094a331712"
],
"path_alias.22643036-3745-42ff-836e-86c2caafac72": [
"node.09407211-6afb-4f28-95b7-5a7c3f9235b8"
],
"path_alias.59cdaf2a-3e6c-4ca9-9816-83a5c59693ae": [
"node.dee73953-50c6-4afb-9c3d-09c53a5fd415"
],
"shortcut.8cd79370-99a8-45fc-89a8-639ffc00bd1c": [],
"shortcut.fd457304-bed3-4d38-926b-e190a05c37d3": [],
"shortcut.0cabd243-2f9c-48ac-b0d9-f536d1cf1eaa": [],
"user.42bd662a-80da-4069-9fb8-14db3a77bb8c": [],
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849": []
"user.b8966985-d4b2-42a7-a319-2e94ccfbb849": [],
"paragraph.e8a7c3c3-c07a-4545-8387-ab03b8f7f47a": [],
"paragraph.373fc0f2-717e-4fea-ae97-ec2c674834fd": [],
"paragraph.8828e7e1-466a-4086-a79a-e120899f0784": [],
"paragraph.34fea7ca-2458-448e-8991-495d69ee6836": [],
"paragraph.08be1735-e111-4a35-a270-2c56640f689f": [],
"paragraph.81b18409-0f9d-49de-a8ff-c75c7c4852c0": [],
"paragraph.e89e2456-b6b5-4c5f-922f-9f34f6e77e68": [],
"paragraph.2f7f892b-75ff-48a0-9034-b9af048efd84": []
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "00204e36-4f2d-4b4a-bcf6-c0140209e835"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Writing good commit messages"
}
],
"created": [
{
"value": "2025-04-04T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/04\/good",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>There are many good resources and interesting articles online about how to write good messages when committing changes to a Git repository.<\/p>\n\n<p>The post I often refer to is <a href=\"https:\/\/cbea.ms\/git-commit\">How to Write a Git Commit Message<\/a> by Chris Beams.<\/p>\n\n<p>In his post, he explains why good commit messages matter and gives these seven rules:<\/p>\n\n<blockquote>\n <ul>\n <li>Separate the subject from body with a blank line.<\/li>\n <li>Limit the subject line to 50 characters.<\/li>\n <li>Capitalize the subject line.<\/li>\n <li>Do not end the subject line with a period.<\/li>\n <li>Use the imperative mood in the subject line.<\/li>\n <li>Wrap the body at 72 characters.<\/li>\n <li>Use the body to explain what and why vs. how.<\/li>\n <\/ul>\n<\/blockquote>\n\n<p>I'd recommend reading the article to get the full context.<\/p>\n\n<p>Rules two and six suggest lengths for the subject line and body which is another reason <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/02\/commit\">why I rarely use <code>-m<\/code><\/a> when committing changes.<\/p>\n\n<p>Whilst you can create multi-line commit messages on the command line, by opening it in my preferred editor (Neovim for me), I can see where the lines should end and be warned if I exceed them.<\/p>\n\n<p>I can even include Chris' rules in my commit message template so I see them whenever I'm about to commit something.<\/p>\n\n<p>This additional feedback helps me create my commit messages how I intend.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>There are many good resources and interesting articles online about how to write good messages when committing changes to a Git repository.<\/p>\n\n<p>The post I often refer to is <a href=\"https:\/\/cbea.ms\/git-commit\">How to Write a Git Commit Message<\/a> by Chris Beams.<\/p>\n\n<p>In his post, he explains why good commit messages matter and gives these seven rules:<\/p>\n\n<blockquote>\n <ul>\n <li>Separate the subject from body with a blank line.<\/li>\n <li>Limit the subject line to 50 characters.<\/li>\n <li>Capitalize the subject line.<\/li>\n <li>Do not end the subject line with a period.<\/li>\n <li>Use the imperative mood in the subject line.<\/li>\n <li>Wrap the body at 72 characters.<\/li>\n <li>Use the body to explain what and why vs. how.<\/li>\n <\/ul>\n<\/blockquote>\n\n<p>I'd recommend reading the article to get the full context.<\/p>\n\n<p>Rules two and six suggest lengths for the subject line and body which is another reason <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/02\/commit\">why I rarely use <code>-m<\/code><\/a> when committing changes.<\/p>\n\n<p>Whilst you can create multi-line commit messages on the command line, by opening it in my preferred editor (Neovim for me), I can see where the lines should end and be warned if I exceed them.<\/p>\n\n<p>I can even include Chris' rules in my commit message template so I see them whenever I'm about to commit something.<\/p>\n\n<p>This additional feedback helps me create my commit messages how I intend.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "7430242f1d716198aa6fb01f727d1f9a",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "03653911-19e8-4f60-8baa-5165b8380d29"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Dependency-free PHP Collections"
}
],
"created": [
{
"value": "2025-03-29T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/29\/collections",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>A few months ago, <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/6-dan-leech-php-tui\">Beyond Blocks podcast guest Dan Leech<\/a> give another talk at the PHP South West user group.<\/p>\n\n<p>This talk was about <a href=\"https:\/\/www.youtube.com\/watch?v=UpyVAcWGQhg\">building an expression language from scratch<\/a>, but in one part of the talk, Dan showed how he creates Collection classes.<\/p>\n\n<p>I use Collection classes a lot, have <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/using-illuminate-collections-outside-laravel\">given talks about them<\/a> and <a href=\"https:\/\/www.drupal.org\/project\/collection_class\">wrote a Collections module for Drupal 7<\/a>.<\/p>\n\n<p>In Dan's talk, instead of using a Collection from Laravel or Doctrine, <a href=\"https:\/\/github.com\/dantleech\/onehourexpr\/blob\/8c4ee4a3c1680455118e16a3e1be2a08418ab207\/src\/Tokens.php\">he created his own<\/a>.<\/p>\n\n<p>His extended PHP's <code>IteratorAggregate<\/code> class so had no external dependencies.<\/p>\n\n<p>He could then add whatever additional methods and functionality he needed.<\/p>\n\n<p>Looking at other repositories on GitHub, I was able to find other examples.<\/p>\n\n<p>I've done this on projects since Dan's talk and like taking the minimalist approach - avoiding adding a dependency to my project and only adding the functionality I need and functionality that's more specific to my domain.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>A few months ago, <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/6-dan-leech-php-tui\">Beyond Blocks podcast guest Dan Leech<\/a> give another talk at the PHP South West user group.<\/p>\n\n<p>This talk was about <a href=\"https:\/\/www.youtube.com\/watch?v=UpyVAcWGQhg\">building an expression language from scratch<\/a>, but in one part of the talk, Dan showed how he creates Collection classes.<\/p>\n\n<p>I use Collection classes a lot, have <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/using-illuminate-collections-outside-laravel\">given talks about them<\/a> and <a href=\"https:\/\/www.drupal.org\/project\/collection_class\">wrote a Collections module for Drupal 7<\/a>.<\/p>\n\n<p>In Dan's talk, instead of using a Collection from Laravel or Doctrine, <a href=\"https:\/\/github.com\/dantleech\/onehourexpr\/blob\/8c4ee4a3c1680455118e16a3e1be2a08418ab207\/src\/Tokens.php\">he created his own<\/a>.<\/p>\n\n<p>His extended PHP's <code>IteratorAggregate<\/code> class so had no external dependencies.<\/p>\n\n<p>He could then add whatever additional methods and functionality he needed.<\/p>\n\n<p>Looking at other repositories on GitHub, I was able to find other examples.<\/p>\n\n<p>I've done this on projects since Dan's talk and like taking the minimalist approach - avoiding adding a dependency to my project and only adding the functionality I need and functionality that's more specific to my domain.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "94b06833489046a4c26442548eee3ca2",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "0710efa0-c7f4-4254-afab-7c7a4f5b6959"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Generating presentation slides with Nix and rst2pdf"
}
],
"created": [
{
"value": "2025-04-07T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/07\/nix-rst2pdf",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Since switching to Nix and NixOS, I've been looking for opportunities to use Nix more in addition to managing my laptop and server configurations and creating development shells for projects.<\/p>\n\n<p>Nix is a build tool, so I've started to use it to build my slide decks which I create using rst2pdf.<\/p>\n\n<p>I write the rst (reStructuredText) file and compile it to a PDF file.<\/p>\n\n<p>I had a flake.nix file to add rst2pdf, pdfpc and other tools to my shell, but the compilation to a PDF file was done in a bash script which I've since removed.<\/p>\n\n<p>Here's how my flake.nix file looks now:<\/p>\n\n<pre><code class=\"nix\">{\n inputs.nixpkgs.url = \"github:NixOS\/nixpkgs\/nixos-unstable\";\n\n outputs = { nixpkgs, ... }:\n let\n system = \"x86_64-linux\";\n pkgs = import nixpkgs { inherit system; };\n\n inherit (nixpkgs.lib) makeOverridable;\n inherit (pkgs.stdenvNoCC) mkDerivation;\n\n shared = mkDerivation {\n name = \"talks-shared\";\n src = .\/src;\n\n installPhase = ''\n runHook preInstall\n\n mkdir $out\n cp -r fonts styles $out\n\n runHook postInstall\n '';\n };\n\n commonBuildInputs = with pkgs; [\n (python310.withPackages (p: with p; [ rst2pdf ]))\n ];\n\n mkTalk = makeOverridable ({ src }: mkDerivation {\n inherit shared src;\n\n name = builtins.head (builtins.attrNames talks);\n\n buildInputs = commonBuildInputs;\n\n buildPhase = ''\n runHook preBuild\n\n mkdir $out\n\n rst2pdf slides.rst \\\n --break-level 1 \\\n --fit-background-mode scale \\\n --font-path \"${toString shared}\/fonts\" \\\n --output \"$out\/slides.pdf\" \\\n --stylesheets bw,\"${toString shared}\/styles\/opdavies-light\"\n\n runHook postBuild\n '';\n });\n\n talks = {\n build-configs = mkTalk { src = .\/src\/building-build-configs; };\n sculpin = mkTalk { src = .\/src\/building-static-websites-sculpin; };\n tailwind-css = mkTalk { src = .\/src\/taking-flight-with-tailwind-css; };\n test-driven-drupal = mkTalk { src = .\/src\/test-driven-drupal; };\n };\n in\n {\n devShells.${system}.default = with pkgs; mkShell {\n packages = with pkgs; commonBuildInputs ++ [\n ghostscript\n just\n pdfpc\n texliveMedium # includes pdfjam\n ];\n };\n\n packages.${system} = {\n inherit shared;\n } \/\/ talks;\n };\n}\n<\/code><\/pre>\n\n<p>Each talk is its own derivation, so I can run <code>nix run .#test-driven-drupal<\/code> and it will generate the appropriate PDF file for me to present or share.<\/p>\n\n<p>The source code is available at <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\">https:\/\/code.oliverdavies.uk\/opdavies\/talks<\/a> if you want to see how I use rst2pdf to create my presentations and I've even <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">given a presentation about how I create presentations<\/a>.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Since switching to Nix and NixOS, I've been looking for opportunities to use Nix more in addition to managing my laptop and server configurations and creating development shells for projects.<\/p>\n\n<p>Nix is a build tool, so I've started to use it to build my slide decks which I create using rst2pdf.<\/p>\n\n<p>I write the rst (reStructuredText) file and compile it to a PDF file.<\/p>\n\n<p>I had a flake.nix file to add rst2pdf, pdfpc and other tools to my shell, but the compilation to a PDF file was done in a bash script which I've since removed.<\/p>\n\n<p>Here's how my flake.nix file looks now:<\/p>\n\n<pre><code class=\"nix\">{\n inputs.nixpkgs.url = \"github:NixOS\/nixpkgs\/nixos-unstable\";\n\n outputs = { nixpkgs, ... }:\n let\n system = \"x86_64-linux\";\n pkgs = import nixpkgs { inherit system; };\n\n inherit (nixpkgs.lib) makeOverridable;\n inherit (pkgs.stdenvNoCC) mkDerivation;\n\n shared = mkDerivation {\n name = \"talks-shared\";\n src = .\/src;\n\n installPhase = ''\n runHook preInstall\n\n mkdir $out\n cp -r fonts styles $out\n\n runHook postInstall\n '';\n };\n\n commonBuildInputs = with pkgs; [\n (python310.withPackages (p: with p; [ rst2pdf ]))\n ];\n\n mkTalk = makeOverridable ({ src }: mkDerivation {\n inherit shared src;\n\n name = builtins.head (builtins.attrNames talks);\n\n buildInputs = commonBuildInputs;\n\n buildPhase = ''\n runHook preBuild\n\n mkdir $out\n\n rst2pdf slides.rst \\\n --break-level 1 \\\n --fit-background-mode scale \\\n --font-path \"${toString shared}\/fonts\" \\\n --output \"$out\/slides.pdf\" \\\n --stylesheets bw,\"${toString shared}\/styles\/opdavies-light\"\n\n runHook postBuild\n '';\n });\n\n talks = {\n build-configs = mkTalk { src = .\/src\/building-build-configs; };\n sculpin = mkTalk { src = .\/src\/building-static-websites-sculpin; };\n tailwind-css = mkTalk { src = .\/src\/taking-flight-with-tailwind-css; };\n test-driven-drupal = mkTalk { src = .\/src\/test-driven-drupal; };\n };\n in\n {\n devShells.${system}.default = with pkgs; mkShell {\n packages = with pkgs; commonBuildInputs ++ [\n ghostscript\n just\n pdfpc\n texliveMedium # includes pdfjam\n ];\n };\n\n packages.${system} = {\n inherit shared;\n } \/\/ talks;\n };\n}\n<\/code><\/pre>\n\n<p>Each talk is its own derivation, so I can run <code>nix run .#test-driven-drupal<\/code> and it will generate the appropriate PDF file for me to present or share.<\/p>\n\n<p>The source code is available at <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\">https:\/\/code.oliverdavies.uk\/opdavies\/talks<\/a> if you want to see how I use rst2pdf to create my presentations and I've even <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">given a presentation about how I create presentations<\/a>.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "83515e94d744b93f13eb7b8a56283bcc",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "09407211-6afb-4f28-95b7-5a7c3f9235b8"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "How much would it cost to build Drupal?"
}
],
"created": [
{
"value": "2025-03-03T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/03\/cost",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Drupal comes with a lot of features available out of the box.<\/p>\n\n<p>It has a flexible system to create complex content models with different content types and fields, rich media management and Views - a visual query builder - to create lists of content.<\/p>\n\n<p>It has the JSON:API module to expose your content via an API for use by other systems, such as mobile apps.<\/p>\n\n<p>It has user management, authentication, password resets, roles and permissions.<\/p>\n\n<p>It has configuration management to easily manage settings and configuration between environments and to provide traceability.<\/p>\n\n<p>It has the Layout Builder to create page layouts with a drag and drop interface and a built-in WYSIWYG editor for entering rich content.<\/p>\n\n<p>These are just some of the features I could mention.<\/p>\n\n<p>Running cloc on Drupal's <code>core<\/code> directory shows 18,289 files and 1,095,970 lines of code.<\/p>\n\n<p>If you were to get someone to build a CMS from scratch with the same features, how long would that take?<\/p>\n\n<p>How much would it cost?<\/p>\n\n<p>This is what you get for free by using free and open source software like Drupal.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "3b2f48d28cdda255b884d631a4a170ad",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "0dc3cee6-40bf-4f95-8d25-4c32e95e7462"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Building static websites with Drupal"
}
],
"created": [
{
"value": "2025-04-18T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/18\/static-drupal",
"langcode": "en"
}
],
"body": [
{
"value": "\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=\"https:\/\/www.oliverdavies.uk\/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 ",
"format": "full_html",
"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=\"https:\/\/www.oliverdavies.uk\/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": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "2b68ef78311dde7624d8a9f3bf2aecd5",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "197ba205-f2c2-4c14-8d71-ac01bf602861"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Work in sprints, deploy continuously"
}
],
"created": [
{
"value": "2025-03-28T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/28\/continuous",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Agile and Scrum have become standard approaches in software development.<\/p>\n\n<p>Work is planned and organised into iterations\/cycles\/sprints, usually lasting two weeks.<\/p>\n\n<p>As one sprint is being worked on, the next is usually already planned and the following one is being discussed.<\/p>\n\n<p>It's common for code deployments and releases to follow the same pattern.<\/p>\n\n<p>When a sprint is finished, the changes are released.<\/p>\n\n<p>But that means if a task is worked on at the start of the sprint, it won't be available for at least two weeks.<\/p>\n\n<p>It may be longer if the sprint is longer or if there are steps like manual testing that also happen.<\/p>\n\n<p>What if a change is needed or a bug is found?<\/p>\n\n<p>Is that going to be at least another two weeks before it can be addressed?<\/p>\n\n<p>Do you need to start using hotfixes and dealing with multiple branches and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/18\/conflicts\">merge conflicts<\/a>?<\/p>\n\n<p>I suggest separating your planning and work schedules from your deployments.<\/p>\n\n<p>Deploy as often as possible, even during a sprint.<\/p>\n\n<p>You want your feedback loop to be as small and quick as possible.<\/p>\n\n<p>But, importantly, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/06\/21\/deployments-or-releases\">deploying code is different to releasing features<\/a>.<\/p>\n\n<p>If you need to do manual testing, use feature flags to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/12\/07\/separating-releases-from-deployments-with-feature-flags\">separate deploying the code from releasing the feature<\/a>.<\/p>\n\n<p>Then, when it's ready to go live, you only need to enable the feature flag - no code deployment needed.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Agile and Scrum have become standard approaches in software development.<\/p>\n\n<p>Work is planned and organised into iterations\/cycles\/sprints, usually lasting two weeks.<\/p>\n\n<p>As one sprint is being worked on, the next is usually already planned and the following one is being discussed.<\/p>\n\n<p>It's common for code deployments and releases to follow the same pattern.<\/p>\n\n<p>When a sprint is finished, the changes are released.<\/p>\n\n<p>But that means if a task is worked on at the start of the sprint, it won't be available for at least two weeks.<\/p>\n\n<p>It may be longer if the sprint is longer or if there are steps like manual testing that also happen.<\/p>\n\n<p>What if a change is needed or a bug is found?<\/p>\n\n<p>Is that going to be at least another two weeks before it can be addressed?<\/p>\n\n<p>Do you need to start using hotfixes and dealing with multiple branches and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/18\/conflicts\">merge conflicts<\/a>?<\/p>\n\n<p>I suggest separating your planning and work schedules from your deployments.<\/p>\n\n<p>Deploy as often as possible, even during a sprint.<\/p>\n\n<p>You want your feedback loop to be as small and quick as possible.<\/p>\n\n<p>But, importantly, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2023\/06\/21\/deployments-or-releases\">deploying code is different to releasing features<\/a>.<\/p>\n\n<p>If you need to do manual testing, use feature flags to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/12\/07\/separating-releases-from-deployments-with-feature-flags\">separate deploying the code from releasing the feature<\/a>.<\/p>\n\n<p>Then, when it's ready to go live, you only need to enable the feature flag - no code deployment needed.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "45f407c2e5e4e066da156bd49eb7991a",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "231b6fcb-38a4-4461-b0d4-63ab4c31806c"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "First pull request submitted to nixpkgs"
}
],
"created": [
{
"value": "2025-03-31T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/31\/nixpkgs",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I've been using the Nix package manager for some time.<\/p>\n\n<p>The first commit of a flake.nix file to my <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/nix-config\">nixos-config repository<\/a> (formerly my dotfiles repository) was on the 26th of September 2022.<\/p>\n\n<p>The following month, I started to experiment with NixOS - the operating system built on the Nix language and the package manager.<\/p>\n\n<p>Rarely have I found a package missing within the 120,000+ available, but I have added some missing Vim plugins and some custom packages to my own configuration.<\/p>\n\n<p>But I hadn't tried to contribute any package definitions back to nixpkgs until today.<\/p>\n\n<p>I've submitted <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\/pull\/395028\">my first pull request to nixpkgs<\/a> to add a small script that I've added to my own configuration.<\/p>\n\n<p>It's always nerve-racking contributing to a new projects, but let's see how it goes.<\/p>\n\n<p>I'm thinking of it as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/14\/free-code-reviews\">a free code review<\/a>.<\/p>\n\n<p>If it goes well, I can see me contributing more packages in the future.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I've been using the Nix package manager for some time.<\/p>\n\n<p>The first commit of a flake.nix file to my <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/nix-config\">nixos-config repository<\/a> (formerly my dotfiles repository) was on the 26th of September 2022.<\/p>\n\n<p>The following month, I started to experiment with NixOS - the operating system built on the Nix language and the package manager.<\/p>\n\n<p>Rarely have I found a package missing within the 120,000+ available, but I have added some missing Vim plugins and some custom packages to my own configuration.<\/p>\n\n<p>But I hadn't tried to contribute any package definitions back to nixpkgs until today.<\/p>\n\n<p>I've submitted <a href=\"https:\/\/github.com\/NixOS\/nixpkgs\/pull\/395028\">my first pull request to nixpkgs<\/a> to add a small script that I've added to my own configuration.<\/p>\n\n<p>It's always nerve-racking contributing to a new projects, but let's see how it goes.<\/p>\n\n<p>I'm thinking of it as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/14\/free-code-reviews\">a free code review<\/a>.<\/p>\n\n<p>If it goes well, I can see me contributing more packages in the future.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "0e6a565b98631d7f71c7e8d4d68d0d26",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "2a8452d0-735c-41de-88ef-a9cc81f4035f"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Caching with decorators"
}
],
"created": [
{
"value": "2025-04-06T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/06\/caching",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/05\/strategies\">working with different versions of an API<\/a>, I was able to use the same technique I wrote about yesterday to easily add a cacheable version of the API client.<\/p>\n\n<p>As they all implement the same <code>ApiClientInterface<\/code>, I can inject and decorate a client with another client, making one solely responsible for caching the result from the API whilst keeping the API interaction logic separate (aka <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/12\/08\/the-decorator-design-pattern\">the Decorator design pattern<\/a>).<\/p>\n\n<p>Here's an example based on the code I wrote:<\/p>\n\n<pre><code class=\"php\">final class CacheableApiClient implements ApiClientInterface {\n\n \/**\n * The cache duration in seconds.\n *\/\n private const CACHE_DURATION = 3600;\n\n public function __construct(\n private readonly ApiClientInterface $client,\n private readonly TimeInterface $time,\n private readonly CacheBackendInterface $cache,\n ) {\n }\n\n public function getResults(): Collection {\n $key = $this-&gt;getCacheKey();\n\n $cache = $this-&gt;cache-&gt;get($key);\n\n if ($cache !== FALSE) {\n return $cache-&gt;data;\n }\n\n $result = $this-&gt;client-&gt;getResults();\n\n $this-&gt;cache-&gt;set(\n cid: $key,\n data: $result,\n expire: $this-&gt;time-&gt;getRequestTime() + self::CACHE_DURATION,\n );\n\n return $result;\n }\n\n}\n<\/code><\/pre>\n\n<p>Nothing in this instance is specific to either version of the API.<\/p>\n\n<p>This client is only concerned with retrieving and saving cache data, and delegating any other logic to the original version.<\/p>\n\n<p>With this approach, I can switch between <code>V1ApiClient<\/code>, <code>V2ApiClient<\/code> or any other version with the same methods without having to reimplement caching as that's handled within the <code>CacheableApiClient<\/code>.<\/p>\n\n<p>But what if I don't want to interact with the API at all?<\/p>\n\n<p>For local development, I have a <code>FakeApiClient<\/code> that returns a static response that I can work with.<\/p>\n\n<p>The possibilities are endless.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/05\/strategies\">working with different versions of an API<\/a>, I was able to use the same technique I wrote about yesterday to easily add a cacheable version of the API client.<\/p>\n\n<p>As they all implement the same <code>ApiClientInterface<\/code>, I can inject and decorate a client with another client, making one solely responsible for caching the result from the API whilst keeping the API interaction logic separate (aka <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2022\/12\/08\/the-decorator-design-pattern\">the Decorator design pattern<\/a>).<\/p>\n\n<p>Here's an example based on the code I wrote:<\/p>\n\n<pre><code class=\"php\">final class CacheableApiClient implements ApiClientInterface {\n\n \/**\n * The cache duration in seconds.\n *\/\n private const CACHE_DURATION = 3600;\n\n public function __construct(\n private readonly ApiClientInterface $client,\n private readonly TimeInterface $time,\n private readonly CacheBackendInterface $cache,\n ) {\n }\n\n public function getResults(): Collection {\n $key = $this-&gt;getCacheKey();\n\n $cache = $this-&gt;cache-&gt;get($key);\n\n if ($cache !== FALSE) {\n return $cache-&gt;data;\n }\n\n $result = $this-&gt;client-&gt;getResults();\n\n $this-&gt;cache-&gt;set(\n cid: $key,\n data: $result,\n expire: $this-&gt;time-&gt;getRequestTime() + self::CACHE_DURATION,\n );\n\n return $result;\n }\n\n}\n<\/code><\/pre>\n\n<p>Nothing in this instance is specific to either version of the API.<\/p>\n\n<p>This client is only concerned with retrieving and saving cache data, and delegating any other logic to the original version.<\/p>\n\n<p>With this approach, I can switch between <code>V1ApiClient<\/code>, <code>V2ApiClient<\/code> or any other version with the same methods without having to reimplement caching as that's handled within the <code>CacheableApiClient<\/code>.<\/p>\n\n<p>But what if I don't want to interact with the API at all?<\/p>\n\n<p>For local development, I have a <code>FakeApiClient<\/code> that returns a static response that I can work with.<\/p>\n\n<p>The possibilities are endless.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "dd52b641b6c5a3e8eb19c47343f766c3",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "3cbded61-45d4-46f7-95a0-a0ebb3da9847"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Dont commit changes with `-m`"
}
],
"created": [
{
"value": "2025-04-02T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/02\/commit",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>A common thing I see when reading posts or watch videos where people are using Git is using the <code>-m<\/code> option when committing changes.<\/p>\n\n<p><code>-m<\/code> allows you to specify the commit message inline or, more specifically, the first line of the commit message.<\/p>\n\n<p>If you think of a commit message as an email, the first line is the subject line which is followed by the body of the message.<\/p>\n\n<p>If you don't use <code>-m<\/code>, Git will open an editor and you can type the full commit message into a file and save it.<\/p>\n\n<p>This includes the subject line and, more importantly, the body of the message where you can include as much additional information as you want.<\/p>\n\n<p>The subject line summarises the change, but the body can be used to explain why it was needed.<\/p>\n\n<p>You can describe the issue or requirements in more detail (don't just link to the issue or enter the issue number).<\/p>\n\n<p>You can describe any other approaches you considered or tried.<\/p>\n\n<p>You can describe any anticipated effects or consequences of this commit, any manual deployment steps or follow up tasks that will need to be created.<\/p>\n\n<p>You can include any additional information you were aware of at the time of making the commit that could be useful to yourself or others in the future.<\/p>\n\n<p>Think what information would you like to see when you next run <code>git log<\/code>.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>A common thing I see when reading posts or watch videos where people are using Git is using the <code>-m<\/code> option when committing changes.<\/p>\n\n<p><code>-m<\/code> allows you to specify the commit message inline or, more specifically, the first line of the commit message.<\/p>\n\n<p>If you think of a commit message as an email, the first line is the subject line which is followed by the body of the message.<\/p>\n\n<p>If you don't use <code>-m<\/code>, Git will open an editor and you can type the full commit message into a file and save it.<\/p>\n\n<p>This includes the subject line and, more importantly, the body of the message where you can include as much additional information as you want.<\/p>\n\n<p>The subject line summarises the change, but the body can be used to explain why it was needed.<\/p>\n\n<p>You can describe the issue or requirements in more detail (don't just link to the issue or enter the issue number).<\/p>\n\n<p>You can describe any other approaches you considered or tried.<\/p>\n\n<p>You can describe any anticipated effects or consequences of this commit, any manual deployment steps or follow up tasks that will need to be created.<\/p>\n\n<p>You can include any additional information you were aware of at the time of making the commit that could be useful to yourself or others in the future.<\/p>\n\n<p>Think what information would you like to see when you next run <code>git log<\/code>.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "60ee5cbd523e01444113724332b468eb",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "3d15dfe9-c530-40d6-8923-f5d7e7b3018b"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "CSS variables everywhere"
}
],
"created": [
{
"value": "2025-03-08T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/08\/variables",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Now <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/28\/preprocessors\">CSS supports variables<\/a> (aka custom properties) and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/07\/16\/tailwind-css-v4--with-even-more-css\">Tailwind CSS v4 is configured using CSS<\/a> instead of JavaScript, I've been making heavy use of CSS variables in my front-end code.<\/p>\n\n<p>I still use Tailwind to do the heavy lifting, but I can use CSS variables to extract themeable classes with variables like <code>--color-primary<\/code> that can change value based on a data attribute or by something else.<\/p>\n\n<p>These variables can still use Tailwind's core variables by doing <code>--color-primary: var(--color-red-500)<\/code>, rather than having to recreate all its colors, spacing or whatever variables I need to use.<\/p>\n\n<p>Tailwind has a arbitrary syntax to easily use CSS variables - e.g. <code>bg-(--color-primary)<\/code> - and you can define one-off variables with <code>[--box-spacing:30px]<\/code> or <code>[--box-spacing:--spacing(3)]<\/code> and using the standard arbitrary class syntax.<\/p>\n\n<p>CSS variables aren't specific to Tailwind CSS, so if I wasn't using Tailwind in a codebase, I'd use the new native CSS features <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/26\/is-it-time-to-stop-writing-sass\">instead of a preprocessor like Sass<\/a>.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Now <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/28\/preprocessors\">CSS supports variables<\/a> (aka custom properties) and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/07\/16\/tailwind-css-v4--with-even-more-css\">Tailwind CSS v4 is configured using CSS<\/a> instead of JavaScript, I've been making heavy use of CSS variables in my front-end code.<\/p>\n\n<p>I still use Tailwind to do the heavy lifting, but I can use CSS variables to extract themeable classes with variables like <code>--color-primary<\/code> that can change value based on a data attribute or by something else.<\/p>\n\n<p>These variables can still use Tailwind's core variables by doing <code>--color-primary: var(--color-red-500)<\/code>, rather than having to recreate all its colors, spacing or whatever variables I need to use.<\/p>\n\n<p>Tailwind has a arbitrary syntax to easily use CSS variables - e.g. <code>bg-(--color-primary)<\/code> - and you can define one-off variables with <code>[--box-spacing:30px]<\/code> or <code>[--box-spacing:--spacing(3)]<\/code> and using the standard arbitrary class syntax.<\/p>\n\n<p>CSS variables aren't specific to Tailwind CSS, so if I wasn't using Tailwind in a codebase, I'd use the new native CSS features <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/26\/is-it-time-to-stop-writing-sass\">instead of a preprocessor like Sass<\/a>.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "5a6518d8c14841bf4d760dcc8afd3399",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "3fa16a10-1230-498f-ad80-4cab3ae96a28"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Legacy code is anything older than..."
}
],
"created": [
{
"value": "2025-03-22T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/22\/legacy",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>How do you define legacy code?<\/p>\n\n<p>Code that was written by someone else?<\/p>\n\n<p>Code that doesn't have tests?<\/p>\n\n<p>Any code that has been released to production?<\/p>\n\n<p>Code that's more than a day old?<\/p>\n\n<p>In a talk I recently watched, the speaker suggested that any code written more than thirty minutes ago is legacy code.<\/p>\n\n<p>Once you've written some code and left it for half an hour, you need to re-read it to remember and re-learn what the code does and decide how you want to implement your next requirement.<\/p>\n\n<p>This is the same approach for code that was written longer ago or written by someone else.<\/p>\n\n<p>What do you think?<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>How do you define legacy code?<\/p>\n\n<p>Code that was written by someone else?<\/p>\n\n<p>Code that doesn't have tests?<\/p>\n\n<p>Any code that has been released to production?<\/p>\n\n<p>Code that's more than a day old?<\/p>\n\n<p>In a talk I recently watched, the speaker suggested that any code written more than thirty minutes ago is legacy code.<\/p>\n\n<p>Once you've written some code and left it for half an hour, you need to re-read it to remember and re-learn what the code does and decide how you want to implement your next requirement.<\/p>\n\n<p>This is the same approach for code that was written longer ago or written by someone else.<\/p>\n\n<p>What do you think?<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "9983c859dafd94ec45282b7438b9cb59",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "43617141-40c9-4462-b130-60ee7697ab9d"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Automate Drupal deployments with configuration"
}
],
"created": [
{
"value": "2025-04-20T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/20\/config",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Almost all Drupal projects have multiple environments - production and one or more pre-production environments.<\/p>\n\n<p>A lot have development and staging, some have QA or UAT, or an environment per feature or sprint.<\/p>\n\n<p>Each Developer has their own local environment to work on.<\/p>\n\n<p>As new things are added, such as content types, fields and views, they need to be present on all environments.<\/p>\n\n<p>When I started using Drupal, I needed to perform the same steps to manually recreate the changes on each environment.<\/p>\n\n<p>Later, people started to use the Features module to export configuration into modules that could be committed and deployed as code.<\/p>\n\n<p>This was accompanied by \"deploy\" modules that included update hooks to revert features or perform other tasks.<\/p>\n\n<p>Since version 8, Drupal has had the Configuration Synchronization module.<\/p>\n\n<p>Developers make changes once and export them to files using a simple command like <code>drush config:export<\/code>.<\/p>\n\n<p>Once the code has been deployed to each environment, run <code>drush config:import<\/code> to import the changes.<\/p>\n\n<p>This will synchronise the configuration on the environment, making it the same as the exported configuration in an automated way - the same way every time.<\/p>\n\n<p>Much quicker and more robust than doing it manually.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Almost all Drupal projects have multiple environments - production and one or more pre-production environments.<\/p>\n\n<p>A lot have development and staging, some have QA or UAT, or an environment per feature or sprint.<\/p>\n\n<p>Each Developer has their own local environment to work on.<\/p>\n\n<p>As new things are added, such as content types, fields and views, they need to be present on all environments.<\/p>\n\n<p>When I started using Drupal, I needed to perform the same steps to manually recreate the changes on each environment.<\/p>\n\n<p>Later, people started to use the Features module to export configuration into modules that could be committed and deployed as code.<\/p>\n\n<p>This was accompanied by \"deploy\" modules that included update hooks to revert features or perform other tasks.<\/p>\n\n<p>Since version 8, Drupal has had the Configuration Synchronization module.<\/p>\n\n<p>Developers make changes once and export them to files using a simple command like <code>drush config:export<\/code>.<\/p>\n\n<p>Once the code has been deployed to each environment, run <code>drush config:import<\/code> to import the changes.<\/p>\n\n<p>This will synchronise the configuration on the environment, making it the same as the exported configuration in an automated way - the same way every time.<\/p>\n\n<p>Much quicker and more robust than doing it manually.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "a689477a80adb90fdbe7160be7d2450e",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "4403292d-2b01-4681-afb2-0ab2113694a6"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "No-one is paying us to..."
}
],
"created": [
{
"value": "2025-03-19T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/19\/effective",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>A great quote I recently heard whilst watching a conference talk was \"No-one is paying us to do <em>something<\/em>, but we need to do it to work effectively\".<\/p>\n\n<p>This talk was about decoupling, but the same quote could be re-used for various topics.<\/p>\n\n<p>A common one is automated tests and test-driven development.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/23\/line-item\">Tests aren't a line item<\/a> that should be billed for separately and clients and customers just want their software to work.<\/p>\n\n<p>Generally, they aren't concerned that there are automated tests or whether the tests were written before the code.<\/p>\n\n<p>If I need to give an estimate, I always include time to write automated tests.<\/p>\n\n<p>It's part of my software development process.<\/p>\n\n<p>Whether people know they're paying for me to write automated tests as part or not, I can work more effectively when I have tests and am doing test-driven development.<\/p>\n\n<p>Both when writing the code, but also if it needs to be <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/10\/20\/test-then-refactor\">refactored or changed in the future<\/a>.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>A great quote I recently heard whilst watching a conference talk was \"No-one is paying us to do <em>something<\/em>, but we need to do it to work effectively\".<\/p>\n\n<p>This talk was about decoupling, but the same quote could be re-used for various topics.<\/p>\n\n<p>A common one is automated tests and test-driven development.<\/p>\n\n<p><a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/23\/line-item\">Tests aren't a line item<\/a> that should be billed for separately and clients and customers just want their software to work.<\/p>\n\n<p>Generally, they aren't concerned that there are automated tests or whether the tests were written before the code.<\/p>\n\n<p>If I need to give an estimate, I always include time to write automated tests.<\/p>\n\n<p>It's part of my software development process.<\/p>\n\n<p>Whether people know they're paying for me to write automated tests as part or not, I can work more effectively when I have tests and am doing test-driven development.<\/p>\n\n<p>Both when writing the code, but also if it needs to be <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/10\/20\/test-then-refactor\">refactored or changed in the future<\/a>.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "4b75b46173ea0fe84b24f6e4c3cfcdb5",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "5cc406b1-f4ca-49c0-a296-d352086fc7f4"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "nix is like nvm, but for everything"
}
],
"created": [
{
"value": "2025-04-15T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/15\/nix-nvm",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I was recently explaining and demonstrating Nix and direnv to a colleague and showing how, when I moved into a directory, new packages or different versions of packages became available.<\/p>\n\n<p>If I left the directory, I was reverted back to my global packages and versions.<\/p>\n\n<p>In this demonstration, I was showing how I can have different versions of PHP and node for a particular project - replacing a lot of what I'd previously used tools like Vagrant and Docker for.<\/p>\n\n<p>I came up with a comparison between Nix and nvm - the node version manager - a tool that allows you to install multiple versions of nodejs and switch between them.<\/p>\n\n<p>Using Nix and direnv is more seamless, but it works for everything.<\/p>\n\n<p>I'm able to switch versions of PHP, MySQL, MariaDB, PostgreSQL or anything else I need with Nix.<\/p>\n\n<p>Not just node, and without needing containers.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I was recently explaining and demonstrating Nix and direnv to a colleague and showing how, when I moved into a directory, new packages or different versions of packages became available.<\/p>\n\n<p>If I left the directory, I was reverted back to my global packages and versions.<\/p>\n\n<p>In this demonstration, I was showing how I can have different versions of PHP and node for a particular project - replacing a lot of what I'd previously used tools like Vagrant and Docker for.<\/p>\n\n<p>I came up with a comparison between Nix and nvm - the node version manager - a tool that allows you to install multiple versions of nodejs and switch between them.<\/p>\n\n<p>Using Nix and direnv is more seamless, but it works for everything.<\/p>\n\n<p>I'm able to switch versions of PHP, MySQL, MariaDB, PostgreSQL or anything else I need with Nix.<\/p>\n\n<p>Not just node, and without needing containers.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "1f1938498491ff40c88d4e5b99fc617b",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "641d773f-97cb-4da8-93e9-69df3c89261c"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Automated tests reduce debugging time"
}
],
"created": [
{
"value": "2025-04-01T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/01\/debugging",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>In my <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/tdd-test-driven-drupal\">talk about automated testing and test-driven development<\/a>, I speak about a custom module I wrote for a client's Drupal project.<\/p>\n\n<p>It was to integrate with a third-party job application system that they would log into and enter information via a form (similar to Drupal's node forms) that they wanted to appear on the Drupal website.<\/p>\n\n<p>Once submitted, the system would send a POST request to an endpoint provided by the custom module. The module would process it and create the job node with the appropriate field values.<\/p>\n\n<p>I wrote automated tests and did test-driven development.<\/p>\n\n<p>Everything worked great for a few weeks or months.<\/p>\n\n<p>Later, we had a message from the client saying the integration was broken and we needed to fix it.<\/p>\n\n<p>I don't think I'd worked on this module for a while, but I was concerned that a Drupal core update or another change could have caused a regression.<\/p>\n\n<p>The first thing I did was run the tests I'd written and verify they still passed, which they did.<\/p>\n\n<p>This confirmed that as long as the data was being sent in the expected format, the node would be created and the integration would work.<\/p>\n\n<p>Due to an upstream issue, the data was no longer in the expected format, which meant the node could not be created.<\/p>\n\n<p>Once it was fixed, everything started working again.<\/p>\n\n<p>My debugging time was practically zero as I was able to rely on my tests and that they were still passing.<\/p>\n\n<p>I could confidently tell the client that the issue wasn't with our code and must've been an third-party issue.<\/p>\n\n<p>Writing tests takes some time, but having tests saves time.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>In my <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/tdd-test-driven-drupal\">talk about automated testing and test-driven development<\/a>, I speak about a custom module I wrote for a client's Drupal project.<\/p>\n\n<p>It was to integrate with a third-party job application system that they would log into and enter information via a form (similar to Drupal's node forms) that they wanted to appear on the Drupal website.<\/p>\n\n<p>Once submitted, the system would send a POST request to an endpoint provided by the custom module. The module would process it and create the job node with the appropriate field values.<\/p>\n\n<p>I wrote automated tests and did test-driven development.<\/p>\n\n<p>Everything worked great for a few weeks or months.<\/p>\n\n<p>Later, we had a message from the client saying the integration was broken and we needed to fix it.<\/p>\n\n<p>I don't think I'd worked on this module for a while, but I was concerned that a Drupal core update or another change could have caused a regression.<\/p>\n\n<p>The first thing I did was run the tests I'd written and verify they still passed, which they did.<\/p>\n\n<p>This confirmed that as long as the data was being sent in the expected format, the node would be created and the integration would work.<\/p>\n\n<p>Due to an upstream issue, the data was no longer in the expected format, which meant the node could not be created.<\/p>\n\n<p>Once it was fixed, everything started working again.<\/p>\n\n<p>My debugging time was practically zero as I was able to rely on my tests and that they were still passing.<\/p>\n\n<p>I could confidently tell the client that the issue wasn't with our code and must've been an third-party issue.<\/p>\n\n<p>Writing tests takes some time, but having tests saves time.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "8f98e53db611d2a06dded4a390a73ab4",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "6900edff-5340-44ce-b328-9f3d24ab673b"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "With patches, I can change anything I want"
}
],
"created": [
{
"value": "2025-04-21T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/21\/patch",
"langcode": "en"
}
],
"body": [
{
"value": "\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 ",
"format": "full_html",
"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": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "8a79fcd4efce6727224214430cc26505",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "6d520198-e187-44dd-b434-034f0f74d5c2"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Building fonts with Nix"
}
],
"created": [
{
"value": "2025-04-16T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/16\/fonts",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I recently started <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">using Nix to build my PDF presentation slides<\/a> that <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">I create with rst2pdf<\/a>.<\/p>\n\n<p>I removed the custom build script that was generating the PDF files and moved that code into a Nix derivation.<\/p>\n\n<p>Now I can run <code>nix build .#test-driven-drupal<\/code> and it will generate the slides for that talk.<\/p>\n\n<p>As well as the files specific to each presentation, I also have a derivation for shared assets that apply to all talks - i.e. the stylesheets and fonts that are embedded within the PDF.<\/p>\n\n<p>The font files were stored in the repository but I wanted to remove them and use the font files available in nixpkgs.<\/p>\n\n<p>After some small changes, <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\/commit\/e24d2df83f04e492151b1a1f4901490ce76ffd45\">I was able to do it<\/a> and remove the font files from my repository.<\/p>\n\n<p>The Inter font is available in nixpkgs, but it downloads an <code>InterVariable.ttf<\/code> font that rst2pdf didn't know how to change the font weight for, so I made my own derivation of the static Inter font based on the releases from GitHub.<\/p>\n\n<p>I'm happy that I was able to achieve this, as my repository is leaner and I'm continuing to find new and interesting uses for Nix in my workflows.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I recently started <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">using Nix to build my PDF presentation slides<\/a> that <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/building-presenting-slide-decks-rst2pdf\">I create with rst2pdf<\/a>.<\/p>\n\n<p>I removed the custom build script that was generating the PDF files and moved that code into a Nix derivation.<\/p>\n\n<p>Now I can run <code>nix build .#test-driven-drupal<\/code> and it will generate the slides for that talk.<\/p>\n\n<p>As well as the files specific to each presentation, I also have a derivation for shared assets that apply to all talks - i.e. the stylesheets and fonts that are embedded within the PDF.<\/p>\n\n<p>The font files were stored in the repository but I wanted to remove them and use the font files available in nixpkgs.<\/p>\n\n<p>After some small changes, <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/talks\/commit\/e24d2df83f04e492151b1a1f4901490ce76ffd45\">I was able to do it<\/a> and remove the font files from my repository.<\/p>\n\n<p>The Inter font is available in nixpkgs, but it downloads an <code>InterVariable.ttf<\/code> font that rst2pdf didn't know how to change the font weight for, so I made my own derivation of the static Inter font based on the releases from GitHub.<\/p>\n\n<p>I'm happy that I was able to achieve this, as my repository is leaner and I'm continuing to find new and interesting uses for Nix in my workflows.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "663165d347672170986b3ba5d0f0ac13",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "6e6b15bc-6319-4392-ac07-7c38fb66e6a4"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Don't hack core"
}
],
"created": [
{
"value": "2025-05-01T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/05\/01\/hack",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I've worked on a lot of Drupal projects and there have been times when previous Developers have \"hacked\" Drupal core or contrib modules by editing them to add or change functionality.<\/p>\n\n<p>This is a short-sighted approach, as any changes to third-party code such as Drupal core or contrib modules would be lost when a new version is downloaded.<\/p>\n\n<p>Most of the time, changes can be made within custom code that won't be overwritten and accidentally lost.<\/p>\n\n<p>But in cases where the source code needs to be changed, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/01\/14\/patching-drupal\">you can patch files instead<\/a>.<\/p>\n\n<p>You can commit the patch file to your project to save the changes, but you're responsible for maintaining it and ensuring it applies to any future updates of the code.<\/p>\n\n<p>If you can, commit the changes upstream.<\/p>\n\n<p>Then you won't need to patch files and others will benefit from the contribution.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I've worked on a lot of Drupal projects and there have been times when previous Developers have \"hacked\" Drupal core or contrib modules by editing them to add or change functionality.<\/p>\n\n<p>This is a short-sighted approach, as any changes to third-party code such as Drupal core or contrib modules would be lost when a new version is downloaded.<\/p>\n\n<p>Most of the time, changes can be made within custom code that won't be overwritten and accidentally lost.<\/p>\n\n<p>But in cases where the source code needs to be changed, <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/01\/14\/patching-drupal\">you can patch files instead<\/a>.<\/p>\n\n<p>You can commit the patch file to your project to save the changes, but you're responsible for maintaining it and ensuring it applies to any future updates of the code.<\/p>\n\n<p>If you can, commit the changes upstream.<\/p>\n\n<p>Then you won't need to patch files and others will benefit from the contribution.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "7ea5ffe7f98df1811de34c43a7caca75",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "72729ed3-73ab-4e13-9115-fbf6d8ca6b08"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Upgrading incrementally"
}
],
"created": [
{
"value": "2025-04-17T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/17\/incrementally",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Did you know you can have more than one version of your website in production at the same time?<\/p>\n\n<p>If you're migrating to a new platform or upgrading to a new major version, such as Drupal 7 to Drupal 11, you don't need to do it all at once.<\/p>\n\n<p>You can do it incrementally, and seamlessly for end users.<\/p>\n\n<p>The MVP for your new website can be a particular section or a single page that is developed and launched to production alongside the current website.<\/p>\n\n<p>Depending on the path someone goes to, they'll either see the new website or the old one.<\/p>\n\n<p>Focusing on delivering a single page or section of a website is much faster compared to rebuilding the entire thing, it's a lot less risky as the feedback loop is much shorter and you get feedback from real users.<\/p>\n\n<p>Once you have the new MVP deployed to a hosting environment, you can configure a proxy to assign traffic between it and the existing hosting based on path, request headers, or a combination of different options.<\/p>\n\n<p>This is an approach I've taken with my website as I've upgraded between different versions of Drupal and also static site generators such as Jekyll, Astro and Sculpin.<\/p>\n\n<p>Instead of waiting for months to deliver a new website to production, do it in days or weeks.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Did you know you can have more than one version of your website in production at the same time?<\/p>\n\n<p>If you're migrating to a new platform or upgrading to a new major version, such as Drupal 7 to Drupal 11, you don't need to do it all at once.<\/p>\n\n<p>You can do it incrementally, and seamlessly for end users.<\/p>\n\n<p>The MVP for your new website can be a particular section or a single page that is developed and launched to production alongside the current website.<\/p>\n\n<p>Depending on the path someone goes to, they'll either see the new website or the old one.<\/p>\n\n<p>Focusing on delivering a single page or section of a website is much faster compared to rebuilding the entire thing, it's a lot less risky as the feedback loop is much shorter and you get feedback from real users.<\/p>\n\n<p>Once you have the new MVP deployed to a hosting environment, you can configure a proxy to assign traffic between it and the existing hosting based on path, request headers, or a combination of different options.<\/p>\n\n<p>This is an approach I've taken with my website as I've upgraded between different versions of Drupal and also static site generators such as Jekyll, Astro and Sculpin.<\/p>\n\n<p>Instead of waiting for months to deliver a new website to production, do it in days or weeks.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "36902cd3f90743c2ffd35168c819c294",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "737d4134-6074-4498-b8d3-f51d7b71d006"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "What's the correct way to add PHPStan to an existing codebase?"
}
],
"created": [
{
"value": "2025-03-16T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/16\/what-s-the-correct-way-to-add-phpstan-to-an-existing-codebase",
"langcode": "en"
}
],
"body": [
{
"value": "\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=\"https:\/\/www.oliverdavies.uk\/podcast\/22-dave-liddament\">episode 22 of the Beyond Blocks podcast<\/a> with Dave Liddament.<\/p>\n\n ",
"format": "full_html",
"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=\"https:\/\/www.oliverdavies.uk\/podcast\/22-dave-liddament\">episode 22 of the Beyond Blocks podcast<\/a> with Dave Liddament.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "9a7b258b69b97c51304c0aa8d6b87263",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "8c006484-b85e-4eb7-a2c2-f0094a331712"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Feature branching slows delivery"
}
],
"created": [
{
"value": "2025-03-05T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/05\/slow",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/18\/conflicts\">causing merge conflicts<\/a>, feature branches slow the delivery of new features.<\/p>\n\n<p>Someone needs to be responsible for merging the branches once they've been reviewed and approved, and any further merges into release branches.<\/p>\n\n<p>This is typically the Lead Developer on the project, but this person then becomes a bottleneck.<\/p>\n\n<p>They need to oversee all the branches and merges, and know what needs to be merged and where it needs to be deployed.<\/p>\n\n<p>This slows down the speed of delivery.<\/p>\n\n<p>In a trunk-based environment, there's only one branch and everyone can commit and push to it.<\/p>\n\n<p>And, if you're <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/17\/ci-cd\">doing continuous delivery<\/a>, any changes will be automatically deployed.<\/p>\n\n<p>No more bottleneck.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>As well as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/18\/conflicts\">causing merge conflicts<\/a>, feature branches slow the delivery of new features.<\/p>\n\n<p>Someone needs to be responsible for merging the branches once they've been reviewed and approved, and any further merges into release branches.<\/p>\n\n<p>This is typically the Lead Developer on the project, but this person then becomes a bottleneck.<\/p>\n\n<p>They need to oversee all the branches and merges, and know what needs to be merged and where it needs to be deployed.<\/p>\n\n<p>This slows down the speed of delivery.<\/p>\n\n<p>In a trunk-based environment, there's only one branch and everyone can commit and push to it.<\/p>\n\n<p>And, if you're <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/17\/ci-cd\">doing continuous delivery<\/a>, any changes will be automatically deployed.<\/p>\n\n<p>No more bottleneck.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "abfbb6e640ea537c7cb537b44b7994ab",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "98e87c30-8dd5-4d80-aed3-53439673de97"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Extra PHPDoc types with PHPStan"
}
],
"created": [
{
"value": "2025-03-21T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/21\/phpdoc",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Here are some examples of PHP code from Drupal core:<\/p>\n\n<pre><code class=\"php\">\/**\n * The weight of this role in administrative listings.\n *\n * @var int\n *\/\nprotected $weight;\n<\/code><\/pre>\n\n<pre><code class=\"php\">\/**\n * Path of the image file.\n *\n * @var string\n *\/\nprotected $source = '';\n<\/code><\/pre>\n\n<pre><code class=\"php\">\/**\n * Alter the list of mail backend plugin definitions.\n *\n * @param array $info\n * The mail backend plugin definitions to be altered.\n *\/\n<\/code><\/pre>\n\n<p>These use some of the standard PHPDoc types of <code>int<\/code>, <code>string<\/code> and <code>array<\/code>.<\/p>\n\n<p>Although they are comments, docblocks are checked by static analysis tools like PHPStan to parse the code and report any potential errors.<\/p>\n\n<p>If you want to go deeper, PHPStan has <a href=\"https:\/\/phpstan.org\/writing-php-code\/phpdoc-types\">its own PHPDoc types<\/a> that you can use to add more information and context.<\/p>\n\n<p>Instead of specifying an argument must be a <code>string<\/code>, you can specify it's a <code>non-empty-string<\/code> or a <code>class-string<\/code>.<\/p>\n\n<p>You can specify whether an integer is a <code>positive-int<\/code> or <code>negative-int<\/code>, or within a certain range.<\/p>\n\n<p>You can define the shape of an array or object, whether an array is empty, or the types of keys and values in an array.<\/p>\n\n<p>All of this is used by PHPStan when analysing the code and will give better results and find more potential bugs before anyone else does.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Here are some examples of PHP code from Drupal core:<\/p>\n\n<pre><code class=\"php\">\/**\n * The weight of this role in administrative listings.\n *\n * @var int\n *\/\nprotected $weight;\n<\/code><\/pre>\n\n<pre><code class=\"php\">\/**\n * Path of the image file.\n *\n * @var string\n *\/\nprotected $source = '';\n<\/code><\/pre>\n\n<pre><code class=\"php\">\/**\n * Alter the list of mail backend plugin definitions.\n *\n * @param array $info\n * The mail backend plugin definitions to be altered.\n *\/\n<\/code><\/pre>\n\n<p>These use some of the standard PHPDoc types of <code>int<\/code>, <code>string<\/code> and <code>array<\/code>.<\/p>\n\n<p>Although they are comments, docblocks are checked by static analysis tools like PHPStan to parse the code and report any potential errors.<\/p>\n\n<p>If you want to go deeper, PHPStan has <a href=\"https:\/\/phpstan.org\/writing-php-code\/phpdoc-types\">its own PHPDoc types<\/a> that you can use to add more information and context.<\/p>\n\n<p>Instead of specifying an argument must be a <code>string<\/code>, you can specify it's a <code>non-empty-string<\/code> or a <code>class-string<\/code>.<\/p>\n\n<p>You can specify whether an integer is a <code>positive-int<\/code> or <code>negative-int<\/code>, or within a certain range.<\/p>\n\n<p>You can define the shape of an array or object, whether an array is empty, or the types of keys and values in an array.<\/p>\n\n<p>All of this is used by PHPStan when analysing the code and will give better results and find more potential bugs before anyone else does.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "ded9b2a6eb8ff4129f9653ceb3555f58",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "9e166002-410b-4261-aac8-e47dbe1c04d8"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "You can deploy on Fridays"
}
],
"created": [
{
"value": "2025-03-11T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/11\/friday",
"langcode": "en"
}
],
"body": [
{
"value": "\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=\"https:\/\/www.oliverdavies.uk\/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=\"https:\/\/www.oliverdavies.uk\/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 ",
"format": "full_html",
"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=\"https:\/\/www.oliverdavies.uk\/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=\"https:\/\/www.oliverdavies.uk\/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": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "021827be7bf3f083020a0f2ff11066d8",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "af5c2fcc-7f6c-4363-943c-f23dcdab51ca"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Static websites are easy to backup"
}
],
"created": [
{
"value": "2025-03-14T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/14\/backup",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>As well as being <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simple to deploy<\/a>, static websites are easy to backup and, if needed, restore.<\/p>\n\n<p>I backup several static websites from my server using rsync - the same command I use to deploy them.<\/p>\n\n<p>rsync is fast as it only downloads files that have changed, so backing up several websites only takes a few seconds.<\/p>\n\n<p>There are are no databases to worry about - all I need to do is backup the static files themselves.<\/p>\n\n<p>Running the backups is also easy.<\/p>\n\n<p>I have a scheduled cron job that downloads everything from the <code>\/var\/www\/vhosts<\/code> directory on my server and creates a local copy.<\/p>\n\n<p>If I need to restore from a backup or migrate to a different server, I just run the appropriate rsync command to re-upload them - the same as how I deployed them originally.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>As well as being <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simple to deploy<\/a>, static websites are easy to backup and, if needed, restore.<\/p>\n\n<p>I backup several static websites from my server using rsync - the same command I use to deploy them.<\/p>\n\n<p>rsync is fast as it only downloads files that have changed, so backing up several websites only takes a few seconds.<\/p>\n\n<p>There are are no databases to worry about - all I need to do is backup the static files themselves.<\/p>\n\n<p>Running the backups is also easy.<\/p>\n\n<p>I have a scheduled cron job that downloads everything from the <code>\/var\/www\/vhosts<\/code> directory on my server and creates a local copy.<\/p>\n\n<p>If I need to restore from a backup or migrate to a different server, I just run the appropriate rsync command to re-upload them - the same as how I deployed them originally.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "cad56e3254ecbb16885c1f741f0776f9",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "b09ccd35-261d-4fa5-99cc-52f781c52e87"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Be more selective"
}
],
"created": [
{
"value": "2025-04-03T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/03\/selective",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Another common Git issue I see is people using <code>git add .<\/code> to commit every change in every file they have locally.<\/p>\n\n<p>Similar to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/02\/commit\">committing with <code>-m<\/code><\/a>, this seems to be a common in Git tutorials, but can have consequences due to unexpected changes being staged and committed.<\/p>\n\n<p>Maybe there are unrelated changes in the same file or other files have been changed that you don't want to commit yet.<\/p>\n\n<p>What if something was committed and pushed that caused the CI pipeline to fail or break production?<\/p>\n\n<p>At the least, it's going to add time and delay getting the intended changes live as someone will need to revert and fix the commits or address the changes in a code review.<\/p>\n\n<p>I'm very selective about what I include in each commit to keep my code stable and the commits easy to review and, if needed, revert.<\/p>\n\n<p>I always use <code>git add -p<\/code> to interactively stage changes from the command line or use keybindings in my Neovim configuration to add particular lines.<\/p>\n\n<p>I'll also review my staged changes before committing and the commit once it's been made using <code>git log --stat<\/code> to see what's included.<\/p>\n\n<p>Only once I'm sure my commits include only what I intended will I push them or submit them for review.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Another common Git issue I see is people using <code>git add .<\/code> to commit every change in every file they have locally.<\/p>\n\n<p>Similar to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/02\/commit\">committing with <code>-m<\/code><\/a>, this seems to be a common in Git tutorials, but can have consequences due to unexpected changes being staged and committed.<\/p>\n\n<p>Maybe there are unrelated changes in the same file or other files have been changed that you don't want to commit yet.<\/p>\n\n<p>What if something was committed and pushed that caused the CI pipeline to fail or break production?<\/p>\n\n<p>At the least, it's going to add time and delay getting the intended changes live as someone will need to revert and fix the commits or address the changes in a code review.<\/p>\n\n<p>I'm very selective about what I include in each commit to keep my code stable and the commits easy to review and, if needed, revert.<\/p>\n\n<p>I always use <code>git add -p<\/code> to interactively stage changes from the command line or use keybindings in my Neovim configuration to add particular lines.<\/p>\n\n<p>I'll also review my staged changes before committing and the commit once it's been made using <code>git log --stat<\/code> to see what's included.<\/p>\n\n<p>Only once I'm sure my commits include only what I intended will I push them or submit them for review.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "397621c34a88f800e52750829b4bc177",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "bed02c02-476f-4f2f-8d21-0c8c45edb128"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Submit your session proposal for DrupalCon Europe"
}
],
"created": [
{
"value": "2025-03-09T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/09\/submit-your-session-proposal-for-drupalcon-europe",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>The call for session proposals is currently open for DrupalCon Europe, which is happening in Vienna this October.<\/p>\n\n<p>The tracks are slightly different this year.<\/p>\n\n<p>The tracks are:<\/p>\n\n<ul>\n<li>Agency &amp; Business.<\/li>\n<li>Coding &amp; Site Building.<\/li>\n<li>Community Health.<\/li>\n<li>Clients &amp; Industry Experience.<\/li>\n<li>Drupal CMS.<\/li>\n<li>InfoSec &amp; DevOps.<\/li>\n<li>Open Web.<\/li>\n<\/ul>\n\n<p>I'm happy to be on the Coding &amp; Site Building track team this year, having been on the Makers &amp; Builders track previously.<\/p>\n\n<p>The call for proposals is open until the 28th of April, but feel free to <a href=\"https:\/\/kuonicongress.eventsair.com\/drupalcon-vienna-2025\/session-submission-portal\">get your session proposal in now<\/a>.<\/p>\n\n<p>For more information and blog posts from the track teams, go to <a href=\"https:\/\/events.drupal.org\/vienna2025\">https:\/\/events.drupal.org\/vienna2025<\/a>.<\/p>\n\n<p>If you're submitting to the Coding &amp; Site Building track, I'll look forward to reading your proposal.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>The call for session proposals is currently open for DrupalCon Europe, which is happening in Vienna this October.<\/p>\n\n<p>The tracks are slightly different this year.<\/p>\n\n<p>The tracks are:<\/p>\n\n<ul>\n<li>Agency &amp; Business.<\/li>\n<li>Coding &amp; Site Building.<\/li>\n<li>Community Health.<\/li>\n<li>Clients &amp; Industry Experience.<\/li>\n<li>Drupal CMS.<\/li>\n<li>InfoSec &amp; DevOps.<\/li>\n<li>Open Web.<\/li>\n<\/ul>\n\n<p>I'm happy to be on the Coding &amp; Site Building track team this year, having been on the Makers &amp; Builders track previously.<\/p>\n\n<p>The call for proposals is open until the 28th of April, but feel free to <a href=\"https:\/\/kuonicongress.eventsair.com\/drupalcon-vienna-2025\/session-submission-portal\">get your session proposal in now<\/a>.<\/p>\n\n<p>For more information and blog posts from the track teams, go to <a href=\"https:\/\/events.drupal.org\/vienna2025\">https:\/\/events.drupal.org\/vienna2025<\/a>.<\/p>\n\n<p>If you're submitting to the Coding &amp; Site Building track, I'll look forward to reading your proposal.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "44eace8ddc51bab34e23bb1cc0825908",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "c4ee2105-bde9-49ad-89e2-2c06c215bdbb"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Chaining tools for maximum benefit"
}
],
"created": [
{
"value": "2025-04-08T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/08\/chaining",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Yesterday I showed <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">how I'm using Nix to build my presentation slide decks<\/a> with rst2pdf.<\/p>\n\n<p>This allows me to run a simple command like <code>nix build .#test-driven-drupal<\/code> to build the slides for the given presentation.<\/p>\n\n<p>But I can use other tools to make this even easier.<\/p>\n\n<p>What if I wanted to have a list of the available presentations to select from, and selecting one would build it?<\/p>\n\n<p>Following the UNIX philosophy, I can use multiple tools together to achieve this.<\/p>\n\n<p>Firstly, I can run <code>nix flake show --json<\/code> to show the output from my flake.nix file, which looks something like this:<\/p>\n\n<pre><code class=\"json\">{\n \"devShells\": { ... },\n \"formatter\": { ... },\n \"packages\": {\n \"x86_64-linux\": {\n \"build-configs\": { ... },\n \"sculpin\": { ... },\n \"shared\": { ... }\n }\n }\n}\n<\/code><\/pre>\n\n<p>The package names - a.k.a. the presentation names - are what I want to select from.<\/p>\n\n<p>I can parse the JSON object with <code>jq<\/code>, remove any unwanted options with <code>grep -v<\/code> and use <code>fzf<\/code> to give me a list I can fuzzy search in.<\/p>\n\n<p>In a Bash script, I can assign this to a variable:<\/p>\n\n<pre><code class=\"bash\">selected=$(nix flake show --json | jq --raw-output '.packages[\"x86_64-linux\"] | keys[]' | grep -v shared | fzf)\n<\/code><\/pre>\n\n<p>Once I have selected a name, I can call <code>nix build<\/code> on it.<\/p>\n\n<pre><code class=\"bash\">nix build .#\"$selected\"\n<\/code><\/pre>\n\n<p>This is a simple example, but it shows how programs can be used together and output can be passed through each program to get the result you want.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Yesterday I showed <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/07\/nix-rst2pdf\">how I'm using Nix to build my presentation slide decks<\/a> with rst2pdf.<\/p>\n\n<p>This allows me to run a simple command like <code>nix build .#test-driven-drupal<\/code> to build the slides for the given presentation.<\/p>\n\n<p>But I can use other tools to make this even easier.<\/p>\n\n<p>What if I wanted to have a list of the available presentations to select from, and selecting one would build it?<\/p>\n\n<p>Following the UNIX philosophy, I can use multiple tools together to achieve this.<\/p>\n\n<p>Firstly, I can run <code>nix flake show --json<\/code> to show the output from my flake.nix file, which looks something like this:<\/p>\n\n<pre><code class=\"json\">{\n \"devShells\": { ... },\n \"formatter\": { ... },\n \"packages\": {\n \"x86_64-linux\": {\n \"build-configs\": { ... },\n \"sculpin\": { ... },\n \"shared\": { ... }\n }\n }\n}\n<\/code><\/pre>\n\n<p>The package names - a.k.a. the presentation names - are what I want to select from.<\/p>\n\n<p>I can parse the JSON object with <code>jq<\/code>, remove any unwanted options with <code>grep -v<\/code> and use <code>fzf<\/code> to give me a list I can fuzzy search in.<\/p>\n\n<p>In a Bash script, I can assign this to a variable:<\/p>\n\n<pre><code class=\"bash\">selected=$(nix flake show --json | jq --raw-output '.packages[\"x86_64-linux\"] | keys[]' | grep -v shared | fzf)\n<\/code><\/pre>\n\n<p>Once I have selected a name, I can call <code>nix build<\/code> on it.<\/p>\n\n<pre><code class=\"bash\">nix build .#\"$selected\"\n<\/code><\/pre>\n\n<p>This is a simple example, but it shows how programs can be used together and output can be passed through each program to get the result you want.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "6a6105cc8b6ef3e9fadb5aec0f56aaeb",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "c6ef991b-2262-4942-b1f9-34c8a3afced4"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Rebase and reorder"
}
],
"created": [
{
"value": "2025-03-07T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/07\/rebase-and-reorder",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Sometimes when <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/11\/tidy\">tidying my commits<\/a> or updating a local branch with remote changes, the order of commits changes - making them out of order in when running <code>git log<\/code>.<\/p>\n\n<p>I want the commits in the log to be in the correct sequential order.<\/p>\n\n<p>If not, it would be confusing if I review the commits in the future.<\/p>\n\n<p>This is easy to fix when running <code>git rebase -i<\/code> to perform an interactive rebase on the commits.<\/p>\n\n<p>The commit has a <code>-x<\/code> or <code>--exec<\/code> option that will perform a given command on each commit.<\/p>\n\n<p>The commit date can be reset using <code>git reset --amend<\/code>, and combining these commands will amend the date of each commit.<\/p>\n\n<p>Running <code>git rebase --interactive --exec \"git commit --amend --no-edit --date now\"<\/code> will amend and update each commit, keeping the commit message the same, but changing the commit date to the current time - leaving the Git log in the correct order.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Sometimes when <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/11\/tidy\">tidying my commits<\/a> or updating a local branch with remote changes, the order of commits changes - making them out of order in when running <code>git log<\/code>.<\/p>\n\n<p>I want the commits in the log to be in the correct sequential order.<\/p>\n\n<p>If not, it would be confusing if I review the commits in the future.<\/p>\n\n<p>This is easy to fix when running <code>git rebase -i<\/code> to perform an interactive rebase on the commits.<\/p>\n\n<p>The commit has a <code>-x<\/code> or <code>--exec<\/code> option that will perform a given command on each commit.<\/p>\n\n<p>The commit date can be reset using <code>git reset --amend<\/code>, and combining these commands will amend the date of each commit.<\/p>\n\n<p>Running <code>git rebase --interactive --exec \"git commit --amend --no-edit --date now\"<\/code> will amend and update each commit, keeping the commit message the same, but changing the commit date to the current time - leaving the Git log in the correct order.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "945a9ecd7e9f1ec6de312f2b03035231",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "cac7e909-60cc-48f1-9535-b1b3ea3347ad"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Drupal test writer for hire"
}
],
"created": [
{
"value": "2025-03-27T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/27\/for-hire",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>There are a lot of <a href=\"https:\/\/www.drupal.org\/project\/issues\/search?status[]=1&amp;status[]=13&amp;issue_tags_op=%3D&amp;issue_tags=Needs+tests\">open issues on Drupal.org<\/a> that have the \"Needs tests\" label.<\/p>\n\n<p>These could be maintainers leaving themselves reminders to add tests once they've finished spiking out the first version of their code, or someone contributing to project but needs an accompanying test to ensure the feature works or that a bug is fixed (and will stay fixed).<\/p>\n\n<p>There are currently 3,711 issues across all projects and 2,030 in Drupal core.<\/p>\n\n<p>If one of those is your issue or module, I can write the tests you need.<\/p>\n\n<p>After my <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/11-mark-conroy\">first podcast episode with Mark Conroy<\/a>, I did this for <a href=\"https:\/\/www.drupal.org\/project\/content_access_by_path\/issues\/3428680\">the Content Access by Path module<\/a>, which now has tests and automated checks with GitLab CI - so they will be run for every future merge request.<\/p>\n\n<p>You can see me writing them on <a href=\"https:\/\/www.youtube.com\/watch?v=XTpliKd47Lg\">a previous live stream<\/a>.<\/p>\n\n<p>If you need tests written or help writing them yourself, reply and get in touch.<\/p>\n\n<p>If you want to <a href=\"https:\/\/www.oliverdavies.uk\/sponsor\">sponsor my contribution time<\/a>, you'll also get contribution credit for any issues or merge requests I work on during that time and, whilst it's been a while since my last live stream, you'd also be mentioned there as I work on these issues.<\/p>\n\n<p>I have around a day a week available for sponsored contribution time.<\/p>\n\n<p>If you're interested, reply and let's get it arranged.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>There are a lot of <a href=\"https:\/\/www.drupal.org\/project\/issues\/search?status[]=1&amp;status[]=13&amp;issue_tags_op=%3D&amp;issue_tags=Needs+tests\">open issues on Drupal.org<\/a> that have the \"Needs tests\" label.<\/p>\n\n<p>These could be maintainers leaving themselves reminders to add tests once they've finished spiking out the first version of their code, or someone contributing to project but needs an accompanying test to ensure the feature works or that a bug is fixed (and will stay fixed).<\/p>\n\n<p>There are currently 3,711 issues across all projects and 2,030 in Drupal core.<\/p>\n\n<p>If one of those is your issue or module, I can write the tests you need.<\/p>\n\n<p>After my <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/11-mark-conroy\">first podcast episode with Mark Conroy<\/a>, I did this for <a href=\"https:\/\/www.drupal.org\/project\/content_access_by_path\/issues\/3428680\">the Content Access by Path module<\/a>, which now has tests and automated checks with GitLab CI - so they will be run for every future merge request.<\/p>\n\n<p>You can see me writing them on <a href=\"https:\/\/www.youtube.com\/watch?v=XTpliKd47Lg\">a previous live stream<\/a>.<\/p>\n\n<p>If you need tests written or help writing them yourself, reply and get in touch.<\/p>\n\n<p>If you want to <a href=\"https:\/\/www.oliverdavies.uk\/sponsor\">sponsor my contribution time<\/a>, you'll also get contribution credit for any issues or merge requests I work on during that time and, whilst it's been a while since my last live stream, you'd also be mentioned there as I work on these issues.<\/p>\n\n<p>I have around a day a week available for sponsored contribution time.<\/p>\n\n<p>If you're interested, reply and let's get it arranged.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "0800e5f256b7e5d9dc618733f4dec508",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "d130088a-0c2c-4dfb-b65d-3b2ee8d6239c"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Self hosting my website"
}
],
"created": [
{
"value": "2025-04-19T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/19\/self-hosting",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I'm now self-hosting my website, the other static websites I maintain and <a href=\"https:\/\/code.oliverdavies.uk\">my Forgejo instance<\/a> that contains my Git repositories.<\/p>\n\n<p>These were previously on a VPS (virtual private server) but now are running on my homelab - an old laptop that I've been using to run Jellyfin and other software.<\/p>\n\n<p>It's great to know I have full control over my data and to not pay a recurring monthly cost forever to host my website. Later, if I decide my current homelab laptop needs an upgrade, I can do that.<\/p>\n\n<p>With NixOS, the transition from the Cloud to my home server was simple and just a case of moving some configuration files from one directory to another as I've set it up in a modular way.<\/p>\n\n<p>I've also switched back to Nginx which was also easy to migrate my configuration and redirects using the Nix language and NixOS configuration options.<\/p>\n\n<p>My website is a static website, mostly <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">generated by Sculpin<\/a> but there are some pages generated by Tome - <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/18\/static-drupal\">a static site generator for Drupal<\/a>.<\/p>\n\n<p>I'm migrating my email list archive and daily pages first and will later move my presentations, podcast episodes and old blog posts, but I'm <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/17\/incrementally\">doing it incrementally based on the path<\/a> so I can do pieces as I can around client work.<\/p>\n\n<p>If you like, you can <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/nixos-configuraton\">see my NixOS configuration<\/a> that includes my laptop and homelab and <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk-tome\">my Tome repository<\/a> and keep track as I migrate things across.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I'm now self-hosting my website, the other static websites I maintain and <a href=\"https:\/\/code.oliverdavies.uk\">my Forgejo instance<\/a> that contains my Git repositories.<\/p>\n\n<p>These were previously on a VPS (virtual private server) but now are running on my homelab - an old laptop that I've been using to run Jellyfin and other software.<\/p>\n\n<p>It's great to know I have full control over my data and to not pay a recurring monthly cost forever to host my website. Later, if I decide my current homelab laptop needs an upgrade, I can do that.<\/p>\n\n<p>With NixOS, the transition from the Cloud to my home server was simple and just a case of moving some configuration files from one directory to another as I've set it up in a modular way.<\/p>\n\n<p>I've also switched back to Nginx which was also easy to migrate my configuration and redirects using the Nix language and NixOS configuration options.<\/p>\n\n<p>My website is a static website, mostly <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">generated by Sculpin<\/a> but there are some pages generated by Tome - <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/18\/static-drupal\">a static site generator for Drupal<\/a>.<\/p>\n\n<p>I'm migrating my email list archive and daily pages first and will later move my presentations, podcast episodes and old blog posts, but I'm <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/04\/17\/incrementally\">doing it incrementally based on the path<\/a> so I can do pieces as I can around client work.<\/p>\n\n<p>If you like, you can <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/nixos-configuraton\">see my NixOS configuration<\/a> that includes my laptop and homelab and <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk-tome\">my Tome repository<\/a> and keep track as I migrate things across.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "8afd6c68260de0b67d4d42f8c66d87b0",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "d51a4843-892e-4d52-81ca-66c2af097478"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Contrib-first doesn't mean building for every use case"
}
],
"created": [
{
"value": "2025-03-10T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/10\/contrib-first",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Most Drupal projects include writing custom code to add functionality required for that project or that doesn't yet exist.<\/p>\n\n<p>Some of this may be identified as code that could be contributed back to the Drupal community and uploaded to Drupal.org as reusable code for others to use.<\/p>\n\n<p>Usually, this involves \"cleaning up\" the code to make it ready to be open sourced - ensuring it complies with coding standards, follows best practices and doesn't contain any sensitive or project-specific data.<\/p>\n\n<p>Unfortunately, this doesn't always happen as the next project or task is waiting to be started, and the code is never contributed.<\/p>\n\n<p>I like to do contrib-first or contrib-driven development, where the code is open sourced upfront, developed in the open and used on the project as I'm developing it.<\/p>\n\n<p>A common argument to this approach is that it takes too much time.<\/p>\n\n<p>I assume that's because people think I need to cover every use case and situation in the module because I'm open sourcing it.<\/p>\n\n<p>That's not true.<\/p>\n\n<p>When I wrote modules like <a href=\"https:\/\/www.drupal.org\/project\/system_user\">System User<\/a>, <a href=\"https:\/\/www.drupal.org\/project\/null_user\">Null User<\/a> and <a href=\"https:\/\/www.drupal.org\/project\/private_message_queue\">Private Message Queue<\/a>, I wrote the same code I'd have written if I was going to contribute it later - but I didn't need to clean it up afterwards.<\/p>\n\n<p>I didn't need to ensure it didn't contain anything I'd need to remove.<\/p>\n\n<p>It wasn't a big task to open source them as they were already open sourced.<\/p>\n\n<p>If other people want to use the module and need additional features, they could open an issue, submit a patch or create their own patches.<\/p>\n\n<p>Just because code has been open sourced doesn't mean it needs to do everything for everyone.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Most Drupal projects include writing custom code to add functionality required for that project or that doesn't yet exist.<\/p>\n\n<p>Some of this may be identified as code that could be contributed back to the Drupal community and uploaded to Drupal.org as reusable code for others to use.<\/p>\n\n<p>Usually, this involves \"cleaning up\" the code to make it ready to be open sourced - ensuring it complies with coding standards, follows best practices and doesn't contain any sensitive or project-specific data.<\/p>\n\n<p>Unfortunately, this doesn't always happen as the next project or task is waiting to be started, and the code is never contributed.<\/p>\n\n<p>I like to do contrib-first or contrib-driven development, where the code is open sourced upfront, developed in the open and used on the project as I'm developing it.<\/p>\n\n<p>A common argument to this approach is that it takes too much time.<\/p>\n\n<p>I assume that's because people think I need to cover every use case and situation in the module because I'm open sourcing it.<\/p>\n\n<p>That's not true.<\/p>\n\n<p>When I wrote modules like <a href=\"https:\/\/www.drupal.org\/project\/system_user\">System User<\/a>, <a href=\"https:\/\/www.drupal.org\/project\/null_user\">Null User<\/a> and <a href=\"https:\/\/www.drupal.org\/project\/private_message_queue\">Private Message Queue<\/a>, I wrote the same code I'd have written if I was going to contribute it later - but I didn't need to clean it up afterwards.<\/p>\n\n<p>I didn't need to ensure it didn't contain anything I'd need to remove.<\/p>\n\n<p>It wasn't a big task to open source them as they were already open sourced.<\/p>\n\n<p>If other people want to use the module and need additional features, they could open an issue, submit a patch or create their own patches.<\/p>\n\n<p>Just because code has been open sourced doesn't mean it needs to do everything for everyone.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "b44dc92d2bb08665b6bec22b5fda467c",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "d52128e8-0927-4232-8c77-e9c0d60578ca"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Don't repeat yourself"
}
],
"created": [
{
"value": "2025-03-26T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/26\/repeat",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>When most people think of \"Don't repeat yourself\" or DRY, they probably think about not duplicating logic in code.<\/p>\n\n<p>If you've written some functionality once, you should avoid writing it again.<\/p>\n\n<p>I was recently browsing the code for an open source package and saw this:<\/p>\n\n<pre><code class=\"php\">\/**\n * Flush everything.\n *\/\npublic function flush(): void;\n\n\/**\n * Sets the formatter.\n *\/\npublic function setFormatter(FormatterInterface $formatter): void;\n\n\/**\n * Gets the formatter.\n *\/\npublic function getFormatter(): FormatterInterface;\n<\/code><\/pre>\n\n<p>This is another instance of repetition.<\/p>\n\n<p>The docblocks are just repeating what the code already tells me.<\/p>\n\n<p>I can understand from the method names what each function does, and I can see what parameters they have and their types.<\/p>\n\n<p>I can see if each method returns anything and, if so, what type it returns - e.g. <code>getFormatter<\/code> returns a <code>FormatterInterface<\/code>.<\/p>\n\n<p>I think these docblocks aren't needed and in my projects, would suggest they be removed.<\/p>\n\n<p>Unless they're adding more information, such as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/21\/phpdoc\">PHPStan PHPDoc types<\/a>, there's no need to repeat what the code already says.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>When most people think of \"Don't repeat yourself\" or DRY, they probably think about not duplicating logic in code.<\/p>\n\n<p>If you've written some functionality once, you should avoid writing it again.<\/p>\n\n<p>I was recently browsing the code for an open source package and saw this:<\/p>\n\n<pre><code class=\"php\">\/**\n * Flush everything.\n *\/\npublic function flush(): void;\n\n\/**\n * Sets the formatter.\n *\/\npublic function setFormatter(FormatterInterface $formatter): void;\n\n\/**\n * Gets the formatter.\n *\/\npublic function getFormatter(): FormatterInterface;\n<\/code><\/pre>\n\n<p>This is another instance of repetition.<\/p>\n\n<p>The docblocks are just repeating what the code already tells me.<\/p>\n\n<p>I can understand from the method names what each function does, and I can see what parameters they have and their types.<\/p>\n\n<p>I can see if each method returns anything and, if so, what type it returns - e.g. <code>getFormatter<\/code> returns a <code>FormatterInterface<\/code>.<\/p>\n\n<p>I think these docblocks aren't needed and in my projects, would suggest they be removed.<\/p>\n\n<p>Unless they're adding more information, such as <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/21\/phpdoc\">PHPStan PHPDoc types<\/a>, there's no need to repeat what the code already says.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "0dcfb92613016648b8799e1ee65ada87",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "d96c36ac-6f81-4a25-a5d1-b62c846680d1"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Covering icky code with automated tests"
}
],
"created": [
{
"value": "2025-03-24T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/24\/icky",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Every codebase has \"icky\" code.<\/p>\n\n<p>Code that works but is difficult to read and understand, that most people will avoid working on.<\/p>\n\n<p>It could be fragile and occasionally return different results or error.<\/p>\n\n<p>It could be a suboptimal implementation.<\/p>\n\n<p>I mention in <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/tdd-test-driven-drupal\">my test-driven drupal talk<\/a> when I wrote some code that worked locally but didn't work because of the hosting setup and I had to rewrite the code in a different and less optimal way.<\/p>\n\n<p>How do you build confidence around this code?<\/p>\n\n<p>Write more automated tests around it.<\/p>\n\n<p>This will make it easier to understand what the code does as the tests will act like examples and, as you find situations where the code can break, you can write tests to ensure it works as expected once fixed and will continue to work.<\/p>\n\n<p>Once there are tests, the code will be easier to add to, change, refactor, read and understand.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Every codebase has \"icky\" code.<\/p>\n\n<p>Code that works but is difficult to read and understand, that most people will avoid working on.<\/p>\n\n<p>It could be fragile and occasionally return different results or error.<\/p>\n\n<p>It could be a suboptimal implementation.<\/p>\n\n<p>I mention in <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/tdd-test-driven-drupal\">my test-driven drupal talk<\/a> when I wrote some code that worked locally but didn't work because of the hosting setup and I had to rewrite the code in a different and less optimal way.<\/p>\n\n<p>How do you build confidence around this code?<\/p>\n\n<p>Write more automated tests around it.<\/p>\n\n<p>This will make it easier to understand what the code does as the tests will act like examples and, as you find situations where the code can break, you can write tests to ensure it works as expected once fixed and will continue to work.<\/p>\n\n<p>Once there are tests, the code will be easier to add to, change, refactor, read and understand.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "b0767502ae62064308bde448678dfb0b",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "dc67f83a-6499-4b35-a040-3c7f89549cc7"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Dealing with different API versions"
}
],
"created": [
{
"value": "2025-04-05T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/04\/05\/strategies",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I've recently developed a new custom module for a Drupal project.<\/p>\n\n<p>It pulls in an initial set of data from an API, builds a block that includes a form, makes another API request based on the form selections and displays the result to the user.<\/p>\n\n<p>Part of the API base URL is <code>v1<\/code> so I assume that, at some point, there could be breaking changes in the API response and the endpoint will change to be <code>v2<\/code>.<\/p>\n\n<p>If this happens, I don't to have to change the existing code to make it work with a new version of the API.<\/p>\n\n<p>I want to be able to write new code to work with the new version.<\/p>\n\n<p>For the version 1 of the API, I've written a <code>V1ApiClient<\/code> that makes and processes the response from the v1 endpoint.<\/p>\n\n<p>If we move to version v2, I'll write a <code>V2ApiClient<\/code> that will work with the version 2 response.<\/p>\n\n<p>Each implementation will have its own logic but implement a <code>ClientInterface<\/code> to ensure both have the same required methods to make them interchangeable.<\/p>\n\n<p>Then I can switch between implementations as needed and not have to change the existing code.<\/p>\n\n<p>Similar to feature toggles, this allows me to deploy new code without making it immediately active.<\/p>\n\n<p>I don't need to hold back merging changes until I'm ready to change the API version.<\/p>\n\n<p>The same approach works in other situations, like payment gateways.<\/p>\n\n<p>What if you need to switch to a different payment gateway or map service?<\/p>\n\n<p>Instead of tightly coupling to one implementation, creating different implementations and strategies makes the code more flexible and easier if you need to change implementations or support multiple implementations at once.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I've recently developed a new custom module for a Drupal project.<\/p>\n\n<p>It pulls in an initial set of data from an API, builds a block that includes a form, makes another API request based on the form selections and displays the result to the user.<\/p>\n\n<p>Part of the API base URL is <code>v1<\/code> so I assume that, at some point, there could be breaking changes in the API response and the endpoint will change to be <code>v2<\/code>.<\/p>\n\n<p>If this happens, I don't to have to change the existing code to make it work with a new version of the API.<\/p>\n\n<p>I want to be able to write new code to work with the new version.<\/p>\n\n<p>For the version 1 of the API, I've written a <code>V1ApiClient<\/code> that makes and processes the response from the v1 endpoint.<\/p>\n\n<p>If we move to version v2, I'll write a <code>V2ApiClient<\/code> that will work with the version 2 response.<\/p>\n\n<p>Each implementation will have its own logic but implement a <code>ClientInterface<\/code> to ensure both have the same required methods to make them interchangeable.<\/p>\n\n<p>Then I can switch between implementations as needed and not have to change the existing code.<\/p>\n\n<p>Similar to feature toggles, this allows me to deploy new code without making it immediately active.<\/p>\n\n<p>I don't need to hold back merging changes until I'm ready to change the API version.<\/p>\n\n<p>The same approach works in other situations, like payment gateways.<\/p>\n\n<p>What if you need to switch to a different payment gateway or map service?<\/p>\n\n<p>Instead of tightly coupling to one implementation, creating different implementations and strategies makes the code more flexible and easier if you need to change implementations or support multiple implementations at once.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "03586d61c309592caca993fa7b3f584c",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "dee73953-50c6-4afb-9c3d-09c53a5fd415"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Solve one problem at a time"
}
],
"created": [
{
"value": "2025-03-01T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/01\/one-problem",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>When using <code>git log<\/code> to look through the history of codebases, I often see large commits that combine several changes.<\/p>\n\n<p>These also lead to vague commit messages like \"Changes\", \"wip\" or \"Fixes\".<\/p>\n\n<p>These aren't helpful when reviewing the history and large commits are difficult to review and revert if there is a problem.<\/p>\n\n<p>Each commit should be focused on a single change, whether its adding part of a new feature, fixing a bug or refactoring.<\/p>\n\n<p>If it's a combination, they should be split into separate commits.<\/p>\n\n<p>Each commit should have its own well-written commit message that explains why the change was needed, any consequences or manual deployment steps, alternative approaches that were tried, issues encountered and any follow up actions.<\/p>\n\n<p>If you can't properly describe the changes made in a commit, the commit is too big.<\/p>\n\n<p>You should uncommit the changes and use <code>git add -p<\/code> to create a more focused commit.<\/p>\n\n<p>This is why <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/11\/don-t-delete-my-commit-messages\">I don't squash commits<\/a>.<\/p>\n\n<p>If people have made an effort to create good commits with good commit messages, I don't want them to be lost when the commits are merged.<\/p>\n\n<p>I want to keep the history of the changes intact and as it originally was.<\/p>\n\n<p>I do sometimes need to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/11\/tidy\">tidy up my own commits<\/a>, though, before I push them for anyone else to see.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>When using <code>git log<\/code> to look through the history of codebases, I often see large commits that combine several changes.<\/p>\n\n<p>These also lead to vague commit messages like \"Changes\", \"wip\" or \"Fixes\".<\/p>\n\n<p>These aren't helpful when reviewing the history and large commits are difficult to review and revert if there is a problem.<\/p>\n\n<p>Each commit should be focused on a single change, whether its adding part of a new feature, fixing a bug or refactoring.<\/p>\n\n<p>If it's a combination, they should be split into separate commits.<\/p>\n\n<p>Each commit should have its own well-written commit message that explains why the change was needed, any consequences or manual deployment steps, alternative approaches that were tried, issues encountered and any follow up actions.<\/p>\n\n<p>If you can't properly describe the changes made in a commit, the commit is too big.<\/p>\n\n<p>You should uncommit the changes and use <code>git add -p<\/code> to create a more focused commit.<\/p>\n\n<p>This is why <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2024\/05\/11\/don-t-delete-my-commit-messages\">I don't squash commits<\/a>.<\/p>\n\n<p>If people have made an effort to create good commits with good commit messages, I don't want them to be lost when the commits are merged.<\/p>\n\n<p>I want to keep the history of the changes intact and as it originally was.<\/p>\n\n<p>I do sometimes need to <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/02\/11\/tidy\">tidy up my own commits<\/a>, though, before I push them for anyone else to see.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "4fba08380c1f4dbf231499fd7ddea935",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "e3631745-3190-45d0-b893-7e5315faeba3"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Building Bootstrap components with Tailwind CSS"
}
],
"created": [
{
"value": "2025-03-25T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/25\/bootstrap",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>A while ago, I started to build <a href=\"https:\/\/bootstrap-with-tailwind.oliverdavies.uk\">some of Bootstrap's example components<\/a> with Tailwind CSS.<\/p>\n\n<p>Because it's a lower-level framework with less-opinionated classes, you can use Tailwind classes to make different looking UIs.<\/p>\n\n<p>There is no \"Tailwind-looking website\" as there is with component-level frameworks like Bootstrap and Bulma.<\/p>\n\n<p>You can also see this in the <a href=\"https:\/\/www.oliverdavies.uk\/blog\/uis-ive-rebuilt-tailwind-css\">other UIs I've rebuilt with Tailwind<\/a> - some of which I show in my <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">Taking Flight with Tailwind CSS talk<\/a>.<\/p>\n\n<p>I've created the Album and Pricing card examples so far, but may do more soon.<\/p>\n\n<p>I've recently ported this website to be <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">a Sculpin-powered website<\/a>, so if you want to see another Sculpin example, you can take a look <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/bootstrap-with-tailwind\">at the source code<\/a>.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>A while ago, I started to build <a href=\"https:\/\/bootstrap-with-tailwind.oliverdavies.uk\">some of Bootstrap's example components<\/a> with Tailwind CSS.<\/p>\n\n<p>Because it's a lower-level framework with less-opinionated classes, you can use Tailwind classes to make different looking UIs.<\/p>\n\n<p>There is no \"Tailwind-looking website\" as there is with component-level frameworks like Bootstrap and Bulma.<\/p>\n\n<p>You can also see this in the <a href=\"https:\/\/www.oliverdavies.uk\/blog\/uis-ive-rebuilt-tailwind-css\">other UIs I've rebuilt with Tailwind<\/a> - some of which I show in my <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">Taking Flight with Tailwind CSS talk<\/a>.<\/p>\n\n<p>I've created the Album and Pricing card examples so far, but may do more soon.<\/p>\n\n<p>I've recently ported this website to be <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">a Sculpin-powered website<\/a>, so if you want to see another Sculpin example, you can take a look <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/bootstrap-with-tailwind\">at the source code<\/a>.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "ba45cf6bd571c26a3efb9057e7bc5fd3",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "e3d951af-bba8-45f4-b249-c7e0f73e2a47"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "An example of feature flagging"
}
],
"created": [
{
"value": "2025-03-17T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/17\/feature-flags",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>I've written a number of emails about feature flags, or feature toggles, and am a strong advocate of using them whilst developing new features.<\/p>\n\n<p>I've used them a couple of times recently on my website so I wanted to share them as examples.<\/p>\n\n<h2 id=\"experimenting-with-presentation-layouts\">Experimenting with presentation layouts<\/h2>\n\n<p>Firstly, I wanted to experiment with a different layout for my presentation pages.<\/p>\n\n<p>They currently have a list of events, embedded slides and a video recording when there is one.<\/p>\n\n<p>Each event linked to its website, where applicable, and the slides and video were from one of the most recent versions of the presentation.<\/p>\n\n<p>I wanted to change this so each event would have links to its own slides, example code or demo.<\/p>\n\n<p>I didn't want to change this yet for all presentations, only <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">my Sculpin talk<\/a> as it's the most recent and, if I like it, later apply it to the others.<\/p>\n\n<p>My website is built with Sculpin, so adding a feature flag was as simple as adding <code>new: true<\/code> to the YAML front matter at the top of the file for that presentation.<\/p>\n\n<p>This is available as <code>page.new<\/code> in the layout file and I can use this <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk\/commit\/8b721e63fb64f3c98b81353ca9cec7545d72a595\">to load different markup<\/a>.<\/p>\n\n<h2 id=\"rewriting-my-css\">Rewriting my CSS<\/h2>\n\n<p>Secondly, I've been wanting to re-style my website with Tailwind CSS 4 and refactor some of the templating.<\/p>\n\n<p>As this is a change I wanted to be site-wide, I added <code>new_css: true<\/code> to my sculpin_site.yml file.<\/p>\n\n<p>This time, I was able to use <code>site.new_css<\/code> to <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk\/commit\/fa884644cf5aa233ad22fb28b83c5a0b150b037d\">toggle the loaded stylesheet<\/a> and using Sculpin's environment files - e.g. sculpin_site_dev.yml and sculpin_site_prod.yml - I can be explicit about which stylesheets are used locally and for my live website.<\/p>\n\n<h2 id=\"summary\">Summary<\/h2>\n\n<p>Feature flags are a great approach to splitting up large changes into manageable, deployable pieces, and they don't need to be complicated.<\/p>\n\n<p>Essentially, they are a simple boolean value that you can use to execute different code based on whether it's false or true.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>I've written a number of emails about feature flags, or feature toggles, and am a strong advocate of using them whilst developing new features.<\/p>\n\n<p>I've used them a couple of times recently on my website so I wanted to share them as examples.<\/p>\n\n<h2 id=\"experimenting-with-presentation-layouts\">Experimenting with presentation layouts<\/h2>\n\n<p>Firstly, I wanted to experiment with a different layout for my presentation pages.<\/p>\n\n<p>They currently have a list of events, embedded slides and a video recording when there is one.<\/p>\n\n<p>Each event linked to its website, where applicable, and the slides and video were from one of the most recent versions of the presentation.<\/p>\n\n<p>I wanted to change this so each event would have links to its own slides, example code or demo.<\/p>\n\n<p>I didn't want to change this yet for all presentations, only <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">my Sculpin talk<\/a> as it's the most recent and, if I like it, later apply it to the others.<\/p>\n\n<p>My website is built with Sculpin, so adding a feature flag was as simple as adding <code>new: true<\/code> to the YAML front matter at the top of the file for that presentation.<\/p>\n\n<p>This is available as <code>page.new<\/code> in the layout file and I can use this <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk\/commit\/8b721e63fb64f3c98b81353ca9cec7545d72a595\">to load different markup<\/a>.<\/p>\n\n<h2 id=\"rewriting-my-css\">Rewriting my CSS<\/h2>\n\n<p>Secondly, I've been wanting to re-style my website with Tailwind CSS 4 and refactor some of the templating.<\/p>\n\n<p>As this is a change I wanted to be site-wide, I added <code>new_css: true<\/code> to my sculpin_site.yml file.<\/p>\n\n<p>This time, I was able to use <code>site.new_css<\/code> to <a href=\"https:\/\/code.oliverdavies.uk\/opdavies\/oliverdavies.uk\/commit\/fa884644cf5aa233ad22fb28b83c5a0b150b037d\">toggle the loaded stylesheet<\/a> and using Sculpin's environment files - e.g. sculpin_site_dev.yml and sculpin_site_prod.yml - I can be explicit about which stylesheets are used locally and for my live website.<\/p>\n\n<h2 id=\"summary\">Summary<\/h2>\n\n<p>Feature flags are a great approach to splitting up large changes into manageable, deployable pieces, and they don't need to be complicated.<\/p>\n\n<p>Essentially, they are a simple boolean value that you can use to execute different code based on whether it's false or true.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "55f785f8b5b1bcd8bf8f2c8c09ee1e1e",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "ecf2d8ff-f99c-4faf-899d-8e80e1b74287"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Why use Collections?"
}
],
"created": [
{
"value": "2025-03-30T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:31+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/30\/why-collections",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Yesterday, I wrote how to create <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/29\/collections\">dependency free Collection classes in PHP<\/a> (thanks to Dan Leech).<\/p>\n\n<p>I said that <a href=\"https:\/\/www.oliverdavies.uk\/blog\/using-laravel-collections-drupal\">I've written blog posts<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/using-illuminate-collections-outside-laravel\">given talks<\/a> on using Collection classes.<\/p>\n\n<p>But why do I like Collections and why may you want to use them instead of native arrays?<\/p>\n\n<p>The first reason is that I can add extra functionality to Collections, because they're objects.<\/p>\n\n<p>Whether it's a generic action such as filtering or sorting the items, or something more specific like returning a list of station codes from a collection of train stations, this can be added to specific collection classes.<\/p>\n\n<p>I'll usually have an <code>AbstractCollection<\/code> that has the generic methods and is extended by specific Collection types with methods more specific methods.<\/p>\n\n<p>Having specific types of Collection objects also gives my code more context.<\/p>\n\n<p>Instead of an array that could contain anything, by reading the code and seeing which Collection types are used, I know what the collection contains and what I can do with it.<\/p>\n\n<p>This is also why I like value objects.<\/p>\n\n<p>Giving objects specific names instead of relying on the language's primitive types makes the code more robust and easier to read and understand.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Yesterday, I wrote how to create <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/29\/collections\">dependency free Collection classes in PHP<\/a> (thanks to Dan Leech).<\/p>\n\n<p>I said that <a href=\"https:\/\/www.oliverdavies.uk\/blog\/using-laravel-collections-drupal\">I've written blog posts<\/a> and <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/using-illuminate-collections-outside-laravel\">given talks<\/a> on using Collection classes.<\/p>\n\n<p>But why do I like Collections and why may you want to use them instead of native arrays?<\/p>\n\n<p>The first reason is that I can add extra functionality to Collections, because they're objects.<\/p>\n\n<p>Whether it's a generic action such as filtering or sorting the items, or something more specific like returning a list of station codes from a collection of train stations, this can be added to specific collection classes.<\/p>\n\n<p>I'll usually have an <code>AbstractCollection<\/code> that has the generic methods and is extended by specific Collection types with methods more specific methods.<\/p>\n\n<p>Having specific types of Collection objects also gives my code more context.<\/p>\n\n<p>Instead of an array that could contain anything, by reading the code and seeing which Collection types are used, I know what the collection contains and what I can do with it.<\/p>\n\n<p>This is also why I like value objects.<\/p>\n\n<p>Giving objects specific names instead of relying on the language's primitive types makes the code more robust and easier to read and understand.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:31+00:00",
"guid": null,
"hash": "9ed6c1d39227210765b7041449aae5f5",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "ed274b9c-e558-4ccb-a0e3-845fd4252c85"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Archiving Drupal websites as static websites"
}
],
"created": [
{
"value": "2025-03-18T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/18\/archiving",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Static websites can be created by writing each file by hand or using a tool like <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">a static site generator<\/a>.<\/p>\n\n<p>But what if you've already got a dynamic website that you no longer need to be editable?<\/p>\n\n<p>What if it was for an event that has passed, like a DrupalCamp?<\/p>\n\n<p>If you no longer need to update the content via the admin UI, you could archive it by converting it to a static website.<\/p>\n\n<p>Then you no longer need to maintain and update it, and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simplify your hosting environment<\/a>.<\/p>\n\n<p>You could use <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/15\/tome\">Tome, a static site generator for Drupal<\/a>, or use command line tools like <code>wget<\/code> with options like <code>--mirror<\/code> to create a static version by crawling a live website.<\/p>\n\n<p>There are <a href=\"https:\/\/www.drupal.org\/docs\/administering-a-drupal-site\/creating-a-static-archive-of-a-drupal-site\">a few options on Drupal.org<\/a>, which will also work with other CMSes and frameworks.<\/p>\n\n<p>This is what I've done for old websites like our old DrupalCamp Bristol websites.<\/p>\n\n<p>That means they're still available for people to see, but without the maintenance and security overhead.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Static websites can be created by writing each file by hand or using a tool like <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">a static site generator<\/a>.<\/p>\n\n<p>But what if you've already got a dynamic website that you no longer need to be editable?<\/p>\n\n<p>What if it was for an event that has passed, like a DrupalCamp?<\/p>\n\n<p>If you no longer need to update the content via the admin UI, you could archive it by converting it to a static website.<\/p>\n\n<p>Then you no longer need to maintain and update it, and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">simplify your hosting environment<\/a>.<\/p>\n\n<p>You could use <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/15\/tome\">Tome, a static site generator for Drupal<\/a>, or use command line tools like <code>wget<\/code> with options like <code>--mirror<\/code> to create a static version by crawling a live website.<\/p>\n\n<p>There are <a href=\"https:\/\/www.drupal.org\/docs\/administering-a-drupal-site\/creating-a-static-archive-of-a-drupal-site\">a few options on Drupal.org<\/a>, which will also work with other CMSes and frameworks.<\/p>\n\n<p>This is what I've done for old websites like our old DrupalCamp Bristol websites.<\/p>\n\n<p>That means they're still available for people to see, but without the maintenance and security overhead.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "a776e5053b9f9b1276b131cb30130d86",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "efea11de-28be-4cb0-9bad-9b04322c2126"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Static websites are easy to build"
}
],
"created": [
{
"value": "2025-03-12T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/12\/easy",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Static websites are the easiest way to build websites.<\/p>\n\n<p>You create an index.html file, type some words, open the file in a browser and you'll see the words you entered.<\/p>\n\n<p>You built a website!<\/p>\n\n<p>Then you can create any more pages you need and style it with CSS.<\/p>\n\n<p>This how I built my first website, for a Tae Kwon-Do school I used to train at.<\/p>\n\n<p>This worked great, but at some point, becomes hard to scale.<\/p>\n\n<p>What if you want to add a new link to your navigation menu? You'd need to update each HTML page separately.<\/p>\n\n<p>At this point, I started to learn about PHP and MySQL, and then Drupal.<\/p>\n\n<p>Static site generators like Sculpin, Jekyll and Hugo also fix this problem.<\/p>\n\n<p>They allow you to write HTML files with a template language like Twig and use includes, loops and conditions to make your files easier to create and maintain with a language and tools you're familiar with.<\/p>\n\n<p>It still generates a static website with HTML files, but in a more maintainable way.<\/p>\n\n<p>As a PHP Developer, I like Sculpin but also like Tome to export a Drupal website to static HTML.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Static websites are the easiest way to build websites.<\/p>\n\n<p>You create an index.html file, type some words, open the file in a browser and you'll see the words you entered.<\/p>\n\n<p>You built a website!<\/p>\n\n<p>Then you can create any more pages you need and style it with CSS.<\/p>\n\n<p>This how I built my first website, for a Tae Kwon-Do school I used to train at.<\/p>\n\n<p>This worked great, but at some point, becomes hard to scale.<\/p>\n\n<p>What if you want to add a new link to your navigation menu? You'd need to update each HTML page separately.<\/p>\n\n<p>At this point, I started to learn about PHP and MySQL, and then Drupal.<\/p>\n\n<p>Static site generators like Sculpin, Jekyll and Hugo also fix this problem.<\/p>\n\n<p>They allow you to write HTML files with a template language like Twig and use includes, loops and conditions to make your files easier to create and maintain with a language and tools you're familiar with.<\/p>\n\n<p>It still generates a static website with HTML files, but in a more maintainable way.<\/p>\n\n<p>As a PHP Developer, I like Sculpin but also like Tome to export a Drupal website to static HTML.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "898a4ca4ca6f3d15e2c342efb0420bee",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "f21de9a2-c741-4a12-9109-c7b5a452dccb"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Static websites are easy to host and deploy"
}
],
"created": [
{
"value": "2025-03-13T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/13\/deploy",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Another reason I like static websites is that they're easy and quick to deploy.<\/p>\n\n<p>Whether you use write each HTML file by hand or <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">use a static site generator<\/a>, a simple Web server like Caddy, Nginx or Apache can load and serve your website for everyone to see.<\/p>\n\n<p>My Sculpin website generates an output_prod directory after I run <code>sculpin generate<\/code> with my deployable files.<\/p>\n\n<p>I manage my own server with NixOS that hosts a number of static websites, such as examples from talks and blog posts.<\/p>\n\n<p>To upload my files onto the server, I just use rsync - a small command line tool to synchronise files between computers.<\/p>\n\n<p>It's a single command to upload the contents of my output_prod directory to the directory on my server.<\/p>\n\n<p>No complex CI pipelines or database migrations.<\/p>\n\n<p>It's fast, simple and minimal.<\/p>\n\n<p>If you prefer to use a service like Netlify or Vercel, they work great for static websites too.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Another reason I like static websites is that they're easy and quick to deploy.<\/p>\n\n<p>Whether you use write each HTML file by hand or <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">use a static site generator<\/a>, a simple Web server like Caddy, Nginx or Apache can load and serve your website for everyone to see.<\/p>\n\n<p>My Sculpin website generates an output_prod directory after I run <code>sculpin generate<\/code> with my deployable files.<\/p>\n\n<p>I manage my own server with NixOS that hosts a number of static websites, such as examples from talks and blog posts.<\/p>\n\n<p>To upload my files onto the server, I just use rsync - a small command line tool to synchronise files between computers.<\/p>\n\n<p>It's a single command to upload the contents of my output_prod directory to the directory on my server.<\/p>\n\n<p>No complex CI pipelines or database migrations.<\/p>\n\n<p>It's fast, simple and minimal.<\/p>\n\n<p>If you prefer to use a service like Netlify or Vercel, they work great for static websites too.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "2241d99c8a6cb9f5e690ba3e1a835383",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,100 @@
{
"uuid": [
{
"value": "ffcfe425-1787-4842-bbbf-0362a655a830"
}
],
"langcode": [
{
"value": "en"
}
],
"type": [
{
"target_id": "daily_email",
"target_type": "node_type",
"target_uuid": "8bde1f2f-eef9-4f2d-ae9c-96921f8193d7"
}
],
"revision_timestamp": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"revision_uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"revision_log": [],
"status": [
{
"value": true
}
],
"uid": [
{
"target_type": "user",
"target_uuid": "b8966985-d4b2-42a7-a319-2e94ccfbb849"
}
],
"title": [
{
"value": "Building static websites with Drupal"
}
],
"created": [
{
"value": "2025-03-15T00:00:00+00:00"
}
],
"changed": [
{
"value": "2025-05-01T23:43:32+00:00"
}
],
"promote": [
{
"value": false
}
],
"sticky": [
{
"value": false
}
],
"default_langcode": [
{
"value": true
}
],
"revision_translation_affected": [
{
"value": true
}
],
"path": [
{
"alias": "\/daily\/2025\/03\/15\/tome",
"langcode": "en"
}
],
"body": [
{
"value": "\n <p>Over the last few days, I've written a few emails about static websites and static site generators before I <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">speak about Sculpin<\/a> at PHP Thames Valley.<\/p>\n\n<p>They are <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and deploy and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">cheap to host<\/a>.<\/p>\n\n<p>As a PHP Developer, I use Sculpin as it uses Symfony components, is extendable by writing my own PHP code and uses Twig for templating.<\/p>\n\n<p>As a Drupal Developer, I like the power of Drupal's out of the box functionality and additional modules like Pathauto and Redirect, but I'd like to deploy it as a static website for it to be faster and more secure.<\/p>\n\n<p>Enter Tome, <a href=\"https:\/\/www.drupal.org\/project\/tome\">a Drupal module to create static websites<\/a>.<\/p>\n\n<p>You build the website the same as you would locally and export the content and files to a static website - the same you would generate with a static site generator like Sculpin or writing the files by hand.<\/p>\n\n<p>Sam Mortenson (the creator of Tome) and I discussed it on <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/19-sam-mortenson\">episode 19 of the Beyond Blocks podcast<\/a>.<\/p>\n\n<p>So, if you like Drupal and the benefits of static sites, Tome may be a good solution for you.<\/p>\n\n ",
"format": "full_html",
"processed": "\n <p>Over the last few days, I've written a few emails about static websites and static site generators before I <a href=\"https:\/\/www.oliverdavies.uk\/presentations\/sculpin\">speak about Sculpin<\/a> at PHP Thames Valley.<\/p>\n\n<p>They are <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/12\/easy\">easy to build<\/a> and deploy and <a href=\"https:\/\/www.oliverdavies.uk\/daily\/2025\/03\/13\/deploy\">cheap to host<\/a>.<\/p>\n\n<p>As a PHP Developer, I use Sculpin as it uses Symfony components, is extendable by writing my own PHP code and uses Twig for templating.<\/p>\n\n<p>As a Drupal Developer, I like the power of Drupal's out of the box functionality and additional modules like Pathauto and Redirect, but I'd like to deploy it as a static website for it to be faster and more secure.<\/p>\n\n<p>Enter Tome, <a href=\"https:\/\/www.drupal.org\/project\/tome\">a Drupal module to create static websites<\/a>.<\/p>\n\n<p>You build the website the same as you would locally and export the content and files to a static website - the same you would generate with a static site generator like Sculpin or writing the files by hand.<\/p>\n\n<p>Sam Mortenson (the creator of Tome) and I discussed it on <a href=\"https:\/\/www.oliverdavies.uk\/podcast\/19-sam-mortenson\">episode 19 of the Beyond Blocks podcast<\/a>.<\/p>\n\n<p>So, if you like Drupal and the benefits of static sites, Tome may be a good solution for you.<\/p>\n\n ",
"summary": null
}
],
"feeds_item": [
{
"imported": "2025-05-01T23:43:32+00:00",
"guid": null,
"hash": "c2ab309fc48ad7634f3e37920943b3e3",
"target_type": "feeds_feed",
"target_uuid": "90c85284-7ca8-4074-9178-97ff8384fe76"
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "04fe45c5-c54e-4110-9d33-b9681b32f3b5"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/2a8452d0-735c-41de-88ef-a9cc81f4035f"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/06\/caching"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "0bc43805-583f-45cc-9604-2cb7a000af0b"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/43617141-40c9-4462-b130-60ee7697ab9d"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/20\/config"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "0d5d3954-a5ea-4230-9508-c69a31e739d6"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/f21de9a2-c741-4a12-9109-c7b5a452dccb"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/13\/deploy"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "1274b1fe-43b6-4277-bb7e-cfc25e8786ce"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/ecf2d8ff-f99c-4faf-899d-8e80e1b74287"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/30\/why-collections"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "1ce9a885-2b61-4222-b340-08b54135eb2f"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/e3d951af-bba8-45f4-b249-c7e0f73e2a47"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/17\/feature-flags"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "1e2d3a71-a014-477a-864c-d9c816b1c7a6"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/af5c2fcc-7f6c-4363-943c-f23dcdab51ca"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/14\/backup"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "1e93c591-9fdc-415d-9ddd-ffe9ce2005f9"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/00204e36-4f2d-4b4a-bcf6-c0140209e835"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/04\/good"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "22643036-3745-42ff-836e-86c2caafac72"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/09407211-6afb-4f28-95b7-5a7c3f9235b8"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/03\/cost"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "2dff4b65-61ed-46e9-94c9-71d479303461"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/6d520198-e187-44dd-b434-034f0f74d5c2"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/16\/fonts"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "3515d35d-aae2-46e9-9f2d-19482dd90011"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/efea11de-28be-4cb0-9bad-9b04322c2126"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/12\/easy"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "3aad1a8d-456a-4f50-9dbd-c1073580e74e"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/231b6fcb-38a4-4461-b0d4-63ab4c31806c"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/31\/nixpkgs"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "4071b156-9da4-4f65-a6ab-e390d9e955a0"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/9e166002-410b-4261-aac8-e47dbe1c04d8"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/11\/friday"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "450e40e9-6a07-4c26-a4fb-80f415e8af57"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/c6ef991b-2262-4942-b1f9-34c8a3afced4"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/07\/rebase-and-reorder"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "45376d45-20cf-41ca-84ab-8b50e6828666"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/0710efa0-c7f4-4254-afab-7c7a4f5b6959"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/07\/nix-rst2pdf"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "4b5dab9d-c0b7-47df-b2de-8f46fc45cb9c"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/737d4134-6074-4498-b8d3-f51d7b71d006"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/16\/what-s-the-correct-way-to-add-phpstan-to-an-existing-codebase"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "58d1c76c-54a4-4a93-8637-766e5bb2e598"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/4403292d-2b01-4681-afb2-0ab2113694a6"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/19\/effective"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "59cdaf2a-3e6c-4ca9-9816-83a5c59693ae"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/dee73953-50c6-4afb-9c3d-09c53a5fd415"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/01\/one-problem"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "66bb38d3-d7b4-43e4-875f-25afb877d1ce"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/98e87c30-8dd5-4d80-aed3-53439673de97"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/21\/phpdoc"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "6898a61e-17b6-4103-9293-840d7a012850"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/0dc3cee6-40bf-4f95-8d25-4c32e95e7462"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/18\/static-drupal"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "749b2e45-4c6f-4413-99a8-8bdb7295a36b"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/ed274b9c-e558-4ccb-a0e3-845fd4252c85"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/18\/archiving"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "7ffe966e-9311-4627-a058-e120afdfe341"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/8c006484-b85e-4eb7-a2c2-f0094a331712"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/05\/slow"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "82fb019d-185d-4b8f-a3ad-ed260b5c60e2"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/b09ccd35-261d-4fa5-99cc-52f781c52e87"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/03\/selective"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "8cdf0c15-fa61-4f6c-beba-6e2750a43add"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/d96c36ac-6f81-4a25-a5d1-b62c846680d1"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/24\/icky"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "903ec0e9-3c6a-4279-b40f-620441e7d816"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/3d15dfe9-c530-40d6-8923-f5d7e7b3018b"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/08\/variables"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "a0821366-eddb-4182-9987-d6d0caf0559a"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/5cc406b1-f4ca-49c0-a296-d352086fc7f4"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/15\/nix-nvm"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "a635bfb6-ac56-4a3e-87bb-e7487942d5a7"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/641d773f-97cb-4da8-93e9-69df3c89261c"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/01\/debugging"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "a703bebc-ef85-4b26-9aca-8923ec103114"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/3fa16a10-1230-498f-ad80-4cab3ae96a28"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/22\/legacy"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "a7edd40d-18da-4a9b-ba0a-a8190cc6d104"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/72729ed3-73ab-4e13-9115-fbf6d8ca6b08"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/17\/incrementally"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "aa4704f0-c8d1-4554-a133-fe2fca90c142"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/d130088a-0c2c-4dfb-b65d-3b2ee8d6239c"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/19\/self-hosting"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "ac2378ff-1de4-442b-9018-54c9c8322961"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/03653911-19e8-4f60-8baa-5165b8380d29"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/29\/collections"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "b0482047-f38c-41f4-9020-e5447d48ee1d"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/197ba205-f2c2-4c14-8d71-ac01bf602861"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/28\/continuous"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "b7ca03cd-c00c-404e-8efb-9c043c6c896d"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/e3631745-3190-45d0-b893-7e5315faeba3"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/25\/bootstrap"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "be97582e-4563-4f43-a6f2-c0a61b3832ce"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/ffcfe425-1787-4842-bbbf-0362a655a830"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/15\/tome"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "c0415faf-ca06-4f19-a31a-c7c1b3cf1e3f"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/6e6b15bc-6319-4392-ac07-7c38fb66e6a4"
}
],
"alias": [
{
"value": "\/daily\/2025\/05\/01\/hack"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "cde23a5c-26c9-4cb1-9dc6-3de19ec7856f"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/3cbded61-45d4-46f7-95a0-a0ebb3da9847"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/02\/commit"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "d395f86e-2911-4b1d-a49c-ca3dd1c32083"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/6900edff-5340-44ce-b328-9f3d24ab673b"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/21\/patch"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "d69bb36d-01d6-4dde-8b03-c01808447a14"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/d51a4843-892e-4d52-81ca-66c2af097478"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/10\/contrib-first"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "dbd11573-f435-42c5-9fb2-eeff934fa7e1"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/c4ee2105-bde9-49ad-89e2-2c06c215bdbb"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/08\/chaining"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "dc5689bd-8f0d-4c07-9a50-0bbc35a52c8f"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/dc67f83a-6499-4b35-a040-3c7f89549cc7"
}
],
"alias": [
{
"value": "\/daily\/2025\/04\/05\/strategies"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "e2818d79-08ab-43de-a46a-611692fb21ad"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/d52128e8-0927-4232-8c77-e9c0d60578ca"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/26\/repeat"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "fcfbfce9-4005-4385-9904-527459c0193b"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/cac7e909-60cc-48f1-9535-b1b3ea3347ad"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/27\/for-hire"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -0,0 +1,27 @@
{
"uuid": [
{
"value": "fe4be376-f835-439b-9d6a-e281d8c8e6bc"
}
],
"langcode": [
{
"value": "en"
}
],
"path": [
{
"value": "\/node\/bed02c02-476f-4f2f-8d21-0c8c45edb128"
}
],
"alias": [
{
"value": "\/daily\/2025\/03\/09\/submit-your-session-proposal-for-drupalcon-europe"
}
],
"status": [
{
"value": true
}
]
}

View file

@ -52,12 +52,12 @@
],
"access": [
{
"value": "2025-04-29T17:51:43+00:00"
"value": "2025-05-02T07:37:10+00:00"
}
],
"login": [
{
"value": "2025-04-29T13:07:32+00:00"
"value": "2025-05-01T23:24:47+00:00"
}
],
"init": [