One feller's views on the state of everyday computer science & its application (and now, OTHER STUFF) who isn't rich enough to shell out for www.myfreakinfirst-andlast-name.com
Using 89% of the same design the blog had in 2001.
FOR ENTERTAINMENT PURPOSES ONLY!!!
Back-up your data and, when you bike,
always wear white.
As an Amazon Associate, I earn from qualifying purchases. Affiliate links in green.
Note to self: To look through your git history to find files by name (and to have the files edited in each commit listed)...
git log --all --name-only -- "**/*Base*"
Though note that it's case sensitive, and not because I used a capital B. It's always case sensitive. Looks like there are weird workaround depending on your git version, like not including a glob for filename and piping EVERYTHING to grep to figure out or using git's really unwieldy pathspec.
I mean, I already knew (if only based on the hard flattening of my own reputation graph) that StackOverlow was dead, but sheesh. Today I learned...
Prompt: how to mark xunit test as do not run but still allow test to be debugged
xUnit v3 introduces an [Explicit] property on the [Fact] and [Theory] attributes specifically for this purpose. Explicit tests are not run by default unless specifically requested via a runner command line switch or by an IDE gesture (like right-clicking and running an individual test).
... or, later...
Apply a Trait: Ensure your tests are decorated with the [Trait] attribute, for example:
[Trait("Category", "Integration")]
Enter Filter Syntax: In the search box at the top of the Test Explorer, type the exclusion filter using the following format:
-Trait:"Category [Integration]"
That second, in my case, does, in fact, work well.
Why bother with clicking into SO to search for a good answer when I can have a conversation with AI and prompt my way to perfection?
I posted to Mastodon a while back that anyone who thinks we ยญaren't going to have humanoid robots sooner than later is missing the boat. Like different sized railroad gauges determining the size of the trains that use them, or that cars are roughly horse-buggy-sized, or that mobile homes are constrained by the width of our interstate lanes, or... or any other practical standard -- our world is largely human-sized! Not just that chair, but doors, tools, the things that tools work on (eg, nuts and bolts), the cabin of trucks/tractors/fire buckets, If you want to plug a multi-use device into something that can also be used by a human, you should have (among your stable) a humanish-sized model of your robot.
Same for LLMs! If you want "compute" to mesh well with humans, you need to create an interface that we already use if you're aiming for quick adoption. Keyboards & mice are good examples -- human hands are insanely dexterous if the keys and devices fit them. And poof, I have an 123 key (give or take) device to create code.
And so, no surprise, we quickly went from 0s and 1s to hex to assembler to coding languages that are decent compromises between written language and charted logic.
But that's for minutiae in code. If we want to describe at a higher level, we talk about it!
You get the point. Anyone who bet against LLMs better not use language themselves either, or they only have themselves to blame.
It's neat that you can program logic on most any computer that boots nowadays. Maybe not crazy UI libraries, but wrap properly factored middleware logic in a console interface or just write some unit tests and you can get a lot done without much compute at all.
What's important in a working laptop?
I used to look for stuff like replaceable batteries, but with USB-C power packs, that's not an issue if it has a USB-C port that allows power. Now my list is, give or take...
Power & input in, video out via one port
Expandable RAM
Replaceable M.2 SSD
Fingerprint reader (so I'm not typing PINs and passwords in public)
Fits in my laptop bag (a nebulous requirement for the reader, I realize)
A backlight for the keyboard (any color is fine)
Nice to haves:
A good keyboard (usually I'm plugged to a dock, but sometimes laptops on the lap (or coffeeshop table) are nice)
Better than 1920x1080 screen
Enough nits to see the screen outside
A TrackPoint nubbin'
My old ThinkPad P51, a serious monster that had a high-def screen and USB-C video output, finally gave up the Windows 11 ghost. The version I'd gotten (used) was, at one point, good enough to run Windows 11, but then was taken off the list! At some point, that was actually enforced and it stopped updating, no security updates, nothing.
I got a few more months out of Insider Preview, but then my keyboard and mouse died due to drivers, I spent hours debugging, and I finally gave up.
So back in the market. In the meanwhile, I've been using a gaming laptop from 2021 as my dedicated workstation in the home office (and at coworking a little), a super cheap ThinkPad E490s for mobile dev (coffeehouses, trips, and coworking), and my M1 MacBook Air when I need to macOS.
These museum pieces do well, and I've never regretted buying gaming laptops, as their CPUs alone give them years of headroom as development boxes.
But as it's time to replace both the high-end (gaming laptop is old) and the mobile workstation (P51 is dead), I am stressing too much about processors. I usually check out PassMark scores to get an idea of how fast they are. I don't know if it's accurate or useful at all, but it seems to give a pretty good relative number.
Unfortunately I keep forgetting what my current boxes' scores are for comparison. So hard right on this post's topic as we swerve into "note to self" land and record them. (I have a vague recollection I've done this before. Apologies.)
Box
CPU
Single-thread
Multithread
ThinkPad P51
i7-7820HX
2115
7185
ThinkPad E590s
i5-8265U
2019
5810
IdeaPad Gaming (3? from 2021)
Ryzen 5 4600H
2416
14178
M1 MacBook Air
M1
3678
14145
Lenovo LOQ 15 (2025)
i7-13650HX
3756
30479
ThinkPad E14 Gen 7
Ryzen 7 250
3761
25508
The last two are ones I'm looking at now. LOQ is for sale now for $850, and the E14 as I'd want it is $823.65.
Getting back on topic, the neat part is that the E590s I got off of eBay for under $100 a while back and inserted some extra RAM into has been my mobile box for a few months. It's occasionally a little slow, but for logic work it's... just fine.
If you've got $100, internet, and someplace to plug in with the ability and drive to learn to develop software, you've got a livelihood.
public class Service : IService
{
public string GetData(int value)
{
return string.Format("You entered: {0}", value);
}
// ...
}
And all that's in Service.svc is <%@ ServiceHost Language="C#" Debug="true" Service="Service" CodeBehind="~/App_Code/Service.cs" %>
Again, this is the stock WCF project so far. Not overly fancy.
I wanted to be able to hit the stock endpoint from a WCF project in Visual Studio without using a WCF client. Getting to the WSDL file was easy: You can steal that from the page the WCF Test Client refers to if nothing else.
To test this service, you will need to create a client and use it to call the service. You can do this using the svcutil.exe tool from the command line with the following syntax:
What you want to be able to do to invoke a service endpoint directly is, of course, find its URL. In my case, with a little help from SoapUI, I figured out it was the same URL that started up initially:
http://localhost:60817/Service.svc
The first key is that you have to set up two headers to the request:
Content-Type
In my case, text/xml
SOAPAction
So for me, based on the WSDL above, that's http://tempuri.org/IService/GetData for the GetData action.
But you seem to have to send it a body in a POST -- GET gives you that stock opening page again. (Since GET and POST are REST conventions, I half-way expected the WCF service not to care, especially since we know GET can have a body now.)
I am not yet going to claim I know how the WSDL tells you what your input parameters should have looked like.
Again, do this all in a Postman POST request with a body that's raw with XML as its type and you're golden. Change tem:value to another int to see more insanely interesting messages like...
Looking into updating a slew of VB.NET API to C# for a client, and figured I should brush off my VB.NET before getting too serious.
And I figured that means I should make a console app, connect to a sample API URI at JsonPlaceholder, grab a collection of entities, serialize them, loop through, and access something on each individual entity instance. That's half (or more) of what any API does, right?
Oh wow. Slightly larger rabbit hole than I figured.
First, you can't make the top-level Main sub Async in VB.NET. DotNetPerls makes it sound like you can wait out an Async Sub from the Sub Main, but you can't.
Subs should not be async. Event handlers are the only exception to that rule. You awaitย Taskย which can only be returned from aย Function. If the intention is to make that interface async then all the members need to be functions that return aย Taskย or its derivative.
Can confirm.
The right answer is to set up an awaiter in a synchronous [sic] Sub Main.
You won't be able to do top-levelย Awaitย in a console program. You can still make it work (preserving theย Asyncย onย LoadData) with the following changes:
Change the signature ofย LoadDataย toย Private Async Function LoadData() As Task
Change the call inย Mainย toย LoadData.GetAwaiter().GetResult()
It is somewhat amazing how few upvotes these have gotten at SO. Aka "VB.NET was never super popular, relatively speaking [to C#], was it?"
Here's some working code.
Imports Newtonsoft.Json
Module Program
Public Sub Main()
Console.WriteLine("Hello World!")
DoStuff.GetAwaiter().GetResult()
Console.WriteLine("Done")
End Sub
Private Async Function DoStuff() As Task
Dim httpClient As New Net.Http.HttpClient
httpClient.BaseAddress = New Uri("https://jsonplaceholder.typicode.com/")
Try
Console.WriteLine("Starting")
' BaseAddress is strangely draconian:
' https://stackoverflow.com/a/23438417/1028230
'Dim result1 As String = Await webClient.GetStringAsync("https://jsonplaceholder.typicode.com/users/1/todos") ' if you don't use BaseAddress
Dim result1 As String = Await httpClient.GetStringAsync("users/1/todos")
Console.WriteLine("Ending")
Dim allTodos = JsonConvert.DeserializeObject(Of IEnumerable(Of Todo))(result1)
For Each element In allTodos
Console.Write(element.Id & "--")
Next
Catch ex As Exception
Console.WriteLine(ex.Message)
End Try
End Function
End Module
and the Todo class looks like this:
Public Class Todo
'{
' "userId": 1,
' "id": 1,
' "title": "delectus aut autem",
' "completed": false
'},
Public Property UserId As Integer
Public Property Id As Integer
Public Property Title As String
Public Property Completed As Boolean
End Class
As I think I've mentioned before, a prof told me in college, "They're all different dialects of the same language." Or as a buddy on the ultimate team said, "It's all zeroes and ones." Nothing fancy going on here, but it does take a while to get your ears tuned to the new (ancient?) dialect.
So here's a quick bit of play with the Stripe API using .NET. This looks up information according to a "Checkout Session" id, which is what you'll get sent if you set up a "After Payment" processor URL, which is what I'm doing -- for now in an Azure Function, just to be kewl.
(No, you shouldn't use hard-coded paths in your code. No, you shouldn't pass around anonymous classed entities in C#. You're right, there's one instance of insanely not-DRY-ness in this code that almost bugs me. And I hate kludging ! after a nullable and would refactor that. This is pretty serious "proof of concept" territory here.)
Steps:
Create a .NET console app.
For bonus points, use the newer template without a Main method version, I guess. I got this and was too lazy to start over.
Add stripe.net to your project via your preferred NuGet manager.
Get your Stripe secret key and put it in C:\temp\stripe.key
Paste in the below junk, wiping out the Main method that's in there by default.
static void Main(string[] args)
{
Console.WriteLine("Hello, World!");
StripeConfiguration.ApiKey = File.ReadAllText(@"C:\temp\stripe.key").Trim();
Service = new Stripe.Checkout.SessionService();
var csId = "cs_test_a1RRCS1VWdOwOwvpJfdZCtjb9jSVOxp9ZhQibAI1Hv3dsvi6BfCPIWwdZP";
var info = LookupStripeCheckoutSession(csId);
Console.WriteLine(JsonConvert.SerializeObject(info, Formatting.Indented));
File.WriteAllText(
@"C:\temp\importantStuff.txt",
JsonConvert.SerializeObject(info, Formatting.Indented)
);
}
// Stripe Fees:
// https://stripe.com/pricing
// "Starts at 2.9% + 30ยข [per successful transaction for domestic cards]"
// Fudge.
public static object LookupStripeCheckoutSession(string checkoutSessionId)
{
Session session = Service!.Get(checkoutSessionId);
if (!session.Metadata.TryGetValue("appId", out var appId))
{
throw new MissingFieldException("Checkout session does not contain a product id");
}
System.IO.File.WriteAllText(
@"C:\temp\session.txt",
JsonConvert.SerializeObject(session, Formatting.Indented)
);
return new {
CheckoutSessionId = session.Id,
RufworkAppId = appId,
session.AmountTotal,
session.Created,
session.PaymentStatus, // One of: <c>no_payment_required</c>, <c>paid</c>, or <c>unpaid</c>.
// Note that TotalDetails (as with any child object brought over)
// has its own json settings, so it'll, eg, serialize to snake case
// if you don't munge it.
session.TotalDetails,
session.CustomerDetails.Email,
};
}
I'd been thinking about having a $3/year [sic] subscription for an extension. After a test, I learned what the comments, above, mention: That'll cost me ($3 x .029 + 30ยข somehow equals...) 41ยข [???] per transaction.
Okay, apparently there's a "Usage fee" in addition to the charge?
Stripe allows you to perform end-to-end financial transactions with its suite of integrated offerings:
Stripe Billing - This lets you create recurring subscriptions and invoices. Pricing starts at 2.9% + 30๏ฟ for every successful transaction.
Usage pricing for pay-as-you-go is 0.7% of billing volume.
If you need a Custom billing domain it is $10 per month.
Stripe Invoicing - Allows you to invoice upto 25 customers for free every month. Thereafter an overage of 0.4% is applied per invoice paid.
Stripe Tax - Offers you tools for tax calculation priced at 0.5% for each transaction.
Stripe Atlas - Assists you with setting up a company at a one-time setup fee of $500.
Stripe Sigma - Offers reporting and analytics using SQL. Pricing starts at 2๏ฟ per transaction and includes a $10 fee towards infrastructure. (emphasis mine -mfn)
Billing.ย For businesses with a subscription-based or usage-based model, Stripe enables automated billing on a pay-as-you-go or monthly payment basis. Fees start at 0.7% of the billing volume or $620 per month for a one-year contract.
Cool cool. 13 2/3% of $3. Just below Apple's small dev 15%.
Good times. Least the API is simple. More to come.
Debit card purchases in the US are more extreme: there are no returns on interchange fees on any US debit card transaction.
If your business often processes refunds shortly after a payment, you can combat these potential lost fees by leaving the transaction authorization open rather than settling the sale right away. This is possible because you only pay interchange fees once a transaction is settled. If you leave the authorization open and a customer makes a return, you can simply reverse the authorization and avoid losing any extra interchange fees (since you never paid those fees to begin with).
For example, if you captured and settled a $100 debit card transaction and a customer requested a return, you could lose $0.42. However, if you had left the authorization open, you could only lose slightly less than $0.04.
You can generally leave an authorization open for up to two days before you pay additional fees, so this approach is most relevant for industries with the immediate delivery of goods (such as food delivery services).
@Cthutu I wish "It's like the difference of [declaring] an address vsย [commanding] directions. The address is useful no matter you are. Whereas directions are invalid if you start somewhere else," was the selected answer all by itself.ย ;^)ย
Taken from MSDN's page on InvalidOperationException: "InvalidOperationException is used in cases when the failure to invoke a method is caused by reasons other than invalid arguments."ย
I often forget what the "right" exception is to throw when it's not an argument issue -- and linters are getting better at reminding me not to be lazy and to stop using Exception with no subtype. "Code is evidence of the beliefs of its authors" after all.
I suppose InvalidOperationException is as good a fallback as any.
Okay, I've had this open in drafts too long. I think it's got most of the info I wanted, so let's cut it loose for when I need it in the future.
I often take a different laptop with me when I'm travelling than whatever the "prime" development box is for a project, often to ensure I don't lose sensitive information if the laptop "disappears" while I'm out. When doing this, I usually copy the folder I'm working in, remotes (so personal access tokens, VPN setup, etc) be darned, and work from that.
The issue is often getting that work back onto the "prime" boxen. That usually means remembering how to make and apply git patches.
Look, here's the deal... ;)
If you want to copy over and preserve individual commits, you want to use "email" formatted patches. You can envision why. If you came before the time when everyone had shared remotes or if your workforce is distributed and most simply don't have remote access, it's easy to schlep around code via email. And so git has email support built-int! Though do note we're only using the format, as it carefully preserves each commit separately; we're not actually emailing anything. Unless you really want to.
On the travelling box:
Let's say I wanted the last 5 commits. I'd use this command to create an email-formatted patch file:
git format-patch -k --stdout HEAD~5 > patch.patch
Open up the text file and take a look! It's actually kinda interesting, begging for an SMTP server to send it on its way.
On the "prime" development box:
git am -3 patch.patch
Now look, if you used git apply here, it would apply EVERYTHING IN THE FILE AS A SINGLE ACTION and not commit anything. It's like rolling all the changes into a single worksession that needs to be committed. Using git apply for an email versioned patch reduces to the same operation as creating a diff with git diff and git applying it.
We DON'T want that. You have to use git am to get the email action going.
Remove this unread private field '_logger' or refactor the code to use its value.
Well, duh. We have an endpoint with no logging. When would we need to log? Probably when we're doing something more complicated than creating random 8-ball style forecasts.
So let's pretend it's more difficult, throw in a try... catch, and actually log the exception.
CS0029 Cannot implicitly convert type 'Microsoft.AspNetCore.Mvc.ActionResult<System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>>' to 'System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>'
CS0266 Cannot implicitly convert type 'Microsoft.AspNetCore.Mvc.BadRequestObjectResult' to 'System.Collections.Generic.IEnumerable<MyApp.WeatherForecast>'. An explicit conversion exists (are you missing a cast?)
Dare you to tell me what to do next. Heck, I don't know. I do know WebAPIs have been around so long there are tons of wrong answers on the net.
Let's just show one example that does work and call it a day.
Why do I need to wrap the return type with ActionResult to be able to return BadRequest REST code? I don't know man. It should implicitly cast imo into any hard type return value. I mean it's not like we're literally returning IEnumerable<WeatherForecast> in the original code. We are doing some magic behind the scenes to make that a JSON return value for our consumers. Why not do the same for any REST code convenience type too?
Anyhow, I just want to remember this trick for the next time it happens so to the blog it goes.
I've been using BBEdit as my compare tool on macOS for a few years now, but recently noticed that it keeps opening a window that's maybe 90% of my screen's width and height. That might be useful on a large monitor, but my 13" MacBook Air felt especially cramped.
Welp, to change the default size of a window in BBEdit, you apparently use the menu! Feels very OS 9-. /nostalgia
From "BBEdit > New Window Size & Location > Set Default" on ArsTechnica:
When I began my search for how to set the default location for new windows in BBEdite, I was certain that to set this required a "write defaults" from the command line.
Not so -- no need for BBEdit Expert Preferences for this ... it's in the menu:
Menubar > Window > Save Default <type of> Window
The Save Default Window command stores the position and size of the front window in BBEditโs preferences, and BBEdit will create all new windows of the same type with the stored position and size.
In my case, the "type of" is "Difference".
So, to be overly clear, first open a difference window, size and position it to taste, and then run the "Menubar > Window > Save Default Window" jive.
I'm sure I've mentioned I've been using BBEdit since the year of its birth (not sure exactly, but certainly in 1992. I still fondly remember [a few years later] using it in tandem with Transmit). I'd wandered away from BBEdit for years, using Ultra-Edit on Windows for a while, then VIm, Visual Studio, and a number of language-specific editors (sort of like (and including) PhpStorm), Coda (super briefly), Sublime Text, and now largely (and largely happily) VS Code crossplatform.
It's kinda neat to have a daily use for the "old grey lady" of text editing again and to continue not to be disappointed in its feature-set. BBEdit doesn't suck.
If you want to release a .NET MAUI app on macOS (or even iOS), I'm going to assume you want to develop at least part time there.
Visual Studio on macOS is dying, which leaves us with either using a dying app or using VSCode (which I wish everyone would write like that... VSCode. Then it'd actually be easily googleable).
Using VSCode for .NET MAUI isn't straightforward, imo, but I found a pretty danged good tutorial on it from James Montemagno, a long-time Xamarin user and MS employee.
MAUI on VSCode lacks some stuff like XAML designers, iirc, but he actually decided to use VSCode and straight-code the UI in C# for this video, which makes it perhaps the best, "How to use MAUI on macOS" tutorial out there right now.
but if I want something quick that can run anywhere [someone's on Windows], I use a batch file, and over the years, kinda like VIm, I've slowly become if not proficient then at least competent.
Here you go, create a .bat file with the following in it :
@echo off
REM Creating a Newline variable (the two blank lines are required!)
set NLM=^
set NL=^^^%NLM%%NLM%^%NLM%%NLM%
REM Example Usage:
echo There should be a newline%NL%inserted here.
echo.
pause
Wha? set NL=^^^%NLM%%NLM%^%NLM%%NLM%??? What in the world? Huh?
The caret is an escape character for the next character, or at the line end it is used as multiline character, but this is nearly the same.
At the line end it simply escapes the next character, in this case theย <Linefeed>, but there is a hidden feature, so if the escaped character is aย <LF>ย it is ignored and the next character is read and escaped, but this charater will be always escaped, even if it is also aย <LF>.
...
So you need to add an escaped linefeed into the line and that is the NL-Variable. The NL-Variable consists of only three characters. NL=^<LF><LF>ย And if this is expanded, it creates only one escapedย <LF>ย as the firstย <LF>ย after the caret will be ignored.
I mean, um, that requires Inception level cognition to understand. ๐ (Okay, actually it's pretty straightforward, but talk about a lack of discoverability.)
What's even more interesting is that dev's answer to the original question, which is this:
Like the answer of Ken, but with the use of the delayed expansion.
setlocal EnableDelayedExpansion
(set \n=^
%=Do not remove this line=%
)
echo Line1!\n!Line2
echo Works also with quotes "!\n!line2"
Aside
Okay, before I point out what I want to point out, note that %=_____=% is an inline comment format for batch.
From ss64.com, a common domain when I'm in batchland:
Batch variableย namesย can contain spaces and punctuation, we can take advantage of this fact by typing our comment as a non-existent variable. Because it doesnโt exist, this variable will expand to nothing and so will have no effect when the script is run.
n.b. This only works in batch files, not directly at the command prompt.
To be sure that we donโt accidently choose a name which is in fact a real variable, start and end the comment with "="
@Echo off
Echo This is an example of an %= Inline Comment =% in the middle of a line.
(Variable names starting with "=" are reserved for undocumented dynamicย variables. Those dynamic variables never end with "=", so by using an "=" at both the start and end of our comment, there isย noย possibility of a name clash.)
And we're back
Okay, now what I'd like to point out here is the setlocal EnableDelayedExpansion, something I think I had to use recently in a script I wrote. It's a bizarre new world.
Delayed Expansion will cause variables within a batch file to be expanded at execution time rather than at parse time[;] this option is turned on with the SETLOCAL
EnableDelayedExpansionย command.
Variable expansion means replacing a variable (e.g.ย %windir%)ย with its valueย C:\WINDOWS
By default expansion will happen just once, before each line is executed. In this case, a "line" includes bracketed expressions even if they are formatted to run over several lines.
Whenย !delayed!ย expansion is turned on, the variables will be expanded each time the line is executed, or for each loop in a FOR looping command.
And here's a decent example from that page showing how it works in practice.
@echo off SETLOCALย EnableDelayedExpansion Set "_var=first" Set "_var=second"ย & Echo %_var% !_var!
The output is
first second
Note that the above example has to be in a batch file to work properly.
Anyhow, it's an insane rabbit hole that I didn't schedule for this sprint and fell into anyhow. I'm going to cut my losses now that I've warned others who may have been as naรฏve about batch scripts as I was, oh so long (less than three weeks ๐) ago.
Oh good heavens. I guess this makes sense. The first example can be taken by itself as a brain teaser, I think.
@echo off
Setlocal
Set _html=Hello^>World
Echo %_html%
In the above, the Echo command will create a text file called 'world' - not quite what we wanted!
Got that figured out? If not, sit and think about it for a second before reading more.
Got it? Good. Clever, huh?
This is because the variable is expanded at parse time, so the last line is executing Echo Hello > World and the > character is interpreted as a redirection operator.
If we now try the same thing with EnableDelayedExpansion:
Setlocal EnableDelayedExpansion
Set _html=Hello^>World
Echo !_html!
With delayed expansion, the variable (including the > ) is only expanded at execution time so the > character is never interpreted as a redirection operator.
This makes it possible to work with HTML and XML formatted strings in a variable.
The longer I work in Windows, the more I find myself using cmd.exe. I use PowerShell plenty too, but if I want something quick that can run anywhere [someone's on Windows], I use a batch file, and over the years, kinda like VIm, I've slowly become if not proficient then at least competent.
Nearly (and maybe even over at this point) thirty years ago I had a guy wisely tell me, when I was considering buying a new Mac or Windows PC, "It's all zeros and ones." Same for script languages, mostly. It might be a pain to learn batch scripting on Windows sometimes, but there's very little you can't do if you set your mind to it.
REM https://stackoverflow.com/questions/18462169/how-to-loop-through-array-in-batch
echo off
set Arr[0]=apple
set Arr[1]=banana
set Arr[2]=cherry
set Arr[3]=donut
set "x=0"
:SymLoop
if defined Arr[%x%] (
call echo %%Arr[%x%]%%
set /a "x+=1"
GOTO :SymLoop
Clever.
While I'm at it, here's a PowerShell script I've been using to approximate grep there. I've dabbled in this problem before, but it's usually a good idea to reduce it to script instead of leaving only human-readable lessons learned:
That captures the Out-File wackiness from the previous post but also wraps Select-Stringing a file, making it easier to remember, not that it's difficult. I should fix the casing.
I always forget where this is, and had another iPhone die from a fall yesterday, which often requires the SIM card moving to my backup Android until a replacement iPhone is secured.
That site where you get sent an SMS on the new phone and Apple removes your phone number from Message's SMS black hole is this:
Fwiw -- Looks like g [in a command like :g/pattern/d] is for "global" and v [like we have here] is for "inverse" (if you believe what you read on vim.famdom).
So look, I'm at a company where much of the app is still in AngularJS. The downside is that this reminds me of a post I'd shared before that said:
If your list of things to develop is really a list of things that you wonโt have to do in a more functional environment, none of which will make you more employable elsewhereโฆ itโs time to walk away.
That's not wrong. More experience in AngularJS isn't making me more employable at this point unless I want to work in the small subset of positions that plan to continue using it well past its dead-on date. Which, again, was January of this year.
That admission aside, as I've mentioned before, there is, however, a practical advantage: Most of the questions I have when I'm developing have nearly canonical answers. I mean, have you seen StackOverflow's blog on the Next.JS conference? The blog's title is "Goodbye Webpack, hello Turbopack!". With just a little bit of exaggeration, webpack, we barely knew ye.
I was looking to get us to a React stack, and I'd initially liked this "minimal" build tool suite over at 2ality. Okay, okay, it's been over three years, but it's now kaput. Gone. Snowpack is dead. I'd heard a bit about ViteJS already a bit from my RSS sub to Shawn Wildermuth's blog, and sure enough that's what Snowpack says to use now. If I'd learned Snowpack inside and out (not that it was tough to learn, and I've been playing with it on and off for a few years), that'd be nearly wasted time now.
Look, it's fine to need to keep up a little, it's not like .NET 6 doesn't have a decent amount of new stuff (does it though?), and maybe it's just that I've been around long enough to see things change several times, but it doesn't really have to, does it? There are database admins who haven't needed to learn anything exceptional for their whole career. I mean, look, it's not like our buddy Mr. Dave hasn't kept things moving, but it ain't client-side JavaScript. He's had a chance to get good at what he does, to really drill-down and understand rdbms engines.
Okay, yes. I'm a little jealous. ๐
And if I haven't mentioned it before, I'm pretty sure it's because of conferences, YouTube, and Twitter. Everybody who wants to be a bleeding edge type can be, and they all race each other to be the new expert of something nobody else can be yet because it was just released. God made humans, but constantly evolving tech stacks made humans equal[ly clueless].
That said, I do need to write my follow-up to my intro to why we're using transpilation-free Preact. Preact really is a wonderful bridge from a hopelessly out of date client-side app to, well, at least a TypeScript-powered React refactor. Need to develop something new now but can't, for whatever reason, create all new tooling?
If you can limit support to IE 9+ or so, Preact (with hooks) is your stack. We've used Preact with Enzyme to create a tested, cradle-to-grave page with a reasonably complex UI and have it running well right smack in the middle of an AngularJS app. Set down new work in Preact now, start eating from the top with React, and when they meet in the middle, it's a very quick port.
So maybe I won't need to remember that cheat to get all directives, constants, etc out of an AngularJS site for too long. But when I forget again, at least I'll know I've got it sitting here.
Collection<T> seems like ICollection, but itโs actually a very different abstraction. We found that ICollection was not very useful. At the same time, we did not have an abstraction that represented an read/write non-indexed collection. ICollection<T> is such abstraction and you could say that ICollection does not have an exact corresponding peer in the generic world; IEnumerable<T> is the closest.
This means LINQ works on the latter but not the former. Luckily ICollection (no T) is still enumerable, so foreach away! (Are there considerations about iteration, like can you do it twice? Maybe? I haven't run into one yet.)
That said, DictionaryBase's entry gives us a nice example implementation so we don't have to dig up a concrete implementation somewhere else, like Exception.Data (which is what created this rabbit hole for me).
Now our generic Dictionary<TKey,TValue>can use LINQ's Where to filter.
var filtered = newStyleDictionary.Where(x => x.Value.Contains("c"));
foreach (var kvp in filtered)
{
Console.WriteLine($"{kvp.Key}: {kvp.Value}");
}
Here endeth the lesson.
And begineth another -- quick update from this SO answer:
The most painful difference is that for the generic Dictionary<string, string>, when I call this[key] for a key that does not exist, I get an exception stating the key does not exist.
On a DictionaryBase I get back null with no exception. This was painful in my case because the system was full of code that did not check that the dictionary ContainsKey before trying to get the keys value. It was made more painful by me assuming I messed something up with serialization.
The postings on this site are [usually] my own and do not necessarily reflect the views of any employer, past or present, or other entity. About Our Author