├── Sunday.jpg ├── app ├── models │ ├── model1.rb │ ├── model2.rb │ └── model3.rb └── yourAppName.rb ├── bin └── run.rb ├── config └── environment.rb ├── Gemfile ├── db └── seeds.rb ├── Rakefile ├── instructions_data.rb └── README.md /Sunday.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sylwiavargas/Ruby-CLI-Setup/HEAD/Sunday.jpg -------------------------------------------------------------------------------- /app/models/model1.rb: -------------------------------------------------------------------------------- 1 | class Model1 < ActiveRecord::Base 2 | # add associatons! 3 | end 4 | -------------------------------------------------------------------------------- /app/models/model2.rb: -------------------------------------------------------------------------------- 1 | class Model2 < ActiveRecord::Base 2 | # add associatons! 3 | end 4 | -------------------------------------------------------------------------------- /app/models/model3.rb: -------------------------------------------------------------------------------- 1 | class Model3 < ActiveRecord::Base 2 | # add associatons! 3 | end 4 | -------------------------------------------------------------------------------- /bin/run.rb: -------------------------------------------------------------------------------- 1 | require_relative '../config/environment' 2 | 3 | app = nameOfYourApp.new 4 | app.run 5 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require 3 | 4 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: 'db/development.db') 5 | ActiveRecord::Base.logger = nil 6 | require_all 'app' 7 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "activerecord", "~> 5.2" 4 | gem "sinatra-activerecord" 5 | gem "rake" 6 | 7 | gem "require_all" 8 | gem "sqlite3" 9 | gem "pry" 10 | gem "faker" 11 | 12 | gem "colorize" 13 | -------------------------------------------------------------------------------- /app/yourAppName.rb: -------------------------------------------------------------------------------- 1 | class YourAppName 2 | # here will be your CLI! 3 | # it is not an AR class so you need to add attr 4 | 5 | def run 6 | # welcome 7 | # login_or_signup 8 | # wanna_see_favs? 9 | # some_method(some_argument) 10 | # exit 11 | end 12 | 13 | private 14 | 15 | 16 | end 17 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # THIS SEED FILE NEEDS TO BE ENTIRELY REPLACED -- I'M LEAVING CODE FOR YOUR REFERENCE ONLY! 2 | 3 | Plant.destroy_all 4 | Person.destroy_all 5 | PlantParenthood.destroy_all 6 | Plant.reset_pk_sequence 7 | Person.reset_pk_sequence 8 | PlantParenthood.reset_pk_sequence 9 | 10 | ########### different ways to write your seeds ############ 11 | 12 | # 1: save everything to variables (makes it easy to connect models, best for when you want to be intentional about your seeds) 13 | basil = Plant.create(name: "basil the herb", bought: 20200610, color: "green") 14 | sylwia = Person.create(name: "Sylwia", free_time: "none", age: 30) 15 | pp1 = PlantParenthood.create(plant_id: basil.id, person_id: sylwia.id, affection: 1_000_000, favorite?: true) 16 | 17 | # 2. Mass create -- in order to connect them later IN SEEDS (not through the app) you'll need to find their id 18 | ## a. by passing an array of hashes: 19 | Plant.create([ 20 | {name: "Corn Tree", bought: 20170203, color: "green"}, 21 | {name: "Prayer plant", bought: 20190815, color: "purple"}, 22 | {name: "Cactus", bought: 20200110, color: "ugly green"} 23 | ]) 24 | ## b. by interating over an array of hashes: 25 | plants = [{name: "Elephant bush", bought: 20180908, color: "green"}, 26 | {name: "Photos", bought: 20170910, color: "green"}, 27 | {name: "Dragon tree", bought: 20170910, color: "green"}, 28 | {name: "Snake plant", bought: 20170910, color: "dark green"}, 29 | {name: "polka dot plant", bought: 20170915, color: "pink and green"}, 30 | {name: "Cactus", bought: 20200517, color: "green"}] 31 | 32 | plants.each{|hash| Plant.create(hash)} 33 | 34 | # 3. Use Faker and mass create 35 | ## step 1: write a method that creates a person 36 | def create_person 37 | free = ["mornings", "evenings", "always", "afternoons", "weekends", "none"].sample 38 | 39 | person = Person.create( 40 | name: Faker::Movies::HitchhikersGuideToTheGalaxy.character, 41 | free_time: free, 42 | age: rand(11...70) 43 | ) 44 | end 45 | 46 | ## step 2: write a method that creates a joiner 47 | def create_joiners(person) 48 | plants_number = rand(1..4) 49 | plants_number.times do 50 | PlantParenthood.create( 51 | plant_id: Plant.all.sample.id, 52 | person_id: person.id, 53 | affection: rand(101), 54 | favorite?: [true, false].sample 55 | ) 56 | end 57 | end 58 | 59 | ## step 3: invoke creating joiners by passing in an instance of a person 60 | 10.times do 61 | create_joiners(create_person) 62 | ##### ALTERNATIVE: 63 | # person = create_person 64 | # create_joiners(person) 65 | end 66 | 67 | indoor = Category.create(name: "indoors") 68 | 69 | Plant.update(category_id: indoor.id) 70 | 71 | 72 | puts "🔥 🔥 🔥 🔥 " -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require_relative './config/environment' 2 | require_relative './instructions_data.rb' 3 | 4 | require 'sinatra/activerecord/rake' 5 | require 'colorize' 6 | 7 | desc "Start our app console" 8 | task :console do 9 | ActiveRecord::Base.logger = Logger.new(STDOUT) 10 | Pry.start 11 | end 12 | 13 | desc "start our app" 14 | task :start do 15 | app = nameOfYourApp.new 16 | app.run 17 | end 18 | 19 | namespace :remind_me_about do 20 | 21 | desc "🎁 process for adding a new table" 22 | task :adding_table do 23 | reminder_title("💧", "adding a table") 24 | iterate_over_steps($adding_table, "🌱") 25 | end 26 | 27 | desc "🎁 process for adding a column" 28 | task :adding_column do 29 | reminder_title("🌬 ", "adding a column") 30 | iterate_over_steps($adding_column, "🌴") 31 | end 32 | 33 | desc "🎁 process for rolling back a column" 34 | task :rolling_back do 35 | reminder_title("🍂 ", "rolling back") 36 | iterate_over_steps($rolling_back, "🍁") 37 | end 38 | 39 | desc "🎁 process for creating an instance" 40 | task :creating_an_instance do 41 | reminder_title("🥚 ", "bringing new life") 42 | iterate_over_steps($creating_instance, "🐣") 43 | end 44 | 45 | desc "🎁 process for reading instances" 46 | task :reading_instances do 47 | reminder_title("📚 ", "reading the instances") 48 | iterate_over_steps($reading_instances, "🤓 ") 49 | end 50 | 51 | desc "🎁 process for updating an instance" 52 | task :updating_an_instance do 53 | reminder_title("🐛 ", "updating an instance") 54 | iterate_over_steps($updating_instance, "🦋 ") 55 | end 56 | 57 | desc "🎁 process for updating all instances" 58 | task :updating_all_instances do 59 | reminder_title("⚡️ ", "updating all instances") 60 | iterate_over_steps($updating_instances, "🔥 ") 61 | end 62 | 63 | desc "🎁 process for deleting an instance" 64 | task :deleting_an_instance do 65 | reminder_title("💀 ", "deleting an instance") 66 | iterate_over_steps($deleting_instance, "👻 ") 67 | end 68 | 69 | desc "🎁 process for deleting all instances" 70 | task :deleting_all_instances do 71 | reminder_title("👽 ", "deleting all instances") 72 | iterate_over_steps($deleting_instances, "🧟‍♀️ ") 73 | end 74 | 75 | end 76 | 77 | 78 | def reminder_title(emoji, title) 79 | puts "\n\n #{four(emoji)} INSTRUCTIONS FOR #{title.upcase} #{four(emoji)} \n\n" 80 | end 81 | 82 | def four(emoji) 83 | "#{emoji} "*4 84 | end 85 | 86 | def iterate_over_steps(array_of_hashes, emoji) 87 | array_of_hashes.each_with_index do |hash, index| 88 | puts "#{four(emoji)} STEP #{index} #{four(emoji)} " 89 | puts hash[:name].colorize(:grey) 90 | code = hash[:code].empty? ? "no code!" : hash[:code].colorize(:green) 91 | puts "code:".colorize(:red).underline + " " + code 92 | puts "#{four("🛑")} NOTE: #{hash[:note]}" if hash[:note] 93 | puts "\n\n" 94 | end 95 | end 96 | 97 | ######### REFACTORED / ABSTRACTED FOR THE CURIOUS SOULS 98 | 99 | # desc "adding a new table" 100 | # task :adding_table do 101 | # task_body({title_emoji: "💧", title: "adding a table", global_var: $adding_table, steps_emoji: "🌱"}) 102 | # end 103 | 104 | # desc "adding a column" 105 | # task :adding_column do 106 | # task_body({title_emoji: "🌬 ", title: "adding a column", global_var: $adding_column, steps_emoji: "🌴"}) 107 | # end 108 | 109 | # desc "rolling back a column" 110 | # task :rolling_back do 111 | # task_body({title_emoji: "🍂 ", title: "rolling in the deep", global_var: $rolling_back, steps_emoji: "🍁"}) 112 | # end 113 | 114 | # desc "creating instances" 115 | # task :creating_instances do 116 | # task_body({title_emoji: "🥚 ", title: "bringing new life", global_var: $creating_instances, steps_emoji: "🐣"}) 117 | # end 118 | 119 | # end 120 | 121 | # def task_body(args_hash) 122 | # reminder_title(args_hash[:title_emoji], args_hash[:title]) 123 | # iterate_over_steps(args_hash[:global_var], args_hash[:steps_emoji]) 124 | # end 125 | 126 | -------------------------------------------------------------------------------- /instructions_data.rb: -------------------------------------------------------------------------------- 1 | $adding_table = [ 2 | { 3 | name: "open the rake console", 4 | code: "rake console" 5 | }, 6 | { 7 | name: "in the rake console check the plural form of the noun you use in your model", 8 | code: "'noun'.pluralize" 9 | }, 10 | { 11 | name: "close the rake console", 12 | code: "exit" 13 | }, 14 | { 15 | name: "create a migration file (hint: the argument to the create_migration method should be e.g. 'NAME=create_people')", 16 | code: "rake db:create_migration NAME=create_plural_form_of_the_noun" 17 | }, 18 | { 19 | name: "check whether the migration file has been created in the db/migrate", 20 | code: "" 21 | }, 22 | { 23 | name: "open the migration file and using the create_table method, specify the table columns", 24 | code: "create_table :name_of_the_table " 25 | }, 26 | { 27 | name: "tell ActiveRecord to act on this migration blueprint -- to create a table in your database", 28 | code: "rake db:migrate" 29 | }, 30 | { 31 | name: "check the migration status", 32 | code: "rake db:migrate:status" 33 | }, 34 | { 35 | name: "check the 'db/schema.rb' file to see if your columns are of the correct name and datatype and if your tables are called in plural", 36 | code: "" 37 | }, 38 | { 39 | name: "in the app/models/ folder create a file with a name corresopnding to the table (but singular). Add class.", 40 | code: "class Noun end" 41 | }, 42 | { 43 | name: "connect the class name to ActiveRecord", 44 | code: "class Noun < ActiveRecord::Base" 45 | } 46 | ] 47 | 48 | $adding_column = [ 49 | { 50 | name: "create a migration file (e.g. add_color_to_plant)", 51 | code: "rake db:create_migration NAME=add_column_name_to_name_of_the_table" 52 | }, 53 | { 54 | name: "check whether the migration file has been created in the db/migrate", 55 | code: "" 56 | }, 57 | { 58 | name: "open the migration file and using the add_column method, add columns", 59 | code: "add_column :name_of_the_table, :name_of_the_column, :datatype" 60 | }, 61 | { 62 | name: "tell ActiveRecord to act on this migration blueprint -- to create a table in your database", 63 | code: "rake db:migrate" 64 | }, 65 | { 66 | name: "check the migration status", 67 | code: "rake db:migrate:status" 68 | }, 69 | { 70 | name: "check the 'db/schema.rb' file to see if your columns are of the correct name and datatype and if your tables are called in plural", 71 | code: "" 72 | } 73 | ] 74 | 75 | $rolling_back = [ 76 | { 77 | name: "check the migration status", 78 | code: "rake db:migrate:status" 79 | }, 80 | { 81 | name: "decide how many migrations you need to rollback and rollback", 82 | code: "rake db:rollback STEP=number_of_migrations" 83 | }, 84 | { 85 | name: "check the migration status", 86 | code: "rake db:migrate:status" 87 | }, 88 | { 89 | name: "check the 'db/schema.rb' file to see if your columns are of the correct name and datatype and if your tables are called in plural", 90 | code: "" 91 | }, 92 | { 93 | name: "delete the migration file", 94 | code: "" 95 | }, 96 | { 97 | name: "again check the migration status", 98 | code: "rake db:migrate:status" 99 | } 100 | ] 101 | 102 | $creating_instance = [ 103 | { 104 | name: "open rake console", 105 | code: "rake console" 106 | }, 107 | { 108 | name: "create a new instance by invoking '.new' method and passing a hash an argument", 109 | code: "variable = Noun.new(attribute: 'some string', attribute: 'some integer')" 110 | }, 111 | { 112 | name: "save it to the database", 113 | code: "variable.save", 114 | note: "you can replace new+save with .create" 115 | }, 116 | { 117 | name: "check if it was created", 118 | code: "Noun.last" 119 | }, 120 | { 121 | name: "exit console", 122 | code: "exit" 123 | }, 124 | ] 125 | 126 | $reading_instances = [ 127 | { 128 | name: "open rake console", 129 | code: "rake console" 130 | }, 131 | { 132 | name: "see all instances of a model", 133 | code: "Noun.all" 134 | }, 135 | { 136 | name: "check the first, second, third instance", 137 | code: "Noun.first Noun.second Noun.third", 138 | }, 139 | { 140 | name: "find an instance by id", 141 | code: "Noun.find(0)" 142 | }, 143 | { 144 | name: "find an instance by an attribute", 145 | code: "Noun.where(attribute: value, another_attribute: value)" 146 | }, 147 | { 148 | name: "close the rake console", 149 | code: "exit" 150 | } 151 | ] 152 | 153 | $updating_instance = [ 154 | { 155 | name: "open rake console", 156 | code: "rake console" 157 | }, 158 | { 159 | name: "see all instances of a model", 160 | code: "Noun.all" 161 | }, 162 | { 163 | name: "find the one instance you want to change and save it to a variable", 164 | code: "variable = Noun.find_by(attribute: value)", 165 | }, 166 | { 167 | name: "update the instance by passing an argument of a hash", 168 | code: "variable.update(attribute: value, another_attribute: value)" 169 | }, 170 | { 171 | name: "make sure you updated it by reading the SQL logger and by calling the variable", 172 | code: "variable" 173 | }, 174 | { 175 | name: "close the rake console", 176 | code: "exit" 177 | } 178 | ] 179 | 180 | $deleting_instance = [ 181 | { 182 | name: "open rake console", 183 | code: "rake console" 184 | }, 185 | { 186 | name: "find the one instance you want to delete and save it to a variable", 187 | code: "variable = Noun.find_by(attribute: value)", 188 | }, 189 | { 190 | name: "delete the instance", 191 | code: "variable.destroy" 192 | }, 193 | { 194 | name: "make sure you updated it by reading the SQL logger and by calling the variable", 195 | code: "variable" 196 | }, 197 | { 198 | name: "close the rake console", 199 | code: "exit" 200 | } 201 | ] 202 | 203 | $updating_instances = [ 204 | { 205 | name: "open rake console", 206 | code: "rake console" 207 | }, 208 | { 209 | name: "update all instances by passing an argument of a hash", 210 | code: "Noun.update(attribute: value, another_attribute: value)" 211 | }, 212 | { 213 | name: "see the results", 214 | code: "Noun.all" 215 | }, 216 | { 217 | name: "close the rake console", 218 | code: "exit" 219 | } 220 | ] 221 | 222 | $deleting_instances = [ 223 | { 224 | name: "open rake console", 225 | code: "rake console" 226 | }, 227 | { 228 | name: "delete all instances", 229 | code: "Noun.destroy_all" 230 | }, 231 | { 232 | name: "see the results", 233 | code: "Noun.all" 234 | }, 235 | { 236 | name: "close the rake console", 237 | code: "exit" 238 | } 239 | ] 240 | 241 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ruby CLI stub repo 2 | 3 | ### NOTE 4 | - this setup contains a rakefile with helpful process reminders 5 | 6 | ## Table of Contents 7 | - [Getting Started](#getting-started) 8 | - [Setup](#setup) 9 | - [Coding](#coding) 10 | - [Github Workflow Primer](#github-workflow-primer) 11 | - [Tools](#tools) 12 | - [Fun Gems](#fun-gems) 13 | - [Other tty gems](#other-tty-gems) 14 | - [List of APIs](#apis) 15 | - [Tutorials](#tutorials) 16 | - [Other tools](#other-tools) 17 | 18 | --- 19 | 20 | ## Getting started 21 | 22 | After you have drawn your ERD out (with the "crow feet" & attributes/proper foreign keys) and decided on the user stories, follow these steps. 23 | 24 | ### Setup 25 | 0. Fork and clone the repo, `cd` to the folder and then run `rm -rf .git` to remove git tracking. This will allow you to use this repo as your own repository, unconnected to mine. Go to github, create a new repository and upload the starter there. 26 | 1. Create/update the info in the Gemfile + run `bundle` 27 | 2. Create migrations: run `rake db:create_migration` and add the syntax you need (e.g. `NAME=create_users`) 28 | 3. Migrate: run `rake db:migrate` 29 | * **REMEMBER** never ever ever change anything manually in the schema. 30 | 4. Go to `.bin/run.rb` and change the name of the app on line 3 AND/OR go to `Rakefile` and change the name of the app on line 15 31 | 5. Go to `./app/models` and change the names of the files (e.g. `User.rb` <- singular) and the class names inside 32 | 6. Set up association macros in classes (`belongs_to` / `has_many` / `has_many, through:`) 33 | 7. Create seed file in `db/seed.rb` + then run `rake db:seed` 34 | * remember: seeding does not give you any output so no news on your console == good news 35 | 8. Check if you've seeded correctly: run `rake console` and check the last instance of any of your class 36 | 9. Go to `./app/yourAppName.rb` and change that file's name and whatever is inside (the class name should correspond to what you wrote in `.bin/run.rb` on line 3) 37 | 10. Go to `./Rakefile` and change nameOfYourApp there as well (save the file!); 38 | 11. Now you can start your app either by running `rake start` in your terminal OR by running `ruby ./bin/run.rb`. You can delete the rake task or the `run.rb` if you have a preference. You can also change the rake task name, or even -- more advanced and not necessarily the best use of your time -- create an alias that would start your app 39 | 12. Remember to change the readme at the end. If you need an inspiration on how to write a good readme, check [this blog post](https://dev.to/sylwiavargas/recipe-for-a-good-readme-3m6j). 40 | 41 | ### Coding 42 | 1. Create a dummy version of your logic -> hardcode it and don't yet make your code save anything to the database, just make sure that the logic works 43 | 2. Test often by running the file, by running `rake console` or by `binding.pry` 44 | 3. What I fould useful is drawing out every step I want to guide users through before coding - my whiteboard looked like this: 45 | ![](Sunday.jpg) 46 | 4. I first coded the dummy version (without saving to the database, just to see if all "if"s are working and all that jazz) 47 | 5. Then I added the database methods. 48 | 6. Then only I beautified the code and the app to make it visually pleasing 49 | 50 | --- 51 | 52 | ## Github workflow primer 53 | I thought I’d share the git workflow my tech collective follows: 54 | 1. Add everyone as a collaborator to the repo 55 | 2. clone the repo; in the repo, have two standard branches: `main` and `dev`; 56 | 3. before a coding session (each time), first run `git pull origin main`, then `git checkout -b nameOfTheFeature-yourName`; every now and then, once you’re done with some deliverable, add/commit/push the code to that new branch; 57 | - ALWAYS WRITE DESCRIPTIVE COMMIT MESSAGES, it is extremely important for the future employer -- here's [a guide to what makes a good commit message](https://www.freecodecamp.org/news/writing-good-commit-messages-a-practical-guide/) 58 | 4. once the feature is “done” and you checked whether it’s not bugging out, merge it to `dev`: 59 | - add/commit/push your code to your feature branch 60 | - then change the branch to dev: `git checkout dev` 61 | - then merge: `git merge nameOfTheFeature-yourName` 62 | - then test all your code (in rake console test the associations, in CLI test the workflow) 63 | - fix bugs, add/commit/push 64 | 5. once it’s on the `dev`, your partner checks your code and if it’s okay, they merge it to `main`; 65 | **SO:** 66 | - `main branch`: ONLY push to this branch if the feature is working + reviewed by both partners 67 | - `dev branch`: code that works + is up for review (it’s like a pull request) 68 | - `feature branch`: always created from the last version of the `dev branch`, contains code in progress 69 | 70 | PLEASE DO USE MULTIPLE BRANCHES AND NOT ONLY ONE to minimize the risk of overwriting your working code with an accidental commit. Traveling in time with git is possible but it’s not the most pleasurable thing to do with your day. 71 | 72 | For project management, we use [git projects](https://github.com/features/project-management/) (instead of trello or asana) and git issues. We do stand ups and stand downs every day, where we distribute tasks and give summaries of what has been done. We pair program whenever possible, stair each pairing with discussing ground rules and end with giving each other feedback. However, given the fact that we all have full-time jobs it is not always the case. 73 | 74 | --- 75 | 76 | ## Tools 77 | 78 | I collected tools for your app that might be helpful. 79 | 80 | ### Fun Gems 81 | - `tty-prompt` - nice interface for prompting for user input -- [see here how to set it up](https://github.com/sylwiavargas/tty-prompt-activity) 82 | - `rest-client` - make HTTP requests and get data from APIs 83 | - `faker` - randomly generated seed data fun 84 | - `colorize` - colored text output in your terminal 85 | - `lolcat` - enabling rainbow text coloring 86 | - `formatador` - styling output information into a neat table 87 | - `artii` - creating text banners 88 | 89 | ### Other tty gems 90 | - [tty-box](https://github.com/piotrmurach/tty-box) -- creating boxes in your terminal 91 | - [tty-progressbar](https://github.com/piotrmurach/tty-progressbar) -- flexible progress bar drawing in terminal emulators 92 | - [tty-tree](https://github.com/piotrmurach/tty-tree) -- if you want to represent e.g. file structure 93 | - [tty-reader](https://github.com/piotrmurach/tty-reader) -- allows you to trigger some events on specific keys 94 | - [tty-markdown](https://github.com/piotrmurach/tty-markdown-cli) -- formatting tools for long texts 95 | 96 | ### APIs 97 | - [APIs](https://rapidapi.com/collection/cool-apis) 98 | - [And more APIs](https://medium.com/@vicbergquist/18-fun-apis-for-your-next-project-8008841c7be9) 99 | 100 | ### Tutorials 101 | - [Lecture: Setting up TTY Prompt in your CLI by Sylwia Vargas](https://github.com/sylwiavargas/tty-prompt-activity) 102 | - [Adding animations to your CLI by Sylwia Vargas](https://medium.com/better-programming/add-an-animation-or-a-giph-to-your-ruby-cli-29952e8c46ea) 103 | - [Adding ASCII Art to your CLI by Sylwia Vargas](https://medium.com/@sylwiavargas/adding-pictures-to-your-ruby-cli-4252b89823a) 104 | - [Reload method for when your CLI doesn't reflect the changes you've just made -- a blog by Alex Suthammanont](https://medium.com/@asuthamm/activerecord-association-reload-d2e1be8b4aa) 105 | - [Sending SMS with Twilio API in your CLI by Danny Sasse](https://dsasse07.medium.com/sending-sms-text-from-a-ruby-app-f8222c30e986) 106 | - [Refactoring your app into modules by Danny Sasse](https://dsasse07.medium.com/refactoring-app-features-into-modules-in-ruby-fa5fd11dec61) 107 | - [Introducing music to your CLI](https://simplemitch.com/2020/02/20/afplay-definitive-guide-to-manipulating-audio-in-your-cli-application-ruby/) 108 | - [Streamlining git add/commit/push by Shane Lonergan](https://medium.com/swlh/creating-new-bash-commands-and-aliases-c9272fd589c4) 109 | - [Good git primer by Isabel K. Lee](https://dev.to/isabelxklee/a-beginner-s-guide-to-git-github-41jc) 110 | - [A git cheat-sheet](https://education.github.com/git-cheat-sheet-education.pdf) 111 | - [Project tracking tool from GitHub (where you can connect projects, issues, etc) by Isabel K. Lee:](https://medium.com/swlh/how-git-and-github-can-make-your-life-easier-2ff5b9e1f6f1) 112 | 113 | ### Other tools 114 | - [Terminal recording](https://asciinema.org/) for your readme 115 | - [Github projects](https://github.com/features/project-management/) for better project management 116 | --------------------------------------------------------------------------------