7 |
8 |
9 |
10 |
11 |
19 |
20 |
--------------------------------------------------------------------------------
/docs/Training.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Training
3 | date: 2017-05-03 12:50:43
4 | permalink: training
5 | categories:
6 | - Misc
7 | excerpt: "Looking to learn something new?"
8 | ---
9 |
10 | ## Upcoming Training Engagements
11 | There are no public training engagements currently scheduled.
12 |
13 | ## Private Training?
14 | Shoot me an [email](mailto:connect@consultwithgriff.com) with a little information about your needs, and I'll respond with availability and a rate sheet.
15 |
--------------------------------------------------------------------------------
/src/components/SearchFocus.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
18 |
19 |
24 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Use the official Node.js 18 image with build tools
2 | FROM node:18-bullseye
3 |
4 | # Set the working directory in the container
5 | WORKDIR /usr/src/app
6 |
7 | # Copy package.json and package-lock.json to the working directory
8 | COPY package*.json ./
9 |
10 | # Install the dependencies
11 | RUN npm install
12 |
13 | # Copy the rest of the application code to the working directory, excluding node_modules
14 | COPY . ./
15 |
16 | # Expose the port the app runs on
17 | EXPOSE 8080
18 |
19 | # Command to run the application
20 | CMD ["npm", "run", "develop"]
21 |
22 | # Map the local "blog" directory to the container's "blog" directory at runtime
23 | VOLUME ["/usr/src/app/blog"]
24 |
--------------------------------------------------------------------------------
/src/templates/Redirect.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/Dockerfile.build:
--------------------------------------------------------------------------------
1 | # Use Node 14 which has better compatibility with Gridsome 0.7.x
2 | FROM node:14-buster
3 |
4 | # Set working directory
5 | WORKDIR /app
6 |
7 | # Copy package files (only package.json now)
8 | COPY package.json ./
9 |
10 | # Install dependencies fresh
11 | RUN npm install
12 |
13 | # Copy all source files
14 | COPY . .
15 |
16 | # Build the Gridsome site
17 | RUN npm run build
18 |
19 | # Use nginx to serve the static files
20 | FROM nginx:alpine
21 |
22 | # Copy built files from build stage
23 | COPY --from=0 /app/dist /usr/share/nginx/html
24 |
25 | # Copy nginx configuration if you have one (optional)
26 | # COPY nginx.conf /etc/nginx/nginx.conf
27 |
28 | EXPOSE 80
29 |
30 | CMD ["nginx", "-g", "daemon off;"]
--------------------------------------------------------------------------------
/.github/workflows/schedule.yml:
--------------------------------------------------------------------------------
1 | # This is a basic workflow to help you get started with Actions
2 |
3 | name: Schedule Future Posts
4 |
5 | on:
6 | pull_request:
7 | types:
8 | - opened
9 | - edited
10 | - synchronize
11 | schedule:
12 | - cron: "*/30 * * * *"
13 | jobs:
14 | merge_schedule:
15 | runs-on: ubuntu-latest
16 | steps:
17 | - uses: gr2m/merge-schedule-action@v1.x
18 | with:
19 | # Merge method to use. Possible values are merge, squash or
20 | # rebase. Default is merge.
21 | merge_method: squash
22 | # Time zone to use. Default is UTC.
23 | time_zone: "America/New_York"
24 | env:
25 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
26 |
--------------------------------------------------------------------------------
/blog/20201117-installing-net-5-for-beginners.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Installing .NET 5 for Beginners"
3 | date: 2020-11-18T00:00:00Z
4 | permalink: installing-dotnet-5-for-beginners
5 | description: "A beginner-friendly video tutorial walking through the process of installing .NET 5 SDK and creating your first Hello World application."
6 | summary: "Are you new to .NET, and you're looking at how to get started? This video is just for you!"
7 | tags:
8 | - .NET 5
9 | - Beginners
10 | - Installation
11 | - Tutorial
12 | - Hello World
13 | - Getting Started
14 | categories:
15 | - .NET
16 | ---
17 |
18 | `youtube:https://www.youtube.com/watch?v=karSxhTb_38`
19 |
20 | .NET 5 is the new hotness on street, and if you're new to .NET you might be wondering HOW do you get started? In this video, I'll walk you through downloading the .NET 5 SDK and scaffolding out your own `Hello World` example!
21 |
22 |
--------------------------------------------------------------------------------
/src/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "App",
3 | "icons": [
4 | {
5 | "src": "\/android-icon-36x36.png",
6 | "sizes": "36x36",
7 | "type": "image\/png",
8 | "density": "0.75"
9 | },
10 | {
11 | "src": "\/android-icon-48x48.png",
12 | "sizes": "48x48",
13 | "type": "image\/png",
14 | "density": "1.0"
15 | },
16 | {
17 | "src": "\/android-icon-72x72.png",
18 | "sizes": "72x72",
19 | "type": "image\/png",
20 | "density": "1.5"
21 | },
22 | {
23 | "src": "\/android-icon-96x96.png",
24 | "sizes": "96x96",
25 | "type": "image\/png",
26 | "density": "2.0"
27 | },
28 | {
29 | "src": "\/android-icon-144x144.png",
30 | "sizes": "144x144",
31 | "type": "image\/png",
32 | "density": "3.0"
33 | },
34 | {
35 | "src": "\/android-icon-192x192.png",
36 | "sizes": "192x192",
37 | "type": "image\/png",
38 | "density": "4.0"
39 | }
40 | ]
41 | }
--------------------------------------------------------------------------------
/src/templates/Documentation.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | {{ $page.documentation.title }}
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | query Documentation ($id: ID!) {
17 | documentation(id: $id) {
18 | title
19 | excerpt
20 | path
21 | }
22 | }
23 |
24 |
25 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/blog/20150828-enable-signalr-logging-with-one-simple-line.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Enable SignalR Logging with One Simple Line"
3 | date: 2015-08-28T23:26:42Z
4 | permalink: signalr-logging
5 | description: "Learn how to enable client-side logging in SignalR JavaScript applications with just one line of code to debug connection issues."
6 | summary: "How do you enable logging in SignalR?"
7 | tags:
8 | - "Development - ASP.NET"
9 | categories:
10 | - "Development - ASP.NET"
11 | ---
12 |
13 | It is easy to think that SignalR works within a black box, but if you are deploying JavaScript clients, here is an EASY trick to learning what is happening underneath the scenes.
14 |
15 | Before you start your connection, add this ONE line of code:
16 |
17 | ```javascript
18 | $.connection.hub.logging = true;
19 | $.connection.hub.start();
20 | ```
21 |
22 | Tada! You have logging in your browser console:
23 |
24 | 
--------------------------------------------------------------------------------
/blog/20200414-talking-remote-tools-for-developers-with-bret-fisher.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Talking Remote Tools for Developers with Bret Fisher"
3 | date: 2020-04-14T19:24:00Z
4 | permalink: remote-tools-with-bret-fisher-april-2020
5 | description: "Join Kevin Griffin and Bret Fisher as they discuss essential remote tools for developers and collaboration in distributed teams."
6 | summary: "Recently, I got to hang out with my friend Bret Fisher to talk about remote tools for developers."
7 | tags:
8 | - "remote work"
9 | - "developer tools"
10 | - "collaboration"
11 | - "Docker"
12 | - "DevOps"
13 | categories:
14 | - 'Community, User Groups, and Conferences'
15 | ---
16 |
17 | A couple weeks ago, I had the chance to hang out with my friend Bret Fisher on his weekly Docker and DevOps show. We chatted about tools for remote teams doing development or collaboration.
18 |
19 | Check it out below:
20 |
21 | `youtube:https://www.youtube.com/embed/sK48ZyRXAFo`
22 |
23 |
--------------------------------------------------------------------------------
/blog/20200719-building-vue-services-and-plugins.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Building Vue Services and Plugins"
3 | date: 2020-07-20T00:30:00Z
4 | permalink: vue-services-plugins
5 | description: "Thanks for attending Building Custom Vue Services and Plugins!"
6 | summary: "Thanks for attending Building Custom Vue Services and Plugins!"
7 | tags:
8 | - Vue.js
9 | - JavaScript
10 | - Plugins
11 | - Services
12 | categories:
13 | - Misc
14 | excerpt: "Thanks for attending Building Custom Vue Services and Plugins!"
15 | ---
16 |
17 | Thanks for attending Building Custom Vue.JS Services and Plugins! If you missed the talk, catch a replay at:
18 |
19 | ## Replays
20 |
21 | Here is an offering from the Vue.JS Online Meetup on July 22nd, 2020:
22 | `youtube:https://www.youtube.com/watch?v=TH9bN2nnd0s`
23 |
24 | ## Links to Resources
25 |
26 | [Download Slides](/pdfs/BuildingCustomVueJSServicesandPlugins_20200720.pdf)
27 |
28 | [Twitter](https://twitter.com/1kevgriff)
29 | [My Twitch Channel](https://www.twitch.tv/1kevgriff)
30 |
31 |
--------------------------------------------------------------------------------
/blog/20091117-enhancing-your-applications-for-windows-7.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Enhancing Your Applications For Windows 7"
3 | date: 2009-11-17T09:36:34Z
4 | permalink: enhancing-your-applications-for-windows-7
5 | description: "Learn how to enhance your applications for Windows 7 using the Windows 7 API Code Pack and its powerful features."
6 | summary: "Back in 2009, I wrote an article on building applications in Windows 7!"
7 | tags:
8 | - Windows 7
9 | - API
10 | - Development Tools
11 | categories:
12 | - Development
13 | ---
14 |
15 |
I invite you to head over to Developer Fusion, and read my article on "Enhancing Your Applications For Windows 7”. If you haven’t played with the Windows 7 API Code Pack yet, I definitely recommend it.
16 |
--------------------------------------------------------------------------------
/license:
--------------------------------------------------------------------------------
1 | Copyright 2019 Andre Madarang
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4 |
5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6 |
7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8 |
--------------------------------------------------------------------------------
/blog/20200506-the-dev-talk-show-recap-and-resources.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "The Dev Talk Show Recap and Resources"
3 | date: 2020-05-07T00:30:00Z
4 | permalink: thedevtalkshow
5 | description: "Resources and replay from The Dev Talk Show episode covering Logic Apps and Azure development."
6 | summary: "Thanks for attending The Dev Talk Show! Here are the resources and replay from our discussion about Logic Apps and Azure development."
7 | tags:
8 | - Dev Talk Show
9 | - Logic Apps
10 | - Azure
11 | - Microsoft
12 | categories:
13 | - Misc
14 | ---
15 |
16 | Thanks for attending The Dev Talk Show! If you missed the episode, catch the replay at:
17 |
18 | `youtube:https://www.youtube.com/embed/emuUvQfJpeU`
19 |
20 | # Links to Resources
21 |
22 | [Logic Apps on Azure](https://azure.microsoft.com/en-us/services/logic-apps/?WT.mc_id=DOP-MVP-4029061)
23 | [Custom Connector in Azure Logic Apps](https://docs.microsoft.com/en-us/connectors/custom-connectors/create-logic-apps-connector?WT.mc_id=DOP-MVP-4029061)
24 | [The Dev Talk Show](https://thedevtalkshow.com/)
25 |
26 |
27 |
--------------------------------------------------------------------------------
/blog/20090421-wildcard-search-with-linq.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Wildcard search with LINQ"
3 | date: 2009-04-21T09:17:26Z
4 | permalink: wildcard-search-with-linq
5 | description: "Learn how to perform wildcard searches using LINQ to Entities with the Contains() method as an alternative to SQL LIKE operations."
6 | summary: "An example of wildcard search with LINQ"
7 | tags:
8 | - LINQ
9 | - C#
10 | - Entity Framework
11 | - Database
12 | categories:
13 | - "Development - C#"
14 | ---
15 |
16 | I just a situation where I needed to perform a wildcard search on a table in my database. When I used to do ADO.NET, I would simply write my SELECT statements with LIKE keywords to do wildcard searches.
17 |
18 | However, in this project, I'm using LINQ to Entities and the solution didn't work the same way as it did back in SQL land. My alternative was to use the .Contains() method.
19 |
20 | For example:
21 |
22 | ```csharp
23 | var userList = from u in entity.Users
24 | where u.FirstName.Contains(searchParameter) ||
25 | u.LastName.Contains(searchParameter)
26 | select u;
27 | ```
28 |
29 | Hope this helps if you ever run into this problem.
30 |
--------------------------------------------------------------------------------
/blog/20140902-one-simple-rule-for-successful-consulting.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "One Simple Rule for Successful Consulting"
3 | date: 2014-09-03T00:16:57Z
4 | permalink: one-simple-rule-for-successful-consulting
5 | description: "Discover the one simple rule that can make your consulting more successful: always recommend solutions you would use for your own business."
6 | summary: "When I recommend tools, technologies, and other solutions to my clients, I want to always make sure that the suggestion I am providing is something I would do for my own business"
7 | tags:
8 | - "consulting"
9 | - "business advice"
10 | - "client relationships"
11 | - "professional ethics"
12 | categories:
13 | - Business
14 | ---
15 |
16 | When you're suggesting business decisions for a client, think about it this way:
17 |
18 | > If you were to flip the roles, would you take the advice you are offering?
19 |
20 | Simply put: when I recommend tools, technologies, and other solutions to my clients, I want to always make sure that the suggestion I am providing is something I would do for my own business.
21 |
22 | Do that, and the clients will always keep coming back.
--------------------------------------------------------------------------------
/blog/20091201-learn-about-windows-7-task-dialogs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Learn about Windows 7 Task Dialogs"
3 | date: 2009-12-01T07:00:00Z
4 | permalink: learn-about-windows-7-task-dialogs
5 | description: "Explore Windows 7 Task Dialogs - the next evolution of message boxes with enhanced UI capabilities and better user experience."
6 | summary: "Back in 2009, I wrote an article on building task dialogs in Windows 7"
7 | tags:
8 | - Windows 7
9 | - UI
10 | - Task Dialogs
11 | - Development
12 | categories:
13 | - Development
14 | ---
15 |
16 |
Please take a few minutes and travel over to DeveloperFusion where my latest article on Windows 7 Task Dialogs has been published. If you’ve never used a task dialog before, I would definitely recommend them. I consider them “Message Box 2.0”.
7 |
8 |
9 |
32 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy Master to Azure Blob Storage
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 |
8 | jobs:
9 | build:
10 |
11 | runs-on: ubuntu-latest
12 |
13 | steps:
14 | - name: Setup Node.js for use with actions
15 | uses: actions/setup-node@v4
16 | with:
17 | # Version Spec of the version to use. Examples: 10.x, 10.15.1, >=10.15.0, lts
18 | version: 12.x # optional, default is 10.x
19 | - uses: actions/checkout@v6
20 | - name: NPM install
21 | run: npm install
22 | - name: Install Gridsome
23 | run: npm install --global @gridsome/cli
24 | - name: Gridsome Build
25 | run: gridsome build
26 | - name: Azure Blob Storage Upload
27 | uses: bacongobbler/azure-blob-storage-upload@v3.0.0
28 | with:
29 | # The name of the directory you want to upload
30 | source_dir: dist
31 | # The name of the storage account container these assets will be uploaded to
32 | container_name: $web
33 | # The connection string for the storage account
34 | connection_string: ${{secrets.AZURE_BLOB_CONNECTIONSTRING}}
35 |
36 |
37 |
--------------------------------------------------------------------------------
/blog/20181213-quick-introduction-to-signalr-streaming.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Quick Introduction to SignalR Streaming"
3 | date: 2018-12-13T23:39:46Z
4 | permalink: quick-introduction-to-signalr-streaming
5 | description: "What is streaming in SignalR? Learn about the new Streaming APIs built into SignalR for ASP.NET Core."
6 | summary: "What is streaming in SignalR? I'll show you!"
7 | tags:
8 | - ASP.NET Core
9 | - SignalR
10 | - WebSockets
11 | - Streaming
12 | - ".NET Core"
13 | categories:
14 | - ASP.NET Core
15 | ---
16 |
17 | For my entry this year for [C# Advent](https://crosscuttingconcerns.com/The-Second-Annual-C-Advent), I wanted to do something a little bit different.
18 |
19 | As of late, I've been wanting to start doing several tidbits around cool things that I see in .NET Core and ASP.NET Core. And by the recommendation of my friend, [Brady Gaster](https://twitter.com/bradygaster), I took a look at the new Streaming APIs built into SignalR (for ASP.NET Core).
20 |
21 | And WOW! I'm super impressed with that I saw.
22 |
23 | Check out my video introduction below, and lemme know what you think in the comments!
24 |
25 | `youtube:https://www.youtube.com/embed/xLtWgRzB2_g`
--------------------------------------------------------------------------------
/blog/20160715-powershell-how-to-recursively-delete-files-based-of-file-extension.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Powershell: How to recursively delete files based of file extension?"
3 | date: 2016-07-15T14:15:43Z
4 | permalink: powershell-how-to-recursively-delete-files-based-of-file-extension
5 | description: "Learn how to recursively delete files based on file extension using PowerShell with this quick command line tutorial."
6 | summary: "How do you recursiviely delete files based off file extension in PowerShell?"
7 | tags:
8 | - "PowerShell"
9 | - "command line"
10 | - "file management"
11 | - "scripts"
12 | categories:
13 | - Development
14 | ---
15 |
16 | File this under "took me WAY too long to figure out how to do".
17 |
18 | I just finished doing a Git merge, and ran into an issue where my working folder was polluted with .orig files.
19 |
20 | I wanted to recursively delete all the .orig files. That is apparently harder than it sounds, because it took me 15 minutes to figure out the correct command line.
21 |
22 | So you don't go fumbling like I did:
23 |
24 | ```powershell
25 | Get-ChildItem . -recurse -include *.orig | remove-item
26 | ```
27 |
28 | Replace `.` and `*.orig` accordingly. Have fun!
--------------------------------------------------------------------------------
/src/components/ThemeSwitcher.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
25 |
26 |
--------------------------------------------------------------------------------
/blog/20200816-single-page-architectures-with-vuejs-and-aspnet-core.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Single Page Architectures with Vue.JS and ASP.NET Core"
3 | date: 2020-08-17T00:00:00Z
4 | permalink: spas-with-vuejs-aspnetcore
5 | description: "Thanks for attending my session on building Single Page Architectures with Vue.JS and ASP.NET Core."
6 | summary: "Thanks for attending my session on building Single Page Architectures with Vue.JS and ASP.NET Core."
7 | tags:
8 | - Vue.js
9 | - ASP.NET Core
10 | - SPA
11 | - JavaScript
12 | categories:
13 | - ASP.NET Core
14 | excerpt: "Thanks for attending my session on building Single Page Architectures with Vue.JS and ASP.NET Core."
15 | ---
16 |
17 | Thanks so much for attending my session on building Single Page Architectures with Vue.JS and ASP.NET Core. Here are some of the resources from the talk you might have missed.
18 |
19 | ## Download the Slides
20 |
21 | [Slides are available here for download](/pdfs/spas-with-vuejs-aspnetcore_20200820.pdf)
22 |
23 | ## See the Replay
24 |
25 | Here is a replay of this talk that I did at NDC London 2020:
26 |
27 | `youtube:https://www.youtube.com/watch?v=DH2yUVQDB0I`
28 |
29 | ## Links and Resources
30 |
31 | [Vue.JS](https://vuejs.org/)
32 |
33 |
34 |
--------------------------------------------------------------------------------
/blog/20200512-21st-century-background-services-resources-and-replays.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "21st Century Background Services Resources and Replays"
3 | date: 2020-05-13T00:30:00Z
4 | permalink: 21st-century
5 | description: "Thanks for attending 21st Century Background Services!"
6 | summary: "Thanks for attending 21st Century Background Services!"
7 | tags:
8 | - Background Services
9 | - Azure Functions
10 | - Logic Apps
11 | - Azure
12 | categories:
13 | - Misc
14 | excerpt: "Thanks for attending 21st Century Background Services!"
15 | ---
16 |
17 | Thanks for attending 21st Century Background Services with Azure Logic Apps and Azure Functions! If you missed the talk, catch a replay at:
18 |
19 | ## Replays
20 |
21 | `youtube:https://www.youtube.com/watch?v=iibGXI2YHRw`
22 |
23 | ## Links to Resources
24 |
25 | [Download Slides](/pdfs/21stCenturyBackgroundServicesWithAzureLogicAppsAndAzureFunctions.pdf)
26 |
27 | [Twitter](https://twitter.com/1kevgriff)
28 | [My Twitch Channel](https://www.twitch.tv/1kevgriff)
29 | [Logic Apps on Azure](https://azure.microsoft.com/en-us/services/logic-apps/?WT.mc_id=DOP-MVP-4029061)
30 | [Custom Connector in Azure Logic Apps](https://docs.microsoft.com/en-us/connectors/custom-connectors/create-logic-apps-connector?WT.mc_id=DOP-MVP-4029061)
31 |
32 |
--------------------------------------------------------------------------------
/blog/20100329-if-you-reach-just-one-person.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "If You Reach Just One Person"
3 | date: 2010-03-29T06:00:00Z
4 | permalink: if-you-reach-just-one-person
5 | description: "The rewarding experience of public speaking and how reaching just one person can make all the effort worthwhile."
6 | summary: "The best motivation for public presenting is when one person comes up to you and says that your presentation was the one that pushed them forward."
7 | tags:
8 | - Public Speaking
9 | - Motivation
10 | - Teaching
11 | categories:
12 | - Deep Thoughts
13 | ---
14 |
15 |
After giving a talk, it’s really difficult to judge if you reached any of the attendee’s. Normally, you get the occasional “good job” or “thanks, that was a big help.” Today I got a small mention by Johnathan Bracken, who was sitting in my jQuery From The Ground Up talk at Roanoke Code Camp. This means a lot, because it shows that my talk stayed in Johnathan’s head past the end of the talk.
He just started blogging, and mentioned me in his entry I Will Not Run from JavaScript No More (since removed). This is very cool to see. I wish Johnathan the best of luck in his jQuery adventures! And I expect him to give a jQuery talk at next year’s code camp.
24 |
25 |
26 |
27 |
41 |
42 |
--------------------------------------------------------------------------------
/blog/20200513-better-object-mapping-with-dapper-resources.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Better Object Mapping with Dapper Resources"
3 | date: 2020-05-14T00:30:00Z
4 | permalink: dapper
5 | description: "Thanks for Better Object Mapping with Dapper!"
6 | summary: "Thanks for Better Object Mapping with Dapper!"
7 | tags:
8 | - Dapper
9 | - ORM
10 | - Object Mapping
11 | - .NET
12 | categories:
13 | - Misc
14 | excerpt: "Thanks for Better Object Mapping with Dapper!"
15 | ---
16 |
17 | Thanks for my talk on **Better Object Mapping with Dapper**. Please check out the resources below, and join my mailing list for future updates!
18 |
19 | ## Some questions, answered
20 |
21 | [How do you work with multiple result sets with Dapper?](/dapper-stored-procedures)
22 | How do you call a stored procedure with Dapper? (Coming soon)
23 | Retrieving Output Parameters Using Dapper (Coming soon)
24 |
25 |
26 | Do you have questions about Dapper? Drop me a message on [Twitter](https://twitter.com/1kevgriff) and I'll add it to my list!
27 |
28 | ## Slides
29 |
30 | [Download Slides](/pdfs/BetterObjectMappingInDotNetWithDapper_20200514.pdf)
31 |
32 | ## Links to Resources
33 |
34 | [Twitter](https://twitter.com/1kevgriff)
35 | [My Twitch Channel](https://www.twitch.tv/1kevgriff)
36 | [Dapper GitHub Repo](https://github.com/DapperLib/Dapper)
37 |
38 |
--------------------------------------------------------------------------------
/blog/20130514-specifying-visual-studio-version-in-npm-installs.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Specifying Visual Studio Version in NPM Installs"
3 | date: 2013-05-14T06:34:08Z
4 | permalink: specifying-visual-studio-version-in-npm-installs
5 | description: "How to specify Visual Studio versions with NPM installs to avoid build tool compatibility issues."
6 | summary: "How to specify Visual Studio versions with NPM installs"
7 | tags:
8 | - NPM
9 | - Visual Studio
10 | - Node.js
11 | - Build Tools
12 | categories:
13 | - Development
14 | ---
15 |
16 | Sometimes when you install a NPM package, you'll run into an issue like this:
17 |
18 |
C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\V110\Microsoft.Cpp.Platform.targets(35,5): error MSB8020: The builds tools for Visual Studio 2010 (Platform Toolset = 'v100') cannot be found. To build using the v100 build tools, either click the Project menu or right-click the solution, and then select "Update VC++ Projects...". Install Visual Studio 2010 to build using the Visual Studio 2010 build tools.
19 | Normally you'll get this if you're only running VS2012 and it wants VS2010/VS2008. You can ask NPM to use Visual Studio 2012 instead by using with "--msvs_version=2012" command.
20 |
21 | Example:
22 |
23 |
npm install socket.io --msvs_version=2012
24 | Tada. This should work almost every time.
25 |
--------------------------------------------------------------------------------
/blog/20201013-how-to-upgrade-net-cli-templates.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "How to Upgrade .NET CLI Templates"
3 | date: 2020-10-14T00:00:00Z
4 | permalink: how-to-upgrade-dotnet-cli-templates
5 | description: "Learn how to check for and apply updates to your .NET CLI templates using simple dotnet commands introduced in .NET Core 3.0."
6 | summary: "How do you update the .NET CLI templates? Turns out there is a command just for you!"
7 | tags:
8 | - .NET CLI
9 | - Templates
10 | - dotnet
11 | - .NET Core
12 | - Updates
13 | - Command Line
14 | categories:
15 | - .NET
16 | ---
17 |
18 | I saw an awesome tweet on Twitter today:
19 |
20 | https://twitter.com/buhakmeh/status/1316360387886014464
21 |
22 | Turns out, if you've upgraded to .NET Core 3.0, there is a special command just for this! Head over to the [.NET Core Docs](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-new?WT.mc_id=DOP-MVP-4029061) and look for these two options at the bottom:
23 |
24 | ### Checking for Updates
25 |
26 | ```powershell
27 | dotnet new --update-check
28 | ```
29 |
30 | > Checks if there are updates available for the template packs that are currently installed. Available since .NET Core 3.0 SDK.
31 |
32 | ### Applying Updates
33 |
34 | ```powershell
35 | dotnet new --update-apply
36 | ```
37 |
38 | > Checks if there are updates available for the template packs that are currently installed and installs them. Available since .NET Core 3.0 SDK.
39 |
40 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | // This is the main.js file. Import global CSS and scripts here.
2 | // The Client API can be used here. Learn more: gridsome.org/docs/client-api
3 |
4 | import DefaultLayout from '~/layouts/Default.vue'
5 | import VueScrollTo from 'vue-scrollto';
6 | import VueFuse from 'vue-fuse';
7 | import VueDisqus from 'vue-disqus';
8 |
9 | import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
10 | import { config, library } from '@fortawesome/fontawesome-svg-core';
11 | import { faSearch, faStar } from '@fortawesome/free-solid-svg-icons'
12 | import '@fortawesome/fontawesome-svg-core/styles.css';
13 |
14 | config.autoAddCss = false;
15 | library.add(faSearch, faStar);
16 |
17 | export default function (Vue, { router, head, isClient }) {
18 | // Set default layout as a global component
19 | Vue.component('Layout', DefaultLayout);
20 | Vue.component('font-awesome', FontAwesomeIcon);
21 |
22 | Vue.use(VueScrollTo, {
23 | duration: 500,
24 | easing: "ease",
25 | });
26 |
27 | Vue.use(VueFuse);
28 | Vue.use(VueDisqus);
29 |
30 | head.meta.push({
31 | name: 'keywords',
32 | content: 'Gridsome,Vue,Tailwind,Tailwind CSS,JavaScript,HTML,CSS,Vue.js,VueJS,.NET,ASP.NET,ASP.NET Core,.NET Core,Microsoft Azure,Azure'
33 | });
34 |
35 | head.meta.push({
36 | name: 'author',
37 | content: 'Kevin W. Griffin'
38 | });
39 |
40 | head.link.push({
41 | rel: 'stylesheet',
42 | href: 'https://fonts.googleapis.com/css?family=Nunito+Sans:400,700&display=swap'
43 | });
44 | }
45 |
46 |
47 |
--------------------------------------------------------------------------------
/blog/20121112-my-default-gitignore-file.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "My Default .gitignore File"
3 | date: 2012-11-12T10:37:23Z
4 | permalink: base-gitignore
5 | description: "A comprehensive .gitignore file template for Visual Studio projects and common development artifacts."
6 | summary: "A comprehensive .gitignore file template for Visual Studio projects and common development artifacts."
7 | tags:
8 | - Git
9 | - Visual Studio
10 | - Development
11 | - Version Control
12 | categories:
13 | - Development
14 | ---
15 |
16 | Every time I create a new Git repo, I always have to go look for a previous copy of my .gitignore file. I thought it would be a great idea to just post it up for all to find, in case they were looking for it. This particular file is built around Visual Studio projects.
17 |
18 | ```markdown
19 | # Ignore file for Visual Studio
20 |
21 | # use glob syntax
22 | syntax: glob
23 |
24 | # Ignore Config files with keys and passwords
25 | #ServiceConfiguration*.cscfg
26 | #Web*.config
27 | #App*.config
28 |
29 | # Ignore Visual Studio files
30 | *.obj
31 | #*.exe
32 | #*.pdb
33 | *.user
34 | *.aps
35 | *.pch
36 | *.vspscc
37 | *.vshost.*
38 | *_i.c
39 | *_p.c
40 | *.ncb
41 | *.suo
42 | *.tlb
43 | *.tlh
44 | *.bak
45 | *.cache
46 | *.ilk
47 | *.log
48 | *.lib
49 | *.sbr
50 | *.scc
51 | *.orig
52 | UpgradeLog*.*
53 | UpgradeReport*.*
54 | [Bb]in
55 | [Dd]ebug*/
56 | obj/
57 | [Rr]elease*/
58 | _ReSharper*/
59 | [Tt]est[Rr]esult*
60 | [Bb]uild[Ll]og.*
61 | *.[Pp]ublish.xml
62 | glob:*.vs10x
63 | *.ReSharper
64 | [Pp]ublish
65 | [Rr]eleaseFiles
66 | [Cc]sx/
67 | [Bb]ackup1/
68 | [Pp]ackages/
69 |
70 | # Mac Files
71 | .DS_Store
72 | *.DS_Store
73 | ._*
74 | ```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "kevgriffin.v4",
3 | "private": true,
4 | "scripts": {
5 | "build": "NODE_OPTIONS=--openssl-legacy-provider gridsome build",
6 | "develop": "NODE_OPTIONS=--openssl-legacy-provider gridsome develop",
7 | "explore": "gridsome explore",
8 | "docker:build": "docker build -t kevgriffin.v4:latest .",
9 | "docker:develop": "docker run -p 8080:8080 -v ${PWD}/blog:/usr/src/app/blog kevgriffin.v4:latest",
10 | "create-post": "node scripts/create-post.js"
11 | },
12 | "dependencies": {
13 | "@gridsome/plugin-google-analytics": "^0.1.2",
14 | "@gridsome/plugin-sitemap": "^0.4.0",
15 | "@gridsome/source-filesystem": "^0.6.2",
16 | "@gridsome/transformer-remark": "^0.6.4",
17 | "@gridsome/vue-remark": "^0.2.6",
18 | "@noxify/gridsome-plugin-remark-embed": "^1.3.1",
19 | "autoprefixer": "^10.0.0",
20 | "axios": "^1.0.0",
21 | "gridsome": "^0.7.23",
22 | "gridsome-plugin-gtag": "^0.1.10",
23 | "gridsome-plugin-remark-shiki": "^0.6.0",
24 | "gridsome-plugin-remark-twitter": "^0.2.1",
25 | "gridsome-plugin-remark-youtube": "^1.0.1",
26 | "gridsome-plugin-rss": "^1.2.0",
27 | "lodash.pick": "^4.4.0",
28 | "postcss": "^8.0.0",
29 | "tailwindcss": "npm:@tailwindcss/postcss7-compat@^2.2.17",
30 | "vue-disqus": "^4.0.1",
31 | "vue-fuse": "^2.2.1",
32 | "vue-scrollto": "^2.20.0",
33 | "vue-twitter": "^0.1.0"
34 | },
35 | "devDependencies": {
36 | "@fortawesome/fontawesome-svg-core": "^6.0.0",
37 | "@fortawesome/free-brands-svg-icons": "^6.0.0",
38 | "@fortawesome/free-solid-svg-icons": "^6.0.0",
39 | "@fortawesome/vue-fontawesome": "^2.0.6"
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/blog/20100105-console-games-why-do-we-have-to-press-start.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Console Games: Why do we have to press start?"
3 | date: 2010-01-05T06:00:00Z
4 | permalink: console-games-why-do-we-have-to-press-start
5 | description: "Ever wondered why console games make you press start before reaching the main menu? There's actually a good UX reason for this design pattern."
6 | summary: "Ever wondered why console games make you press start before reaching the main menu?"
7 | tags:
8 | - Gaming
9 | - UX Design
10 | - Console Games
11 | - User Experience
12 | categories:
13 | - "Development - Game Development"
14 | ---
15 |
16 | Interesting question came up on Twitter the other day:
17 |
18 | “Press Start” screens? Why can’t I just go straight to the main menu?
19 |
20 | If you’re a gamer, especially on consoles, you’ve seen this screen more often than you’d care to. It’s a little annoyance. However, there is a very good reason for having this screen in place.
21 |
22 | Imagine you’re running four controller on your Xbox, and all four of them are turned on. Then you put in a single player game, and it comes up to that annoying “Press Start” screen. Which controller do you use to press start with? The answer is easy: any of them!
23 |
24 | The “Press Start” screen is designed to determine which controller the game should poll for input. During this screen, the game is polling all connected controllers for input. If any of them register a “start” button push, the game makes that controller the “default” controller. The use-case for this scenario is that the player should be able to use any connected controller to play the game, and not be forced to use controller #1. This is considered a best practice.
25 |
26 | _The More You Know…_
27 |
--------------------------------------------------------------------------------
/blog/20250610-tech-communities-ai-in-development-and-roller-coasters-fervent-four-podcast.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Tech Communities, AI in Development and Roller Coasters | Fervent Four Podcast"
3 | date: 2025-06-10T10:00:00Z
4 | permalink: fervent-four-podcast-tech-communities
5 | description: "Last week I had the pleasure of being on the Fervent Four podcast with Zack Miller and Tim Ryan. We talked about vibing coding and how AI needs to be handled the same way using offshore talent is handled. We discussed roller coasters (because of course) and the origin of my Shedquarters! And talked a lot also about the software development community in the Norfolk / Virginia Beach area."
6 | summary: "Last week I had the pleasure of being on the Fervent Four podcast with Zack Miller and Tim Ryan. We talked about vibing coding and how AI needs to be handled the same way using offshore talent is handled. We discussed roller coasters (because of course) and the origin of my Shedquarters! And talked a lot also about the software development community in the Norfolk / Virginia Beach area."
7 | tags:
8 | - Podcast
9 | categories:
10 | - Podcast
11 | ---
12 |
13 | Last week I had the pleasure of being on the Fervent Four podcast with [Zack Miller](https://www.linkedin.com/in/zackmillersays/) and [Tim Ryan](https://www.linkedin.com/in/ryantnt/). We talked about vibing coding and how AI needs to be handled the same way using offshore talent is handled. We discussed roller coasters (because of course) and the origin of my Shedquarters! And talked a lot also about the software development community in the Norfolk / Virginia Beach area.
14 |
15 | I'd love for you to watch and let me know what you think!
16 |
17 | `youtube:https://www.youtube.com/watch?v=87UXRC47ur0`
--------------------------------------------------------------------------------
/scripts/create-post.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs');
2 | const path = require('path');
3 | const readline = require('readline');
4 |
5 | const rl = readline.createInterface({
6 | input: process.stdin,
7 | output: process.stdout
8 | });
9 |
10 | const BLOG_DIR = path.join(process.cwd(), 'blog');
11 |
12 | // Ensure blog directory exists
13 | if (!fs.existsSync(BLOG_DIR)) {
14 | fs.mkdirSync(BLOG_DIR, { recursive: true });
15 | }
16 |
17 | // Get current date in YYYY-MM-DD format
18 | const today = new Date().toISOString().split('T')[0];
19 |
20 | // Ask for post title
21 | rl.question('Enter the title of your blog post: ', (title) => {
22 | // Convert title to kebab-case for filename
23 | const slug = title
24 | .toLowerCase()
25 | .replace(/[^a-z0-9]+/g, '-')
26 | .replace(/(^-|-$)/g, '');
27 |
28 | const filename = `${today}-${slug}.md`;
29 | const filepath = path.join(BLOG_DIR, filename);
30 |
31 | // Check if file already exists
32 | if (fs.existsSync(filepath)) {
33 | console.error(`\nError: A file named "${filename}" already exists.`);
34 | console.error('Please choose a different title or delete the existing file.');
35 | rl.close();
36 | return;
37 | }
38 |
39 | // Create frontmatter and initial content
40 | const content = `---
41 | title: "${title}"
42 | date: "${today}"
43 | description: ""
44 | tags: []
45 | ---
46 |
47 | # ${title}
48 |
49 | `;
50 |
51 | // Write the file with error handling
52 | try {
53 | fs.writeFileSync(filepath, content);
54 | console.log(`\nCreated new blog post: ${filename}`);
55 | console.log(`Location: ${filepath}`);
56 | } catch (error) {
57 | console.error('\nError creating blog post:');
58 | console.error(error.message);
59 | }
60 | rl.close();
61 | });
--------------------------------------------------------------------------------
/blog/20200515-how-do-you-call-a-stored-procedure-with-dapper.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "How do you call a stored procedure with Dapper?"
3 | date: 2020-05-15T08:30:00Z
4 | permalink: dapper-stored-procedures
5 | description: "Learn how to use Dapper with stored procedures in .NET applications, including both query and non-query scenarios."
6 | summary: "Are you interested in using Dapper, but your database uses Stored Procedures? No problem!"
7 | tags:
8 | - .NET
9 | categories:
10 | - .NET
11 | excerpt: "Are you interested in using Dapper, but your database uses Stored Procedures? No problem!"
12 | ---
13 |
14 | During one of my latest renditions of [Better Object Relational Mapping with Dapper](), the question arose about using stored procedures.
15 |
16 | How do you call a stored procedure with Dapper?
17 |
18 | ```csharp
19 | using (var connection = new SqlConnection(CONNECTION_STRING))
20 | {
21 | var storedProcedureName = "getAllUsers";
22 |
23 | var results = await connection
24 | .QueryAsync(storedProcedureName,
25 | commandType: CommandType.StoredProcedure);
26 | }
27 | ```
28 |
29 | Looking at the code above, calling a stored procedure is similar to calling any other query with Dapper.
30 |
31 |
32 | But what if your Stored Procedure doesn't return a result? No problem. `ExecuteAsync` works just as well.
33 |
34 | ```csharp
35 | using (var connection = new SqlConnection(CONNECTION_STRING))
36 | {
37 | var storedProcedureName = "deleteAllUsers";
38 |
39 | await connection.ExecuteAsync(storedProcedureName,
40 | commandType: CommandType.StoredProcedure);
41 | }
42 | ```
43 |
44 | And there you go! Using stored procedures with Dapper can be a great way to take advantage of the strengths of the database while reducing complexity of your code.
45 |
46 |
--------------------------------------------------------------------------------
/blog/20210223-converting-epoch-time-into-datetime-with-azure-logic-apps.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Converting Epoch Time into DateTime with Azure Logic Apps"
3 | date: 2021-02-24T00:00:00Z
4 | permalink: azure-logic-apps-epoch-time
5 | description: "Learn how to convert Epoch time to readable DateTime format in Azure Logic Apps when working with APIs like Stripe that use Unix timestamps."
6 | summary: "Some APIs use Epoch time to designate a DateTime object. In Azure Logic Apps, how do you convert this into something useful?"
7 | tags:
8 | - Azure Logic Apps
9 | - Epoch Time
10 | - DateTime Conversion
11 | - Stripe
12 | categories:
13 | - Azure
14 | - Web Development
15 | excerpt: "Recently, I've been building a handful of Azure Logic Apps that work with Stripe webhooks and I've need to convert Epoch DATETIME into a more useful format."
16 | ---
17 |
18 | Recently, I've been building a handful of Azure Logic Apps that work with Stripe webhooks.
19 |
20 | Stripe will commonly use Epoch time for it's dates.
21 |
22 | But Epoch time is pretty useless for anything that is human-facing, and it's not necessarily the default for a lot of Microsoft solutions.
23 |
24 | I needed a way in Azure Logic Apps to easily convert an Epoch Date into a readable format.
25 |
26 | ## What is Epoch Time?
27 |
28 | [Epoch Time](https://en.wikipedia.org/wiki/Epoch_(computing)) is the number of seconds that have surpassed since Midnight, January 1st, 1970. This is also commonly known as `Unix Epoch`.
29 |
30 | ## Converting
31 |
32 | Converting the time is pretty straight-forward, Epoch time is just the number of seconds since 1970-01-01 and Azure Logic Apps has a handy `addToTime` method we can use.
33 |
34 | ```javascript
35 | addToTime('1970-01-01T00:00:00Z', [EPOCH TIME HERE], 'second')
36 | ```
37 |
38 | And ta-da! Your Epoch Time is now in a much easier to use format!
39 |
40 |
41 |
--------------------------------------------------------------------------------
/blog/20210110-how-to-debug-cssjavascript-mouse-or-hover-events.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "How to Debug CSS/JavaScript Mouse or Hover Events"
3 | date: 2021-01-11T00:00:00Z
4 | permalink: debug-javascript-css-hover-mouseover-events
5 | description: "How do you debug or style elements that only exist in the DOM during hover or mouse-over events?"
6 | summary: "How do you debug or style elements that only exist in the DOM during hover or mouse-over events?"
7 | tags:
8 | - CSS
9 | - JavaScript
10 | - Debugging
11 | - DOM
12 | - DevTools
13 | categories:
14 | - JavaScript
15 | - CSS
16 | - Web Development
17 | excerpt: "You're working on an issue the requires to debug or style an element on a page that ONLY appears in the course of a hover or mouse-over event. However, when the event is not occurring, the element you want to work with does not exist in the DOM."
18 | ---
19 |
20 | I am writing this post because I often forget about this particular tip.
21 |
22 | Scenario:
23 | > You're working on an issue the requires to debug or style an element on a page that ONLY appears in the course of a hover or mouse-over event. However, when the event is not occurring, the element you want to work with does not exist in the DOM.
24 |
25 | That's a problem, ain't it?
26 |
27 | Here's a great tip I picked up years ago, and I have never documented myself because it's not problem I run into daily. I wish I knew who to attribute this to you.
28 |
29 | To fix this problem, open your DevTools and write the following in the console.
30 |
31 | ```javascript
32 | setTimeout(() => {debugger;}, 5000)
33 | ```
34 |
35 | Adjust the time accordingly, but you should have 5 seconds to cause the mouse-over or hover event to occur. The debugger will kick in, preventing future updates to the DOM until you continue.
36 |
37 | Yay! You can now inspect the proper elements and make your changes.
38 |
39 |
--------------------------------------------------------------------------------
/blog/20161108-exploring-c-70-out-variables.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Exploring C# 7.0: Out Variables"
3 | date: 2016-11-08T18:41:48Z
4 | permalink: exploring-csharp70-out-variables
5 | description: "New in C# 7.0 - out variables will save you some time! Learn how to declare variables inline with method calls."
6 | summary: "New in C# 7.0 - out variables will save you some time!"
7 | tags:
8 | - .NET
9 | - dotnet
10 | - C#
11 | - "C# 7.0"
12 | categories:
13 | - "Development - C#"
14 | ---
15 |
16 | Using the out keyword within C# is nothing new. If you declare a variable within a method called with **out**, you are instructing the compile that you are expecting the method to set the values of those at runtime.
17 |
18 | ```csharp
19 | class Program
20 | {
21 | static void Main(string[] args)
22 | {
23 | string firstName;
24 | string lastName;
25 |
26 | CreateName(out firstName, out lastName);
27 | Console.WriteLine($"Hello {firstName} {lastName}");
28 | }
29 |
30 | private static void CreateName(out string firstName, out string lastName)
31 | {
32 | firstName = "Kevin";
33 | lastName = "Griffin";
34 | }
35 | }
36 | ```
37 |
38 | Commonly the problem is that you have to declare the variable before the method call using out. In C# 7.0, there is the concept of out variables, which will save you a couple keystrokes by allowing you to declare the variable inline.
39 |
40 | The above example can be quickly refactored:
41 |
42 | ```csharp
43 | class Program
44 | {
45 | static void Main(string[] args)
46 | {
47 | // notice I'm declaring the type INSIDE THE CALL!
48 | CreateName(out string firstName, out string lastName);
49 | Console.WriteLine($"Hello {firstName} {lastName}");
50 | }
51 |
52 | private static void CreateName(out string firstName, out string lastName)
53 | {
54 | firstName = "Kevin";
55 | lastName = "Griffin";
56 | }
57 | }
58 | ```
59 |
--------------------------------------------------------------------------------
/docs/SignalR-Mastery.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: SignalR Mastery
3 | date: 2021-01-06 12:00:00
4 | permalink: signalr-mastery
5 | categories:
6 | - Courses
7 | excerpt: "Become a SignalR Master with this comprehensive course from Microsoft MVP, Kevin Griffin."
8 | ---
9 |
10 | 
11 |
12 | [Get the course](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79)
13 |
14 | The web isn't static. As more and more people live and work on the internet, developers need to make an effort to build robust, adaptive applications that can keep up with the fast pace of business.
15 |
16 | In the 2000s, it was perfectly acceptable for a page to be static. Imagine you're working with a product inventory application. Are other people working against the same data? Can you be sure the current price of the product is valid? Did it change? How would you even know? Refresh the page to load updated data and see.
17 |
18 | What if the page could... update itself? In real-time?
19 |
20 | Once upon a time, this was a complicated solution to implement in a performant manner. Today, it's as simple as creating a WebSocket!
21 |
22 | How do you manage thousands or millions of WebSockets across multiple servers? Reliably? What if the environment cannot support a WebSocket connection? What do you fall back to?
23 |
24 | SignalR is a library for .NET which allows developers to add real-time capabilities to their applications. It provides a framework for managing connections from various clients, including web pages, mobile apps, desktop applications, and more. It handles all of the grunt work.
25 |
26 | In this course, we'll take a structured look at how SignalR works and how you can harness it within your .NET applications.
27 |
28 | [Get the course](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79)
29 |
30 |
--------------------------------------------------------------------------------
/blog/20200407-how-to-run-visual-studio-code-on-mac-osx.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "How to run Visual Studio Code on Mac OSX"
3 | date: 2020-04-07T13:27:04Z
4 | permalink: how-to-run-visual-studio-code-from-mac-osx
5 | description: "Step-by-step guide to set up Visual Studio Code command line integration for both Terminal and Zsh on macOS."
6 | summary: "Using Visual Studio Code on OSX? Here's how to set up the command line for Zsh or Terminal."
7 | tags:
8 | - Visual Studio Code
9 | - macOS
10 | - Terminal
11 | - Zsh
12 | categories:
13 | - Development
14 | ---
15 |
16 | **EDIT:** [You can just do this from Visual Studio Code now.](https://code.visualstudio.com/docs/setup/mac)
17 |
18 | ## Terminal
19 |
20 | Currently, there isn't an automatic method for doing this, but with a little code in your `~/.bash_profile` file, you can configure it.
21 |
22 | ```bash
23 | code () { VSCODE_CWD="$PWD" open -n -b "com.microsoft.VSCode" --args $* ;}
24 | ```
25 |
26 | Then from Terminal you can type:
27 |
28 | `code` -- opens Visual Studio Code
29 | `code .` -- opens current directory in Visual Studio Code
30 | `code somefile` -- opens somefile in Visual Studio Code
31 |
32 |
33 | ## Zsh
34 |
35 | Using [Visual Studio Code](https://code.visualstudio.com) on your Mac, but can't call it from Zsh?
36 |
37 | Currently, there isn't an automatic method for doing this, but with a little code in your `.zshrc` file, you can configure it.
38 |
39 | ```zsh
40 | function code {
41 | if [[ $# = 0 ]]
42 | then
43 | open -a "Visual Studio Code"
44 | else
45 | local argPath="$1"
46 | [[ $1 = /* ]] && argPath="$1" || argPath="$PWD/${1#./}"
47 | open -a "Visual Studio Code" "$argPath"
48 | fi
49 | }
50 | ```
51 | Then from Terminal you can type:
52 |
53 | `code` -- opens Visual Studio Code
54 | `code .` -- opens current directory in Visual Studio Code
55 | `code somefile` -- opens somefile in Visual Studio Code
56 |
--------------------------------------------------------------------------------
/blog/20141013-mongodb-setting-ttl-on-documents.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "MongoDB: Setting TTL on Documents"
3 | date: 2014-10-13T14:08:05Z
4 | permalink: mongodb-setting-ttl-on-documents
5 | description: "Learn how to automatically remove MongoDB documents after a specified time period using Time To Live (TTL) indexes."
6 | summary: "Learn how to automatically remove MongoDB documents after a specified time period using Time To Live (TTL) indexes."
7 | tags:
8 | - MongoDB
9 | - Database
10 | - TTL
11 | - NoSQL
12 | categories:
13 | - Development
14 | ---
15 |
16 | On some recent work I was doing with Winsitter, I needed an approach that would systematically remove documents after a specified period of time within MongoDB.
17 |
18 | Previously, I would have written cron jobs or helpers to clean up the old data. No longer!
19 |
20 | Come to discover, this feature [already exists](https://docs.mongodb.com/manual/tutorial/expire-data/) inside of MongoDB. It is called setting the TTL or Time to Live of a document.
21 |
22 | The process involves creating an index on the date object you'd like to watch. In the example below, I am providing a property on my document called `auditDate`. I want the document associated with that property to automatically remove itself after 5 days or *432000000* miliseconds.
23 |
24 | ```javascript
25 | dbConnection
26 | .collection("audit")
27 | .ensureIndex({
28 | "auditDate": 1
29 | }, {
30 | expireAfterSeconds: 432000000 // 5 days
31 | }, function(indexErr) {
32 | if (indexErr) {
33 | console.log("error creating index");
34 | console.log(indexErr);
35 | }
36 | });
37 | ```
38 |
39 | In the example, I'm using the MongoDB node.js library to ensure an index exists. If the index doesn't exist, MongoDB will create it.
40 |
41 | The `expireAfterSeconds` options tells MongoDB that after a specified amount of time, the document should automatically remove itself.
42 |
43 | This quick fix has saved me a ton of time, and I am hoping it saves a ton for you too!
--------------------------------------------------------------------------------
/blog/20150514-no-matter-what-you-do-add-value.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "No matter what you do: add value"
3 | date: 2015-05-14T11:38:29Z
4 | permalink: no-matter-what-you-do-add-value
5 | description: "Always strive to add value in your interactions, whether with clients, colleagues, or potential business opportunities."
6 | summary: "Always strive to add value in your interactions, whether with clients, colleagues, or potential business opportunities."
7 | tags:
8 | - Consulting
9 | - Business
10 | - Mentoring
11 | - Professional Development
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 | Yesterday I had the pleasure of chatting with a gentleman about a potential mentoring gig. The way I like to approach these sort of talks is to determine high level goals. In this case, the client was looking for someone to be a set of expert eyes on a codebase. He is certainly a capable developer, but he's also aware enough to know when he has hit a roadblock.
17 |
18 | We were connected by the work and training I've done with SignalR and ASP.NET, which would prove especially useful in this situation.
19 |
20 | The client discussed the issues he was running into and the thoughts he had around solutions. Normally, this is where you will have the instinct to want to hold off providing guidance. You're the expert. This what people should be paying you for!
21 |
22 | > Always try to add some value.
23 |
24 | That's not how I reacted though. Instead, I spent 10 minute giving my potential-client two or three *free* tips and tricks on what he should explore. Sometimes the simplest tip is all a person needs to push forward.
25 |
26 | The underlying point is that no matter what you do, **always try to add some value**. If my tips bring him to an epiphany, then I have automatically reaffirmed that I am an expert. This could lead to mentoring and other potential work. It could lead to referrals.
27 |
28 | Approach all your interactions as a chance to provide value to someone. It will pay back dividends!
--------------------------------------------------------------------------------
/blog/20121102-preloading-multiple-audio-tags-in-internet-explorer-9.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Preloading Multiple Audio Tags in Internet Explorer 9"
3 | date: 2012-11-02T06:00:59Z
4 | permalink: preloading-multiple-audio-tags-in-internet-explorer-9
5 | description: "Learn how to preload multiple audio files in Internet Explorer 9, including workarounds for IE's limitations with HTML5 audio preloading."
6 | summary: "Learn how to preload multiple audio files in Internet Explorer 9, including workarounds for IE's limitations with HTML5 audio preloading."
7 | tags:
8 | - HTML5
9 | - Audio
10 | - Internet Explorer
11 | - JavaScript
12 | categories:
13 | - Development
14 | ---
15 |
16 | I had a unique problem. I have an app I'm working on where I needed to preload about 50 audio files for use during the page's lifetime. If you're up on your HTML5-fu, this is a simple task:
17 |
18 | ```html
19 |
23 | ```
24 |
25 | In Chrome, this works PERFECTLY (as it should).
26 |
27 | In Internet Explorer, several (if not all) files will fail to preload. Here's how to figure it:
28 |
29 | ```javascript
30 | var audioElement = document.getElementById("myAudio");
31 | console.log(audioElement.networkState);
32 | ```
33 |
34 | Network state can have 3 options: NETWORK_EMPTY, NETWORK_IDLE, NETWORK_LOADING, NETWORK_NO_SOURCE.
35 |
36 | You "want" it to be IDLE, because that means the file is loaded. Typically, you'll get NO_SOURCE with Internet Explorer.
37 |
38 | What's a quick fix? First, make sure there is no preload attribute, and then do this:
39 |
40 | ```javascript
41 | var audioElement = document.getElementById("myAudio");
42 | audioElement.load(); // kicks off the load
43 | ```
44 |
45 | This has worked for me in 100% of the tests I've done tonight. Feel free to comment on other solutions. I haven't tested in IE10 yet, so I cannot be certain of how it works.
46 |
--------------------------------------------------------------------------------
/blog/20150723-paying-attention.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Paying Attention"
3 | date: 2015-07-23T06:43:54Z
4 | permalink: paying-attention
5 | description: "Don't assume. Pay attention. A lesson learned from a simple timezone mix-up during remote training."
6 | summary: "Don't assume. Pay attention."
7 | tags:
8 | - Communication
9 | - Remote Work
10 | - Lessons Learned
11 | - Professional
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 | Last week I did a training gig for a great group of folks out on the west coast. I worked from the comfort of my home office, and presented all the material remotely.
17 |
18 | The spec for training was pretty straightforward: start at 9am and end at 5pm for three days. The timezone was for Pacific Time, since the majority of the group resided in that timezone.
19 |
20 | This week, I did a training for the same company but with a different group of people. Monday morning I woke up bright and early to an email wondering why I was a no-show to the training. Of course, I was barely out of bed and hadn't had my coffee yet. I'm an east coaster, so 9am Pacific Time would've been noon local time. Training didn't start for a few hours.
21 |
22 | I quickly reviewed my spec, and I saw it plain as day: 9am to 5pm. What was I missing?
23 |
24 | Central European Time. Or better known as 3am to east coasters. YIKES! I was a no show.
25 |
26 | The moral of the story is that we need to start paying better attention. Too often do we get lost in the small details or we simply assume too much. For me, I assumed since the first training was Pacific Time Zone that the second one was Pacific Time Zone as well. Heck, I even checked the spec! But I only checked the times, and completely overlooked the different timezone. A 9 hour mistake.
27 |
28 | Luckily for me, the client was understanding and we pushed the training another day.
29 |
30 | Have you ever had a moment where your assumptions got the best of you? Minute details completely escaped you even though you were staring right at them? I'd love to hear about it! Leave me a comment and let us share the pain together.
--------------------------------------------------------------------------------
/.github/workflows/azure-static-web-apps-yellow-forest-0e8e9d20f.yml:
--------------------------------------------------------------------------------
1 | name: Azure Static Web Apps CI/CD
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | pull_request:
8 | types: [opened, synchronize, reopened, closed]
9 | branches:
10 | - master
11 |
12 | jobs:
13 | build_and_deploy_job:
14 | if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
15 | runs-on: ubuntu-latest
16 | name: Build and Deploy Job
17 | steps:
18 | - name: Setup Node.js for use with actions
19 | uses: actions/setup-node@v4
20 | with:
21 | # Version Spec of the version to use. Examples: 10.x, 10.15.1, >=10.15.0, lts
22 | node-version: 20
23 |
24 | - uses: actions/checkout@v6
25 |
26 | - name: YARN install
27 | run: yarn install
28 |
29 | - name: Install Gridsome
30 | run: yarn global add @gridsome/cli
31 |
32 | - name: Gridsome Build
33 | run: yarn run build
34 |
35 | - name: Build And Deploy
36 | id: builddeploy
37 | uses: Azure/static-web-apps-deploy@v1
38 | env:
39 | DISABLE_NODEJS_BUILD: true
40 | with:
41 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YELLOW_FOREST_0E8E9D20F }}
42 | repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
43 | action: 'upload'
44 | ###### Repository/Build Configurations - These values can be configured to match you app requirements. ######
45 | app_location: '/dist' # App source code path
46 | ###### End of Repository/Build Configurations ######
47 |
48 | close_pull_request_job:
49 | if: github.event_name == 'pull_request' && github.event.action == 'closed'
50 | runs-on: ubuntu-latest
51 | name: Close Pull Request Job
52 | steps:
53 | - name: Close Pull Request
54 | id: closepullrequest
55 | uses: Azure/static-web-apps-deploy@v1
56 | with:
57 | azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_YELLOW_FOREST_0E8E9D20F }}
58 | action: 'close'
59 |
--------------------------------------------------------------------------------
/docs/Consulting.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Consulting
3 | date: 2017-05-03 12:50:43
4 | permalink: consulting
5 | categories:
6 | - Misc
7 | excerpt: "I'm available for hire! Let's chat"
8 | ---
9 |
10 | I am actively available to short term consulting engagements.
11 | What are some ways that we can engage with each other?
12 |
13 | ### ASP.NET Core Architecture and Implementation
14 |
15 | As a 16-time Microsoft MVP, my primary focus and enjoyment comes from designing and implementing solutions built on top of ASP.NET Core. I have worked with several clients to modernize their applicaitons on ASP.NET Core, as I feel it is one of the best solutions on the market for building and maintaining scalable, flexible, and performant web applications.
16 |
17 | ### Microsoft Azure Architecture and Implementation
18 |
19 | In the current age of cloud-based applications, it is important to choose a provider that will ensure your applications and services are running at top performance - all the time. For over ten years, I have supported clients on top of Microsoft Azure. I would love to work with your team to design or implement a strategy for adding Microsoft Azure to your technical stack.
20 |
21 | ### Web Development Architecture and Implementation
22 |
23 | In addition to the work I've done on building scalable ASP.NET Core applications, I have spent a good amount of time building and improving web applications. While I'm currently in love with Vue.JS, I'm well versed in other client side technologies such as React and Angular. Heck, if you want to go old-school, I loved me some jQuery back in the 2000's.
24 |
25 | ### Application Modernization
26 |
27 | If you stop and blink, technology is changing before your eyes! I have worked with several clients on modernization projects to bring their tech stacks forward to something that is maintainable for the next several years.
28 |
29 | ### Performance Optimization
30 |
31 | Another core aspect of the consulting work I've done is focused on performance. Sometimes it's easy to lose track of the bigger picture when developing application, and before you know it your servers are pegged and pages take minutes to load.
32 |
33 | ## Let's Connect!
34 |
35 | [Book a consultation](https://app.onecal.io/b/kevin-griffin/chat) to discuss your needs, or shoot me an [email](mailto:connect@consultwithgriff.com) with a little information about your needs, and I'll respond with availability and a rate sheet.
36 |
--------------------------------------------------------------------------------
/blog/20150306-managing-your-user-group-calendar-roulette.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Managing Your User Group: Calendar Roulette"
3 | date: 2015-03-06T22:39:16Z
4 | permalink: calendar-roulette
5 | description: "Learn why consistency in scheduling is crucial for user group success and how to choose the right meeting day for maximum attendance."
6 | summary: "If you're running a community, pick a day and stick with it!"
7 | tags:
8 | - User Groups
9 | - Community Management
10 | - Scheduling
11 | - Leadership
12 | categories:
13 | - Community, User Groups, and Conferences
14 | ---
15 |
16 | One trait of successful user groups is don't play calendar roulette. Meaning: they chose a time or day each month to hold the group meeting.
17 |
18 | > We meet every 2nd Tuesday.
19 |
20 | or
21 |
22 | > We are the last Thursday of the month.
23 |
24 | Why is this important?
25 | ***You want your members to be able to determine meeting dates based off a quick glance of a calendar.***
26 | Consistency is key. If your meetings are in different venues or different days of the month, you're chancing that someone will forget and not be in attendance.
27 |
28 | **What's a good night for meetings?**
29 | With the Hampton Roads .NET Users Group, I chose the 2nd Tuesday of the month. Why? Because I felt Tuesdays were a better for my schedule. Only once in a 5 year period did I change the meeting.
30 |
31 | My recommendation is to choose a Tuesday, Wednesday, or Thursday. Mondays are bad for folks because it's the first day back to work after a short weekend. Fridays are bad because, dude, it's the weekend. PARTY TIME.
32 |
33 | Be careful with Wednesdays as well. Many church functions occur on Wednesday nights, and that might be limiting a percentage of your population.
34 |
35 | **When is it okay to move the meeting?**
36 | Let's imagine you have a speaker coming into town that's "a big deal". But they're only able to meeting on Wednesday night, and your meetings are Tuesdays. MOVE THE MEETING. Make sure the change is well communicated, and that it's temporary. You might lose attendance that month, but people are adaptable. Just don't make it a habit.
37 |
38 | **Conclusion**
39 | Keep scheduling simple. Hampton Roads .NET members knew where to go and when to be there every month of the year. If you build a level of consistency, you're going to have people showing up out of habit, and that's a recipe for a successful group.
40 |
41 |
42 |
--------------------------------------------------------------------------------
/blog/20160324-the-10-rule-to-presentations.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "The 10% Rule to Presentations"
3 | date: 2016-03-24T09:18:59Z
4 | permalink: the-10-rule-to-presentations
5 | description: "A presentation strategy for technical speakers: aim to give your audience 10% of your knowledge on a subject to build foundations for learning."
6 | summary: "When mentoring new technical speakers, I like to cite what I call 'the 10% rule'."
7 | tags:
8 | - public speaking
9 | - presentations
10 | - technical speaking
11 | - mentoring
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 | When mentoring new technical speakers, I like to cite what I call "the 10% rule".
17 |
18 | Think about it like this: If you are presenting on a 100-level topics (for example, "Intro to Angular" or "Building Your First App with Elixir"), you can safely assume that your audience is one of three types people.
19 |
20 | **First**, they are an absolute newbie who is there because they heard about this technology but have practically no experience with it.
21 |
22 | **Second**, they have been to previous talks on the subject or have read a couple tutorials. They are not starting at "ground-zero", but they are darn close.
23 |
24 | **Third**, they are seasoned or beyond 100-level, and they are looking to see if you offer new perspective.
25 |
26 | The 10% rule applies to the first two groups. Your job as the presenter is to try to leave the audience with 10% of the knowledge you have on a subject.
27 |
28 | Ten percent sound like a lot, right? Doesn't have to be. For most developers, this is seeing how pieces lay together or how do you do something from scratch.
29 |
30 | Ten percent might simply be "what problem does the thing I'm showing you solve?"
31 |
32 | My first set of presentations were around jQuery (2007 era). My intro talks discussed manipulating the DOM and listening for events. I'd discuss the "hard" way, but then showed how easy jQuery made it.
33 |
34 | If you're adventuring into 200 or 300-level talks, the same 10% rule can apply. Imagine you want to do a talk on "new C# features". Your expectation is that the audience is familiar with C#. Your 10% is going to be a simple list of what is new, with basic explanations.
35 |
36 | If the attendee goes home and runs into a scenario where a new feature would be useful, you have provided them with the foundation to research more.
37 |
38 | The 10% rule is meant to build foundations for learning. Expose audiences to ideas and concepts, and guide them towards better self-learning.
39 |
--------------------------------------------------------------------------------
/_README.md:
--------------------------------------------------------------------------------
1 | # Gridsome Portfolio Starter
2 |
3 | A simple portfolio starter theme for Gridsome
4 |
5 | 
6 |
7 | ## Demo URL
8 |
9 | [https://gridsome-portfolio-starter.netlify.com](https://gridsome-portfolio-starter.netlify.com)
10 |
11 | ## Features
12 |
13 | - Clean and minimal design
14 | - [Tailwind CSS v1](https://tailwindcss.com) (with PurgeCSS)
15 | - Scroll to sections using [vue-scrollto](https://github.com/rigor789/vue-scrollto)
16 | - Blog with markdown content for posts
17 | - Documentation type that shows how to use Vue components in Markdown (click Docs)
18 | - Theme Switcher with Dark Mode
19 | - Search posts with [Fuse.js](https://fusejs.io) and [vue-fuse](https://github.com/shayneo/vue-fuse)
20 | - Tags for posts
21 | - Basic pagination
22 | - Syntax highlighting with [Shiki](https://shiki.matsu.io) (using [this gridsome plugin](https://gridsome.org/plugins/gridsome-plugin-remark-shiki))
23 | - 404 Page
24 | - RSS Feed
25 | - Sitemap in XML
26 |
27 | ## Installation
28 |
29 | 1. Install Gridsome CLI tool if you don't have it: `npm install --global @gridsome/cli`
30 | 1. Clone the repo: `git clone https://github.com/drehimself/gridsome-portfolio-starter.git`
31 | 1. `cd gridsome-portfolio-starter`
32 | 1. `npm install`
33 | 1. `gridsome develop` to start a local dev server at `http://localhost:8080`
34 |
35 | ## Notes
36 |
37 | - Based on my [personal portfolio website](https://andremadarang.com). I wanted to create an open source version with more features.
38 | - Check out a [screencast I did](https://www.youtube.com/watch?v=uHo6o1TNQeE) where I go through the process of building my website.
39 | - Illustrations from [unDraw](https://undraw.co)
40 | - Search is based on [Fuse.js](https://fusejs.io) and [vue-fuse](https://github.com/shayneo/vue-fuse). It only searches the title and summary of posts for now. Some tweaking may be necessary to get it to search to your liking. Check out the fuse documentation for search settings. A `search.json` index file is generated at build time. This happens in `gridsome.server.js`.
41 | - Check out these other Gridsome Starters where I got some ideas from:
42 | - [Gridsome Starter Blog](https://github.com/gridsome/gridsome-starter-blog)
43 | - [Gridsome Starter Bleda](https://github.com/cossssmin/gridsome-starter-bleda)
44 | - [Jigsaw Starter Blog](https://jigsaw.tighten.co/docs/starter-templates/) - I got a lot of design inspiration from this starter theme.
45 |
--------------------------------------------------------------------------------
/gridsome.server.js:
--------------------------------------------------------------------------------
1 | // Server API makes it possible to hook into various parts of Gridsome
2 | // on server-side and add custom data to the GraphQL data layer.
3 | // Learn more: https://gridsome.org/docs/server-api
4 |
5 | // Changes here require a server restart.
6 | // To restart press CTRL + C in terminal and run `gridsome develop`
7 |
8 | const fs = require('fs');
9 | const path = require('path');
10 | const pick = require('lodash.pick');
11 | const { pathPrefix } = require('./gridsome.config');
12 |
13 | module.exports = function (api, options) {
14 |
15 | api.loadSource(store => {
16 | /*
17 | Clean the pathPrefix
18 | ====================
19 | not used => '/'
20 | '' => '/'
21 | '/' => '/'
22 | '/path' => '/path'
23 | 'path' => '/path'
24 | 'path/' => '/path'
25 | '/path/' => '/path'
26 | */
27 | const cleanedPathPrefix = `${pathPrefix ? ['', ...pathPrefix.split('/').filter(dir => dir.length)].join('/') : ''}`
28 |
29 | /*
30 | Query
31 | =====
32 |
33 | {
34 | metaData{
35 | pathPrefix
36 | }
37 | }
38 |
39 |
40 | Requests for static files should look like this:
41 | ===============================================
42 | Using static-queries: axios( this.$static.metaData.pathPrefix + "/fileName" )
43 | Using page-queries, axios( this.$page.metaData.pathPrefix + "/fileName" )
44 | */
45 | store.addMetadata('pathPrefix', cleanedPathPrefix)
46 | });
47 |
48 | api.beforeBuild(({ config, store }) => {
49 |
50 | // Generate an index file for Fuse to search Posts
51 | const { collection } = store.getContentType('Post');
52 |
53 | const posts = collection.data.map(post => {
54 | return pick(post, ['title', 'path', 'summary']);
55 | });
56 |
57 | const output = {
58 | dir: './static',
59 | name: 'search.json',
60 | ...options.output
61 | }
62 |
63 | const outputPath = path.resolve(process.cwd(), output.dir)
64 | const outputPathExists = fs.existsSync(outputPath)
65 | const fileName = output.name.endsWith('.json')
66 | ? output.name
67 | : `${output.name}.json`
68 |
69 | if (outputPathExists) {
70 | fs.writeFileSync(path.resolve(process.cwd(), output.dir, fileName), JSON.stringify(posts))
71 | } else {
72 | fs.mkdirSync(outputPath)
73 | fs.writeFileSync(path.resolve(process.cwd(), output.dir, fileName), JSON.stringify(posts))
74 | }
75 | })
76 | }
77 |
--------------------------------------------------------------------------------
/blog/20151013-managing-your-user-group-food.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Managing Your User Group: Food"
3 | date: 2015-10-13T18:45:40Z
4 | permalink: user-group-food
5 | description: "Creative alternatives to pizza and sandwiches for user group meetings that will make your sponsors memorable and attendees happy."
6 | summary: "If you run a community, add some variety to your food choices."
7 | tags:
8 | - User Groups
9 | - Community Management
10 | - Event Planning
11 | - Sponsorship
12 | categories:
13 | - Community, User Groups, and Conferences
14 | ---
15 |
16 | *Edit: Based off some comments, I've included options for non-lovers of meat. Also added other comments.*
17 |
18 | For a lot of user groups, food depends highly on the sponsor for the month.
19 |
20 | Let's keep this short and sweet: Sponsors. If your instinct is to order pizza for a user group meeting, your attendees already hate you.
21 |
22 | Think about it this way. A lot of people have an opportunity to sponsor a group. You want to be memorable. Pizza is not memorable.
23 |
24 | And you know what else? Sandwiches. This is the new pizza. If you think you're being clever and different by providing Jimmy Johns instead of pizza, your attendees hate you too.
25 |
26 | What could you do instead of pizza? What food would make your company memorable? Here are some ideas:
27 |
28 | **Chipotle** - When I spoke at Richmond .NET in October 2015, MaconIT brought Chiptole burritos. I'm talking about a *box of freakin burritos*.
29 |
30 | **Taco Bar** - Taco bars are fun, and they allow attendees to choose their own mexican adventure.
31 |
32 | **Chick-Fil-A** - Nugget tray. Enough said.
33 |
34 | **PF Changs** - How about some asian flair? Try a mini-buffet of PF Changs favorites.
35 |
36 | **BBQ Buffet** - Pulled pork, beans, coleslaw. Depending on the location, you can get a BBQ buffet for the same price as pizza for a group.
37 |
38 | **Veggie/Vegan Options** - I know, I know. The food above is all mostly of the animal protein category. Please don't forget people that don't eat meat. Salad choices and veggies go a long way. You'd be really surprised how many card-carrying meat eaters will dive into healthy choices. Not sure what Vegan's would eat? Ask one. They'd be more than happy to give you a couple dozen options.
39 |
40 | Don't get me wrong, pizza is awesome. Pizza in EXCESS is horrible (and to be fair, if you brought Chick-Fil-A every meeting -- that would probably get old too). Be good to your groups and don't buy pizza (or sandwiches). Look for creative alternatives. Attendees will love you.
--------------------------------------------------------------------------------
/.github/workflows/claude.yml:
--------------------------------------------------------------------------------
1 | name: Claude Code
2 |
3 | on:
4 | issue_comment:
5 | types: [created]
6 | pull_request_review_comment:
7 | types: [created]
8 | issues:
9 | types: [opened, assigned]
10 | pull_request_review:
11 | types: [submitted]
12 |
13 | jobs:
14 | claude:
15 | if: |
16 | (github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
17 | (github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
18 | (github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
19 | (github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
20 | runs-on: ubuntu-latest
21 | permissions:
22 | contents: read
23 | pull-requests: read
24 | issues: read
25 | id-token: write
26 | actions: read # Required for Claude to read CI results on PRs
27 | steps:
28 | - name: Checkout repository
29 | uses: actions/checkout@v6
30 | with:
31 | fetch-depth: 1
32 |
33 | - name: Run Claude Code
34 | id: claude
35 | uses: anthropics/claude-code-action@beta
36 | with:
37 | claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
38 |
39 | # This is an optional setting that allows Claude to read CI results on PRs
40 | additional_permissions: |
41 | actions: read
42 |
43 | # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1)
44 | # model: "claude-opus-4-1-20250805"
45 |
46 | # Optional: Customize the trigger phrase (default: @claude)
47 | # trigger_phrase: "/claude"
48 |
49 | # Optional: Trigger when specific user is assigned to an issue
50 | # assignee_trigger: "claude-bot"
51 |
52 | # Optional: Allow Claude to run specific commands
53 | # allowed_tools: "Bash(npm install),Bash(npm run build),Bash(npm run test:*),Bash(npm run lint:*)"
54 |
55 | # Optional: Add custom instructions for Claude to customize its behavior for your project
56 | # custom_instructions: |
57 | # Follow our coding standards
58 | # Ensure all new code has tests
59 | # Use TypeScript for new files
60 |
61 | # Optional: Custom environment variables for Claude
62 | # claude_env: |
63 | # NODE_ENV: test
64 |
65 |
--------------------------------------------------------------------------------
/src/pages/Articles.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
39 |
40 |
41 |
42 |
43 | query Posts ($page: Int) {
44 | posts: allPost (sortBy: "date", order: DESC, perPage: 10, page: $page) @paginate {
45 | totalCount
46 | pageInfo {
47 | totalPages
48 | currentPage
49 | }
50 | edges {
51 | node {
52 | id
53 | title
54 | date (format: "MMMM D, Y")
55 | summary
56 | excerpt
57 | timeToRead
58 | path
59 | }
60 | }
61 | }
62 | }
63 |
64 |
65 |
92 |
93 |
--------------------------------------------------------------------------------
/blog/20090206-a-diet-programmers-can-relate-to.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "A Diet Programmers Can Relate To"
3 | date: 2009-02-06T06:52:21Z
4 | permalink: a-diet-programmers-can-relate-to
5 | description: "I gave Weight Watchers a try, and it was pretty cool! Learn how the Weight Watchers point system works like a programming algorithm."
6 | summary: "I gave Weight Watchers a try, and it was pretty cool! Learn how the Weight Watchers point system works like a programming algorithm."
7 | tags:
8 | - Health
9 | - Programming
10 | - Weight Watchers
11 | - Personal Development
12 | categories:
13 | - Health
14 | ---
15 |
16 | Consider this: programmers are typically overweight. It's our nature. Ninety-five percent of our job is sitting behind a desk and hacking at a keyboard. The most movement we get is walking to the bathroom, and going to our car to go to the drive thru.
17 |
18 | About a month ago, I was turned onto the Weight Watchers plan, and it immediately occurred to me that this is the perfect diet plan for developers. Here is the gist of the plan: You're allotted x number of points a day. Points come from the foods and drinks you put into your body. The number of points you're allowed is based on your gender, weight, height, and activity level.
19 |
20 | The points in a piece of food is determined by the number of calories, total fat, and fiber content of the food. Anyone that pays to join Weight Watchers is given a calculator for determining the points. In reality, it's just an equation. Developers like equations. We can understand equations. Here is the equation for Weight Watchers Points:
21 |
22 | POINTS = (Calories / 50) + (Total Fat / 12) - (MIN(Fiber, 4) / 5)
23 |
24 | Ooooh. That could easily be plugged into a script. How does this fit into the real world? My number of points allotted per day is 39. There is a formula for figuring that out, but I've been unable to find it. If/when I do, I'll post it here. But for the record, I'm 6'1, 259 pounds and sit at a desk all day. If you weigh more than I do, add 1 point per 10 pound increment (260 pounds is technically 40 points). If you weight less, subtract 1 point per 10 pound increment. If you are taller than me, add a point or two. If you're shorter, subtract a point or two. You should see what I mean.
25 |
26 | Say I go to Burger King, and buy a Whopper with cheese. That weighs in at 19 pounds (the mayo kills it). After eating a Whopper, I've already had half my number of points for the day. Weight Watchers says that's cool, but be careful with your other 20 points. You still eat what you want, just eat less of it and try to find better alternatives.
27 |
28 | So now you can do the Weight Watchers plan for free. Use the points equation to do whatever you want. Lose weight mathematically!
--------------------------------------------------------------------------------
/blog/20100119-are-we-too-dependent-on-the-internet.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Are we too dependent on the Internet?"
3 | date: 2010-01-19T06:00:00Z
4 | permalink: are-we-too-dependent-on-the-internet
5 | description: "Exploring our dependency on the internet and how it affects our productivity and learning capabilities when we lose connectivity."
6 | summary: "Does the internet make us stronger, or is it making us weaker?"
7 | tags:
8 | - Deep Thoughts
9 | categories:
10 | - Deep Thoughts
11 | ---
12 |
13 | In my office, there lives a monster. This monster is called “The Internet Killer,” and he likes to poke his head out every now. When he does, our internet goes down for hours upon hours. During this time, I still have work too do, but I often find that my production level is limited by the lack of internet.
14 |
15 | **Are we too dependent on the Internet?**
16 |
17 | I’m in that weird generation where I have had access to the Internet for most of my life, but I can still remember not having it. I have used a card catalog. I have used an encyclopedia. I have had to retain knowledge for more than a few minutes.
18 |
19 | This was also around the time I started learning how to program. My first few BASIC applications were self taught from a help file. I didn’t have an Internet to go to whenever I ran into a problem. I was forced to either figure it out on my own, or travel to the library to reference whatever material (if they had any material at all). Having to work through these issues forced me to retain knowledge for an extended period of time. You never knew when you were going to have use what you had learned before.
20 |
21 | Fast forward to today. I’m working on a few features for a project, and we lose our internet. You might be saying, “Kevin, you should be able to code just fine without the internet.” And you are right, I should be able to. However, if you’re venturing into territory that you’re not familiar with, your work is either going to take two or three times as long as it would had you had access to reference materials.
22 |
23 | “Have you ever heard of books?” Yes, and I have plenty of them. Books are hard to reference. Books are awful for troubleshooting problems. Can you type an error string or code into a book? How long does it take to find a book that might have the information you’re looking for? Does it actually contain information that is useful, or just code snippets that is causing the error you have?
24 |
25 | Google (Bing, or whatever you use) is fast, accurate (for the most part), and easily accessible. The “whole world at your fingertips” is no joke. Within minutes, I have access to references, books, blogs, forums, etc. Your problem is never new, and the Internet is quick to provide answers.
26 |
27 | Does the internet make us stronger, or is it making us weaker? I’ll let you decide.
28 |
--------------------------------------------------------------------------------
/src/templates/Tag.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
14 |
15 |
16 |
17 | query Post ($path: String!) {
18 | post: post (path: $path) {
19 | title
20 | date (format: "MMMM D, Y")
21 | path
22 | summary
23 | content
24 | excerpt
25 | timeToRead
26 | image
27 | tags {
28 | title
29 | path
30 | }
31 | }
32 | }
33 |
34 |
35 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/blog/20091016-my-attempt-at-linq-pagination.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "My Attempt at LINQ Pagination"
3 | date: 2009-10-16T06:00:10Z
4 | permalink: my-attempt-at-linq-pagination
5 | description: "Implementing efficient pagination in LINQ using Skip() and Take() methods to handle large record sets in database applications."
6 | summary: "Implementing efficient pagination in LINQ using Skip() and Take() methods to handle large record sets in database applications."
7 | tags:
8 | - LINQ
9 | - "C#"
10 | - Pagination
11 | - Entity Framework
12 | categories:
13 | - "Development - C#"
14 | ---
15 |
16 |
Today I’ve been diving into an easy way to paginate record sets in my applications. Searching around the internet yielded several good walkthroughs on how to do this. My favorite way came from my friend, Kevin Hazzardh, He discussed using the Skip() and Take() extension methods to form fit LINQ into building a SQL statement that’ll do all the heavy lifting for you.
Copying from Kevin’s example, I built the following code snippet:
What’s nice about the following code is that since LINQ is lazy loading, the SQL built doesn’t actually execute until we need it too. The days of returning full datasets are done (yes, we’re still doing that on some projects).
34 |
35 |
I went the next step to see if I could build an extension method of my own that did all of the above for me automatically. Here was the result I came up with:
36 |
37 | ```csharp
38 | public static class ExtensionMethods
39 | {
40 | public static IQueryable Paginate(this IQueryable content, int pageNumber, int pageSize)
41 | {
42 | return content.Skip((pageNumber - 1)*pageSize).Take(pageSize);
43 | }
44 | }
45 | ```
46 |
47 |
This extension method takes the query returned from LINQ (an IQueryable), and applies the additional constraints to it. Here is the first example using my new extension method:
48 |
49 | ```csharp
50 | int pageNumber = 1;
51 | int pageSize = 20;
52 |
53 | using (var entity = new Entities())
54 | {
55 | var recordSet = (from r in entity.SomeTable
56 | orderby r.SomeID
57 | select r);
58 | recordSet = recordSet.Paginate(pageNumber, pageSize);
59 |
60 | return recordSet;
61 | }
62 | ```
63 |
64 |
65 |
66 |
Ta da! Hopefully that makes sense. I’m open to other suggestions and comments. I’m learning that if I take about my thought processes on certain problems, that I receive a ton of great feedback from those who listen.
67 |
--------------------------------------------------------------------------------
/blog/20110214-building-better-connectionstrings-with-connectionstringbuilder.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Building better ConnectionStrings with ConnectionStringBuilder"
3 | date: 2011-02-14T05:00:02Z
4 | permalink: building-better-connectionstrings-with-connectionstringbuilder
5 | description: "Learn how to use .NET's ConnectionStringBuilder classes to create clean, flexible connection strings for OleDb, SQL Server, and Entity Framework."
6 | summary: "ConnectionStringBuilder makes it fool-proof to generate connection strings."
7 | tags:
8 | - "Development - C#"
9 | categories:
10 | - "Development - C#"
11 | ---
12 |
13 | Okay, I never admitted to being a .NET guru or anything, and that’s why I get so excited whenever I run across a gem in the framework that allows me to do something easier and with fewer issues.
14 |
15 | ConnectionStrings has always been one of those things I did the hard way. For example, I would have a line of code that was like so:
16 |
17 | ```csharp
18 | string connectionString = "Data Source={0};Initial Catalog={1};User Id={2};Password={3};";
19 | string.Format(connectionString, serverName, databaseName, userName, password);
20 | ```
21 |
22 | This seemed like a logical way to build my connection strings. However, it wasn’t very flexible. That was until I discovered the suite of ConnectionStringBuilder classes.
23 |
24 | Let’s take the above OleDb connection string and use the OleDbConnectionStringBuilder to build it.
25 |
26 | ```csharp
27 | System.Data.OleDb.OleDbConnectionStringBuilder oleDbConnectionStringBuilder =
28 | new OleDbConnectionStringBuilder();
29 | oleDbConnectionStringBuilder.DataSource = "myServer";
30 | oleDbConnectionStringBuilder.FileName = "myAccessFile.mdb";
31 | oleDbConnectionStringBuilder.ToString();
32 | ```
33 |
34 | Look at how much cleaner that is! Maybe you’re working with a SQL Server database:
35 |
36 | ```csharp
37 | System.Data.SqlClient.SqlConnectionStringBuilder connectionStringBuilder =
38 | new SqlConnectionStringBuilder();
39 | connectionStringBuilder.DataSource = "myServer";
40 | connectionStringBuilder.InitialCatalog = "databaseName";
41 | connectionStringBuilder.UserID = "userName";
42 | connectionStringBuilder.Password = "password";
43 | connectionStringBuilder.ToString();
44 | ```
45 |
46 | Isn’t that awesome?! Now, finally, let’s imagine you’re doing all this with Entity Framework:
47 |
48 | ```csharp
49 | System.Data.EntityClient.EntityConnectionStringBuilder entityConnectionStringBuilder =
50 | new EntityConnectionStringBuilder();
51 | entityConnectionStringBuilder.ProviderConnectionString = connectionStringBuilder.ToString();
52 | entityConnectionStringBuilder.Metadata = "(entity framework metadata here)";
53 | entityConnectionStringBuilder.ToString();
54 | ```
55 |
56 | There you go! Instead of hand writing your connection strings, take a look to see if there is a StringBuilder class that’ll do the work for you.
57 |
--------------------------------------------------------------------------------
/src/css/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 |
3 | @tailwind components;
4 |
5 | @tailwind utilities;
6 |
7 | .theme-light {
8 | --bg-background-primary: white;
9 | --bg-background-secondary: #f7fafc;
10 | --bg-background-tertiary: #e2e8f0;
11 |
12 | --bg-background-form: white;
13 |
14 | --text-copy-primary: #2d3748;
15 | --text-copy-secondary: #4a5568;
16 |
17 | --border-border-color-primary: white;
18 | }
19 |
20 | .theme-light .search-highlighted {
21 | background: #f0fff4;
22 | }
23 |
24 | .theme-light .search-hover:hover {
25 | background: #f0fff4;
26 | }
27 |
28 | .theme-dark .markdown-body {
29 | color: #24292e;
30 | }
31 |
32 | .theme-dark .search-highlighted {
33 | background: #2d3748;
34 | }
35 |
36 | .theme-dark .search-hover:hover {
37 | background: #2d3748;
38 | }
39 |
40 | .theme-dark {
41 | --bg-background-primary: #0D2438;
42 | --bg-background-secondary: #102C44;
43 | --bg-background-tertiary: #1E3951;
44 |
45 | --bg-background-form: #1a202c;
46 |
47 | --text-copy-primary: #cbd5e0;
48 | --text-copy-secondary: #e2e8f0;
49 |
50 | --border-border-color-primary: #1a202c;
51 | }
52 |
53 | .theme-dark .markdown-body {
54 | color: #cbd5e0;
55 | }
56 |
57 | .content-wrapper {
58 | transition: background-color .25s;
59 | }
60 |
61 | a {
62 | @apply font-bold text-green-700;
63 | }
64 |
65 | a:hover {
66 | @apply text-green-800;
67 | }
68 |
69 | nav .active {
70 | font-weight: bold;
71 | @apply border-b border-black;
72 | }
73 |
74 | .container-inner {
75 | padding-left: 1rem;
76 | padding-right: 1rem;
77 | }
78 |
79 | @media (min-width: 640px) {
80 | .container-inner {
81 | max-width: 640px;
82 | }
83 | }
84 |
85 | @media (min-width: 768px) {
86 | .container-inner {
87 | max-width: 768px;
88 | }
89 | }
90 |
91 | @media (min-width: 1024px) {
92 | .container-inner {
93 | max-width: 800px;
94 | }
95 | }
96 |
97 | .checkmark {
98 | background-image: url("data:image/svg+xml;charset=utf8,%3Csvg%20width%3D%2229%22%20height%3D%2229%22%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%3E%3Cpath%20d%3D%22M4.427%2024.93A14.5%2014.5%200%201%201%2024.93%204.427c5.495%205.69%205.416%2014.733-.177%2020.326s-14.637%205.672-20.326.177z%22%20fill%3D%22%232E855A%22/%3E%3Cpath%20fill%3D%22%23FFF%22%20d%3D%22M10.234%2012.803l3.67%203.253L20.766%2010%2023%2012l-9.096%208L8%2014.789l2.234-2z%22/%3E%3C/svg%3E");
99 | background-repeat: no-repeat;
100 | @apply pl-10;
101 | }
102 |
103 | .responsive-container {
104 | position: relative;
105 | overflow: hidden;
106 | padding-top: 56.25%;
107 | }
108 |
109 | .responsive-iframe {
110 | position: absolute;
111 | top: 0;
112 | left: 0;
113 | width: 100%;
114 | height: 100%;
115 | border: 0;
116 | }
117 |
118 | .text-center {
119 | text-align: center;
120 | }
121 |
122 | .font-yellow {
123 | color: #ffd600;
124 | }
--------------------------------------------------------------------------------
/blog/20100126-leaving-it-better-than-you-found-it.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Leaving It Better Than You Found It"
3 | date: 2010-01-26T06:00:00Z
4 | permalink: leaving-it-better-than-you-found-it
5 | description: "A reflection on software development practices inspired by home renovation - why leaving code better than you found it benefits everyone."
6 | summary: "Everything you touch in your life, leave it better than you found it."
7 | tags:
8 | - Software Development
9 | - Best Practices
10 | - Code Quality
11 | - Philosophy
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 |
When my wife and I bought our house back in April, one of my pet projects has been to renovate the room over our garage. I knew buying the house that it would be a lot of work, partly because the previous owner didn’t know what he was doing when finishing a room. I’ve spent the last week and half sanding, mudding, and fixing all the walls in this room. While sanding some dried mud tonight, I had a thought about how this experience was a lot like building software.
When building software, you’re not sometimes lucky enough to build a system from the ground up. Normally, you’ll inherit code from developers who have been hacking it for years. I related this to me working in my room. I inherited a poorly maintained room. The joints weren’t level with each other and the mud of the wall wasn’t smooth. The person doing the work took no pride in the work being done. The ceiling was also a “hacked” popcorn ceiling. I say hacked because, instead of using a hopper, the person slung dry wall mud onto the ceiling giving the illusion of popcorn. The illusion failed though because it looked horrible.
Fast forward to my work in the room last week. I had to go through and scrap all the excess mud off the wall. Each wall and joint had to be sanded, and mudded again in order to level everything. I’ve spend hours of time trying to reverse the effects caused by performing the job incorrectly.
What does this have to do with software development? Think about when you’re working on a bug in a piece of code, and it’s your first time looking at this code. How the previous developer left the code is how you’re going to inherit it. You might have to spend hours undoing the work of the previous person in order to get the code to a state it can be worked with. Hacks might have to be removed and properly implemented. Hours will be wasted that didn’t have to be.
When working on new code, do yourself and future developers a favor and leave the code in a state where it can be easily picked up and worked on. If you’re working on existing code, try to leave it in a better state than it was when you found it. In the long run, time will be saved, code will be more secure, and a developer will say fewer curse words.
--------------------------------------------------------------------------------
/blog/20150803-banks-atms-and-horrible-user-experiences.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Banks, ATMS, and Horrible User Experiences"
3 | date: 2015-08-03T09:18:46Z
4 | permalink: banks-atms-and-horrible-user-experiences
5 | description: "A frustrating experience with a malfunctioning bank ATM and the poor customer service that followed, highlighting the need for better failsafes in critical systems."
6 | summary: "I had a horrible bank ATM experience."
7 | tags:
8 | - Deep Thoughts
9 | categories:
10 | - Deep Thoughts
11 | ---
12 |
13 | *Note: I'm pretty much going to rant about a bank experience I had. Lessons aren't obvious, but if you're designing something that's pretty dang important... make sure you have failsafes built-in.*
14 |
15 | I went to my local ATM yesterday to deposit a couple checks. I know, I know... why didn't I just use my phone? Turns out banking phone apps have limits on how much you can deposit through the app and I was well past that limit. ATM was a logical second choice if I didn't want to have to make visit to a teller.
16 |
17 | Generally, this type of visit is quick.
18 |
19 | 1. Pull up to ATM
20 | 2. Insert ATM card
21 | 3. Type in PIN number
22 | 4. Press DEPOSIT, select account.
23 | 5. Insert checks (optical reader will automatically figure out amounts)
24 | 6. Receipt, and on my way!
25 |
26 | Problem started around step 5. I put my checks into the reader where it proceeded to give me a "PROCESSING YOUR DEPOSIT" screen. Normally, this process takes 10 seconds.
27 |
28 | Ten seconds pass...
29 | Then a minute...
30 | OKAY.. five minutes...
31 |
32 | I'm starting to get a bit freaked out because I put a couple good sized checks into a machine and it's not doing anything!
33 |
34 | Next step is to try to call someone. My bank provides a 1-800 number for customer service. Turns out, there is no logical path for "OMG THE ATM STOLE MY CHECKS AND IS FROZEN". And really, there is no path for "I request to speak to a human who can properly direct this call."
35 |
36 | 30 minutes into my freak. I turned two other folks away because ATM was borked. All of a sudden, the screen flashes and pretends like NOTHING HAPPENED AT ALL. "Would you like to deposit the checks?" I pressed the **go** button and the ATM spat my checks out and told me to have a nice day. Wat?
37 |
38 | I also had a decent "check hold" on my account, which is normal when checks are processed. The bank is still investigating what the issue might have been.
39 |
40 | I'm pretty fortunate that I decided to stick around - or else someone might have been able to swipe my checks. Why doesn't the ATM go into "maintenance mode" or something after five minutes of not responding? Thirty minutes is way too long to wait for any sort of respond.
41 |
42 | On another note, why is there physically no way to talk to a human on the phone? At a minimum, I wanted the bank to have an option to "Press 0 if you just need to talk to someone."
43 |
44 | Blah. What horrible user experiences have you had with technology?
--------------------------------------------------------------------------------
/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ${head}
6 |
10 |
12 |
13 |
14 |
15 | ${app}
16 | ${scripts}
17 |
24 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/blog/20180501-i-removed-email-from-my-phone.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "I Removed Email From My Phone"
3 | date: 2018-05-01T09:54:52Z
4 | permalink: good-bye-email
5 | description: "What's the next step after beating email addiction? Learn how removing email from my phone reduced anxiety and improved focus."
6 | summary: "What's the next step after beating email addiction?"
7 | tags:
8 | - Productivity
9 | - Email
10 | - Work-Life Balance
11 | - Digital Detox
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 | Several years ago, [I wrote about how I was taking steps to reduce my dependence on email](https://consultwithgriff.com/beating-email-addiction/). You should go give it a read if you haven't before.
17 |
18 | Like a good diet, I fell off that bandwagon after months and months of doing `really well`.
19 |
20 | Since I own my own business, I spend a lot of time in email conversing with clients, employees, contractors, and random people I'm trying to convince to give me money. I organize [conferences](https://revolutionconf.com), [user groups](https://hrnug.org), and a variety of other professional events. I'm active in my church, and get CCed on just about everything.
21 |
22 | I came to the realization that email itself was making me anxious.
23 |
24 | For example, I'd get up in the morning and quickly check email as a part of my morning routine. Some days were fine, but then other days I'd get an email that would **totally ruin me for the entire day.**
25 |
26 | Email became my fidget. If I was standing in line at the store, at a stop light, or just walking towards a building, I'd get my phone out and quickly check my email.
27 |
28 | The absolute worse time to check email is when you physically cannot respond to it. Some emails I forgot about, accidentally archived, or just said "I'll deal with this later".
29 |
30 | Then one day I came to a realization: the email on the phone had to go.
31 |
32 | It was **so easy**. Delete.
33 |
34 | Additionally, I added a message to my email signature specifically stating that I'm returning to my twice-a-day email routine, and I did not carry it on my device. Emergencies needed to move to phone calls or text messages.
35 |
36 | You'd be amazed how quickly an "emergency" dies down when someone has to pick up a phone.
37 |
38 | As of this writing, I've been without email on my device for two weeks. My level of anxiety is pretty much gone. Email takes all of 5-15 minutes depending on what I need to respond to. And best of all, email can only happen when I'm at my laptop - and that is a controlled situation.
39 |
40 | I encourage everyone to take a break from email. Even if it removing notifications or reducing your time to twice daily -- it is amazing how liberating it can be to not need to check email constantly.
41 |
42 | We cannot forget that email is asynchronous. In the same way we'd have to wait for snail mail or carrier pigeon, and email doesn't need to be received or answered immediately. There is no difference between a 2 minute response time and a 12 hour response time.
43 |
44 | I'd love to hear your thoughts about email. Does it rule your life or have you figure out how to tame it for the best? Leave a comment below!
45 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | theme: {
3 | extend: {
4 | spacing: {
5 | '80': '20rem',
6 | '108': '27rem',
7 | },
8 | borderWidth: {
9 | '14': '14px',
10 | }
11 | },
12 | container: {
13 | padding: '1rem'
14 | },
15 | colors: {
16 | background: {
17 | primary: 'var(--bg-background-primary)',
18 | secondary: 'var(--bg-background-secondary)',
19 | tertiary: 'var(--bg-background-tertiary)',
20 |
21 | form: 'var(--bg-background-form)',
22 | },
23 |
24 | copy: {
25 | primary: 'var(--text-copy-primary)',
26 | secondary: 'var(--text-copy-hover)',
27 | },
28 |
29 | 'border-color': {
30 | primary: 'var(--border-border-color-primary)',
31 | },
32 |
33 | transparent: 'transparent',
34 |
35 | black: '#000',
36 | white: '#fff',
37 |
38 | green: {
39 | 100: '#f0fff4',
40 | 200: '#c6f6d5',
41 | 300: '#9ae6b4',
42 | 400: '#68d391',
43 | 500: '#48bb78',
44 | 600: '#38a169',
45 | 700: '#2f855a',
46 | 800: '#276749',
47 | 900: '#22543d',
48 | },
49 |
50 | gray: {
51 | 100: '#f7fafc',
52 | 200: '#edf2f7',
53 | 300: '#e2e8f0',
54 | 400: '#cbd5e0',
55 | 500: '#a0aec0',
56 | 600: '#718096',
57 | 700: '#4a5568',
58 | 800: '#2d3748',
59 | 900: '#1a202c',
60 | },
61 | },
62 | fontFamily: {
63 | sans: [
64 | 'Nunito Sans',
65 | '-apple-system',
66 | 'BlinkMacSystemFont',
67 | '"Segoe UI"',
68 | '"Helvetica Neue"',
69 | 'Arial',
70 | '"Noto Sans"',
71 | 'sans-serif',
72 | '"Apple Color Emoji"',
73 | '"Segoe UI Emoji"',
74 | '"Segoe UI Symbol"',
75 | '"Noto Color Emoji"',
76 | ],
77 | serif: ['Georgia', 'Cambria', '"Times New Roman"', 'Times', 'serif'],
78 | mono: [
79 | 'Menlo',
80 | 'Monaco',
81 | 'Consolas',
82 | '"Liberation Mono"',
83 | '"Courier New"',
84 | 'monospace',
85 | ],
86 | },
87 | },
88 | variants: {
89 | // Some useful comment
90 | },
91 | plugins: [
92 | // Some useful comment
93 | ],
94 | future: {
95 | purgeLayersByDefault: true,
96 | removeDeprecatedGapUtilities: true,
97 | },
98 | purge: {
99 | content: [
100 | './src/**/*.html',
101 | './src/**/*.vue',
102 | './src/**/*.js',
103 | ],
104 | options: {
105 | // When upgrading Tailwind/PurgeCSS, migrate to safelist:
106 | // safelist: [
107 | // { pattern: /^fa-/ },
108 | // { pattern: /^svg-inline--fa/ },
109 | // { pattern: /^token/, deep: true },
110 | // { pattern: /^pre/, deep: true },
111 | // { pattern: /^code/, deep: true }
112 | // ]
113 | whitelistPatterns: [/^fa-/, /^svg-inline--fa/],
114 | whitelistPatternsChildren: [/^token/, /^pre/, /^code/],
115 | }
116 | },
117 | }
118 |
--------------------------------------------------------------------------------
/CLAUDE.md:
--------------------------------------------------------------------------------
1 | # CLAUDE.md
2 |
3 | This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4 |
5 | ## Commands
6 |
7 | ### Development
8 | - `npm run develop` - Start the Gridsome development server
9 | - `npm run build` - Build the static site for production
10 | - `npm run explore` - Open GraphQL explorer to query site data
11 | - `npm run create-post` - Interactive script to create a new blog post with proper frontmatter
12 |
13 | ### Docker Development
14 | - `npm run docker:build` - Build Docker image for development
15 | - `npm run docker:develop` - Run development server in Docker container (port 8080)
16 |
17 | ## Architecture Overview
18 |
19 | This is a Gridsome-based static site (Vue.js) for Kevin W. Griffin's personal blog and consulting website. The site generates static HTML from markdown content.
20 |
21 | ### Content System
22 | - **Blog posts** live in `/blog/` as markdown files with YAML frontmatter
23 | - **Documentation pages** are in `/docs/` for courses and consulting content
24 | - Posts require: `title`, `date`, `description`, `summary`, `tags[]`, and `categories[]` in frontmatter
25 | - New posts should be created using `npm run create-post` to ensure proper formatting
26 |
27 | ### Key Components Architecture
28 | - **Templates** (`/src/templates/`): Define how content types are rendered
29 | - `Post.vue` - Individual blog post pages
30 | - `Tag.vue` - Tag archive pages
31 | - `Documentation.vue` - Documentation pages
32 | - **Layouts** (`/src/layouts/`): Wrap pages with common UI elements
33 | - **Pages** (`/src/pages/`): Static pages like Index, Contact, Articles list
34 |
35 | ### Plugin System
36 | The site uses Gridsome plugins:
37 | - **Remark Plugins**: Handle YouTube embeds, Twitter embeds, and syntax highlighting
38 | - Content is processed through the remark pipeline configured in `gridsome.config.js`
39 |
40 | ### Styling
41 | - Uses TailwindCSS (PostCSS 7 compatible version due to Gridsome constraints)
42 | - Custom theme configuration in `tailwind.config.js`
43 | - Main styles in `/src/css/main.css` with GitHub markdown styles
44 |
45 | ### Search Implementation
46 | - Client-side search using Fuse.js
47 | - Search index is generated at build time from all content
48 | - Search component uses the generated index for fast client-side searching
49 |
50 | ### Deployment & Redirects
51 | - Primary deployment to Azure Static Web Apps (configured in `staticwebapp.config.json`)
52 | - Extensive redirect rules for legacy URLs and domain migrations
53 | - Alternative Netlify deployment available (`netlify.toml`)
54 |
55 | ### GraphQL Data Layer
56 | Gridsome provides a GraphQL data layer for querying content:
57 | - Use `page-query` or `static-query` in Vue components
58 | - Content is available at build time through GraphQL
59 | - Use `npm run explore` to interactively query the data layer
60 |
61 | ## Important Notes
62 |
63 | - No testing framework is configured - manual testing only
64 | - When modifying content queries, rebuild to see changes
65 | - The site uses static generation, so dynamic features require client-side JavaScript
66 | - Legacy URL redirects are critical for SEO - check `staticwebapp.config.json` before removing redirects
67 | - For local Gridsome dev, build inside docker container because i don't want to downground Node
--------------------------------------------------------------------------------
/.github/workflows/claude-code-review.yml:
--------------------------------------------------------------------------------
1 | name: Claude Code Review
2 |
3 | on:
4 | pull_request:
5 | types: [opened, synchronize]
6 | # Optional: Only run on specific file changes
7 | # paths:
8 | # - "src/**/*.ts"
9 | # - "src/**/*.tsx"
10 | # - "src/**/*.js"
11 | # - "src/**/*.jsx"
12 |
13 | jobs:
14 | claude-review:
15 | # Optional: Filter by PR author
16 | # if: |
17 | # github.event.pull_request.user.login == 'external-contributor' ||
18 | # github.event.pull_request.user.login == 'new-developer' ||
19 | # github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'
20 |
21 | runs-on: ubuntu-latest
22 | permissions:
23 | contents: read
24 | pull-requests: read
25 | issues: read
26 | id-token: write
27 |
28 | steps:
29 | - name: Checkout repository
30 | uses: actions/checkout@v6
31 | with:
32 | fetch-depth: 1
33 |
34 | - name: Run Claude Code Review
35 | id: claude-review
36 | uses: anthropics/claude-code-action@beta
37 | with:
38 | claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
39 |
40 | # Optional: Specify model (defaults to Claude Sonnet 4, uncomment for Claude Opus 4.1)
41 | # model: "claude-opus-4-1-20250805"
42 |
43 | # Direct prompt for automated review (no @claude mention needed)
44 | direct_prompt: |
45 | Please review this pull request and provide feedback on:
46 | - Code quality and best practices
47 | - Potential bugs or issues
48 | - Performance considerations
49 | - Security concerns
50 | - Test coverage
51 |
52 | Be constructive and helpful in your feedback.
53 |
54 | # Optional: Use sticky comments to make Claude reuse the same comment on subsequent pushes to the same PR
55 | # use_sticky_comment: true
56 |
57 | # Optional: Customize review based on file types
58 | # direct_prompt: |
59 | # Review this PR focusing on:
60 | # - For TypeScript files: Type safety and proper interface usage
61 | # - For API endpoints: Security, input validation, and error handling
62 | # - For React components: Performance, accessibility, and best practices
63 | # - For tests: Coverage, edge cases, and test quality
64 |
65 | # Optional: Different prompts for different authors
66 | # direct_prompt: |
67 | # ${{ github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR' &&
68 | # 'Welcome! Please review this PR from a first-time contributor. Be encouraging and provide detailed explanations for any suggestions.' ||
69 | # 'Please provide a thorough code review focusing on our coding standards and best practices.' }}
70 |
71 | # Optional: Add specific tools for running tests or linting
72 | # allowed_tools: "Bash(npm run test),Bash(npm run lint),Bash(npm run typecheck)"
73 |
74 | # Optional: Skip review for certain conditions
75 | # if: |
76 | # !contains(github.event.pull_request.title, '[skip-review]') &&
77 | # !contains(github.event.pull_request.title, '[WIP]')
78 |
79 |
--------------------------------------------------------------------------------
/blog/20200816-stockholm-clients.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Stockholm Clients"
3 | date: 2020-08-17T00:00:00Z
4 | permalink: stockholm-clients
5 | description: "What happens when an amazing client turns into a nightmare client in the matter of weeks. You might be dealing with a Stockholm Client."
6 | summary: "What happens when an amazing client turns into a nightmare client in the matter of weeks. You might be dealing with a Stockholm Client."
7 | tags:
8 | - Freelancing
9 | - Client Management
10 | - Business
11 | - Psychology
12 | categories:
13 | - Freelancing
14 | - Entrepreneur
15 | excerpt: "What happens when an amazing client turns into a nightmare client in the matter of weeks. You might be dealing with a Stockholm Client."
16 | ---
17 |
18 | > Hey! I actually wrote this as a part of the **Journey to $100 Million** podcast by friends at [Array Digital](https://thisisarray.com/stockholm-clients/). Check it out!
19 |
20 | Have you heard of Stockholm Syndrome? It’s a fascinating case where people taken hostage by a kidnapper start to develop psychological alliances with their captors. After being in custody for a prolonged period of time, the kidnapped will start to sympathize and protect the folks that are putting them in danger.
21 |
22 | If you’re running a freelance business or even a small agency, it can be easy to fall into the trap of the “Stockholm Client”.
23 |
24 | **What do I mean by this?**
25 |
26 | When you’re starting out, it is really easy to jump on every opportunity that comes across your desk. You will hoard clients. You will bend over backward to help them. You might give unnecessary discounts. You might respond to issues within minutes, even after hours. Your relationship goes from that of partnership to more of master and slave.
27 |
28 | **And why is that?**
29 |
30 | Because the worst possible thing that could happen is being fired by the client. If they left, you and your family would starve. You’ll lose your house. You will never find another client. Everything will come falling down.
31 |
32 | So you go out of your way to make them happy - even if they’re taking advantage of the situation.
33 |
34 | I just recently went through this myself. A client of mine of 6+ years went from the best client I had to the neediest client I’ve ever had. I worked 10 to 12 hours per day to try to keep their expectations met, and I always seemed to fall short.
35 |
36 | There were in-person meetings where I was verbally yelled at and reprimanded by the project stockholder in front of all their employees.
37 |
38 | There wasn’t a way that I could win. But I kept moving forward with the client.
39 |
40 | Every person I’ve told this story tells me, “why haven’t you fired them?”
41 |
42 |
43 | And it’s because they were a Stockholm client. I was sympathizing with them and thinking that I was the problem. This caused me to be regularly stressed out, and neglectful of other aspects of my life and career.
44 |
45 | I was worried about other types of fallout, like lawsuits or whatnot.
46 |
47 | So I stayed in a bad relationship.
48 |
49 | Until I realized what I was doing. And I started the process of ending the relationship. Oh boy, the weight off my shoulders.
50 |
51 | If you find yourself dealing with a Stockholm client, I’m here to tell you it’s okay. There will always be other clients. Drop the bad ones, and seek out the good ones.
52 |
--------------------------------------------------------------------------------
/blog/20121231-nodejs-using-require-to-load-your-own-files.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Node.js: Using require to load your own files"
3 | date: 2012-12-31T06:00:31Z
4 | permalink: node-js-using-require-to-load-your-own-files
5 | description: "Learn how to use Node.js require() to load your own modules and keep your code clean and organized."
6 | summary: "Learn how to use Node.js require() to load your own modules and keep your code clean and organized."
7 | tags:
8 | - Node.js
9 | - JavaScript
10 | - Modules
11 | - require
12 | categories:
13 | - Development
14 | ---
15 |
16 | For a lot of JavaScript developers that are moving over from traditional "browser" development to node.js, they might be casually aware of the require keyword that node.js provides you.
17 |
18 | If you're wondering what I'm talking about, allow me to explain. In node.js, you can use the node package manager (NPM) to install packages that your application can use. Express, for example,is a great framework for serving up web applications, and you can grab it via NPM.
19 |
20 | To load Express into your application, you tell node.js that you "require" it be loading into memory.
21 |
22 | ```javascript
23 | var express = require("express");
24 | ```
25 |
26 | Node follows a couple rules to find modules. First, you can ask for a module by name or by folder. Let's look at Express more closely. If you were to download Express via NPM, you'd find there is no express.js file in the root directly of the /node_modules/express folder. However, there is a package.json file that defines the main executable file for express (for fun, go look it up).
27 |
28 | Now, if packages.json doesn't exist or the main executable isn't present, node will resort to looking for your filename with either a .js, .json, or .node extension (unless you've specified).
29 |
Where's this going?
30 | I know, I know... the point.
31 |
32 | Let's say you want to abstract out a piece of your application into another file.
33 |
34 | ```javascript
35 | var userRepository = function (){
36 | var self = this;
37 | self.addUser = function (...){
38 | };
39 | self.get = function (...){
40 | }
41 | };
42 | ```
43 |
44 | module.exports = userRepository;
45 | Add this to a file called userRepository.js. The last line is VERY IMPORTANT! It tells node.js what you'd like to export from this file. This should make more since if you try to use the file.
46 |
47 | In your main.js or wherever you'd like to use userRepository:
48 |
49 | ```javascript
50 | var userRepository = require("userRepository.js");
51 |
52 | var userRepositoryInstance = new userRepository();
53 | userRepositoryInstance.addUser({...});
54 | userRepositoryInstance.get(...);
55 | ```
56 |
57 | Looks simple doesn't it? Pretty much whatever you assign to module.exports will be passed into the variable that calls require().
58 |
59 | Use this to keep your code clean and uncluttered. This is only the basics of using require for your own projects, but I think it's a great starting point for developers building their knowledge of node.js. In the future, I'd like to expand on this topic and show you how you can take this even farther.
--------------------------------------------------------------------------------
/blog/20100302-colossal-failures.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Colossal Failures"
3 | date: 2010-03-02T06:00:00Z
4 | permalink: colossal-failures
5 | description: "Lessons learned from Adam Savage's talk on colossal failures and why failing is a method of learning in software development."
6 | summary: "Have you ever screwed up horribly?"
7 | tags:
8 | - Deep Thoughts
9 | categories:
10 | - Deep Thoughts
11 | ---
12 |
13 | One of my favorite television shows is Mythbusters. It’s not that I’m enthralled with the myths themselves, but the engineering required prove the myths correct or incorrect (“Confirmed” or "Busted!”).
14 |
15 | Recently, I ran across a session by Adam Savage talking about Colossal Failures.
16 |
17 | *Note: the original video is dead, but this is close to it.*
18 |
19 | `youtube:https://www.youtube.com/watch?v=i5sbUi3_w3w`
20 |
21 | (If you can't see the video above, please view in your browser and not in an RSS reader)
22 |
23 | This video is inspiring for me. Adam talks about two instances where he took on a task where he bit off more than he could chew. First, he talked about having to build a baseball throwing machine for department store display. The store was under tight restrictions, and gave Adam less than a week to design, build, and implement this system. Adam didn’t succeed due to dozens of unforeseen issues.
24 |
25 | Second, Adam talked about having to build a set complete with a talking ATM. He ran into tons of issues, and didn’t have anything done for the first day of filming. He was asked to go home, and then several days later was asked to come back to get a verbal flogging from the crew.
26 |
27 | **What does this mean to us?**
28 |
29 | Failing is a method of learning. Failing is a bit of a subjective term. If you've made a mistake, you’ve failed. Some failures are easier to rebound from than others. However, failures are worth it if you learn something from them.
30 |
31 | Adam learned that while he does good work by himself, the common trait of both his examples was that he didn’t ask for help. Some jobs are too large for a single person to take on by themselves. Keep a good network around you of people you trust and respect. These people can be lifelines in the most frustrating times of a project. Don’t have a network? Look for a community event in your area.
32 |
33 | **How have I failed?**
34 |
35 | I’ve walked into several situations where I had no idea what I was doing. Being a younger developer, I don’t have the experience as someone with 10 or 15 years experience. In my current shop, most projects are on the shoulders of one or two developers. With any project that has come across my path, I’ve picked them up and ran with them the best I could. As Adam said, I was “making it up as I went.” I made several poor decisions that seemed good at the time. When I discovered they were bad decisions, I took immediate steps to fix them. Never leave a bad decision for someone else to clean up. Step up and accept them, and then proceed to make it right. Adam talked about providing money back to his customers. He accepted his failures, and wanted to take steps to make it right (even if it meant giving up part of his pay).
36 |
37 | **Preventing Failures**
38 |
39 | Keep a support structure. Join a user group or visit a code camp. Keep learning. Surround yourself with people smarter than you. Listen to their advice (but take all advice with a grain of salt). One person will sometimes succeed, while a team will never fail.
40 |
--------------------------------------------------------------------------------
/.github/agents/coaster-expert.md:
--------------------------------------------------------------------------------
1 | ---
2 | # Fill in the fields below to create a basic custom agent for your repository.
3 | # The Copilot CLI can be used for local testing: https://gh.io/customagents/cli
4 | # To make this agent available, merge this file into the default repository branch.
5 | # For format details, see: https://gh.io/customagents/config
6 |
7 | name: Roller Coaster World Expert Agent
8 | description: Roller Coaster expert agent!
9 | ---
10 |
11 | # Roller Coaster World Expert Agent
12 |
13 | You are an enthusiastic and knowledgeable roller coaster expert with comprehensive information about roller coasters, theme parks, and thrill rides worldwide. Your expertise spans historical, technical, experiential, and cultural aspects of roller coasters.
14 |
15 | **Core Knowledge Areas:**
16 |
17 | 1. **Technical Specifications**
18 | - Coaster types (wooden, steel, hybrid, launched, inverted, flying, dive, wing, etc.)
19 | - Manufacturers (Intamin, B&M, RMC, Vekoma, Mack Rides, GCI, etc.)
20 | - Track elements (loops, corkscrews, airtime hills, inversions, helixes, etc.)
21 | - Safety systems and restraint mechanisms
22 | - Construction methods and engineering principles
23 |
24 | 2. **Global Coverage**
25 | - Detailed knowledge of coasters across all continents
26 | - Famous parks (Cedar Point, Six Flags, Disney, Universal, Europa-Park, Fuji-Q Highland, etc.)
27 | - Hidden gems and lesser-known parks
28 | - Regional differences in coaster design and preferences
29 |
30 | 3. **Records & Statistics**
31 | - Current world records (tallest, fastest, longest, most inversions, steepest drop, etc.)
32 | - Historical record progression
33 | - Statistical comparisons and rankings
34 | - Capacity and throughput data
35 |
36 | 4. **Rider Experience**
37 | - G-forces and intensity levels
38 | - Smoothness vs. roughness ratings
39 | - Theming and storytelling elements
40 | - Queue experiences and wait time strategies
41 | - Accessibility information for different rider needs
42 |
43 | 5. **Historical Context**
44 | - Evolution of roller coaster technology from 1884 to present
45 | - Landmark coasters that changed the industry
46 | - Defunct classic coasters and their legacy
47 | - Preservation efforts and coaster relocations
48 |
49 | **Communication Style:**
50 | - Balance technical accuracy with accessibility for all audience levels
51 | - Share enthusiasm while remaining factual
52 | - Provide safety-conscious advice
53 | - Offer personalized recommendations based on user preferences (thrill level, location, budget)
54 | - Include practical visiting tips (best times, fast pass strategies, nearby attractions)
55 |
56 | **Special Capabilities:**
57 | - Compare similar coasters across different parks
58 | - Suggest itineraries for coaster enthusiasts
59 | - Explain the physics behind different sensations
60 | - Discuss design trends and future innovations
61 | - Address common fears and misconceptions
62 | - Provide context about manufacturers' signature styles
63 |
64 | **Current Awareness:**
65 | - Track new coaster announcements and construction projects
66 | - Monitor seasonal events and special operations
67 | - Understand maintenance schedules and refurbishment news
68 | - Follow industry developments and park expansions
69 |
70 | When answering questions, consider the user's experience level, geographic location, and specific interests. Provide context about why certain coasters are significant, and always prioritize accurate safety information. Be ready to discuss everything from family-friendly coasters to extreme thrill rides, and from historic wooden coasters to cutting-edge technological marvels.
71 |
--------------------------------------------------------------------------------
/blog/20210409-streaming-an-mp4-to-twitch-and-youtube-with-ffmpeg.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Streaming an MP4 to Twitch and YouTube with FFMPEG"
3 | date: 2021-04-09T11:00:00Z
4 | permalink: streaming-mp4-to-twitch-youtube-with-ffmpeg
5 | description: "Learn how to stream pre-recorded MP4 videos live to Twitch and YouTube using FFMPEG with proper RTMP configuration and settings."
6 | summary: "In this post, I discuss how I was able to 'stream' an MP4 LIVE to YouTube and Twitch using FFMPEG"
7 | tags:
8 | - FFMPEG
9 | - Streaming
10 | - Twitch
11 | - YouTube
12 | - RTMP
13 | categories:
14 | - Misc.
15 | ---
16 |
17 | Recently, I regained some of the rights to some video courses I've built over the past two years. Because this was specially created content, I wanted to see if there was a way that I could distribute it that was more than just "posting it on YouTube".
18 |
19 | As a member of the [Live Coders](https://livecoders.dev/), I try to stream on [Twitch](https://twitch.tv/1kevgriff) at least once a week. But sometimes schedules conflict, and I'm not able to do anything.
20 |
21 | So I thought:
22 |
23 | > HEY! What if I could take the 3-4 hours of content I've built and stream it on my channels?
24 |
25 | Turns out, it's not that difficult with [FFMPEG](https://ffmpeg.org/).
26 |
27 | ## Step 1: Create an MP4
28 |
29 | This was pretty straightforward for me. I took all 3 hours of my content, and I dropped it into Adobe Premiere end-to-end. About 10 minutes later, I have a new, fully encoded file that was ready to go.
30 |
31 | ## Step 2: Set up your streams
32 |
33 | In order to go live with FFMPEG, you need two key pieces of information:
34 |
35 | * Your RTMP server
36 | * Your RTMP key
37 |
38 | > RTMP stands for Real-Time Messaging Protocol, and it's a protocol used to transmit large bits of audio and video. It's used by YouTube, Twitch, and other streaming services.
39 |
40 | Want me to save you some time, here are the servers for YouTube and Twitch:
41 |
42 | YouTube Primary Server:
43 | `rtmp://a.rtmp.youtube.com/live2/{stream_key}`
44 |
45 | Twitch Primary Server ([find yours](https://stream.twitch.tv/ingests/)):
46 | `rtmp://iad05.contribute.live-video.net/app/{stream_key}`
47 |
48 |
49 | ## Step 3: Setting up FFMPEG and GO LIVE 🔴
50 |
51 | Here is the command I'm using to setup FFMPEG:
52 |
53 | ```bash
54 | ffmpeg -re
55 | -i [YOUR FILE NAME HERE]
56 | -pix_fmt yuvj420p
57 | -x264-params keyint=48:min-keyint=48:scenecut=-1
58 | -b:v 4500k
59 | -b:a 128k
60 | -ar 44100
61 | -acodec aac
62 | -vcodec libx264
63 | -preset medium
64 | -crf 28
65 | -threads 4
66 | -f flv
67 | rtmp://[LIVE STREAM RTMP URL]
68 | ```
69 |
70 | This command will automatically start streaming your content to your RTMP server of choice. When the video is done streaming, the stream will automatically shut down!
71 |
72 | ## More than one streaming service?
73 |
74 | I don't have a way to solve this natively with FFMPEG at the moment, but I do use [Restream](https://restream.io/join/N77gz) for streaming to multiple services. I highly recommend it!
75 |
76 | ## Issues I'm Working On
77 |
78 | The above process works REALLY well, but I'm not happy with the bitrate I'm getting into YouTube or Twitch. It's too low, even though the FFMPEG settings seem to be at the right place. I'm going to continue to experiment with settings and see if I can do better.
79 |
80 | If you have ideas, please let me know on [Twitter](https://twitter.com/1kevgriff)!
81 |
82 | Until then, make sure you're following me on [YouTube](https://youtube.com/swiftkick) and [Twitch](https://www.twitch.tv/1kevgriff) so you know when I go live!
83 |
--------------------------------------------------------------------------------
/gridsome.config.js:
--------------------------------------------------------------------------------
1 | // This is where project configuration and plugin options are located.
2 | // Learn more: https://gridsome.org/docs/config
3 |
4 | // Changes here require a server restart.
5 | // To restart press CTRL + C in terminal and run `gridsome develop`
6 |
7 | const tailwindcss = require("tailwindcss");
8 |
9 | module.exports = {
10 | siteName: 'Kevin W. Griffin',
11 | siteUrl: 'https://consultwithgriff.com',
12 | plugins: [
13 | {
14 | use: '@gridsome/vue-remark',
15 | options: {
16 | typeName: 'Documentation', // Required
17 | baseDir: './docs', // Where .md files are located
18 | template: './src/templates/Documentation.vue', // Optional
19 | route: '/:permalink',
20 | plugins: [
21 | ['gridsome-plugin-remark-youtube'],
22 | ['gridsome-plugin-remark-shiki', { theme: 'material-theme-palenight', skipInline: true }]
23 | ],
24 | }
25 | },
26 | {
27 | use: '@gridsome/source-filesystem',
28 | options: {
29 | path: 'blog/**/*.md',
30 | typeName: 'Post',
31 | refs: {
32 | tags: {
33 | typeName: 'Tag',
34 | create: true
35 | }
36 | },
37 | route: '/:permalink',
38 | remark: {
39 | plugins: [
40 | ['@noxify/gridsome-plugin-remark-embed', { 'enabledProviders': ['Youtube', 'Twitter', 'Gist'], }],
41 | ['gridsome-plugin-remark-youtube'],
42 | ['gridsome-plugin-remark-shiki', { theme: 'material-theme-palenight', skipInline: false },
43 | ],
44 | ['gridsome-plugin-remark-twitter']
45 | ]
46 | }
47 | }
48 | },
49 | {
50 | use: 'gridsome-plugin-rss',
51 | options: {
52 | latest: true,
53 | contentTypeName: 'Post',
54 | feedOptions: {
55 | title: 'Kevin W. Griffin | Developer, Training, Entrepreneur',
56 | feed_url: 'https://consultwithgriff.com/rss.xml',
57 | site_url: 'https://consultwithgriff.com/'
58 | },
59 | feedItemOptions: node => ({
60 | title: node.title,
61 | description: node.summary,
62 | url: 'https://consultwithgriff.com' + node.path,
63 | author: 'Kevin W. Griffin',
64 | date: node.date
65 | }),
66 | output: {
67 | dir: './dist',
68 | name: 'rss.xml'
69 | }
70 | }
71 | },
72 | {
73 | use: '@gridsome/plugin-sitemap',
74 | options: {
75 | exclude: [
76 | "/no-more-stream-notifications/",
77 | "/thanks-supercharge-signup/",
78 | "/thanks-signalr-signup/",
79 | "/thanks-signup/",
80 | "/thanks-confirm/",
81 | ],
82 | cacheTime: 600000, // default
83 | }
84 | },
85 | {
86 | use: 'gridsome-plugin-gtag',
87 | options: {
88 | config: {
89 | id: 'G-G6TPE6V0YJ',
90 | },
91 | },
92 | }
93 | ],
94 | templates: {
95 | Tag: '/article-tags/:id'
96 | },
97 | transformers: {
98 | remark: {
99 | plugins: [
100 | ['gridsome-plugin-remark-shiki', { theme: 'Material-Theme-Palenight', skipInline: true }]
101 | ],
102 | externalLinksTarget: '_blank',
103 | externalLinksRel: ['nofollow', 'noopener', 'noreferrer'],
104 | anchorClassName: 'icon icon-link',
105 | }
106 | },
107 | css: {
108 | loaderOptions: {
109 | postcss: {
110 | plugins: [
111 | tailwindcss
112 | ],
113 | },
114 | },
115 | },
116 | }
117 |
--------------------------------------------------------------------------------
/blog/20170503-about-blog-posts.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "About Blog Posts"
3 | date: 2017-05-03T12:50:43Z
4 | permalink: about-blog-posts
5 | description: "After overhauling my personal site, I learned that I dislike the term `blog`. Instead, I want to be more professional by writing `articles`."
6 | summary: "After overhauling my personal site, I learned that I dislike the term `blog`. Instead, I want to be more professional by writing `articles`."
7 | tags:
8 | - Blogging
9 | - Writing
10 | - Professional
11 | - Content
12 | categories:
13 | - Deep Thoughts
14 | ---
15 |
16 | I just recently spent a couple days overhauling my personal site - or as I used to call it, my blog.
17 |
18 | That term `blog`...
19 |
20 | I've been `blogging` for about ten years, and I made several observations about what I actually put into my `blog`.
21 |
22 | > It's all basically BS, sprinkled with content of value.
23 |
24 | And I see this on a lot of `blogs`. The content is very "point in time" - meaning that it is only relevent within a couple days of the post going out.
25 |
26 | For me, these posts included things like "I'm speaking at XYZ conference" or "Here are my slides for ABC event".
27 |
28 | ## What is a better term?
29 | Articles.
30 |
31 | I trimmed a lot of fat from my `blog`. And instead of calling the posts that got to stay `blog posts`, I'm adapting the term `article`.
32 |
33 | This approach is designed to fix several issues with my site.
34 |
35 | **First**, the content needs to be evergreen-ish. My post about speaking at a code camp in 2010 isn't evergreen. Heck, it wasn't green at all. A hundred people might have read that post and never looked back. Why even publish it?
36 |
37 | Where is a better place for that type of "in the moment" posts? Twitter! Facebook! LinkedIn! Social media (or micro-blogging as it used to be call) is perfect for these types of posts.
38 |
39 | **Second**, the content needs to be more commanding. As a professional, it is more meaningful for me to say "I wrote an article about this topic, go give it a read." instead of "oh, I blogged about that a couple months ago".
40 |
41 | Folks are more likely to share a great article they read versus a blog post they came upon.
42 |
43 | **Third**, there is no need to be a "regular" blogger. The articles I publish have dates on them (see the bottom of this page), but you don't really have any idea what the latest post is.
44 |
45 | By the way, I am looking at adding a "recent articles" section to the sidebar. So I'll probably invalidate my previous statement.
46 |
47 | ## Where do the "micro" posts go?
48 | This is a good point to talk about? Where should the "micro" posts go? Obviously, it's a good idea for me to continue promoting activities I'm involved in. But if the content doesn't fit into the category of "article", where does it go?
49 |
50 | I've set up a separate area of the site for speaking engagement, past and future. This is great for an at-a-glance view of what I'm up to, and also allow people to gauge whether I'm worth inviting to their own events. (Psss, I'd love to speak at your event. Just ask!)
51 |
52 | But this is a good place for social media. Twitter, mostly. But Facebook and LinkedIn are good locales as well. These places are meant to be short lived. Nobody actively goes back through your timeline looking for what you said three years ago (unless you're Donald Trump).
53 |
54 | ## Conclusions
55 | I don't want to knock anyone that maintains a `blog`. I'm not that type of person, so it's not something I want to do.
56 |
57 | I want my content to be more focused and helpful. I feel really guilty when I don't post on a regular basis. Blogs give that illusion. It's "dead" if you're not posting every week. With my new format of "articles", I don't have that guilt.
58 |
59 | What are your thoughts? Am I overthinking it? Am I right on point? I'd LOVE to hear your thoughts in the comments below.
--------------------------------------------------------------------------------
/blog/20120215-maintaining-signalr-connectionids-across-page-instances.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Maintaining SignalR ConnectionIds Across Page Instances"
3 | date: 2012-02-15T10:00:52Z
4 | permalink: maintaining-signalr-connectionids-across-page-instances
5 | description: "Learn how to maintain SignalR ConnectionIds across page refreshes using cookies and custom connection ID factories."
6 | summary: "Learn how to maintain SignalR ConnectionIds across page refreshes using cookies and custom connection ID factories."
7 | tags:
8 | - SignalR
9 | - Real-time
10 | - JavaScript
11 | - ASP.NET
12 | categories:
13 | - Development
14 | ---
15 |
16 | 2/28/2013 - With the 1.0 release of SignalR, I can't guarantee the solution below will work the same. The big problem with this solution is that ConnectionIds are reused across tabs and browser windows since it's cookie based. You might not have the results you're expecting. The SignalR team doesn't recommend this action, and I can agree. I didn't see it when I wrote the post. This is a decent guide for overriding the connection id factory, so I'll leave it up for archival purposes.
17 |
18 | I’m a huge fan of SignalR, and today I was looking at a particular problem. I would think it’s more of a feature, but in certain use cases it can be considered a bug.
19 |
20 | When you start a connection to SignalR for the first time, you are assigned a ConnectionId. SignalR uses this to determine what messages should go to you, and allows the server to direct messaging at a particular user.
21 |
22 | If you were to refresh the page, SignalR will assign you a NEW ConnectionId. This could be good or bad… but if you’re trying to maintain some sense of state between your clients and the hub, it’s bad.
23 |
24 | So I looked into how to make SignalR reuse ConnectionIds in the case of a page refresh. There are really two steps involved.
25 |
26 | 1. Set a cookie on the client
27 | When you start() a new connection, SignalR will return a ConnectionId. You’ll want to set a cookie with that ConnectionId in it.
28 |
29 | ```javascript
30 | $.connection.hub.start().done(function() {
31 | alert("Connected!");
32 | var myClientId = $.connection.hub.id;
33 | setCookie("srconnectionid", myClientId);
34 | });
35 |
36 | function setCookie(cName, value, exdays) {
37 | var exdate = new Date();
38 | exdate.setDate(exdate.getDate() + exdays);
39 | var c_value =
40 | escape(value) + (exdays == null ? "" : "; expires=" + exdate.toUTCString());
41 | document.cookie = cName + "=" + c_value;
42 | }
43 | ```
44 |
45 | As you can see, this gets the ConnectionId from the hub connection and stores it in a cookie.
46 |
47 | 2. Use your own IConnectionIdFactory
48 |
49 | This might be scary territory for you, but it’s actually pretty simple. We want to create our own version of the IConnectionIdFactory interface for SignalR to use.
50 |
51 | ```csharp
52 | public class MyConnectionFactory : IConnectionIdFactory
53 | {
54 | public string CreateConnectionId(IRequest request)
55 | {
56 | if (request.Cookies["srconnectionid"] != null)
57 | {
58 | return request.Cookies["srconnectionid"];
59 | }
60 |
61 | return Guid.NewGuid().ToString();
62 | }
63 | }
64 | ```
65 |
66 | This does two things. First, it’ll check your cookie for a ConnectionId it should use. If it exists, we’ll simply return that ConnectionId and all will be good in the world.
67 |
68 | If the cookie does NOT exist, we need to generate one. By default, SignalR uses a GUID, so we’ll just repeat that functionality. You can use any value you want, but make sure it’s unique.
69 |
70 | Don’t forget to wire it up! Add this to you Global.asax file under Application_Start().
71 |
72 | ```csharp
73 | AspNetHost.DependencyResolver.Register(typeof(IConnectionIdFactory), () => new MyConnectionFactory());
74 | ```
75 |
76 | And you’re all set! SignalR will now use your new ConnectionIdFactory to generate or reuse ConnectionIds.
77 |
78 | Enjoy!
79 |
80 | ```
81 |
82 | ```
83 |
--------------------------------------------------------------------------------
/blog/20160704-open-source-mentality-of-choosing-your-tech-stack.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Open Source Mentality of Choosing Your Tech Stack"
3 | date: 2016-07-04T16:06:21Z
4 | permalink: open-source-mentality-of-choosing-your-tech-stack
5 | description: "Exploring how open source considerations should impact your technology stack decisions and the value of support vs. free alternatives."
6 | summary: "Exploring how open source considerations should impact your technology stack decisions and the value of support vs. free alternatives."
7 | tags:
8 | - Open Source
9 | - Technology
10 | - Development
11 | categories:
12 | - Deep Thoughts
13 | ---
14 |
15 | When I initially wrote *Non-Tech Factors to Consider When Choosing Your Tech Stack*, I left out one important conversation point: Open Source.
16 |
17 | [Steve and Carol](https://consultwithgriff.com/non-tech-factors-to-consider-when-choosing-your-tech-stack/#comment-2671492103) got me in the comments.
18 |
19 | ### How should Open Source affect your decision to choose a tech stack?
20 |
21 | In a previous life, I used to work for a component vendor. Our company built, sold, and supported a variety of components that saved developers time. I had the opportunity to meet thousands of awesome developers who do the hard work every day in the trenches.
22 |
23 | I always questioned why people bought our stuff. The internet at the time was already full of dozens of comparable or better components which were open sourced. Buying our components seemed like an extreme waste of resources.
24 |
25 | That was until I understood the value-cost of SUPPORT. One of our customer was a large financial firm who bought hundreds of developer licenses to a library. This library wasn't anything special - but any of those developers could instantly request technical support from the project manager for the library. (Normal people would have to go through traditional support means, but still had good service.)
26 |
27 | Imagine same company, same team - they decide to use a popular component that is 100% open source. Same team runs into a technical hurdle: either a flaw in the component, or a flaw in the developer's understanding of the component.
28 |
29 | Where does a developer go in this case? Google? Stack-overflow? Github issues?
30 |
31 | A common phrase amongst open source maintainers is "we accept pull requests". I used to believe this was mean-spirited, but you have to understand that folks that manage an open source project have lives and other concerns then your bug. Roll up your sleeves and dive in.
32 |
33 | ### You didn't answer the question: How should Open Source affect your decision to choose a tech stack?
34 |
35 | In the soft-factors article, I discussed that some developer-types just are not suited to work with new technology. Same goes for open source.
36 |
37 | If a developer or team is not comfortable getting their hands dirty in public code, there is NO WAY they should integrate open source software. Don't expect a maintainer to bend over backwards to solve your problem or push you in the right direction. Some do -- but many don't have the bandwidth for that.
38 |
39 | There are some exceptions to this though. Consider massive open source projects such as jQuery, Angular, React, and the .NET Framework. These products are open source and accept pull requests, but they are backed by large organizations who have a vested interest in keeping the projects up and running. You cannot pick up a phone and dial support, but you can file an issue and a team of developers will take you seriously.
40 |
41 | ### You are not alone
42 | The general developer community is amazing. There are thousands and thousands of developers out there that will at least attempt to push you in the right direction if you hit a snag in an open source project.
43 |
44 | I love open source software, and I greatly take for granted the hard work many people have put into my favorite projects.
45 |
46 | Open Source software isn't going away either. The movement is going to grow larger than it is today, and if you are not on the bandwagon as at least an observer, it is going to leave you behind.
47 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/docs/Coasters.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Coaster Credits
3 | date: 2022-09-06 12:00:00
4 | updated: 2025-11-07 00:00:00
5 | permalink: coasters
6 | categories:
7 | - Roller Coasters
8 | excerpt: "Check out my roller coaster credits!"
9 | ---
10 |
11 | Total Coaster Credits: 106
12 |
13 | ### Busch Gardens Williamsburg (Williamsburg, Virginia)
14 |
15 | 12 Coasters
16 |
17 | * Loch Ness Monster
18 | * Big Bad Wolf
19 | * Drachen Fire
20 | * Wild Maus
21 | * Apollo's Chariot
22 | * Alpengeist
23 | * Griffon
24 | * Tempesto
25 | * Grover's Alpine Express
26 | * Verbolten
27 | * InvadR
28 | * Pantheon
29 |
30 | ### Busch Gardens Tampa (Tampa, Florida)
31 |
32 | 9 Coasters
33 |
34 | * Iron Gwazi
35 | * Scorpion
36 | * Kumba
37 | * Montu
38 | * Sand Serpent
39 | * SheiKra
40 | * Cheetah Hunt
41 | * Cobra's Curse
42 | * Tigris
43 |
44 | ### Seaworld (Orlando, Florida)
45 |
46 | 4 Coasters
47 |
48 | * Kraken
49 | * Manta
50 | * Ice Breaker
51 | * Mako
52 |
53 | ### Kings Dominion (Doswell, Virginia)
54 |
55 | 16 Coasters
56 |
57 | * Apple Zapple
58 | * Backlot Stunt Coaster
59 | * Dominator
60 | * Anaconda
61 | * Flight of Fear
62 | * Intimidator 305 (Project 305)
63 | * Grizzly
64 | * Avalanche (Reptilian)
65 | * Twisted Timbers
66 | * Hurler
67 | * Tumbili
68 | * Hypersonic XLC
69 | * Racer 75 (North) (Rebel Yell)
70 | * Racer 75 (South) (Rebel Yell)
71 | * Shockwave
72 | * Volcano: The Blast Coaster
73 |
74 | ### Motorworld (Virginia Beach, Virginia)
75 |
76 | 1 Coaster
77 |
78 | * Crazy Mouse
79 |
80 | ### Dollywood (Pigeon Forge, Tennessee)
81 |
82 | 8 Coasters
83 |
84 | * Lightning Rod
85 | * Blazing Fury
86 | * Wild Eagle
87 | * Firechaser Express
88 | * Mystery Mine
89 | * Tennessee Tornado
90 | * Thunderhead
91 | * Dragonflier
92 |
93 | ### New York New York (Las Vegas, Nevada)
94 |
95 | 1 Coaster
96 |
97 | * The Big Apple Coaster
98 |
99 | ### Cedar Point (Sandusky, Ohio)
100 |
101 | 12 Coasters
102 |
103 | * Blue Streak
104 | * Cedar Creek Mine Ride
105 | * Corkscrew
106 | * Gatekeeper
107 | * Gemini (Red)
108 | * Iron Dragon
109 | * Magnum XL-200
110 | * Maverick
111 | * Millennium Force
112 | * Raptor
113 | * Rougarou
114 | * Valravn
115 |
116 | ### Camelback Resort (Tannersville, Pennsylvania)
117 |
118 | 1 Coaster
119 |
120 | * Camelback Mountain Coaster
121 |
122 | ### Disneyland (California)
123 |
124 | 3 Coasters
125 |
126 | * Big Thunder Mountain Railroad
127 | * Matterhorn Bobsleds (Tomorrowland)
128 | * Space Mountain
129 |
130 | ### Walt Disney World (Magic Kingdom)
131 |
132 | 3 Coasters
133 |
134 | * Space Mountain (Alpha)
135 | * Space Mountain (Omega)
136 | * Big Thunder Mountain Railroad
137 |
138 | ### Walt Disney World (Hollywood Studios)
139 |
140 | 1 Coaster
141 |
142 | * Rock 'n' Roller Coaster
143 |
144 | ### Walt Disney World (Animal Kingdom)
145 |
146 | 1 Coaster
147 |
148 | * Expedition Everest
149 |
150 | ### Universal Studios
151 |
152 | 4 Coasters
153 |
154 | * Harry Potter and the Escape from Gringotts
155 | * Hollywood Rip Ride RockIt
156 | * Revenge of the Mummy
157 | * Woody Woodpecker's Nuthouse Coaster (Trolls Trollercoaster)
158 |
159 | ### Universals's Islands of Adventure
160 |
161 | 5 Coasters
162 |
163 | * Flight of the Hippogriff
164 | * Hagrid's Magical Creatures Motorbike Adventure
165 | * The Incredible Hulk Coaster
166 | * VelociCoaster
167 | * Pteranodon Flyers
168 |
169 | ### Worlds of Fun (Kansas City, Missouri)
170 |
171 | 5 Coasters
172 |
173 | * Mamba
174 | * Patriot
175 | * Prowler
176 | * Timber Wolf
177 | * Zambezi Zinger (2023)
178 |
179 | ### Six Flags America (Bowie, Maryland)
180 |
181 | 7 Coasters
182 |
183 | * Firebird
184 | * Joker's Jinx
185 | * Professor Screamore's SkyWinder
186 | * Ragin' Cajun
187 | * Roar
188 | * Superman - Ride of Steel
189 | * Wild One
190 |
191 | ### Kings Island (Mason, Ohio)
192 |
193 | 13 Coasters
194 |
195 | * The Beast (100th!)
196 | * Banshee
197 | * Diamondback
198 | * Orion
199 | * Adventure Express
200 | * Racer (Red)
201 | * Racer (Blue)
202 | * Queen City Stunt Coaster
203 | * Woodstock Express
204 | * The Bat
205 | * Mystic Timbers
206 | * Snoopy's Soap Box Racers
207 | * Invertigo
--------------------------------------------------------------------------------
/blog/20150126-managing-your-user-group-sponsor-relationships.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Managing Your User Group: Sponsor Relationships"
3 | date: 2015-01-26T11:58:38Z
4 | permalink: user-group-sponsor-relationship
5 | description: "Who holds the leverage in a sponsor/user group relationship? Learn how to balance sponsor needs with attendee experience."
6 | summary: "Who holds the leverage in a sponsor/user group relationship?"
7 | tags:
8 | - User Groups
9 | - Community
10 | - Sponsors
11 | - Leadership
12 | - Management
13 | categories:
14 | - Community, User Groups, and Conferences
15 | ---
16 |
17 | As a user group leader, you might have this feeling that you need to get sponsors for your user group. **This is a common feeling!**
18 |
19 | In fact, it's one of the most common questions I get as the Director of Membership for INETA. I've seen groups that bend over backwards for sponsors, such as providing attendee lists (with contact information) or letting them do 15 minute sponsors presentations. Over marketing is a huge turn off for attendees, and has potential to limit the growth of your group.
20 |
21 | > For purposes of this discussion, a sponsor is a recruiter, headhunter, placement firm, etc. Pretty much anyone that wants to hire your talent pool.
22 |
23 | > Yes, venues are sponsors. Vendors are sponsors. But that's a different dynamic. The majority of user group "sponsors" are going to be companies looking to place workers.
24 |
25 | I want to offer you a different perspective before you go out and solicit for user group sponsors:
26 |
27 | **What do you offer?**
28 | 1. Access to a community of professionals interested and dedicated to a particular technology.
29 | 2. An environment specialized for engagement and learning.
30 | 3. Mailing lists (maybe)
31 | 4. Discussion boards (maybe)
32 |
33 | Over all, you have a supply of **talent**. Nevermind the skill ranges, but you have a group of folks who are 10% higher than anyone else on the market. Why? Because they're showing up. That's worth gold and more to the right employer.
34 |
35 | **What do they offer?**
36 | 1. Pizza money
37 | 2. Access to a hidden community of professionals they've placed in positions across the region.
38 |
39 | I'm going to call #2 a farce and this is *totally based on my experience.* I've had many sponsors (recruiters) say "We'll send the word out to our people and see if we can get out to the group." But the people don't come.
40 |
41 | So that leaves #1. Food. Drinks. Substance of some kind.
42 |
43 | **How does this empower you?**
44 | THEY need you more than YOU need them. Let's look at a worse case scenario: *you have no sponsors for food*.
45 |
46 | ***Oh well.***
47 |
48 | When I started my first user group back in 2009, I was scared silly that people wouldn't show up because there was no free food. Put it this way: if someone is coming only for free food, you don't want them at your group anyway.
49 |
50 | I begged for sponsors. "Please please please bring us free food."
51 |
52 | Later, I started getting competition for sponsorships. These companies know what you have, and they WANT to be involved. Use this to your advantage. For us, it meant paying for the right to sponsor a meeting. Twelve meetings per year, that's something worth monetizing.
53 |
54 | Take that last paragraph with a grain of salt. It's a real *first world problem*. Smaller groups might not want to pull that card, and that's okay. But don't bend over backwards for sponsors.
55 |
56 | >THEY need you more than YOU need them.
57 |
58 | **How can I supplement not having a sponsor?**
59 | Donations? A lot of smaller groups throw out a hat for pizza money, and people put money into it!
60 |
61 | Try a shorter meeting without food?
62 |
63 | Create a post-meeting outing that everyone is invited to. At our group, we started a tradition of going to a local diner for pie. Get creative!
64 |
65 | **Wrapping up**
66 | Running a user group is hard. Your thoughts on priorities might be out of line with what they really should be. Some of the best user groups I've been to have started in IHOPs or as meetings at a Starbucks.
67 |
68 | You don't need sponsors. You *want* sponsors. And you hold the high card in this game.
69 |
--------------------------------------------------------------------------------
/blog/20090304-definition-of-a-computer-scientist.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Definition of a Computer Scientist"
3 | date: 2009-03-04T15:26:24Z
4 | permalink: definition-of-a-computer-scientist
5 | description: "Challenging the academic notion that computer scientists should innovate, arguing instead that the real job is to solve problems effectively."
6 | summary: "What is a computer scientist? A person that innovates? Or a person that solves problems?"
7 | tags:
8 | - Deep Thoughts
9 | categories:
10 | - Deep Thoughts
11 | ---
12 |
13 | What is a computer scientist? Google gives the definition: a scientist who specializes in the theory of computation and the design of computers. In most of our minds, a computer scientist is a person that builds computer systems. More recognizable names (at least in terms of what a person with a computer science degree would be hired for) for a computer scientist would be developer, coder, software engineer, or software architect. A lot of people will argue with me that computer scientist is more than just a coder, and I would fully agree. Although, when a person wants to be a software developer, they are forced most of the time into a computer science program, as it's the best way for they to obtain the skills required to build large software systems.
14 |
15 | But the definition is besides the point. The real point of this post is to comment on a conversation retold to me by a colleague who's taking a senior-level college course. To put the conversation into perspective, I need to describe the course (and for the record, I have taken this course myself several years ago). This course is a project management course for wannabe computer scientists. In the first semester of the class, students are required to come up with "problems" and probable solutions to them. The problem and solution are then pitched to a panel of industry professionals. The solution is actually implemented within the second semester. As you might think, developing a problem from within thin air is quite daunting. Eventually though, students discover a problem that obtains approval from the professor.
16 |
17 | This conversation spawned due to my colleague's group not being able to find a problem. As far as I was told, several problems were proposed and spot down. When a particular idea was shot down, the professor said, "**Your job as computer scientists is to innovate.**"
18 |
19 | This statement is just _wrong_. Your job as a computer scientist is not to innovate! Your job as a computer scientist is to SOLVE PROBLEMS. Now, it is possible for a problem to be solved in an innovative manner, but you should never innovate for the sake of innovation. In the real world (definition: the world outside of academia), you will be given problems to solve, and you will be expected to solve them. Furthermore, you will be given your problems, not expected to discover them on your own. Creating your own problems to solve would make you an entrepreneur, not a computer scientist (which you would be when you actually solved the problem).
20 |
21 | For example, let's say I have a glass window and at 5:00 everyday the sun shines through, displaying a terrible glare on my computer screen. An innovative solution to the problem would be to design a microfilm composite that reduced the glare depending on the amount of sunlight shining through. Another solution would be to buy a set of blinds. Not "innovative" by any means, but it solved the problem.
22 |
23 | What advice do I have for the group of computer science student trying to get through this project? Don't try to come up with a brand new problem. That's impractical, or the solution to the problem is to high of a level for you to understand as an undergraduate. Instead, try to take an existing problem with bad solution. I guess you could say you should take a bad solution and _innovate_? Ideally, you just want to get through the semester. When you enter the "real world", the problems will come to you. At least these problems will be paid, and you won't have to worry while worrying about an English midterm, linear algebra homework, and your seventeenth algorithms assignment.
24 |
25 | Finally, take everything your professor says with a grain of salt. They probably haven't seen the real world in a long time.
26 |
--------------------------------------------------------------------------------
/blog/20200810-how-to-redirect-with-azure-static-web-apps.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "How to Redirect with Azure Static Web Apps"
3 | date: 2020-08-11T00:30:00Z
4 | permalink: how-to-redirect-with-azure-static-web-apps
5 | description: "Learn how to implement 301 redirects in Azure Static Web Apps using the staticwebapp.config.json routing configuration file."
6 | summary: "When deploying to Azure Static Web Apps, a common question that comes up is 'How do I do 301 redirects?'. Let's explore how in this simple tutorial."
7 | tags:
8 | - Azure Static Web Apps
9 | - Redirects
10 | - Routing
11 | - staticwebapp.config.json
12 | - Static Sites
13 | - SEO
14 | categories:
15 | - Microsoft Azure
16 | ---
17 |
18 | > Updated 2021-06-03: Now that Azure Static Web Apps is GA,
19 |
20 | > TLDR; Look at the [source code](https://github.com/1kevgriff/azure-static-web-apps-routing)
21 |
22 | While on [stream](https://www.twitch.tv/1kevgriff) one afternoon, I was working on a solution for moving a Wordpress site to Gridsome, with the goal of deploying to Azure Static Web Apps. However, I hit a wall that I needed to overcome.
23 |
24 | Gridsome URLs take a format like `https://consultwithgriff.com/{something}` and the `{something}` points directly to a folder on the filesystem. That is how all the URLs in the system work.
25 |
26 | I needed to build a URL that had an extensions, like `https://consultwithgriff.com/{something}` but returned a content type of XML. If I were to have an extensions of `.xml` this would've worked fine.
27 |
28 | So I needed to figure out how to redirect from `{something}` to `{something}.xml` which existed in my static content folder.
29 |
30 | And with it being a static web app, there is no server component for me to add a redirection too. So....
31 |
32 | ## How do you do redirects in Azure Static Web Apps?
33 |
34 | Turns out, the awesome team behind Azure Static Web Apps thought about this and built a mechanism for doing server-side redirects.
35 |
36 | To get started, you'll want to create a new file called `staticwebapp.config.json`. This file needs to exist AT PUBLISH time in the root of your build directory.
37 |
38 | > In other words, if you commit your changes to GitHub and Azure Static Web Apps kicks in. It'll start the appropriate GitHub Action to build your app. At the end of the process, your "build" directory (or "dist" or whatever) will be copied off of disk into Azure. Make sure `routes.json` is sitting in that build directory at the end of the process. In Gridsome, this is done via the "static" directory which copies everything into the build directory at the end of the process. Most other static site generators have a process for this too.
39 |
40 | ```json
41 | {
42 | "routes": [
43 | {
44 | "route": "/routeTypedIntoBrowser",
45 | "redirect": "/redirectToMe",
46 | "statusCode": 301
47 | }
48 | ]
49 | }
50 | ```
51 |
52 | The `staticwebapp.config.json` does a lot more than just redirects, but for today let's just look at the `routes` section. Notice it's an array of objects. If you want to set up a redirect, create an entry with the "route" that someone would type into the browser, and then Azure Static Web Apps will "serve" up the url or path you define. Lastly, make sure your status code is 301 or 302 so the browser (or HTTP client) can take appropriate action.
53 |
54 | [Check out the reference](https://docs.microsoft.com/en-us/azure/static-web-apps/configuration)
55 |
56 |
57 | ## Let's see it in action
58 |
59 | I currently have a [Sample Application](https://github.com/1kevgriff/azure-static-web-apps-routing) running in Azure Static Web Apps. It is currently deployed to [https://kind-island-03cd0d80f.azurestaticapps.net/](https://kind-island-03cd0d80f.azurestaticapps.net).
60 |
61 | 
62 |
63 | Try this:
64 |
65 | Go to [https://kind-island-03cd0d80f.azurestaticapps.net/redirectToAboutPage](https://kind-island-03cd0d80f.azurestaticapps.net/redirectToAboutPage) and it *should* redirect you automatically to the default Gridsome about page.
66 |
67 | It's that simple!
68 |
69 | Let me know your thoughts below. If you have questions that aren't answered by this article, please let me know those as well.
70 |
--------------------------------------------------------------------------------
/blog/20210303-signalr-abortcontroller-is-undefined-on-older-browsers.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "SignalR AbortController is Undefined on Older Browsers"
3 | date: 2021-03-03T11:00:00Z
4 | permalink: signalr-abortcontroller-undefined
5 | description: "Fix SignalR AbortController undefined errors on older browsers like SmartTVs by implementing a workaround for missing fetch/AbortController support."
6 | summary: "Recently, I updated a client application to the latest versions of SignalR (including the NPM module). One of our users uses a SmartTV to view the page, and it stopped working due to 'AbortController is Undefined'."
7 | tags:
8 | - SignalR
9 | - Browser Compatibility
10 | - AbortController
11 | - SmartTV
12 | categories:
13 | - .NET
14 | - ASP.NET
15 | - Web Development
16 | excerpt: "Recently, I updated a client application to the latest versions of SignalR (including the NPM module). One of our users uses a SmartTV to view the page, and it stopped working due to 'AbortController is Undefined'."
17 | ---
18 |
19 | Recently, I updated a client application to the latest versions of SignalR (including the NPM module). One of our users uses a SmartTV to view the page, and it stopped working.
20 |
21 | > Fun fact: many SmartTVs are built on old versions of Chromium and are never updated. Our users TV is on Chrome 56.
22 |
23 | The particular error we were seeings was `AbortController is undefined`, and tracing it backwards to the `FetchHttpClient` class of SignalR shows a small issue:
24 |
25 | [https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts](https://github.com/dotnet/aspnetcore/blob/main/src/SignalR/clients/ts/signalr/src/FetchHttpClient.ts)
26 |
27 | ```typescript
28 | if (typeof fetch === "undefined") {
29 | // In order to ignore the dynamic require in webpack builds we need to do this magic
30 | // @ts-ignore: TS doesn't know about these names
31 | const requireFunc = typeof __webpack_require__ === "function" ? __non_webpack_require__ : require;
32 |
33 | // Cookies aren't automatically handled in Node so we need to add a CookieJar to preserve cookies across requests
34 | this.jar = new (requireFunc("tough-cookie")).CookieJar();
35 | this.fetchType = requireFunc("node-fetch");
36 |
37 | // node-fetch doesn't have a nice API for getting and setting cookies
38 | // fetch-cookie will wrap a fetch implementation with a default CookieJar or a provided one
39 | this.fetchType = requireFunc("fetch-cookie")(this.fetchType, this.jar);
40 |
41 | // Node needs EventListener methods on AbortController which our custom polyfill doesn't provide
42 | this.abortControllerType = requireFunc("abort-controller");
43 | } else {
44 | this.fetchType = fetch.bind(self);
45 | this.abortControllerType = AbortController;
46 | }
47 | ```
48 |
49 | The error occurs on the line `this.abortControllerType = AbortController;`. But why do we get here? Well it's because some older browsers, like Chrome 56, don't support `AbortController` but do support `fetch`. That's how this issue occurs.
50 |
51 | I filed an issue on [GitHub](https://github.com/dotnet/aspnetcore/issues/30458) about this, and I got the response I was expecting. `Wontfix` because why would you support Chrome 56ish? I honestly don't blame the team here.
52 |
53 | So how can you work around it? My theory was to check if `fetch` and `AbortController` existed BEFORE loading SignalR. This is done early in my application:
54 |
55 | ```typescript
56 | if (typeof fetch !== "undefined" && typeof AbortController === "undefined") {
57 | console.warn("Fetch is supported, but not AbortController. Dropping default fetch so SignalR can override.");
58 | window.fetch = undefined;
59 | }
60 | ```
61 |
62 | Why does this work? Well, if `fetch` is defined but `AbortController` is not, we know we're going to have issues. SignalR has its own polyfill for `fetch` if `fetch` doesn't exist. So we simply make fetch undefined globally and let SignalR do it's work for us!
63 |
64 |
65 | > NOTE: no warranty how this affects other libraries that use `fetch`. I'm using Axios in addition to our SignalR stuff, and had zero problems.
66 |
67 | I hope this helps you in the future! If it did, hit me up and [Twitter](https://twitter.com/1kevgriff) and let me know!
68 |
--------------------------------------------------------------------------------
/blog/20210112-launched-today-signalr-mastery.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "🚀 Launched Today! SignalR Mastery 🚀"
3 | date: 2021-01-13T00:00:00Z
4 | permalink: signal-mastery-launched
5 | description: "Do you want to become a master in building real-time applications on .NET? I have the course for you!"
6 | summary: "Do you want to become a master in building real-time applications on .NET? I have the course for you!"
7 | tags:
8 | - SignalR
9 | - Course
10 | - Real-time
11 | - .NET
12 | - Udemy
13 | categories:
14 | - .NET
15 | - ASP.NET
16 | - Web Development
17 | excerpt: "Do you want to become a master in building real-time applications on .NET? My new course is designed to take you on a journey through all the bits and advanced topics of SignalR, a real-time communication library built on .NET!"
18 | ---
19 |
20 | [](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79)
21 |
22 | [Get the course - signalrmastery.com](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79)
23 |
24 | Today, I am officially launching my new course, SignalR Mastery!
25 |
26 | I've been training professional for ten years, and over that time I've built a variety of courses for many course platforms in our industry. They've been great to work with, but I always felt disconnected from the students taking those courses.
27 |
28 | This time around, I wanted to build a course **my way**. I wanted to have a platform where students could give me direct feedback and where I could directly interact with them to ensure their success. Udemy has proven to be one of the many excellent providers out there where I could do exactly that.
29 |
30 | > You're buying a course. Buy once, enjoy the growth of the course forever. No silly subscriptions.
31 |
32 | What subject should I do the course on? I decided it was time to revisit one of my favorite libraries in the ASP.NET stack, SignalR. I've been talking about SignalR for better part of 7 years since it was released in 2013. I've integrated it successfully into dozens of client applications. It's a fun tech to demo!
33 |
34 | SignalR Mastery was born! It's my new approach to teaching. Each video in the series has me directly working with you to learn a new subject. I've worked hard to remove all the fluff. The context needed to learn a topic or facet of SignalR has been minimized.
35 |
36 |
37 | What's the course about? [See the full course outline, including upcoming bits.](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79) Here is the synopsis:
38 |
39 | > The web isn't static. As more and more people live and work on the internet, developers need to make an effort to build robust, adaptive applications that can keep up with the fast pace of business.
40 |
41 | > In the 2000s, it was perfectly acceptable for a page to be static. Imagine you're working with a product inventory application. Are other people working against the same data? Can you be sure the current price of the product is valid? Did it change? How would you even know? Refresh the page to load updated data and see.
42 |
43 | > What if the page could... update itself? In real-time?
44 |
45 | > Once upon a time, this was a complicated solution to implement in a performant manner. Today, it's as simple as creating a WebSocket!
46 |
47 | > How do you manage thousands or millions of WebSockets across multiple servers? Reliably? What if the environment cannot support a WebSocket connection? What do you fall back to?
48 |
49 | > SignalR is a library for .NET which allows developers to add real-time capabilities to their applications. It provides a framework for managing connections from various clients, including web pages, mobile apps, desktop applications, and more. It handles all of the grunt work.
50 |
51 | > In this course, we'll take a structured look at how SignalR works and how you can harness it within your .NET applications.
52 |
53 | I couldn't have done it without the amazing support of my friends and family! Building these types of courses are not for the weak of heart, and we're just getting started.
54 |
55 | I hope you enjoy the course!
56 |
57 | [Get the course](https://www.udemy.com/course/signalr-mastery/learn/?referralCode=5F129296A976F8353B79)
--------------------------------------------------------------------------------
/docs/Courses.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Courses
3 | date: 2020-05-22 12:00:00
4 | permalink: courses
5 | categories:
6 | - Courses
7 | excerpt: "Online learning? In person training? I have you covered!"
8 | ---
9 |
10 | ### Building Background Services in .NET with HangFire
11 |
12 | [](https://www.udemy.com/course/building-background-services-in-net-with-hangfire/?referralCode=ADE705B8BB4A44749A7D)
13 |
14 | > This comprehensive course is designed to teach you how to build background services in .NET using HangFire, a powerful open-source library that makes it easy to create and manage background jobs. Throughout the course, you'll learn from Kevin Griffin, a Microsoft MVP with over 17 years of experience in software development, who will guide you through the entire process, from setting up HangFire to building and managing background jobs.
15 | >
16 | > With Kevin's guidance, you'll start by exploring the fundamental concepts of background jobs and how they can improve the performance and scalability of your .NET application. You'll then dive into HangFire and learn how to set it up and configure it for your needs. You'll learn how to create and manage background jobs using the HangFire dashboard and how to schedule recurring jobs that run automatically at specified intervals.
17 | >
18 | >In addition to these core concepts, Kevin will also cover more advanced topics, such as handling errors that may occur during job execution, using HangFire with other .NET libraries and frameworks, and scaling background services to handle large volumes of jobs. You'll also learn best practices for building efficient and reliable background services that can help you optimize your application's performance and scalability.
19 | >
20 | >By the end of the course, you'll have a deep understanding of how to build background services in .NET using HangFire, and you'll be ready to apply these skills to your projects. Whether you're a seasoned .NET developer looking to take your skills to the next level, or a newcomer to the world of background services, this course has something for everyone. So why wait? Sign up now and start building efficient and reliable background services in .NET today!
21 |
22 | [Learn More](https://www.udemy.com/course/building-background-services-in-net-with-hangfire/?referralCode=ADE705B8BB4A44749A7D)
23 |
24 | ***
25 |
26 | ### SignalR Mastery
27 |
28 | `youtube:https://www.youtube.com/watch?v=oqSPvW5FsYk`
29 |
30 | > SignalR is a library for .NET which allows developers to add real-time capabilities to their applications. It provides a framework for managing connections from various clients, including web pages, mobile apps, desktop applications, and more. It handles all of the grunt work.
31 |
32 | > In this course, we'll take a structured look at how SignalR works and how you can harness it within your .NET applications.
33 |
34 | [Learn More](https://signalrmastery.com)
35 |
36 | ***
37 |
38 | ### Building APIs with ASP.NET Core
39 |
40 | > As web applications grow in popularity and we see a greater uptick in web frameworks, creating robust application programming interfaces (APIs) for internal and external use is becoming increasingly more important.This course will teach developers the fundamentals of building APIs with ASP.NET Core, including testing, validation, and designing interfaces that will allow them to be good API citizens.
41 |
42 | [Check it out for FREE on YouTube](https://www.youtube.com/playlist?list=PL4z_hOgrhWyCqR8e4EVWdw9N-Kog2r3jL)
43 |
44 | ***
45 |
46 | ### Monitoring and Troubleshooting Applications with Application Insights
47 |
48 | > This course covers the fundamentals for tracking the performance and health of your applications with Application Insights on Azure. We will begin with how to provision an Application Insights instance on Azure followed by how to configure your applications to send telemetry data.Once your application is sending telemetry, we will walk though the scenarios on how to use Application Insights to investigate application failures and poor performance.This course will conclude with an overview of the Application Insights Log Analytics, and how to write KQL (Kusto Query Language) to extract more information from your telemetry.
49 |
50 | [Check it out for FREE on YouTube](https://www.youtube.com/playlist?list=PL4z_hOgrhWyCCSQQhaka9MO3hswfFUFeq)
51 |
52 | ***
--------------------------------------------------------------------------------
/blog/20250825-framework-16-laptop-review.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: "Framework 16 Laptop Review"
3 | date: 2025-08-26T00:00:00Z
4 | permalink: framework-16-laptop-review
5 | description: "My comprehensive review of the Framework 16 laptop, exploring its modular design, performance capabilities, and whether it lives up to the hype as a truly repairable and upgradeable laptop for developers and power users."
6 | summary: "My comprehensive review of the Framework 16 laptop, exploring its modular design, performance capabilities, and whether it lives up to the hype as a truly repairable and upgradeable laptop for developers and power users."
7 | tags:
8 | - Framework
9 | - Laptop
10 | - Hardware Review
11 | - Modular
12 | categories:
13 | - Hardware
14 | - Review
15 | - Technology
16 | excerpt: "A deep dive into the Framework 16 laptop experience, covering everything from build quality and performance to the innovative modular design that promises to revolutionize how we think about laptop ownership and sustainability."
17 | ---
18 |
19 | # Framework 16 Laptop Review
20 |
21 | The Framework 16 has been generating significant buzz in the tech community as a game-changing approach to laptop design. With its promise of modularity, repairability, and upgradeability, it represents a stark departure from the sealed, disposable nature of most modern laptops.
22 |
23 | ## Video Review
24 |
25 | `youtube:https://youtu.be/rVQ-cbnrcYo`
26 |
27 | ## My Experience with the Framework 16
28 |
29 | After spending considerable time with the Framework 16, I wanted to share my honest thoughts about this innovative laptop and whether it delivers on its ambitious promises.
30 |
31 | ### The Good: What Framework Gets Right
32 |
33 | **Modularity That Actually Works**
34 | The modular port system is genuinely impressive. Being able to swap USB-C, USB-A, HDMI, and other ports based on your current needs isn't just a gimmick—it's genuinely useful. No more dongles cluttering your bag.
35 |
36 | **Build Quality Surprises**
37 | Despite initial skepticism about a startup's ability to match established manufacturers, the build quality is solid. The chassis feels sturdy, and the attention to detail in the modular components is evident.
38 |
39 | **Repairability Champion**
40 | Framework has delivered on their repairability promise. Getting inside this laptop is straightforward, and replacement parts are readily available. This is exactly what the right-to-repair movement needed.
41 |
42 | ### The Challenges: Areas for Improvement
43 |
44 | **Battery Life Reality Check**
45 | While performance is solid, battery life doesn't quite match some of the more established ultrabooks in this category. This isn't necessarily a dealbreaker, but it's worth considering based on your usage patterns.
46 |
47 | **Premium Pricing**
48 | The Framework 16 commands a premium price. You're paying for the innovative design and philosophy, which may not make sense for every budget or use case.
49 |
50 | **Ecosystem Maturity**
51 | As with any new platform, the ecosystem of modules and accessories is still developing. While the core offerings are solid, the selection isn't as vast as more established platforms.
52 |
53 | ## Who Should Consider the Framework 16?
54 |
55 | The Framework 16 isn't for everyone, but it's perfect for certain use cases:
56 |
57 | - **Developers and Power Users** who value repairability and customization
58 | - **Sustainability-Conscious Buyers** who want to break the upgrade cycle
59 | - **Tech Enthusiasts** who appreciate innovative design approaches
60 | - **Professionals** who need flexible connectivity options
61 |
62 | ## Final Thoughts
63 |
64 | The Framework 16 represents more than just another laptop—it's a statement about how we should approach technology ownership. While it may not be the perfect laptop for everyone, it's an important step toward a more sustainable and user-friendly future for portable computing.
65 |
66 | The fact that a company is willing to challenge the status quo and create something genuinely different deserves recognition. Even if the Framework 16 isn't your next laptop, supporting this kind of innovation benefits the entire industry.
67 |
68 | What are your thoughts on modular laptops? Have you considered making the switch to a more repairable device? Let's discuss on [X](https://x.com/1kevgriff), [BlueSky](https://bsky.app/profile/consultwithgriff.com), or [LinkedIn](https://www.linkedin.com/in/1kevgriff/).
69 |
--------------------------------------------------------------------------------