We have updated the content of our program. To access the current Software Engineering curriculum visit curriculum.turing.edu.
Project Etiquette
Ruby Project Etiquette: How to Mind Your P’s and Q’s in a Ruby Project
Directory and File Organization
File/Class Naming Conventions
- Snake-case file names (
my_file.rb
rather thanmyFile.rb
ormy-file.rb
) - End files in
.rb
- Classes are named using PascalCase a.k.a. UpperCamelCase
- Match file names to the class name – e.g. a file containing the class
RotationGenerator
should berotation_generator.rb
Directory Structure
Every project should have one top level directory that contains everything for that project. This is called the Root Directory. Be careful: “Root Directory” can also refer to the root of your computer. In this case, we are referring to the root of the project. Context is very important.
In a standard Ruby project, we tend to organize code into 4 sub-directories underneath the root directory:
lib
for source codetest
for test filesdata
for data-related files (.txt, .csv, etc)bin
for any “executable” files (you may not have encountered any of these yet; if you don’t have them, leavebin
out)
Example Directory Structure & Naming
If we are creating an “Enigma” project that needs a RotationGenerator
class, our directory structure would look like this:
enigma
├── lib
│ ├── rotation_generator.rb
└── test
├── rotation_generator_test.rb
# lib/rotation_generator.rb
class RotationGenerator
def first_method
# do a thing
end
end
# lib/rotation_generator_test.rb
class RotationGeneratorTest < Minitest::Test
def test_first_method
# test that is does a thing
end
end
Notice the relationship between class and test class names, source code file and test file names, and file to class names.
Require Statements
Require statements often trip us up, but there are some straightforward guidelines we can follow that make things much more reliable:
require
vs. require_relative
Here’s a quick overview of how require
and require_relative
work.
require_relative
attempts to require a second file using a path relative to the file that is requiring it.
- Does NOT matter where you run the test from (searches for path relative to the file the requirement is in)
- As directory structure gets more complex, navigating relative to the file you require come can become convoluted (
require_relative '../../../lib/enigma'
) 🙀.
require
attempts to require a second file relative to the place from which the first file is being run – that is, relative to your present working directory when you type ruby file_one.rb
- DOES matter where you run the test from
- require tends to behave more consistently in complex scenarios and project structures (
require './lib/enigma'
) - require is also what we’ll use for external gems and libraries. This is because…
- require is designed to cooperate with ruby’s $LOAD_PATH
- Rails assumes we’re running from the main project directory.
Err on the Side of require
Consider a project with the following structure.
enigma
├── lib
│ ├── enigma.rb
└── test
├── enigma_test.rb
Generally within the Ruby community it is assumed that you will be running your test files from the root directory, in this case “enigma”, and not from within the /test
directory.
Avoid the temptation to navigate into go into test or lib directories through terminal to run code (i.e. test$ ruby enigma_test
). Use enigma$ ruby test/enigma_test.rb
instead.
Why do we prefer require
?
Assuming the directory structure above, enigma_test.rb
could include either of the following lines (remember you can leave .rb
off when you are requiring a file):
require './lib/enigma'
require_relative '../lib/enigma'
If you are running your test files from within the project
directory, both of these will work the same. If you move down into the project/test
directory, the first would then be unable to find the enigma.rb
file. So why would we use something that might break depending on where we execute our code? Well, there are tradeoffs.
What seems more brittle in this case is likely actually more resilient to future changes. Remember the example above: if our application and test suite grow, we may decide that we want to include subdirectories for our tests. If we use require_relative
that means that we have to add a ../
to each and every one of our tests. If we use require
we can simply move our files to a new subdirectory and continue to run our tests from the project
directory as we have been doing.
Additionally, using require tends to be more common within the community. Programmers get worked up about weird things and sometimes it’s best to just go with the flow
.
Rakefiles
Rake tasks come from make tasks, Unix origins. They are used for task management and building projects. For a C project, “building” means compiling and verifying that things work. Ruby projects don’t get compiled, so what does “building” mean for a Ruby project?
Rake tries to create a standardized solution for this problem so that you can interact with build and program prep the same no matter which application you’re running. Not only does it give you set commands, but you can also build your own tasks and run them through Rake.
By default, Rake will look for a file name Rakefile
in the root of your project. You’ll define your RakeTasks in that file.
Rakefile to Run all your Tests
In your project’s root directory, create a file called Rakefile
. Add this code to it:
require 'rake/testtask'
Rake::TestTask.new do |t|
t.pattern = "test/**/*_test.rb"
end
task default: ["test"]
A “task” is referring to something rake can do for you. The Rake::TestTask
is a special task built in to rake for running all your tests. Instead of having to run all your test individually by calling ruby test/file_name
for each test file, you can run the command rake
in the command line from your project root directory and it will run all of the test files inside your test directory as if it were one, big test file.
note: You may need to gem install rake
.
Notice that the Rake::TestTask
is being passed a block, kind of like how we pass blocks to enumerables. This block is specifying where the test files are located with the line t.pattern = "test/**/*_test.rb"
. The *
is a wildcard character, which means it will pattern match anything. This line of code is saying “our tests are located in the test directory in files that end with _test.rb
”.
The line task default: ["test"]
is setting our default task to “test”. Normally when you run rake
, you have to specify the name of task you want to run, but setting “test” as the default allows us to just run rake
from the command line to run all the tests.
Additional Resources
- Using Rake to Automate Tasks
- You may see alternate patterns in older Ruby Versions Testing Rake Task