Brief Notes
Noteworthy links, summaries and concise prose on stuff.

Ruby on Rails development

All about developing with Ruby on Rails.

Notes

To create a new app that will use PostgreSQL as the database for all environments (development, test & production):

rails new myapp --database=postgresql

Configure config/database.yml with the proper PostgreSQL connection details.


To rename a Rails 4 app, starting from version 4.1 onwards, all that’s required is to change the app name in:

  • config/application.rb
  • config/database.yml (don’t forget to rename the database itself, eg in PostgreSQL, issue the command ALTER DATABASE oldapp_production RENAME TO newapp_production)
  • config/initializers/session_store.rb

Add a stock resource to config/routes.rb:

Rails.application.routes.draw do

  resources :stocks

These routes are automatically created:

$ rake routes
    Prefix Verb   URI Pattern                Controller#Action
    stocks GET    /stocks(.:format)          stocks#index
           POST   /stocks(.:format)          stocks#create
 new_stock GET    /stocks/new(.:format)      stocks#new
edit_stock GET    /stocks/:id/edit(.:format) stocks#edit
     stock GET    /stocks/:id(.:format)      stocks#show
           PATCH  /stocks/:id(.:format)      stocks#update
           PUT    /stocks/:id(.:format)      stocks#update
           DELETE /stocks/:id(.:format)      stocks#destroy

Create a stocks controller:

rails generate controller stocks

This generator will not create routes.


Destroy a stocks controller:

rails destroy controller stocks

A frequent practice is to place the standard CRUD actions in each controller in the following order: index, show, new, edit, create, update and destroy. You may use any order you choose, but keep in mind that these are public methods; as mentioned earlier in this guide, they must be placed before any private or protected method in the controller in order to work.

Define empty public methods index, show, new, edit, create, update, destroy and private method stock_params in the stocks controller:

class StocksController < ApplicationController
  def index
  end

  def show
  end

  def new
  end

  def edit
  end

  def create
  end

  def update
  end

  def destroy
  end

private
  def stock_params
  end
end

The following template works for StocksController#new:

  • lives at app/views/stocks/new.html.erb
  • uses a form_for form builder helper method
  • stocks_path helper is passed to the :url option. The helper tells Rails to point to the URI pattern associated with the stocks prefix; and the form will (by default) send a POST request to that route, which is, in turn, associated with StocksController#create
<h1>New stock</h1>

<%= form_for :stock, url: stocks_path do |f| %>
  <p>
    <%= f.label :code %><br>
    <%= f.text_field :code %>
  </p>

  <p>
    <%= f.label :name %><br>
    <%= f.text_field :name %>
  </p>

  <p>
    <%= f.label :company %><br>
    <%= f.text_field :company %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

To show the parameters that the above POST request sends the fields of the form as:

class StocksController < ApplicationController

  # ...

  def create
    render plain: params[:stock].inspect # => {"code"=>"5959.KL", "name"=>"A&M", "company"=>"A & M REALTY BHD"}
  end
end

Models in Rails use a singular name, and their corresponding database tables use a plural name.

Read Rails Model Generator Shortcuts for command line model generation ideas.

Generate a Stock model:

  • unique constraint on code; in development mode, inserting a duplicate code value throws the error message PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "index_stocks_on_code" DETAIL: Key (code)=(5959.KL) already exists. : INSERT INTO "stocks" ("code", "name", "company", "business_background", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
  • index on name
  • just mentioning the field name company without specifying the type means the same as company:string, that is, string is the default type for fields
rails generate model Stock code:uniq name:index company

The following migration is generated:

class CreateStocks < ActiveRecord::Migration
  def change
    create_table :stocks do |t|
      t.string :code
      t.string :name
      t.string :company

      t.timestamps null: false
    end
    add_index :stocks, :code, unique: true
    add_index :stocks, :name
  end
end

Run the migration to create the database objects for Stock:

rake db:migrate

In PostgreSQL, the stocks table looks like:

\d stocks
                                     Table "public.stocks"
   Column   |            Type             |                      Modifiers                      
------------+-----------------------------+-----------------------------------------------------
 id         | integer                     | not null default nextval('stocks_id_seq'::regclass)
 code       | character varying           | 
 name       | character varying           | 
 company    | character varying           | 
 created_at | timestamp without time zone | not null
 updated_at | timestamp without time zone | not null
Indexes:
    "stocks_pkey" PRIMARY KEY, btree (id)
    "index_stocks_on_code" UNIQUE, btree (code)
    "index_stocks_on_name" btree (name)
Referenced by:
    TABLE "stock_daily_metrics" CONSTRAINT "fk_rails_2331ee8942" FOREIGN KEY (stock_id) REFERENCES stocks(id)

Create a StockDailyMetric model to store daily stock open/high/low/close prices and volume, and which references the Stock model:

rails generate model StockDailyMetric stock:references date:date:index open:decimal high:decimal low:decimal close:decimal volume:integer

In PostgreSQL, the stock_daily_metrics table looks like:

\d stock_daily_metrics
                                     Table "public.stock_daily_metrics"
   Column   |            Type             |                            Modifiers                             
------------+-----------------------------+------------------------------------------------------------------
 id         | integer                     | not null default nextval('stock_daily_metrics_id_seq'::regclass)
 date       | date                        | 
 open       | numeric                     | 
 high       | numeric                     | 
 low        | numeric                     | 
 close      | numeric                     | 
 volume     | integer                     | 
 stock_id   | integer                     | 
 created_at | timestamp without time zone | not null
 updated_at | timestamp without time zone | not null
Indexes:
    "stock_daily_metrics_pkey" PRIMARY KEY, btree (id)
    "index_stock_daily_metrics_on_date" btree (date)
    "index_stock_daily_metrics_on_stock_id" btree (stock_id)
Foreign-key constraints:
    "fk_rails_2331ee8942" FOREIGN KEY (stock_id) REFERENCES stocks(id)

Create a migration to add the field business_background to Stock:

rails generate migration AddBusinessBackgroundToStocks business_background:text

Don’t forget to run rake db:migrate to add the business_background column to the stocks table.

Also, update StocksController params, views or forms* as appropriate.


Update StocksController to allow for proper persistence of newly-created Stock objects:

class StocksController < ApplicationController
  # ...

  def create
    # render plain: params[:stock].inspect
    @stock = Stock.new(stock_params)
    @stock.save
    redirect_to @stock
  end

private
  def stock_params
    params.require(:stock).permit(:code, :name, :company, :business_background)
  end
end

Update views/stocks/new.html.erb to include a form field for business_background:

<h1>New stock</h1>

<%= form_for :stock, url: stocks_path do |f| %>
  <!-- ... -->

  <p>
    <%= f.label :business_background %><br>
    <%= f.text_area :business_background, cols: 60, rows: 20 %>
  </p>

  <p>
    <%= f.submit %>
  </p>
<% end %>

Update StocksController#show to enable information display for a single stock:

class StocksController < ApplicationController
  # ...

  def show
    @stock = Stock.find(params[:id])
  end

  # ...

Create views/stocks/show.html.erb:

<p>
  <strong>Code:</strong>
  <%= @stock.code %>
</p>

<p>
  <strong>Name:</strong>
  <%= @stock.name %>
</p>

<p>
  <strong>Company:</strong>
  <%= @stock.company %>
</p>

<p>
  <strong>Business background:</strong>
  <%= @stock.business_background %>
</p>

To list all stocks, first, update StocksController#index:

class StocksController < ApplicationController
  def index
    @stocks = Stock.all
  end

  # ...

Finally, create app/views/stocks/index.html.erb:

<h1>Stocks listing</h1>

<table>
  <tr>
    <th>Code</th>
    <th>Name</th>
    <th>Company</th>
    <th colspan="2"></th>
  </tr>

  <% @stocks.each do |stock| %>
    <tr>
      <td><%= stock.code %></td>
      <td><%= stock.name %></td>
      <td><%= stock.company %></td>
      <td><%= link_to 'Show', stock_path(stock) %></td>
      <td><%= link_to 'Edit', edit_stock_path(stock) %></td>
    </tr>
  <% end %>
</table>

At this point, the Edit link will not work as StocksController#edit isn’t defined yet.


3 steps are needed to create the application home page that will have a link pointing to the stocks listing:

Step 1: Create a controller welcome with 1 action index that will have 1 view app/views/welcome/index.html.erb:

rails generate controller welcome index

This generator will also create the route get 'welcome/index' in config/routes.rb.

Step 2: Uncomment the route root in config/routes.rb and define the following to have the root of the application mapped to the welcome controller’s index action:

root 'welcome#index'

To prevent site visitors from reaching WelcomeController#index via welcome/index, comment the following route:

# get 'welcome/index'

Step 3: Update app/views/welcome/index.html.erb with a link to the stocks listing (note the 2 ways to specify the destination view for link_to:

<h1>Welcome</h1>

<p>Site navigation:</p>

<ul>
  <li><%= link_to 'Stocks listing', stocks_path %></li>
  <!-- <li><%= link_to 'Stocks listing', controller: 'stocks' %></li> -->
</ul>

Update the following views with link_tos:

  • app/views/stocks/index.html.erb => Add <p><%= link_to 'New stock', new_stock_path %></p> below <h1>
  • app/views/stocks/new.html.erb => Add <p><%= link_to 'Back', stocks_path %></p> below <h1>
  • app/views/stocks/show.html.erb => Add <p><%= link_to 'Back', stocks_path %></p> right at the top

To show parent values from foreign key references in a form select field on the new or edit forms of the child, read this discussion ruby - Rails - Displaying Foreign Key References in a form - Stack Overflow.

Note that the snippet @sports = Sport.scoped should be replaced by @sports = Sport.all as Model.scoped has been deprecated (see Trying to understand Model.all(rails 4+) vs Model.scoped(rails 3.2) : rails).


To show the parent key values (instead of Object IDs) of a foreign key relationship in the index page of the referencing entity, see mysql - Ruby on Rails - How to show foreign key values in one model to another - Stack Overflow.


Noteworthy links

Mac OS X Ruby on Rails installation instructions.