The code works with the version of libraries included in the book’s downloadable source code, but if you do a latest git pull of all 3 libraries you need to change the code as per the example as given in the Hasher GitHub readme

Also, the crossroads routes must be set up before the hasher.init(). Otherwize the hasher initialized callback will have no routes to execute.
Otherwise, the book is great! Bunch of people in the office want to borrow it once I am done with it:)

Note: db_shard_id tuple is required for mysql db_adapter because of the way mysql db_adapter included with CB creates connection pools. Think of it as a connection pool name. DBIdentifier in source code.

Sharding examples are endless: you can persist models of click-stream data to Riak, store content of pages in PostgreSQL, store some logs in Archive type of MySQL database and so on, all based on one configuration file.

Let’s say we want to create a basic 1:Many entity relationship between a blog post and comments in our hypothetical blog software. 1 blog post can have 0 or many comments. Create a new empty ChicagoBoss application by running:

Each blog post can have many comments, CB requires that you add -has({comments, many}). to the module declaration. Note that the comment model name must end with s as the first element in the tuple (tag of the tuple).

Comment must belong to a post. We add a simple length of the PostId check into validation_tests/0. Put this into src/model/comment.erl

Make sure that ChicagoBoss successfully builds. Otherwise, you might need to download and install the latest version of Erlang and all it’s dependencies. To ask questions about ChicagoBoss, join the growing ChicagoBoss community on Google Groups.

and navigate to URL: http://localhost:8001/pages/index to see Hello World rendered in your browser.
The naming convention for ChicagoBoss controllers is: [OTP_application_name]_[controller_name]_controller.erl inside of src/controller directory.

Data Model
In this example, all the wiki data will be stored in MySQL database.
Modify db_host, db_port, db_adapter and add db_username, db_password, db_database in boss.config to result in:

Naturally, db_* tuple parameters will vary depending on your specific deployment of MySQL.
For more information on setting up database connectivity, see README_DATABASE.Note: The "id" field should be a serial integer in README_DATABASE means that id column in that table is SERIAL which is an alias for BIGINT UNSIGNED NOT NULL AUTO_INCREMENT UNIQUE

Visit http://localhost:8001/doc/page to verify that the page model is available.
Let’s test adding content to MySQL database from the development console. After you start the server with ./start-dev.sh you find yourself in an erlang shell. Enter the following to create a new row in the pages table:
FirstPage = page:new(id, "First Page", "Page Content").
FirstPage:save().

“First Page” corresponds to the PageTitle parameter of the page.erl module, while “Page Content” corresponds to the PageText parameter of the page.erl module.
To load the saved page into a new variable, we use boss_db api such as:
[FoundPage] = boss_db:find(page, [{page_title, 'equals', "First Page"}], 1).

The URLs in the wiki are going to be:

/pages/index displays the list of all wiki pages

/pages/view/page-id displays a wiki page

/pages/create allows to add a page

/pages/edit/page-id allows to edit a page

This is a basic application example, and all the business logic will reside inside of one controller.

Let’s go back and modify the pages controller, index function, to return a list of pages in our wiki.

After you save index.html and browse to http://localhost:8001/pages, you will get an HTML page with a list of page titles or “No Wiki Pages” depending on if you manually added pages to the MySQL pages table.

When you browse to http://localhost:8001/pages/index or http://localhost:8001/pages, a GET request gets sent to the server by the browser and ChicagoBoss routes the request to index function inside the example_wiki_pages_controller.

Line Pages = boss_db:find(page, []) selects all rows from pages table, assigns it to Pages variable and the controller’s function returns the results packed inside a tuple to the index.html view.

The returned data of controller’s functions can be different tuples:

{output, <>} will bypass views and send back raw data as is to the browser

Just for kicks, change the return result of example_wiki_pages_controller:index/2 to be {json, [{pages, Pages}]} instead of {ok, [{pages, Pages}]} and load the URL in your browser: http://localhost:8001/pages to see JSON representation of all pages inside of the MySQL database.

We need to be able to create, view and edit wiki pages using the browser. Let’s edit src/controller/example_wiki_pages_controller.erl to allow this functionality.

When we want to see a wiki page, the page’s id will be appended to the /pages/view/ URL. View function will look-up the page in the database, if the page is found, page_text() function will be invoked on the object to get the page’s content. When new pages are created, it is possible to link from one page to another by adding [page-id] markup to it’s content, and the hackish/ugly/smelly code inside of the view function replaces [page-id] format into hrefs to the page and sends the data to the view.html view.

Create.html view template contains a form that POSTs data to /pages/create URL and that means that our controller needs to be able to receive the POST content, have the model validate the data and insert the data of a new wiki page as a new row into the pages table. If the data validates and is inserted, we redirect the browser to the controller’s view() function with the new page id as a parameter and if there is an error, the create.html pages gets rendered again but this time with an error message and title and pages textarea populated to previous values.

The view.html template contains a link to be able to edit a wiki page. URL to edit a wiki page is in the /pages/edit/page-id format. edit(‘GET’, [Id]) function in the controller gets invoked. It looks up the page in the database and passes it’s data to the src/view/pages/edit.html template