Skip to content

Instantly share code, notes, and snippets.

@tmr08c
Last active August 29, 2015 14:06
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tmr08c/102cacf255d09e2122ce to your computer and use it in GitHub Desktop.
Save tmr08c/102cacf255d09e2122ce to your computer and use it in GitHub Desktop.
Writeup for the live coding section of my talk for ACM@UCF (Fall 2014).

#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&copy;.

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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment