Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
mastery_via_git_commits.md

To master anything, we need to build mental models, and build some habits around using those mental models.

For example, the first time you set up a ruby file with testing, you probably had to refer to something to come up with the right file structure.

Lets say you have dragon.rb:

class Dragon
  # maybe some attr_readers

  def initialize(args)
    # some instance vars here
  end

  def do_a_thing
    # stuff
  end

end

from that file, you can quickly spin up the test file, right?

dragon_test.rb

require 'minitest'
require 'minitest/autorun'
require './dragon'

class DragonTest < Minitest::Test

  def test_it_exists
    dragon = Dragon.new(args)
    assert_instance_of Dragon, dragon
  end

  def test_do_a_thing
    dragon = Dragon.new(args)
    assert_equal "thing", dragon.do_a_thing
  end

end

All of this structure can just kinda flow out of your fingers, once you're comfortable setting a test up.

It probably felt really foreign the first time you set one of these up. It did for me. Now, it's pretty easy. (Maybe I have syntax errors above. It was all from memory, but I feel content with my ability to spin one of those up and iron out errors.)

Now, if that felt hard then, and easy now, what does that imply for something that feels hard now?

Easy! Chunck it up and practice it!

But, now, here's the problem. We're using sinatra, and if we want to do something trivial, like "add a row to a certain table", it might involve quite a few steps, like:

  1. Create new migration
  2. Write code to instruct what should happen
  3. Run migration
  4. Update controller, model, to handle new data

At the end of that, we're ready to do something with the new data. But those four steps (each of which have a few sub-steps) need to be as quick to execute as adding an instance variable to a Ruby class.

For example, if I have a person class:

class Person
  attr_reader :name, :page

  def initialize(name, age)
    @name = name
    @age = age
  end

end

and I want to add a favorite_food instance variable, its easy. Just add favorite_food as an initialize argument, and instance variable, and an attr_reader.

Now, to do something similar with a sinatra/rails app, it's a bit more involved. First, here's some basic file structure:

├── Gemfile
├── Gemfile.lock
├── README.md
├── Rakefile
├── app
│   ├── controllers
│   │   └── bike_share_app.rb
│   ├── models
│   │   ├── city.rb
│   │   └── station.rb
│   └── views
│       └── stations
├── config
│   ├── database.rb
│   ├── database.yml
│   └── environment.rb
├── config.ru
├── db
│   ├── csv
│   │   └── station.csv
│   ├── migrate
│   │   ├── 20170317003831_create_stations.rb
│   │   ├── 20170318175920_create_cities.rb
│   │   ├── 20170318191305_add_city_id_to_stations_table.rb
│   │   ├── 20170318193651_remove_city_name.rb
│   │   └── 20170318215501_add_lat_and_long.rb
│   ├── schema.rb
│   └── seeds.rb
└── spec
    ├── controllers
    ├── features
    │   ├── can_create_a_station_spec.rb
    │   ├── can_delete_station_spec.rb
    │   └── can_edit_a_station_spec.rb
    ├── models
    │   └── station_spec.rb
    └── spec_helper.rb

13 directories, 24 files

To add the same functionality to a people table in ActiveRecord (which has instance variables mapped to column names, among other things) we'd need to do:

  • Run rake db:create_migration NAME=add_favorite_food_to_people
  • Open up /db/migrate/datehash_add_favorite_food_to_people.rb
  • Modify the file to add something like:
add_column :people, :favorite_food, :text
  • Run rake db:migrate

  • Open up app/models/people.rb

Make any changes if needed.

  • Modify db/seeds.rb so we can have some sample data when interacting with our databases
  • probably some other things that I cannot remember
  • Update RSpec tests to capture the new modifications. THIS HAS A BUNCH MORE STEPS!

DANG! That's a lot of steps. And if you miss one, the error messages, while they point you in the right direction, are not quite as specific and direct as some of the ones I'm used to seeing when just testing methods.

So... I want to use github to practice making and executing migrations, so I can get this embedded in my head really well. Once that process is mastered, I can add more. (Like quickly adding CSV>ActiveRecord imports!)

Using Github for Deliberate Practice

Here's what I have in my mind, that I'm about to try to work out:

  1. We have commits on both sides of these successful migrations.
  2. In theory, I can check out a commit before all these changes, practice migrating and updating our models, and then compare the finished files to the next commit in the chain.
  3. Regardless of if I'm correct or not, I can discard all the modifications and never impact the work we've done/did.

Dependencies

For this to work, I must rely upon a few things:

View git diffs in some interface

I'd like to be able to get an overview of what I'm going to do by comparing two commits side-by-side in a code editor. I think there's an Atom package I can use to do this. It will function as a little "preview" of what I have to do.

Tolerable usage of git

I feel like I'll get a bit better at Git through this, since, well, it's a bit more than just the standard git add/commit workflow.

Walkthrough:

Here we go. I'm working on the bike share repo that is a mod 2 project.

when I run git log, I'm going to look for the first commit before it looks like real "work" was done. Here's where I'm focusing:

| * cea796c 2017-03-17 | all attributes need to be present test passing [Seth]
| * bd11c77 2017-03-17 | preventing explosion [Seth]
| * af20534 2017-03-17 | merged jons setup database info [Seth]
| *   061f1f6 2017-03-17 | Merge remote-tracking branch 'remotes/origin/i1_jk_setup' into i1_sdm_testing [Seth]
| |\
| | * 3a40632 2017-03-16 | add route to controller, update index to display information of station (origin/i1_jk_setup) [jk1dd]
| | * 26c5de6 2017-03-16 | create migration, create station model, create station view [jk1dd]
| |/
|/|
| * e9dfcce 2017-03-17 | did validation test [Seth]
| * a504dbf 2017-03-17 | fixed gemfile [Seth]
| * c22e465 2017-03-17 | add feature test for station [Seth]
|/
*   71cb133 2017-03-16 | Merge pull request #7 from slague/i1_practice_branch [Jonathan Kidd]
|\
| * 7531f19 2017-03-16 | add josh name to readme (origin/i1_practice_branch) [Josh Thompson]

I think some of the bottom ones are what's most interesting. commits c22e465 vs 71cb133, for example, is where we added some testing.

I'm going to try to figure out how to quickly compare those against each other.

...

...

Looks like this split diff Atom package is useful. Installing...

Meh. Doesn't look good right now.

To google: git compare differences between commits

looks like something like git diff 26c5de6 e9dfcce should do it. Just pass two hashes as args to git diff

OK, that worked. It'll show me the diffs. Now I want to view it graphically, and not just in my command line.

Now I'm learning about "difftool" inside of Git. Looks like i will benefit from a visual merge tool.

Wow, what a rabbit hole. kdiff looks like a nightmare. I'll try

OK, looks like there's an atom extension called git-time-machine. Lets run with that for a bit.

...

...

Gonna run with this for a while. I'm not yet 100% sure how to best use git-time-machine, and I'm gonna do some actual work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment