diff --git a/.gitignore b/.gitignore
index 7fe04b9..a695304 100644
--- a/.gitignore
+++ b/.gitignore
@@ -43,3 +43,4 @@ yarn-debug.log*
config/settings.local.yml
config/settings/*.local.yml
config/environments/*.local.yml
+config/application.yml
diff --git a/Gemfile b/Gemfile
index 774eee8..6d42c8a 100644
--- a/Gemfile
+++ b/Gemfile
@@ -9,6 +9,7 @@ gem "bootstrap-sass", "3.4.1"
gem "bootstrap-will_paginate", "1.0.0"
gem "config"
gem "faker", "2.1.2"
+gem "figaro"
gem "jbuilder", "~> 2.7"
gem "puma", "~> 4.1"
gem "rails", "~> 6.0.3", ">= 6.0.3.3"
@@ -18,6 +19,9 @@ gem "sqlite3", "~> 1.4"
gem "turbolinks", "~> 5"
gem "webpacker", "~> 4.0"
gem "will_paginate", "3.1.8"
+gem "image_processing", "1.9.3"
+gem "mini_magick", "4.9.5"
+gem "active_storage_validations", "0.8.2"
group :development, :test do
gem "byebug", platforms: [:mri, :mingw, :x64_mingw]
diff --git a/Gemfile.lock b/Gemfile.lock
index 5e3b49c..a600a44 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -37,6 +37,8 @@ GEM
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.1, >= 1.2.0)
+ active_storage_validations (0.8.2)
+ rails (>= 5.2.0)
activejob (6.0.3.3)
activesupport (= 6.0.3.3)
globalid (>= 0.3.6)
@@ -59,7 +61,7 @@ GEM
addressable (2.7.0)
public_suffix (>= 2.0.2, < 5.0)
ast (2.4.1)
- autoprefixer-rails (10.0.0.2)
+ autoprefixer-rails (10.0.1.0)
execjs
bcrypt (3.1.13)
bindex (0.8.1)
@@ -98,7 +100,7 @@ GEM
concurrent-ruby (~> 1.0)
dry-equalizer (0.3.0)
dry-inflector (0.2.0)
- dry-initializer (3.0.3)
+ dry-initializer (3.0.4)
dry-logic (1.0.8)
concurrent-ruby (~> 1.0)
dry-core (~> 0.2)
@@ -130,10 +132,15 @@ GEM
faker (2.1.2)
i18n (>= 0.8)
ffi (1.13.1)
+ figaro (1.2.0)
+ thor (>= 0.14.0, < 2)
globalid (0.4.2)
activesupport (>= 4.2.0)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
+ image_processing (1.9.3)
+ mini_magick (>= 4.9.5, < 5)
+ ruby-vips (>= 2.0.13, < 3)
jaro_winkler (1.5.4)
jbuilder (2.10.1)
activesupport (>= 5.0.0)
@@ -149,6 +156,7 @@ GEM
mimemagic (~> 0.3.2)
method_source (1.0.0)
mimemagic (0.3.5)
+ mini_magick (4.9.5)
mini_mime (1.0.2)
mini_portile2 (2.4.0)
minitest (5.14.2)
@@ -215,6 +223,8 @@ GEM
rack (>= 1.1)
rubocop (>= 0.72.0)
ruby-progressbar (1.10.1)
+ ruby-vips (2.0.17)
+ ffi (~> 1.9)
rubyzip (2.3.0)
sass-rails (6.0.0)
sassc-rails (~> 2.1, >= 2.1.1)
@@ -283,6 +293,7 @@ PLATFORMS
web-unknown
DEPENDENCIES
+ active_storage_validations (= 0.8.2)
bcrypt (= 3.1.13)
bootsnap (>= 1.4.2)
bootstrap-sass (= 3.4.1)
@@ -291,8 +302,11 @@ DEPENDENCIES
capybara (>= 2.15)
config
faker (= 2.1.2)
+ figaro
+ image_processing (= 1.9.3)
jbuilder (~> 2.7)
listen (~> 3.2)
+ mini_magick (= 4.9.5)
puma (~> 4.1)
rails (~> 6.0.3, >= 6.0.3.3)
rails-i18n
diff --git a/app/assets/stylesheets/custom.scss b/app/assets/stylesheets/custom.scss
index bc8ee01..6ff0d25 100644
--- a/app/assets/stylesheets/custom.scss
+++ b/app/assets/stylesheets/custom.scss
@@ -146,3 +146,101 @@ input {
border-bottom: 1px solid $gray-lighter;
}
}
+
+.microposts {
+ list-style: none;
+ padding: 0;
+ li {
+ padding: 10px 0;
+ border-top: 1px solid #e8e8e8;
+ }
+ .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-right: 10px;
+ width: 8%;
+ }
+}
+aside {
+ textarea {
+ height: 100px;
+ margin-bottom: 5px;
+ }
+}
+span.image {
+ margin-top: 10px;
+ width: 20px;
+ input {
+ border: 0;
+ }
+}
+
+input[type="file"] {
+ display: block;
+ position: absolute;
+}
+
+.gravatar {
+ float: left;
+ margin-right: 10px;
+}
+
+.gravatar_edit {
+ margin-top: 15px;
+}
+
+.stats {
+ overflow: auto;
+ margin-top: 0;
+ padding: 0;
+ a {
+ float: left;
+ padding: 0 10px;
+ border-left: 1px solid $gray-lighter;
+ color: gray;
+ &:first-child {
+ padding-left: 0;
+ border: 0;
+ }
+ &:hover {
+ text-decoration: none;
+ color: blue;
+
+ }
+ strong {
+ display: block;
+ }
+ }
+
+ .user_avatars {
+ overflow: auto;
+ margin-top: 10px;
+ .gravatar {
+ margin: 1px 1px;
+ }
+ a {
+ padding: 0;
+ }
+ }
+
+ .users.follow {
+ padding: 0;
+ }
+}
diff --git a/app/controllers/account_activations_controller.rb b/app/controllers/account_activations_controller.rb
new file mode 100644
index 0000000..3cb0aa4
--- /dev/null
+++ b/app/controllers/account_activations_controller.rb
@@ -0,0 +1,14 @@
+class AccountActivationsController < ApplicationController
+ def edit
+ user = User.find_by email: params[:email]
+ if user && !user.activated? && user.authenticated?(:activation, params[:id])
+ user.activate
+ log_in user
+ flash[:success] = "Account activated!"
+ redirect_to user
+ else
+ flash[:danger] = "Invalid activation link"
+ redirect_to root_url
+ end
+ end
+end
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index c5f5898..edde33a 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -11,4 +11,12 @@ def set_locale
def default_url_options
{locale: I18n.locale}
end
+
+ def logged_in_user
+ unless logged_in?
+ store_location
+ flash[:danger] = t("login_in.messages")
+ redirect_to login_url
+ end
+ end
end
diff --git a/app/controllers/microposts_controller.rb b/app/controllers/microposts_controller.rb
new file mode 100644
index 0000000..8a8068b
--- /dev/null
+++ b/app/controllers/microposts_controller.rb
@@ -0,0 +1,33 @@
+class MicropostsController < ApplicationController
+ before_action :logged_in_user, only: [:create, :destroy]
+ before_action :correct_user, only: :destroy
+
+ def create
+ @micropost = current_user.microposts.build micropost_params
+ @micropost.image.attach(params[:micropost][:image])
+ if @micropost.save
+ flash[:success] = t("micropost.messages1")
+ redirect_to root_url
+ else
+ @feed_items = current_user.feed.paginate(page: params[:page])
+ render "static_pages/home"
+ end
+ end
+
+ def destroy
+ @micropost.destroy
+ flash[:success] = t("micropost.messages2")
+ 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 unless @micropost
+ end
+end
diff --git a/app/controllers/password_resets_controller.rb b/app/controllers/password_resets_controller.rb
new file mode 100644
index 0000000..3ea2bbc
--- /dev/null
+++ b/app/controllers/password_resets_controller.rb
@@ -0,0 +1,49 @@
+class PasswordResetsController < ApplicationController
+ before_action :get_user, only: [:edit, :update]
+ before_action :valid_user, only: [:edit, :update]
+
+ def new; end
+
+ def create
+ @user = User.find_by email: params[:password_reset][:email].downcase
+ if @user
+ @user.create_reset_digest
+ @user.send_password_reset_email
+ flash[:info] = "Email sent with password reset instructions"
+ redirect_to root_url
+ else
+ flash.now[:danger] = "Email address not found"
+ render :new
+ end
+ end
+
+ def update
+ if params[:user][:password].empty?
+ @user.errors.add(:password, "can't be empty")
+ render 'edit'
+ elsif @user.update(user_params)
+ log_in @user
+ @user.update_attribute(:reset_digest, nil)
+ flash[:success] = "Password has been reset."
+ redirect_to @user
+ else
+ render :edit
+ end
+ end
+
+ def edit; end
+
+ private
+
+ def get_user
+ @user = User.find_by email: params[:email]
+ end
+ def valid_user
+ return if (@user && @user.activated? && @user.authenticated?(:reset, params[:id]))
+ redirect_to root_url
+ end
+
+ def user_params
+ params.require(:user).permit :password, :password_confirmation
+ end
+end
diff --git a/app/controllers/relationships_controller.rb b/app/controllers/relationships_controller.rb
new file mode 100644
index 0000000..910249a
--- /dev/null
+++ b/app/controllers/relationships_controller.rb
@@ -0,0 +1,21 @@
+class RelationshipsController < ApplicationController
+ before_action :logged_in_user
+
+ def create
+ @user = User.find_by(id: 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_by(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/sessions_controller.rb b/app/controllers/sessions_controller.rb
index 01a4ae5..9e28da6 100644
--- a/app/controllers/sessions_controller.rb
+++ b/app/controllers/sessions_controller.rb
@@ -4,10 +4,16 @@ def new; end
def create
user = User.find_by(email: params[:session][:email].downcase)
if user&.authenticate(params[:session][:password])
- log_in user
- params[:session][:remember_me] == "1" ? remember(user) : forget(user)
- remember user
- redirect_back_or user
+ if user.activated?
+ log_in user
+ params[:session][:remember_me] == "1" ? remember(user) : forget(user)
+ remember user
+ redirect_back_or user
+ else
+ message = "Account not activated. Check your email for the activation link."
+ flash[:warning] = message
+ redirect_to root_url
+ end
else
flash.now[:danger] = t("session.flash")
render :new
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb
index 5268023..b0e8676 100644
--- a/app/controllers/static_pages_controller.rb
+++ b/app/controllers/static_pages_controller.rb
@@ -1,5 +1,10 @@
class StaticPagesController < ApplicationController
- def home; end
+ def home
+ if logged_in?
+ @micropost = current_user.microposts.build
+ @feed_items = current_user.feed.paginate(page: params[:page])
+ end
+ end
def help; end
end
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
index 0ed694b..67758db 100644
--- a/app/controllers/users_controller.rb
+++ b/app/controllers/users_controller.rb
@@ -3,8 +3,11 @@ class UsersController < ApplicationController
before_action :load_user, except: %i(new index create)
before_action :correct_user, only: %i(edit update)
before_action :admin_user, only: :destroy
+ before_action :logged_in_user, only: [:index, :edit, :update, :destroy,:following, :followers]
- def show; end
+ def show
+ @microposts = @user.microposts.paginate(page: params[:page])
+ end
def edit; end
@@ -15,9 +18,9 @@ def new
def create
@user = User.new user_params
if @user.save
- flash[:success] = t ".success"
- log_in @user
- redirect_to @user
+ @user.send_activation_email
+ flash[:info] = t "email.messages"
+ redirect_to root_url
else
render :new
end
@@ -45,6 +48,21 @@ def destroy
redirect_to users_url
end
+ def following
+ @title = "Following"
+ @user = User.find_by(id: params[:id])
+ @users = @user.following.paginate(page: params[:page])
+ render "show_follow"
+ end
+
+ def followers
+ @title = "Followers"
+ @user = User.find_by(id: params[:id])
+ @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
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/helpers/password_resets_helper.rb b/app/helpers/password_resets_helper.rb
new file mode 100644
index 0000000..0c9d96e
--- /dev/null
+++ b/app/helpers/password_resets_helper.rb
@@ -0,0 +1,2 @@
+module PasswordResetsHelper
+end
diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb
index b2505a6..ef14bb6 100644
--- a/app/helpers/sessions_helper.rb
+++ b/app/helpers/sessions_helper.rb
@@ -14,7 +14,8 @@ def current_user
@current_user ||= User.find_by id: user_id
elsif (user_id = cookies.signed[:user_id])
user = User.find_by id: user_id
- if user&.authenticated?(cookies[:remember_token])
+
+ if user&.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb
index 3c34c81..2dfdba2 100644
--- a/app/mailers/application_mailer.rb
+++ b/app/mailers/application_mailer.rb
@@ -1,4 +1,4 @@
class ApplicationMailer < ActionMailer::Base
- default from: "from@example.com"
+ default from: "noreply@example.com"
layout "mailer"
end
diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb
new file mode 100644
index 0000000..aaae448
--- /dev/null
+++ b/app/mailers/user_mailer.rb
@@ -0,0 +1,11 @@
+class UserMailer < ApplicationMailer
+ def account_activation user
+ @user = user
+ mail to: user.email, subject: "Account activation"
+ end
+
+ def password_reset user
+ @user = user
+ mail to: user.email, subject: "Password reset"
+ end
+end
diff --git a/app/models/micropost.rb b/app/models/micropost.rb
new file mode 100644
index 0000000..a678d26
--- /dev/null
+++ b/app/models/micropost.rb
@@ -0,0 +1,13 @@
+class Micropost < ApplicationRecord
+ belongs_to :user
+ # default_scope -> { order(created_at: :desc) }
+ scope :recent_posts, -> {order(created_at: :desc) }
+ has_one_attached :image
+ validates :user_id, presence: true
+ validates :content, presence: true, length: { maximum: 140 }
+ validates :image, content_type: { in: %w[image/jpeg image/gif image/png], message: "must be a valid image format" }, size: { less_than: 5.megabytes, message: "should be less than 5MB" }
+
+ def display_image
+ image.variant(resize_to_limit: [500, 500])
+ end
+end
diff --git a/app/models/relationship.rb b/app/models/relationship.rb
new file mode 100644
index 0000000..39489b2
--- /dev/null
+++ b/app/models/relationship.rb
@@ -0,0 +1,7 @@
+class Relationship < ApplicationRecord
+ belongs_to :follower, class_name: User.name
+ belongs_to :followed, class_name: User.name
+
+ validates :follower_id, presence: true
+ validates :followed_id, presence: true
+end
diff --git a/app/models/user.rb b/app/models/user.rb
index 99ab078..ee393d4 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,7 +1,8 @@
class User < ApplicationRecord
- attr_accessor :remember_token
+ attr_accessor :remember_token, :activation_token, :reset_token
VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i.freeze
-
+ before_save :downcase_email
+ before_create :create_activation_digest
before_save{email.downcase!}
validates :name, presence: true, length: {maximum: Settings.user.name}
@@ -12,6 +13,12 @@ class User < ApplicationRecord
has_secure_password
validates :password, presence: true, length: {minimum: Settings.user.password}, allow_nil: true
+ has_many :microposts, dependent: :destroy
+ has_many :active_relationships, class_name: Relationship.name, foreign_key: :follower_id, dependent: :destroy
+ has_many :passive_relationships, class_name: Relationship.name, foreign_key: :followed_id, dependent: :destroy
+ has_many :following, through: :active_relationships, source: :followed
+ has_many :followers, through: :passive_relationships, source: :follower
+
def self.digest string
cost = if ActiveModel::SecurePassword.min_cost
BCrypt::Engine::MIN_COST
@@ -34,9 +41,63 @@ def authenticated? remember_token
return false if remember_digest.nil?
BCrypt::Password.new(remember_digest).is_password?(remember_token)
+
+ end
+
+ def authenticated? attribute, token
+ digest = send "#{attribute}_digest"
+ return false if digest.nil?
+
+ BCrypt::Password.new(digest).is_password? token
+ end
+
+ def activate
+ update_attribute :activated, true
+ update_attribute :activated_at, DateTime.now
+ end
+
+ def send_activation_email
+ UserMailer.account_activation(self).deliver_now
+ end
+
+ def create_reset_digest
+ self.reset_token = User.new_token
+ update_attributes reset_digest: User.digest(reset_token), reset_sent_at: DateTime.now
+ end
+
+ def send_password_reset_email
+ UserMailer.password_reset(self).deliver_now
+
end
def forget
update_attribute(:remember_digest, nil)
end
+
+ def feed
+ Micropost.where("user_id IN (?) OR user_id = ?", following_ids, id)
+ end
+
+ def follow other_user
+ following << other_user
+ end
+
+ def unfollow other_user
+ following.delete other_user
+ end
+
+ def following? other_user
+ following.include? other_user
+ end
+
+ private
+
+ def downcase_email
+ self.email.downcase!
+ end
+
+ def create_activation_digest
+ self.activation_token = User.new_token
+ self.activation_digest = User.digest(activation_token)
+ end
end
diff --git a/app/views/microposts/_micropost.html.erb b/app/views/microposts/_micropost.html.erb
new file mode 100644
index 0000000..a02d782
--- /dev/null
+++ b/app/views/microposts/_micropost.html.erb
@@ -0,0 +1,14 @@
+
+ <%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
+ <%= link_to micropost.user.name, micropost.user %>
+
+ <%= micropost.content %>
+ <%= image_tag micropost.display_image if micropost.image.attached? %>
+
+
+ <%= t("micropost.posted") %> <%= time_ago_in_words(micropost.created_at) %> <%= t("micropost.ago") %>.
+ <% if current_user? micropost.user %>
+ <%= link_to t("micropost.button"), micropost, method: :delete, data: {confirm: t("micropost.confirm")} %>
+ <% end %>
+
+
diff --git a/app/views/password_resets/edit.html.erb b/app/views/password_resets/edit.html.erb
new file mode 100644
index 0000000..1292fbe
--- /dev/null
+++ b/app/views/password_resets/edit.html.erb
@@ -0,0 +1,15 @@
+<% provide(:title, "Reset password") %>
+Reset password
+
+
+ <%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
+ <%= render 'shared/error_messages', object: f.object %>
+ <%= hidden_field_tag :email, @user.email %>
+ <%= f.label :password %>
+ <%= f.password_field :password, class: 'form-control' %>
+ <%= f.label :password_confirmation, "Confirmation" %>
+ <%= f.password_field :password_confirmation, class: 'form-control' %>
+ <%= f.submit "Update password", class: "btn btn-primary" %>
+ <% end %>
+
+
diff --git a/app/views/password_resets/new.html.erb b/app/views/password_resets/new.html.erb
new file mode 100644
index 0000000..4c3799c
--- /dev/null
+++ b/app/views/password_resets/new.html.erb
@@ -0,0 +1,11 @@
+<% provide :title, "Forgot password" %>
+Forgot password
+
+
+ <%= form_for :password_reset, url: password_resets_path do |f| %>
+ <%= f.label :email %>
+ <%= f.email_field :email, class: "form-control" %>
+ <%= f.submit "Submit", class: "btn btn-primary" %>
+ <% end %>
+
+
diff --git a/app/views/relationships/create.js.erb b/app/views/relationships/create.js.erb
new file mode 100644
index 0000000..9a2277b
--- /dev/null
+++ b/app/views/relationships/create.js.erb
@@ -0,0 +1,2 @@
+$("#follow_form").html("<%= j 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..27bf26b
--- /dev/null
+++ b/app/views/relationships/destroy.js.erb
@@ -0,0 +1,2 @@
+$("#follow_form").html("<%= j render "users/follow" %>");
+$("#followers").html("<%= @user.followers.count %>");
diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb
index a51c500..c3415c2 100644
--- a/app/views/sessions/new.html.erb
+++ b/app/views/sessions/new.html.erb
@@ -6,6 +6,7 @@
<%= f.label :email %>
<%= f.email_field :email, class: "form-control" %>
<%= f.label :password, t("users.new.lable_password") %>
+ <%= link_to "(forgot password)", new_password_reset_path %>
<%= f.password_field :password, class: "form-control" %>
<%= f.label :remember_me, class: "checkbox inline" do %>
<%= f.check_box :remember_me %>
diff --git a/app/views/shared/_error_messages.html.erb b/app/views/shared/_error_messages.html.erb
index 6ae0a98..10fa4c8 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("users.messages") %> <%= pluralize(@user.errors.count, t("users.error")) %>.
+ <%= t("users.messages") %> <%= pluralize object.errors.count, t("users.error") %>.
- <% @user.errors.full_messages.each do |msg| %>
+ <% object.errors.full_messages.each do |msg| %>
<%= msg %>
<% end %>
diff --git a/app/views/shared/_feed.html.erb b/app/views/shared/_feed.html.erb
new file mode 100644
index 0000000..f15a53c
--- /dev/null
+++ b/app/views/shared/_feed.html.erb
@@ -0,0 +1,6 @@
+<% if @feed_items.any? %>
+
+ <%= render @feed_items %>
+
+ <%= will_paginate @feed_items, params: { controller: :static_pages, action: :home } %>
+<% 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..3f829fe
--- /dev/null
+++ b/app/views/shared/_micropost_form.html.erb
@@ -0,0 +1,19 @@
+<%= form_for(@micropost, html: { multipart: true }) do |f| %>
+ <%= render "shared/error_messages", object: f.object %>
+
+ <%= f.text_area :content, placeholder: t("micropost.compose_new") %>
+
+ <%= f.submit t("micropost.post"), class: "btn btn-primary" %>
+
+ <%= f.file_field :image %>
+
+<% end %>
+
+
diff --git a/app/views/shared/_stats.html.erb b/app/views/shared/_stats.html.erb
new file mode 100644
index 0000000..c597a87
--- /dev/null
+++ b/app/views/shared/_stats.html.erb
@@ -0,0 +1,15 @@
+<% @user ||= current_user %>
+
diff --git a/app/views/shared/_user_info.html.erb b/app/views/shared/_user_info.html.erb
new file mode 100644
index 0000000..b9c43ba
--- /dev/null
+++ b/app/views/shared/_user_info.html.erb
@@ -0,0 +1,4 @@
+<%= link_to gravatar_for(current_user, size: 50), current_user %>
+
<%= current_user.name %>
+
<%= link_to t("micropost.title"), current_user %>
+
<%= pluralize(current_user.microposts.count, "micropost") %>
diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb
index 8431798..060a4db 100644
--- a/app/views/static_pages/home.html.erb
+++ b/app/views/static_pages/home.html.erb
@@ -1,4 +1,22 @@
-<% provide :title, t("home") %>
+<% if logged_in? %>
+
+
+
+ <%= render "shared/user_info" %>
+
+
+ <%= render "shared/stats" %>
+
+
+ <%= render "shared/micropost_form" %>
+
+
+
+
Micropost Feed
+ <%= render "shared/feed" %>
+
+
+<% else %>
<%= t("title") %>
@@ -8,3 +26,4 @@
<%= link_to t("static_pages.home.button"), '#', class: "btn btn-lg btn-primary" %>
+<% end %>
diff --git a/app/views/user_mailer/account_activation.html.erb b/app/views/user_mailer/account_activation.html.erb
new file mode 100644
index 0000000..f64fe84
--- /dev/null
+++ b/app/views/user_mailer/account_activation.html.erb
@@ -0,0 +1,8 @@
+
<%= t("logo") %>
+
Hi <%= @user.name %>,
+
+ <%= t("email.sendemail")%>
+ <%= t("email.account")%>
+
+<%= link_to t("email.activate"),
+edit_account_activation_url(id: @user.activation_token, email: @user.email) %>
diff --git a/app/views/user_mailer/account_activation.text.erb b/app/views/user_mailer/account_activation.text.erb
new file mode 100644
index 0000000..4f396b4
--- /dev/null
+++ b/app/views/user_mailer/account_activation.text.erb
@@ -0,0 +1,7 @@
+Hi <%= @user.name %>,
+
+<%= t("email.sendemail")%>
+<%= t("email.account")%>
+
+<%= edit_account_activation_url(id:@user.activation_token, email:
+@user.email) %>
diff --git a/app/views/user_mailer/password_reset.html.erb b/app/views/user_mailer/password_reset.html.erb
new file mode 100644
index 0000000..f65dbb0
--- /dev/null
+++ b/app/views/user_mailer/password_reset.html.erb
@@ -0,0 +1,7 @@
+
Password reset
+
To reset your password click the link below:
+<%= link_to "Reset password", edit_password_reset_url(@user.reset_token, email: @user.email) %>
+
This link will expire in two hours.
+
+If you did not request your password to be reset, please ignore this email and your password will stay as it is.
+
diff --git a/app/views/user_mailer/password_reset.text.erb b/app/views/user_mailer/password_reset.text.erb
new file mode 100644
index 0000000..6d29feb
--- /dev/null
+++ b/app/views/user_mailer/password_reset.text.erb
@@ -0,0 +1,6 @@
+To reset your password click the link below:
+
+<%= edit_password_reset_url @user.reset_token, email: @user.email %>
+
+This link will expire in two hours.
+If you did not request your password to be reset, please ignore this email and your password will stay as it is.
diff --git a/app/views/users/_follow.html.erb b/app/views/users/_follow.html.erb
new file mode 100644
index 0000000..6ad39b9
--- /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.button_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..d4221b1
--- /dev/null
+++ b/app/views/users/_follow_form.html.erb
@@ -0,0 +1,9 @@
+<% unless current_user?(@user) %>
+
+ <% if current_user.following?(@user) %>
+ <%= render "unfollow" %>
+ <% else %>
+ <%= render "follow" %>
+ <% end %>
+
+<% end %>
diff --git a/app/views/users/_form.html.erb b/app/views/users/_form.html.erb
index 9021e90..6f19a6e 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", object: @user %>
+<%= form_for (@user) do |f| %>
+ <%= render 'shared/error_messages', object: f.object %>
<%= f.label :name, t("users.new.lable_name") %>
<%= f.text_field :name, class: "form-control" %>
<%= f.label :email %>
diff --git a/app/views/users/_unfollow.html.erb b/app/views/users/_unfollow.html.erb
new file mode 100644
index 0000000..6e4dbc6
--- /dev/null
+++ b/app/views/users/_unfollow.html.erb
@@ -0,0 +1,3 @@
+<%= form_for(current_user.active_relationships.find_by(followed_id: @user.id), html: { method: :delete , remote: true}) do |f| %>
+ <%= f.submit t("follow.unfollow"), class: "btn" %>
+<% end %>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
index 911cfc8..31fd762 100644
--- a/app/views/users/index.html.erb
+++ b/app/views/users/index.html.erb
@@ -1,4 +1,4 @@
-<% provide(:title, 'All users') %>
+<% provide(:title, t("users.new.all_users")) %>
<%= t("session.title") %>
<%= will_paginate %>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
index 1460e92..9a30066 100644
--- a/app/views/users/show.html.erb
+++ b/app/views/users/show.html.erb
@@ -1,11 +1,21 @@
-<% provide :title, t("users.user") %>
+<% provide(:title, @user.name) %>
-
- <%= gravatar_for @user %>
- <%= @user.name %>
-
+
+ <%= gravatar_for @user %>
+ <%= @user.name %>
+
+
+ <%= render "follow_form" if logged_in? %>
+ <% if @user.microposts.any? %>
+
Microposts (<%= @user.microposts.count %>)
+
+ <%= render @microposts %>
+
+ <%= will_paginate @microposts %>
+ <% end %>
+
diff --git a/app/views/users/show_follow.html.erb b/app/views/users/show_follow.html.erb
new file mode 100644
index 0000000..3c4374d
--- /dev/null
+++ b/app/views/users/show_follow.html.erb
@@ -0,0 +1,31 @@
+<% provide(:title, @title) %>
+
+
+
+
<%= @title %>
+ <% if @users.any? %>
+
+ <%= will_paginate %>
+ <% end %>
+
+
diff --git a/config/application.rb b/config/application.rb
index 72d4a91..2591f9a 100644
--- a/config/application.rb
+++ b/config/application.rb
@@ -22,6 +22,8 @@ class Application < Rails::Application
config.i18n.default_locale = :en
config.time_zone = Settings.time_zone
+
+ config.action_view.embed_authenticity_token_in_remote_forms = true
end
end
diff --git a/config/environments/development.rb b/config/environments/development.rb
index 7648e65..e8d8764 100644
--- a/config/environments/development.rb
+++ b/config/environments/development.rb
@@ -11,7 +11,6 @@
# Show full error reports.
config.consider_all_requests_local = true
-
# Enable/disable caching. By default caching is disabled.
# Run rails dev:cache to toggle caching.
if Rails.root.join("tmp", "caching-dev.txt").exist?
@@ -28,11 +27,23 @@
config.cache_store = :null_store
end
+
+
# Store uploaded files on the local file system (see config/storage.yml for options).
config.active_storage.service = :local
- # Don"t care if the mailer can"t send.
- config.action_mailer.raise_delivery_errors = false
+ config.action_mailer.raise_delivery_errors = true
+ config.action_mailer.delivery_method = :smtp
+ config.action_mailer.default_url_options = {host: "localhost:3000" }
+ # SMTP settings for gmail
+ config.action_mailer.smtp_settings = {
+ address: "smtp.gmail.com",
+ port: 587,
+ user_name: ENV["USER_NAME"],
+ password: ENV["PASS_WORD"],
+ authentication: "plain",
+ enable_starttls_auto: true
+ }
config.action_mailer.perform_caching = false
@@ -58,5 +69,4 @@
# Use an evented file watcher to asynchronously detect changes in source code,
# routes, locales, etc. This feature depends on the listen gem.
- config.file_watcher = ActiveSupport::EventedFileUpdateChecker
end
diff --git a/config/environments/production.rb b/config/environments/production.rb
index 0ebb667..89a7808 100644
--- a/config/environments/production.rb
+++ b/config/environments/production.rb
@@ -16,7 +16,7 @@
# Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
# or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
- # config.require_master_key = true
+ config.require_master_key = true
# Disable serving static files from the `/public` folder by default since
# Apache or NGINX already handles this.
@@ -109,4 +109,18 @@
# config.active_record.database_selector = { delay: 2.seconds }
# config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
# config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
+
+ config.action_mailer.raise_delivery_errors = true
+ config.action_mailer.delivery_method = :smtp
+ host = '.herokuapp.com'
+ config.action_mailer.default_url_options = {host: host}
+ ActionMailer::Base.smtp_settings = {
+ address: 'smtp.sendgrid.net',
+ port: '587',
+ authentication: :plain,
+ user_name: ENV['SENDGRID_USERNAME'],
+ password: ENV['SENDGRID_PASSWORD'],
+ domain: 'heroku.com',
+ enable_starttls_auto: true
+ }
end
diff --git a/config/locales/en.yml b/config/locales/en.yml
index 02ff24e..2b7626e 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -38,6 +38,7 @@ en:
lable_password: password
confirmation: confirmation
create_my_account: create my account
+ all_users: "All users"
success: "Welcome to the sample"
errors: "Not Found."
@@ -60,6 +61,7 @@ en:
view: "delete"
messages3: "delete error"
+
login_in:
messages: "Please log in."
@@ -68,3 +70,29 @@ en:
button: "Save changes"
header: "Update your profile"
link: "change"
+
+ email:
+ messages: "Please check your email to activate your account."
+ messagerb: "Account activation"
+ sendemail: " Welcome to the Sample App! Click on the link below to activate your"
+ account: "account:"
+ activate: "Activate"
+
+ micropost:
+ messages1: "Micropost created!"
+ messages2: "Micropost deleted"
+ button: "delete"
+ confirm: "You sure?"
+ messages3: "Maximum file size is 5MB. Please choose a smaller file."
+ title: "view my profile"
+ compose_new: "Compose new micropost..."
+ post: "Post"
+ posted: "Posted"
+ ago: "ago"
+
+ follow:
+ button_follow: "Follow"
+ unfollow: "Unfollow"
+ microposts: "Microposts"
+ following: "following"
+ followers: "followers"
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index 915ce05..5bc449b 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -38,6 +38,7 @@ vi:
lable_password: Mật khẩu
confirmation: xác thục mật khẩu
create_my_account: Tạo tài khoản
+ all_users: "Tất cả các người dùng"
success: "chào mừng đến với trang web"
errors: "Thông Báo Lỗi"
@@ -68,3 +69,29 @@ vi:
button: "Lưu Thay đổi"
header: "Cập nhật hồ sơ của bạn"
link: "Thay đổi"
+
+ email:
+ messages: "Hãy kiểm tra email của bạn để kích hoạt tài khoản của bạn."
+ messagesrb: "Kích hoạt tài khoản"
+ sendemail: " Chào mừng bạn đến với Ứng dụng mẫu! Nhấp vào liên kết bên dưới để kích hoạt"
+ account: "tài khoản:"
+ activate: "Kích hoạt"
+
+ micropost:
+ messages1: "Đã tạo micropost!"
+ messages2: "Micropost đã bị xóa"
+ button: "Xóa"
+ confirm: "Bạn chắc chắn?"
+ messages3: "Kích thước tệp tối đa là 5MB. Vui lòng chọn một tệp nhỏ hơn."
+ title: "xem hồ sơ của tôi"
+ compose_new: "Soạn micropost mới ..."
+ post: "Đăng"
+ posted: "Bài đăng"
+ ago: "trước"
+
+ follow:
+ button_follow: "Theo dõi"
+ unfollow: "Bỏ theo dõi"
+ microposts: "Bài Đăng"
+ following: "theo dõi"
+ followers: "Người theo dõi"
diff --git a/config/routes.rb b/config/routes.rb
index 6ea0181..540ef2f 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -1,4 +1,6 @@
Rails.application.routes.draw do
+ get 'password_resets/new'
+ get 'password_resets/edit'
root "static_pages#home"
get "/help", to: "static_pages#help"
get "/contact", to: "static_pages#contact"
@@ -9,5 +11,15 @@
post "/login", to: "sessions#create"
delete "/logout", to: "sessions#destroy"
post "/edit", to: "users#edit"
+
+ resources :users do
+ member do
+ get :following, :followers
+ end
+ end
resources :users
+ resources :account_activations, only: :edit
+ resources :password_resets, only: [:new, :create, :edit, :update]
+ resources :microposts, only: [:create, :destroy]
+ resources :relationships, only: [:create, :destroy]
end
diff --git a/db/migrate/20200930024226_add_activation_to_users.rb b/db/migrate/20200930024226_add_activation_to_users.rb
new file mode 100644
index 0000000..fdf40e8
--- /dev/null
+++ b/db/migrate/20200930024226_add_activation_to_users.rb
@@ -0,0 +1,7 @@
+class AddActivationToUsers < ActiveRecord::Migration[6.0]
+ def change
+ add_column :users, :activation_digest, :string
+ add_column :users, :activated, :boolean
+ add_column :users, :activated_at, :datetime
+ end
+end
diff --git a/db/migrate/20200930152853_add_reset_to_users.rb b/db/migrate/20200930152853_add_reset_to_users.rb
new file mode 100644
index 0000000..03d33ed
--- /dev/null
+++ b/db/migrate/20200930152853_add_reset_to_users.rb
@@ -0,0 +1,6 @@
+class AddResetToUsers < ActiveRecord::Migration[6.0]
+ def change
+ add_column :users, :reset_digest, :string
+ add_column :users, :reset_sent_at, :datetime
+ end
+end
diff --git a/db/migrate/20200930160008_create_microposts.rb b/db/migrate/20200930160008_create_microposts.rb
new file mode 100644
index 0000000..2a370a2
--- /dev/null
+++ b/db/migrate/20200930160008_create_microposts.rb
@@ -0,0 +1,11 @@
+class CreateMicroposts < ActiveRecord::Migration[6.0]
+ def change
+ create_table :microposts do |t|
+ t.text :content
+ t.references :user, null: false, foreign_key: true
+
+ t.timestamps
+ end
+ add_index :microposts, [:user_id, :created_at]
+ end
+end
diff --git a/db/migrate/20201001033623_create_active_storage_tables.active_storage.rb b/db/migrate/20201001033623_create_active_storage_tables.active_storage.rb
new file mode 100644
index 0000000..0b2ce25
--- /dev/null
+++ b/db/migrate/20201001033623_create_active_storage_tables.active_storage.rb
@@ -0,0 +1,27 @@
+# This migration comes from active_storage (originally 20170806125915)
+class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
+ def change
+ create_table :active_storage_blobs do |t|
+ t.string :key, null: false
+ t.string :filename, null: false
+ t.string :content_type
+ t.text :metadata
+ t.bigint :byte_size, null: false
+ t.string :checksum, null: false
+ t.datetime :created_at, null: false
+
+ t.index [ :key ], unique: true
+ end
+
+ create_table :active_storage_attachments do |t|
+ t.string :name, null: false
+ t.references :record, null: false, polymorphic: true, index: false
+ t.references :blob, null: false
+
+ t.datetime :created_at, null: false
+
+ t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
+ t.foreign_key :active_storage_blobs, column: :blob_id
+ end
+ end
+end
diff --git a/db/migrate/20201002035955_create_relationships.rb b/db/migrate/20201002035955_create_relationships.rb
new file mode 100644
index 0000000..c84b4d5
--- /dev/null
+++ b/db/migrate/20201002035955_create_relationships.rb
@@ -0,0 +1,13 @@
+class CreateRelationships < ActiveRecord::Migration[6.0]
+ 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/schema.rb b/db/schema.rb
index de286f1..f5a190e 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -10,7 +10,47 @@
#
# It's strongly recommended that you check this file into your version control system.
-ActiveRecord::Schema.define(version: 2020_09_29_064821) do
+ActiveRecord::Schema.define(version: 2020_10_02_035955) do
+
+ create_table "active_storage_attachments", force: :cascade do |t|
+ t.string "name", null: false
+ t.string "record_type", null: false
+ t.integer "record_id", null: false
+ t.integer "blob_id", null: false
+ t.datetime "created_at", null: false
+ t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
+ t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
+ end
+
+ create_table "active_storage_blobs", force: :cascade do |t|
+ t.string "key", null: false
+ t.string "filename", null: false
+ t.string "content_type"
+ t.text "metadata"
+ t.bigint "byte_size", null: false
+ t.string "checksum", null: false
+ t.datetime "created_at", null: false
+ t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
+ end
+
+ create_table "microposts", force: :cascade do |t|
+ t.text "content"
+ t.integer "user_id", null: false
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, null: false
+ t.index ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
+ t.index ["user_id"], name: "index_microposts_on_user_id"
+ end
+
+ create_table "relationships", force: :cascade do |t|
+ t.integer "follower_id"
+ t.integer "followed_id"
+ t.datetime "created_at", precision: 6, null: false
+ t.datetime "updated_at", precision: 6, 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"
@@ -20,7 +60,14 @@
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin"
+ t.string "activation_digest"
+ t.boolean "activated"
+ t.datetime "activated_at"
+ t.string "reset_digest"
+ t.datetime "reset_sent_at"
t.index ["email"], name: "index_users_on_email", unique: true
end
+ add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
+ add_foreign_key "microposts", "users"
end
diff --git a/db/seeds.rb b/db/seeds.rb
index 5d4bf72..1642d78 100644
--- a/db/seeds.rb
+++ b/db/seeds.rb
@@ -11,7 +11,10 @@
"example@railstutorial.org",
password: "12345",
password_confirmation: "12345",
- admin: true)
+ admin: true,
+ activated: true,
+ activated_at: DateTime.now)
+
99.times do |n|
name = Faker::Name.name
email = "example-#{n+1}@railstutorial.org"
@@ -19,5 +22,20 @@
User.create!(name: name,
email: email,
password: password,
- password_confirmation: password)
+ password_confirmation: password,
+ activated: true,
+ activated_at: DateTime.now)
+end
+
+users = User.order(:created_at).take(6)
+50.times do
+content = Faker::Lorem.sentence(word_count: 5)
+users.each { |user| user.microposts.create!(content: content) }
end
+
+users = User.all
+user = users.first
+following = users[2..20]
+followers = users[3..15]
+following.each{|followed| user.follow(followed)}
+followers.each{|follower| follower.follow(user)}
diff --git a/test/controllers/account_activations_controller_test.rb b/test/controllers/account_activations_controller_test.rb
new file mode 100644
index 0000000..6840b2e
--- /dev/null
+++ b/test/controllers/account_activations_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class AccountActivationsControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/controllers/microposts_controller_test.rb b/test/controllers/microposts_controller_test.rb
new file mode 100644
index 0000000..1127b0e
--- /dev/null
+++ b/test/controllers/microposts_controller_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class MicropostsControllerTest < ActionDispatch::IntegrationTest
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/controllers/password_resets_controller_test.rb b/test/controllers/password_resets_controller_test.rb
new file mode 100644
index 0000000..eeb93ef
--- /dev/null
+++ b/test/controllers/password_resets_controller_test.rb
@@ -0,0 +1,14 @@
+require 'test_helper'
+
+class PasswordResetsControllerTest < ActionDispatch::IntegrationTest
+ test "should get new" do
+ get password_resets_new_url
+ assert_response :success
+ end
+
+ test "should get edit" do
+ get password_resets_edit_url
+ assert_response :success
+ end
+
+end
diff --git a/test/fixtures/microposts.yml b/test/fixtures/microposts.yml
new file mode 100644
index 0000000..33f7a30
--- /dev/null
+++ b/test/fixtures/microposts.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ content: MyText
+ user: one
+
+two:
+ content: MyText
+ user: two
diff --git a/test/fixtures/relationships.yml b/test/fixtures/relationships.yml
new file mode 100644
index 0000000..d70987c
--- /dev/null
+++ b/test/fixtures/relationships.yml
@@ -0,0 +1,9 @@
+# Read about fixtures at https://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
+
+one:
+ follower_id: 1
+ followed_id: 1
+
+two:
+ follower_id: 1
+ followed_id: 1
diff --git a/test/mailers/previews/user_mailer_preview.rb b/test/mailers/previews/user_mailer_preview.rb
new file mode 100644
index 0000000..0d5b43a
--- /dev/null
+++ b/test/mailers/previews/user_mailer_preview.rb
@@ -0,0 +1,14 @@
+# Preview all emails at http://localhost:3000/rails/mailers/user_mailer
+class UserMailerPreview < ActionMailer::Preview
+
+ # Preview this email at http://localhost:3000/rails/mailers/user_mailer/account_activation
+ def account_activation
+ UserMailer.account_activation
+ end
+
+ # Preview this email at http://localhost:3000/rails/mailers/user_mailer/password_reset
+ def password_reset
+ UserMailer.password_reset
+ end
+
+end
diff --git a/test/mailers/user_mailer_test.rb b/test/mailers/user_mailer_test.rb
new file mode 100644
index 0000000..8a8f654
--- /dev/null
+++ b/test/mailers/user_mailer_test.rb
@@ -0,0 +1,20 @@
+require 'test_helper'
+
+class UserMailerTest < ActionMailer::TestCase
+ test "account_activation" do
+ mail = UserMailer.account_activation
+ assert_equal "Account activation", mail.subject
+ assert_equal ["to@example.org"], mail.to
+ assert_equal ["from@example.com"], mail.from
+ assert_match "Hi", mail.body.encoded
+ end
+
+ test "password_reset" do
+ mail = UserMailer.password_reset
+ assert_equal "Password reset", mail.subject
+ assert_equal ["to@example.org"], mail.to
+ assert_equal ["from@example.com"], mail.from
+ assert_match "Hi", mail.body.encoded
+ end
+
+end
diff --git a/test/models/micropost_test.rb b/test/models/micropost_test.rb
new file mode 100644
index 0000000..def8e93
--- /dev/null
+++ b/test/models/micropost_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class MicropostTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end
diff --git a/test/models/relationship_test.rb b/test/models/relationship_test.rb
new file mode 100644
index 0000000..700cc41
--- /dev/null
+++ b/test/models/relationship_test.rb
@@ -0,0 +1,7 @@
+require 'test_helper'
+
+class RelationshipTest < ActiveSupport::TestCase
+ # test "the truth" do
+ # assert true
+ # end
+end