WDE-Slides-11.pptx
Web System Development with
Ruby on Rails
Day 11(8/Dec/2013)
Project: mini twitter site
Face and ‘Follow’ table
Face table for users
p Class Name: Face
p Table Name: faces (plural)
p 1 to 1 relation with users table,
p User has_one :face
p Face belongs_to :user
p Via user.face, if related link from face is
nil, display app/assets/images/
default_face.png file (default icon).
Structure of Face Class
Face Classのフィールド:
user_id, integer (index to User)
name, string (File Name)
content_type, string (MIME type name)
content, blob (content of image files)
Generation of Face Class
Type the following command in 1 line;
rails generate model Face user_id:integer
name:string content_type:string
content:binary
Then, generate the database.
rake db:migrate
Setting up Relations
Add
belongs_to :user
in app/models/face.rb, then add
has_one :face
in app/models/user.rb.
Registration of faces
To register faces, we generate faces
controller which have register action.
rails generate controller faces register
views/faces/register.html.erb
Register Face Photo
<%= form_for @face, :url => “/faces/update”,
:html => {:multipart => true}, :method => :put do %>
User: <%= @user.handle %>
<%= file_field :file, :upload %>
<% end %>
controllers/faces_controller.rb
Prepare register method, and update method.
# GET /faces/register
# GET /faces/register.json
def register
@face = Face.new
@user = User.find( current_user.id )
end
Update method
# PUT /faces/update
# PUT /faces/update.json
def update
@face = Face.find_by_user_id( current_user.id )
@face.destroy if @face
@user = User.find( current_user.id )
if params[:file]
@file = params[:file][:upload]
if @file && @file.respond_to?(:original_filename)
face_params = {
:user_id => @user.id,
:name => @file.original_filename,
:content_type => @file.content_type,
:content => @file.read
}
@face = Face.new( face_params )
end
end
if @face.save
respond_to do |format|
format.html { redirect_to edit_user_path( :id => current_user.id ),
:notice => ‘Face photo was successfully uploaded.’ }
format.json { render @user, status: :created, location: @user }
end
else
respond_to do |format|
format.html { render action: “register”, :alert => ‘Failed to register face photo.’ }
format.json { render json: @faces.errors, status: :unprocessable_entity }
end
end
end
Update Method
If any ‘face’ record had been already
registered, destroy the record, then
retrieve the new File parameter to register
new face upload.
Update = destroy and newly register.
If succeed, redirect to user registration
view.
Update method
File action in Chirps_controller
Add face image transfer action, when chirps are
displayed.
def face
face = Face.find( params[:id] )
send_data face.content, :filename => face.name,
:type=>face.content_type
end
config/routes.rb
To register and display, add two routings.
get “chirps/face” => “chirps#face”
put “faces/update” => “faces#update”
The following line should have been
generated by the generator.
put “faces/register”
Prepare default face file
Move ‘default_face.png’ file to app/assets/
images
Index of Chirps
Add Face photo display part.
<% if chirp.user.face %>
<% face = chirp.user.face %>
<% if face.content_type =~ /^image\/.*?(png|jpeg|gif)$/ %>
<%= image_tag url_for({:action => ‘face’, :id=> face.id,
:filename => face.name}), :alt => face.name
%>
<% else %>
<%= image_tag 'default_face.png', :alt => ‘No Photo’ %>
<% end %>
<% else %>
<%= image_tag 'default_face.png', :alt => ‘No Photo’ %>
<% end %>
Index of Chirps
Registration of Face
http://127.0.0.1:3000/faces/register
If you think you need to add link to this
path, try it by yourself.
Registration of Face
Now you should have Face photo (of the
twitter owner,) and the photo for tweets.
Arrange the links
Screen link is not well arranged. So, let us re-
arrange the screen links.
[Root view] ‘List of Chirps’
à Sign out:
à Edit your info
à Back(to ‘List of chirps’)
à Upload your photo
à Back(to ‘Edit info’)
à Change Password
à Back(to ‘List of chirps’)
app/views/chirps/index.html.erb
Arrange the top right index;
<% if current_user %>
Hello, <%= current_user.email %>
<%= link_to('Sign out', destroy_user_session_path, :method => :delete) %>
<%= link_to 'Edit your info', edit_user_path( :id => current_user.id ) %>
<%= link_to 'Change Password', edit_user_registration_path %>
<% end %>
after ‘Edit your info’ path
This view is in the file app/views/users/
edit.html.erb.
So we need to arrange ‘Back’ link from
<%= link_to 'Back', users_path %>
To
<%= link_to 'Back', chirps_path %>
Also in the file app/views/users/
show.html.erb.
Link: edit user à upload face photo
[Root view] ‘List of Chirps’
à Edit your info
à Upload your photo
à Back(to ‘Edit info’)
Now modify this views link;
1) hand @face info link to view/users/edit.html.erb
2) after uploading file, link back to edit users.
3) confirm uploaded face in users/show.html.erb
1) Face photo upload in edit users
Add the following in app/views/users/
_form.html.erb
<%= form_for @face, :url => “/faces/update”,
:html => {:multipart => true}, :method => :put do %>
<%= file_field :file, :upload %>
<% end %>
app/views/users/_form.html.erb
Users controller, edit action
So that face could be registered, @face
should be given for ‘edit’ template;
# GET /users/1/edit
def edit
if @user.face
@face = Face.find(@user.face.id)
else
@face = Face.new
end
end
Users controller, edit action
2) After uploading file, link back
Check the faces_controller# update method;
It already has link back to edit user path.
3) Confirm uploaded face
Add users/face path to config/routes.rb
get “users/face” => “users#face”
Then add face method to users_controller
Add face in users/show.html.erb
<% if @user.face %>
Your face photo:
<% face = @user.face %>
<% if face.content_type =~ /^image\/.*?(png|jpeg|gif)$/ %>
<%= image_tag url_for({:action => ‘face’, :id=> face.id,
:filename => face.name}), :alt => face.name %>
<% end %>
<% else %>
<%= image_tag 'default_face.png', :alt => ‘No Photo’ %>
<% end %>
app/views/users/show.html.erb
Arranged Top chirps screen
Remove timestamp input;
Chirped_at timestamp should be given by
the system, so remove the input from
_form, and give Time.now in new method.
:chirped_at => Time.now
Tweets without photo
Now controller does not save tweets without photo.
Modify this part so that the system accept the
tweets without photo;
Chirps_controller.rb ‘else’ for ‘if params[:chirp]
[:photo]’
Also, when update, ‘user_id’, ‘chirped_at time
stamps’ should not be updated.
Chrips controller, create action
else
@chirp = Chirp.new( chirp_params )
@chirp.user_id = current_user.id
@chirp.chirped_at = Time.now
respond_to do |format|
if @chirp.save
format.html { redirect_to @chirp, notice: ‘Chirp was successfully created.’ }
format.json { render :show, status: :created, location: @chirp }
else
format.html { render :new }
format.json { render json: @chirp.errors, status: :unprocessable_entity }
end
end
end
Chirps Controller, create action
Chirps controller, update action
# PATCH/PUT /chirps/1
# PATCH/PUT /chirps/1.json
def update
if params[:chirp][:photo]
@file = params[:chirp][:photo]
@stat = @file.tempfile.stat
p “chirped_at:”
p params[:chirp][:chirped_at]
respond_to do |format|
if @chirp.update_attributes(
:chirp => params[:chirp][:chirp],
:file_name => @file.original_filename,
:file_type => @file.content_type,
:photo => @file.read,
)
format.html { redirect_to @chirp, notice: ‘Chirp was successfully updated.’ }
format.json { render :show, status: :ok, location: @chirp }
else
format.html { render :edit }
format.json { render json: @chirp.errors, status: :unprocessable_entity }
end
end
else
respond_to do |format|
if @chirp.update_attributes(
:chirp => params[:chirp][:chirp],
)
format.html { redirect_to @chirp, notice: ‘Chirp was successfully updated.’ }
format.json { render :show, status: :ok, location: @chirp }
else
format.html { render :edit }
format.json { render json: @chirp.errors, status: :unprocessable_entity }
end
end
end
end
Chirps controller, update action
Now today’s next topics: Follow
My Twitter
We have introduced face photo to ‘my twitter,’ for
my case, “Chirpy” WEB site.
My lecture slides only covered the basic structure of
the project. I hope you extend the previous
lecture slides to realize the following features/
functions;
(1) Internationalization,
(2) Screen Division/right, top bar and footer,
(3) Top banner image display, and such.
Today’s Next Topics
Today, we will add the following features;
“Follows” table, and list only followers
messages.
Follows Table
The structure is simple.
User → Follow table (one to many)
Follow Class
integer user_id Follower’s id
integer to_id Whom followed
rails generate model follow user_id:integer
to_id:integer
Setting up Relations for Follows
Add
in models/follow.rb
belongs_to :user
in models/user.rb
has_many :follows
Make Controller
Do not forget migration,
rake db:migrate
Now we show “all” users list, and show
follow / not follow button to the list, and
then reflect this “follow/not follow”
selection.
First, make list view
rails generate controller follows list
Generation of Controller
List method of Follows controller
class FollowsController < ApplicationController def list @users = User.all @users -= [current_user] end end Access Controller So that always ‘Logged in users’ could tweet and follow, to avoid ‘current_user is nil,’ add the following, as is in the chirps_controller.rb; before_filter :authenticate_user! In both faces_controller.rb, and follows_controller.rb. Views/follows/list.html.erb
Follows#list
<% if user.face %> <% if user.face.content_type =~ /^image¥/.*?(png|jpeg|gif)$/ %> <%= image_tag url_for({:controller => ‘chirps’, :action => ‘face’, :id=> user.face.id, :filename => user.face.name}), :alt => user.face.name %> <% else %> <%= image_tag 'default_face.png', :alt => user.face.name %> <% end %> <% else %> <%= image_tag 'default_face.png', :alt => ‘No Name’ %> <% end %> |
<%= user.handle %> | <%= user.email %> |
Views/follows/list.html.erb
Show all users except for self
Users display except for self
Add follows information to list
Add the following one line in the list method
of follows_controller;
@follows = Follow.where( user_id: current_user.id )
Add Follow buttons
Add the following in the views/follows/
list.html.erb, in the table.
<%= form_tag "/follows/cancel/"+follow.id.to_s, :method => ‘put’ do %>
<%= submit_tag "Stop Follow", :name => “Cancel” %>
<% end %>
<% else %>
<%= form_tag "/follows/follow/"+user.id.to_s, :method => ‘put’ do %>
<%= submit_tag "Follow", :name => “Follow” %>
<% end %>
<% end %>
Config/routes.rb
Add two paths of /follows/follow, and /
follows/cancel
put “follows/follow/:id” => “follows#follow”, :as =>
‘follows_follow’
put “follows/cancel/:id” => “follows#cancel”, :as =>
‘follows_cancel’
Method for when “Followed”
# put /follows/follow
# put /follows/follow.json
def follow
follow_params = {
:to_id => params[:id],
:user_id => current_user.id
}
@follow = Follow.new( follow_params )
respond_to do |format|
if @follow.save
format.html { redirect_to follows_list_path }
format.json { render :json => @follow }
else
format.html { render action: “list”, :alert => ‘Failed to follow.’ }
format.json { render json: @follow.errors }
end
end
end
Method for when “Followed”
Method for when “Cancelled”
# put /follows/cancel
# put /follows/cancel.json
def cancel
@follow = Follow.find_by_id( params[:id] )
@follow.destroy
redirect_to follows_list_path
end
Test run for Follows/list
Now you would have the following view, to
switch follow/stop follow selection.
Choose the followers to show chirps
Modify index action in chirps controller from
def index
@chirps = Chirp.all
end
to the following;
@follows = Follow.find_all_by_user_id( current_user.id )
@users = @follows.collect { |f| f.to_id }
@users << current_user.id
@chirps = Chirp.select{|c| @users.include?( c.user_id )}
@chirps = @chirps.sort_by{ |c| c[:created_at] }.reverse
Choose own follows in the 1st line, then make ids
list in the 2nd line, add self in the 3rd line, extract
followed chirps in the 4th line, and sort in reverse
order if tweeted time in the 5th line.
Index action of Chirps controller
views/chirps/index.html.erb
So that ‘faces’ are always displayed, modify
the face display part as the following;
Now it looks like twitter, a bit…
How far we should go, to get similar
with the real one…
We have a lot of things to do for “real twitter” site.
Display the “elapsed time” since the tweet is
uploaded, instead of “tweeted_at” timestamp.
(Also, time should be formatted)
Display self-introduction.
How to cope with the tweets after the user’s
withdrawal, and such.
Whatever you want to do, do them by yourself as
your study challenge.