From 92ac97890e3aa8219b45ffdd817f588eb909e796 Mon Sep 17 00:00:00 2001 From: nghiadk Date: Sun, 2 Jul 2017 19:12:25 +0700 Subject: [PATCH 1/2] Add user microposts --- Gemfile | 3 + Gemfile.lock | 237 +++++++++++++++--- app/assets/stylesheets/custom.scss | 47 ++++ app/controllers/application_controller.rb | 9 + app/controllers/microposts_controller.rb | 32 +++ app/controllers/password_resets_controller.rb | 4 +- app/controllers/static_pages_controller.rb | 4 + app/controllers/users_controller.rb | 1 + app/helpers/microposts_helper.rb | 2 + app/models/micropost.rb | 20 ++ app/models/user.rb | 6 + app/uploaders/picture_uploader.rb | 19 ++ app/views/microposts/_micropost.html.erb | 18 ++ app/views/shared/_error_messages.html.erb | 6 +- app/views/shared/_feed.html.erb | 6 + app/views/shared/_micropost_form.html.erb | 19 ++ app/views/shared/_user_info.html.erb | 5 + app/views/static_pages/home.html.erb | 35 ++- app/views/users/_form.html.erb | 2 +- app/views/users/show.html.erb | 11 + config/locales/en.yml | 24 +- config/routes.rb | 1 + config/settings.yml | 5 + db/schema.rb | 31 --- db/seeds.rb | 6 + 25 files changed, 463 insertions(+), 90 deletions(-) create mode 100644 app/controllers/microposts_controller.rb create mode 100644 app/helpers/microposts_helper.rb create mode 100644 app/models/micropost.rb create mode 100644 app/uploaders/picture_uploader.rb create mode 100644 app/views/microposts/_micropost.html.erb create mode 100644 app/views/shared/_feed.html.erb create mode 100644 app/views/shared/_micropost_form.html.erb create mode 100644 app/views/shared/_user_info.html.erb delete mode 100644 db/schema.rb diff --git a/Gemfile b/Gemfile index c32421d..db32093 100644 --- a/Gemfile +++ b/Gemfile @@ -9,11 +9,14 @@ gem "bcrypt", "3.1.11" gem "bootstrap-sass", "3.3.7" gem "bootstrap-will_paginate" gem "capistrano-rails", group: :development +gem "carrierwave" gem "coffee-rails", "~> 4.2" gem "config" gem "ffaker" +gem "fog" gem "jbuilder", "~> 2.5" gem "jquery-rails" +gem "mini_magick" gem "puma", "~> 3.7" gem "rails", "~> 5.1.1" gem "rails-controller-testing" diff --git a/Gemfile.lock b/Gemfile.lock index b0b975d..ca21e8c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,47 @@ GEM remote: https://rubygems.org/ specs: - actioncable (5.1.1) - actionpack (= 5.1.1) + CFPropertyList (2.3.5) + actioncable (5.1.2) + actionpack (= 5.1.2) nio4r (~> 2.0) websocket-driver (~> 0.6.1) - actionmailer (5.1.1) - actionpack (= 5.1.1) - actionview (= 5.1.1) - activejob (= 5.1.1) + actionmailer (5.1.2) + actionpack (= 5.1.2) + actionview (= 5.1.2) + activejob (= 5.1.2) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.1.1) - actionview (= 5.1.1) - activesupport (= 5.1.1) + actionpack (5.1.2) + actionview (= 5.1.2) + activesupport (= 5.1.2) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.1.1) - activesupport (= 5.1.1) + actionview (5.1.2) + activesupport (= 5.1.2) builder (~> 3.1) erubi (~> 1.4) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.3) - activejob (5.1.1) - activesupport (= 5.1.1) + activejob (5.1.2) + activesupport (= 5.1.2) globalid (>= 0.3.6) - activemodel (5.1.1) - activesupport (= 5.1.1) - activerecord (5.1.1) - activemodel (= 5.1.1) - activesupport (= 5.1.1) + activemodel (5.1.2) + activesupport (= 5.1.2) + activerecord (5.1.2) + activemodel (= 5.1.2) + activesupport (= 5.1.2) arel (~> 8.0) - activesupport (5.1.1) + activesupport (5.1.2) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.5.1) public_suffix (~> 2.0, >= 2.0.2) - airbrussh (1.2.0) + airbrussh (1.3.0) sshkit (>= 1.6.1, != 1.7.0) arel (8.0.0) autoprefixer-rails (7.1.1.2) @@ -54,7 +55,7 @@ GEM will_paginate builder (3.2.3) byebug (9.0.6) - capistrano (3.8.1) + capistrano (3.8.2) airbrussh (>= 1.0.0) i18n rake (>= 10.0.0) @@ -65,14 +66,18 @@ GEM capistrano-rails (1.3.0) capistrano (~> 3.1) capistrano-bundler (~> 1.1) - capybara (2.14.3) + capybara (2.14.4) addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) - childprocess (0.7.0) + carrierwave (1.1.0) + activemodel (>= 4.0.0) + activesupport (>= 4.0.0) + mime-types (>= 1.16) + childprocess (0.7.1) ffi (~> 1.0, >= 1.0.11) coffee-rails (4.2.2) coffee-script (>= 2.2.0) @@ -86,13 +91,155 @@ GEM activesupport (>= 3.0) deep_merge (~> 1.1.1) deep_merge (1.1.1) - erubi (1.6.0) + erubi (1.6.1) + excon (0.57.1) execjs (2.7.0) ffaker (2.6.0) ffi (1.9.18) + fission (0.5.0) + CFPropertyList (~> 2.2) + fog (1.40.0) + fog-aliyun (>= 0.1.0) + fog-atmos + fog-aws (>= 0.6.0) + fog-brightbox (~> 0.4) + fog-cloudatcost (~> 0.1.0) + fog-core (~> 1.43) + fog-digitalocean (>= 0.3.0) + fog-dnsimple (~> 1.0) + fog-dynect (~> 0.0.2) + fog-ecloud (~> 0.1) + fog-google (<= 0.1.0) + fog-json + fog-local + fog-openstack + fog-powerdns (>= 0.1.1) + fog-profitbricks + fog-rackspace + fog-radosgw (>= 0.0.2) + fog-riakcs + fog-sakuracloud (>= 0.0.4) + fog-serverlove + fog-softlayer + fog-storm_on_demand + fog-terremark + fog-vmfusion + fog-voxel + fog-vsphere (>= 0.4.0) + fog-xenserver + fog-xml (~> 0.1.1) + ipaddress (~> 0.5) + json (>= 1.8, < 2.0) + fog-aliyun (0.1.0) + fog-core (~> 1.27) + fog-json (~> 1.0) + ipaddress (~> 0.8) + xml-simple (~> 1.1) + fog-atmos (0.1.0) + fog-core + fog-xml + fog-aws (1.4.0) + fog-core (~> 1.38) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) + fog-brightbox (0.11.0) + fog-core (~> 1.22) + fog-json + inflecto (~> 0.0.2) + fog-cloudatcost (0.1.2) + fog-core (~> 1.36) + fog-json (~> 1.0) + fog-xml (~> 0.1) + ipaddress (~> 0.8) + fog-core (1.44.3) + builder + excon (~> 0.49) + formatador (~> 0.2) + fog-digitalocean (0.3.0) + fog-core (~> 1.42) + fog-json (>= 1.0) + fog-xml (>= 0.1) + ipaddress (>= 0.5) + fog-dnsimple (1.0.0) + fog-core (~> 1.38) + fog-json (~> 1.0) + fog-dynect (0.0.3) + fog-core + fog-json + fog-xml + fog-ecloud (0.3.0) + fog-core + fog-xml + fog-google (0.1.0) + fog-core + fog-json + fog-xml + fog-json (1.0.2) + fog-core (~> 1.0) + multi_json (~> 1.10) + fog-local (0.3.1) + fog-core (~> 1.27) + fog-openstack (0.1.21) + fog-core (>= 1.40) + fog-json (>= 1.0) + ipaddress (>= 0.8) + fog-powerdns (0.1.1) + fog-core (~> 1.27) + fog-json (~> 1.0) + fog-xml (~> 0.1) + fog-profitbricks (3.0.0) + fog-core (~> 1.42) + fog-json (~> 1.0) + fog-rackspace (0.1.5) + fog-core (>= 1.35) + fog-json (>= 1.0) + fog-xml (>= 0.1) + ipaddress (>= 0.8) + fog-radosgw (0.0.5) + fog-core (>= 1.21.0) + fog-json + fog-xml (>= 0.0.1) + fog-riakcs (0.1.0) + fog-core + fog-json + fog-xml + fog-sakuracloud (1.7.5) + fog-core + fog-json + fog-serverlove (0.1.2) + fog-core + fog-json + fog-softlayer (1.1.4) + fog-core + fog-json + fog-storm_on_demand (0.1.1) + fog-core + fog-json + fog-terremark (0.1.0) + fog-core + fog-xml + fog-vmfusion (0.1.0) + fission + fog-core + fog-voxel (0.1.0) + fog-core + fog-xml + fog-vsphere (1.11.0) + fog-core + rbvmomi (~> 1.9) + fog-xenserver (0.3.0) + fog-core + fog-xml + fog-xml (0.1.3) + fog-core + nokogiri (>= 1.5.11, < 2.0.0) + formatador (0.2.5) globalid (0.4.0) activesupport (>= 4.2.0) i18n (0.8.4) + inflecto (0.0.2) + ipaddress (0.8.3) jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) @@ -100,6 +247,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + json (1.8.6) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -112,6 +260,7 @@ GEM mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mini_magick (4.7.2) mini_portile2 (2.2.0) minitest (5.10.2) multi_json (1.12.1) @@ -126,17 +275,17 @@ GEM rack (2.0.3) rack-test (0.6.3) rack (>= 1.0) - rails (5.1.1) - actioncable (= 5.1.1) - actionmailer (= 5.1.1) - actionpack (= 5.1.1) - actionview (= 5.1.1) - activejob (= 5.1.1) - activemodel (= 5.1.1) - activerecord (= 5.1.1) - activesupport (= 5.1.1) + rails (5.1.2) + actioncable (= 5.1.2) + actionmailer (= 5.1.2) + actionpack (= 5.1.2) + actionview (= 5.1.2) + activejob (= 5.1.2) + activemodel (= 5.1.2) + activerecord (= 5.1.2) + activesupport (= 5.1.2) bundler (>= 1.3.0, < 2.0) - railties (= 5.1.1) + railties (= 5.1.2) sprockets-rails (>= 2.0.0) rails-controller-testing (1.0.2) actionpack (~> 5.x, >= 5.0.1) @@ -147,16 +296,21 @@ GEM nokogiri (>= 1.6) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (5.1.1) - actionpack (= 5.1.1) - activesupport (= 5.1.1) + railties (5.1.2) + actionpack (= 5.1.2) + activesupport (= 5.1.2) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) rake (12.0.0) - rb-fsevent (0.9.8) + rb-fsevent (0.10.2) rb-inotify (0.9.10) ffi (>= 0.5.0, < 2) + rbvmomi (1.11.3) + builder (~> 3.0) + json (>= 1.8) + nokogiri (~> 1.5) + trollop (~> 2.1) ruby_dep (1.5.0) rubyzip (1.2.1) sass (3.4.24) @@ -182,12 +336,13 @@ GEM activesupport (>= 4.0) sprockets (>= 3.0.0) sqlite3 (1.3.13) - sshkit (1.13.1) + sshkit (1.14.0) net-scp (>= 1.1.2) net-ssh (>= 2.8.0) thor (0.19.4) thread_safe (0.3.6) tilt (2.0.7) + trollop (2.1.2) turbolinks (5.0.1) turbolinks-source (~> 5) turbolinks-source (5.0.3) @@ -204,6 +359,7 @@ GEM websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) will_paginate (3.1.6) + xml-simple (1.1.5) xpath (2.1.0) nokogiri (~> 1.3) @@ -217,12 +373,15 @@ DEPENDENCIES byebug capistrano-rails capybara (~> 2.13) + carrierwave coffee-rails (~> 4.2) config ffaker + fog jbuilder (~> 2.5) jquery-rails listen (>= 3.0.5, < 3.2) + mini_magick puma (~> 3.7) rails (~> 5.1.1) rails-controller-testing diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index 4888a24..6e896dd 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -7,6 +7,7 @@ $blue-dark: #fff; $gray-medium-light: #eaeaea; $light-gray: #777; $gray: #bbb; +$dark-red: #e8e8e8; @mixin box_sizing { -moz-box-sizing: border-box; @@ -208,3 +209,49 @@ input { padding: 10px 0; } } + +/* microposts */ + +.microposts { + list-style: none; + padding: 0; + li { + padding: 10px 0; + border-top: 1px solid $dark-red; + } + .user { + margin-top: 5em; + padding-top: 0; + } + .content { + display: block; + margin-left: 60px; + img { + display: block; + padding: 5px 0; + } + } + .timestamp { + color: $gray-light; + display: block; + margin-left: 60px; + } + .gravatar { + float: left; + margin: 5px 10px; + } +} + +aside { + textarea { + height: 100px; + margin-bottom: 5px; + } +} + +span.picture { + margin-top: 10px; + input { + border: 0; + } +} diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 7193857..ad3a246 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,4 +1,13 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception include SessionsHelper + + private + + def logged_in_user + return if logged_in? + store_location + flash[:danger] = t ".please_login" + redirect_to login_url + end end diff --git a/app/controllers/microposts_controller.rb b/app/controllers/microposts_controller.rb new file mode 100644 index 0000000..3b6a499 --- /dev/null +++ b/app/controllers/microposts_controller.rb @@ -0,0 +1,32 @@ +class MicropostsController < ApplicationController + before_action :logged_in_user, only: [:create, :destroy] + before_action :correct_user, only: :destroy + + def create + @micropost = current_user.microposts.build micropost_params + if @micropost.save + flash[:success] = t ".micropost_created!" + redirect_to root_url + else + @feed_items = [] + render "static_pages/home" + end + end + + def destroy + @micropost.destroy + flash[:success] = t ".micropost_deleted" + redirect_to request.referrer || root_url + end + + private + + def micropost_params + params.require(:micropost).permit :content, :picture + end + + def correct_user + @micropost = current_user.microposts.find_by id: params[:id] + redirect_to root_url if @micropost.nil? + end +end diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb index 7cce5ad..1159c4a 100644 --- a/app/controllers/password_resets_controller.rb +++ b/app/controllers/password_resets_controller.rb @@ -50,13 +50,13 @@ def load_user def valid_user return if @user && @user.activated? && - @user.authenticated?(:reset, params[:id]) + @user.authenticated? :reset, params[:id] redirect_to root_url end def check_expiration return unless @user.password_reset_expired? - flash[:danger] = t ".password_reset_has_expired." + flash[:danger] = t ".password_reset_has_expired" redirect_to new_password_reset_url end end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index bff6ab8..c298f5e 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -1,5 +1,9 @@ class StaticPagesController < ApplicationController def home + if logged_in? + @micropost = current_user.microposts.build + @feed_items = current_user.feed.paginate page: params[:page] + end end def help diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index a3f5871..5ad82d0 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -27,6 +27,7 @@ def create end def show + @microposts = @user.microposts.paginate page: params[:page] end def edit diff --git a/app/helpers/microposts_helper.rb b/app/helpers/microposts_helper.rb new file mode 100644 index 0000000..f08aad2 --- /dev/null +++ b/app/helpers/microposts_helper.rb @@ -0,0 +1,2 @@ +module MicropostsHelper +end diff --git a/app/models/micropost.rb b/app/models/micropost.rb new file mode 100644 index 0000000..9b4b55b --- /dev/null +++ b/app/models/micropost.rb @@ -0,0 +1,20 @@ +class Micropost < ApplicationRecord + belongs_to :user + + default_scope -> {order created_at: :desc} + + mount_uploader :picture, PictureUploader + + validates :user_id, presence: true + validates :content, presence: true, + length: {maximum: Settings.model.microposts.size_post} + validate :picture_size + + private + + def picture_size + if picture.size > Settings.model.microposts.size_bit.megabytes + errors.add :picture, t(".should_be_5MB") + end + end +end diff --git a/app/models/user.rb b/app/models/user.rb index c21b72d..2576c80 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,4 +1,6 @@ class User < ApplicationRecord + has_many :microposts, dependent: :destroy + attr_accessor :remember_token, :activation_token, :reset_token before_save :downcase_email @@ -67,6 +69,10 @@ def forget update_attributes remember_digest: nil end + def feed + Micropost.where("user_id = ?", id) + end + private def downcase_email diff --git a/app/uploaders/picture_uploader.rb b/app/uploaders/picture_uploader.rb new file mode 100644 index 0000000..faf52af --- /dev/null +++ b/app/uploaders/picture_uploader.rb @@ -0,0 +1,19 @@ +class PictureUploader < CarrierWave::Uploader::Base + include CarrierWave::MiniMagick + + process resize_to_limit: [400, 400] + + if Rails.env.production? + storage :fog + else + storage :file + end + + def store_dir + "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}" + end + + def extension_white_list + %w(jpg jpeg gif png) + end +end diff --git a/app/views/microposts/_micropost.html.erb b/app/views/microposts/_micropost.html.erb new file mode 100644 index 0000000..4c5b57b --- /dev/null +++ b/app/views/microposts/_micropost.html.erb @@ -0,0 +1,18 @@ +
  • + <%= link_to gravatar_for(micropost.user, size: Settings.microposts.size_img), + micropost.user %> + <%= link_to micropost.user.name, micropost.user %> + + <%= micropost.content %> + <%= image_tag micropost.picture.url if micropost.picture? %> + + + <%= t ".posted" %> + <%= time_ago_in_words micropost.created_at %> + <%= t ".ago" %> + <% if current_user.is_user? micropost.user %> + <%= link_to t(".delete"), micropost, method: :delete, + data: {confirm: t(".you_sure")} %> + <% end %> + +
  • diff --git a/app/views/shared/_error_messages.html.erb b/app/views/shared/_error_messages.html.erb index f344378..380bcdf 100644 --- a/app/views/shared/_error_messages.html.erb +++ b/app/views/shared/_error_messages.html.erb @@ -1,10 +1,10 @@ -<% if @user.errors.any? %> +<% if object.errors.any? %>
    - <%= t ".the_form_contains" %> <%= pluralize @user.errors.count, t(".error") %>. + <%= t ".the_form_contains" %> <%= pluralize object.errors.count, t(".error") %>.
    diff --git a/app/views/shared/_feed.html.erb b/app/views/shared/_feed.html.erb new file mode 100644 index 0000000..ce61492 --- /dev/null +++ b/app/views/shared/_feed.html.erb @@ -0,0 +1,6 @@ +<% if @feed_items.any? %> +
      + <%= render @feed_items %> +
    + <%= will_paginate @feed_items %> +<% end %> diff --git a/app/views/shared/_micropost_form.html.erb b/app/views/shared/_micropost_form.html.erb new file mode 100644 index 0000000..4a23a4a --- /dev/null +++ b/app/views/shared/_micropost_form.html.erb @@ -0,0 +1,19 @@ +<%= form_for(@micropost) do |f| %> + <%= render "shared/error_messages", object: f.object %> +
    + <%= f.text_area :content, placeholder: t(".compose_new_micropost") %> +
    + <%= f.submit t(".post"), class: "btn btn-primary" %> + + <%= f.file_field :picture, accept: "image/jpeg,image/gif,image/png" %> + +<% end %> + + diff --git a/app/views/shared/_user_info.html.erb b/app/views/shared/_user_info.html.erb new file mode 100644 index 0000000..c2bc4eb --- /dev/null +++ b/app/views/shared/_user_info.html.erb @@ -0,0 +1,5 @@ +<%= link_to gravatar_for(current_user, size: Settings.microposts.size_img), + current_user %> +

    <%= current_user.name %>

    +<%= link_to t(".view_my_profile"), current_user %> +<%= pluralize current_user.microposts.count, t(".micropost") %> diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index ae45311..a7e1fc4 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,11 +1,28 @@ -
    -

    <%= t ".welcome" %>

    +<% if logged_in? %> +
    + +
    +

    <%= t ".micropost_feed" %>

    + <%= render "shared/feed" %> +
    +
    +<% else %> +
    +

    <%= t ".welcome" %>

    -

    - <%= t ".this_is_the_home_page" %> - <%= link_to t(".rubyonrails"), "http://www.railstutorial.org/" %> - <%= t ".sample_application" %> -

    +

    + <%= t ".this_is_the_home_page" %> + <%= link_to t(".rubyonrails"), "http://www.railstutorial.org/" %> + <%= t ".sample_application" %> +

    - <%= link_to t(".signup_now"), signup_path, class: "btn btn-lg btn-primary" %> -
    + <%= link_to t(".signup_now"), signup_path, class: "btn btn-lg btn-primary" %> +
    +<% end %> diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb index 0074206..8790121 100644 --- a/app/views/users/_form.html.erb +++ b/app/views/users/_form.html.erb @@ -1,5 +1,5 @@ <%= form_for @user do |f| %> - <%= render "shared/error_messages" %> + <%= render "shared/error_messages", object: f.object %> <%= f.label t(".name") %> <%= f.text_field :name, class: "form-control" %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 3142684..27c3e8d 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -8,4 +8,15 @@ +
    + <% if @user.microposts.any? %> +

    + <%= t ".micopost" %>(<%= @user.microposts.count %>) +

    +
      + <%= render @microposts %> +
    + <%= will_paginate @microposts %> + <% end %> +
    diff --git a/config/locales/en.yml b/config/locales/en.yml index 2bd8911..5d8ff99 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -32,6 +32,7 @@ en: signup_now: "Sign up now!" this_is_the_home_page: "This is the home page for the" welcome: "Welcome to the Sample App" + micropost_feed: "Micropost feed" layouts: _footer: about: "About" @@ -78,18 +79,23 @@ en: password_confirmation: "Password_confirmation" index: all_users: "All users" - user: - delete: "Delete" - you_sure: "You sure ?" destroy: user_delete: "User deleted" error: "deleted error" update: profile_updated: "Profile updated" + show: + micropost: "Micropost" shared: error_messages: the_form_contains: "The form contains has" error: "error" + micropost_form: + post: "Post" + compose_new_micropost: "Compose new micropost" + user_info: + view_my_profile: "View my profile" + micropost: "Micropost" sessions: create: invalid: "Invalid email/password combination" @@ -135,5 +141,13 @@ en: update: password_has_been_reset: "Password has been reset" this_link: "This link will expire in two hours." - - + microposts: + create: + micropost_created!: "Micropost created !" + destroy: + micropost_deleted: "Micropost deleted" + micropost: + posted: "Posted" + ago: "ago." + delete: "delete" + you_sure: "You Sure ?" diff --git a/config/routes.rb b/config/routes.rb index 2658503..7f3eab4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -10,4 +10,5 @@ resources :users resources :account_activations, only: :edit resources :password_resets + resources :microposts, only: [:create, :destroy] end diff --git a/config/settings.yml b/config/settings.yml index d006e86..8119dd0 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -4,6 +4,9 @@ model: maximum_email: 255 minimum_password: 6 time: 2 + microposts: + size_post: 140 + size_bit: 5 view: users: size: 50 @@ -12,3 +15,5 @@ helpers: controller: user_controller: size_page: 20 +microposts: + size_img: 50 diff --git a/db/schema.rb b/db/schema.rb deleted file mode 100644 index 6d1a95a..0000000 --- a/db/schema.rb +++ /dev/null @@ -1,31 +0,0 @@ -# This file is auto-generated from the current state of the database. Instead -# of editing this file, please use the migrations feature of Active Record to -# incrementally modify your database, and then regenerate this schema definition. -# -# Note that this schema.rb definition is the authoritative source for your -# database schema. If you need to create the application database on another -# system, you should be using db:schema:load, not running all the migrations -# from scratch. The latter is a flawed and unsustainable approach (the more migrations -# you'll amass, the slower it'll run and the greater likelihood for issues). -# -# It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20170630010620) do - - create_table "users", force: :cascade do |t| - t.string "name" - t.string "email" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.string "password_digest" - t.string "remember_digest" - t.boolean "admin" - t.string "activation_digest" - t.boolean "activated", default: false - t.datetime "activated_at" - t.string "reset_digest" - t.datetime "reset_sent_at" - t.index ["email"], name: "index_users_on_email", unique: true - end - -end diff --git a/db/seeds.rb b/db/seeds.rb index c991ec3..e97edb9 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -10,3 +10,9 @@ password_confirmation: password, admin: false, activated: true, activated_at: Time.zone.now end + +users = User.order(:created_at).take 6 +10.times do + content = FFaker::Lorem.sentence 5 + users.each {|user| user.microposts.create! content: content} +end From 0948b4c40d5e735446f01318134663c3884af532 Mon Sep 17 00:00:00 2001 From: nghiadk Date: Mon, 3 Jul 2017 10:33:22 +0700 Subject: [PATCH 2/2] Add user following --- app/assets/stylesheets/custom.scss | 38 ++++++++++++++ app/controllers/relationships_controller.rb | 22 ++++++++ app/controllers/users_controller.rb | 12 +++++ app/helpers/microposts_helper.rb | 2 - app/models/relationship.rb | 7 +++ app/models/user.rb | 22 +++++++- app/views/relationships/create.js.erb | 2 + app/views/relationships/destroy.js.erb | 2 + app/views/shared/_stats.html.erb | 15 ++++++ app/views/static_pages/home.html.erb | 3 ++ app/views/users/_follow.html.erb | 4 ++ app/views/users/_follow_form.html.erb | 9 ++++ app/views/users/_unfollow.html.erb | 4 ++ app/views/users/show.html.erb | 4 ++ app/views/users/show_follow.html.erb | 30 +++++++++++ config/application.rb | 5 +- config/locales/en.yml | 4 ++ config/routes.rb | 7 ++- .../20170703022319_create_relationships.rb | 13 +++++ .../20170703023350_create_microposts.rb | 11 ++++ db/schema.rb | 50 +++++++++++++++++++ db/seeds.rb | 7 +++ 22 files changed, 265 insertions(+), 8 deletions(-) create mode 100644 app/controllers/relationships_controller.rb delete mode 100644 app/helpers/microposts_helper.rb create mode 100644 app/models/relationship.rb create mode 100644 app/views/relationships/create.js.erb create mode 100644 app/views/relationships/destroy.js.erb create mode 100644 app/views/shared/_stats.html.erb create mode 100644 app/views/users/_follow.html.erb create mode 100644 app/views/users/_follow_form.html.erb create mode 100644 app/views/users/_unfollow.html.erb create mode 100644 app/views/users/show_follow.html.erb create mode 100644 db/migrate/20170703022319_create_relationships.rb create mode 100644 db/migrate/20170703023350_create_microposts.rb create mode 100644 db/schema.rb diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss index 6e896dd..ac71622 100644 --- a/app/assets/stylesheets/custom.scss +++ b/app/assets/stylesheets/custom.scss @@ -154,6 +154,44 @@ aside { margin-top: 15px; } +.stats { + overflow: auto; + margin-top: 0; + padding: 0; + a { + border-left: 1px solid $gray-lighter; + color: gray; + float: left; + padding: 0 10px; + &:first-child { + border: 0; + padding-left: 0; + } + &:hover { + color: blue; + text-decoration: none; + } + } + strong { + display: block; + } +} + +.user_avatars { + margin-top: 10px; + overflow: auto; + .gravatar { + margin: 1px 1px; + } + a { + padding: 0; + } +} + +.users.follow { + padding: 0; +} + /* forms */ input, textarea, select, .uneditable-input { diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb new file mode 100644 index 0000000..38f43b5 --- /dev/null +++ b/app/controllers/relationships_controller.rb @@ -0,0 +1,22 @@ +class RelationshipsController < ApplicationController + before_action :logged_in_user + + def create + @user = User.find followedid: params[:followed_id] + current_user.follow @user + respond_to do |format| + format.html {redirect_to @user} + format.js + end + end + + def destroy + @user = Relationship.find(id: params[:id]).followed + current_user.unfollow @user + respond_to do |format| + format.html {redirect_to @user} + format.js + end + end + +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5ad82d0..b2b3a76 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -51,6 +51,18 @@ def destroy redirect_to users_url end + def following + @title = "Following" + @users = @user.following.paginate page: params[:page] + render "show_follow" + end + + def followers + @title = "Followers" + @users = @user.followers.paginate page: params[:page] + render "show_follow" + end + private def user_params diff --git a/app/helpers/microposts_helper.rb b/app/helpers/microposts_helper.rb deleted file mode 100644 index f08aad2..0000000 --- a/app/helpers/microposts_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module MicropostsHelper -end diff --git a/app/models/relationship.rb b/app/models/relationship.rb new file mode 100644 index 0000000..edeea3f --- /dev/null +++ b/app/models/relationship.rb @@ -0,0 +1,7 @@ +class Relationship < ApplicationRecord + belongs_to :follower, class_name: "User" + belongs_to :followed, class_name: "User" + + validates :follower_id, presence: true + validates :followed_id, presence: true +end diff --git a/app/models/user.rb b/app/models/user.rb index 2576c80..2531859 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,11 @@ class User < ApplicationRecord has_many :microposts, dependent: :destroy + has_many :active_relationships, class_name: "Relationship", + foreign_key: "follower_id", dependent: :destroy + has_many :passive_relationships, class_name: "Relationship", + foreign_key: "followed_id", dependent: :destroy + has_many :following, through: :active_relationships, source: :followed + has_many :followers, through: :passive_relationships, source: :follower attr_accessor :remember_token, :activation_token, :reset_token @@ -70,7 +76,21 @@ def forget end def feed - Micropost.where("user_id = ?", id) + following_ids = "SELECT followed_id FROM relationships WHERE follower_id = :user_id" + Micropost.where("user_id IN (#{following_ids}) OR user_id = :user_id", + user_id: id) + end + + def follow other_user + active_relationships.create(followed_id: other_user.id) + end + + def unfollow other_user + following.delete other_user + end + + def following? other_user + following.include? other_user end private diff --git a/app/views/relationships/create.js.erb b/app/views/relationships/create.js.erb new file mode 100644 index 0000000..b9f9f7a --- /dev/null +++ b/app/views/relationships/create.js.erb @@ -0,0 +1,2 @@ +$("#follow_form").html("<%= escape_javascript(render('users/unfollow')) %>"); +$("#followers").html('<%= @user.followers.count %>'); diff --git a/app/views/relationships/destroy.js.erb b/app/views/relationships/destroy.js.erb new file mode 100644 index 0000000..3c45f4d --- /dev/null +++ b/app/views/relationships/destroy.js.erb @@ -0,0 +1,2 @@ +$("#follow_form").html("<%= escape_javascript(render('users/follow')) %>"); +$("#followers").html('<%= @user.followers.count %>'); diff --git a/app/views/shared/_stats.html.erb b/app/views/shared/_stats.html.erb new file mode 100644 index 0000000..c8776e1 --- /dev/null +++ b/app/views/shared/_stats.html.erb @@ -0,0 +1,15 @@ +<% @user ||= current_user %> + diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index a7e1fc4..3fedf55 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -4,6 +4,9 @@ +
    + <%= render "shared/stats" %> +
    <%= render "shared/micropost_form" %>
    diff --git a/app/views/users/_follow.html.erb b/app/views/users/_follow.html.erb new file mode 100644 index 0000000..df8452d --- /dev/null +++ b/app/views/users/_follow.html.erb @@ -0,0 +1,4 @@ +<%= form_for current_user.active_relationships.build, remote: true do |f| %> +
    <%= hidden_field_tag :followed_id, @user.id %>
    + <%= f.submit t(".follow"), class: "btn btn-primary" %> +<% end %> diff --git a/app/views/users/_follow_form.html.erb b/app/views/users/_follow_form.html.erb new file mode 100644 index 0000000..1dac4b6 --- /dev/null +++ b/app/views/users/_follow_form.html.erb @@ -0,0 +1,9 @@ +<% unless current_user.is_user? @user %> +
    + <% if current_user.following? @user %> + <%= render "unfollow" %> + <% else %> + <%= render "follow" %> + <% end %> +
    +<% end %> diff --git a/app/views/users/_unfollow.html.erb b/app/views/users/_unfollow.html.erb new file mode 100644 index 0000000..219c416 --- /dev/null +++ b/app/views/users/_unfollow.html.erb @@ -0,0 +1,4 @@ +<%= form_for current_user.active_relationships.find_by(followed_id: @user.id), + html: {method: :delete}, remote: true do |f| %> + <%= f.submit "Unfollow", class: "btn" %> +<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 27c3e8d..b926f25 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -7,8 +7,12 @@ <%= @user.name %> +
    + <%= render "shared/stats" %> +
    + <%= render "follow_form" if logged_in? %> <% if @user.microposts.any? %>

    <%= t ".micopost" %>(<%= @user.microposts.count %>) diff --git a/app/views/users/show_follow.html.erb b/app/views/users/show_follow.html.erb new file mode 100644 index 0000000..2574a64 --- /dev/null +++ b/app/views/users/show_follow.html.erb @@ -0,0 +1,30 @@ +<% provide(:title, @title) %> +
    + +
    +

    <%= @title %>

    + <% if @users.any? %> + + <%= will_paginate %> + <% end %> +
    +
    diff --git a/config/application.rb b/config/application.rb index 8d32e9c..f814991 100644 --- a/config/application.rb +++ b/config/application.rb @@ -10,9 +10,6 @@ module SampleApp class Application < Rails::Application # Initialize configuration defaults for originally generated Rails version. config.load_defaults 5.1 - - # Settings in config/environments/* take precedence over those specified here. - # Application configuration should go into files in config/initializers - # -- all .rb files in that directory are automatically loaded. + config.action_view.embed_authenticity_token_in_remote_forms = true end end diff --git a/config/locales/en.yml b/config/locales/en.yml index 5d8ff99..da2eea1 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -86,6 +86,10 @@ en: profile_updated: "Profile updated" show: micropost: "Micropost" + follow: + follow: "Follow" + user: + delete: "Delete" shared: error_messages: the_form_contains: "The form contains has" diff --git a/config/routes.rb b/config/routes.rb index 7f3eab4..2f90bd2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -7,8 +7,13 @@ get "/login", to: "sessions#new" post "/login", to: "sessions#create" delete "/logout", to: "sessions#destroy" - resources :users + resources :users do + member do + get :following, :followers + end + end resources :account_activations, only: :edit resources :password_resets resources :microposts, only: [:create, :destroy] + resources :relationships, only: [:create, :destroy] end diff --git a/db/migrate/20170703022319_create_relationships.rb b/db/migrate/20170703022319_create_relationships.rb new file mode 100644 index 0000000..dd01cdc --- /dev/null +++ b/db/migrate/20170703022319_create_relationships.rb @@ -0,0 +1,13 @@ +class CreateRelationships < ActiveRecord::Migration[5.1] + def change + create_table :relationships do |t| + t.integer :follower_id + t.integer :followed_id + + t.timestamps + end + add_index :relationships, :follower_id + add_index :relationships, :followed_id + add_index :relationships, [:follower_id, :followed_id], unique: true + end +end diff --git a/db/migrate/20170703023350_create_microposts.rb b/db/migrate/20170703023350_create_microposts.rb new file mode 100644 index 0000000..84f428f --- /dev/null +++ b/db/migrate/20170703023350_create_microposts.rb @@ -0,0 +1,11 @@ +class CreateMicroposts < ActiveRecord::Migration[5.1] + def change + create_table :microposts do |t| + t.text :content + t.references :user, foreign_key: true + + t.timestamps + end + add_index :microposts, [:user_id, :created_at] + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000..0a403ec --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,50 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20170703023350) do + + create_table "microposts", force: :cascade do |t| + t.text "content" + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at" + t.index ["user_id"], name: "index_microposts_on_user_id" + end + + create_table "relationships", force: :cascade do |t| + t.integer "follower_id" + t.integer "followed_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["followed_id"], name: "index_relationships_on_followed_id" + t.index ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true + t.index ["follower_id"], name: "index_relationships_on_follower_id" + end + + create_table "users", force: :cascade do |t| + t.string "name" + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "password_digest" + t.string "remember_digest" + t.boolean "admin" + t.string "activation_digest" + t.boolean "activated", default: false + t.datetime "activated_at" + t.string "reset_digest" + t.datetime "reset_sent_at" + t.index ["email"], name: "index_users_on_email", unique: true + end + +end diff --git a/db/seeds.rb b/db/seeds.rb index e97edb9..361e43e 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -16,3 +16,10 @@ content = FFaker::Lorem.sentence 5 users.each {|user| user.microposts.create! content: content} end + +users = User.all +user = users.first +following = users[2..50] +followers = users[3..40] +following.each {|followed| user.follow followed} +followers.each {|follower| follower.follow user}