Ever wanted to know how to write something in binary?
1 2 3 4 5 6 7 8 9 10 11 12 13 14
functiontextToBinaryString(text) { let binaryArray = []; for (let i = 0; i < text.length; i++) { // string.charCodeAt(index) will give you the UTF-16 code point of a character at a given index let charCode = text.charCodeAt(i); // Number.toString(radix) takes a number and returns the string equivalent of that number in the provided base (radix) let binary = charCode.toString(2); // Since we know that we're going to be working with just letters a-zA-Z, we know we only care about up to 8 bits of data, so we can pad the start of the string so that a binary letter is represented by 8 bits let binaryString = binary.padStart(8, 0); binaryArray.push(binaryString); } // Now in order to get a string rather than the array, we need to join the elements of the array together. Since we want to differentiate our letters so they are more easily readable, we can include spaces between each letter. return binaryArray.join(" "); }
Ever wanted to convert a binary string to text?
Let’s say we have the binary string “01010100 01001000 01000001 01001110 01001011 00100000 01011001 01001111 01010101” :
functionbinaryStringToText(binaryString) { let binaryLetters = binaryString.split(" "); // Each letter is represented by an 8-bit binary string
let sentenceAsArray = binaryLetters.map(binaryLetter => { // Each letter is first parsed as an integer using the radix (number base) of 2 (for binary) in order to get the character code for that letter let charCode = parseInt(binaryLetter, 2); /* If you were to have a separate function just handling this conversion to character codes, you would get an array like: [84, 72, 65, 78, 75, 32, 89, 79, 85] */
// We then get the letter by calling the String method fromCharCode to get the ASCII value associated with that char code
/* Since we're only dealing with 8-byte strings, it's backward-compatable with with ASCII, so you could look the values up in an ASCII table: 84 - T 72 - H 65 - A 78 - N 75 - K 32 - Space 89 - Y 79 - O 85 - U */ let letter = String.fromCharCode(charCode); return letter; });
/* The above map returns an array like this: ['T', 'H', 'A', 'N', 'K', ' ', 'Y', 'O', 'U'] So in order to get the sentence back in string form, you just join the elements of the array on an empty string */
let sentence = sentenceAsArray.join(""); return sentence; // "THANK YOU" }
In the previous post, I’d said I thought that the reason split_consonants had split in such a way that the consonants were in the first part of the split and the vowels were in the second was because the only words that would have made use of the variables created by split_consonants would be words that started with consonants. After playing around with IO.inspect() on the vowel and consonant variables given different input, I believe that to be the case.
So I realized that since two of my conditionals evaluated to the same thing, I decided to combine them into one condition:
Original:
1 2 3 4 5
String.match?(first_char, ~r/[aeio]|(?<!q)u/) -> word <> "ay"
String.match?(first_char, ~r/y|x/) && !String.match?(second_char, ~r/[aeio]|(?<!q)u/) -> word <> "ay"
Refactor:
1 2 3
String.match?(first_char, ~r/[aeio]|(?<!q)u/) or (String.match?(first_char, ~r/y|x/) && !String.match?(second_char, ~r/[aeio]|(?<!q)u/)) -> word <> "ay"
Another change I ended up making was to clean up the translate function. Since I’m passing the words argument into successive functions, I don’t actually need to name the variable – I could just use the pipe operator to pass it into the next function in the sequence:
Original:
1 2 3 4 5 6
deftranslate(phrase) do words = String.split(phrase, " ", trim:true)
Enum.map(words, fn x -> piglify(x) end) |> Enum.join(" ") end
Refactor:
1 2 3 4 5
deftranslate(phrase) do String.split(phrase, " ", trim:true) |> Enum.map(fn x -> piglify(x) end) |> Enum.join(" ") end
defmodulePigLatindo @doc""" Given a `phrase`, translate it a word at a time to Pig Latin. Words beginning with consonants should have the consonant moved to the end of the word, followed by "ay". Words beginning with vowels (aeiou) should have "ay" added to the end of the word. Some groups of letters are treated like consonants, including "ch", "qu", "squ", "th", "thr", and "sch". Some groups are treated like vowels, including "yt" and "xr". """ @spec translate(phrase :: String.t()) :: String.t() deftranslate(phrase) do String.split(phrase, " ", trim:true) |> Enum.map(fn x -> piglify(x) end) |> Enum.join(" ") end
defpiglify(word) do first_char = String.at(word, 0) second_char = String.at(word, 1)
split_consonants = Regex.split(~r/([aeio]|(?<!q)u)(.*)/, word, trim:true, parts:2, include_captures:true )
conddo String.match?(first_char, ~r/[aeio]|(?<!q)u/) or (String.match?(first_char, ~r/y|x/) && !String.match?(second_char, ~r/[aeio]|(?<!q)u/)) -> word <> "ay"
true -> vowel <> consonant <> "ay" end end end
Short post today because I woke up early to watch the in-flight abort test for Space-X and I spent the day watching the Liverpool FC game, a hockey game, the Kansas City Chiefs game, and now the Packers game – and I am tired.
Create a program that translates English into Pig Latin. In case you didn’t go through the Pig Latin phase as a kid (or in case you’ve forgotten how it works), here are the rules:
If a word begins with a vowel, add “ay” to the end of the word
If a word begins with a consonant, move it to the end of the word before adding “ay” to the end
If a ‘q’ is followed by a ‘u’, treat them as one consonant group
If a word starts with ‘y’ or ‘x’ followed by a consonant, treat them as vowels
The Test Input
Testing Rule 1
Given the input “apple”, the program should return “appleay”
Given the input “ear”, the program should return “earay”
Given the input “igloo”, the program should return “iglooay”
Given the input “object”, the program should return “objectay”
Given the input “under”, the program should return “underay”
Given the input “equal”, the program should return “equalay”
Testing Rule 2 with one starting consonant
Given the input “pig”, the program should return “igpay”
Given the input “koala”, the program should return “oalakay”
Given the input “yellow”, the program should return “ellowyay”
Given the input “xenon”, the program should return “enonxay”
Given the input “qat”, the program should return “atqay”
Given the input “pleasure”, the program should return “easureplay”
Given the input “stringify”, the program should return “ingifystray”
Given the input “zkrrkrkrkrzzzkewk”, the program should return “ewkzkrrkrkrkrzzzkay”
Testing Rule 2 with multiple starting consonants
Given the input “chair”, the program should return “airchay”
Given the input “therapy”, the program should return “erapythay”
Given the input “thrush”, the program should return “ushthray”
Given the input “school”, the program should return “oolschay”
Testing Rule 3
Given the input “queen”, the program should return “eenquay”
Given the input “square”, the program should return “aresquay”
Testing Rule 4
Given the input “yttria”, the program should return “yttriaay”
Given the input “yddria”, the program should return “yddriaay”
Given the input “xray”, the program should return “xrayay”
Given the input “xbot”, the program should return “xbotay”
Testing Phrases
Given the phrase “quick fast run”, the program should return “ickquay astfay unray”
Given the phrase “the quick brown fox jumps over the lazy dog”, the program should return “ethay ickquay ownbray oxfay umpsjay overay ethay azylay ogday”
Solving the Problem
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Split the phrase into words For each word: If first character is a vowel: Add "ay" to end of word Else While the character we're looking at is a consonant, look at the next character If the next character is a 'u' and the current character is a 'q': treat them both as if they were consonants and continue If the next character is a consonant and the current character is a 'y' or 'x': treat the 'y' or 'x' as if it was a vowel If the next character is treated as a vowel: - Split the word before the vowel, - Move the partial string before the vowel to the end of the word - Add "ay" to the end of the word
1 2 3 4 5 6
deftranslate(phrase) do words = String.split(phrase, " ")
Enum.map(words, fn x -> piglify(x) end) |> Enum.join(" ") end
String.split(phrase, " ") is taking the phrase and splitting them by a single space, in order to get a list of words. We then call Enum.map on that list of words in order to make a call to piglify, which will translate those words into Pig Latin. Then we use the pipe operator |> to take the output of the map and use it as the first argument to Enum.join(" "), which turns the list of piglified words back into a string, preserving the space between words.
defmodulePigLatindo @doc""" Given a `phrase`, translate it a word at a time to Pig Latin. Words beginning with consonants should have the consonant moved to the end of the word, followed by "ay". Words beginning with vowels (aeiou) should have "ay" added to the end of the word. Some groups of letters are treated like consonants, including "ch", "qu", "squ", "th", "thr", and "sch". Some groups are treated like vowels, including "yt" and "xr". """ @spec translate(phrase :: String.t()) :: String.t() deftranslate(phrase) do words = String.split(phrase, " ", trim:true)
Enum.map(words, fn x -> piglify(x) end) |> Enum.join(" ") end
defpiglify(word) do first_char = String.at(word, 0) second_char = String.at(word, 1)
split_consonants = Regex.split(~r/([aeio]|(?<!q)u)(.*)/, word, trim:true, parts:2, include_captures:true )
conddo String.match?(first_char, ~r/[aeio]|(?<!q)u/) -> word <> "ay"
String.match?(first_char, ~r/y|x/) && !String.match?(second_char, ~r/[aeio]|(?<!q)u/) -> word <> "ay"
true -> vowel <> consonant <> "ay" end end end
String.at() is a fairly straightforward function. It takes a string as the first argument, an index as the second, and returns the grapheme (essentially, letter) that exists at that index of the string (or nil if the index is greater than the length of the string). So first_char and second_char are literally just the first and second letters of the word.
What is going on with this regex?!, you might ask. And it’s a fair question. Let’s break it down.
~r/([aeio]|(?<!q)u)(.*)
~r is a sigil for creating regular expressions The way I interpreted () was as a capture group because that’s what I’m familiar with it being in other languages, though I’m not certain that’s the same thing that’s happening in Elixir. [aeio] is to match characters who are members in that set. | is, predictably, ‘or’ (?<!q)u is “match u but only if it isn’t following a ‘q’” (.*) is “match everything else in the string”, since . is the wildcard to match any character and * is a quantifier to match 0 or more.
So if this is doing what I think it is, it’s taking the word and splitting it into two parts. Because the only cases we’re going to be working with split_consonants is in situations where the first part of the word is a consonant, I think that’s why the first part of the split is a consonant and the second is the vowel. I honestly need to play around with it a bit more to know for sure.
Then for the conditional, we want to have a case for if the first character in the string matches the regular expression we’ve made to determine if a character is a vowel or will be treated as a vowel and if it is, return the word concatenated with “ay” word <> "ay".
We then have a conditional for if the first character is a y or x if the second character is a consonant (which is really just putting a not in front of the String.match? for vowels). If that’s the case, we also want to return the word concatenated with “ay”.
Then, since evrerything else should follow the alternative method for building up pig latin, we can make this conditional simply be true since if the other two conditions fail, this should always pass. If the word gets to this conditional, then the vowel portion of the word should be concatenated with the consonant-leading portion of the word concatenated with “ay”, vowel <> consonant <> "ay"
–
Let me know if you thought this was helpful (or what you thought of it in general)!
defmoduleTwelveDaysdo @doc""" Given a `number`, return the song's verse for that specific day, including all gifts for previous days in the same line. """ @spec verse(number :: integer) :: String.t() defverse(number) do end
@doc""" Given a `starting_verse` and an `ending_verse`, return the verses for each included day, one per line. """ @spec verses(starting_verse :: integer, ending_verse :: integer) :: String.t() defverses(starting_verse, ending_verse) do end
@doc""" Sing all 12 verses, in order, one verse per line. """ @spec sing() :: String.t() defsingdo end end
I knew that there was repetition involved in this problem, and I was originally thinking about how to go about generating the previous days’ gifts, but I psyched myself out. You’re overthinking again! You could just hardcode it!
@spec verse(number :: integer) :: String.t() defverse(number) do case number do 1 -> "On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree."
2 -> "On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree."
3 -> "On the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
4 -> "On the fourth day of Christmas my true love gave to me: four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
5 -> "On the fifth day of Christmas my true love gave to me: five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
6 -> "On the sixth day of Christmas my true love gave to me: six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
7 -> "On the seventh day of Christmas my true love gave to me: seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
8 -> "On the eighth day of Christmas my true love gave to me: eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
9 -> "On the ninth day of Christmas my true love gave to me: nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
10 -> "On the tenth day of Christmas my true love gave to me: ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
11 -> "On the eleventh day of Christmas my true love gave to me: eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
12 -> "On the twelfth day of Christmas my true love gave to me: twelve Drummers Drumming, eleven Pipers Piping, ten Lords-a-Leaping, nine Ladies Dancing, eight Maids-a-Milking, seven Swans-a-Swimming, six Geese-a-Laying, five Gold Rings, four Calling Birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
_ -> nil end end
Yeah, I know, cheating. But it wasn’t like I could avoid recursion if I wanted to write the verses function.
This part was actually difficult for me. Recursion and I have not exactly historically been friends and I couldn’t use my usual changing variables technique that I could use in JavaScript, since mutation isn’t possible in Elixir. SO RECURSION IT IS.
All right, if I’m going to do recursion, I’m going to need to know the base case. At what point am I done with recursion? Thinking about it in the paradigm I’m more used to, this would be the end condition of a while loop.
The first thing I generally do when approaching problems like this is say in English what I want to happen. Sometimes, I even do this aloud (if no one’s around to hear me).
Make a variable current_verse equal to starting_verse. While current_verse is less than or equal to ending_verse, we want to call verse on the current_verse and add that verse to the verses I’ve already got from calling verse, making sure to add a new line between them.
In JavaScript, that’d look like:
1 2 3 4 5 6 7 8
functionverses(starting_verse, ending_verse) { let current_verse = starting_verse; let complete_verses = verse(current_verse); while (current_verse <= ending_verse) { complete_verses += "\n" + verse(++current_verse); } return complete_verses; }
Thankfully for me, you can do string concatenation in Elixir too! <> is the way to accomplish it.
1 2 3 4 5 6 7 8 9 10
defverses(starting_verse, ending_verse) do if starting_verse === ending_verse do verse(ending_verse) else combined_verses = verse(starting_verse)
current_verse = starting_verse + 1 combined_verses <> "\n" <> verses(current_verse, ending_verse) end end
If the starting_verse equals the ending_verse, return verse(ending_verse). If the starting_verse doesn’t equal the ending_verse, take the value of the return of verse(starting_verse) and assign it to the variable combined_verses. Initialize a variable current_verse with the value of one greater than starting_verse. Return combined_verses concatenated with a newline concatenated with the return of the call to verses(current_verse, ending_verse).
Given verses(1, 3), the execution would go something like this:
def verses(1, 3) do if 1 === 3 do verse(3) else combined_verses = verse(1) ( "On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree.")
current_verse = 1 + 1 combined_verses <> "\n" <> verses(2, 3) end end
def verses(2, 3) do if 2 === 3 do verse(3) else combined_verses = verse(2) ( "On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree.")
current_verse = 2 + 1 combined_verses <> "\n" <> verses(3, 3) end end
verses(2, 3) returns "On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree.\nOn the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
def verses(3, 3) do if 3 === 3 do verse(3) ("On the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.")
-- else doesn't evaluate end end
verses(3, 3) returns “On the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.” So when we evaluate verses(2, 3), we can plug that value in to get the output of verses(2, 3). So the output of verses(2, 3) becomes “On the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree.\nOn the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.” Then the output of verses(2, 3) can be plugged into the function call to verses(1, 3) to give us “On the first day of Christmas my true love gave to me: a Partridge in a Pear Tree.\nOn the second day of Christmas my true love gave to me: two Turtle Doves, and a Partridge in a Pear Tree.\nOn the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.”
The final function was easier - sing all 12 verses in order:
1 2 3
defsingdo verses(1, 12) end
Then after talking it over with some other programmers whom I respect the opinion of, it was pointed out to me that I completely ignored the point 9of the exercise in that I wasn’t really employing the functional paradigm that Elixir is known for to solve the problem. And that one of the best things with Erlang and Elixir is pattern-matching, so I might as well use it.
sigh
Not wanting to spend a million years trying to solve a problem while fighting syntax, I went the easy route and tried to solve the problem using functional programming in JavaScript (I am ever thankful that you can use multiple paradigms):
functioncomposeVerse(5) { let verse = '' while (5 > 1) { verse += `${giftMap[5]}, `; // giftMap[5] is "five Golden Rings", so: // "five Golden Rings, " 5--; // 4 } while (4 > 1) { verse += `${giftMap[4]}, `; // giftMap[4] is "four calling birds", so: // "five Golden Rings, four calling birds, " 4--; // 3 } while (3 > 1) { verse += `${giftMap[3]}, `; // giftMap[3] is "three French Hens", so: // "five Golden Rings, four calling birds, three French Hens, " 3--; // 2 } while (2 > 1) { verse += `${giftMap[2]}, `; // giftMap[2] is "two Turtle Doves", so: // "five Golden Rings, four calling birds, three French Hens, two Turtle Doves, " 2--; // 1 } while (1 > 1) // evaluates to false, so while loop ends verse += `and ${giftMap[1]}.` // giftMap[1] is "a Patridge in a Pear Tree", so: // "five Golden Rings, four calling birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree." return verse; }
composeVerse(5) returns “five Golden Rings, four calling birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.”
The call to verse(5), then, returns:
“On the fifth day of Christmas, my true love gave to me: five Golden Rings, four calling birds, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.”
These are pretty much the exact same thing as the giftMap and ordinal in the JavaScript implementation. In both languages, I’m using maps to represent the key-value pairs for the gifts and the ordinal day. So far so good.
1 2
defstart(number), do:"On the #{Map.get(@ordinal, number)} day of Christmas my true love gave to me: "
This is almost the same as the JavaScript implementation. Only, this time I’m not calling another function to compose the verse. At least not in this function. This function is literally just to start us off. Map.get(map, key) is a way to get the value of a given key from a map in Elixir, so it’s equivalent to what we saw earlier with ordinal[number]. #{} is like the equivalent of ${} in JavaScript - an indication to evaluate what’s inside before turning it into a string.
I wrote some helper functions to return the gifts out of the giftMap. One of the cool things with Elixir is you can have multi-clause functions – the first function whose function clause matches the argument you provided will execute.
1 2 3 4 5 6 7 8
@spec gift(number :: integer) :: String.t() defgift(1) do "and #{Map.get(@giftMap, 1)}" end
defgift(num) do "#{Map.get(@giftMap, num)}, " end
So the first gift is only run if the number 1 is passed in, else the second function definition is used.
1 2 3 4 5 6 7 8 9 10 11
defverse(1) do "#{start(1)}#{Map.get(@giftMap, 1)}." end
defverse(number) do "#{start(number)}#{ Enum.reduce(number..1, "", fn verse, verses -> verses <> gift(verse) end) }." end
So… if we call verse(1), the first function definition for verse is executed.
The result of verse(1) still being:
“On the first day of Christmas, my true love gave to me: and a Partridge in a Pear Tree.”
Now let’s take a closer look at that second function:
1 2 3 4 5 6 7
def verse(number) do "#{start(number)}#{ Enum.reduce(number..1, "", fn verse, verses -> verses <> gift(verse) end) }." end
Enum.reduce(enumerable, acc, fun) is the reduce being used here. number..1 is a range from number to 1 - if number is 3, for example, this would be [3, 2, 1]. acc stands for accumulator - the initial value being passed into the function. The result of the function is used as the accumulator in the next run of the function. In this case, the original accumulator is an empty string (like the verse variable in the earlier JavaScript implementation). The function, then, takes the verse being iterated on (the current number in the range) and the accumulator and returns the accumulator concatenated with the value of the call to gift(verse).
Say you wanted to get the third verse, the Enum.reduce evaluation would go like this:
1 2 3 4 5 6 7 8 9 10 11
Enum.reduce(3..1, "", fn 3, "" -> "" <> gift(3) // "three French Hens, " fn 2, "three French Hens, " -> "three French Hens, " <> gift(2) // "three French Hens, two Turtle Doves, " fn 1, "three French Hens, two Turtle Doves, " -> "three French Hens, two Turtle Doves, " <> gift(1) // "three French Hens, two Turtle Doves, and a Partridge in a Pear Tree" end)
#{start(3)} would return:
“On the third day of Christmas my true love gave to me: “
“three French Hens, two Turtle Doves, and a Partridge in a Pear Tree”
And the end of the function is to add a period, giving us:
“On the third day of Christmas my true love gave to me: three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.”
Hurray!
1 2 3 4
@spec verses(starting_verse :: integer, ending_verse :: integer) :: String.t() defverses(starting_verse, ending_verse) do Enum.map_join(starting_verse..ending_verse, "\n", &verse(&1)) end
I will admit this looks intimidating if you don’t know Elixir. I literally wrote a note to myself under this function when I was writing it to remind myself what the &verse(&1) was equivalent to so that I wouldn’t freak out when next I cast eyes upon it.
Enum.map_join(enumerable, joiner \ “”, mapper. Joiner is something you want to join the enumerable things on. It’s like if you call String.join(‘,’) in JavaScript. Mapper is going to be a function that you call on the elements in the enumerable (since you’re mapping over them as well as joining – very intuitive naming).
In our case, & is a capture operator to partially apply a function and &1 is a value placeholder. So if we wanted to write code that was less confusing for other people, we could write &verse(&1) as fn x -> verse(x) end.
Anyway, it’s getting late and I spent a good hour on this longer than I meant to because I had a cat fall asleep on my arm. (Want to know how difficult it is to write code blocks with one arm? so hard)
If you thought I’d gotten to the point where I could deploy this blog without incident, you’re sorely mistaken. And it’s evident from the way the rest of the night went.
You see, the thing I had been trying to do, adding this blog onto the website I already had deployed through another Github repo on Netlify, wasn’t as easy as the documentation would have led me to believe. Or rather, there were some gotchas.
When this blog was deployed on Netilfy by itself, everything seemed fine. The CSS seemed to load appropriately, the navigation seemed to be operating as it should, things looked fine.
Yesterday at 6:18 PM
1 2 3 4 5
[[redirects]] from = "/blog/*" to = "https://modest-yonath-e1349c.netlify.com/:splat" status = 200 force = true
Result:
Grah! So, it looks good, except for the fact that the images aren’t loading. Hitting the website link takes me back to my main site, style’s the way I have it set. That’s got to be a quick fix!
None of the links work. The CSS doesn’t work. What happened to that post with the images in it? (I can’t honestly remember when I started writing the post, but looking at the Netlify preview from before this, I must have started it before this point?
Yesterday at 7:49 PM
Okay, okay, maybe it’s a pathing issue, I think. Maybe it’s that I need to enable the post_asset_folder, maybe I need to move the images around. Ooh, index_generator, that sounds like a good thing. Says something about blog root – I should probably change that to /blog/!
_config.yml:
1 2 3 4 5 6
post_asset_folder: true relative_link: true
... index_generator: path: "/blog"
You know, now I think Netlify’s playing a mean trick with time, since the blog deploys still don’t have the last post…. hm.
Most of the links work! And the styling’s working! The Website link reads as broken, however, and the endpoint is at /blog instead of at the root. Hm.
Yesterday at 7:55 PM
Changed the root back:
_config.yml:
1 2
index_generator: path: ""
Yesterday at 8:00 PM
Here’s yesterday’s post!
How do you know?, you might ask.
Because at this point I was feeling confident enough to have responsible git messages. The last … several… have been a variation of Config, but this one literally says Second post.
Now we’re to the point where the first image comes into play on the blog side of things. Style looked okay, images were broken. Meh.
The rest of the night in pictures
Commits on blog repo:
Commits on website repo:
It even got so bad that Netlify seemed to be trying to get me to take a break:
The Importance of Breaks
See how many commits were made in the course of three hours? Do you see how uninformative the majority of those commit messages were?
There’s a reason for that. I was getting frustrated with my inability to do a task that others had written about being easy, I’d let it get to my head that not accomplishing the task would brand me a failure, and I was too stubborn to stop. The only reason I did stop was it became clear there was no way I was going to solve the problem any time soon and I had to be up in less than 7 hours for work.
Bringing us to today …
_config.yml:
1
relative_link: false
WHY ARE YOU HAPPY ABOUT THIS?! you might ask. Because it looks terribly broken. All that lovely CSS is gone. Clicking any of the links takes you to a page not found. And you don’t have to take my word for it.
I’m happy because with it looking like that on the blog’s own route, it looked like
Take-away:
If you find yourself struggling with something that other people have labeled as easy, don’t sweat it. Everyone’s experience is different - it’s dependent on what you’ve done before, what you’re familiar with, if you’ve solved similar problems in the past.
If your git commit messages get to be flippant and give no information about what kind of changes are contained within, that might be the time to take a break from it. I was pounding my head at this problem for literally hours yesterday when it literally only took a few minutes of time today to realize what the actual problem was.
I’m going to cross my fingers, make a hopefully more thorough commit message, and hope that adding this post doesn’t completely break everything all over again.
I promise future posts will not all be rambling and uninformative like this. I hope to start actually talking about programming in the near future. A few co-workers and I currently spend an hour every Wednesday going through https://exercism.io/ problems in the Elixir track to try to learn Elixir and I think it’d be fun to start documenting some of my experience with that, especially breaking down syntax and comparing it with different programming languages I’m more familiar with.
I’m going to give you a quick overview of how this ‘getting a blog together’ thing has been (a story in git commits):
All right, you tell yourself. _I’m trying out this framework I’ve never used before, but it seems really intuitive! I should have a blog in no time!
Okay, don’t panic, you tell yourself when the layout you spent the last better part of an hour playing around with doesn’t render in the slightest. Static files are a common problem. You know this. Remember when you didn’t know Django? Static files were your nemesis until you found Whitenoise. This is probably just as simple.
The first indicator that I do not understand what is going on with my code is when my git commit message isn’t informational in nature.
The second indicator is that I use the same commit message. This means that not only do I not have a high confidence level in the change being effective, but I’m too aware of this fact to put any effort into the operation beyond hitting the up arrow a set number of times and hitting enter.
git add . Up arrow Up arrow Enter Up arrow Up arrow Up arrow Enter
Last Known Good Configuration is a blog where I try to explain programming as I know it to whomever will listen in the hope that I can help someone else on their journey to becoming a better programmer.
We have nothing to fear but fear itself
When I was starting to learn to program, I would see my peers publishing articles about their milestones - when they learned a concept or mastered a new part of tech, they’d memorialize it by consolidating the information into an article on Medium or some other platform. And part of me always felt jealous of that. That they felt comfortable enough to share their vulnerability like that - admitting that they didn’t understand it right away, acknowledging that it was a process.
By the time I’d gained enough confidence in my own abilities, gotten over my imposter syndrome as it were, I felt that the mini milestones I achieved weren’t worthy writing about.
But the truth is that the reasoning is the same. Fear. Fear of appearing the fool, of being proven wrong, of being called out as not understanding something well enough.
“Amy’s not naturally smart, she just works hard” - a sentence fragment meant to inspire my brother who was “naturally smart” to work harder, but only really succeeded in making me feel like I would never reach the ceiling others could.
Am I working hard enough? Will I ever?
Future Plans
It is my firm belief that there are no dumb questions, only people too self-absorbed or impatient or tunnel-visioned to see the difference in view, to work out the gaps necessary to bridge misconception. Questions might seem stupid, but we often underestimate the amount we know, take it for granted, and forget that not everything we take to be common knowledge actually is. And we can forget that just because we’ve come to a conclusion about something doesn’t mean that we’ve come to the right conclusion, or for the right reasons.
I’ll admit that although one of my passions in life is to mentor and teach, I still fall short at times. It’s easy to fall into the temptation of offering a solution instead of encouraging someone to come up with their own solution.
But I’m working on it.
I want this to serve as a reminder to sit down and make the effort to truly understand the code that I’m writing in order to help other people better understand why it’s written the way that it is. And I want to share that journey.
So, if you can tolerate my sense of humor and my stumbles in the dark, maybe we can do this thing together.