Commit 3551f4ca by Mai Hoang Thai Ha

ad user microposts

parent 6c7469e0
Pipeline #1290 failed with stages
in 0 seconds
......@@ -5,6 +5,9 @@ ruby '3.0.1'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails', branch: 'main'
gem 'rails', '~> 6.1.3', '>= 6.1.3.2'
gem 'image_processing', '~> 1.12', '>= 1.12.1'
gem 'mini_magick', '~> 4.11'
gem 'active_storage_validations', '~> 0.9.4'
gem 'bcrypt', '3.1.13'
gem 'faker', '~> 2.18'
gem 'will_paginate', '~> 3.3.0'
......@@ -68,6 +71,7 @@ end
group :production do
gem 'pg', '1.1.4'
gem 'aws-sdk-s3', '~> 1.96', '>= 1.96.1', require: false
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
......
......@@ -39,6 +39,8 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
active_storage_validations (0.9.4)
rails (>= 5.2.0)
activejob (6.1.3.2)
activesupport (= 6.1.3.2)
globalid (>= 0.3.6)
......@@ -65,6 +67,22 @@ GEM
ansi (1.5.0)
autoprefixer-rails (10.2.5.1)
execjs (> 0)
aws-eventstream (1.1.1)
aws-partitions (1.470.0)
aws-sdk-core (3.114.3)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.239.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-kms (1.44.0)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.96.1)
aws-sdk-core (~> 3, >= 3.112.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.1)
aws-sigv4 (1.2.3)
aws-eventstream (~> 1, >= 1.0.2)
bcrypt (3.1.13)
bindex (0.8.1)
bootsnap (1.7.5)
......@@ -111,8 +129,12 @@ GEM
minitest (>= 3.0)
i18n (1.8.10)
concurrent-ruby (~> 1.0)
image_processing (1.12.1)
mini_magick (>= 4.9.5, < 5)
ruby-vips (>= 2.0.17, < 3)
jbuilder (2.11.2)
activesupport (>= 5.0.0)
jmespath (1.4.0)
listen (3.5.1)
rb-fsevent (~> 0.10, >= 0.10.3)
rb-inotify (~> 0.9, >= 0.9.10)
......@@ -124,6 +146,7 @@ GEM
mini_mime (>= 0.1.1)
marcel (1.0.1)
method_source (1.0.0)
mini_magick (4.11.0)
mini_mime (1.0.3)
minitest (5.14.4)
minitest-reporters (1.3.8)
......@@ -191,6 +214,8 @@ GEM
regexp_parser (2.1.1)
rexml (3.2.5)
ruby-progressbar (1.11.0)
ruby-vips (2.1.2)
ffi (~> 1.12)
rubyzip (2.3.0)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
......@@ -249,7 +274,9 @@ PLATFORMS
x86_64-linux
DEPENDENCIES
active_storage_validations (~> 0.9.4)
autoprefixer-rails
aws-sdk-s3 (~> 1.96, >= 1.96.1)
bcrypt (= 3.1.13)
bootsnap (>= 1.4.4)
bootstrap-sass (= 3.4.1)
......@@ -259,8 +286,10 @@ DEPENDENCIES
faker (~> 2.18)
guard (= 2.16.2)
guard-minitest (= 2.4.6)
image_processing (~> 1.12, >= 1.12.1)
jbuilder (~> 2.7)
listen (~> 3.3)
mini_magick (~> 4.11)
minitest (= 5.14.4)
minitest-reporters (= 1.3.8)
pg (= 1.1.4)
......
......@@ -223,3 +223,50 @@ input {
border-bottom: 1px solid $gray-medium-light;
}
}
// micropost
.microposts {
list-style: none;
padding: 0;
li {
padding: 10px 0;
border-top: 1px solid #e8e8e8;
}
.user {
margin-top: 5em;
padding-top: 0px;
}
.content {
display: block;
margin-left: 60px;
img {
display: block;
padding: 5px 0;
}
}
.timestamp {
color: rgb(117, 117, 117);
display: block;
margin-left: 60px;
}
.gravatar {
float: left;
margin-right: 10px;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.image {
margin-top: 10px;
input {
border: 0;
}
}
\ No newline at end of file
// Place all the styles related to the Microposts controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: https://sass-lang.com/
class ApplicationController < ActionController::Base
include SessionsHelper
private
# Confirms a logged-in user
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
class MicropostsController < ApplicationController
before_action :logged_in_user, only: [:create, :destroy]
before_action :correct_user, only: :destroy
def create
@micropost = current_user.microposts.build(micropost_params)
@micropost.image.attach(params[:micropost][:image])
if @micropost.save
flash[:success] = "Micropost created!"
redirect_to root_url
else
@feed_items = current_user.feed.paginate(page: params[:page])
render 'static_pages/home'
end
end
def destroy
@micropost.destroy
flash[:success] = "Micropost deleted"
redirect_to request.referrer || root_url
end
private
def micropost_params
params.require(:micropost).permit(:content, :image)
end
def correct_user
@micropost = current_user.microposts.find_by(id: params[:id])
redirect_to root_url if @micropost.nil?
end
end
\ No newline at end of file
class StaticPagesController < ApplicationController
def home
if logged_in?
@micropost = current_user.microposts.build
@feed_items = current_user.feed.paginate(page: params[:page])
end
end
def help
......
......@@ -9,6 +9,7 @@ class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def new
......@@ -54,21 +55,13 @@ class UsersController < ApplicationController
end
# Before filters
# Confirm a logged_in user
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
# Confirms the correct user
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
......
module MicropostsHelper
end
class Micropost < ApplicationRecord
belongs_to :user
has_one_attached :image
default_scope -> { order(created_at: :desc) }
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
validates :image, content_type: { in: %w[image/jpeg image/gif image/png],
message: "must be a valid image format" },
size: { less_than: 5.megabytes,
message: "should be less than 5MB" }
# Return ta resized image for display
def display_image
image.variant(resize_to_limit: [500, 500])
end
end
class User < ApplicationRecord
has_many :microposts, dependent: :destroy
attr_accessor :remember_token, :activation_token, :reset_token
before_save :downcase_email
before_create :create_activation_digest
......@@ -68,6 +69,12 @@ class User < ApplicationRecord
reset_sent_at <2.hours.ago
end
# Defines a proto-feed
# See "Following users" for the full implementtation
def feed
Micropost.where("user_id = ?", id)
end
private
# Convaerts email to all lowr_case
......
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.display_image if micropost.image.attached? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</span>
</li>
\ No newline at end of file
......@@ -5,7 +5,7 @@
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, url: password_reset_path(params[:id]),
local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= render 'shared/error_messages', object: f.object %>
<%= hidden_field_tag :email, @user.email %>
......
<% if @user.errors.any? %>
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>
The form contains <%= pluralize(object.errors.count, "error") %>
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
......
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items, params: { controller: :static_pages, action: :home } %>
<% end %>
\ No newline at end of file
<%= form_with(model: @micropost, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="image">
<%= f.file_field :image, accept: "image/jpeg,image/gif,image/png" %>
</span>
<% end %>
<script type="text/javascript">
$("#micropost_image").bind("change", function() {
const size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert("Maximum file size is 5MB. Please choose a smaller file.");
$("#micropost_image").val("");
}
});
</script>
\ No newline at end of file
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>
\ No newline at end of file
<div class="center jumbotron">
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
</div>
<% else %>
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>
<h2>
This is the home page for the
<a href="https://www.railstutorial.org/">Ruby on Rails Tutorial</a>
sample application.
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary"%>
</div>
<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200"),
</div>
<%= link_to image_tag("rails.svg", alt: "Rails logo", width: "200"),
"https://rubyonrails.org" %>
<% end %>
\ No newline at end of file
......@@ -4,7 +4,7 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
......
......@@ -4,7 +4,7 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: @user, local: true) do |f| %>
<%= render 'shared/error_messages' %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
......
......@@ -9,4 +9,13 @@
</h1>
</section>
</aside>
<div class="col-md-8">
<% if @user.microposts.any? %>
<h3>Microposts (<%= @user.microposts.count %>)</h3>
<ol class="microposts">
<%= render @microposts %>
</ol>
<%= will_paginate @microposts %>
<% end %>
</div>
</div>
\ No newline at end of file
Rails.application.routes.draw do
get 'password_resets/new'
get 'password_resets/edit'
get 'sessions/new'
get 'users/new'
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
......@@ -14,4 +10,5 @@ Rails.application.routes.draw do
resources :users
resources :account_activations, only: [:edit]
resources :password_resets, only: [:new, :create, :edit, :update]
resources :microposts, only: [:create, :destroy]
end
class CreateMicroposts < ActiveRecord::Migration[6.1]
def change
create_table :microposts do |t|
t.text :content
t.references :user, null: false, foreign_key: true
t.timestamps
end
add_index :microposts, [:user_id, :created_at]
end
end
# This migration comes from active_storage (originally 20170806125915)
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.string :service_name, null: false
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
create_table :active_storage_variant_records do |t|
t.belongs_to :blob, null: false, index: false
t.string :variation_digest, null: false
t.index %i[ blob_id variation_digest ], name: "index_active_storage_variant_records_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
......@@ -10,7 +10,44 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 2021_06_17_081805) do
ActiveRecord::Schema.define(version: 2021_06_22_061651) do
create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
t.string "record_type", null: false
t.integer "record_id", null: false
t.integer "blob_id", null: false
t.datetime "created_at", null: false
t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
end
create_table "active_storage_blobs", force: :cascade do |t|
t.string "key", null: false
t.string "filename", null: false
t.string "content_type"
t.text "metadata"
t.string "service_name", null: false
t.bigint "byte_size", null: false
t.string "checksum", null: false
t.datetime "created_at", null: false
t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
end
create_table "active_storage_variant_records", force: :cascade do |t|
t.integer "blob_id", null: false
t.string "variation_digest", null: false
t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
end
create_table "microposts", force: :cascade do |t|
t.text "content"
t.integer "user_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
t.index ["user_id"], name: "index_microposts_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
......@@ -28,4 +65,7 @@ ActiveRecord::Schema.define(version: 2021_06_17_081805) do
t.index ["email"], name: "index_users_on_email", unique: true
end
add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
add_foreign_key "microposts", "users"
end
......@@ -17,3 +17,10 @@ User.create!( name: "Example User",
password,activated: true,
activated_at: Time.zone.now)
end
# Generate microposts for a subset of users
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(word_count: 5)
users.each { |user| user.microposts.create!(content: content) }
end
\ No newline at end of file
require "test_helper"
class MicropostsControllerTest < ActionDispatch::IntegrationTest
def setup
@micropost = microposts(:orange)
end
test "should redirect create when not logged in" do
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "Lorem ipsum" } }
end
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'Micropost.count' do
delete micropost_path(@micropost)
end
assert_redirected_to login_url
end
test "should redirect destroy for wrong micropost" do
log_in_as(users(:michael))
micropost = microposts(:ants)
assert_no_difference 'Micropost.count' do
delete micropost_path(micropost)
end
assert_redirected_to root_url
end
end
# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
user: michael
tau_manifesto:
content: "Check out the @tauday site by @mhartl: https://tauday.com"
created_at: <%= 3.years.ago %>
user: michael
cat_video:
content: "Sad cats are sad: https://youtu.be/PKffm2uI4dk"
created_at: <%= 2.hours.ago %>
user: michael
most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>
user: michael
<% 30.times do |n| %>
micropost_<%= n %>:
content: <%= Faker::Lorem.sentence(word_count: 5) %>
created_at: <%= 42.days.ago %>
user: michael
<% end %>
ants:
content: "Oh, is that what you want? Because that's how you get ants!"
created_at: <%= 2.years.ago %>
user: archer
zone:
content: "Danger zone!"
created_at: <%= 3.days.ago %>
user: archer
tone:
content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
created_at: <%= 10.minutes.ago %>
user: lana
van:
content: "Dude, this van's, like, rolling probable cause."
created_at: <%= 4.hours.ago %>
user: lana
\ No newline at end of file
require "test_helper"
class MicropostsInterfaceTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
# Invalid submission
assert_no_difference 'Micropost.count' do
post microposts_path, params: { micropost: { content: "" } }
end
assert_select 'div#error_explanation'
assert_select 'a[href=?]', '/?page=2' # Correct pagination link
# Valid submission
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
post microposts_path, params: { micropost: { content: content } }
end
assert_redirected_to root_url
follow_redirect!
assert_match content, response.body
# Delete post
assert_select 'a', text: 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# Visit different user (no delete links)
get user_path(users(:archer))
assert_select 'a', text: 'delete', count: 0
end
end
require "test_helper"
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
@user = users(:michael)
end
test "profile display" do
get user_path(@user)
assert_template 'users/show'
assert_select 'title', full_title(@user.name)
assert_select 'h1', text: @user.name
assert_select 'h1>img.gravatar'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination'
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
end
require "test_helper"
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
@microposts = @user.microposts.build(content: "Example Content")
end
test "should be valid" do
assert @microposts.valid?
end
test "user id should be present" do
@microposts.user_id = nil
assert_not @microposts.valid?
end
test "content should be present" do
@microposts.content = ""
assert_not @microposts.valid?
end
test "contemt should be at most 140 charecter" do
@microposts.content = "a" * 141
assert_not @microposts.valid?
end
test "oder should be most recent first" do
assert_equal microposts(:most_recent), Micropost.first
end
end
......@@ -58,4 +58,12 @@ class UserTest < ActiveSupport::TestCase
test "authenticated? should return false for a user with nil digest" do
assert_not @user.authenticated?(:remember, '')
end
test "associated micropests should be destroyed" do
@user.save
@user.microposts.create!(content: "Example Content")
assert_difference 'Micropost.count', -1 do
@user.destroy
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment