- The Basics
- Outline:
- Learn to create a new Rails project to keep track of your Starship Fleet. (You have a Fleet, right?)
- Outline:
- Some good, clean, DRY code
- Coming next time...
- Making our Tracker actually useful
- Coming next time...
- Layouts, Mailers, and Looking good while we work
- Coming soon...
-
-
Save skplunkerin/7185fc7291f4fc51787386f0af0d7e5c to your computer and use it in GitHub Desktop.
Have you ever wanted to keep track of your Starship Fleet, but just can't trust to use the available Starship Fleet Tracker? Mercenary, you're not alone... for all we know the Fleet Trackers out there are controlled by the Empire, the Hutts, or even worse... Microsoft! Let's trust the best source we can, ourselves. Besides, we're mercs... if we can't handle this job we might as well just pack our bags and go work back home at the moisture farm, which... we know we don't want that right? Yeah... didn't think so, so let's hop to it!
I'm not going to get your merc environment setup for you, but if you need help check out my scribbles here.
# Create Project (and cd into project)
$ rails new starship_fleet
# Start the Rails server
$ rails s
# visit your new site: http://127.0.0.1:3000
Note: Controller names are typically "plural". In this case (and usually for a home page), we skip that "standard" and go for a "singular" named controller instead. Checkout Step 8 for the plural rule in action.
g
= generate
, and can be used interchangeably. (i.e. rails generate controller welcome
)
$ rails g controller welcome
Edit file: config/routes.rb
and add the following to line 2.
root 'welcome#index'
Now... reloading your webpage will show a big fat Unknown action error, let's fix that.
Edit file: app/controllers/welcome_controller.rb
to look like the following:
class WelcomeController < ApplicationController
def index
end
end
HA! Take that ~insert derogatory name here`! I've never been much for those bugs... Now reload your page again. :) Well well well... these bugs are just asking for it, so let's give it to 'em!
Create file: app/views/welcome/index.html.erb
with the following content.
<h1>Howdy Partner...</h1>
<p>
It's been awhile, hasn't it... You're looking good! Let's get our Starship Fleet taken care of, it's about time you actually do some work around here.
</p>
Alright, now load that page again! Hey! Looks like we're not alone. Good thing too, we've got alot of work up ahead of us. Come on partner, let's get to it.
Edit file: app/views/welcome/index.html.erb
to look something like the following.
<h1>Starship Fleet Tracker</h1>
<p>
It's about time we start tracking our Fleet... all those Starships are just piling up aren't they? Let's make sure this memory of mine doesn't go bad
on us before this is done.
</p>
Ok, here's where things get fun! Let's get our Model under way... think of it like our "Hangar" that holds all of our Starships... OH! JUST LIKE DATABASE! Yeah, think of it like that! Like, the door to a database of information... yeahhhh... I'm liking where this is going.
NOTE: The rule of thumb for creating a Model is to use "singular" names. Yeah, Controller names should be "plural", and Model names "singular"... don't ask questions!
$ rails g model ship
This created two important things for us:
- A Model file (
app/models/ship.rb
) which will be used to specify relationships, data requirements, and any kind of logic for our data that we need. - A Migration file (
db/migrate/yyyymmdd######_create_ships.rb
) where we will specify the Database table being created, the available columns, and the column data types.
Let's udpate file: db/migrate/yyyymmdd######_create_ships.rb
to have the following contents:
class CreateShips < ActiveRecord::Migration
def change
create_table :ships do |t|
t.string :name
t.timestamps null: false
end
end
end
Wipe that stupid grin off your face, we're not done yet! All we did was write down a quick blueprint on a napkin, let's actually build something now.
$ rake db:migrate
By default, a new Rails project comes equipped with a basic SQLite Database. For now, we'll stick with this implementation but be aware that if you're wanting to use MySQL, Postgres, or any other Database solution there's more steps involved (i.e., install ruby gems, setup your Database Solution, update the projects database configuration, etc).
Alright... who like's save points? Heck, I sure do! We're mercs right?! Last thing I want to do is get myself mixed up with some bad decisions without being able to revert back to my last good decision... not that that ever happens to us mercs, we're professionals! (but just in case, let's cover our tracks!)
$ git init
$ git add --all
$ git config user.email [email protected]
$ git commit -m "- Initialize project with home page and Ship model/migration"
I know we're friends, so I'll tell you this once... I'm not your mom! Make sure you keep your save points recent and up-to-date cause I'm sure as hell aen't going to remind ya... again!
It doesn't look like it, but our Database is pipe'n hot! Let's create a Controller so we can start using it already.
$ rails g controller ships index show new create edit update destroy
Wait, what is this after ships
? Well, I'll let you in on a little secret... never buy a droid with a bad motivator, NEVER! Anyways, let's see how our new file: app/controllers/ships_controller.rb
is looking:
class ShipsController < ApplicationController
def index
end
def show
end
def new
end
def create
end
def edit
end
def update
end
def destroy
end
end
Alright, looks like all of the functions we'll need are created for us. Let's see if we need to create our corresponding Views.
Check the files under app/views/ships/
, does it look anything like the following?
ships/
create.html.erb
edit.html.erb
destroy.html.erb
index.html.erb
new.html.erb
show.html.erb
update.html.erb
If so, you're doing it all wrong! Let's get rid of what we're not going to use. Delete create.html.erb
, destroy.html.erb
, and update.html.erb
until we look like:
ships/
edit.html.erb
index.html.erb
new.html.erb
show.html.erb
Good job! I knew you had hears, you might be useful yet!
RESTful architecture:
- GET
- POST
- PATCH / PUT
- DELETE
You might not know it yet, but the Controller methods we setup have put us in a good spot to use the RESTful architecture.
Remember, having a Controller means nothing unless we check out file: config/routes.rb
... looks like it's gotten all messed up, does yours look like this?
Rails.application.routes.draw do
get 'ships/index'
get 'ships/show'
get 'ships/new'
get 'ships/create'
get 'ships/edit'
get 'ships/update'
get 'ships/destroy'
root 'welcome#index'
[...file abbreviated...]
Crap! Let's clean this mess up... what did I tell you about droids with bad motivators, huh? Always messing things up...
Rails.application.routes.draw do
root 'welcome#index'
resources :ships
[...file abbreviated...]
Well neat... now we're looking pretty nice, but how do we even load our new pages? Let's check what Routes we have available:
$ rake routes
See anything, familiar...?
Prefix Verb URI Pattern Controller#Action
root GET / welcome#index
ships GET /ships(.:format) ships#index
POST /ships(.:format) ships#create
new_ship GET /ships/new(.:format) ships#new
edit_ship GET /ships/:id/edit(.:format) ships#edit
ship GET /ships/:id(.:format) ships#show
PATCH /ships/:id(.:format) ships#update
PUT /ships/:id(.:format) ships#update
DELETE /ships/:id(.:format) ships#destroy
If this looks right, let's try loading the #index
method by visiting http://127.0.0.1:3000/ships/ and see what happens. Perfect, no bugs here right? Told you you can count on me.
I don't have time to explain it! Just keep up with me, will ya!?
First, let's link to our #new
View.
Edit file: app/views/ships/index.html.erb
to contain:
<h1>Starship Fleet</h1>
<hr />
<%= link_to "Add New Starship to Fleet", new_ship_path %>
<h2>Starship Fleet</h2>
<ul>
<li>Starships will be listed here...</li>
</ul>
Before we go reloading our page and clicking on our new link, how about we actually have a form to add our new Starship.
Edit file: app/views/ships/new.html.erb
to have:
<h1>Add New Starship</h1>
<% if @ship.errors.any? %>
<div class="error_messages">
<h2>Look, if you're going to do the job then do it right the first time!</h2>
<ul>
<% for message in @ship.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for @ship do |f| %>
<div>
<%= f.label :name %><br />
<%= f.text_field :name, placeholder: "X-Wing" %>
</div>
<div>
<%= f.submit "Add" %>
</div>
<% end %>
Before this will work, make sure you update the #new
method inside of file: apps/controllers/ships_controller.rb
to the following:
def new
@ship = Ship.new
end
Now go ahead, reload it and just try adding your new Starship... hahah, blew up didn't it? STOP PUSHING BUTTONS UNTIL WE'RE DONE! Geez... always jumping the gun.
Now, we need to do a couple things in our Ships Controller before we can even use the form we just created...
Strong Parameters are a nice method to keep us a little bit safer from hacks. We want to make sure the data we're expecting is what we're getting, and ignore everything else sent to us.
Add the following to the end of the file: app/views/controllers/ships_controller.rb
:
[...beginning of file abbreviated...]
def destroy
end
private
def ship_params
params.require(:ship).permit(:name)
end
end
This makes sure that the parameters we're expecting POSTed to us from our form are the fields we know should be returned. Anything else is ignored.
Hokay, so here's where we fix the mess you created by pushing the shiny new button. Our #create
method needs some updating.
Edit file: app/views/controllers/ships_controller.rb
, and change the #create
method to the following:
def create
@ship = Ship.new(ship_params)
if @ship.save
flash[:success] = "Starship added!"
redirect_to ships_url
else
flash[:error] = "#{@ship.errors.full_messages.to_sentence}"
render new
end
end
Now, wipe your greasy fingerprint off our button and try it again... is it working? Hmmm, let's make sure we load all Starships onto our Ships#index.
Yeah yeah, I know... just follow my lead below.
Edit file: app/controllers/ships_controller.rb
, let's have the #index
method look like:
def index
@ships = Ship.all
end
Edit file: app/views/ships/index.html.erb
to be:
[...beginning of file abbreviated...]
<h2>Starship Fleet</h2>
<ul>
<% @ships.each do |ship| %>
<li><%= ship.name %></li>
<% end %>
</ul>
Neat huh? Yeah well... we're not done yet!
Now, we don't have much more to look at besides the Starships name... but let's use our #show
method so it doesn't start collecting dust.
Edit file: app/controllers/ships_controller.rb
, have the #show
method match my example below:
def show
@ship = Ship.find(params[:id])
end
Now, edit app/views/ships/index.html.erb
like so:
[...abbreviated...]
<ul>
<% @ships.each do |ship| %>
<li><%= link_to ship.name, ship_path(ship) %></li>
<% end %>
</ul>
And edit app/views/ships/show.html.erb
to:
<h1><%= @ship.name %></h1>
<p>
Someday, we'll be able to see stats for our little <%= @ship.name %>, but that's not free is it?
<br/>
Pay up or shut up!
</p>
Pretty basic stuff here, I'm actually surprised you didn't know this. Whelp... guess that can't be helped much with your lack of experience. Let's keep on going.
Wouldn't it be nice to fix your Starship names typo... I mean, change the name to something cooler? Yeah, let's do just that!
Edit file: app/views/ships/show.html.erb
<h1><%= @ship.name %> <%= link_to "Edit", edit_ship_path(@ship) %></h1>
[...abbrev...]
Update app/controllers/ships_controller.rb
, change the #edit
method to:
def edit
@ship = Ship.find(params[:id])
end
Now, edit app/views/ships/edit.html.erb
to:
<h1>Editing: <%= @ship.name %></h1>
<% if @ship.errors.any? %>
<div class="error_messages">
<h2>Droids with bad motivators mess up less than you!</h2>
<ul>
<% for message in @ship.errors.full_messages %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for @ship do |f| %>
<div>
<%= f.label :name %><br />
<%= f.text_field :name, placeholder: @ship.name %>
</div>
<div>
<%= f.submit "Update" %><br />
<%= link_to "Cancel", ship_path(@ship) %>
</div>
<% end %>
Now, before you try pushing this new button, how about we finish things up huh?
Edit the #update
method in file: app/controllers/ships_controller.rb
to:
def update
@ship = Ship.find(params[:id])
if @ship.update_attributes(ship_params)
flash[:success] = "Starship updated!"
redirect_to ships_url
else
flash[:error] = "#{@ship.errors.full_messages.to_sentence}"
render edit
end
end
Now go ahead an test that puppy out. (and fix your typos while you're at it...)
HAA! Nice try... I'm pretty sure you don't have a Death Star on hand. You must've thought since we couldn't delete false entries that people would just believe whatever they read on the internet... this isn't 2016 anymore! OF COURSE WE CAN DELETE FALSE ENTRIES!
Edit app/controllers/ships_controller.rb
, and get the #destroy
method fleshed out:
def destroy
@ship = Ship.find(params[:id])
if @ship.destroy
flash[:success] = "Starship deleted!"
redirect_to ships_url
else
flash[:error] = "#{@ship.errors.full_messages.to_sentence}"
render edit
end
end
Now update app/views/ships/edit.html.erb
with:
[...abbrev...]
<div>
<%= f.submit "Update" %><br />
<%= link_to "Cancel", ship_path(@ship) %><br />
<%= link_to "Delete", ship_path(@ship), data: {confirm: "Do you really want to delete #{@ship.name}?"}, method: :delete %>
</div>
<% end %>
Now go ahead and remove your falsified Death Star...
https://gist.github.com/skplunkerin/7185fc7291f4fc51787386f0af0d7e5c#6-creating-a-model-for-ship-data
There's a typo. Udpate should be Update. ^_^
Also, should we mention that we can create the controller/view definition when we first generate the controller?