I’m excited to be on to the 3rd assessment project of Flatiron’s Learn-Verified course. The coding is all done and now I just owe a blog post and video walkthrough(see above).
For the 3rd project, we were tasked with building a Rails application. The project requirements were pretty extensive and it included things like have a “has_many through” relationship, nested form, nested resources, OmniAuth login, and more.
For my app, I decided to continue my basketball theme from the 1st assessment project by building a rudimentary fantasy basketball app. I had a blast building this and spent more time than I had planned working on it. There were just so many things I wanted to do with it.
NBA Fantasy had the following models: Users, Leagues, Teams, Players, and RosterSpots. Teams in this case refers to fantasy teams created by users, not real NBA teams. RosterSpots is a join model that tracks which teams have which players. A Player can be on multiple fantasy teams so I couldn’t use a simple “belongs_to :team” association.
Check out the associations below:
In addition to fulfilling the project requirements, I made it a point to have skinny controllers, fat models, and low-logic views. I had built some small Rails apps before doing this course, and I had always made the common mistake of fat controllers, skinny models. I also committed the sin of having too much logic in my views. While building NBA Fantasy, this was something I wanted to correct.
I initially let myself write the code I normally would with lots of logic in views and controllers. After getting the core features set up, I reviewed my code and made it more “DRY.” This exercise of doing it the wrong way and then fixing it was really eye opening for why “DRY” is so important. Finding and fixing all the repetitive code was a time-consuming task. Now that I feel I have sufficiently tortured myself doing it the wrong way, I’ll be doing it the right way from the start from now on.
Another big takeaway from this clean up was the discovery of “polymorphic_paths” which allow you to use a single helper to dynamically generate paths for any model. I had links across NBA Fantasy for editing or deleting players, leagues, and teams. Instead of building all those links separately, I could dynamically build all of them with 1 helper ApplicationHelper#permitted_links(model).
Here are some of the partials, helpers, and model methods I used to slim down my views and controllers:
When I first sat down to build NBA Fantasy, I was just going to build a template for people to create teams and and manually add player stats. However, because of my experience building the NBA CLI GEM, I couldn’t justify not having real NBA stats in there so I re-purposed some of my NBA scraping code to get real stats in NBA Fantasy.
This way whenever someone wants to add a player, all they have to do is copy and paste their profile URL from basketballreference.com. The player#update_info method does all the rest of the work to finding and save that player’s information into the database.
player#update_info is also an important method because it’s the key to updating team’s scores. Fantasy team scores should be updated every day there’s an NBA game. player#update_info does more than just pull in the data once, it also checks to see if that player’s “score” has changed. If it has changed, it calls player#update_teams_score which updates the score on every team that player is on.
Players can be updated individually, but they can also be updated in mass in the class method player#update_all_players. For now, an admin user must initiate this request manually. If this was a real world application, I’d setup a Rake/Cron task to automatically do this every day.
Check out the player info code below:
One of the things that proved surprisingly difficult was enforcing the salary cap. NBA Fantasy restricts all teams to a maximum of $70,000,000 in player salaries. Adding a player to an existing team worked fine and NBA Fantasy would reject that addition if the team didn’t have enough salary remaining.
But I had some trouble with the new team form that had check boxes for players. If I created a new team and selected several players that had a cumulative salary exceeding $70,000,000, I was able to cheat the salary cap. My solution to this was to create a team#enforce_salary_cap method that would drop players from the team until the team was below the salary cap. This worked by identifying the least valuable player on the team to drop. “Valuable” in this case means the player with the worst ratio of score to salary.
Check out the enforce_salary_cap code below:
All in all this was a super fun project that I certainly want to add to in the future.
If you’d like to see my code, see the GitHub repo here: https://github.com/alexbarron/nba_fantasy
You can also clone the repo and play with the app yourself. Be sure to run “bundle” and the migrations. If you want some real players and stats, run “rake db:seed”.