Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ gem 'simple_form'
# Reduces boot times through caching; required in config/boot.rb
gem 'bootsnap', '>= 1.1.0', require: false

gem 'will_paginate', '~> 3.1.0'
gem 'acts_as_votable'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
Expand All @@ -58,6 +61,10 @@ group :development do
gem 'spring-watcher-listen', '~> 2.0.0'
end

group :test do
gem 'database_cleaner-active_record'
end


# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
11 changes: 10 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ GEM
i18n (>= 0.7, < 2)
minitest (~> 5.1)
tzinfo (~> 1.1)
acts_as_votable (0.13.1)
addressable (2.5.2)
public_suffix (>= 2.0.2, < 4.0)
arel (9.0.0)
Expand All @@ -68,6 +69,10 @@ GEM
coffee-script-source (1.12.2)
concurrent-ruby (1.0.5)
crass (1.0.4)
database_cleaner-active_record (2.0.0)
activerecord (>= 5.a)
database_cleaner-core (~> 2.0.0)
database_cleaner-core (2.0.1)
devise (4.4.3)
bcrypt (~> 3.0)
orm_adapter (~> 0.1)
Expand Down Expand Up @@ -220,17 +225,20 @@ GEM
websocket-driver (0.7.0)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.3)
will_paginate (3.1.8)
xpath (3.0.0)
nokogiri (~> 1.8)

PLATFORMS
ruby

DEPENDENCIES
acts_as_votable
bootsnap (>= 1.1.0)
byebug
capybara
coffee-rails (~> 4.2)
database_cleaner-active_record
devise
factory_bot_rails
jbuilder (~> 2.5)
Expand All @@ -249,9 +257,10 @@ DEPENDENCIES
tzinfo-data
uglifier (>= 1.3.0)
web-console (>= 3.3.0)
will_paginate (~> 3.1.0)

RUBY VERSION
ruby 2.4.4p296

BUNDLED WITH
1.16.2
2.2.11
36 changes: 33 additions & 3 deletions app/controllers/dogs_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,16 @@
class DogsController < ApplicationController
before_action :set_dog, only: [:show, :edit, :update, :destroy]
before_action :set_dog, only: [:show, :edit, :update, :destroy, :like]

# GET /dogs
# GET /dogs.json
def index
@dogs = Dog.all
if params[:sort] == 'likes'
dogs = Dog.order(:cached_weighted_average => :desc)
else
dogs = Dog
end
@sorted = params[:sort] == 'likes'
@dogs = dogs.paginate(page: params[:page], per_page: 5)
end

# GET /dogs/1
Expand All @@ -14,17 +20,22 @@ def show

# GET /dogs/new
def new
redirect_to dogs_url, notice: 'Dog was successfully destroyed.' if current_user.nil?
@dog = Dog.new
end

# GET /dogs/1/edit
def edit
redirect_to(@dog, notice: 'You do not own this dog') if current_user.nil? || @dog.owner != current_user
end

# POST /dogs
# POST /dogs.json
def create
redirect_to(@dog, notice: 'You are not signed in.') if current_user.nil?

@dog = Dog.new(dog_params)
@dog.user = current_user

respond_to do |format|
if @dog.save
Expand All @@ -42,6 +53,7 @@ def create
# PATCH/PUT /dogs/1
# PATCH/PUT /dogs/1.json
def update
redirect_to(@dog, notice: 'You do not own this dog.') if current_user.nil? || @dog.owner != current_user
respond_to do |format|
if @dog.update(dog_params)
@dog.images.attach(params[:dog][:image]) if params[:dog][:image].present?
Expand All @@ -58,13 +70,31 @@ def update
# DELETE /dogs/1
# DELETE /dogs/1.json
def destroy
redirect_to(@dog, notice: 'You do not own this dog.') if current_user.nil? || @dog.owner != current_user
@dog.destroy
respond_to do |format|
format.html { redirect_to dogs_url, notice: 'Dog was successfully destroyed.' }
format.json { head :no_content }
end
end

# GET /dogs/1/like
# GET /dogs/1/like.json
def like
redirect_to(@dog, notice: 'You own this dog.') if current_user.nil? || @dog.owner == current_user

if current_user.voted_for? @dog
@dog.unliked_by current_user
else
@dog.liked_by current_user
end

respond_to do |format|
format.html { redirect_to @dog }
format.json { render :show, status: :ok, location: @dog }
end
end

private
# Use callbacks to share common setup or constraints between actions.
def set_dog
Expand All @@ -73,6 +103,6 @@ def set_dog

# Never trust parameters from the scary internet, only allow the white list through.
def dog_params
params.require(:dog).permit(:name, :description, :images)
params.require(:dog).permit(:name, :description, images: [])
end
end
10 changes: 10 additions & 0 deletions app/models/dog.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
class Dog < ApplicationRecord
has_many_attached :images
belongs_to :user

alias_method :owner, :user

validates :name, presence: true
validates :owner, presence: true
validates :description, presence: true
validates :images, presence: true

acts_as_votable
end
4 changes: 4 additions & 0 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,8 @@ class User < ApplicationRecord
# :confirmable, :lockable, :timeoutable and :omniauthable
devise :database_authenticatable, :registerable,
:recoverable, :rememberable, :trackable, :validatable

has_many :dogs

acts_as_voter
end
6 changes: 3 additions & 3 deletions app/views/dogs/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
<%= simple_form_for @dog do |f| %>
<%= f.input :name %>
<%= f.input :description, as: :text %>
<%= f.input :image, as: :file %>
<%= f.input :images, as: :file, input_html: { multiple: true } %>

<% if @dog.images.any? %>
<%= image_tag @dog.images.first %>
<% @dog.images.each do |image| %>
<%= image_tag image %>
<% end %>

<%= f.button :submit %>
Expand Down
3 changes: 3 additions & 0 deletions app/views/dogs/_thumbnail.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,6 @@
<%= image_tag url_for(dog.images.first), class: "dog-photo", alt: "Photo of #{dog.name}" %>
</article>
</a>
<% if dog_counter % 2 == 1 %>
<%= image_tag 'ad.jpg', class: "ad", alt: "A very cool ad." %>
<% end %>
7 changes: 7 additions & 0 deletions app/views/dogs/index.html.erb
Original file line number Diff line number Diff line change
@@ -1 +1,8 @@
<% if @sorted %>
<%= link_to "Unsort", root_path, title: "sort by likes", id: 'sort' %>
<% else %>
<%= link_to "Sort by Likes", root_path + '?sort=likes', title: "unsort by likes", id: 'sort' %>
<% end %>
<%= will_paginate @dogs %>
<%= render partial: 'thumbnail', collection: @dogs, as: :dog %>
<%= will_paginate @dogs %>
19 changes: 15 additions & 4 deletions app/views/dogs/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,13 +1,24 @@
<section>
<h2><%= @dog.name %></h2>
<h2>
<%= @dog.name %>
<% unless current_user.nil? || @dog.owner == current_user %>
<% if current_user&.voted_for?(@dog) %>
<%= link_to "❤️", like_dog_path(@dog), title: "click to unlike", id: 'like_dog_link' %>
<% else %>
<%= link_to "🤍", like_dog_path(@dog), title: "click to like", id: 'unlike_dog_link' %>
<% end %>
<% end %>
</h2>

<% @dog.images.each do |image| %>
<%= image_tag url_for(image), alt: "Photo of #{@dog.name}" %>
<% end %>

<p><%= @dog.description %></p>

<%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %>
<br>
<%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %>
<% if current_user && @dog.owner == current_user %>
<%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %>
<br>
<%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
</section>
6 changes: 5 additions & 1 deletion config/routes.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
Rails.application.routes.draw do
# For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
devise_for :users
resources :dogs
resources :dogs do
member do
get "like"
end
end
root to: "dogs#index"
end
5 changes: 5 additions & 0 deletions db/migrate/20210219021259_add_owner_to_dogs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddOwnerToDogs < ActiveRecord::Migration[5.2]
def change
add_reference :dogs, :user, index: true, foreign_key: true
end
end
22 changes: 22 additions & 0 deletions db/migrate/20210219042627_acts_as_votable_migration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
class ActsAsVotableMigration < ActiveRecord::Migration[4.2]
def self.up
create_table :votes do |t|

t.references :votable, :polymorphic => true
t.references :voter, :polymorphic => true

t.boolean :vote_flag
t.string :vote_scope
t.integer :vote_weight

t.timestamps
end

add_index :votes, [:voter_id, :voter_type, :vote_scope]
add_index :votes, [:votable_id, :votable_type, :vote_scope]
end

def self.down
drop_table :votes
end
end
16 changes: 16 additions & 0 deletions db/migrate/20210220172412_add_cached_votes_to_dogs.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
class AddCachedVotesToDogs < ActiveRecord::Migration[5.2]
def change
change_table :dogs do |t|
t.integer :cached_votes_total, default: 0
t.integer :cached_votes_score, default: 0
t.integer :cached_votes_up, default: 0
t.integer :cached_votes_down, default: 0
t.integer :cached_weighted_score, default: 0
t.integer :cached_weighted_total, default: 0
t.float :cached_weighted_average, default: 0.0
end

# Uncomment this line to force caching of existing votes
Dog.find_each(&:update_cached_votes)
end
end
25 changes: 24 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 2018_06_07_114248) do
ActiveRecord::Schema.define(version: 2021_02_20_172412) do

create_table "active_storage_attachments", force: :cascade do |t|
t.string "name", null: false
Expand Down Expand Up @@ -40,6 +40,15 @@
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "cached_votes_total", default: 0
t.integer "cached_votes_score", default: 0
t.integer "cached_votes_up", default: 0
t.integer "cached_votes_down", default: 0
t.integer "cached_weighted_score", default: 0
t.integer "cached_weighted_total", default: 0
t.float "cached_weighted_average", default: 0.0
t.index ["user_id"], name: "index_dogs_on_user_id"
end

create_table "users", force: :cascade do |t|
Expand All @@ -60,4 +69,18 @@
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end

create_table "votes", force: :cascade do |t|
t.string "votable_type"
t.integer "votable_id"
t.string "voter_type"
t.integer "voter_id"
t.boolean "vote_flag"
t.string "vote_scope"
t.integer "vote_weight"
t.datetime "created_at"
t.datetime "updated_at"
t.index ["votable_id", "votable_type", "vote_scope"], name: "index_votes_on_votable_id_and_votable_type_and_vote_scope"
t.index ["voter_id", "voter_type", "vote_scope"], name: "index_votes_on_voter_id_and_voter_type_and_vote_scope"
end

end
2 changes: 2 additions & 0 deletions spec/factories/dogs.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
FactoryBot.define do
factory :dog do
association :user
description { 'the goodest dog' }
sequence :name do |n|
"Good Pup #{n}"
end
Expand Down
8 changes: 8 additions & 0 deletions spec/factories/user.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
FactoryBot.define do
factory :user do
sequence :email do |n|
"email#{n}@example.com"
end
password { '123456' }
end
end
8 changes: 6 additions & 2 deletions spec/features/dog_resource_spec.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
require 'rails_helper'
require_relative '../support/devise'

describe 'Dog resource', type: :feature do
let!(:spec_user) { User.first || FactoryBot.create(:user) }
before { login_as spec_user }

it 'can create a profile' do
visit new_dog_path
fill_in 'Name', with: 'Speck'
Expand All @@ -11,15 +15,15 @@
end

it 'can edit a dog profile' do
dog = create(:dog)
dog = create(:dog, user: spec_user)
visit edit_dog_path(dog)
fill_in 'Name', with: 'Speck'
click_button 'Update Dog'
expect(dog.reload.name).to eq('Speck')
end

it 'can delete a dog profile' do
dog = create(:dog)
dog = create(:dog, user: spec_user)
visit dog_path(dog)
click_link "Delete #{dog.name}'s Profile"
expect(Dog.count).to eq(0)
Expand Down
Loading