No Scaffolds Needed
Sure We All Love Scaffolding...
It gives us all that great stuff in just one command:
- Model
- Controller
- Views
- Routes
- Stylesheet
- JavaScript File
- Helper
- Mailer
But what if we don't care about all that?
And what if we don't want a whole new set of files for each Database?
...
We only really need one of most of these (e.g., CSS & JS files).
Can't Leave It All Behind
That's not to say we won't end up with doubles.
We will need at least one controller, which is packaged with all those extras (helper, css, etc.).
But even with multiple databases, we'll be able to work with a single controller, and thus have no need bulk up on files that scaffolding each database would grant us.
Taking Stock
Let's take stock of what we will need to create:
A MODEL, which is synonymous with the Database.
ATTRIBUTES (columns) for the Database.
A CONTROLLER,
and least one VIEW.
Appropriate ROUTES for the View(s).
I'm Big Into Modelling
Assuming you're in a new project, or just any project that has no pending problems...
Let's generate a Model, which will create both the Database and your resource.rb file in the models folder.
$ rails g model Country name:string capital:string population:integer
That sure looks familiar -- just like when we Scaffold!
So, just like when we Scaffold, we need to follow up with...
$ rake db:migrate
So now all we have is a lonely Model and its Database.
But we have no way of sharing with the world what's in that DB.
Heck, we don't even have a way to enter data.
I Ran RAILS G MODEL
and All I Got Was...
Okay, there's RAILS CONSOLE. But besides that...
Sorry To Be So Controlling
Let's create a CONTROLLER, with one VIEW.
$ rails g controller Geobase sweethome
Now we've got a controller with an action,
a view, and a route!
Rails.application.routes.draw do
get 'geobase/sweethome'
end
routes.rb
But we'll be re-visiting the Routes file in a bit.
Let's Go Viewing
On this single View page, I want to be able to:
- Create a new Country
- View All Existing Countries
So we'll need a form, and a loop through a collection of all countries.
geobase/sweethome.html.erb
<h1>Welcome to GEOBASE!</h1>
<div>
<h3>Create a New Country</h3>
<%= form_tag('/homestretch/homebase') do %>
<strong>Name:</strong> <%= text_field_tag :country_name %><br />
<strong>Capital:</strong> <%= text_field_tag :capital %><br />
<strong>Population:</strong> <%= text_field_tag :population %><br />
<%= submit_tag "Create!" %>
<% end %>
</div>
<div>
<h3>Listing All Countries</h3>
<% @countries.each do |country| %>
<p>
<%= country.name %> • <%= country.capital %> • <%= country.population %>
</p>
<% end %>
</div>
Two Routes to the Same View
Notice how the Form points right back at the current page.
That makes 'geobase/sweethome' both a GET page,
and a POST page.
Let's update routes.rb to reflect that:
Rails.application.routes.draw do
# might as well set the page as our root
# and that will act as our "GET" route
root 'homestretch#homebase'
post 'homestretch/homebase' => 'homestretch#homebase'
end
Get It Under Control
Now we need to put some power behind that View. That means: It's Controller Time!
geobase_controller.rb
class HomestretchController < ApplicationController
def sweethome
# let's lay it out like we have the view page
# first we need space to create the new Country
# using the parameters passed through the form
country = Country.new
country.name = params[:country_name]
country.capital = params[:capital]
country.population = params[:population]
country.save
# then we'll collect all the Countries
# in an instance variable
@countries = Country.all
end
end
Not Quite In Control
We are going to run into a problem here, though.
As we have it now, every time we come to the page, a new country will be created, whether we entered data or not.
Let's wrap the Country creation code is an IF statement, so it only creates a country if data has been submitted.
if params[:country_name] != nil
country = Country.new
country.name = params[:country_name]
country.capital = params[:capital]
country.population = params[:population]
country.save
end
# a new country will only be created if
# the bare minimum of data (a name, in this
# case) is provided.
Within geobase_controller.rb, sweethome action:
Check It Out!
Twice the Models
Let's up the ante...
Why not create a second Database,
but use the same Controller and View to create new entries, and to display them?
So it's not just a hodgepodge of data, let's make the second resource something that's associated with the first...
$ rails g model City name:string country_id:integer
$ rake db:migrate
Association Time
Let's get those association definition done:
country.rb
city.rb
class Country < ActiveRecord::Base
has_many :cities
end
class City < ActiveRecord::Base
belongs_to :country
end
Add to the View
In the view, we can create a new Form by copying the old one and changing the parameters passed.
<!-- Create a New City -->
<div>
<h3>Create a New City</h3>
<%= form_tag('/homestretch/homebase') do %>
<strong>Name:</strong> <%= text_field_tag :city_name %><br />
<strong>Country:</strong> <%= select_tag :country_id,
options_for_select(@countries.collect {|country| [ country.name,
country.id ] }) %><br />
<%= submit_tag "Create!" %>
<% end %>
</div>
One note:
There are some line breaks in the that select_tag that you'll need to get ride of if you're copy-n-pasting
Add to the View, Too
To display the cities, let's pull them through the associated Countries, and display as an unordered list below each country.
<div>
<h3>Listing Countries</h3>
<% @countries.each do |country| %>
<p>
<%= country.name %> •
<%= country.capital %> •
<%= country.population %><br />
<ul>
<% country.cities.each do |city| %>
<li><%= city.name %></li>
<% end %>
</ul>
</p>
<% end %>
</div>
More In Control
Now let's add the creation of a new city to the controller. We can just copy the country creation code, and change the variables.
Within geobase_controller.rb, sweethome action:
# the country creation code is above here
# we'll again use an IF statement to
# prevent an empty city being created
# each time we visit the page (or when
# no city data has been entered)
if params[:city_name] != nil
city = City.new
city.name = params[:city_name]
city.country_id = params[:country_id]
city.save
end
# the line collecting all Countries is below
Let's Take a Look...
A Parting Challenge...
The Capital of a Country is also a City within it, so why shouldn't it have a place in the Cities database?
Can you think of a way to add the Capital to the Cities database upon creation of the new Country?
No Scaffolds Needed
By argroch
No Scaffolds Needed
Creating and interacting with a database without calling the 'rails g scaffold' command.
- 1,350