FYI
I’ve succumbed to industry trends and moved my blogging over to Medium
almost home
Show & Tell
TVSTRANGERTHINGS
ojovivo
One Nice Bug Per Day
RMH
No title available
taylor price
Cosmic Funnies
"I'm Dorothy Gale from Kansas"
🪼

Origami Around
YOU ARE THE REASON
d e v o n

@theartofmadeline
will byers stan first human second

⁂

oozey mess
Three Goblin Art
Sade Olutola
seen from India

seen from Germany
seen from Malaysia
seen from Italy

seen from Malaysia
seen from India
seen from Malaysia
seen from United States
seen from United States

seen from Portugal

seen from T1

seen from Malaysia
seen from United States
seen from Thailand

seen from Germany
seen from Canada

seen from Malaysia

seen from Mexico

seen from Malaysia

seen from Malaysia
@professionalshaps-blog
FYI
I’ve succumbed to industry trends and moved my blogging over to Medium
Appetite for Destructuring
For my most recent project, I’ve been working on a React-Redux implementation of Minesweeper. In that process, I frequently access specific squares in the grid. I generally do so by typing:
grid[1][5]
which got me the sixth square across the second row. (Ah, zero indexing.) For some functionality, I needed to pass around sets of coordinates as arrays, a la [1, 5]. To access those arguments, I found myself typing things like
grid[array[0]][array[1]]
Messy, but okay. Or so I thought, before I wasted a good half hour of debugging without noticing the error right in front of my face. I had actually typed
grid[array[0][array[1]]]
I vaguely remembered there was ES6 syntax that would have avoided this issue, but at the time I was so happy to have caught the error I didn't bother to refactor. Turns out, that feature is called destructuring assignment, and it is neato. Destructuring is a powerful and convenient way to extract values from Javascript objects/arrays. I could have saved myself a bracket headache had I just said:
let [row, column] = array row => 1 column => 5 grid[row][column]
It works with various levels of nesting, too! Let's say I was looking at my entire grid (we'll say it's 6x6), and I wanted to find square 1, 5 again.
let [ ,[ , , , , , square]] = grid
square
=> {clicked: false, column: 5, flag: false, mine: false, row: 1, text: 3}
Object destructuring is also possible. I know it's a bit circular, but to demonstrate, I'll grab the row and column #s out of the square object.
let {row: a, column: b} = square
a
=> 1
b
=> 5
The more nested your data is, the more complex your destructuring might look. This just scratches the surface of the possibilities, but there's tons more info out there. This tutorial is quite detailed, with fun examples. Here's a textbook chapter that includes a behind-the-scenes look at the algorithm. And here's somebody's great Gist if you just want to see some code in action.
Everybody’s Got the Right
Assassins reference or common sense statement? Only title will tell...
Access to the Internet is a fundamental right in modern society. As web developers, it is vital that we make sure our applications and sites are available to any and all people who might want to use them. W3C, the organization that sets web standards, provides a great deal of information and resources towards that goal.
Their Web Content Accessibility Guidelines 2.0 are organized into four key concepts. I'll discuss each one a bit, then link out to some resources for more information.
Web content must be perceivable
Deaf people and folks with low/no vision use the Internet. They might need captions or transcripts for video/audio content. They might need alt-text for images and label-tags on forms for their screen-readers. They might need higher contrast color palettes.
Web content must be operable
Some people operate their computers solely by keyboard, as they do not have the fine motor control to manage a mouse. Your site should be prepared for this use case. Important information should not be hidden in mouseovers/hovers.
Web content must be understandable
Your user interface should be consistent in form and predictable in navigation. If the user makes a mistake, the specifics of the problem and how to correct it should be clearly stated in the error message.
Web content must be robust
Your website should be compatible with as many technologies that might access it as possible. Besides desktop browser and mobile web, consider assistive technologies like screen-readers.
For further learning:
Google has a free online course on the topic.
WebAIM (Accessibility In Mind) is an amazing website with a ton of information - start with the introduction, or check out this quick reference you can download as a PDF for safekeeping.
Here's a list of best practices organized by design element.
Here's a page that is has a few examples of accessible websites, with code examples.
Here's a blog post with 10 simple action steps you can take right now.
For your existing projects, there are TONS of tools that will provide feedback on your compliance with these standards and how you can improve your site. If you aren't the sole decision-maker on these projects (i.e. you're an employee at a company), W3 even provides a guide to making the business case for your organization.
Words flooded my senses
title quote from Hamilton
One of the many miniprojects I've been futzing with these days is a Facebook chatbot. I've been interested in chatbots since I was a youngling on AIM trying to get SmarterChild to use cusswords.
My favorite part of any chatbot experience is trying to trip it up with unexpected inputs. By coming out of left field, I can see past the limits of the dialog tree's imagination. But in creating my own, I realized how I'd overlooked how complicated even the simplest action could be. Just to acknowledge a greeting, I need my bot to know that
"hi" === "Hi" === "hello" === "hey" === "sup" === "yo" === "hola"
just confusing a TV show, nbd
Now surely Siri, Alexa, Cortana, and Google's (non-anthropomorphized) Assistant don't have thousand-line if/else statements checking every version of a greeting. No, they're a few steps ahead because their systems are using Natural Language Processing.
NLP is a fascinating hybrid of linguistics and compsci. There are two big subcategories: getting computers to understand humans, and getting computers to talk like humans.
You may have encountered the former in things like Google.com, or the grammar check function in word processors, and you may have noticed that it is sometimes absolutely wrong. That's because language is really complicated - there's still no consensus on how humans manage it. Words have meaning in isolation, but then combined with other words in phrases that meaning might switch entirely. (Think about "the alarm went off" vs "I shut off the alarm" - damn contranyms.)
via here
NLP is a hugely complicated and quickly growing field, especially in this era of machine learning for everyone. I am nowhere close to creating my own algorithms, but I don't necessarily need to! There are several SaaS companies dedicated to NLP, plus Tensorflow/Google has released SyntaxNet, including an amazing API for a pre-trained processor that they call Parsey McParseface. I'm looking forward to testing out Parsey and learning more about NLP - this blog post has given me some great context and further reading links.
Take a Memo
I've been doing a lot of practice code puzzles lately, on sites like HackerRank and Codewars. One of the problems that came up was to implement a function that tells you what number would be at a given spot in the Fibonacci sequence.
The Fibonacci sequence, if you don't remember offhand, is a series of numbers in which each number equals the sum of the number before it and the number before that one. The formula for that sounds like some pretty basic recursion stuff, right?
And you can, in fact, write it thusly. But once your input number gets to be a certain size, the recursive stack level gets too deep. (The below implementation started noticibly stalling out around fibo(35).)
So let's think about recursion for a second. By the time you call fibo(n-2), you've actually already calculated the result, because n-2 is the same thing as saying n-1 when you're in the fibo(n-1) call. All these ns are confusing, but I'm just saying that 35-2 is the same as 34-1.
So maybe we can save our previous calculations somewhere? The array where we store them can't be scoped to the method, because then it'll get reset every time we call it. Let's make an array right above our method, and then have our method check that array before it bothers doing any math at all.
Turns out, this process is called memoization, and can be very useful when you have "expensive" function calls or database queries that recur in your code. I found a number of useful articles about how memoization might help you.
The coolest thing I found was that, in Ruby, a lot of memoized calls use the ||= operator. ||= basically means: if the left hand has a (truthy) value, then keep that value; otherwise, assign it the right hand value. That gives us this awesome one-line solution!
Holding Out For a Heroku
I have now deployed four Rails apps to Heroku. They make it really easy, but there's some troubleshooting I've had to do every time. I figured to writing out the steps of my most recent attempt as a blog post will help both future-me and anyone else who's encountered the same errors.
The basic steps are all detailed in Heroku's helpful getting started with rails documentation. I'll repeat the commands here for reference:
heroku create
git push heroku master
But nothing showed up when I went to open my page? Obviously, I should have kept reading down the instructions and/or thought about it for a minute. With Postgres, you always need to re-migrate your tables on every new database instance.
heroku run rake db:migrate (aka)
heroku run bash
rake db:migrate
But I still seemed to be crashing. My logs showed me 500 HTTP Status and an H10 Heroku error. (Neither of those error codes are very helpful, troubleshooting-wise.)
for a cute time, check out HTTP Status Dogs
I remembered a previous deployment had been saved by restarting the dyno. Heroku defines a dyno as "a lightweight Linux container that runs a single user-specified command." For a free app, you usually only get one, and it's a web dyno. Web dynos receive HTTP traffic from the routes and serves up the application to the world. (So it's your server, but it's just a little container rather than a dedicated server box, because your free app shouldn't need much.)
heroku restart (aka)
heroku ps:scale web=0
heroku ps:scale web=1
After those didn't work, I found someone on StackOverflow with a great suggestion. So I tried
heroku run rails console
And I finally got a useful error message!
In my case, I had commented out the before_action in my ApplicationController, but left a skip_before_action in my SessionsController. Even though it was a skip, that still confused the app -- it still needed to know what it wasn't supposed to be doing.
Once I fixed that and pushed up my working code, my logs showed me a 200 code and my JSON was rendered. Phew!
An In-tro to the In-dex
This week, we're back to discussing SQL-based databases. Here-in, I am going to deliniate the basic outline of what a database index is.
Much like a regular old table, an index is a data structure containing information from your database. However, an index is organized in a manner that will make your life easier and your queries faster. There are two primary methods of indexing: clustered and non-clustered.
A clustered index is one that reorders the way the data is actually stored. It is most analagous to a phone book (remember those?); you go to S and you find me (Shapiro) right next to my phone number. If you need to find the number for a Sawyer or a Smith, you can just look around the nearby pages.
Clustered indexes are usually sorted on a Primary Key column, which in that example is LastName. You will want to choose a primary key that you invoke frequently in your database queries, which will maximize the performance enhancement you get from indexing this way.
A non-clustered index is one in which the order in the index does not match the table's order on the disk. It's more like a textbook's index - you go to S and you see Shapiro pointing to page 87, Sawyer pointing to page 208, and Smith pointing to page 145. You'll still have to turn to page 87 in the book (memory) to find me.
An example of a non-clustered index with its pointers.
Non-clustered indexes usually only contain the indexed column's data and a pointer to the location in memory where the rest of the rows are at. The indexed column can be whatever the heck you want, so it ends up being columns that you find yourself using frequently in JOIN and WHERE and ORDER BY.
You can include multiple columns in your non-clustered index, but it might slow your performance, so you would only architect your index that way if you need additional information to complete your queries. (If both I and my sister were featured in the textbook, to find me you would need to limit your search to find the page WHERE FirstName = Laura AND LastName = Shapiro.)
The advantages of using an index are hopefully obvious at this point - improved query speed / fetch time, and easy sorting. There are some disadvantages, though. Namely: it will increase the time to modify (insert/update/delete) a DB record, and may take up extra disk space.
NoSQL: Golden Receiver
terrible title pun courtesy of Air Bud, the concept of sequels, and a stretch of an allusion to flexibility in data input
Now that I've completed the three month bootcamp intensive, I am realizing just how many more techonologies there are to learn. One of the things that's caught my attention while perusing job listings is the movement towards NoSQL databases such as MongoDB and Redis.
SQL was created in 1974, before PCs were in homes; the data was generally created (or at least inputted) by on-site professionals and took known forms. Now everyone with a smartphone is creating tweets and photos and expecting them to immediately show up on their friends' screens. These services need to be quick, distributed across multiple redundant servers, and able to evolve quickly without downtime for the users.
And so, people began to create non-relational databases. The term 'NoSQL' seems to have been widely adopted after an unofficial meetup in 2009. (That archive.org link has links to a number of fascinating slideshows and videos if you're interested in primary sources on this.) As of now, NoSQL refers to a few different database structures, none of which use the familiar tables with columns/rows.
Redis, for example, uses key-value storage -- basically all the data is in a hash, and you can only retrieve the data if you know its specific key. Mongo is a Document model db -- data is stored in a nested JSON-esque structure (called BSON), and associated data is stored on the same document rather than represented by a foreign key. (The foreign key model is what makes SQLdbs "relational.")
One key advantage of NoSQL databases is their flexibility. No schema need be defined before you start putting in data, so there's no need to migrate if you need to add a column. Or if you, say, notice a decimal place while inputting data, you need not drop your tables and re-migrate everything so that the column is now Float rather than Int. On the flip side, this makes querying across documents slower and more unpredictable. So here, it seems the benefit is to the developer and not necessarily the user.
Elastigirl here repping flexibility - no relation to Elasticsearch
Another advantage of NoSQL is the ability to scale rapidly and with minimal configuration. Because SQL needs access to all the tables at once in case of a JOIN query, large SQL databases that are larger need to be clustered or sharded or otherwise customized to scale across multiple servers. NoSQL databases have these processes built in, and are easily distributed across multiple servers (which can even be in different geographical locations).
Of course, you won't be able to use good old SELECT statements once you've got your DB set up. SQL, after all, is a language for queries and transcations based on the relational structure. It will not know what to do with a collection of BSON documents. Luckily, BSON's object-oriented structure means that you should be able to parse and coerce the data easily once you've managed to pull it up.
Both Mongo and Redis have robust documentation and many tutorials available. Mongo also has a ton of whitepapers, many of which I've downloaded to my Kindle for a little light reading. I've also just started a series of Udemy classes re: Mongo, and I'm looking forward to expanding my data storage horizons.
Singin’ in the id=“main”
Userscripts (a.k.a User Scripts, User scripts, or .user.js) are open-source licensed add-ons for web browsers that change web pages as they are loaded.
-OpenUserJS.org
I've been an infrequent user of Greasemonkey and/or Tampermonkey for a long time, but it took a full week of Javascript before I realized I could totally build them myself now. And it turns out, I had a problem I wanted to solve.
One particularly obscure hobby of mine is Figure out the Lyrics quizzes on Sporcle.com. These quizzes give you a bunch of empty fields, and you have to guess each word in the song. Many of them don't provide you with the song name, so you won't even know whether or not you'll recognize it until you get a scaffold of common words ("and" "you" "am" "on" "what") in there.
I had long ago developed a mental list of words to try at the start of in each quiz, but now, after a long day of coding, typing each possible conjugation of "be" felt like I was pushing my luck on the repetitive stress injury front.
Version 0.1: Console
The first thing I did was write my common word list into an array of strings. I ended up with about 90 elements in that array.
The second thing I did was inspect a quiz page in my developer console. I found the input field (document.getElementById('gameinput')). I figured I could write a function that iterated through my array, then replaced the innerHTML of that element with each word.
There were two bugs with that approach. The first was that the input only read as submitted on a keyup keyboard event (or a keypress event of 'enter'). The second was that the computer worked too fast, and even once I had the keypress working, it only noticed the last element in the array. I solved that one with a 3 second timeout during each run through of the iterator.
That being solved, I could now open a quiz, open my developer console, paste in my (self-executing) function, and bam! I had a decent baseline of filled-in words. Can you recognize the song below?
Version 0.15: Tampermonkey
Well, a few days of this, and I decided that opening the file with the function and doing all that copy-pasting was still too much effort. I wanted it to run immediately upon page load, like my ad-blocker.
The first thing I tried was just replacing the function call in the default screen (below) with my script. It did not work, because it was running before my $(document) was .ready. (I actually didn't use any jQuery in this project, but I could have imported it in one of the meta tags if I needed it.)
This meant I had to add an event listener to tell me when the DOMContentLoaded (i.e.: when the webpage had stuff on it). Then, once it had loaded, I called my function.
That worked! But it also tried to work on every webpage I visited. That was fine on webpages without an element with an id of userinput, but I didn't want random search bars to be filling up all willy-nilly.
One thing you may have noticed on the new script page was a meta-field for @match, which tells you upon which websites to run the script. First step, then, was limiting it to Sporcle. But what if I was taking a different kind of quiz (say, one that fails when you input something incorrect) and the script would be a hindrance? That's when I added a conditional to check for a link back to the "lyrics" subcategory page, so it would only run when there was a reasonable expectation that it was the right kind of quiz.
Version 0.2: GreasyFork and Beyond
I know that the audience for this script is quite small. The most popular lyrics quiz of all time has fewer than a million plays. Nonetheless, I decided the world needed the option, and I went forth and released my script to the masses.
The script home base, back when O'Reilly published a book on Greasemonkey, was Userscripts.org, but it turns out they've been down for a couple years. I found two alternatives, OpenUserJS and GreasyFork.
OpenUserJS has a slightly nicer (but still far too busy) interface, but GreasyFork had some great validation messages that helped me put together the meta-information in a way that would be helpful to my hypothetical users. If you submit an invalid script on OpenUserJS, it just reloads the page, without even saving your input for revisions.
Greasyfork and OpenUserJS were also great resources when I was debugging my script. All the source code of the other user-uploaded scripts is available to peruse, so you can search for scripts with similar functionality to what you want to form a baseline of what you'll need to do.
Publishing my first userscript was a great exercise in both JavaScript and patience. There are a lot of scripts already out there, but I'm glad to know next time I have a niggling need to fix the internet, I'll have the tools to do it myself. If you'd like to download my script, there's links in "connect with me" up near the top of this page.
it was Here Comes the Sun, by the way. back in image 3?
This Debate is Bananas
People have been having massive arguments about functional vs OO for as long as the ideas have existed.People also say that Javascript, our current focus, lives in a liminal space between functional and object-oriented programming.
Although my instinct, as a recently initiated Rubyist, is to side with object-oriented programming, I can definitely understand those who say it is less efficient. As one guy[*] said, “You wanted a banana but what you got was a gorilla holding the banana and the entire jungle.” Once I’ve summarized both concepts, I’ll get back to this banana thing.
Functional programming is a paradigm that avoids mutable data and focuses on expressions over statements. In layman’s terms, you might say that functional programming treats its arguments like basic math treats numbers: if you put x = 5 and y = 10, you would always get the same answers to x+y, x-y, x*y, and x/y. The data itself is not changable; the function performs its expression (arithmetic) and returns the only possible answer for the given arguments. This avoids side effects (nothing about z = 15 changes when you say that a = x+y).
Another major distinction of functional programming is the existence of “first class”/“higher order” functions. These are functions that can take other functions as arguments, or return them as results. We saw these in last week’s blog post on closures.
Unit testing and debugging in purely functional languages is a lot easier, mostly because of the purity/lack of side effects. You can be confident that the bug isn’t caused by a dependency in a file deep in your call stack, and you can rest slightly more assured that fixing the bug will not cause a brand new bug to pop up elsewhere.
Functional languages allegedly have other advantages for concurrency, pattern matching, continuations, recursion, and many other principles I don’t quite understand yet.
Object-oriented programming is a paradigm based on the idea of “objects” (obviously) that contain data about themselves (attributes) and procedures you can do on/with them (methods). They are supposed to be better at modeling real world situations and the complex relationships among them. Basically, the data itself is the boss, and all the logic you might create to use or manipulate that data is intrinsically associated with the data(structure) itself.
Thus, when you’re looking for a banana, you might find that it is a member of the class Foods, and objects of that class “belong to” objects from another class called Eaters. Eaters can have many foods (so the gorilla could have a banana or a bamboo shoot or a termite), but themselves belong to a Climate (the jungle).
And so, when you say banana = Food.find_by(name: “banana”), you can usually then say gorilla = banana.eater and find the Eater object named “gorilla”, and then do jungle = gorilla.climate and find out you’re in the jungle. But sometimes, your banana’s eater might be a human), and then the jungle you’re in is a metaphorical concrete one. I think that would be useful information to have in most contexts.
But let’s say you’re using banana in a calorie calculator app. Then, the only information that banana really needs to hold is the number 105. Every time you invoke banana in your addUpDailyCalories() function, you want it to represent 105, regardless of whether you’re calculating for a human or your friendly neighborhood gorilla. So perhaps that app should be written functionally, since it only really relies on evaluation of mathematical expressions.
As we are training to become web developers, I suspect data mutability is going to be the killer feature that makes OOP the default choice. Websites are fundamentally reliant on human behavior, and humans like to change things. If I’m making a to-do list, createTask() simply has to return different results based on the user input, even if the difference is just that the task appears one line-item down on the page. Immutable data is swell in theory, but in practice, especially the the way we’ve been linking objects to information stored in databases, it’s unattainable.
I hope you don’t make this amateur blog post your only source for information on this debate. I’ve linked a few interesting articles throughout the above, but want to draw special attention to this PDF (pamphlet? mini-book?) from O’Reilly.
[*]Joe Armstrong, the founder of the (functional) programming language Erlang
Closure to Fine
apologies for the terrible Indigo Girls pun
It seems like just this morning that our class started it's journey into Javascript. Oh, wait! It actually was this morning.
Well, 12 hours in and I can already anticipate we're going to have a lot of functions inside other functions, which are known as closures.The outer function often a called a parent, and the inner a child, because it inherits stuff.
We need closures when we'd like to access local variables that only exist in the parent function. Local variables are re-created for every function call. This is by design, to minimize confusion (in a big project, you're gonna have a bunch of arrays called "array"). But sometimes you need those local variables for something else. You could set them as the "return" of the parent function, but what if the parent function has a function other than returning your misplaced variables? (It doesn't in the example below, but it could.)
Example
Here, our parent function is called "thing." It takes an argument and returns an anonymous closure, which takes another argument and returns a string that uses both arguments. Referencing the name of the parent function returns the entire parent function. Calling the parent function, with or without an argument, returns the child function/closure.
In this step, we set a new variable as equal to the parent function called with an argument. When you reference the new variable, it just returns the closure, with no mention of the argument. But I promise that when you call the new variable with an argument of its own, it will turn out to have remembered the first argument all along.
As you can see, we can get the same result by calling the parent function with what looks like two arguments, but is actually another call to the anonymous closure.
Anonymous functions are worth a blog post of their own, but for now, we can just assume it's okay that our closure was anonymous because we cannot call it on its own outside the scope of the parent function.
As a theater nerd and bachelor of psychology, I've mostly defined closure as "a sense of emotional resolution." That being said, I hope you've gotten some feelings-closure from this demonstration of coding-closure.
Helper Methods: Open Up The Door
Today in class, our instructor told us he'd like us to limit each method to around 7 lines of code. This made sense, based on our understanding of the single responsibiliy principle. Nonetheless, it was alarming -- what if fulfilling that responsibility required several steps? Well, that's when I realized I needed somebody - namely, a helper method.
A helper method does pretty much what it says on the tin - it is a method that helps another method perform it's task. You can use one to extract complicated logic to make a method more readable. You can also use one because you know you're going to be using that functionality frequently (ie: >=3 times).
In a way, the entire premise of Ruby on Rails is that it generates a giant pile of useful helper methods for developers to get their projects off the ground. For example, both "form_for" and "form_tag" are Rails helper methods that take in erb code and create clean HTML.
Rails also encourages that we write our own helper methods. To that end, it comes packaged with a class method, called helper, that declares another method (or a module) is available to help us with anything to do with that class.
Here's an example of how helpers cleaned up my code. I've been working on a side project I'm calling Movie Notebook. One of the goals of this project was to practice creating a functional app, from the ground up, without any instructions or expectations from teachers. Part of that, I knew, would involve writing my very first rspec test suite.
While creating my tests, I realized that I was writing @movie = Movie.create(et cetera) an awful lot. Since I already had five different spec files, I decided that I would create a module that would, among other things, create a movie for me to edit. Check out my module below.
And here are some before and after shots of my tests:
In fact, to get rspec to work at all, you'll need a helper--it's a configuration file found at (in my case) /spec/rails_helper.rb. So it was very easy to include my module in all my extant specs and any I create in the future. I just went into my "RSpec.configure do |config|" block and added "config.include MovieHelper" (though if I'd wanted it just for controller tests, I'd've added ", :type => :feature").
today’s title was courtesy of the Beatles
Scheduler.CLI: My First Solo App-tempt
Long ago, before I was a developer, I made my living by managing a small business. These days, I've identified a ton of processes from my management life that I can now automate or make easier through the power of programming. (Don't have to worry about putting myself out of a job anymore!)
One task that happened every Wednesday, no matter what, was making the employee schedule for the upcoming week. We had four employees to cover 14 shifts, an AM and a PM for each weekday.
This process used to involve emails, paper spreadsheets, excel templates, and a calculator. Nowadays, it involves a database, activerecord, and two command line interfaces. (One day, if I find time to port it, it will be a web app.)
For now, I'll show you a bit about how it works - I've included an image of my table schemas, and videos of the two interfaces.
Boss Interface
Employee Interface
Table Schemas
note to self: de-migrate crufty old "weeks" table
Things I Learned
Plan ahead, think incrementally, and probably write a readme.
My very first readme for this application is now pretty much just my "stretch goals" section. My first five commits have almost no code I'm still using.
It took 12 commits before I made a clear outline for how the application will work and what the basic commands should do.
Do not do this! Take some time and determine what you're trying to do before your first def / end.
Once I had my outline, I could come back to my project after lunch and know what I was doing and how it was supposed to work. Since I never made a spec, the readme was invaluable.
Checkout my ReadMe to get the gist of how many features I'd still like to add.
DRY your code out before your whole app gets damp.
At one point, I had 374 lines of code just for assigning shifts. By writing a few helper methods (not included in final line count) and implementing a few conditionals, my .assign method is now just 25 lines.
Relatedly: helper methods with a runner are a godsend. My BossCli.schedulerunner used to be an infinitely long, incomprehensible, un-debuggable mess, but after a refactor, it is functional and only 20 lines.
Explore your tools before you start writing code!
I was three days into creating this code when I figured out that ActiveRecord associations could have created a bunch of useful helper methods for me.
I have not yet refactored to take them into consideration, mostly because I have two joins tables with the same foreign key columns (shifts_no_good and shfits_assigned) and I haven't decided how to deal with it.
Command line interfaces aren't fun for muggles.
I tried to show this app to my mother, and she remarked "oh, it looks like MS-DOS!" She used to kinda know how to use DOS - 30 years ago.
One confounding factor: the end user sees way more of my SQL than they probably should.
At one point, I ended up writing a 34 line method that took a hash and turned it into something slightly more legible. That would probably be unnecessary if I'd waited until we were using web forms.
It turns out that creating a CLI/unix executable file requires terminal commands, and you have to redo this every time someone new clones the repo. Lame!
Creating an app from scratch, without an assignment or specs to pass, was a really great learning exercise. I know we're all busy here at the Flatiron School, but if inspiration strikes, I encourage you to try for yourself.
Oh, and feel free to check out the source code and/or fork it for yourself.
SQL Injection: The Phantom Menace
comic via XKCD
Even civilians have heard of the dreaded SQL injection - "one of the most basic and oldest tricks hackers use to get into websites and the contents of backend databases connected to those sites" (source). But now that we're going to be creators of the web, we're going to need to know where we're vulnerable and what we can do about it.
A Bad Person Who Wants In Our Database
How can a SQL injection happen?
Every text input field on your website that gets saved somewherein your SQL database is a potential point of entry for our hacker (above).
Since SQL queries mostly look like plain-text, our villain can put a SQL-y input into your input field.
If their evil input is crafted correctly and your database is vulnerable, your code will not be able to distinguish what they added from the original purpose of the input field. This means our shadowy figure could UPDATE, INSERT into, or SELECT from your tables.
An example of a very basic dirty trick:
Orginal database query:
"SELECT * FROM users where username = '" + input + "';"
Blackhat puts in username field:
'foo OR '1'='1
Database assumes you now mean:
"SELECT * FROM users where username = 'foo' OR '1'='1';"
Since '1'='1' is always true, the limiting factor of your "where" statement has been rendered null.
And since there's no constraint on the information being selected (we've used *), this will return everything from the table called users. That could include passwords or credit card numbers, which is not good at all.
How to prevent it:
Well, for one thing, using an ORM framework, like we've learned in class, makes it slightly more difficult for our enemy to inject their malicious string. One of the reasons for this is that ORMs generally use parameterized inputs.
Parameterized database query:
"SELECT * FROM users where username = :username;"
So our nemesis puts in:
'foo OR '1'='1
Which gets stored into our Ruby memory:
parameters = {:username => "' OR '1'='1"}
We can then pass our parameters hash through some checkpoints, such as:
parameters.reject! {|k, v| v.include? " " || v.include? "="}
Which will reject the fake 'username' for invalid characters before we even .execute the query!
For more information on how to parameterize your inputs in languages other than Ruby, you should check out the Bobby Tables, a code collection created in honor of the webcomic up top.
There's tons more info about this topic scattered throughout the Internet. I'll link you to a bonus resource that you can download for offline reading - SQL Injection Cheat Sheet PDF.
alternate subtitle ideas: Catching Fire, Beyond Thunderdome, The Winter Soldier, Secret of the Ooze. Get it?
Why I Pry
title pun courtesy of The Magnetic Fields
What is IRB?
IRB is what's called a REPL, or read-eval-print loop. It takes individual inputs, and returns their results. It is accessible through the terminal, and is useful for experimentation in real time. It comes packaged with a default Ruby install, and there's even a version of it available through your browser.
What is Pry?
Pry is a Ruby gem that is also a REPL, but significantly more robust than the IRB. Most notably, you can start Pry inside a running program. This allows you to a) debug a program as it runs and b) experiment with the program while retainin access to all the variables, classes, methods, etc. that you've already defined, without having to copy-paste into your Terminal nearly as much.
Filesystem-style Commands
"Pry commands are not methods; instead they are special strings that are interpreted directly by Pry before the input buffer is evaluated."
-from the Pry wiki
Pry commands allow us to manipulate things other than the expression we are trying to REPL. Here is an example I would have found extremely useful in previous labs, had I known it existed.
We can use "ls" to show a list of variables and methods in the current scope.
We can use "cd" to enter (and experiment within) a different scope.
We can use "show-source" to show the code that created the method/variable whose scope we are in.
We can use "edit" to open that code in our default text editor.
For example: I ran binding.Pry inside the creation of a new TicTacToe game. I used cd to get into the new game, then cd'd into the current (empty) board. Then, I used show-source, and it showed me the entire contents of lib/board.rb. (I've used a partial screenshot here for space's sake.)
Then I typed "edit board" which opened board.rb in Sublime Text. As an experiment, I decided that board.reset should give us an array of 9 "z"s, rather than 9 " "s.
Once I saved my changes and closed board.rb in Sublime, I ran show-source again, and it showed the revised contents.
Not only did it show me the edits, but the edited code is now useable in the current Pry session.
The ends justifies the means
When you write shit code but it still works somehow so you leave it be