From dffe30009c3a7d70145569b0ec46ead0318ab9e2 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Tue, 18 Oct 2016 13:56:35 -0700 Subject: [PATCH 001/144] Test gems and env stuff fully loaded --- .gitignore | 8 +- .ruby-gemset | 1 - .ruby-version | 1 - Gemfile | 21 ++++- Gemfile.lock | 176 +++++++++++++++++++++++++++------------- README.md | 194 -------------------------------------------- README.rdoc | 28 +++++++ bin/spring | 7 +- config/database.yml | 78 ++---------------- config/secrets.yml | 4 +- test/test_helper.rb | 3 + 11 files changed, 187 insertions(+), 334 deletions(-) delete mode 100644 .ruby-gemset delete mode 100644 .ruby-version delete mode 100644 README.md create mode 100644 README.rdoc diff --git a/.gitignore b/.gitignore index 6db3c9a5bc..e38a9978b9 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,13 @@ # Ignore bundler config. /.bundle +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + # Ignore all logfiles and tempfiles. /log/* !/log/.keep /tmp - -.DS_Store +coverage +.env diff --git a/.ruby-gemset b/.ruby-gemset deleted file mode 100644 index d5c660823f..0000000000 --- a/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -betsy diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 2bf1c1ccf3..0000000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -2.3.1 diff --git a/Gemfile b/Gemfile index c69f4ddde9..d89f616a42 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,10 @@ source 'https://rubygems.org' -ruby '2.3.1' + # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' -gem 'rails', '4.2.6' -# Use postgresql as the database for Active Record -# gem 'pg', '~> 0.15' +gem 'rails', '4.2.7' +# Use sqlite3 as the database for Active Record +gem 'sqlite3' # Use SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets @@ -22,6 +22,8 @@ gem 'turbolinks' gem 'jbuilder', '~> 2.0' # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', '~> 0.4.0', group: :doc +gem "omniauth" +gem "omniauth-github" # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -35,10 +37,21 @@ gem 'sdoc', '~> 0.4.0', group: :doc group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' + gem 'dotenv-rails' +end + + +group :test do + gem 'minitest-reporters' + gem 'simplecov' end group :development do # Access an IRB console on exception pages or by using <%= console %> in views + + gem "better_errors" + gem "binding_of_caller" + gem "pry-rails" gem 'web-console', '~> 2.0' # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring diff --git a/Gemfile.lock b/Gemfile.lock index 20975578b8..c9a6ee3c34 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,52 @@ GEM remote: https://rubygems.org/ specs: - actionmailer (4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) + actionmailer (4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 1.0, >= 1.0.5) - actionpack (4.2.6) - actionview (= 4.2.6) - activesupport (= 4.2.6) + actionpack (4.2.7) + actionview (= 4.2.7) + activesupport (= 4.2.7) rack (~> 1.6) rack-test (~> 0.6.2) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (4.2.6) - activesupport (= 4.2.6) + actionview (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 1.0, >= 1.0.5) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (4.2.6) - activesupport (= 4.2.6) + activejob (4.2.7) + activesupport (= 4.2.7) globalid (>= 0.3.0) - activemodel (4.2.6) - activesupport (= 4.2.6) + activemodel (4.2.7) + activesupport (= 4.2.7) builder (~> 3.1) - activerecord (4.2.6) - activemodel (= 4.2.6) - activesupport (= 4.2.6) + activerecord (4.2.7) + activemodel (= 4.2.7) + activesupport (= 4.2.7) arel (~> 6.0) - activesupport (4.2.6) + activesupport (4.2.7) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + ansi (1.5.0) arel (6.0.3) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) binding_of_caller (0.7.2) debug_inspector (>= 0.0.1) builder (3.2.2) - byebug (8.2.5) + byebug (9.0.6) + coderay (1.1.1) coffee-rails (4.1.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.1.x) @@ -48,46 +54,84 @@ GEM coffee-script-source execjs coffee-script-source (1.10.0) - concurrent-ruby (1.0.1) + concurrent-ruby (1.0.2) debug_inspector (0.0.2) + docile (1.1.5) + dotenv (2.1.1) + dotenv-rails (2.1.1) + dotenv (= 2.1.1) + railties (>= 4.0, < 5.1) erubis (2.7.0) - execjs (2.6.0) - globalid (0.3.6) + execjs (2.7.0) + faraday (0.9.2) + multipart-post (>= 1.2, < 3) + globalid (0.3.7) activesupport (>= 4.1.0) + hashie (3.4.6) i18n (0.7.0) - jbuilder (2.4.1) + jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) - jquery-rails (4.1.1) + jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.6) loofah (2.0.3) nokogiri (>= 1.5.9) mail (2.6.4) mime-types (>= 1.16, < 4) - mime-types (3.0) + method_source (0.8.2) + mime-types (3.1) mime-types-data (~> 3.2015) - mime-types-data (3.2016.0221) - mini_portile2 (2.0.0) - minitest (5.8.4) - multi_json (1.11.3) - nokogiri (1.6.7.2) - mini_portile2 (~> 2.0.0.rc2) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minitest (5.9.1) + minitest-reporters (1.1.11) + ansi + builder + minitest (>= 5.0) + ruby-progressbar + multi_json (1.12.1) + multi_xml (0.5.5) + multipart-post (2.0.0) + nokogiri (1.6.8.1) + mini_portile2 (~> 2.1.0) + oauth2 (1.2.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (>= 1.2, < 3) + omniauth (1.3.1) + hashie (>= 1.2, < 4) + rack (>= 1.0, < 3) + omniauth-github (1.1.2) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.1) + omniauth-oauth2 (1.4.0) + oauth2 (~> 1.0) + omniauth (~> 1.2) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-rails (0.3.4) + pry (>= 0.9.10) rack (1.6.4) rack-test (0.6.3) rack (>= 1.0) - rails (4.2.6) - actionmailer (= 4.2.6) - actionpack (= 4.2.6) - actionview (= 4.2.6) - activejob (= 4.2.6) - activemodel (= 4.2.6) - activerecord (= 4.2.6) - activesupport (= 4.2.6) + rails (4.2.7) + actionmailer (= 4.2.7) + actionpack (= 4.2.7) + actionview (= 4.2.7) + activejob (= 4.2.7) + activemodel (= 4.2.7) + activerecord (= 4.2.7) + activesupport (= 4.2.7) bundler (>= 1.3.0, < 2.0) - railties (= 4.2.6) + railties (= 4.2.7) sprockets-rails rails-deprecated_sanitizer (1.0.3) activesupport (>= 4.2.0.alpha) @@ -97,40 +141,50 @@ GEM rails-deprecated_sanitizer (>= 1.0.1) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (4.2.6) - actionpack (= 4.2.6) - activesupport (= 4.2.6) + railties (4.2.7) + actionpack (= 4.2.7) + activesupport (= 4.2.7) rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.1.2) + rake (11.3.0) rdoc (4.2.2) json (~> 1.4) + ruby-progressbar (1.8.1) sass (3.4.22) - sass-rails (5.0.4) - railties (>= 4.0.0, < 5.0) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) sass (~> 3.1) sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - sdoc (0.4.1) + sdoc (0.4.2) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) - spring (1.7.1) - sprockets (3.6.0) + simplecov (0.12.0) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) + slop (3.6.0) + spring (2.0.0) + activesupport (>= 4.2) + sprockets (3.7.0) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.0.4) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + sqlite3 (1.3.12) thor (0.19.1) thread_safe (0.3.5) - tilt (2.0.2) - turbolinks (2.5.3) - coffee-rails + tilt (2.0.5) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.0) + uglifier (3.0.2) execjs (>= 0.3.0, < 3) web-console (2.3.0) activemodel (>= 4.0) @@ -142,20 +196,26 @@ PLATFORMS ruby DEPENDENCIES + better_errors + binding_of_caller byebug coffee-rails (~> 4.1.0) + dotenv-rails jbuilder (~> 2.0) jquery-rails - rails (= 4.2.6) + minitest-reporters + omniauth + omniauth-github + pry-rails + rails (= 4.2.7) sass-rails (~> 5.0) sdoc (~> 0.4.0) + simplecov spring + sqlite3 turbolinks uglifier (>= 1.3.0) web-console (~> 2.0) -RUBY VERSION - ruby 2.3.1p112 - BUNDLED WITH - 1.13.5 + 1.13.1 diff --git a/README.md b/README.md deleted file mode 100644 index 220a0423c4..0000000000 --- a/README.md +++ /dev/null @@ -1,194 +0,0 @@ -# bEtsy -[b]Etsy will be an online store where a wide variety of products can be listed and sold by any user. In this project we will focus on reinforcing the major components of Rails, Model Validation, as well as introducing some more complex logic such as user authentication. - -## Project Learning Goals -- Core comprehension of: - - Routes - - Controllers - - Models - - Views -- User based application logic -- User authentication -- Agile practices -- Feature branch management with Git -- Group project ownership - -## Guidelines -- Groups of three or four will collaborate in pairs or individually and will report to their assigned Project Manager (one of the instructors) -- Use a task manager like [Trello](http://trello.com) to track your team's efforts -- Build a logical user-flow that moves across multiple controllers and models -- Use HTML/CSS to style your website - -### Restrictions -- Do not use gems for user authentication (such as Devise) -- You must use oAuth for user authentication. - -## Getting Started -1. As a group decide on an app name (this may help lead the aesthetic) -1. As a group decide on a team name (this will amuse your instructors) -1. Have one person on your team fork/clone the project master as per usual - 1. Add all other team members as collaborators - 1. Each team member should clone the repo to their computer -1. Figure out your workflow for the project, re: Git and Task management - 1. Determine who will be the Stand Up Leader and Task Leader for the first week -1. Create a Trello board and ensure that all team members and instructors have access -1. Review the User Stories below and create Trello tasks to represent them -1. Slack your team name, app name, and link to your trello board to your Project Manager - -## Expectations -Build an online system for listing, selling, reviewing, and buying a wide variety of products listed by multiple merchants. - -### General Requirements -- Unit tests and/or specs for - - Controllers - - Models - - Routes -- Test code coverage (using SimpleCov - remember me!) - - 90% for all controller and model classes - -### User Stories -#### Guest User (Unauthenticated) -As a guest to the website (not signed in) I **can**: - -- Browse all products -- Browse products by category -- Browse products by merchant (users) -- View any individual product with additional details -- Leave a review for a product providing: - - A Text review - - A rating out of 5 -- Add in-stock products to my cart -- Remove products from my cart -- Change the quantity of an existing product in my cart -- Purchase the items in my cart, providing: - - Email Address - - Mailing Address - - Name on credit card - - Credit card number - - Credit cart expiration - - Credit Card CVV (security code) - - Billing zip code -- Purchasing an order makes the following changes: - - Reduces the number of inventory for each product - - Changes the order state from "pending" to "paid" - - Clears the current cart -- After purchasing an order, I can view a confirmation screen including: - - Each item in the order with a quantity and line-item subtotal - - A link to the item description page - - Order total price - - DateTime the order was placed - - The current status of the order -- Sign up to be a merchant using OAuth - - Every merchant must have a username -- Sign in to my merchant account using OAuth - -As a guest I **cannot**: - -- Add products to the cart that are out of stock -- View any link or page to manage any products -- View any of the account pages - -#### Authenticated Users -As a signed-in user, I **can**: - -- Do everything a guest user can do except for sign up and sign in -- Sign out -- Create new categories (categories are shared between all merchants) -- Create a new product providing: - - name - - description - - price - - photo URL - - stock -- Assign my products to any number of categories -- Retire a product from being sold, which hides it from browsing -- View an account page to edit/update my existing products -- View an account page showing my order fulfillment -- On the order fulfillment page: - - Total Revenue - - Total Revenue by status - - Total number of orders by status - - Filter orders displayed by status - - Link to each individual order - - A list of orders including at least one of my products: - - Each order item sold by me with a quantity and line-item subtotal - - A link to the item description page - - DateTime the order was placed - - Link to transition the order item to marked as shipped - - The current status of the order ("pending", "paid", "complete", "cancelled") -- View an individual order to see the user's: - - Name - - Email address - - Mailing address - - Last four digits of their credit card - - Credit card expiration date - -As a signed-in user, I **cannot**: - -- Review my own products -- View order items from a shared order that belong to another merchant -- View another users private data (i.e. order fulfillment or product management) - -### Model Validations -Many of our models will have attributes that are required for our application to use and display data consistently. Each model will have attributes with requirements for a valid record. The requirements are summarized below: - -#### Merchant -- Username must be present -- Username must be unique -- Email Address must be present -- Email Address must be unique - -#### Product -- Name must be present -- Name must be unique -- Price must be present -- Price must be a number -- Price must be greater than 0 -- Product must belong to a User - -#### Order -- An Order must have one or more Order Items - -#### OrderItem -- Must belong to a Product -- Must belong to an Order -- Quantity must be present -- Quantity must be an integer -- Quantity must be greater than 0 - -#### Review -- Rating must be present -- Rating must be an integer -- Rating must be between 1 and 5 - -## Submission Guidelines -Your final project must be deployed to [Heroku](http://heroku.com). Your team will open a single pull request for the entire project. Include the link to your Heroku deployment in the PR's description, as well as the team name and the names of all members. - -## Team Leaders -Each team will have team leaders who are responsible for keeping track of each team member's contributions. Rotate leader roles at the beginning of each week; every team member should be in at least one leader role during the project. - -- Stand Up Leader - - Notifies team members about meeting schedule and ensures that everyone is present and ready - - Takes notes about each person's daily report in Stand Up - - Keeps the meeting moving -- Task Leader - - Leads discussion on task assignment - - Decide if a task should be completed alone or in a pair - - Assign tasks based on... - - Individual comfort - - Desire - - Ability - - Ensures the task list stays up to date - -## Stand Up Meetings -The Project Manager for your team will determine the timing for all Stand Up meetings. Because PMs are managing multiple projects at once this time will be different for each team, and may change from day to day. The meeting schedule will be communicated to the Stand Up Leader as soon as it is determined. - -At the end of each day, your team's assigned Project Manager will review the Trello board for all tasks discussed during that day's Stand Up meeting. - -## Weekly Demos -In a real world work environment, a team's success is measured by their product as opposed to each individual's contribution. - -Each team will present their progress and respond to questions from their Project Manager each Friday. Every team member will participate in these demos; the PM will ask specific questions regarding -1. The team's progress and plan for completing the project -1. The technical decisions and implementation -1. Every team member's understanding of the underlying technical structures. diff --git a/README.rdoc b/README.rdoc new file mode 100644 index 0000000000..dd4e97e22e --- /dev/null +++ b/README.rdoc @@ -0,0 +1,28 @@ +== README + +This README would normally document whatever steps are necessary to get the +application up and running. + +Things you may want to cover: + +* Ruby version + +* System dependencies + +* Configuration + +* Database creation + +* Database initialization + +* How to run the test suite + +* Services (job queues, cache servers, search engines, etc.) + +* Deployment instructions + +* ... + + +Please feel free to use a different markup language if you do not plan to run +rake doc:app. diff --git a/bin/spring b/bin/spring index 7fe232c3aa..9bc076b9ea 100755 --- a/bin/spring +++ b/bin/spring @@ -7,9 +7,10 @@ unless defined?(Spring) require 'rubygems' require 'bundler' - if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) - Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } - gem 'spring', match[1] + lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) + if spring = lockfile.specs.detect { |spec| spec.name == "spring" } + Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path + gem 'spring', spring.version require 'spring/binstub' end end diff --git a/config/database.yml b/config/database.yml index 04f24be2ba..1c1a37ca8d 100644 --- a/config/database.yml +++ b/config/database.yml @@ -1,85 +1,25 @@ -# PostgreSQL. Versions 8.2 and up are supported. +# SQLite version 3.x +# gem install sqlite3 # -# Install the pg driver: -# gem install pg -# On OS X with Homebrew: -# gem install pg -- --with-pg-config=/usr/local/bin/pg_config -# On OS X with MacPorts: -# gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config -# On Windows: -# gem install pg -# Choose the win32 build. -# Install PostgreSQL and put its /bin directory on your path. -# -# Configure Using Gemfile -# gem 'pg' +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' # default: &default - adapter: postgresql - encoding: unicode - # For details on connection pooling, see rails configuration guide - # http://guides.rubyonrails.org/configuring.html#database-pooling + adapter: sqlite3 pool: 5 + timeout: 5000 development: <<: *default - database: betsy_development - - # The specified database role being used to connect to postgres. - # To create additional roles in postgres see `$ createuser --help`. - # When left blank, postgres will use the default role. This is - # the same name as the operating system user that initialized the database. - #username: betsy - - # The password associated with the postgres role (username). - #password: - - # Connect on a TCP socket. Omitted by default since the client uses a - # domain socket that doesn't need configuration. Windows does not have - # domain sockets, so uncomment these lines. - #host: localhost - - # The TCP port the server listens on. Defaults to 5432. - # If your server runs on a different port number, change accordingly. - #port: 5432 - - # Schema search path. The server defaults to $user,public - #schema_search_path: myapp,sharedapp,public - - # Minimum log levels, in increasing order: - # debug5, debug4, debug3, debug2, debug1, - # log, notice, warning, error, fatal, and panic - # Defaults to warning. - #min_messages: notice + database: db/development.sqlite3 # Warning: The database defined as "test" will be erased and # re-generated from your development database when you run "rake". # Do not set this db to the same as development or production. test: <<: *default - database: betsy_test + database: db/test.sqlite3 -# As with config/secrets.yml, you never want to store sensitive information, -# like your database password, in your source code. If your source code is -# ever seen by anyone, they now have access to your database. -# -# Instead, provide the password as a unix environment variable when you boot -# the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database -# for a full rundown on how to provide these environment variables in a -# production deployment. -# -# On Heroku and other platform providers, you may have a full connection URL -# available as an environment variable. For example: -# -# DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" -# -# You can use this database configuration with: -# -# production: -# url: <%= ENV['DATABASE_URL'] %> -# production: <<: *default - database: betsy_production - username: betsy - password: <%= ENV['BETSY_DATABASE_PASSWORD'] %> + database: db/production.sqlite3 diff --git a/config/secrets.yml b/config/secrets.yml index 6442868fc1..d26d6727cf 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -11,10 +11,10 @@ # if you're sharing your code publicly. development: - secret_key_base: eaebee6b0ce07b1e5b22743dacf6e6aaec01bf01c88370f9f86c3ae0d8592b4313c5e9da09754cd613eda8d206168bd74d5ed9910230b3957725352c70e2cf80 + secret_key_base: 6ecbe27cf12a699c0f0842fcb1ce4add1a562a92f96d08f03fa31192a94cde8c5fa571fc9fdc4e96ad64b9e3aa11134cd85f4c7b0065a1ecbf26d3de3f8acb62 test: - secret_key_base: f9c5bd6c383573755e5f8cf214402a51eb5ce8c7d0df9860dbf980f03fbd6c508e97412644b6a80d7034774602b05e05988bf7ab8ff594912950fdb590589a4d + secret_key_base: 81722cec9809519a189142b7cc0ab478412efc5da7e93a2490c862734470561356f2d1dfe64a2934052e123f02bf8d8954875d87d2bdf3a677efadf855489423 # Do not keep production secrets in the repository, # instead read values from the environment. diff --git a/test/test_helper.rb b/test/test_helper.rb index 92e39b2d78..2c5373e528 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,10 +1,13 @@ ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' +require 'simplecov' +SimpleCov.start 'rails' class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all + Minitest::Reporters.use! # Add more helper methods to be used by all tests here... end From 2bc96722e5655ede4e178d7e7b60e2916a2bdd8b Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 15:54:25 -0700 Subject: [PATCH 002/144] Added .DS_Store to .gitignore and saved the routes we drafted together --- .gitignore | 1 + config/routes.rb | 85 ++++++++++++++++++------------------------------ 2 files changed, 32 insertions(+), 54 deletions(-) diff --git a/.gitignore b/.gitignore index e38a9978b9..c43c6b0bf2 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ /tmp coverage .env +.DS_Store diff --git a/config/routes.rb b/config/routes.rb index 3f66539d54..17e043cf12 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,56 +1,33 @@ Rails.application.routes.draw do - # The priority is based upon order of creation: first created -> highest priority. - # See how all your routes lay out with "rake routes". - - # You can have the root of your site routed with "root" - # root 'welcome#index' - - # Example of regular route: - # get 'products/:id' => 'catalog#view' - - # Example of named route that can be invoked with purchase_url(id: product.id) - # get 'products/:id/purchase' => 'catalog#purchase', as: :purchase - - # Example resource route (maps HTTP verbs to controller actions automatically): - # resources :products - - # Example resource route with options: - # resources :products do - # member do - # get 'short' - # post 'toggle' - # end - # - # collection do - # get 'sold' - # end - # end - - # Example resource route with sub-resources: - # resources :products do - # resources :comments, :sales - # resource :seller - # end - - # Example resource route with more complex sub-resources: - # resources :products do - # resources :comments - # resources :sales do - # get 'recent', on: :collection - # end - # end - - # Example resource route with concerns: - # concern :toggleable do - # post 'toggle' - # end - # resources :posts, concerns: :toggleable - # resources :photos, concerns: :toggleable - - # Example resource route within a namespace: - # namespace :admin do - # # Directs /admin/products/* to Admin::ProductsController - # # (app/controllers/admin/products_controller.rb) - # resources :products - # end + + root 'ffc#index' + + resources :merchants, only: [:show] do + resources :products + resources :orders, except: [:new, :create, :delete] + # NESTED? do + # resources :order_items, only: [:update] + # end + end + + resources :products, only: [:index, :show] do + resources :reviews, only: [:new, :create] + end + + resources :categories, only: [:new, :create, :show] + +# We're going to talk about this more if any of us needs to edit this. :) + resources :orders, only: [:new, :create, :show] do + resources :order_items, except [:index, :show] + end + +# Sessions routes - can be further flushed out... + get '/auth/:provider/callback' => 'sessions#create' + + # get "/sessions/login_failure", to: "sessions#login_failure", as: "login_failure" + + get '/sessions', to: 'sessions#index', as: 'sessions' + + delete '/sessions', to: 'sessions#destroy' + end From 194de3d3cde4c2b9accbfe3d0d222a37e5576bce Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 16:22:57 -0700 Subject: [PATCH 003/144] fixed a route, colon was missing --- config/routes.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/routes.rb b/config/routes.rb index 17e043cf12..2d0b71e807 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -18,7 +18,7 @@ # We're going to talk about this more if any of us needs to edit this. :) resources :orders, only: [:new, :create, :show] do - resources :order_items, except [:index, :show] + resources :order_items, except: [:index, :show] end # Sessions routes - can be further flushed out... From 12047204232ec2916c7dd08f7d490602b4a3f8d6 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 16:31:46 -0700 Subject: [PATCH 004/144] created REVIEW model --- app/models/review.rb | 2 ++ db/migrate/20161018233022_create_reviews.rb | 8 ++++++++ test/fixtures/reviews.yml | 11 +++++++++++ test/models/review_test.rb | 7 +++++++ 4 files changed, 28 insertions(+) create mode 100644 app/models/review.rb create mode 100644 db/migrate/20161018233022_create_reviews.rb create mode 100644 test/fixtures/reviews.yml create mode 100644 test/models/review_test.rb diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 0000000000..2fbac34e64 --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,2 @@ +class Review < ActiveRecord::Base +end diff --git a/db/migrate/20161018233022_create_reviews.rb b/db/migrate/20161018233022_create_reviews.rb new file mode 100644 index 0000000000..39c5709271 --- /dev/null +++ b/db/migrate/20161018233022_create_reviews.rb @@ -0,0 +1,8 @@ +class CreateReviews < ActiveRecord::Migration + def change + create_table :reviews do |t| + + t.timestamps null: false + end + end +end diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml new file mode 100644 index 0000000000..937a0c002e --- /dev/null +++ b/test/fixtures/reviews.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/review_test.rb b/test/models/review_test.rb new file mode 100644 index 0000000000..11aa5204f0 --- /dev/null +++ b/test/models/review_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ReviewTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 045f86f73365dae484d95290198b21b6aef2b83c Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 20:28:48 -0700 Subject: [PATCH 005/144] model: review: set-up model table columns in the db migrate... file; set-up a few preliminarty fixtures for testing; and pasted commented text into my review test model file to begin testing customization --- app/models/review.rb | 11 +++ db/migrate/20161018233022_create_reviews.rb | 18 +++-- test/fixtures/reviews.yml | 23 ++++--- test/models/review_test.rb | 74 ++++++++++++++++++++- 4 files changed, 108 insertions(+), 18 deletions(-) diff --git a/app/models/review.rb b/app/models/review.rb index 2fbac34e64..7084174a22 100644 --- a/app/models/review.rb +++ b/app/models/review.rb @@ -1,2 +1,13 @@ class Review < ActiveRecord::Base + validates :rating, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } + validates :description, presence: true, length: { maximum: 400 } + validates :product_id, presence: true + + belongs_to :product end + +# Reviews: +# Rating: integer default to 1 +# Description: string +# Product_ID +# (belongs to a product) diff --git a/db/migrate/20161018233022_create_reviews.rb b/db/migrate/20161018233022_create_reviews.rb index 39c5709271..b395d0d607 100644 --- a/db/migrate/20161018233022_create_reviews.rb +++ b/db/migrate/20161018233022_create_reviews.rb @@ -1,8 +1,18 @@ class CreateReviews < ActiveRecord::Migration - def change - create_table :reviews do |t| + def change + create_table :reviews do |t| + t.integer :rating + t.string :description + t.integer :product_id - t.timestamps null: false + t.timestamps null: false + end end - end end + + +# Reviews: +# Rating: integer default to 1 +# Description: string +# Product_ID +# (belongs to a product) diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml index 937a0c002e..79da576c84 100644 --- a/test/fixtures/reviews.yml +++ b/test/fixtures/reviews.yml @@ -1,11 +1,12 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value +one_star: + rating: 1 + description: This product is awesome. +four_star: + rating: 4 + description: Highly recommend purchasing this item. +five_star: + rating: 5 + description: Another description... +long_description: + rating: 4 + description: Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf diff --git a/test/models/review_test.rb b/test/models/review_test.rb index 11aa5204f0..c8444c57f7 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -1,7 +1,75 @@ require 'test_helper' class ReviewTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + # test "Can create an album with only a name provided" do + # assert albums(:valid_data).valid? + # end + # + # test "Cannot create an album without a name" do + # album = Album.new + # assert_not album.valid? + # end + # + # test "Create two albums with different titles" do + # album1 = albums(:valid_data) + # album2 = albums(:another_album) + # assert album2.valid? + # end + # + # test "Cannot create two albums with the same title" do + # album1 = albums(:valid_data) + # album2 = Album.new(name: "Hello") + # assert_not(album2.valid?) + # end + # + # test "Can create an album with a description 808 characters long" do + # album1 = albums(:valid_data) + # album2 = Album.new(name: "Hello") + # assert_not(album2.valid?) + # end + # + # test "Cannot create an album with a description 810 characters long" do + # album_too_long = Album.new(name: "Too Long", description: "Here is a description of Book Title #12. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf os;IF ;KLSDJf l;SJf;ijsefl j SDKL:fj es f;oSJDF l;kSDJFio SJefm LS:Dfj ;djf ;LKSDFj ;Ldjs ;ILEJSF l;SDJF SDIJfp oSfj ko;SDfopPSEfj PAEIOSfj lsidfj IOSEfj KL o;iSDfj efj DFIJS IOSEf oIAEJSf oDJS fo;IJSDF ;oIJDF ioSdfj dios;f jo;ISJf io;SDFJ ;SDKOFj ;Sdfj ;Jf :IOSDFJ ads o;iASdfj AKLSdj O:IADJ ;AOId oSDFJ S:OIDF :OSjm S:DFJ oiDJSf L:DFSZ O:IDSZJF ;oISJDf ;OSDFJ L:SDIFj L:SDKFJ O:ISDJf ;DFJS ;sIOJF ;oDFJS IO:SEfjDJSF ;SDFJ ioSfjUH") + # assert_not(album_too_long.valid?) + # end + # + # test "Creating a new album will instantiate vote count at zero" do + # b = Album.new + # assert_equal(0, b.votes) + # end + # + # test "Calling upvote_one increments votes by 1 correctly when starting with a new album" do + # b = Album.new + # b.upvote_one + # assert_equal(1, b.votes) + # end + # + # test "Calling upvote_one increments votes by 1 correctly with any integer higher than zero" do + # b = Album.new + # b.votes = 5 + # b.upvote_one + # assert_equal(6, b.votes) + # end + # + # test "Cannot set votes to a negative integer" do + # b = Album.new(name: "yellow") + # b.votes = -4 + # assert(b.invalid?) + # assert_includes(b.errors, :votes) + # end + # + # test "Votes cannot be set to nil" do + # b = Album.new(name: "yellow") + # b.votes = nil + # assert(b.invalid?) + # assert_includes(b.errors, :votes) + # end end + +# on rails console, object.new goes straight to the model, bypassing the controller completely. + +# Reviews: +# Rating: integer default to 1 +# Description: string +# Product_ID +# (belongs to a product) From 769b9847baf5d52a91d4b4aa89b11747d71c3d43 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 20:29:49 -0700 Subject: [PATCH 006/144] model: review: set up validations and belongs_to relationship in the review model file. --- app/models/review.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/review.rb b/app/models/review.rb index 7084174a22..b0786530c1 100644 --- a/app/models/review.rb +++ b/app/models/review.rb @@ -6,6 +6,7 @@ class Review < ActiveRecord::Base belongs_to :product end + # Reviews: # Rating: integer default to 1 # Description: string From 30832a4a3c6be0033ebbeaa4906bb8aeedcd0063 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 20:31:46 -0700 Subject: [PATCH 007/144] raked db migrate --- db/schema.rb | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 db/schema.rb diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..8c4cd49d99 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,24 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20161018233022) do + + create_table "reviews", force: :cascade do |t| + t.integer "rating" + t.string "description" + t.integer "product_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end From 9b46e77d7d894988f6f511b2476ae407cbfc6021 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 20:52:50 -0700 Subject: [PATCH 008/144] wrote 5 tests confirming reviews--model--rating validations --- test/fixtures/reviews.yml | 4 ++++ test/models/review_test.rb | 42 ++++++++++++++++++++++++++------------ 2 files changed, 33 insertions(+), 13 deletions(-) diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml index 79da576c84..02c94cc1f3 100644 --- a/test/fixtures/reviews.yml +++ b/test/fixtures/reviews.yml @@ -1,12 +1,16 @@ one_star: rating: 1 description: This product is awesome. + product_id: 123 four_star: rating: 4 description: Highly recommend purchasing this item. + product_id: 123 five_star: rating: 5 description: Another description... + product_id: 123 long_description: rating: 4 description: Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf + product_id: 123 diff --git a/test/models/review_test.rb b/test/models/review_test.rb index c8444c57f7..c78a23be2d 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -1,14 +1,35 @@ require 'test_helper' class ReviewTest < ActiveSupport::TestCase - # test "Can create an album with only a name provided" do - # assert albums(:valid_data).valid? - # end - # - # test "Cannot create an album without a name" do - # album = Album.new - # assert_not album.valid? - # end + test "Creating a new review will instantiate rating as nil" do + r = Review.new + assert_equal(nil, r.rating) + end + + test "Cannot save a nil rating to the database" do + no_rating = Review.new(description: "whatever", product_id: 123) + assert_not no_rating.valid? + end + + test "Can create reviews with ratings between 1 and 5" do + assert reviews(:one_star).valid? + assert reviews(:four_star).valid? + assert reviews(:five_star).valid? + end + + test "Can only create reviews with integer ratings" do + float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) + string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) + assert_not float_rating.valid? + assert_not string_rating.valid? + end + + test "Cannot create reviews with ratings less than 1 or greater than 5" do + too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) + too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) + assert_not too_low_rating.valid? + assert_not too_high_rating.valid? + end # # test "Create two albums with different titles" do # album1 = albums(:valid_data) @@ -33,11 +54,6 @@ class ReviewTest < ActiveSupport::TestCase # assert_not(album_too_long.valid?) # end # - # test "Creating a new album will instantiate vote count at zero" do - # b = Album.new - # assert_equal(0, b.votes) - # end - # # test "Calling upvote_one increments votes by 1 correctly when starting with a new album" do # b = Album.new # b.upvote_one From fc1a55b3fe8d8d267a1d25925f6d59c0fcaf8f54 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 21:01:20 -0700 Subject: [PATCH 009/144] wrote 2 tests confirming reviews--model--description validations --- test/fixtures/reviews.yml | 2 +- test/models/review_test.rb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml index 02c94cc1f3..71935fdf51 100644 --- a/test/fixtures/reviews.yml +++ b/test/fixtures/reviews.yml @@ -10,7 +10,7 @@ five_star: rating: 5 description: Another description... product_id: 123 -long_description: +character_400: rating: 4 description: Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf product_id: 123 diff --git a/test/models/review_test.rb b/test/models/review_test.rb index c78a23be2d..4c2652e2b7 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -30,6 +30,18 @@ class ReviewTest < ActiveSupport::TestCase assert_not too_low_rating.valid? assert_not too_high_rating.valid? end + + test "Review creation requires a description [string]" do + no_description = Review.new(rating: 0, product_id: 123) + assert_not no_description.valid? + assert reviews(:one_star).valid? + end + + test "Can only create reviews with descriptions 400 characters or less" do + assert reviews(:character_400).valid? + character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") + assert_not character_401.valid? + end # # test "Create two albums with different titles" do # album1 = albums(:valid_data) From e18bb9a3ae6161b362413f00a5df599c885a51ff Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 21:16:36 -0700 Subject: [PATCH 010/144] wrote 1 test confirming reviews--model--product_id validations --- app/models/review.rb | 7 ----- test/models/review_test.rb | 64 +++++--------------------------------- 2 files changed, 8 insertions(+), 63 deletions(-) diff --git a/app/models/review.rb b/app/models/review.rb index b0786530c1..e094e15fec 100644 --- a/app/models/review.rb +++ b/app/models/review.rb @@ -5,10 +5,3 @@ class Review < ActiveRecord::Base belongs_to :product end - - -# Reviews: -# Rating: integer default to 1 -# Description: string -# Product_ID -# (belongs to a product) diff --git a/test/models/review_test.rb b/test/models/review_test.rb index 4c2652e2b7..31b0bd8d69 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -42,62 +42,14 @@ class ReviewTest < ActiveSupport::TestCase character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") assert_not character_401.valid? end - # - # test "Create two albums with different titles" do - # album1 = albums(:valid_data) - # album2 = albums(:another_album) - # assert album2.valid? - # end - # - # test "Cannot create two albums with the same title" do - # album1 = albums(:valid_data) - # album2 = Album.new(name: "Hello") - # assert_not(album2.valid?) - # end - # - # test "Can create an album with a description 808 characters long" do - # album1 = albums(:valid_data) - # album2 = Album.new(name: "Hello") - # assert_not(album2.valid?) - # end - # - # test "Cannot create an album with a description 810 characters long" do - # album_too_long = Album.new(name: "Too Long", description: "Here is a description of Book Title #12. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf os;IF ;KLSDJf l;SJf;ijsefl j SDKL:fj es f;oSJDF l;kSDJFio SJefm LS:Dfj ;djf ;LKSDFj ;Ldjs ;ILEJSF l;SDJF SDIJfp oSfj ko;SDfopPSEfj PAEIOSfj lsidfj IOSEfj KL o;iSDfj efj DFIJS IOSEf oIAEJSf oDJS fo;IJSDF ;oIJDF ioSdfj dios;f jo;ISJf io;SDFJ ;SDKOFj ;Sdfj ;Jf :IOSDFJ ads o;iASdfj AKLSdj O:IADJ ;AOId oSDFJ S:OIDF :OSjm S:DFJ oiDJSf L:DFSZ O:IDSZJF ;oISJDf ;OSDFJ L:SDIFj L:SDKFJ O:ISDJf ;DFJS ;sIOJF ;oDFJS IO:SEfjDJSF ;SDFJ ioSfjUH") - # assert_not(album_too_long.valid?) - # end - # - # test "Calling upvote_one increments votes by 1 correctly when starting with a new album" do - # b = Album.new - # b.upvote_one - # assert_equal(1, b.votes) - # end - # - # test "Calling upvote_one increments votes by 1 correctly with any integer higher than zero" do - # b = Album.new - # b.votes = 5 - # b.upvote_one - # assert_equal(6, b.votes) - # end - # - # test "Cannot set votes to a negative integer" do - # b = Album.new(name: "yellow") - # b.votes = -4 - # assert(b.invalid?) - # assert_includes(b.errors, :votes) - # end - # - # test "Votes cannot be set to nil" do - # b = Album.new(name: "yellow") - # b.votes = nil - # assert(b.invalid?) - # assert_includes(b.errors, :votes) - # end + + test "Review creation requires a product_id [integer]" do + no_product_id = Review.new(rating: 0, description: "hello") + assert_not no_product_id.valid? + assert reviews(:one_star).valid? + end end -# on rails console, object.new goes straight to the model, bypassing the controller completely. +# note: have not written any tests on the belongs_to relationship, as we haven't set-up the product_id in the controller using params yet. I think this relationship can be tested in the controller instead of the model tests? -# Reviews: -# Rating: integer default to 1 -# Description: string -# Product_ID -# (belongs to a product) +# fyi from earlier project: on rails console, object.new goes straight to the model, bypassing the controller completely. From b25f4ee2e26af66ada57efd135faef2bf786c0e7 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 22:33:47 -0700 Subject: [PATCH 011/144] wrote 3 order item MODEL tests, one is not passing, too tired to figure this out right now--all these tests relate to the quantity validations in the order item model --- app/models/order_item.rb | 12 ++++ .../20161019043527_create_order_items.rb | 12 ++++ db/schema.rb | 11 ++- test/fixtures/order_items.yml | 10 +++ test/models/order_item_test.rb | 71 +++++++++++++++++++ test/models/review_test.rb | 21 ++++-- 6 files changed, 132 insertions(+), 5 deletions(-) create mode 100644 app/models/order_item.rb create mode 100644 db/migrate/20161019043527_create_order_items.rb create mode 100644 test/fixtures/order_items.yml create mode 100644 test/models/order_item_test.rb diff --git a/app/models/order_item.rb b/app/models/order_item.rb new file mode 100644 index 0000000000..3192d4dab1 --- /dev/null +++ b/app/models/order_item.rb @@ -0,0 +1,12 @@ +class OrderItem < ActiveRecord::Base + validates :quantity, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 } + # less_than_or_equal_to: Product.find(self.product_id).quantity } + # I'd like this last line of code to work in the numericality hash--it's not working right now. Maybe it will work once the Product model has been created in this version of our app? + # Also, need to confirm what the quantity/stock variable is in the Product model and update the last word of this commented out code in line 3. + validates :product_id, presence: true + validates :order_id, presence: true + validates :shipped?, presence: true + + belongs_to :product + belongs_to :order +end diff --git a/db/migrate/20161019043527_create_order_items.rb b/db/migrate/20161019043527_create_order_items.rb new file mode 100644 index 0000000000..c3be31db3b --- /dev/null +++ b/db/migrate/20161019043527_create_order_items.rb @@ -0,0 +1,12 @@ +class CreateOrderItems < ActiveRecord::Migration + def change + create_table :order_items do |t| + t.integer :quantity, :default => 1 + t.integer :product_id + t.integer :order_id + t.boolean :shipped?, :default => false + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 8c4cd49d99..0ff0936d58 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,16 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161018233022) do +ActiveRecord::Schema.define(version: 20161019043527) do + + create_table "order_items", force: :cascade do |t| + t.integer "quantity", default: 1 + t.integer "product_id" + t.integer "order_id" + t.boolean "shipped?", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end create_table "reviews", force: :cascade do |t| t.integer "rating" diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml new file mode 100644 index 0000000000..ea52eb6faf --- /dev/null +++ b/test/fixtures/order_items.yml @@ -0,0 +1,10 @@ +one_unit: + quantity: 1 + product_id: 123 + order_id: 456 + shipped?: false +four_unit: + quantity: 4 + product_id: 123 + order_id: 456 + shipped?: false diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb new file mode 100644 index 0000000000..90544ec1a5 --- /dev/null +++ b/test/models/order_item_test.rb @@ -0,0 +1,71 @@ +require 'test_helper' + +class OrderItemTest < ActiveSupport::TestCase + test "Cannot create an order item with nil quantity" do + nil_quantity = OrderItem.new(quantity: nil, product_id: 123, order_id: 456) + assert_equal(nil, nil_quantity.quantity) + assert_not nil_quantity.valid? + assert_includes(nil_quantity.errors, :quantity) + end + + test "Order item quantity is always instantiated at 1" do + default_quantity = OrderItem.new(product_id: 123, order_id: 456) + assert_equal(1, default_quantity.quantity) + end + + test "Order item quantity must be a positive (non-zero) integer (LESS THAN OR EQUAL TO THE PRODUCT'S QUANTITY/STOCK, WOULD lIKE TO BUILD IN THIS PART OF THE TEST)" do + assert order_items(:four_unit).valid? + assert order_items(:one_unit).valid? + + zero_quantity = OrderItem.new(quantity: 0, product_id: 123, order_id: 456) + assert_not zero_quantity.valid? + assert_includes(zero_quantity.errors, :quantity) + + negative_quantity = OrderItem.new(quantity: -6, product_id: 123, order_id: 456) + assert_not negative_quantity.valid? + assert_includes(negative_quantity.errors, :quantity) + end + + + + # test "Can only create reviews with integer ratings" do + # float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) + # string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) + # assert_not float_rating.valid? + # assert_not string_rating.valid? + # end + # + # test "Cannot create reviews with ratings less than 1 or greater than 5" do + # too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) + # too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) + # assert_not too_low_rating.valid? + # assert_not too_high_rating.valid? + # end + # + # test "Review creation requires a description [string]" do + # no_description = Review.new(rating: 0, product_id: 123) + # assert_not no_description.valid? + # assert reviews(:one_star).valid? + # end + # + # test "Can only create reviews with descriptions 400 characters or less" do + # assert reviews(:character_400).valid? + # character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") + # assert_not character_401.valid? + # end + # + # test "Review creation requires a product_id [integer]" do + # no_product_id = Review.new(rating: 0, description: "hello") + # assert_not no_product_id.valid? + # assert reviews(:one_star).valid? + # end + # + # test "Creating a new review will instantiate rating as nil" do + # r = Review.new + # assert_equal(nil, r.rating) + # end +end + + # note: have not written any tests on the belongs_to relationship, as we haven't set-up the product_id in the controller using params yet. I think this relationship can be tested in the controller instead of the model tests? + + # fyi from earlier project: on rails console, object.new goes straight to the model, bypassing the controller completely. diff --git a/test/models/review_test.rb b/test/models/review_test.rb index 31b0bd8d69..27a82295bd 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -9,6 +9,7 @@ class ReviewTest < ActiveSupport::TestCase test "Cannot save a nil rating to the database" do no_rating = Review.new(description: "whatever", product_id: 123) assert_not no_rating.valid? + assert_includes(no_rating.errors, :rating) end test "Can create reviews with ratings between 1 and 5" do @@ -19,34 +20,46 @@ class ReviewTest < ActiveSupport::TestCase test "Can only create reviews with integer ratings" do float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) - string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) assert_not float_rating.valid? + assert_includes(float_rating.errors, :rating) + + string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) assert_not string_rating.valid? + assert_includes(string_rating.errors, :rating) end test "Cannot create reviews with ratings less than 1 or greater than 5" do too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) - too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) assert_not too_low_rating.valid? + assert_includes(too_low_rating.errors, :rating) + + too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) assert_not too_high_rating.valid? + assert_includes(too_high_rating.errors, :rating) end test "Review creation requires a description [string]" do + assert reviews(:one_star).valid? + no_description = Review.new(rating: 0, product_id: 123) assert_not no_description.valid? - assert reviews(:one_star).valid? + assert_includes(no_description.errors, :description) end test "Can only create reviews with descriptions 400 characters or less" do assert reviews(:character_400).valid? + character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") assert_not character_401.valid? + assert_includes(character_401.errors, :description) end test "Review creation requires a product_id [integer]" do + assert reviews(:one_star).valid? + no_product_id = Review.new(rating: 0, description: "hello") assert_not no_product_id.valid? - assert reviews(:one_star).valid? + assert_includes(no_product_id.errors, :product_id) end end From b8fccf47467711e8495d50463d15f2f1daba7755 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 22:41:48 -0700 Subject: [PATCH 012/144] wrote 3 more tests for the remaining order item model validations--however two of these tests are not yet passing, need to come back to these with FRESH EYES. :) --- test/models/order_item_test.rb | 56 ++++++++++++---------------------- 1 file changed, 20 insertions(+), 36 deletions(-) diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb index 90544ec1a5..6125b55eb6 100644 --- a/test/models/order_item_test.rb +++ b/test/models/order_item_test.rb @@ -26,44 +26,28 @@ class OrderItemTest < ActiveSupport::TestCase assert_includes(negative_quantity.errors, :quantity) end + test "Order item creation requires a product_id [integer]" do + no_product_id = OrderItem.new(quantity: 2, order_id: 456) + assert_not no_product_id.valid? + assert_includes(no_product_id.errors, :product_id) + assert order_items(:one_unit).valid? + assert order_items(:four_unit).valid? + end - # test "Can only create reviews with integer ratings" do - # float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) - # string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) - # assert_not float_rating.valid? - # assert_not string_rating.valid? - # end - # - # test "Cannot create reviews with ratings less than 1 or greater than 5" do - # too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) - # too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) - # assert_not too_low_rating.valid? - # assert_not too_high_rating.valid? - # end - # - # test "Review creation requires a description [string]" do - # no_description = Review.new(rating: 0, product_id: 123) - # assert_not no_description.valid? - # assert reviews(:one_star).valid? - # end - # - # test "Can only create reviews with descriptions 400 characters or less" do - # assert reviews(:character_400).valid? - # character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") - # assert_not character_401.valid? - # end - # - # test "Review creation requires a product_id [integer]" do - # no_product_id = Review.new(rating: 0, description: "hello") - # assert_not no_product_id.valid? - # assert reviews(:one_star).valid? - # end - # - # test "Creating a new review will instantiate rating as nil" do - # r = Review.new - # assert_equal(nil, r.rating) - # end + test "Order item creation requires an order_id [integer]" do + no_order_id = OrderItem.new(quantity: 2, product_id: 456) + assert_not no_order_id.valid? + assert_includes(no_order_id.errors, :order_id) + + assert order_items(:one_unit).valid? + assert order_items(:four_unit).valid? + end + + test "Creating an order item instantiate :shipped? as false" do + o = OrderItem.new + assert_equal(false, o.shipped?) + end end # note: have not written any tests on the belongs_to relationship, as we haven't set-up the product_id in the controller using params yet. I think this relationship can be tested in the controller instead of the model tests? From eac1b0282829fe8170cd5b4629de8fa15cd7cf03 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Tue, 18 Oct 2016 22:42:21 -0700 Subject: [PATCH 013/144] wrote 3 more tests for the remaining order item model validations--however two of these tests are not yet passing, need to come back to these with FRESH EYES. --- test/models/order_item_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb index 6125b55eb6..ac7973fe02 100644 --- a/test/models/order_item_test.rb +++ b/test/models/order_item_test.rb @@ -50,6 +50,6 @@ class OrderItemTest < ActiveSupport::TestCase end end - # note: have not written any tests on the belongs_to relationship, as we haven't set-up the product_id in the controller using params yet. I think this relationship can be tested in the controller instead of the model tests? + # note: have not written any tests on the belongs_to relationships, as we haven't set-up the product_id or order_id in the controller using params yet. I think these relationships can be tested in the controller instead of the model tests? # fyi from earlier project: on rails console, object.new goes straight to the model, bypassing the controller completely. From 8ce365d521d563dee9e65ab95a7615c7056628f3 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 08:46:19 -0700 Subject: [PATCH 014/144] wrote Product model and tests --- app/models/product.rb | 6 ++ db/migrate/20161019045258_create_products.rb | 14 ++++ db/schema.rb | 27 ++++++++ test/fixtures/products.yml | 12 ++++ test/models/product_test.rb | 72 ++++++++++++++++++++ 5 files changed, 131 insertions(+) create mode 100644 app/models/product.rb create mode 100644 db/migrate/20161019045258_create_products.rb create mode 100644 db/schema.rb create mode 100644 test/fixtures/products.yml create mode 100644 test/models/product_test.rb diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 0000000000..6887aee930 --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,6 @@ +class Product < ActiveRecord::Base + has_and_belongs_to_many :categories + belongs_to :merchant + validates :name, presence: true, uniqueness: true + validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} +end diff --git a/db/migrate/20161019045258_create_products.rb b/db/migrate/20161019045258_create_products.rb new file mode 100644 index 0000000000..4929487895 --- /dev/null +++ b/db/migrate/20161019045258_create_products.rb @@ -0,0 +1,14 @@ +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.string :description + t.integer :stock + t.integer :price + t.string :photo_url + t.belongs_to :merchant + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..023c2295a4 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,27 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20161019045258) do + + create_table "products", force: :cascade do |t| + t.string "name" + t.string "description" + t.integer "stock" + t.integer "price" + t.string "photo_url" + t.integer "merchant_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml new file mode 100644 index 0000000000..3940fc3bc6 --- /dev/null +++ b/test/fixtures/products.yml @@ -0,0 +1,12 @@ +cat_suit: + name: cat suit + price: 1234 + description: your cat will look dapper in this one-piece suit! + stock: 4 + photo_url: http://placekitten.com/200/300 +hamster_monocle: + name: hamster monocle + price: 500 + description: a classy way to correct your hamster's vision. + stock: 20 + photo_url: http://placekitten.com/200/300 diff --git a/test/models/product_test.rb b/test/models/product_test.rb new file mode 100644 index 0000000000..39e84bf73a --- /dev/null +++ b/test/models/product_test.rb @@ -0,0 +1,72 @@ +require 'test_helper' + +class ProductTest < ActiveSupport::TestCase + test "Cannot create a product without a name" do + product = Product.new + assert_not product.valid? + assert_includes product.errors, :name + end + + test "Cannot create a product without a price" do + product = Product.new(name: "kitten tux") + assert_not product.valid? + assert_includes product.errors, :price + end + + test "Cannot create a product with a duplicate name" do + product = Product.new(name: "cat suit", price: 224) + assert_not product.valid? + assert_not product.save + assert_includes product.errors, :name + end + + test "Price must be an integer" do + product = Product.new(name: "dog sunglasses", price: "foo") + assert_not product.valid? + assert_not product.save + assert_equal ["is not a number"], product.errors.messages[:price] + end + + test "Price can't be 0" do + product = Product.new(name: "foobar", price: 0) + assert_not product.valid? + assert_not product.save + assert_equal ["must be greater than 0"], product.errors.messages[:price] + end + + test "Price can't be less than 0" do + product = Product.new(name: "foobar", price: -1) + assert_not product.valid? + assert_not product.save + assert_equal ["must be greater than 0"], product.errors.messages[:price] + end + + test "create Product with valid data" do + product = Product.new(name: "penguin bowtie", price: 2150) + assert product.valid? + assert_not_nil product.name + assert_not_nil product.price + end + + test "create products with different names" do + products.each do |product| + assert product.valid? + assert product.save + end + end + + # test "Product can be assigned a merchant id" do + # product = Product.create!(name: "mouse hat", price: 1240) + # merchant = Merchant.create!(username: "testing", email: "test@test.com") + # + # product.merchant = merchant + # assert product.save + # + # assert_equal product.merchant_id, merchant.id + # assert_includes merchant.products, product + # end + + # test "Product can have many categories" do + # # INSERT TEST HERE + # end +end From 88082bfda806d0c3831d59ca641b089f5c461c97 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 11:49:03 -0700 Subject: [PATCH 015/144] Created the merchant model and made sure the Git login information was in the env file --- app/models/merchant.rb | 12 +++++++++ config/initializers/omniauth.rb | 3 +++ db/migrate/20161019005406_create_merchants.rb | 11 ++++++++ db/schema.rb | 25 +++++++++++++++++++ test/fixtures/merchants.yml | 11 ++++++++ test/models/merchant_test.rb | 12 +++++++++ 6 files changed, 74 insertions(+) create mode 100644 app/models/merchant.rb create mode 100644 config/initializers/omniauth.rb create mode 100644 db/migrate/20161019005406_create_merchants.rb create mode 100644 db/schema.rb create mode 100644 test/fixtures/merchants.yml create mode 100644 test/models/merchant_test.rb diff --git a/app/models/merchant.rb b/app/models/merchant.rb new file mode 100644 index 0000000000..101c02c85a --- /dev/null +++ b/app/models/merchant.rb @@ -0,0 +1,12 @@ +class Merchant < ActiveRecord::Base + validates :email, :user_name, :uid, :provider, presence: true + + def self.build_from_github(auth_hash) + merchant = Merchant.new + merchant.uid = auth_hash[:uid] + merchant.provider = 'github' + merchant.user_name = auth_hash['info']['nickname'] + merchant.email = auth_hash['info']['email'] + return merchant + end +end diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb new file mode 100644 index 0000000000..fd4416122a --- /dev/null +++ b/config/initializers/omniauth.rb @@ -0,0 +1,3 @@ +Rails.application.config.middleware.use OmniAuth::Builder do + provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email" +end diff --git a/db/migrate/20161019005406_create_merchants.rb b/db/migrate/20161019005406_create_merchants.rb new file mode 100644 index 0000000000..758eccaa6f --- /dev/null +++ b/db/migrate/20161019005406_create_merchants.rb @@ -0,0 +1,11 @@ +class CreateMerchants < ActiveRecord::Migration + def change + create_table :merchants do |t| + t.string :user_name + t.string :email + t.integer :uid, null: false + t.string :provider, null: false + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..dad67ec5e1 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,25 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20161019005406) do + + create_table "merchants", force: :cascade do |t| + t.string "user_name" + t.string "email" + t.integer "uid", null: false + t.string "provider", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml new file mode 100644 index 0000000000..937a0c002e --- /dev/null +++ b/test/fixtures/merchants.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb new file mode 100644 index 0000000000..9ab2cc202f --- /dev/null +++ b/test/models/merchant_test.rb @@ -0,0 +1,12 @@ +require 'test_helper' + +class MerchantTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end + test "create a merchant with no email or uid" do + merchant = Merchant.new + assert_not merchant.valid? + end + +end From 7502d5eefa5e7532b7965174016c3ad5372a02e6 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 19 Oct 2016 11:49:39 -0700 Subject: [PATCH 016/144] fixed order_item model validation to work with a boolean value so that all tests now pass. Updated order_item yml file to test for a true and false shipped? --- app/models/order_item.rb | 2 +- test/fixtures/order_items.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/models/order_item.rb b/app/models/order_item.rb index 3192d4dab1..6aaa4fbdb4 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -5,7 +5,7 @@ class OrderItem < ActiveRecord::Base # Also, need to confirm what the quantity/stock variable is in the Product model and update the last word of this commented out code in line 3. validates :product_id, presence: true validates :order_id, presence: true - validates :shipped?, presence: true + validates :shipped?, inclusion: { in: [true, false] } belongs_to :product belongs_to :order diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml index ea52eb6faf..3d983a3d27 100644 --- a/test/fixtures/order_items.yml +++ b/test/fixtures/order_items.yml @@ -7,4 +7,4 @@ four_unit: quantity: 4 product_id: 123 order_id: 456 - shipped?: false + shipped?: true From 594f97c584a97d062353701b2f9983f8343e865e Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 11:50:32 -0700 Subject: [PATCH 017/144] modified test data for merchant validation --- test/models/product_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/models/product_test.rb b/test/models/product_test.rb index 39e84bf73a..869ce5c1db 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -57,7 +57,7 @@ class ProductTest < ActiveSupport::TestCase # test "Product can be assigned a merchant id" do # product = Product.create!(name: "mouse hat", price: 1240) - # merchant = Merchant.create!(username: "testing", email: "test@test.com") + # merchant = Merchant.create!(user_name: "testing", email: "test@test.com") # # product.merchant = merchant # assert product.save From b4a4007c32a0f2e107772c0cf5b665b8ce78dc2c Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 12:20:30 -0700 Subject: [PATCH 018/144] Erased data in the yml file, added some session set up in test_helper and put in empty merchant test --- test/fixtures/merchants.yml | 5 ----- test/models/merchant_test.rb | 2 +- test/test_helper.rb | 9 ++++++++- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml index 937a0c002e..be145aeb97 100644 --- a/test/fixtures/merchants.yml +++ b/test/fixtures/merchants.yml @@ -4,8 +4,3 @@ # model remove the '{}' from the fixture names and add the columns immediately # below each fixture, per the syntax in the comments below # -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb index 9ab2cc202f..b9694f0c87 100644 --- a/test/models/merchant_test.rb +++ b/test/models/merchant_test.rb @@ -4,7 +4,7 @@ class MerchantTest < ActiveSupport::TestCase # test "the truth" do # assert true # end - test "create a merchant with no email or uid" do + test "create a merchant with no email, name or uid" do merchant = Merchant.new assert_not merchant.valid? end diff --git a/test/test_helper.rb b/test/test_helper.rb index 2c5373e528..8d650ce276 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -8,6 +8,13 @@ class ActiveSupport::TestCase # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. fixtures :all Minitest::Reporters.use! + def setup - # Add more helper methods to be used by all tests here... + OmniAuth.config.test_mode = true + + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ + provider: 'github', uid: '123545', info: { email: "a@b.com", name: "Ada" } + }) + end + # Add more helper methods to be used by all tests here... end From c41976c367bf453e0a9393b99995a6fc01164c41 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 13:52:33 -0700 Subject: [PATCH 019/144] Other test added but commented out for market model --- test/models/merchant_test.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb index b9694f0c87..877bea082b 100644 --- a/test/models/merchant_test.rb +++ b/test/models/merchant_test.rb @@ -9,4 +9,11 @@ class MerchantTest < ActiveSupport::TestCase assert_not merchant.valid? end + # test "the provider must be from github" do + # git_hash = {user_name: "kitty", email: "abc@aol.com", uid: 15, provider: "twitter"} + # merchant = Merchant.build_from_github(git_hash) + # assert_not merchant.valid? + # + # end + end From 5698a3dd1a86f09857d96c3f0f5bd1f005e1dd09 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 13:52:43 -0700 Subject: [PATCH 020/144] index: true added to belongs_to :merchant --- app/models/product.rb | 1 + db/migrate/20161019045258_create_products.rb | 2 +- db/schema.rb | 2 ++ 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/app/models/product.rb b/app/models/product.rb index 6887aee930..f21abd6d59 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,5 +1,6 @@ class Product < ActiveRecord::Base has_and_belongs_to_many :categories + has_many :orderitems belongs_to :merchant validates :name, presence: true, uniqueness: true validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} diff --git a/db/migrate/20161019045258_create_products.rb b/db/migrate/20161019045258_create_products.rb index 4929487895..d3eab0c07e 100644 --- a/db/migrate/20161019045258_create_products.rb +++ b/db/migrate/20161019045258_create_products.rb @@ -6,7 +6,7 @@ def change t.integer :stock t.integer :price t.string :photo_url - t.belongs_to :merchant + t.belongs_to :merchant, index: true t.timestamps null: false end diff --git a/db/schema.rb b/db/schema.rb index 023c2295a4..e67a5fd00a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -24,4 +24,6 @@ t.datetime "updated_at", null: false end + add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" + end From 723c4b5842de4dcc40dafa7b01c7287936a093da Mon Sep 17 00:00:00 2001 From: Yeni Date: Wed, 19 Oct 2016 13:59:00 -0700 Subject: [PATCH 021/144] Created order and category models and created the tests for order. Added validations to Order. --- app/models/category.rb | 3 ++ app/models/order.rb | 15 ++++++++ .../20161019080932_create_categories.rb | 8 +++++ db/migrate/20161019094620_create_orders.rb | 19 ++++++++++ .../20161019205504_remove_total_from_order.rb | 5 +++ db/schema.rb | 36 +++++++++++++++++++ test/fixtures/categories.yml | 11 ++++++ test/fixtures/orders.yml | 11 ++++++ test/models/category_test.rb | 7 ++++ test/models/order_test.rb | 24 +++++++++++++ 10 files changed, 139 insertions(+) create mode 100644 app/models/category.rb create mode 100644 app/models/order.rb create mode 100644 db/migrate/20161019080932_create_categories.rb create mode 100644 db/migrate/20161019094620_create_orders.rb create mode 100644 db/migrate/20161019205504_remove_total_from_order.rb create mode 100644 db/schema.rb create mode 100644 test/fixtures/categories.yml create mode 100644 test/fixtures/orders.yml create mode 100644 test/models/category_test.rb create mode 100644 test/models/order_test.rb diff --git a/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000000..7f018cd725 --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,3 @@ +class Category < ActiveRecord::Base + has_and_belongs_to_many :products +end diff --git a/app/models/order.rb b/app/models/order.rb new file mode 100644 index 0000000000..9adfb3ab8e --- /dev/null +++ b/app/models/order.rb @@ -0,0 +1,15 @@ +class Order < ActiveRecord::Base + has_many :order_items + validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 16 } + validates :cc_exp_year, presence: true, length: {is: 4} + validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} + validate :valid_exp + + def valid_exp + if cc_exp_year < Time.now.year + errors.add(:cc_exp_year, "Card year is expired") + elsif cc_exp_year == Time.now.year && cc_exp_month < Time.now.month + errors.add(:cc_exp_month, "Card month is expired") + end + end +end diff --git a/db/migrate/20161019080932_create_categories.rb b/db/migrate/20161019080932_create_categories.rb new file mode 100644 index 0000000000..4b0ff05055 --- /dev/null +++ b/db/migrate/20161019080932_create_categories.rb @@ -0,0 +1,8 @@ +class CreateCategories < ActiveRecord::Migration + def change + create_table :categories do |t| + t.string :name + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161019094620_create_orders.rb b/db/migrate/20161019094620_create_orders.rb new file mode 100644 index 0000000000..569c40288e --- /dev/null +++ b/db/migrate/20161019094620_create_orders.rb @@ -0,0 +1,19 @@ +class CreateOrders < ActiveRecord::Migration + def change + create_table :orders do |t| + + t.string :status + t.datetime :date_purchased + t.string :email + t.string :address + t.string :cc_name + t.integer :cc_number + t.integer :cc_exp_year + t.integer :cc_exp_month + t.integer :billing_zip + t.integer :total + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161019205504_remove_total_from_order.rb b/db/migrate/20161019205504_remove_total_from_order.rb new file mode 100644 index 0000000000..3f023ddf11 --- /dev/null +++ b/db/migrate/20161019205504_remove_total_from_order.rb @@ -0,0 +1,5 @@ +class RemoveTotalFromOrder < ActiveRecord::Migration + def change + remove_column(:orders, :total) + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..25c9333501 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,36 @@ +# encoding: UTF-8 +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20161019205504) do + + create_table "categories", force: :cascade do |t| + t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + create_table "orders", force: :cascade do |t| + t.string "status" + t.datetime "date_purchased" + t.string "email" + t.string "address" + t.string "cc_name" + t.integer "cc_number" + t.integer "cc_exp_year" + t.integer "cc_exp_month" + t.integer "billing_zip" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + +end diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml new file mode 100644 index 0000000000..937a0c002e --- /dev/null +++ b/test/fixtures/categories.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml new file mode 100644 index 0000000000..937a0c002e --- /dev/null +++ b/test/fixtures/orders.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/category_test.rb b/test/models/category_test.rb new file mode 100644 index 0000000000..4733541516 --- /dev/null +++ b/test/models/category_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CategoryTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/order_test.rb b/test/models/order_test.rb new file mode 100644 index 0000000000..53a53e3a38 --- /dev/null +++ b/test/models/order_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class OrderTest < ActiveSupport::TestCase + test "credit card must present and be 16 characters long" do + order = Order.new(cc_number: 2345123454326789, cc_exp_year: 2016, cc_exp_month: 10) + order2 = Order.new(cc_number: 1234, cc_exp_year: 1990, cc_exp_month: 7) + order3 = Order.new(cc_number: 2345123454326789234, cc_exp_year: 1990, cc_exp_month: 7) + + assert order.valid? + assert_not order2.valid? + assert_not order3.valid? + end + + test "credit card should not have an expired date" do + order = Order.new(cc_exp_year: 2014, cc_exp_month: 12) + order2 = Order.new(cc_exp_month: 10, cc_exp_year: 2013) + order3 = Order.new(cc_exp_year: 2016, cc_exp_month: 9, cc_number: 2345123454326789) + + assert_not order.valid? + assert_not order2.valid? + #this assertion should not pass since month is in the past, month is not yet linked to year, working on it + assert_not order3.valid? + end +end From 195a2345dd3a8b2e2888222f05093befd432acb7 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 14:12:43 -0700 Subject: [PATCH 022/144] schema changes --- db/schema.rb | 58 +++++++++++++++++++++++++++------------------------- 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index b1e76cda0d..9ca4ea1782 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,37 +11,12 @@ # # It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20161019043527) do - - create_table "order_items", force: :cascade do |t| - t.integer "quantity", default: 1 - t.integer "product_id" - t.integer "order_id" - t.boolean "shipped?", default: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end - - create_table "reviews", force: :cascade do |t| - t.integer "rating" - t.string "description" - t.integer "product_id" - end - - create_table "products", force: :cascade do |t| - t.string "name" - t.string "description" - t.integer "stock" - t.integer "price" - t.string "photo_url" - t.integer "merchant_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - end +ActiveRecord::Schema.define(version: 20161019205504) do create_table "categories", force: :cascade do |t| t.string "name" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "merchants", force: :cascade do |t| @@ -53,6 +28,14 @@ t.datetime "updated_at", null: false end + create_table "order_items", force: :cascade do |t| + t.integer "quantity", default: 1 + t.integer "product_id" + t.integer "order_id" + t.boolean "shipped?", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end create_table "orders", force: :cascade do |t| t.string "status" @@ -68,6 +51,25 @@ t.datetime "updated_at", null: false end + create_table "products", force: :cascade do |t| + t.string "name" + t.string "description" + t.integer "stock" + t.integer "price" + t.string "photo_url" + t.integer "merchant_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" + create_table "reviews", force: :cascade do |t| + t.integer "rating" + t.string "description" + t.integer "product_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + end From 6eb420b7e3b97ac0de392de10c459ffdc7072cfd Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 19 Oct 2016 14:34:30 -0700 Subject: [PATCH 023/144] added 2 controllers: home and sessions --- app/assets/javascripts/home.coffee | 3 +++ app/assets/javascripts/sessions.coffee | 3 +++ app/assets/stylesheets/home.scss | 3 +++ app/assets/stylesheets/sessions.scss | 3 +++ app/controllers/home_controller.rb | 3 +++ app/controllers/sessions_controller.rb | 2 ++ app/helpers/home_helper.rb | 2 ++ app/helpers/sessions_helper.rb | 2 ++ config/routes.rb | 4 ++-- test/controllers/home_controller_test.rb | 7 +++++++ test/controllers/sessions_controller_test.rb | 7 +++++++ 11 files changed, 37 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/home.coffee create mode 100644 app/assets/javascripts/sessions.coffee create mode 100644 app/assets/stylesheets/home.scss create mode 100644 app/assets/stylesheets/sessions.scss create mode 100644 app/controllers/home_controller.rb create mode 100644 app/controllers/sessions_controller.rb create mode 100644 app/helpers/home_helper.rb create mode 100644 app/helpers/sessions_helper.rb create mode 100644 test/controllers/home_controller_test.rb create mode 100644 test/controllers/sessions_controller_test.rb diff --git a/app/assets/javascripts/home.coffee b/app/assets/javascripts/home.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/home.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/javascripts/sessions.coffee b/app/assets/javascripts/sessions.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/sessions.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss new file mode 100644 index 0000000000..f0ddc6846a --- /dev/null +++ b/app/assets/stylesheets/home.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the home controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 0000000000..7bef9cf826 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb new file mode 100644 index 0000000000..3930f023d2 --- /dev/null +++ b/app/controllers/home_controller.rb @@ -0,0 +1,3 @@ +class HomeController < ApplicationController + def index; end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..16d11b5710 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,2 @@ +class SessionsController < ApplicationController +end diff --git a/app/helpers/home_helper.rb b/app/helpers/home_helper.rb new file mode 100644 index 0000000000..23de56ac60 --- /dev/null +++ b/app/helpers/home_helper.rb @@ -0,0 +1,2 @@ +module HomeHelper +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 0000000000..309f8b2eb3 --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/config/routes.rb b/config/routes.rb index 2d0b71e807..4fe790456b 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,6 @@ Rails.application.routes.draw do - root 'ffc#index' + root 'home#index' resources :merchants, only: [:show] do resources :products @@ -24,7 +24,7 @@ # Sessions routes - can be further flushed out... get '/auth/:provider/callback' => 'sessions#create' - # get "/sessions/login_failure", to: "sessions#login_failure", as: "login_failure" + get "/sessions/login_failure", to: "sessions#login_failure", as: "login_failure" get '/sessions', to: 'sessions#index', as: 'sessions' diff --git a/test/controllers/home_controller_test.rb b/test/controllers/home_controller_test.rb new file mode 100644 index 0000000000..730478d380 --- /dev/null +++ b/test/controllers/home_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class HomeControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb new file mode 100644 index 0000000000..d30ebc380a --- /dev/null +++ b/test/controllers/sessions_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SessionsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end From baae425f4e4098bbaed73023920816f63b85e868 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 14:43:15 -0700 Subject: [PATCH 024/144] Created Products controller and added two tests for index and show --- app/assets/javascripts/products.coffee | 3 +++ app/assets/stylesheets/products.scss | 3 +++ app/controllers/products_controller.rb | 11 +++++++++++ app/helpers/products_helper.rb | 2 ++ test/controllers/products_controller_test.rb | 20 ++++++++++++++++++++ test/fixtures/products.yml | 1 + 6 files changed, 40 insertions(+) create mode 100644 app/assets/javascripts/products.coffee create mode 100644 app/assets/stylesheets/products.scss create mode 100644 app/controllers/products_controller.rb create mode 100644 app/helpers/products_helper.rb create mode 100644 test/controllers/products_controller_test.rb diff --git a/app/assets/javascripts/products.coffee b/app/assets/javascripts/products.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/products.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss new file mode 100644 index 0000000000..89e2e8db07 --- /dev/null +++ b/app/assets/stylesheets/products.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the products controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb new file mode 100644 index 0000000000..8260adda46 --- /dev/null +++ b/app/controllers/products_controller.rb @@ -0,0 +1,11 @@ +class ProductsController < ApplicationController + + def index + @product = Product.all + end + + def show + @product = Product.find() + end + +end diff --git a/app/helpers/products_helper.rb b/app/helpers/products_helper.rb new file mode 100644 index 0000000000..ab5c42b325 --- /dev/null +++ b/app/helpers/products_helper.rb @@ -0,0 +1,2 @@ +module ProductsHelper +end diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb new file mode 100644 index 0000000000..e24dfd03dd --- /dev/null +++ b/test/controllers/products_controller_test.rb @@ -0,0 +1,20 @@ +require 'test_helper' + +class ProductsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end + test "should show the index page" do + get :index + assert_template :index + assert_response :success + + end + + test "show should a specific product" do + get :show, {id: products(:cat_suit).id} + assert_response :success + assert_template :show + assert_equal assigns(:product), products(:cat_suit) + end +end diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index 3940fc3bc6..88dccbd403 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -4,6 +4,7 @@ cat_suit: description: your cat will look dapper in this one-piece suit! stock: 4 photo_url: http://placekitten.com/200/300 + hamster_monocle: name: hamster monocle price: 500 From 6b692cb212768f0f7b5ee23ee141fc7962195b2a Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 14:47:36 -0700 Subject: [PATCH 025/144] Created views for product and passed tests --- app/controllers/products_controller.rb | 7 ++++++- app/views/products/index.html.erb | 1 + app/views/products/show.html.erb | 1 + test/controllers/products_controller_test.rb | 1 - 4 files changed, 8 insertions(+), 2 deletions(-) create mode 100644 app/views/products/index.html.erb create mode 100644 app/views/products/show.html.erb diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 8260adda46..93fa2cf387 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -5,7 +5,12 @@ def index end def show - @product = Product.find() + @product = Product.find(params[:id]) + end + private + + def product_params + params.require(:product).permit(:name, :description, :stock, :price,:photo_url, :merchant_id) end end diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb new file mode 100644 index 0000000000..9a2e16e17c --- /dev/null +++ b/app/views/products/index.html.erb @@ -0,0 +1 @@ +

All Products Here

diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb new file mode 100644 index 0000000000..6dffa8bf1a --- /dev/null +++ b/app/views/products/show.html.erb @@ -0,0 +1 @@ +

Show single product here

diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index e24dfd03dd..46de3ddb10 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -8,7 +8,6 @@ class ProductsControllerTest < ActionController::TestCase get :index assert_template :index assert_response :success - end test "show should a specific product" do From a4e3975c4f6bd086994ffae6416d449fdf1bea3d Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 19 Oct 2016 15:14:22 -0700 Subject: [PATCH 026/144] created SESSIONS controller--and 2 views: index and login_failure. Created the home_index view. Added login and logout buttons to views. --- app/controllers/sessions_controller.rb | 38 +++++++++++++++++++++++ app/views/home/index.html.erb | 5 +++ app/views/sessions/index.html.erb | 5 +++ app/views/sessions/login_failure.html.erb | 3 ++ 4 files changed, 51 insertions(+) create mode 100644 app/views/home/index.html.erb create mode 100644 app/views/sessions/index.html.erb create mode 100644 app/views/sessions/login_failure.html.erb diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 16d11b5710..96a8907a04 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,2 +1,40 @@ class SessionsController < ApplicationController +# If we want to implement this later, will need to customize authorization specifics... + # skip_before_action :require_login, only: [:login, :create] + + def login_failure; end + + def login; end + + def create + auth_hash = request.env['omniauth.auth'] + redirect_to login_failure_path unless auth_hash['uid'] + + @merchant = Merchant.find_by(uid: auth_hash[:uid], provider: 'github') + if @merchant.nil? + # Merchant doesn't match anything in the DB. + # Attempt to create a new merchant. + @merchant = Merchant.build_from_github(auth_hash) + render :login_failure unless @merchant.save + end + + # Save the merchant ID in the session + session[:merchant_id] = @merchant.id + + redirect_to sessions_path + end + + def index + if session[:merchant_id].nil? + redirect_to login_failure_path + else + @merchant = Merchant.find(session[:merchant_id]) # < recalls the value set in a previous request + end + end + + def destroy + session.delete(:merchant_id) + redirect_to login_failure_path + end + end diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb new file mode 100644 index 0000000000..2f5646d339 --- /dev/null +++ b/app/views/home/index.html.erb @@ -0,0 +1,5 @@ +<% if flash[:error] %> +

<%= flash[:error] %>

+<% end %> + +

Please <%= link_to "log in", "/auth/github" %>

diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb new file mode 100644 index 0000000000..44df1ee082 --- /dev/null +++ b/app/views/sessions/index.html.erb @@ -0,0 +1,5 @@ +

Merchant Portal...

+

Login successful.

+

Welcome <%= @merchant.email %>!

+ +<%= button_to "Log out", sessions_path, method: :delete %> diff --git a/app/views/sessions/login_failure.html.erb b/app/views/sessions/login_failure.html.erb new file mode 100644 index 0000000000..c2c225e6c2 --- /dev/null +++ b/app/views/sessions/login_failure.html.erb @@ -0,0 +1,3 @@ +

You failed to login.

+ +

Please <%= link_to "log in", "/auth/github" %>

From 21177544a5b1276b0e187de069d1354a429de957 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 15:26:22 -0700 Subject: [PATCH 027/144] Added Seed data to product and played around with the index page --- app/views/products/index.html.erb | 8 +++++ db/seeds.rb | 49 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 9a2e16e17c..5af24b4aa4 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1 +1,9 @@

All Products Here

+ +<%@product.each do |product|%> +
+ <%= image_tag "#{product.url}"%> + <%= product.name %> + <%= product.description %> +
+<%end%> diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e857e..8df550054a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,52 @@ # # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) + +product = [ + { + name: "cat pants", + description: "a nice pair of slacks with an elastic waist", + stock: 15, + price: 850, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg/200/300" + }, + { + name: "llama scarf", + description: "long and warm and not made with other llamas", + stock: 20, + price: 1000, + photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg/200/300" + }, + { + name: "penguin hawaiin shirt", + description: "For the penguin who wants to get away", + stock: 4, + price: 2000, + photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg/200/300" + }, + { + name: "top hat for dogs", + description: "For the dog who likes nice things", + stock: 30, + price: 500, + photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png/200/300" + }, + { + name: "pigglet dance outfit", + description: "For the lil piggies who just wanna dance!", + stock: 12, + price: 3050, + photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg/200/300" + }, + { + name: "Baby Bunny outfit", + description: "Are you a new mommy? Dress you little hopper in style!", + stock: 4, + price: 1900, + photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg/200/300" + } +] + +product.each do |item| + Product.create(item) +end From 3215e9472008e44d842026f99bd4460370f05875 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 19 Oct 2016 15:48:44 -0700 Subject: [PATCH 028/144] Changed the index page for products --- app/views/products/index.html.erb | 2 +- db/seeds.rb | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 5af24b4aa4..97217e5c32 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -2,8 +2,8 @@ <%@product.each do |product|%>
- <%= image_tag "#{product.url}"%> <%= product.name %> + <%= image_tag "#{product.photo_url}"%> <%= product.description %>
<%end%> diff --git a/db/seeds.rb b/db/seeds.rb index 8df550054a..23f0682148 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -12,42 +12,42 @@ description: "a nice pair of slacks with an elastic waist", stock: 15, price: 850, - photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg/200/300" + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg" }, { name: "llama scarf", description: "long and warm and not made with other llamas", stock: 20, price: 1000, - photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg/200/300" + photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg" }, { name: "penguin hawaiin shirt", description: "For the penguin who wants to get away", stock: 4, price: 2000, - photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg/200/300" + photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg" }, { name: "top hat for dogs", description: "For the dog who likes nice things", stock: 30, price: 500, - photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png/200/300" + photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png" }, { name: "pigglet dance outfit", description: "For the lil piggies who just wanna dance!", stock: 12, price: 3050, - photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg/200/300" + photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg" }, { name: "Baby Bunny outfit", description: "Are you a new mommy? Dress you little hopper in style!", stock: 4, price: 1900, - photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg/200/300" + photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg" } ] From 72625b6d86fe65c8a6b1156fbcc44c34a38cebd4 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 19:40:28 -0700 Subject: [PATCH 029/144] implemented join table for Products and Categories --- db/migrate/20161020020026_create_join_table.rb | 8 ++++++++ db/schema.rb | 10 +++++++++- 2 files changed, 17 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20161020020026_create_join_table.rb diff --git a/db/migrate/20161020020026_create_join_table.rb b/db/migrate/20161020020026_create_join_table.rb new file mode 100644 index 0000000000..5923f7079e --- /dev/null +++ b/db/migrate/20161020020026_create_join_table.rb @@ -0,0 +1,8 @@ +class CreateJoinTable < ActiveRecord::Migration + def change + create_join_table :products, :categories do |t| + t.index [:product_id, :category_id] + t.index [:category_id, :product_id] + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 9ca4ea1782..60adab146f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161019205504) do +ActiveRecord::Schema.define(version: 20161020020026) do create_table "categories", force: :cascade do |t| t.string "name" @@ -19,6 +19,14 @@ t.datetime "updated_at", null: false end + create_table "categories_products", id: false, force: :cascade do |t| + t.integer "product_id", null: false + t.integer "category_id", null: false + end + + add_index "categories_products", ["category_id", "product_id"], name: "index_categories_products_on_category_id_and_product_id" + add_index "categories_products", ["product_id", "category_id"], name: "index_categories_products_on_product_id_and_category_id" + create_table "merchants", force: :cascade do |t| t.string "user_name" t.string "email" From e3f4c851597955ee747fc481822f13eeadc22c65 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Wed, 19 Oct 2016 20:24:45 -0700 Subject: [PATCH 030/144] modified join table migration to remove primary key --- db/migrate/20161020020026_create_join_table.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/db/migrate/20161020020026_create_join_table.rb b/db/migrate/20161020020026_create_join_table.rb index 5923f7079e..f0eb23754e 100644 --- a/db/migrate/20161020020026_create_join_table.rb +++ b/db/migrate/20161020020026_create_join_table.rb @@ -1,6 +1,6 @@ class CreateJoinTable < ActiveRecord::Migration def change - create_join_table :products, :categories do |t| + create_join_table :products, :categories, id:false do |t| t.index [:product_id, :category_id] t.index [:category_id, :product_id] end From aadb76a19b713427cd9b632e1a7dcb0123d2017d Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 20 Oct 2016 14:13:59 -0700 Subject: [PATCH 031/144] added category seeds and generated categories controller --- app/assets/javascripts/categories.coffee | 3 ++ app/assets/stylesheets/categories.scss | 3 ++ app/controllers/categories_controller.rb | 2 ++ app/helpers/categories_helper.rb | 2 ++ app/views/products/index.html.erb | 24 ++++++++++++--- db/seeds.rb | 29 +++++++++++++++++-- .../controllers/categories_controller_test.rb | 7 +++++ 7 files changed, 63 insertions(+), 7 deletions(-) create mode 100644 app/assets/javascripts/categories.coffee create mode 100644 app/assets/stylesheets/categories.scss create mode 100644 app/controllers/categories_controller.rb create mode 100644 app/helpers/categories_helper.rb create mode 100644 test/controllers/categories_controller_test.rb diff --git a/app/assets/javascripts/categories.coffee b/app/assets/javascripts/categories.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/categories.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss new file mode 100644 index 0000000000..ef1657f8c9 --- /dev/null +++ b/app/assets/stylesheets/categories.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the categories controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000000..a14959525a --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,2 @@ +class CategoriesController < ApplicationController +end diff --git a/app/helpers/categories_helper.rb b/app/helpers/categories_helper.rb new file mode 100644 index 0000000000..e06f31554c --- /dev/null +++ b/app/helpers/categories_helper.rb @@ -0,0 +1,2 @@ +module CategoriesHelper +end diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 97217e5c32..e5daef4b33 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,9 +1,25 @@

All Products Here

<%@product.each do |product|%> -
- <%= product.name %> - <%= image_tag "#{product.photo_url}"%> - <%= product.description %> +
+

+ <%= product.name %> +

+

+ <%= image_tag "#{product.photo_url}"%> +

+

+ <%= product.description %> +

+ +

+ Categories +

+ +
    + <% product.categories.each do |category| %> +
  • <%= category.name %>
  • + <% end %> +
<%end%> diff --git a/db/seeds.rb b/db/seeds.rb index 23f0682148..1c999d6d50 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -22,7 +22,7 @@ photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg" }, { - name: "penguin hawaiin shirt", + name: "penguin hawaiian shirt", description: "For the penguin who wants to get away", stock: 4, price: 2000, @@ -36,7 +36,7 @@ photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png" }, { - name: "pigglet dance outfit", + name: "piglet dance outfit", description: "For the lil piggies who just wanna dance!", stock: 12, price: 3050, @@ -44,7 +44,7 @@ }, { name: "Baby Bunny outfit", - description: "Are you a new mommy? Dress you little hopper in style!", + description: "Are you a new mommy? Dress your little hopper in style!", stock: 4, price: 1900, photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg" @@ -54,3 +54,26 @@ product.each do |item| Product.create(item) end + +categories = ["cat", "pants", "llama", "scarves", "penguin", "shirts", "hats", "dogs", "pig", "tutu", "baby", "bunny"] + +categories.each do |category| + Category.create(name: category) +end + +all_categories = Category.all +all_products = Product.all + + +# NOTE: The code below only works for the seed! My seed data has 2 categories per product in the seed. If this seed +# data changes, then this code WILL NOT WORK. +i = 0 +j = 0 +until i == all_products.size + product = all_products[i] + all_products[i].categories << all_categories[j] + j += 1 + all_products[i].categories << all_categories[j] + i+=1 + j += 1 +end diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb new file mode 100644 index 0000000000..12ca7c1c06 --- /dev/null +++ b/test/controllers/categories_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CategoriesControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end From 26ade11ee01ee37e068d733f832db7e59f2b4329 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 20 Oct 2016 14:27:22 -0700 Subject: [PATCH 032/144] WIP Product / Category tests --- app/models/category.rb | 1 + app/models/merchant.rb | 1 + test/fixtures/categories.yml | 17 ++++++----------- test/models/category_test.rb | 4 +--- test/models/product_test.rb | 20 ++++++++++---------- 5 files changed, 19 insertions(+), 24 deletions(-) diff --git a/app/models/category.rb b/app/models/category.rb index 7f018cd725..ea38f95005 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,3 +1,4 @@ class Category < ActiveRecord::Base has_and_belongs_to_many :products + end diff --git a/app/models/merchant.rb b/app/models/merchant.rb index 101c02c85a..0c4b55bc54 100644 --- a/app/models/merchant.rb +++ b/app/models/merchant.rb @@ -1,5 +1,6 @@ class Merchant < ActiveRecord::Base validates :email, :user_name, :uid, :provider, presence: true + has_many :products def self.build_from_github(auth_hash) merchant = Merchant.new diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml index 937a0c002e..662436aebe 100644 --- a/test/fixtures/categories.yml +++ b/test/fixtures/categories.yml @@ -1,11 +1,6 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value +shoes: + name: shoes +ducks: + name: ducks +hats: + name: hats diff --git a/test/models/category_test.rb b/test/models/category_test.rb index 4733541516..3b9edf75cc 100644 --- a/test/models/category_test.rb +++ b/test/models/category_test.rb @@ -1,7 +1,5 @@ require 'test_helper' class CategoryTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end + end diff --git a/test/models/product_test.rb b/test/models/product_test.rb index 869ce5c1db..90fbe23328 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -55,16 +55,16 @@ class ProductTest < ActiveSupport::TestCase end end - # test "Product can be assigned a merchant id" do - # product = Product.create!(name: "mouse hat", price: 1240) - # merchant = Merchant.create!(user_name: "testing", email: "test@test.com") - # - # product.merchant = merchant - # assert product.save - # - # assert_equal product.merchant_id, merchant.id - # assert_includes merchant.products, product - # end + test "Product can be assigned a merchant id" do + product = Product.create!(name: "mouse hat", price: 1240) + merchant = Merchant.create!(user_name: "testing", email: "test@test.com", uid: 124, provider: "github") + + product.merchant = merchant + assert product.save + + assert_equal product.merchant_id, merchant.id + assert_includes merchant.products, product + end # test "Product can have many categories" do # # INSERT TEST HERE From 8530855a6b220aa0b8add557525b318a9e6f9d32 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 20 Oct 2016 14:34:09 -0700 Subject: [PATCH 033/144] created (and ran) migration, adding an active? column [boolean datatype] into the products table so newly created products are active by default, and we have a space in the database that will hold this active status, so that merchants can 'retire' their products if they choose. The retire code will come later. --- ...20161020212452_add_active_column_into_products_table.rb | 5 +++++ db/schema.rb | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20161020212452_add_active_column_into_products_table.rb diff --git a/db/migrate/20161020212452_add_active_column_into_products_table.rb b/db/migrate/20161020212452_add_active_column_into_products_table.rb new file mode 100644 index 0000000000..45ac8c9bfa --- /dev/null +++ b/db/migrate/20161020212452_add_active_column_into_products_table.rb @@ -0,0 +1,5 @@ +class AddActiveColumnIntoProductsTable < ActiveRecord::Migration + def change + add_column(:products, :active?, :boolean, :default => true) + end +end diff --git a/db/schema.rb b/db/schema.rb index 9ca4ea1782..463d9eb7df 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161019205504) do +ActiveRecord::Schema.define(version: 20161020212452) do create_table "categories", force: :cascade do |t| t.string "name" @@ -58,8 +58,9 @@ t.integer "price" t.string "photo_url" t.integer "merchant_id" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.boolean "active?", default: true end add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" From d0af4a09f2fe4645cc7fff0a64ce719b8df92077 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 20 Oct 2016 15:26:22 -0700 Subject: [PATCH 034/144] created (and ran) migration adding total column back into the order table. Yeni was correct! This is needed for cart functionality. --- ...oops_adding_total_back_into_order_table_yeni_is_wise.rb | 5 +++++ db/schema.rb | 7 ++++--- 2 files changed, 9 insertions(+), 3 deletions(-) create mode 100644 db/migrate/20161020222236_oops_adding_total_back_into_order_table_yeni_is_wise.rb diff --git a/db/migrate/20161020222236_oops_adding_total_back_into_order_table_yeni_is_wise.rb b/db/migrate/20161020222236_oops_adding_total_back_into_order_table_yeni_is_wise.rb new file mode 100644 index 0000000000..017e951f4f --- /dev/null +++ b/db/migrate/20161020222236_oops_adding_total_back_into_order_table_yeni_is_wise.rb @@ -0,0 +1,5 @@ +class OopsAddingTotalBackIntoOrderTableYeniIsWise < ActiveRecord::Migration + def change + add_column(:orders, :total, :integer, :default => 0) + end +end diff --git a/db/schema.rb b/db/schema.rb index 463d9eb7df..9e858bfbe1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161020212452) do +ActiveRecord::Schema.define(version: 20161020222236) do create_table "categories", force: :cascade do |t| t.string "name" @@ -47,8 +47,9 @@ t.integer "cc_exp_year" t.integer "cc_exp_month" t.integer "billing_zip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "total", default: 0 end create_table "products", force: :cascade do |t| From 0d4fee257c4684b6aaa62f8bd17c91c860051b0a Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 20 Oct 2016 15:29:12 -0700 Subject: [PATCH 035/144] CART set-up: updated order model --- app/models/order.rb | 51 +++++++++++++++++++++++++++++++++------------ 1 file changed, 38 insertions(+), 13 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index 9adfb3ab8e..0ea89d0aa0 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,15 +1,40 @@ class Order < ActiveRecord::Base - has_many :order_items - validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 16 } - validates :cc_exp_year, presence: true, length: {is: 4} - validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} - validate :valid_exp - - def valid_exp - if cc_exp_year < Time.now.year - errors.add(:cc_exp_year, "Card year is expired") - elsif cc_exp_year == Time.now.year && cc_exp_month < Time.now.month - errors.add(:cc_exp_month, "Card month is expired") - end - end + has_many :order_items + validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 16 } + validates :cc_exp_year, presence: true, length: {is: 4} + validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} + validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} + validate :valid_exp + validates_associated :order_items + validate :acceptable_status + before_create :set_order_status + before_save :update_total + + def valid_exp + if cc_exp_year < Time.now.year + errors.add(:cc_exp_year, "Card year is expired") + elsif cc_exp_year == Time.now.year && cc_exp_month < Time.now.month + errors.add(:cc_exp_month, "Card month is expired") + end + end + + def acceptable_status + if status != "PENDING" && status != "PAID" && status != "COMPLETE" && status != "CANCELLED" + errors.add(:status, "Must be PENDING, PAID, COMPLETE or CANCELLED") + end + end + + def total + order_items.collect { |oi| oi.valid? ? (oi.quantity * Product.find(oi.product_id).price) : 0 }.sum + end + + + private + def set_order_status + self.status = "PENDING" + end + + def update_total + self[:total] = total + end end From cfc33fe03ae19a76cb8f18d7616d9b7246352bd5 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 20 Oct 2016 18:53:10 -0700 Subject: [PATCH 036/144] finished tests for Product model --- test/fixtures/categories.yml | 14 ++++++++------ test/fixtures/products.yml | 2 ++ test/models/product_test.rb | 14 ++++++++++---- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml index 662436aebe..3a7f156d98 100644 --- a/test/fixtures/categories.yml +++ b/test/fixtures/categories.yml @@ -1,6 +1,8 @@ -shoes: - name: shoes -ducks: - name: ducks -hats: - name: hats +cat: + name: cat +formal_wear: + name: formal wear +hamster: + name: hamster +eyewear: + name: eyewear diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index 88dccbd403..6f1f89c783 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -4,6 +4,7 @@ cat_suit: description: your cat will look dapper in this one-piece suit! stock: 4 photo_url: http://placekitten.com/200/300 + categories: cat, formal_wear hamster_monocle: name: hamster monocle @@ -11,3 +12,4 @@ hamster_monocle: description: a classy way to correct your hamster's vision. stock: 20 photo_url: http://placekitten.com/200/300 + categories: hamster, eyewear diff --git a/test/models/product_test.rb b/test/models/product_test.rb index 90fbe23328..fa4a09991a 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -42,7 +42,8 @@ class ProductTest < ActiveSupport::TestCase end test "create Product with valid data" do - product = Product.new(name: "penguin bowtie", price: 2150) + product = products(:cat_suit) + assert product.valid? assert_not_nil product.name assert_not_nil product.price @@ -66,7 +67,12 @@ class ProductTest < ActiveSupport::TestCase assert_includes merchant.products, product end - # test "Product can have many categories" do - # # INSERT TEST HERE - # end + test "Product can have many categories" do + product = products(:hamster_monocle) + category_one = categories(:hamster) + category_two = categories(:eyewear) + assert_equal 2, product.categories.length + assert_includes product.category_ids, category_one.id + assert_includes product.category_ids, category_two.id + end end From 283274d39769f3259be0889c8bc4fad04cfdd28c Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 20 Oct 2016 19:06:57 -0700 Subject: [PATCH 037/144] wrote tests for and implemented Category model --- app/models/category.rb | 2 +- test/models/category_test.rb | 22 +++++++++++++++++++++- test/models/product_test.rb | 1 + 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/app/models/category.rb b/app/models/category.rb index ea38f95005..77f7f99af7 100644 --- a/app/models/category.rb +++ b/app/models/category.rb @@ -1,4 +1,4 @@ class Category < ActiveRecord::Base has_and_belongs_to_many :products - + validates :name, presence: true, uniqueness: true end diff --git a/test/models/category_test.rb b/test/models/category_test.rb index 3b9edf75cc..a068b01dda 100644 --- a/test/models/category_test.rb +++ b/test/models/category_test.rb @@ -1,5 +1,25 @@ require 'test_helper' class CategoryTest < ActiveSupport::TestCase - + test "category can't have an empty name" do + category = Category.new + assert_not category.valid? + assert_not category.save + assert_includes category.errors, :name + end + + test "category is valid if it has a name" do + categories.each do |category| + category = Category.create!(name: category.name) + assert category.valid? + assert_not_nil category.name + end + end + + test "category name must be unique" do + category = Category.new(name: "formal wear") + assert_not category.valid? + assert_not category.save + assert_equal ["has already been taken"], category.errors.messages[:name] + end end diff --git a/test/models/product_test.rb b/test/models/product_test.rb index fa4a09991a..3f87ceeaed 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -18,6 +18,7 @@ class ProductTest < ActiveSupport::TestCase assert_not product.valid? assert_not product.save assert_includes product.errors, :name + assert_equal ["has already been taken"], product.errors.messages[:name] end test "Price must be an integer" do From ea1158cb4390a721adc3e9999ec5d7d0249aa0a7 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 20 Oct 2016 20:27:05 -0700 Subject: [PATCH 038/144] created (and ran) migrations to add two columns to the order items table, unit price and total price. This is needed so that the price when a customer adds something to their cart will not change if the merchant decides to edit that product's price prior to the customer making their final purchase. --- .../20161020231651_add_unit_price_to_order_items_table.rb | 6 ++++++ ...161021032202_rename_total_column_in_order_items_table.rb | 5 +++++ db/schema.rb | 4 +++- 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20161020231651_add_unit_price_to_order_items_table.rb create mode 100644 db/migrate/20161021032202_rename_total_column_in_order_items_table.rb diff --git a/db/migrate/20161020231651_add_unit_price_to_order_items_table.rb b/db/migrate/20161020231651_add_unit_price_to_order_items_table.rb new file mode 100644 index 0000000000..d4cb319224 --- /dev/null +++ b/db/migrate/20161020231651_add_unit_price_to_order_items_table.rb @@ -0,0 +1,6 @@ +class AddUnitPriceToOrderItemsTable < ActiveRecord::Migration + def change + add_column(:order_items, :unit_price, :integer) + add_column(:order_items, :total, :integer) + end +end diff --git a/db/migrate/20161021032202_rename_total_column_in_order_items_table.rb b/db/migrate/20161021032202_rename_total_column_in_order_items_table.rb new file mode 100644 index 0000000000..ed5397c41d --- /dev/null +++ b/db/migrate/20161021032202_rename_total_column_in_order_items_table.rb @@ -0,0 +1,5 @@ +class RenameTotalColumnInOrderItemsTable < ActiveRecord::Migration + def change + rename_column(:order_items, :total, :total_price) + end +end diff --git a/db/schema.rb b/db/schema.rb index 9e858bfbe1..4eeb4e2991 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161020222236) do +ActiveRecord::Schema.define(version: 20161020231651) do create_table "categories", force: :cascade do |t| t.string "name" @@ -35,6 +35,8 @@ t.boolean "shipped?", default: false t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "unit_price" + t.integer "total" end create_table "orders", force: :cascade do |t| From e1849d0cfdc216fd509eb0e0f283a02913788d1c Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 20 Oct 2016 20:27:30 -0700 Subject: [PATCH 039/144] forgot to add schema to the previous commit --- db/schema.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index 4eeb4e2991..b0346accdb 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161020231651) do +ActiveRecord::Schema.define(version: 20161021032202) do create_table "categories", force: :cascade do |t| t.string "name" @@ -29,14 +29,14 @@ end create_table "order_items", force: :cascade do |t| - t.integer "quantity", default: 1 + t.integer "quantity", default: 1 t.integer "product_id" t.integer "order_id" - t.boolean "shipped?", default: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.boolean "shipped?", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "unit_price" - t.integer "total" + t.integer "total_price" end create_table "orders", force: :cascade do |t| From 7e78a496e7e492edc963ce17e46cc517a7505399 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 20 Oct 2016 21:23:35 -0700 Subject: [PATCH 040/144] added test to merchant --- app/models/merchant.rb | 5 +++-- test/models/merchant_test.rb | 12 ++++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/app/models/merchant.rb b/app/models/merchant.rb index 101c02c85a..35484eb289 100644 --- a/app/models/merchant.rb +++ b/app/models/merchant.rb @@ -2,11 +2,12 @@ class Merchant < ActiveRecord::Base validates :email, :user_name, :uid, :provider, presence: true def self.build_from_github(auth_hash) + merchant = Merchant.new merchant.uid = auth_hash[:uid] merchant.provider = 'github' - merchant.user_name = auth_hash['info']['nickname'] - merchant.email = auth_hash['info']['email'] + merchant.user_name = auth_hash[:info]['nickname'] + merchant.email = auth_hash[:info]['email'] return merchant end end diff --git a/test/models/merchant_test.rb b/test/models/merchant_test.rb index 877bea082b..0af2333c8f 100644 --- a/test/models/merchant_test.rb +++ b/test/models/merchant_test.rb @@ -9,11 +9,11 @@ class MerchantTest < ActiveSupport::TestCase assert_not merchant.valid? end - # test "the provider must be from github" do - # git_hash = {user_name: "kitty", email: "abc@aol.com", uid: 15, provider: "twitter"} - # merchant = Merchant.build_from_github(git_hash) - # assert_not merchant.valid? - # - # end + test "the Merchant will not be valid without an email" do + git_hash = {uid: 15, provider: "twitter", + info: {nickname: 'kitty'}} + merchant = Merchant.build_from_github(git_hash) + assert_not merchant.valid? + end end From 2bd7f597d586ac41c7e177001d5f8fbd6d6ae941 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Fri, 21 Oct 2016 08:35:05 -0700 Subject: [PATCH 041/144] modified views for Products, modified seeds to test Category show pages --- app/controllers/categories_controller.rb | 8 +++++++ app/views/categories/show.html.erb | 11 +++++++++ app/views/products/index.html.erb | 16 +++---------- app/views/products/show.html.erb | 20 +++++++++++++++- db/seeds.rb | 13 ++++++++++- .../controllers/categories_controller_test.rb | 23 ++++++++++++++++--- 6 files changed, 73 insertions(+), 18 deletions(-) create mode 100644 app/views/categories/show.html.erb diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index a14959525a..5ee0e80115 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -1,2 +1,10 @@ class CategoriesController < ApplicationController + def show + begin + @category = Category.find(params[:id]) + @products = @category.products + rescue ActiveRecord::RecordNotFound => err + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found + end + end end diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb new file mode 100644 index 0000000000..054e874b66 --- /dev/null +++ b/app/views/categories/show.html.erb @@ -0,0 +1,11 @@ +

<%= @category.name.capitalize %>

+<% if @products.empty? %> +

+ No products in this category! +

+<% end %> + +<% @products.each do |product| %> +

<%= product.name.capitalize %>

+ <%= link_to image_tag("#{product.photo_url}"), product_path(product) %> +<% end %> diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index e5daef4b33..110577de92 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,9 +1,9 @@ -

All Products Here

+

All Products

<%@product.each do |product|%>

- <%= product.name %> + <%= link_to product.name, product_path(product) %>

<%= image_tag "#{product.photo_url}"%> @@ -11,15 +11,5 @@

<%= product.description %>

- -

- Categories -

- -
    - <% product.categories.each do |category| %> -
  • <%= category.name %>
  • - <% end %> -
-
+
<%end%> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 6dffa8bf1a..235fc1d109 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1 +1,19 @@ -

Show single product here

+

<%= @product.name.capitalize %>

+
+

+ <%= image_tag "#{@product.photo_url}" %> +

+ +

+ Description: <%= @product.description %> +

+ +<% unless @product.categories.empty? %> +

Categories

+
    + <% @product.categories.each do |category| %> +
  • <%= link_to "#{category.name}", category_path(category) %>
  • + <% end %> +
+<% end %> +
diff --git a/db/seeds.rb b/db/seeds.rb index 1c999d6d50..3e70288c89 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -55,7 +55,7 @@ Product.create(item) end -categories = ["cat", "pants", "llama", "scarves", "penguin", "shirts", "hats", "dogs", "pig", "tutu", "baby", "bunny"] +categories = ["cat", "pants", "llama", "scarves", "penguin", "shirts", "hats", "dogs", "pig", "tutu", "baby", "bunny", "mammals", "birds"] categories.each do |category| Category.create(name: category) @@ -77,3 +77,14 @@ i+=1 j += 1 end + +birds = Category.find_by(name: "birds") +mammals = Category.find_by(name: "mammals") + +all_products.each do |item| + if item.name == "penguin hawaiian shirt" + item.categories << birds + else + item.categories << mammals + end +end diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb index 12ca7c1c06..159fac6006 100644 --- a/test/controllers/categories_controller_test.rb +++ b/test/controllers/categories_controller_test.rb @@ -1,7 +1,24 @@ require 'test_helper' class CategoriesControllerTest < ActionController::TestCase - # test "the truth" do - # assert true - # end + test "show page should show the proper category" do + cat_id = categories(:cat).id + get :show, {id: cat_id} + assert_response :success + assert_template :show + + category = assigns(:category) + assert_not_nil category + assert_equal category.id, cat_id + end + + test "can't show a category that doesn't exist" do + cat_id = 12345 + assert_raises ActiveRecord::RecordNotFound do + Category.find(cat_id) + end + + get :show, {id: cat_id} + assert_response :not_found + end end From 90f2278c085fcb19e431392acd296a4fd345c2db Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 21 Oct 2016 13:09:53 -0700 Subject: [PATCH 042/144] tried to implement all of the guidance website's code, customizing pieces to our structure. This didn't seem to work, so will begin refactoring wiht Shari --- app/assets/javascripts/carts.coffee | 3 ++ app/assets/javascripts/order_items.coffee | 3 ++ app/assets/stylesheets/carts.scss | 3 ++ app/assets/stylesheets/order_items.scss | 3 ++ app/controllers/application_controller.rb | 15 +++++++-- app/controllers/carts_controller.rb | 5 +++ app/controllers/order_items_controller.rb | 26 ++++++++++++++++ app/controllers/products_controller.rb | 3 +- app/helpers/carts_helper.rb | 2 ++ app/helpers/order_items_helper.rb | 2 ++ app/models/order.rb | 5 +-- app/models/order_item.rb | 19 +++++++++--- app/models/product.rb | 13 +++++--- app/views/carts/_cart_row.html.erb | 28 +++++++++++++++++ app/views/carts/_shopping_cart.html.erb | 18 +++++++++++ app/views/carts/show.html.erb | 3 ++ app/views/layouts/_cart_text.html.erb | 1 + app/views/layouts/application.html.erb | 31 ++++++++++++++----- app/views/order_items/create.js.erb | 5 +++ app/views/order_items/destroy.js.erb | 2 ++ app/views/order_items/update.js.erb | 2 ++ app/views/products/_product_row.html.erb | 22 +++++++++++++ app/views/products/index.html.erb | 26 +++++++++++----- config/routes.rb | 2 ++ test/controllers/carts_controller_test.rb | 7 +++++ .../order_items_controller_test.rb | 7 +++++ test/fixtures/order_items.yml | 2 +- test/models/order_item_test.rb | 4 +-- 28 files changed, 228 insertions(+), 34 deletions(-) create mode 100644 app/assets/javascripts/carts.coffee create mode 100644 app/assets/javascripts/order_items.coffee create mode 100644 app/assets/stylesheets/carts.scss create mode 100644 app/assets/stylesheets/order_items.scss create mode 100644 app/controllers/carts_controller.rb create mode 100644 app/controllers/order_items_controller.rb create mode 100644 app/helpers/carts_helper.rb create mode 100644 app/helpers/order_items_helper.rb create mode 100644 app/views/carts/_cart_row.html.erb create mode 100644 app/views/carts/_shopping_cart.html.erb create mode 100644 app/views/carts/show.html.erb create mode 100644 app/views/layouts/_cart_text.html.erb create mode 100644 app/views/order_items/create.js.erb create mode 100644 app/views/order_items/destroy.js.erb create mode 100644 app/views/order_items/update.js.erb create mode 100644 app/views/products/_product_row.html.erb create mode 100644 test/controllers/carts_controller_test.rb create mode 100644 test/controllers/order_items_controller_test.rb diff --git a/app/assets/javascripts/carts.coffee b/app/assets/javascripts/carts.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/carts.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/javascripts/order_items.coffee b/app/assets/javascripts/order_items.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/order_items.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/carts.scss b/app/assets/stylesheets/carts.scss new file mode 100644 index 0000000000..62647c9dde --- /dev/null +++ b/app/assets/stylesheets/carts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the carts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/order_items.scss b/app/assets/stylesheets/order_items.scss new file mode 100644 index 0000000000..584862de9b --- /dev/null +++ b/app/assets/stylesheets/order_items.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the OrderItems controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e1b9..c4d9355895 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,14 @@ class ApplicationController < ActionController::Base - # Prevent CSRF attacks by raising an exception. - # For APIs, you may want to use :null_session instead. - protect_from_forgery with: :exception + # Prevent CSRF attacks by raising an exception. + # For APIs, you may want to use :null_session instead. + protect_from_forgery with: :exception + helper_method :current_order + + def current_order + if !session[:order_id].nil? + Order.find(session[:order_id]) + else + Order.new + end + end end diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb new file mode 100644 index 0000000000..82507d4df7 --- /dev/null +++ b/app/controllers/carts_controller.rb @@ -0,0 +1,5 @@ +class CartsController < ApplicationController + def show + @order_items = current_order.order_items + end +end diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb new file mode 100644 index 0000000000..f3f00ba416 --- /dev/null +++ b/app/controllers/order_items_controller.rb @@ -0,0 +1,26 @@ +class OrderItemsController < ApplicationController + def create + @order = current_order + @order_item = @order.order_items.new(order_item_params) + @order.save + session[:order_id] = @order.id + end + + def update + @order = current_order + @order_item = @order.order_items.find(params[:id]) + @order_item.update_attributes(order_item_params) + @order_items = @order.order_items + end + + def destroy + @order = current_order + @order_item = @order.order_items.find(params[:id]) + @order_item.destroy + @order_items = @order.order_items + end + private + def order_item_params + params.require(:order_item).permit(:quantity, :product_id) + end +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 93fa2cf387..1b554c24ca 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,7 +1,8 @@ class ProductsController < ApplicationController def index - @product = Product.all + @products = Product.all + @order_item = current_order.order_items.new end def show diff --git a/app/helpers/carts_helper.rb b/app/helpers/carts_helper.rb new file mode 100644 index 0000000000..d99c380cb5 --- /dev/null +++ b/app/helpers/carts_helper.rb @@ -0,0 +1,2 @@ +module CartsHelper +end diff --git a/app/helpers/order_items_helper.rb b/app/helpers/order_items_helper.rb new file mode 100644 index 0000000000..e197528ae1 --- /dev/null +++ b/app/helpers/order_items_helper.rb @@ -0,0 +1,2 @@ +module OrderItemsHelper +end diff --git a/app/models/order.rb b/app/models/order.rb index 0ea89d0aa0..13175c661c 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,5 +1,6 @@ class Order < ActiveRecord::Base has_many :order_items + validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 16 } validates :cc_exp_year, presence: true, length: {is: 4} validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} @@ -7,6 +8,7 @@ class Order < ActiveRecord::Base validate :valid_exp validates_associated :order_items validate :acceptable_status + before_create :set_order_status before_save :update_total @@ -25,10 +27,9 @@ def acceptable_status end def total - order_items.collect { |oi| oi.valid? ? (oi.quantity * Product.find(oi.product_id).price) : 0 }.sum + order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.product.price) : 0 }.sum end - private def set_order_status self.status = "PENDING" diff --git a/app/models/order_item.rb b/app/models/order_item.rb index 6aaa4fbdb4..665c4955ae 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -1,12 +1,21 @@ class OrderItem < ActiveRecord::Base + belongs_to :product + belongs_to :order + validates :quantity, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 } - # less_than_or_equal_to: Product.find(self.product_id).quantity } - # I'd like this last line of code to work in the numericality hash--it's not working right now. Maybe it will work once the Product model has been created in this version of our app? - # Also, need to confirm what the quantity/stock variable is in the Product model and update the last word of this commented out code in line 3. validates :product_id, presence: true validates :order_id, presence: true validates :shipped?, inclusion: { in: [true, false] } + validate :valid_quantity - belongs_to :product - belongs_to :order + def total_price + self.product.price * quantity + end + + private + def valid_quantity + if quantity > product.stock + errors.add(:quantity, "there is not enough stock of this product to fulfill your request, please try again") + end + end end diff --git a/app/models/product.rb b/app/models/product.rb index f21abd6d59..d3b607cce2 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,7 +1,10 @@ class Product < ActiveRecord::Base - has_and_belongs_to_many :categories - has_many :orderitems - belongs_to :merchant - validates :name, presence: true, uniqueness: true - validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} + has_and_belongs_to_many :categories + has_many :orderitems + belongs_to :merchant + + validates :name, presence: true, uniqueness: true + validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} + + default_scope { where(active?: true) } end diff --git a/app/views/carts/_cart_row.html.erb b/app/views/carts/_cart_row.html.erb new file mode 100644 index 0000000000..deccf8ed76 --- /dev/null +++ b/app/views/carts/_cart_row.html.erb @@ -0,0 +1,28 @@ +
+ +
+
+

<%= product.name %>

+
+
+ + <%= form_for order_item, remote: true do |f| %> +

Unit Price: <%= number_to_currency order_item.unit_price %>

+
+
+ <%= f.number_field :quantity, value: order_item.quantity.to_i, class: "form-control", min: 1 %> + <%= f.hidden_field :product_id, value: product.id %> +
+
+
+ <%= f.submit "Update Quantity", class: "btn btn-primary" %> + <%= link_to "Delete", order_item, { data: { confirm: "Are you sure you wish to delete the product '#{ order_item.product.name }' from your cart?" }, method: :delete, remote: true, class: "btn btn-danger" } %> +
+
+
+

Total Price: <%= number_to_currency order_item.total_price %>

+ <% end %> +
+ +
+
diff --git a/app/views/carts/_shopping_cart.html.erb b/app/views/carts/_shopping_cart.html.erb new file mode 100644 index 0000000000..cadfc553cf --- /dev/null +++ b/app/views/carts/_shopping_cart.html.erb @@ -0,0 +1,18 @@ +<% if !@order_item.nil? && @order_item.errors.any? %> +
+
    + <% @order_item.errors.full_messages.each do |msg| %> +
  • <%= msg %>
  • + <% end %> +
+
+<% end %> +<% if @order_items.size == 0 %> +

+ There are no items in your shopping cart. Please <%= link_to "go back", root_path %> and add some items to your cart. +

+<% else %> + <% @order_items.each do |order_item| %> + <%= render 'carts/cart_row', product: order_item.product, order_item: order_item, show_total: true %> + <% end %> +<% end %> diff --git a/app/views/carts/show.html.erb b/app/views/carts/show.html.erb new file mode 100644 index 0000000000..82c62a8d08 --- /dev/null +++ b/app/views/carts/show.html.erb @@ -0,0 +1,3 @@ +
+ <%= render "shopping_cart" %> +
diff --git a/app/views/layouts/_cart_text.html.erb b/app/views/layouts/_cart_text.html.erb new file mode 100644 index 0000000000..89d05ac0f6 --- /dev/null +++ b/app/views/layouts/_cart_text.html.erb @@ -0,0 +1 @@ +<%= link_to "#{current_order.order_items.size} Items in Cart ( #{number_to_currency current_order.total} )", cart_path, class: "btn btn-link" %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9b70e01c2e..a50849697a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,14 +1,31 @@ - Betsy - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> - <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> - <%= csrf_meta_tags %> + Betsy + <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> + <% if request.ssl? %> + <%= stylesheet_link_tag 'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css' %> + <%= javascript_include_tag 'https://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js' %> + <% else %> + <%= stylesheet_link_tag 'http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css' %> + <%= javascript_include_tag 'http://maxcdn.bootstrapcdn.com/bootstrap/3.2.0/js/bootstrap.min.js' %> + <% end %> + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> + + <%= csrf_meta_tags %> - -<%= yield %> - +
+
+
+

<%= link_to "My Store", root_path %>

+
+
+

<%= render 'layouts/cart_text' %>

+
+
+
+ <%= yield %> +
diff --git a/app/views/order_items/create.js.erb b/app/views/order_items/create.js.erb new file mode 100644 index 0000000000..dbc113b962 --- /dev/null +++ b/app/views/order_items/create.js.erb @@ -0,0 +1,5 @@ +<% if @order.errors.any? || @order_item.errors.any? %> +alert("not valid.") +<% else %> +$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") +<% end %> diff --git a/app/views/order_items/destroy.js.erb b/app/views/order_items/destroy.js.erb new file mode 100644 index 0000000000..d5038cd48e --- /dev/null +++ b/app/views/order_items/destroy.js.erb @@ -0,0 +1,2 @@ +$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") +$(".shopping-cart").html("<%= escape_javascript(render 'carts/shopping_cart') %>") diff --git a/app/views/order_items/update.js.erb b/app/views/order_items/update.js.erb new file mode 100644 index 0000000000..d5038cd48e --- /dev/null +++ b/app/views/order_items/update.js.erb @@ -0,0 +1,2 @@ +$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") +$(".shopping-cart").html("<%= escape_javascript(render 'carts/shopping_cart') %>") diff --git a/app/views/products/_product_row.html.erb b/app/views/products/_product_row.html.erb new file mode 100644 index 0000000000..dd73d4f7b2 --- /dev/null +++ b/app/views/products/_product_row.html.erb @@ -0,0 +1,22 @@ +
+ +
+
+

<%= product.name %>

+
+
+ + <%= form_for @order_item, remote: true do |f| %> +

Unit Price: <%= number_to_currency product.price %>

+
+ <%= f.number_field :quantity, value: 1, class: "form-control", min: 1 %> +
+ <%= f.hidden_field :product_id, value: product.id %> + <%= f.submit "Add to Cart", class: "btn btn-primary" %> +
+
+ <% end %> +
+ +
+
diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 97217e5c32..7096af2f87 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,9 +1,19 @@ -

All Products Here

+ -<%@product.each do |product|%> -
- <%= product.name %> - <%= image_tag "#{product.photo_url}"%> - <%= product.description %> -
-<%end%> +<%#@products.each do |product|%> + + <%# product.name %> + <%# image_tag "#{product.photo_url}"%> + <%# product.description %> + +<%#end%> + +

WebTutorial -- Products for Sale

+ +
+
+ <% @products.each do |product| %> + <%= render "product_row", product: product, order_item: @order_item %> + <% end %> +
+
diff --git a/config/routes.rb b/config/routes.rb index 4fe790456b..aff59f062c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,6 +16,8 @@ resources :categories, only: [:new, :create, :show] + resource :cart, only: [:show] + # We're going to talk about this more if any of us needs to edit this. :) resources :orders, only: [:new, :create, :show] do resources :order_items, except: [:index, :show] diff --git a/test/controllers/carts_controller_test.rb b/test/controllers/carts_controller_test.rb new file mode 100644 index 0000000000..1e748b83e3 --- /dev/null +++ b/test/controllers/carts_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CartsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/order_items_controller_test.rb b/test/controllers/order_items_controller_test.rb new file mode 100644 index 0000000000..2969f1a158 --- /dev/null +++ b/test/controllers/order_items_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class OrderItemsControllerTest < ActionController::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml index 3d983a3d27..3060530e6f 100644 --- a/test/fixtures/order_items.yml +++ b/test/fixtures/order_items.yml @@ -2,7 +2,7 @@ one_unit: quantity: 1 product_id: 123 order_id: 456 - shipped?: false + shipped?: true four_unit: quantity: 4 product_id: 123 diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb index ac7973fe02..1b6fc10c2c 100644 --- a/test/models/order_item_test.rb +++ b/test/models/order_item_test.rb @@ -31,7 +31,7 @@ class OrderItemTest < ActiveSupport::TestCase assert_not no_product_id.valid? assert_includes(no_product_id.errors, :product_id) - assert order_items(:one_unit).valid? + assert_equal(123, order_items(:one_unit)) assert order_items(:four_unit).valid? end @@ -44,7 +44,7 @@ class OrderItemTest < ActiveSupport::TestCase assert order_items(:four_unit).valid? end - test "Creating an order item instantiate :shipped? as false" do + test "Creating an order item instantiates :shipped? as false" do o = OrderItem.new assert_equal(false, o.shipped?) end From 7d845ddec095529d84b1c0a70eefafd0a5672430 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Fri, 21 Oct 2016 13:51:17 -0700 Subject: [PATCH 043/144] stubbed out necessary product controllers --- app/controllers/products_controller.rb | 29 ++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 93fa2cf387..0eab65a23e 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,5 +1,16 @@ class ProductsController < ApplicationController +# products products#index +# merchant_products products#index +# " " products#create +# new_merchant_product products#new +# edit_merchant_product products#edit +# product products#show +# merchant_product products#show +# " " products#update +# " " products#update +# " " products#destroy + def index @product = Product.all end @@ -7,6 +18,24 @@ def index def show @product = Product.find(params[:id]) end + +# should be limited to merchants + # def new + # end + # + # def create + # end + # + # + # def edit + # end + # + # def update + # end + # + # def destroy + # end + private def product_params From 30402bc5e27c6b92b7553b5541ecab0fb9827688 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Fri, 21 Oct 2016 15:01:44 -0700 Subject: [PATCH 044/144] cleaned up views and product tests --- app/models/product.rb | 2 +- app/views/categories/show.html.erb | 8 ++++---- app/views/products/show.html.erb | 7 ++++++- test/models/product_test.rb | 7 +++++++ 4 files changed, 18 insertions(+), 6 deletions(-) diff --git a/app/models/product.rb b/app/models/product.rb index f21abd6d59..70ea3cc125 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,6 +1,6 @@ class Product < ActiveRecord::Base has_and_belongs_to_many :categories - has_many :orderitems + has_many :order_items belongs_to :merchant validates :name, presence: true, uniqueness: true validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb index 054e874b66..277fadf86e 100644 --- a/app/views/categories/show.html.erb +++ b/app/views/categories/show.html.erb @@ -1,11 +1,11 @@

<%= @category.name.capitalize %>

<% if @products.empty? %> -

- No products in this category! -

+

+ No products in this category! +

<% end %> <% @products.each do |product| %>

<%= product.name.capitalize %>

- <%= link_to image_tag("#{product.photo_url}"), product_path(product) %> + <%= link_to image_tag("#{product.photo_url}"), product_path(product) %> <% end %> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 235fc1d109..33131fef37 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -8,10 +8,15 @@ Description: <%= @product.description %>

+

+ + Price: $<%= @product.price / 100.00 %> +

+ <% unless @product.categories.empty? %>

Categories

    - <% @product.categories.each do |category| %> + <% @product.categories.each do |category| %>
  • <%= link_to "#{category.name}", category_path(category) %>
  • <% end %>
diff --git a/test/models/product_test.rb b/test/models/product_test.rb index 3f87ceeaed..b73caabd80 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -76,4 +76,11 @@ class ProductTest < ActiveSupport::TestCase assert_includes product.category_ids, category_one.id assert_includes product.category_ids, category_two.id end + + # test "Products can have many order_items" do + # product = Product.create!(name: "mouse hat", price: 1240) + # order_item = OrderItem.create!(quantity: 1, product_id: product.id, order_id: 1294, shipped?: false) + # + # assert_equal order_item.product_id, product.id + # end end From a992011f02bfb199fbd385bb2abde53bf3f8f23c Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 21 Oct 2016 15:06:38 -0700 Subject: [PATCH 045/144] started adding tests for order --- test/models/order_test.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/test/models/order_test.rb b/test/models/order_test.rb index 53a53e3a38..cab804e7a1 100644 --- a/test/models/order_test.rb +++ b/test/models/order_test.rb @@ -21,4 +21,5 @@ class OrderTest < ActiveSupport::TestCase #this assertion should not pass since month is in the past, month is not yet linked to year, working on it assert_not order3.valid? end + end From dd00a914ec46502f9f3b17eca06c9ec1519c1638 Mon Sep 17 00:00:00 2001 From: Yeni Date: Fri, 21 Oct 2016 15:09:44 -0700 Subject: [PATCH 046/144] merged files from branch --- app/assets/stylesheets/application.css | 40 ++++++++++++-------- app/controllers/sessions_controller.rb | 23 ++++++++--- app/views/home/index.html.erb | 3 +- app/views/layouts/application.html.erb | 13 ++++++- app/views/sessions/index.html.erb | 4 +- app/views/sessions/login_failure.html.erb | 2 +- test/controllers/sessions_controller_test.rb | 27 +++++++++++-- test/test_helper.rb | 2 +- 8 files changed, 82 insertions(+), 32 deletions(-) diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css index f9cd5b3483..19d085d894 100644 --- a/app/assets/stylesheets/application.css +++ b/app/assets/stylesheets/application.css @@ -1,15 +1,25 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any styles - * defined in the other CSS/SCSS files in this directory. It is generally better to create a new - * file per style scope. - * - *= require_tree . - *= require_self - */ +.nav-bar li { + display: inline; + font-family: 'Julius Sans One', sans-serif; + border-bottom: 2pt solid #F5B19A; + color: #666699; + /*previous color #39324b*/ + margin-right: 8%; + margin-left: 8%; + padding: 5px 25px; + font-size: 9pt; +} + +nav a { + color: grey; +} + +a:hover { + color: #666699; +} + +a { + color: #F5B19A; + font-family: 'Quicksand'; + text-decoration: none; +} diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 96a8907a04..1e6c83f172 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -12,16 +12,27 @@ def create @merchant = Merchant.find_by(uid: auth_hash[:uid], provider: 'github') if @merchant.nil? - # Merchant doesn't match anything in the DB. - # Attempt to create a new merchant. - @merchant = Merchant.build_from_github(auth_hash) - render :login_failure unless @merchant.save + @merchant = Merchant.build_from_github(auth_hash) + render :login_failure unless @merchant.save end - # Save the merchant ID in the session session[:merchant_id] = @merchant.id - redirect_to sessions_path + # if @merchant.nil? + # # Merchant doesn't match anything in the DB. + # # Attempt to create a new merchant. + # @merchant = Merchant.build_from_github(auth_hash) + # if @merchant.save + # redirect_to sessions_path + # else + # redirect_to login_failure_path + # end + # else + # # Save the merchant ID in the session + # session[:merchant_id] = @merchant.id + # + # redirect_to sessions_path + # end end def index diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 2f5646d339..2e53dc63fc 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -1,5 +1,4 @@ + <% if flash[:error] %>

<%= flash[:error] %>

<% end %> - -

Please <%= link_to "log in", "/auth/github" %>

diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9b70e01c2e..f890f0d524 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -5,9 +5,20 @@ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> + + - +
+

<%= @page_title %>

+ +
<%= yield %> diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb index 44df1ee082..2d0694d203 100644 --- a/app/views/sessions/index.html.erb +++ b/app/views/sessions/index.html.erb @@ -1,5 +1,3 @@ -

Merchant Portal...

+

<% @page_title= "Merchant Portal..."%>

Login successful.

Welcome <%= @merchant.email %>!

- -<%= button_to "Log out", sessions_path, method: :delete %> diff --git a/app/views/sessions/login_failure.html.erb b/app/views/sessions/login_failure.html.erb index c2c225e6c2..fbcf1eec0a 100644 --- a/app/views/sessions/login_failure.html.erb +++ b/app/views/sessions/login_failure.html.erb @@ -1,3 +1,3 @@

You failed to login.

-

Please <%= link_to "log in", "/auth/github" %>

+

Please log into your account

diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index d30ebc380a..49799a552a 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -1,7 +1,28 @@ require 'test_helper' class SessionsControllerTest < ActionController::TestCase - # test "the truth" do - # assert true - # end + def login_a_user + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github] + get :create, {provider: "github"} + end + + test "Can Create a user" do + assert_difference('Merchant.count', 1) do + login_a_user + assert_response :redirect + assert_redirected_to sessions_path + end + end + + test "If a user logs in twice it doesn't create a 2nd user" do + assert_difference('Merchant.count', 1) do + login_a_user + end + assert_no_difference('Merchant.count') do + login_a_user + assert_response :redirect + assert_redirected_to sessions_path + assert_not_nil session[:merchant_id] + end + end end diff --git a/test/test_helper.rb b/test/test_helper.rb index 8d650ce276..f782ad5465 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -13,7 +13,7 @@ def setup OmniAuth.config.test_mode = true OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ - provider: 'github', uid: '123545', info: { email: "a@b.com", name: "Ada" } + provider: 'github', uid: '123545', info: { email: "a@b.com", nickname: "Ada" } }) end # Add more helper methods to be used by all tests here... From f57160bc332b60b2bd1a2cbeb540ddac8e8013ff Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 21 Oct 2016 15:43:12 -0700 Subject: [PATCH 047/144] friday refactoring with Shari --- app/controllers/order_items_controller.rb | 23 ++++++++++++++----- app/models/product.rb | 2 +- app/views/order_items/_form.html.erb | 5 ++++ app/views/order_items/create.js.erb | 5 ---- app/views/order_items/destroy.js.erb | 2 -- app/views/order_items/edit.html.erb | 1 + app/views/order_items/update.js.erb | 2 -- .../20161021201059_refactoring_with_shari.rb | 6 +++++ db/schema.rb | 12 ++++------ 9 files changed, 35 insertions(+), 23 deletions(-) create mode 100644 app/views/order_items/_form.html.erb delete mode 100644 app/views/order_items/create.js.erb delete mode 100644 app/views/order_items/destroy.js.erb create mode 100644 app/views/order_items/edit.html.erb delete mode 100644 app/views/order_items/update.js.erb create mode 100644 db/migrate/20161021201059_refactoring_with_shari.rb diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb index f3f00ba416..8b9119674c 100644 --- a/app/controllers/order_items_controller.rb +++ b/app/controllers/order_items_controller.rb @@ -4,23 +4,34 @@ def create @order_item = @order.order_items.new(order_item_params) @order.save session[:order_id] = @order.id + redirect_to products_path end def update @order = current_order @order_item = @order.order_items.find(params[:id]) - @order_item.update_attributes(order_item_params) - @order_items = @order.order_items + if @order_item.update(order_item_params) + @order_item.save + redirect_to order_path(params[:order_id]) + else + render :edit + end end - def destroy + def edit @order = current_order @order_item = @order.order_items.find(params[:id]) - @order_item.destroy - @order_items = @order.order_items end + + def destroy + @order = current_order + @order_item = @order.order_items.find(params[:id]).destroy + redirect_to order_path(params[:order_id]) + end + + private def order_item_params - params.require(:order_item).permit(:quantity, :product_id) + params.require(:order_item).permit(:quantity, :product_id, :order_id, :shipped?) end end diff --git a/app/models/product.rb b/app/models/product.rb index d3b607cce2..9a8e453996 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,6 +1,6 @@ class Product < ActiveRecord::Base has_and_belongs_to_many :categories - has_many :orderitems + has_many :order_items belongs_to :merchant validates :name, presence: true, uniqueness: true diff --git a/app/views/order_items/_form.html.erb b/app/views/order_items/_form.html.erb new file mode 100644 index 0000000000..41eaa1a8df --- /dev/null +++ b/app/views/order_items/_form.html.erb @@ -0,0 +1,5 @@ +<%= form_for @order_item do |f| %> +<%= f.label :quantity %> +<%= f.text_field :quantity %> +<%= f.submit %> +<% end %> diff --git a/app/views/order_items/create.js.erb b/app/views/order_items/create.js.erb deleted file mode 100644 index dbc113b962..0000000000 --- a/app/views/order_items/create.js.erb +++ /dev/null @@ -1,5 +0,0 @@ -<% if @order.errors.any? || @order_item.errors.any? %> -alert("not valid.") -<% else %> -$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") -<% end %> diff --git a/app/views/order_items/destroy.js.erb b/app/views/order_items/destroy.js.erb deleted file mode 100644 index d5038cd48e..0000000000 --- a/app/views/order_items/destroy.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") -$(".shopping-cart").html("<%= escape_javascript(render 'carts/shopping_cart') %>") diff --git a/app/views/order_items/edit.html.erb b/app/views/order_items/edit.html.erb new file mode 100644 index 0000000000..d44c608b6a --- /dev/null +++ b/app/views/order_items/edit.html.erb @@ -0,0 +1 @@ +<%= render partial: "form" %> diff --git a/app/views/order_items/update.js.erb b/app/views/order_items/update.js.erb deleted file mode 100644 index d5038cd48e..0000000000 --- a/app/views/order_items/update.js.erb +++ /dev/null @@ -1,2 +0,0 @@ -$(".cart-text").html("<%= escape_javascript(render 'layouts/cart_text') %>") -$(".shopping-cart").html("<%= escape_javascript(render 'carts/shopping_cart') %>") diff --git a/db/migrate/20161021201059_refactoring_with_shari.rb b/db/migrate/20161021201059_refactoring_with_shari.rb new file mode 100644 index 0000000000..2acd10f98e --- /dev/null +++ b/db/migrate/20161021201059_refactoring_with_shari.rb @@ -0,0 +1,6 @@ +class RefactoringWithShari < ActiveRecord::Migration + def change + remove_column(:order_items, :unit_price) + remove_column(:order_items, :total_price) + end +end diff --git a/db/schema.rb b/db/schema.rb index b0346accdb..1d7c4b534b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161021032202) do +ActiveRecord::Schema.define(version: 20161021201059) do create_table "categories", force: :cascade do |t| t.string "name" @@ -29,14 +29,12 @@ end create_table "order_items", force: :cascade do |t| - t.integer "quantity", default: 1 + t.integer "quantity", default: 1 t.integer "product_id" t.integer "order_id" - t.boolean "shipped?", default: false - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.integer "unit_price" - t.integer "total_price" + t.boolean "shipped?", default: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false end create_table "orders", force: :cascade do |t| From 8a55835c0a2ac7d6221b9bf8edf37acc9e97e85b Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Sat, 22 Oct 2016 18:04:02 -0700 Subject: [PATCH 048/144] Created cart routes and put in functionality for cart in controller. Added some basic html but no tests --- app/controllers/carts_controller.rb | 31 +++++++++++++++++++++++-- app/views/carts/_cart_row.html.erb | 28 ---------------------- app/views/carts/_shopping_cart.html.erb | 18 -------------- app/views/carts/index.html.erb | 27 +++++++++++++++++++++ app/views/carts/show.html.erb | 3 --- app/views/products/index.html.erb | 5 +++- config/routes.rb | 7 +++++- db/schema.rb | 4 +--- 8 files changed, 67 insertions(+), 56 deletions(-) delete mode 100644 app/views/carts/_cart_row.html.erb delete mode 100644 app/views/carts/_shopping_cart.html.erb create mode 100644 app/views/carts/index.html.erb delete mode 100644 app/views/carts/show.html.erb diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 82507d4df7..d82514aed1 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,5 +1,32 @@ class CartsController < ApplicationController - def show - @order_items = current_order.order_items + def index + if session[:cart] + @cart= session[:cart] + else + @cart ={} + end + end + + def add_to_cart + id = params[:id] + + if session[:cart] + cart = session[:cart] + else + session[:cart] = {} + cart = session[:cart] + end + + if cart[id] + cart[id] = cart[id] + 1 + else + cart[id] = 1 + end + redirect_to carts_path + end + + def empty_cart + session[:cart] = nil + redirect_to carts_path end end diff --git a/app/views/carts/_cart_row.html.erb b/app/views/carts/_cart_row.html.erb deleted file mode 100644 index deccf8ed76..0000000000 --- a/app/views/carts/_cart_row.html.erb +++ /dev/null @@ -1,28 +0,0 @@ -
- -
-
-

<%= product.name %>

-
-
- - <%= form_for order_item, remote: true do |f| %> -

Unit Price: <%= number_to_currency order_item.unit_price %>

-
-
- <%= f.number_field :quantity, value: order_item.quantity.to_i, class: "form-control", min: 1 %> - <%= f.hidden_field :product_id, value: product.id %> -
-
-
- <%= f.submit "Update Quantity", class: "btn btn-primary" %> - <%= link_to "Delete", order_item, { data: { confirm: "Are you sure you wish to delete the product '#{ order_item.product.name }' from your cart?" }, method: :delete, remote: true, class: "btn btn-danger" } %> -
-
-
-

Total Price: <%= number_to_currency order_item.total_price %>

- <% end %> -
- -
-
diff --git a/app/views/carts/_shopping_cart.html.erb b/app/views/carts/_shopping_cart.html.erb deleted file mode 100644 index cadfc553cf..0000000000 --- a/app/views/carts/_shopping_cart.html.erb +++ /dev/null @@ -1,18 +0,0 @@ -<% if !@order_item.nil? && @order_item.errors.any? %> -
-
    - <% @order_item.errors.full_messages.each do |msg| %> -
  • <%= msg %>
  • - <% end %> -
-
-<% end %> -<% if @order_items.size == 0 %> -

- There are no items in your shopping cart. Please <%= link_to "go back", root_path %> and add some items to your cart. -

-<% else %> - <% @order_items.each do |order_item| %> - <%= render 'carts/cart_row', product: order_item.product, order_item: order_item, show_total: true %> - <% end %> -<% end %> diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb new file mode 100644 index 0000000000..12ec11df04 --- /dev/null +++ b/app/views/carts/index.html.erb @@ -0,0 +1,27 @@ + +

Your Cart

+ +


+<% total = 0 %> + + +
    + <% @cart.each do |id, quantity| %> + <% product = Product.find(id) %> +
  • +

    <%=product.name%>

    +

    <%=link_to "Learn More", product_path(id)%>

    +

    <%=number_to_currency(product.price, :unit => '$')%>

    +

    Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%>

    +
  • + <% total += quantity * product.price %> + <%end%> + <%if total != 0%> +

    Total: <%=number_to_currency(total, :unit => '$')%>

    +

    Continue shopping <%=link_to "Here", products_path %>

    +

    + <%= link_to "Empty your cart", carts_empty_cart_path%> +
+<%else%> +

Your cart is currently empty. Continue shopping <%=link_to "Here", products_path %>

+<%end%> diff --git a/app/views/carts/show.html.erb b/app/views/carts/show.html.erb deleted file mode 100644 index 82c62a8d08..0000000000 --- a/app/views/carts/show.html.erb +++ /dev/null @@ -1,3 +0,0 @@ -
- <%= render "shopping_cart" %> -
diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 110577de92..47b1115da6 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,6 +1,6 @@

All Products

-<%@product.each do |product|%> +<%@products.each do |product|%>

<%= link_to product.name, product_path(product) %> @@ -11,5 +11,8 @@

<%= product.description %>

+

+ Add to Cart +

<%end%> diff --git a/config/routes.rb b/config/routes.rb index aff59f062c..075eb8ce09 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -16,7 +16,7 @@ resources :categories, only: [:new, :create, :show] - resource :cart, only: [:show] + # We're going to talk about this more if any of us needs to edit this. :) resources :orders, only: [:new, :create, :show] do @@ -32,4 +32,9 @@ delete '/sessions', to: 'sessions#destroy' + #specific routes for the cart! + get '/carts' => 'carts#index' + get 'carts/empty_cart' =>'carts#empty_cart' + get '/carts/:id', to: 'carts#add_to_cart', as: "add_cart" + end diff --git a/db/schema.rb b/db/schema.rb index d949fdf785..9999476e2d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,9 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20161020020026) do - +ActiveRecord::Schema.define(version: 20161021201059) do create_table "categories", force: :cascade do |t| t.string "name" From 9fdff79a1996c829625253a35966bf18cedf85c5 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Sat, 22 Oct 2016 18:08:39 -0700 Subject: [PATCH 049/144] Fixed indentation --- app/controllers/carts_controller.rb | 48 ++++++++++++++--------------- 1 file changed, 24 insertions(+), 24 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index d82514aed1..e2caac6aa2 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,32 +1,32 @@ class CartsController < ApplicationController - def index - if session[:cart] - @cart= session[:cart] - else - @cart ={} - end + def index + if session[:cart] + @cart= session[:cart] + else + @cart ={} end + end - def add_to_cart - id = params[:id] + def add_to_cart + id = params[:id] - if session[:cart] - cart = session[:cart] - else - session[:cart] = {} - cart = session[:cart] - end - - if cart[id] - cart[id] = cart[id] + 1 - else - cart[id] = 1 - end - redirect_to carts_path + if session[:cart] + cart = session[:cart] + else + session[:cart] = {} + cart = session[:cart] end - def empty_cart - session[:cart] = nil - redirect_to carts_path + if cart[id] + cart[id] = cart[id] + 1 + else + cart[id] = 1 end + redirect_to carts_path + end + + def empty_cart + session[:cart] = nil + redirect_to carts_path + end end From 958a26ec2bbf876abcd7691f20393f828937f39b Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Sat, 22 Oct 2016 23:10:37 -0700 Subject: [PATCH 050/144] In Progress: Having items in cart get reduce or deleted --- app/controllers/carts_controller.rb | 16 ++++++++++++++-- app/views/carts/index.html.erb | 7 ++++++- config/routes.rb | 2 ++ test/controllers/carts_controller_test.rb | 2 ++ 4 files changed, 24 insertions(+), 3 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index e2caac6aa2..5bfee82616 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,7 +1,7 @@ class CartsController < ApplicationController def index if session[:cart] - @cart= session[:cart] + @cart = session[:cart] else @cart ={} end @@ -18,13 +18,25 @@ def add_to_cart end if cart[id] - cart[id] = cart[id] + 1 + cart[id] = cart[id] - 1 else cart[id] = 1 end redirect_to carts_path end + def sub_cart + cart = session[:cart] + cart[params[:id]] = cart[params[:id]] - 1 + redirect_to carts_path + end + + def destroy + cart = session[:cart] + cart.destroy + redirect_to carts_path + end + def empty_cart session[:cart] = nil redirect_to carts_path diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 12ec11df04..70173246b1 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -12,7 +12,12 @@

<%=product.name%>

<%=link_to "Learn More", product_path(id)%>

<%=number_to_currency(product.price, :unit => '$')%>

-

Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%>

+

Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%> + <%=link_to "Delete Item", sub_cart_path(product.id), method: :delete%> +

+

+ <%=link_to "Reduce!", sub_cart_path(product.id)%> +

<% total += quantity * product.price %> <%end%> diff --git a/config/routes.rb b/config/routes.rb index 075eb8ce09..6fe1c855a0 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -36,5 +36,7 @@ get '/carts' => 'carts#index' get 'carts/empty_cart' =>'carts#empty_cart' get '/carts/:id', to: 'carts#add_to_cart', as: "add_cart" + get '/carts/:id', to: 'carts#sub_cart', as: "sub_cart" + delete '/carts/:id', to: 'carts#destroy' end diff --git a/test/controllers/carts_controller_test.rb b/test/controllers/carts_controller_test.rb index 1e748b83e3..f3bb6a94a8 100644 --- a/test/controllers/carts_controller_test.rb +++ b/test/controllers/carts_controller_test.rb @@ -4,4 +4,6 @@ class CartsControllerTest < ActionController::TestCase # test "the truth" do # assert true # end + + end From 4e7c7275c37f0319da4167750a234627e093d006 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Sun, 23 Oct 2016 13:24:16 -0700 Subject: [PATCH 051/144] edited tests for reviews, products, and categories; wrote helper methods for displaying prices and capitalization, created reviews controller and new view, created review form partial --- app/assets/javascripts/reviews.coffee | 3 + app/assets/stylesheets/reviews.scss | 3 + app/controllers/reviews_controller.rb | 13 +++ app/helpers/application_helper.rb | 8 ++ app/helpers/reviews_helper.rb | 2 + app/models/product.rb | 1 + app/views/products/index.html.erb | 2 +- app/views/products/show.html.erb | 5 +- app/views/reviews/_form.html.erb | 34 +++++++ app/views/reviews/new.html.erb | 9 ++ test/controllers/reviews_controller_test.rb | 22 +++++ test/fixtures/categories.yml | 2 + test/fixtures/products.yml | 14 ++- test/fixtures/reviews.yml | 24 ++--- test/models/category_test.rb | 17 +++- test/models/product_test.rb | 23 +++-- test/models/review_test.rb | 104 ++++++++++---------- 17 files changed, 206 insertions(+), 80 deletions(-) create mode 100644 app/assets/javascripts/reviews.coffee create mode 100644 app/assets/stylesheets/reviews.scss create mode 100644 app/controllers/reviews_controller.rb create mode 100644 app/helpers/reviews_helper.rb create mode 100644 app/views/reviews/_form.html.erb create mode 100644 app/views/reviews/new.html.erb create mode 100644 test/controllers/reviews_controller_test.rb diff --git a/app/assets/javascripts/reviews.coffee b/app/assets/javascripts/reviews.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/reviews.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/reviews.scss b/app/assets/stylesheets/reviews.scss new file mode 100644 index 0000000000..6ea2454d26 --- /dev/null +++ b/app/assets/stylesheets/reviews.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the reviews controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 0000000000..f9aaf76d30 --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,13 @@ +class ReviewsController < ApplicationController + def new + @product = Product.find(params[:product_id]) + @review = Review.new + end + + private + + def review_params + params.require(:review).permit(:rating, :description, :product_id) + end + +end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index de6be7945c..10f0e02796 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,2 +1,10 @@ module ApplicationHelper + def capitalize_each_word(phrase) + phrase.split.map(&:capitalize).join(" ") + end + + def show_dollars(price) + price = price / 100.0 + number_to_currency(price) + end end diff --git a/app/helpers/reviews_helper.rb b/app/helpers/reviews_helper.rb new file mode 100644 index 0000000000..682b7b1abc --- /dev/null +++ b/app/helpers/reviews_helper.rb @@ -0,0 +1,2 @@ +module ReviewsHelper +end diff --git a/app/models/product.rb b/app/models/product.rb index 70ea3cc125..15cb079758 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -1,6 +1,7 @@ class Product < ActiveRecord::Base has_and_belongs_to_many :categories has_many :order_items + has_many :reviews belongs_to :merchant validates :name, presence: true, uniqueness: true validates :price, presence: true, numericality: {only_integer: true, greater_than: 0} diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 110577de92..ec98476bec 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -3,7 +3,7 @@ <%@product.each do |product|%>

- <%= link_to product.name, product_path(product) %> + <%= link_to capitalize_each_word(product.name), product_path(product) %>

<%= image_tag "#{product.photo_url}"%> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 33131fef37..7c318857e5 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1,4 +1,4 @@ -

<%= @product.name.capitalize %>

+

<%= capitalize_each_word(@product.name) %>

<%= image_tag "#{@product.photo_url}" %> @@ -9,8 +9,7 @@

- - Price: $<%= @product.price / 100.00 %> + Price: <%= show_dollars(@product.price) %>

<% unless @product.categories.empty? %> diff --git a/app/views/reviews/_form.html.erb b/app/views/reviews/_form.html.erb new file mode 100644 index 0000000000..d0cb62ab2c --- /dev/null +++ b/app/views/reviews/_form.html.erb @@ -0,0 +1,34 @@ +
+ <%= form_for @review, method: http_method, url: local_path do |f| %> + <% if @review.errors.any? %> +
+
    + <% @review.errors.each do |column, message| %> +
  • + <%= column.capitalize %> + <%= message %> +
  • + <% end %> +
+
+ <% end %> + +
+

+ <%= f.label :rating, "Rating (1 (worst) to 5 (best)): " %> +

+

+ <%= f.number_field :rating, in: 1..5, size: "30x1" %> +

+ +

+ <%= f.label :description, "Description (max 400 characters):" %> +

+

+ <%= f.text_area :description, size: "30x10" %> +

+ + <%= f.submit "Submit", class: "success button" %> + <% end %> +
+
diff --git a/app/views/reviews/new.html.erb b/app/views/reviews/new.html.erb new file mode 100644 index 0000000000..5927a092a9 --- /dev/null +++ b/app/views/reviews/new.html.erb @@ -0,0 +1,9 @@ +

Review <%= capitalize_each_word(@product.name) %>

+ +<%= render partial: "form", + locals: { + http_method: :post, + local_path: product_reviews_path + } %> + +<%= link_to "Back to Product", product_path(@product) %> diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb new file mode 100644 index 0000000000..dc867be63c --- /dev/null +++ b/test/controllers/reviews_controller_test.rb @@ -0,0 +1,22 @@ +require 'test_helper' + +class ReviewsControllerTest < ActionController::TestCase + test "should be able to view the form to add a new review" do + product = products(:cat_suit) + ex_review = reviews(:one_star) + all_reviews = product.reviews + + get :new, {product_id: product.id} + assert_response :success + assert_template :new + assert_template partial: "_form" + end + + # test "should be able to save a review to the database" do + # product = products(:cat_suit) + # end + # + # test "should not be able to save a review with empty values" do + # + # end +end diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml index 3a7f156d98..b08e755443 100644 --- a/test/fixtures/categories.yml +++ b/test/fixtures/categories.yml @@ -6,3 +6,5 @@ hamster: name: hamster eyewear: name: eyewear +mammals: + name: mammals diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index 6f1f89c783..069ea7d029 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -4,7 +4,8 @@ cat_suit: description: your cat will look dapper in this one-piece suit! stock: 4 photo_url: http://placekitten.com/200/300 - categories: cat, formal_wear + categories: cat, formal_wear, mammals + merchant: 1234 hamster_monocle: name: hamster monocle @@ -12,4 +13,13 @@ hamster_monocle: description: a classy way to correct your hamster's vision. stock: 20 photo_url: http://placekitten.com/200/300 - categories: hamster, eyewear + categories: hamster, eyewear, mammals + merchant: 1234 + +dog_bunny_ears: + name: Dog bunny ears + price: 3453 + description: make your dog look like a bunny with these adorable bunny ears! + stock: 1 + photo_url: http://placekitten.com/200/300 + merchant: 2341 diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml index 71935fdf51..ba9fb29869 100644 --- a/test/fixtures/reviews.yml +++ b/test/fixtures/reviews.yml @@ -1,16 +1,16 @@ one_star: - rating: 1 - description: This product is awesome. - product_id: 123 + rating: 1 + description: This product is terrible! + product: cat_suit four_star: - rating: 4 - description: Highly recommend purchasing this item. - product_id: 123 + rating: 4 + description: Highly recommend purchasing this item. + product: hamster_monocle five_star: - rating: 5 - description: Another description... - product_id: 123 + rating: 5 + description: Another description... + product: dog_bunny_ears character_400: - rating: 4 - description: Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf - product_id: 123 + rating: 4 + description: Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf + product: cat_suit diff --git a/test/models/category_test.rb b/test/models/category_test.rb index a068b01dda..cc4feecad5 100644 --- a/test/models/category_test.rb +++ b/test/models/category_test.rb @@ -1,14 +1,14 @@ require 'test_helper' class CategoryTest < ActiveSupport::TestCase - test "category can't have an empty name" do + test "Category can't have an empty name" do category = Category.new assert_not category.valid? assert_not category.save assert_includes category.errors, :name end - test "category is valid if it has a name" do + test "Category is valid if it has a name" do categories.each do |category| category = Category.create!(name: category.name) assert category.valid? @@ -16,10 +16,21 @@ class CategoryTest < ActiveSupport::TestCase end end - test "category name must be unique" do + test "Category name must be unique" do category = Category.new(name: "formal wear") assert_not category.valid? assert_not category.save assert_equal ["has already been taken"], category.errors.messages[:name] end + + test "Category can have many products" do + category = categories(:mammals) + product_one = products(:cat_suit) + product_two = products(:hamster_monocle) + + assert_equal 2, category.products.length + assert_includes category.product_ids, product_one.id + assert_includes category.product_ids, product_two.id + assert_respond_to category, :products + end end diff --git a/test/models/product_test.rb b/test/models/product_test.rb index b73caabd80..afe47c7246 100644 --- a/test/models/product_test.rb +++ b/test/models/product_test.rb @@ -57,7 +57,7 @@ class ProductTest < ActiveSupport::TestCase end end - test "Product can be assigned a merchant id" do + test "Product belongs to a merchant" do product = Product.create!(name: "mouse hat", price: 1240) merchant = Merchant.create!(user_name: "testing", email: "test@test.com", uid: 124, provider: "github") @@ -66,21 +66,28 @@ class ProductTest < ActiveSupport::TestCase assert_equal product.merchant_id, merchant.id assert_includes merchant.products, product + assert_respond_to product, :merchant end test "Product can have many categories" do product = products(:hamster_monocle) category_one = categories(:hamster) category_two = categories(:eyewear) - assert_equal 2, product.categories.length + category_three = categories(:mammals) + assert_equal 3, product.categories.length assert_includes product.category_ids, category_one.id assert_includes product.category_ids, category_two.id + assert_includes product.category_ids, category_three.id + assert_respond_to product, :categories end - # test "Products can have many order_items" do - # product = Product.create!(name: "mouse hat", price: 1240) - # order_item = OrderItem.create!(quantity: 1, product_id: product.id, order_id: 1294, shipped?: false) - # - # assert_equal order_item.product_id, product.id - # end + test "Products can have many order items" do + product = products(:hamster_monocle) + assert_respond_to product, :order_items + end + + test "Products can have many reviews" do + product = products(:cat_suit) + assert_respond_to product, :reviews + end end diff --git a/test/models/review_test.rb b/test/models/review_test.rb index 27a82295bd..25077ad0b9 100644 --- a/test/models/review_test.rb +++ b/test/models/review_test.rb @@ -1,68 +1,70 @@ require 'test_helper' class ReviewTest < ActiveSupport::TestCase - test "Creating a new review will instantiate rating as nil" do - r = Review.new - assert_equal(nil, r.rating) - end + test "Creating a new review will instantiate rating as nil" do + r = Review.new + assert_equal(nil, r.rating) + end - test "Cannot save a nil rating to the database" do - no_rating = Review.new(description: "whatever", product_id: 123) - assert_not no_rating.valid? - assert_includes(no_rating.errors, :rating) - end + test "Cannot save a nil rating to the database" do + no_rating = Review.new(description: "whatever", product_id: 123) + assert_not no_rating.valid? + assert_includes(no_rating.errors, :rating) + end - test "Can create reviews with ratings between 1 and 5" do - assert reviews(:one_star).valid? - assert reviews(:four_star).valid? - assert reviews(:five_star).valid? - end + test "Can create reviews with ratings between 1 and 5" do + assert reviews(:one_star).valid? + assert reviews(:four_star).valid? + assert reviews(:five_star).valid? + end - test "Can only create reviews with integer ratings" do - float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) - assert_not float_rating.valid? - assert_includes(float_rating.errors, :rating) + test "Can only create reviews with integer ratings" do + float_rating = Review.new(rating: 3.2, description: "whatever", product_id: 123) + assert_not float_rating.valid? + assert_includes(float_rating.errors, :rating) - string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) - assert_not string_rating.valid? - assert_includes(string_rating.errors, :rating) - end + string_rating = Review.new(rating: "excellent", description: "whatever", product_id: 123) + assert_not string_rating.valid? + assert_includes(string_rating.errors, :rating) + end - test "Cannot create reviews with ratings less than 1 or greater than 5" do - too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) - assert_not too_low_rating.valid? - assert_includes(too_low_rating.errors, :rating) + test "Cannot create reviews with ratings less than 1 or greater than 5" do + too_low_rating = Review.new(rating: 0, description: "whatever", product_id: 123) + assert_not too_low_rating.valid? + assert_includes(too_low_rating.errors, :rating) - too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) - assert_not too_high_rating.valid? - assert_includes(too_high_rating.errors, :rating) - end + too_high_rating = Review.new(rating: 6, description: "whatever", product_id: 123) + assert_not too_high_rating.valid? + assert_includes(too_high_rating.errors, :rating) + end - test "Review creation requires a description [string]" do - assert reviews(:one_star).valid? + test "Review creation requires a description [string]" do + assert reviews(:one_star).valid? - no_description = Review.new(rating: 0, product_id: 123) - assert_not no_description.valid? - assert_includes(no_description.errors, :description) - end + no_description = Review.new(rating: 0, product_id: 123) + assert_not no_description.valid? + assert_includes(no_description.errors, :description) + end - test "Can only create reviews with descriptions 400 characters or less" do - assert reviews(:character_400).valid? + test "Can only create reviews with descriptions 400 characters or less" do + assert reviews(:character_400).valid? - character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") - assert_not character_401.valid? - assert_includes(character_401.errors, :description) - end + character_401 = Review.new(rating: 0, product_id: 123, description: "Here is a review of a book for sale. It is a great book, worth reading and possibly adding to your personal library. You will not be disappointed. Check this book out from your local library so you can form your own opinion and upvote it here. klsfjshrjdf lkajsf jej faiosejf esj oiasejf lsdjf ;oasjef ojsdfkl jasefj sodf lsdfj oisejf SDJf lKSDJFo iejw lKDSFJ l;SEF SIefj l;KSZDf sd eddjjrhdm fydowkf!") + assert_not character_401.valid? + assert_includes(character_401.errors, :description) + end - test "Review creation requires a product_id [integer]" do - assert reviews(:one_star).valid? + test "Review creation requires a product_id [integer]" do + assert reviews(:one_star).valid? - no_product_id = Review.new(rating: 0, description: "hello") - assert_not no_product_id.valid? - assert_includes(no_product_id.errors, :product_id) - end -end + no_product_id = Review.new(rating: 0, description: "hello") + assert_not no_product_id.valid? + assert_includes(no_product_id.errors, :product_id) + end -# note: have not written any tests on the belongs_to relationship, as we haven't set-up the product_id in the controller using params yet. I think this relationship can be tested in the controller instead of the model tests? + test "Review belongs to a product" do + review = reviews(:one_star) -# fyi from earlier project: on rails console, object.new goes straight to the model, bypassing the controller completely. + assert_respond_to review, :product + end +end From d89ad9bc65f6ce503d622b11ac41ed1d5f047747 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Sun, 23 Oct 2016 15:18:01 -0700 Subject: [PATCH 052/144] implemented review post function --- app/controllers/reviews_controller.rb | 11 ++++++++ app/views/categories/show.html.erb | 6 ++--- app/views/products/index.html.erb | 2 +- app/views/products/show.html.erb | 25 +++++++++++++++++- app/views/reviews/_form.html.erb | 6 ++--- app/views/reviews/new.html.erb | 5 +++- test/controllers/reviews_controller_test.rb | 29 ++++++++++++++++----- 7 files changed, 67 insertions(+), 17 deletions(-) diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb index f9aaf76d30..4a1eb591d8 100644 --- a/app/controllers/reviews_controller.rb +++ b/app/controllers/reviews_controller.rb @@ -4,6 +4,17 @@ def new @review = Review.new end + def create + @review = Review.new(review_params) + @product = Product.find(params[:product_id]) + @review.product_id = @product.id + if @review.save + redirect_to product_path(@product.id) + else + render :new + end + end + private def review_params diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb index 277fadf86e..aab8acdf43 100644 --- a/app/views/categories/show.html.erb +++ b/app/views/categories/show.html.erb @@ -1,4 +1,4 @@ -

<%= @category.name.capitalize %>

+

<%= capitalize_each_word(@category.name) %>

<% if @products.empty? %>

No products in this category! @@ -6,6 +6,6 @@ <% end %> <% @products.each do |product| %> -

<%= product.name.capitalize %>

- <%= link_to image_tag("#{product.photo_url}"), product_path(product) %> +

<%= capitalize_each_word(product.name) %>

+ <%= link_to image_tag("#{product.photo_url}", alt: "#{product.name}"), product_path(product) %> <% end %> diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index ec98476bec..35e5d98f4f 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -6,7 +6,7 @@ <%= link_to capitalize_each_word(product.name), product_path(product) %>

- <%= image_tag "#{product.photo_url}"%> + <%= image_tag "#{product.photo_url}", alt: "#{product.name}"%>

<%= product.description %> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 7c318857e5..ad4228047b 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1,7 +1,7 @@

<%= capitalize_each_word(@product.name) %>

- <%= image_tag "#{@product.photo_url}" %> + <%= image_tag "#{@product.photo_url}", alt: "#{@product.name}" %>

@@ -20,4 +20,27 @@ <% end %> <% end %> + + +

Reviews

+<% if @product.reviews.empty? %> +

+ No reviews! <%= link_to "Be the first.", new_product_review_path(@product.id) %> +

+<% else %> +<% @product.reviews.each do |review| %> + +

+ <% review.created_at %> +

+ +

+ Rating: <%= review.rating %> +

+ +

+ Description: <%= review.description %> +

+<% end %> +<% end %>
diff --git a/app/views/reviews/_form.html.erb b/app/views/reviews/_form.html.erb index d0cb62ab2c..0e0ea482e7 100644 --- a/app/views/reviews/_form.html.erb +++ b/app/views/reviews/_form.html.erb @@ -16,16 +16,14 @@

<%= f.label :rating, "Rating (1 (worst) to 5 (best)): " %> -

-

<%= f.number_field :rating, in: 1..5, size: "30x1" %>

<%= f.label :description, "Description (max 400 characters):" %>

-

- <%= f.text_area :description, size: "30x10" %> +

+ <%= f.text_area :description, size: "35x10" %>

<%= f.submit "Submit", class: "success button" %> diff --git a/app/views/reviews/new.html.erb b/app/views/reviews/new.html.erb index 5927a092a9..3a237816c5 100644 --- a/app/views/reviews/new.html.erb +++ b/app/views/reviews/new.html.erb @@ -1,4 +1,5 @@

Review <%= capitalize_each_word(@product.name) %>

+<%#= image_tag "#{@product.photo_url}", alt: "#{@product.name}" %> <%= render partial: "form", locals: { @@ -6,4 +7,6 @@ local_path: product_reviews_path } %> -<%= link_to "Back to Product", product_path(@product) %> +

+ <%= link_to "Back to Product", product_path(@product) %> +

diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb index dc867be63c..f0d82fd179 100644 --- a/test/controllers/reviews_controller_test.rb +++ b/test/controllers/reviews_controller_test.rb @@ -12,11 +12,26 @@ class ReviewsControllerTest < ActionController::TestCase assert_template partial: "_form" end - # test "should be able to save a review to the database" do - # product = products(:cat_suit) - # end - # - # test "should not be able to save a review with empty values" do - # - # end + test "should be able to save a review to the database" do + product = products(:cat_suit) + review = { review: {rating: 1, description: "ugh"}, product_id: product.id } + + assert_difference("Review.count") do + post :create, review + end + + assert_redirected_to product_path(product.id) + + end + + test "should not save an invalid review to the database" do + product = products(:cat_suit) + review = { review: {rating: 1}, product_id: product.id } + + assert_no_difference("Review.count") do + post :create, review + end + + assert_template :new + end end From 88e22286f34d3fb958f4a92299fb13a716b4cfda Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Sun, 23 Oct 2016 15:32:02 -0700 Subject: [PATCH 053/144] modified products show views to show reviews/links to reviews --- app/helpers/application_helper.rb | 6 ++- app/views/products/show.html.erb | 62 +++++++++++++++++-------------- 2 files changed, 40 insertions(+), 28 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 10f0e02796..3dfdd67f4a 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -5,6 +5,10 @@ def capitalize_each_word(phrase) def show_dollars(price) price = price / 100.0 - number_to_currency(price) + return number_to_currency(price) + end + + def date_format(time) + return time.strftime("%B %-d, %Y at %l:%M:%S %p") end end diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index ad4228047b..bb480379af 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -11,36 +11,44 @@

Price: <%= show_dollars(@product.price) %>

+
-<% unless @product.categories.empty? %> -

Categories

-
    - <% @product.categories.each do |category| %> -
  • <%= link_to "#{category.name}", category_path(category) %>
  • - <% end %> -
-<% end %> - +
+ <% unless @product.categories.empty? %> +

Categories

+
    + <% @product.categories.each do |category| %> +
  • <%= link_to "#{category.name}", category_path(category) %>
  • + <% end %> +
+ <% end %> +
-

Reviews

-<% if @product.reviews.empty? %> -

- No reviews! <%= link_to "Be the first.", new_product_review_path(@product.id) %> -

-<% else %> -<% @product.reviews.each do |review| %> +
+

Reviews

+ <% if @product.reviews.empty? %> +

+ No reviews! <%= link_to "Be the first.", new_product_review_path(@product.id) %> +

+ <% else %> + <% @product.reviews.each do |review| %> -

- <% review.created_at %> -

+
+

+ <%= date_format(review.created_at) %> +

-

- Rating: <%= review.rating %> -

+

+ Rating: <%= review.rating %> +

-

- Description: <%= review.description %> -

-<% end %> -<% end %> +

+ Description: <%= review.description %> +

+ <% end %> +
+

+ <%= link_to "Review this product!", new_product_review_path(@product.id) %> +

+ <% end %>
From db07b1105d9a4651f6237e68c19c8ec353b11016 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Sun, 23 Oct 2016 15:47:44 -0700 Subject: [PATCH 054/144] modified products show page --- app/views/products/show.html.erb | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index bb480379af..316ecc6128 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -3,7 +3,9 @@

<%= image_tag "#{@product.photo_url}", alt: "#{@product.name}" %>

+
+

Description: <%= @product.description %>

@@ -11,6 +13,10 @@

Price: <%= show_dollars(@product.price) %>

+ +

+ Quantity: <%= @product.stock %> +

From aebdc5fca09570868fe37b7f14506b1a992a443d Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Sun, 23 Oct 2016 16:47:25 -0700 Subject: [PATCH 055/144] modified views --- app/views/categories/show.html.erb | 3 +-- app/views/products/show.html.erb | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb index aab8acdf43..713e2c9ab6 100644 --- a/app/views/categories/show.html.erb +++ b/app/views/categories/show.html.erb @@ -6,6 +6,5 @@ <% end %> <% @products.each do |product| %> -

<%= capitalize_each_word(product.name) %>

- <%= link_to image_tag("#{product.photo_url}", alt: "#{product.name}"), product_path(product) %> + <%= link_to capitalize_each_word(product.name), product_path(product) %> <% end %> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 316ecc6128..4a0298c1b6 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -24,7 +24,9 @@

Categories

    <% @product.categories.each do |category| %> -
  • <%= link_to "#{category.name}", category_path(category) %>
  • +
  • + <%= link_to "#{category.name}", category_path(category) %> +
  • <% end %>
<% end %> From ef8a823533d5130b10fde6aeecfcfc0962ab5f32 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Sun, 23 Oct 2016 20:32:15 -0700 Subject: [PATCH 056/144] added and refined some tests --- app/models/order.rb | 4 ++-- app/models/order_item.rb | 2 +- db/schema.rb | 4 +--- test/fixtures/order_items.yml | 6 +++--- test/fixtures/orders.yml | 16 +++++----------- test/fixtures/products.yml | 1 + test/models/order_item_test.rb | 2 +- test/models/order_test.rb | 30 ++++++++++++++++++------------ 8 files changed, 32 insertions(+), 33 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index 13175c661c..a6549da21b 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,7 +1,7 @@ class Order < ActiveRecord::Base has_many :order_items - validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 16 } + validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 4 } validates :cc_exp_year, presence: true, length: {is: 4} validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} @@ -32,7 +32,7 @@ def total private def set_order_status - self.status = "PENDING" + self[:status] = "PENDING" end def update_total diff --git a/app/models/order_item.rb b/app/models/order_item.rb index 665c4955ae..da66d7da8f 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -14,7 +14,7 @@ def total_price private def valid_quantity - if quantity > product.stock + if quantity > self.product.stock errors.add(:quantity, "there is not enough stock of this product to fulfill your request, please try again") end end diff --git a/db/schema.rb b/db/schema.rb index d949fdf785..9999476e2d 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,9 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. - -ActiveRecord::Schema.define(version: 20161020020026) do - +ActiveRecord::Schema.define(version: 20161021201059) do create_table "categories", force: :cascade do |t| t.string "name" diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml index 3060530e6f..65ef7e714e 100644 --- a/test/fixtures/order_items.yml +++ b/test/fixtures/order_items.yml @@ -1,10 +1,10 @@ one_unit: quantity: 1 - product_id: 123 + product_id: 1234 order_id: 456 shipped?: true four_unit: quantity: 4 - product_id: 123 - order_id: 456 + product_id: 1234 + order_id: 458 shipped?: true diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml index 937a0c002e..08633dea3b 100644 --- a/test/fixtures/orders.yml +++ b/test/fixtures/orders.yml @@ -1,11 +1,5 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value +valid_order: + cc_number: 6789 + cc_exp_year: 2016 + cc_exp_month: 12 + status: PENDING diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index 6f1f89c783..afd682091d 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -1,4 +1,5 @@ cat_suit: + id: 1234 name: cat suit price: 1234 description: your cat will look dapper in this one-piece suit! diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb index 1b6fc10c2c..bf0e54135a 100644 --- a/test/models/order_item_test.rb +++ b/test/models/order_item_test.rb @@ -31,7 +31,7 @@ class OrderItemTest < ActiveSupport::TestCase assert_not no_product_id.valid? assert_includes(no_product_id.errors, :product_id) - assert_equal(123, order_items(:one_unit)) + assert_equal(1234, order_items(:one_unit)) assert order_items(:four_unit).valid? end diff --git a/test/models/order_test.rb b/test/models/order_test.rb index cab804e7a1..5f18c45cd2 100644 --- a/test/models/order_test.rb +++ b/test/models/order_test.rb @@ -1,25 +1,31 @@ require 'test_helper' class OrderTest < ActiveSupport::TestCase - test "credit card must present and be 16 characters long" do - order = Order.new(cc_number: 2345123454326789, cc_exp_year: 2016, cc_exp_month: 10) - order2 = Order.new(cc_number: 1234, cc_exp_year: 1990, cc_exp_month: 7) - order3 = Order.new(cc_number: 2345123454326789234, cc_exp_year: 1990, cc_exp_month: 7) + test "credit card number must be present and be 4 characters long" do + order2 = Order.new(cc_number: 34, cc_exp_year: 1990, cc_exp_month: 7, status: "PENDING") + order3 = Order.new(cc_number: 6789234, cc_exp_year: 1990, cc_exp_month: 7, status: "PENDING") - assert order.valid? + assert orders(:valid_order).valid? assert_not order2.valid? assert_not order3.valid? end test "credit card should not have an expired date" do - order = Order.new(cc_exp_year: 2014, cc_exp_month: 12) - order2 = Order.new(cc_exp_month: 10, cc_exp_year: 2013) - order3 = Order.new(cc_exp_year: 2016, cc_exp_month: 9, cc_number: 2345123454326789) + expired_year = Order.new(cc_number: 1234, cc_exp_year: (Time.now.year - 2), cc_exp_month: 12, status: "PENDING") + expired_month_in_current_year = Order.new(cc_number: 1234, cc_exp_year: Time.now.year, cc_exp_month: (Time.now.month - 2), status: "PENDING") - assert_not order.valid? - assert_not order2.valid? - #this assertion should not pass since month is in the past, month is not yet linked to year, working on it - assert_not order3.valid? + assert_not expired_year.valid? + assert_not expired_month_in_current_year.valid? end + + test "an order model object's status is nil upon initiation, then set to PENDING due to the set_order_status private method in the model" do + order = Order.new(cc_number: 1231, cc_exp_year: 2016, cc_exp_month: 12) + assert_equal(nil, order.status) + + order.save + assert_equal("PENDING", order.status) + end + + end From 54c967e419945b393f845732b981f92a4c3d5f1e Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Mon, 24 Oct 2016 10:46:28 -0700 Subject: [PATCH 057/144] edited review --- app/models/review.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/models/review.rb b/app/models/review.rb index e094e15fec..2df58a6e9b 100644 --- a/app/models/review.rb +++ b/app/models/review.rb @@ -1,7 +1,7 @@ class Review < ActiveRecord::Base - validates :rating, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } - validates :description, presence: true, length: { maximum: 400 } - validates :product_id, presence: true + validates :rating, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } + validates :description, presence: true, length: { maximum: 400 } + validates :product_id, presence: true - belongs_to :product + belongs_to :product end From 0b377652b2baba54753c5b952906b316251196de Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Mon, 24 Oct 2016 11:52:00 -0700 Subject: [PATCH 058/144] created (and ran) migration that set the order model's default status field to PENDING so that we can delete a private method that's unnecessary. --- .../20161024184627_orders_table_add_default_to_status.rb | 5 +++++ db/schema.rb | 8 ++++---- 2 files changed, 9 insertions(+), 4 deletions(-) create mode 100644 db/migrate/20161024184627_orders_table_add_default_to_status.rb diff --git a/db/migrate/20161024184627_orders_table_add_default_to_status.rb b/db/migrate/20161024184627_orders_table_add_default_to_status.rb new file mode 100644 index 0000000000..18fd76f605 --- /dev/null +++ b/db/migrate/20161024184627_orders_table_add_default_to_status.rb @@ -0,0 +1,5 @@ +class OrdersTableAddDefaultToStatus < ActiveRecord::Migration + def change + change_column(:orders, :status, :string, :default => "PENDING") + end +end diff --git a/db/schema.rb b/db/schema.rb index 9999476e2d..5def01b7bc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161021201059) do +ActiveRecord::Schema.define(version: 20161024184627) do create_table "categories", force: :cascade do |t| t.string "name" @@ -46,7 +46,7 @@ end create_table "orders", force: :cascade do |t| - t.string "status" + t.string "status", default: "PENDING" t.datetime "date_purchased" t.string "email" t.string "address" @@ -55,8 +55,8 @@ t.integer "cc_exp_year" t.integer "cc_exp_month" t.integer "billing_zip" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false t.integer "total", default: 0 end From e046ad6f6eb16e1b9c008e34802b5dacf561f739 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Mon, 24 Oct 2016 12:25:55 -0700 Subject: [PATCH 059/144] debugging so that all of the following model tests now pass: orders and order_items --- app/models/order.rb | 13 ++++++++----- app/models/order_item.rb | 2 ++ test/fixtures/order_items.yml | 4 ++-- test/fixtures/orders.yml | 1 - test/fixtures/products.yml | 1 - test/models/order_item_test.rb | 16 +++++++--------- test/models/order_test.rb | 22 +++++++++++----------- 7 files changed, 30 insertions(+), 29 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index a6549da21b..ebf8d3c3fe 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -9,7 +9,6 @@ class Order < ActiveRecord::Base validates_associated :order_items validate :acceptable_status - before_create :set_order_status before_save :update_total def valid_exp @@ -31,11 +30,15 @@ def total end private - def set_order_status - self[:status] = "PENDING" - end - def update_total self[:total] = total + puts ">>>>>>>> UPDATING TOTAL!" end end + +# test +# create order model object +# add order items to it (fixtures) +# +# assert_difference +# end diff --git a/app/models/order_item.rb b/app/models/order_item.rb index da66d7da8f..aaa04b2d96 100644 --- a/app/models/order_item.rb +++ b/app/models/order_item.rb @@ -14,6 +14,8 @@ def total_price private def valid_quantity + return false if quantity.nil? + return false if self.product.nil? if quantity > self.product.stock errors.add(:quantity, "there is not enough stock of this product to fulfill your request, please try again") end diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml index 65ef7e714e..5a06f882a4 100644 --- a/test/fixtures/order_items.yml +++ b/test/fixtures/order_items.yml @@ -1,10 +1,10 @@ one_unit: quantity: 1 - product_id: 1234 + product: cat_suit order_id: 456 shipped?: true four_unit: quantity: 4 - product_id: 1234 + product: cat_suit order_id: 458 shipped?: true diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml index 08633dea3b..1be061624c 100644 --- a/test/fixtures/orders.yml +++ b/test/fixtures/orders.yml @@ -2,4 +2,3 @@ valid_order: cc_number: 6789 cc_exp_year: 2016 cc_exp_month: 12 - status: PENDING diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index afd682091d..6f1f89c783 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -1,5 +1,4 @@ cat_suit: - id: 1234 name: cat suit price: 1234 description: your cat will look dapper in this one-piece suit! diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb index bf0e54135a..155d44c124 100644 --- a/test/models/order_item_test.rb +++ b/test/models/order_item_test.rb @@ -2,26 +2,26 @@ class OrderItemTest < ActiveSupport::TestCase test "Cannot create an order item with nil quantity" do - nil_quantity = OrderItem.new(quantity: nil, product_id: 123, order_id: 456) + nil_quantity = OrderItem.new(quantity: nil, product: products(:cat_suit), order_id: 456) assert_equal(nil, nil_quantity.quantity) assert_not nil_quantity.valid? assert_includes(nil_quantity.errors, :quantity) end test "Order item quantity is always instantiated at 1" do - default_quantity = OrderItem.new(product_id: 123, order_id: 456) + default_quantity = OrderItem.new(product: products(:cat_suit), order_id: 456) assert_equal(1, default_quantity.quantity) end test "Order item quantity must be a positive (non-zero) integer (LESS THAN OR EQUAL TO THE PRODUCT'S QUANTITY/STOCK, WOULD lIKE TO BUILD IN THIS PART OF THE TEST)" do - assert order_items(:four_unit).valid? + assert order_items(:four_unit).valid?, "Validation failed: #{order_items(:four_unit).errors.messages}" assert order_items(:one_unit).valid? - zero_quantity = OrderItem.new(quantity: 0, product_id: 123, order_id: 456) + zero_quantity = OrderItem.new(quantity: 0, product: products(:cat_suit), order_id: 456) assert_not zero_quantity.valid? assert_includes(zero_quantity.errors, :quantity) - negative_quantity = OrderItem.new(quantity: -6, product_id: 123, order_id: 456) + negative_quantity = OrderItem.new(quantity: -6, product: products(:cat_suit), order_id: 456) assert_not negative_quantity.valid? assert_includes(negative_quantity.errors, :quantity) end @@ -31,12 +31,12 @@ class OrderItemTest < ActiveSupport::TestCase assert_not no_product_id.valid? assert_includes(no_product_id.errors, :product_id) - assert_equal(1234, order_items(:one_unit)) + assert_equal(products(:cat_suit).id, order_items(:one_unit).product_id) assert order_items(:four_unit).valid? end test "Order item creation requires an order_id [integer]" do - no_order_id = OrderItem.new(quantity: 2, product_id: 456) + no_order_id = OrderItem.new(quantity: 2, product: products(:cat_suit)) assert_not no_order_id.valid? assert_includes(no_order_id.errors, :order_id) @@ -50,6 +50,4 @@ class OrderItemTest < ActiveSupport::TestCase end end - # note: have not written any tests on the belongs_to relationships, as we haven't set-up the product_id or order_id in the controller using params yet. I think these relationships can be tested in the controller instead of the model tests? - # fyi from earlier project: on rails console, object.new goes straight to the model, bypassing the controller completely. diff --git a/test/models/order_test.rb b/test/models/order_test.rb index 5f18c45cd2..7d98f2c9ae 100644 --- a/test/models/order_test.rb +++ b/test/models/order_test.rb @@ -2,8 +2,8 @@ class OrderTest < ActiveSupport::TestCase test "credit card number must be present and be 4 characters long" do - order2 = Order.new(cc_number: 34, cc_exp_year: 1990, cc_exp_month: 7, status: "PENDING") - order3 = Order.new(cc_number: 6789234, cc_exp_year: 1990, cc_exp_month: 7, status: "PENDING") + order2 = Order.new(cc_number: 34, cc_exp_year: 1990, cc_exp_month: 7) + order3 = Order.new(cc_number: 6789234, cc_exp_year: 1990, cc_exp_month: 7) assert orders(:valid_order).valid? assert_not order2.valid? @@ -11,21 +11,21 @@ class OrderTest < ActiveSupport::TestCase end test "credit card should not have an expired date" do - expired_year = Order.new(cc_number: 1234, cc_exp_year: (Time.now.year - 2), cc_exp_month: 12, status: "PENDING") - expired_month_in_current_year = Order.new(cc_number: 1234, cc_exp_year: Time.now.year, cc_exp_month: (Time.now.month - 2), status: "PENDING") + expired_year = Order.new(cc_number: 1234, cc_exp_year: (Time.now.year - 2), cc_exp_month: 12) + expired_month_in_current_year = Order.new(cc_number: 1234, cc_exp_year: Time.now.year, cc_exp_month: (Time.now.month - 2)) assert_not expired_year.valid? assert_not expired_month_in_current_year.valid? end - test "an order model object's status is nil upon initiation, then set to PENDING due to the set_order_status private method in the model" do - order = Order.new(cc_number: 1231, cc_exp_year: 2016, cc_exp_month: 12) - assert_equal(nil, order.status) - - order.save - assert_equal("PENDING", order.status) + test "an order model object's status defaults to PENDING upon being saved to the database" do + o = orders(:valid_order) + o.save + assert_equal("PENDING", o.status) end - + + + end From a2d54c561bb3337aa9113d5fa6676c2a2d120be7 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Mon, 24 Oct 2016 12:52:29 -0700 Subject: [PATCH 060/144] added a test to confirm the private update_total method is running, given that these relationships are interconnected and required fields --- app/models/order.rb | 8 -------- test/fixtures/order_items.yml | 2 +- test/models/order_test.rb | 17 ++++++++++++++++- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/app/models/order.rb b/app/models/order.rb index ebf8d3c3fe..d3257a5674 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -32,13 +32,5 @@ def total private def update_total self[:total] = total - puts ">>>>>>>> UPDATING TOTAL!" end end - -# test -# create order model object -# add order items to it (fixtures) -# -# assert_difference -# end diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml index 5a06f882a4..95b93accf6 100644 --- a/test/fixtures/order_items.yml +++ b/test/fixtures/order_items.yml @@ -6,5 +6,5 @@ one_unit: four_unit: quantity: 4 product: cat_suit - order_id: 458 + order: valid_order shipped?: true diff --git a/test/models/order_test.rb b/test/models/order_test.rb index 7d98f2c9ae..ba8121065c 100644 --- a/test/models/order_test.rb +++ b/test/models/order_test.rb @@ -24,8 +24,23 @@ class OrderTest < ActiveSupport::TestCase assert_equal("PENDING", o.status) end + test "confirming the private method update_total occurs on an order object" do + p = Order.new + assert_equal(0, p.total) + o = orders(:valid_order) + assert_equal(4936, o.total) - + # assert_difference("o.order_items = order_items(:four_unit)", 0) do + # o.save + # end + end end + +# test +# create order model object +# add order items to it (fixtures) +# +# assert_difference +# end From f871a2f5335a7860eb8c3a93bd36301160c2e309 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Mon, 24 Oct 2016 14:25:35 -0700 Subject: [PATCH 061/144] implemented Merchant show page; edited routes to allow all categories (index) view --- app/assets/javascripts/merchants.coffee | 3 + app/assets/stylesheets/merchants.scss | 3 + app/controllers/merchants_controller.rb | 20 +++ app/helpers/merchants_helper.rb | 2 + app/views/merchants/show.html.erb | 9 ++ config/routes.rb | 2 +- test/controllers/merchants_controller_test.rb | 133 ++++++++++++++++++ test/fixtures/merchants.yml | 21 ++- 8 files changed, 186 insertions(+), 7 deletions(-) create mode 100644 app/assets/javascripts/merchants.coffee create mode 100644 app/assets/stylesheets/merchants.scss create mode 100644 app/controllers/merchants_controller.rb create mode 100644 app/helpers/merchants_helper.rb create mode 100644 app/views/merchants/show.html.erb create mode 100644 test/controllers/merchants_controller_test.rb diff --git a/app/assets/javascripts/merchants.coffee b/app/assets/javascripts/merchants.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/merchants.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/merchants.scss b/app/assets/stylesheets/merchants.scss new file mode 100644 index 0000000000..2327c00afe --- /dev/null +++ b/app/assets/stylesheets/merchants.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the merchants controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 0000000000..9c5217d7a1 --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,20 @@ +class MerchantsController < ApplicationController + before_action :find_merchant, only: [:show, :edit, :update] + + def show; end + + + private + + def find_merchant + begin + @merchant = Merchant.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found + end + end + + def merchant_params + params.require(:merchant).permit(:user_name, :email, :uid, :provider) + end +end diff --git a/app/helpers/merchants_helper.rb b/app/helpers/merchants_helper.rb new file mode 100644 index 0000000000..5337747b0f --- /dev/null +++ b/app/helpers/merchants_helper.rb @@ -0,0 +1,2 @@ +module MerchantsHelper +end diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb new file mode 100644 index 0000000000..b700c1d119 --- /dev/null +++ b/app/views/merchants/show.html.erb @@ -0,0 +1,9 @@ +

<%= @merchant.user_name %>

+ +<% @merchant.products.each do |product| %> + +

+ <%= link_to capitalize_each_word(product.name), product_path(product) %> +

+ +<% end %> diff --git a/config/routes.rb b/config/routes.rb index 4fe790456b..21e20ce8fa 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,7 +14,7 @@ resources :reviews, only: [:new, :create] end - resources :categories, only: [:new, :create, :show] + resources :categories, only: [:index, :new, :create, :show] # We're going to talk about this more if any of us needs to edit this. :) resources :orders, only: [:new, :create, :show] do diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb new file mode 100644 index 0000000000..0ce73b3a9b --- /dev/null +++ b/test/controllers/merchants_controller_test.rb @@ -0,0 +1,133 @@ +require 'test_helper' + +class MerchantsControllerTest < ActionController::TestCase + test "show the individual merchant page" do + merchant_id = merchants(:hilarious).id + + get :show, {id: merchant_id} + assert_response :success + assert_template :show + + merchant = assigns(:merchant) + assert_not_nil merchant + assert_equal merchant.id, merchant_id + end + + test "show a merchant that doesn't exist" do + merchant_id = 334456777592 + assert_raises ActiveRecord::RecordNotFound do + Merchant.find(merchant_id) + end + + get :show, {id: merchant_id} + assert_response :not_found + end +end + + + +# test "should get the new form" do +# get :new +# assert_template :new +# assert_template partial: '_form' +# assert_response :success +# end +# +# test "add a new book to the database" do +# post_params = {book: {title: "Anna Karenina", author: "Tolstoy?"} } +# assert_difference("Book.count", 1) do +# post :create, post_params +# end +# +# assert_redirected_to books_path +# end +# +# test "a book with no title can't change the database" do +# post_params = { book: {author: "someone", description: "empty values"}} +# +# assert_no_difference("Book.count") do +# post :create, post_params +# end +# +# assert_template :new +# end +# +# test "a book with no author can't change the database" do +# post_params = { book: {title: "something", description: "empty values"}} +# +# assert_no_difference("Book.count") do +# post :create, post_params +# end +# +# assert_template :new +# end +# +# test "should get the edit form" do +# book_id = books(:lotr).id +# get :edit, {id: book_id} +# assert_template :edit +# assert_template partial: '_form' +# assert_response :success +# +# book = assigns(:book) +# assert_not_nil book +# assert_equal book.id, book_id +# end +# +# test "update should change the book" do +# book_id = books(:of_mice_and_men).id +# patch :update, {id: book_id, book: {title: "Of Mice & Men"} } +# assert_equal "Of Mice & Men", Book.find(book_id).title +# +# assert_redirected_to book_path +# end +# +# test "update should not allow nil title" do +# book_id = books(:lotr).id +# patch :update, {id: book_id, book: {title: nil} } +# +# assert_equal "Lord of the Rings", Book.find(book_id).title +# +# assert_template :edit +# end +# +# test "update should not allow nil author" do +# book_id = books(:gone_with_the_wind).id +# patch :update, {id: book_id, book: {author: nil} } +# +# assert_equal "Margaret Mitchell", Book.find(book_id).author +# assert_template :edit +# end +# +# test "destroy should delete the item" do +# book_id = books(:lotr).id +# +# assert_difference("Book.count", -1) do +# delete :destroy, {id: book_id} +# end +# +# assert_raises ActiveRecord::RecordNotFound do +# Book.find(book_id) +# end +# +# assert_redirected_to books_path +# end +# +# test "upvote should increment rank by one" do +# book_id = books(:lotr).id +# +# assert_difference("Book.find(book_id).rank", 1) do +# patch :upvote, {id: book_id} +# end +# +# assert_redirected_to book_path(book_id) +# end +# +# test "upvote should set nil ranks to one" do +# book_id = books(:nil_rank).id +# +# assert_difference("Book.find(book_id).rank", 1) do +# patch :upvote, {id: book_id} +# end +# end +# end diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml index be145aeb97..b1c3150bb4 100644 --- a/test/fixtures/merchants.yml +++ b/test/fixtures/merchants.yml @@ -1,6 +1,15 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# +hilarious: + user_name: Hillary Oss + email: hillary@oss.com + uid: 125673 + provider: github +chanda: + user_name: Chanda Lear + email: chandelier@gmail.com + uid: 67890 + provider: github +doctor: + user_name: The Doctor + email: doctorwho@tardis.net + uid: 42 + provider: github From 20f8aa1301356ca67e701263ad18997c771517b3 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Mon, 24 Oct 2016 14:37:24 -0700 Subject: [PATCH 062/144] cleanup merchant test --- test/controllers/merchants_controller_test.rb | 108 ------------------ 1 file changed, 108 deletions(-) diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb index 0ce73b3a9b..19b0b41149 100644 --- a/test/controllers/merchants_controller_test.rb +++ b/test/controllers/merchants_controller_test.rb @@ -23,111 +23,3 @@ class MerchantsControllerTest < ActionController::TestCase assert_response :not_found end end - - - -# test "should get the new form" do -# get :new -# assert_template :new -# assert_template partial: '_form' -# assert_response :success -# end -# -# test "add a new book to the database" do -# post_params = {book: {title: "Anna Karenina", author: "Tolstoy?"} } -# assert_difference("Book.count", 1) do -# post :create, post_params -# end -# -# assert_redirected_to books_path -# end -# -# test "a book with no title can't change the database" do -# post_params = { book: {author: "someone", description: "empty values"}} -# -# assert_no_difference("Book.count") do -# post :create, post_params -# end -# -# assert_template :new -# end -# -# test "a book with no author can't change the database" do -# post_params = { book: {title: "something", description: "empty values"}} -# -# assert_no_difference("Book.count") do -# post :create, post_params -# end -# -# assert_template :new -# end -# -# test "should get the edit form" do -# book_id = books(:lotr).id -# get :edit, {id: book_id} -# assert_template :edit -# assert_template partial: '_form' -# assert_response :success -# -# book = assigns(:book) -# assert_not_nil book -# assert_equal book.id, book_id -# end -# -# test "update should change the book" do -# book_id = books(:of_mice_and_men).id -# patch :update, {id: book_id, book: {title: "Of Mice & Men"} } -# assert_equal "Of Mice & Men", Book.find(book_id).title -# -# assert_redirected_to book_path -# end -# -# test "update should not allow nil title" do -# book_id = books(:lotr).id -# patch :update, {id: book_id, book: {title: nil} } -# -# assert_equal "Lord of the Rings", Book.find(book_id).title -# -# assert_template :edit -# end -# -# test "update should not allow nil author" do -# book_id = books(:gone_with_the_wind).id -# patch :update, {id: book_id, book: {author: nil} } -# -# assert_equal "Margaret Mitchell", Book.find(book_id).author -# assert_template :edit -# end -# -# test "destroy should delete the item" do -# book_id = books(:lotr).id -# -# assert_difference("Book.count", -1) do -# delete :destroy, {id: book_id} -# end -# -# assert_raises ActiveRecord::RecordNotFound do -# Book.find(book_id) -# end -# -# assert_redirected_to books_path -# end -# -# test "upvote should increment rank by one" do -# book_id = books(:lotr).id -# -# assert_difference("Book.find(book_id).rank", 1) do -# patch :upvote, {id: book_id} -# end -# -# assert_redirected_to book_path(book_id) -# end -# -# test "upvote should set nil ranks to one" do -# book_id = books(:nil_rank).id -# -# assert_difference("Book.find(book_id).rank", 1) do -# patch :upvote, {id: book_id} -# end -# end -# end From e516a9ba37ccb8533cdd945a3cd47bc0d87d3ce2 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Mon, 24 Oct 2016 15:31:53 -0700 Subject: [PATCH 063/144] added uniqueness to join table: --- app/controllers/merchants_controller.rb | 11 ++++------- .../20161024221053_add_uniqueness_to_cat_prod.rb | 8 ++++++++ db/schema.rb | 6 +++--- 3 files changed, 15 insertions(+), 10 deletions(-) create mode 100644 db/migrate/20161024221053_add_uniqueness_to_cat_prod.rb diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb index 9c5217d7a1..030610c038 100644 --- a/app/controllers/merchants_controller.rb +++ b/app/controllers/merchants_controller.rb @@ -1,12 +1,6 @@ class MerchantsController < ApplicationController - before_action :find_merchant, only: [:show, :edit, :update] - def show; end - - - private - - def find_merchant + def show begin @merchant = Merchant.find(params[:id]) rescue ActiveRecord::RecordNotFound @@ -14,6 +8,9 @@ def find_merchant end end + + private + def merchant_params params.require(:merchant).permit(:user_name, :email, :uid, :provider) end diff --git a/db/migrate/20161024221053_add_uniqueness_to_cat_prod.rb b/db/migrate/20161024221053_add_uniqueness_to_cat_prod.rb new file mode 100644 index 0000000000..99d47f6230 --- /dev/null +++ b/db/migrate/20161024221053_add_uniqueness_to_cat_prod.rb @@ -0,0 +1,8 @@ +class AddUniquenessToCatProd < ActiveRecord::Migration + def change + remove_index(:categories_products, [:product_id, :category_id]) + remove_index(:categories_products, [:category_id, :product_id]) + add_index(:categories_products, [:product_id, :category_id], :unique => true) + add_index(:categories_products, [:category_id, :product_id], :unique => true) + end +end diff --git a/db/schema.rb b/db/schema.rb index 60adab146f..beb24139dc 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161020020026) do +ActiveRecord::Schema.define(version: 20161024221053) do create_table "categories", force: :cascade do |t| t.string "name" @@ -24,8 +24,8 @@ t.integer "category_id", null: false end - add_index "categories_products", ["category_id", "product_id"], name: "index_categories_products_on_category_id_and_product_id" - add_index "categories_products", ["product_id", "category_id"], name: "index_categories_products_on_product_id_and_category_id" + add_index "categories_products", ["category_id", "product_id"], name: "index_categories_products_on_category_id_and_product_id", unique: true + add_index "categories_products", ["product_id", "category_id"], name: "index_categories_products_on_product_id_and_category_id", unique: true create_table "merchants", force: :cascade do |t| t.string "user_name" From 0ec46103f4c35baeb99ac8b67b7d230e62c1301c Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Mon, 24 Oct 2016 15:59:32 -0700 Subject: [PATCH 064/144] Was playing around with different ways on how to have the cart add items. Reverted back to an old state --- app/controllers/application_controller.rb | 9 +++++++- app/controllers/carts_controller.rb | 24 +++++++++----------- app/controllers/order_items_controller.rb | 2 +- app/models/merchant.rb | 1 - app/views/carts/index.html.erb | 2 +- app/views/products/index.html.erb | 27 ++++++++++++----------- config/routes.rb | 2 +- 7 files changed, 35 insertions(+), 32 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index c4d9355895..d87c0156be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception - helper_method :current_order + helper_method :current_order, :shopping_cart def current_order if !session[:order_id].nil? @@ -11,4 +11,11 @@ def current_order Order.new end end + def shopping_cart + if session[:cart] + @cart = session[:cart] + else + @cart ={} + end + end end diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 5bfee82616..29fa15b197 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,24 +1,17 @@ class CartsController < ApplicationController + before_action :shopping_cart, only: [:index] def index - if session[:cart] - @cart = session[:cart] - else - @cart ={} - end + return @cart end def add_to_cart id = params[:id] - if session[:cart] - cart = session[:cart] - else - session[:cart] = {} - cart = session[:cart] - end + @product = Product.find(id) + if cart[id] - cart[id] = cart[id] - 1 + cart[id] = cart[id] + 1 else cart[id] = 1 end @@ -32,8 +25,11 @@ def sub_cart end def destroy - cart = session[:cart] - cart.destroy + + @product = Product.find(params[:id]) + session[:cart].delete(@product) + # @cart = shopping_cart + # @product = @cart.product.find(params[:id]).destroy redirect_to carts_path end diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb index 8b9119674c..49180bbe96 100644 --- a/app/controllers/order_items_controller.rb +++ b/app/controllers/order_items_controller.rb @@ -26,7 +26,7 @@ def edit def destroy @order = current_order @order_item = @order.order_items.find(params[:id]).destroy - redirect_to order_path(params[:order_id]) + redirect_to order_path(params[:order_id]) end diff --git a/app/models/merchant.rb b/app/models/merchant.rb index e540e6ae23..644e7a85c6 100644 --- a/app/models/merchant.rb +++ b/app/models/merchant.rb @@ -3,7 +3,6 @@ class Merchant < ActiveRecord::Base has_many :products def self.build_from_github(auth_hash) - merchant = Merchant.new merchant.uid = auth_hash[:uid] merchant.provider = 'github' diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 70173246b1..d5b74574c5 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -13,7 +13,7 @@

<%=link_to "Learn More", product_path(id)%>

<%=number_to_currency(product.price, :unit => '$')%>

Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%> - <%=link_to "Delete Item", sub_cart_path(product.id), method: :delete%> + <%=link_to "Delete Item", delete_cart_path(product.id), method: :delete%>

<%=link_to "Reduce!", sub_cart_path(product.id)%> diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 47b1115da6..267bb6818e 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,18 +1,19 @@

All Products

<%@products.each do |product|%> -
-

- <%= link_to product.name, product_path(product) %> -

-

- <%= image_tag "#{product.photo_url}"%> -

-

- <%= product.description %> -

-

- Add to Cart +

+

+ <%= link_to product.name, product_path(product) %> +

+

+ <%= image_tag "#{product.photo_url}"%> +

+

+ <%= product.description %> +

+

+ + Add to Cart

-<%end%> + <%end%> diff --git a/config/routes.rb b/config/routes.rb index 6fe1c855a0..48805f1f7d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -37,6 +37,6 @@ get 'carts/empty_cart' =>'carts#empty_cart' get '/carts/:id', to: 'carts#add_to_cart', as: "add_cart" get '/carts/:id', to: 'carts#sub_cart', as: "sub_cart" - delete '/carts/:id', to: 'carts#destroy' + delete '/carts/products/:id', to: 'carts#destroy', as: 'delete_cart' end From f1b092389d778ecd9b9471221476cfca5d71a039 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Mon, 24 Oct 2016 16:29:22 -0700 Subject: [PATCH 065/144] I changed some of the cart controller --- app/controllers/carts_controller.rb | 19 ++++++++++++++----- app/views/carts/index.html.erb | 15 ++++++++------- 2 files changed, 22 insertions(+), 12 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 29fa15b197..ed050dc8fb 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,19 +1,28 @@ class CartsController < ApplicationController - before_action :shopping_cart, only: [:index] + before_action :shopping_cart def index + @cart.each do | k, v| + @id = k + end + @product = Product.find(@id) + @order_item = OrderItem.new + @product.order_items << @order_item + @product.save return @cart end def add_to_cart id = params[:id] - @product = Product.find(id) + @order_item = OrderItem.new + @product.order_items << @order_item + @product.save - if cart[id] - cart[id] = cart[id] + 1 + if @cart[id] + @cart[id] = @cart[id] + 1 else - cart[id] = 1 + @cart[id] = 1 end redirect_to carts_path end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index d5b74574c5..528aa196bd 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -7,19 +7,20 @@
<%end%> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 4a0298c1b6..c40fe5bd7a 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1,62 +1,69 @@ -

<%= capitalize_each_word(@product.name) %>

-
-

- <%= image_tag "#{@product.photo_url}", alt: "#{@product.name}" %> -

-
- -
-

- Description: <%= @product.description %> -

- -

- Price: <%= show_dollars(@product.price) %> -

- -

- Quantity: <%= @product.stock %> -

-
- -
- <% unless @product.categories.empty? %> -

Categories

-
    - <% @product.categories.each do |category| %> -
  • - <%= link_to "#{category.name}", category_path(category) %> -
  • - <% end %> -
- <% end %> -
- -
-

Reviews

- <% if @product.reviews.empty? %> -

- No reviews! <%= link_to "Be the first.", new_product_review_path(@product.id) %> -

- <% else %> - <% @product.reviews.each do |review| %> +
+
+

<%= capitalize_each_word(@product.name) %>

+

+ <%= image_tag "#{@product.photo_url}", alt: "#{@product.name}", class: "product-image" %> +

+
-
+

- <%= date_format(review.created_at) %> + Price: <%= show_dollars(@product.price) %>

- Rating: <%= review.rating %> + In Stock: <%= @product.stock %>

- Description: <%= review.description %> + Description: +
+ <%= @product.description %> +

+ + + <% unless @product.categories.empty? %> + + <% end %> + <%= link_to "Add to cart", add_cart_path(@product), class: "hollow button" %> +
+
+ +<%# REVIEWS SECTION: %> +
+
+

Reviews

+ <% if @product.reviews.empty? %> +

+ No reviews! <%= link_to "Be the first to review!", new_product_review_path(@product.id) %> +

+ <% else %> + <% @product.reviews.each do |review| %> + +
+

+ <%= date_format(review.created_at) %> +

+ +

+ Rating: <%= review.rating %> +

+ +

+ Description: <%= review.description %> +

+ <% end %> +
+

+ <%= link_to "Review this product!", new_product_review_path(@product.id) %>

<% end %>
-

- <%= link_to "Review this product!", new_product_review_path(@product.id) %> -

- <% end %> -
+ From 02027397bb934baaf2aff2fb5f9db14033000a25 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Tue, 25 Oct 2016 20:43:12 -0700 Subject: [PATCH 084/144] Once again attempting cart logic --- app/controllers/application_controller.rb | 2 +- app/controllers/carts_controller.rb | 10 ++++++---- app/views/carts/index.html.erb | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b729d004b8..b9c12d26be 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -33,7 +33,7 @@ def shopping_cart if !session[:cart].nil? @cart = session[:cart] else - session[:cart]= {} + session[:cart]= [] @cart = session[:cart] end end diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 52ee4021cf..efdf9c14e7 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -3,11 +3,13 @@ class CartsController < ApplicationController def index @cart.each do | cart_item| - @id = cart_item.to_i + @id = cart_item.keys.map {|k| k} + @id = @id.join.to_i + end # @order_item = OrderItem.find_by(product_id: @id) -raise + if @id != nil @product = Product.find(@id) @@ -35,8 +37,8 @@ def add_to_cart @order_item = OrderItem.create @product.order_items << @order_item - @product.save - # @cart { @product.id => @order_item.quantity }) + @product.save + @cart.push({ @product.id => @order_item.quantity }) @order_item.save end redirect_to carts_path diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index a159061282..0e2cee539a 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -10,7 +10,7 @@
  • raise

    <%=@product.name%>

    -

    <%=link_to "Learn More", product_path(id)%>

    +

    <%=link_to "Learn More", product_path(@id)%>

    <%=number_to_currency(@product.price, :unit => '$')%>

    Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(@product.id), method: :get%> <%=link_to "Delete Item", delete_cart_path(@product.id), method: :delete%> From ad7447c1be6934863447a6e3b48c7d217335ba4b Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 10:58:18 -0700 Subject: [PATCH 085/144] moved PRODUCT-IMAGE class from products stylesheet to application stylesheet for app-wide access --- app/assets/stylesheets/application.scss | 5 +++++ app/assets/stylesheets/products.scss | 5 +---- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 102f05a63f..5a9554c608 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -53,6 +53,11 @@ a, ul li { text-decoration: none; } +.product-image { + border-radius: 25px; + width: 300px; +} + footer { font-family: 'Quicksand', sans-serif; color: #F5B19A; diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss index f1da003c0a..3e124681fc 100644 --- a/app/assets/stylesheets/products.scss +++ b/app/assets/stylesheets/products.scss @@ -1,10 +1,7 @@ // Place all the styles related to the products controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ -.product-image { - border-radius: 25px; - width: 300px; -} + .prod-dets { margin: 5%; From 139cb23f3d054625f76fc75cc84a1556a0b8bd75 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 11:33:59 -0700 Subject: [PATCH 086/144] CATEGORY INDEX VIEW: added a placeholder image to spread out the category names --- app/assets/images/star_category_image.jpg | Bin 0 -> 286586 bytes app/views/categories/index.html.erb | 11 +++++++---- 2 files changed, 7 insertions(+), 4 deletions(-) create mode 100644 app/assets/images/star_category_image.jpg diff --git a/app/assets/images/star_category_image.jpg b/app/assets/images/star_category_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..65cea42f93f1feb163fa4441b08af4f5c0749e37 GIT binary patch literal 286586 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

    v8Q2(_&?LSle@`kZUm(%Am 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

    m--o|qN_Tf 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 Categories +

    All Categories...

      <% @categories.each do |category| %> -
    • - <%= link_to capitalize_each_word(category.name), category_path(category) %> -

    • +
    • +

      + placeholder pinwheel +

      <%= link_to capitalize_each_word(category.name), category_path(category) %>

      +

      +
    • <%end%>
    From c6461a0c734753a0d85544ec107b047f1800d71c Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 12:10:05 -0700 Subject: [PATCH 087/144] product index view: added foundation grid and made images links to the product's show page, for increased accessibility --- app/views/products/index.html.erb | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index cf8ad7dafc..df040ebec6 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -1,14 +1,12 @@

    All Products

    - -<%@products.each do |product|%> -
    -

    - <%= link_to capitalize_each_word(product.name), product_path(product) %> -

    -

    - <%= image_tag "#{product.photo_url}", alt: "#{product.name}", class: "product-image"%> -

    - -
    - <%end%> +
      +<% @products.each do |product| %> +
    • +
      + <%= link_to (image_tag "#{product.photo_url}", alt: "#{product.name}", class: "product-image"), product_path(product) %> +

      <%= link_to capitalize_each_word(product.name), product_path(product) %>

      +
      +
    • +<%end%> +
    From 462c759a24dd3c88d46d683628ea2d5d32c3592b Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 12:11:32 -0700 Subject: [PATCH 088/144] category show view: added foundation grid, added product images and made these images links to the product's show page, for increased accessibility --- app/views/categories/index.html.erb | 8 ++++---- app/views/categories/show.html.erb | 14 +++++++++++--- 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb index d4d2ec28e5..9329ba3698 100644 --- a/app/views/categories/index.html.erb +++ b/app/views/categories/index.html.erb @@ -1,12 +1,12 @@ -

    All Categories...

    +

    All Categories

      <% @categories.each do |category| %>
    • -

      - placeholder pinwheel +

      + <%= link_to (image_tag "star_category_image.jpg", alt: "placeholder pinwheel for #{category.name}", class: "product-image"), category_path(category) %>

      <%= link_to capitalize_each_word(category.name), category_path(category) %>

      -

      +
    • <%end%>
    diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb index e150956518..f70f4fbeeb 100644 --- a/app/views/categories/show.html.erb +++ b/app/views/categories/show.html.erb @@ -1,10 +1,18 @@

    Category: <%= capitalize_each_word(@category.name) %>

    + <% if @products.empty? %>

    - No products in this category! + There are not any products currently in this category.

    <% end %> +
      <% @products.each do |product| %> - <%= link_to capitalize_each_word(product.name), product_path(product) %> -<% end %> +
    • +
      + <%= link_to (image_tag "#{product.photo_url}", alt: "#{product.name}", class: "product-image"), product_path(product) %> +

      <%= link_to capitalize_each_word(product.name), product_path(product) %>

      +
      +
    • +<%end%> +
    From 1ac30ca364bb7a5d099e69cd3d74c62d86439c72 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 12:14:17 -0700 Subject: [PATCH 089/144] category index view: made all of the placeholder images into links to the associated category, increasing accessibility. (also added spacing on string interpolation for reading ease) --- app/views/categories/index.html.erb | 2 +- app/views/categories/show.html.erb | 2 +- app/views/products/index.html.erb | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb index 9329ba3698..3648da5ac9 100644 --- a/app/views/categories/index.html.erb +++ b/app/views/categories/index.html.erb @@ -4,7 +4,7 @@ <% @categories.each do |category| %>
  • - <%= link_to (image_tag "star_category_image.jpg", alt: "placeholder pinwheel for #{category.name}", class: "product-image"), category_path(category) %> + <%= link_to (image_tag "star_category_image.jpg", alt: "placeholder pinwheel for #{ category.name }", class: "product-image"), category_path(category) %>

    <%= link_to capitalize_each_word(category.name), category_path(category) %>

  • diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb index f70f4fbeeb..506306668e 100644 --- a/app/views/categories/show.html.erb +++ b/app/views/categories/show.html.erb @@ -10,7 +10,7 @@ <% @products.each do |product| %>
  • - <%= link_to (image_tag "#{product.photo_url}", alt: "#{product.name}", class: "product-image"), product_path(product) %> + <%= link_to (image_tag "#{ product.photo_url }", alt: "#{ product.name }", class: "product-image"), product_path(product) %>

    <%= link_to capitalize_each_word(product.name), product_path(product) %>

  • diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index df040ebec6..445ebeba4e 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -4,7 +4,7 @@ <% @products.each do |product| %>
  • - <%= link_to (image_tag "#{product.photo_url}", alt: "#{product.name}", class: "product-image"), product_path(product) %> + <%= link_to (image_tag "#{ product.photo_url }", alt: "#{ product.name }", class: "product-image"), product_path(product) %>

    <%= link_to capitalize_each_word(product.name), product_path(product) %>

  • From c230e664d40a4bdbb910a2afaf61a03b4483ce55 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 12:41:36 -0700 Subject: [PATCH 090/144] merchant controller: created index action. merchant index view: set all of this up with foundation and image link accessibility, mirroring the other product index pages. --- app/controllers/merchants_controller.rb | 26 ++++++++++++++----------- app/views/merchants/index.html.erb | 12 ++++++++++++ app/views/merchants/show.html.erb | 23 +++++++++++++++------- 3 files changed, 43 insertions(+), 18 deletions(-) create mode 100644 app/views/merchants/index.html.erb diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb index fa67a22300..c9f6b6c09d 100644 --- a/app/controllers/merchants_controller.rb +++ b/app/controllers/merchants_controller.rb @@ -1,20 +1,24 @@ class MerchantsController < ApplicationController # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. - skip_before_action :require_login, only: [:show] + skip_before_action :require_login, only: [:index, :show] - def show - begin - @merchant = Merchant.find(params[:id]) - rescue ActiveRecord::RecordNotFound - render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found + def index + @merchants = Merchant.all.order(:user_name) + end + + def show + begin + @merchant = Merchant.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found + end end - end - private + private - def merchant_params - params.require(:merchant).permit(:user_name, :email, :uid, :provider) - end + def merchant_params + params.require(:merchant).permit(:user_name, :email, :uid, :provider) + end end diff --git a/app/views/merchants/index.html.erb b/app/views/merchants/index.html.erb new file mode 100644 index 0000000000..f7eb8cdee5 --- /dev/null +++ b/app/views/merchants/index.html.erb @@ -0,0 +1,12 @@ +

    All Merchants

    + +
      +<% @merchants.each do |merchant| %> +
    • +
      + <%= link_to (image_tag "star_category_image.jpg", alt: "placeholder pinwheel for #{ merchant.user_name }", class: "product-image"), merchant_path(merchant) %> +

      <%= link_to capitalize_each_word(merchant.user_name), merchant_path(merchant) %>

      +
      +
    • +<%end%> +
    diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb index b700c1d119..f5827f0e0b 100644 --- a/app/views/merchants/show.html.erb +++ b/app/views/merchants/show.html.erb @@ -1,9 +1,18 @@ -

    <%= @merchant.user_name %>

    - -<% @merchant.products.each do |product| %> - -

    - <%= link_to capitalize_each_word(product.name), product_path(product) %> -

    +

    <%= @merchant.user_name %>'s Products

    +<% if @merchant.products.empty? %> +

    + Currently, this merchant does not have any products. +

    <% end %> + +
      +<% @merchant.products.each do |product| %> +
    • +
      + <%= link_to (image_tag "#{ product.photo_url }", alt: "#{ product.name }", class: "product-image"), product_path(product) %> +

      <%= link_to capitalize_each_word(product.name), product_path(product) %>

      +
      +
    • +<%end%> +
    From af9ab5e683ae138409d40a3f487f94a703e9b876 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Wed, 26 Oct 2016 14:47:57 -0700 Subject: [PATCH 091/144] Re-did cart and am currently working on the index html --- app/controllers/application_controller.rb | 15 +++-- app/controllers/carts_controller.rb | 73 +++++++++++++---------- app/views/carts/index.html.erb | 22 ++++--- 3 files changed, 61 insertions(+), 49 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b9c12d26be..6e9c8ff9f2 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -29,13 +29,12 @@ def require_login # end # end - def shopping_cart - if !session[:cart].nil? - @cart = session[:cart] - else - session[:cart]= [] - @cart = session[:cart] - end - end + # def shopping_cart + # if !session[:cart].nil? + # @cart = session[:cart] + # else + # @cart = session[:cart] + # end + # end end diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index efdf9c14e7..8b70a5bbe5 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -2,45 +2,40 @@ class CartsController < ApplicationController before_action :shopping_cart def index - @cart.each do | cart_item| - @id = cart_item.keys.map {|k| k} - @id = @id.join.to_i - - end - # @order_item = OrderItem.find_by(product_id: @id) - - - - if @id != nil - @product = Product.find(@id) - @order_item = OrderItem.find_by(product_id: @id) - - - # @product.order_items << @order_item - # @product.save - # @order_item.save - # return @cart - else - return @cart + @products = [] + @cart.each do |item| + @products << Product.find(item["id"]) end end def add_to_cart id = params[:id] @product = Product.find(id) - begin - @order_item = OrderItem.find(params[:product_id]) - rescue ActiveRecord::RecordNotFound - nil - end - if @order_item.nil? + if !@cart.empty? - @order_item = OrderItem.create - @product.order_items << @order_item - @product.save - @cart.push({ @product.id => @order_item.quantity }) - @order_item.save + @cart.each do |k, v| + if k["id"] = @product.id + k["quantity"] = k["quantity"] + 1 + end + end + else + @cart << {"id" => @product.id, "quantity" =>1} end + session[:cart] = @cart + + # begin + # @order_item = OrderItem.find(params[:product_id]) + # rescue ActiveRecord::RecordNotFound + # nil + # end + # if @order_item.nil? + # + # @order_item = OrderItem.create + # @product.order_items << @order_item + # @product.save + # + # @order_item.save + # end redirect_to carts_path end @@ -51,7 +46,7 @@ def sub_cart end def destroy - @order_item = OrderItem.find_by(params[:product_id]) + @order_item = OrderItem.find_by(params[:product_id]) # @cart = shopping_cart @@ -60,7 +55,21 @@ def destroy end def empty_cart + session[:cart] = nil redirect_to carts_path end + + private + + def shopping_cart + if !session[:cart].nil? + @cart = session[:cart] + else + session[:cart] = [] + @cart = session[:cart] + + end + end + end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 0e2cee539a..541a4c839c 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -6,21 +6,25 @@
      - <% @cart.each do |id, quantity| %> +
    • - raise -

      <%=@product.name%>

      -

      <%=link_to "Learn More", product_path(@id)%>

      -

      <%=number_to_currency(@product.price, :unit => '$')%>

      -

      Quantity: <%=quantity%> <%=link_to "Add More!", add_cart_path(@product.id), method: :get%> - <%=link_to "Delete Item", delete_cart_path(@product.id), method: :delete%> + <%@products.each do |product|%> + +

      <%=product.name%>

      +

      <%=link_to "Learn More", product_path(product)%>

      +

      <%=number_to_currency(product.price, :unit => '$')%>

      +

      Quantity: <%=k["quantity"]%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%> + <%=link_to "Delete Item", delete_cart_path(product.id), method: :delete%>

      - <%=link_to "Reduce!", sub_cart_path(@product.id)%> + <%=link_to "Reduce!", sub_cart_path(product.id)%>

    • - <% total += quantity * @product.price %> + <% total += k["quantity"] * product.price %> <%end%> + + + <%if total != 0%>

      Total: <%=number_to_currency(total, :unit => '$')%>

      Continue shopping <%=link_to "Here", products_path %>

      From 442637735c3012197347c73c8b78c5800fa0374d Mon Sep 17 00:00:00 2001 From: Yeni Date: Wed, 26 Oct 2016 15:48:49 -0700 Subject: [PATCH 092/144] fixed the footer to display better. --- app/assets/stylesheets/application.scss | 19 +++++++++++++++++-- app/views/layouts/application.html.erb | 8 +++++--- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 5a9554c608..2e13cd6395 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -47,6 +47,11 @@ a:hover { color: #666699; } +p { + color: #66CDAA; + font-family: 'Quicksand'; +} + a, ul li { color: #F5B19A; font-family: 'Quicksand'; @@ -58,15 +63,25 @@ a, ul li { width: 300px; } +h2 { + color: #66CDAA; + font-family: 'Quicksand'; + text-align: center; +} footer { font-family: 'Quicksand', sans-serif; color: #F5B19A; right: 0; bottom: 0; left: 0; - background-color: #efefef; - /*text-align: center;*/ + border-top: 2pt solid #F5B19A; + background-color: white; margin-top: 5%; padding-left: 5%; position: fixed; } + +/* Make space for the footer */ +#footer-space { + height: 7%; +} diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index b43e944988..980464efb3 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -27,8 +27,10 @@ <%= yield %> - + +
      +

      >>>DISCLAIMER: This is not a real pet shop, even though it looks pretty darn good. So please, don't give us your actual credit card information<<<

      +
      From 53422f40c4f5150fb558e0af3011d8748139c6a4 Mon Sep 17 00:00:00 2001 From: Yeni Date: Wed, 26 Oct 2016 15:50:37 -0700 Subject: [PATCH 093/144] worked on the homepage to display top 4 (random) products. Added index method to the home controller and edited the index view to include foundation. --- app/assets/stylesheets/home.scss | 3 +++ app/controllers/home_controller.rb | 6 ++++-- app/views/home/index.html.erb | 15 +++++++++++++++ 3 files changed, 22 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/home.scss b/app/assets/stylesheets/home.scss index f0ddc6846a..8089912210 100644 --- a/app/assets/stylesheets/home.scss +++ b/app/assets/stylesheets/home.scss @@ -1,3 +1,6 @@ // Place all the styles related to the home controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ +h2 { + margin: 6% 0 0 0; +} diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index e3c9b97d26..441d960fb9 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,6 +1,8 @@ class HomeController < ApplicationController # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. skip_before_action :require_login - - def index; end + + def index + @products = Product.all.shuffle.first(4) + end end diff --git a/app/views/home/index.html.erb b/app/views/home/index.html.erb index 2e53dc63fc..74c41c3352 100644 --- a/app/views/home/index.html.erb +++ b/app/views/home/index.html.erb @@ -2,3 +2,18 @@ <% if flash[:error] %>

      <%= flash[:error] %>

      <% end %> + +

      ~Top Products~

      + +
        +<% @products.each do |product| %> +
      • +

        + <%= link_to (image_tag "#{ product.photo_url }", alt: "#{ product.name }", class: "product-image"), product_path(product) %> +

        +

        + <%= link_to capitalize_each_word(product.name), product_path(product) %> +

        +
      • +<%end%> +
      From 5f912340ac39e0ca41273ab8b7d5bd428ba9d053 Mon Sep 17 00:00:00 2001 From: Yeni Date: Wed, 26 Oct 2016 15:52:20 -0700 Subject: [PATCH 094/144] edited the products form to remove : that were including an extra line in the form. Added create product link to products index to test its functionality. Works great! Fixed position of text in show view. --- app/assets/stylesheets/products.scss | 23 +++++++++++++++++++++-- app/views/products/_form.html.erb | 10 +++++----- app/views/products/index.html.erb | 4 ++++ app/views/products/show.html.erb | 6 +++--- 4 files changed, 33 insertions(+), 10 deletions(-) diff --git a/app/assets/stylesheets/products.scss b/app/assets/stylesheets/products.scss index 3e124681fc..83cffb6fe6 100644 --- a/app/assets/stylesheets/products.scss +++ b/app/assets/stylesheets/products.scss @@ -21,11 +21,30 @@ ul li { .category-link { text-decoration: underline; - margin: 1%; + margin: 2%; } .button { border-radius: 25px; - // background-color: white; + color: #66CDAA; + +} + +.single-reviews { + // margin-top: 5%; + padding: 0 5% 0 5%; + border: 2pt solid #F5B19A; + border-radius: 25px; + color: #66CDAA; +} +h3 { + color: #F5B19A; + text-align: center; + font-family: 'Quicksand'; +} +.show-title { + color: #66CDAA; + font-family: 'Quicksand'; + text-align: left; } diff --git a/app/views/products/_form.html.erb b/app/views/products/_form.html.erb index 361d9c143a..88c1417ed6 100644 --- a/app/views/products/_form.html.erb +++ b/app/views/products/_form.html.erb @@ -13,27 +13,27 @@ <%= form_for @product, url: local_path do |f| %>

      - <%= f.label :name %>: + <%= f.label :name %> <%= f.text_field :name %>

      - <%= f.label :description %>: + <%= f.label :description %> <%= f.text_field :description %>

      - <%= f.label :stock, "How many are you selling? (max 500)" %>: + <%= f.label :stock, "How many are you selling? (max 500)" %> <%= f.number_field :stock, in: 1..500 %>

      - <%= f.label :price %>: + <%= f.label :price %> <%= f.text_field :price %>

      - <%= f.label :photo_url, "URL of photo (sorry, we can't host photos)" %>: + <%= f.label :photo_url, "URL of photo (sorry, we can't host photos)" %> <%= f.url_field :photo_url %>

      diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb index 445ebeba4e..a1381befea 100644 --- a/app/views/products/index.html.erb +++ b/app/views/products/index.html.erb @@ -10,3 +10,7 @@ <%end%>
    + +

    + <%= link_to "Add a product", new_product_path %> +

    diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index c40fe5bd7a..3a5b40d9d1 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1,6 +1,6 @@
    -

    <%= capitalize_each_word(@product.name) %>

    +

    <%= capitalize_each_word(@product.name) %>

    <%= image_tag "#{@product.photo_url}", alt: "#{@product.name}", class: "product-image" %>

    @@ -38,7 +38,7 @@ <%# REVIEWS SECTION: %>
    -
    +

    Reviews

    <% if @product.reviews.empty? %>

    @@ -47,7 +47,7 @@ <% else %> <% @product.reviews.each do |review| %> -

    +

    <%= date_format(review.created_at) %>

    From 1cc4f72baa5e45ce295c7868cdc2e236e997b2ca Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 22:20:22 -0700 Subject: [PATCH 095/144] created a lot more products in the seeds. created merchant framework. seeds aren't loading because of a uniqe constraint failure--hmm. --- db/seeds.rb | 194 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 183 insertions(+), 11 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 3e70288c89..8866481d12 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,62 +5,234 @@ # # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) +merchants = [ + # { + # user_name: "", + # uid: , + # provider: "github", + # id: 1 + # }, + { + user_name: "sckirk", + uid: 17997728, + provider: "github", + id: 2 + } + # }, + # { + # user_name: "", + # uid: , + # provider: "github", + # id: 3 + # }, + # { + # user_name: "", + # uid: , + # provider: "github", + # id: 4 + # } +] + +merchants.each do |merchant| + Merchant.create(merchant) +end -product = [ + +products = [ { name: "cat pants", description: "a nice pair of slacks with an elastic waist", stock: 15, price: 850, - photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg" + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg", + merchant_id: 1 + }, + { + name: "dog booties", + description: "a nice pair of slacks with an elastic waist", + stock: 40, + price: 2026, + photo_url: "http://s7d1.scene7.com/is/image/PETCO/2410644-center-1?$ProductList-medium$", + merchant_id: 1 + }, + { + name: "santa gp", + description: "For the guinea pig who loves cookies and milk!", + stock: 90, + price: 1886, + photo_url: "http://i.huffpost.com/gadgets/slideshows/318566/slide_318566_2975396_free.jpg", + merchant_id: 1 + }, + { + name: "Pirate Pig", + description: "rrrrrrr, matey! Oink!", + stock: 40, + price: 8678, + photo_url: "http://www.whenpigsflynaked.com/uploads/1/1/9/8/11981007/2001761.jpg?276", + merchant_id: 1 }, { name: "llama scarf", description: "long and warm and not made with other llamas", stock: 20, price: 1000, - photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg" + photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg", + merchant_id: 2 + }, + { + name: "Freddy the Frog - pig", + description: "for the pig who longs to be a frog", + stock: 200, + price: 4898, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/82/cf/6a/82cf6a68cb2a7ee1ecf461efcb445a78.jpg", + merchant_id: 2 + }, + { + name: "pig hoodie", + description: "comfortable fleece-lined hoodie for your mini pig", + stock: 88, + price: 4204, + photo_url: "http://66.media.tumblr.com/tumblr_m0h5qg83CY1rr5f7co1_500.jpg", + merchant_id: 2 + }, + { + name: "hedgehog bagpipes", + description: "For your hedgehog who loves to Scottish jigs", + stock: 20, + price: 2695, + photo_url: "https://s-media-cache-ak0.pinimg.com/564x/29/8c/a4/298ca4654d55ffbe01aa4afa3a9e66c8.jpg", + merchant_id: 2 + }, + { + name: "pumpkin gp", + description: "this little piggy is ready to go trick-or-treating", + stock: 60, + price: 1995, + photo_url: "http://s.marketwatch.com/public/resources/MWimages/MW-BN838_pfpets_MG_20131024180004.jpg", + merchant_id: 2 }, { name: "penguin hawaiian shirt", description: "For the penguin who wants to get away", stock: 4, price: 2000, - photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg" + photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg", + merchant_id: 2 + }, + { + name: "beaver pipes", + description: "For the beaver who's roots are in Scotland", + stock: 26, + price: 2804, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/e1/36/a7/e136a71ba59f26a4f47bb6e34d2129b5.jpg", + merchant_id: 3 + }, + { + name: "stylish puffy jacket", + description: "For the dog who gets a little chilly", + stock: 26, + price: 48000, + photo_url: "http://www.dogsmartway.com/upload/product_ad_1881.jpg", + merchant_id: 3 + }, + { + name: "walk the plank", + description: "For the cat who's always in charge", + stock: 40, + price: 6000, + photo_url: "https://d8s0dvdlqz4n8.cloudfront.net/wp-content/uploads/2015/03/Pirate-Cat-Costume1.jpg", + merchant_id: 3 + }, + { + name: "active stripes", + description: "For the dog who can't wait to hit the track", + stock: 80, + price: 2200, + photo_url: "https://img0.etsystatic.com/138/0/10819873/il_570xN.1019173514_9lsr.jpg", + merchant_id: 3 }, { name: "top hat for dogs", description: "For the dog who likes nice things", stock: 30, price: 500, - photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png" + photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png", + merchant_id: 3 + }, + { + name: "Pig Cowboy Hat", + description: "Yeehawg! Get along, lil piggy!", + stock: 20, + price: 400, + photo_url: "https://67.media.tumblr.com/tumblr_m8bbiv8kil1r9uia2o1_1280.jpg", + merchant_id: 3 }, { name: "piglet dance outfit", description: "For the lil piggies who just wanna dance!", stock: 12, price: 3050, - photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg" + photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg", + merchant_id: 3 + }, + { + name: "weiner dog bobsled", + description: "For the dog who craves speed and adventure", + stock: 12, + price: 36800, + photo_url: "http://cdn3-www.dogtime.com/assets/uploads/gallery/wiener-dog-halloween-costumes/8-wiener-dog-bobsled-team.jpg", + merchant_id: 3 + }, + { + name: "Nyan Cat", + description: "A pop tart cat inspired costume for your cat to nyan around the house in. Made out of materials that won't harm your pet and safe to lay in as well.", + stock: 150, + price: 5000, + photo_url: "https://img0.etsystatic.com/139/0/10302358/il_570xN.1030148796_77jh.jpg", + merchant_id: 4 + }, + { + name: "Aye aye Captain", + description: "For the cat who longs to be at sea.", + stock: 60, + price: 1800, + photo_url: "http://www.momdoesreviews.com/wp-content/uploads/2015/10/Sailor-Dog-Cat-Costume.jpg", + merchant_id: 4 + }, + { + name: "Minnie Pig", + description: "For the pig who dreams of the magical land!", + stock: 40, + price: 11800, + photo_url: "http://i.mobofree.com/?u=http%3A%2F%2Flostininternet.com%2Fwp-content%2Fuploads%2F2014%2F10%2FPoppy-and-priscilla-piglets-01.jpg&w=600&h=1500", + merchant_id: 4 }, { name: "Baby Bunny outfit", description: "Are you a new mommy? Dress your little hopper in style!", stock: 4, price: 1900, - photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg" + photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg", + merchant_id: 4 } ] -product.each do |item| +products.each do |item| Product.create(item) end -categories = ["cat", "pants", "llama", "scarves", "penguin", "shirts", "hats", "dogs", "pig", "tutu", "baby", "bunny", "mammals", "birds"] + + +categories = ["pants", "booties", "santa", "pirate", "scarves", "frog", "hoodies", "bagpipes", "pumpkin", "shirts", "beaver", "jackets", "cat", "track suits", "hats", "cowboy hats", "tutus", "bobsleds", "nyan", "sailors", "minnie mouse", "bunny", "mammals", "birds"] + +# categories = ["cat", "pants", "dog", "booties", "santa", "guinea pig", "pirate", "pig", "llama", "scarves", "frog", "pig", "pig", "hoodies", "hedgehog", "bagpipes", "pumpkin", "guinea pig", "penguin", "shirts", "beaver", "bagpipes", "dog", "jackets", "pirate", "cat", "dog", "track suits", "hats", "dog", "pig", "cowboy hats", "pig", "tutus", "dog", "bobsleds", "nyan", "cat", "cat", "sailors", "pig", "minnie mouse", "baby", "bunny", "mammals", "birds"] categories.each do |category| Category.create(name: category) end +# My apologies Noelle, I'm adding in more seed product data, so that our views look more robust for our Friday demo. + all_categories = Category.all all_products = Product.all @@ -72,8 +244,8 @@ until i == all_products.size product = all_products[i] all_products[i].categories << all_categories[j] - j += 1 - all_products[i].categories << all_categories[j] + # j += 1 + # all_products[i].categories << all_categories[j] i+=1 j += 1 end From 848cf2d0f99c8d38a3a8c472c72395d165b86bc2 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Wed, 26 Oct 2016 23:00:01 -0700 Subject: [PATCH 096/144] added logic into reviews controller so that a merchant can't review their own products. Added a flash error and printed this flash error to the show product view. --- app/controllers/reviews_controller.rb | 40 +++++++++++++++------------ app/views/products/show.html.erb | 4 +++ 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb index 5237925f64..3e15cebde5 100644 --- a/app/controllers/reviews_controller.rb +++ b/app/controllers/reviews_controller.rb @@ -2,27 +2,31 @@ class ReviewsController < ApplicationController # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. skip_before_action :require_login - - def new - @product = Product.find(params[:product_id]) - @review = Review.new - end - def create - @review = Review.new(review_params) - @product = Product.find(params[:product_id]) - @review.product_id = @product.id - if @review.save - redirect_to product_path(@product.id) - else - render :new + def new + @product = Product.find(params[:product_id]) + @review = Review.new + if @product.merchant_id == session[:user_id] + flash[:error] = "You are not allowed to review your own products." + redirect_to product_path(@product) + end end - end - private + def create + @review = Review.new(review_params) + @product = Product.find(params[:product_id]) + @review.product_id = @product.id + if @review.save + redirect_to product_path(@product.id) + else + render :new + end + end + + private - def review_params - params.require(:review).permit(:rating, :description, :product_id) - end + def review_params + params.require(:review).permit(:rating, :description, :product_id) + end end diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index c40fe5bd7a..0baf9f755e 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -1,3 +1,7 @@ +<% if flash[:error] %> +

    <%= flash[:error] %>

    +<% end %> +

    <%= capitalize_each_word(@product.name) %>

    From 7ad3a5d250262c99da89cc2986d2df44cb0db59d Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 10:11:08 -0700 Subject: [PATCH 097/144] Cart pretty much works just clearning up add to logic --- app/controllers/carts_controller.rb | 64 ++++++++++++++++++++++------- app/helpers/carts_helper.rb | 12 ++++++ app/views/carts/index.html.erb | 11 ++--- config/routes.rb | 5 ++- 4 files changed, 71 insertions(+), 21 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 8b70a5bbe5..74aeb54d1e 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,27 +1,69 @@ class CartsController < ApplicationController before_action :shopping_cart + after_action :no_item, only: [:less_prod, :delete_product] def index - @products = [] + @products = {} @cart.each do |item| - @products << Product.find(item["id"]) + a = Product.find(item["id"]) + b = item["quantity"] + @products.merge!(a => b) + end + return @products end def add_to_cart id = params[:id] @product = Product.find(id) if !@cart.empty? - @cart.each do |k, v| - if k["id"] = @product.id - k["quantity"] = k["quantity"] + 1 + unless k.has_value?(@product.id) + @cart << {"id" => @product.id, "quantity" =>1} end end else @cart << {"id" => @product.id, "quantity" =>1} end session[:cart] = @cart + redirect_to carts_path + end + + + def more_prod + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.has_value?(@product.id) + k["quantity"] = ( k["quantity"] + 1) + end + end + redirect_to carts_path + end + + def less_prod + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.has_value?(@product.id) + k["quantity"] = ( k["quantity"] - 1) + end + end + redirect_to carts_path + end + + def delete_product + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.has_value?(@product.id) + k["quantity"] = 0 + end + end + redirect_to carts_path + end + + # begin # @order_item = OrderItem.find(params[:product_id]) @@ -36,14 +78,6 @@ def add_to_cart # # @order_item.save # end - redirect_to carts_path - end - - def sub_cart - cart = session[:cart] - cart[params[:id]] = cart[params[:id]] - 1 - redirect_to carts_path - end def destroy @order_item = OrderItem.find_by(params[:product_id]) @@ -68,8 +102,10 @@ def shopping_cart else session[:cart] = [] @cart = session[:cart] - end end + def no_item + @cart.delete_if {|k| k["quantity"] == 0} + end end diff --git a/app/helpers/carts_helper.rb b/app/helpers/carts_helper.rb index d99c380cb5..788fd85e31 100644 --- a/app/helpers/carts_helper.rb +++ b/app/helpers/carts_helper.rb @@ -1,2 +1,14 @@ module CartsHelper + + def numbers + [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 6] + ] + end + end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 541a4c839c..b7ffbef397 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -8,23 +8,24 @@
    • - <%@products.each do |product|%> + <%@products.each do |product, quantity|%>

      <%=product.name%>

      <%=link_to "Learn More", product_path(product)%>

      <%=number_to_currency(product.price, :unit => '$')%>

      -

      Quantity: <%=k["quantity"]%> <%=link_to "Add More!", add_cart_path(product.id), method: :get%> - <%=link_to "Delete Item", delete_cart_path(product.id), method: :delete%> +

      Quantity: <%=quantity%> <%=link_to "Add More!", more_products_path(product.id), method: :patch%> + <%=link_to "Delete Item", delete_products_path(product.id), method: :delete%>

      - <%=link_to "Reduce!", sub_cart_path(product.id)%> + <%=link_to "Reduce!", less_products_path(product.id), method: :patch%>

    • - <% total += k["quantity"] * product.price %> + <% total += quantity * product.price %> <%end%> + <%if total != 0%>

      Total: <%=number_to_currency(total, :unit => '$')%>

      Continue shopping <%=link_to "Here", products_path %>

      diff --git a/config/routes.rb b/config/routes.rb index ac1a951452..6a23be384d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -35,8 +35,9 @@ get '/carts' => 'carts#index' get 'carts/empty_cart' =>'carts#empty_cart' get '/carts/:id', to: 'carts#add_to_cart', as: "add_cart" - - get '/carts/:id', to: 'carts#sub_cart', as: "sub_cart" + patch 'carts/:id', to: 'carts#more_prod', as: 'more_products' + patch 'carts/:id/reduce', to: 'carts#less_prod', as: 'less_products' + delete '/carts/:id/delete_product', to: 'carts#delete_product', as: 'delete_products' delete '/carts/products/:id', to: 'carts#destroy', as: 'delete_cart' end From 701a9b7b8393e21ac6b013a04274fc5b71b88f63 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 13:33:00 -0700 Subject: [PATCH 098/144] All basic cart function works and moved the cart session to the application controller --- app/controllers/application_controller.rb | 23 ++---- app/controllers/carts_controller.rb | 97 ++++++++++------------- 2 files changed, 51 insertions(+), 69 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6e9c8ff9f2..6bfee343e5 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -21,20 +21,13 @@ def require_login end end - # def current_order - # if !session[:order_id].nil? - # Order.find(session[:order_id]) - # else - # Order.new - # end - # end - - # def shopping_cart - # if !session[:cart].nil? - # @cart = session[:cart] - # else - # @cart = session[:cart] - # end - # end + def shopping_cart + if !session[:cart].nil? + @cart = session[:cart] + else + session[:cart] = [] + @cart = session[:cart] + end + end end diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 74aeb54d1e..0b167fc95a 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -8,9 +8,8 @@ def index a = Product.find(item["id"]) b = item["quantity"] @products.merge!(a => b) - end - return @products + return @products end def add_to_cart @@ -18,8 +17,8 @@ def add_to_cart @product = Product.find(id) if !@cart.empty? @cart.each do |k, v| - unless k.has_value?(@product.id) - @cart << {"id" => @product.id, "quantity" =>1} + unless k.values[0] == @product.id + @cart << {"id" => @product.id, "quantity" =>1} end end else @@ -30,66 +29,56 @@ def add_to_cart end - def more_prod - id = params[:id] - @product = Product.find(id) - @cart.each do |k, v| - if k.has_value?(@product.id) - k["quantity"] = ( k["quantity"] + 1) - end - end - redirect_to carts_path + def more_prod + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.values[0] == @product.id + k["quantity"] = ( k["quantity"] + 1) + end end + redirect_to carts_path + end - def less_prod - id = params[:id] - @product = Product.find(id) - @cart.each do |k, v| - if k.has_value?(@product.id) - k["quantity"] = ( k["quantity"] - 1) - end - end - redirect_to carts_path + def less_prod + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.values[0] == @product.id + k["quantity"] = ( k["quantity"] - 1) + end end + redirect_to carts_path + end - def delete_product - id = params[:id] - @product = Product.find(id) - @cart.each do |k, v| - if k.has_value?(@product.id) - k["quantity"] = 0 - end - end - redirect_to carts_path + def delete_product + id = params[:id] + @product = Product.find(id) + @cart.each do |k, v| + if k.values[0] == @product.id + k["quantity"] = 0 + end end + redirect_to carts_path + end - # begin - # @order_item = OrderItem.find(params[:product_id]) - # rescue ActiveRecord::RecordNotFound - # nil - # end - # if @order_item.nil? - # - # @order_item = OrderItem.create - # @product.order_items << @order_item - # @product.save - # - # @order_item.save - # end - - def destroy - @order_item = OrderItem.find_by(params[:product_id]) - - - # @cart = shopping_cart - # @product = @cart.product.find(params[:id]).destroy - redirect_to carts_path - end + # begin + # @order_item = OrderItem.find(params[:product_id]) + # rescue ActiveRecord::RecordNotFound + # nil + # end + # if @order_item.nil? + # + # @order_item = OrderItem.create + # @product.order_items << @order_item + # @product.save + # + # @order_item.save + # end def empty_cart - session[:cart] = nil redirect_to carts_path end From e73bb9870833784c74083aebe0bc523bcf1e1495 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 13:59:50 -0700 Subject: [PATCH 099/144] In the middle of refractoring cart --- app/controllers/application_controller.rb | 2 +- app/controllers/carts_controller.rb | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6bfee343e5..33a56347e4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception - helper_method :current_order, :shopping_cart + helper_method :shopping_cart private diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 0b167fc95a..4a145d1030 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,5 +1,6 @@ class CartsController < ApplicationController before_action :shopping_cart + before_action :product_ref, except: [:index] after_action :no_item, only: [:less_prod, :delete_product] def index @@ -84,17 +85,12 @@ def empty_cart end private - - def shopping_cart - if !session[:cart].nil? - @cart = session[:cart] - else - session[:cart] = [] - @cart = session[:cart] - end - end def no_item @cart.delete_if {|k| k["quantity"] == 0} end + def product_ref + @product = Product.find(params[:id]) + end + end From c8d83d12b364697d0903e4923923a93bb3943f9a Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 14:11:24 -0700 Subject: [PATCH 100/144] woo hoo! all merchants added and working--that rails server was the issue last night. lol --- db/seeds.rb | 42 +++++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 8866481d12..b7fbad61ec 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -6,31 +6,35 @@ # cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) # Mayor.create(name: 'Emanuel', city: cities.first) merchants = [ - # { - # user_name: "", - # uid: , - # provider: "github", - # id: 1 - # }, + { + user_name: "SSBinks", + uid: 16868372, + provider: "github", + email: "smeggs1@gmail.com", + id: 1 + }, { user_name: "sckirk", uid: 17997728, provider: "github", + email: "skirk.seattle@gmail.com", id: 2 } - # }, - # { - # user_name: "", - # uid: , - # provider: "github", - # id: 3 - # }, - # { - # user_name: "", - # uid: , - # provider: "github", - # id: 4 - # } + }, + { + user_name: "johnanmorris", + uid: 10949311, + provider: "github", + email: "johna.n.morris@gmail.com", + id: 3 + }, + { + user_name: "yenicapotediaz", + uid: 17466680, + provider: "github", + email: "yenicapote08@gmail.com", + id: 4 + } ] merchants.each do |merchant| From f52062c2fb47e9d8c6ac47f84f557112f6f7ba35 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 14:13:57 -0700 Subject: [PATCH 101/144] worked on categories index and updated the view to show the categories as buttons. --- app/assets/stylesheets/categories.scss | 29 ++++++++++++++++++++++++++ app/views/categories/index.html.erb | 10 ++++----- app/views/layouts/application.html.erb | 6 +++--- 3 files changed, 37 insertions(+), 8 deletions(-) diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss index ef1657f8c9..b6502f1d81 100644 --- a/app/assets/stylesheets/categories.scss +++ b/app/assets/stylesheets/categories.scss @@ -1,3 +1,32 @@ // Place all the styles related to the categories controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ + +// .teal-square { +// position: relative; +// width: 100%; /* for IE 6 */ +// } + +.categories { +margin-top: 10%; +} + +.text-center { +width: 200px; +height: 200px; +} + +h4 a { + border: 2pt solid #F5B19A; + border-radius: 25px; + background-color: #F5B19A; + color: white; + font-family: 'Anton', sans-serif; + font-size: 2em; + padding: 20% ; + // max-width: 200px; +} + +h4 a:hover { + color: white; +} diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb index 3648da5ac9..7927207dec 100644 --- a/app/views/categories/index.html.erb +++ b/app/views/categories/index.html.erb @@ -1,12 +1,12 @@

      All Categories

      -
        +
          <% @categories.each do |category| %>
        • -
          - <%= link_to (image_tag "star_category_image.jpg", alt: "placeholder pinwheel for #{ category.name }", class: "product-image"), category_path(category) %> -

          <%= link_to capitalize_each_word(category.name), category_path(category) %>

          -
          +
          + <%# link_to (image_tag "http://cdn.wallpapersafari.com/38/84/OmVub1.png", alt: "placeholder for #{ category.name }", class: "product-image"), category_path(category) %> +

          <%= link_to capitalize_each_word(category.name), category_path(category) %>

          +
        • <%end%>
        diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 980464efb3..e75d2bf495 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -6,7 +6,7 @@ <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> - +
        @@ -29,8 +29,8 @@ <%= yield %> -
        + From 81b7a5a8c2a811cf53e2ef93d09221cd4b40af51 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 14:16:38 -0700 Subject: [PATCH 102/144] I had and extra } that needed to be deleted. yay, all good. --- db/seeds.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index b7fbad61ec..8bf4f66c00 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -19,7 +19,6 @@ provider: "github", email: "skirk.seattle@gmail.com", id: 2 - } }, { user_name: "johnanmorris", From 71fcaf1e7fa812c77cafdfbb8d0889c8c73bdb0d Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 14:25:34 -0700 Subject: [PATCH 103/144] Fixed before action on cart --- app/controllers/carts_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 4a145d1030..4fea1114f8 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,6 +1,6 @@ class CartsController < ApplicationController before_action :shopping_cart - before_action :product_ref, except: [:index] + before_action :product_ref, except: [:index, :empty_cart] after_action :no_item, only: [:less_prod, :delete_product] def index From 043c006efcc98f1ed9cbd606efabc982486d408e Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 14:38:32 -0700 Subject: [PATCH 104/144] Made small style changes --- app/views/carts/index.html.erb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index b7ffbef397..c53bb1488c 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -10,9 +10,9 @@
      • <%@products.each do |product, quantity|%> -

        <%=product.name%>

        +

        <%=capitalize_each_word(product.name)%>

        <%=link_to "Learn More", product_path(product)%>

        -

        <%=number_to_currency(product.price, :unit => '$')%>

        +

        <%=show_dollars(product.price)%>

        Quantity: <%=quantity%> <%=link_to "Add More!", more_products_path(product.id), method: :patch%> <%=link_to "Delete Item", delete_products_path(product.id), method: :delete%>

        @@ -27,7 +27,7 @@ <%if total != 0%> -

        Total: <%=number_to_currency(total, :unit => '$')%>

        +

        Total: <%=show_dollars(total)%>

        Continue shopping <%=link_to "Here", products_path %>



        <%= link_to "Empty your cart", carts_empty_cart_path%> From 1665e1a852c2195ba7f345afe790254b35ba7c83 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 14:53:43 -0700 Subject: [PATCH 105/144] Cleaned up a bit of sytling on Cart but should go back and make a change --- app/controllers/carts_controller.rb | 11 +++-------- app/views/carts/index.html.erb | 21 ++++++--------------- 2 files changed, 9 insertions(+), 23 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 4fea1114f8..57cef5a73b 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -14,8 +14,6 @@ def index end def add_to_cart - id = params[:id] - @product = Product.find(id) if !@cart.empty? @cart.each do |k, v| unless k.values[0] == @product.id @@ -31,8 +29,7 @@ def add_to_cart def more_prod - id = params[:id] - @product = Product.find(id) + @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = ( k["quantity"] + 1) @@ -42,8 +39,7 @@ def more_prod end def less_prod - id = params[:id] - @product = Product.find(id) + @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = ( k["quantity"] - 1) @@ -53,8 +49,7 @@ def less_prod end def delete_product - id = params[:id] - @product = Product.find(id) + @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = 0 diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index c53bb1488c..6192b1e81d 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -4,32 +4,23 @@


        <% total = 0 %> -
          -
        • <%@products.each do |product, quantity|%> - -

          <%=capitalize_each_word(product.name)%>

          -

          <%=link_to "Learn More", product_path(product)%>

          +

          <%=link_to capitalize_each_word(product.name), product_path(product)%>

          <%=show_dollars(product.price)%>

          -

          Quantity: <%=quantity%> <%=link_to "Add More!", more_products_path(product.id), method: :patch%> - <%=link_to "Delete Item", delete_products_path(product.id), method: :delete%> -

          -

          - <%=link_to "Reduce!", less_products_path(product.id), method: :patch%> +

          Quantity: <%=quantity%> <%=link_to "Add", more_products_path(product.id), method: :patch%> + <%=link_to "Reduce", less_products_path(product.id), method: :patch%> + <%=link_to "Delete", delete_products_path(product.id), method: :delete%>

        • <% total += quantity * product.price %> <%end%> - - - - <%if total != 0%>

          Total: <%=show_dollars(total)%>

          +

          Continue shopping <%=link_to "Here", products_path %>

          -

          +
          <%= link_to "Empty your cart", carts_empty_cart_path%>
        <%else%> From 0c08e5a43ac601f43a0cf68fc46ad7fda5560f95 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 14:57:07 -0700 Subject: [PATCH 106/144] fixed issue with spacing of categories in index view. --- app/assets/stylesheets/categories.scss | 12 ++++++------ app/views/categories/index.html.erb | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/app/assets/stylesheets/categories.scss b/app/assets/stylesheets/categories.scss index b6502f1d81..cf9f7c772f 100644 --- a/app/assets/stylesheets/categories.scss +++ b/app/assets/stylesheets/categories.scss @@ -8,14 +8,14 @@ // } .categories { -margin-top: 10%; +margin-top: 5%; } - +// .text-center { -width: 200px; -height: 200px; +height: auto; +margin-bottom: 15%; } - +// h4 a { border: 2pt solid #F5B19A; border-radius: 25px; @@ -23,7 +23,7 @@ h4 a { color: white; font-family: 'Anton', sans-serif; font-size: 2em; - padding: 20% ; + padding: 5% ; // max-width: 200px; } diff --git a/app/views/categories/index.html.erb b/app/views/categories/index.html.erb index 7927207dec..2f3947fccd 100644 --- a/app/views/categories/index.html.erb +++ b/app/views/categories/index.html.erb @@ -1,6 +1,6 @@

        All Categories

        -
          +
            <% @categories.each do |category| %>
          • From bb815c0177629bb74688e7f3fc3076cd42ec1f10 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 15:17:26 -0700 Subject: [PATCH 107/144] added a meaningful message where there was a duplicate link that is now handled by Yeni's navigation bar --- app/views/sessions/login.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/sessions/login.html.erb b/app/views/sessions/login.html.erb index e4b46df29c..095b127bff 100644 --- a/app/views/sessions/login.html.erb +++ b/app/views/sessions/login.html.erb @@ -1 +1 @@ -<%= link_to "Merchant Portal", merchants_path %> +

            You are not logged in. Please log in using the above navigation link to the right - or access the top left options without logging in.

            From 6e57f5ced00a3bc6c359107b1f84611ad231df79 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 15:26:15 -0700 Subject: [PATCH 108/144] Created link to merchants portal in nav bar that toggles to not logged in. --- app/helpers/application_helper.rb | 3 ++- app/views/layouts/application.html.erb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index b8a01f8948..479bc47739 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -29,7 +29,8 @@ def user_name def login_status if session[:user_id] - "Logged in as #{user_name}" + # "Logged in as #{user_name}" + link_to "#{user_name}'s Portal", portal_path else "Not logged in" end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index e75d2bf495..f14f8089dd 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -29,8 +29,8 @@ <%= yield %> - +
      • From 8baddbe153e7780f867088aeae922f15e8f410af Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 16:13:06 -0700 Subject: [PATCH 109/144] Updated form to include instructions for price in cents. --- app/views/products/_form.html.erb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/products/_form.html.erb b/app/views/products/_form.html.erb index 88c1417ed6..f8c3d118ce 100644 --- a/app/views/products/_form.html.erb +++ b/app/views/products/_form.html.erb @@ -28,7 +28,7 @@

        - <%= f.label :price %> + <%= f.label :price, "Price (in cents)" %> <%= f.text_field :price %>

        From d7e7ca9cf5b49ae3d91729e67fad30045d2b2d34 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 16:14:19 -0700 Subject: [PATCH 110/144] added title to app page. --- app/assets/stylesheets/application.scss | 6 +++--- app/views/layouts/application.html.erb | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 2e13cd6395..58b90a1f1a 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -15,7 +15,7 @@ */ body { - margin: 0 5%; + margin: 0 4%; } .top-bar { @@ -27,8 +27,7 @@ border-bottom: 2pt solid #F5B19A; color: #666699; background-color: white; - margin-right: 8%; - margin-left: 8%; + margin: 0 8%; padding: 5px 25px; font-size: 9pt; } @@ -68,6 +67,7 @@ h2 { font-family: 'Quicksand'; text-align: center; } + footer { font-family: 'Quicksand', sans-serif; color: #F5B19A; diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index f14f8089dd..a18956f3c5 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -10,7 +10,8 @@
        -

        <%= @page_title %>

        +

        ~FootFoot Couture~

        +
    diff --git a/config/routes.rb b/config/routes.rb index 6a23be384d..a9e5ebeff2 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,27 +8,19 @@ resources :reviews, only: [:new, :create] end + patch 'products/:id/retire' => 'products#retire', as: :retire_product resources :orders, except: [:new, :create, :delete] - resources :order_items, except: [:index, :show] - resources :categories, only: [:index, :new, :create, :show] - + resources :order_items, except: [:index, :show] -# We're going to talk about this more if any of us needs to edit this. :) - # resources :orders, only: [:new, :create, :show] do - # # resources :order_items, except: [:index, :show] - # end + resources :categories, only: [:index, :new, :create, :show] -# Sessions routes - can be further flushed out... + # Sessions routes get '/auth/:provider/callback' => 'sessions#create' - get "/sessions/login_failure", to: "sessions#login_failure", as: "login_failure" - get '/sessions', to: 'sessions#index', as: 'portal' - delete '/auth/logout', to: 'sessions#logout', as: "logout" - get '/auth/login', to: 'sessions#login', as: 'login' #specific routes for the cart! @@ -39,5 +31,4 @@ patch 'carts/:id/reduce', to: 'carts#less_prod', as: 'less_products' delete '/carts/:id/delete_product', to: 'carts#delete_product', as: 'delete_products' delete '/carts/products/:id', to: 'carts#destroy', as: 'delete_cart' - end diff --git a/db/migrate/20161027220154_rename_active_column_in_products.rb b/db/migrate/20161027220154_rename_active_column_in_products.rb new file mode 100644 index 0000000000..c083fdd8e6 --- /dev/null +++ b/db/migrate/20161027220154_rename_active_column_in_products.rb @@ -0,0 +1,5 @@ +class RenameActiveColumnInProducts < ActiveRecord::Migration + def change + rename_column(:products, :active?, :active) + end +end diff --git a/db/schema.rb b/db/schema.rb index fc09d7da5e..a75ce06c2a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161024221053) do +ActiveRecord::Schema.define(version: 20161027220154) do create_table "categories", force: :cascade do |t| t.string "name" @@ -69,7 +69,7 @@ t.integer "merchant_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.boolean "active?", default: true + t.boolean "active", default: true end add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index caa48ab1e3..095bc3e192 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -121,4 +121,23 @@ class ProductsControllerTest < ActionController::TestCase assert_redirected_to portal_path end + + test "retire should retire the product if the product is active" do + product_id = products(:cat_suit).id + patch :retire, {id: product_id} + + product = assigns(:product) + assert_equal product.id, product_id + assert_equal false, product.active + + assert_redirected_to portal_path + end + + test "retire should activate the product if the product is retired" do + product_id = products(:cat_suit).id + patch :retire, {id: product_id} + assert_equal false, Product.find(product_id).active + patch :retire, {id: product_id} + assert_equal true, Product.find(product_id).active + end end diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index 069ea7d029..e6ccc0ce1c 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -6,6 +6,7 @@ cat_suit: photo_url: http://placekitten.com/200/300 categories: cat, formal_wear, mammals merchant: 1234 + active: true hamster_monocle: name: hamster monocle @@ -15,6 +16,7 @@ hamster_monocle: photo_url: http://placekitten.com/200/300 categories: hamster, eyewear, mammals merchant: 1234 + active: true dog_bunny_ears: name: Dog bunny ears @@ -23,3 +25,4 @@ dog_bunny_ears: stock: 1 photo_url: http://placekitten.com/200/300 merchant: 2341 + active: true From e0410e911a0db55e961ff9216695ec8dd885d704 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 16:38:10 -0700 Subject: [PATCH 114/144] fixed an issue with merchant css styling being applied to products (not sure how). Products are now good. --- app/assets/stylesheets/application.scss | 2 +- app/assets/stylesheets/merchants.scss | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 58b90a1f1a..a2d7c876c3 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -25,7 +25,7 @@ .top-bar-left li, .top-bar-right li { font-family: 'Julius Sans One', sans-serif; border-bottom: 2pt solid #F5B19A; - color: #666699; + color: #ff5050; background-color: white; margin: 0 8%; padding: 5px 25px; diff --git a/app/assets/stylesheets/merchants.scss b/app/assets/stylesheets/merchants.scss index 21462ed715..9b659a1f4c 100644 --- a/app/assets/stylesheets/merchants.scss +++ b/app/assets/stylesheets/merchants.scss @@ -11,7 +11,7 @@ height: auto; margin-bottom: 15%; } // -h3 a { +.merchant-names a { border: 2pt solid #F5B19A; border-radius: 25px; background-color: #F5B19A; From 9413cc3092dd9b6f7a18e5edfebbbe4c571fe077 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 16:38:45 -0700 Subject: [PATCH 115/144] rollbacked Noelle's migration --- db/schema.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/schema.rb b/db/schema.rb index fc09d7da5e..a75ce06c2a 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20161024221053) do +ActiveRecord::Schema.define(version: 20161027220154) do create_table "categories", force: :cascade do |t| t.string "name" @@ -69,7 +69,7 @@ t.integer "merchant_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.boolean "active?", default: true + t.boolean "active", default: true end add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" From e2323e195644532e4fc04370f467a733704109b4 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 16:49:02 -0700 Subject: [PATCH 116/144] Set up purchace with new and create in Order --- app/assets/javascripts/orders.coffee | 3 ++ app/assets/stylesheets/orders.scss | 3 ++ app/controllers/application_controller.rb | 6 ++-- app/controllers/carts_controller.rb | 4 +-- app/controllers/orders_controller.rb | 42 ++++++++++++++++++++++ app/helpers/carts_helper.rb | 10 ------ app/helpers/orders_helper.rb | 32 +++++++++++++++++ app/models/order.rb | 17 ++++----- app/views/carts/index.html.erb | 9 ++--- app/views/orders/create.html.erb | 2 ++ app/views/orders/destroy.html.erb | 2 ++ app/views/orders/index.html.erb | 2 ++ app/views/orders/new.html.erb | 21 +++++++++++ app/views/orders/show.html.erb | 2 ++ app/views/orders/update.html.erb | 2 ++ config/routes.rb | 2 +- test/controllers/orders_controller_test.rb | 34 ++++++++++++++++++ 17 files changed, 163 insertions(+), 30 deletions(-) create mode 100644 app/assets/javascripts/orders.coffee create mode 100644 app/assets/stylesheets/orders.scss create mode 100644 app/controllers/orders_controller.rb create mode 100644 app/helpers/orders_helper.rb create mode 100644 app/views/orders/create.html.erb create mode 100644 app/views/orders/destroy.html.erb create mode 100644 app/views/orders/index.html.erb create mode 100644 app/views/orders/new.html.erb create mode 100644 app/views/orders/show.html.erb create mode 100644 app/views/orders/update.html.erb create mode 100644 test/controllers/orders_controller_test.rb diff --git a/app/assets/javascripts/orders.coffee b/app/assets/javascripts/orders.coffee new file mode 100644 index 0000000000..24f83d18bb --- /dev/null +++ b/app/assets/javascripts/orders.coffee @@ -0,0 +1,3 @@ +# 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/ diff --git a/app/assets/stylesheets/orders.scss b/app/assets/stylesheets/orders.scss new file mode 100644 index 0000000000..741506954d --- /dev/null +++ b/app/assets/stylesheets/orders.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Orders controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6c76f5e495..5fb840346d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,8 +2,7 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception - helper_method :shopping_cart - + helper_method :shopping_cart, :total private def current_user @@ -21,6 +20,9 @@ def require_login end end + def total + @total = 0 + end def shopping_cart if !session[:cart].nil? diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 57cef5a73b..10c5990591 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,6 +1,7 @@ class CartsController < ApplicationController before_action :shopping_cart before_action :product_ref, except: [:index, :empty_cart] + before_action :total after_action :no_item, only: [:less_prod, :delete_product] def index @@ -29,7 +30,6 @@ def add_to_cart def more_prod - @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = ( k["quantity"] + 1) @@ -39,7 +39,6 @@ def more_prod end def less_prod - @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = ( k["quantity"] - 1) @@ -49,7 +48,6 @@ def less_prod end def delete_product - @cart.each do |k, v| if k.values[0] == @product.id k["quantity"] = 0 diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb new file mode 100644 index 0000000000..3a7949024f --- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,42 @@ +class OrdersController < ApplicationController + def index + end + + def new + @order = Order.new + end + def show + @order = Order.find(params[:id]) + end + + def create + @order = Order.new(order_params) + if @order.save + @order_item = OrderItem.new + @cart.each do |item| + @order_item.product_id = item.values[0] + @order_item.quantity = item.values[1] + @order_item.order_id = @order.id + @order_item.save + redirect_to order_path + end + else + render :new + end + end + + + + def update + @order = Order.find(params[:id]) + if @order.update.save + end + end + + def destroy + end + private + def order_params + params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip) + end +end diff --git a/app/helpers/carts_helper.rb b/app/helpers/carts_helper.rb index 788fd85e31..fabe823e44 100644 --- a/app/helpers/carts_helper.rb +++ b/app/helpers/carts_helper.rb @@ -1,14 +1,4 @@ module CartsHelper - def numbers - [ - [1, 1], - [2, 2], - [3, 3], - [4, 4], - [5, 5], - [6, 6] - ] - end end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb new file mode 100644 index 0000000000..f44c66e7b4 --- /dev/null +++ b/app/helpers/orders_helper.rb @@ -0,0 +1,32 @@ +module OrdersHelper + def years + [ + [2016, 2016], + [2017, 2017], + [2018, 2018], + [2019, 2019], + [2020, 2020], + [2021, 2021], + [2022, 2022], + [2023, 2023] + ] + end + + def months + [ + [1, 1], + [2, 2], + [3, 3], + [4, 4], + [5, 5], + [6, 6], + [7, 7], + [8, 8], + [9, 9], + [10, 10], + [11, 11], + [12, 12] + ] + end + +end diff --git a/app/models/order.rb b/app/models/order.rb index d3257a5674..ae5ed8313d 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -5,22 +5,22 @@ class Order < ActiveRecord::Base validates :cc_exp_year, presence: true, length: {is: 4} validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} - validate :valid_exp + # validate :valid_exp validates_associated :order_items - validate :acceptable_status + # validate :acceptable_status - before_save :update_total + # before_save :update_total def valid_exp - if cc_exp_year < Time.now.year + if @order.cc_exp_year < Time.now.year errors.add(:cc_exp_year, "Card year is expired") - elsif cc_exp_year == Time.now.year && cc_exp_month < Time.now.month + elsif @order.cc_exp_year == Time.now.year && @order.cc_exp_month < Time.now.month errors.add(:cc_exp_month, "Card month is expired") end end def acceptable_status - if status != "PENDING" && status != "PAID" && status != "COMPLETE" && status != "CANCELLED" + if @order.status != "PENDING" && @order.status != "PAID" && @order.status != "COMPLETE" && @order.status != "CANCELLED" errors.add(:status, "Must be PENDING, PAID, COMPLETE or CANCELLED") end end @@ -29,8 +29,5 @@ def total order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.product.price) : 0 }.sum end - private - def update_total - self[:total] = total - end + end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 6192b1e81d..21a8777397 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -2,7 +2,7 @@

    Your Cart




    -<% total = 0 %> +
    • @@ -14,12 +14,13 @@ <%=link_to "Delete", delete_products_path(product.id), method: :delete%>

    • - <% total += quantity * product.price %> + <% @total += quantity * product.price %> <%end%> - <%if total != 0%> -

      Total: <%=show_dollars(total)%>

      + <%if @total != 0%> +

      Total: <%=show_dollars(@total)%>


      Continue shopping <%=link_to "Here", products_path %>

      +

      <%=link_to "Purchase Now", new_order_path%>


      <%= link_to "Empty your cart", carts_empty_cart_path%>
    diff --git a/app/views/orders/create.html.erb b/app/views/orders/create.html.erb new file mode 100644 index 0000000000..295bd84094 --- /dev/null +++ b/app/views/orders/create.html.erb @@ -0,0 +1,2 @@ +

    Orders#create

    +

    Find me in app/views/orders/create.html.erb

    diff --git a/app/views/orders/destroy.html.erb b/app/views/orders/destroy.html.erb new file mode 100644 index 0000000000..d14d0a3508 --- /dev/null +++ b/app/views/orders/destroy.html.erb @@ -0,0 +1,2 @@ +

    Orders#destroy

    +

    Find me in app/views/orders/destroy.html.erb

    diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb new file mode 100644 index 0000000000..d63a69fb54 --- /dev/null +++ b/app/views/orders/index.html.erb @@ -0,0 +1,2 @@ +

    Orders#index

    +

    Find me in app/views/orders/index.html.erb

    diff --git a/app/views/orders/new.html.erb b/app/views/orders/new.html.erb new file mode 100644 index 0000000000..ac9af75fee --- /dev/null +++ b/app/views/orders/new.html.erb @@ -0,0 +1,21 @@ +

    Checkout

    +<%= form_for @order do |f| %> +<%= f.label :email%> +<%= f.text_field :email %> +<%= f.label :address %> +<%= f.text_field :address %> +<%= f.label "Name on the Credit Card" %> +<%= f.text_field :cc_name %> +<%= f.label "Credit Card Number" %> +<%= f.text_field :cc_number %> +<%= f.label "Expiration Year" %> +<%= select_tag :cc_exp_year, options_for_select(years) %> +<%= f.label "Expiration Month" %> +<%= select_tag :cc_exp_month, options_for_select(months) %> +<%= f.label "Zip Code" %> +<%= f.text_field :billing_zip %> +<%= f.hidden_field :date_purchased, :value => Time.now %> +<%= f.hidden_field :total, :value => @total %> + +<%= f.submit%> +<% end %> diff --git a/app/views/orders/show.html.erb b/app/views/orders/show.html.erb new file mode 100644 index 0000000000..22eb495f6f --- /dev/null +++ b/app/views/orders/show.html.erb @@ -0,0 +1,2 @@ +

    Orders#show

    +

    Find me in app/views/orders/show.html.erb

    diff --git a/app/views/orders/update.html.erb b/app/views/orders/update.html.erb new file mode 100644 index 0000000000..21caac1f70 --- /dev/null +++ b/app/views/orders/update.html.erb @@ -0,0 +1,2 @@ +

    Orders#update

    +

    Find me in app/views/orders/update.html.erb

    diff --git a/config/routes.rb b/config/routes.rb index 6a23be384d..6586d66158 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -9,7 +9,7 @@ end - resources :orders, except: [:new, :create, :delete] + resources :orders resources :order_items, except: [:index, :show] resources :categories, only: [:index, :new, :create, :show] diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb new file mode 100644 index 0000000000..d7053b71e9 --- /dev/null +++ b/test/controllers/orders_controller_test.rb @@ -0,0 +1,34 @@ +require 'test_helper' + +class OrdersControllerTest < ActionController::TestCase + test "should get index" do + get :index + assert_response :success + end + + test "should get show" do + get :show + assert_response :success + end + + test "should get create" do + get :create + assert_response :success + end + + test "should get edit" do + get :edit + assert_response :success + end + + test "should get update" do + get :update + assert_response :success + end + + test "should get destroy" do + get :destroy + assert_response :success + end + +end From 056e1845b13699bbdb5485365eec46764625adf8 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 17:20:50 -0700 Subject: [PATCH 117/144] changed tag name to class name in css to prevent it from applying anywhere else. --- app/assets/stylesheets/merchants.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/assets/stylesheets/merchants.scss b/app/assets/stylesheets/merchants.scss index 9b659a1f4c..6a589471e3 100644 --- a/app/assets/stylesheets/merchants.scss +++ b/app/assets/stylesheets/merchants.scss @@ -11,7 +11,7 @@ height: auto; margin-bottom: 15%; } // -.merchant-names a { +.merchant-name a { border: 2pt solid #F5B19A; border-radius: 25px; background-color: #F5B19A; @@ -22,6 +22,6 @@ margin-bottom: 15%; // max-width: 200px; } -h3 a:hover { +.merchant-name a:hover { color: white; } From 13fca3f2bc469117b15db2557285c3c3fe3fcf40 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 17:30:14 -0700 Subject: [PATCH 118/144] An order can be created --- app/controllers/orders_controller.rb | 15 ++++++++++----- app/views/orders/new.html.erb | 4 ++-- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 3a7949024f..56513fcbce 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,10 +1,13 @@ class OrdersController < ApplicationController + before_action :total, :shopping_cart def index + @orders = Order.all end def new - @order = Order.new + @order = Order.new end + def show @order = Order.find(params[:id]) end @@ -18,25 +21,27 @@ def create @order_item.quantity = item.values[1] @order_item.order_id = @order.id @order_item.save - redirect_to order_path end + redirect_to orders_path else + raise render :new end end - def update - @order = Order.find(params[:id]) + @order = Order.find(params[:id]) if @order.update.save end end def destroy end + private + def order_params - params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip) + params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address, :total, ) end end diff --git a/app/views/orders/new.html.erb b/app/views/orders/new.html.erb index ac9af75fee..6d81e461f4 100644 --- a/app/views/orders/new.html.erb +++ b/app/views/orders/new.html.erb @@ -9,9 +9,9 @@ <%= f.label "Credit Card Number" %> <%= f.text_field :cc_number %> <%= f.label "Expiration Year" %> -<%= select_tag :cc_exp_year, options_for_select(years) %> +<%= f.select :cc_exp_year, options_for_select(years) %> <%= f.label "Expiration Month" %> -<%= select_tag :cc_exp_month, options_for_select(months) %> +<%= f.select :cc_exp_month, options_for_select(months) %> <%= f.label "Zip Code" %> <%= f.text_field :billing_zip %> <%= f.hidden_field :date_purchased, :value => Time.now %> From 48230229bc935451fefbc94fc915ce5f5a9ad0c5 Mon Sep 17 00:00:00 2001 From: Yeni Date: Thu, 27 Oct 2016 18:01:09 -0700 Subject: [PATCH 119/144] removed retire button from product show page. --- app/views/products/show.html.erb | 5 ----- 1 file changed, 5 deletions(-) diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb index 39ddf880e0..21bfea7dea 100644 --- a/app/views/products/show.html.erb +++ b/app/views/products/show.html.erb @@ -37,11 +37,6 @@ <% end %> <%= link_to "Add to cart", add_cart_path(@product), class: "hollow button" %> - <% if @product.active %> - <%= link_to "RETIRE PRODUCT", retire_product_path(@product), remote: true, method: "patch", class: "warning button" %> - <% else %> - <%= link_to "ACTIVATE PRODUCT", retire_product_path(@product), remote: true, method: "patch", class: "warning button" %> - <% end %>
    From 542a850d2e85a836b4dafc54cf054e24c2954677 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 27 Oct 2016 19:27:37 -0700 Subject: [PATCH 120/144] rewrote products controller test for login --- app/controllers/application_controller.rb | 1 + app/controllers/categories_controller.rb | 1 - app/controllers/products_controller.rb | 122 +++++++++---------- app/views/products/manage.html.erb | 6 +- test/controllers/products_controller_test.rb | 79 ++++++------ test/fixtures/products.yml | 6 +- 6 files changed, 111 insertions(+), 104 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5fb840346d..de7ba54930 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,6 +3,7 @@ class ApplicationController < ActionController::Base # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception helper_method :shopping_cart, :total + before_action :require_login private def current_user diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index cbc37b27c4..d18f8c6af8 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -1,5 +1,4 @@ class CategoriesController < ApplicationController - # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. skip_before_action :require_login def index diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 5664e0a9ce..367ef40cbb 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,84 +1,80 @@ class ProductsController < ApplicationController - before_action :find_product, only: [:show, :edit, :update, :destroy] + before_action :find_product, except: [:index, :new, :create] + skip_before_action :require_login, only: [:index, :show] - # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. - skip_before_action :require_login, only: [:show, :index] + def index + @products = Product.where(active:true) + # @order_item = current_order.order_items.new + end + def show; end - def index - @products = Product.where(active:true) - # @merchant_id = session[:user_id] - # @merchant_products = Product.where(merchant_id: @merchant_id) - # @order_item = current_order.order_items.new - end - - def manage - @products = Product.where(merchant_id: session[:user_id]) - end + def new + @product = Product.new + end - def show; end - - # should be limited to merchants - def new - @product = Product.new + def create + @product = Product.new(product_params) + @product.merchant_id = session[:user_id] + if @product.save + redirect_to product_path(@product) + else + render :new end + end - def create - @product = Product.new(product_params) - @product.merchant_id = session[:user_id] - if @product.save - redirect_to product_path(@product) - else - render :new - end + def edit + if @product.merchant_id == session[:user_id] + render :edit + else + flash[:error] = "Please note: You are only allowed to edit your own products." + redirect_to manage_products_path end + end - def edit - unless @product.merchant_id == session[:user_id] - flash[:error] = "Please note: You are only allowed to edit your own products." - redirect_to manage_products_path - end + def update + if @product.update(product_params) + redirect_to product_path(@product) + else + render :edit end + end - def update - if @product.update(product_params) - redirect_to product_path(@product) - else - render :edit - end + def destroy + unless @product.merchant_id == session[:user_id] + flash[:error] = "Please note: You are only allowed to delete your own products." + # redirect_to manage_products_path end + @product.destroy + redirect_to portal_path + end - def retire - if @product.active - @product.active = false - else - @product.active = true - end - @product.save - redirect_to portal_path - end + def manage + @products = Product.where(merchant_id: session[:user_id]) + end - def destroy - unless @product.merchant_id == session[:user_id] - flash[:error] = "Please note: You are only allowed to delete your own products." - redirect_to manage_products_path - end - @product.destroy - redirect_to portal_path + def retire + if @product.active + @product.active = false + else + @product.active = true end + @product.save + redirect_to portal_path + end + private - private - def product_params - params.require(:product).permit(:name, :description, :stock, :price,:photo_url, :merchant_id) - end + def product_params + params.require(:product).permit(:name, :description, :stock, :price,:photo_url, :merchant_id) + end - def find_product - begin - @product = Product.find(params[:id]) - rescue ActiveRecord::RecordNotFound - render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found - end + def find_product + begin + @product = Product.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found end + end end diff --git a/app/views/products/manage.html.erb b/app/views/products/manage.html.erb index 43b07bc095..a3ac8af97c 100644 --- a/app/views/products/manage.html.erb +++ b/app/views/products/manage.html.erb @@ -11,10 +11,10 @@

    <%= capitalize_each_word(product.name) %>

    <%= link_to "EDIT", edit_product_path(product) %>

    CATEGORIZE

    -

    <% if @product.active %> - <%= link_to "RETIRE PRODUCT", retire_product_path(@product), remote: true, method: "patch", class: "warning button" %> +

    <% if product.active %> + <%= link_to "RETIRE PRODUCT", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% else %> - <%= link_to "ACTIVATE PRODUCT", retire_product_path(@product), remote: true, method: "patch", class: "warning button" %> + <%= link_to "ACTIVATE PRODUCT", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% end %>

    diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index 095bc3e192..663f3f796f 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -25,6 +25,7 @@ class ProductsControllerTest < ActionController::TestCase end test "should get the new form" do + merchant_id = merchants(:doctor).id get :new assert_template :new assert_template partial: '_form' @@ -32,9 +33,10 @@ class ProductsControllerTest < ActionController::TestCase end test "create should add a new product to the database" do + user = merchants(:doctor) post_params = {product: {name: "dog sunglasses", description: "too cool!", price: 549, stock: 10}} assert_difference("Product.count", 1) do - post :create, post_params + post :create, post_params, {user_id: user.id} end product = assigns(:product) @@ -42,102 +44,111 @@ class ProductsControllerTest < ActionController::TestCase end test "product with no name can't change the database" do + user = merchants(:doctor) post_params = {product: {description: "too cool!", price: 549, stock: 10}} assert_no_difference("Product.count") do - post :create, post_params + post :create, post_params, {user_id: user.id} end assert_template :new end test "product with no price can't change the database" do + user = merchants(:doctor) post_params = {product: {name: "dog sunglasses", description: "too cool!", stock: 10}} assert_no_difference("Product.count") do - post :create, post_params + post :create, post_params, {user_id: user.id} end assert_template :new end test "product with no stock can't change the database" do + user = merchants(:doctor) post_params = {product: {name: "dog sunglasses", description: "too cool!"}} assert_no_difference("Product.count") do - post :create, post_params + post :create, post_params, {user_id: user.id} end assert_template :new end test "should get the product edit form" do - product_id = products(:cat_suit).id - get :edit, {id: product_id} + product = products(:cat_suit) + user_id = product.merchant_id + get :edit, {id: product.id}, {user_id: user_id} assert_template :edit assert_template partial: '_form' assert_response :success - product = assigns(:product) - assert_not_nil product - assert_equal product.id, product_id + # product = assigns(:product) + # assert_not_nil product + # assert_equal product.id, product_id end test "update should change the product" do - product_id = products(:cat_suit).id - patch :update, {id: product_id, product: {name: "cat fancy suit"}} - assert_equal "cat fancy suit", Product.find(product_id).name + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :update, {id: product.id, product: {name: "cat fancy suit"}}, {user_id: merchant_id} + assert_equal "cat fancy suit", Product.find(product.id).name assert_redirected_to product_path end test "update should not allow empty name" do - product_id = products(:cat_suit).id - patch :update, {id: product_id, product: {name: nil}} - assert_equal "cat suit", Product.find(product_id).name + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :update, {id: product.id, product: {name: nil}}, {user_id: merchant_id} + assert_equal "cat suit", Product.find(product.id).name assert_template :edit end test "update should not allow invalid price" do - product_id = products(:cat_suit).id - patch :update, {id: product_id, product: {price: 0}} - assert_equal 1234, Product.find(product_id).price + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :update, {id: product.id, product: {price: 0}}, {user_id: merchant_id} + assert_equal 1234, Product.find(product.id).price assert_template :edit end test "update should not allow empty stock" do - product_id = products(:cat_suit).id - patch :update, {id: product_id, product: {stock: nil}} - assert_equal 4, Product.find(product_id).stock + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :update, {id: product.id, product: {stock: nil}}, {user_id: merchant_id} + assert_equal 4, Product.find(product.id).stock assert_template :edit end test "destroy should delete the product" do - product_id = products(:cat_suit).id + product = products(:cat_suit) + merchant_id = product.merchant_id assert_difference("Product.count", -1) do - delete :destroy, {id: product_id} + delete :destroy, {id: product.id}, {user_id: merchant_id} end assert_raises ActiveRecord::RecordNotFound do - Product.find(product_id) + Product.find(product.id) end assert_redirected_to portal_path end test "retire should retire the product if the product is active" do - product_id = products(:cat_suit).id - patch :retire, {id: product_id} + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :retire, {id: product.id}, {user_id: merchant_id} - product = assigns(:product) - assert_equal product.id, product_id - assert_equal false, product.active + assert_equal false, Product.find(product.id).active assert_redirected_to portal_path end test "retire should activate the product if the product is retired" do - product_id = products(:cat_suit).id - patch :retire, {id: product_id} - assert_equal false, Product.find(product_id).active - patch :retire, {id: product_id} - assert_equal true, Product.find(product_id).active + product = products(:cat_suit) + merchant_id = product.merchant_id + patch :retire, {id: product.id}, {user_id: merchant_id} + assert_equal false, Product.find(product.id).active + patch :retire, {id: product.id}, {user_id: merchant_id} + assert_equal true, Product.find(product.id).active end end diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml index e6ccc0ce1c..441731bfb8 100644 --- a/test/fixtures/products.yml +++ b/test/fixtures/products.yml @@ -5,7 +5,7 @@ cat_suit: stock: 4 photo_url: http://placekitten.com/200/300 categories: cat, formal_wear, mammals - merchant: 1234 + merchant: hilarious active: true hamster_monocle: @@ -15,7 +15,7 @@ hamster_monocle: stock: 20 photo_url: http://placekitten.com/200/300 categories: hamster, eyewear, mammals - merchant: 1234 + merchant: hilarious active: true dog_bunny_ears: @@ -24,5 +24,5 @@ dog_bunny_ears: description: make your dog look like a bunny with these adorable bunny ears! stock: 1 photo_url: http://placekitten.com/200/300 - merchant: 2341 + merchant: doctor active: true From 87a5c213b79d7ba15a6afead38ffa98380e5d01a Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 27 Oct 2016 20:03:26 -0700 Subject: [PATCH 121/144] added test for products controller, edited before_action for login on all controllers --- app/controllers/application_controller.rb | 2 +- app/controllers/categories_controller.rb | 1 - app/controllers/home_controller.rb | 3 --- app/controllers/merchants_controller.rb | 6 +----- app/controllers/products_controller.rb | 4 ++-- app/controllers/reviews_controller.rb | 5 +---- app/controllers/sessions_controller.rb | 3 +-- config/routes.rb | 4 +++- test/controllers/products_controller_test.rb | 21 +++++++++++++------- test/controllers/reviews_controller_test.rb | 2 +- 10 files changed, 24 insertions(+), 27 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index de7ba54930..dbdd9904d1 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -3,7 +3,7 @@ class ApplicationController < ActionController::Base # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception helper_method :shopping_cart, :total - before_action :require_login + # before_action :require_login private def current_user diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index d18f8c6af8..25fc477112 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -1,5 +1,4 @@ class CategoriesController < ApplicationController - skip_before_action :require_login def index @categories = Category.all.order(:name) diff --git a/app/controllers/home_controller.rb b/app/controllers/home_controller.rb index 441d960fb9..16acad6abb 100644 --- a/app/controllers/home_controller.rb +++ b/app/controllers/home_controller.rb @@ -1,7 +1,4 @@ class HomeController < ApplicationController - # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. - skip_before_action :require_login - def index @products = Product.all.shuffle.first(4) end diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb index c9f6b6c09d..e14528ae32 100644 --- a/app/controllers/merchants_controller.rb +++ b/app/controllers/merchants_controller.rb @@ -1,8 +1,5 @@ class MerchantsController < ApplicationController - # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. - skip_before_action :require_login, only: [:index, :show] - def index @merchants = Merchant.all.order(:user_name) end @@ -15,9 +12,8 @@ def show end end - private - + def merchant_params params.require(:merchant).permit(:user_name, :email, :uid, :provider) end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 367ef40cbb..1d91cde5d2 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,10 +1,10 @@ class ProductsController < ApplicationController before_action :find_product, except: [:index, :new, :create] - skip_before_action :require_login, only: [:index, :show] + before_action :require_login, except: [:index, :show] def index @products = Product.where(active:true) - # @order_item = current_order.order_items.new + # @order_item = current_order.order_items.new end def show; end diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb index 3e15cebde5..06ec5b9a82 100644 --- a/app/controllers/reviews_controller.rb +++ b/app/controllers/reviews_controller.rb @@ -1,8 +1,5 @@ class ReviewsController < ApplicationController - - # Will likely need to remove this line (or further customize this), once we've narrowed down which pages require login. This line allows our tests to pass. - skip_before_action :require_login - + def new @product = Product.find(params[:product_id]) @review = Review.new diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 0f0f46d144..908a491000 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -1,6 +1,5 @@ class SessionsController < ApplicationController -# If we want to implement this later, will need to customize authorization specifics... - skip_before_action :require_login, only: [:login, :create] + before_action :require_login, except: [:login, :create] def login_failure; end diff --git a/config/routes.rb b/config/routes.rb index cc7af7f0c8..c4cccc37f9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,12 +5,14 @@ resources :merchants, only: [:index, :show] # We want logged in merchants to be able to view ALL products in an index--and to manage only their products, so I'm adding in a products#manage Controller action to create this: - get '/products/manage', to: 'products#manage', as: 'manage_products' + resources :products do resources :reviews, only: [:new, :create] end + get 'products/manage', to: 'products#manage', as: 'manage_products' + patch 'products/:id/retire' => 'products#retire', as: :retire_product resources :orders diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index 663f3f796f..d67128e8fb 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -25,8 +25,8 @@ class ProductsControllerTest < ActionController::TestCase end test "should get the new form" do - merchant_id = merchants(:doctor).id - get :new + merchant = merchants(:doctor) + get :new, {user_id: merchant.id} assert_template :new assert_template partial: '_form' assert_response :success @@ -72,15 +72,22 @@ class ProductsControllerTest < ActionController::TestCase test "should get the product edit form" do product = products(:cat_suit) - user_id = product.merchant_id - get :edit, {id: product.id}, {user_id: user_id} + merchant_id = product.merchant_id + get :edit, {id: product.id}, {user_id: merchant_id} assert_template :edit assert_template partial: '_form' assert_response :success - # product = assigns(:product) - # assert_not_nil product - # assert_equal product.id, product_id + product = assigns(:product) + assert_not_nil product + assert_equal product.id, product.id + end + + test "merchant should not be able to edit another merchant's product" do + product = products(:cat_suit) + merchant = merchants(:doctor) + get :edit, {id: product.id}, {user_id: merchant.id} + assert_redirected_to manage_products_path end test "update should change the product" do diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb index f0d82fd179..2661e4c423 100644 --- a/test/controllers/reviews_controller_test.rb +++ b/test/controllers/reviews_controller_test.rb @@ -16,7 +16,7 @@ class ReviewsControllerTest < ActionController::TestCase product = products(:cat_suit) review = { review: {rating: 1, description: "ugh"}, product_id: product.id } - assert_difference("Review.count") do + assert_difference("Review.count", 1) do post :create, review end From 91f31eb1380399339db1af788ab94f37c103b7ce Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 20:29:12 -0700 Subject: [PATCH 122/144] products controller: find_product before action IS NOT on the manage action now --- app/controllers/products_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 1d91cde5d2..d19eb493f4 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,5 +1,5 @@ class ProductsController < ApplicationController - before_action :find_product, except: [:index, :new, :create] + before_action :find_product, except: [:index, :new, :manage, :create] before_action :require_login, except: [:index, :show] def index From 89617de7a9b3e193e9d59a277ef9127f2e01e4dd Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Thu, 27 Oct 2016 20:31:41 -0700 Subject: [PATCH 123/144] able to create an order but working on how to display on for a merchant --- app/controllers/orders_controller.rb | 12 ++++++++++-- app/views/carts/index.html.erb | 2 -- app/views/orders/manage_orders.html.erb | 4 ++++ app/views/sessions/index.html.erb | 2 +- config/routes.rb | 1 + 5 files changed, 16 insertions(+), 5 deletions(-) create mode 100644 app/views/orders/manage_orders.html.erb diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 56513fcbce..60d07c3063 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -22,18 +22,26 @@ def create @order_item.order_id = @order.id @order_item.save end + session[:cart] = nil redirect_to orders_path else - raise render :new end end def update + @order = Order.find(params[:id]) - if @order.update.save + end + + def manage_orders + @orders = Order.all + @product = Product.find_by(merchant_id: session[:user_id]) + @product.each do |prod| + @order_item = OrderItem.find_by(product_id: prod.id) end + @order end def destroy diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 21a8777397..b821a1c401 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -2,8 +2,6 @@

    Your Cart




    - -
    • <%@products.each do |product, quantity|%> diff --git a/app/views/orders/manage_orders.html.erb b/app/views/orders/manage_orders.html.erb new file mode 100644 index 0000000000..073b761ddd --- /dev/null +++ b/app/views/orders/manage_orders.html.erb @@ -0,0 +1,4 @@ +

      Manage IT!

      + +<%@order_item.each do |item| %> +<%= @order_item. diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb index ea7b059966..9b40b47b8c 100644 --- a/app/views/sessions/index.html.erb +++ b/app/views/sessions/index.html.erb @@ -18,7 +18,7 @@
    • -

      <%= link_to "Manage your orders (currently fake link)", new_product_path %>

      +

      <%= link_to "Manage your orders", manage_orders_path %>

    diff --git a/config/routes.rb b/config/routes.rb index cc7af7f0c8..4e4e9fd571 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,7 @@ # We want logged in merchants to be able to view ALL products in an index--and to manage only their products, so I'm adding in a products#manage Controller action to create this: get '/products/manage', to: 'products#manage', as: 'manage_products' + get 'orders/manage', to: 'orders#manage', as: 'manage_orders' resources :products do resources :reviews, only: [:new, :create] From 5ee977d9018ed409b356a89181e81bfd2659be7f Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Thu, 27 Oct 2016 21:02:31 -0700 Subject: [PATCH 124/144] we can now add a new category as a signed in merchant --- app/controllers/categories_controller.rb | 22 +++++++++++++ app/views/categories/new.html.erb | 42 ++++++++++++++++++++++++ app/views/products/manage.html.erb | 2 +- app/views/sessions/index.html.erb | 2 +- 4 files changed, 66 insertions(+), 2 deletions(-) create mode 100644 app/views/categories/new.html.erb diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 25fc477112..65e92ac549 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -1,4 +1,5 @@ class CategoriesController < ApplicationController + before_action :require_login, only: [:new, :create] def index @categories = Category.all.order(:name) @@ -12,4 +13,25 @@ def show render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found end end + + def new + @category = Category.new + @categories = Category.all.order(:name) + end + + def create + @category = Category.new(category_params) + if @category.save + redirect_to categories_path + else + render :new + end + end + + + private + + def category_params + params.require(:category).permit(:name) + end end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000000..b0c24a3a98 --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,42 @@ +

    Create a category:

    +Categories must have unique names and cannot be deleted. + +<% if flash[:error] %> +

    <%= flash[:error] %>

    +<% end %> + +
    + <% if @category.errors.any? %> +
      + <% @category.errors.each do |column, message| %> +
    • + + <%= column.capitalize %> + <%= message %> +
    • + <% end %> +
    + <% end %> + + <%= form_for @category, url: categories_path do |f| %> +

    + <%= f.label :name %> + <%= f.text_field :name %> +

    + +

    + <%= f.submit "Submit" %> +

    + <% end %> +
    + +

    Here's a list of current categories...

    +
      +<% @categories.each do |category| %> +
    • +
      +

      <%= category.name %>

      +
      +
    • +<%end%> +
    diff --git a/app/views/products/manage.html.erb b/app/views/products/manage.html.erb index a3ac8af97c..a75ef2ce16 100644 --- a/app/views/products/manage.html.erb +++ b/app/views/products/manage.html.erb @@ -1,4 +1,4 @@ -

    Product management

    +

    Product management:

    <% if flash[:error] %>

    <%= flash[:error] %>

    diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb index ea7b059966..29f8f7c172 100644 --- a/app/views/sessions/index.html.erb +++ b/app/views/sessions/index.html.erb @@ -13,7 +13,7 @@
  • -

    <%= link_to "Create a new category (currently fake link)", new_product_path %>

    +

    <%= link_to "Create a new category", new_category_path %>

  • From 6ca6e72f03460466b3b6f35c6c624c8b53c09663 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Thu, 27 Oct 2016 21:10:34 -0700 Subject: [PATCH 125/144] tests for merchants controller, products, controller, and reviews controller; WIP sessions controller --- app/controllers/products_controller.rb | 2 +- config/routes.rb | 4 ++-- test/controllers/merchants_controller_test.rb | 6 ++++++ test/controllers/products_controller_test.rb | 2 +- test/controllers/reviews_controller_test.rb | 9 +++++++++ test/controllers/sessions_controller_test.rb | 9 +++++++++ 6 files changed, 28 insertions(+), 4 deletions(-) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index 1d91cde5d2..b9e845de58 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -1,5 +1,5 @@ class ProductsController < ApplicationController - before_action :find_product, except: [:index, :new, :create] + before_action :find_product, except: [:index, :new, :create, :manage] before_action :require_login, except: [:index, :show] def index diff --git a/config/routes.rb b/config/routes.rb index c4cccc37f9..25428944b6 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -11,9 +11,9 @@ resources :reviews, only: [:new, :create] end - get 'products/manage', to: 'products#manage', as: 'manage_products' + get '/products/manage' => 'products#manage', as: :manage_products - patch 'products/:id/retire' => 'products#retire', as: :retire_product + patch '/products/:id/retire' => 'products#retire', as: :retire_product resources :orders diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb index 19b0b41149..7defa7c070 100644 --- a/test/controllers/merchants_controller_test.rb +++ b/test/controllers/merchants_controller_test.rb @@ -1,6 +1,12 @@ require 'test_helper' class MerchantsControllerTest < ActionController::TestCase + test "should get the index page" do + get :index + assert_template :index + assert_response :success + end + test "show the individual merchant page" do merchant_id = merchants(:hilarious).id diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index d67128e8fb..9d774c187c 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -1,7 +1,7 @@ require 'test_helper' class ProductsControllerTest < ActionController::TestCase - test "should show the index page" do + test "should get the index page" do get :index assert_template :index assert_response :success diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb index 2661e4c423..24ff2074e0 100644 --- a/test/controllers/reviews_controller_test.rb +++ b/test/controllers/reviews_controller_test.rb @@ -34,4 +34,13 @@ class ReviewsControllerTest < ActionController::TestCase assert_template :new end + + test "should not be able to review one's own product" do + product = products(:cat_suit) + merchant_id = product.merchant_id + + get :new, {product_id: product.id}, {user_id: merchant_id} + + assert_redirected_to product_path(product) + end end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb index f839fe5faf..b76f3f77bf 100644 --- a/test/controllers/sessions_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -18,7 +18,9 @@ def login_a_user assert_difference('Merchant.count', 1) do login_a_user end + assert_not_nil session[:user_id] + assert_no_difference('Merchant.count') do login_a_user assert_response :redirect @@ -26,4 +28,11 @@ def login_a_user assert_not_nil session[:user_id] end end + + # test "a logged-in user can view the index" do + # login_a_user + # assert_not_nil session[:user_id] + # assert_response :success + # assert_template :index + # end end From c129fb3961d01f64691fd78dee04e7b0437bcbb5 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 08:34:09 -0700 Subject: [PATCH 126/144] products#manage route needs to stay above the resources_products routes so that our pages work correctly, due to ordering --- config/routes.rb | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/config/routes.rb b/config/routes.rb index c4cccc37f9..ab57f58368 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -5,14 +5,12 @@ resources :merchants, only: [:index, :show] # We want logged in merchants to be able to view ALL products in an index--and to manage only their products, so I'm adding in a products#manage Controller action to create this: - + get 'products/manage', to: 'products#manage', as: 'manage_products' resources :products do resources :reviews, only: [:new, :create] end - get 'products/manage', to: 'products#manage', as: 'manage_products' - patch 'products/:id/retire' => 'products#retire', as: :retire_product resources :orders From 0446f3f388df1a76688edd06ea6dda97aadb9689 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 08:51:38 -0700 Subject: [PATCH 127/144] Have a page to let user know about order --- app/controllers/orders_controller.rb | 1 - app/views/orders/index.html.erb | 6 ++++-- test/controllers/carts_controller_test.rb | 5 ++++- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 60d07c3063..af5b442de9 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -31,7 +31,6 @@ def create def update - @order = Order.find(params[:id]) end diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb index d63a69fb54..c718dd8962 100644 --- a/app/views/orders/index.html.erb +++ b/app/views/orders/index.html.erb @@ -1,2 +1,4 @@ -

    Orders#index

    -

    Find me in app/views/orders/index.html.erb

    +<%@orders.each do |order|%> +

    Thank you for your order <%="#{order.cc_name}"%>!

    +

    Your order number is <%= "#{order.id}" %>

    + <%end%> diff --git a/test/controllers/carts_controller_test.rb b/test/controllers/carts_controller_test.rb index f3bb6a94a8..9ae6b6dc6f 100644 --- a/test/controllers/carts_controller_test.rb +++ b/test/controllers/carts_controller_test.rb @@ -4,6 +4,9 @@ class CartsControllerTest < ActionController::TestCase # test "the truth" do # assert true # end +setup do + @cart = session[:cart] + end + - end From c14def947589810d99d9d3f0ece9172d47ab655a Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 09:26:20 -0700 Subject: [PATCH 128/144] added a new seed for Dan --- db/seeds.rb | 393 +++++++++++++++++++++++++++------------------------- 1 file changed, 202 insertions(+), 191 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 8bf4f66c00..33ffb93f8b 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -37,201 +37,209 @@ ] merchants.each do |merchant| - Merchant.create(merchant) + Merchant.create(merchant) end products = [ - { - name: "cat pants", - description: "a nice pair of slacks with an elastic waist", - stock: 15, - price: 850, - photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg", - merchant_id: 1 - }, - { - name: "dog booties", - description: "a nice pair of slacks with an elastic waist", - stock: 40, - price: 2026, - photo_url: "http://s7d1.scene7.com/is/image/PETCO/2410644-center-1?$ProductList-medium$", - merchant_id: 1 - }, - { - name: "santa gp", - description: "For the guinea pig who loves cookies and milk!", - stock: 90, - price: 1886, - photo_url: "http://i.huffpost.com/gadgets/slideshows/318566/slide_318566_2975396_free.jpg", - merchant_id: 1 - }, - { - name: "Pirate Pig", - description: "rrrrrrr, matey! Oink!", - stock: 40, - price: 8678, - photo_url: "http://www.whenpigsflynaked.com/uploads/1/1/9/8/11981007/2001761.jpg?276", - merchant_id: 1 - }, - { - name: "llama scarf", - description: "long and warm and not made with other llamas", - stock: 20, - price: 1000, - photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg", - merchant_id: 2 - }, - { - name: "Freddy the Frog - pig", - description: "for the pig who longs to be a frog", - stock: 200, - price: 4898, - photo_url: "https://s-media-cache-ak0.pinimg.com/236x/82/cf/6a/82cf6a68cb2a7ee1ecf461efcb445a78.jpg", - merchant_id: 2 - }, - { - name: "pig hoodie", - description: "comfortable fleece-lined hoodie for your mini pig", - stock: 88, - price: 4204, - photo_url: "http://66.media.tumblr.com/tumblr_m0h5qg83CY1rr5f7co1_500.jpg", - merchant_id: 2 - }, - { - name: "hedgehog bagpipes", - description: "For your hedgehog who loves to Scottish jigs", - stock: 20, - price: 2695, - photo_url: "https://s-media-cache-ak0.pinimg.com/564x/29/8c/a4/298ca4654d55ffbe01aa4afa3a9e66c8.jpg", - merchant_id: 2 - }, - { - name: "pumpkin gp", - description: "this little piggy is ready to go trick-or-treating", - stock: 60, - price: 1995, - photo_url: "http://s.marketwatch.com/public/resources/MWimages/MW-BN838_pfpets_MG_20131024180004.jpg", - merchant_id: 2 - }, - { - name: "penguin hawaiian shirt", - description: "For the penguin who wants to get away", - stock: 4, - price: 2000, - photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg", - merchant_id: 2 - }, - { - name: "beaver pipes", - description: "For the beaver who's roots are in Scotland", - stock: 26, - price: 2804, - photo_url: "https://s-media-cache-ak0.pinimg.com/236x/e1/36/a7/e136a71ba59f26a4f47bb6e34d2129b5.jpg", - merchant_id: 3 - }, - { - name: "stylish puffy jacket", - description: "For the dog who gets a little chilly", - stock: 26, - price: 48000, - photo_url: "http://www.dogsmartway.com/upload/product_ad_1881.jpg", - merchant_id: 3 - }, - { - name: "walk the plank", - description: "For the cat who's always in charge", - stock: 40, - price: 6000, - photo_url: "https://d8s0dvdlqz4n8.cloudfront.net/wp-content/uploads/2015/03/Pirate-Cat-Costume1.jpg", - merchant_id: 3 - }, - { - name: "active stripes", - description: "For the dog who can't wait to hit the track", - stock: 80, - price: 2200, - photo_url: "https://img0.etsystatic.com/138/0/10819873/il_570xN.1019173514_9lsr.jpg", - merchant_id: 3 - }, - { - name: "top hat for dogs", - description: "For the dog who likes nice things", - stock: 30, - price: 500, - photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png", - merchant_id: 3 - }, - { - name: "Pig Cowboy Hat", - description: "Yeehawg! Get along, lil piggy!", - stock: 20, - price: 400, - photo_url: "https://67.media.tumblr.com/tumblr_m8bbiv8kil1r9uia2o1_1280.jpg", - merchant_id: 3 - }, - { - name: "piglet dance outfit", - description: "For the lil piggies who just wanna dance!", - stock: 12, - price: 3050, - photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg", - merchant_id: 3 - }, - { - name: "weiner dog bobsled", - description: "For the dog who craves speed and adventure", - stock: 12, - price: 36800, - photo_url: "http://cdn3-www.dogtime.com/assets/uploads/gallery/wiener-dog-halloween-costumes/8-wiener-dog-bobsled-team.jpg", - merchant_id: 3 - }, - { - name: "Nyan Cat", - description: "A pop tart cat inspired costume for your cat to nyan around the house in. Made out of materials that won't harm your pet and safe to lay in as well.", - stock: 150, - price: 5000, - photo_url: "https://img0.etsystatic.com/139/0/10302358/il_570xN.1030148796_77jh.jpg", - merchant_id: 4 - }, - { - name: "Aye aye Captain", - description: "For the cat who longs to be at sea.", - stock: 60, - price: 1800, - photo_url: "http://www.momdoesreviews.com/wp-content/uploads/2015/10/Sailor-Dog-Cat-Costume.jpg", - merchant_id: 4 - }, - { - name: "Minnie Pig", - description: "For the pig who dreams of the magical land!", - stock: 40, - price: 11800, - photo_url: "http://i.mobofree.com/?u=http%3A%2F%2Flostininternet.com%2Fwp-content%2Fuploads%2F2014%2F10%2FPoppy-and-priscilla-piglets-01.jpg&w=600&h=1500", - merchant_id: 4 - }, - { - name: "Baby Bunny outfit", - description: "Are you a new mommy? Dress your little hopper in style!", - stock: 4, - price: 1900, - photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg", - merchant_id: 4 - } + { + name: "cat pants", + description: "a nice pair of slacks with an elastic waist", + stock: 15, + price: 850, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/72/77/86/7277863e594b27677d2228101ef7e293.jpg", + merchant_id: 1 + }, + { + name: "dog booties", + description: "a nice pair of slacks with an elastic waist", + stock: 40, + price: 2026, + photo_url: "http://s7d1.scene7.com/is/image/PETCO/2410644-center-1?$ProductList-medium$", + merchant_id: 1 + }, + { + name: "santa gp", + description: "For the guinea pig who loves cookies and milk!", + stock: 90, + price: 1886, + photo_url: "http://i.huffpost.com/gadgets/slideshows/318566/slide_318566_2975396_free.jpg", + merchant_id: 1 + }, + { + name: "Pirate Pig", + description: "rrrrrrr, matey! Oink!", + stock: 40, + price: 8678, + photo_url: "http://www.whenpigsflynaked.com/uploads/1/1/9/8/11981007/2001761.jpg?276", + merchant_id: 1 + }, + { + name: "llama scarf", + description: "long and warm and not made with other llamas", + stock: 20, + price: 1000, + photo_url: "http://www.yesandyes.org/wp-content/uploads/2011/01/scarf5.jpg", + merchant_id: 2 + }, + { + name: "Freddy the Frog - pig", + description: "for the pig who longs to be a frog", + stock: 200, + price: 4898, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/82/cf/6a/82cf6a68cb2a7ee1ecf461efcb445a78.jpg", + merchant_id: 2 + }, + { + name: "pig hoodie", + description: "comfortable fleece-lined hoodie for your mini pig", + stock: 88, + price: 4204, + photo_url: "http://66.media.tumblr.com/tumblr_m0h5qg83CY1rr5f7co1_500.jpg", + merchant_id: 2 + }, + { + name: "hedgehog bagpipes", + description: "For your hedgehog who loves to Scottish jigs", + stock: 20, + price: 2695, + photo_url: "https://s-media-cache-ak0.pinimg.com/564x/29/8c/a4/298ca4654d55ffbe01aa4afa3a9e66c8.jpg", + merchant_id: 2 + }, + { + name: "pumpkin gp", + description: "this little piggy is ready to go trick-or-treating", + stock: 60, + price: 1995, + photo_url: "http://s.marketwatch.com/public/resources/MWimages/MW-BN838_pfpets_MG_20131024180004.jpg", + merchant_id: 2 + }, + { + name: "penguin hawaiian shirt", + description: "For the penguin who wants to get away", + stock: 4, + price: 2000, + photo_url: "https://s-media-cache-ak0.pinimg.com/originals/bf/8a/c8/bf8ac87e3ae2ff56ec7db987edd60d7d.jpg", + merchant_id: 2 + }, + { + name: "beaver pipes", + description: "For the beaver who's roots are in Scotland", + stock: 26, + price: 2804, + photo_url: "https://s-media-cache-ak0.pinimg.com/236x/e1/36/a7/e136a71ba59f26a4f47bb6e34d2129b5.jpg", + merchant_id: 3 + }, + { + name: "stylish puffy jacket", + description: "For the dog who gets a little chilly", + stock: 26, + price: 48000, + photo_url: "http://www.dogsmartway.com/upload/product_ad_1881.jpg", + merchant_id: 3 + }, + { + name: "walk the plank", + description: "For the cat who's always in charge", + stock: 40, + price: 6000, + photo_url: "https://d8s0dvdlqz4n8.cloudfront.net/wp-content/uploads/2015/03/Pirate-Cat-Costume1.jpg", + merchant_id: 3 + }, + { + name: "active stripes", + description: "For the dog who can't wait to hit the track", + stock: 80, + price: 2200, + photo_url: "https://img0.etsystatic.com/138/0/10819873/il_570xN.1019173514_9lsr.jpg", + merchant_id: 3 + }, + { + name: "top hat for dogs", + description: "For the dog who likes nice things", + stock: 30, + price: 500, + photo_url: "http://2damnfunny.com/wp-content/uploads/2013/12/Classy-Dog-With-a-Fancy-Top-Hat-a-Rich-Mahogany-Smoke-Pipe.png", + merchant_id: 3 + }, + { + name: "Pig Cowboy Hat", + description: "Yeehawg! Get along, lil piggy!", + stock: 20, + price: 400, + photo_url: "https://67.media.tumblr.com/tumblr_m8bbiv8kil1r9uia2o1_1280.jpg", + merchant_id: 3 + }, + { + name: "piglet dance outfit", + description: "For the lil piggies who just wanna dance!", + stock: 12, + price: 3050, + photo_url: "http://www.fashionworld.co.uk/blog/wp-content/uploads/2016/01/Cute-Animals-Wearing-Clothes-Pigs-in-Tutus-Playing-Pets-25.jpg", + merchant_id: 3 + }, + { + name: "weiner dog bobsled", + description: "For the dog who craves speed and adventure", + stock: 12, + price: 36800, + photo_url: "http://cdn3-www.dogtime.com/assets/uploads/gallery/wiener-dog-halloween-costumes/8-wiener-dog-bobsled-team.jpg", + merchant_id: 3 + }, + { + name: "Nyan Cat", + description: "A pop tart cat inspired costume for your cat to nyan around the house in. Made out of materials that won't harm your pet and safe to lay in as well.", + stock: 150, + price: 5000, + photo_url: "https://img0.etsystatic.com/139/0/10302358/il_570xN.1030148796_77jh.jpg", + merchant_id: 4 + }, + { + name: "Aye aye Captain", + description: "For the cat who longs to be at sea.", + stock: 60, + price: 1800, + photo_url: "http://www.momdoesreviews.com/wp-content/uploads/2015/10/Sailor-Dog-Cat-Costume.jpg", + merchant_id: 4 + }, + { + name: "Minnie Pig", + description: "For the pig who dreams of the magical land!", + stock: 40, + price: 11800, + photo_url: "http://i.mobofree.com/?u=http%3A%2F%2Flostininternet.com%2Fwp-content%2Fuploads%2F2014%2F10%2FPoppy-and-priscilla-piglets-01.jpg&w=600&h=1500", + merchant_id: 4 + }, + { + name: "Baby Bunny outfit", + description: "Are you a new mommy? Dress your little hopper in style!", + stock: 4, + price: 1900, + photo_url: "http://4.bp.blogspot.com/-hZqBuA0W9W8/Uda9I5QVTiI/AAAAAAAABes/Ijk17zrUo-w/s500/tumblr_lsxp1kA2Ks1r4zdm8o1_500.jpg", + merchant_id: 4 + }, + { + name: "trendy mini hat", + description: "Halloween hat for geckos...", + stock: 46, + price: 1996, + photo_url: "https://s-media-cache-ak0.pinimg.com/564x/24/a8/d5/24a8d5ad50b163a11766e62af2cde5ce.jpg", + merchant_id: 4 + } ] products.each do |item| - Product.create(item) + Product.create(item) end -categories = ["pants", "booties", "santa", "pirate", "scarves", "frog", "hoodies", "bagpipes", "pumpkin", "shirts", "beaver", "jackets", "cat", "track suits", "hats", "cowboy hats", "tutus", "bobsleds", "nyan", "sailors", "minnie mouse", "bunny", "mammals", "birds"] +categories = ["pants", "booties", "santa", "pirate", "scarves", "frog", "hoodies", "bagpipes", "pumpkin", "shirts", "beaver", "jackets", "cat", "track suits", "hats", "cowboy hats", "tutus", "bobsleds", "nyan", "sailors", "minnie mouse", "bunny", "leopard print", "mammals", "birds", "amphibians"] # categories = ["cat", "pants", "dog", "booties", "santa", "guinea pig", "pirate", "pig", "llama", "scarves", "frog", "pig", "pig", "hoodies", "hedgehog", "bagpipes", "pumpkin", "guinea pig", "penguin", "shirts", "beaver", "bagpipes", "dog", "jackets", "pirate", "cat", "dog", "track suits", "hats", "dog", "pig", "cowboy hats", "pig", "tutus", "dog", "bobsleds", "nyan", "cat", "cat", "sailors", "pig", "minnie mouse", "baby", "bunny", "mammals", "birds"] categories.each do |category| - Category.create(name: category) + Category.create(name: category) end # My apologies Noelle, I'm adding in more seed product data, so that our views look more robust for our Friday demo. @@ -245,21 +253,24 @@ i = 0 j = 0 until i == all_products.size - product = all_products[i] - all_products[i].categories << all_categories[j] - # j += 1 - # all_products[i].categories << all_categories[j] - i+=1 - j += 1 + product = all_products[i] + all_products[i].categories << all_categories[j] + # j += 1 + # all_products[i].categories << all_categories[j] + i+=1 + j += 1 end birds = Category.find_by(name: "birds") +amphibians = Category.find_by(name: amphibians) mammals = Category.find_by(name: "mammals") all_products.each do |item| - if item.name == "penguin hawaiian shirt" - item.categories << birds - else - item.categories << mammals - end + if item.name == "penguin hawaiian shirt" + item.categories << birds + elsif item.name == "trendy mini hat" + item.categories << amphibians + else + item.categories << mammals + end end From 3241ec3f6ade4f0d035b6513674b658f6bc11dfc Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 09:39:03 -0700 Subject: [PATCH 129/144] made changes for the orders to display and playing with a total session --- app/controllers/application_controller.rb | 11 ++++++++--- app/controllers/carts_controller.rb | 2 +- app/controllers/orders_controller.rb | 9 ++++++--- app/views/carts/index.html.erb | 7 ++++--- app/views/orders/index.html.erb | 10 ++++++---- 5 files changed, 25 insertions(+), 14 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5fb840346d..e7ee999bb4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -20,9 +20,14 @@ def require_login end end - def total - @total = 0 - end + # def total + # if !session[:total].nil? + # session[:total] = @total + # else + # session[:total] = 0 + # @total = session[:total] + # end + # end def shopping_cart if !session[:cart].nil? diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 10c5990591..893f4b4b8f 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,7 +1,7 @@ class CartsController < ApplicationController before_action :shopping_cart before_action :product_ref, except: [:index, :empty_cart] - before_action :total + # before_action :total after_action :no_item, only: [:less_prod, :delete_product] def index diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index af5b442de9..7b310947d7 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,7 +1,7 @@ class OrdersController < ApplicationController before_action :total, :shopping_cart def index - @orders = Order.all + @order = session[:order] end def new @@ -15,13 +15,14 @@ def show def create @order = Order.new(order_params) if @order.save - @order_item = OrderItem.new @cart.each do |item| + @order_item = OrderItem.new @order_item.product_id = item.values[0] @order_item.quantity = item.values[1] @order_item.order_id = @order.id @order_item.save end + session[:order] = @order session[:cart] = nil redirect_to orders_path else @@ -35,7 +36,7 @@ def update end def manage_orders - @orders = Order.all + @order_items = OrderItems.all @product = Product.find_by(merchant_id: session[:user_id]) @product.each do |prod| @order_item = OrderItem.find_by(product_id: prod.id) @@ -51,4 +52,6 @@ def destroy def order_params params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address, :total, ) end + + end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index b821a1c401..170f9e809f 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -2,6 +2,7 @@

    Your Cart




    +<%total = 0%>
    • <%@products.each do |product, quantity|%> @@ -12,10 +13,10 @@ <%=link_to "Delete", delete_products_path(product.id), method: :delete%>

    • - <% @total += quantity * product.price %> + <% total += quantity * product.price %> <%end%> - <%if @total != 0%> -

      Total: <%=show_dollars(@total)%>

      + <%if total != 0%> +

      Total: <%=show_dollars(total)%>


      Continue shopping <%=link_to "Here", products_path %>

      <%=link_to "Purchase Now", new_order_path%>

      diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb index c718dd8962..87bd9b174f 100644 --- a/app/views/orders/index.html.erb +++ b/app/views/orders/index.html.erb @@ -1,4 +1,6 @@ -<%@orders.each do |order|%> -

      Thank you for your order <%="#{order.cc_name}"%>!

      -

      Your order number is <%= "#{order.id}" %>

      - <%end%> + +

      Thank you for your order <%="#{@order["cc_name"]}"%>!

      +

      Your order number is <%= "#{@order["id"]}" %>

      +

      + Total comes to: <%= @total["total"] %> +

      From 609972bb76e89fc8b9d80e65015261f5dbe78667 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 09:41:12 -0700 Subject: [PATCH 130/144] fixed seeds, missing a pair of quotation marks --- db/seeds.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/db/seeds.rb b/db/seeds.rb index 33ffb93f8b..808bfc0929 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -262,14 +262,14 @@ end birds = Category.find_by(name: "birds") -amphibians = Category.find_by(name: amphibians) +amphibians = Category.find_by(name: "amphibians") mammals = Category.find_by(name: "mammals") all_products.each do |item| if item.name == "penguin hawaiian shirt" item.categories << birds elsif item.name == "trendy mini hat" - item.categories << amphibians + item.categories << amphibians else item.categories << mammals end From 5e7066fa8ac9b5c1c906768a6c6c65fea7132375 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 09:41:37 -0700 Subject: [PATCH 131/144] redirected retiring a product method to our products_manage_path --- app/controllers/products_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index d19eb493f4..f10c457ef8 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -60,7 +60,7 @@ def retire @product.active = true end @product.save - redirect_to portal_path + redirect_to manage_products_path end private From 1c9f02ad0b130efa695adc259ae7a6a40362b294 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 10:01:07 -0700 Subject: [PATCH 132/144] Fixe total logic to make more sense but still not able to carry it to order --- app/controllers/application_controller.rb | 8 -------- app/controllers/carts_controller.rb | 7 +++++++ app/views/carts/index.html.erb | 8 ++++---- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index e3f5230753..af05efcaa4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -21,14 +21,6 @@ def require_login end end - # def total - # if !session[:total].nil? - # session[:total] = @total - # else - # session[:total] = 0 - # @total = session[:total] - # end - # end def shopping_cart if !session[:cart].nil? diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 893f4b4b8f..7b2647f684 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -9,6 +9,10 @@ def index @cart.each do |item| a = Product.find(item["id"]) b = item["quantity"] + if @total.nil? + @total = 0 + end + @total += b + a.price @products.merge!(a => b) end return @products @@ -82,6 +86,9 @@ def no_item @cart.delete_if {|k| k["quantity"] == 0} end + def total + end + def product_ref @product = Product.find(params[:id]) end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 170f9e809f..6ce3b706a9 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -2,7 +2,7 @@

      Your Cart




      -<%total = 0%> +
      • <%@products.each do |product, quantity|%> @@ -13,10 +13,10 @@ <%=link_to "Delete", delete_products_path(product.id), method: :delete%>

      • - <% total += quantity * product.price %> + <% @total += quantity * product.price %> <%end%> - <%if total != 0%> -

        Total: <%=show_dollars(total)%>

        + <%if @total != 0 && !@total.nil?%> +

        Total: <%=show_dollars(@total)%>


        Continue shopping <%=link_to "Here", products_path %>

        <%=link_to "Purchase Now", new_order_path%>

        From a0fffe6f9d0bd886e97da7fbf307cc2e9fa65822 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 10:13:55 -0700 Subject: [PATCH 133/144] added a dragon costume from Yeni --- db/seeds.rb | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/db/seeds.rb b/db/seeds.rb index 808bfc0929..e578b7e69d 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -42,6 +42,14 @@ products = [ + { + name: "dragon costume", + description: "For your gecko who's always wanted to be a dragon", + stock: 48, + price: 1798, + photo_url: "http://img05.deviantart.net/156d/i/2006/304/e/d/happy_halloween_by_snakeskii.jpg", + merchant_id: 1 + }, { name: "cat pants", description: "a nice pair of slacks with an elastic waist", @@ -234,7 +242,7 @@ -categories = ["pants", "booties", "santa", "pirate", "scarves", "frog", "hoodies", "bagpipes", "pumpkin", "shirts", "beaver", "jackets", "cat", "track suits", "hats", "cowboy hats", "tutus", "bobsleds", "nyan", "sailors", "minnie mouse", "bunny", "leopard print", "mammals", "birds", "amphibians"] +categories = ["dragon", "pants", "booties", "santa", "pirate", "scarves", "frog", "hoodies", "bagpipes", "pumpkin", "shirts", "beaver", "jackets", "cat", "track suits", "hats", "cowboy hats", "tutus", "bobsleds", "nyan", "sailors", "minnie mouse", "bunny", "leopard print", "mammals", "birds", "amphibians"] # categories = ["cat", "pants", "dog", "booties", "santa", "guinea pig", "pirate", "pig", "llama", "scarves", "frog", "pig", "pig", "hoodies", "hedgehog", "bagpipes", "pumpkin", "guinea pig", "penguin", "shirts", "beaver", "bagpipes", "dog", "jackets", "pirate", "cat", "dog", "track suits", "hats", "dog", "pig", "cowboy hats", "pig", "tutus", "dog", "bobsleds", "nyan", "cat", "cat", "sailors", "pig", "minnie mouse", "baby", "bunny", "mammals", "birds"] From 013f406956ed3853945f75a53acf8e501c304d6d Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 10:15:21 -0700 Subject: [PATCH 134/144] added logic so that only active products are viewed when looking at products by category --- app/controllers/merchants_controller.rb | 3 ++- app/views/merchants/show.html.erb | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb index e14528ae32..5ea0f827a1 100644 --- a/app/controllers/merchants_controller.rb +++ b/app/controllers/merchants_controller.rb @@ -7,13 +7,14 @@ def index def show begin @merchant = Merchant.find(params[:id]) + @products = @merchant.products.where(active: true) rescue ActiveRecord::RecordNotFound render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found end end private - + def merchant_params params.require(:merchant).permit(:user_name, :email, :uid, :provider) end diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb index f5827f0e0b..4cf39d7d79 100644 --- a/app/views/merchants/show.html.erb +++ b/app/views/merchants/show.html.erb @@ -1,13 +1,13 @@

        <%= @merchant.user_name %>'s Products

        -<% if @merchant.products.empty? %> +<% if @products.empty? %>

        Currently, this merchant does not have any products.

        <% end %>
          -<% @merchant.products.each do |product| %> +<% @products.each do |product| %>
        • <%= link_to (image_tag "#{ product.photo_url }", alt: "#{ product.name }", class: "product-image"), product_path(product) %> From 71d34f3a5f33baf0fa914b409147a0b2e9682b98 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 10:17:10 -0700 Subject: [PATCH 135/144] in the last commit--I meant viewing products by merchant. In this commit, added logic so that only active products are viewed when looking at products by category. --- app/controllers/categories_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb index 65e92ac549..552cf634e5 100644 --- a/app/controllers/categories_controller.rb +++ b/app/controllers/categories_controller.rb @@ -8,7 +8,7 @@ def index def show begin @category = Category.find(params[:id]) - @products = @category.products + @products = @category.products.where(active:true) rescue ActiveRecord::RecordNotFound render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found end From f5b0ed5f2ba872f36a6ab1aab4b7cc7052d6c8bf Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 10:18:01 -0700 Subject: [PATCH 136/144] minor updates to these two views, adding tbd: before links we don't have running at the moment --- app/views/products/manage.html.erb | 2 +- app/views/sessions/index.html.erb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/products/manage.html.erb b/app/views/products/manage.html.erb index a75ef2ce16..5f4b692490 100644 --- a/app/views/products/manage.html.erb +++ b/app/views/products/manage.html.erb @@ -10,7 +10,7 @@

          <%= capitalize_each_word(product.name) %>

          <%= link_to "EDIT", edit_product_path(product) %>

          -

          CATEGORIZE

          +

          tbd: CATEGORIZE

          <% if product.active %> <%= link_to "RETIRE PRODUCT", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% else %> diff --git a/app/views/sessions/index.html.erb b/app/views/sessions/index.html.erb index 29f8f7c172..040c2fb267 100644 --- a/app/views/sessions/index.html.erb +++ b/app/views/sessions/index.html.erb @@ -18,7 +18,7 @@

        • -

          <%= link_to "Manage your orders (currently fake link)", new_product_path %>

          +

          <%= link_to "tbd: Manage your orders", new_product_path %>

        From 1da34b497fc4aebe040328c4e2cb68881051bb0a Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 10:35:47 -0700 Subject: [PATCH 137/144] turned on the DELETE functionality for products, and standardized buttons on the product management view to align with the retire button --- app/controllers/products_controller.rb | 4 ++-- app/views/products/manage.html.erb | 11 ++++++----- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index f10c457ef8..00cb8753d0 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -43,10 +43,10 @@ def update def destroy unless @product.merchant_id == session[:user_id] flash[:error] = "Please note: You are only allowed to delete your own products." - # redirect_to manage_products_path + redirect_to manage_products_path end @product.destroy - redirect_to portal_path + redirect_to manage_products_path end def manage diff --git a/app/views/products/manage.html.erb b/app/views/products/manage.html.erb index 5f4b692490..a56950d605 100644 --- a/app/views/products/manage.html.erb +++ b/app/views/products/manage.html.erb @@ -8,14 +8,15 @@ <% @products.each do |product| %>
      • -

        <%= capitalize_each_word(product.name) %>

        -

        <%= link_to "EDIT", edit_product_path(product) %>

        -

        tbd: CATEGORIZE

        +

        <%= capitalize_each_word(product.name) %>

        +

        <%= link_to "EDIT", edit_product_path(product), remote: true, class: "button" %>

        +

        <%= link_to "tbd: CATEGORIZE", edit_product_path(product), remote: true, class: "success button" %>

        <% if product.active %> - <%= link_to "RETIRE PRODUCT", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> + <%= link_to "RETIRE", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% else %> - <%= link_to "ACTIVATE PRODUCT", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> + <%= link_to "ACTIVATE", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% end %>

        +

        <%= link_to "DELETE", product_path(product), remote: true, method: "delete", class: "alert button" %>

      • <%end%> From 3bd32553191e03c5fa982a57a303b5365dc71b77 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 10:57:59 -0700 Subject: [PATCH 138/144] Working on order model --- app/controllers/application_controller.rb | 3 +-- app/controllers/carts_controller.rb | 25 ++++------------------- app/controllers/orders_controller.rb | 9 ++++++-- app/models/order.rb | 4 ++-- app/views/carts/index.html.erb | 1 - app/views/orders/new.html.erb | 2 -- 6 files changed, 14 insertions(+), 30 deletions(-) diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index af05efcaa4..51cd3a8f99 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,7 +2,7 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception - helper_method :shopping_cart, :total + helper_method :shopping_cart # before_action :require_login private @@ -21,7 +21,6 @@ def require_login end end - def shopping_cart if !session[:cart].nil? @cart = session[:cart] diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 7b2647f684..5e015c0a50 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -1,19 +1,18 @@ class CartsController < ApplicationController before_action :shopping_cart before_action :product_ref, except: [:index, :empty_cart] - # before_action :total after_action :no_item, only: [:less_prod, :delete_product] def index @products = {} @cart.each do |item| - a = Product.find(item["id"]) - b = item["quantity"] + product = Product.find(item["id"]) + quantity = item["quantity"] if @total.nil? @total = 0 end - @total += b + a.price - @products.merge!(a => b) + @total += quantity * product.price + @products.merge!(product => quantity) end return @products end @@ -60,22 +59,6 @@ def delete_product redirect_to carts_path end - - - # begin - # @order_item = OrderItem.find(params[:product_id]) - # rescue ActiveRecord::RecordNotFound - # nil - # end - # if @order_item.nil? - # - # @order_item = OrderItem.create - # @product.order_items << @order_item - # @product.save - # - # @order_item.save - # end - def empty_cart session[:cart] = nil redirect_to carts_path diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 7b310947d7..015a9a9965 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,5 +1,5 @@ class OrdersController < ApplicationController - before_action :total, :shopping_cart + before_action :shopping_cart def index @order = session[:order] end @@ -14,6 +14,7 @@ def show def create @order = Order.new(order_params) + @order.total = 0 if @order.save @cart.each do |item| @order_item = OrderItem.new @@ -22,10 +23,14 @@ def create @order_item.order_id = @order.id @order_item.save end + @order.update_total + + raise session[:order] = @order session[:cart] = nil redirect_to orders_path else + raise render :new end end @@ -50,7 +55,7 @@ def destroy private def order_params - params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address, :total, ) + params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address, :total ) end diff --git a/app/models/order.rb b/app/models/order.rb index ae5ed8313d..917dca23d9 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -9,7 +9,7 @@ class Order < ActiveRecord::Base validates_associated :order_items # validate :acceptable_status - # before_save :update_total + before_save :update_total def valid_exp if @order.cc_exp_year < Time.now.year @@ -25,7 +25,7 @@ def acceptable_status end end - def total + def update_total order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.product.price) : 0 }.sum end diff --git a/app/views/carts/index.html.erb b/app/views/carts/index.html.erb index 6ce3b706a9..efaf56677a 100644 --- a/app/views/carts/index.html.erb +++ b/app/views/carts/index.html.erb @@ -13,7 +13,6 @@ <%=link_to "Delete", delete_products_path(product.id), method: :delete%>

        - <% @total += quantity * product.price %> <%end%> <%if @total != 0 && !@total.nil?%>

        Total: <%=show_dollars(@total)%>

        diff --git a/app/views/orders/new.html.erb b/app/views/orders/new.html.erb index 6d81e461f4..a19fe23635 100644 --- a/app/views/orders/new.html.erb +++ b/app/views/orders/new.html.erb @@ -15,7 +15,5 @@ <%= f.label "Zip Code" %> <%= f.text_field :billing_zip %> <%= f.hidden_field :date_purchased, :value => Time.now %> -<%= f.hidden_field :total, :value => @total %> - <%= f.submit%> <% end %> From 3623b04038bd08ab624b6569664879ee14a793b6 Mon Sep 17 00:00:00 2001 From: Yeni Date: Fri, 28 Oct 2016 11:46:24 -0700 Subject: [PATCH 139/144] added categories to form for product. Can edit product and change categories and create product and choose as many categories as they want. --- app/controllers/products_controller.rb | 3 +-- app/views/products/_form.html.erb | 12 ++++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb index b9e845de58..dd10e5f952 100644 --- a/app/controllers/products_controller.rb +++ b/app/controllers/products_controller.rb @@ -4,7 +4,6 @@ class ProductsController < ApplicationController def index @products = Product.where(active:true) - # @order_item = current_order.order_items.new end def show; end @@ -66,7 +65,7 @@ def retire private def product_params - params.require(:product).permit(:name, :description, :stock, :price,:photo_url, :merchant_id) + params.require(:product).permit(:name, :description, :stock, :price,:photo_url, :merchant_id, category_ids: []) end def find_product diff --git a/app/views/products/_form.html.erb b/app/views/products/_form.html.erb index f8c3d118ce..15ff721480 100644 --- a/app/views/products/_form.html.erb +++ b/app/views/products/_form.html.erb @@ -37,6 +37,18 @@ <%= f.url_field :photo_url %>

        +

        + <%= f.label "Choose at least one category" %> +

          + <%= f.collection_check_boxes :category_ids, Category.all, :id, :name do |b| %> +
        • + <%= b.check_box %> + <%= b.label %> +
        • + <% end %> +
        +

        +

        <%= f.submit "Submit" %>

        From 6a2f52baa28d581ee26b89fdbd6492aa1c0da7c2 Mon Sep 17 00:00:00 2001 From: Suzannah Kirk-Daligcon Date: Fri, 28 Oct 2016 12:01:24 -0700 Subject: [PATCH 140/144] deleted the remote true so that the edit button works --- app/views/products/manage.html.erb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/app/views/products/manage.html.erb b/app/views/products/manage.html.erb index a56950d605..fb555ed122 100644 --- a/app/views/products/manage.html.erb +++ b/app/views/products/manage.html.erb @@ -9,8 +9,7 @@
      • <%= capitalize_each_word(product.name) %>

        -

        <%= link_to "EDIT", edit_product_path(product), remote: true, class: "button" %>

        -

        <%= link_to "tbd: CATEGORIZE", edit_product_path(product), remote: true, class: "success button" %>

        +

        <%= link_to "EDIT", edit_product_path(product), class: "button" %>

        <% if product.active %> <%= link_to "RETIRE", retire_product_path(product), remote: true, method: "patch", class: "warning button" %> <% else %> From 18e7c912116ed8ff20cfb40b137e93805a8620fe Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 12:07:05 -0700 Subject: [PATCH 141/144] Got total to work on order page --- app/controllers/orders_controller.rb | 14 ++++---- app/models/order.rb | 47 ++++++++++++++------------ app/views/layouts/application.html.erb | 3 +- app/views/orders/index.html.erb | 6 ++-- 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 015a9a9965..86fa859b94 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,7 +1,7 @@ class OrdersController < ApplicationController - before_action :shopping_cart + before_action :shopping_cart def index - @order = session[:order] + @order = Order.find(session[:order_id]) if session[:order_id] end def new @@ -17,20 +17,18 @@ def create @order.total = 0 if @order.save @cart.each do |item| - @order_item = OrderItem.new + @order_item = @order.order_items.new @order_item.product_id = item.values[0] @order_item.quantity = item.values[1] - @order_item.order_id = @order.id @order_item.save end + @order.reload @order.update_total - - raise - session[:order] = @order + @order.save + session[:order_id] = @order.id session[:cart] = nil redirect_to orders_path else - raise render :new end end diff --git a/app/models/order.rb b/app/models/order.rb index 917dca23d9..3a59cd083e 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -1,33 +1,38 @@ class Order < ActiveRecord::Base - has_many :order_items + has_many :order_items - validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 4 } - validates :cc_exp_year, presence: true, length: {is: 4} - validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} - validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} - # validate :valid_exp - validates_associated :order_items - # validate :acceptable_status + validates :cc_number, presence: true, numericality: {only_integer: true}, length: { is: 4 } + validates :cc_exp_year, presence: true, length: {is: 4} + validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} + validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} + # validate :valid_exp + validates_associated :order_items + # validate :acceptable_status - before_save :update_total + before_save :update_total - def valid_exp - if @order.cc_exp_year < Time.now.year - errors.add(:cc_exp_year, "Card year is expired") - elsif @order.cc_exp_year == Time.now.year && @order.cc_exp_month < Time.now.month - errors.add(:cc_exp_month, "Card month is expired") - end + def valid_exp + if @order.cc_exp_year < Time.now.year + errors.add(:cc_exp_year, "Card year is expired") + elsif @order.cc_exp_year == Time.now.year && @order.cc_exp_month < Time.now.month + errors.add(:cc_exp_month, "Card month is expired") end + end - def acceptable_status - if @order.status != "PENDING" && @order.status != "PAID" && @order.status != "COMPLETE" && @order.status != "CANCELLED" - errors.add(:status, "Must be PENDING, PAID, COMPLETE or CANCELLED") - end + def acceptable_status + if @order.status != "PENDING" && @order.status != "PAID" && @order.status != "COMPLETE" && @order.status != "CANCELLED" + errors.add(:status, "Must be PENDING, PAID, COMPLETE or CANCELLED") end + end - def update_total - order_items.collect { |oi| oi.valid? ? (oi.quantity * oi.product.price) : 0 }.sum + def update_total + total = 0 + order_items.each do |item| + product = item.product + total += product.price end + self.total = total + end end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index a18956f3c5..174b603b54 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -11,7 +11,7 @@

        ~FootFoot Couture~

        - +
        + <%= yield %> diff --git a/app/views/orders/index.html.erb b/app/views/orders/index.html.erb index 87bd9b174f..a758adace1 100644 --- a/app/views/orders/index.html.erb +++ b/app/views/orders/index.html.erb @@ -1,6 +1,4 @@

        Thank you for your order <%="#{@order["cc_name"]}"%>!

        -

        Your order number is <%= "#{@order["id"]}" %>

        -

        - Total comes to: <%= @total["total"] %> -

        +

        Your order number is: <%= "#{@order["id"]}" %>

        +

        Your total comes to: <%= show_dollars(@order.total)%>

        From 0ab26b01bd5e98ae2683e275969c85e825386f53 Mon Sep 17 00:00:00 2001 From: "Johna N. Morris" Date: Fri, 28 Oct 2016 12:10:52 -0700 Subject: [PATCH 142/144] implemented rudimentary manage order action and view --- app/controllers/orders_controller.rb | 51 ++++++++++++++++------ app/models/order.rb | 2 +- app/views/orders/manage.html.erb | 16 +++++++ app/views/orders/manage_orders.html.erb | 4 -- config/routes.rb | 2 +- test/controllers/orders_controller_test.rb | 9 ++-- 6 files changed, 62 insertions(+), 22 deletions(-) create mode 100644 app/views/orders/manage.html.erb delete mode 100644 app/views/orders/manage_orders.html.erb diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb index 7b310947d7..8d714bedb9 100644 --- a/app/controllers/orders_controller.rb +++ b/app/controllers/orders_controller.rb @@ -1,17 +1,18 @@ class OrdersController < ApplicationController - before_action :total, :shopping_cart + # before_action :total, :shopping_cart + before_action :shopping_cart + before_action :find_order, except: [:index, :new, :create, :manage] + def index @order = session[:order] end + def show; end + def new @order = Order.new end - def show - @order = Order.find(params[:id]) - end - def create @order = Order.new(order_params) if @order.save @@ -30,28 +31,52 @@ def create end end - def update @order = Order.find(params[:id]) end - def manage_orders - @order_items = OrderItems.all - @product = Product.find_by(merchant_id: session[:user_id]) - @product.each do |prod| - @order_item = OrderItem.find_by(product_id: prod.id) + def manage + @products = Product.where(merchant_id: session[:user_id]) + @ordered_products = [] + @order_items = [] + @orders = [] + + @products.each do |product| + unless product.order_items.nil? + @ordered_products << product + end + end + + @ordered_products.each do |product| + order_items = OrderItem.where(product_id: product.id) + order_items.each do |item| + @order_items << item + end + end + + @order_items.each do |item| + order = Order.find_by(id: item.order_id) + @orders << order end - @order end def destroy + @order.destroy + redirect_to portal_path end private def order_params - params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address, :total, ) + params.require(:order).permit(:date_purchased, :email, :cc_name, :cc_number, :cc_exp_year, :cc_exp_month, :billing_zip, :address) end + def find_order + begin + @order = Order.find(params[:id]) + rescue ActiveRecord::RecordNotFound + render file: "#{Rails.root}/public/404.html", layout: false, status: :not_found + end + end end diff --git a/app/models/order.rb b/app/models/order.rb index ae5ed8313d..030c9465e5 100644 --- a/app/models/order.rb +++ b/app/models/order.rb @@ -6,7 +6,7 @@ class Order < ActiveRecord::Base validates :cc_exp_month, presence: true, numericality: { greater_than_or_equal_to: 1, less_than_or_equal_to: 12} validates :total, presence: true, numericality: { greater_than_or_equal_to: 0} # validate :valid_exp - validates_associated :order_items + # validates_associated :order_items # validate :acceptable_status # before_save :update_total diff --git a/app/views/orders/manage.html.erb b/app/views/orders/manage.html.erb new file mode 100644 index 0000000000..d9a6cfb4f8 --- /dev/null +++ b/app/views/orders/manage.html.erb @@ -0,0 +1,16 @@ +

        Manage Orders

        + +<% @orders.each do |order| %> +

        + Status: <%= order.status %> +

        +

        + Email: <%= order.email %> +

        +

        + Date Purchased: <%= date_format(order.date_purchased) %> +

        +

        + +

        +<% end %> diff --git a/app/views/orders/manage_orders.html.erb b/app/views/orders/manage_orders.html.erb deleted file mode 100644 index 073b761ddd..0000000000 --- a/app/views/orders/manage_orders.html.erb +++ /dev/null @@ -1,4 +0,0 @@ -

        Manage IT!

        - -<%@order_item.each do |item| %> -<%= @order_item. diff --git a/config/routes.rb b/config/routes.rb index d199b2f605..320caceff9 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -8,7 +8,7 @@ get '/products/manage', to: 'products#manage', as: 'manage_products' - get 'orders/manage', to: 'orders#manage', as: 'manage_orders' + get '/orders/manage', to: 'orders#manage', as: 'manage_orders' resources :products do diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb index d7053b71e9..a5cad9c248 100644 --- a/test/controllers/orders_controller_test.rb +++ b/test/controllers/orders_controller_test.rb @@ -1,18 +1,21 @@ require 'test_helper' class OrdersControllerTest < ActionController::TestCase - test "should get index" do + test "should get the index page" do get :index + assert_template :index assert_response :success end test "should get show" do - get :show + order_id = orders(:valid_order).id + get :show, {id: order_id} assert_response :success end test "should get create" do - get :create + order = orders(:valid_order) + get :create, {id: order.id} assert_response :success end From 277e24abfc2db936850a8264f750172303194b39 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 12:10:59 -0700 Subject: [PATCH 143/144] Deleted a method --- app/controllers/carts_controller.rb | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/controllers/carts_controller.rb b/app/controllers/carts_controller.rb index 5e015c0a50..3efddb2fd9 100644 --- a/app/controllers/carts_controller.rb +++ b/app/controllers/carts_controller.rb @@ -69,9 +69,6 @@ def no_item @cart.delete_if {|k| k["quantity"] == 0} end - def total - end - def product_ref @product = Product.find(params[:id]) end From 767366e7850efba7cfdb24c31baa27e2be22a662 Mon Sep 17 00:00:00 2001 From: Shari Meggs Date: Fri, 28 Oct 2016 13:45:50 -0700 Subject: [PATCH 144/144] Changed stuff --- app/views/layouts/application.html.erb | 2 +- test/controllers/carts_controller_test.rb | 20 ++++++++++++++++++-- test/controllers/products_controller_test.rb | 1 - 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 174b603b54..ea0b253f6c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,7 +1,7 @@ - Betsy + Foot Foot Couture <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> diff --git a/test/controllers/carts_controller_test.rb b/test/controllers/carts_controller_test.rb index 9ae6b6dc6f..d80b51d923 100644 --- a/test/controllers/carts_controller_test.rb +++ b/test/controllers/carts_controller_test.rb @@ -4,8 +4,24 @@ class CartsControllerTest < ActionController::TestCase # test "the truth" do # assert true # end -setup do - @cart = session[:cart] +def cart_create + if !session[:cart].nil? + @cart = session[:cart] + else + session[:cart] = [] + @cart = session[:cart] + end +end +test "should get the index page" do + get :index + assert_template :index + assert_response :success +end + + test "Can create a cart" do + cart_create + assert_difference('@cart.count', 1) do + end end diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb index 9d774c187c..6be0d6c5d9 100644 --- a/test/controllers/products_controller_test.rb +++ b/test/controllers/products_controller_test.rb @@ -19,7 +19,6 @@ class ProductsControllerTest < ActionController::TestCase assert_raises ActiveRecord::RecordNotFound do Product.find(product_id) end - get :show, {id: product_id} assert_response :not_found end