-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
1,033 additions
and
84 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,52 @@ | ||
## About this book@chapIntroIn this book, we will guide you to develop a mini project: a small web application, named TinyBlog, that manages a blog system \(see its final state in Figure *@TinyBlogOnPharoCloudHere@*\).The idea is that a visitor of the web site can read the posts and that the post author can connect to the web site as admin to manage its posts \(add, remove and modify existing ones\).TinyBlog is a small pedagogical application that will show you how to define and deploy a web application using Pharo / Seaside / Mongo and frameworks available in Pharo such as NeoJSON.Our goal is that you will be able to reuse and adapt such an infrastructure to create your own web applications.### StructureIn the first part called "Core Tutorial", you will develop and deploy, TinyBlog, an application and its administration using Pharo, the Seaside application web server framework as well as some other frameworks such as Voyage and Magritte. Deployment with Mongo DB is optional but it allows you to see that Voyage is an elegant facade to persist your data within Mongo.In the second part and optional part, we will show you some optional aspects such as data export, use of Mustache or how to expose your application using a REST API.Presented solutions are sometimes not the best.This is done that way to offer you a room for improvement.Our goal is not to be exhaustive.We present one way to develop TinyBlog nevertheless we invite the reader to read further references such as books or tutorials on Pharo to deepen his expertise and enhance his application.Finally, to help you to get over possible errors and avoid to get stuck, the last chapter describes how to load the code described in each chapter.![The TinyBlog application.](figures/TinyBlogOnPharoCloud.png width=100&label=TinyBlogOnPharoCloudHere)### Pharo InstallationIn this tutorial, we suppose that you are using the Pharo MOOC image \(it is currently a Pharo 8.0 image\) in which many frameworks and web libraries have been loaded: Seaside \(component-based web application server\), Magritte \(an automatic generation report system based on descriptions\), Bootstrap \(a library to visually tune web applications\), Voyage \(a framework to save your objects in document databases\) and some others.You can get the Pharo MOOC image using the Pharo Launcher \([http://pharo.org/download](http://pharo.org/download)\).### Naming RulesIn the following, we prefix all the class names `TB` \(for TinyBlog\).You may:- either choose another prefix \(by example `TBM`\) to be able to load the solution side by side to your own. This way you will be able to compare the two solutions,- either choose the same prefix to fusion the proposed solutions in your code. The merge tool will help you see the differences and learn from the changes. This solution may be more complex if you implement your own extra functionalities.### ResourcesPharo has many strong pedagogial resources as well as a super friendly community of users. Here is a list of resources:- [http://books.pharo.org](http://books.pharo.org) proposes books around Pharo. Pharo by Example can help you to discove the language and its libraries. Entreprise Pharo: a Web Perspective presents other aspects useful for web development.- [http://book.seaside.st](http://book.seaside.st) is one of the books on Seaside. It is currently under migration as an open-source book [https://github.com/SquareBracketAssociates/DynamicWebDevelopmentWithSeaside](https://github.com/SquareBracketAssociates/DynamicWebDevelopmentWithSeaside).- [http://mooc.pharo.org](http://mooc.pharo.org) proposes an excellent Mooc with more than 90 videos explaining syntactically points as well as object programming key concepts.- A discord channel where many Pharoers exchange information and help each other is accessible here: [http://www.pharo.org/community](http://www.pharo.org/community) | ||
## About this book | ||
|
||
@chapIntro | ||
In this book, we will guide you to develop a mini project: a small web application, named TinyBlog, that manages a blog system (see its final state in Figure *@TinyBlogOnPharoCloudHere@*). | ||
The idea is that a visitor of the website can read the posts and that the post author can connect to the website as admin to manage its posts (add, remove and modify existing ones). | ||
|
||
TinyBlog is a small pedagogical application that will show you how to define and deploy a web application using Pharo / Seaside / Mongo and frameworks available in Pharo such as NeoJSON. | ||
|
||
Our goal is that you will be able to reuse and adapt such an infrastructure to create your own web applications. | ||
|
||
|
||
### Structure | ||
|
||
In the first part called "Core Tutorial", you will develop and deploy, TinyBlog, an application and its administration using Pharo, the Seaside application web server framework as well as some other frameworks such as Voyage and Magritte. Deployment with Mongo DB is optional but it allows you to see that Voyage is an elegant facade to persist your data within Mongo. | ||
|
||
In the second part and optional part, we will show you some optional aspects such as data export, use of Mustache or how to expose your application using a REST API. | ||
|
||
Presented solutions are sometimes not the best. | ||
This is done that way to offer you room for improvements. | ||
Our goal is not to be exhaustive. | ||
We present one way to develop TinyBlog nevertheless we invite the reader to read further references such as books or tutorials on Pharo to deepen his expertise and enhance his application. | ||
|
||
Finally, to help you to get over possible errors and avoid getting stuck, the last chapter describes how to load the code described in each chapter. | ||
|
||
![The TinyBlog application. % width=100&label=TinyBlogOnPharoCloudHere](figures/TinyBlogOnPharoCloud.png) | ||
|
||
|
||
### Pharo Installation | ||
|
||
|
||
In this tutorial, we suppose that you are using the Pharo MOOC image (it is currently a Pharo 8.0 image) in which many frameworks and web libraries have been loaded: Seaside (component-based web application server), Magritte (an automatic generation report system based on descriptions), Bootstrap (a library to visually tune web applications), Voyage (a framework to save your objects in document databases) and some others. | ||
|
||
You can get the Pharo MOOC image using the Pharo Launcher ([http://pharo.org/download](http://pharo.org/download)). | ||
|
||
### Naming Rules | ||
|
||
|
||
In the following, we prefix all the class names `TB` (for TinyBlog). | ||
You may: | ||
- either choose another prefix (by example `TBM`) to be able to load the solution side by side to your own. This way you will be able to compare the two solutions, | ||
- either choose the same prefix to fusion the proposed solutions in your code. The merge tool will help you see the differences and learn from the changes. This solution may be more complex if you implement your own extra functionalities. | ||
|
||
|
||
### Resources | ||
|
||
|
||
Pharo has many strong pedagogical resources and a super-friendly user community. Here is a list of resources: | ||
|
||
- [http://books.pharo.org](http://books.pharo.org) proposes books around Pharo. Pharo by Example can help you to discover the language and its libraries. Entreprise Pharo: a Web Perspective presents other aspects useful for web development. | ||
- [http://book.seaside.st](http://book.seaside.st) is one of the books on Seaside. It is currently under migration as an open-source book [https://github.com/SquareBracketAssociates/DynamicWebDevelopmentWithSeaside](https://github.com/SquareBracketAssociates/DynamicWebDevelopmentWithSeaside). | ||
- [http://mooc.pharo.org](http://mooc.pharo.org) proposes an excellent Mooc with more than 90 videos explaining syntactical points as well as object programming key concepts. | ||
- A discord channel where many Pharoers exchange information and help each other is accessible here: [http://www.pharo.org/community](http://www.pharo.org/community) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,59 +1,251 @@ | ||
## TinyBlog Application: Core model@cha:model% full code at: 9c87f4b8abb956469e145d2ba8afeba8b66159d2In this chapter we will start to develop part of the domain model of TinyBlog. The model is extremely simple: it starts with just a post. In the next chapter we will add a blog containing a list of these posts.### TBPost Class![TBPost: a really basic class mostly handling data.](figures/postUML.pdf width=20&label=postUml)We start with the post representation. It is very simple, as you can see in Figure *@postUml@*. It is defined by the class `TBPost`:```Object subclass: #TBPost | ||
## TinyBlog Application: Core model | ||
|
||
@cha:model | ||
% full code at: 9c87f4b8abb956469e145d2ba8afeba8b66159d2 | ||
|
||
In this chapter, we will start to develop part of the domain model of TinyBlog. The model is extremely simple: it starts with just a post. In the next chapter, we will add a blog containing a list of these posts. | ||
|
||
|
||
### TBPost Class | ||
|
||
|
||
![TBPost: a really basic class mostly handling data. %width=20&label=postUml](figures/postUML.pdf ) | ||
|
||
We start with the post representation. It is very simple, as you can see in Figure *@postUml@*. It is defined by the class `TBPost`: | ||
|
||
``` | ||
Object subclass: #TBPost | ||
instanceVariableNames: 'title text date category visible' | ||
classVariableNames: '' | ||
package: 'TinyBlog'```A blog post is described by 5 instance variables:| Variable | Signification | || --- | --- | --- || `title` | post title | || `text` | post text | || `date` | date of writing | || `category` | name of the category of the post | || `visible` | is the post publicly visible or not? | |All of these variables have corresponding accessor methods in the 'accessing' protocol. You can use a refactoring to automatically create all of the following methods:```TBPost >> title | ||
^ title``````TBPost >> title: aString | ||
title := aString``````TBPost >> text | ||
^ text``````TBPost >> text: aString | ||
text := aString``````TBPost >> date | ||
^ date``````TBPost >> date: aDate | ||
date := aDate``````TBPost >> visible | ||
^ visible``````TBPost >> visible: aBoolean | ||
visible := aBoolean``````TBPost >> category | ||
^ category``````TBPost >> category: anObject | ||
category := anObject```### Post VisibilityWe should add methods to make a post visible or not and also test if it is visible. Those methods are defined in the 'action' protocol.```TBPost >> beVisible | ||
self visible: true``````TBPost >> notVisible | ||
self visible: false```### InitializationThe `initialize` method \(in the 'initialization' protocol\) sets the date to the current day and the visibility to false; the user must explicitly make a post visible. This allows them to write a draft post and only publish it when it is finished. By default, a post belongs to the 'Unclassified' category that we define at the class level. This category name is defined on the class-side by the `unclassifiedTag` method.```TBPost class >> unclassifiedTag | ||
^ 'Unclassified'```Pay attention the method `unclassifiedTag` should be defined on the _class-side_ of the class `TBPost` \(click on the `class` button to define it\). The other methods are defined on the instance-side and will be applied to `TBBlog` instances. ```TBPost >> initialize | ||
package: 'TinyBlog' | ||
``` | ||
|
||
|
||
A blog post is described by 5 instance variables: | ||
|
||
|
||
| Variable | Signification | | | ||
| --- | --- | --- | | ||
| `title` | post title | | | ||
| `text` | post text | | | ||
| `date` | date of writing | | | ||
| `category` | name of the category of the post | | | ||
| `visible` | is the post publicly visible or not? | | | ||
|
||
All of these variables have corresponding accessor methods in the 'accessing' protocol. You can use a refactoring to automatically create all of the following methods: | ||
|
||
``` | ||
TBPost >> title | ||
^ title | ||
``` | ||
|
||
``` | ||
TBPost >> title: aString | ||
title := aString | ||
``` | ||
|
||
``` | ||
TBPost >> text | ||
^ text | ||
``` | ||
|
||
``` | ||
TBPost >> text: aString | ||
text := aString | ||
``` | ||
|
||
``` | ||
TBPost >> date | ||
^ date | ||
``` | ||
|
||
``` | ||
TBPost >> date: aDate | ||
date := aDate | ||
``` | ||
|
||
``` | ||
TBPost >> visible | ||
^ visible | ||
``` | ||
|
||
``` | ||
TBPost >> visible: aBoolean | ||
visible := aBoolean | ||
``` | ||
|
||
``` | ||
TBPost >> category | ||
^ category | ||
``` | ||
|
||
``` | ||
TBPost >> category: anObject | ||
category := anObject | ||
``` | ||
|
||
|
||
|
||
### Post Visibility | ||
|
||
|
||
We should add methods to make a post visible or not and also test if it is visible. Those methods are defined in the 'action' protocol. | ||
|
||
``` | ||
TBPost >> beVisible | ||
self visible: true | ||
``` | ||
|
||
``` | ||
TBPost >> notVisible | ||
self visible: false | ||
``` | ||
|
||
|
||
|
||
### Initialization | ||
|
||
|
||
The `initialize` method (in the 'initialization' protocol) sets the date to the current day and the visibility to false; the user must explicitly make a post visible. This allows them to write a draft post and only publish it when it is finished. By default, a post belongs to the 'Unclassified' category that we define at the class level. This category name is defined on the class-side by the `unclassifiedTag` method. | ||
|
||
``` | ||
TBPost class >> unclassifiedTag | ||
^ 'Unclassified' | ||
``` | ||
|
||
|
||
Pay attention the method `unclassifiedTag` should be defined on the _class-side_ of the class `TBPost` \(click on the `class` button to define it\). The other methods are defined on the instance-side and will be applied to `TBBlog` instances. | ||
|
||
``` | ||
TBPost >> initialize | ||
super initialize. | ||
self category: TBPost unclassifiedTag. | ||
self date: Date today. | ||
self notVisible```In the solution above, it would be better for the `initialize` method not to hard code the reference to the `TBPost` class. Try to think of a solution. The part 3 in week 6 of the Mooc will help you to understand why it is better to avoid hardcoding class references \(See [http://mooc.pharo.org](http://mooc.pharo.org)\).### Posts Creation MethodsOn the class-side, we add class methods \(i.e. methods that execute on the class object\) to make post creation more convenient - usually such methods are grouped under the protocol 'instance creation'.We define two methods:```TBPost class >> title: aTitle text: aText | ||
self notVisible | ||
``` | ||
|
||
|
||
In the solution above, it would be better for the `initialize` method not to hard code the reference to the `TBPost` class. Try to think of a solution. The part 3 in week 6 of the Mooc will help you to understand why it is better to avoid hardcoding class references (See [http://mooc.pharo.org](http://mooc.pharo.org)). | ||
|
||
### Posts Creation Methods | ||
|
||
|
||
On the class-side, we add class methods (i.e. methods that execute on the class object) to make post-creation more convenient - usually such methods are grouped under the protocol 'instance creation'. | ||
|
||
We define two methods: | ||
``` | ||
TBPost class >> title: aTitle text: aText | ||
^ self new | ||
title: aTitle; | ||
text: aText; | ||
yourself``````TBPost class >> title: aTitle text: aText category: aCategory | ||
yourself | ||
``` | ||
|
||
|
||
``` | ||
TBPost class >> title: aTitle text: aText category: aCategory | ||
^ (self title: aTitle text: aText) | ||
category: aCategory; | ||
yourself```### Creating a PostLet us create some posts to check the objects that are created. Using the Playground tool, execute the following expression:```TBPost | ||
yourself | ||
``` | ||
|
||
|
||
### Creating a Post | ||
|
||
|
||
Let us create some posts to check the objects that are created. Using the Playground tool, execute the following expression: | ||
|
||
``` | ||
TBPost | ||
title: 'Welcome in TinyBlog' | ||
text: 'TinyBlog is a small blog engine made with Pharo.' | ||
category: 'TinyBlog'```When you inspect the code above \(right click and "Inspect it"\), you will open an inspector on the newly created object as shown in Figure *@inspectorOnTBPost@*.![Inspector on a TBPost instance.](figures/inspectorOnTBPost.png width=100&label=inspectorOnTBPost)### Adding Some Unit TestsManually looking at objects is not a good way to systematically verify that such objects behave as expected. Even though the model is quite simple we can still define some tests. When doing Test Driven Developement we write our tests first, but here we have preferred to define a little class to familiarize ourselves with the IDE first. Let us fix this!We define the class `TBPostTest` \(as subclass of the class `TestCase`\):```TestCase subclass: #TBPostTest | ||
category: 'TinyBlog' | ||
``` | ||
|
||
|
||
When you inspect the code above (right click and "Inspect it"), you will open an inspector on the newly created object as shown in Figure *@inspectorOnTBPost@*. | ||
|
||
![Inspector on a TBPost instance. %width=100&label=inspectorOnTBPost](figures/inspectorOnTBPost.png ) | ||
|
||
### Adding Some Unit Tests | ||
|
||
|
||
Manually looking at objects is not a good way to systematically verify that such objects behave as expected. Even though the model is quite simple we can still define some tests. When doing Test-Driven Developement we write our tests first, but here we have preferred to define a little class to familiarize ourselves with the IDE first. Let us fix this! | ||
|
||
We define the class `TBPostTest` (as subclass of the class `TestCase`): | ||
|
||
``` | ||
TestCase subclass: #TBPostTest | ||
instanceVariableNames: '' | ||
classVariableNames: '' | ||
package: 'TinyBlog-Tests'```Let us define two tests:```TBPostTest >> testWithoutCategoryIsUnclassified | ||
package: 'TinyBlog-Tests' | ||
``` | ||
|
||
|
||
Let us define two tests: | ||
|
||
``` | ||
TBPostTest >> testWithoutCategoryIsUnclassified | ||
| post | | ||
post := TBPost | ||
title: 'Welcome to TinyBlog' | ||
text: 'TinyBlog is a small blog engine made with Pharo.'. | ||
self assert: post title equals: 'Welcome to TinyBlog'. | ||
self assert: post category = TBPost unclassifiedTag``````TBPostTest >> testPostIsCreatedCorrectly | ||
self assert: post category = TBPost unclassifiedTag | ||
``` | ||
|
||
|
||
``` | ||
TBPostTest >> testPostIsCreatedCorrectly | ||
| post | | ||
post := TBPost | ||
title: 'Welcome to TinyBlog' | ||
text: 'TinyBlog is a small blog engine made with Pharo.' | ||
category: 'TinyBlog'. | ||
self assert: post title equals: 'Welcome to TinyBlog'. | ||
self assert: post text equals: 'TinyBlog is a small blog engine made with Pharo.'```Your tests should pass.### Post QueriesIn the protocol 'testing', define the following methods that checks whether a post is visible, and whether it is classified or not:```TBPost >> isVisible | ||
^ self visible``````TBPost >> isUnclassified | ||
^ self category = TBPost unclassifiedTag```Again, it is not really good practice to hardcode a reference to the class `TBPost` in a method body. Try to think of a solution.In addition, let us take the time to update our tests to demonstrate this new behavior:```TBPostTest >> testWithoutCategoryIsUnclassified | ||
self assert: post text equals: 'TinyBlog is a small blog engine made with Pharo.' | ||
``` | ||
|
||
|
||
Your tests should pass. | ||
|
||
### Post Queries | ||
|
||
|
||
In the protocol 'testing', define the following methods that check whether a post is visible, and whether it is classified or not: | ||
|
||
``` | ||
TBPost >> isVisible | ||
^ self visible | ||
``` | ||
|
||
|
||
``` | ||
TBPost >> isUnclassified | ||
^ self category = TBPost unclassifiedTag | ||
``` | ||
|
||
|
||
Again, it is not really good practice to hardcode a reference to the class `TBPost` in a method body. Try to think of a solution. | ||
|
||
In addition, let us take the time to update our tests to demonstrate this new behavior: | ||
|
||
``` | ||
TBPostTest >> testWithoutCategoryIsUnclassified | ||
| post | | ||
post := TBPost | ||
title: 'Welcome to TinyBlog' | ||
text: 'TinyBlog is a small blog engine made with Pharo.'. | ||
self assert: post title equals: 'Welcome to TinyBlog' . | ||
self assert: post title equals: 'Welcome to TinyBlog'. | ||
self assert: post isUnclassified. | ||
self deny: post isVisible```### ConclusionWe have developed the first part of the model \(the class `TBPost`\) and some tests. We strongly suggest writing some more unit tests to make sure that your model works as expected. | ||
self deny: post isVisible | ||
``` | ||
|
||
|
||
### Conclusion | ||
|
||
|
||
We have developed the first part of the model (the class `TBPost`) and some tests. We strongly suggest writing some more unit tests to make sure that your model works as expected. |
Oops, something went wrong.