Commit 974672c5 by Tan Phat Nguyen

Add user microposts

parent d69e6794
source 'https://rubygems.org'
gem 'rails', '4.2.0.beta4'
gem 'carrierwave', '0.10.0'
gem 'mini_magick', '3.8.0'
gem 'fog', '1.23.0'
gem 'bcrypt', '3.1.7'
gem 'faker', '1.4.2'
gem 'will_paginate', '3.0.7'
......@@ -34,4 +37,5 @@ end
group :production do
gem 'pg', '0.17.1'
gem 'rails_12factor', '0.0.2'
gem 'unicorn', '4.8.3'
end
......@@ -50,6 +50,11 @@ GEM
columnize (~> 0.8)
debugger-linecache (~> 1.2)
slop (~> 3.6)
carrierwave (0.10.0)
activemodel (>= 3.2.0)
activesupport (>= 3.2.0)
json (>= 1.7)
mime-types (>= 1.16)
celluloid (0.16.0)
timers (~> 4.0.0)
coderay (1.1.0)
......@@ -64,14 +69,38 @@ GEM
debug_inspector (0.0.2)
debugger-linecache (1.2.0)
erubis (2.7.0)
excon (0.41.0)
execjs (2.2.2)
faker (1.4.2)
i18n (~> 0.5)
ffi (1.9.6)
fog (1.23.0)
fog-brightbox
fog-core (~> 1.23)
fog-json
fog-softlayer
ipaddress (~> 0.5)
nokogiri (~> 1.5, >= 1.5.11)
fog-brightbox (0.6.1)
fog-core (~> 1.22)
fog-json
inflecto
fog-core (1.24.0)
builder
excon (~> 0.38)
formatador (~> 0.2)
mime-types
net-scp (~> 1.1)
net-ssh (>= 2.1.3)
fog-json (1.0.0)
multi_json (~> 1.0)
fog-softlayer (0.3.23)
fog-core
fog-json
formatador (0.2.5)
globalid (0.3.0)
activesupport (>= 4.1.0)
guard (2.8.0)
guard (2.8.1)
formatador (>= 0.2.4)
listen (~> 2.7)
lumberjack (~> 1.0)
......@@ -83,6 +112,8 @@ GEM
hike (1.2.3)
hitimes (1.2.2)
i18n (0.7.0.beta1)
inflecto (0.0.2)
ipaddress (0.8.0)
jbuilder (2.2.3)
activesupport (>= 3.0.0, < 5)
multi_json (~> 1.2)
......@@ -106,7 +137,9 @@ GEM
mini_backtrace (0.1.3)
minitest (> 1.2.0)
rails (>= 2.3.3)
mini_portile (0.6.0)
mini_magick (3.8.0)
subexec (~> 0.2.1)
mini_portile (0.6.1)
minitest (5.4.2)
minitest-reporters (1.0.5)
ansi
......@@ -115,8 +148,11 @@ GEM
ruby-progressbar
multi_json (1.10.1)
mysql2 (0.3.16)
nokogiri (1.6.3.1)
mini_portile (= 0.6.0)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-ssh (2.9.1)
nokogiri (1.6.4)
mini_portile (~> 0.6.0)
pg (0.17.1)
pry (0.10.1)
coderay (~> 1.1.0)
......@@ -161,7 +197,7 @@ GEM
ffi (>= 0.5.0)
rdoc (4.1.2)
json (~> 1.4)
ruby-progressbar (1.6.1)
ruby-progressbar (1.7.0)
sass (3.4.7)
sass-rails (5.0.0.beta1)
railties (>= 4.0.0, < 5.0)
......@@ -189,6 +225,7 @@ GEM
activesupport (>= 4.0)
sprockets (~> 2.8)
sqlite3 (1.3.9)
subexec (0.2.3)
thor (0.19.1)
thread_safe (0.3.4)
tilt (1.4.1)
......@@ -220,12 +257,15 @@ DEPENDENCIES
bootstrap-sass (= 3.2.0.0)
bootstrap-will_paginate (= 0.0.10)
byebug (= 3.4.0)
carrierwave (= 0.10.0)
coffee-rails (= 4.0.1)
faker (= 1.4.2)
fog (= 1.23.0)
guard-minitest (= 2.3.1)
jbuilder (= 2.2.3)
jquery-rails (= 4.0.0.beta2)
mini_backtrace (= 0.1.3)
mini_magick (= 3.8.0)
minitest-reporters (= 1.0.5)
mysql2
pg (= 0.17.1)
......
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
......@@ -206,3 +206,50 @@ input {
border-bottom: 1px solid $gray-lighter;
}
}
/* microposts */
.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;
margin-top: 5px;
}
}
aside {
textarea {
height: 100px;
margin-bottom: 5px;
}
}
span.picture {
margin-top: 10px;
input {
border: 0;
}
}
\ No newline at end of file
// Place all the styles related to the Microposts controller here.
// They will automatically be included in application.css.
// You can use Sass (SCSS) here: http://sass-lang.com/
......@@ -3,4 +3,15 @@ class ApplicationController < ActionController::Base
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
include SessionsHelper
private
# Confirms a logged-in user.
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in."
redirect_to login_url
end
end
end
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] = "Micropost created!"
redirect_to root_url
else
@feed_items = []
render 'static_pages/home'
end
end
def destroy
@micropost.destroy
flash[:success] = "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
class StaticPagesController < ApplicationController
def self.home
def home
@micropost = current_user.microposts.build if logged_in?
@feed_items = current_user.feed.paginate(page: params[:page])
end
def help
......
......@@ -10,6 +10,7 @@ class UsersController < ApplicationController
def show
@user = User.find(params[:id])
@microposts = @user.microposts.paginate(page: params[:page])
end
def new
@user = User.new
......
module MicropostsHelper
end
class Micropost < ActiveRecord::Base
belongs_to :user
default_scope -> { order('created_at DESC') }
mount_uploader :picture, PictureUploader
validates :user_id, presence: true
validates :content, presence: true, length: { maximum: 140 }
validate :picture_size
private
def picture_size
if picture.size > 5.megabytes
errors.add(:picture, "should be less than 5MB")
end
end
end
class User < ActiveRecord::Base
has_many :microposts, dependent: :destroy
attr_accessor :remember_token
before_save {self.email = email.downcase}
validates :name, presence: true, length: {maximum: 50}
......@@ -34,4 +35,9 @@ class User < ActiveRecord::Base
def forget
update_attribute(:remember_digest, nil)
end
def feed
# This is preliminary. See "Following users" for the full implementation.
Micropost.where("user_id = ?", id)
end
end
class PictureUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
process resize_to_limit: [400, 400]
if Rails.env.production?
storage :fog
else
storage :file
end
# Override the directory where uploaded files will be stored.
# This is a sensible default for uploaders that are meant to be mounted:
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
# Add a white list of extensions which are allowed to be uploaded.
def extension_white_list
%w(jpg jpeg gif png)
end
end
\ No newline at end of file
<li id="micropost-<%= micropost.id %>">
<%= link_to gravatar_for(micropost.user, size: 50), micropost.user %>
<span class="user"><%= link_to micropost.user.name, micropost.user %></span>
<span class="content">
<%= micropost.content %>
<%= image_tag micropost.picture.url if micropost.picture? %>
</span>
<span class="timestamp">
Posted <%= time_ago_in_words(micropost.created_at) %> ago.
<% if current_user?(micropost.user) %>
<%= link_to "delete", micropost, method: :delete,
data: { confirm: "You sure?" },
title: micropost.content %>
<% end %>
</span>
</li>
<% if @user.errors.any? %>
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
......
<% if @feed_items.any? %>
<ol class="microposts">
<%= render @feed_items %>
</ol>
<%= will_paginate @feed_items %>
<% end %>
<%= form_for(@micropost, html: {multipart: true}) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.submit "Post", class: "btn btn-primary" %>
<span class="picture">
<%= f.file_field :picture, accept: 'image/jpeg,image/gif,image/png' %>
</span>
<% end %>
<script type="text/javascript">
$('#micropost_picture').bind('change', function() {
size_in_megabytes = this.files[0].size/1024/1024;
if (size_in_megabytes > 5) {
alert('Maximum file size is 5MB. Please choose a smaller file.');
}
});
</script>
<%= link_to gravatar_for(current_user, size: 50), current_user %>
<h1><%= current_user.name %></h1>
<span><%= link_to "view my profile", current_user %></span>
<span><%= pluralize(current_user.microposts.count, "micropost") %></span>
\ No newline at end of file
<% provide(:title, "Home") %>
<div class="center jumbotron">
<% if logged_in? %>
<div class="row">
<aside class="col-md-4">
<section class="user_info">
<%= render 'shared/user_info' %>
</section>
<section class="micropost_form">
<%= render 'shared/micropost_form' %>
</section>
</aside>
<div class="col-md-8">
<h3>Micropost Feed</h3>
<%= render 'shared/feed' %>
</div>
</div>
<% else %>
<div class="center jumbotron">
<h1>Welcome to the Sample App</h1>
<h2>
......@@ -9,8 +25,8 @@
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
</div>
</div>
<%= link_to image_tag("rails.png", alt: "Rails logo"),
<%= link_to image_tag("rails.png", alt: "Rails logo"),
'http://rubyonrails.org/' %>
<% end %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.text_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
\ No newline at end of file
......@@ -4,19 +4,7 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.text_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= render 'fields', f: f %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
......
......@@ -3,19 +3,8 @@
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages' %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
<%= f.text_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= render 'fields', f: f %>
<%= f.submit "Create my account", class: "btn btn-primary" %>
<% end %>
......
......@@ -8,4 +8,14 @@
</h1>
</section>
</aside>
<div class="col-md-8">
<% if @user.microposts.any? %>
<h3>Microposts (<%= @user.microposts.count %>)</h3>
<ol class="microposts">
<%= render @microposts %>
</ol>
<%= will_paginate @microposts %>
<% end %>
</div>
</div>
if Rails.env.production?
CarrierWave.configure do |config|
config.fog_credentials = {
# Configuration for Amazon S3
:provider => 'AWS',
:aws_access_key_id => ENV['S3_ACCESS_KEY'],
:aws_secret_access_key => ENV['S3_SECRET_KEY']
}
config.fog_directory = ENV['S3_BUCKET']
end
end
\ No newline at end of file
......@@ -8,4 +8,5 @@ Rails.application.routes.draw do
post 'login' => 'sessions#create'
delete 'logout' => 'sessions#destroy'
resources :users
resources :microposts, only: [:create, :destroy]
end
class CreateMicroposts < ActiveRecord::Migration
def change
create_table :microposts do |t|
t.text :content
t.references :user, index: true
t.timestamps null: false
end
add_index :microposts, [:user_id, :created_at]
end
end
class AddPictureToMicroposts < ActiveRecord::Migration
def change
add_column :microposts, :picture, :string
end
end
......@@ -11,7 +11,18 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20141106073354) do
ActiveRecord::Schema.define(version: 20141107025430) do
create_table "microposts", force: true do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "picture"
end
add_index "microposts", ["user_id", "created_at"], name: "index_microposts_on_user_id_and_created_at"
add_index "microposts", ["user_id"], name: "index_microposts_on_user_id"
create_table "users", force: true do |t|
t.string "name"
......
......@@ -13,3 +13,9 @@ User.create!(name: "Example User",
password: password,
password_confirmation: password)
end
users = User.order(:created_at).take(6)
50.times do
content = Faker::Lorem.sentence(5)
users.each { |user| user.microposts.create!(content: content) }
end
\ No newline at end of file
require 'test_helper'
class MicropostsControllerTest < ActionController::TestCase
def setup
@micropost = microposts(:orange)
end
test "should redirect create when not logged in" do
assert_no_difference 'Micropost.count' do
post :create, micropost: { content: "Lorem ipsum" }
end
assert_redirected_to login_url
end
test "should redirect destroy when not logged in" do
assert_no_difference 'Micropost.count' do
delete :destroy, id: @micropost
end
assert_redirected_to login_url
end
test "should redirect destroy for wrong micropost" do
log_in_as(users(:michael))
micropost = microposts(:ants)
assert_no_difference 'Micropost.count' do
delete :destroy, id: micropost
end
assert_redirected_to root_url
end
end
\ No newline at end of file
orange:
content: "I just ate an orange!"
created_at: <%= 10.minutes.ago %>
user: michael
tau_manifesto:
content: "Check out the @tauday site by @mhartl: http://tauday.com"
created_at: <%= 3.years.ago %>
user: michael
cat_video:
content: "Sad cats are sad: http://youtu.be/PKffm2uI4dk"
created_at: <%= 2.hours.ago %>
user: michael
most_recent:
content: "Writing a short test"
created_at: <%= Time.zone.now %>
user: michael
ants:
content: "Oh, is that what you want? Because that's how you get ants!"
created_at: <%= 2.years.ago %>
user: archer
zone:
content: "Danger zone!"
created_at: <%= 3.days.ago %>
user: archer
tone:
content: "I'm sorry. Your words made sense, but your sarcastic tone did not."
created_at: <%= 10.minutes.ago %>
user: lana
van:
content: "Dude, this van's, like, rolling probable cause."
created_at: <%= 4.hours.ago %>
user: lana
<% 30.times do |n| %>
micropost_<%= n %>:
content: <%= Faker::Lorem.sentence(5) %>
created_at: <%= 42.days.ago %>
user: michael
<% end %>
require 'test_helper'
class MicropostInterfaceTest < ActionDispatch::IntegrationTest
def setup
@user = users(:michael)
end
test "micropost interface" do
log_in_as(@user)
get root_path
assert_select 'div.pagination'
# Invalid submission
post microposts_path, micropost: { content: "" }
assert_select 'div#error_explanation'
# Valid submission
content = "This micropost really ties the room together"
assert_difference 'Micropost.count', 1 do
post microposts_path, micropost: { content: content }
end
follow_redirect!
assert_match content, response.body
# Delete a post.
assert_select 'a', 'delete'
first_micropost = @user.microposts.paginate(page: 1).first
assert_difference 'Micropost.count', -1 do
delete micropost_path(first_micropost)
end
# Visit a different user.
get user_path(users(:archer))
assert_select 'a', { text: 'delete', count: 0 }
end
end
require 'test_helper'
class UsersProfileTest < ActionDispatch::IntegrationTest
include ApplicationHelper
def setup
@user = users(:michael)
end
test "profile display" do
get user_path(@user)
assert_select 'title', full_title(@user.name)
assert_match @user.name, response.body
assert_select 'img.gravatar'
assert_match @user.microposts.count.to_s, response.body
assert_select 'div.pagination'
@user.microposts.paginate(page: 1).each do |micropost|
assert_match micropost.content, response.body
end
end
end
\ No newline at end of file
require 'test_helper'
class MicropostTest < ActiveSupport::TestCase
def setup
@user = users(:michael)
# This code is not idiomatically correct.
@micropost = @user.microposts.build(content: "Lorem ipsum")
end
test "micropost should be valid" do
assert @micropost.valid?
end
test "user id should be present" do
@micropost.user_id = nil
assert_not @micropost.valid?
end
test "order should be most recent first" do
assert_equal Micropost.first, microposts(:most_recent)
end
test "content should be present " do
@micropost.content = " "
assert_not @micropost.valid?
end
test "content should be at most 140 characters" do
@micropost.content = "a" * 141
assert_not @micropost.valid?
end
end
\ No newline at end of file
......@@ -53,4 +53,12 @@ class UserTest < ActiveSupport::TestCase
test "authenticated? should return false for a user with nil digest" do
assert_not @user.authenticated?('')
end
test "associated microposts should be destroyed" do
@user.save
@user.microposts.create!(content: "Lorem ipsum")
assert_difference 'Micropost.count', -1 do
@user.destroy
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment