diff --git a/.gitignore b/.gitignore index 6db3c9a5bc..2efb468fb7 100644 --- a/.gitignore +++ b/.gitignore @@ -7,9 +7,14 @@ # 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 +.env +coverage diff --git a/Gemfile b/Gemfile index c69f4ddde9..e65d9f0c03 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,8 @@ 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 SCSS for stylesheets gem 'sass-rails', '~> 5.0' # Use Uglifier as compressor for JavaScript assets @@ -14,6 +12,11 @@ gem 'coffee-rails', '~> 4.1.0' # See https://github.com/rails/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby +# Foundation Rails Gem +gem 'foundation-rails' +# Required due to an incompatibility between TurboLinks and Foundation. +gem 'jquery-turbolinks' + # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks @@ -23,6 +26,10 @@ 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' @@ -31,10 +38,22 @@ gem 'sdoc', '~> 0.4.0', group: :doc # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'simplecov', :require => false, :group => :test group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug' + gem 'dotenv-rails' + gem 'better_errors' + gem 'binding_of_caller' + gem 'pry' + gem 'sqlite3' + +end + +group :production do + gem 'pg' + gem 'rails_12factor' end group :development do @@ -43,4 +62,6 @@ group :development do # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring gem 'spring' + + gem "rails-erd" end diff --git a/Gemfile.lock b/Gemfile.lock index 20975578b8..9adeb061c7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,46 +1,56 @@ 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) arel (6.0.3) + babel-source (5.8.35) + babel-transpiler (0.7.0) + babel-source (>= 4.0, < 6) + execjs (~> 2.0) + 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) + choice (0.2.0) + coderay (1.1.1) coffee-rails (4.1.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.1.x) @@ -48,46 +58,85 @@ 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) + foundation-rails (6.2.4.0) + railties (>= 3.1.0) + sass (>= 3.3.0, < 3.5) + sprockets-es6 (>= 0.9.0) + 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) + jquery-turbolinks (2.1.0) + railties (>= 3.1.0) + turbolinks 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) + 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) + pg (0.19.0) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) 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) @@ -95,42 +144,66 @@ GEM activesupport (>= 4.2.0.beta, < 5.0) nokogiri (~> 1.6.0) rails-deprecated_sanitizer (>= 1.0.1) + rails-erd (1.5.0) + activerecord (>= 3.2) + activesupport (>= 3.2) + choice (~> 0.2.0) + ruby-graphviz (~> 1.2) rails-html-sanitizer (1.0.3) loofah (~> 2.0) - railties (4.2.6) - actionpack (= 4.2.6) - activesupport (= 4.2.6) + rails_12factor (0.0.3) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.5) + rails_stdout_logging (0.0.5) + 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-graphviz (1.2.2) 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-es6 (0.9.2) + babel-source (>= 5.8.11) + babel-transpiler + sprockets (>= 3.0.0) + 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.3) execjs (>= 0.3.0, < 3) web-console (2.3.0) activemodel (>= 4.0) @@ -142,20 +215,30 @@ PLATFORMS ruby DEPENDENCIES + better_errors + binding_of_caller byebug coffee-rails (~> 4.1.0) + dotenv-rails + foundation-rails jbuilder (~> 2.0) jquery-rails - rails (= 4.2.6) + jquery-turbolinks + omniauth + omniauth-github + pg + pry + rails (= 4.2.7) + rails-erd + rails_12factor 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.6 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/app/assets/images/alligator.jpeg b/app/assets/images/alligator.jpeg new file mode 100644 index 0000000000..716dd24c1d Binary files /dev/null and b/app/assets/images/alligator.jpeg differ diff --git a/app/assets/images/baboon.jpeg b/app/assets/images/baboon.jpeg new file mode 100644 index 0000000000..b4529ab8ed Binary files /dev/null and b/app/assets/images/baboon.jpeg differ diff --git a/app/assets/images/bananas.jpeg b/app/assets/images/bananas.jpeg new file mode 100644 index 0000000000..e38ae62dc4 Binary files /dev/null and b/app/assets/images/bananas.jpeg differ diff --git a/app/assets/images/bearded_dragon.jpeg b/app/assets/images/bearded_dragon.jpeg new file mode 100644 index 0000000000..49c4c69fd4 Binary files /dev/null and b/app/assets/images/bearded_dragon.jpeg differ diff --git a/app/assets/images/big_catnip.jpeg b/app/assets/images/big_catnip.jpeg new file mode 100644 index 0000000000..087647c5c6 Binary files /dev/null and b/app/assets/images/big_catnip.jpeg differ diff --git a/app/assets/images/big_cats_book.jpeg b/app/assets/images/big_cats_book.jpeg new file mode 100644 index 0000000000..ca00abdf36 Binary files /dev/null and b/app/assets/images/big_cats_book.jpeg differ diff --git a/app/assets/images/boots.jpeg b/app/assets/images/boots.jpeg new file mode 100644 index 0000000000..57da99b95c Binary files /dev/null and b/app/assets/images/boots.jpeg differ diff --git a/app/assets/images/capuchin.jpeg b/app/assets/images/capuchin.jpeg new file mode 100644 index 0000000000..46acdb7c34 Binary files /dev/null and b/app/assets/images/capuchin.jpeg differ diff --git a/app/assets/images/chameleon.jpeg b/app/assets/images/chameleon.jpeg new file mode 100644 index 0000000000..4d9bfc4e7d Binary files /dev/null and b/app/assets/images/chameleon.jpeg differ diff --git a/app/assets/images/cheetah.jpeg b/app/assets/images/cheetah.jpeg new file mode 100644 index 0000000000..63d1419986 Binary files /dev/null and b/app/assets/images/cheetah.jpeg differ diff --git a/app/assets/images/chimpanzee.jpeg b/app/assets/images/chimpanzee.jpeg new file mode 100644 index 0000000000..e557e1d52b Binary files /dev/null and b/app/assets/images/chimpanzee.jpeg differ diff --git a/app/assets/images/crocodile.jpeg b/app/assets/images/crocodile.jpeg new file mode 100644 index 0000000000..5d56b23797 Binary files /dev/null and b/app/assets/images/crocodile.jpeg differ diff --git a/app/assets/images/cymbals.jpeg b/app/assets/images/cymbals.jpeg new file mode 100644 index 0000000000..124a867d4a Binary files /dev/null and b/app/assets/images/cymbals.jpeg differ diff --git a/app/assets/images/dwarf_caiman.jpeg b/app/assets/images/dwarf_caiman.jpeg new file mode 100644 index 0000000000..b27457234c Binary files /dev/null and b/app/assets/images/dwarf_caiman.jpeg differ diff --git a/app/assets/images/elephant.jpeg b/app/assets/images/elephant.jpeg new file mode 100644 index 0000000000..cf6b26ee5c Binary files /dev/null and b/app/assets/images/elephant.jpeg differ diff --git a/app/assets/images/eucalyptus_leaves.jpeg b/app/assets/images/eucalyptus_leaves.jpeg new file mode 100644 index 0000000000..289c12ba66 Binary files /dev/null and b/app/assets/images/eucalyptus_leaves.jpeg differ diff --git a/app/assets/images/falabella.jpeg b/app/assets/images/falabella.jpeg new file mode 100644 index 0000000000..7cfe5c0344 Binary files /dev/null and b/app/assets/images/falabella.jpeg differ diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100755 index 0000000000..86da0d1cd6 Binary files /dev/null and b/app/assets/images/favicon.ico differ diff --git a/app/assets/images/giant_ball.jpeg b/app/assets/images/giant_ball.jpeg new file mode 100644 index 0000000000..1fd97a3a24 Binary files /dev/null and b/app/assets/images/giant_ball.jpeg differ diff --git a/app/assets/images/giraffe.jpeg b/app/assets/images/giraffe.jpeg new file mode 100644 index 0000000000..1887cd61d8 Binary files /dev/null and b/app/assets/images/giraffe.jpeg differ diff --git a/app/assets/images/harness.jpeg b/app/assets/images/harness.jpeg new file mode 100644 index 0000000000..100485c722 Binary files /dev/null and b/app/assets/images/harness.jpeg differ diff --git a/app/assets/images/hat.jpeg b/app/assets/images/hat.jpeg new file mode 100644 index 0000000000..7e4e9d5bab Binary files /dev/null and b/app/assets/images/hat.jpeg differ diff --git a/app/assets/images/hippopotamus.jpeg b/app/assets/images/hippopotamus.jpeg new file mode 100644 index 0000000000..c49c5cd597 Binary files /dev/null and b/app/assets/images/hippopotamus.jpeg differ diff --git a/app/assets/images/koala.jpeg b/app/assets/images/koala.jpeg new file mode 100644 index 0000000000..49bf0e32ea Binary files /dev/null and b/app/assets/images/koala.jpeg differ diff --git a/app/assets/images/leash.jpeg b/app/assets/images/leash.jpeg new file mode 100644 index 0000000000..d73637c811 Binary files /dev/null and b/app/assets/images/leash.jpeg differ diff --git a/app/assets/images/lion.jpeg b/app/assets/images/lion.jpeg new file mode 100644 index 0000000000..79ef1cef00 Binary files /dev/null and b/app/assets/images/lion.jpeg differ diff --git a/app/assets/images/marsupials_book.jpeg b/app/assets/images/marsupials_book.jpeg new file mode 100644 index 0000000000..74b2799e3f Binary files /dev/null and b/app/assets/images/marsupials_book.jpeg differ diff --git a/app/assets/images/micro_pig.jpeg b/app/assets/images/micro_pig.jpeg new file mode 100644 index 0000000000..180c916623 Binary files /dev/null and b/app/assets/images/micro_pig.jpeg differ diff --git a/app/assets/images/monkey_book.jpeg b/app/assets/images/monkey_book.jpeg new file mode 100644 index 0000000000..613f9822e5 Binary files /dev/null and b/app/assets/images/monkey_book.jpeg differ diff --git a/app/assets/images/poop_scoop.jpeg b/app/assets/images/poop_scoop.jpeg new file mode 100644 index 0000000000..9fc9006297 Binary files /dev/null and b/app/assets/images/poop_scoop.jpeg differ diff --git a/app/assets/images/pygmy_goat.jpeg b/app/assets/images/pygmy_goat.jpeg new file mode 100644 index 0000000000..fe8dbcf4b3 Binary files /dev/null and b/app/assets/images/pygmy_goat.jpeg differ diff --git a/app/assets/images/pygmy_hedgehog.jpeg b/app/assets/images/pygmy_hedgehog.jpeg new file mode 100644 index 0000000000..4edd780304 Binary files /dev/null and b/app/assets/images/pygmy_hedgehog.jpeg differ diff --git a/app/assets/images/quokka.jpeg b/app/assets/images/quokka.jpeg new file mode 100644 index 0000000000..0a29c25bee Binary files /dev/null and b/app/assets/images/quokka.jpeg differ diff --git a/app/assets/images/reptile_book.jpeg b/app/assets/images/reptile_book.jpeg new file mode 100644 index 0000000000..931466da88 Binary files /dev/null and b/app/assets/images/reptile_book.jpeg differ diff --git a/app/assets/images/rhesus_macaque.jpeg b/app/assets/images/rhesus_macaque.jpeg new file mode 100644 index 0000000000..f8c054b8b9 Binary files /dev/null and b/app/assets/images/rhesus_macaque.jpeg differ diff --git a/app/assets/images/scratching_post.jpeg b/app/assets/images/scratching_post.jpeg new file mode 100644 index 0000000000..a7965a8f1f Binary files /dev/null and b/app/assets/images/scratching_post.jpeg differ diff --git a/app/assets/images/small_crate.jpeg b/app/assets/images/small_crate.jpeg new file mode 100644 index 0000000000..1cac28e3f4 Binary files /dev/null and b/app/assets/images/small_crate.jpeg differ diff --git a/app/assets/images/snow_leopard.jpeg b/app/assets/images/snow_leopard.jpeg new file mode 100644 index 0000000000..bb84f33283 Binary files /dev/null and b/app/assets/images/snow_leopard.jpeg differ diff --git a/app/assets/images/squirrel_monkey.jpeg b/app/assets/images/squirrel_monkey.jpeg new file mode 100644 index 0000000000..ecb56002a1 Binary files /dev/null and b/app/assets/images/squirrel_monkey.jpeg differ diff --git a/app/assets/images/tasmanian_devil.jpeg b/app/assets/images/tasmanian_devil.jpeg new file mode 100644 index 0000000000..ad3df344d1 Binary files /dev/null and b/app/assets/images/tasmanian_devil.jpeg differ diff --git a/app/assets/images/tiger.jpeg b/app/assets/images/tiger.jpeg new file mode 100644 index 0000000000..073a04beb2 Binary files /dev/null and b/app/assets/images/tiger.jpeg differ diff --git a/app/assets/images/wallaby.jpeg b/app/assets/images/wallaby.jpeg new file mode 100644 index 0000000000..8ac9a71635 Binary files /dev/null and b/app/assets/images/wallaby.jpeg differ diff --git a/app/assets/images/zebra.jpeg b/app/assets/images/zebra.jpeg new file mode 100644 index 0000000000..7065ac6f76 Binary files /dev/null and b/app/assets/images/zebra.jpeg differ diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index e07c5a830f..038d8d7ecc 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -12,5 +12,15 @@ // //= require jquery //= require jquery_ujs +//= require foundation //= require turbolinks //= require_tree . + +$(function(){ $(document).foundation(); }); + +//= require foundation +$(document).foundation(); + +$(document).on('turbolinks:load', function() { + $(function(){ $(document).foundation(); }); +}); 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/stylesheets/_settings.scss b/app/assets/stylesheets/_settings.scss new file mode 100644 index 0000000000..8a3527f8c6 --- /dev/null +++ b/app/assets/stylesheets/_settings.scss @@ -0,0 +1,573 @@ +// Foundation for Sites Settings +// ----------------------------- +// +// Table of Contents: +// +// 1. Global +// 2. Breakpoints +// 3. The Grid +// 4. Base Typography +// 5. Typography Helpers +// 6. Abide +// 7. Accordion +// 8. Accordion Menu +// 9. Badge +// 10. Breadcrumbs +// 11. Button +// 12. Button Group +// 13. Callout +// 14. Close Button +// 15. Drilldown +// 16. Dropdown +// 17. Dropdown Menu +// 18. Flex Video +// 19. Forms +// 20. Label +// 21. Media Object +// 22. Menu +// 23. Meter +// 24. Off-canvas +// 25. Orbit +// 26. Pagination +// 27. Progress Bar +// 28. Reveal +// 29. Slider +// 30. Switch +// 31. Table +// 32. Tabs +// 33. Thumbnail +// 34. Title Bar +// 35. Tooltip +// 36. Top Bar + +@import 'util/util'; + +// 1. Global +// --------- + +$global-font-size: 100%; +$global-width: rem-calc(1200); +$global-lineheight: 1.5; +$foundation-palette: ( + primary: #2199e8, + secondary: #777, + success: #3adb76, + warning: #ffae00, + alert: #ec5840, +); +$light-gray: #e6e6e6; +$medium-gray: #cacaca; +$dark-gray: #8a8a8a; +$black: #0a0a0a; +$white: #fefefe; +$body-background: $white; +$body-font-color: $black; +$body-font-family: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif; +$body-antialiased: true; +$global-margin: 1rem; +$global-padding: 1rem; +$global-weight-normal: normal; +$global-weight-bold: bold; +$global-radius: 0; +$global-text-direction: ltr; +$global-flexbox: false; +$print-transparent-backgrounds: true; + +@include add-foundation-colors; + +// 2. Breakpoints +// -------------- + +$breakpoints: ( + small: 0, + medium: 640px, + large: 1024px, + xlarge: 1200px, + xxlarge: 1440px, +); +$breakpoint-classes: (small medium large); + +// 3. The Grid +// ----------- + +$grid-row-width: $global-width; +$grid-column-count: 12; +$grid-column-gutter: ( + small: 20px, + medium: 30px, +); +$grid-column-align-edge: true; +$block-grid-max: 8; + +// 4. Base Typography +// ------------------ + +$header-font-family: $body-font-family; +$header-font-weight: $global-weight-normal; +$header-font-style: normal; +$font-family-monospace: Consolas, 'Liberation Mono', Courier, monospace; +$header-sizes: ( + small: ( + 'h1': 24, + 'h2': 20, + 'h3': 19, + 'h4': 18, + 'h5': 17, + 'h6': 16, + ), + medium: ( + 'h1': 48, + 'h2': 40, + 'h3': 31, + 'h4': 25, + 'h5': 20, + 'h6': 16, + ), +); +$header-color: inherit; +$header-lineheight: 1.4; +$header-margin-bottom: 0.5rem; +$header-text-rendering: optimizeLegibility; +$small-font-size: 80%; +$header-small-font-color: $medium-gray; +$paragraph-lineheight: 1.6; +$paragraph-margin-bottom: 1rem; +$paragraph-text-rendering: optimizeLegibility; +$code-color: $black; +$code-font-family: $font-family-monospace; +$code-font-weight: $global-weight-normal; +$code-background: $light-gray; +$code-border: 1px solid $medium-gray; +$code-padding: rem-calc(2 5 1); +$anchor-color: $primary-color; +$anchor-color-hover: scale-color($anchor-color, $lightness: -14%); +$anchor-text-decoration: none; +$anchor-text-decoration-hover: none; +$hr-width: $global-width; +$hr-border: 1px solid $medium-gray; +$hr-margin: rem-calc(20) auto; +$list-lineheight: $paragraph-lineheight; +$list-margin-bottom: $paragraph-margin-bottom; +$list-style-type: disc; +$list-style-position: outside; +$list-side-margin: 1.25rem; +$list-nested-side-margin: 1.25rem; +$defnlist-margin-bottom: 1rem; +$defnlist-term-weight: $global-weight-bold; +$defnlist-term-margin-bottom: 0.3rem; +$blockquote-color: $dark-gray; +$blockquote-padding: rem-calc(9 20 0 19); +$blockquote-border: 1px solid $medium-gray; +$cite-font-size: rem-calc(13); +$cite-color: $dark-gray; +$keystroke-font: $font-family-monospace; +$keystroke-color: $black; +$keystroke-background: $light-gray; +$keystroke-padding: rem-calc(2 4 0); +$keystroke-radius: $global-radius; +$abbr-underline: 1px dotted $black; + +// 5. Typography Helpers +// --------------------- + +$lead-font-size: $global-font-size * 1.25; +$lead-lineheight: 1.6; +$subheader-lineheight: 1.4; +$subheader-color: $dark-gray; +$subheader-font-weight: $global-weight-normal; +$subheader-margin-top: 0.2rem; +$subheader-margin-bottom: 0.5rem; +$stat-font-size: 2.5rem; + +// 6. Abide +// -------- + +$abide-inputs: true; +$abide-labels: true; +$input-background-invalid: map-get($foundation-palette, alert); +$form-label-color-invalid: map-get($foundation-palette, alert); +$input-error-color: map-get($foundation-palette, alert); +$input-error-font-size: rem-calc(12); +$input-error-font-weight: $global-weight-bold; + +// 7. Accordion +// ------------ + +$accordion-background: $white; +$accordion-plusminus: true; +$accordion-item-color: foreground($accordion-background, $primary-color); +$accordion-item-background-hover: $light-gray; +$accordion-item-padding: 1.25rem 1rem; +$accordion-content-background: $white; +$accordion-content-border: 1px solid $light-gray; +$accordion-content-color: foreground($accordion-content-background, $body-font-color); +$accordion-content-padding: 1rem; + +// 8. Accordion Menu +// ----------------- + +$accordionmenu-arrows: false; +$accordionmenu-arrow-color: $primary-color; + +// 9. Badge +// -------- + +$badge-background: $primary-color; +$badge-color: foreground($badge-background); +$badge-padding: 0.3em; +$badge-minwidth: 2.1em; +$badge-font-size: 0.6rem; + +// 10. Breadcrumbs +// --------------- + +$breadcrumbs-margin: 0 0 $global-margin 0; +$breadcrumbs-item-font-size: rem-calc(11); +$breadcrumbs-item-color: $primary-color; +$breadcrumbs-item-color-current: $black; +$breadcrumbs-item-color-disabled: $medium-gray; +$breadcrumbs-item-margin: 0.75rem; +$breadcrumbs-item-uppercase: true; +$breadcrumbs-item-slash: true; + +// 11. Button +// ---------- + +$button-padding: 0.85em 1em; +$button-margin: 0 0 $global-margin 0; +$button-fill: solid; +$button-background: $primary-color; +$button-background-hover: scale-color($button-background, $lightness: -15%); +$button-color: $white; +$button-color-alt: $black; +$button-radius: $global-radius; +$button-sizes: ( + tiny: 0.6rem, + small: 0.75rem, + default: 0.9rem, + large: 1.25rem, +); +$button-opacity-disabled: 0.25; +$button-background-hover-lightness: -20%; +$button-hollow-hover-lightness: -50%; + +// 12. Button Group +// ---------------- + +$buttongroup-margin: 1rem; +$buttongroup-spacing: 1px; +$buttongroup-child-selector: '.button'; +$buttongroup-expand-max: 6; + +// 13. Callout +// ----------- + +$callout-background: $white; +$callout-background-fade: 85%; +$callout-border: 1px solid rgba($black, 0.25); +$callout-margin: 0 0 1rem 0; +$callout-padding: 1rem; +$callout-font-color: $body-font-color; +$callout-font-color-alt: $body-background; +$callout-radius: $global-radius; +$callout-link-tint: 30%; + +// 14. Close Button +// ---------------- + +$closebutton-position: right top; +$closebutton-offset-horizontal: 1rem; +$closebutton-offset-vertical: 0.5rem; +$closebutton-size: 2em; +$closebutton-lineheight: 1; +$closebutton-color: $dark-gray; +$closebutton-color-hover: $black; + +// 15. Drilldown +// ------------- + +$drilldown-transition: transform 0.15s linear; +$drilldown-arrows: true; +$drilldown-arrow-color: $primary-color; +$drilldown-background: $white; + +// 16. Dropdown +// ------------ + +$dropdown-padding: 1rem; +$dropdown-border: 1px solid $medium-gray; +$dropdown-font-size: 1rem; +$dropdown-width: 300px; +$dropdown-radius: $global-radius; +$dropdown-sizes: ( + tiny: 100px, + small: 200px, + large: 400px, +); + +// 17. Dropdown Menu +// ----------------- + +$dropdownmenu-arrows: true; +$dropdownmenu-arrow-color: $anchor-color; +$dropdownmenu-min-width: 200px; +$dropdownmenu-background: $white; +$dropdownmenu-border: 1px solid $medium-gray; + +// 18. Flex Video +// -------------- + +$flexvideo-margin-bottom: rem-calc(16); +$flexvideo-ratio: 4 by 3; +$flexvideo-ratio-widescreen: 16 by 9; + +// 19. Forms +// --------- + +$fieldset-border: 1px solid $medium-gray; +$fieldset-padding: rem-calc(20); +$fieldset-margin: rem-calc(18 0); +$legend-padding: rem-calc(0 3); +$form-spacing: rem-calc(16); +$helptext-color: $black; +$helptext-font-size: rem-calc(13); +$helptext-font-style: italic; +$input-prefix-color: $black; +$input-prefix-background: $light-gray; +$input-prefix-border: 1px solid $medium-gray; +$input-prefix-padding: 1rem; +$form-label-color: $black; +$form-label-font-size: rem-calc(14); +$form-label-font-weight: $global-weight-normal; +$form-label-line-height: 1.8; +$select-background: $white; +$select-triangle-color: $dark-gray; +$select-radius: $global-radius; +$input-color: $black; +$input-placeholder-color: $medium-gray; +$input-font-family: inherit; +$input-font-size: rem-calc(16); +$input-background: $white; +$input-background-focus: $white; +$input-background-disabled: $light-gray; +$input-border: 1px solid $medium-gray; +$input-border-focus: 1px solid $dark-gray; +$input-shadow: inset 0 1px 2px rgba($black, 0.1); +$input-shadow-focus: 0 0 5px $medium-gray; +$input-cursor-disabled: not-allowed; +$input-transition: box-shadow 0.5s, border-color 0.25s ease-in-out; +$input-number-spinners: true; +$input-radius: $global-radius; +$button-radius: $global-radius; + +// 20. Label +// --------- + +$label-background: $primary-color; +$label-color: foreground($label-background); +$label-font-size: 0.8rem; +$label-padding: 0.33333rem 0.5rem; +$label-radius: $global-radius; + +// 21. Media Object +// ---------------- + +$mediaobject-margin-bottom: $global-margin; +$mediaobject-section-padding: $global-padding; +$mediaobject-image-width-stacked: 100%; + +// 22. Menu +// -------- + +$menu-margin: 0; +$menu-margin-nested: 1rem; +$menu-item-padding: 0.7rem 1rem; +$menu-item-color-active: $white; +$menu-item-background-active: map-get($foundation-palette, primary); +$menu-icon-spacing: 0.25rem; + +// 23. Meter +// --------- + +$meter-height: 1rem; +$meter-radius: $global-radius; +$meter-background: $medium-gray; +$meter-fill-good: $success-color; +$meter-fill-medium: $warning-color; +$meter-fill-bad: $alert-color; + +// 24. Off-canvas +// -------------- + +$offcanvas-size: 250px; +$offcanvas-background: $light-gray; +$offcanvas-zindex: -1; +$offcanvas-transition-length: 0.5s; +$offcanvas-transition-timing: ease; +$offcanvas-fixed-reveal: true; +$offcanvas-exit-background: rgba($white, 0.25); +$maincontent-class: 'off-canvas-content'; +$maincontent-shadow: 0 0 10px rgba($black, 0.5); + +// 25. Orbit +// --------- + +$orbit-bullet-background: $medium-gray; +$orbit-bullet-background-active: $dark-gray; +$orbit-bullet-diameter: 1.2rem; +$orbit-bullet-margin: 0.1rem; +$orbit-bullet-margin-top: 0.8rem; +$orbit-bullet-margin-bottom: 0.8rem; +$orbit-caption-background: rgba($black, 0.5); +$orbit-caption-padding: 1rem; +$orbit-control-background-hover: rgba($black, 0.5); +$orbit-control-padding: 1rem; +$orbit-control-zindex: 10; + +// 26. Pagination +// -------------- + +$pagination-font-size: rem-calc(14); +$pagination-margin-bottom: $global-margin; +$pagination-item-color: $black; +$pagination-item-padding: rem-calc(3 10); +$pagination-item-spacing: rem-calc(1); +$pagination-radius: $global-radius; +$pagination-item-background-hover: $light-gray; +$pagination-item-background-current: $primary-color; +$pagination-item-color-current: foreground($pagination-item-background-current); +$pagination-item-color-disabled: $medium-gray; +$pagination-ellipsis-color: $black; +$pagination-mobile-items: false; +$pagination-mobile-current-item: false; +$pagination-arrows: true; + +// 27. Progress Bar +// ---------------- + +$progress-height: 1rem; +$progress-background: $medium-gray; +$progress-margin-bottom: $global-margin; +$progress-meter-background: $primary-color; +$progress-radius: $global-radius; + +// 28. Reveal +// ---------- + +$reveal-background: $white; +$reveal-width: 600px; +$reveal-max-width: $global-width; +$reveal-padding: $global-padding; +$reveal-border: 1px solid $medium-gray; +$reveal-radius: $global-radius; +$reveal-zindex: 1005; +$reveal-overlay-background: rgba($black, 0.45); + +// 29. Slider +// ---------- + +$slider-width-vertical: 0.5rem; +$slider-transition: all 0.2s ease-in-out; +$slider-height: 0.5rem; +$slider-background: $light-gray; +$slider-fill-background: $medium-gray; +$slider-handle-height: 1.4rem; +$slider-handle-width: 1.4rem; +$slider-handle-background: $primary-color; +$slider-opacity-disabled: 0.25; +$slider-radius: $global-radius; + +// 30. Switch +// ---------- + +$switch-background: $medium-gray; +$switch-background-active: $primary-color; +$switch-height: 2rem; +$switch-height-tiny: 1.5rem; +$switch-height-small: 1.75rem; +$switch-height-large: 2.5rem; +$switch-radius: $global-radius; +$switch-margin: $global-margin; +$switch-paddle-background: $white; +$switch-paddle-offset: 0.25rem; +$switch-paddle-radius: $global-radius; +$switch-paddle-transition: all 0.25s ease-out; + +// 31. Table +// --------- + +$table-background: $white; +$table-color-scale: 5%; +$table-border: 1px solid smart-scale($table-background, $table-color-scale); +$table-padding: rem-calc(8 10 10); +$table-hover-scale: 2%; +$table-row-hover: darken($table-background, $table-hover-scale); +$table-row-stripe-hover: darken($table-background, $table-color-scale + $table-hover-scale); +$table-striped-background: smart-scale($table-background, $table-color-scale); +$table-stripe: even; +$table-head-background: smart-scale($table-background, $table-color-scale / 2); +$table-head-row-hover: darken($table-head-background, $table-hover-scale); +$table-foot-background: smart-scale($table-background, $table-color-scale); +$table-foot-row-hover: darken($table-foot-background, $table-hover-scale); +$table-head-font-color: $body-font-color; +$table-foot-font-color: $body-font-color; +$show-header-for-stacked: false; + +// 32. Tabs +// -------- + +$tab-margin: 0; +$tab-background: $white; +$tab-background-active: $light-gray; +$tab-item-font-size: rem-calc(12); +$tab-item-background-hover: $white; +$tab-item-padding: 1.25rem 1.5rem; +$tab-expand-max: 6; +$tab-content-background: $white; +$tab-content-border: $light-gray; +$tab-content-color: foreground($tab-background, $primary-color); +$tab-content-padding: 1rem; + +// 33. Thumbnail +// ------------- + +$thumbnail-border: solid 4px $white; +$thumbnail-margin-bottom: $global-margin; +$thumbnail-shadow: 0 0 0 1px rgba($black, 0.2); +$thumbnail-shadow-hover: 0 0 6px 1px rgba($primary-color, 0.5); +$thumbnail-transition: box-shadow 200ms ease-out; +$thumbnail-radius: $global-radius; + +// 34. Title Bar +// ------------- + +$titlebar-background: $black; +$titlebar-color: $white; +$titlebar-padding: 0.5rem; +$titlebar-text-font-weight: bold; +$titlebar-icon-color: $white; +$titlebar-icon-color-hover: $medium-gray; +$titlebar-icon-spacing: 0.25rem; + +// 35. Tooltip +// ----------- + +$has-tip-font-weight: $global-weight-bold; +$has-tip-border-bottom: dotted 1px $dark-gray; +$tooltip-background-color: $black; +$tooltip-color: $white; +$tooltip-padding: 0.75rem; +$tooltip-font-size: $small-font-size; +$tooltip-pip-width: 0.75rem; +$tooltip-pip-height: $tooltip-pip-width * 0.866; +$tooltip-radius: $global-radius; + +// 36. Top Bar +// ----------- + +$topbar-padding: 0.5rem; +$topbar-background: $light-gray; +$topbar-submenu-background: $topbar-background; +$topbar-title-spacing: 1rem; +$topbar-input-width: 200px; +$topbar-unstack-breakpoint: medium; diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css deleted file mode 100644 index f9cd5b3483..0000000000 --- a/app/assets/stylesheets/application.css +++ /dev/null @@ -1,15 +0,0 @@ -/* - * 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 - */ diff --git a/app/assets/stylesheets/application.css.scss b/app/assets/stylesheets/application.css.scss new file mode 100644 index 0000000000..026bf7ba43 --- /dev/null +++ b/app/assets/stylesheets/application.css.scss @@ -0,0 +1,130 @@ + +/* + * 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 + *= require foundation_and_overrides + + */ + /*Header and footer CSS*/ + body { + margin: 0 5%; + } + + .top-bar { + border: 1px blue dashed; + margin: 2% -2%; + } + + .top-bar-left { + align-self: center; + } + + .lower-nav li { + display: inline-block; + } + + .title_area { + margin-right: 3em; + } + + .github-button, .log-out-button, .dashboard-button, .cart-button, .edit-button-show, .add-button { + color: white; + font-weight: bold; + border-radius: 5px; + padding: 5px; + } + + .github-button { + background-color: blue; + } + + .log-out-button { + background-color: red; + } + + .dashboard-button, .edit-button-show { + background-color: green; + } + + .cart-button, .add-button { + background-color: purple; + } + + .get-products-button, .retire-button { + background-color: lightgrey; + border-radius: 5px; + } + + .search-form { + display: inline-block; + } + +// All images + .rounded-image { + border-radius: 45px; + margin: 0 auto; + max-width: 85%; + max-height: 200px; + } + + .image-container { + display: block; + padding: 4px; + border: 1px solid clear; + border-radius: 4px; + } + + .product-name { + position: absolute; + bottom: 2rem; + padding: 1rem; + text-align: center; + } + + .thumbnail { + width: 300px; + height: 300px; + overflow: hidden; + margin: auto; + position: relative; + } + + .edit-button { + position: absolute; + bottom: 0; + padding: 1rem; + } + + // Products by Category page + .category-header, .product-description { + text-align: center; + } + + .dashboard-orders-table { + border: 1px dotted blue; + margin: 30px 10px; + } + + .dashboard-summary { + margin: 0 50px; + } + +.product-edit-form, .order-form, .review-form { + width: 50%; + margin: 0 auto; +} + + // .caption { + // margin-bottom: 40px; + // } diff --git a/app/assets/stylesheets/carts.scss b/app/assets/stylesheets/carts.scss new file mode 100644 index 0000000000..2fe1b9e710 --- /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/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/assets/stylesheets/foundation_and_overrides.scss b/app/assets/stylesheets/foundation_and_overrides.scss new file mode 100644 index 0000000000..a38f03d463 --- /dev/null +++ b/app/assets/stylesheets/foundation_and_overrides.scss @@ -0,0 +1,52 @@ +@charset 'utf-8'; + +@import 'settings'; +@import 'foundation'; + +// If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. +// +@import 'motion-ui/motion-ui'; + +// We include everything by default. To slim your CSS, remove components you don't use. + +@include foundation-global-styles; +@include foundation-grid; +@include foundation-typography; +@include foundation-button; +@include foundation-forms; +@include foundation-visibility-classes; +@include foundation-float-classes; +@include foundation-accordion; +@include foundation-accordion-menu; +@include foundation-badge; +@include foundation-breadcrumbs; +@include foundation-button-group; +@include foundation-callout; +@include foundation-close-button; +@include foundation-drilldown-menu; +@include foundation-dropdown; +@include foundation-dropdown-menu; +@include foundation-flex-video; +@include foundation-label; +@include foundation-media-object; +@include foundation-menu; +@include foundation-menu-icon; +@include foundation-off-canvas; +@include foundation-orbit; +@include foundation-pagination; +@include foundation-progress-bar; +@include foundation-slider; +@include foundation-sticky; +@include foundation-reveal; +@include foundation-switch; +@include foundation-table; +@include foundation-tabs; +@include foundation-thumbnail; +@include foundation-title-bar; +@include foundation-tooltip; +@include foundation-top-bar; + +// If you'd like to include motion-ui the foundation-rails gem comes prepackaged with it, uncomment the 3 @imports, if you are not using the gem you need to install the motion-ui sass package. +// + @include motion-ui-transitions; +@include motion-ui-animations; diff --git a/app/assets/stylesheets/guests.scss b/app/assets/stylesheets/guests.scss new file mode 100644 index 0000000000..646b204ef1 --- /dev/null +++ b/app/assets/stylesheets/guests.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the guests 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/homepages.scss b/app/assets/stylesheets/homepages.scss new file mode 100644 index 0000000000..2305c36d10 --- /dev/null +++ b/app/assets/stylesheets/homepages.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Homepages 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/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/assets/stylesheets/orderitems.scss b/app/assets/stylesheets/orderitems.scss new file mode 100644 index 0000000000..3ae284dd37 --- /dev/null +++ b/app/assets/stylesheets/orderitems.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/assets/stylesheets/orders.scss b/app/assets/stylesheets/orders.scss new file mode 100644 index 0000000000..3b0428a94e --- /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/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/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/assets/stylesheets/scaffolds.scss b/app/assets/stylesheets/scaffolds.scss new file mode 100644 index 0000000000..ed7a765d9a --- /dev/null +++ b/app/assets/stylesheets/scaffolds.scss @@ -0,0 +1,73 @@ +body { + background-color: #fff; + color: #333; + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +p, ol, ul, td { + font-family: verdana, arial, helvetica, sans-serif; + font-size: 13px; + line-height: 18px; +} + +pre { + background-color: #eee; + padding: 10px; + font-size: 11px; +} + +a { + color: #000; + + &:visited { + color: #666; + } + + &:hover { + color: #fff; + background-color: #000; + } +} + +div { + &.field, &.actions { + margin-bottom: 10px; + } +} + +#notice { + color: green; +} + +.field_with_errors { + padding: 2px; + background-color: red; + display: table; +} + +#error_explanation { + width: 450px; + border: 2px solid red; + padding: 7px; + padding-bottom: 0; + margin-bottom: 20px; + background-color: #f0f0f0; + + h2 { + text-align: left; + font-weight: bold; + padding: 5px 5px 5px 15px; + font-size: 12px; + margin: -7px; + margin-bottom: 0px; + background-color: #c00; + color: #fff; + } + + ul li { + font-size: 12px; + list-style: square; + } +} 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/assets/stylesheets/tasks.scss b/app/assets/stylesheets/tasks.scss new file mode 100644 index 0000000000..c5e7712d42 --- /dev/null +++ b/app/assets/stylesheets/tasks.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Tasks 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..66fe61c9d0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -2,4 +2,29 @@ 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 + + + + def current_order + + if !session[:order_id].nil? + begin + Order.find(session[:order_id]) + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + else + Order.new + end + end + + private + + def current_user + @current_user ||= Merchant.find_by(id: session[:merchant_id]) + end + + helper_method :current_user ## What is this? + end diff --git a/app/controllers/categories_controller.rb b/app/controllers/categories_controller.rb new file mode 100644 index 0000000000..239ea86997 --- /dev/null +++ b/app/controllers/categories_controller.rb @@ -0,0 +1,35 @@ +class CategoriesController < ApplicationController + before_action :require_login, only: [:create, :new] + + def new + @category = Category.new + end + + def show + @category = Category.find(params[:category_id]) + @products = @category.products.where(active: true) + end + + def create + @category = Category.new(category_params) + if @category.save + redirect_to(:back) + else + render :new + end + end + + private + + def category_params + params.require(:category).permit(:name) + end + + def require_login + if current_user.nil? + flash[:error] = "You must be logged in to view this section" + redirect_to login_failure_path + end + end + +end diff --git a/app/controllers/guests_controller.rb b/app/controllers/guests_controller.rb new file mode 100644 index 0000000000..d9e0a89324 --- /dev/null +++ b/app/controllers/guests_controller.rb @@ -0,0 +1,7 @@ +class GuestsController < ApplicationController + def index + @products = Product.all + @product_count = @products.length + end + +end diff --git a/app/controllers/merchants_controller.rb b/app/controllers/merchants_controller.rb new file mode 100644 index 0000000000..e6487aa9e7 --- /dev/null +++ b/app/controllers/merchants_controller.rb @@ -0,0 +1,58 @@ +class MerchantsController < GuestsController + before_action :require_login, only: [:show] + + + # def index + # @merchant = Merchant.all + # end + + # def new + # @merchant = Merchant.new + # end + # + def create + @merchant = Merchant.new(merchant_params) + # Need to intialize number of merchants to 1 upon creation + if @merchant.save + redirect_to merchant_path(@merchant) + else + render :new + end + end + + def show + @merchant = Merchant.find(params[:id]) + @products = Product.where(merchant_id: @merchant.id).where(active: true) + + @inactive_products = Product.where(merchant_id: @merchant.id).where(active: false) if current_user + + + end + + def products + @merchants = Merchant.all + @merchant = Merchant.find(params[:merchant_id]) + @products = Product.where(merchant_id: @merchant.id).where(active: true) + + @inactive_products = Product.where(merchant_id: @merchant.id).where(active: false) if current_user + end + + private + + def merchant_params + params.require(:merchant).permit(:username, :email) + end + + def current_user + @current_user ||= Merchant.find_by(id: session[:merchant_id]) + end + + helper_method :current_user ## What is this? + + def require_login + if current_user.nil? + flash[:error] = "You must be logged in to view this section" + redirect_to login_failure_path + end + end +end diff --git a/app/controllers/order_items_controller.rb b/app/controllers/order_items_controller.rb new file mode 100644 index 0000000000..1f6055f382 --- /dev/null +++ b/app/controllers/order_items_controller.rb @@ -0,0 +1,77 @@ +class OrderItemsController < ApplicationController + before_action :is_product_in_cart, only: [:create] + def index + @order_items = current_order.order_items + @order = current_order + end + + def new + @order_item = OrderItem.new + end + + def create + + @order = current_order + + if @updated_quantity + # @current_product.quantity = @updated_quantity + item = @order.order_items.find_by(product_id: @current_product.product_id) + item.quantity = @updated_quantity + if item.save + session[:order_id] = @order.id + redirect_to order_items_path + else + redirect_to(:back) + end + else + @order_item = @order.order_items.new(order_item_params) + # @order_item.quantity = 1 if @order_item.quantity == nil + if @order_item.save + session[:order_id] = @order.id + redirect_to order_items_path + else + redirect_to :back + end + end + end + + def update + @order = current_order + @order_item = OrderItem.find(params[:id]) + @order_item.update_attributes(order_item_params) + redirect_to order_items_path + end + + def destroy + @order = current_order + @order_item = @order.order_items.find(params[:id]) + @order_item.destroy + @order_items = @order.order_items + redirect_to order_items_path + end + + def is_product_in_cart + order = current_order + @product_id = params[:order_item][:product_id] + if order.order_items.find_by(product_id: @product_id) + @current_product = order.order_items.find_by(product_id: @product_id) + @updated_quantity = @current_product.quantity + params[:order_item][:quantity].to_i + end + end + + def update_cart(product_in_cart) + current_quantity = product_in_cart.quantity + additional_quantity = params[:order_item][:quantity].to_i + params[:order_item][:quantity] = current_quantity + additional_quantity + + product_in_cart.update_attributes(order_item_params) + + redirect_to order_items_path + end + + private + + def order_item_params + params.require(:order_item).permit(:quantity, :product_id, :merchant_id) + end +end diff --git a/app/controllers/orders_controller.rb b/app/controllers/orders_controller.rb new file mode 100644 index 0000000000..5e75477f54 --- /dev/null +++ b/app/controllers/orders_controller.rb @@ -0,0 +1,65 @@ +class OrdersController < ApplicationController + before_action :find_order, only: [:edit, :update] + before_action :paid_order_status, only: [:update] + + # def index; end # this will show order for only that merchant. Like auth hash - takes from session + + def show + begin + @order = Order.find(params[:id]) + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + + @order_items = current_order.order_items + end + + def new + @order = Order.new + end + + def create + @order = Order.new(post_params) + if @order.save + redirect_to root_path(@order) + else + render :new + end + end + + def edit + @order.order_status = "almost paid" + end + + def update + @order.order_status = "almost paid" + if @order.update(post_params) + redirect_to order_path + else + render :edit + end + end + + def destroy + @order = Order.find(params[:id]).destroy + redirect_to root_path + end + + private + + def post_params + params.require(:order).permit(:name, :email, :street_address, :city, :state, :mailing_zip, :cc_number, :cc_exp_month, :cc_exp_year, :cc_sec_code, :billing_zip) + end + + def find_order + @order = Order.find(params[:id]) + end + + + + def paid_order_status + @order.order_status = "paid" + @order.save + end + +end diff --git a/app/controllers/products_controller.rb b/app/controllers/products_controller.rb new file mode 100644 index 0000000000..4e689589f8 --- /dev/null +++ b/app/controllers/products_controller.rb @@ -0,0 +1,110 @@ +class ProductsController < ApplicationController + before_action :find_product, only: [:edit, :update, :reinstate_product, :retire_product] + before_action :merchant_owns_product, only: [:edit] + + def index + if params[:search] + @products = Product.where(active: true).search(params[:search]).order("created_at DESC") + else + @products = Product.where(active: true).order("created_at DESC") + if current_user + @inactive_products = Product.where(merchant_id: current_user.id).where(active: false) + end + end + end + + def show + + begin + product = Product.find(params[:id]) + if product.active == true || product.merchant_id == current_user.id + @product = product + @reviews = Review.where(product_id: @product.id) + @average = Product.average(@reviews) + else + redirect_to products_path + end + rescue StandardError => err + render "/errors/not_found", status: :not_found + end + @order = current_order + @order_item = @order.order_items.new + end + + def new + @product = Product.new + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + @prod_cat = CategoryProduct.new + end + + def create + @product = Product.new(product_params) + @product.merchant_id = session[:merchant_id] + if @product.save + @product.price *= 100 + @product.save + + params[:category_ids].each do |c| + @product.categories << Category.find(c) + end + redirect_to product_path(@product) + else + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + render :new + end + end + + def edit; + @product.price /= 100.0 + @categories_array = Category.all.map { |category| [Category.find(category.id).name, category.id] } + end + + def update + @product.price *= 100 + if @product.update(product_params) + @product.categories.each do |i| + @product.categories.delete(Category.find(i.id)) + end + params[:category_ids].each do |c| + @product.categories << Category.find(c) + end + redirect_to product_path + else + render :edit + end + end + + def destroy + @product = Product.find(params[:id]).destroy + redirect_to products_path + end + + def retire_product + @product.active = false + @product.save + redirect_to products_path + end + + def reinstate_product + @product.active = true + @product.save + redirect_to product_path + end + + def merchant_owns_product + product = Product.find(params[:id]) + unless current_user && current_user.id == product.merchant_id + redirect_to product_path(product) + end + end + + private + + def product_params + params.require(:product).permit(:name, :price, :inventory, :description, :photo_url) + end + + def find_product + @product = Product.find(params[:id]) + end +end diff --git a/app/controllers/reviews_controller.rb b/app/controllers/reviews_controller.rb new file mode 100644 index 0000000000..01878eb7aa --- /dev/null +++ b/app/controllers/reviews_controller.rb @@ -0,0 +1,34 @@ +class ReviewsController < ApplicationController + before_action :merchant_product_self_review, only: [:new] + + def new + @review = Review.new + @review.product_id = params[:product_id] + end + + def create + @review = Review.new(review_params) + @product = Product.find(params[:id]) + @review.product_id = @product.id + if @review.save + redirect_to product_path(@review.product_id) + else + render :new + end + end + + def merchant_product_self_review + product = Product.find(params[:product_id]) + if current_user && current_user.id == product.merchant_id + # flash[:error] = "You cannot review your own product" + redirect_to product_path(product) + end + end + + private + + def review_params + params.require(:review).permit(:rating, :description, :product_id) + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 0000000000..f7e53e0844 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,39 @@ +class SessionsController < ApplicationController +skip_before_action :require_login, only: [:create, :login_failure] + + def index + if session[:merchant_id].nil? + redirect_to root_path + return + else + @merchant = Merchant.find(session[:merchant_id]) # < recalls the value set in a previous request + end + end + + def create + auth_hash = request.env['omniauth.auth'] + redirect_to login_failure_path if auth_hash['uid'].nil? + + @merchant = Merchant.find_by(uid: auth_hash[:uid], provider: 'github') # Remember: .find_by always returns one object (the first that matches) + 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 and return unless @merchant.save # This line saves the newly built @merchant to the database if it can be saved + end + # Save the merchant ID in the session + session[:merchant_id] = @merchant.id + redirect_to root_path + end + + def login_failure; end + + def destroy + if current_user + session.delete(:merchant_id) + session.delete(:order_id) + end + redirect_to root_path + end + +end 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/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/helpers/guests_helper.rb b/app/helpers/guests_helper.rb new file mode 100644 index 0000000000..00317bf2cf --- /dev/null +++ b/app/helpers/guests_helper.rb @@ -0,0 +1,2 @@ +module GuestsHelper +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/helpers/orderitems_helper.rb b/app/helpers/orderitems_helper.rb new file mode 100644 index 0000000000..c74e407d1c --- /dev/null +++ b/app/helpers/orderitems_helper.rb @@ -0,0 +1,2 @@ +module OrderitemsHelper +end diff --git a/app/helpers/orders_helper.rb b/app/helpers/orders_helper.rb new file mode 100644 index 0000000000..6e7f858045 --- /dev/null +++ b/app/helpers/orders_helper.rb @@ -0,0 +1,23 @@ +module OrdersHelper + + def states + ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', + 'District of Columbia', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', + 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', + 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', + 'New Mexico', 'New York', 'North Carolina', 'North Dakota', 'Ohio', 'Oklahoma', 'Oregon', + 'Pennsylvania', 'Puerto Rico', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', + 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'] + end + + def months + [1,2,3,4,5,6,7,8,9,10,11,12] + end + + def expiration_year + yrs = [] + year_today = Date.today.strftime("%y").to_i + year_today.upto(year_today+10).each do |y| yrs << y.to_s;end + return yrs + 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/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/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/app/models/category.rb b/app/models/category.rb new file mode 100644 index 0000000000..c841c80eab --- /dev/null +++ b/app/models/category.rb @@ -0,0 +1,6 @@ +class Category < ActiveRecord::Base + has_many :category_products + has_many :products, through: :category_products + + validates :name, presence: true, uniqueness: true +end diff --git a/app/models/category_product.rb b/app/models/category_product.rb new file mode 100644 index 0000000000..631d4e952c --- /dev/null +++ b/app/models/category_product.rb @@ -0,0 +1,4 @@ +class CategoryProduct < ActiveRecord::Base + belongs_to :category + belongs_to :product +end diff --git a/app/models/merchant.rb b/app/models/merchant.rb new file mode 100644 index 0000000000..0be60fb72b --- /dev/null +++ b/app/models/merchant.rb @@ -0,0 +1,18 @@ +class Merchant < ActiveRecord::Base + has_many :products + + validates :username, presence: true, uniqueness: true + validates :email, presence: true, uniqueness: true + # make email have format requirements + + def self.build_from_github(auth_hash) # factoring method - creates a new User and populates it with data from the auth_hash + merchant = Merchant.new + merchant.uid = auth_hash[:uid] + merchant.provider = 'github' + merchant.username = auth_hash['info']['name'] + merchant.email = auth_hash['info']['email'] + + return merchant + end + +end diff --git a/app/models/order.rb b/app/models/order.rb new file mode 100644 index 0000000000..388e0c1b2f --- /dev/null +++ b/app/models/order.rb @@ -0,0 +1,44 @@ +class Order < ActiveRecord::Base + has_many :order_items + before_validation :initial_order_status, only: [:create] + + validates :name, presence: true, uniqueness: { scope: :street_address, :message => "There is already a buyer with this name and address. Please enter a different name and/or address." }, unless: :pending? + validates :email, presence: true, uniqueness: true, format: { with: /\A[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]+\z/, + message: "Email address is not in the correct format" }, unless: :pending? + validates :street_address, presence: true, unless: :pending? + validates :city, presence: true, unless: :pending? + validates :state, presence: true, unless: :pending? + validates :mailing_zip, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{5}\z/, + message: "Mailing zip code is invalid, must be 5 digits long" }, unless: :pending? + validates :cc_number, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{15,16}\z/, + message: "Credit card is not valid" }, unless: :pending? + validates :cc_exp_month, presence: true, unless: :pending? + validates :cc_exp_year, presence: true, unless: :pending? + validates :cc_sec_code, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{3,4}\z/, + message: "Credit card security code is not valid" }, unless: :pending? + validates :billing_zip, presence: true, numericality: { only_integer: true }, format: { with: /\A[0-9]{5}\z/, + message: "Billing zip code is invalid, must be 5 digits long" }, unless: :pending? + + # validate :card_not_expired, unless: :pending? # will come back to this + + + private + + # def card_not_expired + # return if cc_exp_month.nil? || cc_exp_year.nil? + # if (cc_exp_month < Time.now.month) && (cc_exp_year <= Time.now.year) || + # errors.add(:cc_exp_year, "Bummer. Your card is expired. Please use a different card.") + # end + # end + + def initial_order_status + if order_status == nil + self.order_status = "pending" + end + end + + def pending? + self.order_status == "pending" + end + +end diff --git a/app/models/order_item.rb b/app/models/order_item.rb new file mode 100644 index 0000000000..79c473b5d6 --- /dev/null +++ b/app/models/order_item.rb @@ -0,0 +1,15 @@ +class OrderItem < ActiveRecord::Base + belongs_to :order + belongs_to :product + + validates :quantity, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1 } + validate :quantity_not_greater_than_inventory + + private + + def quantity_not_greater_than_inventory + if quantity == nil || quantity > Product.find(product_id).inventory + errors.add(:quantity, "Bummer. There are only #{Product.find(product_id).inventory} #{Product.find(product_id).name.pluralize} available.") + end + end +end diff --git a/app/models/product.rb b/app/models/product.rb new file mode 100644 index 0000000000..098baadcb9 --- /dev/null +++ b/app/models/product.rb @@ -0,0 +1,36 @@ +class Product < ActiveRecord::Base + has_many :order_items + has_many :reviews + has_many :category_products + has_many :categories, through: :category_products + belongs_to :merchant + + validates :name, presence: true, uniqueness: true + validates :price, presence: true, numericality: { greater_than: 0 } + validates :inventory, presence: true, numericality: { only_integer: true, greater_than: 0 } + validates :description, presence: true + validates :photo_url, presence: true + + def self.find_photo_image(product) + if product.photo_url == "1" + image = "#{ product.name.downcase.gsub(' ', '_') }.jpeg" + else + image = product.photo_url + end + return image + end + + def self.search(search) + where(("name ILIKE ? OR description ILIKE ?"), "%#{search}%", "%#{search}%") + end + + def self.average(reviews) + ratings = [] + num_ratings = reviews.count + reviews.each do |i| + ratings << i[:rating] + end + average = (ratings.sum)/num_ratings.to_f + return sprintf('%.1f' % average) + end +end diff --git a/app/models/review.rb b/app/models/review.rb new file mode 100644 index 0000000000..0c415dd1f2 --- /dev/null +++ b/app/models/review.rb @@ -0,0 +1,6 @@ +class Review < ActiveRecord::Base + belongs_to :product + + validates :rating, presence: true, numericality: { only_integer: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5 } + +end diff --git a/app/views/categories/new.html.erb b/app/views/categories/new.html.erb new file mode 100644 index 0000000000..05f987d1c7 --- /dev/null +++ b/app/views/categories/new.html.erb @@ -0,0 +1,17 @@ +<% if @category.errors.any? %> + +<% end %> + +

New Category

+<%= form_for [@category] do |f| %> + <%= f.label "Category Name" %> + <%= f.text_field :name %> + + <%= f.submit "Save and Add Another"%> +<% end %> diff --git a/app/views/categories/show.html.erb b/app/views/categories/show.html.erb new file mode 100644 index 0000000000..b0e50fb630 --- /dev/null +++ b/app/views/categories/show.html.erb @@ -0,0 +1,16 @@ +

<%= @category.name %>

+ +
+ <% @products.each do |p| %> +
+
+ <% image = Product.find_photo_image(p) %> +
+

+ <%= link_to image_tag(image, alt: "#{ p.name }", class: "rounded-image medium-10 columns"), product_path(p) %> +
+

<%= link_to "#{ p.name }", product_path(p) %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>

+
+
+ <% end %> +
diff --git a/app/views/errors/not_found.html.erb b/app/views/errors/not_found.html.erb new file mode 100644 index 0000000000..e45a7d3624 --- /dev/null +++ b/app/views/errors/not_found.html.erb @@ -0,0 +1 @@ +

404 Not Found

diff --git a/app/views/guests/index.html.erb b/app/views/guests/index.html.erb new file mode 100644 index 0000000000..8e7fea5e35 --- /dev/null +++ b/app/views/guests/index.html.erb @@ -0,0 +1,16 @@ +
+ <% number = (1..(@product_count - 1)).to_a.shuffle %> + <% 6.times do %> + <% featured_image = @products[number.pop] %> +
+
+ <% image = Product.find_photo_image(featured_image) %> +
+

+ <%= link_to image_tag(image, alt: "#{ featured_image.name }", class: "rounded-image medium-10 columns"), product_path(featured_image) %> +
+

<%= link_to featured_image.name, product_path(featured_image) %> <%= "$#{ sprintf('%.2f' % (featured_image.price/100.0)) }" %>

+
+
+ <% end %> +
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 9b70e01c2e..3cd1191c8c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,14 +1,94 @@ - - - Betsy - <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> - <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> - <%= csrf_meta_tags %> - - - -<%= yield %> - - + + + + + <%= favicon_link_tag 'favicon.ico' %> + Zoolily + + <%= stylesheet_link_tag "application" %> + <%= javascript_include_tag "application", 'data-turbolinks-track' => true %> + <%= csrf_meta_tags %> + + + +
+ +
+ +
+ + <%= yield %> + + diff --git a/app/views/merchants/_form.html.erb b/app/views/merchants/_form.html.erb new file mode 100644 index 0000000000..c634bb2be7 --- /dev/null +++ b/app/views/merchants/_form.html.erb @@ -0,0 +1,21 @@ +<% if @merchant.errors.any? %> + +<% end %> + +<%= form_for [@merchant] do |f| %> + <%= f.label "Username" %> + <%= f.text_field :username %> + <%= f.label "Email" %> + <%= f.text_field :email %> + <%= f.label "Password" %> + <%= f.password_field :password %> + <%= f.label "Confirm Password" %> + <%= f.password_field :password_confirmation %> + <%= f.submit %> +<% end %> diff --git a/app/views/merchants/index.html.erb b/app/views/merchants/index.html.erb new file mode 100644 index 0000000000..95c10c83a3 --- /dev/null +++ b/app/views/merchants/index.html.erb @@ -0,0 +1,5 @@ +

Merchants

+ +<% @merchants.each do |m| %> +

<%= link_to "m.name", merchant_path(m) %>

+<% end %> diff --git a/app/views/merchants/new.html.erb b/app/views/merchants/new.html.erb new file mode 100644 index 0000000000..f071cba10a --- /dev/null +++ b/app/views/merchants/new.html.erb @@ -0,0 +1,2 @@ + +<%#= render partial: "form" %> diff --git a/app/views/merchants/products.html.erb b/app/views/merchants/products.html.erb new file mode 100644 index 0000000000..a9d77ad040 --- /dev/null +++ b/app/views/merchants/products.html.erb @@ -0,0 +1,37 @@ +

<%= @merchant.username %>

+ +
+ <% @products.each do |p| %> +
+
+ <% image = Product.find_photo_image(p) %> +
+

+ <%= link_to image_tag(image, alt: "#{ p.name }", class: "rounded-image medium-10 columns"), product_path(p) %> +
+

<%= link_to p.name, product_path(p) %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>

+ <% if current_user != nil && @merchant.id == current_user.id %> +

<%= button_to "Edit Product", edit_product_path(p), method: :get, class: "edit-button" %>

+ <% end %> +
+
+ <% end %> +
+ +<% if @inactive_products && @inactive_products.count > 0 %> +

My Inactive Products

+ <% @inactive_products.each do |i| %> +

+ <%= i.name %> +
+ <% image = i.name.downcase.gsub(' ', '_') %> + + <% image = Product.find_photo_image(i) %> + <%= link_to image_tag(image, alt: "#{ i.name }", class: "rounded-image"), product_path(i) %> +

+ <% end %> +<% end %> + +<% if current_user != nil && @merchant.id == current_user.id %> +

<%= link_to "Add a New Product", new_product_path %>

+<% end %> diff --git a/app/views/merchants/show.html.erb b/app/views/merchants/show.html.erb new file mode 100644 index 0000000000..38732abfb3 --- /dev/null +++ b/app/views/merchants/show.html.erb @@ -0,0 +1,91 @@ +

Welcome to your Dashboard, <%= "#{ @merchant.username }" %>

+ +
+ <%= button_to "Add a Product", new_product_path, method: :get, class: "column small-4" %> + + <%= button_to "View My Products", merchant_products_path(@merchant), method: :get, class: "column small-4" %> + + <%= button_to "Add a Category", new_category_path, method: :get, class: "column small-4" %> +
+ +
+ + + + + + + + + + + +
+ Order Number + + Products + + +
+ Dynamic list of order number with ordered on: information + + Products in that order + + Order status generated by dropdown menu +
+
+ +
+
+

Summary

+
+
+
+

Revenue

+

+ Total Revenue for Pending Orders: +

+

+ Total Revenue for Paid Orders: +

+

+ Total Revenue for Cancelled Orders: +

+

+ Total Revenue for Completed Orders: +

+

+ Total Net Revenue: +

+
+
+

Orders

+

+ Total Pending Orders: +

+

+ Total Cancelled Orders: +

+

+ Total Paid Orders: +

+

+ Total Completed Orders: +

+

+ Total Orders: +

+
+
+
diff --git a/app/views/order_items/_shopping_cart_items.html.erb b/app/views/order_items/_shopping_cart_items.html.erb new file mode 100644 index 0000000000..4d550c5d79 --- /dev/null +++ b/app/views/order_items/_shopping_cart_items.html.erb @@ -0,0 +1,10 @@ +

<%= product.name %>

+ +<%= form_for order_item do |f| %> +

<%= "Unit Price: $#{ sprintf('%.2f' % (product.price / 100.0)) }" %>

+ <%= f.number_field :quantity, value: order_item.quantity.to_i, min: 1 %> + <%= f.hidden_field :product_id, value: product.id %> + + <%= f.submit "Update Quantity", class: "btn btn-primary" %> + <%= link_to "Remove item from cart", 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" } %> +<% end %> diff --git a/app/views/order_items/index.html.erb b/app/views/order_items/index.html.erb new file mode 100644 index 0000000000..30a6560613 --- /dev/null +++ b/app/views/order_items/index.html.erb @@ -0,0 +1,15 @@ +

Cart

+ +<% grand_total = 0 %> +<% 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 'order_items/shopping_cart_items', product: order_item.product, order_item: order_item, show_total: true %> + <% product_total_price = (order_item.product.price * order_item.quantity / 100.0).to_f %> +

<%= "Total Price: $#{ sprintf('%.2f' % product_total_price) }" %>

+ <% grand_total += product_total_price %> + <% end %> +

<%= "Cart total: $#{ sprintf('%.2f' % grand_total) }" %>

+ <%= link_to "Checkout", edit_order_path(@order), class: "button" %> +<% end %> diff --git a/app/views/orders/_form.html.erb b/app/views/orders/_form.html.erb new file mode 100644 index 0000000000..0571f2636f --- /dev/null +++ b/app/views/orders/_form.html.erb @@ -0,0 +1,49 @@ +<% if @order.errors.any? %> +alert("not valid.") + +<% end %> + +
+ <%= form_for @order do |f| %> + <%= f.label :name %> + <%= f.text_field :name %> +
+ <%= f.label "Email address" %> + <%= f.text_field :email %> +
+ <%= f.label "Street address" %> + <%= f.text_field :street_address %> +
+ <%= f.label :city %> + <%= f.text_field :city %> +
+ <%= f.label :state %> + <%= f.select(:state, options_for_select(states)) %> +
+ <%= f.label "Mailing zip code" %> + <%= f.text_field :mailing_zip %> +
+ <%= f.label "Credit card number" %> + <%= f.text_field :cc_number %> +
+ <%= f.label "Expiration month" %> + <%= f.select(:cc_exp_month, options_for_select(months)) %> +
+ <%= f.label "Expiration year" %> + <%= f.select(:cc_exp_year, options_for_select(expiration_year)) %> +
+ <%= f.label "Credit card security code" %> + <%= f.text_field :cc_sec_code %> +
+ <%= f.label "Billing zip code" %> + <%= f.text_field :billing_zip %> +
+ <%= f.submit %> + <% end %> +
diff --git a/app/views/orders/edit.html.erb b/app/views/orders/edit.html.erb new file mode 100644 index 0000000000..2c2ef396ec --- /dev/null +++ b/app/views/orders/edit.html.erb @@ -0,0 +1,2 @@ +

Please enter your information:

+<%= render partial: "form" %> diff --git a/app/views/orders/new.html.erb b/app/views/orders/new.html.erb new file mode 100644 index 0000000000..cd8f08a9ea --- /dev/null +++ b/app/views/orders/new.html.erb @@ -0,0 +1,3 @@ +

Please enter your information:

+<%= render partial: "form" %> +<%# may not need a new order form anymore bc technically we are editing an already existing order and changing its status from pending to paid after submitting %> diff --git a/app/views/orders/show.html.erb b/app/views/orders/show.html.erb new file mode 100644 index 0000000000..48bfd5c550 --- /dev/null +++ b/app/views/orders/show.html.erb @@ -0,0 +1,11 @@ +

Order details for Order # <%= @order.id %>

+

Customer Name: <%= @order.name %>

+
Address: <%= @order.street_address %>, <%= @order.city %>, <%= @order.state %> <%= @order.mailing_zip %>
+
Email: <%= @order.email %>
+

Payment Information

+ +
Exp: <%= @order.cc_exp_month %> / <%= @order.cc_exp_year %>
+

Items Ordered

+<% @order_items.each do |item| %> + <%= item.product.name %> +<% end %> diff --git a/app/views/products/_form.html.erb b/app/views/products/_form.html.erb new file mode 100644 index 0000000000..27c1dc498d --- /dev/null +++ b/app/views/products/_form.html.erb @@ -0,0 +1,30 @@ +<% if @product.errors.any? %> + +<% end %> + +
+ <%= form_for [@product] do |f| %> + <%= f.label "Product name" %> + <%= f.text_field :name %> + <%= f.hidden_field :merchant_id, :value => @product.merchant_id %> + <%= f.label "Product description" %> + <%= f.text_area :description %> + + <%= f.label "Product categories" %> + <%= select_tag :category_ids, options_for_select(@categories_array), {:multiple => true, :style => "width:450px;" } %> + + <%= f.label "Product price" %> + <%= f.text_field :price %> + <%= f.label "Inventory" %> + <%= f.number_field :inventory %> + <%= f.label "Photo URL" %> + <%= f.text_field :photo_url %> + <%= f.submit %> + <% end %> +
diff --git a/app/views/products/edit.html.erb b/app/views/products/edit.html.erb new file mode 100644 index 0000000000..79805ff116 --- /dev/null +++ b/app/views/products/edit.html.erb @@ -0,0 +1,2 @@ +

Please edit your product:

+<%= render partial: "form" %> diff --git a/app/views/products/index.html.erb b/app/views/products/index.html.erb new file mode 100644 index 0000000000..22d01223f1 --- /dev/null +++ b/app/views/products/index.html.erb @@ -0,0 +1,37 @@ +<% if params[:search].nil? %> +

All products

+<% else %> +

Search Results for "<%= "#{ params[:search] }" %>"

+<% end %> + +
+ <% @products.each do |p| %> +
+
+ <%# image = p.name.downcase.gsub(' ', '_') %> + <% image = Product.find_photo_image(p) %> +
+

+ <%= link_to image_tag(image, alt: "#{ p.name }", class: "rounded-image medium-10 columns"), product_path(p) %> +
+

<%= p.name %> <%= "$#{ sprintf('%.2f' % (p.price/100.0)) }" %>

+
+
+ <% end %> +
+ +
+ <% if @inactive_products && @inactive_products.count > 0 %> +

My Inactive Products

+ <% @inactive_products.each do |i| %> +
+
+ <%# image = i.name.downcase.gsub(' ', '_') %> + <% image = Product.find_photo_image(i) %> + <%= link_to image_tag(image, alt: "#{ i.name }", class: "rounded-image"), product_path(i) %> +

<%= i.name %> <%= "$#{ sprintf('%.2f' % (i.price/100.0)) }" %>

+
+
+ <% end %> + <% end %> +
diff --git a/app/views/products/new.html.erb b/app/views/products/new.html.erb new file mode 100644 index 0000000000..6bff82aaeb --- /dev/null +++ b/app/views/products/new.html.erb @@ -0,0 +1,2 @@ +

Please enter your new product:

+<%= render partial: "form" %> diff --git a/app/views/products/show.html.erb b/app/views/products/show.html.erb new file mode 100644 index 0000000000..6c6f7eef2b --- /dev/null +++ b/app/views/products/show.html.erb @@ -0,0 +1,53 @@ +

<%= "*Product is currently retired*" if @product.active == false %> + +
+
+ <% image = Product.find_photo_image(@product) %> +

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

+
+

<%= "Description: #{ @product.description }" %>

+
+
+
+

<%= @product.name %>

+

<%= "Price: $#{ sprintf('%.2f' % (@product.price/100.0)) }" %>

+ <%= form_for @order_item, method: :post do |f| %> + <%= f.number_field :quantity, value: 1, min: 1 %> + + <%= f.hidden_field :product_id, value: @product.id %> +
<%= f.submit "Add to cart", class: "add-button" %>
+ <% end %> +

Stock: <%= @product.inventory %>

+
+
+ + <% if current_user && current_user.id == @product.merchant_id %> +

<%= button_to "Edit Product", edit_product_path, method: :get, class: "edit-button-show" %>
+ <% if @product.active == true %> +
<%= button_to "Retire product", retire_product_path, method: :patch, class: "retire-button" %>
+ <% else %> +
<%= button_to "Reinstate product", reinstate_product_path, method: :patch, class: "retire-button" %>
+ <% end %> + <% end %> + +

Reviews

+ <% unless @reviews.count == 0 %> +
<%= "Average rating: #{ @average } " %>
+ <% @reviews.each do |r| %> +

<% r.rating.times do |i| %> + <%= "🐵 " %> + <% end %> + <%= ": #{ r.description }" %>

+ <% end %> + <% else %> +

No reviews yet.

+ <% end %> + + <% unless current_user != nil && @product.merchant_id == current_user.id %> +

<%= link_to "Review this product", new_product_review_path(@product) %>

+ <% else %> +

You cannot review your own product

+ <% end %> + + + diff --git a/app/views/reviews/_form.html.erb b/app/views/reviews/_form.html.erb new file mode 100644 index 0000000000..12360aaed0 --- /dev/null +++ b/app/views/reviews/_form.html.erb @@ -0,0 +1,23 @@ +<% if @review.errors.any? %> + +<% end %> + +
+ <%= form_for @review, url: product_path(@review.product_id) do |f| %> + <%= f.label "Rating" %> + <%= f.select(:rating, options_for_select(1..5)) %> + + <%= f.hidden_field :product_id, value: @review.product_id %> + + <%= f.label "Description" %> + <%= f.text_area :description %> + + <%= f.submit %> + <% end %> +
diff --git a/app/views/reviews/new.html.erb b/app/views/reviews/new.html.erb new file mode 100644 index 0000000000..18e87e0924 --- /dev/null +++ b/app/views/reviews/new.html.erb @@ -0,0 +1,2 @@ +

Add a review for <%= Product.find(@review.product_id).name %>

+<%= render partial: "form" %> diff --git a/app/views/sessions/login_failure.html.erb b/app/views/sessions/login_failure.html.erb new file mode 100644 index 0000000000..ab96f50fb2 --- /dev/null +++ b/app/views/sessions/login_failure.html.erb @@ -0,0 +1,3 @@ +

Login failed. Please try again.

+ +<%= button_to "Try again", '/auth/github' %> diff --git a/bin/rails b/bin/rails index 0138d79b75..b9eea18d29 100755 --- a/bin/rails +++ b/bin/rails @@ -1,4 +1,10 @@ #!/usr/bin/env ruby +if ENV['RAILS_ENV'] == 'test' + require 'simplecov' + SimpleCov.start 'rails' + puts "required simplecov" +end + begin load File.expand_path('../spring', __FILE__) rescue LoadError => e 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/environments/production.rb b/config/environments/production.rb index 5c1b32e486..e744ae6c97 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -4,6 +4,11 @@ # Code is not reloaded between requests. config.cache_classes = true + + + config.serve_static_files = true + config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' + # Eager load code on boot. This eager loads most of Rails and # your application in memory, allowing both threaded web servers # and those relying on copy on write to perform better. @@ -29,7 +34,7 @@ # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + config.assets.compile = true # Asset digests allow you to set far-future HTTP expiration dates on all assets, # yet still be able to expire them through the digest params. diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb index 01ef3e6630..bab1f8da13 100644 --- a/config/initializers/assets.rb +++ b/config/initializers/assets.rb @@ -9,3 +9,5 @@ # Precompile additional assets. # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. # Rails.application.config.assets.precompile += %w( search.js ) + +Rails.application.config.assets.precompile += %w( vendor/modernizr.js ) 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/config/routes.rb b/config/routes.rb index 3f66539d54..b970c00103 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,38 @@ Rails.application.routes.draw do + + get 'carts/show', as: "cart" + + root 'guests#index' + # do we need route w/ callback? + get "/auth/:provider/callback" => "sessions#create" # called from the provider (github) OAuth strategy and not by a link the user clicks on + get "/sessions", to: "sessions#index", as: "sessions" + delete "/sessions", to: "sessions#destroy", as: "logout" + + + resources :products do + resources :reviews, except: [:index, :show] + end + + post "/products/:id", to: "reviews#create" + + # get "/products/:id/add_to_cart", to: "order_items#add_to_cart", as: "add_to_cart" + + patch "/products/:id/retire", to: "products#retire_product", as: "retire_product" + + patch "/products/:id/reinstate", to: "products#reinstate_product", as: "reinstate_product" + + resources :merchants, only: [:new, :create, :show] + + resources :order_items, except: [:show] + + resources :orders, except: [:index] + resources :categories, only: [:create, :new] + + get 'category/:category_id/products', to: 'categories#show', as: 'category' + get 'merchant/:merchant_id/products', to: 'merchants#products', as: 'merchant-products' + + + get "/sessions/login_failure", to: "sessions#login_failure", as: "login_failure" # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/config/secrets.yml b/config/secrets.yml index 6442868fc1..b4c347a766 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: 1f6b68ce2441aff76d04c898328a47112049bb30e0f8a87b96dc91d813d28539751610a16664d1484923d8c675b0554c1c5a510ae5bc01c295de03be73d8a98f test: - secret_key_base: f9c5bd6c383573755e5f8cf214402a51eb5ce8c7d0df9860dbf980f03fbd6c508e97412644b6a80d7034774602b05e05988bf7ab8ff594912950fdb590589a4d + secret_key_base: 2b5b0e949a7f8156e16d19f65ad283a32d289df66ccd158ea51aa8dadd2d0aa7f97b633c2876a8eb4b09b380174770b23a76f94ed400e0a4ebd34bc1fc6572c0 # Do not keep production secrets in the repository, # instead read values from the environment. diff --git a/db/migrate/20161018190406_create_merchants.rb b/db/migrate/20161018190406_create_merchants.rb new file mode 100644 index 0000000000..31a35702e3 --- /dev/null +++ b/db/migrate/20161018190406_create_merchants.rb @@ -0,0 +1,10 @@ +class CreateMerchants < ActiveRecord::Migration + def change + create_table :merchants do |t| + t.string :username + t.string :email + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161018190428_create_products.rb b/db/migrate/20161018190428_create_products.rb new file mode 100644 index 0000000000..2541ea32b4 --- /dev/null +++ b/db/migrate/20161018190428_create_products.rb @@ -0,0 +1,11 @@ +class CreateProducts < ActiveRecord::Migration + def change + create_table :products do |t| + t.string :name + t.integer :price + t.belongs_to :merchant, index: true + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161018190433_create_orders.rb b/db/migrate/20161018190433_create_orders.rb new file mode 100644 index 0000000000..7d4555c4a2 --- /dev/null +++ b/db/migrate/20161018190433_create_orders.rb @@ -0,0 +1,8 @@ +class CreateOrders < ActiveRecord::Migration + def change + create_table :orders do |t| + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161018190438_create_order_items.rb b/db/migrate/20161018190438_create_order_items.rb new file mode 100644 index 0000000000..2579b2729b --- /dev/null +++ b/db/migrate/20161018190438_create_order_items.rb @@ -0,0 +1,11 @@ +class CreateOrderItems < ActiveRecord::Migration + def change + create_table :order_items do |t| + t.integer :quantity + t.belongs_to :product, index: true + t.belongs_to :order, index: true + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161018190443_create_reviews.rb b/db/migrate/20161018190443_create_reviews.rb new file mode 100644 index 0000000000..826c1f479a --- /dev/null +++ b/db/migrate/20161018190443_create_reviews.rb @@ -0,0 +1,10 @@ +class CreateReviews < ActiveRecord::Migration + def change + create_table :reviews do |t| + t.integer :rating + t.belongs_to :product, index: true + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161018220958_add_inventory_to_products.rb b/db/migrate/20161018220958_add_inventory_to_products.rb new file mode 100644 index 0000000000..de69fedb52 --- /dev/null +++ b/db/migrate/20161018220958_add_inventory_to_products.rb @@ -0,0 +1,5 @@ +class AddInventoryToProducts < ActiveRecord::Migration + def change + add_column :products, :inventory, :integer + end +end diff --git a/db/migrate/20161019184326_add_a_lot_to_orders.rb b/db/migrate/20161019184326_add_a_lot_to_orders.rb new file mode 100644 index 0000000000..fe4c93acf2 --- /dev/null +++ b/db/migrate/20161019184326_add_a_lot_to_orders.rb @@ -0,0 +1,17 @@ +class AddALotToOrders < ActiveRecord::Migration + def change + add_column :orders, :name, :string + add_column :orders, :email, :string + add_column :orders, :street_address, :string + add_column :orders, :city, :string + add_column :orders, :state, :string + add_column :orders, :mailing_zip, :string + add_column :orders, :cc_number, :string + add_column :orders, :cc_exp_month, :integer + add_column :orders, :cc_exp_year, :integer + add_column :orders, :cc_sec_code, :integer + add_column :orders, :billing_zip, :string + add_column :orders, :order_status, :boolean + add_column :orders, :time_placed, :datetime + end +end diff --git a/db/migrate/20161019184617_add_uid_and_provider_to_merchants.rb b/db/migrate/20161019184617_add_uid_and_provider_to_merchants.rb new file mode 100644 index 0000000000..5f684ef73e --- /dev/null +++ b/db/migrate/20161019184617_add_uid_and_provider_to_merchants.rb @@ -0,0 +1,7 @@ +class AddUidAndProviderToMerchants < ActiveRecord::Migration + def change + add_column :merchants, :uid, :integer + add_column :merchants, :provider, :integer + + end +end diff --git a/db/migrate/20161019184956_add_password_to_merchants.rb b/db/migrate/20161019184956_add_password_to_merchants.rb new file mode 100644 index 0000000000..974aa76765 --- /dev/null +++ b/db/migrate/20161019184956_add_password_to_merchants.rb @@ -0,0 +1,5 @@ +class AddPasswordToMerchants < ActiveRecord::Migration + def change + add_column :merchants, :password, :string + end +end diff --git a/db/migrate/20161019185033_change_provider_column.rb b/db/migrate/20161019185033_change_provider_column.rb new file mode 100644 index 0000000000..dd355ab87d --- /dev/null +++ b/db/migrate/20161019185033_change_provider_column.rb @@ -0,0 +1,5 @@ +class ChangeProviderColumn < ActiveRecord::Migration + def change + change_column :merchants, :provider, :string + end +end diff --git a/db/migrate/20161019185925_remove_password_from_merchants.rb b/db/migrate/20161019185925_remove_password_from_merchants.rb new file mode 100644 index 0000000000..2d3575c3df --- /dev/null +++ b/db/migrate/20161019185925_remove_password_from_merchants.rb @@ -0,0 +1,5 @@ +class RemovePasswordFromMerchants < ActiveRecord::Migration + def change + remove_column(:merchants, :password) + end +end diff --git a/db/migrate/20161020212520_create_ordered_products.rb b/db/migrate/20161020212520_create_ordered_products.rb new file mode 100644 index 0000000000..4fd9d80c92 --- /dev/null +++ b/db/migrate/20161020212520_create_ordered_products.rb @@ -0,0 +1,8 @@ +class CreateOrderedProducts < ActiveRecord::Migration + def change + create_table :ordered_products do |t| + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20161020212610_add_columns_to_ordered_products.rb b/db/migrate/20161020212610_add_columns_to_ordered_products.rb new file mode 100644 index 0000000000..5c0f7dbe08 --- /dev/null +++ b/db/migrate/20161020212610_add_columns_to_ordered_products.rb @@ -0,0 +1,8 @@ +class AddColumnsToOrderedProducts < ActiveRecord::Migration + def change + create_join_table :category, :products do |t| + t.index :product_id + t.index :category_id + end + end +end diff --git a/db/migrate/20161020213543_create_category_products.rb b/db/migrate/20161020213543_create_category_products.rb new file mode 100644 index 0000000000..621e3afa49 --- /dev/null +++ b/db/migrate/20161020213543_create_category_products.rb @@ -0,0 +1,8 @@ +class CreateCategoryProducts < ActiveRecord::Migration + def change + # create_table :category_products do |t| + # + # t.timestamps null: false + # end + end +end diff --git a/db/migrate/20161021190043_add_category_to_products.rb b/db/migrate/20161021190043_add_category_to_products.rb new file mode 100644 index 0000000000..2d4c5a20fa --- /dev/null +++ b/db/migrate/20161021190043_add_category_to_products.rb @@ -0,0 +1,5 @@ +class AddCategoryToProducts < ActiveRecord::Migration + def change + add_column :products, :category, :integer + end +end diff --git a/db/migrate/20161021190822_add_description_to_products.rb b/db/migrate/20161021190822_add_description_to_products.rb new file mode 100644 index 0000000000..4879e84db5 --- /dev/null +++ b/db/migrate/20161021190822_add_description_to_products.rb @@ -0,0 +1,5 @@ +class AddDescriptionToProducts < ActiveRecord::Migration + def change + add_column :products, :description, :string + end +end diff --git a/db/migrate/20161021213103_drop_ordered_products_table.rb b/db/migrate/20161021213103_drop_ordered_products_table.rb new file mode 100644 index 0000000000..d5185afa42 --- /dev/null +++ b/db/migrate/20161021213103_drop_ordered_products_table.rb @@ -0,0 +1,5 @@ +class DropOrderedProductsTable < ActiveRecord::Migration + def change + drop_table :ordered_products + end +end diff --git a/db/migrate/20161021222829_create_categories_table.rb b/db/migrate/20161021222829_create_categories_table.rb new file mode 100644 index 0000000000..eda744dab1 --- /dev/null +++ b/db/migrate/20161021222829_create_categories_table.rb @@ -0,0 +1,6 @@ +class CreateCategoriesTable < ActiveRecord::Migration + def change + create_table :categories_tables do |t| + end + end +end diff --git a/db/migrate/20161024044744_add_description_to_reviews.rb b/db/migrate/20161024044744_add_description_to_reviews.rb new file mode 100644 index 0000000000..9447842178 --- /dev/null +++ b/db/migrate/20161024044744_add_description_to_reviews.rb @@ -0,0 +1,5 @@ +class AddDescriptionToReviews < ActiveRecord::Migration + def change + add_column :reviews, :description, :string + end +end diff --git a/db/migrate/20161024175847_change_category_to_id_in_products.rb b/db/migrate/20161024175847_change_category_to_id_in_products.rb new file mode 100644 index 0000000000..2aa267a320 --- /dev/null +++ b/db/migrate/20161024175847_change_category_to_id_in_products.rb @@ -0,0 +1,6 @@ +class ChangeCategoryToIdInProducts < ActiveRecord::Migration + def change + add_column :products, :category_id, :integer + remove_column :products, :category, :integer + end +end diff --git a/db/migrate/20161024180610_add_column_url_to_products.rb b/db/migrate/20161024180610_add_column_url_to_products.rb new file mode 100644 index 0000000000..45e9158654 --- /dev/null +++ b/db/migrate/20161024180610_add_column_url_to_products.rb @@ -0,0 +1,5 @@ +class AddColumnUrlToProducts < ActiveRecord::Migration + def change + add_column :products, :url, :string + end +end diff --git a/db/migrate/20161024181022_add_photo_url_to_products.rb b/db/migrate/20161024181022_add_photo_url_to_products.rb new file mode 100644 index 0000000000..f2a85a8921 --- /dev/null +++ b/db/migrate/20161024181022_add_photo_url_to_products.rb @@ -0,0 +1,5 @@ +class AddPhotoUrlToProducts < ActiveRecord::Migration + def change + add_column :products, :photo_url, :string + end +end diff --git a/db/migrate/20161024191950_remove_url_from_products.rb b/db/migrate/20161024191950_remove_url_from_products.rb new file mode 100644 index 0000000000..677e3b6366 --- /dev/null +++ b/db/migrate/20161024191950_remove_url_from_products.rb @@ -0,0 +1,5 @@ +class RemoveUrlFromProducts < ActiveRecord::Migration + def change + remove_column :products, :url, :string + end +end diff --git a/db/migrate/20161024224642_add_active_to_products.rb b/db/migrate/20161024224642_add_active_to_products.rb new file mode 100644 index 0000000000..fb7b2d0c51 --- /dev/null +++ b/db/migrate/20161024224642_add_active_to_products.rb @@ -0,0 +1,5 @@ +class AddActiveToProducts < ActiveRecord::Migration + def change + add_column :products, :active, :boolean + end +end diff --git a/db/migrate/20161025212925_change_order_status_datatype.rb b/db/migrate/20161025212925_change_order_status_datatype.rb new file mode 100644 index 0000000000..206beb273c --- /dev/null +++ b/db/migrate/20161025212925_change_order_status_datatype.rb @@ -0,0 +1,5 @@ +class ChangeOrderStatusDatatype < ActiveRecord::Migration + def change + change_column :orders, :order_status, :string + end +end diff --git a/db/migrate/20161026184612_change_default_value_of_active_product_to_true.rb b/db/migrate/20161026184612_change_default_value_of_active_product_to_true.rb new file mode 100644 index 0000000000..63f79f8e21 --- /dev/null +++ b/db/migrate/20161026184612_change_default_value_of_active_product_to_true.rb @@ -0,0 +1,5 @@ +class ChangeDefaultValueOfActiveProductToTrue < ActiveRecord::Migration + def change + change_column :products, :active, :boolean, default: true + end +end diff --git a/db/migrate/20161027021452_change_datatype_price_product.rb b/db/migrate/20161027021452_change_datatype_price_product.rb new file mode 100644 index 0000000000..d3e7feb327 --- /dev/null +++ b/db/migrate/20161027021452_change_datatype_price_product.rb @@ -0,0 +1,5 @@ +class ChangeDatatypePriceProduct < ActiveRecord::Migration + def change + change_column :products, :price, :float + end +end diff --git a/db/migrate/20161027220418_remove_cateogory_id_from_products.rb b/db/migrate/20161027220418_remove_cateogory_id_from_products.rb new file mode 100644 index 0000000000..873a5bfc0b --- /dev/null +++ b/db/migrate/20161027220418_remove_cateogory_id_from_products.rb @@ -0,0 +1,5 @@ +class RemoveCateogoryIdFromProducts < ActiveRecord::Migration + def change + remove_column :products, :category_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 0000000000..8ff5887162 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,95 @@ +# 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: 20161027220418) 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 "categories_tables", force: :cascade do |t| + end + + create_table "category_products", id: false, force: :cascade do |t| + t.integer "category_id", null: false + t.integer "product_id", null: false + end + + add_index "category_products", ["category_id"], name: "index_category_products_on_category_id" + add_index "category_products", ["product_id"], name: "index_category_products_on_product_id" + + create_table "merchants", force: :cascade do |t| + t.string "username" + t.string "email" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "uid" + t.string "provider" + end + + create_table "order_items", force: :cascade do |t| + t.integer "quantity" + t.integer "product_id" + t.integer "order_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "order_items", ["order_id"], name: "index_order_items_on_order_id" + add_index "order_items", ["product_id"], name: "index_order_items_on_product_id" + + create_table "orders", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "name" + t.string "email" + t.string "street_address" + t.string "city" + t.string "state" + t.string "mailing_zip" + t.string "cc_number" + t.integer "cc_exp_month" + t.integer "cc_exp_year" + t.integer "cc_sec_code" + t.string "billing_zip" + t.string "order_status" + t.datetime "time_placed" + end + + create_table "products", force: :cascade do |t| + t.string "name" + t.float "price" + t.integer "merchant_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.integer "inventory" + t.string "description" + t.string "photo_url" + t.boolean "active", default: true + end + + add_index "products", ["merchant_id"], name: "index_products_on_merchant_id" + + create_table "reviews", force: :cascade do |t| + t.integer "rating" + t.integer "product_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "description" + end + + add_index "reviews", ["product_id"], name: "index_reviews_on_product_id" + +end diff --git a/db/seed_csvs/all_products.csv b/db/seed_csvs/all_products.csv new file mode 100644 index 0000000000..78aa31f96a --- /dev/null +++ b/db/seed_csvs/all_products.csv @@ -0,0 +1,42 @@ +1,Pygmy Goat,20000,tiny animals,30,It's a tiny goat! The females can be about 16 inches tall and the males are about 23 inches tall. For weight the females range from 50 to 75 pounds. The males are somewhat larger with a weight from 60 to 85 pounds. +2,Micro Pig,150000,tiny animals,16,It's a tiny pig! Fun Fact: Pigs are hypoallergenic. +3,Falabella,400000,tiny animals,22,It's a tiny horse! Body weight ranges from 40 pounds to 100 pounds. +4,Pygmy Hedgehog,15000,tiny animals,38,It's a tiny hedgehog! It's nocturnal. +1,Dwarf Caiman,30000,tiny animals,23,It's a tiny caiman! Their diet includes fish and mice. +2,Squirrel Monkey,600000,tiny animals,15,"It's a tiny monkey! Squirrel monkeys are omnivores. They eat different types of flowers, leaves, buds, nuts, insects, lizards and eggs." +3,Small Crate,9000,tiny animals,63,A secure container for your pet. +4,Boots,2000,tiny animals,55,Little booties. Recommended for Micro Pig and Pygmy Goat especially. +1,Quokka,300000,marsupials,100,Shout out to our gurl Kari. +2,Koala,250000,marsupials,14,Cute as a button. Life span: 20 years. +3,Wallaby,400000,marsupials,66,"Like a kangaroo, kind of." +4,Tasmanian Devil,900000,marsupials,3,You can name it 'Taz'. +1,Eucalyptus Leaves,5000,marsupials,100,"Yum yum, food." +2,Marsupials Book,2500,marsupials,29,Learn stuff about your new animal. +3,Lion,750000,big cats,30,"Unlike other cats, lions are very social animals. They live in groups, called prides, of around 30 lions. We recommend buying several." +4,Tiger,700000,big cats,5,The tiger is the biggest species of the cat family +1,Snow Leopard,800000,big cats,3,"Snow leopards primarily hunt wild sheep and goats. Snow leopards are also known to eat smaller animals like rodents, hares and game birds." +2,Cheetah,850000,big cats,6,"The cheetah is the fastest land animal in the world, reaching speeds of up to 113km/h. They can accelerate from 0 to 100km/h in just 3 seconds." +3,Big Catnip,1500,big cats,65,We recommend getting A LOT. +4,Scratching Post,5000,big cats,48,Big cats will tear this ish up. Buy in bulk. +1,Big Cats Book,2500,big cats,45,Learn stuff about your new animal. +2,Elephant,5000000,safari,4,Fun Fact: Elephants have matriarchal societies. +3,Giraffe,4700000,safari,2,"Fun Fact: Just like snowflakes and human fingerprints, no two giraffes have the same spot pattern." +4,Zebra,3500000,safari,6,"Joke: What black, white, and red all over? A zebra in a blender. Note: We do not recommend putting your Zebra in a blender." +1,Hippopotamus,4000000,safari,10,Fun Fact: The name Hippopotamus comes from the Ancient Greek 'river horse'. +2,Poop Scoop,10000,safari,99,Clean up after your pets! +3,Giant Ball,5000,safari,59,Recommended for large animals like Elephants. +4,Capuchin,600000,monkey,12,Think Marcel from Friends. +1,Chimpanzee,6000000,monkey,14,Fun Fact: We share approximately 98% of our DNA with chimps. +2,Baboon,350000,monkey,6,Recommended name: Rafiki. +3,Rhesus Macaque,350000,monkey,20,"Their natural range includes Afghanistan, Pakistan, India, Southeast Asia, and China. A few troops of introduced rhesus monkeys now live wild in Florida. " +4,Hat,2000,monkey,60,So cute. Recommended with Cymbals. +1,Cymbals,2500,monkey,77,Loud and fun toy for your monkey. Recommended with Hat. +2,Bananas,900,monkey,123,Monkey food! +3,Monkey Book,2500,monkey,96,Learn stuff about your new animal. +4,Alligator,700000,reptile,53,Not recommended for households with children. +1,Crocodile,600000,reptile,35,Not recommended for households with children. +2,Chameleon,90000,reptile,32,Guaranteed to change colors! +3,Bearded Dragon,100000,reptile,22,#BeardSwag +4,Harness,3000,reptile,67,Make sure it can't escape during walks. Recommended with Leash. +1,Leash,2000,reptile,77,Take that thing for a walk. Recommended with Harness. +2,Reptile Book,2500,reptile,50,Learn stuff about your new animal. diff --git a/db/seed_csvs/categories.csv b/db/seed_csvs/categories.csv new file mode 100644 index 0000000000..803a3378c2 --- /dev/null +++ b/db/seed_csvs/categories.csv @@ -0,0 +1,6 @@ +tiny animals +marsupials +big cats +safari +monkey +reptile diff --git a/db/seed_csvs/categoryproducts.csv b/db/seed_csvs/categoryproducts.csv new file mode 100644 index 0000000000..cb9d3f4fd7 --- /dev/null +++ b/db/seed_csvs/categoryproducts.csv @@ -0,0 +1,42 @@ +1,1 +2,1 +3,1 +4,1 +5,1 +6,1 +7,1 +8,1 +9,2 +10,2 +11,2 +12,2 +13,2 +14,2 +15,3 +16,3 +17,3 +18,3 +19,3 +20,3 +21,3 +22,4 +23,4 +24,4 +25,4 +26,4 +27,4 +28,5 +29,5 +30,5 +31,5 +32,5 +33,5 +34,5 +35,5 +36,6 +37,6 +38,6 +39,6 +40,6 +41,6 +42,6 diff --git a/db/seed_csvs/products.csv b/db/seed_csvs/products.csv new file mode 100644 index 0000000000..e6f62e542a --- /dev/null +++ b/db/seed_csvs/products.csv @@ -0,0 +1,42 @@ +1,Pygmy Goat,20000,1,30,It's a tiny goat! The females can be about 16 inches tall and the males are about 23 inches tall. For weight the females range from 50 to 75 pounds. The males are somewhat larger with a weight from 60 to 85 pounds. +2,Micro Pig,150000,1,16,It's a tiny pig! Fun Fact: Pigs are hypoallergenic. +3,Falabella,400000,1,22,It's a tiny horse! Body weight ranges from 40 pounds to 100 pounds. +4,Pygmy Hedgehog,15000,1,38,It's a tiny hedgehog! It's nocturnal. +1,Dwarf Caiman,30000,1,23,It's a tiny caiman! Their diet includes fish and mice. +2,Squirrel Monkey,600000,1,15,"It's a tiny monkey! Squirrel monkeys are omnivores. They eat different types of flowers, leaves, buds, nuts, insects, lizards and eggs." +3,Small Crate,9000,1,63,A secure container for your pet. +4,Boots,2000,1,55,Little booties. Recommended for Micro Pig and Pygmy Goat especially. +1,Quokka,300000,2,100,Shout out to our gurl Kari. +2,Koala,250000,2,14,Cute as a button. Life span: 20 years. +3,Wallaby,400000,2,66,"Like a kangaroo, kind of." +4,Tasmanian Devil,900000,2,3,You can name it 'Taz'. +1,Eucalyptus Leaves,5000,2,100,"Yum yum, food." +2,Marsupials Book,2500,2,29,Learn stuff about your new animal. +3,Lion,750000,3,30,"Unlike other cats, lions are very social animals. They live in groups, called prides, of around 30 lions. We recommend buying several." +4,Tiger,700000,3,5,The tiger is the biggest species of the cat family +1,Snow Leopard,800000,3,3,"Snow leopards primarily hunt wild sheep and goats. Snow leopards are also known to eat smaller animals like rodents, hares and game birds." +2,Cheetah,850000,3,6,"The cheetah is the fastest land animal in the world, reaching speeds of up to 113km/h. They can accelerate from 0 to 100km/h in just 3 seconds." +3,Big Catnip,1500,3,65,We recommend getting A LOT. +4,Scratching Post,5000,3,48,Big cats will tear this ish up. Buy in bulk. +1,Big Cats Book,2500,3,45,Learn stuff about your new animal. +2,Elephant,5000000,4,4,Fun Fact: Elephants have matriarchal societies. +3,Giraffe,4700000,4,2,"Fun Fact: Just like snowflakes and human fingerprints, no two giraffes have the same spot pattern." +4,Zebra,3500000,4,6,"Joke: What black, white, and red all over? A zebra in a blender. Note: We do not recommend putting your Zebra in a blender." +1,Hippopotamus,4000000,4,10,Fun Fact: The name Hippopotamus comes from the Ancient Greek 'river horse'. +2,Poop Scoop,10000,4,99,Clean up after your pets! +3,Giant Ball,5000,4,59,Recommended for large animals like Elephants. +4,Capuchin,600000,5,12,Think Marcel from Friends. +1,Chimpanzee,6000000,5,14,Fun Fact: We share approximately 98% of our DNA with chimps. +2,Baboon,350000,5,6,Recommended name: Rafiki. +3,Rhesus Macaque,350000,5,20,"Their natural range includes Afghanistan, Pakistan, India, Southeast Asia, and China. A few troops of introduced rhesus monkeys now live wild in Florida. " +4,Hat,2000,5,60,So cute. Recommended with Cymbals. +1,Cymbals,2500,5,77,Loud and fun toy for your monkey. Recommended with Hat. +2,Bananas,900,5,123,Monkey food! +3,Monkey Book,2500,5,96,Learn stuff about your new animal. +4,Alligator,700000,6,53,Not recommended for households with children. +1,Crocodile,600000,6,35,Not recommended for households with children. +2,Chameleon,90000,6,32,Guaranteed to change colors! +3,Bearded Dragon,100000,6,22,#BeardSwag +4,Harness,3000,6,67,Make sure it can't escape during walks. Recommended with Leash. +1,Leash,2000,6,77,Take that thing for a walk. Recommended with Harness. +2,Reptile Book,2500,6,50,Learn stuff about your new animal. diff --git a/db/seeds.rb b/db/seeds.rb index 4edb1e857e..3af3e9f47a 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -1,7 +1,16 @@ -# This file should contain all the record creation needed to seed the database with its default values. -# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup). -# -# Examples: -# -# cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }]) -# Mayor.create(name: 'Emanuel', city: cities.first) +require 'csv' + +CSV.read('db/seed_csvs/products.csv').each do |line| + product_hash = { merchant_id: line[0].to_i, name: line[1], price: line[2].to_f, inventory: line[4], description: line[5], photo_url: "1", active: true } + Product.create!(product_hash) +end + +CSV.read('db/seed_csvs/categories.csv').each do |line| + category_hash = { name: line[0] } + Category.create!(category_hash) +end + +CSV.read('db/seed_csvs/categoryproducts.csv').each do |line| + category_product_hash = { product_id: line[0], category_id: line[1] } + CategoryProduct.create!(category_product_hash) +end diff --git a/erd.pdf b/erd.pdf new file mode 100644 index 0000000000..e796796f98 Binary files /dev/null and b/erd.pdf differ diff --git a/test/controllers/carts_controller_test.rb b/test/controllers/carts_controller_test.rb new file mode 100644 index 0000000000..c50e2b3867 --- /dev/null +++ b/test/controllers/carts_controller_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class CartsControllerTest < ActionController::TestCase + test "should get show" do + get :show + assert_response :success + end + +end diff --git a/test/controllers/categories_controller_test.rb b/test/controllers/categories_controller_test.rb new file mode 100644 index 0000000000..7f183c63b4 --- /dev/null +++ b/test/controllers/categories_controller_test.rb @@ -0,0 +1,38 @@ +require 'test_helper' + +class CategoriesControllerTest < ActionController::TestCase + test "should get the new form" do + get :new + assert_response :success + assert_template :new + end + + test "should show the products within a certain category" do + category = categories(:big_animals) + get :show, { category_id: category.id } + assert_response :success + assert_template :show + + products = assigns(:products) + products.each do |product| + assert_includes product.categories, category + end + end + + test "should create a new category" do + assert_difference('Category.count') do + post_params = { category: { name: "Large Animals" } } + post :create, post_params + end + category = assigns(:category) + assert_not_nil category + end + + test "should not create an invalid category" do + assert_difference('Category.count', 0) do + post :create, category: { id: 5 } + end + + assert_template :new + end +end diff --git a/test/controllers/guests_controller_test.rb b/test/controllers/guests_controller_test.rb new file mode 100644 index 0000000000..26dffaa860 --- /dev/null +++ b/test/controllers/guests_controller_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class GuestsControllerTest < ActionController::TestCase + test "should get index" do + get :index + assert_response :success + end +end diff --git a/test/controllers/merchants_controller_test.rb b/test/controllers/merchants_controller_test.rb new file mode 100644 index 0000000000..ff6250408d --- /dev/null +++ b/test/controllers/merchants_controller_test.rb @@ -0,0 +1,61 @@ +require 'test_helper' + +class MerchantsControllerTest < ActionController::TestCase + + def login_a_user + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github] + get :create, merchant: { email: "a@b.com", name: "Ada", id: 123545 } + end + + test "if a guest tries to access merchant show page, redirected to login failure" do + session[:user_id] = nil # ensure no one is logged in + + get :show, id: merchants(:snoopy).id + # if they are not logged in they cannot see the resource and are redirected to login. + assert_redirected_to login_failure_path + assert_equal "You must be logged in to view this section", flash[:error] + end + + # test "should show the view page for the specified merchant" do + # login_a_user + # + # get :show, {session[:user_id => 123545]} + # puts request.GET.inspect + # assert_response :success + # assert_template :show + # + # merchant = assigns(:merchant) + # assert_not_nil merchant + # assert_equal merchant.id, merchant_id + # end + + # test "should show the show page for the specified product" do + # login_a_user + # # product_id = products(:elephant).id + # get :show#, { id: product_id } + # assert_response :success + # assert_template :show + # + # product = assigns(:product) + # assert_not_nil product + # assert_equal product.id, product_id + # end + + test "cannot add invalid merchant" do + assert_difference('Merchant.count', 0) do + merchant_params = {merchant: { username: "Snoopy", email: "snoopy@gmail.com", id: 1 } } + post :create, merchant_params + end + assert_template :new + end + + test "can create a new merchant" do + assert_difference('Merchant.count', 1) do + merchant_params = {merchant: { username: "Yolo", email: "yolo@gmail.com" } } + post :create, merchant_params + end + merchant = assigns(:merchant) + assert_redirected_to merchant_path(merchant) + assert_not_nil merchant + end +end diff --git a/test/controllers/orderitems_controller_test.rb b/test/controllers/orderitems_controller_test.rb new file mode 100644 index 0000000000..e1fef3b230 --- /dev/null +++ b/test/controllers/orderitems_controller_test.rb @@ -0,0 +1,36 @@ +require 'test_helper' + +class OrderItemsControllerTest < ActionController::TestCase + + test "should get index" do + get :index + assert_response :success + end + + test "should create new order_item" do + assert_difference('OrderItem.count', 1) do + post_params = {order_item: { id: 100000000, quantity: 1, product_id: 2 } } + post :create, post_params + end + + order_item = assigns(:order_item) + assert_redirected_to order_items_path + assert_not_nil order_item + end + + test "should update order item" do + order_item = order_items(:one) + patch :update, id: order_item, order_item: { quantity: order_item.quantity } + assert_redirected_to order_items_path + end + + test "will remove an item from order items when deleted" do + order_item = order_items(:one) + assert_difference('OrderItem.count', -1) do + delete :destroy, id: order_item + end + + assert_redirected_to order_items_path + end + +end diff --git a/test/controllers/orders_controller_test.rb b/test/controllers/orders_controller_test.rb new file mode 100644 index 0000000000..72fe724d9e --- /dev/null +++ b/test/controllers/orders_controller_test.rb @@ -0,0 +1,94 @@ +require 'test_helper' + +class OrdersControllerTest < ActionController::TestCase + test "should get the new form" do + get :new + assert_response :success + assert_template :new + end + + test "should get the new form for a new order" do + get :new + assert_response :success + assert_template :new + + order = assigns(:order) + assert_not_nil order + assert_nil order.id + end + + test "should get the edit form" do + order = orders(:ian) + get :edit, id: order + assert_response :success + assert_template :edit + end + + test "should create new order" do + post_params = {order: { name: "Igor Smith", email: "hello@gmail.com", street_address: "38 Main St", + city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", + cc_exp_month: 5, cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102" } } + assert_difference('Order.count') do + post :create, post_params + end + + order = assigns(:order) + assert_redirected_to root_path(order) + assert_not_nil order + end + + test "should update order" do + order = orders(:buffy) + patch :update, id: order, order: { name: order.name, email: order.email, street_address: order.street_address, city: order.city, + state: order.state, mailing_zip: order.mailing_zip, cc_number: order.cc_number, cc_exp_month: order.cc_exp_month, cc_exp_year: order.cc_exp_year, + cc_sec_code: order.cc_sec_code, billing_zip: order.billing_zip} + assert_redirected_to order_path + end + + test "should render edit if update doesn't save" do + order = orders(:xander) + patch :update, id: order, order: { name: "", email: order.email, street_address: order.street_address, city: order.city, + state: order.state, mailing_zip: order.mailing_zip, cc_number: order.cc_number, cc_exp_month: order.cc_exp_month, cc_exp_year: order.cc_exp_year, + cc_sec_code: order.cc_sec_code, billing_zip: order.billing_zip } + assert_template :edit + end + + test "should show the show page for the specified order" do + order_id = orders(:xander).id + get :show, { id: order_id } + assert_response :success + assert_template :show + + order = assigns(:order) + assert_not_nil order + assert_equal order.id, order_id + end + + test "should show 404 error for invalid order id" do + order_id = 1346134598 # Bogus ID shouldn't be in DB, otherwise test is invalid + assert_raises StandardError do + Order.find(order_id) + end + + get :show, { id: order_id } + assert_response :not_found + end + + test "cannot add invalid order to DB" do + assert_difference('Order.count', 0 ) do + post_params = {order: { email: "m@gmail.com", street_address: "123 yolo st", city: "Seattle", state: "WA", mailing_zip: "98126", cc_number: "1223456743254456", cc_exp_month: 5, cc_exp_year: 2017, cc_sec_code: 328, billing_zip: 98126 }} + post :create, post_params + end + + assert_template :new + end + + test "will delete an order from the DB" do + order = orders(:buffy) + assert_difference('Order.count', -1) do + delete :destroy, id: order + end + + assert_redirected_to root_path + end +end diff --git a/test/controllers/products_controller_test.rb b/test/controllers/products_controller_test.rb new file mode 100644 index 0000000000..26ebc80016 --- /dev/null +++ b/test/controllers/products_controller_test.rb @@ -0,0 +1,140 @@ +require 'test_helper' + +class ProductsControllerTest < ActionController::TestCase + + test "should get index" do + get :index + assert_response :success + end + + test "should get the new form" do + get :new + assert_response :success + assert_template :new + end + + test "should get the new form for a new product" do + get :new + assert_response :success + assert_template :new + + product = assigns(:product) + assert_not_nil product + assert_nil product.id + end + + test "should get the edit form" do + product = products(:elephant) + get :edit, id: product + assert_response :success + assert_template :edit + end + + test "should update product" do + product = products(:elephant) + patch :update, id: product, product: { name: product.name, price: 55 } + assert_redirected_to product_path + end + + test "should render edit if updating product is unsuccessful" do + product = products(:elephant) + patch :update, id: product, product: { name: "", price: "5" } + assert_template :edit + end + + test "should create new product" do + assert_difference('Product.count') do + post_params = {product: { name: "Tigers", price: 234513, inventory: 5, description: "Yay tigers", photo_url: "tigers.jpeg"} } + post :create, post_params + end + + product = assigns(:product) + assert_redirected_to product_path(product) + assert_not_nil product + end + + + test "should render new if new product doesn't save" do + assert_difference('Product.count', 0) do + post_params = {product: { name: "Tigers", price: 234513, inventory: 5, description: "striped" } } + post :create, post_params + end + + assert_template :new + end + + + test "should show the show page for the specified product" do + product_id = products(:elephant).id + get :show, { id: product_id } + assert_response :success + assert_template :show + + product = assigns(:product) + assert_not_nil product + assert_equal product.id, product_id + end + + test "should redirect to products path if product isn't active" do + product_id = products(:monkey).id + get :show, { id: product_id } + assert_redirected_to products_path + end + + test "should show 404 error for invalid product id" do + product_id = 1346134598 # Bogus ID shouldn't be in DB, otherwise test is invalid + assert_raises StandardError do + Product.find(product_id) + end + + get :show, { id: product_id } + assert_response :not_found + end + + test "will delete a product from the DB" do + product = products(:snake) + assert_difference('Product.count', -1) do + delete :destroy, id: product + end + + assert_redirected_to products_path + end + + + # def retire_product + # p = Product.find(params[:id]) + # p.active = false + # p.save + # redirect_to products_path + # end + + test "will retire a product from the DB" do + product = products(:snake) + assert_equal product.active, true + product.retire_product + assert_equal product.active, false + end + + + + + # test "purchasing a product will decrease its number by one" do + # post :purchase, id: products(:elephant).id + # product = assigns(:product) + # assert_equal product, xxxxxx # Need to have number of products + # end + + # test "will delete a product from the DB when all are purchased" do + # product = products(:elephant) + # assert_difference('Product.count', -1) do + # delete :destroy, id: product + # end + # + # assert_redirected_to products_path + # end + + # test "will throw an error if products ordered are greater than inventory" do + # To be done + # end + +end diff --git a/test/controllers/reviews_controller_test.rb b/test/controllers/reviews_controller_test.rb new file mode 100644 index 0000000000..956f6b1532 --- /dev/null +++ b/test/controllers/reviews_controller_test.rb @@ -0,0 +1,36 @@ +require 'test_helper' + +class ReviewsControllerTest < ActionController::TestCase + test "should get the new form" do + get :new, :product_id => 1 + assert_response :success + assert_template :new + end + + test "should create a new review and add to the DB" do + product = Product.create(name: "Elephant", price: 100200, inventory: 2, description: "Elephants are good", photo_url: "elephants.jpeg", active: true, merchant_id: 1, id: 1) + assert_difference('Review.count') do + post_params = {id: product.id, review: {description: "gsdg", rating: 4, product_id: product.id} } + post :create, post_params + end + review = assigns(:review) + assert_not_nil review + assert_redirected_to product_path(review.product_id) + end + + test "should not create a review with missing fields" do + product = Product.create(name: "Elephant", price: 100200, inventory: 2, description: "Elephants are good", photo_url: "elephants.jpeg", active: true, merchant_id: 1, id: 1) + assert_difference('Review.count', 0) do + post_params = {id: product.id, review: {description: "gsdg", product_id: product.id} } + post :create, post_params + end + assert_template :new + end + + # test "merchant should not be able to review their own product" do + # product = Product.create(name: "Elephant", price: 100200, inventory: 2, description: "Elephants are good", photo_url: "elephants.jpeg", active: true, merchant_id: 1, id: 1) + # @current_user && @current_user.id = product.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 new file mode 100644 index 0000000000..5a3dcfc9dd --- /dev/null +++ b/test/controllers/sessions_controller_test.rb @@ -0,0 +1,40 @@ +require 'test_helper' + +class SessionsControllerTest < ActionController::TestCase + def login_a_merchant + request.env['omniauth.auth'] = OmniAuth.config.mock_auth[:github] + get :create, { provider: "github", uid: 123 } + end + + test "can login a merchant" do + assert_difference('Merchant.count', 1) do + login_a_merchant + assert_response :redirect + assert_redirected_to root_path + assert_not_nil session[:merchant_id] + end + end + + test "if a merchant logs in twice, it doesn't create a second merchant" do + assert_difference('Merchant.count', 1) do + login_a_merchant + end + assert_no_difference('Merchant.count') do + login_a_merchant + assert_response :redirect + assert_redirected_to root_path + end + end + + test "can logout a merchant" do + assert_difference('Merchant.count', 1) do + login_a_merchant + assert_not_nil session[:merchant_id] + end + assert_difference('Merchant.count', -1) do + delete :destroy + end + + assert_nil session[:merchant_id] + end +end diff --git a/test/fixtures/categories.yml b/test/fixtures/categories.yml new file mode 100644 index 0000000000..3a62b56fdb --- /dev/null +++ b/test/fixtures/categories.yml @@ -0,0 +1,10 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +big_animals: + name: Big Animals + +small_animals: + name: Small Animals + +reptiles: + name: Reptiles diff --git a/test/fixtures/category_products.yml b/test/fixtures/category_products.yml new file mode 100644 index 0000000000..bed4049056 --- /dev/null +++ b/test/fixtures/category_products.yml @@ -0,0 +1,15 @@ +one: + product: elephant + category: big_animals + +two: + product: kuakka + category: small_animals + +three: + product: monkey + category: small_animals + +four: + product: snake + category: reptiles diff --git a/test/fixtures/merchants.yml b/test/fixtures/merchants.yml new file mode 100644 index 0000000000..2508256b43 --- /dev/null +++ b/test/fixtures/merchants.yml @@ -0,0 +1,9 @@ +snoopy: + username: Snoopy + email: snoopy@gmail.com + id: 1 + +Ada: + username: Ada + email: a@b.com + id: 123545 diff --git a/test/fixtures/order_items.yml b/test/fixtures/order_items.yml new file mode 100644 index 0000000000..30f924843e --- /dev/null +++ b/test/fixtures/order_items.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + quantity: 1 + order_id: 1 + product_id: 1 + id: 1 diff --git a/test/fixtures/orders.yml b/test/fixtures/orders.yml new file mode 100644 index 0000000000..4795b466d5 --- /dev/null +++ b/test/fixtures/orders.yml @@ -0,0 +1,71 @@ +# 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 +# + +nicole: + name: Nicole + email: email@gmail.com + street_address: 12 Main St + city: Seattle + state: WA + mailing_zip: 98201 + cc_number: 1223456743254456 + cc_exp_month: 11 + cc_exp_year: 2017 + cc_sec_code: 250 + billing_zip: 98201 + +ian: + name: Ian + email: hi@gmail.com + street_address: 786 Square St + city: Pittsburgh + state: PA + mailing_zip: 15201 + cc_number: 8888777766665555 + cc_exp_month: 5 + cc_exp_year: 2018 + cc_sec_code: 847 + billing_zip: 15201 + +buffy: + name: Buffy Summers + email: howdy@gmail.com + street_address: 786 Springfield Rd + city: Sunnydale + state: CA + mailing_zip: 42183 + cc_number: 7766558765435675 + cc_exp_month: 1 + cc_exp_year: 2020 + cc_sec_code: 456 + billing_zip: 42183 + +xander: + name: Xander + email: yinz@gmail.com + street_address: 347 Alpine Rd + city: Sunnydale + state: CA + mailing_zip: 42184 + cc_number: 8976545687678976 + cc_exp_month: 5 + cc_exp_year: 2018 + cc_sec_code: 354 + billing_zip: 42184 + +willow: + name: Willow + email: Willow@gmail.com + street_address: 123 Yolo St + city: Sunnydale + state: CA + mailing_zip: 42184 + cc_number: 8976545687678977 + cc_exp_month: 9 + cc_exp_year: 2015 + cc_sec_code: 355 + billing_zip: 42184 diff --git a/test/fixtures/products.yml b/test/fixtures/products.yml new file mode 100644 index 0000000000..eae9ba4a1b --- /dev/null +++ b/test/fixtures/products.yml @@ -0,0 +1,39 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +elephant: + name: Elephant + price: 100200 + inventory: 2 + description: Elephants are good + photo_url: elephants.jpeg + active: true + merchant_id: 1 + id: 1 + +monkey: + name: Monkey + price: 10290 + inventory: 3 + description: Monkeys rule! + photo_url: monkeys.jpeg + active: false + merchant_id: 1 + id: 2 + +kuakka: + name: Kuakka + price: 10340 + inventory: 43 + description: Yay kari + photo_url: kuakkas.jpeg + active: true + merchant_id: 1 + +snake: + name: Snake + price: 20000 + inventory: 1 + description: Snakes have no legs + photo_url: nolegs.jpeg + active: true + merchant_id: 1 diff --git a/test/fixtures/reviews.yml b/test/fixtures/reviews.yml new file mode 100644 index 0000000000..0004075ce3 --- /dev/null +++ b/test/fixtures/reviews.yml @@ -0,0 +1,20 @@ +best_product: + rating: 5 + product_id: 1 + description: BEST EVA! + +no_rating: + rating: + product_id: 2 + +non_integer: + rating: 2.5 + product_id: 3 + +too_low: + rating: 0 + product_id: 4 + +too_high: + rating: 10 + product_id: 5 diff --git a/test/models/category_product_test.rb b/test/models/category_product_test.rb new file mode 100644 index 0000000000..7fc8c0fe83 --- /dev/null +++ b/test/models/category_product_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CategoryProductTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end 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/merchant_test.rb b/test/models/merchant_test.rb new file mode 100644 index 0000000000..6d87b08610 --- /dev/null +++ b/test/models/merchant_test.rb @@ -0,0 +1,30 @@ +require 'test_helper' + +class MerchantTest < ActiveSupport::TestCase + test "Create a valid merchant" do + assert merchants(:snoopy).valid? + end + + test "Cannot create a merchant with missing information" do + merchant = Merchant.new + assert_not merchant.valid? + assert_includes merchant.errors, :username + assert_includes merchant.errors, :email + end + + test "Should not create a merchant with the same username or email as another" do + Merchant.create!(username: "foo", email: "foo@hotmail.com") + m2 = Merchant.new(username: "foo", email: "other@hotmail.com") + m3 = Merchant.new(username: "other", email: "foo@hotmail.com") + assert_not m2.valid? + assert_not m3.valid? + end + + test "can create a merchant from github" do + auth_hash = OmniAuth.config.mock_auth[:github] + user_gh_built = Merchant.build_from_github(auth_hash) + + assert user_gh_built.valid? + end + +end diff --git a/test/models/order_item_test.rb b/test/models/order_item_test.rb new file mode 100644 index 0000000000..b950fca6ad --- /dev/null +++ b/test/models/order_item_test.rb @@ -0,0 +1,8 @@ +require 'test_helper' + +class OrderItemTest < ActiveSupport::TestCase + + test "Create a valid orderitem" do + assert order_items(:one).valid? + end +end diff --git a/test/models/order_test.rb b/test/models/order_test.rb new file mode 100644 index 0000000000..db1330d348 --- /dev/null +++ b/test/models/order_test.rb @@ -0,0 +1,131 @@ +require 'test_helper' + +class OrderTest < ActiveSupport::TestCase + + # test "attempting to create order with expired credit card give error message" do + # order = orders(:willow) + # assert_not order.valid? + # assert_includes order.errors, :cc_exp_year + # end + # + # test "can create an order with valid data" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order.valid? + # end + # + # test "cannot create an order with a non-numerical zip code" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "hwgow", cc_number: "abcd456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: "abd", billing_zip: "hha02") + # assert_not order.valid? + # assert_includes order.errors, :mailing_zip + # assert_includes order.errors, :cc_number + # assert_includes order.errors, :cc_sec_code + # assert_includes order.errors, :billing_zip + # end + # + # test "cannot create two orders with the same name and street address" do + # order1 = orders(:nicole) + # order2 = Order.new(name: "Nicole", email: "xkcd@gmail.com", street_address: "12 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert_not order2.valid? + # assert_includes order2.errors, :name + # end + # + # test "can create two orders with different street addresses" do + # order1 = orders(:nicole) + # order2 = Order.new(name: "Nicole", email: "xkcd@gmail.com", street_address: "68 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order2.valid? + # end + # + # test "cannot create an order with missing information" do + # order = Order.new + # assert_not order.valid? + # assert_includes order.errors, :name + # assert_includes order.errors, :email + # assert_includes order.errors, :street_address + # assert_includes order.errors, :city + # assert_includes order.errors, :state + # assert_includes order.errors, :mailing_zip + # assert_includes order.errors, :cc_number + # assert_includes order.errors, :cc_exp_month + # assert_includes order.errors, :cc_exp_year + # assert_includes order.errors, :cc_sec_code + # assert_includes order.errors, :billing_zip + # end + # + # + # test "zip code must be 5 digits long" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order.valid? + # end + # + # test "zip code cannot be longer or shorter than 5 digits" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "9810", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert_not order.valid? + # assert_includes order.errors, :mailing_zip + # end + # + # test "email address must be in a valid format" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order.valid? + # end + # + # test "email address cannot be in an invalid format" do + # order = Order.new(name: "Igor Smith", email: "xkcdgmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert_not order.valid? + # assert_includes order.errors, :email + # end + # + # # In a world where we could only accept valid credit cards, we would use the Luhn + # # algorithm here. In this world, AmEx is 15 and the rest are 16 digits long, so that's + # # what we're testing + # test "credit card number must be 15 to 16 digits long" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order.valid? + # end + # + # test "credit card number cannot be invalid" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert_not order.valid? + # assert_includes order.errors, :cc_number + # end + # + # # The security code for AmEx is 4 digits, whereas other cards are only 3 + # test "credit card security number must be 3 to 4 digits long" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 328, billing_zip: "98102") + # assert order.valid? + # end + # + # test "credit card security number cannot be anything other than 3 or 4 digits long" do + # order = Order.new(name: "Igor Smith", email: "xkcd@gmail.com", street_address: "38 Main St", + # city: "Seattle", state: "WA", mailing_zip: "98102", cc_number: "1223456743254456", cc_exp_month: 5, + # cc_exp_year: 2017, cc_sec_code: 32348, billing_zip: "98102") + # assert_not order.valid? + # assert_includes order.errors, :cc_sec_code + # end + # + # # AmEx is 15 digits with 4-digit security, whereas other cards are 16 digits with 3-digit security + # # test "credit card number.length plus security number.length must total 19" do + # # Need to figure out how to do this + # # end +end diff --git a/test/models/ordered_product_test.rb b/test/models/ordered_product_test.rb new file mode 100644 index 0000000000..e1e6779615 --- /dev/null +++ b/test/models/ordered_product_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class OrderedProductTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/product_test.rb b/test/models/product_test.rb new file mode 100644 index 0000000000..0446d421be --- /dev/null +++ b/test/models/product_test.rb @@ -0,0 +1,25 @@ +require 'test_helper' + +class ProductTest < ActiveSupport::TestCase + test "Create a valid product" do + assert products(:snake).valid? + end + + test "Cannot create an product with any missing information" do + product = Product.new + assert_not product.valid? + assert_includes product.errors, :name + assert_includes product.errors, :inventory + assert_includes product.errors, :price + end + + test "Should not create a product with invalid price" do + p1 = Product.new(name: "prod1", inventory: 1) + p2 = Product.new(name: "prod2", inventory: 1, price: "$1.00") + p3 = Product.new(name: "prod3", inventory: 1, price: -1) + assert_not p1.valid? + assert_not p2.valid? + assert_not p3.valid? + end + +end diff --git a/test/models/review_test.rb b/test/models/review_test.rb new file mode 100644 index 0000000000..f3819a2c40 --- /dev/null +++ b/test/models/review_test.rb @@ -0,0 +1,34 @@ +require 'test_helper' + +class ReviewTest < ActiveSupport::TestCase + test "can create a valid review" do + assert reviews(:best_product).valid? + end + + test "cannot create a review with no rating" do + assert_not reviews(:no_rating).valid? + assert_includes reviews(:no_rating).errors, :rating + end + + test "cannot create a review with a rating less than 1" do + assert_not reviews(:too_low).valid? + assert_includes reviews(:too_low).errors, :rating + end + + test "cannot create a review with a rating greater than 5" do + assert_not reviews(:too_high).valid? + assert_includes reviews(:too_high).errors, :rating + end + + test "cannot create a review with a non-integer rating" do + r = Review.new + r.rating = 2.5 + r.product_id = 3 + assert_not r.valid? + assert_includes r.errors, :rating + + # assert_not reviews(:non_integer).valid? + # assert_includes reviews(:non_integer).errors, :rating + end + +end diff --git a/test/test_helper.rb b/test/test_helper.rb index 92e39b2d78..2e8762bf23 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -1,3 +1,6 @@ +require 'simplecov' +SimpleCov.start 'rails' + ENV['RAILS_ENV'] ||= 'test' require File.expand_path('../../config/environment', __FILE__) require 'rails/test_help' @@ -7,4 +10,16 @@ class ActiveSupport::TestCase fixtures :all # Add more helper methods to be used by all tests here... + def setup + # Once you have enabled test mode, all requests + # to OmniAuth will be short circuited to use the mock authentication hash. + # A request to /auth/provider will redirect immediately to /auth/provider/callback. + OmniAuth.config.test_mode = true + + # The mock_auth configuration allows you to set per-provider (or default) authentication + # hashes to return during testing. + OmniAuth.config.mock_auth[:github] = OmniAuth::AuthHash.new({ + provider: 'github', uid: '123545', info: { email: "a@b.com", name: "Ada" } + }) + end end