Rapid Application Development
COSC2675 2021 Week 6
A/Prof. Andy Song andy.song@rmit.edu.au
Last Week
• Rails Basics
• Rails philosophy
• Understand layouts **
• Adding images
• The layout method
• Template working with a layout
• Action Pack
• Model
• Controller
• Workingwithasimplewebform
A/Prof. Andy Song RMIT University 2021
2
Connect to a DB through a model
• Create a model $ rails generate model entry
Running via Spring preloader in process 11245
invoke active_record
create db/migrate/[Time Stamp]_create_entries.rb create app/models/entry.rb
invoke test_unit
create test/models/entry_test.rb
create test/fixtures/entries.yml
• Contentofdb/migrate/[TimeStamp]_create_entries.rb:
class CreateEntries < ActiveRecord::Migration[5.2] def change
end end
end
create_table :entries do |t| t.timestamps
#what is this?
A/Prof. Andy Song RMIT University 2021
3
The migrate rb
• Atablewithpluralformofthemodelnameiscreated
• Sotablebooksformodelbook,entriesforentry,micefor mouse, waters for water, sheep for sheep, gooses for goose, men for man, wolves for wolf, oxen for ox, children for child.
• Methodchangereplacedself.upandself.downforundo.
• Add one line:
class CreateEntries < ActiveRecord::Migration[5.0]
def change
create_table :entries do |t|
t.string :name
t.timestamps end
end end
• Thislineistocreateacolumnofstringtostorenames. A/Prof. Andy Song RMIT University 2021
4
Migration
• Tomakethelastchangetakeeffect,run$railsdb:migrate
• Migrations are a convenient way to change your database
schema over time.
• EachmigrationcanbeconsideredasanewversionoftheDB
• Aschemastartsoffwithnothinginit.
• Eachmigrationmodifiesittoaddorremovetables,columns or entries.
• It is managed by ActiveRecord which controls the versions.
• You can $ rails db:migrate:redo STEP=3 $ rails db:rollback STEP=3
$ rails db:migarte VERSION=20210412120000
A/Prof. Andy Song RMIT University 2021
5
The skinny model
• Open app/models/entry.rb, we see simply class Entry < ApplicationRecord
end
• BeforeRails5.0,amodelisadirectsubclassofActiveRecord.
• Now it is through ApplicationRecord, open
app/models/application_record.rb
class ApplicationRecord < ActiveRecord::Base self.abstract_class = true
end
• It tries to be an abstract class although Ruby does not have abstract method and class in its syntax.
A/Prof. Andy Song RMIT University 2021
6
The controller
• Edit the controller app/controllers/entries_controller.rb class EntriesController < ApplicationController
def sign_in
@name = params[:visitor_name]
@entry = Entry.create({:name => @name})
end
• Thislineeffectivelydoesthreetasksforeachentry @entry = Entry.new
@entry.name = @name # @name is from the form
@entry.save
• Name@entryisnotspecial,canbe@data,@info,@fooetc.
• The new version is better as its avoids empty entries and read all entries.
unless @name.blank?
@entry = Entry.create({:name => @name})
end
@entries = Entry.all
A/Prof. Andy Song RMIT University 2021
7
•
Then the view
Edit the view: app/views/entries/sign_in.html.erb
Hello <%= @name %>
<%= form_tag action: 'sign_in' do %>
Enter your name:
<%= text_field_tag 'visitor_name', @name %>
<%= submit_tag 'Sign in' %>
<% end %>
There are <%= @entries.size %> previous visitors
-
<% @entries.each do |entry| %>
- <%= entry.name %>
<% end %>
A/Prof. Andy Song RMIT University 2021
8
Now you should see
A/Prof. Andy Song RMIT University 2021
9
Take a look at the console
Started POST “/entries/sign_in” for [IP] at [TimeStamp] Processing by EntriesController#sign_in as HTML
Parameters: {“utf8″=>””, “authenticity_token”=>”pw..[cut]…==”, “visitor_name”=>”Louise”, “commit”=>”Sign in”}
(0.1ms) begin transaction
SQL (1.1ms) INSERT INTO “entries” (“name”, “created_at”, “updated_at”) VALUES (?, ?, ?) [[“name”, “Louise”], [“created_at”, [TimeStamp] UTC], [“updated_at”, [TimeStamp] UTC]]
(15.0ms) commit transaction
Rendering entries/sign_in.html.erb within layouts/application
(0.3ms) SELECT COUNT(*) FROM “entries”
Entry Load (0.2ms) SELECT “entries”.* FROM “entries”
Rendered entries/sign_in.html.erb within layouts/application (153.6ms)
Completed 200 OK in 196ms (Views: 175.3ms | ActiveRecord: 16.8ms)
Rails is working hard behind the scene!
A/Prof. Andy Song RMIT University 2021
10
Model Methods
• Entry.all, Entry.find(1).id
• Entry.first, Entry.last.name
• Entry.where(name: “Alex”)
• Entry.limit 3, Entry.limt(3).offset(2)
• Entry.order(:name) # and many DB query methods!
• The Rails documentation also has some http://guides.rubyonrails.org/active_record_querying.html
• ThesemethodsarefromActiveRecord.Seedetailsofthese methods on Rails API: api.rubyonrails.org
• These methods can also be called in Rails console (>rails c)!
>>Entry.find(1)
Entry Load (0.3ms) SELECT “entries”.* FROM “entries” WHERE
“entries”.”id” = ? LIMIT ? [[“id”, 1], [“LIMIT”, 1]]
=> #
A/Prof. Andy Song RMIT University 2021
11
Scaffolding a model
• ScaffoldingisoneofthemaincontributingfactorsforrapidRails development.
• Let us try that on a new guest book application, say guestbook_2 $ rails generate scaffold Person name:string
$ rails generate controller entries sign_in [no scaffolding]
• Itgeneratesscaffolding,aroundmodelPersonwithoneattribute
name. Note the table name is people not persons.
• Try to view it on localhost:3000/people What do you see?
• Tryagainafterrunning$railsdb:migrate A/Prof. Andy Song RMIT University 2021
12
What happened in scaffolding
• ScaffoldingallowsRailscreatealistofcomponents − a model with tests
− a data migration to establish the tables for the model − a new route to map user requests to the controller
− a controller to connect different components and pass data − four views (index, edit, show and new) and a partial form − tests for the controller
− an empty file for helper methods
− a CoffeeScript file for scripting the pages − Stylesheets for all of those views
A/Prof. Andy Song RMIT University 2021
13
What happened in scaffolding
• config/routes.rb
Rails.application.routes.draw do
resources :people
end
• Not much in model app/models/person.rb class Person < ApplicationRecord
end
• 70+ lines added automatically to the controller app/controllers/people_controller.rb
• The view is a bit complicated. A/Prof. Andy Song RMIT University 2021
14
Interacting with the site
Notice the URL of each page?
A/Prof. Andy Song RMIT University 2021
15
RESTful Rails
• TheURLsaremeaningful: .../people/edit
.../people/new
.../people, .../people/1, .../people/4
• The actions are visible through these URLs
• You can bookmark a data resource say .../people/4
or an action .../people/2/edit
• Adminscanmanagewebtrafficwithoutworryingabout
disrupting the application.
• AneatfitwithRails’MVCarchitecture.
• EasiertobehaveRESTfully.
A/Prof. Andy Song RMIT University 2021
16
REST
• REpresentational State Transfer, an architecture defined by Roy Fielding in his 2000 PhD thesis at UC Irvine.
• Allows access text-based web resources using a uniform and pre- defined set of stateless operations.
• Does not create new techniques.
• Based on HTTP request, GET = read, POST = write, DELETE =
destroy, PUT = update etc.
• Supports CRUD (create, read, update, destroy) which are common persistent resource operations, often seen in DB, Web, Files.
• Simpler than SOAP (Simple Object Access Protocol, XML based) and WSDL ( Web Services Description Language, XML based)
• Other properties: good scalability, performance, visibility, portability, modifiability, reliability.
A/Prof. Andy Song RMIT University 2021
17
RESTful controller
• Requires a shift in the way developers think about controllers and writing web applications.
• Controllers can be verbs. Each controller implements the same verbs.
• The controller becomes a standardized connection to data model.
• URLs connect the client to a resource (noun) on the server in predictable ways.
• Based on HTTP request, GET = read, POST = write, DELETE = destroy, PUT = update etc.
• ItisnotonlyforHTML,butcanbeusedonresourcesfor JSON, Ajax.
A/Prof. Andy Song RMIT University 2021
18
RESTful Routes
• Remember what is in config/routes resources :people
• Ifwerunthiscommand$railsroutes
Prefix Verb people GET
URI Pattern
Controller#Action people#index people#create people#new people#edit people#show people#update people#update people#destroy
new_person GET edit_person GET person GET
/people/new(.:format) /people/:id/edit(.:format) /people/:id(.:format)
/people(.:format) POST /people(.:format)
PATCH /people/:id(.:format) PUT /people/:id(.:format) DELETE /people/:id(.:format)
• ResourcesgiveasetofRESTfulURLs.
• HTTP requests are mapped to actions. Meaning a URL
such as GET /people/1/edit is processed by people#edit A/Prof. Andy Song RMIT University 2021
19
RESTful Routes
(Learning Rails 5, Mark Locklear Eric Gruber)
20
A/Prof. Andy Song RMIT University 2021
Can work on JSON files
(Learning Rails 5, Mark Locklear Eric Gruber)
A/Prof. Andy Song RMIT University 2021
21
Can work on JSON files
• You can try in a browser localhost:3000/people.json If we run this command $ rails routes
• TheoutputofJSONfilescanbeviewedatthebrowser [{"id":1,"name":"Alexei","created_at":”[TimeStamp]",
"updated_at":"[TimeStamp]","url":" localhost:3000/people/1.json"}, {"id":2,"name":"Ali","created_at":"[TimeStamp]", "updated_at":"[TimeStamp]","url":" localhost:3000/people/2.json"}, {"id”:4,"name":"Louise","created_at":"[TimeStamp]", "updated_at":"[TimeStamp]","url":" localhost:3000/people/4.json"}]
• HTTP requests are mapped to actions. Meaning a URL such as GET /people/1/edit is processed by people#edit
A/Prof. Andy Song RMIT University 2021
22
Specify a layout
A/Prof. Andy Song RMIT University 2021
23
A/Prof. Andy Song RMIT University 2021
24
RESTful Methods
• before_action :set_person, only: [:show, :edit, :update, :destroy]
• The private callback set_person
• The new method does not touch the database, only in memory.
• The controller simply pass the model to the view new.html.erb, without worrying about the schema etc.
• Person.new(person_params) is another callback
• respond_to method is from ActionController, allows responses in
various formats on the same data.
A/Prof. Andy Song RMIT University 2021
25
Multiple Tables
• Working with multiple tables is made easy by Rails.
• Every model maps to a table.
• The relationships between tables are managed as relationships between models.
• Scaffoldingwillbehelpful.Howevertherearequitesome coding need to done manually.
• Thetaskwillbemuchmoredifficultifyouareretrofittingan existing database (not generated by Rails) rather than creating a fresh new database in Rails.
A/Prof. Andy Song RMIT University 2021
26
A Students Application
• Create a new application called students :
$ rails generate scaffold student given_name:string middle_name:string family_name:string date_of_birth:date grade_point_average:decimal start_date:date
$ rails generate scaffold award name:string year:integer student_id:integer
Two models have been created. Rails doesn’t know they are connected yet.
Model award has a student_id field.
A/Prof. Andy Song RMIT University 2021
27
A Students Application
• Open app/models/student.rb and add # a student can have many awards
has_many :awards
• Open app/models/award.rb and add
# every award is linked to a student, through student_id
belongs_to :student
• has_many and belongs_to are just two methods
• Theyspecifytheassociationbetweenmodels
• Similar methods include: has_one
has_and_belongs_to_many
A/Prof. Andy Song RMIT University 2021
28
Association methods
• Detailsofhas_manyandbelongs_tocanbeseenintheRailsAPI
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#meth od-i-has_many http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#meth od-i-belongs_to
• Theyareclassmethodsfrom ActiveRecord::Associations::ClassMethods
• Their method signatures are:
has_many(name, scope = nil, options = {}, &extension) belongs_to(name, scope = nil, options = {})
A/Prof. Andy Song RMIT University 2021
29
Association methods
has_many :friends, -> { where(author_id: 1) },
through: :societies, source: :member, do
def find_or_create_by_name(name)
first_name, last_name = name.split(” “, 2) find_or_create_by(first_name: first_name, last_name: last_name)
end end
belongs_to :firm, foreign_key: “client_of”
belongs_to :person, primary_key: “name”, foreign_key: “person_name” belongs_to :author, class_name: “Person”, foreign_key: “author_id” belongs_to :project, -> { readonly }
belongs_to :attachable, polymorphic: true
belongs_to :valid_coupon, ->(o) { where “discounts > ?”, o.payments_count },
class_name: “Coupon”, foreign_key: “coupon_id”
A/Prof. Andy Song RMIT University 2021
30
Run the application
• Through these association methods Rails knows the connection between models.
• Rails does not add automatic checking or validation to ensure that the relationships would work.
• E.g.doesnotrequireavalidstudentIDforeveryaward.
• Although that can achieved using scope, option
• Now you can start the Students Application $ rails db:migrate
$ rails s
• Create new students at localhost:3000/students/new
A/Prof. Andy Song RMIT University 2021
31
Adding new records
How about create an award at
localhost:3000/awards/new
A/Prof. Andy Song RMIT University 2021
32
Add an Award
• Aselectfieldsoundslikeagoodideatosolvetheproblem.
• Editthestudentfieldinpartialapp/views/awards/_form.html.erb
• Remove <%= form.number_field :student_id, id: :award_student_id %> and change to
< div class =" field" >
<%= form.label :student_id %>
<%= form.select :student_id,
Student.all.order(:family_name, :given_name).collect {|s| [( s.given_name + " " + s.family_name), s.id]}%>