Forbuden frukt

Kort historie om barn og frukt – © Lars Fosdal

I blokka der jeg bodde som åtte-åring, var hele gårdsrommet asfaltert.  Det gikk en betongtunnel fra gaten og inn i gården. Midt i tunnelen var døren inn til vaktmesterboligen, hvor vaktmester Olsen bodde.  Han gikk i grå lagerfrakk, og sørget for at ikke et eneste grønt strå trengte frem gjennom asfalten inne i gården. Det var bare i de regulerte blomsterkassene man kunne finne spor av liv.  Gårdsrommet og sandkassen ble som en ekstra privat liten ørken inne i betonggården.  

Nabohuset var et stort, gult murhus med hage og et hvitt stakittgjerde rundt.  I hagen sto det to epletrær.  Høsten 1972 var et godt år for epler.  Det innerste treet var et vinterepletre, med dyprøde fargekuler som hang inn over den halvannen meter høye muren opp mot nabohagen. Det var allikevel ikke det treet som fenget vår interesse der jeg og mine fire jevnaldrende skolekamerater fra samme oppgang, lekte med biler i sandkassen.  

Over det hvite stakittgjerdet, ut mot gaten, hang grenene fra det store glassepletreet med sine store saftige epler som ropte “Plukk oss!”  Alle barn er lydhøre for den slags oppfordringer!  

Det var bare det at naboen, en gammel mann, voktet treets skatter nøye mot nabolagets barn. Han la nok merke til våre lange blikk mot røde fristelser, der vi gikk forbi på vei til skolen. Han så ganske så streng ut, der han satt i kjøkkenvinduet og drakk kaffen sin, mens han leste i avisen.  Med jevne mellomrom skottet han ut vinduet mot epletreet ved gjerdet.  

De røde og gyldengule fruktene på treet var utilgjengelig på dagtid, så en høstkveld i tusmørket,  snek vi oss langs stakittgjerdet med en plan. Når den gamle mannen ikke satt ved kjøkkenbordet, skulle vi klatre opp på gjerdet og strekke oss etter eplene.  Timing var viktig.  Hva om han skulle dukke opp i vinduet mens vi sanket våre etterlengtede fruktskatter?  

Vi ventet og ventet.  Ble han aldri nødt til å gå på do eller noe?  Da kom øyeblikket.  Den gamle mannen forsvant fra vinduet.  Vi løp til gjerdet og klatret så høyt vi klarte. Vi strakk oss etter epler med den ene hånden, mens vi klamret oss til gjerdet med den andre. Det knaket i epletregrener. Det var en grensesprengende opplevelse å legge hendene rundt ulovlige epler! Vi tok bare et par – tre epler hver, så han ikke skulle merke det.  Med epler i hender og jakkelommer, løp vi tilbake rundt hjørnet og inn i betongtunnelen. Vi satte oss ned med ryggen mot husveggen og satte tennene i fangsten. Hvilken smak! Hvilken glede!  Det blanke fruktkjøttet var saftig og søtt. Fem nybakte syndere smattet høylydt, mens de kniste og lo.  Kuppet vårt hadde lyktes!  Verden var vår!

Det var da vaktmesteren kom ut gjennom den grønne døren midt i tunnelen.  “Hva driver dere med da, gutter?”  Fem gutteslamper satt som lammet, med glinsende kinn og klissete fingre.  “Har dere vært på epleslang?”  Det var ingen vits i å nekte. Vi var tatt med halvspist bevis i hånden.  “Bli med meg. Vi skal hilse på en kar jeg kjenner som heter Gjørvad.  Han er gammel politimann.”  

Det var et nokså blekt og fortutlet fantefølge som toget bak vaktmesteren ut av tunnelen, langs blokken, rundt hjørnet og opp fortauet forbi stakittgjerdet.  En brukket gren lå på fortauet.  Vaktmesteren stoppet, plukket den opp, og snudde seg og så på oss.  Han sa ingenting, men øynene hans var alvorlige.  Noen i flokken snufset.

På trappen til det gule huset ble vi stilt på geledd, før vaktmesteren ringte på døren.  Døren gikk opp, og ut kom den gamle mannen fra vinduet.  Politimannen.  Og vi… vi var epletyver.  Jeg kan ikke snakke for de andre, men jeg så for meg stripete drakt og fotlenke med tung kule.  Eplene lå som et ulmende bål i magen.

Vaktmester Olsen snakket med mannen i døren.  Vi kunne ikke høre hva de snakket om, men de kikket vår vei et par ganger.  Vaktmesteren viste fram den brukne grenen, og politimannen så på hver enkelt av oss.  Øynene hans traff som spydspisser i samvittigheten.  De to mennene trykket hverandre i hånden, og den gamle mannen lukket døren bak seg.  

Vaktmesteren stilte seg midt i trappen over oss og sa med bestemt røst: “I morgen, etter skolen, kommer hver eneste en av dere hit til trappen klokken tre!”  Han fulgte oss hjem til blokken, og sendte oss inn oppgangen med formaning om å gå rett til hjem og tenke gjennom hva vi hadde gjort. Han minnet oss på at han visste hva vi hette og hvor vi bodde.  Jeg sov dårlig den natten.

Neste dag turde vi nesten ikke se på hverandre på skolen.  Vi fryktet hva som ville skje når vi skulle stå til rette for våre ugjerninger.  Vi visste vi ikke hadde annet å gjøre enn å følge oppfordringen til vaktmesteren.  Presis klokken tre stillte vi på trappen til det gule huset.  Den gamle mannen kom ut av døren.  Politikonstabel Gjørvad var kledd i uniform.  Det kom ikke en eneste lyd fra noen av oss.  Han gikk rolig ned trappen og åpnet grinda mot hagen.  Et kort kast på hodet anmodet oss om å komme inn.  Det lille følget av guttunger gikk inn grinda med bøyd nakke.

Rundt glassepletreet sto det to gardintrapper, og inntil treet sto en stige. Under treet lå det noen tomme bæreposer. “Jeg har en passende oppgave til dere, gutter!” – Stemmen til Gjørvad var dyp og kraftig. “Du – du tar stigen.” – Han pekte på meg.  Jeg klatret opp og ventet halvt å se en renneløkke dingle fra den tykkeste greina.  De grå øynene til Politikonstabel Gjørvad holdt blikket mitt fanget.  “Vær nå forsiktig, så du ikke faller ned.  Plukk det du får tak i og gi det til gutten nedenfor, som legger det i bæreposen.  Hvert eneste eple skal ned.  Alle har poser?  Bra!  Vær varsom med treet, slik at ingen flere grener blir skadet!”  Noen snufset igjen.  

Vi arbeidet i taushet.  Jeg klatret og plukket, og sendte eplene ned til gutten ved foten av treet.  De andre byttet på å bruke gardintrappene og plukket rett i hver sin pose.  I løpet av en halvtimes tid, hadde vi snauplukket treet. Det sto elleve bæreposer med epler sto under treet.

Gjørvad hadde båret inn en kasse med epler han selv hadde plukket.  

Vi fikk beskjed om å ta to poser hver, og Gjørvad tok den siste.  Det var, på mer enn en måte, en tung tur tilbake til oppgangen i blokken.  Vi måtte ta pauser underveis, siden epleposene var ganske tunge for små never.  Enda tyngre var det at Gjørvad fulgte oss i uniform.  Gjørvad ringte på hos Vaktmester Olsen, og rakte ham en eplepose.  Olsen satte posen på gulvet i gangen og kom ut.   

Tyngst var det at både Olsen og Gjørvad fulgte oss til trappeoppgangen.  Gjørvad snudde seg og sa “Olsen står her og holder dere med selskap, mens jeg følger hver og enkelt av dere hjem og snakker med foreldrene deres”. Det var en hard dom.  

Han tok med seg en og en. Etasje for etasje.  Siden jeg bodde øverst, ble jeg stående igjen alene.  Olsen kikket på meg og sa “Jeg håper dette har vært en lærerik opplevelse?”  Jeg nikket.  Klumpen i halsen var stor og vanskelig å svelge.  Det var en meget epletung og skyldtung åtte-åring som fulgte Politikonstabel Gjørvad opp til tredje etasje.  Han ringte på døren. 

Mor åpnet, og så først litt forskrekket på politimannen, og deretter enda mer forskrekket på meg.   Hun åpnet munnen og skulle til å si noe, da Gjørvad sa: “Jeg ville bare takke for hjelpen, frue.  Sønnen deres har vært svært flink og hjulpet meg å plukke frukten av glassepletreet i hagen min!  Jeg visste ikke min arme råd for hvordan jeg skulle få av alle eplene, men sønnen deres og kameratene hans har vært til uvurderlig hjelp.  Som takk har jeg sendt med to store poser med epler, slik at de kommer til glede og nytte!  Det er en kjekk kar dette, og det er bra de har oppdratt slik en nevenyttig og hjelpsom ung mann!”  

Jeg vet ikke hvem av oss som gapte høyest, mor eller jeg.  Gjørvad takket nok en gang for hjelpen, og snudde seg og blunket til meg.  “På gjensyn, unge mann, og takk for hjelpen.”  Han skysset meg inn i mors favn med noen ord om at nå måtte han på jobb, og forsvant ned trappen med faste, tunge steg.  Mor ga meg en stor klem, men sant og si husker jeg ikke et ord av hva hun sa.  Jeg husker egentlig bare at glasseplene jeg måtte spise i ukene som kom, ikke smakte like godt som de aller første.

Jeg snakket en del med Gjørvad i årene som fulgte, og hver høst frem til jeg flyttet, var jeg og mine kamerater med og plukket med oss epler hjem.  Den lure gamle mannen hadde bidratt med sitt til at fem eplekjekke unge guttesinn sto imot å slå inn på en eventuell kriminell løpebane.  Glasseplene smakte egentlig helt fortreffelig også når de ikke måtte svelges med skyldfølelse.

Nabohuset står der fortsatt like gult, men stakittgjerdet og glassepletreet er borte, og i blokken er det ikke lenger noen vaktmester.  Men gårdsrommet, som en gang var en ørken, har nå plen – og bak sandkassen står det et morelltre. Et lite skilt festet til støttemuren sier: “Olsens Forbudne Frukter”.  Jeg er sikker på at “forbudte” moreller smaker like vidunderlig som forbudte glassepler.


[Red.Merk: Denne historien har rot i virkeligheten og blokka er Bjørnsons gate 3 på Gjøvik, men ikke alt i dette skriveriet er helt sannferdig ;)]

Post a message to Teams from Delphi using WebHooks

Want to send a message to a Teams channel from your Delphi Application?
It is easy – simply post the message to your Teams channel’s WebHook URL.

Update – added Uwe Raabe’s TRESTClient version.

Well, perhaps not these hooks.
Photo by cottonbro on Pexels.com

Here is the low down basics of how to do it from Delphi. You simply do a http post with a preformatted Json string.

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling

interface
uses
  System.Classes, System.SysUtils, System.Json, Rest.Json,
  IDGlobal, IdHTTP, IdIOHandler, IdSSL, IdSSLOpenSSL;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FHTTP: TIdHTTP;
    FURL: string;
  protected
    property HTTP: TIdHTTP read FHTTP;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;

    procedure Post(const aJson: string);
    procedure PostMessage(const aMsg: TWebhookMessage);

    property URL:String read FURL write FURL;
  end;


implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  FURL := aURL;
  // Remember that you need libeay32.dll and ssleay32.dll in the path for https to work
  FHTTP := TIdHTTP.Create(nil);
  HTTP.Request.ContentType := 'application/json';
  HTTP.Request.ContentEncoding := 'utf-8';
  HTTP.Request.CacheControl := 'no-cache';
  HTTP.HTTPOptions := HTTP.HTTPOptions - [hoForceEncodeParams] + [hoNoProtocolErrorException, hoWantProtocolErrorContent];
end;

destructor TWebHook.Destroy;
begin
  FHTTP.Free;
  inherited;
end;

procedure TWebHook.Post(const aJson: string);
var
  ReqString: TStringList;
  ResStream: TStringStream;
begin
  ReqString := TStringList.Create;
  try
    try
      ReqString.Text := aJson;
      ResStream := TStringStream.Create;
      try
//        DebugOut('HTTP Post ' + ReqString.Text);
        HTTP.Post(URL, ReqString, ResStream, IndyTextEncoding_UTF8);
//        DebugOut('Response: ' + ResStream.DataString);
      finally
        ResStream.Free;
      end;
    except // HTTP Exception
      on E: Exception
      do begin
//        DebugOutException(E, 'HTTP Post');
      end;
    end;
  finally
    ReqString.Free;
  end;
end;

procedure TWebHook.PostMessage(const aMsg: TWebhookMessage);
begin
  try
    Post(
      TJson.ObjectToJsonString(
        aMsg,
        [joIgnoreEmptyStrings, joIgnoreEmptyArrays, joDateIsUTC, joDateFormatISO8601]
      )
    );
  finally
    aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  Text := aText;
end;

end.

How to use it. This is a very minimal example for a single text line.

procedure TestExampleToATeamsChannel;
var
  Teams: TWebhook;
begin
  Teams := TWebhook.Create('https:// ... YourLongWebHookURLHere ... ');
  try
    Teams.PostMessage(TSimpleText.Create('Hello from Delphi! :"{/=''æ}[øå] ÆØÅ'));
  finally
    Teams.Free;
  end;
end;

Make sure to read up on throttling and other rules of engagement before you start spamming your Teams channels. See the MS article on WebHooks for more detail.

Update! Uwe Raabe did a refurbished version that eliminates Indy and the need for the the OpenSLL DLLs, and makes the code work cross platform!

unit O365WebHook;

// Lars Fosdal, 2020 OCT 16
// Simple example without error handling
// Uwe Raabe replaced Indy for TRESTClient, eliminating the need for the OpenSLL DLLS

interface
uses
  System.Classes, System.SysUtils,
  REST.Json, REST.Client, REST.Types;

type
  TWebHookMessage = class
  end;

/// <summary> See https://docs.microsoft.com/en-us/microsoftteams/platform/webhooks-and-connectors/how-to/connectors-using
/// for examples of how to structure the json for creating advanced formats</summary>
  TSimpleText = class(TWebHookMessage)
  private
    FText: String;
  public
    property Text: String read FText write FText;
    constructor Create(const aText: string);
  end;

type
  TWebHook = class
  private
    FClient: TRESTClient;
    FRequest: TCustomRESTRequest;
    FURL: string;
  protected
    property Client: TRESTClient read FClient;
    property Request: TCustomRESTRequest read FRequest;
  public
    constructor Create(const aURL: string = '');
    destructor Destroy; override;
    function PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
    property URL: string read FURL write FURL;
  end;

implementation

{ TWebHook }

constructor TWebHook.Create(const aURL: string);
begin
  inherited Create;
  FURL := aURL;

  FClient := TRESTClient.Create(nil);
  FRequest := TCustomRESTRequest.Create(nil);
  FRequest.Client := FClient;
end;

destructor TWebHook.Destroy;
begin
  FRequest.Free;
  FClient.Free;
  inherited;
end;

function TWebHook.PostMessage(const aMsg: TWebhookMessage; aOwnsMsg: Boolean = False): Boolean;
begin
  try
    Request.Client.BaseURL := URL;
    Request.Method := rmPOST;
    Request.AddBody(aMsg);
    Request.Execute;
    Result := Request.Response.Status.Success;
  finally
    if aOwnsMsg then
      aMsg.Free;
  end;
end;

{ TSimpleText }

constructor TSimpleText.Create(const aText: string);
begin
  inherited Create;
  FText := aText;
end;

end.

There is a known issue with a workaround for Delphi 10.2.

Have fun!

First Steps with Blazor – Simple Talk

It is always fun to play with new toys! This article from Julio Sampaio introduces you to Blazor – Web pages done in C#.

Blazor stands for Browser + Razor, which gives you an idea of what’s behind the new framework. Razor is the ASP.NET programming syntax that Microsoft uses to create its C# (or VB.NET) dynamic pages. Now, you can create web applications using only C# and run them in a web browser.

Source: First Steps with Blazor – Simple Talk

Get started writing Linux apps with Delphi 10.3 and 10.4

Firstly, you need an Ubuntu installation. If you don’t have a physical machine with Ubuntu, you may chose a suitable Virtual Machine, such as VMWare, VirtualBox or Hyper-Visor.

I chose Hyper-V since it is included with Windows 10.

To enable Hyper-V on your Windows installation, Open the “Windows Features” tool and check Hyper-V. If you alse use Visual Studio, you may want to add Windows Hypervisor Platform as well.

A word of advice with regards to storage: Place your VMs on an SSD disk.

Reboot your Windows installation if so instructed.

When ready, Start Hyper-V Manager, and chose Actions | Quick Create… | Ubuntu 18.04.3 LTS 


A 1.6Gb download will pursue, so the time depends on your bandwidth.

Before firing up the VM, tweak the new VM config to f.x. 4 CPUs and 8192Mb RAM to give the VM some juice, then power up and go through the base installation.

To manually add the build tools for Ubuntu, open a terminal, and run

sudo apt update
sudo apt install build-essential

The first ensures your VM pull all relevant updates, and the second installs gcc and the suite of build tools for Linux.

Next, download the appropriate PAServer for Rio:
http://docwiki.embarcadero.com/RADStudio/Rio/en/Installing_the_Platform_Assistant_on_Linux
or Sydney:
http://docwiki.embarcadero.com/RADStudio/Sydney/en/Installing_the_Platform_Assistant_on_Linux

Note that you can install PAServers for both Rio and Sydney. Just remember to edit the paserver.config and change the default port number for one of them.

Install it to a folder of your liking, as per the instructions in the link and start ./paserver and enter the (optional) password you want the PAServer to use.

To find the IP address of your local Ubuntu VM, open a terminal and run

ip addr show

The IP address we are looking for is in address 2 – in this case: 172.17.202.68

When you first try to compile a Linux app, start with something simple like a console application.

program LinuxTest;

{$APPTYPE CONSOLE}

{$R *.res}

uses
  System.SysUtils;

begin
  try
    try
      Writeln('Hello whirled');
    except
      on E: Exception do
        Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    Write('Press Enter: ');
    Readln;
  end;
end.


On the first ever compile, the IDE will prompt you for a connection profile.

Since we didn’t change the configuration, all we need is to fill in the IP address for the host name.
If you entered a password when starting the PAServer, that password is needed here. Remember to click on Test Connection to verify that the PAServer is reachable.

On the first connection, the IDE will download the SDK through the PAServer. This takes a little while and the progress is presented on a desktop-always-on-top progress dialog which is somewhat annoying if you want to do something else while waiting.

After it is complete, you will have a collection of files that allows the linker to find the right link targets for your Ubuntu host.

At this point, your simple console app should be ready to run. It will compile, link, and then upload it to the Ubuntu VM and start it through the PAServer.

By default, it seems that we won’t get to see the actual console where the app runs when run through PAServer, so if you want to run it manually – locate the uploaded file on your installation.
It will typically reside under a long path that reflects the name of your connection and your Windows username. The PAServer folder will be in the same folder where you installed the PAServer-20.0 (if you are using 10.3.3).

Under scratch-dir, you find the folders for your winuser-connection combos.

Under there again, you will find a folder that matches your Delphi project name, and in that folder your executable which you can start with “./exename”.

At this point, you should be able to do the same.

The next step is to download FMX for Linux via GetIt.
Then you can get started on something beautiful like this 😎
File | New | Multi-device application – change target to Linux 64-bit and run!

Please note that there may be pitfalls with FMX for Linux.
http://docwiki.embarcadero.com/RADStudio/Sydney/en/FireMonkey_for_Linux

Lars Fosdal, August 26th, 2020

Postscript:
I have also done this on VirtualBox, but when comparing Hyper-V and VirtualBox it is clear that Hyper-V is much better at managing HW resources – preventing the constant buzz of the CPU cooling fan which was quite annoying when using VirtualBox.

Frink – Numbers with units of measure

As unusual programming languages go, this one actually is pretty amazing.
It adds units of measure to the number.

“One day Alan Eliasen read a fart joke and got so mad he invented a programming language. 20 years later Frink is one of the best special purpose languages for dealing with units.“But why do we need a language just for dealing with units?” Glad you asked! Intro to Units A unit is the physical property a number represents, like distance or time. We almost always are talking about SI units, or Système international.”

by Hillel Wayne

Source: The Frink is Good, the Unit is Evil • Hillel Wayne