In my last article, I discussed my history with the Active Record and Active Record Query functionality in Rails, and my struggles and frustrations to understand both. The article also discussed the steps I had finally taken to move forward and grasp these concepts because while I might find other ways to get my applications to show data (by Rails ability to process raw SQL queries), without them I really wouldn’t be progressing in my desire to be a Rails developer, which is the whole reason for all of this. So I took my step back, started reading the documentation and was able to finally learn something. I started to understand the concepts, and was able to even write some code that accomplished something I wanted to accomplish for a long time but had never figured out how to, and that was great, but in the immortal words of President Josiah Bartlett, “What’s next?”.

An application like this nba project, or the nfl project that is a bit farther behind in the development life, requires on queries, a lot of queries. Being able to get the data doesn’t mean a whole heck of a lot if you can’t figure out ways to present it and analyze it in meaningful ways, so one small query isn’t the goal, it’s just the first step, so it’s time to take the next step, which I decided to tackle not only a gathering of data but a presentation of it as well, so this would be a slightly different process because not only are we gathering the data, we are displaying it to users, so that adds a layer of work, and testing to be done.

Gathering the Data

I decided that the first user presentation I wanted to work on was going to be the standings page. There was no specific reasoning for it, but since I had written a function to get a specific teams record, that’s where my thinking was at. The next step after getting one teams record would be to get them all (not so hard) and list them with standings like this page. This is your typical standings page for a sports league. You’ve got your teams, by their groupings listed by best record to worst record, with the games behind the first place team and a winning percentage. On the linked page there is also a variety of additional won loss combinations that I am not going to focus on just yet. For now I want to focus on the most simple standings report I can, because even that will require quite a bit of work for me.

Previously I had written a method within the Team model to get the record of a specific team. Using my new found friend pluck, I had rewritten that method to run more effectively, but it was only getting the record of one team at a time. I needed to not only get the records of all the teams at once, but I had to group them by the division and figure out which team in that division was in first place and the games behind.

That’s a lot to have to do based on what I had built previously, but I did have an idea how to do it, but the first thing I needed to do was identify what the target information was, which starts first with using raw SQL to query my data dump stored in the development database. The query below identifies teams, and their divisions, and the number of wins and losses they have within the data I’m working with. Taking that data into excel, I was able to determine the winning percentage and games behind the first place team each team in a given division was.

The SQL below gets the starting data I need to determine what each teams winning percentage (and games behind if not in first place) should be. The goal is to write Ruby code that collects all the information in a way that I can display it to visitors to the web site going forward.

Select divisions.name, city
,(SELECT count(*) FROM participants WHERE team_id = teams.id AND winloss='W') AS Wins
,(SELECT count(*) FROM participants where team_id = teams.id AND winloss='L') AS Loss
FROM teams
INNER JOIN divisions
ON teams.division_id = divisions.i
ORDER BY divisions.name, wins DESC

By taking this raw query result and pasting it into a spreadsheet, I can more easily calculate the winning percentage and the games behind, where necessary, for each team. This will give me an idea of what sort of content I want to view on my page when I display the data, so now I can move on to the next stage.

How to write the tests.

I decided to write this testing step as a feature test (at least that’s what I call it). By writing a test that follows a specific assigned path to a view and then determining that the specific data is in the specific place, I will test all the components of this process in one step. To me, the testing will be a multi-step process that will, I believe:

  1. Verify that www.approot.com/standings is a route that the application will follow
  2. Verify that www.approot.com/standings points to the right controller and method in the application, and thus the corresponding view
  3. Write the view that the user will see when they visit www.approot.com/standings
  4. Write the ruby code in controller and or model to make sure the correct data gets to the view to be displayed properly.

This will require changing multiple files in multiple ways, but in the beginning it always starts with the tests, so I start writing the tests simply and then more complicated.

Creating the Route

The first step is of course creating the test file, which I called standings_spec.rb. I gave it a generic name like this because as I expand the standings I want to see and how I break them down, the tests can expand as well. (If you clicked on the link above for the nba web site you’ll see that the standings can be seen in more than one way so that will be something I’ll want to do and test).

The first part of the test is pretty simple. You want to go to the standings page. To do that, I would tell the test to visit standings_path in the first step. However, that test will fail, because paths need to be created and the standings path has not been created yet. So the first error to resolve is undefined local variable or method standings_path’`, which is as simple as creating the path in the routes file.

Creating paths and links in Rails, like all things, has some great built-in functionality described in a guide, that provides you the ability to tell any kind of url you want to go to a specific part of your application. For this application, I want approot.com/standings to access the teams controller standings method. As such this is written simply in the routes file as:

get 'standings' => 'teams#standings'

Unfortunately, the route still doesn’t, yet, get me to the standings page. It instead throws a new error ActionController::RoutingError: uninitialized constant TeamsController. The routing file points the application to access specific methods in a controller, but as the error says, the teams controller can’t be found. Which makes sense, since I hadn’t created it yet. Up to this point, all the work on this project had been focused on getting data, populating the database, and writing basic queries to access said data. That is all work done with models, so controllers hadn’t been needed yet, so most hadn’t been created yet. (I did create the games controller at one point as I thought it would be needed for my data population functionality, but I was wrong about that). So, to solve this error, I just had to create the controller, and while I’m at it, created the standings method because I know I’m going to need it: rails g controller teams standings.

This created the missing controller from my last error, and also created the template view for standings where I would put the content. I was reminded by a different error that I needed to pre-load my data dump (as explained here ) to run these tests or the data wouldn’t be available to me, so I put that in before clause at the beginning of the file.

So the first couple steps were done. I had made it so that the standings page was in the right place and recognized by my application, and written tests to verify that they are found, but honestly, those first two steps of the 4 above are about 10% of the work to get to the end goal. Steps 3 & 4 are much more involved and much more effort for me, but first:

A Quick Aside for a New Method, or Two

It occurred to me as I was sketching out the layout for this functionality that it would be easier to call one method to display a full team name (i.e. Philadelphia Seventy-Sixers), then to make a call to the city (Philadelphia) and nickname (Seventy-Sixers) separately and then concatenating them on the view page. Thankfully, as with a lot of things, Rails makes this really easy. Within the RSpec file that was testing the team model in a variety of ways, I wrote a quick scenario that made sure the method I was about to write (display_name) yielded the correct result. My test ran and failed and then I went into the team model file and quickly wrote the display_name function, and then the failing test passed:

  def display_name
    "#{city} #{nickname}"
  end

Some might think that I should have overridden the default to_s methodology, and I thought about it, but for now I’m not going to do that because I might use that in a different way in the future.

Similarly, the gamedate attribute is stored as a date in the database this way ‘DD-MM-YYY’. Don’t ask me why, but that’s how databases store dates, and I’ve never questioned it, just worked with it. Not really a fan of this display and thought it would be a good idea to come up with a way to display the game date easily in a more readable fashion (“MonthName Day, YYYY” was my plan). I modified my testing of the game model to see if I could get this to work, and at first it didn’t which is a shame.

Rails comes with a built in read attribute function for any attribute you’ve put on a model and you can easily over ride it if you want.

  def attribute
    read_attribute(:attribute).other_methods
  end

Using striftime, I initially overrode the build in gamedate attribute reader using the method above, but it turns out that if you use something like strftime and you are also validating, you run into conflicts, so in order to get past it I had to build a separate method for converting the date to my preferred format.

  def gamedate_display
    gamedate.strftime("%B %d, %Y")
  end

This method passed the new test without breaking the old tests so that was a good tweak as well, and now we head back to the primary purpose of the article, getting the standings, and the next step, which is creating the layout to display the data I will provide to it.

Displaying the Content - Part 1

As I have written in various articles, what I consider my biggest weakness as a solo developer is creating the pages / interfaces for users to interact with my output, and let’s face it, that’s a pretty big weakness, and at some point you just have to suck it up and present something or your project just stays your own forever. I mean the layout for this blog isn’t earth shattering but it does present information for the few who would read it. At some point, I want this application to be available to the public so l just have to suck it up and create layouts that while not perfect for what I want, at least present the data in a readable format for the user. In this case (and in many future ones for an application like this) there are examples that I can use as suggestions for my own layout. As such, I figured since it was good enough for nba.com, I would use the same layout structure for my standings page as used on that web site. (If you have read previous articles you’ll realize that this will mean a bit of method refactoring, but that’s alright, as it’s not a huge problem, though a few tests will have to be rewritten as well)

Laying out the structure of the page, however, isn’t as simple as just writing out the HTML / ERB combinations. The first thing I have to do is figure out how the information will be passed to the view for processing, and this did take a bit of thinking.

Passing the Information - Part 1

There’s a lot of information that needs to be gathered for the the standings page, and a lot of it is not difficult to get. Some of the information is static: the conference names, the divisions that make up the conferences, the teams that make up the division. Some of the information isn’t static, but it’s already calculated for each team: the wins, the losses, and the winning percentage.

The standings for each division need to be ordered by the winning percentage, but the winning percentage is calculated on the fly, for each team, when the teams record is calculated. This led me to a problem that needed to be solved. How do I get the record for each team in a division, sorted by the winning percentage?, which led to a second question How do I use Rails and Ruby to determine the games behind each team that isn’t in first place is after I get the division members sorted by winning percentage?, but let’s deal with one step at a time, shall we?

The standings information from each team lends itself to be stored as a hash. That in turn leads to the idea that the standings for each division could be stored within a hash where the key is the division name and the value for that key is an array of hashes, each hash represents the information for each team in the division, ordered (in reverse order) by the winning percentage of said team. The portion of the output for divisionx would look something like this

 standingshash["divisionx"] = [ {team: "Team1 Name", wins: 10, losses: 0, pct: 100}, {team: "Team2 Name", wins: 8, losses: 4, pct: 0.667}, etc...]

However, that [:pct] is calculated for each team as I loop through the teams and call the record method on them. For instance, for the Atlantic division, the resultant array looks like this:

[{team: "Philadelphia Seventy-Sixers", wins: 0, losses: 18, pct: 0.0},
 {team: "Toronto Raptors", wins: 11, losses: 7, pct: 0.611},
 {team: "Brooklyn Nets", wins: 4, losses: 13, pct:0.235},
 {team: "New York Knicks", wins: 8, losses: 10, pct: 0.444},
 {team: "Boston Celtics", wins: 9, losses: 8, pct: 0.529}]

Now, arrays are looped through, that’s what they’re for, and you tend to loop through them in order. If my application looped through this array in the above order to display the standings in the Atlantic division, the last place team would be in first place and the first place team would be in second, and well, let’s face it, it shouldn’t look like that. It occurred to me, though I had never run into it, that I might be able to sort the array on a specific key/value pair in each hash, so I went off to the internet to see if such a thing existed (because it would be the easiest solution really. I could probably write some crude code to sort myself, but if something else already exists, why bother reinventing the wheel?). As usual stack overflow helped me out and introduced me to sort_by. This explanation and the one comment that indicated newer versions of ruby has a bang (!) version of sort_by! solved the issue of winning percentage (we’ll get to games behind later), and thus I could present my data to the view the way I intended, and I could get back to displaying the content.

Displaying the Content - Part 2

Now that I could sort my division standings array of hashes so that it was in the order I needed it to be (and yes, games behind isn’t done yet, but I think at this point it’s a separate article), I needed to work my way through the standings for each division. Now, I could pass 6 different instance variables for each division and work it out like that, but that seems kind of clunky. With hashes, and arrays, I could pass everything in one instance variable and then I just have to intelligently work through it so all the data gets where I want it to be. However, I sometimes start out a little cautious, so when I started writing the lay out for the standings, I started with an instance that represented one conference, @eastern. That instance variable have three keys, representing the three division names in the eastern conference, (Atlantic, Central, Southeast) and each of those keys would have a value that is an array that represents the five teams in said division, with their standings information, in descending order of the win percentage (i.e., what I wrote about in the previous section).

So, the first thing I did was sketch the idea out on paper, because I knew I’d make mistakes, and have to scribble some stuff out, and I’d probably forget something (which I did) so I could draw arrows to where it was supposed to go, and after a few fits and starts I did come up with erb code that looked like it would properly display the standings, by division, assuming it got passed the proper information, which, now that I had some place (hopefully) to display the information, was the obvious next step, getting the information.

Passing the Information - Part 2

In part 1 of this idea, I didn’t so much as work on passing the information as do the research of how I was going to properly sort it once I had obtained the information, what I had to do now was properly obtain the information and store it in my array of hashes linked to the division name like I talked about above. However, before I created that, I would have to revise something else.

When I wrote about discovering the power of pluck, I demonstrated that power by re-writing a previously written method to determine the record of a team:

  def record_version_two
    outcomes = self.participants.pluck(:winloss)
    wins = outcomes.count("W")
    losses = outcomes.length - wins
    {record: "#{wins}-#{losses}", pct: (wins/outcomes.length.to_f).round(3)}
  end

As it written, this works well enough for one team, but if I were to get the records of many teams, I wouldn’t have a way to identify them. The second issue is that joining the wins and losses like that seemed like a good idea at the time, I’ve noticed that multiple sites tend to separate the wins and losses on the standings page, so I wanted to rewrite this method again to present the data in a more raw form, and later on I could style the data differently depending on the needs of the view (on the view itself or helper methods).

So the first step of getting the data actually involved rewriting (refactoring) the method above to suit my new needs (which of course meant re-writing the test so that it would continue to pass as well).

  def record
    outcomes = self.participants.pluck(:winloss)
    wins = outcomes.count("W")
    losses = outcomes.length - wins
    {team: self.display_name, wins: wins, losses: losses, pct: (wins/outcomes.length.to_f).round(3)}
  end

Observant readers will notices that I incorporated the display_name method mentioned earlier in this article. I honestly hadn’t planned to use it right away but it just made sense. So this method is basically the some concept, I just separate the wins and losses as separate key/value pairs. In the future, if I wish to display them in the manner of the original method, I still have all the data I need, I just have to format it on the view layer, which makes more sense).

Alright, we’ve got our record method rewritten so that data will be passed, but now it’s time to get all the records of all the teams at once.

Not sure if it’s the Rails Way to do it or not, but since standings is a controller method, I wrote all the code to obtain the standings of the teams in the eastern conference in that controller method, and after a few fits and starts, I came up with what I was sure would work

def standings
  @eastern = Hash.new
  @standingsdate = Game.last.gamedate_display
  Division.east.order(:name).each do |division|
    @eastern[division.name] = []
    division.teams.each do |team|
      @eastern[division.name].push(team.record)
      @eastern[division.name].sort_by{|hash| hash[:pct]}.reverse
    end
  end
end

Notes on the method: @standingsdate is a simple variable that just will be used to present the most recent game date of the data being processed in case it falls behind for some reason, and .push is written in this article even though the *shovel is used in the application because markdown doesn’t like the shovel*

My test file, to start was pretty simple. It visited the route (which we created earlier) and then looked for two pieces of information:

  1. Was the right date properly displayed
  2. Were the Toronto Raptors the first team listed in the standings for the Atlantic division?

So, I run my test, and the first one passes, because, well, that’s a pretty easy test to get right, but the second test fails, and unfortunately, it doesn’t fail because of wrong content being displayed, which is often helpful, but it fails because my layout is full of divs, and at this point I’m still working out how to navigate the DOM properly. My test was supposed to look in (“div#atlantic div:nth-of-type(1)”) and find Toronto. Unfortunately, I seem to have written a layout that confuses the issue because the error I got read: ` Ambiguous match, found 19 elements matching css “div#atlantic div:nth-of-type(1)`. So, too many divs, and I would have to figure that problem out, but, I still wanted to know if it was at least displaying properly, and fortunately I had a way.

Rails does many wonderful things, but sometimes those wonderful things cost, and one of the costs is that getting a Rails site active on the internet isn’t exactly as straightforward as other languages and platforms, which would usually mean seeing your application in action would be difficult. However, one of the many wonderful things Rails does do is allow you to run your application locally with a simple command. As many (but not all) who will read this know, rails s is all you need to enter into your command line (within the folder of your application) and the built-in magic of Rails will launch your application locally at the default location of localhost:3000. So, to see if my data was at least displaying, I ran that simple command, entered localhost:3000/standings and prepared to see my standings page in all its glory, but, alas, it was not to be.

Oh, I mean, all the data was there, and I hadn’t left out a stray div or anything to throw off the wonderful bootstrap grid, but the teams were not displaying in descending order of winning percentage but instead in the order that that would have been found. It took me a minute to realize what I had forgotten in my standings method above, but as usual it’s the small things. As those who program will have guessed already, the last line of my method does not transform @eastern from the state it is in in the line before. The issue of course is the bang (!). For those of my readers who aren’t every day coders, that little !, which you call an exclamation points and us ruby coders call a bang (don’t ask me why it’s called a bang, I just accept these things) at the end of certain methods is a significant thing.

The last line of the method reads:

  @eastern[division.name].sort_by{|hash| hash[:pct]}.reverse

It lacks bang. There is no bang anywhere in this string of commands, so, when ruby does what it’s told (sort by the winning percentage and then reverse the order of that sort) it’s not a permanent change to the parent @eastern[division.name]. It just happens in the moment, and when it’s done, @eastern[division.name] returns to its original order. There are two ways to handle this. You can assign this command to a new variable, call it @x. @x will have the data desired in the order desired and @eastern[division.name] will still be in its original order (the order each team was found). This is fine, I guess, but there’s another way around it that doesn’t require creating another instance variable, and that’s the bang. In ruby, there are many methods that have ‘two forms’. There’s the one without the bang, like sort_by and then there is the bang version sort_by!. While the bang version does not exist for all methods, where it does exist, it always does the same thing. It makes the temporary change permanent. So I could permanently sort, and reverse, my @eastern[division.name] without having to introduce any new variables to my method, which is preferable (since as we all know, this method only gets half the information so I will have to extend it, and I will, I promise).

So rewriting the last line, like below should rectify the issue I was seeing on my local application:

  @eastern[division.name].sort_by!{|hash| hash[:pct]}.reverse!

And so it did. The data was now displaying properly, each division broken out, and the teams listed in the proper order first to last with wins losses and winning percentage, but there are still a few outstanding issues:

  • The test isn’t passing, somehow either the test, or the view, need to be rewritten (most likely both) so that specific information can be pinpointed. This isn’t just useful for testing, it might also serve purpose in the future when including javascript into my application.
  • Games behind is not yet being calculated. Which is good, because it turns out the way I was calculating it wasn’t working properly for the edge case where the second place team actually has less losses (and wins) than the first place team. It’s not a common occurrence, but it can happen in a league like the nba where one team may be as many as 3 or 4 games ahead on the schedule as another team.

Both the above issues, and any others that might come up are vital, but this article has gotten to an unwieldy point, so I believe it’s time to cut this off here. I haven’t committed any of these changes to the git repository, and as usual, I’m working on the main branch, so I’m going to stop here. If you are interested in all the code changes in this aspect of the project you can see the git commit at your leisure.

Hopefully you’ll come back for part 2, when it arrives.