DEV Community

Chris Baum
Chris Baum

Posted on

Portfolio Project 3: Net Worth Tracker

For my Rails Portfolio Project I decided to create a net worth tracker so that users can keep track of their finances. I have an interest in Personal Finance and thought that this would be a great application to build in rails. The application would allow a user to enter accounts, both assets and debts, and automatically calculate their net worth. It would also allow a user to categorize each account and the application would track the total value of each category. This could be useful information for the owner of the application...especially if they were a provider of financial services.

There are similar applications that automatically link to accounts such as Personal Capital and Mint. However for the sake of learning and creating an application with Rails, this application will be based on manual entries by the user.

One of the first tasks I performed was breaking out which models my application would incorporate. I knew that I needed a User model that would save user information and login credentials. I knew my users would need to enter their financial accounts, with each account being either an asset or debt, so I would need an Account model with a type attribute. I also knew that I wanted to have the accounts categorized, but I wanted pre-defined categories that a user would have to choose from. I also wanted to be able to have a value associated with the category as this information may be valuable to the owner of the application. For these reasons I created a Category model.

One of the requirements that would end up shaping my project was the requirement that we have a many-to-many relationship. I first took a look at the Accounts model as it seemed to be the lowest level model. An account belong to a single user and a single category. So there were no has-many relationships associate with it. Now thinking about Users and Categories. We know a User has many Categories, but does a Category have many users? If the perspective is just from a single standard user, then no, but if the perspective is from an Admin or company that wants to track data and provide needed services to their users based on information from each Category, then yes a Category does have many users. This is also many times the purpose of an application and how a company monetizes an application, as an application like this can help with marketing and target sales/services. So my many-to-many relationship would be: Users have many Categories and Categories have many Users (note that this association would be through a join table which is discussed later on in this post).

I believe going through this thought process with these relationships was eye opening because originally my perspective was from that of a single user, but thinking about the many-to-many relationship led me to think about the perspective of the Admin and also the perspective of the company that may develop this application. Having the correct perspective can change how the application is setup and developed.

Another beneficial mental exercise was thinking about how to setup a join table. We are required to have two has-many-through relationships, therefore I would need a join table somewhere to allow the models to access each other. I could create multiple join tables to do this, but is there an easier way? After thinking about the relationships I focus on the Account model because an account belongs to a user and also belongs to a category, so shouldn't I be able to join a user and category together with an account? After all, I want the association to be there if a user actually has an account in a particular category. I ended up adding the foreign keys for both user and category to the Account model.

create_table "accounts", force: :cascade do |t|
    t.string "name"
    t.string "account_type"
    t.integer "dollar_value"
    t.datetime "created_at", precision: 6, null: false
    t.datetime "updated_at", precision: 6, null: false
    t.integer "user_id"
    t.integer "category_id"
  end
Enter fullscreen mode Exit fullscreen mode

Through that join table I am able to access User.Categories and Category.Users. Once again going through the thought process of how the relationships work and should be setup was eye opening because it can be the difference between creating multiple tables and extra work versus finding a simpler solution.

Much of the rest of the standard application development went fairly smoothly. I focused on the "get it to work", even if it didn't look the prettiest, as I would later add in validations, add-in features (such as OmniAuth), and refactoring the code.

After getting most of the standard user application working I moved on to the Admin user experience. This did take some thought as I had to think how much access is appropriate for an admin. Do we want an admin to have access to all of the users' information? Do we want them to be able to view accounts only or also create accounts? For that question I did settle on an admin being able to manipulate and create accounts in case a user needs help, delete a user in case they have not been active for quite some time, but not be able to manipulate their username, email, or password since changing either one of those could lock the user out of their account. One important function I wanted to include for an admin was that of Category management. A standard user would have a selection of pre-existing categories. The admins would have to manage this list. The categories would be along the lines of Savings, Retirement, Real Estate, Credit Cards, Crypto, etc. The reason that may need to managed is if you think back just 10 years ago, Crypto would not be a category on this list and would have to be added. Also for simplification or clarifications you may end up breaking a category out. Possibly the category started out as Investments but there are a lot of accounts that fall under this since the category name is so broad. Later on, to get better information and categorization, we may delete Investments and instead create a Stocks, Bonds, REITs, etc. category.

Another benefit to having a standard experience and an admin experience was that it allowed me to meet another requirement. That requirement was that we use a class level scope method. Within the User model I was able to use the scope method for a standard user and admin user to quickly reference which they were within the code of my application.

scope :admin, -> { where(admin: true) }
  scope :standard, -> {where(admin: false)}
Enter fullscreen mode Exit fullscreen mode

Something that did trip me up within the Admin experience was, what happens if I delete a category? What happens to the accounts associate with category since Account has a Category foreign key? We don't want the accounts to just disappear from a user, which would happen since I display the user's accounts by category in their show page. I also calculate net worth based on these accounts and their net worth would show a different number than if they added up the accounts they see. However, I also don't want to delete the accounts. This would cause confusion and frustration with a user since they put the time and effort into putting the account in, which typically is more work than just typing some letters and numbers on the Accounts/new page. Likely they had to track down account statements or do some calculations to get the account balance. What I settled on was to create an "Uncategorized" category and make it unable to be edited or deleted by the Admin. This would act as the catch all when I delete a category as it assigns all of the accounts associate with the deleted category to the "Uncategorized" category. These accounts would then still show up for a user and would just need the user to recategorize it by editing the category. Though still slightly annoying, not nearly as annoying as disappearing accounts and inaccurate net worth value.

Another interesting problem I had to solve was later on when including the OmniAuth feature as I didn't have much experience using it, the parameters it outputs, and how I would associate a Facebook user with a new User object. After all I wanted a Facebook user to be able to save their account information and bring it up every time they logged in. Therefore upon initial login they would have to be associate with a User object. I ended up having to add another attribute "UID" to the User model much farther into the project build than I would have liked. This attribute would default to "0" for a non-Facebook user but save the UID from a Facebook user. The sign-up/login sequence would then be different based on whether that value was "0" or non-zero. For the facebook user a random password would be generated using SecureRandom.urlsafe_base64 because we do not want this left blank as someone could still try a standard login with the username.

def omniauth_new_user
    @user = User.new
    @user.username = auth[:info][:name]
    @user.email = auth[:info][:email]
    @user.uid = auth[:uid]

    #assign random password for omniauth
    @user.password = SecureRandom.urlsafe_base64
    @user.password_confirmation = @user.password
  end
Enter fullscreen mode Exit fullscreen mode

This also highlights how things can be done different ways. In retrospect, I could possible have a "type" attribute and assign "standard" for normal login, "facebook" for ominauth and only allow standard login for a type "standard" user. That is the nice thing about programming is that there are many solutions to a single problem and many improvements that can be made over time.

As a final thought, one thing that I struggled with was the idea that "this is a very simple application, is this good enough?" After some thought I do believe it is due to the added admin features that make it a more complete application with different relationships and a purpose (data mining to eventually target financial services). Something that all programmers need to remember is that applications usually start out simple and small and then get built out over time. Even Facebook originally did not have many features. Only over a significant amount of time did it continue to grow, hire more programmers, and cause development to accelerate. It is easy to see why companies have many full time software engineers/programmers. Though the application I created works, it is fairly simple and there are always new and better features that could be created. For instance, many real-world applications that do something similar allow you to directly link to your account so they auto-update daily or upon sign-in. Also, with so many users and accounts, data mining can mean profits for a company and just to get the data into a useful form requires the application to continue to be developed even if the standard user experience doesn't change much. For this class, I have to be satisfied with learning and creating a "simple" startup application.

Top comments (0)