Ruby on Rails Resource Generator: Instant Full-Stack MVC Framework (CRUD not included)

Yong-Nicholas A. Kim
9 min readApr 30, 2018

--

This is the second of four blogs I am writing during my 15-week Software Development bootcamp at Flatiron School, in downtown NYC. For each blog, I will focus on a topic that directly relates to my learning now. The metacognitive benefits to myself are obvious, and I hope they may be useful resources to my peers as well. My first blog was all about setting up my own custom Visual Studio Code environment.

In this blog, I will describe how to instantly set up a very basic full-stack web content management framework using Ruby, Rails, and the resource generator. Amongst the various generators available in Rails, the resource generator occupies a nice sweet spot, offering plenty of prepackaged functionality, but not too much bloat code that you’ll probably never end up using. The generated code adheres to RESTful conventions, as well as Model-View-Controller (MVC) patterns. Surprisingly, googling ‘Ruby Rails resource generator’ brings up very little information. It seems that everybody prefers the scaffold generator, and for good reason. It provides way more prepackaged functionality, but at the cost of forcing you to do things in a very pre-scripted way. Sometimes, it’s useful, but sometimes not, especially if it overwhelms your needs. One tutorial I recently read refers to this phenomena as ‘[using] a chainsaw to build a model airplane’ (Flatiron School Web Development Immersive).

Double Happiness

For my project I will be building out a basic has many — belongs to — has many relationship between three Ruby classes for a donation website called ‘Double Happiness’. The 3 classes are: Giver, Getter, and Thing. The database tables will look like this:

table “givers”
string “name”
string “location”

table “getters”
string “name”
string “location”

table “things”
string “name”
integer “point_value”
integer “artist_id”
integer “genre_id”

And that’s basically all you need to get started. First, I cd to a target folder in my terminal, and create a new Rails project.

rails new double-happiness

Resource Generator

And voila, lots of folders and useful files, most of which I don’t understand, yet. Next, I cd into the newly created Rails project directory, and generate resources for my three classes (g is short for generate). For my sample project, I’m using a no-test-framework, meaning it won’t create any tests for the newly-generated models, controllers, etc. That’s fine for now, I think.

rails g resource Giver name:string location:string — no-test-frameworkrails g resource Getter name:string location:string — no-test-frameworkrails g resource Thing name:string point_value:integer getter_id:integer giver_id:integer — no-test-framework

For each of my classes, I get:

invoke active_record
create db/migrate/20180430015713_create_givers.rb
create app/models/giver.rb
invoke controller
create app/controllers/givers_controller.rb
invoke erb
create app/views/givers
invoke helper
create app/helpers/givers_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/givers.coffee
invoke scss
create app/assets/stylesheets/givers.scss
invoke resource_route
route resources :givers
invoke active_record
create db/migrate/20180430015726_create_getters.rb
create app/models/getter.rb
invoke controller
create app/controllers/getters_controller.rb
invoke erb
create app/views/getters
invoke helper
create app/helpers/getters_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/getters.coffee
invoke scss
create app/assets/stylesheets/getters.scss
invoke resource_route
route resources :getters
invoke active_record
create db/migrate/20180430015738_create_things.rb
create app/models/thing.rb
invoke controller
create app/controllers/things_controller.rb
invoke erb
create app/views/things
invoke helper
create app/helpers/things_helper.rb
invoke assets
invoke coffee
create app/assets/javascripts/things.coffee
invoke scss
create app/assets/stylesheets/things.scss
invoke resource_route
route resources :things

Wow, that’s a lot of stuff!

That’s a lot of code that Rails just generated for us. For a newbie like me, the ones that jump out are:

  • migration files that will create new database tables for the attributes passed to it in the generator
  • model files that inherit from ActiveRecord::Base
  • controller files that inherit from ApplicationController
  • view directories, but no view template files
  • view helper files
  • Coffeescript files for specific JavaScripts for that controller
  • scss files for the styles for the controller
  • full resources call in the routes.rb file

Migrations

Let’s take a look at the migration files before we run our migrations.

class CreateGivers < ActiveRecord::Migration[5.1]
def change
create_table :givers do |t|
t.string :name
t.string :location
t.timestamps
end
end
end
class CreateGetters < ActiveRecord::Migration[5.1]
def change
create_table :getters do |t|
t.string :name
t.string :location
t.timestamps
end
end
end
class CreateThings < ActiveRecord::Migration[5.1]
def change
create_table :things do |t|
t.string :name
t.integer :point_value
t.integer :getter_id
t.integer :giver_id
t.timestamps
end
end
end

Everything looks perfect. Next, we run our migrations rake db:migrate, and the terminal tells us that our migrations appear to be successful.

== 20180430015713 CreateGivers: migrating =====================================
— create_table(:givers)
-> 0.0031s
== 20180430015713 CreateGivers: migrated (0.0033s) ============================
== 20180430015726 CreateGetters: migrating ====================================
— create_table(:getters)
-> 0.0017s
== 20180430015726 CreateGetters: migrated (0.0018s) ===========================
== 20180430015738 CreateThings: migrating =====================================
— create_table(:things)
-> 0.0013s
== 20180430015738 CreateThings: migrated (0.0014s) ============================

Just to be sure, we should look at our schema.

ActiveRecord::Schema.define(version: 20180430015738) do  create_table “getters”, force: :cascade do |t|
t.string “name”
t.string “location”
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end
create_table “givers”, force: :cascade do |t|
t.string “name”
t.string “location”
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end
create_table “things”, force: :cascade do |t|
t.string “name”
t.integer “point_value”
t.integer “getter_id”
t.integer “giver_id”
t.datetime “created_at”, null: false
t.datetime “updated_at”, null: false
end
end

We can confirm that the tables have been created exactly as we wanted.

Class Associations

Next we need to define the associations between our classes:

  • A giver has many things
  • An getter has many things
  • A thing belongs to a giver
  • A thing belongs to a getter

Inside the app/models folder, we find the class files.

class Giver < ApplicationRecord
end

We change this to:

class Giver < ApplicationRecord
has_many :things
end

and

class Getter < ApplicationRecord
end

to

class Getter < ApplicationRecord
has_many :things
end

and finally

class Thing < ApplicationRecord
end

to

class Thing < ApplicationRecord
belongs_to :giver
belongs_to :getter
end

This will allow the Thing class to act as a join class between the Giver and Getter classes.

Controllers

All that’s left to do at this point is to define some actions in the respective controllers, and insert some basic HTML in our views folders. Many people may see the lack of any out-of-the-box controller functions as one of the weaknesses of the resource generator. On the other hand, it gives you, the programmer, total control over how you want to define your actions. Here, I am including all of the basic CRUD functionality, except delete, which I haven’t learned how to implement yet.

class GiversController < ApplicationController  def index
@givers = all_givers
end
def show
@giver = find_giver_id
end
def new
@giver = Giver.new
end
def create
@giver = Giver.new(post_params(:name, :location))
@giver.save
redirect_to giver_path(@giver)
end
def edit
@giver = find_giver_id
end
def update
@giver = find_giver_id
@giver.update(post_params(:name, :location))
redirect_to giver_path(@giver)
end

private
def all_givers
Giver.all
end
def find_giver_id
Giver.find(params[:id])
end
def post_params(*args)
params.require(:giver).permit(*args)
end
end

You can see that the other classes look pretty similar.

class GettersController < ApplicationController  def index
@getters = all_getters
end
def show
@getter = find_getter_id
end
def new
@getter = Getter.new
end
def create
@getter = Getter.new(post_params(:name, :location))
@getter.save
redirect_to getter_path(@getter)
end
def edit
@getter = find_getter_id
end
def update
@getter = find_getter_id
@getter.update(post_params(:name, :location))
redirect_to getter_path(@getter)
end

private
def all_getters
Getter.all
end
def find_getter_id
Getter.find(params[:id])
end
def post_params(*args)
params.require(:getter).permit(*args)
end
end

Similarly, for the Thing class. However, as you can see in the show method below, as the join class, Thing has access to theGiver and Getter classes, as well as their attributes, name and location, which we’ll access in one of our views later.

class ThingsController < ApplicationController  def index
@things = all_things
end
def show
@thing = find_thing_id
@giver = @thing.giver
@getter = @thing.getter
end
def new
@thing = Thing.new
end
def create
@thing = Thing.new(post_params(:name, :point_value, :giver_id,
:getter_id))
@thing.save
redirect_to thing_path(@thing)
end
def edit
@thing = find_thing_id
end
def update
@thing = find_thing_id
@thing.update(post_params(:name, :point_value, :giver_id,
:getter_id))
redirect_to thing_path(@thing)
end

private
def all_things
Thing.all
end
def find_thing_id
Thing.find(params[:id])
end
def post_params(*args)
params.require(:thing).permit(*args)
end
end

Views

The last thing to do is to build out our views files. As you can see below, the resource generator was smart enough to give us some appropriately named folders, but we need to create html.erb files (embedded Ruby) so our actions can render or redirect, as needed.

At their most basic, each folder will need index, show, new, and edit files.

<!-- givers/index.html.erb -->
<% @givers.each do |giver| %>
<%= link_to giver.name, giver_path(giver) %>
<% end %>
<!-- givers/show.html.erb -->
<%= link_to @giver.name, edit_giver_path(@giver) %></p>
<%= @giver.location %></p>
<!-- givers/new.html.erb -->
<%= form_for(@giver) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.submit %>
<% end %>
<!-- givers/edit.html.erb -->
<%= form_for(@giver) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.submit %>
<% end %>

Similarly.

<!-- getters/index.html.erb -->
<% @getters.each do |getter| %>
<%= link_to getter.name, getter_path(getter) %>
<% end %>
<!-- getters/show.html.erb -->
<%= link_to @getter.name, edit_getter_path(@getter) %></p>
<%= @getter.location %></p>
<!-- getters/new.html.erb -->
<%= form_for(@getter) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.submit %>
<% end %>
<!-- getters/edit.html.erb -->
<%= form_for(@getter) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :location %>
<%= f.text_field :location %>
<%= f.submit %>
<% end %>

And in the code for the show.html.erb file below, we can see that the Thing class has access to data in the adjoining Giver and Getter classes, in this case, the #name method. Wow!

<!-- things/index.html.erb -->
<% @things.each do |thing| %>
<div><%= link_to thing.name, thing_path(thing) %></div>
<% end %>
<!-- things/show.html.erb -->
<%= link_to @thing.name, thing_path(@thing) %>
<%= link_to @getter.name, getter_path(@getter) if @getter %>
<%= link_to @giver.name, giver_path(@giver) if @giver %>
<!-- things/new.html.erb -->
<%= form_for(@thing) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :point_value %>
<%= f.text_field :point_value %>
<%= f.label :giver_id %>
<%= f.text_field :giver_id %>
<%= f.label :getter_id %>
<%= f.text_field :getter_id %>
<%= f.submit %>
<% end %>
<!-- things/edit.html.erb -->
<%= form_for(@thing) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :point_value %>
<%= f.text_field :point_value %>
<%= f.label :giver_id %>
<%= f.text_field :giver_id %>
<%= f.label :getter_id %>
<%= f.text_field :getter_id %>
<%= f.submit %>
<% end %>

Testing It Out

Now that all of our essential files are built out, we can make sure everything is working properly. First, we start our application by entering rails s in our terminal. You should see something like this:

=> Booting Puma
=> Rails 5.1.6 application starting in development
=> Run `rails server -h` for more startup options
Puma starting in single mode…
* Version 3.11.4 (ruby 2.3.3-p222), codename: Love Song
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://0.0.0.0:3000
Use Ctrl-C to stop

This tells us that our application is running. We can open our browser, and enter the URI for our website: http://localhost:3000. We can navigate to our /givers, /getters, and /things routes to see their respective index pages. But all I see is blank webpages! That’s okay because we don’t have any data to display yet. Navigating over to one of our /new pages that we created earlier, we can enter and submit data into the form. And it works! Similarly, entering data into our other forms, we can make sure our website is behaving as we intended. Playing around with our new creation, we can begin plotting and planning many improvements we would like to make. But, more on that some other time.

The Heart of Ruby Magic

A real functioning website will need plenty more HTML, CSS, and JavaScript to even begin to look usable. But our website’s skeleton is now standing, and there’s data flowing. That flow is managed by the resources macros in our config/routes.rb file. And hence, where this generator gets its name, the resource generator. For the flow of data in our application, these routes.rb resources are the heart of Ruby magic.

Rails.application.routes.draw do
resources :things
resources :getters
resources :givers
end

For more information: http://guides.rubyonrails.org/command_line.html#rails-generate

--

--

Responses (1)