% -*- mode: latex; -*- mustache tags:  
\ifx\wholebook\relax\else

	\documentclass[10pt,twoside,english]{root/support/latex/sbabook/sbabook}
  \usepackage{import}
  \subimport{root/support/latex/}{common.tex}

  \hypersetup{pdfinfo = {
    Title = {TinyBlog: Extending and Testing the Model},
    Author = {},
    Keywords = {pharo, smalltalk}}}
  \begin{document}
\fi

\chapter{TinyBlog: Extending and testing the model}\section{Previous week solution}
You can load the solution of the previous week using the following snippet:

\begin{displaycode}{smalltalk}
Gofer new 
   smalltalkhubUser: 'PharoMooc' project: 'TinyBlog';
   package: 'ConfigurationOfTinyBlog';
   load. 
#ConfigurationOfTinyBlog asClass loadWeek1Correction
\end{displaycode}

Then, you can browse the code of the \textcode{TBPost} class.
With this solution, you can complete the code of \textbf{your} TinyBlog application if needed before continuing.
\section{Save your code}
Even if you can save the Pharo image which contains all the objects of the system
(and as such the classes you wrote), this section explains how Pharoers usually save their code as packages on dedicated servers with the Pharo versioning system named Monticello. Smalltalkhub \url{http://smalltalkhub.com} is one of these servers to host saved Pharo code. You can also use SS3 at \url{http://ss3.gemstone.com}. 
\subsection{Create a repository}
\begin{itemize}
\item Create an account on  \url{http://smalltalkhub.com/}.
\item Log yourself on the web site.
\item Create a project (it may happen that you get a connection problem because the web site is still in beta, in such a case retry with a different web browser. If the problems persist use \url{http://ss3.gemstone.com}).
\begin{itemize}
\item Name it the way you want. For example \symbol{34}TinyBlog\symbol{34}
\end{itemize}

\end{itemize}
\subsection{Save your package}
\begin{itemize}
\item In Pharo, open the Monticello Browser available in the main menu.
\item Add a repository (of type either SmalltalkHub or HTTP for SS3)
\item Select the repository and the menu item 'Add to package...' to add this repository to the package TinyBlog. 
\item Select your package and press the 'Save' button.
\item Enter a log describing the changes you did. 
\end{itemize}

The code of your application is now in your SmalltalkHub repository. You should now be able to load your code in a new Pharo image. Nevertheless, if you use a stock Pharo image, you must also load Seaside and other libraries and frameworks.

In this project, we encourage you to always use the image we gave with the preloaded packages and that you can find at \url{http://mooc.pharo.org}. This will let you load new code without having to take care about package dependencies. 
\section{About dependencies}
Good practices in Pharo developments are to clearly specify the dependencies of used packages. The idea is to ensure that the building of a project is fully reproducible.
It is important because once we have such reproducible process we can take advantage about automatic build servers such as Jenkins or Travis. 

To express dependencies between projects and packages within a project
Pharo offers special classes called Configurations. Such configurations express the architecture (main repository, dependencies to other projects, structure of the projects) as well as versionned packages. 

In the context of the TinyBlog project, we do not got any further on this. Note that a full chapter on dependency expression is available in the \textit{Deep In Pharo} book.
 
\section{TBBlog class}
A \textcode{TBBlog} contains posts. We will now develop this \textcode{TBBlog} class and its unit tests.

\begin{displaycode}{smalltalk}
Object subclass: #TBBlog
   instanceVariableNames: 'posts'
   classVariableNames: ''
   package: 'TinyBlog'
\end{displaycode}

We initialize the posts to hold an empty collection.

\begin{displaycode}{smalltalk}
TBBlog >> initialize
   super initialize.
   posts := OrderedCollection new. 
\end{displaycode}
\section{Only one blog object}
In the rest of this project, we assume that we will manage only one blog. Later, you may add the possibility to manage multiple blogs such as one per user of the TinyBlog application.
Currently, we use a Singleton design pattern on the \textcode{TBBlog} class.

Since all the management of a singleton is a class behavior, we define such methods at the class level. 
 

\begin{displaycode}{smalltalk}
TBBlog class
   instanceVariableNames: 'uniqueInstance'
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog class >> reset
   uniqueInstance := nil
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog class >> current
   "answer the instance of the TBRepository"
   ^ uniqueInstance ifNil: [ uniqueInstance := self new ]
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog class >> initialize
   self reset
\end{displaycode}
\section{Testing the Model}
We now adopt a Test Driven Development approach i.e., we will write a unit test first and then develop the business functionality until the test is green. We will repeat this process for each functionality of the model.

We create unit tests in the \textcode{TBBlogTest} class that belongs to the \textcode{TinyBlog-Tests} tag. A tag is just a label to sort classes inside a package (See menu item 'Add Tag...'). We use a tag because using two packages will make this project more complex. However, while implementing a real application, it is recommended to have a separate package for the tests.

\begin{displaycode}{smalltalk}
TestCase subclass: #TBBlogTest
   instanceVariableNames: 'blog post first'
   classVariableNames: ''
   category: 'TinyBlog-Tests'
\end{displaycode}

Before each test execution, the \textcode{setUp} method initializes the context of tests.
For example, it erases the blog content, adds one post and creates another temporary post that is not saved.

\begin{displaycode}{smalltalk}
TBBlogTest >> setUp
   blog := TBBlog current.
   blog removeAllPosts.

   first := TBPost title: 'A title' text: 'A text' category: 'First Category'.
   blog writeBlogPost: first.

   post := (TBPost title: 'Another title' text: 'Another text' category: 'Second Category') beVisible
\end{displaycode}

As you may notice, we test different configurations. 
Posts do not belong to the same category, one is visible and the other is not visible.

At the end of each test, the \textcode{tearDown}  method is executed and resets the blog.

\begin{displaycode}{smalltalk}
TBBlogTest >> tearDown
   TBBlog reset
\end{displaycode}

Here we see the limits of using a Singleton. Indeed, if you deploy a blog and then execute the tests, you will lose all posts that have been created because we reset the Blog singleton.

We will now develop tests first and then implement all functionalities to make them green.
\subsection{First test}
The first test adds a post in the blog and verifies that this post is effectivly added.

\begin{displaycode}{smalltalk}
TBBlogTest >> testAddBlogPost
   blog writeBlogPost: post.
   self assert: blog size equals: 2
\end{displaycode}

If you try to execute it, you will notice that this test is not green because we did not define the methods \textcode{writeBlogPost:}, \textcode{removeAllPosts} and \textcode{size}. Let's add them:

\begin{displaycode}{smalltalk}
TBBlog >> removeAllPosts
   posts := OrderedCollection new
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> writeBlogPost: aPost
   "Write the blog post in database"
   posts add: aPost
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> size
   ^ posts size
\end{displaycode}

The previous test should now pass (i.e. be green).
We should also add tests to cover all functionalities that we introduced.
\subsection{Test the number of blog posts}
\begin{displaycode}{smalltalk}
TBBlogTest >> testSize
   self assert: blog size equals: 1
\end{displaycode}
\subsection{Remove all posts}
\begin{displaycode}{smalltalk}
TBBlogTest >> testRemoveAllBlogPosts
   blog removeAllPosts.
   self assert: blog size equals: 0
\end{displaycode}
\section{Other functionalities}
We now develop new functionalities as methods in the 'action' protocol of the  \textcode{TBBlog}.
While doing that, we regularly ensure that tests pass.
\subsection{Retrieve all posts (visible and invisible)}
\begin{displaycode}{smalltalk}
TBBlogTest >> testAllBlogPosts
   blog writeBlogPost: post.
   self assert: blog allBlogPosts size equals: 2
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> allBlogPosts
   ^ posts
\end{displaycode}
\subsection{Retrieve visible posts}
\begin{displaycode}{smalltalk}
TBBlogTest >> testAllVisibleBlogPosts
   blog writeBlogPost: post.
   self assert: blog allVisibleBlogPosts size equals: 1
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> allVisibleBlogPosts
   ^ posts select: [ :p | p isVisible ]
\end{displaycode}
\subsection{Retrieve all posts of one category}
\begin{displaycode}{smalltalk}
TBBlogTest >> testAllBlogPostsFromCategory
   self assert: (blog allBlogPostsFromCategory: 'First Category') size equals: 1
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> allBlogPostsFromCategory: aCategory
   ^ posts select: [ :p | p category = aCategory ]
\end{displaycode}
\subsection{Retrieve all visible posts of one category}
\begin{displaycode}{smalltalk}
TBBlogTest >> testAllVisibleBlogPostsFromCategory
   blog writeBlogPost: post.
   self assert: (blog allVisibleBlogPostsFromCategory: 'First Category') size equals: 0.
   self assert: (blog allVisibleBlogPostsFromCategory: 'Second Category') size equals: 1
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> allVisibleBlogPostsFromCategory: aCategory
   ^ posts select: [ :p | p category = aCategory and: [ p isVisible ] ]
\end{displaycode}
\subsection{Check unclassified posts}
\begin{displaycode}{smalltalk}
TBBlogTest >> testUnclassifiedBlogPosts
   self assert: (blog allBlogPosts select: [ :p | p isUnclassified ]) size equals: 0
\end{displaycode}
\subsection{Retrieve all categories}
\begin{displaycode}{smalltalk}
TBBlogTest >> testAllCategories
   blog writeBlogPost: post.
   self assert: blog allCategories size equals: 2
\end{displaycode}

\begin{displaycode}{smalltalk}
TBBlog >> allCategories
   ^ (self allBlogPosts collect: [ :p | p category ]) asSet
\end{displaycode}
\section{Possible extensions}
Many extensions can be made such as: retrieve the list of categories that contains at least one visible post, delete a category and all posts that it contains, rename a category, move a post from one category to another, make (in)visible one category and all its content, etc. We encourage you to develop some of them. 

To help you testing the application, you can add the following method that creates multiple posts.

\begin{displaycode}{smalltalk}
TBBlog class >> createDemoPosts
   "TBBlog createDemoPosts"
   self current 
      writeBlogPost: ((TBPost title: 'Welcome in TinyBlog' text: 'TinyBlog is a small blog engine made with Pharo.' category: 'TinyBlog') visible: true);
      writeBlogPost: ((TBPost title: 'Report Pharo Sprint' text: 'Friday, June 12 there was a Pharo sprint / Moose dojo. It was a nice event with more than 15 motivated sprinters. With the help of candies, cakes and chocolate, huge work has been done' category: 'Pharo') visible: true);
      writeBlogPost: ((TBPost title: 'Brick on top of Bloc - Preview' text: 'We are happy to announce the first preview version of Brick, a new widget set created from scratch on top of Bloc. Brick is being developed primarily by Alex Syrel (together with Alain Plantec, Andrei Chis and myself), and the work is sponsored by ESUG. 
      Brick is part of the Glamorous Toolkit effort and will provide the basis for the new versions of the development tools.' category: 'Pharo') visible: true);
      writeBlogPost: ((TBPost title: 'The sad story of unclassified blog posts' text: 'So sad that I can read this.') visible: true);
      writeBlogPost: ((TBPost title: 'Working with Pharo on the Raspberry Pi' text: 'Hardware is getting cheaper and many new small devices like the famous Raspberry Pi provide new computation power that was one once only available on regular desktop computers.' category: 'Pharo') visible: true)
\end{displaycode}

If you inspect the result of the following snippet, you will see that the current blog contains 5 posts:

\begin{displaycode}{smalltalk}
	TBBlog createDemoPosts ; current
\end{displaycode}
\section{Conclusion}
You get now the full model of TinyBlog as well as some tests. You are now ready to implement more advanced functionality such as the database storage or a first HTTP server. Do not forget to save your code. 


\ifx\wholebook\relax\else
\end{document}
\fi