Project (Part 1): Controllers and Models


The remainder of the Rails section of the bootcamp will be a task for you to implement a small project. It will be split into two parts, each covering various concepts that will be relevant to you for your Blueprint project. For the first part, you will be starting without starter code, so you can work with just the fundamentals and use Rails's command line resources for yourself. In the next part, we will be providing starter code so that everyone begins with the same (working) code.

Unlike most tutorials, such as the official Rails walkthrough you just completed, there will not be a lot of hand-holding. A lot of this part is just working with the command line, so it's important for you to reference documentation or the walkthrough in case you don't know how to proceed. This part of the spec contains a lot of information, so don't worry if you're feeling overwhelmed--the whole implementation itself will take you fewer than 100 lines of non-generated code. The brevity that Rails lends you will be sometimes your greatest friend, and other times can be your biggest frustration. Don't hesitate to ask around for help if you need it!

Assignment

In this project, you will be implementing the back-end for a "Magic Mirror", a software implementation of the Weasley family's grandfather clock.

For those who have not seen the Harry Potter movies. The magical grandfather clock has hands for every family member in the house. Alongside the perimeter of the clock are various categories of places people might be, like "Home", "Lost", and "Forest". The hands will always rotate to the appropriate category depending on where the current location of its associated family member (let's hope it's never "Mortal Peril").

The back end is responsible for storing the data for all existing magical clocks, as well as the necessary information of all family members belonging to each clock. We'll be referring to these magical clocks as Mirrors from now on (you'll see why), and the people belonging to each clock as Members. This was adapted from a personal project in which I used a Raspberry Pi to connect the display to a screen that projects through a one-way mirror, hence why we will refer to the clocks as Mirrors. If you're interested in "finishing off" this project later in your free time or doing something similar, let me know and I'd be happy to help you.

For the purposes of the Rails back end, it solely handles storing and updating Mirror information according to how Member activities change. A basic overview of the API you will implement is as follows.

API Design

ACTION URI ENDPOINT DESCRIPTION
GET / Shows a list of all mirrors.
GET /mirrors Shows a list of all mirrors.
POST /mirrors Creates a new mirror with codename.
PUT /mirrors/:id Updates an existing mirror's codename.
DELETE /mirrors/:id Destroys an existing mirror and all of its members.
GET /mirrors/:id Shows mirror with specified id.
GET /mirrors/:mirror_id/members Show all members belonging to specified mirror.
POST /mirrors/:mirror_id/members Create a new member and assign it to this mirror.
GET /mirrors/:id/activities Get activities for all members belonging to this mirror.

Some actions are omitted in this spec to avoid having you do repetitive things, and they'll be put in for you in the second part of this project.

Controllers

You will be implementing controller actions to handle each of the endpoints described above. Everything except for the last endpoint (GET /mirrors/:id/activities) is part of Rails' standard RESTful actions belonging to a REST resource. If you're not sure what action should handle which endpoint, refer to the previous section.

Some tips

  • Don't forget to use strong parameters! You will get an error otherwise.
  • An object will not be available for you to render in the view if you haven't set it as an instance variable in the controller (like @mirror).

Models

Mirror

For now, the mirror model only needs to keep track of a codename, which is a unique string identifier of the particular mirror. Each mirror can own many members.

Member

The member model belongs to a single mirror. It should have a first_name and last_name. It should also have an activity, which is an enum (which is represented in Rails as an integer, defaulted to 0) precisely defined as the following:

# member.rb
class Member < ApplicationRecord

  enum activity: [
    :home,
    :work,
    :school,
    :groceries,
    :shopping,
    :partying,
    :adventure,
    :unknown,
    :eating,
    :mortal_peril,
  ]

  ### OTHER STUFF BY YOU ###

end

Note that you are unable to add the activity field by command-line model generation (rails g model [...]) because it requires a default field, so try using a migration to do this instead.

Some tips

  • Don't forget to add a references column to the members table that references a mirror_id! In database terms, this is called a foreign key, which uniquely identifies the mirror to which the particular member is related.

Views

This project will have some extremely basic views that you will implement in embedded ruby (ERB). This is the built-in templating engine, also the one that you used in the Rails tutorial. For every GET route (except GET /mirrors/:id/activities), you will only need to render a basic view that displays just the minimum information necessary to show that you completed the feature successfully. The following is sufficient:

  • GET /mirrors: show a list of mirror codenames
  • GET /mirrors/:id: show that mirror's codename
  • GET /mirrors/:mirror_id/members: show the first name and last name of all members belonging to this mirror

We will do something slightly different with the GET /mirrors/:id/activities endpoint. Instead of rendering an ERB template, the controller action that handles this endpoint will instead render JSON of all members' first names mapped to their current activities. You will need to use an explicit render function (see "Rendering JSON" section here), which is different from the previous three cases in which Rails looked for a template like index.html.erb. You do not need a template here. The response should look like this:

{
  member1: "work", 
  member2: "home", 
  member3: "mortal_peril"
}

Getting Started

We will not be providing starter code for the first part of this project, but you will need to follow this procedure for checking in your files once you've completed. First, create a new branch called part_two. You need to create a new Rails project called rails_part_two in the ruby_rails directory:

cd ruby_rails
git checkout -b part_one

# Create your rails project now, called rails-part-one

As you're reviewing the spec, are some basic steps that you may take to completing the required features. You DO NOT have to follow this particular sequence, as there are many ways to get to a functional end result.

  • Create the Rails project

  • Create a resource route for Mirror

  • Create a controller for Mirror

  • Add appropriate Mirror controller methods

  • Create the Mirror model and run migrations

  • Add basic views for indexing and showing Mirrors

  • Create the Member model and associate with Mirror. Add resource route. Run migrations

  • Create the appropriate Member controller methods

  • Add basic views for indexing Members belonging to a Mirror

  • Test and submit!

Testing

Please test that your implementation works before submitting. We will be looking through individual submissions and all of your code to give feedback, but this is a good opportunity for you to learn how to test the applications that you will eventually build.

Seeding

Seeding is the process of setting up your database with some default data. We will be using this throughout the development process so we always have some objects to work with.

Go ahead and locate the file db/seeds.rb, and paste this as the body of that file:

def create_mirrors
  Mirror.create codename: 'blueprint'
end

def create_members
  5.times do |i|
    Member.create activity: i,
                  first_name: "first#{i}",
                  last_name: "last#{i}",
                  mirror_id: 1
  end
end

create_mirrors
create_members

This will create a single mirror with codename "blueprint" and five members belonging to that mirror with various activities. In the next section we will learn how to modify and delete these objects, as well as create new ones.

Postman

Postman is an API development suite that you can use to test your application API. It supports a ton of features including automated testing and mock servers, but we will be primarily using it to send various HTTP requests to our server and checking the response. You can download the app here, or the chrome extension here.

In the main URL bar, go ahead and enter http://localhost:3000 (or whatever port your server is running on), and you should see the root page of your application!

Here is an example of how you could test sending a POST request to your application to create a new mirror. There are a couple of small details that you need to include or you will encounter errors, so read on carefully.

We want to target the POST /mirrors endpoint in order to create a new mirror. After you've configured the top URL bar, you will need to go to the "Headers" tab and add a "Content-Type" header. Set it to "application/json", which tells the server that your request body is JSON data (otherwise it will try to parse it as a string, leading to errors).

Then, go to the "Body" tab to fill in the request object that you wish to send. Make sure you use the raw data format so you can enter the JSON object properly. You also need to tag the JSON (application/json) data type, as seen in the orange text above the input field (oddly, you need both this AND the Content-Type header we set before). Rails expects a top level "mirror" with fields below it, as such:

Try sending the request. If you run into an authorization error, that is Rails's built-in CSRF protection. You can learn more about CSRF protection here. We will disable CSRF protection for testing our API, but be aware that you shouldn't disable it for your production app unless you know what you're doing. In order to disable protection for a particular controller, you can add a line like this at the top:

class MembersController < ApplicationController

  skip_before_action :verify_authenticity_token

  ### THE REST OF YOUR CODE ###

end

Play around and send a few different requests to your application according to the API design we provided above, testing GET and POST, as well as DELETE routes. Once you're pretty sure that everything works as intended, proceed to the following section to verify your API.

Running Tests

We have provided a set of minimal tests to check the correctness of your implementation. Make sure that your application is running on localhost:3000, and then run the following command while in the rails curriculum root directory: insert command line script here.

If the test passes, continue on to submit your code!

How To Submit

Run the following commands to check in your code to GitHub.

git status  # Check that everything is okay

git add -A
git commit -m "Your commit message."
git push origin part_one

results matching ""

    No results matching ""