Remove the existing controller and controller spec

Users index

Let's start by implementing a Users' index API call.

Create the Users' controller test file

Create the file ./spec/controllers/1/users_controller_spec.lua, and let's write the first test:

require'spec.spec_helper'describe("UsersController",function()describe("#index",function()it("shows the list of users",function()localresponse=hit({method='GET',path="/users"})assert.are.equal(200,response.status)assert.are.same({},response.body)end)end)end)

As a start, we just want our Users' controller to return a successful response with an empty JSON body. Let's run the tests:

Congratulations! Now let's modify our controller to return the users in the database.

Setup Database

We will be using a MySql database, so let's edit the ./db/mysql.lua file with our settings.

localSqlDatabase=require'gin.db.sql'localGin=require'gin.core.gin'-- First, specify the environment settings for this database, for instance:localDbSettings={development={adapter='mysql',host="127.0.0.1",port=3306,database="demo_development",user="root",password="",pool=5},test={adapter='mysql',host="127.0.0.1",port=3306,database="demo_test",user="root",password="",pool=5},production={adapter='mysql',host="127.0.0.1",port=3306,database="demo_production",user="root",password="",pool=5}}-- Then initialize and return your database:localMySql=SqlDatabase.new(DbSettings[Gin.env])returnMySql

We have now defined a connection to our MySql database.

Create a Users' table

Let's go ahead and generate a new migration for the newly created MySql connection:

Edit the file ./db/migrations/20131116131423.lua to create the users table in the up() method (called when the migration is run), and destroy it in the down() method (when the migration is rolled back), like this:

This defines a model Users that corresponds to the database table users, for the database connection MySql.

By convention, models in Gin have plural names.

Modify Users' controller test

Let's now modify the Users' controller test to return users. Edit the file ./spec/controllers/1/users_controller_spec.lua to add some fixture users in our database:

require'spec.spec_helper'localMySql=require'db.mysql'localUsers=require'app.models.users'localfunctionclean_db()MySql:execute("TRUNCATE TABLE users;")enddescribe("UsersController",function()before_each(function()clean_db()end)after_each(function()clean_db()end)describe("#index",function()before_each(function()roberto=Users.create({first_name='roberto',last_name='gin'})hedy=Users.create({first_name='hedy',last_name='tonic'})end)after_each(function()roberto=nilhedy=nilend)it("shows the list of users ordered by first name",function()localresponse=hit({method='GET',path="/users"})assert.are.equal(200,response.status)assert.are.same({[1]=hedy,[2]=roberto},response.body)end)end)end)

Remember to clean up after your tests, this is what the MySql:execute("TRUNCATE TABLE users;") call in clean_db() is for. We are calling clean_db() before and after all tests, to ensure that the database has a clean start even in the case that previous tests were run improperly or aborted for any reason.

Also, don't forget to reset any global variables, like roberto and hedy in the example here above.

Users create

For readability concerns, code related to the index action described in the previous section is partially omitted.

Add controller's test

Edit your controller test to add the tests for the create action.

require'spec.spec_helper'localMySql=require'db.mysql'localUsers=require'app.models.users'localfunctionclean_db()MySql:execute("TRUNCATE TABLE users;")enddescribe("UsersController",function()before_each(function()clean_db()end)after_each(function()clean_db()end)[...]describe("#create",function()it("adds a new user",function()localresponse=hit({method='POST',path="/users",body={first_name='new-user',last_name='gin'}})localnew_user=Users.find_by({first_name='new-user'})assert.are_not.equals(nil,new_user)assert.are.equal(201,response.status)assert.are.same(new_user,response.body)end)end)end)

Accepted params

We're currently not filtering out the params that we are allowing external callers to set in our models. In this example, for instance, we do not want an external caller to be able to set the id they want on new users, nor to make our server raise an error due to non-existent params being passed in.

Add the test:

require'spec.spec_helper'localMySql=require'db.mysql'localUsers=require'app.models.users'localfunctionclean_db()MySql:execute("TRUNCATE TABLE users;")enddescribe("UsersController",function()before_each(function()clean_db()end)after_each(function()clean_db()end)[...]describe("#create",function()it("adds a new user filtering out unaccepted params",function()localrequest_new_user={first_name='new-user',last_name='gin',id=400,nonexisent_param='non-existent'}localresponse=hit({method='POST',path="/users",body=request_new_user})localnew_user=Users.find_by({first_name='new-user'})assert.are_not.equals(nil,new_user)assert.are.equal(201,response.status)assert.are.same('new-user',new_user.first_name)assert.are.same('gin',new_user.last_name)assert.are.not_equals(400,new_user.id)assert.are.not_equals('non-existent',new_user.nonexisent_param)end)end)end)

One last thing: we need to return a 404 if the user cannot be found. The test for the show action becomes:

require'spec.spec_helper'localMySql=require'db.mysql'localUsers=require'app.models.users'localfunctionclean_db()MySql:execute("TRUNCATE TABLE users;")enddescribe("UsersController",function()before_each(function()clean_db()end)after_each(function()clean_db()end)[...]describe("#show",function()describe("when the user can be found",function()before_each(function()roberto=Users.create({first_name='roberto',last_name='gin'})end)after_each(function()roberto=nilend)it("shows a user",function()localresponse=hit({method='GET',path="/users/roberto"})assert.are.equal(200,response.status)assert.are.same(roberto,response.body)end)end)describe("when the user cannot be found",function()it("returns a 404",function()localresponse=hit({method='GET',path="/users/roberto"})assert.are.equal(404,response.status)assert.are.same({},response.body)end)end)end)end)