Today I finished R3 D100 of 100DaysOfCode! đ Thanks to all the amazing content creators including freeCodeCamp, Codecademy, FrontendMasters, FluxSauce, ste_grider, and wesbos! https://virtual.github.io/100daysofcode/
Favorite course of the year from ste_grider: Microservices with Node JS and React. Great lessons on learning Docker and Typescript and actually integrating them into projects. I love the hands-on approach and âimproving codeâ progression. đ https://www.udemy.com/course/microservices-with-node-js-and-react/
Interested in test-driven design TDD? Thereâs so much information in this course: Node.js: Testing and Code Quality from FluxSauce! I learned about tools I didnât even know existed for auditing and reporting on code coverage. https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html
If youâre interested in doing 100DaysOfCode, start with a list of possible projects youâre interested in working on. Itâs helpful when you finish something (or are a bit bored) & need a change of pace. Great options: javascript30 freecodecamp
Auth service
npm init -y
npm install typescript ts-node-dev express @types/express
tsc --init
auth/src/index.ts
(ts!)"start": "ts-node-dev src/index.ts"
Setup K8s early on!
FROM node:alpine...
)infra/k8s
Skaffold
skaffold.yaml
and fill inskaffold dev
-poll
(I use poll
a lot for other projects!)
ts-node-dev --poll src/index.ts
addMarker(mappable: User | Company): void { }
User | Company | Parks | CarLots
âŠinterface Mappable {
location: {
lat: number;
lng: number;
};
}
// Ideal things we can do with the map in index.ts
export class CustomMap {
// addMarker(mappable: User | Company): void {
// Instead of maintaing list types, we can use the Mappable type
addMarker(mappable: Mappable): void {}
}
Typescript does an implicit check of User and Company to make sure they pass as type Mappable
To help identify errors in classes we can use export
:
// by adding export, we can use this to help with error checking in User
// and Company classees
export interface Mappable {}
And then add implements
to the Class itself
import { Mappable } from './CustomMap';
// Since we export Mappable, we can use implements
// (not extends!) Mappable to help id errors
// Not required, but helpful :)
export class User implements Mappable {}
Now errors will show inside the User class showing expectations of Mappable
Typical Typescript file
Review of why this project used Parcel: âParcel is a Web Application Bundler. Itâs in the same tool category of webpack, with a different value proposition. Parcel promises to do many things without any configuration at all, and be fast too.â
npm install faker
- allows generation for fake dataname.d.ts
) tells the TS compiler the functions that are in a file and what kind of types it takes/uses@types/faker
: use install npm install @types/faker
Example of User.js that creates a user with faker data; uses the export keyword:
import faker from 'faker';
export class User {
name: string;
location: {
lat: number;
lng: number;
};
constructor() {
// generate random info using faker
this.name = faker.name.firstName();
// this.location.lat -- this is undefined!
this.location = {
lat: parseFloat(faker.address.latitude()), // return a number to match definition above
lng: parseFloat(faker.address.longitude())
};
}
}
import { User } from './User';
// anything we export something from a file, we use curly braces
// this allows us to export mulitple items with diff names
// different from `export default` as that will be the default
// export default is rarely used in TS
const user = new User();
index.html
, we have created a global variable google
: <script src="https://maps.googleapis.com/maps/api/js?key=API_KEY"></script>
/// <reference types="@types/googlemaps" />
opts?
is a flag for optional argumentLimit access to methods on third-party plugins:
// Instead of creating and calling this directly in index.ts, let's
// create a Map class to limit the functionality allowed in index.ts
const map = new google.maps.Map(document.getElementById('map'), {
zoom: 2,
center: {
lat: 0,
lng: 0
}
});
Constructors
// way 1
color: string;
// a constructor is instantly executed and uses arguments
constructor(color: string) {
this.color = color; // if you use a constructor, then don't define it above
}
// end way 1
// way 2 -- add public, equivalent to way 1!
constructor(public color: string) { }
// end way 2
constructors that extend a parent class need super()
calls
Going to use npm install -g parcel-bundler
for packaging TS code going forward
Typescript Classes
// refer to as the Super Class
class Vehicle {
drive(): void {
console.log('chugga chugga');
}
honk(): void {
console.log('beep');
}
}
// refer to as the Child Class
class CarClass extends Vehicle {
// copies all props from Vehicle
// if we want to override, we can
drive(): void {
console.log('vroom');
}
}
const carInstance = new CarClass();
carInstance.drive();
carInstance.honk();
Modifers: keywords we can place on methods and props inside a class
Notes:
Tuples:
[color, carbonated, sugarContent]
const pepsiArray = ['brown', true, 40];
const pepsiTuple: [string, boolean, number] = ['brown', true, 40];
// Can also be done as a "type alias"
// not an array--> defines the tuple!
type Drink = [string, boolean, number];
const coke: Drink = ['brown', true, 39];
const tea: Drink = ['brown', false, 15];
Interfaces in Typescript
interface Vehicle {
name: string;
year: number;
broken: boolean;
}
const listVehicle = (vehicle: Vehicle): void => {
console.log(`Name: ${vehicle.name}`);
console.log(`Year: ${vehicle.year}`);
console.log(`Broken: ${vehicle.broken}`);
};
// Refactoring
// Q: But what happens to all the type checking for the vehicles?
// Q: Can we have optional, but defined, variables in the Interface?
interface Reportable {
summary(): string;
}
const car1 = {
prop1: 'vw',
summary(): string {
return `This ${this.prop1} is awesome.`;
}
};
const printSummary = (item: Reportable): void => {
console.log(item.summary());
};
printSummary(car1);
let numberAboveZero: boolean | number = false;
Destructured function example:
const logWeatherDestr = ({
date,
weather
}: {
date: Date;
weather: string;
}): void => {
console.log(date, weather);
};
Object example:
const {
coords: { lat, lng }
}: { coords: { lat: number; lng: number } } = profile;
What is it? Basically:
Typescript:
npm install -g typescript ts-node
tsc --help
(typescript compiler)Resources:
FetchJSON app
tsc index.ts
(creates compiled js)node index.js
ts-node
compiles and executes in one commandas Todo
Primitive types:
Object types: (we can tell TS to treat as a diff Object type)
Inferences vs. annotation
const color = 'red'
let apples;
& apples = 5
, typescript gives apples a type of any and is not able to infer the typeReviewing the setup of our first Microservices app and setup for next
Review upcoming ticket app
Data
Services
payments
kubectl rollout restart deployment [depl_name]
skaffold dev
ctrl+c
, Skaffold will remove objects (services, deployments, etc)Exploring Deno with Bozeman JS group :)
Adding Ingress config:
Create ingress-srv.yaml
and apply file
apiVersion: networking.k8s.io/v1beta1
kind: Ingress
metadata:
name: ingress-srv
annotations: # helps ingress understand that we are sending it routing rules
kubernetes.io/ingress.class: nginx
spec:
rules: # all the routing rules to teach ingress how to route incoming traffic
- host: posts.com # ingress assumes you may be hosting multiple domains
http:
paths:
- path: /post # match what the posts/index.js expects traffic on
backend:
serviceName: posts-clusterip-srv # send requests to this service
servicePort: 4000 # matches what is in posts-depl.yaml port
/etc/hosts
file (Mac)# kubernetes microservices-blog
# connect to localhost
127.0.0.1 posts.com
http://localhost:4000/posts
becomes http://posts.com/posts
Q: Issue with adding a post and refreshing; need to rebuild query service in order for new posts to show up?
Outside world -> Load Balancer -> Ingress Controller -> Cluster (Pod)
kubectl get pods -n ingress-nginx
infra/k8s
using kubectl apply -f .
Integrating React portion
Add cluster IP service to existing deployment file for each pod config to make it easier to manage incoming requests
How to Communicate between services
kubectl get services
)await axios.post('http://localhost:4005/events'
would become await axios.post('http://event-bus-srv:4005/events'
localhost:30594/posts
Will allow posts and event bus to talk to eachother
Made a quick Go program for a GDG meetup with James Perkins!
madlibs/main.go
package main
import "fmt"
func main() {
var exclamation string = ""
var adverb string = ""
var noun = ""
var adjective string = ""
fmt.Print("Enter a exclamation!")
fmt.Scanln(&exclamation)
fmt.Print("Enter an adverb!")
fmt.Scanln(&adverb)
fmt.Print("Enter a noun!")
fmt.Scanln(&noun)
fmt.Print("Enter an adjective!")
fmt.Scanln(&adjective)
fmt.Println(exclamation+"!", "She said", adverb, "as she jumped into her",
adjective, noun, "and drove off with her", adjective, "dog.")
}
Recommended method using Docker Hub (no need for hardcoded versions)
docker push satinflame/posts
kubectl rollout restart deployment [depl_name]
(depl_name should refer to the deployment NAME when you view kubectl get deployments
)K8s Services provide networking between pods
Types of services:
Creating a NodePort service in posts-srv.yaml:
apiVersion: v1
kind: Service
metadata:
name: posts-srv
spec:
type: NodePort
selector:
app: posts
ports:
- name: posts
protocol: TCP
port: 4000
targetPort: 4000
k apply -f posts-srv.yaml
service/posts-srv created
k get services
NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE
kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 2d19h
posts-srv NodePort 10.102.129.168 <none> 4000:30595/TCP 19s
NodePort now available (30595) and you can access this endpoint at http://localhost:30595/posts
kubectl get pods
- list running podskubectl exec -it [pod_name] [cmd]
- execute a command in a running podkubectl logs [pod_name]
- print pod logskubectl delete pod [pod_name]
- Use delete to destroy and recreate (restart) podkubectl apply -f [config.yaml]
- tell K8s to process config (start pod)kubectl describe pod [pod_name]
- print info about running pod (debug)alias k="kubectl"
source ~/.bashrc
# Get the aliases and functions
if [ -f ~/.bashrc ]; then
. ~/.bashrc
fi
posts-depl.yaml (No longer going to use posts.yaml for single pod)
apiVersion: apps/v1
kind: Deployment
metadata:
name: posts-depl
spec:
replicas: 1
selector:
matchLabels:
app: posts
template:
metadata:
labels:
app: posts
spec:
containers:
- name: posts
image: satinflame/posts:0.0.1
kubectl get deployments
- list running deploymentskubectl describe deployment [deployment_name]
- print out info on a deploymentkubectl apply -f [config.yaml]
- create deployment from configkubectl delete deployment [deployment_name]
- Use delete to destroy a deployment (and delete related pods)kubectl
- the command to interact with a K8s clusterKubernetes vocab:
Always use config files to create ObjectsâDeployments, Pods, and Services; do not type direct commands
docker build -t satinflame/posts:0.0.1 .
apiVersion: v1
kind: Pod
metadata:
name: posts
spec:
containers:
- name: posts
image: satinflame/posts:0.0.1
kubectl apply -f posts.yaml
kubectl get pods
- return a list of running podsCreate Dockerfile for posts
.dockerignore
file to ignore node_modules
and other directories you donât want copied to the Docker imageCommon commands:
docker build -t stephengrinder/posts .
- build an image based on the dockerfile in the current directory, tag it as âstephengrinder/postsâdocker run[image id or image tag]
- Create and start a container based on the provided image id or tagdocker run -it [image id or image tag][cmd]
- Create and start container, but also override the default commanddocker ps
- Print out information about all of the running containersdocker exec -it [container id][cmd]
- Execute the given command in a running containerdocker logs [container id]
- Print out logs from the given containerKubernetes is a tool for running many different containers
kubectl version
Initial Attempt:
# base image
FROM alpine
# Install dependencies
RUN npm install
# Set start command
CMD [ "npm", "start" ]
docker build .
Possible errors:
FROM node:6.14
or FROM node:alpine
in the DockerfileCOPY
command to copy files from local file system to containerCOPY ./ ./
(pwd)docker build -t satinflame/simpleweb .
docker run satinflame/simpleweb
localhost:8080
?
docker run -p 5000:8080 satinflame/simpleweb
Cleaning up working directory
docker run -it satinflame/simpleweb sh
youâll start in the WORKDIRMaking changes to the local files
COPY ./package.json ./
COPY ./ ./
Our final, awesome Dockerfile:
# base image
FROM node:alpine
# Create work directory
# If not exists, this folder will be created
WORKDIR /usr/app
# Split COPY into two steps to avoid having to npm install each rebuild
COPY ./package.json ./
# Install dependencies
RUN npm install
# Copy local file system to container
# Move this after npm install so npm install doesn't need to be rerun
COPY ./ ./
# Set start command
CMD [ "npm", "start" ]
SEO reviewing common pitfalls
docker build .
runs build on the DockerfileTagging an image
<yourDockerId>/<reponame>:<version>
, e.g. docker build -t stephengrinder/redis:latest .
<reponame>
is what you want to reference it ascommit -c
- for manually creatingMore about -it
-it
is really -i
and -t
-i
- attach the terminal to the STDIN when executing this command-t
- formats input/output so it shows up nicely and allows for autocompleteRunning a terminal inside your running container
docker exec -it 9320 sh
cd ~/
or ls
exit
sh
is the default command processor (or bash
)Docker Images
Creating an Image:
# Use existing image (alpine) as base
FROM alpine
# Download and Install depedency
RUN apk add --update redis
# Tell image what to do with in starts as a container
CMD ["redis-server"]
FROM, RUN, CMD
are docker commands used in the Dockerfile
docker run <image name> command
docker run
= docker create
+ docker start
ls
or echo hi
) that doesnât exist in imageâs filesystem, we receive an error.docker ps
- show running imagesdocker ps --all
- show any image ever created one machinedocker start -a <containerid>
- -a
(attach), watch it and tell it to output to our console from the containerdocker run
shows all output; docker start
does notLifecycles
docker system prune
- clean existing imagesdocker stop <id>
- gives running processes time to shut down (10s)docker kill <id>
- forces it to stop immediately (such as ping
)Seeing what was run:
docker create busybox echo hi there
docker start b895
(if you forgot the -a
flag)docker logs b895
Redis:
docker run redis
- starts Redis serverredis-cli
â> need to start it inside the containerdocker exec -it <containerid> <command>
to execute a second command inside a running container (-it
allows us to provide input to the container)docker exec -it 8ca6 redis-cli
Playing with Jamdocs on Netlify, uses gridsome and Forestry: https://virtual-jamdocs.netlify.app/
docker run hello-world
- A simple app that shows that docker is running successfully in your environmentdocker run <IMGNAME>
creates a container of an image from your local machine or Docker HubStoring events
Steps:
Best practice: Query service only listens for update
events:
data
attrs and copy themBeginning adding comment moderation to Udemy Microservices Blog feature
Added handlesbars for implementing conditional content in sendgrid email templatesâhopefully can reduce the large amount of email templates down to a few!
Implement query service for listing posts and comments. GitHub changes
A little break from programming; working on SEO Gap analysis for my website and some complex Excel functions.
=SUMIFS('Overall Aggregate'!$F:$F,'Overall Aggregate'!$C:$C,"<4",'Overall Aggregate'!$C:$C,">0")
=IFERROR(VLOOKUP(A14,'Our site (Pos 1-100, non-brand)'!A:B,2,FALSE),"GAP")
Current data flow:
Proposed: Asynchronous call using an Event bus
Emitting events
type
(eg âPostCreatedâ) and data
(object of new item)Notes:
body-parser
and why do we need body-parser?
req.body
.Playing with football APIs. ESPN seems to have a goodâalbeit undocumentedâAPI.
https://site.web.api.espn.com/apis/common/v3/sports/football/nfl/athletes/101
Notes:
const
vs. import
for certain libraries?
require
is the node.js way to load modulesimport
since thatâs the part of the standard. There is no technical reason to prefer import over require though: everything that can be done using require
can be done with import
and vice versa. In some cases one will be more concise, in another - the other.Microservices react components for (listing and adding) Posts and Comments complete.
Inspired by Scott Mathsonâs SEO on the Jamstack presentation - VirtuaCon (Jekyll) to add schema to a website using Hugo.
Notes:
<script>
tag.You have to extend React.Component to create a stateful component which then will need a constructor and youâll be able to use the state.
Create Component using Class
class Main extends React.Component {
constructor(props) {
super(props);
this.state = {
title: 'Hello React'
};
}
render() {
return <div>{this.state.title}</div>;
}
}
Implicit
export default function Foo(props) {
return <div>My component</div>;
}
Using arrow functions
Using an arrow function with a class property ensures that the method is always invoked with the component as the value for this, meaning that the manual rebinding here is redundant:
this.handleUpdateInput = this.handleUpdateInput.bind (this);
export default (details) => {
// code
};
or from Dan Abramov: âArrows work just fine if you give them implicit name first.â
const RenderDetails = (details) => {
// code
};
export default RenderDetails;
Begin adding React components
App
sudo lsof -i :3000
res.send(commentsByPostId[req.params.id] || [])
Section 2: A Mini-Microservices App
App steps
Microservices built with Node, React, Docker and Kubernetes
Communicating between services (not the same meaning as JavaScript)
type: UserQuery, data: {id: 1}
Typed arrays are actually objects, and they donât have access to array methods like push and pop. đ€·ââïž But they can help with performance by only allocating the memory space you need. Learning data structures from #freecodecamp! #womenintech #codenewbie R3D56/#100DaysOfCode
VSCode: Alt + Shift
to edit multiple lines
Type | Each element size in bytes |
---|---|
Int8Array |
1 |
Uint8Array |
1 |
Uint8ClampedArray |
1 |
Int16Array |
2 |
Uint16Array |
2 |
Int32Array |
4 |
Uint32Array |
4 |
Float32Array |
4 |
Float64Array |
8 |
Typed arrays do not have some of the methods traditional arrays have such as .pop() or .push(). Typed arrays are type object.
Last-In-First-Out using .push()
and .pop()
Working on mocking up my faux Knight University website using TailwindCSS. Iâm finding maybe itâs best to componentize all the things (like buttons for example.) Maybe even headers (h2, h3)? Curious how others manage this. https://github.com/virtual/knightu R3D55/#100DaysOfCode
Set up my SSH config on my Mac so I donât have to type crazy-long SSH lines! Logged into DigitalOcean to look into setting up a droplet to try out Docker, TDD & CI/CD and realized I still have no idea what Iâm doing. https://www.ostechnix.com/how-to-create-ssh-alias-in-linux/ R3D54/#100DaysOfCode
A little DGD while I code an AMP example with GDG! Thanks for teaching the #AMPstudygroup benmorss! R3D53/#100DaysOfCode https://virtual-gdg-amp-study-group.glitch.me
Feeling motivated an inspired today. Morning yoga, Toastmasers, and an AMP workshop with GDG!
Tell the browser your page is AMP using one of two ways:
<html amp ...>
<html ⥠...></html>
</html>
#development=1
to the end of the URL, errors will log to the consoleTypes implemented:
amp-img
amp-youtube
- Some videos require scriptsamp-carousel
amp-sidebar
Required scripts have to go before the call to the element.
Extra:
Ports
-p <host-port>:<container-port>
option. The Redis container exposes the service on port 6379. If you wanted to map this port directly on the host, weâd use the option -p 6379:6379.
docker run -d --name redisHostPort -p 6379:6379 redis:latest
-v <host-dir>:<container-dir>
. When a directory is mounted, the files which exist in that directory on the host can be accessed by the container and any data changed/written to the directory inside the container will be stored on the host.? Maybe
Finished Learn Phaser: Physics from Codecademy; working on technical audit for SEO
Iâve been continuing to play with bugs in Phaser.JS game tutorials and also working out SEO issues while going through Blue Array Academyâs course. (Good infoâfree through May.) R3D47-51/#100DaysOfCode
Continuing Codecademy: Phaser.js Physics
SEO
Continuing Codecademy: Phaser.js Physics
SEO
What are the ideal lengths for meta?
Continuing Codecademy: Phaser.js Physics
SEO
Questions
Continuing Codecademy: Phaser.js Physics
SEO
4 pillars:
SEO Roadmap document
For new sites, focus on content with low competition keywords
A little more fun with Phaser.js today; also trying it out on my own with a little Maplestory panda and some physics like bounce and gravity. R3D46/#100DaysOfCode https://cdpn.io/virtual/debug/QWjaRRo/ZoMBaznVqdEk
Finished the first section on learning Phaser.js for game developmentâmanaging state and taking input. Iâm also pretty excited for the next section (it involves coloring a pegasus!) đŠđš R3D45/#100DaysOfCode https://www.codecademy.com/courses/learn-phaser/lessons/learn-phaser-basics
Phaser.JS
gameState
object and keep track of the state there.setInteractive()
method on it. The setInteractive() method tells Phaser to listen in for interactive events on the GameObject.SEO
Today Iâm learning the basics of Phaser.js for game developmentâincluding preload, create, and update functions. R3D44/#100DaysOfCode https://www.codecademy.com/learn/learn-phaser
Game Development with Phaser.JS
Phaser games are composed of Scenes that we define and pass to Phaser in the config! A Phaser Scene can have any of the following functions:
preload()
, where we load in external files (or âassetsâ) to our game.create()
, where we define the GameObjects that are necessary at the start of our game.update()
where we define animation and interaction in our game (more on this in later exercises!)scene: {
preload, create;
}
Note that we are using JavaScriptâs property-value shorthand, the code above would be the same if we passed { preload: preload, create: create }
to the scene instead.
Worked a little more on my minecraft drawing tool, fixing the grid on load and touch on mobile; but mostly just watching Dance Gavin Dance music videos. R3D43/#100DaysOfCode
Ready for the weekend! Made a silly little Minecraft doodle tool with jQuery. Interesting challenge using mousedown / mousemove to keep drawing. Happy May Day! đŒ R3D42/#100DaysOfCode https://codepen.io/virtual/full/vYNeajo
Learning about continuous integration with TravisCI and automatically building from GitHub to DockerHub. All interesting piecesâwill be neat to figure this out with my own server! R3D41/#100DaysOfCode
.travis.yml
indicates to TravisCI what environment we need to run the test on, where do we deploy the appdocker build -t satinflame/lyndanode
Set up docker-compose files and now know a little more about running Docker containers. đ€ The VSCode Docker plugin is also helpful for seeing active containers and writing out Dockerfiles! (https://marketplace.visualstudio.com/items?itemName=ms-azuretools.vscode-docker) https://www.lynda.com/Docker-tutorials/Leverage-power-Docker/2211315/2221492-4.html R3D40/#100DaysOfCode
đ„ïž Bonus: Ahoy! Docker containers are named after shipping containers. More nautical analogies can be found in this article on demystifying Docker and Kubernetes. https://apifriends.com/api-management/docker-and-kubernetes/
Dockerfile
(no extension) and .dockerignore
(ignores files to place in container).dockerignore
:
node_modules
npm-debug.log
Dockerfile
is the blueprint for a containerdocker build -t folder/backend .
(-t
= Name and optionally a tag in the âname:tagâ format), (.
runs the command)docker images
- see all images youâve created or useddocker rmi folder/backend
- rm an imagedocker run -p 4000:4000 folder/backend
(-p
is for port)docker ps
to list running images (process status)docker stop cc3cc
or docker start cc3cc
- force stop/startdocker pull
/ docker push
docker-compose.yml
- tells Docker which applications we want to startlinks
- listed services must run before this; they are defined in the file (eg mongo
)docker-compose build
to build images in the docker-compose filedocker logs cc3cc
if you want to view logs for a particular servicedocker-compose stop
- turn off alldocker run -p 3000:3000 lynda/frontend
fullstack
folder. Put frontend
and backend
folders in and rename to client
and api
, respectively.backend/docker-compose.yml
to main folderbackend/Dockerfile
version
to docker-compose.yml, to indicate the version of docker-composelinks
vs. depends_on
?docker-compose up -d mongo
docker-compose up -d app
docker-compose up -d client
Labs: Introduction to Containers and Docker
History of servers
Container â Abstract the hardware, virtualize the Kernal (decoupled), maintain Dependencies and Application code.
IRL: you push and pull images from a registry
What are advantages of containers versus virtual machines?
Which types of actions can you perform in a container Dockerfile?
What is the default hostname to publish a Docker image to the Google Registry?
Kubernetes â an open-source orchestrator for a container environment, it manages and delegates jobs
Labels â metadata you can assign to any API object and represent identity, used for filtering/searching pods; e.g.
Pod1;
App: MyApp;
Phase: prod;
Role: FE;
Pod2;
App: MyApp;
Phase: test;
Role: BE;
Why use Kubernetes (i.e. what benefit does it provide to containers?)
What does a pod specify?
Which component do you use to send requests to API servers on masters to configure the cluster?
kubectl
What is the purpose of the ReplicaSet?
When are rolling deployments triggered?
How does Kubernetes choose instances in the second deployment of a canary deployment?
Deploying a continuous delivery pipeline enables you to build, stage, test, and deploy your application, using automated triggers and manual approvals. (e.g. Spinnaker or Jenkins)
Why is it useful to set up continuous delivery with Kubernetes?
Which 3 deployments are used in our instance of Kubernetes continuous delivery?
Which of the following best describes why we used Jenkins for continous delivery with Kubernetes?
Finished the second infosec challenge for #freecodecamp! I learned a lot about returning statuses, errors, and messages from the API and more types of assertions with #ChaiJS. R3D38/#100DaysOfCode https://glitch.com/~virtual-fcc-issue-tracker
------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------
All files | 43.84 | 42.11 | 30.77 | 47.83 |
app | 23.23 | 3.13 | 0 | 26.74 |
assertion-analyser.js | 1.54 | 0 | 0 | 1.92 | 31-128
server.js | 64.71 | 25 | 0 | 64.71 | 26,32,43,52-61
app/controllers | 57.14 | 57.69 | 44.44 | 57.14 |
dataHandler.js | 57.14 | 57.69 | 44.44 | 57.14 | 16-17,20-21,24-25,28-29,32-33,47,50,54,57,60
app/routes | 54.93 | 51.06 | 41.38 | 59.09 |
api.js | 76.67 | 65.75 | 100 | 77.53 | 27,74,77,83,86,89,105-110,159,191,198,208,211,214,217,220-222,232,265,278
fcctesting.js | 17.31 | 0 | 5.56 | 20.93 | 38-41,46-49,54-57,63-73,77-102
------------------------|---------|----------|---------|---------|---------------------------------------------------------------------------
Oof.
ÂŻ_(ă)_/ÂŻ 3 hours on deconstructing a boolean to send in a MongoDB query. Still no idea. https://glitch.com/edit/#!/virtual-fcc-issue-tracker?path=routes/api.js:128:22 R3D37/#100DaysOfCode
Reviewing HTTP response statuses sent in APIs. It seems that thereâs not even agreement over the correct status for GET for an element that does not exist in DB (404/204/null object/500?), but I now have plenty of references! R3D36/#100DaysOfCode https://nordicapis.com/best-practices-api-error-handling
Example of 404 response:
// GET (BY ID)
app.get('/api/tasks/:id', (request, response) => {
const taskId = request.params.id;
const task = tasks.find((task) => task.id === parseInt(taskId));
if (!task)
return response
.status(404)
.send('The task with the provided ID does not exist.');
response.send(task);
});
Recommended example (ref RFC 7807) from Best Practices for REST API Error Handling:
This schema is composed of five parts:
{
"type": "/errors/incorrect-user-pass",
"title": "Incorrect username or password.",
"status": 403,
"detail": "Authentication failed due to incorrect username or password.",
"instance": "/login/log/abc123"
}
For example, in HTML, a problem could be embedded by encapsulating JSON in a script tag (pg 16):
<script type="application/problem+json">
{
"type": "https://example.com/probs/out-of-credit",
"title": "You do not have enough credit.",
"detail": "Your current balance is 30, but that costs 50.",
"instance": "/account/12345/msgs/abc",
"balance": 30,
"accounts": ["/account/12345",
"/account/67890"]
}
</script>
Example from twitter API:
{
"errors": [
{
"message": "Sorry, that page does not exist",
"code": 34
}
]
}
Example from facebook API:
{
"error": {
"message": "An active access token must be used to query information about the current user.",
"type": "OAuthException",
"code": 2500,
"fbtrace_id": "A6S6sVDcfVJd7p7vcOABR0d"
}
}
Example from Twilio API:
{
"code": 21211,
"message": "The 'To' number 5551234567 is not a valid phone number.",
"more_info": "https://www.twilio.com/docs/errors/21211",
"status": 400
}
Back to #freecodecampâs 2nd InfoSec challengeâIâm still not understanding how best to write functional tests. It appears that I need to learn more about returning error statuses from the API. (Any recommendations on related reading?) đ€ R3D35/#100DaysOfCode #chaijs
Add coverage to FCC Issue Tracker Glitch project:
tests
--recursive
"coverage": "nyc --reporter=text --reporter=html mocha -u tdd"
------------------------|---------|----------|---------|---------|--------------------------------
File | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s
------------------------|---------|----------|---------|---------|--------------------------------
All files | 29.29 | 4.85 | 24 | 32.41 |
app | 23.23 | 3.13 | 0 | 26.74 |
assertion-analyser.js | 1.54 | 0 | 0 | 1.92 | 31-128
server.js | 64.71 | 25 | 0 | 64.71 | 26,32,43,52-61
app/controllers | 51.72 | 0 | 71.43 | 51.72 |
dataHandler.js | 51.72 | 0 | 71.43 | 51.72 | 36-40,43-65
app/routes | 28.83 | 7.84 | 24.14 | 31.68 |
api.js | 38.98 | 13.33 | 54.55 | 39.66 | 27,88,133,147-255
fcctesting.js | 17.31 | 0 | 5.56 | 20.93 | 38-41,46-49,54-57,63-73,77-102
------------------------|---------|----------|---------|---------|--------------------------------
Digging into CSS & JS SourceMaps; add â.sourceMaps()â to webpack mix.
Iâll be adding these into my clientsâ websites as I work on themâan easier method to debug code without needing to ship an unminified version to production! R3D34/#100DaysOfCode https://css-tricks.com/should-i-use-source-maps-in-production/
Add sourceMaps()
to webpack.mix.js:
mix.js('source/_assets/js/main.js', 'js')
.sourceMaps(productionToo = true,
type = 'eval-source-map')
...
Checkbox ranges using Shift! And now that I know how to incorporate them, Iâll need to make sure more of my projects use them.
âïž Challenge 10/#javascript30 R3D33/#100DaysOfCode https://virtual-javascript30.herokuapp.com/
After learning more about ESLinting, I added similar functionality for .scss to a project using stylelint! Lots of information and plugins out there, but Iâd love to see examples of your .stylelintrc files too if you use it. R3D32/#100DaysOfCode https://github.com/stylelint/awesome-stylelint
One of my goals for incorporating style linting is to be better at arranging my properties in a coherent manner.
âIf you can always count on certain properties being in the same place, you can understand the CSS a bit faster (less scanning).â https://css-tricks.com/poll-results-how-do-you-order-your-css-properties/
WIP Stylelinting
"stylelint": "^13.3.3",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-declaration-block-no-ignored-properties": "^2.3.0",
"stylelint-order": "^4.0.0",
npx stylelint source/_assets/sass/FILE.scss --fix
to autofixStylelint tips:
Glad youâre learning to use stylelint! The 50+ rules that limit language features are often overlooked. They are *-pattern
, *-blacklist
, *-whitelist
and *-max-*
rules that you can use to enforce non-stylistic conventions in your CSS.
{
"declaration-no-important": true,
"media-feature-name-whitelist": ["min-width"],
"selector-class-pattern": "^[a-z][a-zA-Z0-9]+$",
"selector-max-id": 0,
"unit-whitelist": ["rem"]
}
To enforce rem
units, min-width
media queries, camelCase selectors and so on.
Been putting this off since Januaryâupdated my webserver to the next OS version.
Killed off MariaDB on recommended purge/cleanup, but thankfully when I reinstalled it, all my databases were still there. Small miracles. đ R3D31/#100DaysOfCode
Restless reinvention
Include a variety of voices
Planning
Spent the day learning https://istanbul.js.org/ and code coverage. What is it? The measure (%) of how much code is executed with a given operation. (Goal: 100% coverage!) R3D30/#100DaysOfCode
- Executed statements
- Branches
- Called, defined functions
- Executed lines
đ” Bonus: why do you run the Istanbul test coverage library with
nyc
instead ofistanbul
? Itâs a reference to the song âIstanbul (not Constantinople)â from They Might Be Giants! https://www.youtube.com/watch?v=xo0X77OBJUg
Statementsâperform an action in code
if... else
blockconst...
while
loopBranchesâeach statement within a conditional
||, &&, !
), ternary operatorif (condition) {
statement1(); //branch 1
} else {
statement2(); //branch 2
statement3(); //branch 2
}
Functionâa function that is never called
Lineâa line that is never executed
Code coverage libraries:
nyc
as a ref to Istanbul (not Constantinople) - They Might Be GiantsInstall:
nyc
to dev dependencies: npm install nyc -D
"coverage": "cross-env DEBUG=nadia:* nyc --reporter=text --reporter=html mocha"
npm run -s coverage
Functional Testing
Example user stories for Functional Testing:
As a user, I want to:
Installing Chai HTTP:
npm install chai-http -D
Chai docs on using .get(), end
Because the end function is passed a callback, assertions are run asynchronously. Therefore, a mechanism must be used to notify the testing framework that the callback has completed. Otherwise, the test will pass before the assertions are checked.
For example, in the Mocha test framework, this is accomplished using the done callback, which signal that the callback has completed, and the assertions can be verified:
it('fails, as expected', function (done) {
// <= Pass in done callback
chai
.request('http://localhost:8080')
.get('/')
.end(function (err, res) {
expect(res).to.have.status(123);
done(); // <= Call done to signal callback end
});
});
it('succeeds silently!', function () {
// <= No done callback
chai
.request('http://localhost:8080')
.get('/')
.end(function (err, res) {
expect(res).to.have.status(123); // <= Test completes before this runs
});
});
When done is passed in, Mocha will wait until the call to done(), or until the timeout expires. done also accepts an error parameter when signaling completion.
Need to parse HTML to check for valid output? Check out cheerio.js
â100% Testâ coverage:
Open source continuous integration:
Continuous Integration as a Service:
CI Features to look for:
Mine are here: https://satinflame.com/uses
Brad Traversy
Learning how to test code and watch if it successfully creates entries & which actions are performed. Test doubles, stubs, spies & mocks. Not understanding everything⊠but more info can also be found in this 800+ page book! http://xunitpatterns.com đ R3D29/#100DaysOfCode
npm install sinon@2 sinon-chai -D
Overriding dependencies
npm install proxyquire -D
"test": "cross-env DEBUG=nadia:* mocha"
Process of Stubbing a Custom Response
Example: testing a module that updates the database:
Observing interactions with Spies
Sinon Mocks
Book ref: xUnit Test Patterns: Refactoring Test Code by Gerard Meszaros
Back to more testing with Mocha and Chai. Set up example tests for unit tests, callbacks and promises. Mocha doesnât use arrow functions in order to keep the correct binding for
this
. R3D28/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html
this
npm test -- --recursive
test/mocha.opts
(deprecated) to override default setting; add to file: --recursive
or see .mocharc.jsonit
, usually called done()
.then
or .catch
done()
)describe('Reservation Suite', function () {
context('Date and Time Combination', function () {
it('should return a ISO 8601 date and time with valid input', function () {
const date = '2020/04/17';
const time = '04:53 PM';
Reservation.combineDateTime(date, time).should.equal(
'2020-04-17T16:53:00.000Z'
);
});
});
});
Code things that make you happy! đ
Saw examples of snaking timelines from #smashingconf and havenât stopped thinking about them since! Snake
Haml, CSS, and JS for reordering cards in the responsive design. R3D27/#100DaysOfCode #WomenWhoCode https://codepen.io/virtual/pen/bGVpdyN
Today I customized ESLint to keep code tidy and standardized within a project.
You can also customize your own config and add it to npm for others to use as a dependencyâas Google and Airbnb have done for their teams. Neat! R3D26/#100DaysOfCode https://medium.com/@natterstefan/how-to-create-your-own-shared-eslint-prettier-and-stylelint-configuration-3930dd764de3
Node.js Testing frameworks:
Assertion Libraries (that work with any testing framework):
npm install mocha chai -D
"test": "mocha"
to scriptstest/.eslintrc.js
module.exports = {
env: {
mocha: true
},
rules: {
'no-unused-vars': ['error', { varsIgnorePattern: 'should|expect' }]
}
};
ESLint Configs
git+ssh
.eslintignore
, good for third-party code, contributed files, etcLearning how to set up and configure EditorConfig and ESLint in order to maintain consistent coding standards in my projects! R3D25/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html
Linter:
root: true
only in rootnode --check jsfile.js
ESLint
npm install eslint -D
"eslint": "eslint",
to the scripts in package.json due to the placement of eslintnpm run eslint
or npm run eslint .
eslint init
eslint
, npm run -h eslint
npm run -s eslint file.js
Following along on a @lynda course from @FluxSauce on Node.js testing. Great info on testing libraries vs. assertion libraries, TDD vs. BDD, and soon will be digging into setting up better linting tools! R3D24/#100DaysOfCode https://www.lynda.com/Node-js-tutorials/Node-js-Testing-Code-Quality/587672-2.html
Node.js: Testing and Code Quality - Jon Peck
Different types of testing:
TDD vs BDD
describe/context
- creates a group of specifications (tests)specify/it
- create a specification, test, or test-specHappy Easter! đŒ Taking a step back from freeCodeCamp projects to learn JavaScript testing a bit more thoroughly on Lynda. Are there any recommendations for good walkthroughs on TDD/BDD (Test/Behavior-Driven Development) using Node.js/ChaiJS? R3D23/#100DaysOfCode
Node.js: Testing and Code Quality - Jon Peck
Notes:
TDD General Notes:
describe
is used to group individual testsFive steps to TDD (also known as Red-Green-Refactoring):
Questions:
R3D22/#100DaysOfCode Working out how to use expect functions in addition to assert in Chai, but the docâs examples arenât even working in my app. Need to check version support.
Slow going with adding testing to the 2nd infosec challenge for #freecodecamp.
Questions:
expect
tests working?CRUD Working! Post, Get, Put, and Delete routes for #freecodecampâs 2nd infosec project now setup.
Next to figure out is the testing and learn what I really set up incorrectly. đ R3D21/#100DaysOfCode https://virtual-fcc-issue-tracker.glitch.me/apitest/
Questions:
{ $set: projectObj }
with value-pairs that need to be updated.db.collection("issues").update(
{ _id: ObjectId(issueID) },
{ $set: projectObj },
function(err, result) {
...
Today I created the delete route for #freecodecampâs 2nd Infosec project.
Added a check to see if the ID actually exists prior to deleting it. Using findOne() seems to be better as you donât need to parse an array. R3D20/#100DaysOfCode https://virtual-fcc-issue-tracker.glitch.me/apitest/
Questions:
req.body
info as in Postman? x-www-form-encodedreq.body._id
How do I get the result of a promise?
db.collection('issues').findOne({ _id: ObjectId(req.body._id) }, function (
err,
result
) {
if (err) {
throw err;
} else {
// do things
}
});
Spent another day digging into MongoDB for #freecodecampâs 2nd Security project. Updated my post route to use real form input and worked out the response for the get route as well.
Half of the CRUD is working now! đ https://virtual-fcc-issue-tracker.glitch.me/apitest/ R3D19/#100DaysOfCode
Questions:
Started IBMâs Design Thinking Practitioner Course
Started on the second security challenge for @freeCodeCamp, beginning with a bit of CRUD!
Iâm really rusty on MongoDB, but I finally got my database connection set up and a fake object for Project Mew inserted in a collection! đ R3D18/#100DaysOfCode
Questions:
â Metric-Imperial Converter completed for @freecodecamp!
đč Built out controller functions
đč Verified correct return types
đč Added HelmetJS for security
đč Created unit and functional tests
đč Wrote API routeshttps://virtual-fcc-mi-convert.glitch.me R3D17/#100DaysOfCode #womenintech
Finished the unit tests for @freecodecampâs M/I converter challenge. Tomorrow: functional tests!
I learned that Chaiâs approximately only works if you actually return numbersâI will need to pay attention to my return types! R3D16/#100DaysOfCode
Enjoy your weekend!
Building the first project for @freecodecampâs security challenges. Added controller functions to process input (32mi) and calc output (51.5kg). @getpostman helps to debug. Love these projects, they really make things click! R3D15/#100DaysOfCode https://virtual-fcc-mi-convert.glitch.me
âControllersâ â functions that separate out the code to route requests from the code that actually processes requests.
Since I always forget the MVC setup:
R3D14/#100DaysOfCode Started working out the first @freeCodeCamp security challenge for a Metric-Imperial Converter. Reviewing how to use regex to split strings and integrate HelmetJS for security.
Continuing @freecodecampâs passport authentication and trying to debug errors preventing tests to pass even though it works. Frustrating! đŁ
I hope all your coding adventures have been more rewarding today! R3D13/#100DaysOfCode
There are so many issues and gotchas with this chapter outlined in a myriad of forum posts and GitHub issues.
Time for me to leave this challenge and hope when I get back to it, itâs been given some TLC. I tried a GitHub PR for the page title issues, but it was rejected.
Just like any time I do server setup, spending way too much time debugging silly things like page titlesâŠ
Is there anyone with @freecodecamp approval access that can review the PRs for this repo? R3D12/#100DaysOfCode https://github.com/freeCodeCamp/boilerplate-advancednode/pulls
Serialization of a User Object
To serialize an object means to convert its contents into a small key essentially that can then be deserialized into the original object. This is what allows us to know whos communicated with the server without having to send the authentication data like username and password at each request for a new page.
Authentication Strategies
A strategy is a way of authenticating a user.
Markdown linksâsomeone once shared that âBracketsâ comes before âParenthesisâ alphabetically, and I havenât screwed up creating a link since! đ (Thank you!)
Working on @freeCodeCampâs Node & Express section, starting with đ¶ #PugJS templates. R3D11/#100DaysOfCode
doctype html
html(lang="en")
head
title= pageTitle
script(type='text/javascript').
if (foo) bar(1 + 5)
body
h1 Pug - node template engine
#container.col
if youAreUsingPug
p You are amazing
else
p Get on it!
p.
Pug is a terse and simple templating language with a
strong focus on performance and powerful features.
Itâs time to set up Passport so we can finally start allowing a user to register or login to an account! In addition to Passport, we will use Express-session to handle sessions. Using this middleware saves the session id as a cookie in the client and allows us to access the session data using that id on the server. This way we keep personal account information out of the cookie used by the client to verify to our server they are authenticated and just keep the key to access the data stored on the server.
const session = require('express-session');
const passport = require('passport');
// ...
app.use(
session({
secret: process.env.SESSION_SECRET,
resave: true,
saveUninitialized: true
})
);
// passport. initialize() is a middle-ware that initialises Passport. Middlewares are functions that have access to the request object (req), the response object (res), and the next middleware function in the application's request-response cycle
app.use(passport.initialize());
// passport.session() acts as a middleware to alter the req object and change the 'user' value that is currently the session id (from the client cookie) into the true deserialized user object.
// `app.use(passport.session());` is equivalent to `app.use(passport.authenticate('session'));`
app.use(passport.session());
Making R3D10/#100DaysOfCode an easier day, reading through the history of languages on Code Complete.
Amused by this tidbit: âThe acronym PHP originally stood for Personal Home Page.â
Language History from Code Complete (ed.2, pp 63-66)
LL = Low-level ML = Mid-level HL = High-level OOL = Object-oriented
Another #javascript30 down! Neat console logging ideas like formatting as errors, custom styles & grouping. Added @prismjs for syntax highlights.
I return for the intro riff đ¶ & stay for the great content from Wes Bos. đ
https://virtual-javascript30.herokuapp.com/09-dev-tools/index.html R3D9/#100DaysOfCode
R3D8/#100DaysOfCode Completed #Helmetjs/bcrypt for @freeCodeCampâs Information Security and QA section.
Have any of you finished this certification? Thinking face Iâm curious about what the projects are like and would love to see your GitHub repos/examples! #womenintech
HelmetJS is a type of middleware for Express-based applications that automatically sets HTTP headers to prevent sensitive information from unintentionally being passed between the server and client. While HelmetJS does not account for all situations, it does include support for common ones like Content Security Policy, XSS Filtering, and HTTP Strict Transport Security, among others. HelmetJS can be installed on an Express project from npm, after which each layer of protection can be configured to best fit the project.
contentSecurityPolicy
for setting Content Security PolicydnsPrefetchControl
controls browser DNS prefetchingframeguard
to prevent clickjackinghidePoweredBy
to remove the X-Powered-By headerhsts
for HTTP Strict Transport SecurityieNoOpen
sets X-Download-Options for IE8+noSniff
to keep clients from sniffing the MIME typereferrerPolicy
to hide the Referer headerxssFilter
adds some small XSS protectionsResources
BCrypt hashes are very secure. A hash is basically a fingerprint of the original data- always unique. This is accomplished by feeding the original data into an algorithm and returning a fixed length result. To further complicate this process and make it more secure, you can also salt your hash. Salting your hash involves adding random data to the original data before the hashing process which makes it even harder to crack the hash.
BCrypt hashes will always looks like $2a$13$ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm
which does have a structure. The first small bit of data $2a
is defining what kind of hash algorithm was used. The next portion $13
defines the cost. Cost is about how much power it takes to compute the hash. It is on a logarithmic scale of 2^cost and determines how many times the data is put through the hashing algorithm. For example, at a cost of 10 you are able to hash 10 passwords a second on an average computer, however at a cost of 15 it takes 3 seconds per hash⊠and to take it further, at a cost of 31 it would takes multiple days to complete a hash. A cost of 12 is considered very secure at this time. The last portion of your hash $ZyprE5MRw2Q3WpNOGZWGbeG7ADUre1Q8QO.uUUtcbqloU0yvzavOm
, looks like one large string of numbers, periods, and letters but it is actually two separate pieces of information. The first 22 characters is the salt in plain text, and the rest is the hashed password!
Finished Unit and Functional testing for @freecodecamp.
I was always curious about how to test an actual form. #ChaiJS can mimic filling in a form & submitting it, and test the output on the screen.
Assertions cheat sheet: https://devhints.io/chai R3D7/#100DaysOfCode
Chai Testing Repo running on Heroku
suite('Functional Tests', function () {
test('submit "surname" : "Vespucci" - write your e2e test...', function (done) {
browser
// fill the form, and submit.
.fill('surname', 'Vespucci')
.pressButton('submit', function () {
// assert that status is OK 200
browser.assert.success(); // 200
// assert that the text inside the element 'span#name' is 'Amerigo'
browser.assert.text('span#name', 'Amerigo', 'name should be Amerigo');
// assert that the text inside the element 'span#surname' is 'Vespucci'
browser.assert.text('span#surname', 'Vespucci', 'surname is Vespucci');
// assert that the element(s) 'span#dates' exist and their count is 1
browser.assert.element('span#dates', 1, 'dates element exists');
// assert.fail();
});
done();
});
});
R3D6/#100DaysOfCode Another day working with Chai on @freeCodeCamp. Iâm impressed with the flexibility of tests like âapproximately.â
Spent a while debugging my testing scripts until I realized I had the wrong branch set to be automatically deployed on Herokuâoops! đ
Happy to now be a monthly supporter for @freeCodeCamp!
R3D5/#100DaysOfCode Started working through the testing curriculum; worked out how to manage via GitHub and Heroku (instead of Glitch) so my workflow is much happier. đ€ https://github.com/virtual/boilerplate-mochachai
Chai Testing Repo running on Heroku
Note on CSS Update for reduced motion:
@âmedia (prefers-reduced-motion: reduce) {
*,
*:after,
*:before {
animation: none !important;
transition: none !important;
}
}
R3D4/#100DaysOfCode In-range but still invalid? Hereâs a pen to help you understand the difference between :invalid and :out-of-range CSS psuedo-selectors. Example from @FrontendMasters CSS In-Depth. https://codepen.io/virtual/pen/abORwjw?editors=0100
Selectors that match based on the current state of the UI
:valid
:invalid
(out of range and/or unmatched step value)
:required
:optional
:in-range
:out-of-range
(out of range, ignores step)
:user-invalid
Similar to on blur
R3D3/#100DaysOfCode Iâll admit, I donât use these much (aside from form styling), and many have been around since CSS2! Do you ever style your elements this way?
Reviewing selectors, specificity, and gotchas with @estelle âyou know, for the next CSS trivia night. Winking face
Selector: âThe part that comes before the declaration blockâ
Pseudo-classes have the same selector weight as classes (0 - 1 - 0)
[attribute]
Has the attribute (e.g. [type])
[attribute='value']
Equal to the exact value only
[attribute~='value']
Includes the full word âvalueâ
[attribute|='en']
Equal to âenâ specifically or âen-â followed by anything
[attribute^='val']
Starts with (e.g. âhttps:â)
[attribute$='lue']
Ends with (e.g. â.pdfâ)
[attribute*='alu']
Has anywhere
Interesting information on CSS failures and faux-commenting while youâre testing code as shown from CSS in Depth (v3) with @estelle, @FrontendMasters.
If you style CSS, take a peek at all these less common selectors! https://drafts.csswg.org/selectors-4/#overview R3D2/#100DaysOfCode
Began CSS In-Depth, v3, Estelle Weyl (FEM)
Scrubbed my toaster, cleaned my knife set, cut my finger, and then decided maybe I should start #100daysofcode back up instead.
Challenge 8/#javascript30ânow enjoying this mindnumbingly fun canvas board I can now doodle on like itâs 2002. R3D1 https://virtual-javascript30.herokuapp.com/08-canvas/index.html
Starting Round 3 of #100DaysOfCodeâhonestly for a mental health break from reading #covid19 info; getting back into Javascript30
Ideas tabled for later (see R3 Day 100 for projects worked on!)