Blog-Archiv

Samstag, 29. Dezember 2018

Labels and Required Fields in HTML Forms

This Blog is about writing HTML forms in an elegant way, concerning the label for a field, and the attribute "required". It is about structure and DRY source-code, not about layout.

Fields need labels to be understood. Fields are required (mandatory) sometimes, and should be identifiable as such right from start, not just after validation. The traditional way is to put an asterisk (star) on the label. Would be nice if labels of required fields also were bold. Further labels should be chained to their fields by id reference, this enables the user to focus the field by clicking onto the label.

Not Elegant Code

Here is a raw login-form, assuming "User" is required but "Password" is optional:

This looks ugly, but forget layout and styling for now. I want to think about the HTML structure of forms. Let's review the source-code of this "Login" form.

Here is CSS defining a basic table-layout, allowing table-cells being other than div elements:

    <style>
      .table
      {
        display: table;
      }
      
      .table > div
      {
        display: table-row;
      }
      
      .table > div > label,
      .table > div > input,
      .table > div > select,
      .table > div > textarea,
      .table > div > div,
      .table > div > fieldset
      {
        display: table-cell;
      }
    </style>

HTML, using label and input as table cells:

    <form>
      <div class="table">
        <div>
          <label for="username-id"><b>* User:</b></label>
          <input name="username" required id="username-id">
        </div>
        <div>
          <label for="password-id">Password:</label>
          <input name="password" id="password-id">
        </div>
      </div>
    
      <div>
        <input type="submit" value="Login">
      </div>
    
    </form>

Why is this HTML source not elegant?

First, the "User" field has been marked three times as required:

  1. through the required attribute (browser validation)
  2. through the * asterisk
  3. through the <b> bold markup

This would have to be repeated for all required fields. Can't we do better using CSS? I would suggest a ::before pseudo-element for the asterisk, and a rule for the bold markup. Also the trailing colon could be done by an ::after pseudo-element.

Second, I would like to avoid the handwritten id-chaining via <label for="username-id">. The label should be automatically chained to the field after it (or, in case of radio-buttons, before it).

Thus the table inside the form above should look like the following:

      <div class="table">
        <div>
          <label>User</label>
          <input name="username" required>
        </div>
        <div>
          <label>Password</label>
          <input name="password">
        </div>
      </div>
  1. Just set required once on input. The associated label should then know by CSS rules how to look like.

  2. Just use the elements without id. Let's generate an artificial id out of the name attribute and chain the label to the input that way automatically.

Why not enclose the field into label?

          <label>User
            <input name="username" required>
          </label>

I admit, the id-chaining would not be necessary then!

But: the field being a child element of the label would it make impossible to arrange them into a table layout. Further I plan to optionally move the label above the input via CSS, which is much easier when the label is not parent but sibling.

The Elegant Way

Not an easy way, beside CSS we will have to use JavaScript.

The basic problem here is that we can not write a CSS selector pointing to the label of a required field. Styles inside the rule-set will be applied to the last element in the selector. It is impossible to write a CSS selector that designates a <label> followed by an <input required>:

label + input[required]  {
  /* this selector would style the INPUT, not the LABEL! */
}

So, why not put the label behind the input-field? This leads to other problems that are not easy to solve either, the label structurally being right of the field. Moreover it would result in HTML that is not intuitive. So let's implement some CSS and a JavaScript that solves the problems.

CSS

      /* Set asterisk before required fields. */
      form label[required]::before
      {
        content: "* ";
      }
      
      /* Add colon after any label. */
      form label::after
      {
        content: ": ";
      }
      
      /* Make labels for required fields better visible. */
      form label[required]
      {
        font-weight: bold;
      }

This CSS puts the * (asterisk) before any label that holds an attribute required, and it puts a : (colon) behind. It displays the label text in a bold font.

Two of these CSS selectors are built on the fact that the label element itself holds the required, which is not true for our goal-HTML above! So the JavaScript needs to copy that attribute from the input (select, textarea) to the label to make this CSS work.

JS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
      /**
       * Avoid duplication of "required" and "id" attributes in HTML forms.
       * Any LABEL before an INPUT must get the INPUT's "id" value into its "for" attribute. 
       * Any LABEL before an INPUT must copy the INPUT's "required" if it exists. 
       * Sometimes the "required" attribute is not in the next element but in its sub-DOM.
       */
      function manageIdAndRequired()
      {
        var count = 1;  /* sequence number for unique ids */
        
        /** Searches for "required" in given element and all sub-elements. */
        function containsRequired(element) {
          if (element.getAttribute("required") !== null)
            return true; 
          
          var children = element.children;
          for (var i = 0; i < children.length; i++)
            if (containsRequired(children[i]))
              return true;
          
          return false;
        }
        /* end containsRequired() */
        
        /** Copies the optional "required" attribute to LABEL, chains it with INPUT's id. */
        function chainLabelToIdAndCopyRequired(label) {
          var nextSibling = label.nextElementSibling;
          var nextTagName = nextSibling ? nextSibling.tagName : undefined;
          
          /* previous is just for radio-buttons */
          var previousSibling = label.previousElementSibling;
          var previousTagName = previousSibling ? previousSibling.tagName : undefined;
          var isRadioLabel = (previousTagName === "INPUT");
          
          if (isRadioLabel ||
                nextTagName === "INPUT" ||
                nextTagName === "SELECT" ||
                nextTagName === "TEXTAREA" ||
                nextTagName === "DIV" ||
                nextTagName === "FIELDSET")
          {
            if ( ! isRadioLabel && containsRequired(nextSibling))
              label.setAttribute("required", "");  /* copy attribute to non-radios */
            
            if (label.getAttribute("for") === null) {  /* chain id */
              var sibling = isRadioLabel ? previousSibling : nextSibling;
              var id = sibling.getAttribute("id");
              
              if ( ! id && sibling.getAttribute("name")) {  /* generate id from name */
                var identity = "_"+count;
                count++;
                id = sibling.getAttribute("name")+identity;
                sibling.setAttribute("id", id);
              }
              
              if (id)
                label.setAttribute("for", id);
            }
          }
        }
        /* end chainLabelToIdAndCopyRequired() */
        
        /* Iterate all forms on page */
        var forms = document.getElementsByTagName("form");
        for (var f = 0; f < forms.length; f++) {
          var elementsInForm = forms[f].getElementsByTagName("*");
          for (var i = 0; i < elementsInForm.length; i++) {
            var element = elementsInForm[i];
            if (element.tagName === "LABEL")
              chainLabelToIdAndCopyRequired(element);
          }
        }
      }
      
      /* Execute function on page-load */
      window.addEventListener("load", manageIdAndRequired);

This script installs a document-load listener that will execute the function manageIdAndRequired() when called. That means, this function will be executed just once when the document is ready.

The manageIdAndRequired() is a function that encapsulates two other internal functions. It loops over all forms and the elements contained in them, searching for label elements. When it found one, it calls chainLabelToIdAndCopyRequired() with the label as parameter.

The chainLabelToIdAndCopyRequired() function looks at the next sibling element. In case it's none of "INPUT", "SELECT", "TEXTAREA", "DIV" or "FIELDSET" it will do nothing (mind that tagName values are uppercase!). Else it will check whether the next sibling has an attribute required. When yes, it copies it to the label, this will make the CSS above work.

Next it will check whether there is an id in next sibling (previous sibling for radio buttons). When no, it uses the sibling's name to generate a unique id for it. That id is then also written to the label's for attribute.

The containsRequired() function is quite simple. It returns true in case the given element holds an attribute named required, else it searches in all child elements for that, recursively.

Mind that the chainLabelToIdAndCopyRequired() function strongly depends on the HTML structure. In case you have label and input separated by td or div elements, it won't work. You would have to introduce a findSibling() function then.

This script has been written to cope with much more than the fields used in the login-form above. It also can handle nested fieldset boxes and radio buttons inside div wrappers.

Here is how the HTML source looks after the script has run:

      <div class="table">
        <div>
          <label required="" for="username_1">User</label>
          <input name="username" required="" id="username_1">
        </div>
        <div>
          <label for="password_2">Password</label>
          <input name="password" id="password_2">
        </div>
      </div>

For each input, the required has been copied to the label when present, and an id has been generated and copied into the label. No need any more to write these things explicitly into the HTML source!

Resume

A simple HTML structure helps to keep CSS and JavaScript code simple. I prefer a DIV-table to a traditional TABLE, because it is more flexible. I want to use this flexibility in a following Blog where I will introduce a form layout proposal, using all native input fields that HTML-5 browsers provide.

Main subject are the labels again. They need to be sometimes above, sometimes left of the fields. We would like to decide this through a CSS class on the DIV-table. Or maybe let the user control it through a toggle button?




Donnerstag, 27. Dezember 2018

HTML Form Talks HTTP

HTML forms are not so much in use any more, outdated by AJAX-driven single page applications. Nevertheless they still work and are a simple way to get inputs from browser users.

Mind that using HTML-forms makes sense only when you have your own web application running inside some public HTTP server, so that you can implement the responses for the incoming requests. What I present here just uses public "echo" servers on the internet.

In subsequent articles I will propose an element-structure and CSS-layout for HTML forms. In this Blog I just want to outline their HTTP capacities.

Form Skeleton

Following is a minimal HTML form with just one text-field (text is the default INPUT type):

<form action="http://www.tipjar.com/cgi-bin/test" method="post">
    <input name="single-line-text">
    <input type="submit" value="Ok">
</form>

→ The action attribute determines to which URL the form data get sent.

→ The method attribute determines the HTTP command to use for the HTTP request. GET and POST would make sense. Both can send data (although the name "GET" would not let expect sending; we could understand it as "GET me the result of the search-pattern I sent").
The difference between GET and POST is that GET transmits the form field values in URL parameters, while POST puts them into the HTTP request body. Consequence is that GET requests are unprotected.

→ Any form field (like single-line-text) needs to have the name attribute, else it would be ignored on sending.

→ The submit button would actually send the form data after their validation succeeded. Validation refers to syntactical correctness, and to the optional required attribute of HTML elements.

Thus forms are about

  • Field value retrieval, elements are:
    • <INPUT type="....">
    • <SELECT>
    • <TEXTAREA>
  • Value validation
  • Packing into an HTTP request
  • HTTP response processing

Example Form

The example below contains some input-fields and two action buttons. The red "Reset" button would clear the form, the green "Submit" button would send it. Both go just to the form they are contained in, because there can be several forms on a page. By the way, any button inside the form would submit when clicked, except when it is of type="button" or type="reset".

Mind that required fields must have a non-empty value, the browser checks this before sending! In this form, all fields except the "Multiple Lines" textarea are required. The browser would most likely mark invalid fields red on submit.

HTML Example Form
Male Female Other

Before you click the green "Submit" button, choose which action-URL you want to send to, this will determine what you see in the "HTTP Response" area below:

http://jkorpela.fi/cgi-bin/echo.cgi http://www.tipjar.com/cgi-bin/test # (own page)
Thanks to http://jkorpela.fi/cgi-bin/echo.cgi for his gorgeous form echo!

The chosen HTTP method won't play a role now, because the server-response is redirected to the "HTTP Response" area below; just when you choose www.tipjar.com you may see a difference, because this page echoes very detailed.

POST GET

Fill all fields, then click the green "Submit" button. You should see the HTTP response in iframe below (where the form redirects to).

HTTP Response:

Source

This example form was done by following HTML source:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
  <form method="post" action="http://jkorpela.fi/cgi-bin/echo.cgi" target="submit-result">

    <fieldset>
      <legend>HTML Example Form</legend>

      <div class="table">
        <div>
          <label>Single Line</label>
          <input name="single-line-text" id="text-id" required value="Default value in text-field">
        </div>
      
        <div>
          <label>Password</label>
          <input name="hidden-password" type="password" required>
        </div>
      
        <div>
          <label>Multiple Lines</label>
          <textarea name="multiline-text" required>Default
value
in
text
area</textarea>
        </div>
        
        <div>
          <label>Radiobuttons</label>
          <div class="radio-container"> 
            <input name="gender" type="radio" value="male" required>Male
            <input name="gender" type="radio" value="female" required>Female
            <input name="gender" type="radio" value="other" required>Other
          </div>
        </div>
        
        <div>
          <label>Multiple Select</label>
          <select name="multi-select" multiple required>
            <option value="one">Use</option>
            <option value="two">Shift</option>
            <option value="three">and</option>
            <option value="four">Control</option>
            <option value="five">Keys</option>
          </select>
        </div>
            
        <div>
          <label>License Read</label>
          <input name="boolean" type="checkbox" required>
        </div>
      
      </div>  <!-- end table -->
           
      <div class="buttonbar">
        <input type="reset" value="&#x2718;" class="reset-button">
        <input type="submit" value="&#x2714;" class="submit-button">
      </div>

    </fieldset>
  </form>

  <iframe name="submit-result"></iframe>

The form redirects the HTTP response, using the target attribute, into the iframe on bottom. Mind that this iframe was referenced by name, not by id. In case target is not present, the page itself would be replaced by the HTTP response.

Yes, the CSS layout is missing. Do not care, another Blog will explain this in more detail.

Example Response

If you enter the values shown in this screenshot (the password was "aaa"):

you should see following HTTP response:

single-line-textDefault value in text-field
hidden-passwordaaa
multiline-textDefault
value
in
text
area
genderother
multi-selectone
multi-selecttwo
booleanon

→ We can see that the name attribute of the field has been used as field identification, not the id.

→ The password arrived as clear text, thus any modern browser will warn you that the safe https protocol has not been used.

→ The newlines in the multiline-text were preserved correctly.

→ For the gender radio buttons, the value attribute of the INPUT field has been sent, not the visible label.

→ Two multi-select parameters (with same name!) arrived, representing all selected options in the "Multiple Select" field.

→ The boolean field delivered "on", not "true".


Resume

Thanks to web servers that echo form requests we can play with form fields here.

In a past Blog I introduced a way to quickly build a Java servlet via Maven. You could do that, start the servlet with mvn jetty:run, then put the http://localhost/your_serlvet_name URL into the action attribute of the form and see what happens on the server side.




Samstag, 22. Dezember 2018

Responsively Sizing Iframes

I discovered the techniques explained here on Ben Marshal's and John Surdakovski's pages on the web. It is about "responsive" iframe HTML elements, meaning that an embedded iframe should resize when the browser window gets resized.

WARNING: Studying this article on a mobile could lead to misunderstandings, because all example iframes may have the same size then, also you won't be able to resize the browser window.

Following shows how a video player embedded in an iframe looks when you don't assign pixel values to width and height attributes:

  <iframe
    src="https://www.youtube.com/embed/-p3h2Xcay_o"
    frameborder="0"
    allowfullscreen>
  </iframe>

Would be nice now if it filled the whole width, keeping its aspect ratio, and grew bigger when the browser window grows bigger.

But there is a problem with aspect ratio (= height / width). The iframe element can host a whole HTML page, and an HTML page has no height by default (although it is aware of width). That's the reason why you always find the hardcoded width and height attributes on iframes, they express the aspect ratio, but, as a side effect, they also fix its size: <iframe width="640" height="360" ...>. Thus an iframe having these attributes would not resize dynamically, and without them it has no aspect ratio.

Nevertheless the video player has an aspect ratio, only its wrapping iframe does not know about it, because it was made for HTML pages. Tricks like setting the CSS height: auto; and width: 100%; do not help, this looks like the following:

  <iframe
    style="width: 100%; height: auto;"
    src="https://www.youtube.com/embed/-p3h2Xcay_o"
    frameborder="0"
    allowfullscreen>
  </iframe>

You see that the height remained small, while stretching the width actually worked. Setting height to 100% won't help either, because this would work just when all parents' heights had been set to a defined value.

So we need a technique that gives us a rectangle which sizes dynamically. What people use is a parent block element that has a vertical padding percentage, and then putting the iframe onto that padding rectangle, stretching it to full-size. We need to study some basics to understand that.

On the CSS Box Model specification we find for padding-xxx:

Percentages: refer to logical width of containing block.
Logical width: refers to the physical width (horizontal dimension) in horizontal writing modes, and to the physical height (vertical dimension) in vertical writing modes.

Thus a percentage in some padding takes the dynamically calculated dimension as base. So what we need is a parent element with such a dynamic padding rectangle, and then place the iframe absolutely over it. Consider following CSS and HTML.

  .iframe-container
  {
    position: relative;
  }

  .youtube-aspect-ratio
  {
    /* The default YouTube aspect-ratio: height / width */
    padding-bottom: 56.25%;
  }

  .iframe-container iframe
  {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
  }

This CSS defines three rule-sets:

  1. Any element with class iframe-container will have position: relative; that is to be a parent for an absolutely positioned child element.

  2. Any element with class youtube-aspect-ratio will have padding-bottom: 56.25%; that means it takes 56.25% of the width of the element! This is the dynamic rectangle where the iframe can go to. It could also have been the padding-top.

  3. An iframe inside an element with class iframe-container will fill all available space given by the parent (width: 100%; height: 100%;) and be attached to top-left corner through position: absolute; and top and left set to zero. It will not be affected by the undefined height of its parent element, because absolutely positioned elements go out of the normal layout flow. The height of its parent will be zero when nothing is inside.
  <div class="iframe-container youtube-aspect-ratio">
    <iframe
      src="https://www.youtube.com/embed/-p3h2Xcay_o"
      frameborder="0"
      allowfullscreen>
    </iframe>
  </div>

This HTML wraps the iframe into a div element with class iframe-container, and it adopts the youtube-aspect-ratio of the video-player as the aspect ratio for the padding-bottom rectangle. Mind that there are no width and height HTML-attributes on the iframe, just the according CSS-properties were set to 100% through the rule-set above.

Here is the outcome:

That's it!
Resize the browser window to try it out. The video player should resize proportionally, keeping its aspect ratio.
You will want to create different aspect ratio CSS-classes for other links embedded in further iframes.




Sonntag, 9. Dezember 2018

Upgrade to Ubuntu 18.04 LINUX 4.15.0

Every two years a major version of the open-source operating-system Ubuntu LINUX gets released. This time it was 18.04, where 18 refers to 2018, and 04 to April.

If you are considering to upgrade your Ubuntu, this Blog may contain some useful informations for you. It can be done with the graphical "Software Updater" tool. They recommend to install all updates before upgrading.

Caveat

Applications that you installed using the "Software" tool of Ubuntu will be replaced by the newest version without confirmation. So you may lose beloved applications you wouldn't have liked to be upgraded.

I had an old version of the OpenShot video editor that was fast and simple, and I didn't like the newest one which is slow and complex. Moreover it is not fully backwards-compatible, e.g. the video titles' layout is broken.

There was no way back to the old version. The necessary Python libraries are not available any more, and even if they were, they might conflict with the new ones. I will keep on trying, but until now I could not install OpenShot 1.4.3.

Surprise

I am not sure if this was a positive or negative surprise:

The desktop environment is no more Unity. Consequences are as follows.

  1. Desktop icons are deprecated. Gnome decided so. Your old icons still will be there, but without image. If you double-click one, a dialog will ask you to trust it, and then it will work again, and also get back its image. But that is a workaround provided by Ubuntu!

  2. So where to put the quick-starters for our applications? Drag- and dropping a desktop icon into the left-side toolbar failed. Searching the web for adding application launchers to the toolbar failed.
    Update: it is possible! Copy the .desktop file from $HOME/Desktop to $HOME/.local/share/applications and its icon to $HOME/.local/share/icons, then open "Find Applications" (dash), search for it, and drag it into the left-side toolbar.

  3. The "X" button ("Close Window") in the title-bar of windows is on right side again. Yes you can (configure it). Or you sit head-shaking before your Gnome like me (was it left, was it right? :-)

  4. File-chooser "OK" button is in title-bar on top right side, "Cancel" on left, actually ergonomic. Here you have to shake your head vertically:-)

  5. The "Find Applications" button on left-side toolbar travelled from top (Unity) to bottom (Gnome). Another vertical head shake, but not a "Yes":-)

  6. The desktop-bar on top doesn't contain any more the full-screen window title-bar, like it was with Unity. Although confusing for beginners, this saved space on small laptop screens, and I appreciated it.

  7. You can't terminate the screen-saver by a simple mouse-click any more, you need to drag & drop from bottom to top now, like they do on mobiles, or you use the keyboard (ENTER, SPACE, this didn't change).

It seems to be possible to get back Unity, have a look at this page.

Shutter "Edit" Button Disabled?

The screenshot utility Shutter is quite popular, because it provides some basic markup drawing functionality on the screenshot, circles, arrows etc. But on 18.04 the "Edit" button is disabled. Here is a page that explains how you can get it back. You must install three additional libraries: libgoocanvas-common, libgoocanvas3, libgoo-canvas-perl, and then purge Shutter with sudo killall shutter.

Beeping Backspace

Sorry for this personal attitude, but I don't like beeping keyboards. Here is how to get rid of the sound that the BACKSPACE key makes in case it can't delete anything. Turn OFF the "Alert Volume" on tab "Sound Effects":

You find the "Settings" application in top-right system-menu, click the "tools" button.


"Suspend" Button Missing

There is no "Suspend" button any more in the system-menu. "Power Off" will shut down the computer completely. But hibernation is not related to the Gnome desktop. The web says, Ubuntu dropped hibernation in 2012. So why did the 16.04 "Suspend" then work?

A closer investigation showed that 18.04 supports hibernation via the computer's power-button (the physical ON/OFF switch). When you configure in "Settings" - "Power" the option "When the Power Button is pressed" to "Suspend", pressing the computer's power-button will hibernate it. Another press on the power-button will wake it up again.

Mind that holding down the button too long will turn off the computer the hard way, without shutdown!

Get Date in Top Bar

Launch following command in a terminal-window:

$ gsettings set org.gnome.desktop.interface clock-show-date true

Quickly Connecting to Remote File Systems

Here is the new way how to connect to your FTP server using Nautilus file browser. Click "Other Locations" on left side to see this "Connect to Server" input field:

It supports a lot of protocols like FTP, SFTP, NFS, SMB, DAV (HTTP).

New Progress Indication

Copying files in Nautilus file explorer shows a new kind of progress bar. You need to click onto the pie-chart button in top-toolbar to see it, see screenshot below. No other response shows up when starting to copy!



Resume

Lost a full day upgrading. Lost my old OpenShot.

But graphics of 18.04 is definitely better than 16.04. The desktop became simpler. The underlying LINUX is said to be much safer. So don't always insist on your old habits, move forward, it's not so bad!




Samstag, 8. Dezember 2018

Mount Disk on LINUX Startup

I removed 3 of 5 LINUX systems from my laptop hard-disk, using the graphical tool gparted (GNU partition editor). The resulting space I used to create a new hard-disk partition. As gparted did not let me define a mount-point for the new partition, I turned to the fstab file

$ sudo gedit /etc/fstab 

to add a mount line manually. But there I saw following line (for my MINT-partition):

UUID=e6606afe-96d5-4a4b-8f40-bdb5c2e98e51 /media/mint ext4  defaults 0 2

The partition is addressed using its UUID here, no more by device-name (like /dev/sda11). This is said to be safer. So I decided to do it that way - but, how to find out the UUID of the partition?

$ blkid
....
/dev/sda10: LABEL="Mint" UUID="e6606afe-96d5-4a4b-8f40-bdb5c2e98e51" TYPE="ext4" PARTUUID="bb049714-4b60-43b7-b8bd-997ebf94f085"
....

The blkid (block-identity) command helps out. But now I wondered how I can avoid all that investigations for LINUX commands, and do it with a graphical tool like a modern computer user would do! So instead of copy & pasting the UUID that blkid showed my into /etc/fstab, I started to search for a graphical fstab-editor. And here it is (at least for Gnome-systems):

$ sudo gnome-disk

This UI comes up, below it you see the output of the blkid command:

This shows both the device-name and the UUID of my new partition. Here is how to add it to /etc/fstab:

In the tools-menu, you find the item "Edit Mount Options", and there you can do it:

Here I have set the mount-directory to /media/space. Then I chose the UUID-identification from the drop-down below. Saved it, and it was in /etc/fstab! I created the mount-directory:

$ sudo mkdir /media/space
$ ls -l /media
drwxr-xr-x   2 root root 4096 Dez  8 21:24 space

Restarted the computer, and I had a big disk instead of the 3 redundant LINUX installations that I never used. And I learnt how to solve problems with graphical tools instead of mystic command line hacking. Keep yourself organized!




Freitag, 27. Juli 2018

Some Things We Always Do Wrong

It has become an Internet habit: "Five things you must see before you die", or "Ten things that will blow your mind". For software developers, I will contribute "Some things that always go wrong" here and now, hoping to not "blow your mind" :-)

So what do we do wrong every day?

  1. Blowing Up Things
  2. Underestimating Names
  3. Fixing Symptoms Not Causes
  4. Copy and Paste Coding

Maybe I repeat myself. It's a daily struggle to avoid these things, and to make other people realize them as being major obstacles.

Blowing Up Things

When we implement a new feature, we go inside the source and try to find the point where we can insert our solution. We add some parameters, a new loop in the method body, the class gets some new member fields, and that's it, devil-may-care, coffee machine! So coffee goes in and in and in, until the class has 3000 lines of code, and its state is no more controllable, consisting of 30 member fields, full of long methods. Same with packages. Same with modules, libraries, applications, tools. It turns out that the word "implement" is too near to "implant".

Why do we always blow up things from inside? Why don't we build on them, from outside, respecting their responsibilities? Why don't we reuse them, override them, call old methods from new ones, extend classes by creating sub-classes, put new fields into new delegate classes?

Blowing up from inside we violate one of the oldest C programming rules: one function should have just one purpose (single responsibility principle). Instead we should:

  • Refactor until single responsibility is reached (→ "blow down")
  • Implement new features always by new classes, packages, modules
  • Make all new sources comply with single responsibility

What I found useful in real life:

  • No class should have more than 400 lines
  • No method should have more than 1 loop
  • Even the largest method must fit into a 1024 x 768 screen

If you blow up classes to implement several responsibilities, you make redesign difficult up to impossible. Essentially you are breeding the monolith that way. You are doing the opposite of "divide and rule". But breaking down problems to smaller ones is the only way to cope with complexity. And complexity is the primary problem of software development!

Underestimating Names

Every day we create names for classes, functions, fields, variables, packages, modules, libraries, applications, tools, helpers, utilities, .... Does anybody care about these names? Aren't they just for the compiler or reference docs? Following sentence I hear frequently:

Doesn't matter how it's called if it works

So this is a strong sentence, and I need to find an even stronger one now that tells the exact opposite .... what about this:

If names don't matter, why do we use names?

So let's number things. Like they do in the army. Or use completely absurd names like the JS community.

We are still living in times of hand-written source code. Maintenance time for hard-to-read source code is high. Source code is communication, you write primarily for humans, only secondarily for machines. Good names are the key to understandable source code. Following are my advices for better naming:

  • Don't use names that are not in common sense (you are the only one that would understand them)
  • Don't use abbreviations and acronyms (they make reading and understanding much harder)
  • Avoid too general terms like "service", "process", "system", "action", "data" (you may obscure the real intent, and can be misunderstood in many different ways)

Exceptions confirm the rule:
→ abbreviations that everybody uses are acceptable,
→ general terms that are used for high-level abstractions are also fine.

Words should be as precise as possible, and as short as possible.

Yes, this is a contradiction, and it is an art to achieve a compromise of both. You must exercise this for some time, then people will start to understand you fast and easily. Your source should look like a well-structured API manual, not like the code of enigma.

Fixing Symptoms Not Causes

The classic symptom is the NullPointerException. Easy and fast to fix, simply put an if-not-null condition in front. But then the problem occurs again in another place, and in another, and .... because this collection was expected to never be null! So we better should have fixed the cause. Of course the NullPointerException is not the only situation where a symptom may obscure a bug.

It's a little bit like the copy & paste story. As soon as some occurrences have been fixed, and then someone fixes the real cause, it will be not easy to say whether a symptom fix can be removed or not. We end up with code full of if-not-null conditions. Here is my advice how to avoid fixing just the symptoms:

  • Write assertions:
    • Assert parameters
    • Assert returned values
  • Keep methods short and classes small

This helps to narrow mistakes. Early revealed problems will not reach regions where the cause can not be guessed any more. Here is a strategy for finding causes:

  • Make the bug reproducible
  • Set a debugger breakpoint to where it happens, then reproduce it
  • Carefully study the stack trace in the halted debugger, the cause is most likely not on top of the stack!

Understanding the source code around, and its user stories, is absolutely necessary to be able to find causes. Take yourself time for this.

Copy and Paste Coding

Fortunately a well known problem, but also a survival strategy for certain developers. Unfortunately these always write the new code!

Here is the copy & paste story:

  • A buggy block of code has been duplicated 10 times
  • The first bugfix will be done in just one place, the 9 duplicates will stay undetected
  • Tests may uncover further duplicates, other developers will fix them, of course in different ways
  • After 10 developers fixed the dups in 10 different ways, it will not be visible any more that this was all the same functionality
  • The management will say "This software aged rapidly, let's hire some students to write a new one!"

To be fair, I've seen also skilled programmers achieve their feelings of success by copy & paste programming. It is the most wide-spread programming sin. It degrades source down to code. I just can advice to not repeat yourself, stick to DRY.


Conclusion

Even when programmers will be replaced by robots some day, the robots will have to be programmed by hand. Even when it will be possible some day to replace all hand written code by ready-made design patterns, these patterns will have to be programmed by hand. We can't escape. We (we!) have to be clever and avoid these things that always go wrong.




Sonntag, 22. Juli 2018

The Java to JS Transpiler JSweet

JSweet is a Java to JS (JavaScript) translator. It generates significantly less source code than its GWT counterpart, and that source is readable, as you can try out in its sandbox (generally a 1:1 translation). JSweet reads Java source, not class files, facilitating the javac compiler. Essentially it generates TypeScript code, not JS, and uses the TS compiler to generate the final JS output.

JSweet is built component-oriented, that means not only you can write libraries for it (called "candies"), you also can override the way how it generates TypeScript code.

Sounds great! This Blog is about my first steps in trying out JSweet.

The Different Worlds of Java and JS

Programming languages may have different target domains.

  • Java is a general-purpose programming language. The Java world is the computer, its file system, the network, databases, .... Every operating system that provides a JVM can be accessed by Java (at least through JNI).

  • The JS world is the web-browser, and a little bit of the network, cookies, and (since shortly) a local storage. Still different browsers provide different JS interpretations, and although these differences are getting less, you must be able to fix them, like jQuery does.

Thus it should not be a problem to emulate JS in Java, right? Just implement a BOM (browser object model) and a DOM (document object model), some kind of AJAX, and provide browser-specific overrides, that should do it.

But what happens in JS when you open a file in Java? A browser doesn't have access to the file system of the client computer! And what happens when you open a Java AWT Frame?

GWT simply does not support these parts of the Java runtime library (rt.jar). Does JSweet?

Trying it out

I downloaded the Quickstart example using git. To transpile the contained sources, Maven (mvn) must be installed. These were my commands:

cd whereverYouTryOutThings
git clone https://github.com/cincheo/jsweet-quickstart.git
cd jsweet-quickstart
mvn generate-sources

The mvn generate-sources command runs the transpilation from Java to JS. This generates a file target/js/quickstart/QuickStart.js, transpiled from src/main/java/quickstart/QuickStart.java. That JS file is referenced in webapp/index.html, and if you load this into a web browser, you will see what QuickStart.java did.

Then I installed the Eclipse plugin, imported the quickstart Maven project, and tried to modify src/main/java/quickstart/QuickStart.java.

package quickstart;

import static def.dom.Globals.document;
import java.util.ArrayList;
import java.util.List;

public class QuickStart
{
    public static void main(String[] args) {
        final List<String> list = new ArrayList<>();
        list.add("Hello");
        list.add("World");
        document.getElementById("target").innerHTML = ""+list;
    }
}

Mind the ugly access of the public member field innerHTML. That's the way JS does it, and here it invaded Java. Making member fields public is one of the things that you should avoid in OO source code.

The plugin did not generate JS when saving the Java file, so I again had to use the command line to get my changes into JS:

mvn generate-sources

Later I found out that you must activate the plugin for the Eclipse project:

  • select the project in package explorer
  • open the context menu (right mouse button)
  • choose "Configure" - "Enable JSweet builder"

Now every "Save" of a Java file would update the according JS file. Unfortunately not in the target folder but in the js folder, so this plugin is not yet ready, or I missed something.

Transpilation result was the following target/js/quickstart/QuickStart.js. This is already ES6, no more JS, as there is a class definition. The transpiler also can generate ES5, which is closer to JS.

/* Generated from Java with JSweet 2.2.0-SNAPSHOT - http://www.jsweet.org */
var quickstart;
(function (quickstart) {
    /**
     * This class is used within the webapp/index.html file.
     * @class
     */
    class QuickStart {
        static main(args) {
            let list = ([]);
            /* add */ (list.push("Hello") > 0);
            /* add */ (list.push("World") > 0);
            document.getElementById("target").innerHTML = "" + (a => a ? '[' + a.join(', ') + ']' : 'null')(list);
        }
    }
    quickstart.QuickStart = QuickStart;
    QuickStart["__class"] = "quickstart.QuickStart";
})(quickstart || (quickstart = {}));
quickstart.QuickStart.main(null);

Lots of boilerplate, as usual in generated code. Mind the last line where quickstart.QuickStart.main(null) gets called. Thus the page that loads the script doesn't need to initialize anything.

Here comes the webapp/index.html page that loads the script:

<html>
  <head>
    <meta charset="utf-8" />
  </head>
  <body>
    <p id="target"></p>
    <script type="text/javascript" src="../target/js/quickstart/QuickStart.js"></script>
  </body>
</html>

And here is a screenshot of the result:

Concepts

As you may know, you can not transpile every part of the Java runtime library with GWT. JSweet has similar restrictions, it just uses a fork of the GWT Java emulation library. But it provides a way to enlarge the usable range of the Java runtime: "candies". There are candies (libraries) that refer to Java, and such that refer to JS.

  • The Java part is called j4ts (→ "Java for TypeScript"). For programming see the JavaDoc for the core browser API. I saw even candies for AWT and Swing.

  • The JS part is much bigger. It is called jsweet candies. JQuery, Angular, React, and many other JS libraries are available for integration. Thus you could now write Java code that uses these powerful JS components. But mind that the API would be JS-oriented, i.e. you are leaving encapsulation and other object-oriented principles behind. I saw lots of static and public (non-final) in the examples.

You will find more conceptual information on the JSweet specification page. If you want to see source code of JSweet, download it from github, it's all open source. There is also a nice video.

Conclusion

I am a little bit frightened by applying the JS style in Java. It took me a long time to understand and use encapsulation in Java, should I give this up now and change to the fragile and error prone JS style?

Nevertheless the possibility to transpile an AWT application to the web is seducing. I will try this out.