Jordan Moore
Aug 13 · 10 min read

Anyone who has used Ruby has most likely internalized one truth:

Everything in Ruby is an object.

These are objects according to Ruby.

This truth is inescapable, undeniable, and persistent. Ruby’s ability to store anything as an object is immensely powerful.

However, when writing to different file types from within Ruby, how can we store the objects created in our program and access them later?

Let’s say we had a .txt file, and we wrote to it from within our Ruby program. How could we store an object in that file?

One method for storing objects from Ruby is to use a YAML file.

YAML stands for “YAML Ain’t Markup Language”, and is a human friendly data serialization standard for all programming languages. We can use YAML files in conjunction with our Ruby program to store objects, and in the case of web browsing, maintain state by storing data that persists in external files. Additionally, by overwriting YAML files, we can modify the values inside of our stored Ruby objects.

This guide will assume that you have a basic understanding of the following:

  1. Sinatra
  2. Rack
  3. Writing and reading files from within a Ruby program
  4. HTML Forms
  5. OOP
  6. Gemfile
  7. Sessions
  8. BCrypt
  9. Routes

Installing the Project(s) and Dependencies

Download the following projects from my Github:

1. Incomplete Project (used for following tutorial)
2. Complete Project (used for checking results, optional)

Keep in mind, this project is not meant to be pretty. The goal of this project is to store objects from our Ruby program in an external YAML file. This YAML file will allow our application to maintain state when run in a web browser. There will be extremely little CSS styling.

Next, run bundler install from within the root directory of the project to install the necessary dependencies.

If this step didn’t go well and you’re running into problems installing dependencies with Bundler, check out the Free Book from Launch School which includes how to use Bundler (Link to Book).

Now, we’ve got all the dependencies installed and can use the bundle exec ruby app.rb command to run our application in a web browser. Don’t click on any of the links on the page yet; they won’t work as we haven’t created the necessary methods for making this application work.

Note: This command should be written when inside the root directory of the project.

When I ran the aforementioned command, it told me that the application was running on port 4567. I can access this port by typing the following into the web browser: localhost:4567. The port number may differ between different machines.

Take this time to review the various methods within the app.rb file. These methods are already complete and are given to you, free of charge; however, understanding what they do is essential to understanding the methods we create in the next few steps. Additionally, look at the User class within the user_obj.rb file. We will be storing user data in instances of the User class, so having a firm grasp of that class is also essential.

Creating a User’s YAML file

For this project, each user will have their own YAML file. This YAML file will contain the instance of the class User that holds that particular user’s data (username, password, and interests). A database is a much easier and secure way of storing user information, but for the purposes of this project, we will pretend there aren’t obvious security flaws in our storing of user objects in separate, easily-found files.

So, in our application, a user will be able to register with the following credentials:

1. Username: Tim
2. Password: secret

Then, these credentials will be put through validation methods. If they pass the validation methods, an instance of the User class will be instantiated with the correct attributes for @user and @pw. This instance of the User class will be stored in a variable, obj, and then somehow written to a new YAML file within the users directory.

Now that we know the general flow of our registration process, we’ve really got our work cut out for us!

Before moving ahead, be aware that the users already stored in the users directory have the same username as their file name, and their passwords are both: secret .

From here, we’re going to delve into the post “/register” route.

In the Github version of this project, there will be comments next to a few of the sections of the aforementioned route. They are removed from the above snippet for readability.

The goal of this section will be to write the missing method, create_user_yml(obj), and successfully write a User object to a YAML file and then store that YAML file in the users directory. To do this, we’re going to need to modify our existing method, create_user_yml(obj). We will do so after reviewing the all_users method.

First, let’s think about what the @users instance variable’s value is. If we look at the all_users method, we will see the following:

This method doesn’t take any parameters. On the first line of this method, we assign the variable pattern to the value of the data_path method plus the string object: ”/*”. The data_path method always returns the absolute path to the correct directory, and by concatenating the string object ”/*” to it, we are specifying which type of files to search for in a directory. In this case, the file we’re going to search for is *, which matches any file in our directory. Since we are only creating YAML files in this directory, it’s safe to include all files in our search.

As an extra challenge, try refactoring this method to only look for YAML files, which in our project use the .yml extension.

Take this time to review the class method .glob for the class Dir: Ruby: Dir

When we invoke the map method, we are storing only the file name of the file, and not the rest of its absolute path, in an array object within the users local variable. Next, we are loading each YAML file from the users variable. This returns an array of User objects in the same order as they are stored in the users directory.

Don’t feel bad about if multiple re-reads of that paragraph are necessary for understanding. This is the trickiest part of the article.

Let’s modify the create_user_yml method now.

What do we need first? Well, we’ve seen that in order to interact with the users directory, we need to establish the absolute path to it. We do this by invoking the data_path method and storing its return value in a variable. Let’s name it path.

Now our Ruby program will know where to go. But how do we name our new YAML file?

Since we’ve passed the`User object as an argument to this method, we can also access that object’s attributes and instance methods. Each user’s name is unique as per the validation methods. So, we can use the user’s name as the file name, and set the extension to .yml.

Now, we need to construct the path to this exact file that we want to create. This path will include the file name and the absolute path we generated earlier. Luckily for us, we’ve already instantiated local variables with the values we need, path and file_name. We’re going to use File.join to concatenate our two string objects with / to construct the path to our new file.

We’ve got the final path for our file. Let’s actually write this file into existence. For our purposes here, we will use File.open as it is a synonym for File.new. Ruby allows developers to open files with different modes. These modes can be found at this link: Ruby File Modes.

We will use the ”w” mode, as this allows us to create and write to a new file.

The key to understanding the fourth line of code here is the to_yaml method invoked on obj. to_yaml converts a Ruby object into YAML. As you can see from the following screenshot of a successful registration, different data types are stored differently.

The different types of data are written as key/value pairs in specific YAML format. In the next section, we will see how to append new objects to an array within our YAML file.

Before moving on, take this time to play around with to_yaml with different data types in irb.

Appending String Objects to an Instance Variable

In the following picture, one can see that the interests instance variable is an array. There are many ways to add data to an array; however, in our case, simply concatenating string objects into our array won’t be enough. Since the yml file must be rewritten in order to store the new User data.

To do this, we need to:

1. Load the yml file.
2. Convert the User object out of YAML format.
3. Invoke the instance method(s) available to objects of the User class to append an interest to the specific User object.
4. Rewrite the yml file that we initially loaded from the users directory.

We have an idea off how to do 1, 2, and 4 as we’ve already loaded User objects from the users directory and written a yml file in the previous section. So, #3 should be relatively simple to figure out, as we’re only invoking the instance method that involves appending an object onto the @interests instance variable’s array.

First, have a glance at the get “/add_interest” route.

This form is submitting a single string object to the post “/add_interest” route. This string object can be accessed by the params hash and the key :interest. We know this because the <select> tag has the name attribute set to interest. interest becomes a key in the params hash in the post route.

Let’s take a look at our post “/add_interest” route, as this is where we will rewrite the yml file.

On the third line of the aforementioned route, we are initializing an instance variable @user which contains the specific User object pertaining to the username stored in our session data. This session data, :curr_user, is used to ensure that we don’t append interests to a different user. We need access to this User object so we can append a string object to it’s @interests instance variable.

So, now we need to do the following:

1. Add interest to the object stored in @user.
2. Rewrite and store this modified User object as a yml file.

Take a look at the file user_obj.rb to see which instance methods we have available to us.

We have two instance methods pertaining to the @interests instance variable and they seem simple enough to use. Let’s invoke the instance method add_interest on our User object stored in the @user instance variable.

This is all simple OOP right now. We’ve successfully appended the string object stored in the params hash with the key :interest onto the @interests instance variable. Now, we need to write this User object onto a yml file. If we didn’t rewrite the yml file, the actual User object wouldn’t change as it pertains to the state of this application, since the file we use to access the object, its yml file, only changes when it is opened and rewritten.

How did we do this before? Can we simply repeat the same method we used in the previous section?

Yes, we can and we did. In my example, I selected the value Golf to be added to admin’s interests. Our post route handled all the rest of the work. Anyone following this tutorial should have a yml file that looks similar to the following screenshot:

Now, when we look at the index page, we can see more proof that the @interests of the User object we’re updated and rewritten.

Next Steps

As one can see, this project is extremely simple. For anyone wishing to extend their skills as it pertains to this project, explore the following ideas using this project as a skeleton:

1. Allow users to input their own interests.
2. Add more user data to the User class and use it in the application somehow.
3. Style the pages.
4. Add custom objects, maybe a Message class, that allows users to send messages to each other.
5. Create stronger input validation.
6. Refactor the logic out of the post routes and into simpler methods.
7. Add feature to remove interests from User objects.

Thank you for taking the time to read this.

If anyone is interested in seeing a project that only uses YAML files to store objects, have a look at my latest project, PokeComp.

It’s a small application which allows users to create tournaments, articles, and send and receive messages from one another. It’s a small PokeCommunity!

Happy coding!

Launch School

Publications of the Launch School Community

Jordan Moore

Written by

Budding Programmer

Launch School

Publications of the Launch School Community

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade