As of today’s date (March 20, 2016), there are three articles published on this blog. When I access the heroku console and enter Article.all, the order the articles are returned in is not the ideal sort. In fact today it doesn’t even refer to them in order of creation (or update) but the order returned in (based on ID’s) is 1, 3, 2. I don’t know why it’s returning in this order, but it is not the order I want. Since I have the ability to create drafts, some of which might be finished before others, I want to set up a publication date field that, as it’s name implies, will indicate the date I push the Publish Article button to make it live for all users. I also want to set up the sorting so that articles are sorted in reverse by the publication date so that the newest articles are at the top of the list.

In theory, not a complicated process, but still requires to be done in the proper order.

  1. Write the tests, tests fail
  2. Write the code, tests pass

Write the tests, test fail.

There are two things I have to test here. I have a custom method in my controller, publish, that changes the status of an article (from the default draft) to published, and has some small custom validation. This publish method should first be altered to set the publication date as the current date when the publish method is triggered.

So, first off, we write the controller tests, which, before now, I’ve not done in depth so a little Swagbucks searching led me to this blog post

I started off following this but ran into some issues regarding the fact that my publish method is Namespaced under Admin. Doing some quick online research I found there were complications and difficulties with RSpec namespace controller testing, plus the Warden gem wouldn’t work within the controller. It’s set up for feature testing, and since you have to be logged in as the admin, testing this publication date became a feature tst instead of a controller test.

Step one in all testing is to write failed tests. In the interest of brevity and quickness, I added a quick expectation to my current file that would expect to see the current date (to make sure the test works no matter when it’s run) as part of the content when an article is published, and the test failed as expected.

Write the code, have tests pass (in steps)

Creating the process to set the publication date.

Now came the thought part for me. Do i create this as part of the controller or part of the model. At first I thought, set it as part of the controller, but since it’s purely something going on on the model based on another field change in the model, it occurred to me that it should be a callback in the model file. However, I need it to be a conditional action, so that the publication date is set only once. I don’t want it set if an article already has a publication date but is then updated. So, we end up looking at the Ruby Guide for callbacks in the section on conditional callbacks.

After some reading and some re-reading I came up with something I thought would work.

after_update :set_publication_date, if: { |article| articled.published? && article.published_on.nil? }

.published? is a previously set up model method that just checks the contents of the status field and returns true if it is set to published. So my thinking is that this will work so that the publication date is only updated once because of the nil test afterwards, so we run the test again, and to no one’s surprise the test obviously fails, but, as expected, it fails differently. This time it fails because there is no published_on method. However, it’s still good to run this test because if I had written the after_update section wrong it would have given a different error that would point me to an existing syntax issue that would have to be fixed before implementing the published_on field in my model.

Adding the published_on field.

As we all know the first step is the migration of course. Which was pretty simple to write: bin/rails g migration add_published_on_to_articles published_on:date. My migration came out as expected, since I’m getting better at titling them, the code to set up the publication_on field as a date in the articles table was good to go so then all I had to do was type bin/rake db:migrate and the field was created.

Run the test again, and success, the test failed, but the test failed at the next step in the process. There is no set_publication_date method set up in the Article Model yet, so time to set that up.

Setting up the set_publication_date method.

So not terribly complicated, just create the method within the model to set the published_on to the current date.

self.published_on = Date.current

Run the test again, and we are back to failing the content test, i.e. the original expectation written above fails because I haven’t changed the view, but the steps I’ve written seem to be executed properly. (We can’t know for sure, until we change the view and make sure the view does give us what we expect to see).

Altering the article view to include the published date.

This is pretty simple, except that I am going to make it conditional based on the article being published. If an article is published there is no point in having a publication date outputted to be nil. So I set up a small piece of conditional display code to show the publication date if an article had been published. I ran my test, and my test failed. Which, while not unexpected, is disappointing.

However, I could review my error message and since it shows all the content it was looking through, I was able to see the issue. The error message could not find the expected message because the output was ...Published on: .... In my lack of experience in working with dates, I presumed that the default value would be nil, and it turns out it isn’t, so now I have to alter the table yet again, but thanks the miracle of rails I don’t have to write a new migration. Since I’m doing this one step at a time, all I have to do is use the handy dandy bin/rake db:rollback, and it will undo my last migration (adding the published_on date field). Then I can modify that migration to include default: nil at the end, and that should (hopefully) make this test then work. Alas, it did not.

I was still getting the same empty publication date in the output, but something occurred to me. In my model code, I implemented the method to be after_update, so that we could make sure that the publication method actually took (there are some validations on update for publication that I wrote). However, just like in your basic update method (from your CRUD), I’m doing a redirect at the end of it, but not reloading the article. So, I drop a reload @article.reload! in before the redirect_to entry in my publish method.

So my solution was to run my app under localhost and see if the date was getting added when I published an article. It was not. It was still nil. So even though my code is successfully running, the date isn’t getting set properly. I changed the after_update to around_update and that messed things up even more, sadly. And then it occurred to me that it wasn’t the ‘update’ I wanted to work around. It was the save. Therefore I changed my after_update to before_save before my call back, and it worked on the local server. I ran the test, and after a purely content change regarding what the output was, the test passed.

Almost Done

So that took about two hours to do (which means I’m improving slowly I think) but I wasn’t fully done. First I had to alter my creating_article_spec.rb to make sure there was no publication date information added when creating and article, and then make sure all previously created tests still passed. A few failed due to other purely content changes made previously that I tested properly previously. So, I tweaked those, all my tests passed, and then GACP was run, for Heroku and for Github.

This is of course, the first step as I haven’t altered the article sorting for the index nor the views, but I’ll save that for another article, hopefully making this shorter.