My Vimrc
April 05, 2023
This is my simple vimrc. No extensions needed. Also works with VsVim for visual studio.
" Ctrl-d and Ctrl-u but goes one less line and centers view
nnoremap <C-d> <C-d>kzz
nnoremap <C-u> <C-u>jzz
" Provide similar functionality to Visual Studio's Alt+Up and Alt+Down.
" In single line and visual block modes
nnoremap <A-k> ddkP
vnoremap <A-k> xkP`[V`]
nnoremap <A-j> ddp
vnoremap <A-j> xp`[V`]
Windows Terminal PowerShell Tips
June 29, 2022
Profile Specific Settings in profile.ps1
There is an environment variable that holds the ID of the current Windows Terminal profile.
$env:WT_PROFILE_ID
I use it so that I have PSReadLine set to ListView for all but 1 profile. I have a terminal profile for presentations and find ListView to be potentially distracting.
if($env:WT_PROFILE_ID -ne "{4da51a9a-dad3-44bb-a925-38956366f573}"){
Set-PSReadLineOption -PredictionViewStyle ListView
}
To see the profile id, you can view the JSON settings file for Windows PowerShell and take the guid for the profile you want (including {}). You can also run $env:WT_PROFILE_ID to see the id of the current profile.
C:\> $env:WT_PROFILE_ID
{4da51a9a-dad3-44bb-a925-38956366f573}
C:\>
Creating Custom Set-Location Aliases.
Sometimes you find yourself going to the same directories over and over again. It becomes helpful to have a quick way to navigate to those directories. A convenient way is to set a hashtable with the alias name and corresponding path.
$locationAliases = @{
repos = "C:\Users\UserName\source\repos";
proj = "C:\Git\Projects";
cd2 = "..\..\";
cd3 = "..\..\..\";
}
We can then make a single function making use of the $MyInvocation
Automatic variable. This way we can determine which alias was used to call the function.
function Set-Location-Custom{
&Set-Location $locationAliases[$MyInvocation.InvocationName]
}
Then just loop through the keys in the hashtable and create the aliases.
foreach($key in $locationAliases.Keys){
Set-Alias -Name $key -Option AllScope -Value Set-Location-Custom
}
Some more advanced functionality
Say you want these to work more like the standard Set-Location
command. With tab autocompletion to directories relative to the specific location. In that case we just modify the Set-Location-Custom
command to take a parameter for Path
.
function Set-Location-Custom {
param(
[String]$Path
)
process{
$fullPath = Join-Path -Path "$($locationAliases[$MyInvocation.InvocationName])\" -ChildPath $Path;
if (-not (Test-Path -Path $fullPath -PathType Container)){
throw "Invalid path: $fullPath"
}
Set-Location $fullPath
}
}
Then we just need to register a custom argument completer to give us the autocompletion we need.
Register-ArgumentCompleter -CommandName Set-Location-Custom -ParameterName Path -ScriptBlock {
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
# Set-Location-Custom is always called with an alias. We need to get that alias and find the coresponding directory.
$path = $locationAliases[$commandAst.CommandElements[0].Extent.Text]
# Change any forwardslashes to backslashes
$wordToComplete = $wordToComplete -replace '/','\'
if (-not $wordToComplete){
# No input, so list all directories in the $path directory
$directories = Get-ChildItem -Path $path -Directory | ForEach-Object { $_.Name + '\'}
} elseif ($wordToComplete -notlike "*\*"){
# There is input that does not contain any backslashes, so list directories that match the input in the $path directory
$directories = Get-ChildItem -Path $path -Directory -Filter "$wordToComplete*" | ForEach-Object { $_.Name + '\'}
} else{
# The input contains at least one backslash, so list directories that match <input after last backslash>, in the directory ($path + <input through last backslash>)
$prefix, $suffix = $wordToComplete -split '\\(?=[^\\]+$)', 2 # split input into two parts at the last backslash
$prefix = $prefix.TrimEnd('\') + '\' # Depending on input, $prefix may or may not have an ending backslash. This makes sure there always is one and only one
$path = Join-Path -Path $path -ChildPath $prefix
$directories = Get-ChildItem -Path $path -Directory -Filter "$suffix*" | ForEach-Object { "$prefix$($_.Name)\" }
}
return $directories
}
Removing the Windows Message When You Launch a PowerShell.
If you want to remove this message for a cleaner look:
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
Install the latest PowerShell for new features and improvements! https://aka.ms/PSWindows
Loading personal and system profiles took 586ms.
Go into the Windows Terminal Settings. Select the PowerShell profile you want to remove the message from. Edit the "Command line" setting. Add /nologo
after powershell.exe
. Mine looks like this.
powershell.exe /nologo
My PowerShell Profile
October 19, 2021
This is my PowerShell profile (as of 2021). It is based on Scott Hanselmans just minus a few things.
Modules to Install
Install-Module PowershellGet -Force -Scope CurrentUser -AllowClobber
Install-Module posh-git -Scope CurrentUser
Install-Module PSReadLine -AllowPrerelease -Scope CurrentUser -Force
Install-Module -Name Terminal-Icons -Repository PSGallery -Scope CurrentUser
My PowerShell_profile.ps1:
Import-Module -Name posh-git
Import-Module -Name Terminal-Icons
Import-Module -Name PSReadLine
# Chocolatey profile
$ChocolateyProfile = "$env:ChocolateyInstall\helpers\chocolateyProfile.psm1"
if (Test-Path($ChocolateyProfile)) {
Import-Module "$ChocolateyProfile"
}
Set-PSReadLineOption -PredictionSource History
Set-PSReadLineOption -PredictionViewStyle ListView
Set-PSReadLineOption -EditMode Windows
function Get-ChildItem-Formatted{
param(
[String[]] $Path,
[String] $Filter,
[String[]] $Include,
[String[]] $Exclude,
[String[]] $LiteralPath,
[uint32] $Depth,
[switch] $Directory,
[switch] $File,
[switch] $FollowSymlink,
[Alias("a")][switch] $Force,
[switch] $Hidden,
[switch] $Name,
[switch] $ReadOnly,
[switch] $Recurse,
[switch] $System
)
process{
Get-ChildItem @PSBoundParameters @args | Format-Wide
}
}
#ls -> Get-ChildItem
Set-Alias -Name ls -Option AllScope -Value Get-ChildItem-Formatted
Tips on Using React with Electron
March 18, 2020
Following this guide to start off with a light boilerplate: https://medium.com/@impaachu/how-to-build-a-react-based-electron-app-d0f27413f17f
This boilerplate is easy to work with and utilizes create-react-app to start the project. So those familiar with create-react-app should be at home with that guide. However, there are some issues that you may run into while doing various things in the project.
One of the first things is using node packages in your react components. A good way to accomplish this is to do this:
In the electron.js
file in the public folder make sure that your browser window object has a preload file:
mainWindow = new BrowserWindow({
width: 1100,
height: 730,
'minWidth': 500,
'minHeight': 500,
webPreferences: {
nodeIntegration: true,
preload: __dirname + '/preload.js'
},
frame: isDev
});
In your preload.js
file add the node packages to the window object like so:
window.electron = window.require('electron');
window.fs = window.require('fs');
window.path = window.require('path');
This is a potential use case in a component to make a custom window frame:
import React from "react";
import Logo from "../../resources/logo128x128.png"
const remote = window.electron.remote;
const WindowFrame = () => {
const app = remote.getCurrentWindow();
const minimize = () => {
app.minimize();
};
const toggleMaximize = () => {
app.isMaximized() ? app.unmaximize() : app.maximize();
};
const exit = () => {
app.close();
};
return (
<div className="frame">
<img className="frameImage" src={Logo} alt=""/>
<span className="frameTitle">Test Frame Title</span>
<button
className="fas fa-times frameButton exitButton"
onClick={() => exit()}
/>
<button
className="far fa-square frameButton"
onClick={() => toggleMaximize()}
/>
<button className="fas fa-minus frameButton" onClick={() => minimize()} />
</div>
);
};
export default WindowFrame;
Another problem you may run across is calling web API’s in development mode. This was a fairly simple solution that required a small change to the electron.js
file.
We just check if we are in development mode and if we are we disable webSecurity. Now you don’t need to build your application to test it.
const isDev = require("electron-is-dev");
mainWindow = new BrowserWindow({
width: 1100,
height: 730,
'minWidth': 500,
'minHeight': 500,
webPreferences: {
nodeIntegration: true,
webSecurity: !isDev,
preload: __dirname + '/preload.js'
},
frame: isDev
});
Using External API's in a Testable Way with C#
November 19, 2019
Welcome
In this post I will show how to write unit tests for your code when you need to call an external API. I, like usual, will be using the Internet Chuck Norris Database API from http://www.icndb.com/api/ for my example. This example covers using Moq, a .Net Nuget package for creating Mock Objects for your unit tests.
The Problem
If you are calling out to an external API in your code, you may run into the risk where the Unit tests for your code rely on an API that someone else built being up.
This would be terrible because now your unit tests could be slow and even worse be inconsistent.
Setting Up
I will be using the Refit and Refit.HttpClientFactory Nuget packages to call the external API, but the principles will apply either way.
Here is the setup for Refit if you're interested. First, we make our model classes, then we make an interface to represent the API call.
public class Joke
{
public string Type { get; set; }
public JokeContents Value { get; set; }
}
public class JokeContents
{
public int Id { get; set; }
public string Joke { get; set; }
}
public interface IChuckNorrisApi
{
[Get("/jokes/random/")]
Task<Joke> GetRandomJokeAsync();
}
The start of the Solution
We need to abstract our API call behind a service because the API call itself isn’t something we can test. But we want to be able to Mock it in our tests.
First, we need to create an interface that we will be able to create a mock object of later.
public interface IExternalAPIService
{
Task<Joke> GetJokeAsync();
}
Now we can create an implementation of the interface
public class ExternalAPIService
{
public async Task<Joke> GetJokeAsync()
{
var chuckNorrisApi = RestService.For<IChuckNorrisApi>("http://api.icndb.com");
return await chuckNorrisApi.GetRandomJokeAsync();
}
}
Because we want our program to be reliable even if the Chuck Norris database is down, we can do something like this.
public class ExternalAPIService : IExternalAPIService
{
public async Task<Joke> GetJokeAsync()
{
try
{
var chuckNorrisApi = RestService.For<IChuckNorrisApi>("http://api.icndb.com");
return await chuckNorrisApi.GetRandomJokeAsync();
}
catch
{
return new Joke { Type = "Failure", Value = new JokeContents { Id = -1, Joke = "Chuck Norris has counted to infinity. Twice." } };
}
}
}
You can define what you want it to return whenever it fails to be whatever you want. In this case we are making sure we always get a Joke even if an exception is thrown.
Our Application
The Chuck Norris API by default returns “ ” as " " (you can add ?escape=javascript with this API to make it return quotes as normal, but let’s pretend you can’t for the sake of this post).
In our application we’ll want to fix the quotes before returning the joke to the user.
Now let’s create an outline for our own service
public class FormattedJokeService
{
public async Task<string> GetFormattedJokeAsync()
{
throw new NotImplementedException();
}
}
We will also need to add a constructor to this class to Inject the ExternalAPIService
public FormattedJokeService(IExternalAPIService externalAPIService)
{
_externalAPIService = externalAPIService;
}
Writing our Unit Test
In a test project we will create a unit test for this function.
Here’s the setup.
private Mock<IExternalAPIService> mockExternalApiService;
private Joke jokeWithQuotes;
[SetUp]
public void Setup()
{
mockExternalApiService = new Mock<IExternalAPIService>();
jokeWithQuotes = new Joke { Value = new JokeContents { Joke = ""This is a test"" } };
}
And the test for our function.
[Test]
public async Task GetFormattedJokeFixesQuotes()
{
mockExternalApiService.Setup(eas => eas.GetJokeAsync()).ReturnsAsync(jokeWithQuotes);
var formattedJokeService = new FormattedJokeService(mockExternalApiService.Object);
var expected = "\"This is a test\"";
var actual = await formattedJokeService.GetFormattedJokeAsync();
Assert.AreEqual(expected, actual);
}
Implementing the Code
Now that we have our test, we can write the function
public async Task<string> GetFormattedJokeAsync()
{
var joke = await _externalAPIService.GetJokeAsync();
return joke.Value.Joke.Replace(""","\"");
}
And there we have it. We have a tested application that doesn’t rely on the external API.
Closing Thoughts
It is easy to consume external API’s in our applications and still write our code in such a way that it is testable and that our tests are consistent.