From 4784f3fd13166d49bfe4802505968ec72dcc100b Mon Sep 17 00:00:00 2001 From: josher Date: Sat, 9 Jun 2018 08:07:24 +0800 Subject: [PATCH 01/10] set up required model,relationship, routes and fake datas --- app/controllers/application_controller.rb | 10 +++ app/models/followship.rb | 3 +- app/models/like.rb | 2 + app/models/reply.rb | 2 + app/models/tweet.rb | 4 +- app/models/user.rb | 11 +++- app/views/devise/registrations/edit.html.erb | 5 ++ app/views/devise/registrations/new.html.erb | 5 ++ config/routes.rb | 25 ++++++++ ...20180607125647_add_tweets_count_on_user.rb | 9 +++ ...0180607130502_add_replies_count_on_user.rb | 9 +++ db/schema.rb | 4 +- db/seeds.rb | 5 ++ lib/tasks/dev.rake | 64 ++++++++++++++++++- 14 files changed, 152 insertions(+), 6 deletions(-) create mode 100644 db/migrate/20180607125647_add_tweets_count_on_user.rb create mode 100644 db/migrate/20180607130502_add_replies_count_on_user.rb diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0da627f1a..49f7d1167 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,4 +4,14 @@ class ApplicationController < ActionController::Base # 請參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 # https://github.com/plataformatec/devise#strong-parameters # 注意有 sign_up 和 account_update 兩種參數要處理 + + before_action :authenticate_user! + before_action :configure_permitted_parameters, if: :devise_controller? + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) + devise_parameter_sanitizer.permit(:account_update, keys: [:name]) + end end diff --git a/app/models/followship.rb b/app/models/followship.rb index 1aed01396..087d9c397 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,4 +1,5 @@ class Followship < ApplicationRecord validates :following_id, uniqueness: { scope: :user_id } - + belongs_to :following, class_name: "User",counter_cache: :followers_count + belongs_to :user end diff --git a/app/models/like.rb b/app/models/like.rb index d99b93a32..2658e83e2 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,2 +1,4 @@ class Like < ApplicationRecord + belongs_to :user + belongs_to :tweet end diff --git a/app/models/reply.rb b/app/models/reply.rb index bae6f9463..76ef297fe 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,2 +1,4 @@ class Reply < ApplicationRecord + belongs_to :user, counter_cache: :replies_count + belongs_to :tweet end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 6715fada2..aeda722ff 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,4 +1,6 @@ class Tweet < ApplicationRecord validates_length_of :description, maximum: 140 - + belongs_to :user, counter_cache: :tweets_count + has_many :replies, dependent: :destroy + has_many :likes, dependent: :destroy end diff --git a/app/models/user.rb b/app/models/user.rb index 6b05b8c21..695f0c418 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,16 @@ class User < ApplicationRecord # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 - validates_presence_of :name + validates_presence_of :name, uniqueness: true # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + has_many :tweets + has_many :replies + has_many :likes + has_many :liked_tweets, through: :likes, source: :tweets + has_many :followships + has_many :followings, through: :followships + has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" + has_many :followers, through: :inverse_followships, source: :users + end diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 10ed32a9e..6f9615874 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -12,6 +12,11 @@
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
<% end %> +
+ <%= f.label :name %>
+ <%= f.email_field :name, autofocus: true, autocomplete: "off" %> +
+
<%= f.label :password %> (leave blank if you don't want to change it)
<%= f.password_field :password, autocomplete: "off" %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 602803cff..a52627ad2 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -8,6 +8,11 @@ <%= f.email_field :email, autofocus: true, autocomplete: "email" %>
+
+ <%= f.label :name %>
+ <%= f.email_field :name, autofocus: true, autocomplete: "off" %> +
+
<%= f.label :password %> <% if @minimum_password_length %> diff --git a/config/routes.rb b/config/routes.rb index 90856d4fe..547c56489 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -3,5 +3,30 @@ devise_for :users # 請依照專案指定規格來設定路由 + root "tweets#index" + resources :tweets, only: [:index, :create] do + member do + post :like + post :unlike + end + resources :replies, only: [:index, :create] + end + resources :users, only: [:edit] do + member do + get :followings + get :followers + end + collection do + get :likes + end + end + + resources :followships, only: [:create, :destroy] + + + namespace :admin do + resources :tweets, only: [:index, :destroy] + root "tweets#index" + end end diff --git a/db/migrate/20180607125647_add_tweets_count_on_user.rb b/db/migrate/20180607125647_add_tweets_count_on_user.rb new file mode 100644 index 000000000..0976dd951 --- /dev/null +++ b/db/migrate/20180607125647_add_tweets_count_on_user.rb @@ -0,0 +1,9 @@ +class AddTweetsCountOnUser < ActiveRecord::Migration[5.1] + def change + add_column :users, :tweets_count, :integer, default: 0 + + User.pluck(:id).each do |i| + User.reset_counters(i, :tweets) + end + end +end diff --git a/db/migrate/20180607130502_add_replies_count_on_user.rb b/db/migrate/20180607130502_add_replies_count_on_user.rb new file mode 100644 index 000000000..3238e8da0 --- /dev/null +++ b/db/migrate/20180607130502_add_replies_count_on_user.rb @@ -0,0 +1,9 @@ +class AddRepliesCountOnUser < ActiveRecord::Migration[5.1] + def change + add_column :users, :replies_count, :integer, default: 0 + + User.pluck(:id).each do |i| + User.reset_counters(i, :replies) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a6842c86..b99b5a47c 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180201102838) do +ActiveRecord::Schema.define(version: 20180607130502) do create_table "followships", force: :cascade do |t| t.integer "user_id" @@ -66,6 +66,8 @@ t.integer "likes_count", default: 0 t.string "role", default: "normal" t.integer "followers_count", default: 0 + t.integer "tweets_count", default: 0 + t.integer "replies_count", default: 0 t.index ["email"], name: "index_users_on_email", unique: true t.index ["name"], name: "index_users_on_name", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true diff --git a/db/seeds.rb b/db/seeds.rb index 1beea2acc..0ed3ac6c9 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,8 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) + + +# Default admin +User.create(email: "josh@twitter.com", password: "12345678", role: "admin", name: "josh") +puts "Default admin created!" \ No newline at end of file diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 9b1e87ae4..83d134141 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -1,6 +1,8 @@ namespace :dev do # 請先執行 rails dev:fake_user,可以產生 20 個資料完整的 User 紀錄 # 其他測試用的假資料請依需要自行撰寫 + task :rebuild => [:fake_user, "db:seed", :fake_tweet, :fake_reply, :fake_like, :fake_followship] + task fake_user: :environment do User.destroy_all 20.times do |i| @@ -9,9 +11,9 @@ namespace :dev do user = User.new( name: name, - email: "#{name}@example.co", + email: "#{name}@twitter.com", password: "12345678", - introduction: FFaker::Lorem::sentence(30), + introduction: FFaker::Lorem::sentence(20), avatar: file ) @@ -20,4 +22,62 @@ namespace :dev do end end + task fake_tweet: :environment do + Tweet.destroy_all + 30.times do |i| + user = User.all.sample + tweet = Tweet.create!( + description: FFaker::Lorem::sentence(8), + user: user, + created_at: FFaker::Time::datetime + ) + end + puts "Total #{Tweet.count}tweets created !" + end + + task fake_reply: :environment do + Reply.destroy_all + 50.times do |i| + user = User.all.sample + tweet = Tweet.all.sample + reply = Reply.create!( + tweet: tweet, + comment: FFaker::Lorem::sentence(3), + user: user, + created_at: FFaker::Time::datetime + ) + end + puts "Total #{Reply.count}replies created !" + end + + task fake_like: :environment do + Like.destroy_all + 50.times do |i| + user = User.all.sample + tweet = Tweet.all.sample + like = Like.create!( + tweet: tweet, + user: user, + created_at: FFaker::Time::datetime + ) + end + puts "Total #{Like.count}likes created !" + end + + task fake_followship: :environment do + Followship.destroy_all + 30.times do |i| + user = User.all.sample + following = User.all.sample + unless user == following + followship = Followship.create!( + user: user, + following: following, + created_at: FFaker::Time::datetime + ) + end + end + puts "Total #{Followship.count}followships created !" + end + end From 803b14fd90db77a4bb1f72629e771b47f8fd0469 Mon Sep 17 00:00:00 2001 From: josher Date: Sat, 9 Jun 2018 08:25:17 +0800 Subject: [PATCH 02/10] add bootstrap4 gem and set up required settings --- Gemfile | 4 ++++ Gemfile.lock | 16 +++++++++++++++- app/assets/javascripts/application.js | 3 +++ .../{application.css => application.scss} | 4 ++-- 4 files changed, 24 insertions(+), 3 deletions(-) rename app/assets/stylesheets/{application.css => application.scss} (94%) diff --git a/Gemfile b/Gemfile index c1f1ee3e2..57a5cb4ca 100644 --- a/Gemfile +++ b/Gemfile @@ -41,6 +41,10 @@ gem 'jbuilder', '~> 2.5' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'bootstrap', '~> 4.1.1' +gem 'sprockets-rails', '~> 3.1', '>= 3.1.1' +gem 'jquery-rails', '~> 4.3', '>= 4.3.3' + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] diff --git a/Gemfile.lock b/Gemfile.lock index e55e9522f..faf7de70b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,11 +41,17 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) + autoprefixer-rails (8.6.1) + execjs bcrypt (3.1.11) bcrypt (3.1.11-java) bcrypt (3.1.11-x64-mingw32) bcrypt (3.1.11-x86-mingw32) bindex (0.5.0) + bootstrap (4.1.1) + autoprefixer-rails (>= 6.0.3) + popper_js (>= 1.12.9, < 2) + sass (>= 3.5.2) builder (3.2.3) byebug (10.0.0) capybara (2.17.0) @@ -97,6 +103,10 @@ GEM jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + jquery-rails (4.3.3) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -124,6 +134,7 @@ GEM nokogiri (1.8.1-x86-mingw32) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) + popper_js (1.12.9) public_suffix (3.0.1) puma (3.11.2) puma (3.11.2-java) @@ -251,6 +262,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bootstrap (~> 4.1.1) byebug capybara (~> 2.13) carrierwave @@ -259,6 +271,7 @@ DEPENDENCIES factory_bot_rails ffaker jbuilder (~> 2.5) + jquery-rails (~> 4.3, >= 4.3.3) listen (>= 3.0.5, < 3.2) puma (~> 3.7) rails (~> 5.1.4) @@ -269,6 +282,7 @@ DEPENDENCIES shoulda-matchers (~> 3.1) spring spring-watcher-listen (~> 2.0.0) + sprockets-rails (~> 3.1, >= 3.1.1) sqlite3 turbolinks (~> 5) tzinfo-data @@ -276,4 +290,4 @@ DEPENDENCIES web-console (>= 3.3.0) BUNDLED WITH - 1.16.1 + 1.16.2 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 46b20359f..9380ae2e2 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,4 +12,7 @@ // //= require rails-ujs //= require turbolinks +//= require jquery3 +//= require popper +//= require bootstrap-sprockets //= require_tree . diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 94% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index d05ea0f51..24b0110a8 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,6 @@ * files in this directory. Styles in this file should be added after the last require_* statement. * It is generally better to create a new file per style scope. * - *= require_tree . - *= require_self */ + +@import 'bootstrap'; From 3c2e10cb13b6590ace693e5eba5ab654060e694d Mon Sep 17 00:00:00 2001 From: josher Date: Sun, 10 Jun 2018 08:22:56 +0800 Subject: [PATCH 03/10] create required view pages and add .admin? method for User model --- app/models/user.rb | 8 ++++++++ app/views/admin/tweets/index.html.erb | 0 app/views/admin/users/index.html.erb | 0 app/views/layouts/application.html.erb | 2 +- app/views/replies/index.html.erb | 0 app/views/tweets/index.html.erb | 0 app/views/users/edit.html.erb | 0 app/views/users/tweets.html.erb | 0 8 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 app/views/admin/tweets/index.html.erb create mode 100644 app/views/admin/users/index.html.erb create mode 100644 app/views/replies/index.html.erb create mode 100644 app/views/tweets/index.html.erb create mode 100644 app/views/users/edit.html.erb create mode 100644 app/views/users/tweets.html.erb diff --git a/app/models/user.rb b/app/models/user.rb index 695f0c418..41b2ad96c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -20,4 +20,12 @@ class User < ApplicationRecord has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" has_many :followers, through: :inverse_followships, source: :users + def admin? + if self.role == "admin" + true + else + false + end + end + end diff --git a/app/views/admin/tweets/index.html.erb b/app/views/admin/tweets/index.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 952cb7a1b..4017a3f05 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,7 +11,7 @@ <% if current_user %> <% if current_user&.admin? %> -
  • <%= link_to 'Admin Panel', admin_restaurants_path %>
  • +
  • <%= link_to 'Admin Panel', admin_tweets_path %>
  • <% end %>
  • <%= link_to('登出', destroy_user_session_path, method: :delete) %>
  • diff --git a/app/views/replies/index.html.erb b/app/views/replies/index.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/tweets/index.html.erb b/app/views/tweets/index.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb new file mode 100644 index 000000000..e69de29bb From 2d888b2201e0cdc0ae14b4cc42f5c431d897ea29 Mon Sep 17 00:00:00 2001 From: josher Date: Wed, 11 Jul 2018 08:00:37 +0800 Subject: [PATCH 04/10] user can leave tweets on website, also follow and unfollow other users --- app/views/users/followers.html.erb | 0 app/views/users/followings.html.erb | 0 app/views/users/likes.html.erb | 0 3 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 app/views/users/followers.html.erb create mode 100644 app/views/users/followings.html.erb create mode 100644 app/views/users/likes.html.erb diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/users/followings.html.erb b/app/views/users/followings.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb new file mode 100644 index 000000000..e69de29bb From 8cc26431eeaabaa9ce5cb637990b2aaba1c6694b Mon Sep 17 00:00:00 2001 From: josher Date: Fri, 20 Jul 2018 08:15:39 +0800 Subject: [PATCH 05/10] user can like or dislike the tweet --- app/views/share/_follow-btn.html.erb | 6 ++++++ app/views/share/_like-btn.html.erb | 5 +++++ app/views/share/_navbar.html.erb | 23 +++++++++++++++++++++++ app/views/share/_user-panel.html.erb | 21 +++++++++++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 app/views/share/_follow-btn.html.erb create mode 100644 app/views/share/_like-btn.html.erb create mode 100644 app/views/share/_navbar.html.erb create mode 100644 app/views/share/_user-panel.html.erb diff --git a/app/views/share/_follow-btn.html.erb b/app/views/share/_follow-btn.html.erb new file mode 100644 index 000000000..016a304ce --- /dev/null +++ b/app/views/share/_follow-btn.html.erb @@ -0,0 +1,6 @@ +<% if current_user.is_following?(object) %> + <%= link_to 'Unfollow', followship_path(object.id), method: :delete, class: 'btn btn-danger' %> +<% else %> + <%= link_to 'Follow', followships_path(following_id: object.id), method: :post, class: 'btn btn-primary' %> +<% end %> + diff --git a/app/views/share/_like-btn.html.erb b/app/views/share/_like-btn.html.erb new file mode 100644 index 000000000..12fb17bb1 --- /dev/null +++ b/app/views/share/_like-btn.html.erb @@ -0,0 +1,5 @@ +<% if current_user.already_liked?(object) %> + <%= link_to 'Unlike', unlike_tweet_path(object), method: :post, class: 'btn btn-danger' %> +<% else %> + <%= link_to 'Like', like_tweet_path(object), method: :post, class: 'btn btn-info' %> +<% end %> \ No newline at end of file diff --git a/app/views/share/_navbar.html.erb b/app/views/share/_navbar.html.erb new file mode 100644 index 000000000..49a439238 --- /dev/null +++ b/app/views/share/_navbar.html.erb @@ -0,0 +1,23 @@ + diff --git a/app/views/share/_user-panel.html.erb b/app/views/share/_user-panel.html.erb new file mode 100644 index 000000000..43f8403ff --- /dev/null +++ b/app/views/share/_user-panel.html.erb @@ -0,0 +1,21 @@ +
    + <%= image_tag @user.avatar, class: 'img-fluid' if @user %>
    +

    <%= @user.name %>

    +

    <%= @user.introduction %>

    + <%= link_to "Tweets #{@user.tweets_count}", tweets_user_path(@user), class: 'btn btn-outline-primary' %>
    + +
    + <%= link_to "Followers #{@user.followers_count}", followers_user_path(@user), class: 'btn btn-outline-success' %> +
    +
    + <%= link_to "Followings #{@user.followings.size}", followings_user_path(@user), class: 'btn btn-outline-danger' %> +
    +
    + <%= link_to "Likes #{@user.likes_count}", likes_user_path(@user), class: 'btn btn-outline-warning' %> +
    + <% if @user == current_user %> + <%= link_to 'Edit Profile', edit_user_path(@user), class: 'btn btn-primary' %> + <% else %> + <%= render partial: 'share/follow-btn', locals: {object: @user} %> + <% end %> +
    From dd942d5c0d274209b14de8bb28d8e425af04b0b4 Mon Sep 17 00:00:00 2001 From: josher Date: Sat, 21 Jul 2018 14:28:24 +0800 Subject: [PATCH 06/10] add font-awesome CDN and modify like-btn as thumb icon --- Gemfile | 3 ++ Gemfile.lock | 11 ++++++ app/assets/stylesheets/application.scss | 2 + app/controllers/followships_controller.rb | 13 +++++++ app/controllers/replies_controller.rb | 17 +++++++++ app/controllers/tweets_controller.rb | 33 +++++++++++++++- app/controllers/users_controller.rb | 18 +++++++-- app/models/like.rb | 2 +- app/models/reply.rb | 2 +- app/models/tweet.rb | 3 ++ app/models/user.rb | 12 +++++- app/views/layouts/application.html.erb | 39 ++++++++++++------- app/views/replies/index.html.erb | 38 +++++++++++++++++++ app/views/share/_like-btn.html.erb | 4 +- app/views/tweets/index.html.erb | 46 +++++++++++++++++++++++ app/views/users/followers.html.erb | 21 +++++++++++ app/views/users/followings.html.erb | 29 ++++++++++++++ app/views/users/likes.html.erb | 8 ++++ app/views/users/tweets.html.erb | 19 ++++++++++ config/routes.rb | 5 +-- lib/tasks/dev.rake | 5 ++- 21 files changed, 300 insertions(+), 30 deletions(-) diff --git a/Gemfile b/Gemfile index 57a5cb4ca..bc7a0e3bd 100644 --- a/Gemfile +++ b/Gemfile @@ -44,6 +44,9 @@ gem 'jbuilder', '~> 2.5' gem 'bootstrap', '~> 4.1.1' gem 'sprockets-rails', '~> 3.1', '>= 3.1.1' gem 'jquery-rails', '~> 4.3', '>= 4.3.3' +gem 'pry', '~> 0.11.3' + + group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console diff --git a/Gemfile.lock b/Gemfile.lock index faf7de70b..8a4b9a92d 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -67,6 +67,7 @@ GEM mime-types (>= 1.16) childprocess (0.8.0) ffi (~> 1.0, >= 1.0.11) + coderay (1.1.2) coffee-rails (4.2.2) coffee-script (>= 2.2.0) railties (>= 4.0.0) @@ -135,6 +136,13 @@ GEM mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) popper_js (1.12.9) + pry (0.11.3) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + pry (0.11.3-java) + coderay (~> 1.1.0) + method_source (~> 0.9.0) + spoon (~> 0.0) public_suffix (3.0.1) puma (3.11.2) puma (3.11.2-java) @@ -210,6 +218,8 @@ GEM rubyzip (~> 1.0) shoulda-matchers (3.1.2) activesupport (>= 4.0.0) + spoon (0.0.6) + ffi spring (2.0.2) activesupport (>= 4.2) spring-watcher-listen (2.0.1) @@ -273,6 +283,7 @@ DEPENDENCIES jbuilder (~> 2.5) jquery-rails (~> 4.3, >= 4.3.3) listen (>= 3.0.5, < 3.2) + pry (~> 0.11.3) puma (~> 3.7) rails (~> 5.1.4) rails-controller-testing diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 24b0110a8..3c5c92a83 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -13,3 +13,5 @@ */ @import 'bootstrap'; + + diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 05f01b552..2c2e35da2 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,7 +1,20 @@ class FollowshipsController < ApplicationController def create + @followship = Followship.new(user_id: current_user.id, following_id: params[:following_id]) + if @followship.save + redirect_back(fallback_location: root_path) + else + flash[:alert] = "Something went wrong here...please try again" + end end def destroy + @followship = Followship.find_by(user_id: current_user.id, following_id: params[:id]) + if @followship + @followship.destroy + else + flash[:alert] = "Something went wrong here...please try again" + end + redirect_back(fallback_location: root_path) end end diff --git a/app/controllers/replies_controller.rb b/app/controllers/replies_controller.rb index a9b6a315b..5dc259700 100644 --- a/app/controllers/replies_controller.rb +++ b/app/controllers/replies_controller.rb @@ -1,9 +1,26 @@ class RepliesController < ApplicationController def index + @tweet = Tweet.find(params[:tweet_id]) + @replies = @tweet.replies.order('created_at DESC') + @user = current_user + @reply = Reply.new end def create + @tweet = Tweet.find(params[:tweet_id]) + @reply = @tweet.replies.build(reply_params) + @reply.user = current_user + if @reply.save + flash[:notice] = "Reply Created !" + else + flash[:alert] = @reply.errors.full_messages + end + redirect_to tweet_replies_path end + private + def reply_params + params.require(:reply).permit(:comment, :tweet_id, :user_id) + end end diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index ad14115c1..6644610bf 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,16 +1,47 @@ class TweetsController < ApplicationController + before_action :find_tweet, only: [:like, :unlike] def index - @users # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @users = User.order(followers_count: :desc).first(10) # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweet = Tweet.new + @tweets = Tweet.order(created_at: :desc) end def create + @tweet = current_user.tweets.build(tweet_params) + @tweets = Tweet.order(created_at: :desc) + if @tweet.save + flash[:notice] = 'Tweet is created successfully !' + render "index" + else + flash[:alert] = @tweet.errors.messages + redirect_to tweets_path + end end def like + @like = Like.new(user_id: current_user.id, tweet_id: @tweet.id) + if @like.save! + redirect_back(fallback_location: root_path) + else + flash[:alert] = like.errors.messages + end end def unlike + @like = Like.find_by(user_id: current_user.id, tweet_id: @tweet.id) + if @like + @like.destroy + redirect_back(fallback_location: root_path) + end end + private + def tweet_params + params.require(:tweet).permit(:description, :user_id) + end + + def find_tweet + @tweet = Tweet.find(params[:id]) + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 750e3c6b5..f658a0493 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,6 +1,7 @@ class UsersController < ApplicationController - + before_action :find_user, only: [:tweets, :followings, :followers, :likes] def tweets + @tweets = @user.tweets end def edit @@ -10,15 +11,24 @@ def update end def followings - @followings # 基於測試規格,必須講定變數名稱 + @followings = @user.followings + # 基於測試規格,必須講定變數名稱 end def followers - @followers # 基於測試規格,必須講定變數名稱 + @followers = @user.followers + # 基於測試規格,必須講定變數名稱 end def likes - @likes # 基於測試規格,必須講定變數名稱 + @likes = @user.liked_tweets + # 基於測試規格,必須講定變數名稱 + end + + private + def find_user + @user = User.find(params[:id]) end + end diff --git a/app/models/like.rb b/app/models/like.rb index 2658e83e2..f141622f3 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,4 +1,4 @@ class Like < ApplicationRecord belongs_to :user - belongs_to :tweet + belongs_to :tweet, counter_cache: :likes_count end diff --git a/app/models/reply.rb b/app/models/reply.rb index 76ef297fe..df5664581 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,4 +1,4 @@ class Reply < ApplicationRecord belongs_to :user, counter_cache: :replies_count - belongs_to :tweet + belongs_to :tweet, counter_cache: :replies_count end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index aeda722ff..148bc96b4 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -3,4 +3,7 @@ class Tweet < ApplicationRecord belongs_to :user, counter_cache: :tweets_count has_many :replies, dependent: :destroy has_many :likes, dependent: :destroy + + + end diff --git a/app/models/user.rb b/app/models/user.rb index 41b2ad96c..5e8c0649b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -14,11 +14,11 @@ class User < ApplicationRecord has_many :tweets has_many :replies has_many :likes - has_many :liked_tweets, through: :likes, source: :tweets + has_many :liked_tweets, through: :likes, source: :tweet has_many :followships has_many :followings, through: :followships has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" - has_many :followers, through: :inverse_followships, source: :users + has_many :followers, through: :inverse_followships, source: :user def admin? if self.role == "admin" @@ -28,4 +28,12 @@ def admin? end end + def is_following?(user) + self.followings.include?(user) + end + + def already_liked?(tweet) + self.liked_tweets.include?(tweet) + end + end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 4017a3f05..8d8bc0db8 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,23 +6,34 @@ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + - <% if current_user %> - <% if current_user&.admin? %> -
  • <%= link_to 'Admin Panel', admin_tweets_path %>
  • - <% end %> -
  • -
  • <%= link_to('登出', destroy_user_session_path, method: :delete) %>
  • -
  • <%= link_to('修改個人資料', edit_user_path(current_user)) %>
  • -
  • <%= link_to('修改密碼', edit_user_registration_path) %>
  • - <% else %> -
  • <%= link_to('註冊', new_user_registration_path) %>
  • -
  • <%= link_to('登入', new_user_session_path) %>
  • +
    + <%= render 'share/navbar' %> +
    + + <% if flash[:notice] %> +
    + +
    <% end %> -

    <%= notice %>

    -

    <%= alert %>

    - <%= yield %> + + <% if flash[:alert] %> +
    + +
    + <% end %> + +
    + <%= yield %> +
    diff --git a/app/views/replies/index.html.erb b/app/views/replies/index.html.erb index e69de29bb..de097c077 100644 --- a/app/views/replies/index.html.erb +++ b/app/views/replies/index.html.erb @@ -0,0 +1,38 @@ +
    +
    + <%= render partial: 'share/user-panel' %> +
    +
    +

    Tweets

    +
    +
    + <%= image_tag @tweet.user.avatar, class: 'img-fluid' %> +
    +
    + <%= @tweet.user.name %><%= ", #{@tweet.created_at}" %>
    + <%= @tweet.description %>
    + <%= link_to "Reply (#{@tweet.replies.count})", tweet_replies_path(@tweet), class: 'btn btn-info'%> +
    +
    + +

    Replies

    + <% @replies.each do |reply| %> +
    +
    + <%= image_tag reply.user.avatar, class: 'img-fluid' %> +
    +
    + <%= link_to reply.user.name, tweets_user_path(reply.user) %><%= ", #{reply.created_at}" %>
    + <%= reply.comment %>
    +
    +
    + <% end %> +
    + <%= form_for [@tweet,@reply] do |f| %> + <%= f.text_area :comment, class: 'form-control', placeholder: 'leave your comments', rows: '3' %>
    + <%= f.submit 'Submit' , class: 'btn btn-primary' %> + <% end %> +
    + +
    +
    diff --git a/app/views/share/_like-btn.html.erb b/app/views/share/_like-btn.html.erb index 12fb17bb1..c2366c795 100644 --- a/app/views/share/_like-btn.html.erb +++ b/app/views/share/_like-btn.html.erb @@ -1,5 +1,5 @@ <% if current_user.already_liked?(object) %> - <%= link_to 'Unlike', unlike_tweet_path(object), method: :post, class: 'btn btn-danger' %> + <%= link_to " (#{object.likes_count})".html_safe, unlike_tweet_path(object), method: :post %> <% else %> - <%= link_to 'Like', like_tweet_path(object), method: :post, class: 'btn btn-info' %> + <%= link_to " (#{object.likes_count})".html_safe, like_tweet_path(object), method: :post %> <% end %> \ No newline at end of file diff --git a/app/views/tweets/index.html.erb b/app/views/tweets/index.html.erb index e69de29bb..4d1a602a2 100644 --- a/app/views/tweets/index.html.erb +++ b/app/views/tweets/index.html.erb @@ -0,0 +1,46 @@ +
    +
    + <%= form_for @tweet do |f| %> + <%= f.text_area :description, class: 'form-control my-3 py-3', placeholder: 'What is in your mind? ', maxlength: 140, rows: '8' %> + <%= f.submit 'Tweet' %> + <% end %> + <% @tweets.each do |tweet| %> +
    +
    + <%= image_tag tweet.user.avatar, class: 'img-fluid' %> +
    +
    +
    +
    + <%= tweet.user.name %><%= ", #{tweet.created_at}" %>
    + <%= tweet.description %> +
    +
    + <%= link_to "Reply (#{tweet.replies.count})", tweet_replies_path(tweet_id: tweet.id), class: 'btn btn-info'%> +
    +
    + <%= render partial: 'share/like-btn' , locals: {object: tweet}%> +
    +
    +
    +
    + <% end %> +
    + +
    +
    + <% @users.each do |user| %> +
    +
    + <%= image_tag user.avatar, class: 'img-fluid' %> +
    +
    + <%= link_to user.name, tweets_user_path(user) %>
    + <%= user.introduction %>
    + <%= render partial: 'share/follow-btn' , locals: {object: user}%> +
    +
    + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb index e69de29bb..9dbfaea16 100644 --- a/app/views/users/followers.html.erb +++ b/app/views/users/followers.html.erb @@ -0,0 +1,21 @@ +
    +
    + <%= render partial: 'share/user-panel' %> +
    +
    +
    + <% @followers.each do |follower| %> +
    +
    + <%= image_tag follower.avatar, class: 'img-fluid' %> +
    +
    + <%= link_to follower.name, tweets_user_path(follower) %>
    + <%= follower.introduction %>
    + <%= render partial: 'share/follow-btn' , locals: {object: follower}%> +
    +
    + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/followings.html.erb b/app/views/users/followings.html.erb index e69de29bb..025819986 100644 --- a/app/views/users/followings.html.erb +++ b/app/views/users/followings.html.erb @@ -0,0 +1,29 @@ +
    +
    + <%= render partial: 'share/user-panel' %> +
    +
    +
    + <% @followings.each do |following| %> +
    +
    + <%= image_tag following.avatar, class: 'img-fluid' %> +
    +
    +
    +
    + <%= link_to following.name, tweets_user_path(following) %> +
    +
    + <%= following.introduction %> +
    +
    + <%= render partial: 'share/follow-btn' ,locals: {object: following}%> +
    +
    +
    +
    + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb index e69de29bb..159f41e4f 100644 --- a/app/views/users/likes.html.erb +++ b/app/views/users/likes.html.erb @@ -0,0 +1,8 @@ +
    +
    + <%= render partial: 'share/user-panel' %> +
    +
    + +
    +
    \ No newline at end of file diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb index e69de29bb..990c597b0 100644 --- a/app/views/users/tweets.html.erb +++ b/app/views/users/tweets.html.erb @@ -0,0 +1,19 @@ +
    +
    + <%= render partial: 'share/user-panel' %> +
    +
    + <% @tweets.each do |tweet| %> +
    +
    + <%= image_tag tweet.user.avatar, class: 'img-fluid' %> +
    +
    + <%= tweet.user.name %><%= ", #{tweet.created_at}" %>
    + <%= tweet.description %>
    + <%= link_to "Reply (#{tweet.replies.count})", tweet_replies_path(tweet), class: 'btn btn-info'%> +
    +
    + <% end %> +
    +
    \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 547c56489..616e99929 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,9 +16,8 @@ member do get :followings get :followers - end - collection do - get :likes + get :tweets + get :likes end end diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 83d134141..3151d9230 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -1,8 +1,9 @@ namespace :dev do # 請先執行 rails dev:fake_user,可以產生 20 個資料完整的 User 紀錄 # 其他測試用的假資料請依需要自行撰寫 - task :rebuild => [:fake_user, "db:seed", :fake_tweet, :fake_reply, :fake_like, :fake_followship] - + task :build => ["tmp:clear", "log:clear", "db:drop", "db:create", "db:migrate"] + task :rebuild => [ "dev:build", :fake_user,"db:seed", :fake_tweet, :fake_reply, :fake_like, :fake_followship] + task fake_user: :environment do User.destroy_all 20.times do |i| From 306d7bf6f1dbd7a751dd6eafa3450af907933eae Mon Sep 17 00:00:00 2001 From: josher Date: Sat, 21 Jul 2018 15:18:02 +0800 Subject: [PATCH 07/10] user can edit and update his/her own personal profile info --- app/controllers/users_controller.rb | 11 ++++++++++- app/views/share/_navbar.html.erb | 6 +++--- app/views/users/edit.html.erb | 21 +++++++++++++++++++++ config/routes.rb | 2 +- 4 files changed, 35 insertions(+), 5 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f658a0493..8655cb1c3 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,5 +1,5 @@ class UsersController < ApplicationController - before_action :find_user, only: [:tweets, :followings, :followers, :likes] + before_action :find_user, only: [:edit, :update, :tweets, :followings, :followers, :likes] def tweets @tweets = @user.tweets end @@ -8,6 +8,12 @@ def edit end def update + if @user.update(user_profile_params) + flash[:notice] = "User Profile Udpate Successfully !" + else + flash[:alert] = @user.error.full_messsages + end + redirect_to tweets_user_path(@user) end def followings @@ -30,5 +36,8 @@ def find_user @user = User.find(params[:id]) end + def user_profile_params + params.require(:user).permit(:introduction, :avatar, :name) + end end diff --git a/app/views/share/_navbar.html.erb b/app/views/share/_navbar.html.erb index 49a439238..aa768b564 100644 --- a/app/views/share/_navbar.html.erb +++ b/app/views/share/_navbar.html.erb @@ -9,14 +9,14 @@ <% end %>
    diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb index e69de29bb..f494ff181 100644 --- a/app/views/users/edit.html.erb +++ b/app/views/users/edit.html.erb @@ -0,0 +1,21 @@ +
    +
    + <%= form_for @user do |f| %> +
    + <% if @user.avatar.blank? %> + + <% else %> + <%= image_tag @user.avatar, width: '300px' %> + <% end %> +

    + <%= @user.introduction %>

    + <%= f.file_field :avatar %> +
    +
    + <%= f.text_field :name, class: "form-control" ,placeholder: "Name" %>
    + <%= f.text_area :introduction, class: "form-control" ,placeholder: "Write something about your self !" %>
    + <%= f.submit 'Update' %> +
    + <% end %> +
    +
    \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 616e99929..60db48dc3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -12,7 +12,7 @@ resources :replies, only: [:index, :create] end - resources :users, only: [:edit] do + resources :users, only: [:edit, :update] do member do get :followings get :followers From f06f31e560aa02e966dd12a15468f38d2d5674cb Mon Sep 17 00:00:00 2001 From: josher Date: Thu, 30 Aug 2018 21:24:26 +0800 Subject: [PATCH 08/10] add admin panel and fix bugs for passing rspec test --- Gemfile | 1 + Gemfile.lock | 13 +++++++ app/controllers/admin/tweets_controller.rb | 16 +++++++++ app/controllers/admin/users_controller.rb | 11 ++++++ app/controllers/followships_controller.rb | 7 ++-- app/controllers/tweets_controller.rb | 13 ++++--- app/controllers/users_controller.rb | 13 ++++--- app/helpers/application_helper.rb | 3 ++ app/models/followship.rb | 5 +-- app/models/like.rb | 2 +- app/models/tweet.rb | 4 +-- app/models/user.rb | 7 +++- app/views/admin/tweets/index.html.erb | 34 +++++++++++++++++++ app/views/admin/users/index.html.erb | 33 ++++++++++++++++++ app/views/kaminari/_first_page.html.erb | 3 ++ app/views/kaminari/_gap.html.erb | 3 ++ app/views/kaminari/_last_page.html.erb | 3 ++ app/views/kaminari/_next_page.html.erb | 3 ++ app/views/kaminari/_page.html.erb | 9 +++++ app/views/kaminari/_paginator.html.erb | 17 ++++++++++ app/views/kaminari/_prev_page.html.erb | 3 ++ app/views/share/_navbar.html.erb | 12 +++++++ app/views/share/_user-panel.html.erb | 2 +- app/views/tweets/index.html.erb | 4 +++ app/views/users/followers.html.erb | 1 + app/views/users/likes.html.erb | 13 ++++++- app/views/users/tweets.html.erb | 1 + config/routes.rb | 3 +- ...0801222347_add_followings_count_to_user.rb | 7 ++++ db/schema.rb | 3 +- db/seeds.rb | 2 +- lib/tasks/dev.rake | 16 ++++----- 32 files changed, 232 insertions(+), 35 deletions(-) create mode 100644 app/views/kaminari/_first_page.html.erb create mode 100644 app/views/kaminari/_gap.html.erb create mode 100644 app/views/kaminari/_last_page.html.erb create mode 100644 app/views/kaminari/_next_page.html.erb create mode 100644 app/views/kaminari/_page.html.erb create mode 100644 app/views/kaminari/_paginator.html.erb create mode 100644 app/views/kaminari/_prev_page.html.erb create mode 100644 db/migrate/20180801222347_add_followings_count_to_user.rb diff --git a/Gemfile b/Gemfile index bc7a0e3bd..405ef8fba 100644 --- a/Gemfile +++ b/Gemfile @@ -45,6 +45,7 @@ gem 'bootstrap', '~> 4.1.1' gem 'sprockets-rails', '~> 3.1', '>= 3.1.1' gem 'jquery-rails', '~> 4.3', '>= 4.3.3' gem 'pry', '~> 0.11.3' +gem 'kaminari', '~> 1.1', '>= 1.1.1' diff --git a/Gemfile.lock b/Gemfile.lock index 8a4b9a92d..01211fb12 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -108,6 +108,18 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + kaminari (1.1.1) + activesupport (>= 4.1.0) + kaminari-actionview (= 1.1.1) + kaminari-activerecord (= 1.1.1) + kaminari-core (= 1.1.1) + kaminari-actionview (1.1.1) + actionview + kaminari-core (= 1.1.1) + kaminari-activerecord (1.1.1) + activerecord + kaminari-core (= 1.1.1) + kaminari-core (1.1.1) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -282,6 +294,7 @@ DEPENDENCIES ffaker jbuilder (~> 2.5) jquery-rails (~> 4.3, >= 4.3.3) + kaminari (~> 1.1, >= 1.1.1) listen (>= 3.0.5, < 3.2) pry (~> 0.11.3) puma (~> 3.7) diff --git a/app/controllers/admin/tweets_controller.rb b/app/controllers/admin/tweets_controller.rb index 24a57566c..ae034e69c 100644 --- a/app/controllers/admin/tweets_controller.rb +++ b/app/controllers/admin/tweets_controller.rb @@ -1,7 +1,23 @@ class Admin::TweetsController < Admin::BaseController + before_action :authenticate_user! + before_action :authenticate_admin + def index + @tweets = Tweet.order('created_at DESC').page(params[:page]).per(15) + render :index end def destroy + @tweet = Tweet.find(params[:id]) + @tweet.destroy + redirect_to admin_root_path + end + + + private + def authenticate_admin + unless current_user.role == 'admin' + redirect_back(fallback_location: root_path, alert: "You are Not admin !") + end end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3ba9f0a36..3976bfed2 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,15 @@ class Admin::UsersController < Admin::BaseController + before_action :authenticate_user! + before_action :authenticate_admin + def index + @users = User.all.order(tweets_count: :desc).page(params[:page]).per(15) + end + + private + def authenticate_admin + unless current_user.role == 'admin' + redirect_back(fallback_location: root_path, alert: "You are Not admin !") + end end end diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 2c2e35da2..3af5ed9ea 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,11 +1,12 @@ class FollowshipsController < ApplicationController def create @followship = Followship.new(user_id: current_user.id, following_id: params[:following_id]) - if @followship.save - redirect_back(fallback_location: root_path) + if @followship.user_id == @followship.following_id + flash[:alert] = "Something went wrong here...you can not follow yourself..." else - flash[:alert] = "Something went wrong here...please try again" + @followship.save end + redirect_back(fallback_location: root_path) end def destroy diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index 6644610bf..486c091b2 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -4,27 +4,26 @@ class TweetsController < ApplicationController def index @users = User.order(followers_count: :desc).first(10) # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 @tweet = Tweet.new - @tweets = Tweet.order(created_at: :desc) + @tweets = Tweet.order('created_at DESC').page(params[:page]).per(15) end def create @tweet = current_user.tweets.build(tweet_params) - @tweets = Tweet.order(created_at: :desc) - if @tweet.save + if @tweet.save! flash[:notice] = 'Tweet is created successfully !' - render "index" else flash[:alert] = @tweet.errors.messages - redirect_to tweets_path end + redirect_to tweets_path end def like @like = Like.new(user_id: current_user.id, tweet_id: @tweet.id) if @like.save! - redirect_back(fallback_location: root_path) + redirect_to tweets_path else flash[:alert] = like.errors.messages + redirect_back(fallback_location: root_path) end end @@ -32,7 +31,7 @@ def unlike @like = Like.find_by(user_id: current_user.id, tweet_id: @tweet.id) if @like @like.destroy - redirect_back(fallback_location: root_path) + redirect_to tweets_path end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 8655cb1c3..d15c82fcd 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,10 +1,15 @@ class UsersController < ApplicationController before_action :find_user, only: [:edit, :update, :tweets, :followings, :followers, :likes] def tweets - @tweets = @user.tweets + @tweets = @user.tweets.order("created_at DESC") end def edit + if @user == current_user + render :edit + else + redirect_to tweets_user_path(@user) + end end def update @@ -17,17 +22,17 @@ def update end def followings - @followings = @user.followings + @followings = @user.followings.order(followers_count: :desc) # 基於測試規格,必須講定變數名稱 end def followers - @followers = @user.followers + @followers = @user.followers.order(tweets_count: :desc) # 基於測試規格,必須講定變數名稱 end def likes - @likes = @user.liked_tweets + @likes = @user.liked_tweets.order(likes_count: :desc) # 基於測試規格,必須講定變數名稱 end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be7945..dbf5ab368 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,5 @@ module ApplicationHelper + def in_admin_panel? + request.fullpath.include?('admin') + end end diff --git a/app/models/followship.rb b/app/models/followship.rb index 087d9c397..36624864b 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,5 +1,6 @@ class Followship < ApplicationRecord - validates :following_id, uniqueness: { scope: :user_id } + belongs_to :following, class_name: "User",counter_cache: :followers_count - belongs_to :user + belongs_to :user, counter_cache: :followings_count + validates_uniqueness_of :following_id, scope: :user_id end diff --git a/app/models/like.rb b/app/models/like.rb index f141622f3..12938d457 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,4 +1,4 @@ class Like < ApplicationRecord - belongs_to :user + belongs_to :user, counter_cache: :likes_count belongs_to :tweet, counter_cache: :likes_count end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 148bc96b4..92c69f47f 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,9 +1,7 @@ class Tweet < ApplicationRecord + validates_presence_of :description validates_length_of :description, maximum: 140 belongs_to :user, counter_cache: :tweets_count has_many :replies, dependent: :destroy has_many :likes, dependent: :destroy - - - end diff --git a/app/models/user.rb b/app/models/user.rb index 5e8c0649b..ab371c70b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,10 @@ class User < ApplicationRecord # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 - validates_presence_of :name, uniqueness: true + validates_presence_of :name + validates :name, uniqueness: {case_sensitive: true} + + # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) has_many :tweets @@ -19,6 +22,7 @@ class User < ApplicationRecord has_many :followings, through: :followships has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" has_many :followers, through: :inverse_followships, source: :user + scope :recent, -> {order("created_at DESC")} def admin? if self.role == "admin" @@ -36,4 +40,5 @@ def already_liked?(tweet) self.liked_tweets.include?(tweet) end + end diff --git a/app/views/admin/tweets/index.html.erb b/app/views/admin/tweets/index.html.erb index e69de29bb..3c5eaaf9f 100644 --- a/app/views/admin/tweets/index.html.erb +++ b/app/views/admin/tweets/index.html.erb @@ -0,0 +1,34 @@ +
    +
    +

    Simple Twitter Admin Panel + <%= link_to 'Tweets', admin_tweets_path, class: 'btn btn-primary' %> | <%= link_to 'Users', admin_users_path, class: 'btn btn-info' %> +

    +
    + + + + + + + + + + <% @tweets.each do |tweet| %> + + + + + + + + + <% end %> +
    UsernameAvatarTweetsReplies
    <%= tweet.user.name %><%= image_tag tweet.user.avatar, size:100 %><%= tweet.description %>
    <%= link_to 'Delete', admin_tweet_path(tweet), method: :delete, data: {confirm:"Are you sure?"}, class: 'btn btn-danger' %>
    <% tweet.replies.each do |reply| %> + <%= reply.user.name %>:<%=reply.comment %>
    + <% end %> +
    +
    + +
    + <%= paginate @tweets %> +
    \ No newline at end of file diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb index e69de29bb..97146cd0b 100644 --- a/app/views/admin/users/index.html.erb +++ b/app/views/admin/users/index.html.erb @@ -0,0 +1,33 @@ +
    +

    Simple Twitter Admin Panel + <%= link_to 'Tweets', admin_tweets_path, class: 'btn btn-primary' %> | <%= link_to 'Users', admin_users_path, class: 'btn btn-info' %> +

    + + + + + + + + + + + + <% @users.each do |user| %> + + + + + + + + + + + <% end %> +
    UsernameAvatarTweets CountFollowers CountFollowings CountLikes Count
    <%= user.name %><%= image_tag user.avatar, size: 100 %><%= user.tweets_count ||= 0 %><%= user.followers_count ||= 0 %><%= user.followings.count ||= 0%><%= user.likes_count ||= 0%>
    +
    + +
    + <%= paginate @users %> +
    \ No newline at end of file diff --git a/app/views/kaminari/_first_page.html.erb b/app/views/kaminari/_first_page.html.erb new file mode 100644 index 000000000..cc107ec6e --- /dev/null +++ b/app/views/kaminari/_first_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link' %> +
  • diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb new file mode 100644 index 000000000..d241fc4eb --- /dev/null +++ b/app/views/kaminari/_gap.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link' %> +
  • diff --git a/app/views/kaminari/_last_page.html.erb b/app/views/kaminari/_last_page.html.erb new file mode 100644 index 000000000..9a7584eb4 --- /dev/null +++ b/app/views/kaminari/_last_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, remote: remote, class: 'page-link' %> +
  • diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb new file mode 100644 index 000000000..7ca72a819 --- /dev/null +++ b/app/views/kaminari/_next_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.last?, raw(t 'views.pagination.next'), url, rel: 'next', remote: remote, class: 'page-link' %> +
  • diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb new file mode 100644 index 000000000..c8f03f236 --- /dev/null +++ b/app/views/kaminari/_page.html.erb @@ -0,0 +1,9 @@ +<% if page.current? %> +
  • + <%= content_tag :a, page, data: { remote: remote }, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)), class: 'page-link' %> +
  • +<% else %> +
  • + <%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)), class: 'page-link' %> +
  • +<% end %> diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb new file mode 100644 index 000000000..bb5ed0ff3 --- /dev/null +++ b/app/views/kaminari/_paginator.html.erb @@ -0,0 +1,17 @@ +<%= paginator.render do %> + +<% end %> diff --git a/app/views/kaminari/_prev_page.html.erb b/app/views/kaminari/_prev_page.html.erb new file mode 100644 index 000000000..c7803fac4 --- /dev/null +++ b/app/views/kaminari/_prev_page.html.erb @@ -0,0 +1,3 @@ +
  • + <%= link_to_unless current_page.first?, raw(t 'views.pagination.previous'), url, rel: 'prev', remote: remote, class: 'page-link' %> +
  • diff --git a/app/views/share/_navbar.html.erb b/app/views/share/_navbar.html.erb index aa768b564..40f5deb54 100644 --- a/app/views/share/_navbar.html.erb +++ b/app/views/share/_navbar.html.erb @@ -7,6 +7,18 @@ + + +
    + <%= paginate @tweets %>
    \ No newline at end of file diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb index 9dbfaea16..4e5a742c1 100644 --- a/app/views/users/followers.html.erb +++ b/app/views/users/followers.html.erb @@ -13,6 +13,7 @@ <%= link_to follower.name, tweets_user_path(follower) %>
    <%= follower.introduction %>
    <%= render partial: 'share/follow-btn' , locals: {object: follower}%> + <% end %> diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb index 159f41e4f..d05438dfc 100644 --- a/app/views/users/likes.html.erb +++ b/app/views/users/likes.html.erb @@ -3,6 +3,17 @@ <%= render partial: 'share/user-panel' %>
    - + <% @likes.each do |like| %> +
    +
    + <%= image_tag like.user.avatar, class: 'img-fluid' %> +
    +
    + <%= like.user.name %><%= ", #{like.created_at}" %>
    + <%= like.description %>
    + <%= link_to "Reply (#{like.replies.count})", tweet_replies_path(like), class: 'btn btn-info'%> +
    +
    + <% end %>
    \ No newline at end of file diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb index 990c597b0..5a7a68edb 100644 --- a/app/views/users/tweets.html.erb +++ b/app/views/users/tweets.html.erb @@ -12,6 +12,7 @@ <%= tweet.user.name %><%= ", #{tweet.created_at}" %>
    <%= tweet.description %>
    <%= link_to "Reply (#{tweet.replies.count})", tweet_replies_path(tweet), class: 'btn btn-info'%> + <%= render partial: 'share/like-btn' , locals: {object: tweet}%> <% end %> diff --git a/config/routes.rb b/config/routes.rb index 60db48dc3..2b08a573e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -25,7 +25,8 @@ namespace :admin do - resources :tweets, only: [:index, :destroy] + resources :tweets + resources :users, only: [:index] root "tweets#index" end end diff --git a/db/migrate/20180801222347_add_followings_count_to_user.rb b/db/migrate/20180801222347_add_followings_count_to_user.rb new file mode 100644 index 000000000..cc7ea08b3 --- /dev/null +++ b/db/migrate/20180801222347_add_followings_count_to_user.rb @@ -0,0 +1,7 @@ +class AddFollowingsCountToUser < ActiveRecord::Migration[5.1] + def change + add_column :users, :followings_count, :integer, default: 0 + + + end +end diff --git a/db/schema.rb b/db/schema.rb index b99b5a47c..a37546002 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180607130502) do +ActiveRecord::Schema.define(version: 20180801222347) do create_table "followships", force: :cascade do |t| t.integer "user_id" @@ -68,6 +68,7 @@ t.integer "followers_count", default: 0 t.integer "tweets_count", default: 0 t.integer "replies_count", default: 0 + t.integer "followings_count", default: 0 t.index ["email"], name: "index_users_on_email", unique: true t.index ["name"], name: "index_users_on_name", unique: true t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true diff --git a/db/seeds.rb b/db/seeds.rb index 0ed3ac6c9..ba8dca0ce 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,5 +8,5 @@ # Default admin -User.create(email: "josh@twitter.com", password: "12345678", role: "admin", name: "josh") +User.create(email: "josh@twitter.com", password: "12345678", name: "josh") puts "Default admin created!" \ No newline at end of file diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 3151d9230..4e0f895e2 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -67,17 +67,15 @@ namespace :dev do task fake_followship: :environment do Followship.destroy_all - 30.times do |i| - user = User.all.sample - following = User.all.sample - unless user == following - followship = Followship.create!( - user: user, - following: following, - created_at: FFaker::Time::datetime - ) + users = User.all + users.each do |user| + 2.times do |i| + following = users.sample + if !user.is_following?(following) + followship = user.followships.create(following: following) end end + end puts "Total #{Followship.count}followships created !" end From a358b524cf184fa1722567cde9054deff15b4626 Mon Sep 17 00:00:00 2001 From: josher Date: Sun, 9 Sep 2018 16:55:06 +0800 Subject: [PATCH 09/10] fix seeds file set up issue --- app/controllers/tweets_controller.rb | 2 +- app/views/tweets/index.html.erb | 2 +- app/views/users/tweets.html.erb | 2 +- db/seeds.rb | 4 +++- lib/tasks/dev.rake | 8 +++----- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index 486c091b2..1a0b7d55c 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -19,7 +19,7 @@ def create def like @like = Like.new(user_id: current_user.id, tweet_id: @tweet.id) - if @like.save! + if @like.save redirect_to tweets_path else flash[:alert] = like.errors.messages diff --git a/app/views/tweets/index.html.erb b/app/views/tweets/index.html.erb index d95dc1dd6..9ad922d65 100644 --- a/app/views/tweets/index.html.erb +++ b/app/views/tweets/index.html.erb @@ -12,7 +12,7 @@
    - <%= tweet.user.name %><%= ", #{tweet.created_at}" %>
    + <%= link_to tweet.user.name + " #{tweet.created_at}", tweets_user_path(tweet.user), class: 'text-primary' %>
    <%= tweet.description %>
    diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb index 5a7a68edb..48005061b 100644 --- a/app/views/users/tweets.html.erb +++ b/app/views/users/tweets.html.erb @@ -9,7 +9,7 @@ <%= image_tag tweet.user.avatar, class: 'img-fluid' %>
    - <%= tweet.user.name %><%= ", #{tweet.created_at}" %>
    + <%= link_to tweet.user.name + " #{tweet.created_at}", tweets_user_path(tweet.user), class: 'text-primary' %>
    <%= tweet.description %>
    <%= link_to "Reply (#{tweet.replies.count})", tweet_replies_path(tweet), class: 'btn btn-info'%> <%= render partial: 'share/like-btn' , locals: {object: tweet}%> diff --git a/db/seeds.rb b/db/seeds.rb index ba8dca0ce..74c233618 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -8,5 +8,7 @@ # Default admin -User.create(email: "josh@twitter.com", password: "12345678", name: "josh") +ApplicationRecord.reset_column_information +admin_file = File.open("#{Rails.root}/public/avatar/user15.jpg") +admin = User.create!(email: "josh@example.com", password: "12345678", name: "josh", :role=> "admin", avatar: admin_file) puts "Default admin created!" \ No newline at end of file diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 4e0f895e2..0bab0735f 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -10,15 +10,13 @@ namespace :dev do name = FFaker::Name::first_name file = File.open("#{Rails.root}/public/avatar/user#{i+1}.jpg") - user = User.new( + user = User.create!( name: name, email: "#{name}@twitter.com", password: "12345678", introduction: FFaker::Lorem::sentence(20), - avatar: file + avatar: file, ) - - user.save! puts user.name end end @@ -71,7 +69,7 @@ namespace :dev do users.each do |user| 2.times do |i| following = users.sample - if !user.is_following?(following) + if !user.is_following?(following) && following != user followship = user.followships.create(following: following) end end From 2bf204b8bf4ee6bd787dc8e4aaf6184d8722b56e Mon Sep 17 00:00:00 2001 From: poyaboy Date: Mon, 10 Sep 2018 20:59:00 +0800 Subject: [PATCH 10/10] fix user sign up bug that use text field for name input --- app/models/user.rb | 2 +- app/views/devise/registrations/new.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/user.rb b/app/models/user.rb index ab371c70b..eb8c89b26 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,7 @@ class User < ApplicationRecord # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 - validates_presence_of :name + validates_presence_of :name, :email, :password validates :name, uniqueness: {case_sensitive: true} diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index a52627ad2..6c915c91f 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -10,7 +10,7 @@
    <%= f.label :name %>
    - <%= f.email_field :name, autofocus: true, autocomplete: "off" %> + <%= f.text_field :name, autofocus: true, autocomplete: "off" %>