Thursday, July 4, 2024

Easy install and update software on Windows with Chocolatey

Chocolatey provides a way similar to APT in Linux to download and update software on a Windows machine.

Here's how to install it: https://chocolatey.org/install

And here are the available packages: https://community.chocolatey.org/packages

And here's an example of installing some basic software:

choco install totalcommander googlechrome firefox notepadplusplus zoom vlc

Tuesday, April 27, 2021

Javascript Tips and Tricks

  • It is not possible to reassign an argument in a method. (a) => { a = 2; } will not work.
    However, it is possible to reassign a property of an argument object in a method, so (a) => { a.value = 2; } will work.
  • Promise execution has a different queue than normal code. This can be important in testing async code: in the test we'll have to make sure the test execution waits for the promise to resolve, before testing the return value or side effect.
  • Optional chaining saves you the trouble of testing for undefined keys in nested objects. Eg. person.address?.street?.name 
  • Use a variable's value as a key in and object with this syntax: { [yourKeyVariable]: someValue } 
  • ...

Testing a function with setTimeout and promises in Jest in Javascript

Inspiration for the code taken from Jest timer mocks documentation

Given a function that has both async/await and timeout, to test it with Jest, we need to know the following:
  • use jest.useFakeTimers() to get control of the timing
  • for each timeout, use the Jest Timer Control that applies
    • jest.runAllTimers() -- Fast-forward until all timers have been executed
    • jest.runOnlyPendingTimers() -- Fast forward and exhaust only currently pending timers (but not any new timers that get created during that process)
    • jest.advanceTimersByTime(1000) -- Fast-forward the given amount of milliseconds
  • for each await in the tested function, add await Promise.resolve() in the test, or any other way to resolve the promise before the test execution continues


The function to be tested:
async function infiniteAsyncTimerGame (beginningCallback, endingCallback) {
	console.log('start round');
	beginningCallback && await beginningCallback();
	console.log('awaited beginningCallback, will now set timeout');
	setTimeout(async () => {
		console.log('continuing after timeout');
		endingCallback && await endingCallback();
		console.log('awaited endingCallback, will now call function again');
		infiniteAsyncTimerGame(beginningCallback, endingCallback);
	}, 10000);
	console.log('end round');
}
The test:
test('infiniteAsyncTimerGame', async () => {
	// Preparations
	jest.useFakeTimers();

	let beginCounter = 0;
	let endCounter = 0;

	async function beginningCallback () {
		await new Promise(resolve => resolve(++beginCounter));
	}

	async function endingCallback () {
		await new Promise(resolve => resolve(++endCounter));
	}

	// Begin testing
	// notice the await keyword in front of the method call
	await infiniteAsyncTimerGame(beginningCallback, endingCallback);

	// At this point in time, there should have been a call to beginningCallback
	// We need to trigger the processing of each `await` keyword in the tested code
	// with `await Promise.resolve()` in the test run to flush the Promise queue
	// after that we can check for the value the Promise returned
	await Promise.resolve();
	console.log('check beginCounter');
	expect(beginCounter).toEqual(1);
	// After the beginningCallback, there should have been a single call to
	// setTimeout to schedule the next round in 10 seconds
	console.log('check timeout');
	expect(setTimeout).toHaveBeenCalledTimes(1);
	expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);

	// Fast forward and exhaust only currently pending timers
	// (but not any new timers that get created during that process)
	console.log('runOnlyPendingTimers');
	jest.runOnlyPendingTimers();

	// At this point in time, there should have been a call to endingCallback
	await Promise.resolve();
	console.log('check endCounter');
	expect(endCounter).toEqual(1);

	// After the endingCallback, the next round should be started
	// with a new call to beginningCallback
	await Promise.resolve();
	console.log('check beginCounter');
	expect(beginCounter).toEqual(2);
	console.log('check timeout');
	expect(setTimeout).toHaveBeenCalledTimes(2);
	expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
	console.log('runOnlyPendingTimers');
	jest.runOnlyPendingTimers();
	await Promise.resolve();
	console.log('check endCounter');
	expect(endCounter).toEqual(2);

	// third round
	await Promise.resolve();
	console.log('check beginCounter');
	expect(beginCounter).toEqual(3);
	console.log('check timeout');
	expect(setTimeout).toHaveBeenCalledTimes(3);
	expect(setTimeout).toHaveBeenLastCalledWith(expect.any(Function), 10000);
	console.log('runOnlyPendingTimers');
	jest.runOnlyPendingTimers();
	await Promise.resolve();
	console.log('check endCounter');
	expect(endCounter).toEqual(3);
});

Monday, April 26, 2021

Testing the value of promises in Javascript without using Promise

Source: https://stackoverflow.com/a/54029876/4456532

Use case: when testing a code in Jest that already uses promises, I didn't want to use Promise resolve to peek into the promise, in order to not mess up the function execution. 

My helper function:
function promiseValue(p) {
	return process.binding('util').getPromiseDetails(p)[1];
}
Test for it with Jest:
test('promiseValue', () => {
	expect(promiseValue(new Promise(()=>{}))).toEqual(undefined);
	expect(promiseValue(Promise.resolve(2))).toEqual(2);
	expect(promiseValue(Promise.reject(0))).toEqual(0);
})

Testing the state of promises in JavaScript without using Promise

Relevant posts:

Use case: I would like to test a code with Jest that already uses promises, so to not mess up the promise queue, I needed a way to peek into the promise without using Promise.resolve or Promise.race.

My helper function:
const PROMISE_STATUS = Object.freeze({
	PENDING: 'pending',
	FULFILLED: 'fulfilled',
	REJECTED: 'rejected'
})

function promiseStatus(p) {
	if (inspect(p).includes(PROMISE_STATUS.PENDING)) {
		return PROMISE_STATUS.PENDING;
	}
	else if (inspect(p).includes(PROMISE_STATUS.REJECTED)) {
		return PROMISE_STATUS.REJECTED;
	}
	else {
		return PROMISE_STATUS.FULFILLED;
	}
}
Test for it with Jest:
test('promiseStatus', () => {
	expect(promiseStatus(new Promise(()=>{}))).toEqual(PROMISE_STATUS.PENDING);
	expect(promiseStatus(Promise.resolve(2))).toEqual(PROMISE_STATUS.FULFILLED);
	expect(promiseStatus(Promise.reject(0))).toEqual(PROMISE_STATUS.REJECTED);
})

Wednesday, February 17, 2021

Bash: String manipulation

For this example string: 
my_string="example.string.with.dots"
%: Remove match from last occurrence
echo "${my_string%.*}" # --> example.string.with
%%: Remove match from first occurrence
echo "${my_string%%.*}" # --> example
#: Remove match until first occurrence
echo "${my_string#*.}" # --> string.with.dots
##: Remove match until last occurrence
echo "${my_string##*.}" # --> dots
/n/n: Substitute one occurrence of the longest possible match
echo "${my_string/*./,}" # --> ,dots
//n/n: Substitute all occurrences of the longest possible match
echo "${my_string//?./,}" # --> exampl,strin,wit,dots
source: Baeldung


Sunday, December 6, 2020

Learn the concept of HTML and CSS in 5 minutes

HTML

Lesson:
  • We can imagine a HTML document as a series of boxes
  • Inside each box, there can be text and other boxes
  • There is one root box for the visual elements: the body box
  • The official name for the boxes is element
  • There are different types of elements, that can be used for different purposes
Good to know:
  • Using the elements according to their original purpose is generally a good practice and also helps with web accessibility
Exercise: explore the Elements panel of your browser's Developer tools. (Usually opens with F12, or you can reach it via "Inspect this element" from the right click menu on any page.)

CSS

Lesson:
  • To style an HTML element, we can put the styling directly into the element's style attribute (this is called inline styling)
  • To reuse styling across elements, we must mark elements that should be styled in the same way, and also extract the styling to a common place.
  • There are multiple ways to mark an element for styling:
    • for marking unique elements: provide an id attribute
    • for marking non-unique elements or grouping elements together based on a common attribute: provide one or more class attributes
    • for more fine-tuned styling, we can use other attributes than class (like data-*) as well to identify a certain element.
    • the type of elements (like div) can also be used to identify elements for styling, but it's less flexible compared to the class attribute.
  • These marks that enable selecting an element for styling are called CSS Selectors
  • The common place the styling is extracted to can be within a style tag or a separate file
  • To sum it up: CSS is these two things: the selectors and the styling together
Good to know:
  • CSS Selectors can be used for selecting elements on a page for other purpuses as well (like testing or interactive behavior)
  • Browsers provide default styling for the HTML elements
  • Not all styles can be applied to all elements
  • Not all styles are supported by all browsers
  • In case of clashing styles:
    • The style with the more specific selector for a given element will win
    • The style that was loaded later will win
Best Practice:
  • Consider separate classes for styling the look (eg. has no margin) and behavior (eg. primary button)
  • It is not recommended to use the id attribute unless you can guarantee that there will be no other element with the same id on the whole webpage (including embedded content)
  • It is not recommended to use inline styling
  • It is recommended to use relative units for sizing (eg. rem instead of px)
Exercise: open your browser's Developer tools and take a look at the Styles tab (usually on the right side) on the Elements panel. Play around with adding or removing styles, and observe which selectors affect the elements of the page. Check out the Computed tab as well.



Note: Functional Interfaces in Java

 (source: Java SE 8 for the Really Impatient: Programming with Lambdas - 3.3. Choosing a Functional Interface)

Common Functional Interfaces

Functional Interface

Parameter Types

Return Type

Abstract Method Name

Description

Other Methods

Runnable

none

void

run

Runs an action without arguments or return value

Supplier<T>

none

T

get

Supplies a value of type T

Consumer<T>

T

void

accept

Consumes a value of type T

chain

BiConsumer<T, U>

T, U

void

accept

Consumes values of types T and U

chain

Function<T, R>

T

R

apply

A function with argument of type T

compose, andThen, identity

BiFunction<T, U, R>

T, U

R

apply

A function with arguments of types T and U

andThen

UnaryOperator<T>

T

T

apply

A unary operator on the type T

compose, andThen, identity

BinaryOperator<T>

T, T

T

apply

A binary operator on the type T

andThen

Predicate<T>

T

boolean

test

A Boolean-valued function

and, or, negate, isEqual

BiPredicate<T, U>

T, U

boolean

test

A Boolean-valued function with two arguments

and, or, negate

Functional Interfaces for Primitive Types

p, q is int, long, double; P, Q is Int, Long, Double

Functional Interface

Parameter Types

Return Type

Abstract Method Name

BooleanSupplier

none

boolean

getAsBoolean

PSupplier

none

p

getAsP

PConsumer

p

void

accept

ObjPConsumer<T>

T, p

void

accept

PFunction<T>

p

T

apply

PToQFunction

p

q

applyAsQ

ToPFunction<T>

T

p

applyAsP

ToPBiFunction<T, U>

T, U

p

applyAsP

PUnaryOperator

p

p

applyAsP

PBinaryOperator

p, p

p

applyAsP

PPredicate

p

boolean

test

Friday, May 8, 2020

How to update a fork from the original repository

(source)
(more info)

1. [preparation] Clone your fork to have a local copy:
git clone git@github.com:YOUR-USERNAME/YOUR-FORKED-REPO.git
2. [preparation] Add remote from original repository in your forked repository:
cd into/cloned/fork-repo
git remote add upstream git://github.com/ORIGINAL-DEV-USERNAME/REPO-YOU-FORKED-FROM.git
git fetch upstream
3. [routine] Update your (local) fork from original repo:
git pull upstream master
4. [routine] Update your fork's remote:
git push origin master

Wednesday, December 4, 2019

Sunday, August 11, 2019

Idea: Search for .gitconfig in parent folders until found

I manage multiple git remote accounts on a single machine and each has a different user name and user email associated with it.

Forgetting to set the name and email in a newly pulled repo has caused me trouble quite a lot.

It would be so nice, if I could just have one .gitconfig file sitting in each folder that I use to store the repos associated with a single account, and git would just read the nearest config it finds.

Sunday, July 28, 2019

Getting started with Ruby (on Rails)

Ruby language basics

  • Ruby in 20 minutes
    •  the Ruby REPL is irb
    • expressions in a string are escaped with "#{expression}"
    • print function is puts
    • functions are defined like:
      def myfun(somearg = "default")
        # do something with somearg
      end
      • arguments can have default value
      • functions can be called like:
        myfun
        myfun()
        myfun "some other arg"
        myfun("some other arg")
    • classes are defined like:
      class MyClass
        attr_accessor :somearg
        def initialize(somearg = "default")
          @somearg = somearg
        end
        # do something with @somearg anywhere in the class
      end
      • class instantiation is done with the "new" keyword:
        myclass = MyClass.new("some other arg")
      • the instance variable @somearg is private
      • the methods defined in the class are public by default
      • the attribute accessor :somearg allows to get and set the instance variable (public). Usage: myclass.somearg and myclass.somearg= "something new"
    • information about a class can be retrieved with MyClass.instance_methods(show_inherited = true)
    • iterations
      • for each loop
        @names.each do |name|
          puts "Hello #{name}!"
        end
    • conditionals
      • if-else block
        def say_bye
          if @names.nil?
            puts "..."
          elsif @names.respond_to?("join")
            # Join the list elements with commas
            puts "Goodbye #{@names.join(", ")}.  Come back soon!"
          else
            puts "Goodbye #{@names}.  Come back soon!"
          end
        end
Some additional info:

Ruby on Rails basics

rspec testing framework basics

...coming up...
in the meantime here are some resources:


My IDE choice

Monday, July 1, 2019

Developing Gitlab: Setting up a development environment with Vagrant

FYI, I abandoned the Vagrant approach in favor of a VirtualBox machine with Ubuntu.

---
Versions in use:
  • Vagrant 2.2.4
  • VirtualBox 6.0.8-130520
  • gitlab-development-kit revision e519656ac3101770622d9451f500a6c2a08c5250

Set up GDK for the first time

  1. open a CMD with "Run as Administrator" (aka. "elevated") in the gitlab-development-kit directory
  2. follow instructions for Vagrant installation:
    vagrant up --provider=virtualbox --provision

    Troubleshooting
  3. when ready:
    vagrant ssh
  4. cd gitlab-development-kit
  5. follow instructions from gdk install: Option 1 develop in fork
    gdk install gitlab_repo=https://gitlab.com/MY-FORK/gitlab-ce.git

    Troubleshooting
    • fix line endings (because the repo was cloned on a Windows host with windows line endings, but is used on linux)
      1. set the git line endings to retrieve any updates unix style for the gdk repo
        git config core.autocrlf input
      2. install dos2unix on the virtual machine
        sudo apt-get install dos2unix
      3. change the line endings in the support and bin directories
        cd support
        find ./ -type f -exec dos2unix {} \;
        cd ..
        cd bin
        find ./ -type f -exec dos2unix {} \;
        cd ..
  6. Set up tracking of original upstream repo
    support/set-gitlab-upstream
  7. run the server
    gdk run

    Troubleshooting
    • if the run command fails, stop node manually before trying again
      pkill -f node
      gdk run
      
  8. reach it from the Host on localhost:3000 and use the given credentials

Everyday commands

  • vagrant up: starts the virtual machine
  • vagrant ssh: connects the virtual machine
  • cd gitlab-development-kit
  • gdk run: starts the server
  • Ctrl+C: exits the server
  • Ctrl+D: exits the virtual machine
  • vagrant halt: stops the virtual machine
  • How to put a process into background

Restart the whole thing because something went amiss

  • clean your git repos from untracked and ignored files and directories
    • use git clean -ndx to see what would be deleted
    • use git clean -fdx to actually do the deletion
    • don't worry, repositories within a repository are not cleaned by git clean.
      there are 3 repositories in gitlab-development-kit:
      • gitlab-development-kit/gitlab
      • gitlab-development-kit/gitlab-workhorse/src/gitlab.com/gitlab-org/gitlab-workhorse
      • gitlab-development-kit/go-gitlab-shell/src/gitlab.com/gitlab-org/gitlab-shell
    • delete these directories too:
      • gitlab-development-kit/gitlab-workhorse/src
      • gitlab-development-kit/go-gitlab-shell
  • do the "Set up GDK for the first time" part again

Thursday, January 31, 2019

Updating from IntelliJ IDEA Community to Ultimate


  1. export settings from Community and import them to Ultimate
    (see https://intellij-support.jetbrains.com/hc/en-us/community/posts/206858965-Import-Settings-from-Community-Edition-into-Professional-Edition)
  2. install any plugin you had in Community that you miss, like:
    1. Lombok plugin
    2. VueJS plugin
      (see https://www.jetbrains.com/help/idea/vue-js.html)
    3. Markdown file previewer
    4. Java dependency analyzer
  3. add sytax highlighting for any non-default files you usually use, like:
    1. Groovy sytax highlighting to Jenkinsfile (Settings > Editor > File types > Groovy > add "Jenkinsfile*" )
      (see https://stackoverflow.com/questions/47796757/jenkinsfile-syntax-highlighting-in-java-project-using-intellij-idea)​


Tuesday, January 22, 2019

Updating to Maven 3.6.0

On Ubuntu 16.04

Default state:
  • installation directory is: /usr/share/maven
  • version is 3.3.9
Update to new version (following the official, and this and this linux specific tutorials):
  1. download newest version of maven (bin tar.gz) from https://maven.apache.org/download.cgi
  2. unpack to /opt folder (the folder for installing unbundled independent applications)
    sudo tar xvf apache-maven-*.tar.gz --directory /opt
  3. add environmental variables (see also: official reference):
    1. create script file to set the variables system wide:
      sudo gedit /etc/profile.d/maven.sh
    2. add these lines and save it (change jdk path if needed):
      ## Environmental variables needed by Maven
      export JAVA_HOME=/usr/lib/jvm/jdk-11.0.2/
    3. set the file executable
      sudo chmod +x /etc/profile.d/maven.sh
    4. log out and log in again to see its effect
  4. update alternatives for the specific new version (change path if needed)
    sudo update-alternatives --install "/usr/bin/mvn" "mvn" "/opt/apache-maven-3.6.0/bin/mvn" 100
    sudo update-alternatives --set mvn /opt/apache-maven-3.6.0/bin/mvn
Note: With the above approach, the M2_HOME and MAVEN_HOME environmental variables are not needed, and the bin folder doesn't have to be added to the PATH manually.

On Windows 10

  1. download newest version of maven (bin zip) from https://maven.apache.org/download.cgi
  2. unpack zip to "C:\Program Files\Apache\"
  3. add "C:\Program Files\Apache\apache-maven-3.6.0\bin\" to Path environmental variable, or replace older maven version in Path variable with new one
  4. log out and log in for the environmental variable to take effect
Note: the M2_HOME and MAVEN_HOME environmental variables are not needed.

Friday, January 18, 2019

Updating to OpenJDK 11

Download OpenJDK's JDK 11 from https://jdk.java.net/11/

On Windows (10):
  1. extract zip to Program Files/Java/
  2. change JAVA_HOME environmental variable to point to the new JDK
On Ubuntu (16.04) for version 11.0.2:
  1. unpack tar.gz to /usr/lib/jvm/ (command taken from OpenJDK installation guide)
    sudo tar xvf openjdk-11*_bin.tar.gz --directory /usr/lib/jvm/
  2. update alternatives to be able to switch between other java installations (command taken from DZone guide)
    sudo sh -c 'for bin in /usr/lib/jvm/jdk-11.0.2/bin/*; do update-alternatives --install /usr/bin/$(basename $bin) $(basename $bin) $bin 100; done'
    sudo sh -c 'for bin in /usr/lib/jvm/jdk-11.0.2/bin/*; do update-alternatives --set $(basename $bin) $bin; done'
  3. don't forget to update the JAVA_HOME environmental variable if you use maven

Thursday, January 10, 2019

CSS: frequently used selectors

Basic selectors 

Wildcard selector: selects all elements
* {}
Type selector: selects all elements of the given type
div {}
Attribute selector ([]): selects all elements that has the given attribute
[src] {}
ID selector (#): selects the element with the given ID attribute
#menu {} /* or [id="menu"] {} */
Class selector (.): selects all elements with the given class attribute
.centered {} /* or [class~="centered"] {} */

Selector grouping

selector grouping (,): enables to specify common values in one place
div, #menu, .centered {}

Selector chaining

Selector chaining
div.#menu.centered[name="Menu"]:first-child::first-letter {}

Attribute value selectors

[attribute="value"] selector: selects all elements with the specified attribute and value
[target="_blank"] {}
[attribute~="value"] selector: selects all elements whose attribute value contains the specified whole word.
[title~="flower"] {}
[attribute|="value"] selector: selects all elements whose attribute value starts with the specified value, the value being a whole word or the first part of a hyphenated word.
[class|="top"] {}
[attribute^="value"] selector: selects all elements whose attribute value begins with the specified value. (like in regex)
[class^="top"] {}
[attribute$="value"] selector: selects all elements whose attribute value ends with the specified value. (like in regex)
[class$="test"] {}
[attribute*="value"] selector: selects all elements whose attribute value contains the specified value.
[class*="te"] {}

Selector combination

Descendant selector (space): matches all elements that are descendants of a specified element
div p {}
Child selector (>): selects all elements that are the immediate children of a specified element
div > p {}
Adjacent sibling selector (+): selects all elements that are the adjacent siblings of a specified element
div + p {}
General sibling selector (~): selects all elements that are siblings of a specified element
div ~ p {}

Monday, October 8, 2018

Migrating from Dragula to Shopify/Draggable/Sortable

In the following code snippets the variable draggable refers to a Draggable.Sortable instance.
You can also check out the fiddle.

Add revertOnSpill functionality to Sortable

var outContainer;
draggable.on('drag:out:container', (e) => {
 outContainer = e.data.overContainer;
});
draggable.on('sortable:stop', (e) => {
 var newContainer = e.data.newContainer;
  var spill = outContainer && outContainer === newContainer;
  if (spill) {
    var oldContainer = e.data.oldContainer;
    var oldContainerChildren = draggable.getDraggableElementsForContainer(oldContainer);
    var emptyOldContainer = !oldContainerChildren.length;
    var source = e.data.dragEvent.data.source;
    if (emptyOldContainer) {
     oldContainer.appendChild(source);
    } else {
      var oldIndex = e.data.oldIndex;
     oldContainer.insertBefore(source, oldContainer.children[oldIndex]);
    }    
  }
});

Figuring out Dragula library usage

The library in use: https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.js
plus its CSS: https://cdnjs.cloudflare.com/ajax/libs/dragula/3.7.2/dragula.css

Default configuration

HTML body content

<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>

JavaScript content

var containers = Array.prototype.slice.call(document.querySelectorAll('.container'));
var drake = window.dragula(containers);

How it looks like

With its CSSWithout its CSS
See the fiddle for yourself!

Figuring out Shopify/Draggable/Sortable library usage

The library in use: https://cdn.jsdelivr.net/npm/@shopify/draggable@1.0.0-beta.8/lib/draggable.bundle.js

Default configuration

The following plugins come with Draggable by default:
  • Announcement - announcing draggable events for a screenreader.
  • Focusable - adds tabindex to all draggable and container elements and thus makes them focusable
  • Mirror - creates a similar element to the original that will follow the cursor
  • Scrollable - scrolls the container while dragging when container edge is reached
Sortable is built on top of Draggable, so it has also all of this.

HTML body content

<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>
<ul class="container">
<li class="draggable">1</li>
<li class="draggable">2</li>
<li class="draggable">3</li>
</ul>

JavaScript content

var containers = document.querySelectorAll('.container');
var draggable = new window.Draggable.Sortable(containers, {
  draggable: '.draggable'
});

How it looks like

See the fiddle for yourself!