diff --git a/.ruby-version b/.ruby-version index ab6d2789..79a61441 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -2.4.4 \ No newline at end of file +2.4.4 diff --git a/Gemfile b/Gemfile index 3f1e6961..7d199809 100644 --- a/Gemfile +++ b/Gemfile @@ -33,9 +33,17 @@ gem 'jbuilder', '~> 2.5' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'jquery-rails' gem 'devise' gem 'simple_form' +gem 'will_paginate' +gem 'will_paginate-bootstrap' +gem 'bootstrap-sass', '~> 3.4.1' +gem 'sassc-rails', '>= 2.1.0' +gem 'autoprefixer-rails' +gem 'sprockets-rails' +gem 'annotate' # Reduces boot times through caching; required in config/boot.rb gem 'bootsnap', '>= 1.1.0', require: false @@ -45,6 +53,7 @@ group :development, :test do gem 'rspec-rails', '~> 3.7' gem 'factory_bot_rails' gem 'capybara' + gem 'launchy' gem 'pry' gem 'rails-controller-testing' end diff --git a/Gemfile.lock b/Gemfile.lock index 16c3b7bc..b890a88a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -44,11 +44,19 @@ GEM tzinfo (~> 1.1) addressable (2.5.2) public_suffix (>= 2.0.2, < 4.0) + annotate (2.7.4) + activerecord (>= 3.2, < 6.0) + rake (>= 10.4, < 13.0) arel (9.0.0) + autoprefixer-rails (9.4.10.2) + execjs bcrypt (3.1.12) bindex (0.5.0) bootsnap (1.3.0) msgpack (~> 1.0) + bootstrap-sass (3.4.1) + autoprefixer-rails (>= 5.2.1) + sassc (>= 2.0.0) builder (3.2.3) byebug (10.0.2) capybara (2.18.0) @@ -90,6 +98,12 @@ GEM jbuilder (2.7.0) activesupport (>= 4.2.0) multi_json (>= 1.2) + jquery-rails (4.3.3) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + launchy (2.4.3) + addressable (~> 2.3) listen (3.1.5) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -184,6 +198,15 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + sassc (2.0.1) + ffi (~> 1.9) + rake + sassc-rails (2.1.0) + railties (>= 4.0.0) + sassc (>= 2.0) + sprockets (> 3.0) + sprockets-rails + tilt simple_form (4.0.1) actionpack (>= 5.0) activemodel (>= 5.0) @@ -220,6 +243,9 @@ GEM websocket-driver (0.7.0) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.3) + will_paginate (3.1.6) + will_paginate-bootstrap (1.0.2) + will_paginate (>= 3.0.3) xpath (3.0.0) nokogiri (~> 1.8) @@ -227,13 +253,18 @@ PLATFORMS ruby DEPENDENCIES + annotate + autoprefixer-rails bootsnap (>= 1.1.0) + bootstrap-sass (~> 3.4.1) byebug capybara coffee-rails (~> 4.2) devise factory_bot_rails jbuilder (~> 2.5) + jquery-rails + launchy listen (>= 3.0.5, < 3.2) pry puma (~> 3.11) @@ -241,17 +272,21 @@ DEPENDENCIES rails-controller-testing rspec-rails (~> 3.7) sass-rails (~> 5.0) + sassc-rails (>= 2.1.0) simple_form spring spring-watcher-listen (~> 2.0.0) + sprockets-rails sqlite3 turbolinks (~> 5) tzinfo-data uglifier (>= 1.3.0) web-console (>= 3.3.0) + will_paginate + will_paginate-bootstrap RUBY VERSION ruby 2.4.4p296 BUNDLED WITH - 1.16.2 + 2.0.1 diff --git a/README.md b/README.md index 7e19680a..60003e36 100644 --- a/README.md +++ b/README.md @@ -17,3 +17,249 @@ Run the specs with `rspec` Run the server with `rails s` View the site at http://localhost:3000 + +# Backend + +## Add pagination to index page, to display 5 dogs per page + +app/controllers/dogs_controller.rb + +```ruby +@dogs = Dog.paginate(page: params[:page], :per_page => 5) +``` + +app/views/_dogs.html.erb +```ruby +<%= will_paginate @dogs, renderer: BootstrapPagination::Rails %> +``` +## Add the ability to for a user to input multiple dog images on an edit form or new dog form + +app/views/_form.html.erb +```ruby +<%= f.input :images, as: :file, :input_html => { :multiple => true } %> +``` + +app/controllers/dogs_controller.rb +```ruby + def dog_params + params.require(:dog).permit(:name, :description, images: []) + end +``` + +## Associate dogs with owners + +app/models/dog.rb +```ruby + belongs_to :owner, optional: true, + primary_key: :id, + foreign_key: :owner_id, + class_name: 'User' +``` + +## Allow editing only by owner +app/controllers/dogs_controller.rb +```ruby + before_action :require_login, only: [:new, :create] +``` + +app/controllers/application_controller.rb +```ruby +def require_login + redirect_to dogs_url unless current_user + end +``` +app/views/show.html.erb +```ruby +<% if current_user && current_user.id == @dog.owner_id %> + <%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %> + <%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %> + <% end %> +``` + +## Allow users to like other dogs (not their own) + +app/controller/likes_controller.rb +```ruby +class LikesController < ApplicationController + before_action :require_login + + def create + @like = Like.new(like_params) + @like.liker_id = current_user.id + @like.dog_id = params[:dog_id] + + respond_to do |format| + if @like.save + @dog = Dog.find(params[:dog_id]) + + format.html { redirect_to @dog } + format.json { render :show, status: :created, location: @dog } + + else + format.json { render json: @like.errors.full_messages, status: 422 } + end + end + + end + + def destroy + @like = Like.find(params[:id]) + @dog = Dog.find(@like.dog_id) + @like.destroy + + respond_to do |format| + format.html { redirect_to @dog } + end + end + + private + def like_params + params.permit(:dog_id, :liker_id) + end +end +``` + +app/views/show.html.erb +```ruby + <% if current_user && @dog.owner_id != current_user.id %> + <% if @like %> + <%= link_to "Unlike", like_url(@like.id), method: :delete %> + <% else %> + <%= link_to "Like", dog_likes_url(@dog), method: :post %> + <% end %> + <% end %> +``` + +## Allow sorting the index page by number of likes in the last hour +app/controllers/dogs_controller.rb +```ruby + def index + if params[:sort_params] + subquery = Like.select(:id, :dog_id, :user_id).where(created_at: (Time.now - 1.hour)..Time.now) + @dogs = Dog.paginate(page: params[:page], :per_page => 5) + .joins("LEFT JOIN (#{subquery.to_sql}) AS likes ON dogs.id = likes.dog_id") + .group(:id) + .order("count(likes.id) desc") + else + @dogs = Dog.paginate(page: params[:page], :per_page => 5) + end + end +``` + + +## Display the ad.jpg image after every 2 dogs in the index page + +app/views/_thumbnail.html.erb +```ruby +<% if dog_counter % 2 == 1 %> +
+
+

Collar for Sale!

+ <%= image_tag image_url('ad.jpg'), class: "ad-photo", alt: "Photo of ad" %> +
+
+<% end %> +``` + +# Frontend + +## Display profile images in a functioning carousel (for the dog detail page that has more than one profile image) + +app/views/show.html.erb +```ruby +

<%= @dog.name %>

+<% if @dog.images.length > 1 %> +
+ +
+<% else %> + <% @dog.images.each do |image| %> + <%= image_tag url_for(image), class: "item-image mx-auto d-block", alt: "Photo of #{@dog.name}" %> + <% end %> +<% end %> + +
+

<%= @dog.description %>

+

Likes: <%= @dog.likes.count %>

+ +
+ <% if current_user && @dog.owner_id != current_user.id %> + <% if @like %> + <%= link_to "Unlike", like_url(@like.id), method: :delete %> + <% else %> + <%= link_to "Like", dog_likes_url(@dog), method: :post %> + <% end %> + <% end %> + + <% if current_user && current_user.id == @dog.owner_id %> + <%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %> + <%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %> + <% end %> +
+
+``` + +## Use flexbox, CSS grids, or a grid framework to display the homepage's dog profile thumbnails in a responsive grid layout + +app/views/_dogs.html.erb +```ruby +
+
+ <%= link_to "Sort By Likes", :controller => "dogs", :action => "index", :sort_params => true %> +
+
+ <%= render partial: 'thumbnail', collection: @dogs, as: :dog %> +
+ +
+``` + +## Use utility classes within a layout framework (Bootstrap, Foundation, Material Design, or another) to add a structured layout to the page without custom CSS. + +The use of utility classes with Bootstrap can be seen throughout the application. + +## Refactor the homepage from its current state as a server-rendered page to a client-rendered page, where you request data from `/dogs.json` and display data from the response. + +app/view/index.html.erb +```html + +``` +Created a javascript view to to be executed when the ajax request is returned. The following code replaces the contents of the dogs div with the dogs partial.
+
+app/views/index.js.erb +```javascript +$("#dogs").html('<%= escape_javascript(render 'dogs') %>'); +$('.pagination a').attr('data-remote', 'true'); +``` \ No newline at end of file diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 2ca2b461..143d69f4 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -10,9 +10,11 @@ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details // about supported directives. // +//= require jquery //= require rails-ujs //= require activestorage //= require turbolinks +//= require bootstrap-sprockets //= require_tree . console.log('Welcome!'); diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.scss similarity index 60% rename from app/assets/stylesheets/application.css rename to app/assets/stylesheets/application.scss index edb771d6..147d8ac9 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.scss @@ -13,9 +13,52 @@ *= require_tree . *= require_self */ +@import "bootstrap-sprockets"; +@import "bootstrap"; header { align-items: center; display: flex; justify-content: space-between; } + +.dog-description { + text-align: center; +} + +.item { + height: 450px; +} + +.item-image, .item > img { + object-fit: cover; + width: 450px; + height: 450px; + margin: 0 auto; +} + +.dog-photo, .ad-photo { + height: 300px; + width: 300px; + object-fit: cover; +} + +.pt-6 { + padding-top: 5rem !important; +} + +.pt-8 { + padding-top: 8rem !important; +} + +.text-large { + font-size: 150%; +} + +.form-title { + margin: auto; +} + +.carousel-indicators > li { + display: none; +} \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 09705d12..c0497456 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,2 +1,5 @@ class ApplicationController < ActionController::Base + def require_login + redirect_to dogs_url unless current_user + end end diff --git a/app/controllers/dogs_controller.rb b/app/controllers/dogs_controller.rb index cb9eebc5..73d71992 100644 --- a/app/controllers/dogs_controller.rb +++ b/app/controllers/dogs_controller.rb @@ -1,15 +1,28 @@ +require "time" class DogsController < ApplicationController before_action :set_dog, only: [:show, :edit, :update, :destroy] + before_action :require_login, only: [:new, :create] # GET /dogs # GET /dogs.json def index - @dogs = Dog.all + if params[:sort_params] + subquery = Like.select(:id, :dog_id, :user_id).where(created_at: (Time.now - 1.hour)..Time.now) + @dogs = Dog.paginate(page: params[:page], :per_page => 5) + .joins("LEFT JOIN (#{subquery.to_sql}) AS likes ON dogs.id = likes.dog_id") + .group(:id) + .order("count(likes.id) desc") + else + @dogs = Dog.paginate(page: params[:page], :per_page => 5) + end end # GET /dogs/1 # GET /dogs/1.json def show + if current_user + @like = Like.where(liker_id: current_user.id).where(dog_id: @dog.id)[0] + end end # GET /dogs/new @@ -25,6 +38,7 @@ def edit # POST /dogs.json def create @dog = Dog.new(dog_params) + @dog.owner_id = current_user.id respond_to do |format| if @dog.save @@ -73,6 +87,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 diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb new file mode 100644 index 00000000..7618158e --- /dev/null +++ b/app/controllers/likes_controller.rb @@ -0,0 +1,41 @@ +class LikesController < ApplicationController + before_action :require_login + + # POST /dogs/:dog_id/likes + # POST /dogs.json + def create + @like = Like.new(like_params) + @like.liker_id = current_user.id + @like.dog_id = params[:dog_id] + + respond_to do |format| + if @like.save + @dog = Dog.find(params[:dog_id]) + + format.html { redirect_to @dog } + format.json { render :show, status: :created, location: @dog } + + else + format.json { render json: @like.errors.full_messages, status: 422 } + end + end + + end + + # DELETE /likes/1 + # DELETE /likes/1.json + def destroy + @like = Like.find(params[:id]) + @dog = Dog.find(@like.dog_id) + @like.destroy + + respond_to do |format| + format.html { redirect_to @dog } + end + end + + private + def like_params + params.permit(:dog_id, :liker_id) + end +end \ No newline at end of file diff --git a/app/models/dog.rb b/app/models/dog.rb index eb40bf6e..bb1eef86 100644 --- a/app/models/dog.rb +++ b/app/models/dog.rb @@ -1,3 +1,29 @@ +# == Schema Information +# +# Table name: dogs +# +# id :integer not null, primary key +# name :string +# birthday :datetime +# adoption_date :datetime +# description :text +# created_at :datetime not null +# updated_at :datetime not null +# owner_id :integer +# + class Dog < ApplicationRecord + has_many_attached :images + + belongs_to :owner, optional: true, + primary_key: :id, + foreign_key: :owner_id, + class_name: 'User' + + has_many :likes, + primary_key: :id, + foreign_key: :dog_id, + class_name: 'Like' + end diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 00000000..1b2a5a9e --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,29 @@ +# == Schema Information +# +# Table name: likes +# +# id :integer not null, primary key +# dog_id :integer not null +# liker_id :integer not null +# created_at :datetime not null +# updated_at :datetime not null +# + +class Like < ApplicationRecord + + scope :filtered_likes, -> { where(created_at: (Time.now - 1.hour)..Time.now) } + + + + belongs_to :liker, + primary_key: :id, + foreign_key: :liker_id, + class_name: 'User' + + belongs_to :dog, + primary_key: :id, + foreign_key: :dog_id, + class_name: 'Dog' + + +end diff --git a/app/models/user.rb b/app/models/user.rb index b2091f9a..0ef531c9 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,6 +1,39 @@ +# == Schema Information +# +# Table name: users +# +# id :integer not null, primary key +# name :string +# email :string +# created_at :datetime not null +# updated_at :datetime not null +# encrypted_password :string default(""), not null +# reset_password_token :string +# reset_password_sent_at :datetime +# remember_created_at :datetime +# sign_in_count :integer default(0), not null +# current_sign_in_at :datetime +# last_sign_in_at :datetime +# current_sign_in_ip :string +# last_sign_in_ip :string +# + class User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable + + has_many :dogs, + primary_key: :id, + foreign_key: :owner_id, + class_name: 'Dog', + dependent: :destroy + + has_many :likes, + primary_key: :id, + foreign_key: :liker_id, + class_name: 'Like', + dependent: :destroy + end diff --git a/app/views/dogs/_dogs.html.erb b/app/views/dogs/_dogs.html.erb new file mode 100644 index 00000000..c518e5c3 --- /dev/null +++ b/app/views/dogs/_dogs.html.erb @@ -0,0 +1,12 @@ +
+
+ <%= link_to "Sort By Likes", :controller => "dogs", :action => "index", :sort_params => true %> +
+
+ <%= render partial: 'thumbnail', collection: @dogs, as: :dog %> +
+ +
+ diff --git a/app/views/dogs/_form.html.erb b/app/views/dogs/_form.html.erb index 6dfc7dc9..8c81475a 100644 --- a/app/views/dogs/_form.html.erb +++ b/app/views/dogs/_form.html.erb @@ -1,11 +1,21 @@ -<%= simple_form_for @dog do |f| %> - <%= f.input :name %> - <%= f.input :description, as: :text %> - <%= f.input :image, as: :file %> +
+ <%= simple_form_for(@dog, html: { name:'myForm', class: 'pt-6' }) do |f| %> +
+ <%= f.input :name, id: 'dog_upload', input_html: { class: 'form-control' } %> +
+
+ <%= f.input :description, as: :text, input_html: { class: 'form-control' } %> +
+
+ <%= f.input :images, as: :file, :input_html => { :multiple => true } %> +
- <% if @dog.images.any? %> - <%= image_tag @dog.images.first %> - <% end %> + <% if @dog.images.any? %> + <%= image_tag @dog.images.first %> + <% end %> - <%= f.button :submit %> -<% end %> +
+ <%= f.button :submit, :id => 'submit_button', :class => 'btn btn-primary' %> +
+ <% end %> +
diff --git a/app/views/dogs/_thumbnail.html.erb b/app/views/dogs/_thumbnail.html.erb index 4d6bb441..9b426913 100644 --- a/app/views/dogs/_thumbnail.html.erb +++ b/app/views/dogs/_thumbnail.html.erb @@ -1,6 +1,19 @@ - -
-

<%= dog.name %>

- <%= image_tag url_for(dog.images.first), class: "dog-photo", alt: "Photo of #{dog.name}" %> -
-
+ +
+ +
+

<%= dog.name %>

+ <%= image_tag url_for(dog.images.first), class: "dog-photo", alt: "Photo of #{dog.name}" %> +
+
+
+ + +<% if dog_counter % 2 == 1 %> +
+
+

Collar for Sale!

+ <%= image_tag image_url('ad.jpg'), class: "ad-photo", alt: "Photo of ad" %> +
+
+<% end %> diff --git a/app/views/dogs/index.html.erb b/app/views/dogs/index.html.erb index 91ea5603..60dd6984 100644 --- a/app/views/dogs/index.html.erb +++ b/app/views/dogs/index.html.erb @@ -1 +1,9 @@ -<%= render partial: 'thumbnail', collection: @dogs, as: :dog %> +
+ <%= render "dogs" %> +
+ + diff --git a/app/views/dogs/index.js.erb b/app/views/dogs/index.js.erb new file mode 100644 index 00000000..bc3774e8 --- /dev/null +++ b/app/views/dogs/index.js.erb @@ -0,0 +1,2 @@ +$("#dogs").html('<%= escape_javascript(render 'dogs') %>'); +$('.pagination a').attr('data-remote', 'true'); diff --git a/app/views/dogs/new.html.erb b/app/views/dogs/new.html.erb index 11dea8d6..4627cd3c 100644 --- a/app/views/dogs/new.html.erb +++ b/app/views/dogs/new.html.erb @@ -1,2 +1,2 @@ -

Add Your Dog

+

Add Your Dog

<%= render partial: 'form' %> diff --git a/app/views/dogs/show.html.erb b/app/views/dogs/show.html.erb index 562324d2..3c869ded 100644 --- a/app/views/dogs/show.html.erb +++ b/app/views/dogs/show.html.erb @@ -1,13 +1,56 @@ -
-

<%= @dog.name %>

+

<%= @dog.name %>

+<% if @dog.images.length > 1 %> +
+ +
+<% else %> <% @dog.images.each do |image| %> - <%= image_tag url_for(image), alt: "Photo of #{@dog.name}" %> + <%= image_tag url_for(image), class: "item-image mx-auto d-block", alt: "Photo of #{@dog.name}" %> <% end %> +<% end %> -

<%= @dog.description %>

+
+

<%= @dog.description %>

+

Likes: <%= @dog.likes.count %>

- <%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %> -
- <%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %> -
+
+ <% if current_user && @dog.owner_id != current_user.id %> + <% if @like %> + <%= link_to "Unlike", like_url(@like.id), method: :delete %> + <% else %> + <%= link_to "Like", dog_likes_url(@dog), method: :post %> + <% end %> + <% end %> + + <% if current_user && current_user.id == @dog.owner_id %> + <%= link_to "Edit #{@dog.name}'s Profile", edit_dog_path %> + <%= link_to "Delete #{@dog.name}'s Profile", dog_path, method: :delete, data: { confirm: 'Are you sure?' } %> + <% end %> +
+
\ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index d4bfefcd..9a48549a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,21 +7,40 @@ <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track": "reload" %> <%= javascript_include_tag "application", "data-turbolinks-track": "reload" %> + + + +
-

<%= link_to 'Dog Profile Demo', root_path %>

-
- <% if current_user %> - <%= link_to 'Add your dog', new_dog_path %> - <%= link_to 'Sign out', destroy_user_session_path, method: :delete %> - <% else %> - <%= link_to 'Sign in', new_user_session_path %> - <%= link_to 'Sign up', new_user_registration_path %> - <% end %> -
+
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 4b828e80..abea4264 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -12,3 +12,5 @@ # application.js, application.css, and all non-JS/CSS in the app/assets # folder are already added. # Rails.application.config.assets.precompile += %w( admin.js admin.css ) + +Rails.application.config.assets.precompile += %w( submit_button_disabled.js ) \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 06b01adc..8f8c2edb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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 + resources :likes, only: [:create] + end + + resources :likes, only: [:destroy] root to: "dogs#index" end diff --git a/db/migrate/20190307032110_add_owner_idto_dogs.rb b/db/migrate/20190307032110_add_owner_idto_dogs.rb new file mode 100644 index 00000000..18b3fd36 --- /dev/null +++ b/db/migrate/20190307032110_add_owner_idto_dogs.rb @@ -0,0 +1,6 @@ +class AddOwnerIdtoDogs < ActiveRecord::Migration[5.2] + def change + add_column :dogs, :owner_id, :integer + add_index :dogs, :owner_id + end +end diff --git a/db/migrate/20190307153758_create_likes.rb b/db/migrate/20190307153758_create_likes.rb new file mode 100644 index 00000000..3e4566f1 --- /dev/null +++ b/db/migrate/20190307153758_create_likes.rb @@ -0,0 +1,14 @@ +class CreateLikes < ActiveRecord::Migration[5.2] + def change + create_table :likes do |t| + t.integer :dog_id, null: false + t.integer :liker_id, null: false + + t.timestamps + end + + add_index :likes, [:liker_id, :dog_id], unique: true + add_index :likes, :liker_id + add_index :likes, :dog_id + end +end diff --git a/db/schema.rb b/db/schema.rb index 462bd430..7744f421 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2018_06_07_114248) do +ActiveRecord::Schema.define(version: 2019_03_07_153758) do create_table "active_storage_attachments", force: :cascade do |t| t.string "name", null: false @@ -40,6 +40,18 @@ t.text "description" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "owner_id" + t.index ["owner_id"], name: "index_dogs_on_owner_id" + end + + create_table "likes", force: :cascade do |t| + t.integer "dog_id", null: false + t.integer "liker_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["dog_id"], name: "index_likes_on_dog_id" + t.index ["liker_id", "dog_id"], name: "index_likes_on_liker_id_and_dog_id", unique: true + t.index ["liker_id"], name: "index_likes_on_liker_id" end create_table "users", force: :cascade do |t| diff --git a/db/seeds.rb b/db/seeds.rb index d3e6fde3..e7a4016e 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -6,6 +6,9 @@ # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) +Dog.destroy_all +User.destroy_all + dogs = [ { name: 'Bowie', @@ -49,8 +52,10 @@ }, ] +default_owner = User.create!(email: 'maggie@gmail.com', password: 'starwars') + dogs.each do |dog| - dog = Dog.find_or_create_by(name: dog[:name], description: dog[:description]) + dog = Dog.find_or_create_by(name: dog[:name], description: dog[:description], owner_id: default_owner.id) directory_name = File.join(Rails.root, 'db', 'seed', "#{dog[:name].downcase}", "*") Dir.glob(directory_name).each do |filename| diff --git a/spec/factories/dogs.rb b/spec/factories/dogs.rb index 406874f5..a24818b8 100644 --- a/spec/factories/dogs.rb +++ b/spec/factories/dogs.rb @@ -1,3 +1,17 @@ +# == Schema Information +# +# Table name: dogs +# +# id :integer not null, primary key +# name :string +# birthday :datetime +# adoption_date :datetime +# description :text +# created_at :datetime not null +# updated_at :datetime not null +# owner_id :integer +# + FactoryBot.define do factory :dog do sequence :name do |n| diff --git a/spec/features/dog_resource_spec.rb b/spec/features/dog_resource_spec.rb index d9337477..e0f67b61 100644 --- a/spec/features/dog_resource_spec.rb +++ b/spec/features/dog_resource_spec.rb @@ -2,6 +2,11 @@ describe 'Dog resource', type: :feature do it 'can create a profile' do + visit new_user_registration_path + fill_in 'Email', with: 'testing2468@gmail.com' + fill_in 'Password', with: 'starwars' + fill_in 'Password confirmation', with: 'starwars' + click_button 'Sign up' visit new_dog_path fill_in 'Name', with: 'Speck' fill_in 'Description', with: 'Just a dog' @@ -19,9 +24,17 @@ end it 'can delete a dog profile' do - dog = create(:dog) - visit dog_path(dog) - click_link "Delete #{dog.name}'s Profile" + visit new_user_registration_path + fill_in 'Email', with: 'testing24689@gmail.com' + fill_in 'Password', with: 'starwars' + fill_in 'Password confirmation', with: 'starwars' + click_button 'Sign up' + visit new_dog_path + fill_in 'Name', with: 'Speck' + fill_in 'Description', with: 'Just a dog' + attach_file 'Image', 'spec/fixtures/images/speck.jpg' + click_button 'Create Dog' + click_link "Delete Speck's Profile" expect(Dog.count).to eq(0) end end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 4e322aeb..2ebc8be8 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -7,7 +7,7 @@ require 'rspec/rails' # Add additional requires below this line. Rails is not loaded until this point! require 'support/factory_bot' - +require 'devise' # Requires supporting ruby files with custom matchers and macros, etc, in # spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index ce33d66d..383bef31 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -93,4 +93,4 @@ # as the one that triggered the failure. Kernel.srand config.seed =end -end +end \ No newline at end of file diff --git a/spec/support/devise_helper.rb b/spec/support/devise_helper.rb new file mode 100644 index 00000000..1f442dfe --- /dev/null +++ b/spec/support/devise_helper.rb @@ -0,0 +1,16 @@ +module DeviseRequestSpecHelpers + + include Warden::Test::Helpers + + def sign_in(resource_or_scope, resource = nil) + resource ||= resource_or_scope + scope = Devise::Mapping.find_scope!(resource_or_scope) + login_as(resource, scope: scope) + end + + def sign_out(resource_or_scope) + scope = Devise::Mapping.find_scope!(resource_or_scope) + logout(scope) + end + +end