diff --git a/Gemfile b/Gemfile index c1f1ee3e2..6d1847691 100644 --- a/Gemfile +++ b/Gemfile @@ -16,12 +16,14 @@ gem 'ffaker' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.1.4' -# Use sqlite3 as the database for Active Record -gem 'sqlite3' + # Use Puma as the app server gem 'puma', '~> 3.7' # Use SCSS for stylesheets +gem 'bootstrap-sass', '~> 3.3.7' gem 'sass-rails', '~> 5.0' +# For bootstrap +gem 'jquery-rails' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # See https://github.com/rails/execjs#readme for more supported runtimes @@ -51,6 +53,7 @@ group :development, :test do gem 'factory_bot_rails' gem 'shoulda-matchers', '~> 3.1' gem 'rails-controller-testing' + gem 'sqlite3' end group :development do @@ -62,5 +65,9 @@ group :development do gem 'spring-watcher-listen', '~> 2.0.0' end +group :production do + gem 'pg', '~> 0.20' +end + # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock index e55e9522f..5ca2708e9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -41,11 +41,16 @@ GEM addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) arel (8.0.0) + autoprefixer-rails (9.0.0) + 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-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) builder (3.2.3) byebug (10.0.0) capybara (2.17.0) @@ -97,6 +102,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 +133,9 @@ GEM nokogiri (1.8.1-x86-mingw32) mini_portile2 (~> 2.3.0) orm_adapter (0.5.0) + pg (0.21.0) + pg (0.21.0-x64-mingw32) + pg (0.21.0-x86-mingw32) public_suffix (3.0.1) puma (3.11.2) puma (3.11.2-java) @@ -251,6 +263,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bootstrap-sass (~> 3.3.7) byebug capybara (~> 2.13) carrierwave @@ -259,7 +272,9 @@ DEPENDENCIES factory_bot_rails ffaker jbuilder (~> 2.5) + jquery-rails listen (>= 3.0.5, < 3.2) + pg (~> 0.20) puma (~> 3.7) rails (~> 5.1.4) rails-controller-testing @@ -276,4 +291,4 @@ DEPENDENCIES web-console (>= 3.3.0) BUNDLED WITH - 1.16.1 + 1.16.3 diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 46b20359f..ff430067c 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,6 +10,8 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +//= require jquery +//= require bootstrap-sprockets //= require rails-ujs //= require turbolinks //= require_tree . diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 90% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index d05ea0f51..c0378dfd3 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,7 @@ * 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-sprockets'; + @import 'bootstrap'; + @import 'style'; diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss new file mode 100644 index 000000000..74d75ed13 --- /dev/null +++ b/app/assets/stylesheets/style.scss @@ -0,0 +1,18 @@ +//攥寫自己的css. +.tweet-item { + border: 1px solid #ddd; + border-radius: 4px; + padding: 4px; + margin-bottom: 20px; + +} + +//edit page按鈕 +.update-btn { + float:right; +} + +.avatar-image { + height: 100px; + width: auto; +} \ No newline at end of file diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 4a89583f5..735ddcbd4 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,3 +1,14 @@ class Admin::BaseController < ApplicationController +# 後台登入先確認user登入再確認身份為admin,authenticate_admin方法要另外定義 +before_action :authenticate_user! +before_action :authenticate_admin +private + +def authenticate_admin + unless current_user.admin? #admin?要另外在user model定義 + flash[:alert] = "Not allow!" + redirect_to root_path + end + end end diff --git a/app/controllers/admin/tweets_controller.rb b/app/controllers/admin/tweets_controller.rb index 24a57566c..b38c4524f 100644 --- a/app/controllers/admin/tweets_controller.rb +++ b/app/controllers/admin/tweets_controller.rb @@ -1,7 +1,15 @@ class Admin::TweetsController < Admin::BaseController +# 後台登入先確認user登入再確認身份為admin,authenticate_admin方法要另外定義 +before_action :authenticate_user! +before_action :authenticate_admin def index + @tweets = Tweet.all.order(created_at: :desc) #管理者瀏覽所有tweet資料 end def destroy + @tweets = Tweet.find(params[:id]) + @tweets.destroy + redirect_to admin_root_path + flash[:alert] = "tweet was deleted" end end diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3ba9f0a36..10be25a80 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,8 @@ class Admin::UsersController < Admin::BaseController + # 後台登入先確認user登入再確認身份為admin,authenticate_admin方法要另外定義 +before_action :authenticate_user! +before_action :authenticate_admin def index + @users = User.order(tweets_count: :desc) end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 0da627f1a..614f44640 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,7 +1,25 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception + before_action :configure_permitted_parameters, if: :devise_controller? # 請參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 # https://github.com/plataformatec/devise#strong-parameters # 注意有 sign_up 和 account_update 兩種參數要處理 + + protected + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) + devise_parameter_sanitizer.permit(:account_update, keys: [:name]) + end + + private + + def authenticate_admin + unless current_user.admin? + flash[:alert] = "Not allow" + redirect_to root_path + end + end + end diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 05f01b552..b5eb0a050 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,7 +1,18 @@ class FollowshipsController < ApplicationController def create + @followship = current_user.followships.build(following_id: params[:following_id]) + if @followship.save + flash[:notice] = "Successfully followed" + redirect_back(fallback_location: root_path) + else + flash[:alert] = @followship.errors.full_messages.to_sentence + redirect_back(fallback_location: root_path) + end end - def destroy + @followship = current_user.followships.where(following_id: params[:id]) + @followship.destroy_all + flash[:alert] = "Followship destroyed" + redirect_back(fallback_location: root_path) end end diff --git a/app/controllers/replies_controller.rb b/app/controllers/replies_controller.rb index a9b6a315b..850f3ad58 100644 --- a/app/controllers/replies_controller.rb +++ b/app/controllers/replies_controller.rb @@ -1,9 +1,24 @@ class RepliesController < ApplicationController def index + @tweet = Tweet.find(params[:tweet_id]) + @reply = Reply.new + @replies = @tweet.replies.order(created_at: :desc) + @user = @tweet.user end def create + @tweet = Tweet.find(params[:tweet_id]) + @reply = @tweet.replies.build(reply_params) + @reply.user_id = current_user.id + @reply.save! + redirect_to tweet_replies_path(@tweet) + end + + private + + def reply_params + params.require(:reply).permit(:comment) end end diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index ad14115c1..836db8675 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,16 +1,44 @@ class TweetsController < ApplicationController - +# 登入認證 +before_action :authenticate_user! def index - @users # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweets = Tweet.all.order(created_at: :desc) + @tweet = Tweet.new #在index建立tweet容器來建立短文 + # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + # 必須搭配counter_cache才年通過測試 + @users = User.order(followers_count: :desc).limit(10) + # 以下也可以通過自動測試 + #@users = User.all.sort_by {|user| user.followers.size}.reverse.first(10) end def create + @tweet = current_user.tweets.build(tweet_params) + if @tweet.save + flash[:notice] = "Tweeted successfully" + redirect_to tweets_path + else + flash.now[:alert] = "Tweeted faield" + render :index + end end def like + @tweet = Tweet.find(params[:id]) + @tweet.likes.create!(user: current_user) + redirect_back(fallback_location: tweets_path) #導回上一頁 end def unlike + @tweet = Tweet.find(params[:id]) + likes = Like.where(tweet: @tweet, user: current_user) + likes.destroy_all + redirect_to tweets_path + end + + private + + def tweet_params + params.require(:tweet).permit(:description) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 750e3c6b5..5f6b4efe4 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,24 +1,44 @@ class UsersController < ApplicationController - +before_action :set_user, only: [:edit, :update, :followings, :followers, :likes, :tweets] def tweets + @user = User.find(params[:id]) + @tweets = @user.tweets.includes(:user).order("tweets.created_at desc") end - + def edit + unless @user == current_user + redirect_to tweets_user_path(@user) + end end def update + @user.update(user_params) + redirect_to edit_user_path(@user) end def followings - @followings # 基於測試規格,必須講定變數名稱 + # For ordering on the attribute of an associated model you have to include it: + @followings = @user.followings.includes(:followships).order("followships.created_at desc") # 基於測試規格,必須講定變數名稱 end def followers - @followers # 基於測試規格,必須講定變數名稱 + @followers = @user.followers.includes(:followships).order("followships.created_at desc") # 基於測試規格,必須講定變數名稱 + @user.followers_count end def likes - @likes # 基於測試規格,必須講定變數名稱 + @likes = @user.liked_tweets.includes(:likes).order("likes.created_at desc") + # 基於測試規格,必須講定變數名稱 + end + + private + + def set_user + @user = User.find(params[:id]) + end + + def user_params + params.require(:user).permit(:name, :avatar, :introduction) end end diff --git a/app/models/followship.rb b/app/models/followship.rb index 1aed01396..654365b6c 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,4 +1,14 @@ class Followship < ApplicationRecord + # 限制使用者只能追蹤另一個使用者一次 validates :following_id, uniqueness: { scope: :user_id } + # 限制是否追蹤自己 + validate :check_self_following + belongs_to :user + belongs_to :following, class_name:"User" ,counter_cache: :followers_count + def check_self_following + if user_id == following_id + errors[:following_id] << "Can's follow yourself" + end + end end diff --git a/app/models/like.rb b/app/models/like.rb index d99b93a32..5771fe616 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..223589f44 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,2 +1,4 @@ class Reply < ApplicationRecord + belongs_to :user + belongs_to :tweet end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 6715fada2..957e90770 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,4 +1,18 @@ class Tweet < ApplicationRecord validates_length_of :description, maximum: 140 +# 建立user跟tweet的關聯,一個tweet只會有一個user + belongs_to :user, counter_cache: true +# tweet限制字數及空白 + validates :user_id, presence: true + validates :description, presence: true, length: {maximum: 140} +# tweet有很多回覆,當tweet被刪除時,順便刪除回覆 + has_many :replies, dependent: :destroy +# 使用者可以喜歡很多評論 + has_many :likes, dependent: :destroy + has_many :liked_users, through: :likes, source: :user + + def is_liked?(user) + self.liked_users.include?(user) + end end diff --git a/app/models/user.rb b/app/models/user.rb index 6b05b8c21..936f9ce65 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,12 +3,38 @@ class User < ApplicationRecord # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable - +# user大頭照照片上傳器 mount_uploader :avatar, AvatarUploader + +# 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + #validates_presence_of :name, :uniqueness => {:case_sensitive => true} + validates_uniqueness_of :name + validates_presence_of :name + # 設定user跟tweet關聯,一個user可以有很多tweets + has_many :tweets, dependent: :destroy + # 設定追蹤關聯紀錄 + has_many :followships, dependent: :destroy + has_many :followings, through: :followships + + # 設定追蹤者關聯 + has_many :inverse_followships, class_name: "Followship", foreign_key: "following_id" + has_many :followers, through: :inverse_followships, source: :user + + # 設定使用者喜歡很多評論多對多關聯 + has_many :likes, dependent: :destroy + has_many :liked_tweets, through: :likes, source: :tweet + # 設定使用者會回復很多tweets + has_many :replies, dependent: :destroy # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 - validates_presence_of :name - # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + + def admin? + self.role == "admin" + end + + def following?(user) + self.followings.include?(user) + 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..37be61e68 --- /dev/null +++ b/app/views/admin/tweets/index.html.erb @@ -0,0 +1,19 @@ + +
+ +

tweets 後台

+ + +
+ + + +
+ diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb new file mode 100644 index 000000000..673aa5e25 --- /dev/null +++ b/app/views/admin/users/index.html.erb @@ -0,0 +1,34 @@ +
+
+

User List

+ + + + + + + + + + + + + + + <% @users.each do |user| %> + + + + + + + + + <% end %> + +
User IDNameTweetsFollowersFollowingsLike Tweets
<%= user.id %><%= user.name %><%= user.tweets.count %><%= user.followers.count %><%= user.followings.count %><%= user.likes.count %>
+
+
+ \ No newline at end of file diff --git a/app/views/devise/registrations/edit.html.erb b/app/views/devise/registrations/edit.html.erb index 10ed32a9e..20cf3c961 100644 --- a/app/views/devise/registrations/edit.html.erb +++ b/app/views/devise/registrations/edit.html.erb @@ -3,6 +3,11 @@ <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> <%= devise_error_messages! %> + + <%= f.label :name %>
+ <%= f.text_field :name, autofocus: true %> +
+
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 602803cff..b7b06c6a2 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -1,8 +1,12 @@ +

Sign up

<%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> - +
+ <%= f.label :name %>
+ <%= f.text_field :name, autofocus: true %> +
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> @@ -27,3 +31,4 @@ <% end %> <%= render "devise/shared/links" %> +
\ No newline at end of file diff --git a/app/views/devise/sessions/new.html.erb b/app/views/devise/sessions/new.html.erb index 3ebb001d1..2c3e0647f 100644 --- a/app/views/devise/sessions/new.html.erb +++ b/app/views/devise/sessions/new.html.erb @@ -1,3 +1,4 @@ +

Log in

<%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> @@ -24,3 +25,4 @@ <% end %> <%= render "devise/shared/links" %> +
\ No newline at end of file diff --git a/app/views/devise/shared/_links.html.erb b/app/views/devise/shared/_links.html.erb index e6a3e4196..73627e158 100644 --- a/app/views/devise/shared/_links.html.erb +++ b/app/views/devise/shared/_links.html.erb @@ -1,9 +1,9 @@ <%- if controller_name != 'sessions' %> - <%= link_to "Log in", new_session_path(resource_name) %>
+ <%# link_to "Log in", new_session_path(resource_name) %>
<% end -%> <%- if devise_mapping.registerable? && controller_name != 'registrations' %> - <%= link_to "Sign up", new_registration_path(resource_name) %>
+ <%# link_to "Sign up", new_registration_path(resource_name) %>
<% end -%> <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 952cb7a1b..51b808707 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,26 +3,63 @@ SimpleTwitter <%= csrf_meta_tags %> - <%= 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_restaurants_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) %>
  • + + + + + + <% if flash[:notice] %> +
    +
    +
    <%= notice %>
    +
    +
    <% end %> -

    <%= notice %>

    -

    <%= alert %>

    + + <% if flash[:alert] %> +
    +
    +
    <%= alert %>
    +
    +
    + <% end %> + <%= yield %> diff --git a/app/views/replies/index.html.erb b/app/views/replies/index.html.erb new file mode 100644 index 000000000..4a6878a4b --- /dev/null +++ b/app/views/replies/index.html.erb @@ -0,0 +1,49 @@ + +
    +
    +
    + +
    + <%= render partial: "shared/userinfo", locals: {user: @tweet.user} %> +
    +
    + +
    + +

    Tweet

    + <%= render partial: "shared/tweeter", locals: {tweeter: @tweet } %> +
    + +

    Replies

    +
    + <% @replies.each do |reply| %> +
    +
    +
    + <%= image_tag reply.user.avatar, class: "avatar-image img-responsive img-rounded" %> +
    +
    + <%= reply.user.name %>, + <%= reply.user.created_at.strftime('%Y-%m-%d , %H:%M:%S') %> +

    + <%= simple_format reply.comment %> +

    +
    +
    +
    + <% end %> +
    + + <%= form_for [@tweet, @reply] do |f| %> +
    +
    + <%= f.text_area :comment, class: "form-control" %> +
    +
    + <%= f.submit class: "btn btn-primary" %> +
    +
    + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/shared/_avatar.html.erb b/app/views/shared/_avatar.html.erb new file mode 100644 index 000000000..0bf31e2f1 --- /dev/null +++ b/app/views/shared/_avatar.html.erb @@ -0,0 +1,5 @@ +<% if user_avatar.avatar? %> + <%= image_tag user_avatar.avatar, class: "avatar-image img-responsive center-block" %> +<% else %> + +<% end %> \ No newline at end of file diff --git a/app/views/shared/_follow.html.erb b/app/views/shared/_follow.html.erb new file mode 100644 index 000000000..c16548b12 --- /dev/null +++ b/app/views/shared/_follow.html.erb @@ -0,0 +1,7 @@ +<% if user != current_user %> + <% if current_user.following?(user) %> + <%= link_to 'Unfollow', followship_path(user), method: :delete, class: "btn btn-info" %> + <% else %> + <%= link_to 'Follow', followships_path(following_id: user.id), method: :post, class: "btn btn-primary" %> + <% end %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_liked.html.erb b/app/views/shared/_liked.html.erb new file mode 100644 index 000000000..fd63f7155 --- /dev/null +++ b/app/views/shared/_liked.html.erb @@ -0,0 +1,5 @@ +<% if tweet.is_liked?(current_user) %> + <%= link_to "Unlike(#{tweet.likes.count}) ", unlike_tweet_path(tweet),method: :post %> +<% else %> + <%= link_to "Like(#{tweet.likes.count})", like_tweet_path(tweet), method: :post %> +<% end %> \ No newline at end of file diff --git a/app/views/shared/_tweeter.html.erb b/app/views/shared/_tweeter.html.erb new file mode 100644 index 000000000..a53030495 --- /dev/null +++ b/app/views/shared/_tweeter.html.erb @@ -0,0 +1,20 @@ +
    +
    +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: tweeter.user}%> +
    +
    + <%= link_to"#{tweeter.user.name}", tweets_user_path(tweeter.user) %>, + <%= tweeter.created_at.strftime('%Y-%m-%d, %H:%M:%S') %> +

    + <%= tweeter.description %> +

    +

    + <%= render partial: "shared/liked", locals: {tweet: tweeter}%> + <%= link_to "Reply(#{tweeter.replies.count}) ", tweet_replies_path(tweeter), class: "links" %> +

    +
    +
    +
    +
    diff --git a/app/views/shared/_userinfo.html.erb b/app/views/shared/_userinfo.html.erb new file mode 100644 index 000000000..515228aa5 --- /dev/null +++ b/app/views/shared/_userinfo.html.erb @@ -0,0 +1,22 @@ +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: user}%> +

    <%= user.name %>

    +

    <%= user.introduction %>

    +

    <%= link_to "Tweet (#{user.tweets.count}) ", tweets_user_path(user) %>

    +

    <%= link_to "Following (#{user.followings.count}) ",followings_user_path(user) %>

    +

    <%= link_to "Follower (#{user.followers.count}) ", followers_user_path(user) %>

    +

    <%= link_to "Likes (#{user.likes.count}) ", likes_user_path(user) %>

    + + <% if user != current_user %> +

    <%= render partial: "shared/follow", locals: {user: user} %>

    + <% else %> +

    <%= link_to "Edit Profile", edit_user_path(user), class:"btn btn-info" %>

    + <% end %> + + +
    +
    +
    + +
    \ No newline at end of file diff --git a/app/views/tweets/index.html.erb b/app/views/tweets/index.html.erb new file mode 100644 index 000000000..e15152846 --- /dev/null +++ b/app/views/tweets/index.html.erb @@ -0,0 +1,51 @@ + +
    +
    +
    + + <%= form_for @tweet do |f| %> +
    + <%= f.text_area :description, placeholder: "What's on your mind?", class: "form-control" %> +
    +
    + <%= f.submit "Tweet", class: "btn btn-primary" %> +
    + <% end %> + + +
    + <% @tweets.each do |tweet| %> + <%= render partial: "shared/tweeter", locals: {tweeter: tweet}%> + <% end %> +
    +
    + + + +
    +
    +
    +

    Popular

    +
    + <% @users.each do |user| %> +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: user}%> +
    +
    +
    <%= link_to "#{user.name}", tweets_user_path(user) %>
    +

    + <%= user.introduction %> +

    +

    + <%= render partial: "shared/follow", locals: {user: user} %> +

    +
    +
    + <% end %> + + +
    +
    +
    + diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..87b0b5a88 --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,29 @@ +
    +
    + <%= form_for @user do |f| %> + +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: @user} %> +
    +
    + <%= f.file_field :avatar %> +
    +
    + +
    +
    + <%= f.text_field :name, placeholder: "name" %> +
    +
    + <%= f.text_area :introduction, placeholder: "Type anything about you here....",class: "form-control" %> +
    + +
    + <%= f.submit "Update", class: "btn btn-primary" %> +
    +
    +
    + <% end %> +
    +
    diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb new file mode 100644 index 000000000..654126f61 --- /dev/null +++ b/app/views/users/followers.html.erb @@ -0,0 +1,36 @@ + +
    +
    + +
    + <%= render partial: "shared/userinfo", locals: {user: @user}%> +
    + + +
    + +
    +
    +

    Follower

    +
    + <% @followers.each do |follower| %> +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: follower}%> +
    +
    +
    <%= follower.name %>
    +

    + <%= follower.introduction %> +

    + <%= render partial: "shared/follow", locals: {user: follower} %> + +
    +
    + <% end %> +
    +
    +
    +
    +
    + diff --git a/app/views/users/followings.html.erb b/app/views/users/followings.html.erb new file mode 100644 index 000000000..d8a1ef011 --- /dev/null +++ b/app/views/users/followings.html.erb @@ -0,0 +1,37 @@ + +
    +
    + + +
    + <%= render partial: "shared/userinfo", locals: {user: @user}%> +
    + + +
    + +
    +
    +

    Following

    +
    + <% @followings.each do |following| %> +
    +
    + <%= render partial: "shared/avatar", locals: {user_avatar: following}%> +
    +
    +
    <%= following.name %>
    +

    + <%= following.introduction %> +

    + <%= render partial: "shared/follow", locals: {user: following} %> + +
    +
    + <% end %> +
    +
    +
    +
    +
    + diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb new file mode 100644 index 000000000..af0c51a35 --- /dev/null +++ b/app/views/users/likes.html.erb @@ -0,0 +1,16 @@ + +
    +
    + +
    + <%= render partial: "shared/userinfo", locals: {user: @user}%> +
    + +

    Like

    +
    + <% @likes.each do |tweet| %> + <%= render partial: "shared/tweeter", locals: {tweeter: tweet} %> + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb new file mode 100644 index 000000000..90f44600c --- /dev/null +++ b/app/views/users/tweets.html.erb @@ -0,0 +1,14 @@ +

    tweet

    +
    +
    + +
    + <%= render partial: "shared/userinfo", locals: {user: @user}%> +
    +
    + <% @tweets.each do |tweet| %> + <%= render partial: "shared/tweeter", locals: {tweeter: tweet}%> + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/config/database.yml b/config/database.yml index 0d02f2498..1c8d40f2a 100644 --- a/config/database.yml +++ b/config/database.yml @@ -21,5 +21,5 @@ test: database: db/test.sqlite3 production: - <<: *default - database: db/production.sqlite3 + adapter: postgresql + encoding: Unicode diff --git a/config/routes.rb b/config/routes.rb index 90856d4fe..ec0e90c8d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,36 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html devise_for :users - # 請依照專案指定規格來設定路由 + # 設定前台路由 + root "tweets#index" + + # 設定後台路由 + namespace :admin do + resources :tweets, only: [:index, :destroy] + resources :users, only: [:index] + root "tweets#index" + end + # 設定user路由及某user的簡介及推播 + resources :users, only: [:edit, :update] do + member do + get :tweets + get :followings + get :followers + get :likes + end + end + # 設定前台使用者行為路由 + resources :tweets, only: [:index, :create] do + resources :replies, only: [:index, :create] + + # 設定user喜歡或取消喜歡tweet + member do + post :like + post :unlike + end + end + # 設定追蹤路由 + resources :followships, only: [:create, :destroy] -end + end diff --git a/db/migrate/20180731082254_add_tweets_count_to_users.rb b/db/migrate/20180731082254_add_tweets_count_to_users.rb new file mode 100644 index 000000000..abb23c746 --- /dev/null +++ b/db/migrate/20180731082254_add_tweets_count_to_users.rb @@ -0,0 +1,5 @@ +class AddTweetsCountToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :tweets_count, :integer, default: 0 + end +end diff --git a/db/migrate/20180731132055_add_tweets_count_to_users_action.rb b/db/migrate/20180731132055_add_tweets_count_to_users_action.rb new file mode 100644 index 000000000..96f95ddc5 --- /dev/null +++ b/db/migrate/20180731132055_add_tweets_count_to_users_action.rb @@ -0,0 +1,7 @@ +class AddTweetsCountToUsersAction < ActiveRecord::Migration[5.1] + def change + User.pluck(:id).each do |i| + User.reset_counters(i, :tweets) + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 6a6842c86..00bc6deb6 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: 20180731132055) do create_table "followships", force: :cascade do |t| t.integer "user_id" @@ -66,6 +66,7 @@ t.integer "likes_count", default: 0 t.string "role", default: "normal" t.integer "followers_count", default: 0 + t.integer "tweets_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..9fa68056b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,5 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) +User.create(email: "admin@example.com", password: "123456", role: "admin", name: "admin") +puts "Default admin created" \ No newline at end of file