Wednesday, July 29, 2020

Commas have high precedence in PowerShell even when intended for array subscripting

I recently received a confusing error message from PowerShell regarding an expression like this:

$Some2DLongArray[$SomeIndex + 1, 0]

Method invocation failed for op_Addition because Object[] has no such method. Indeed, there is no operator that will add a single value to an array (from the left at least). But there shouldn't have been any object array involved here. Apparently PowerShell interprets the comma as delimiting elements of the array literal 1, 0 then tries to add that array to $SomeIndex. To get my expected interpretation of using $SomeIndex + 1 as the first dimension's index and 0 as the second's I had to enclose $SomeIndex + 1 in parentheses.

Tuesday, July 28, 2020

Windows feature updates may remove support for old file shares

I recently updated a Windows 10 machine with the newest feature update. Afterward it was unable to open a folder shared from a Windows 95 VM. Opening the share produced an error about SMB 1.0 no longer being supported. Fortunately it is possible to reenable support for the old protocol by installing an optional component. In the Windows Features dialog (run optionalfeatures), enabling "SMB 1.0/CIFS File Sharing Support" or specifically "SMB 1.0/CIFS Client" made the share usable again.

Friday, July 17, 2020

The Woocommerce gettext hook receives already-escaped HTML

Today I needed to change the "Billing & Shipping" label on a Woocommerce checkout page. Conveniently there is a gettext hook to which functions can be attached to postprocess the results of translation. This thread has an example, but just adjusting the target text did not work. It turns out that the first $translated_text parameter receives HTML-escaped text, so I actually needed to compare against "Billing & Shipping".

Monday, July 13, 2020

Creating tables with SQL in phpPgAdmin

I'm working on setting up a PostgreSQL database hosted on Namecheap's shared hosting, which seems to only allow access through the phpPgAdmin web app. I'm testing things in a local PostgreSQL instance with a different interface and copying the table definitions to the shared server as SQL "create" commands when ready. I first tried pasting it into the box on the "SQL" tab of the database object, but phpPgAdmin seems to wrap the command in a way that makes it invalid. However, there is a small "SQL" link in the far upper right of the page that produces a new window. Arbitrary commands can be successfully run from there.

Saturday, July 11, 2020

Invoking an object as a function in Kotlin JS

Some JavaScript libraries provide function objects with additional members. Expressing these as Kotlin objects in external class declarations is somewhat difficult. They can be given an operator fun invoke and called like a function using method call syntax, but the compiler will produce a call to an invoke method that doesn't exist. That could be re-@JsNamed to call and appear to work in some cases, but it's not really the right thing to do since call has the special behavior of setting this.

The expected approach seems to be to drop to weak typing by reinterpreting the object as a function. The class can be given an inline extension operator invoke along these lines:

inline operator fun Express.invoke(): ExpressApp {
    return this.unsafeCast<() -> ExpressApp>()()
}

Alternatively the receiver can be treated as the dynamic type:

inline operator fun Express.invoke(): ExpressApp {
    return this.asDynamic()() as ExpressApp
}

Thursday, July 9, 2020

PostgreSQL users may need to be added to databases manually on Namecheap

I recently set up a PostgreSQL database on Namecheap's shared hosting platform. The cPanel interface allowed me to create the database and a user for it, but even after adding the user to the database my app was denied access to the tables. It seems that cPanel didn't actually grant the new user any permissions on the database. To do that, I had to go into phpPgAdmin, go to the database, switch to the Privileges section, and use the Grant link to grant the user privileges.

Wednesday, July 8, 2020

Watch out for returns inside inline functions

Blocks passed to Kotlin inline functions can return from the function calling the inline one - this is one of the advantages of inline functions. However, the inline function should be very careful that it's safe to return successfully (not just exceptionally) after calling a block passed to it. Today I was stumped for a bit on a database transaction wrapper function that mysteriously failed to issue the "commit" command after running the block. The block passed to it contained a plain return statement, which exits the entire enclosing function, when I meant to return a value from the block. The immediate fix was to change the return to return@theInlineFunction, but to avoid the issue in the future I adjusted the inline function to issue the commit in a "finally" block after checking that the "catch" block was not triggered by a crash in the block.

Monday, July 6, 2020

Running Kotlin Node JS projects outside Gradle

It's pretty easy to set up a Kotlin JS project targeting Node JS using the Kotlin JS Gradle plugin, but it's less easy to figure out how to actually run it on a server. The build/js folder contains the compiled JavaScript code, but accompanied by a ton of unnecessary stuff like copies of artifacts that are available on NPM. The compiled version of just the project under development is in a subdirectory of build/js/packages named for the project. Though that folder contains a package.json file that npm install can use, there is no clear way to start the app. For it to work with environments like Passenger, I amended the build process to write an app.js file there containing only:

require('./kotlin/the-project.js'); 

Then only the directory containing that new app.js has to be deployed.

Friday, July 3, 2020

When Android Studio consistently says "default activity not found"

Today I worked on updating my Android app to the newest versions of various components. When I went to run the app, it failed with a "default activity not found" bubble. I tried File | Invalidate Caches / Restart and clearing the .idea folder - both good things to do when Android Studio gets stuck - but they did not change the error. After some stumbling around I happened to look at the Merged Manifest tab of the AndroidManifest.xml editor. The right pane showed an error about one of my dependencies having a minimum API level above that of my app. Switching to a slightly older version of that dependency fixed the problem.