#Creating a RailsApp from Scratch
###Create the initial project structure
rails new acm_attendance
###Create a Repo for the project
# cd into the project direcotry
$ cd acm_attendance
# intialize the git repo
$ git init
###First Commit To get started let's make a commit with what was intially built out
git add .
git commit -am 'Initial commit.'
####Working with GitHub
From your GitHub dashboard click "+ New Repository".
Give it a name and optionally a desscription.
After you create the repository on GitHub they should have the following instruction for pusing an existing repository to GitHub. It should be something like
git remote add origin git@github.com:YOU/acm_attenance.git
git push -u origin master
After pushing your branch you should be able to refresh your browser and see your repository!
#Where we are so far
At this point you can start the server that is running your project
rails server
and visit your site in your favorite browser through http://localhost:3000
#Building the Application
This application will provide a very basic way for a person to say they attended a meeting.
##Creating a Meeting
Since we are working on a new feature it is best to checkout a new git branch:
$ git checkout -b create_meeting
###Scaffolding To begin we will create a scaffold.
A scaffold in Rails is a full set of model, database migration for that model, controller to manipulate it, views to view and manipulate the data, and a test suite for each of the above.[1]
rails g scaffold meeting date:datetime
invoke active_record
# migration
create db/migrate/20140917213533_create_meetings.rb
# model
create app/models/meeting.rb
invoke test_unit
# model tests
create test/models/meeting_test.rb
create test/fixtures/meetings.yml
# route
invoke resource_route
route resources :meetings
invoke scaffold_controller
# controller
create app/controllers/meetings_controller.rb
invoke erb
# views
create app/views/meetings
create app/views/meetings/index.html.erb
create app/views/meetings/edit.html.erb
create app/views/meetings/show.html.erb
create app/views/meetings/new.html.erb
create app/views/meetings/_form.html.erb
invoke test_unit
# controller test
create test/controllers/meetings_controller_test.rb
invoke helper
create app/helpers/meetings_helper.rb
invoke test_unit
create test/helpers/meetings_helper_test.rb
invoke jbuilder
create app/views/meetings/index.json.jbuilder
create app/views/meetings/show.json.jbuilder
invoke assets
# Javascript
invoke coffee
create app/assets/javascripts/meetings.js.coffee
# CSS
invoke scss
create app/assets/stylesheets/meetings.css.scss
invoke scss
create app/assets/stylesheets/scaffolds.css.scss
###Migrate the database Now run:
rake db:migrate
this will tell your database you want to create a table for the new model you are creating.
To see what the migration does look at the migration created by the scaffold located in your db/migrations
directory.
# db/migrate/20140917213533_create_meetings.rb
class CreateMeetings < ActiveRecord::Migration
def change
create_table :meetings do |t|
t.datetime :date
t.timestamps
end
end
end
this creates a table named meeting and gives it a datetime attribute with the name of date.
By default all models have created_at
and updated_at
fields; these are created via t.timestamps
###CRUD The scaffold automatically gives you access to CRUD actions for your new model.
Rails prefers convention over configuration.
One of these convetions is the routes for the CRUD actions
Create - POST /models
Read - GET /models/:id
Update - PATCH /models/:id
Destroy - DELETE /models/:id
you also get an Index page to see all of your meetings (/meetings
).
Let's visit this page via localhost:3000/meetings
scaffolded_meetings_index
from here you can create a new meeting:
scaffolded_new_meeting
Note the URL: http://localhost:3000/meetings/new
Creating a meeting will take you to it's show page where you can edit it or go back (to the index page):
Try refreshing the page. Do you notice anything different?
If we go back to the index page we now see the meeting we created:
At this point our feature of creating a meeting is done, so let's commit.
$ git commit -am "Creates a Meeting model."
we can now merge this feature into our master branch
$ git checkout master
$ git merge create_meeting
and delete the feature branch since we are done with it
$ git branch -d create_meeting
###Changing the Meeting model
Looking at the Meeting index page information about a meeting is quite sparse, maybe we can add a small description about the meeting.
Let's add a new field to the Meetings table.
This is done via migrations; migrations are a way to programtically change the applications database schema.
Before we do this let's checkout a new branch:
$ git checkout -b add_description_to_meeting
Now we can create a migration in a similar way to creating a scaffold via the terminal.
$ rails g migration AddDescriptionToMeetings description:text
this generates the following migration:
class AddDescriptionToMeetings < ActiveRecord::Migration
def change
add_column :meetings, :description, :text
end
end
To run this migration, editing the database schema, run the following from the command line:
$ rake db:migrate
We can check that the model has been updated in the rails console.
In the command line type rails console
(or rails c
) for short to start the rails console.
To check the attributes of the model just type the models name:
Meeting
=> Meeting(id: integer, date: datetime, created_at: datetime, updated_at: datetime, description: text)
If we try to create a new Meeting in the interface (http://localhost:3000/meetings/new) we notice that the description field is not available to be filled out, let's add it.
####Updating the view
If we check app/views/meetings/new.html.erb
we see that it is quite simple:
<h1>New meeting</h1>
<%= render 'form' %>
<%= link_to 'Back', meetings_path %>
```
we don't even see the date field!
This is becasue it is in the form [partial](http://guides.rubyonrails.org/layouts_and_rendering.html#using-partials)
```ruby
<%= render 'form' %>
```
Let's check the partial at `app/views/meetings/_form.html.erb`
```html
<%= form_for(@meeting) do |f| %>
<% if @meeting.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@meeting.errors.count, "error") %> prohibited this meeting from being saved:</h2>
<ul>
<% @meeting.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :date %><br>
<%= f.datetime_select :date %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
```
The first chunk deals with displaying errors:
```html
<% if @meeting.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@meeting.errors.count, "error") %> prohibited this meeting from being saved:</h2>
<ul>
<% @meeting.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
```
If we look at the next section
```html
<div class="field">
<%= f.label :date %><br>
<%= f.datetime_select :date %>
</div>
```
we see `date` which is the other field on the `Meeting` model.
Let's add similar markup for the new description field:
```html
<div class="field">
<%= f.label :description %><br>
<%= f.text_area :description %>
</div>
```
After we refresh the page we now have a text area input box!
**form_with_description_field.png**
#####Strong Parameters
By default Rails uses [strong parameters](https://github.com/rails/strong_parameters).
Strong parameters require you whitelist form fields to be used, protecting you from the scary internet©.
Strong paramerts are set in the Model's controller.
If you look in the `MeetingsController` (`app/controllers/meetings_controller.rb`) you will see the `meetings_params` method:
```ruby
def meeting_params
params.require(:meeting).permit(:date)
end
```
currently we are only permitting `date`.
Let's permit out new `description` attribute:
```ruby
def meeting_params
params.require(:meeting).permit(:date, :description)
end
```
when we create a meeting the `description` field will now be included.
Let's confirm this and create a new meeting with a description field.
When we save we are taken to the show page for the new `Meeting`.
Take note that the only field displayed is the `date` field, not the new `description` field.
We can fix this by updating the `Meeting`'s show page, located at `app/views/meetings/show.html.erb`:
```html
<p id="notice"><%= notice %></p>
<p>
<strong>Date:</strong>
<%= @meeting.date %>
</p>
<%= link_to 'Edit', edit_meeting_path(@meeting) %> |
<%= link_to 'Back', meetings_path %>
```
again, we see the `date` field in the HTML but no `description`.
Similarly to above we can display the `description` field by making similar markup to the `date` markup:
```html
<p>
<strong>Description:</strong>
<%= @meeting.description %>
</p>
```
we now have:
```html
<p id="notice"><%= notice %></p>
<p>
<strong>Date:</strong>
<%= @meeting.date %>
</p>
<p>
<strong>Description:</strong>
<%= @meeting.description %>
</p>
<%= link_to 'Edit', edit_meeting_path(@meeting) %> |
<%= link_to 'Back', meetings_path %>
```
clicking the `Back` button will take us to the `Meeting`s `Index` page.
We see the same problem here - the table only includes the `date` field.
Let's add the `description` field to the `app/views/meetings/index.html.erb` file:
```html
<h1>Listing meetings</h1>
<table>
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @meetings.each do |meeting| %>
<tr>
<td><%= meeting.date %></td>
<td><%= meeting.description %></td>
<td><%= link_to 'Show', meeting %></td>
<td><%= link_to 'Edit', edit_meeting_path(meeting) %></td>
<td><%= link_to 'Destroy', meeting, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
</table>
<br>
<%= link_to 'New Meeting', new_meeting_path %>
```
At this point we have successfully added the description field to the `Meeting` model, let's commit our changes and merge them into master:
```bash
# add all of your changes to staging
$ git add .
# commit the changes
$ git commit -m "Adds description field to Meeting."
# merge the changes into master
$ git checkout master
$ git merge add_description_to_meeting
# delete the feature branch
$ git branch -d add_description_to_meeting
```
##Adding Users
Now that we have meetings we should be able to create user records so we know who atteneded the meetings.
###Creating a User Model
Let's checkout a new branch to begin working on creating a `User` model.
```bash
$ git checkout -b create_user
```
Just like with the `Meeting` model we will use Rais' generators to create a full scaffold for our new `User` model.
We will give our users a name, an email address, and class status.
We will make the class status an [enum](http://edgeapi.rubyonrails.org/classes/ActiveRecord/Enum.html) field.
The enum will be stored as an integer in the database and mapped to the class status in the model.
First we create the scaffold:
```bash
$ rails g scaffold user name:string email:string class_status:integer
```
and migrate the database to add the new model:
```bash
$ rake db:migrate
```
We will then set up the enum in the `User` model (`app/models/user.rb`):
```ruby
class User < ActiveRecord::Base
enum class_statuses: { freshman: 1, sophmore: 2, junior: 3, senior: 4, graduate: 5 }
end
```
we can go into the console and confirm that our statuses enum worked:
```ruby
User.class_statuses
=> {"freshman"=>1, "sophmore"=>2, "junior"=>3, "senior"=>4, "graduate"=>5}
```
###Checking the Views
Like with our `Meeting` scaffold our `User`s already have basic pages built out for them.
Let's check the `index` (http://localhost:3000/users).
Like before we have an empty table and a button to create a new user which takes us to http://localhost:3000/users/new.
As a result of making the `class_status` attribute an enum by default it shows up as a number input, it would look better as a select.
####Making Class Status a Select
To edit the new user form we must edit the `User` form partial (`app/views/users/_form.html.erb`).
This confirms that the `class_status` attribute is being displayed as a number field:
```html
<div class="field">
<%= f.label :class_status %><br>
<%= f.number_field :class_status %>
</div>
```
instead we want to make it a select:
```ruby
<%= f.select :class_status,
options_for_select(
User.class_statuses.map { |status, value| [status.titleize, status] },
selected: @user.class_status
)
%>
```
The `options_for_select` expects a 2D array.
The innter arrays are means to contain the select's option's text to display and value, like so:
```ruby
User.class_statuses.map { |status, value| [status.titleize, status] }
=> [["Freshman", "freshman"], ["Sophmore", "sophmore"], ["Junior", "junior"], ["Senior", "senior"], ["Graduate", "graduate"]]
```
We used `status.titlezie` so the displayed `class_status` will looks nice. Rails accepts the value as the lowercase version.
We also pass the `selected` option to tell it to pre-select the current `class_status` if it exists, this is useful when using the form to edit an existing `User`.
Let's create a new user.
We may now notice when we are taken to the show page that the `class_status` attribute's value shows the value as all lowercase.
Let's make it pretty like we did when we displayed the option in the select in `app/views/users/show.html.erb`:
We will simply change `@user.class_status` to `@user.class_status.titleize`.
We will also have to do the same thing for the index table (`app/views/users/index.html.erb`)
We now have a `User` model; let's commit our changes.
```bash
$ git add .
$ git commit -m "Creates User model."
```
and merge in our changes:
```bash
$ git checkout master
$ git merge create_user
$ git branch -d create_user
```
##Linking Users to Meetings
There is a many to many relationship between Users and Meetings - a user can attend many meetings and a meeting can be attended by many users.
In Rails many to many relationships are handled by [has_many :through](http://guides.rubyonrails.org/association_basics.html#the-has-many-through-association) associations.
The has_many :through relationship adds an additional table in which records will simply include the `id` of the `User` that attended the meeting and the `id` of the meeting that was attended.
We will call this new table `Attendance`.
Let's check out a new branch before we begin
```bash
$ git checkout -b create_attendance
```
###Creating Attendance Model
Instead of creating a scaffold this time we will only generate a new model because this new table will not need to be viewed in the interface, we only need to create the table to link `User`s and `Meeting`s.
Following the Rails convention the foreign keys are the the foriegn model's name joined to the word 'id' by an underscore (`model_id`).
So the foreign key for the `User` model is `user_id` and for `Meeting` is `meeting_id`.
```bash
$ rails g model attender user_id:integer meeting_id:integer
```
and migrate the database
```bash
$ rake db:migrate
```
####Setting up has_many :through
Now we can set up the has_many :through relationship.
The `Attendance` model is the link between `User` and `Meeting` so we will start there (`app/models/attendance.rb`).
The `Attenfance` model `belongs_to` both the `User` and `Meetings`:
```ruby
class Attender < ActiveRecord::Base
belongs_to :user
belongs_to :meeting
end
```
Now in the `User` model we want to let Rails know it `has_many` `Meeting`s. We let it know the relationship will go `through` the `Attendance` model by adding
```ruby
has_many :attendances
has_many :meetings, through: :attendances
```
to the `User` model (`app/models/user.rb`).
Now we will let Rails know the `Meeting` model `has_many` `User`s. We let it know this relationship will go `through` the `Attendance` model by adding
```ruby
has_many :attendances
has_many :users, through: :attendances
```
to the `Meeting` model (`app/models/meeting.rb`)
####Testing the relationship
Let's confirm this relationship in the Rails console.
**Note: If you are not starting a new console you will need to call `reload!` in your console**
If you haven't already create a few `Meeting`s and `User`s in your browser first.
Let's grab a `User`:
```ruby
batman = User.first
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
=> #<User id: 6, name: "Bruce Wayne", email: "bruce@wayneindustries.com", class_status: 5, created_at: "2014-09-22 17:27:48", updated_at: "2014-09-22 17:27:48">
```
and see if they have any meetings:
```ruby
batman.meetings
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1
Meeting Load (0.0ms) SELECT "meetings".* FROM "meetings" INNER JOIN "attendances" ON "meetings"."id" = "attendances"."meeting_id" WHERE "attendances"."user_id" = ? [["user_id", 6]]
=> #<ActiveRecord::Associations::CollectionProxy []>
```
they don' have any meetings but we were able to successfully make the request and looking at the SQL shows it is checking the `Meeting`'s table.
Let's see if we can add a meeting:
```ruby
batman.meetings << Meeting.first
Meeting Load (0.2ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "attenders" ("created_at", "meeting_id", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2014-09-22 18:30:36.399750"], ["meeting_id", 5], ["updated_at", "2014-09-22 18:30:36.399750"], ["user_id", 6]]
(1.1ms) commit transaction
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" INNER JOIN "attenders" ON "meetings"."id" = "attenders"."meeting_id" WHERE "attenders"."user_id" = ? [["user_id", 6]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Meeting id: 5, date: "2014-10-01 17:00:00", created_at: "2014-09-22 18:19:44", updated_at: "2014-09-22 18:19:44", description: "Discuss what we will be dressing up as for Hallowe...">]>
2.1.1 :010 >
batman.meetings.count
(0.2ms) SELECT COUNT(*) FROM "meetings" INNER JOIN "attenders" ON "meetings"."id" = "attenders"."meeting_id" WHERE "attenders"."user_id" = ? [["user_id", 6]]
=> 1
```
and see if we can get it back:
```ruby
batman.meetings
=> #<ActiveRecord::Associations::CollectionProxy [#<Meeting id: 5, date: "2014-10-01 17:00:00", created_at: "2014-09-22 18:19:44", updated_at: "2014-09-22 18:19:44", description: "Discuss what we will be dressing up as for Hallowe...">]>
```
We now have a user that has a meeting!
Since a user should be able to have attended multiple `Meeting`s let's give our user another meeting:
```ruby
batman.meetings << Meeting.last
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" DESC LIMIT 1
(0.0ms) begin transaction
SQL (0.3ms) INSERT INTO "attenders" ("created_at", "meeting_id", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2014-09-22 18:32:44.122913"], ["meeting_id", 7], ["updated_at", "2014-09-22 18:32:44.122913"], ["user_id", 6]]
(1.2ms) commit transaction
=> #<ActiveRecord::Associations::CollectionProxy [#<Meeting id: 5, date: "2014-10-01 17:00:00", created_at: "2014-09-22 18:19:44", updated_at: "2014-09-22 18:19:44", description: "Discuss what we will be dressing up as for Hallowe...">, #<Meeting id: 7, date: "2014-10-31 19:30:00", created_at: "2014-09-22 18:20:47", updated_at: "2014-09-22 18:20:47", description: "Halloween Party!!\r\n">]>
2.1.1 :013 > batman.meetings.count
(0.1ms) SELECT COUNT(*) FROM "meetings" INNER JOIN "attenders" ON "meetings"."id" = "attenders"."meeting_id" WHERE "attenders"."user_id" = ? [["user_id", 6]]
=> 2
```
Let's now also confirm that a `Meeting` has `User`s associated with it:
```ruby
Meeting.first.users
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "attenders" ON "users"."id" = "attenders"."user_id" WHERE "attenders"."meeting_id" = ? [["meeting_id", 5]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 6, name: "Bruce Wayne", email: "bruce@wayneindustrie
Meeting.first.users.count
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
(0.1ms) SELECT COUNT(*) FROM "users" INNER JOIN "attenders" ON "users"."id" = "attenders"."user_id" WHERE "attenders"."meeting_id" = ? [["meeting_id", 5]]
=> 1
```
and try giving a meeting more than one user:
````ruby
Meeting.first.users << User.last
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
User Load (0.1ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
(0.0ms) begin transaction
SQL (0.2ms) INSERT INTO "attenders" ("created_at", "meeting_id", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", "2014-09-22 18:34:29.279353"], ["meeting_id", 5], ["updated_at", "2014-09-22 18:34:29.279353"], ["user_id", 8]]
(2.5ms) commit transaction
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "attenders" ON "users"."id" = "attenders"."user_id" WHERE "attenders"."meeting_id" = ? [["meeting_id", 5]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 6, name: "Bruce Wayne", email: "bruce@wayneindustries.com", class_status: 5, created_at: "2014-09-22 17:27:48", updated_at: "2014-09-22 17:27:48">, #<User id: 8, name: "Oliver Queen", email: "ollie@queenconsolidated ", class_status: 2, created_at: "2014-09-22 18:18:51", updated_at: "2014-09-22 18:18:51">]>
# check the users list
Meeting.first.users
Meeting Load (0.2ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "attenders" ON "users"."id" = "attenders"."user_id" WHERE "attenders"."meeting_id" = ? [["meeting_id", 5]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 6, name: "Bruce Wayne", email: "bruce@wayneindustries.com", class_status: 5, created_at: "2014-09-22 17:27:48", updated_at: "2014-09-22 17:27:48">, #<User id: 8, name: "Oliver Queen", email: "ollie@queenconsolidated ", class_status: 2, created_at: "2014-09-22 18:18:51", updated_at: "2014-09-22 18:18:51">]>
# and user count
Meeting.first.users.count
Meeting Load (0.2ms) SELECT "meetings".* FROM "meetings" ORDER BY "meetings"."id" ASC LIMIT 1
(0.1ms) SELECT COUNT(*) FROM "users" INNER JOIN "attenders" ON "users"."id" = "attenders"."user_id" WHERE "attenders"."meeting_id" = ? [["meeting_id", 5]]
=> 2
```
Since we added `User.last` to our meeting we should confirm that they now have meetings
```ruby
User.last.meetings
User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 1
Meeting Load (0.1ms) SELECT "meetings".* FROM "meetings" INNER JOIN "attenders" ON "meetings"."id" = "attenders"."meeting_id" WHERE "attenders"."user_id" = ? [["user_id", 8]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Meeting id: 5, date: "2014-10-01 17:00:00", created_at: "2014-09-22 18:19:44", updated_at: "2014-09-22 18:19:44", description: "Discuss what we will be dressing up as for Hallowe...">]>
```
which they do!
It looks like our relationships are working properly.
Let's commit the work we have done so far before moving on to displaying the relationship in the interace.
```bash
$ git add .
$ git commit -m "Sets up relationship between Users and Meetings."
```
###Managing the relationship in the interface
Let's use the meetings page to see the list of `User`s attending and add users.
We will need to update the `Meeting` form again (`app/views/meetings/_form.html.erb`).
Let's let a user just check the boxes of user's that attend the meeting.
Rails provides a helper to make this easier, [`collection_check_boxes`](http://api.rubyonrails.org/classes/ActionView/Helpers/FormBuilder.html#method-i-collection_check_boxes)
```html
<div class="field">
<%= f.label :users %>
<%= f.collection_check_boxes :user_ids, User.all, :id, :name %>
</div>
```
You will also need to update the `MeetingsController` to update the allowed parameters:
```ruby
def meeting_params
params.require(:meeting).permit(:date, :description, user_ids: [])
end
```
We can now update and create `Meeting`s to include `User`s.
Let's update the `Meeting`'s show page (`app/views/meetings/show.html.erb`) to include some information about the `User`s in attendance.
We can list out users attending the meeting:
```html
<p>
<strong>Attendees</strong>
<ul>
<% @meeting.users.each do |user| %>
<li>
<%= "#{user.name} <#{user.email}>" %>
</li>
<% end %>
</ul>
</p>
```
We can also break down the attendance by `class_status`:
```html
<p>
<table>
<thead>
<th>Class Status</th>
<th>Attendee Count</th>
</thead>
<tbody>
<% User.class_statuses.each do |status, enum_value| %>
<tr>
<td><%= status.titleize %></td>
<td><%= @meeting.users.where(class_status: enum_value).count %></td>
</tr>
<% end %>
</tbody>
</table>
</p>
```
We can also update the `Meeting`s index page (`app/views/meetings/index.html.erb`), let's add the count for the `User`s that are attending:
First we need to update the headers to include the new column:
```html
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Attendee Count</th>
<th colspan="3"></th>
</tr>
</thead>
```
then update the body to include the count:
```html
<tbody>
<% @meetings.each do |meeting| %>
<tr>
<td><%= meeting.date %></td>
<td><%= meeting.description %></td>
<td><%= meeting.users.count %></td>
<td><%= link_to 'Show', meeting %></td>
<td><%= link_to 'Edit', edit_meeting_path(meeting) %></td>
<td><%= link_to 'Destroy', meeting, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>
<% end %>
</tbody>
```
Now that we can add `User`s to a `Meeting` vai the interface it's a good time to commit our changes.
```bash
$ git commit -am "Allows Users to be added to Meetings via the interface"
$ git checkout master
$ git merge create_attendance
$ git branch -d create_attendance
```
##Adding Bootstrap
We have our application's functionality fleshed out but looks very simple.
Let's checkout a new branch to work on styling
```bash
$ git checkout -b add_styling
```
Let's try adding [Bootstrap](http://getbootstrap.com/), a HTML, CSS, and JS framework.
First we will add the [bootstrap-sass](https://github.com/twbs/bootstrap-sass) gem to our `Gemfile`:
```ruby
gem 'bootstrap-sass'
```
and bundle
```bash
$ bundle
```
Following the instruction from the Gem's README we will need to update `app/assets/stylesheets/applicaion.css`
First we need to add the `scss` extension:
```bash
$ git mv app/assets/stylesheets/applicaion.css app/assets/stylesheets/applicaion.css.scss
```
then add
```css
@import "bootstrap-sprockets";
@import "bootstrap";
```
to the end.
We will then need to update `app/assets/javascripts/application.js` and add ``//= require bootstrap-sprockets` before `//= require_tree .`
We also will want to remove the `scaffold.css.scss` to not overwrite any changes from Bootstrap.
```bash
$ git rm app/assets/stylesheets/scaffolds.css.scss
```
we will then need to require Bootstrap Javascripts in `app/assets/javascripts/application.js`:
```javascript
//= require jquery
//= require bootstrap-sprockets
```
and restart the server.
We will notice some small changes, mostly the typography and spacing.
Let's commit these changes before we work on restyling existing pages.
```bash
$ git add -A .
$ git commit -m "Adds bootstrap."
```
###Stylize the Meeting Index
The following changes will be occurring in `app/views/meetings/index.html.erb`.
First, let's restyle the table.
All we need to do is add a `class` of `table`:
```html
<table class='table'>
```
we can also change the "New Meeting" link into a button by adding the `class` of `btn` and `btn-primary, becoming:
```ruby
<%= link_to 'New Meeting', new_meeting_path, class: 'btn btn-primary' %>
```
Let's commit these changes:
```bash
$ git commit -am "Styles Meeting index"
```
###Stylize the Meeding Show Page
The following changes will be occurring in `app/views/meetings/show.html.erb`
TODO:
* Deployment
* https://toolbelt.heroku.com/osx
* https://devcenter.heroku.com/articles/git
* Better bootstrapping
* Update pushing to GitHub section