diff --git a/Gemfile b/Gemfile index c1f1ee3e2..34f1a04ca 100644 --- a/Gemfile +++ b/Gemfile @@ -16,12 +16,12 @@ 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 'sass-rails', '~> 5.0' +gem 'bootstrap-sass', '~> 3.3.7' +gem 'sass-rails', '>= 3.2' +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 @@ -41,7 +41,14 @@ gem 'jbuilder', '~> 2.5' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'kaminari' + +group :production do + gem 'pg', '~> 0.20' +end + group :development, :test do + gem 'sqlite3' # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] # Adds support for Capybara system testing and selenium driver diff --git a/Gemfile.lock b/Gemfile.lock index e55e9522f..5e2286d58 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,22 @@ 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) + 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) @@ -124,6 +145,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 +275,7 @@ PLATFORMS x86-mswin32 DEPENDENCIES + bootstrap-sass (~> 3.3.7) byebug capybara (~> 2.13) carrierwave @@ -259,12 +284,15 @@ DEPENDENCIES factory_bot_rails ffaker jbuilder (~> 2.5) + jquery-rails + kaminari listen (>= 3.0.5, < 3.2) + pg (~> 0.20) puma (~> 3.7) rails (~> 5.1.4) rails-controller-testing rspec-rails (~> 3.7) - sass-rails (~> 5.0) + sass-rails (>= 3.2) selenium-webdriver shoulda-matchers (~> 3.1) spring @@ -276,4 +304,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..2b4854415 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,4 +12,7 @@ // //= require rails-ujs //= require turbolinks +//= require jquery +//= require bootstrap-sprockets //= 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..f71b82d56 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -10,6 +10,8 @@ * 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'; \ No newline at end of file diff --git a/app/assets/stylesheets/style.scss b/app/assets/stylesheets/style.scss new file mode 100644 index 000000000..337d8d51b --- /dev/null +++ b/app/assets/stylesheets/style.scss @@ -0,0 +1,104 @@ +.navbar { + margin-bottom: 30px; + .img-1 { + padding: 0 10px; + } + img { + width: 50px; + height: 50px; + } +} + +.form { + border: 3px solid #e3f2fd; +} + +.info { + p { + color: #0a80d6; + font-size: 14px; + } +} + +#index { + margin-bottom: 15px; + padding: 10px; + text-align: left; + float: left; + + img { + padding: 6px; + width: 90px; + height: 90px; + } + + p { + font-size: 14px; + } +} + +.tweet { + border: 1px solid #bfbdbd; + border-radius: 10px; + margin-bottom: 10px; + padding: 10px; + + p { + margin: 10px 0; + color: #7D7D7D; + } + + img { + padding: 2px; + width: 100px; + height: 100px; + } + +} + +.reply { + border: 1px solid #bfbdbd; + padding: 5px; + margin-bottom: 10px; +} + +.pop-user { + margin-bottom: 15px; + padding: 10px; + border: 1px solid #bfbdbd; + border-radius: 5px; + + p { + color: #666464; + font-size: 14px; + } + + a.name { + padding: 10px 0; + font-size: 16px; + font-weight: 800; + letter-spacing: 1px; + + } + + btn { + font-size: 14px; + } + + img { + padding: 6px; + width: 90px; + height: 90px; + } + +} + +.like { + padding: 0 5px; + color: #f00; +} + +.unlike { + padding: 0 5px; + color: #0f1; +} \ No newline at end of file diff --git a/app/controllers/admin/base_controller.rb b/app/controllers/admin/base_controller.rb index 4a89583f5..251ee3ebd 100644 --- a/app/controllers/admin/base_controller.rb +++ b/app/controllers/admin/base_controller.rb @@ -1,3 +1,13 @@ class Admin::BaseController < ApplicationController + before_action :authenticate_admin -end + private + + def authenticate_admin + unless current_user.admin? + flash[:alert] = "NOT ALLOW!" + redirect_to root_path + end + end + +end \ No newline at end of file diff --git a/app/controllers/admin/tweets_controller.rb b/app/controllers/admin/tweets_controller.rb index 24a57566c..114253aa8 100644 --- a/app/controllers/admin/tweets_controller.rb +++ b/app/controllers/admin/tweets_controller.rb @@ -1,7 +1,15 @@ class Admin::TweetsController < Admin::BaseController + before_action :authenticate_user! + before_action :authenticate_admin + def index + @tweets = Tweet.all.order(created_at: :desc) end def destroy + @tweet = Tweet.find(params[:id]) + @tweet.destroy + flash[:alert] = "Tweet was deleted!" + redirect_to admin_root_path end -end +end \ No newline at end of file diff --git a/app/controllers/admin/users_controller.rb b/app/controllers/admin/users_controller.rb index 3ba9f0a36..4a2bf1ea9 100644 --- a/app/controllers/admin/users_controller.rb +++ b/app/controllers/admin/users_controller.rb @@ -1,4 +1,7 @@ class Admin::UsersController < Admin::BaseController + 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..7a39e5e95 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -4,4 +4,24 @@ 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? + + + private + + def authenticate_admin + unless current_user.admin? + flash[:alert] = "Not allow!" + redirect_to root_path + end + end + + def configure_permitted_parameters + devise_parameter_sanitizer.permit(:sign_up, keys: [:name]) + end + + + end diff --git a/app/controllers/followships_controller.rb b/app/controllers/followships_controller.rb index 05f01b552..75f821ad2 100644 --- a/app/controllers/followships_controller.rb +++ b/app/controllers/followships_controller.rb @@ -1,7 +1,21 @@ 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]).first + @followship.destroy + flash[:alert] = "Followship destroyed" + redirect_back(fallback_location: root_path) end -end + +end \ No newline at end of file diff --git a/app/controllers/replies_controller.rb b/app/controllers/replies_controller.rb index a9b6a315b..05f308e9c 100644 --- a/app/controllers/replies_controller.rb +++ b/app/controllers/replies_controller.rb @@ -1,9 +1,22 @@ class RepliesController < ApplicationController def index + @tweet = Tweet.find(params[:tweet_id]) + @replies = @tweet.replies + @reply = Reply.new end def create + tweet = Tweet.find(params[:tweet_id]) + reply = tweet.replies.create(reply_params) + reply.user = current_user + reply.save! + redirect_to tweet_replies_path(tweet) end -end +private + + def reply_params + params.require(:reply).permit(:comment) + end +end \ No newline at end of file diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index ad14115c1..2bf320f3f 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,16 +1,44 @@ class TweetsController < ApplicationController + before_action :set_tweet, only: [:like, :unlike] def index - @users # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @users = User.order(followers_count: :desc).limit(10) + # 基於測試規格,必須講定變數名稱,請用此變數中存放關注人數 Top 10 的使用者資料 + @tweet = Tweet.new + @tweets = Tweet.all.order(created_at: :desc) end def create + @tweet = current_user.tweets.build(tweet_params) + if @tweet.save + flash[:notice] = "Tweet successfully!!!" + redirect_to tweets_path + else + flash[:alert] = "限字140" + redirect_to tweets_path + end end def like + @tweet.likes.create!(user: current_user) + redirect_to tweets_path end def unlike + like = Like.where(tweet: @tweet, user: current_user) + like.destroy_all + redirect_to tweets_path end -end + private + + def set_tweet + @tweet = Tweet.find(params[:id]) + end + + def tweet_params + params.require(:tweet).permit(:description) + end + + + end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 750e3c6b5..a71e9a68c 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,24 +1,45 @@ class UsersController < ApplicationController - + before_action :set_user, only: [:tweets, :edit, :update, :followings, :followers, :likes] + def tweets + if @user == current_user + @tweets = current_user.tweets.order(created_at: :desc) + else + @tweets = @user.tweets.order(created_at: :desc) + end end def edit + if current_user != @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 # 基於測試規格,必須講定變數名稱 - end + def followings # 基於測試規格,必須講定變數名稱 + @followings = @user.followships.order(created_at: :desc) + end def followers - @followers # 基於測試規格,必須講定變數名稱 + @followers = @user.followers.order(created_at: :desc) end def likes - @likes # 基於測試規格,必須講定變數名稱 + @likes = @user.liked_tweets.order(created_at: :desc) + end + + private + + def set_user + @user = User.find(params[:id]) + end + + def user_params + params.require(:user).permit(:name, :introduction, :avatar) end end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 2310a240d..476a5ae2b 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,2 +1,2 @@ module UsersHelper -end +end \ No newline at end of file diff --git a/app/models/followship.rb b/app/models/followship.rb index 1aed01396..181a6c8ef 100644 --- a/app/models/followship.rb +++ b/app/models/followship.rb @@ -1,4 +1,8 @@ class Followship < ApplicationRecord + validates :following_id, uniqueness: { scope: :user_id } + belongs_to :user + belongs_to :following, class_name: "User", counter_cache: :followers_count + end diff --git a/app/models/like.rb b/app/models/like.rb index d99b93a32..3d87d154d 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,2 +1,5 @@ class Like < ApplicationRecord + belongs_to :user + belongs_to :tweet, counter_cache: true + end diff --git a/app/models/reply.rb b/app/models/reply.rb index bae6f9463..fb5638d15 100644 --- a/app/models/reply.rb +++ b/app/models/reply.rb @@ -1,2 +1,4 @@ class Reply < ApplicationRecord + belongs_to :tweet, counter_cache: true + belongs_to :user end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 6715fada2..9fa3120e5 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -1,4 +1,16 @@ class Tweet < ApplicationRecord - validates_length_of :description, maximum: 140 + validates_length_of :description, maximum: 140,:message => "限字140" + + belongs_to :user, counter_cache: true + has_many :replies, dependent: :destroy + has_many :replied_users, through: :replies, source: :user + + 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..858ecb4c3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -8,7 +8,35 @@ class User < ApplicationRecord # 需要 app/views/devise 裡找到樣板,加上 name 屬性 # 並參考 Devise 文件自訂表單後通過 Strong Parameters 的方法 - validates_presence_of :name + validates_presence_of :name + validates_uniqueness_of :name + validates_uniqueness_of :email # 加上驗證 name 不能重覆 (關鍵字提示: uniqueness) + has_many :tweets, dependent: :destroy + + # 關聯名稱自訂為 : replied_tweets,需要用 source 來告知 model name 位置 + has_many :replies, dependent: :destroy + has_many :replied_tweets, through: :replies, source: :tweet + + has_many :likes, dependent: :destroy + has_many :liked_tweets, through: :likes, source: :tweet + + # 不需要另加 source (其沒有自訂名稱),Rails 可自動推論指向 User Model + 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 + +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..cf694abde --- /dev/null +++ b/app/views/admin/tweets/index.html.erb @@ -0,0 +1,53 @@ +
+

後台

+
+ + +
+ + + + + + + + + + + + + <% @tweets.each do |t| %> + + + + + + + + + <% end %> + +
usercontenttweet timelikesrepliesdelete
<%= t.user.name %><%= t.description %><%= t.created_at.strftime("%Y-%m-%d") %> + + + + <%= link_to "Delete", admin_tweet_path(t), method: :delete,data: { confirm: "Are you sure?"}, class: "btn" %> +
+
\ No newline at end of file diff --git a/app/views/admin/users/index.html.erb b/app/views/admin/users/index.html.erb new file mode 100644 index 000000000..058e1a819 --- /dev/null +++ b/app/views/admin/users/index.html.erb @@ -0,0 +1,38 @@ +
+

後台管理

+ +
+ + + +
+ + + + + + + + + + + + <% @users.each do |u| %> + + + + + + + + <% end %> + +
nametweetsfollowingfollowerlikes
<%= u.name %><%= u.tweets.count %><%= u.followings.count %><%= u.followers.count %><%= u.likes.count %>
+
\ No newline at end of file diff --git a/app/views/devise/registrations/new.html.erb b/app/views/devise/registrations/new.html.erb index 602803cff..b4f3daf2b 100644 --- a/app/views/devise/registrations/new.html.erb +++ b/app/views/devise/registrations/new.html.erb @@ -3,6 +3,11 @@ <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> <%= devise_error_messages! %> +

+ <%= f.label :name %> + <%= f.text_field :name %> +
+
<%= f.label :email %>
<%= f.email_field :email, autofocus: true, autocomplete: "email" %> diff --git a/app/views/kaminari/_first_page.html.erb b/app/views/kaminari/_first_page.html.erb new file mode 100644 index 000000000..2c5985bc9 --- /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 %> +
  • diff --git a/app/views/kaminari/_gap.html.erb b/app/views/kaminari/_gap.html.erb new file mode 100644 index 000000000..6d3a1490c --- /dev/null +++ b/app/views/kaminari/_gap.html.erb @@ -0,0 +1,3 @@ +
  • + <%= content_tag :a, raw(t 'views.pagination.truncate') %> +
  • diff --git a/app/views/kaminari/_last_page.html.erb b/app/views/kaminari/_last_page.html.erb new file mode 100644 index 000000000..aee252478 --- /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 } %> +
  • diff --git a/app/views/kaminari/_next_page.html.erb b/app/views/kaminari/_next_page.html.erb new file mode 100644 index 000000000..8e9a10e6a --- /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 %> +
  • diff --git a/app/views/kaminari/_page.html.erb b/app/views/kaminari/_page.html.erb new file mode 100644 index 000000000..19c420634 --- /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)) %> +
  • +<% else %> +
  • + <%= link_to page, url, remote: remote, rel: (page.next? ? 'next' : (page.prev? ? 'prev' : nil)) %> +
  • +<% end %> diff --git a/app/views/kaminari/_paginator.html.erb b/app/views/kaminari/_paginator.html.erb new file mode 100644 index 000000000..2c8757b4f --- /dev/null +++ b/app/views/kaminari/_paginator.html.erb @@ -0,0 +1,15 @@ +<%= paginator.render do -%> +
      + <%= first_page_tag unless current_page.first? %> + <%= prev_page_tag unless current_page.first? %> + <% each_page do |page| -%> + <% if page.left_outer? || page.right_outer? || page.inside_window? -%> + <%= page_tag page %> + <% elsif !page.was_truncated? -%> + <%= gap_tag %> + <% end -%> + <% end -%> + <%= next_page_tag unless current_page.last? %> + <%= last_page_tag unless current_page.last? %> +
    +<% 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..f16f6b4bd --- /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 %> +
  • diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 952cb7a1b..ab412a5ec 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -9,20 +9,70 @@ - <% if current_user %> - <% if current_user&.admin? %> -
  • <%= link_to 'Admin Panel', admin_restaurants_path %>
  • + + + + + <% if flash[:notice] %> +
    +
    + +
    + <% end %> + + <% if flash[:alert] %> +
    +
    + +
    <% 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) %>
  • - <% end %> -

    <%= notice %>

    -

    <%= alert %>

    + <%= yield %> diff --git a/app/views/replies/index.html.erb b/app/views/replies/index.html.erb new file mode 100644 index 000000000..99ebf9d94 --- /dev/null +++ b/app/views/replies/index.html.erb @@ -0,0 +1,45 @@ +
    +
    + <%= image_tag @tweet.user.avatar, class: "img-rounded" %> +

    <%= @tweet.user.name %>

    +

    <%= @tweet.user.introduction %>

    +
    +

    Tweets<%= @tweet.user.tweets.count %>

    +

    Following<%= @tweet.user.followings.count %>

    +

    Follower<%= @tweet.user.followers_count %>

    +

    Like<%= @tweet.user.likes_count %>

    +
    +
    + +
    +

    Tweets

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

    Replies

    + <% @replies.each do |t| %> +
    +
    +
    + <%= image_tag(t.user.avatar, class: "img-rounded") %> +
    +
    + <%= link_to "@#{t.user.name},#{t.created_at.to_s(:db)}", tweets_user_path(t.user) %> +

    <%= t.comment %>

    +
    +
    +
    + <% end %> + +
    + <%= form_for [@tweet, @reply] do |f| %> +
    + <%= f.text_area :comment, class: "form-control" %> +
    +
    + <%= f.button "Cancel", type: :reset, class: "pull-right btn btn-default btn-lg" %> + <%= f.submit "Reply", class: "pull-right btn btn-primary btn-lg" %> +
    + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/shared/_index.html.erb b/app/views/shared/_index.html.erb new file mode 100644 index 000000000..992c5b85b --- /dev/null +++ b/app/views/shared/_index.html.erb @@ -0,0 +1,21 @@ +
    + <%= image_tag @user.avatar, class: "img-responsive" %> +

    <%= @user.name %>

    +

    <%= @user.introduction %>

    +
    +

    <%= link_to "Tweets #{@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 "like #{@user.likes.count}",likes_user_path(@user) %>

    +
    + + <% if @user != current_user %> + <% if current_user.following?(@user)%> + <%= link_to 'Unfollow', followship_path(@user), method: :delete, class: "btn btn-default" %> + <% else %> + <%= link_to 'Follow', followships_path(following_id: @user), method: :post, class: "btn btn-primary" %> + <% end %> + <% else %> + <%= link_to "Edit Profile", edit_user_path(current_user), class: "btn btn-primary" %> + <% end %> +
    \ No newline at end of file diff --git a/app/views/shared/_pop.html.erb b/app/views/shared/_pop.html.erb new file mode 100644 index 000000000..cfa0910cf --- /dev/null +++ b/app/views/shared/_pop.html.erb @@ -0,0 +1,20 @@ +
    +
    +
    + <%= image_tag(u.avatar, class: "img-rounded ") %> +
    +
    + <%= link_to "@#{u.name}", tweets_user_path(u), class: "name" %> +

    <%= u.introduction %>

    +
    + <% if u != current_user %> + <% if current_user.following?(u) %> + <%= link_to "Unfollow", followship_path(u), method: :delete, class: "btn btn-info pull-right" %> + <% else %> + <%= link_to "Follow", followships_path(following_id: u), method: :post, class: "btn btn-primary pull-right" %> + <% end %> + <% end %> +
    +
    +
    +
    \ No newline at end of file diff --git a/app/views/shared/_tweet.html.erb b/app/views/shared/_tweet.html.erb new file mode 100644 index 000000000..5f3e895e1 --- /dev/null +++ b/app/views/shared/_tweet.html.erb @@ -0,0 +1,17 @@ +
    +
    +
    + <%= image_tag tweet.user.avatar %> +
    +
    + <%= link_to "@#{tweet.user.name},#{tweet.created_at.to_s(:db)}", tweets_user_path(tweet.user) %> +

    <%= tweet.description %>

    + <%= link_to "Reply(#{tweet.replies_count})", tweet_replies_path(tweet) %> + <% if tweet.is_liked?(current_user) %> + <%= link_to "Unlike(#{tweet.likes_count})", unlike_tweet_path(tweet), method: :post,class: "unlike" %> + <% else %> + <%= link_to "Like(#{tweet.likes_count})", like_tweet_path(tweet), method: :post,class: "like" %> + <% 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..ca5f304e0 --- /dev/null +++ b/app/views/tweets/index.html.erb @@ -0,0 +1,30 @@ +
    +
    +
    + <%= form_for @tweet do |f| %> +
    + <%= f.text_area :description, placeholder: "What's on your mind?", class: "form-control text-area" %> +
    +
    + <%= f.submit "Tweet", class: "btn-primary btn-lg pull-right " %> +
    + <% end %> +
    +
    + +
    + <% @tweets.each do |tweet| %> + <%= render partial: "shared/tweet", locals: { tweet: tweet} %> + <% end %> +
    + +
    +

    Popular

    +
    + <% @users.each do |u| %> + <%= render partial: "shared/pop", locals: { u: u} %> + <% end %> +
    +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..a33b4fe58 --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,23 @@ +
    + <%= form_for @user do |f| %> +
    +
    + <%= f.label :avatar, "Browse file...", class: "btn btn-primary" %> + <% image_tag @user.avatar if @user.avatar? %> + <%= f.file_field :avatar %> +
    +
    +
    +
    + <%= f.text_field :name, placeholder: "Name", class: "form-control text-field" %> +
    +
    + <%= f.text_area :introduction, placeholder: "Type anything about you here....", class: "form-control text-area" %> +
    +
    + <%= f.button "Cancel", type: :reset, class: "pull-right btn btn-default btn-lg" %> + <%= f.submit "Update", class: "pull-right btn btn-primary btn-lg" %> +
    +
    + <% end %> +
    \ No newline at end of file diff --git a/app/views/users/followers.html.erb b/app/views/users/followers.html.erb new file mode 100644 index 000000000..71474b849 --- /dev/null +++ b/app/views/users/followers.html.erb @@ -0,0 +1,15 @@ +
    +
    + <%= render partial: "shared/index", locals: { user: @user} %> +
    + +

    follower

    +
    +
    + <% @followers.each do |f| %> +
    + <%= render partial: "shared/pop", locals: { u: f} %> + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/followings.html.erb b/app/views/users/followings.html.erb new file mode 100644 index 000000000..d9b110a4d --- /dev/null +++ b/app/views/users/followings.html.erb @@ -0,0 +1,15 @@ +
    +
    + <%= render partial: "shared/index", locals: { user: @user} %> +
    + +

    followings

    +
    +
    + <% @followings.each do |f| %> +
    + <%= render partial: "shared/pop", locals: { u: f.following} %> + <% end %> +
    +
    +
    \ No newline at end of file diff --git a/app/views/users/likes.html.erb b/app/views/users/likes.html.erb new file mode 100644 index 000000000..dda47a7d5 --- /dev/null +++ b/app/views/users/likes.html.erb @@ -0,0 +1,17 @@ +
    +
    + <%= render partial: "shared/index", locals: { user: @user} %> +
    + +

    like

    +
    +
    + <% @likes.each do |like| %> + <%= render partial: "shared/tweet", locals: { tweet: like} %> + <% end %> +
    +
    +
    + + + diff --git a/app/views/users/tweets.html.erb b/app/views/users/tweets.html.erb new file mode 100644 index 000000000..f74006b0f --- /dev/null +++ b/app/views/users/tweets.html.erb @@ -0,0 +1,13 @@ +
    +
    + <%= render partial: "shared/index", locals: { user: @user} %> +
    + +
    + <% @tweets.each do |t| %> + <%= render partial: "shared/tweet", locals: { tweet: t} %> + <% 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..f6115021e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,7 +1,46 @@ Rails.application.routes.draw do # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html devise_for :users + resources :users, only: [:edit, :update] do + + member do + + # user page + get :tweets + + # following and follower pages + get :followings + get :followers + + # likes page + get :likes + + end + + end # 請依照專案指定規格來設定路由 + resources :tweets, only: [:index, :create] do + resources :replies, only: [:index, :create] + + member do + + # like button + post :like + post :unlike + + end + + end + + resources :followships, only: [:create, :destroy] + + root "tweets#index" + + namespace :admin do + resources :tweets, only: [:index, :destroy] + resources :users, only: [:index] + root "tweets#index" + end -end +end \ No newline at end of file diff --git a/db/migrate/20180725035428_add_tweets_count_to_users.rb b/db/migrate/20180725035428_add_tweets_count_to_users.rb new file mode 100644 index 000000000..08dac61a2 --- /dev/null +++ b/db/migrate/20180725035428_add_tweets_count_to_users.rb @@ -0,0 +1,9 @@ +class AddTweetsCountToUsers < ActiveRecord::Migration[5.1] + def change + add_column :users, :tweets_count, :integer, default: 0 + #新增tweets的counter cache + User.pluck(:id).each do |i| + User.reset_counters(i, :tweets) + end + end +end \ No newline at end of file diff --git a/db/schema.rb b/db/schema.rb index 6a6842c86..5858020da 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: 20180725035428) 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..93be138bc 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,7 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) + + +User.create(name:'Mike', email:'admin@aaa.com', password:'asdasd',introduction:'This is admin',role:'admin' ) +puts "Default admin created!" \ No newline at end of file diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index 9b1e87ae4..0c512d649 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -3,10 +3,10 @@ namespace :dev do # 其他測試用的假資料請依需要自行撰寫 task fake_user: :environment do User.destroy_all + 20.times do |i| name = FFaker::Name::first_name - file = File.open("#{Rails.root}/public/avatar/user#{i+1}.jpg") - + file = File.open("#{Rails.root}/public/avatar/user#{i+1}.jpg") user = User.new( name: name, email: "#{name}@example.co", @@ -20,4 +20,68 @@ namespace :dev do end end + + task fake_tweet: :environment do + Tweet.destroy_all + + User.all.each do |user| + 3.times do |i| + user.tweets.create!( + description: FFaker::Lorem::phrase(10) + ) + end + + puts "reated fake tweets" + puts "now you have #{Tweet.count} users data" + end end + + task fake_reply: :environment do + Reply.destroy_all + + Tweet.all.each do |r| + 3.times do |i| + r.replies.create!( + comment: FFaker::Lorem.sentence, + user: User.all.sample + ) + end + end + + puts "have created fake replies" + puts "now you have #{Reply.count} replies data" + end + + + task fake_like: :environment do + Like.destroy_all + + 400.times do + Like.create!( + user_id: User.all.ids.sample, + tweet_id: Tweet.all.ids.sample + ) + end + + puts "have created fake likes" + puts "now you have #{Like.count} like data" + end + + task fake_followship: :environment do + Followship.destroy_all + + User.all.each do |user| + num = rand(1..8) + @users = User.where.not(id: user.id).shuffle + num.times do + user.followships.create!( + following: @users.pop + ) + end + end + + puts "have created fake followship" + puts "now you have #{Followship.count} followships data" + end + + end