Users and Memes

Now that we have a database, let's put some tables in it. Two things offhand that I can think of that'll be necessary are users and memes. We'll put them on separate tables and connect them together with has and belongs_to syntax.

For now, let's just create the models:

rails generate model User
rails generate model Meme

Model names, and this is important, are singular. Rails does the work of pluralizing things where it's necessary, so let's not fight that.

That command's going to do two things for us. It'll make models in /app/models. They'll be named the same as we indicated above.

We'll also get a few files in /db/migrate. These'll be named with the exact date/time stamp for the moment it was created, plus "_create_XX", where XX is the plural of the model name we indicated. In these files, the methods for creating tables in our database is already started, we just have to add the columns:

class CreateUsers < ActiveRecord::Migration[5.0]
  def change
    create_table :users do |t|
      t.string "first_name"
      t.string "last_name"
      t.string "username"
      t.string "email"
      t.boolean "is_admin"
      t.string "password_digest"
      t.timestamps
    end
    add_index("users", "username")
    add_index("users", "email")
  end
end
class CreateMemes < ActiveRecord::Migration[5.0]
  def change
    create_table :memes do |t|
      t.integer "user_id"
      t.string "name"
      t.string "content_type"
      t.text "content"
      t.timestamps
    end
    add_index("memes", "user_id")
  end
end

Note that Rails was even kind enough to give our tables plural names. It feels a bit like magic, doesn't it?

I added an index on "user_id" in the memes table because I'll want to keep a meme associated with who uploaded it. I added other indices on "username" and "email" in the users table because the Lynda course told me to, but I'm not sure what I'm going to do with those yet.

I also added a few things to the User model (located in /app/models/user.rb).

class User < ApplicationRecord

  has_secure_password

  EMAIL_REGEX = /\A[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,4}\Z/i

  validates :first_name,  :presence => true
  validates :last_name,   :presence => true
  validates :username,    :presence => true,
                          :uniqueness => true
  validates :email,       :presence => true,
                          :uniqueness => true,
                          :format => EMAIL_REGEX

end

Let's talk about what all that does. 

has_secure_password is a very cool trick that, when combined with un-commenting the bcrypt gem in your Gemfile, allows you to store passwords securely. If you check out the User migration again, you'll see this line:

t.string "password_digest"

That's where the password is going to be stored and encrypted. 

It's great that Rails does so much of this for us.

A Whole New "Hello, World"

I'm writing my first-ever Rails app on my own and I'm apparently foolhardy enough to write about the progress as I go. I've made a functional app in Rails before by following along with the great Lynda course Ruby on Rails 5 Essential Training. This is the first time I'm flying solo.

First thing I'm going to do is create my new Rails app:

rails new memeory -d mysql

The app I've decided to build is going to be for collecting all of the stupid gifs and other memes my friends and I share with each other. That way, if we ever want to reference one again, we'll have one handy place to get to it all. Plus, free opportunity to pun right there in the app name.

I've gotta admit, I was a little stuck here. The question of "where to go next" is so vast, I floundered for a bit. I decided, after a while, that the first thing I'd do is set up a public area and the route to it.

rails generate controller Public index show

This gives me a new controller called Public that has two default actions (and views) - index and show. Index will serve as the general homepage where, at least right now, I picture a list of recently submitted memes (that weren't marked as private) can be displayed. The show action and view will display details on a specific one.

With that in mind, I added that index as the root route.

root 'public#index'

That quick line goes in 'app/config/routes.rb'. Now whenever someone visits the root URL, it'll direct to the index action of the public controller.

I booted up the server and browsed to localhost:3000 in Chrome ... and immediately met my first error.

Truthfully, this screenshot isn't the first error I encountered. At first it gave me the same error for user 'root'@'localhost'.

Then I had to refresh myself a bit on just how to use MySQL. The new Rails app command above specified to use MySQL as the database instead of the normal SQLite. It's just what I learned in the Lynda course and I didn't want to overcomplicate it for myself if I didn't have to.

So I went into MySQL and created the database and granted access for my user:

CREATE DATABASE memeory_development;
GRANT ALL PRIVILEGES ON memeory_development.* TO 'memeory_db'@'localhost' 
IDENTIFIED BY 'password';

Now obviously that wasn't my password, but I can't reveal all my secrets, which brings me to the next step: keeping that password a secret while still getting to use all the lovely features of GitHub. This was something the Lynda course didn't cover, so I had to figure that out on my own.

Some Google-fu led me to ENV variables. On my macOS machine, I can run this:

export MEMEORY_DEV_PASSWORD=password

in terminal and then add this line to my database.yml file:

<%= ENV['MEMEORY_DEV_PASSWORD'] %>

to keep that password free from prying eyes. It does mean that any other computer I attempt to run this Rails app on will have to have a value for DEV_DB_PASSWORD, or I'll likely meet another error.

As an added bonus, I edited ~/.bash_profile to include this export line, so I won't have to create the environment variable every time I reboot.

With all of that work, I have the home page working properly. Expect another post soon with next steps.

Password Entry in Ruby CLI

I'm trying to build a simple app here at work that will allow a user who doesn't have full admin access to our vSphere to mange his VM. We want him to be able to do simple things like reboot it and snapshot it.

My plan is to give him the Ruby code that will ask for his username and password, connect, then with a few simple commands, perform the tasks.

One problem I ran into is with a standard .gets, you're typing your password in the clear.

Then I learned about io/console.

If you add this to your code:

require 'io/console'

and then set your .gets to STDIN.noecho(&:gets) instead, the password you type in will be hidden as you type it.

Database Associations

Just a quite note to myself because I know I'm going to forget this a lot. The following is not optional in Rails 5 (unless you specify that it should be):

Class ModelName < ApplicationRecord
    belongs_to :other_model
end
    

Update: This was probably a little unclear. What I mean is, if you create an association that says that a model belongs to another, if you try to create a record without saying what it belongs to, it will fail.

Valuable Resources

I'm not doing any of this on my own, so I thought it'd be great to start by writing about incredible resources I've found. That way, when I forget something or need to learn something more, maybe I'll stumble across what I've written here. Hopefully you can use it to learn a little something too.

  • CodeNewbie Podcast - I'm only a few episodes in, but a later episode was in my Overcast list for months before I gave it a listen. The interview was incredibly warm, funny, and inspirational. Here's part one of the episode I listened to first. I think once you check that out, you'll want to dive right into part two and maybe start from the beginning like I'm doing.
  • Lynda.com - I know this isn't a resource necessarily available to everyone, but it's been essential to me in my learning. If you're really serious about learning, it is probably worth getting for a month to go through the courses you want. 
  • Codecademy.com - What lynda training laid the groundwork for, codecademy solidified. Their interface for writing code right on their site is brilliant and easy and it makes you show you know a topic before you can move on. I'll probably revisit this to sharped some topics that I don't know as well.
  • CodeWars.com - The first time I solved a challenge on CodeWars was the first time I felt like I could really learn to code. I wouldn't recommend this as a place to start, but it's a good place to practice what you've learned.
  • Dash - RTFM, am I right? If you're going to learn, you'd be wise to dig into the actual documentation. 
  • Friends - Try to find some people who don't mind you asking them 100 coding questions a day. I bugged Sam and Andre every chance that I got. 

I'll update this as I come across more things worth sharing, but if you're reading this, why don't you tell me some of yours?

Hello, World!

Stagnation is death.

If I look back 20 years from now, or just next year, and say that I'm the same person that I am now, I think that'll be a loss. I want to be ever improving, ever moving. I want to tell a story with how I live and stories never leave a person unchanged.

So now I'm teaching myself, or it's probably safer to say online training courses, blogs, and a few friends I bug far too often are teaching me, to code. It's nice. I think my brain works in the right way to do it, even if it often feels like beating my head against the wall trying to learn it.

I've also been looking for a reason to blog again. Maybe something I learn along the way is worth reading about. Maybe I'm shouting into the void. Who really knows with these things.

The point is, I'm going to program and then I'm going to talk about it here sometimes.

Let's see what happens.