diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
new file mode 100644
index 0000000000..0ede9d68a5
--- /dev/null
+++ b/app/views/users/index.html.erb
@@ -0,0 +1,14 @@
+
\ No newline at end of file
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
new file mode 100644
index 0000000000..77791fd2ef
--- /dev/null
+++ b/app/views/users/show.html.erb
@@ -0,0 +1,33 @@
+
+
+ <% if @user.photo_url.nil? %>
+ <%= image_tag 'timmytommy.png', alt: "user image", class: 'user_image' %>
+ <% else %>
+ <%= image_tag @user.photo_url, alt: "user image", class: 'user_image' %>
+ <% end %>
+
+
<%= @user.name %>
+ Joined: <%= @user.created_at.strftime("%m/%d/%Y") %>
+ Username: <%= @user.username %>
+
+
+
+
+
+ <% @user.products.each do |product| %>
+ <% unless product.photo_url == "NA" || product.active == false %>
+
+ <%= link_to(image_tag(product.photo_url, alt: "product", class: "products__index_product-img"), product_path(product)) %>
+
+
+
<%= link_to "#{product.name}", product_path(product) %>
+
<%= product.price %> Bells
+
<%= Review.display_rating(product.average_rating) %> (<%= product.average_rating %>) | <%= product.num_of_ratings %> ratings
+
+
Inventory: <%= product.inventory %>
+
+
+ <% end %>
+ <% end %>
+
+
\ No newline at end of file
diff --git a/babel.config.js b/babel.config.js
new file mode 100644
index 0000000000..12f98da5af
--- /dev/null
+++ b/babel.config.js
@@ -0,0 +1,72 @@
+module.exports = function(api) {
+ var validEnv = ['development', 'test', 'production']
+ var currentEnv = api.env()
+ var isDevelopmentEnv = api.env('development')
+ var isProductionEnv = api.env('production')
+ var isTestEnv = api.env('test')
+
+ if (!validEnv.includes(currentEnv)) {
+ throw new Error(
+ 'Please specify a valid `NODE_ENV` or ' +
+ '`BABEL_ENV` environment variables. Valid values are "development", ' +
+ '"test", and "production". Instead, received: ' +
+ JSON.stringify(currentEnv) +
+ '.'
+ )
+ }
+
+ return {
+ presets: [
+ isTestEnv && [
+ '@babel/preset-env',
+ {
+ targets: {
+ node: 'current'
+ }
+ }
+ ],
+ (isProductionEnv || isDevelopmentEnv) && [
+ '@babel/preset-env',
+ {
+ forceAllTransforms: true,
+ useBuiltIns: 'entry',
+ corejs: 3,
+ modules: false,
+ exclude: ['transform-typeof-symbol']
+ }
+ ]
+ ].filter(Boolean),
+ plugins: [
+ 'babel-plugin-macros',
+ '@babel/plugin-syntax-dynamic-import',
+ isTestEnv && 'babel-plugin-dynamic-import-node',
+ '@babel/plugin-transform-destructuring',
+ [
+ '@babel/plugin-proposal-class-properties',
+ {
+ loose: true
+ }
+ ],
+ [
+ '@babel/plugin-proposal-object-rest-spread',
+ {
+ useBuiltIns: true
+ }
+ ],
+ [
+ '@babel/plugin-transform-runtime',
+ {
+ helpers: false,
+ regenerator: true,
+ corejs: false
+ }
+ ],
+ [
+ '@babel/plugin-transform-regenerator',
+ {
+ async: false
+ }
+ ]
+ ].filter(Boolean)
+ }
+}
diff --git a/bin/bundle b/bin/bundle
new file mode 100755
index 0000000000..a71368e323
--- /dev/null
+++ b/bin/bundle
@@ -0,0 +1,114 @@
+#!/usr/bin/env ruby
+# frozen_string_literal: true
+
+#
+# This file was generated by Bundler.
+#
+# The application 'bundle' is installed as part of a gem, and
+# this file is here to facilitate running it.
+#
+
+require "rubygems"
+
+m = Module.new do
+ module_function
+
+ def invoked_as_script?
+ File.expand_path($0) == File.expand_path(__FILE__)
+ end
+
+ def env_var_version
+ ENV["BUNDLER_VERSION"]
+ end
+
+ def cli_arg_version
+ return unless invoked_as_script? # don't want to hijack other binstubs
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
+ bundler_version = nil
+ update_index = nil
+ ARGV.each_with_index do |a, i|
+ if update_index && update_index.succ == i && a =~ Gem::Version::ANCHORED_VERSION_PATTERN
+ bundler_version = a
+ end
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/
+ bundler_version = $1
+ update_index = i
+ end
+ bundler_version
+ end
+
+ def gemfile
+ gemfile = ENV["BUNDLE_GEMFILE"]
+ return gemfile if gemfile && !gemfile.empty?
+
+ File.expand_path("../../Gemfile", __FILE__)
+ end
+
+ def lockfile
+ lockfile =
+ case File.basename(gemfile)
+ when "gems.rb" then gemfile.sub(/\.rb$/, gemfile)
+ else "#{gemfile}.lock"
+ end
+ File.expand_path(lockfile)
+ end
+
+ def lockfile_version
+ return unless File.file?(lockfile)
+ lockfile_contents = File.read(lockfile)
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/
+ Regexp.last_match(1)
+ end
+
+ def bundler_version
+ @bundler_version ||=
+ env_var_version || cli_arg_version ||
+ lockfile_version
+ end
+
+ def bundler_requirement
+ return "#{Gem::Requirement.default}.a" unless bundler_version
+
+ bundler_gem_version = Gem::Version.new(bundler_version)
+
+ requirement = bundler_gem_version.approximate_recommendation
+
+ return requirement unless Gem::Version.new(Gem::VERSION) < Gem::Version.new("2.7.0")
+
+ requirement += ".a" if bundler_gem_version.prerelease?
+
+ requirement
+ end
+
+ def load_bundler!
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
+
+ activate_bundler
+ end
+
+ def activate_bundler
+ gem_error = activation_error_handling do
+ gem "bundler", bundler_requirement
+ end
+ return if gem_error.nil?
+ require_error = activation_error_handling do
+ require "bundler/version"
+ end
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
+ exit 42
+ end
+
+ def activation_error_handling
+ yield
+ nil
+ rescue StandardError, LoadError => e
+ e
+ end
+end
+
+m.load_bundler!
+
+if m.invoked_as_script?
+ load Gem.bin_path("bundler", "bundle")
+end
diff --git a/bin/rails b/bin/rails
new file mode 100755
index 0000000000..5badb2fde0
--- /dev/null
+++ b/bin/rails
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+APP_PATH = File.expand_path('../config/application', __dir__)
+require_relative '../config/boot'
+require 'rails/commands'
diff --git a/bin/rake b/bin/rake
new file mode 100755
index 0000000000..d87d5f5781
--- /dev/null
+++ b/bin/rake
@@ -0,0 +1,9 @@
+#!/usr/bin/env ruby
+begin
+ load File.expand_path('../spring', __FILE__)
+rescue LoadError => e
+ raise unless e.message.include?('spring')
+end
+require_relative '../config/boot'
+require 'rake'
+Rake.application.run
diff --git a/bin/setup b/bin/setup
new file mode 100755
index 0000000000..5853b5ea87
--- /dev/null
+++ b/bin/setup
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+require 'fileutils'
+
+# path to your application root.
+APP_ROOT = File.expand_path('..', __dir__)
+
+def system!(*args)
+ system(*args) || abort("\n== Command #{args} failed ==")
+end
+
+FileUtils.chdir APP_ROOT do
+ # This script is a way to setup or update your development environment automatically.
+ # This script is idempotent, so that you can run it at anytime and get an expectable outcome.
+ # Add necessary setup steps to this file.
+
+ puts '== Installing dependencies =='
+ system! 'gem install bundler --conservative'
+ system('bundle check') || system!('bundle install')
+
+ # Install JavaScript dependencies
+ # system('bin/yarn')
+
+ # puts "\n== Copying sample files =="
+ # unless File.exist?('config/database.yml')
+ # FileUtils.cp 'config/database.yml.sample', 'config/database.yml'
+ # end
+
+ puts "\n== Preparing database =="
+ system! 'bin/rails db:prepare'
+
+ puts "\n== Removing old logs and tempfiles =="
+ system! 'bin/rails log:clear tmp:clear'
+
+ puts "\n== Restarting application server =="
+ system! 'bin/rails restart'
+end
diff --git a/bin/spring b/bin/spring
new file mode 100755
index 0000000000..d89ee495fa
--- /dev/null
+++ b/bin/spring
@@ -0,0 +1,17 @@
+#!/usr/bin/env ruby
+
+# This file loads Spring without using Bundler, in order to be fast.
+# It gets overwritten when you run the `spring binstub` command.
+
+unless defined?(Spring)
+ require 'rubygems'
+ require 'bundler'
+
+ lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read)
+ spring = lockfile.specs.detect { |spec| spec.name == 'spring' }
+ if 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/bin/webpack b/bin/webpack
new file mode 100755
index 0000000000..1031168d01
--- /dev/null
+++ b/bin/webpack
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
+ENV["NODE_ENV"] ||= "development"
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "bundler/setup"
+
+require "webpacker"
+require "webpacker/webpack_runner"
+
+APP_ROOT = File.expand_path("..", __dir__)
+Dir.chdir(APP_ROOT) do
+ Webpacker::WebpackRunner.run(ARGV)
+end
diff --git a/bin/webpack-dev-server b/bin/webpack-dev-server
new file mode 100755
index 0000000000..dd9662737a
--- /dev/null
+++ b/bin/webpack-dev-server
@@ -0,0 +1,18 @@
+#!/usr/bin/env ruby
+
+ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
+ENV["NODE_ENV"] ||= "development"
+
+require "pathname"
+ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
+ Pathname.new(__FILE__).realpath)
+
+require "bundler/setup"
+
+require "webpacker"
+require "webpacker/dev_server_runner"
+
+APP_ROOT = File.expand_path("..", __dir__)
+Dir.chdir(APP_ROOT) do
+ Webpacker::DevServerRunner.run(ARGV)
+end
diff --git a/bin/yarn b/bin/yarn
new file mode 100755
index 0000000000..460dd565b4
--- /dev/null
+++ b/bin/yarn
@@ -0,0 +1,11 @@
+#!/usr/bin/env ruby
+APP_ROOT = File.expand_path('..', __dir__)
+Dir.chdir(APP_ROOT) do
+ begin
+ exec "yarnpkg", *ARGV
+ rescue Errno::ENOENT
+ $stderr.puts "Yarn executable was not detected in the system."
+ $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
+ exit 1
+ end
+end
diff --git a/config.ru b/config.ru
new file mode 100644
index 0000000000..f7ba0b527b
--- /dev/null
+++ b/config.ru
@@ -0,0 +1,5 @@
+# This file is used by Rack-based servers to start the application.
+
+require_relative 'config/environment'
+
+run Rails.application
diff --git a/config/application.rb b/config/application.rb
new file mode 100644
index 0000000000..39167e2e9a
--- /dev/null
+++ b/config/application.rb
@@ -0,0 +1,23 @@
+require_relative 'boot'
+
+require 'rails/all'
+
+# Require the gems listed in Gemfile, including any gems
+# you've limited to :test, :development, or :production.
+Bundler.require(*Rails.groups)
+
+module Betsy
+ class Application < Rails::Application
+ config.generators do |g|
+ # Force new test files to be generated in the minitest-spec style
+ g.test_framework :minitest, spec: true
+ end
+ # Initialize configuration defaults for originally generated Rails version.
+ config.load_defaults 6.0
+
+ # Settings in config/environments/* take precedence over those specified here.
+ # Application configuration can go into files in config/initializers
+ # -- all .rb files in that directory are automatically loaded after loading
+ # the framework and any gems in your application.
+ end
+end
diff --git a/config/boot.rb b/config/boot.rb
new file mode 100644
index 0000000000..b9e460cef3
--- /dev/null
+++ b/config/boot.rb
@@ -0,0 +1,4 @@
+ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
+
+require 'bundler/setup' # Set up gems listed in the Gemfile.
+require 'bootsnap/setup' # Speed up boot time by caching expensive operations.
diff --git a/config/cable.yml b/config/cable.yml
new file mode 100644
index 0000000000..f2a452f546
--- /dev/null
+++ b/config/cable.yml
@@ -0,0 +1,10 @@
+development:
+ adapter: async
+
+test:
+ adapter: test
+
+production:
+ adapter: redis
+ url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
+ channel_prefix: betsy_production
diff --git a/config/credentials.yml.enc b/config/credentials.yml.enc
new file mode 100644
index 0000000000..afcc1b7aa1
--- /dev/null
+++ b/config/credentials.yml.enc
@@ -0,0 +1 @@
+SVDYMKskORWYsNz0LOTdtzV4tX8C/l10ea9M7+qxRLgEG0nmw4obmn/FB1aI5RMXjkrd95dZTsoVKP+4IlBHXPzvLhuPSHasdx/FIlOBrueYf97Pr7uo+eQJagzrSeyFBG46hDxSiFWR+tyyRiQeig6WH6sSm+Jr5E93qWwb/XYyEXqTvHU5lhy6WW3r1pxEWV5zal4diYdNXGDlRMeBV7KXTch7wQP46EcREGMnD7E7Wb79JRZof5XygmKt3ds/radXQsrXwAmWgZ5ZXya8Rg03Dp/w4a4B6JBRSrS0IlEBfjZdMqfLvz7DEtxMAZ0ophcZTwSS0Oq0/LH7zZ2hb8Wm52LFznVMTLrXbgA7YPdx2eccXSWt2nRZEsoO9d32UhYs5pX9E1jQDVHiR0M8htZXhS2lnf9g1yeK--eyfkDIR9400mf7Mo--gTBbeyX3hiJPyqbH5oXjXA==
\ No newline at end of file
diff --git a/config/database.yml b/config/database.yml
new file mode 100644
index 0000000000..8b9bc23f0c
--- /dev/null
+++ b/config/database.yml
@@ -0,0 +1,85 @@
+# PostgreSQL. Versions 9.3 and up are supported.
+#
+# Install the pg driver:
+# gem install pg
+# On macOS with Homebrew:
+# gem install pg -- --with-pg-config=/usr/local/bin/pg_config
+# On macOS 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'
+#
+default: &default
+ adapter: postgresql
+ encoding: unicode
+ # For details on connection pooling, see Rails configuration guide
+ # https://guides.rubyonrails.org/configuring.html#database-pooling
+ pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
+
+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
+
+# 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
+
+# As with config/credentials.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 https://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'] %>
diff --git a/config/environment.rb b/config/environment.rb
new file mode 100644
index 0000000000..426333bb46
--- /dev/null
+++ b/config/environment.rb
@@ -0,0 +1,5 @@
+# Load the Rails application.
+require_relative 'application'
+
+# Initialize the Rails application.
+Rails.application.initialize!
diff --git a/config/environments/development.rb b/config/environments/development.rb
new file mode 100644
index 0000000000..66df51f6fc
--- /dev/null
+++ b/config/environments/development.rb
@@ -0,0 +1,62 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # In the development environment your application's code is reloaded on
+ # every request. This slows down response time but is perfect for development
+ # since you don't have to restart the web server when you make code changes.
+ config.cache_classes = false
+
+ # Do not eager load code on boot.
+ config.eager_load = false
+
+ # Show full error reports.
+ config.consider_all_requests_local = true
+
+ # Enable/disable caching. By default caching is disabled.
+ # Run rails dev:cache to toggle caching.
+ if Rails.root.join('tmp', 'caching-dev.txt').exist?
+ config.action_controller.perform_caching = true
+ config.action_controller.enable_fragment_cache_logging = true
+
+ config.cache_store = :memory_store
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{2.days.to_i}"
+ }
+ else
+ config.action_controller.perform_caching = false
+
+ config.cache_store = :null_store
+ end
+
+ # Store uploaded files on the local file system (see config/storage.yml for options).
+ config.active_storage.service = :local
+
+ # Don't care if the mailer can't send.
+ config.action_mailer.raise_delivery_errors = false
+
+ config.action_mailer.perform_caching = false
+
+ # Print deprecation notices to the Rails logger.
+ config.active_support.deprecation = :log
+
+ # Raise an error on page load if there are pending migrations.
+ config.active_record.migration_error = :page_load
+
+ # Highlight code that triggered database queries in logs.
+ config.active_record.verbose_query_logs = true
+
+ # Debug mode disables concatenation and preprocessing of assets.
+ # This option may cause significant delays in view rendering with a large
+ # number of complex assets.
+ config.assets.debug = true
+
+ # Suppress logger output for asset requests.
+ config.assets.quiet = true
+
+ # Raises error for missing translations.
+ # config.action_view.raise_on_missing_translations = true
+
+ # Use an evented file watcher to asynchronously detect changes in source code,
+ # routes, locales, etc. This feature depends on the listen gem.
+ config.file_watcher = ActiveSupport::EventedFileUpdateChecker
+end
diff --git a/config/environments/production.rb b/config/environments/production.rb
new file mode 100644
index 0000000000..a511873ff5
--- /dev/null
+++ b/config/environments/production.rb
@@ -0,0 +1,113 @@
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ # Code is not reloaded between requests.
+ config.cache_classes = true
+
+ # 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.
+ # Rake tasks automatically ignore this option for performance.
+ config.eager_load = true
+
+ # Full error reports are disabled and caching is turned on.
+ config.consider_all_requests_local = false
+ config.action_controller.perform_caching = true
+
+ # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
+ # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
+ # config.require_master_key = true
+
+ # Disable serving static files from the `/public` folder by default since
+ # Apache or NGINX already handles this.
+ config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
+
+ # Compress CSS using a preprocessor.
+ # config.assets.css_compressor = :sass
+
+ # Do not fallback to assets pipeline if a precompiled asset is missed.
+ config.assets.compile = false
+
+ # Enable serving of images, stylesheets, and JavaScripts from an asset server.
+ # config.action_controller.asset_host = 'http://assets.example.com'
+
+ # Specifies the header that your server uses for sending files.
+ # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
+ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
+
+ # Store uploaded files on the local file system (see config/storage.yml for options).
+ config.active_storage.service = :local
+
+ # Mount Action Cable outside main process or domain.
+ # config.action_cable.mount_path = nil
+ # config.action_cable.url = 'wss://example.com/cable'
+ # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
+
+ # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
+ # config.force_ssl = true
+
+ # Use the lowest log level to ensure availability of diagnostic information
+ # when problems arise.
+ config.log_level = :debug
+
+ # Prepend all log lines with the following tags.
+ config.log_tags = [ :request_id ]
+
+ # Use a different cache store in production.
+ # config.cache_store = :mem_cache_store
+
+ # Use a real queuing backend for Active Job (and separate queues per environment).
+ # config.active_job.queue_adapter = :resque
+ # config.active_job.queue_name_prefix = "betsy_production"
+
+ config.action_mailer.perform_caching = false
+
+ # Ignore bad email addresses and do not raise email delivery errors.
+ # Set this to true and configure the email server for immediate delivery to raise delivery errors.
+ # config.action_mailer.raise_delivery_errors = false
+
+ # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
+ # the I18n.default_locale when a translation cannot be found).
+ config.i18n.fallbacks = true
+
+ # Send deprecation notices to registered listeners.
+ config.active_support.deprecation = :notify
+
+ # Use default logging formatter so that PID and timestamp are not suppressed.
+ config.log_formatter = ::Logger::Formatter.new
+
+ # Use a different logger for distributed setups.
+ # require 'syslog/logger'
+ # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
+
+ if ENV["RAILS_LOG_TO_STDOUT"].present?
+ logger = ActiveSupport::Logger.new(STDOUT)
+ logger.formatter = config.log_formatter
+ config.logger = ActiveSupport::TaggedLogging.new(logger)
+ end
+
+ # Do not dump schema after migrations.
+ config.active_record.dump_schema_after_migration = false
+
+ # Inserts middleware to perform automatic connection switching.
+ # The `database_selector` hash is used to pass options to the DatabaseSelector
+ # middleware. The `delay` is used to determine how long to wait after a write
+ # to send a subsequent read to the primary.
+ #
+ # The `database_resolver` class is used by the middleware to determine which
+ # database is appropriate to use based on the time delay.
+ #
+ # The `database_resolver_context` class is used by the middleware to set
+ # timestamps for the last write to the primary. The resolver uses the context
+ # class timestamps to determine how long to wait before reading from the
+ # replica.
+ #
+ # By default Rails will store a last write timestamp in the session. The
+ # DatabaseSelector middleware is designed as such you can define your own
+ # strategy for connection switching and pass that into the middleware through
+ # these configuration options.
+ # config.active_record.database_selector = { delay: 2.seconds }
+ # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
+ # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
+
+end
diff --git a/config/environments/test.rb b/config/environments/test.rb
new file mode 100644
index 0000000000..0cb24249b5
--- /dev/null
+++ b/config/environments/test.rb
@@ -0,0 +1,49 @@
+# The test environment is used exclusively to run your application's
+# test suite. You never need to work with it otherwise. Remember that
+# your test database is "scratch space" for the test suite and is wiped
+# and recreated between test runs. Don't rely on the data there!
+
+Rails.application.configure do
+ # Settings specified here will take precedence over those in config/application.rb.
+
+ config.cache_classes = false
+ config.action_view.cache_template_loading = true
+
+ # Do not eager load code on boot. This avoids loading your whole application
+ # just for the purpose of running a single test. If you are using a tool that
+ # preloads Rails for running tests, you may have to set it to true.
+ config.eager_load = false
+
+ # Configure public file server for tests with Cache-Control for performance.
+ config.public_file_server.enabled = true
+ config.public_file_server.headers = {
+ 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
+ }
+
+ # Show full error reports and disable caching.
+ config.consider_all_requests_local = true
+ config.action_controller.perform_caching = false
+ config.cache_store = :null_store
+
+ # Raise exceptions instead of rendering exception templates.
+ config.action_dispatch.show_exceptions = false
+
+ # Disable request forgery protection in test environment.
+ config.action_controller.allow_forgery_protection = false
+
+ # Store uploaded files on the local file system in a temporary directory.
+ config.active_storage.service = :test
+
+ config.action_mailer.perform_caching = false
+
+ # Tell Action Mailer not to deliver emails to the real world.
+ # The :test delivery method accumulates sent emails in the
+ # ActionMailer::Base.deliveries array.
+ config.action_mailer.delivery_method = :test
+
+ # Print deprecation notices to the stderr.
+ config.active_support.deprecation = :stderr
+
+ # Raises error for missing translations.
+ # config.action_view.raise_on_missing_translations = true
+end
diff --git a/config/initializers/action_view.rb b/config/initializers/action_view.rb
new file mode 100644
index 0000000000..142d382f87
--- /dev/null
+++ b/config/initializers/action_view.rb
@@ -0,0 +1 @@
+Rails.application.config.action_view.form_with_generates_remote_forms = false
diff --git a/config/initializers/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb
new file mode 100644
index 0000000000..89d2efab2b
--- /dev/null
+++ b/config/initializers/application_controller_renderer.rb
@@ -0,0 +1,8 @@
+# Be sure to restart your server when you modify this file.
+
+# ActiveSupport::Reloader.to_prepare do
+# ApplicationController.renderer.defaults.merge!(
+# http_host: 'example.org',
+# https: false
+# )
+# end
diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb
new file mode 100644
index 0000000000..4b828e80cb
--- /dev/null
+++ b/config/initializers/assets.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# Version of your assets, change this if you want to expire all your assets.
+Rails.application.config.assets.version = '1.0'
+
+# Add additional assets to the asset load path.
+# Rails.application.config.assets.paths << Emoji.images_path
+# Add Yarn node_modules folder to the asset load path.
+Rails.application.config.assets.paths << Rails.root.join('node_modules')
+
+# Precompile additional assets.
+# application.js, application.css, and all non-JS/CSS in the app/assets
+# folder are already added.
+# Rails.application.config.assets.precompile += %w( admin.js admin.css )
diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb
new file mode 100644
index 0000000000..59385cdf37
--- /dev/null
+++ b/config/initializers/backtrace_silencers.rb
@@ -0,0 +1,7 @@
+# Be sure to restart your server when you modify this file.
+
+# You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+# Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+# You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+# Rails.backtrace_cleaner.remove_silencers!
diff --git a/config/initializers/content_security_policy.rb b/config/initializers/content_security_policy.rb
new file mode 100644
index 0000000000..35d0f26fcd
--- /dev/null
+++ b/config/initializers/content_security_policy.rb
@@ -0,0 +1,30 @@
+# Be sure to restart your server when you modify this file.
+
+# Define an application-wide content security policy
+# For further information see the following documentation
+# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+
+# Rails.application.config.content_security_policy do |policy|
+# policy.default_src :self, :https
+# policy.font_src :self, :https, :data
+# policy.img_src :self, :https, :data
+# policy.object_src :none
+# policy.script_src :self, :https
+# policy.style_src :self, :https
+# # If you are using webpack-dev-server then specify webpack-dev-server host
+# policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
+
+# # Specify URI for violation reports
+# # policy.report_uri "/csp-violation-report-endpoint"
+# end
+
+# If you are using UJS then enable automatic nonce generation
+# Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+
+# Set the nonce only to specific directives
+# Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
+
+# Report CSP violations to a specified URI
+# For further information see the following documentation:
+# https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
+# Rails.application.config.content_security_policy_report_only = true
diff --git a/config/initializers/cookies_serializer.rb b/config/initializers/cookies_serializer.rb
new file mode 100644
index 0000000000..5a6a32d371
--- /dev/null
+++ b/config/initializers/cookies_serializer.rb
@@ -0,0 +1,5 @@
+# Be sure to restart your server when you modify this file.
+
+# Specify a serializer for the signed and encrypted cookie jars.
+# Valid options are :json, :marshal, and :hybrid.
+Rails.application.config.action_dispatch.cookies_serializer = :json
diff --git a/config/initializers/filter_parameter_logging.rb b/config/initializers/filter_parameter_logging.rb
new file mode 100644
index 0000000000..4a994e1e7b
--- /dev/null
+++ b/config/initializers/filter_parameter_logging.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Configure sensitive parameters which will be filtered from the log file.
+Rails.application.config.filter_parameters += [:password]
diff --git a/config/initializers/inflections.rb b/config/initializers/inflections.rb
new file mode 100644
index 0000000000..ac033bf9dc
--- /dev/null
+++ b/config/initializers/inflections.rb
@@ -0,0 +1,16 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new inflection rules using the following format. Inflections
+# are locale specific, and you may define rules for as many different
+# locales as you wish. All of these examples are active by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.plural /^(ox)$/i, '\1en'
+# inflect.singular /^(ox)en/i, '\1'
+# inflect.irregular 'person', 'people'
+# inflect.uncountable %w( fish sheep )
+# end
+
+# These inflection rules are supported but not enabled by default:
+# ActiveSupport::Inflector.inflections(:en) do |inflect|
+# inflect.acronym 'RESTful'
+# end
diff --git a/config/initializers/mime_types.rb b/config/initializers/mime_types.rb
new file mode 100644
index 0000000000..dc1899682b
--- /dev/null
+++ b/config/initializers/mime_types.rb
@@ -0,0 +1,4 @@
+# Be sure to restart your server when you modify this file.
+
+# Add new mime types for use in respond_to blocks:
+# Mime::Type.register "text/richtext", :rtf
diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb
new file mode 100644
index 0000000000..e32a45234e
--- /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
\ No newline at end of file
diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb
new file mode 100644
index 0000000000..bbfc3961bf
--- /dev/null
+++ b/config/initializers/wrap_parameters.rb
@@ -0,0 +1,14 @@
+# Be sure to restart your server when you modify this file.
+
+# This file contains settings for ActionController::ParamsWrapper which
+# is enabled by default.
+
+# Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+ActiveSupport.on_load(:action_controller) do
+ wrap_parameters format: [:json]
+end
+
+# To enable root element in JSON for ActiveRecord objects.
+# ActiveSupport.on_load(:active_record) do
+# self.include_root_in_json = true
+# end
diff --git a/config/locales/en.yml b/config/locales/en.yml
new file mode 100644
index 0000000000..cf9b342d0a
--- /dev/null
+++ b/config/locales/en.yml
@@ -0,0 +1,33 @@
+# Files in the config/locales directory are used for internationalization
+# and are automatically loaded by Rails. If you want to use locales other
+# than English, add the necessary files in this directory.
+#
+# To use the locales, use `I18n.t`:
+#
+# I18n.t 'hello'
+#
+# In views, this is aliased to just `t`:
+#
+# <%= t('hello') %>
+#
+# To use a different locale, set it with `I18n.locale`:
+#
+# I18n.locale = :es
+#
+# This would use the information in config/locales/es.yml.
+#
+# The following keys must be escaped otherwise they will not be retrieved by
+# the default I18n backend:
+#
+# true, false, on, off, yes, no
+#
+# Instead, surround them with single quotes.
+#
+# en:
+# 'true': 'foo'
+#
+# To learn more, please read the Rails Internationalization guide
+# available at https://guides.rubyonrails.org/i18n.html.
+
+en:
+ hello: "Hello world"
diff --git a/config/puma.rb b/config/puma.rb
new file mode 100644
index 0000000000..5ed4437744
--- /dev/null
+++ b/config/puma.rb
@@ -0,0 +1,38 @@
+# Puma can serve each request in a thread from an internal thread pool.
+# The `threads` method setting takes two numbers: a minimum and maximum.
+# Any libraries that use thread pools should be configured to match
+# the maximum value specified for Puma. Default is set to 5 threads for minimum
+# and maximum; this matches the default thread size of Active Record.
+#
+max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
+min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
+threads min_threads_count, max_threads_count
+
+# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
+#
+port ENV.fetch("PORT") { 3000 }
+
+# Specifies the `environment` that Puma will run in.
+#
+environment ENV.fetch("RAILS_ENV") { "development" }
+
+# Specifies the `pidfile` that Puma will use.
+pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }
+
+# Specifies the number of `workers` to boot in clustered mode.
+# Workers are forked web server processes. If using threads and workers together
+# the concurrency of the application would be max `threads` * `workers`.
+# Workers do not work on JRuby or Windows (both of which do not support
+# processes).
+#
+# workers ENV.fetch("WEB_CONCURRENCY") { 2 }
+
+# Use the `preload_app!` method when specifying a `workers` number.
+# This directive tells Puma to first boot the application and load code
+# before forking the application. This takes advantage of Copy On Write
+# process behavior so workers use less memory.
+#
+# preload_app!
+
+# Allow puma to be restarted by `rails restart` command.
+plugin :tmp_restart
diff --git a/config/routes.rb b/config/routes.rb
new file mode 100644
index 0000000000..de2739952a
--- /dev/null
+++ b/config/routes.rb
@@ -0,0 +1,33 @@
+Rails.application.routes.draw do
+ get 'pages/home'
+ # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
+ resources :users, except: [:update, :new, :edit, :update]
+
+ get "/auth/github", as: "github_login"
+ get "/auth/github/callback", to: "users#create", as: "auth_callback"
+ delete "/logout", to: "users#destroy", as: "logout"
+
+ root to: "pages#home"
+ get "/search", to: "products#search", as: "search"
+ get '/frontpage', to: 'pages#frontpage', as: "frontpage"
+
+ patch '/products/:id/toggle_status', to: 'products#toggle_status', as: 'toggle_status'
+ get '/dashboard', to: "users#dashboard", as: "dashboard"
+ get '/ordered', to: "orders#ordered", as: "ordered"
+ get '/cart', to: "orderitems#index", as: 'cart'
+ patch 'order/mark_shipped', to: 'orders#mark_shipped', as: "mark_shipped"
+ patch '/orderitems/add', to:"orderitems#increase_quantity", as: "add"
+ patch '/orderitems/subtract', to:"orderitems#decrease_quantity", as: "subtract"
+
+ resources :products do
+ resources :reviews, only: [:new, :create]
+ end
+
+ resources :orders
+ resources :orderitems, only: [:index, :create]
+ resources :products
+ resources :categories
+end
+
+
+# review route - reference: https://stackoverflow.com/questions/25107038/ruby-on-rails-settting-up-reviews-functionality
\ No newline at end of file
diff --git a/config/spring.rb b/config/spring.rb
new file mode 100644
index 0000000000..db5bf1307a
--- /dev/null
+++ b/config/spring.rb
@@ -0,0 +1,6 @@
+Spring.watch(
+ ".ruby-version",
+ ".rbenv-vars",
+ "tmp/restart.txt",
+ "tmp/caching-dev.txt"
+)
diff --git a/config/storage.yml b/config/storage.yml
new file mode 100644
index 0000000000..d32f76e8fb
--- /dev/null
+++ b/config/storage.yml
@@ -0,0 +1,34 @@
+test:
+ service: Disk
+ root: <%= Rails.root.join("tmp/storage") %>
+
+local:
+ service: Disk
+ root: <%= Rails.root.join("storage") %>
+
+# Use rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key)
+# amazon:
+# service: S3
+# access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
+# secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
+# region: us-east-1
+# bucket: your_own_bucket
+
+# Remember not to checkin your GCS keyfile to a repository
+# google:
+# service: GCS
+# project: your_project
+# credentials: <%= Rails.root.join("path/to/gcs.keyfile") %>
+# bucket: your_own_bucket
+
+# Use rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key)
+# microsoft:
+# service: AzureStorage
+# storage_account_name: your_account_name
+# storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %>
+# container: your_container_name
+
+# mirror:
+# service: Mirror
+# primary: local
+# mirrors: [ amazon, google, microsoft ]
diff --git a/config/webpack/development.js b/config/webpack/development.js
new file mode 100644
index 0000000000..c5edff94ad
--- /dev/null
+++ b/config/webpack/development.js
@@ -0,0 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpack/environment.js b/config/webpack/environment.js
new file mode 100644
index 0000000000..48103041f7
--- /dev/null
+++ b/config/webpack/environment.js
@@ -0,0 +1,12 @@
+const { environment } = require('@rails/webpacker')
+
+module.exports = environment
+const webpack = require('webpack')
+environment.plugins.append(
+ 'Provide',
+ new webpack.ProvidePlugin({
+ $: 'jquery',
+ jQuery: 'jquery',
+ Popper: ['popper.js', 'default']
+ })
+)
diff --git a/config/webpack/production.js b/config/webpack/production.js
new file mode 100644
index 0000000000..be0f53aacf
--- /dev/null
+++ b/config/webpack/production.js
@@ -0,0 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'production'
+
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpack/test.js b/config/webpack/test.js
new file mode 100644
index 0000000000..c5edff94ad
--- /dev/null
+++ b/config/webpack/test.js
@@ -0,0 +1,5 @@
+process.env.NODE_ENV = process.env.NODE_ENV || 'development'
+
+const environment = require('./environment')
+
+module.exports = environment.toWebpackConfig()
diff --git a/config/webpacker.yml b/config/webpacker.yml
new file mode 100644
index 0000000000..8581ac0472
--- /dev/null
+++ b/config/webpacker.yml
@@ -0,0 +1,96 @@
+# Note: You must restart bin/webpack-dev-server for changes to take effect
+
+default: &default
+ source_path: app/javascript
+ source_entry_path: packs
+ public_root_path: public
+ public_output_path: packs
+ cache_path: tmp/cache/webpacker
+ check_yarn_integrity: false
+ webpack_compile_output: true
+
+ # Additional paths webpack should lookup modules
+ # ['app/assets', 'engine/foo/app/assets']
+ resolved_paths: []
+
+ # Reload manifest.json on all requests so we reload latest compiled packs
+ cache_manifest: false
+
+ # Extract and emit a css file
+ extract_css: false
+
+ static_assets_extensions:
+ - .jpg
+ - .jpeg
+ - .png
+ - .gif
+ - .tiff
+ - .ico
+ - .svg
+ - .eot
+ - .otf
+ - .ttf
+ - .woff
+ - .woff2
+
+ extensions:
+ - .mjs
+ - .js
+ - .sass
+ - .scss
+ - .css
+ - .module.sass
+ - .module.scss
+ - .module.css
+ - .png
+ - .svg
+ - .gif
+ - .jpeg
+ - .jpg
+
+development:
+ <<: *default
+ compile: true
+
+ # Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
+ check_yarn_integrity: true
+
+ # Reference: https://webpack.js.org/configuration/dev-server/
+ dev_server:
+ https: false
+ host: localhost
+ port: 3035
+ public: localhost:3035
+ hmr: false
+ # Inline should be set to true if using HMR
+ inline: true
+ overlay: true
+ compress: true
+ disable_host_check: true
+ use_local_ip: false
+ quiet: false
+ pretty: false
+ headers:
+ 'Access-Control-Allow-Origin': '*'
+ watch_options:
+ ignored: '**/node_modules/**'
+
+
+test:
+ <<: *default
+ compile: true
+
+ # Compile test packs to a separate directory
+ public_output_path: packs-test
+
+production:
+ <<: *default
+
+ # Production depends on precompilation of packs prior to booting for performance.
+ compile: false
+
+ # Extract and emit a css file
+ extract_css: true
+
+ # Cache manifest.json for performance
+ cache_manifest: true
diff --git a/coverage/.last_run.json b/coverage/.last_run.json
new file mode 100644
index 0000000000..16aa008b6e
--- /dev/null
+++ b/coverage/.last_run.json
@@ -0,0 +1,5 @@
+{
+ "result": {
+ "covered_percent": 90.04
+ }
+}
diff --git a/coverage/.resultset.json b/coverage/.resultset.json
new file mode 100644
index 0000000000..912eff4de5
--- /dev/null
+++ b/coverage/.resultset.json
@@ -0,0 +1,1466 @@
+{
+ "Minitest": {
+ "coverage": {
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/environment.rb": {
+ "lines": [
+ null,
+ 1,
+ null,
+ null,
+ 1
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/environments/test.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/action_view.rb": {
+ "lines": [
+ 1
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/application_controller_renderer.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/assets.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/backtrace_silencers.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/content_security_policy.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/cookies_serializer.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ 1
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/filter_parameter_logging.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ 1
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/inflections.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/mime_types.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/omniauth.rb": {
+ "lines": [
+ 1,
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/initializers/wrap_parameters.rb": {
+ "lines": [
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/config/routes.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/application_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/categories_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/orderitems_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/orders_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/pages_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/products_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/reviews_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/helpers/users_helper.rb": {
+ "lines": [
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/categories_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 5,
+ 1,
+ 4,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 3,
+ null,
+ 3,
+ 1,
+ 1,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 3,
+ 1,
+ 1,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 6,
+ null,
+ null,
+ 1,
+ 3,
+ 3,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/application_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 94,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ null,
+ 1,
+ 94,
+ 24,
+ null,
+ null,
+ null,
+ 1,
+ 4,
+ null,
+ null,
+ 1,
+ 4,
+ 2,
+ 2,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/orderitems_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 5,
+ 5,
+ null,
+ null,
+ 5,
+ null,
+ 5,
+ null,
+ 5,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ 5,
+ null,
+ null,
+ null,
+ null,
+ 5,
+ 4,
+ 4,
+ 4,
+ 4,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ 2,
+ null,
+ null,
+ 2,
+ 2,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 5,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/orders_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ 2,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ 2,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 2,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/pages_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/products_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ null,
+ 12,
+ 10,
+ 8,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 2,
+ 2,
+ null,
+ 2,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ 3,
+ null,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ 2,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 4,
+ 2,
+ 2,
+ 2,
+ null,
+ null,
+ 2,
+ 2,
+ 2,
+ 2,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 5,
+ null,
+ null,
+ 1,
+ 9,
+ 9,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/reviews_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 2,
+ 2,
+ null,
+ null,
+ 1,
+ 4,
+ null,
+ null,
+ 4,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 3,
+ 1,
+ 1,
+ null,
+ 2,
+ null,
+ null,
+ 3,
+ 2,
+ null,
+ 1,
+ null,
+ 1,
+ 4,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 3,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 3,
+ null,
+ null,
+ 1,
+ 6,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/controllers/users_controller.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ 2,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 37,
+ null,
+ 37,
+ null,
+ 37,
+ 25,
+ null,
+ null,
+ 12,
+ null,
+ 12,
+ 1,
+ null,
+ 11,
+ null,
+ null,
+ null,
+ 37,
+ 37,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 2,
+ 0,
+ null,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/category.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 6,
+ null,
+ 12,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/application_record.rb": {
+ "lines": [
+ 1,
+ 1,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/order_item.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/order.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 3,
+ null,
+ 2,
+ null,
+ 2,
+ 3,
+ 3,
+ null,
+ null,
+ 2,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/validators/order_validator.rb": {
+ "lines": [
+ 1,
+ 1,
+ 42,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/product.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 4,
+ 2,
+ null,
+ 2,
+ null,
+ null,
+ null,
+ 1,
+ 36,
+ null,
+ null,
+ 1,
+ 47,
+ null,
+ 47,
+ 31,
+ null,
+ 53,
+ 16,
+ 16,
+ null,
+ null,
+ null,
+ 1,
+ 19,
+ null,
+ null,
+ 1,
+ 1,
+ 10,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 10,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 3,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/review.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 18,
+ 7,
+ 7,
+ null,
+ null
+ ]
+ },
+ "/Users/oliviamulholland/Ada/Developer/Projects/Rails/betsy/app/models/user.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 13,
+ 13,
+ 13,
+ 13,
+ 13,
+ 13,
+ null,
+ 13,
+ null,
+ null
+ ]
+ }
+ },
+ "timestamp": 1592586145
+ },
+ "Unit Tests": {
+ "coverage": {
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/order_item.rb": {
+ "lines": [
+ 1,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 2,
+ 2,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 2,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/application_record.rb": {
+ "lines": [
+ 1,
+ 1,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/review.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/product.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ 1,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/category.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/order.rb": {
+ "lines": [
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ 10,
+ null,
+ 10,
+ null,
+ 10,
+ 0,
+ null,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ 1,
+ null,
+ 1,
+ 0,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/validators/order_validator.rb": {
+ "lines": [
+ 1,
+ 1,
+ 7,
+ null,
+ null
+ ]
+ },
+ "/Users/corinnafabre/Developer/projects/betsy/app/models/user.rb": {
+ "lines": [
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 1,
+ 1,
+ null,
+ 1,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ null,
+ null,
+ 1,
+ 0,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ 0,
+ null,
+ null,
+ null,
+ null,
+ null,
+ 0,
+ 0,
+ 0,
+ null,
+ null
+ ]
+ }
+ },
+ "timestamp": 1592510061
+ }
+}
diff --git a/coverage/.resultset.json.lock b/coverage/.resultset.json.lock
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png
new file mode 100644
index 0000000000..e1ba61a805
Binary files /dev/null and b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc.png differ
diff --git a/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png
new file mode 100644
index 0000000000..fb11dfe24a
Binary files /dev/null and b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_asc_disabled.png differ
diff --git a/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png
new file mode 100644
index 0000000000..af5bc7c5a1
Binary files /dev/null and b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_both.png differ
diff --git a/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png
new file mode 100644
index 0000000000..0e156deb5f
Binary files /dev/null and b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc.png differ
diff --git a/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png
new file mode 100644
index 0000000000..c9fdd8a150
Binary files /dev/null and b/coverage/assets/0.12.2/DataTables-1.10.20/images/sort_desc_disabled.png differ
diff --git a/coverage/assets/0.12.2/application.css b/coverage/assets/0.12.2/application.css
new file mode 100644
index 0000000000..916699ed2a
--- /dev/null
+++ b/coverage/assets/0.12.2/application.css
@@ -0,0 +1 @@
+html,body,div,span,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,code,del,dfn,em,img,q,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td,article,aside,dialog,figure,footer,header,hgroup,nav,section{margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline}article,aside,dialog,figure,footer,header,hgroup,nav,section{display:block}body{line-height:1.5}table{border-collapse:separate;border-spacing:0}caption,th,td{text-align:left;font-weight:normal}table,td,th{vertical-align:middle}blockquote:before,blockquote:after,q:before,q:after{content:""}blockquote,q{quotes:"" ""}a img{border:0}html{font-size:100.01%}body{font-size:82%;color:#222;background:#fff;font-family:"Helvetica Neue",Arial,Helvetica,sans-serif}h1,h2,h3,h4,h5,h6{font-weight:normal;color:#111}h1{font-size:3em;line-height:1;margin-bottom:.5em}h2{font-size:2em;margin-bottom:.75em}h3{font-size:1.5em;line-height:1;margin-bottom:1em}h4{font-size:1.2em;line-height:1.25;margin-bottom:1.25em}h5{font-size:1em;font-weight:bold;margin-bottom:1.5em}h6{font-size:1em;font-weight:bold}h1 img,h2 img,h3 img,h4 img,h5 img,h6 img{margin:0}p{margin:0 0 1.5em}p img.left{float:left;margin:1.5em 1.5em 1.5em 0;padding:0}p img.right{float:right;margin:1.5em 0 1.5em 1.5em}a:focus,a:hover{color:#000}a{color:#009;text-decoration:underline}blockquote{margin:1.5em;color:#666;font-style:italic}strong{font-weight:bold}em,dfn{font-style:italic}dfn{font-weight:bold}sup,sub{line-height:0}abbr,acronym{border-bottom:1px dotted #666}address{margin:0 0 1.5em;font-style:italic}del{color:#666}pre{margin:1.5em 0;white-space:pre}pre,code,tt{font:1em 'andale mono','lucida console',monospace;line-height:1.5}li ul,li ol{margin:0}ul,ol{margin:0 1.5em 1.5em 0;padding-left:3.333em}ul{list-style-type:disc}ol{list-style-type:decimal}dl{margin:0 0 1.5em 0}dl dt{font-weight:bold}dd{margin-left:1.5em}table{margin-bottom:1.4em;width:100%}th{font-weight:bold}thead th{background:#c3d9ff}th,td,caption{padding:4px 10px 4px 5px}tr.even td{background:#efefef}tfoot{font-style:italic}caption{background:#eee}.small{font-size:.8em;margin-bottom:1.875em;line-height:1.875em}.large{font-size:1.2em;line-height:2.5em;margin-bottom:1.25em}.hide{display:none}.quiet{color:#666}.loud{color:#000}.highlight{background:#ff0}.added{background:#060;color:#fff}.removed{background:#900;color:#fff}.first{margin-left:0;padding-left:0}.last{margin-right:0;padding-right:0}.top{margin-top:0;padding-top:0}.bottom{margin-bottom:0;padding-bottom:0}label{font-weight:bold}fieldset{padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc}legend{font-weight:bold;font-size:1.2em}input[type=text],input[type=password],input.text,input.title,textarea,select{background-color:#fff;border:1px solid #bbb}input[type=text]:focus,input[type=password]:focus,input.text:focus,input.title:focus,textarea:focus,select:focus{border-color:#666}input[type=text],input[type=password],input.text,input.title,textarea,select{margin:.5em 0}input.text,input.title{width:300px;padding:5px}input.title{font-size:1.5em}textarea{width:390px;height:250px;padding:5px}input[type=checkbox],input[type=radio],input.checkbox,input.radio{position:relative;top:.25em}form.inline{line-height:3}form.inline p{margin-bottom:0}.error,.notice,.success{padding:.8em;margin-bottom:1em;border:2px solid #ddd}.error{background:#fbe3e4;color:#8a1f11;border-color:#fbc2c4}.notice{background:#fff6bf;color:#514721;border-color:#ffd324}.success{background:#e6efc2;color:#264409;border-color:#c6d880}.error a{color:#8a1f11}.notice a{color:#514721}.success a{color:#264409}.box{padding:1.5em;margin-bottom:1.5em;background:#e5ecf9}hr{background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:0}hr.space{background:#fff;color:#fff;visibility:hidden}.clearfix:after,.container:after{content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden}.clearfix,.container{display:block}.clear{clear:both}table.dataTable{width:100%;margin:0 auto;clear:both;border-collapse:separate;border-spacing:0}table.dataTable thead th,table.dataTable tfoot th{font-weight:bold}table.dataTable thead th,table.dataTable thead td{padding:10px 18px;border-bottom:1px solid #111}table.dataTable thead th:active,table.dataTable thead td:active{outline:0}table.dataTable tfoot th,table.dataTable tfoot td{padding:10px 18px 6px 18px;border-top:1px solid #111}table.dataTable thead .sorting,table.dataTable thead .sorting_asc,table.dataTable thead .sorting_desc,table.dataTable thead .sorting_asc_disabled,table.dataTable thead .sorting_desc_disabled{cursor:pointer;*cursor:hand;background-repeat:no-repeat;background-position:center right}table.dataTable thead .sorting{background-image:url("DataTables-1.10.20/images/sort_both.png")}table.dataTable thead .sorting_asc{background-image:url("DataTables-1.10.20/images/sort_asc.png")}table.dataTable thead .sorting_desc{background-image:url("DataTables-1.10.20/images/sort_desc.png")}table.dataTable thead .sorting_asc_disabled{background-image:url("DataTables-1.10.20/images/sort_asc_disabled.png")}table.dataTable thead .sorting_desc_disabled{background-image:url("DataTables-1.10.20/images/sort_desc_disabled.png")}table.dataTable tbody tr{background-color:#fff}table.dataTable tbody tr.selected{background-color:#b0bed9}table.dataTable tbody th,table.dataTable tbody td{padding:8px 10px}table.dataTable.row-border tbody th,table.dataTable.row-border tbody td,table.dataTable.display tbody th,table.dataTable.display tbody td{border-top:1px solid #ddd}table.dataTable.row-border tbody tr:first-child th,table.dataTable.row-border tbody tr:first-child td,table.dataTable.display tbody tr:first-child th,table.dataTable.display tbody tr:first-child td{border-top:0}table.dataTable.cell-border tbody th,table.dataTable.cell-border tbody td{border-top:1px solid #ddd;border-right:1px solid #ddd}table.dataTable.cell-border tbody tr th:first-child,table.dataTable.cell-border tbody tr td:first-child{border-left:1px solid #ddd}table.dataTable.cell-border tbody tr:first-child th,table.dataTable.cell-border tbody tr:first-child td{border-top:0}table.dataTable.stripe tbody tr.odd,table.dataTable.display tbody tr.odd{background-color:#f9f9f9}table.dataTable.stripe tbody tr.odd.selected,table.dataTable.display tbody tr.odd.selected{background-color:#acbad4}table.dataTable.hover tbody tr:hover,table.dataTable.display tbody tr:hover{background-color:#f6f6f6}table.dataTable.hover tbody tr:hover.selected,table.dataTable.display tbody tr:hover.selected{background-color:#aab7d1}table.dataTable.order-column tbody tr>.sorting_1,table.dataTable.order-column tbody tr>.sorting_2,table.dataTable.order-column tbody tr>.sorting_3,table.dataTable.display tbody tr>.sorting_1,table.dataTable.display tbody tr>.sorting_2,table.dataTable.display tbody tr>.sorting_3{background-color:#fafafa}table.dataTable.order-column tbody tr.selected>.sorting_1,table.dataTable.order-column tbody tr.selected>.sorting_2,table.dataTable.order-column tbody tr.selected>.sorting_3,table.dataTable.display tbody tr.selected>.sorting_1,table.dataTable.display tbody tr.selected>.sorting_2,table.dataTable.display tbody tr.selected>.sorting_3{background-color:#acbad5}table.dataTable.display tbody tr.odd>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd>.sorting_1{background-color:#f1f1f1}table.dataTable.display tbody tr.odd>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd>.sorting_2{background-color:#f3f3f3}table.dataTable.display tbody tr.odd>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd>.sorting_3{background-color:whitesmoke}table.dataTable.display tbody tr.odd.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_1{background-color:#a6b4cd}table.dataTable.display tbody tr.odd.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_2{background-color:#a8b5cf}table.dataTable.display tbody tr.odd.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.odd.selected>.sorting_3{background-color:#a9b7d1}table.dataTable.display tbody tr.even>.sorting_1,table.dataTable.order-column.stripe tbody tr.even>.sorting_1{background-color:#fafafa}table.dataTable.display tbody tr.even>.sorting_2,table.dataTable.order-column.stripe tbody tr.even>.sorting_2{background-color:#fcfcfc}table.dataTable.display tbody tr.even>.sorting_3,table.dataTable.order-column.stripe tbody tr.even>.sorting_3{background-color:#fefefe}table.dataTable.display tbody tr.even.selected>.sorting_1,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_1{background-color:#acbad5}table.dataTable.display tbody tr.even.selected>.sorting_2,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_2{background-color:#aebcd6}table.dataTable.display tbody tr.even.selected>.sorting_3,table.dataTable.order-column.stripe tbody tr.even.selected>.sorting_3{background-color:#afbdd8}table.dataTable.display tbody tr:hover>.sorting_1,table.dataTable.order-column.hover tbody tr:hover>.sorting_1{background-color:#eaeaea}table.dataTable.display tbody tr:hover>.sorting_2,table.dataTable.order-column.hover tbody tr:hover>.sorting_2{background-color:#ececec}table.dataTable.display tbody tr:hover>.sorting_3,table.dataTable.order-column.hover tbody tr:hover>.sorting_3{background-color:#efefef}table.dataTable.display tbody tr:hover.selected>.sorting_1,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_1{background-color:#a2aec7}table.dataTable.display tbody tr:hover.selected>.sorting_2,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_2{background-color:#a3b0c9}table.dataTable.display tbody tr:hover.selected>.sorting_3,table.dataTable.order-column.hover tbody tr:hover.selected>.sorting_3{background-color:#a5b2cb}table.dataTable.no-footer{border-bottom:1px solid #111}table.dataTable.nowrap th,table.dataTable.nowrap td{white-space:nowrap}table.dataTable.compact thead th,table.dataTable.compact thead td{padding:4px 17px 4px 4px}table.dataTable.compact tfoot th,table.dataTable.compact tfoot td{padding:4px}table.dataTable.compact tbody th,table.dataTable.compact tbody td{padding:4px}table.dataTable th.dt-left,table.dataTable td.dt-left{text-align:left}table.dataTable th.dt-center,table.dataTable td.dt-center,table.dataTable td.dataTables_empty{text-align:center}table.dataTable th.dt-right,table.dataTable td.dt-right{text-align:right}table.dataTable th.dt-justify,table.dataTable td.dt-justify{text-align:justify}table.dataTable th.dt-nowrap,table.dataTable td.dt-nowrap{white-space:nowrap}table.dataTable thead th.dt-head-left,table.dataTable thead td.dt-head-left,table.dataTable tfoot th.dt-head-left,table.dataTable tfoot td.dt-head-left{text-align:left}table.dataTable thead th.dt-head-center,table.dataTable thead td.dt-head-center,table.dataTable tfoot th.dt-head-center,table.dataTable tfoot td.dt-head-center{text-align:center}table.dataTable thead th.dt-head-right,table.dataTable thead td.dt-head-right,table.dataTable tfoot th.dt-head-right,table.dataTable tfoot td.dt-head-right{text-align:right}table.dataTable thead th.dt-head-justify,table.dataTable thead td.dt-head-justify,table.dataTable tfoot th.dt-head-justify,table.dataTable tfoot td.dt-head-justify{text-align:justify}table.dataTable thead th.dt-head-nowrap,table.dataTable thead td.dt-head-nowrap,table.dataTable tfoot th.dt-head-nowrap,table.dataTable tfoot td.dt-head-nowrap{white-space:nowrap}table.dataTable tbody th.dt-body-left,table.dataTable tbody td.dt-body-left{text-align:left}table.dataTable tbody th.dt-body-center,table.dataTable tbody td.dt-body-center{text-align:center}table.dataTable tbody th.dt-body-right,table.dataTable tbody td.dt-body-right{text-align:right}table.dataTable tbody th.dt-body-justify,table.dataTable tbody td.dt-body-justify{text-align:justify}table.dataTable tbody th.dt-body-nowrap,table.dataTable tbody td.dt-body-nowrap{white-space:nowrap}table.dataTable,table.dataTable th,table.dataTable td{box-sizing:content-box}.dataTables_wrapper{position:relative;clear:both;*zoom:1;zoom:1}.dataTables_wrapper .dataTables_length{float:left}.dataTables_wrapper .dataTables_filter{float:right;text-align:right}.dataTables_wrapper .dataTables_filter input{margin-left:.5em}.dataTables_wrapper .dataTables_info{clear:both;float:left;padding-top:.755em}.dataTables_wrapper .dataTables_paginate{float:right;text-align:right;padding-top:.25em}.dataTables_wrapper .dataTables_paginate .paginate_button{box-sizing:border-box;display:inline-block;min-width:1.5em;padding:.5em 1em;margin-left:2px;text-align:center;text-decoration:none !important;cursor:pointer;*cursor:hand;color:#333 !important;border:1px solid transparent;border-radius:2px}.dataTables_wrapper .dataTables_paginate .paginate_button.current,.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover{color:#333 !important;border:1px solid #979797;background-color:white;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,white),color-stop(100%,#dcdcdc));background:-webkit-linear-gradient(top,white 0,#dcdcdc 100%);background:-moz-linear-gradient(top,white 0,#dcdcdc 100%);background:-ms-linear-gradient(top,white 0,#dcdcdc 100%);background:-o-linear-gradient(top,white 0,#dcdcdc 100%);background:linear-gradient(to bottom,white 0,#dcdcdc 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button.disabled,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover,.dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active{cursor:default;color:#666 !important;border:1px solid transparent;background:transparent;box-shadow:none}.dataTables_wrapper .dataTables_paginate .paginate_button:hover{color:white !important;border:1px solid #111;background-color:#585858;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#585858),color-stop(100%,#111));background:-webkit-linear-gradient(top,#585858 0,#111 100%);background:-moz-linear-gradient(top,#585858 0,#111 100%);background:-ms-linear-gradient(top,#585858 0,#111 100%);background:-o-linear-gradient(top,#585858 0,#111 100%);background:linear-gradient(to bottom,#585858 0,#111 100%)}.dataTables_wrapper .dataTables_paginate .paginate_button:active{outline:0;background-color:#2b2b2b;background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#2b2b2b),color-stop(100%,#0c0c0c));background:-webkit-linear-gradient(top,#2b2b2b 0,#0c0c0c 100%);background:-moz-linear-gradient(top,#2b2b2b 0,#0c0c0c 100%);background:-ms-linear-gradient(top,#2b2b2b 0,#0c0c0c 100%);background:-o-linear-gradient(top,#2b2b2b 0,#0c0c0c 100%);background:linear-gradient(to bottom,#2b2b2b 0,#0c0c0c 100%);box-shadow:inset 0 0 3px #111}.dataTables_wrapper .dataTables_paginate .ellipsis{padding:0 1em}.dataTables_wrapper .dataTables_processing{position:absolute;top:50%;left:50%;width:100%;height:40px;margin-left:-50%;margin-top:-25px;padding-top:20px;text-align:center;font-size:1.2em;background-color:white;background:-webkit-gradient(linear,left top,right top,color-stop(0,rgba(255,255,255,0)),color-stop(25%,rgba(255,255,255,0.9)),color-stop(75%,rgba(255,255,255,0.9)),color-stop(100%,rgba(255,255,255,0)));background:-webkit-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-moz-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-ms-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:-o-linear-gradient(left,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%);background:linear-gradient(to right,rgba(255,255,255,0) 0,rgba(255,255,255,0.9) 25%,rgba(255,255,255,0.9) 75%,rgba(255,255,255,0) 100%)}.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter,.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_processing,.dataTables_wrapper .dataTables_paginate{color:#333}.dataTables_wrapper .dataTables_scroll{clear:both}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody{*margin-top:-1px;-webkit-overflow-scrolling:touch}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td{vertical-align:middle}.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>thead>tr>td>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>th>div.dataTables_sizing,.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody>table>tbody>tr>td>div.dataTables_sizing{height:0;overflow:hidden;margin:0 !important;padding:0 !important}.dataTables_wrapper.no-footer .dataTables_scrollBody{border-bottom:1px solid #111}.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,.dataTables_wrapper.no-footer div.dataTables_scrollBody>table{border-bottom:0}.dataTables_wrapper:after{visibility:hidden;display:block;content:"";clear:both;height:0}@media screen and (max-width:767px){.dataTables_wrapper .dataTables_info,.dataTables_wrapper .dataTables_paginate{float:none;text-align:center}.dataTables_wrapper .dataTables_paginate{margin-top:.5em}}@media screen and (max-width:640px){.dataTables_wrapper .dataTables_length,.dataTables_wrapper .dataTables_filter{float:none;text-align:center}.dataTables_wrapper .dataTables_filter{margin-top:.5em}}pre .comment,pre .template_comment,pre .diff .header,pre .javadoc{color:#998;font-style:italic}pre .keyword,pre .css .rule .keyword,pre .winutils,pre .javascript .title,pre .lisp .title{color:#000;font-weight:bold}pre .number,pre .hexcolor{color:#458}pre .string,pre .tag .value,pre .phpdoc,pre .tex .formula{color:#d14}pre .subst{color:#712}pre .constant,pre .title,pre .id{color:#900;font-weight:bold}pre .javascript .title,pre .lisp .title,pre .subst{font-weight:normal}pre .class .title,pre .haskell .label,pre .tex .command{color:#458;font-weight:bold}pre .tag,pre .tag .title,pre .rules .property,pre .django .tag .keyword{color:navy;font-weight:normal}pre .attribute,pre .variable,pre .instancevar,pre .lisp .body{color:teal}pre .regexp{color:#009926}pre .class{color:#458;font-weight:bold}pre .symbol,pre .ruby .symbol .string,pre .ruby .symbol .keyword,pre .ruby .symbol .keymethods,pre .lisp .keyword,pre .tex .special,pre .input_number{color:#990073}pre .builtin,pre .built_in,pre .lisp .title{color:#0086b3}pre .preprocessor,pre .pi,pre .doctype,pre .shebang,pre .cdata{color:#999;font-weight:bold}pre .deletion{background:#fdd}pre .addition{background:#dfd}pre .diff .change{background:#0086b3}pre .chunk{color:#aaa}pre .tex .formula{opacity:.5}.ui-helper-hidden{display:none}.ui-helper-hidden-accessible{position:absolute;left:-99999999px}.ui-helper-reset{margin:0;padding:0;border:0;outline:0;line-height:1.3;text-decoration:none;font-size:100%;list-style:none}.ui-helper-clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden}.ui-helper-clearfix{display:inline-block}/*\*/* html .ui-helper-clearfix{height:1%}.ui-helper-clearfix{display:block}/**/.ui-helper-zfix{width:100%;height:100%;top:0;left:0;position:absolute;opacity:0;filter:Alpha(Opacity=0)}.ui-state-disabled{cursor:default !important}.ui-icon{display:block;text-indent:-99999px;overflow:hidden;background-repeat:no-repeat}.ui-widget-overlay{position:absolute;top:0;left:0;width:100%;height:100%}.ui-widget{font-family:Verdana,Arial,sans-serif;font-size:1.1em}.ui-widget .ui-widget{font-size:1em}.ui-widget input,.ui-widget select,.ui-widget textarea,.ui-widget button{font-family:Verdana,Arial,sans-serif;font-size:1em}.ui-widget-content{border:1px solid #aaa;background:#fff url(images/ui-bg_flat_75_ffffff_40x100.png) 50% 50% repeat-x;color:#222}.ui-widget-content a{color:#222}.ui-widget-header{border:1px solid #aaa;background:#ccc url(images/ui-bg_highlight-soft_75_cccccc_1x100.png) 50% 50% repeat-x;color:#222;font-weight:bold}.ui-widget-header a{color:#222}.ui-state-default,.ui-widget-content .ui-state-default,.ui-widget-header .ui-state-default{border:1px solid #d3d3d3;background:#e6e6e6 url(images/ui-bg_glass_75_e6e6e6_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#555}.ui-state-default a,.ui-state-default a:link,.ui-state-default a:visited{color:#555;text-decoration:none}.ui-state-hover,.ui-widget-content .ui-state-hover,.ui-widget-header .ui-state-hover,.ui-state-focus,.ui-widget-content .ui-state-focus,.ui-widget-header .ui-state-focus{border:1px solid #999;background:#dadada url(images/ui-bg_glass_75_dadada_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-hover a,.ui-state-hover a:hover{color:#212121;text-decoration:none}.ui-state-active,.ui-widget-content .ui-state-active,.ui-widget-header .ui-state-active{border:1px solid #aaa;background:#fff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x;font-weight:normal;color:#212121}.ui-state-active a,.ui-state-active a:link,.ui-state-active a:visited{color:#212121;text-decoration:none}.ui-widget :active{outline:0}.ui-state-highlight,.ui-widget-content .ui-state-highlight,.ui-widget-header .ui-state-highlight{border:1px solid #fcefa1;background:#fbf9ee url(images/ui-bg_glass_55_fbf9ee_1x400.png) 50% 50% repeat-x;color:#363636}.ui-state-highlight a,.ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a{color:#363636}.ui-state-error,.ui-widget-content .ui-state-error,.ui-widget-header .ui-state-error{border:1px solid #cd0a0a;background:#fef1ec url(images/ui-bg_glass_95_fef1ec_1x400.png) 50% 50% repeat-x;color:#cd0a0a}.ui-state-error a,.ui-widget-content .ui-state-error a,.ui-widget-header .ui-state-error a{color:#cd0a0a}.ui-state-error-text,.ui-widget-content .ui-state-error-text,.ui-widget-header .ui-state-error-text{color:#cd0a0a}.ui-priority-primary,.ui-widget-content .ui-priority-primary,.ui-widget-header .ui-priority-primary{font-weight:bold}.ui-priority-secondary,.ui-widget-content .ui-priority-secondary,.ui-widget-header .ui-priority-secondary{opacity:.7;filter:Alpha(Opacity=70);font-weight:normal}.ui-state-disabled,.ui-widget-content .ui-state-disabled,.ui-widget-header .ui-state-disabled{opacity:.35;filter:Alpha(Opacity=35);background-image:none}.ui-icon{width:16px;height:16px;background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-content .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-widget-header .ui-icon{background-image:url(images/ui-icons_222222_256x240.png)}.ui-state-default .ui-icon{background-image:url(images/ui-icons_888888_256x240.png)}.ui-state-hover .ui-icon,.ui-state-focus .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-active .ui-icon{background-image:url(images/ui-icons_454545_256x240.png)}.ui-state-highlight .ui-icon{background-image:url(images/ui-icons_2e83ff_256x240.png)}.ui-state-error .ui-icon,.ui-state-error-text .ui-icon{background-image:url(images/ui-icons_cd0a0a_256x240.png)}.ui-icon-carat-1-n{background-position:0 0}.ui-icon-carat-1-ne{background-position:-16px 0}.ui-icon-carat-1-e{background-position:-32px 0}.ui-icon-carat-1-se{background-position:-48px 0}.ui-icon-carat-1-s{background-position:-64px 0}.ui-icon-carat-1-sw{background-position:-80px 0}.ui-icon-carat-1-w{background-position:-96px 0}.ui-icon-carat-1-nw{background-position:-112px 0}.ui-icon-carat-2-n-s{background-position:-128px 0}.ui-icon-carat-2-e-w{background-position:-144px 0}.ui-icon-triangle-1-n{background-position:0 -16px}.ui-icon-triangle-1-ne{background-position:-16px -16px}.ui-icon-triangle-1-e{background-position:-32px -16px}.ui-icon-triangle-1-se{background-position:-48px -16px}.ui-icon-triangle-1-s{background-position:-64px -16px}.ui-icon-triangle-1-sw{background-position:-80px -16px}.ui-icon-triangle-1-w{background-position:-96px -16px}.ui-icon-triangle-1-nw{background-position:-112px -16px}.ui-icon-triangle-2-n-s{background-position:-128px -16px}.ui-icon-triangle-2-e-w{background-position:-144px -16px}.ui-icon-arrow-1-n{background-position:0 -32px}.ui-icon-arrow-1-ne{background-position:-16px -32px}.ui-icon-arrow-1-e{background-position:-32px -32px}.ui-icon-arrow-1-se{background-position:-48px -32px}.ui-icon-arrow-1-s{background-position:-64px -32px}.ui-icon-arrow-1-sw{background-position:-80px -32px}.ui-icon-arrow-1-w{background-position:-96px -32px}.ui-icon-arrow-1-nw{background-position:-112px -32px}.ui-icon-arrow-2-n-s{background-position:-128px -32px}.ui-icon-arrow-2-ne-sw{background-position:-144px -32px}.ui-icon-arrow-2-e-w{background-position:-160px -32px}.ui-icon-arrow-2-se-nw{background-position:-176px -32px}.ui-icon-arrowstop-1-n{background-position:-192px -32px}.ui-icon-arrowstop-1-e{background-position:-208px -32px}.ui-icon-arrowstop-1-s{background-position:-224px -32px}.ui-icon-arrowstop-1-w{background-position:-240px -32px}.ui-icon-arrowthick-1-n{background-position:0 -48px}.ui-icon-arrowthick-1-ne{background-position:-16px -48px}.ui-icon-arrowthick-1-e{background-position:-32px -48px}.ui-icon-arrowthick-1-se{background-position:-48px -48px}.ui-icon-arrowthick-1-s{background-position:-64px -48px}.ui-icon-arrowthick-1-sw{background-position:-80px -48px}.ui-icon-arrowthick-1-w{background-position:-96px -48px}.ui-icon-arrowthick-1-nw{background-position:-112px -48px}.ui-icon-arrowthick-2-n-s{background-position:-128px -48px}.ui-icon-arrowthick-2-ne-sw{background-position:-144px -48px}.ui-icon-arrowthick-2-e-w{background-position:-160px -48px}.ui-icon-arrowthick-2-se-nw{background-position:-176px -48px}.ui-icon-arrowthickstop-1-n{background-position:-192px -48px}.ui-icon-arrowthickstop-1-e{background-position:-208px -48px}.ui-icon-arrowthickstop-1-s{background-position:-224px -48px}.ui-icon-arrowthickstop-1-w{background-position:-240px -48px}.ui-icon-arrowreturnthick-1-w{background-position:0 -64px}.ui-icon-arrowreturnthick-1-n{background-position:-16px -64px}.ui-icon-arrowreturnthick-1-e{background-position:-32px -64px}.ui-icon-arrowreturnthick-1-s{background-position:-48px -64px}.ui-icon-arrowreturn-1-w{background-position:-64px -64px}.ui-icon-arrowreturn-1-n{background-position:-80px -64px}.ui-icon-arrowreturn-1-e{background-position:-96px -64px}.ui-icon-arrowreturn-1-s{background-position:-112px -64px}.ui-icon-arrowrefresh-1-w{background-position:-128px -64px}.ui-icon-arrowrefresh-1-n{background-position:-144px -64px}.ui-icon-arrowrefresh-1-e{background-position:-160px -64px}.ui-icon-arrowrefresh-1-s{background-position:-176px -64px}.ui-icon-arrow-4{background-position:0 -80px}.ui-icon-arrow-4-diag{background-position:-16px -80px}.ui-icon-extlink{background-position:-32px -80px}.ui-icon-newwin{background-position:-48px -80px}.ui-icon-refresh{background-position:-64px -80px}.ui-icon-shuffle{background-position:-80px -80px}.ui-icon-transfer-e-w{background-position:-96px -80px}.ui-icon-transferthick-e-w{background-position:-112px -80px}.ui-icon-folder-collapsed{background-position:0 -96px}.ui-icon-folder-open{background-position:-16px -96px}.ui-icon-document{background-position:-32px -96px}.ui-icon-document-b{background-position:-48px -96px}.ui-icon-note{background-position:-64px -96px}.ui-icon-mail-closed{background-position:-80px -96px}.ui-icon-mail-open{background-position:-96px -96px}.ui-icon-suitcase{background-position:-112px -96px}.ui-icon-comment{background-position:-128px -96px}.ui-icon-person{background-position:-144px -96px}.ui-icon-print{background-position:-160px -96px}.ui-icon-trash{background-position:-176px -96px}.ui-icon-locked{background-position:-192px -96px}.ui-icon-unlocked{background-position:-208px -96px}.ui-icon-bookmark{background-position:-224px -96px}.ui-icon-tag{background-position:-240px -96px}.ui-icon-home{background-position:0 -112px}.ui-icon-flag{background-position:-16px -112px}.ui-icon-calendar{background-position:-32px -112px}.ui-icon-cart{background-position:-48px -112px}.ui-icon-pencil{background-position:-64px -112px}.ui-icon-clock{background-position:-80px -112px}.ui-icon-disk{background-position:-96px -112px}.ui-icon-calculator{background-position:-112px -112px}.ui-icon-zoomin{background-position:-128px -112px}.ui-icon-zoomout{background-position:-144px -112px}.ui-icon-search{background-position:-160px -112px}.ui-icon-wrench{background-position:-176px -112px}.ui-icon-gear{background-position:-192px -112px}.ui-icon-heart{background-position:-208px -112px}.ui-icon-star{background-position:-224px -112px}.ui-icon-link{background-position:-240px -112px}.ui-icon-cancel{background-position:0 -128px}.ui-icon-plus{background-position:-16px -128px}.ui-icon-plusthick{background-position:-32px -128px}.ui-icon-minus{background-position:-48px -128px}.ui-icon-minusthick{background-position:-64px -128px}.ui-icon-close{background-position:-80px -128px}.ui-icon-closethick{background-position:-96px -128px}.ui-icon-key{background-position:-112px -128px}.ui-icon-lightbulb{background-position:-128px -128px}.ui-icon-scissors{background-position:-144px -128px}.ui-icon-clipboard{background-position:-160px -128px}.ui-icon-copy{background-position:-176px -128px}.ui-icon-contact{background-position:-192px -128px}.ui-icon-image{background-position:-208px -128px}.ui-icon-video{background-position:-224px -128px}.ui-icon-script{background-position:-240px -128px}.ui-icon-alert{background-position:0 -144px}.ui-icon-info{background-position:-16px -144px}.ui-icon-notice{background-position:-32px -144px}.ui-icon-help{background-position:-48px -144px}.ui-icon-check{background-position:-64px -144px}.ui-icon-bullet{background-position:-80px -144px}.ui-icon-radio-off{background-position:-96px -144px}.ui-icon-radio-on{background-position:-112px -144px}.ui-icon-pin-w{background-position:-128px -144px}.ui-icon-pin-s{background-position:-144px -144px}.ui-icon-play{background-position:0 -160px}.ui-icon-pause{background-position:-16px -160px}.ui-icon-seek-next{background-position:-32px -160px}.ui-icon-seek-prev{background-position:-48px -160px}.ui-icon-seek-end{background-position:-64px -160px}.ui-icon-seek-start{background-position:-80px -160px}.ui-icon-seek-first{background-position:-80px -160px}.ui-icon-stop{background-position:-96px -160px}.ui-icon-eject{background-position:-112px -160px}.ui-icon-volume-off{background-position:-128px -160px}.ui-icon-volume-on{background-position:-144px -160px}.ui-icon-power{background-position:0 -176px}.ui-icon-signal-diag{background-position:-16px -176px}.ui-icon-signal{background-position:-32px -176px}.ui-icon-battery-0{background-position:-48px -176px}.ui-icon-battery-1{background-position:-64px -176px}.ui-icon-battery-2{background-position:-80px -176px}.ui-icon-battery-3{background-position:-96px -176px}.ui-icon-circle-plus{background-position:0 -192px}.ui-icon-circle-minus{background-position:-16px -192px}.ui-icon-circle-close{background-position:-32px -192px}.ui-icon-circle-triangle-e{background-position:-48px -192px}.ui-icon-circle-triangle-s{background-position:-64px -192px}.ui-icon-circle-triangle-w{background-position:-80px -192px}.ui-icon-circle-triangle-n{background-position:-96px -192px}.ui-icon-circle-arrow-e{background-position:-112px -192px}.ui-icon-circle-arrow-s{background-position:-128px -192px}.ui-icon-circle-arrow-w{background-position:-144px -192px}.ui-icon-circle-arrow-n{background-position:-160px -192px}.ui-icon-circle-zoomin{background-position:-176px -192px}.ui-icon-circle-zoomout{background-position:-192px -192px}.ui-icon-circle-check{background-position:-208px -192px}.ui-icon-circlesmall-plus{background-position:0 -208px}.ui-icon-circlesmall-minus{background-position:-16px -208px}.ui-icon-circlesmall-close{background-position:-32px -208px}.ui-icon-squaresmall-plus{background-position:-48px -208px}.ui-icon-squaresmall-minus{background-position:-64px -208px}.ui-icon-squaresmall-close{background-position:-80px -208px}.ui-icon-grip-dotted-vertical{background-position:0 -224px}.ui-icon-grip-dotted-horizontal{background-position:-16px -224px}.ui-icon-grip-solid-vertical{background-position:-32px -224px}.ui-icon-grip-solid-horizontal{background-position:-48px -224px}.ui-icon-gripsmall-diagonal-se{background-position:-64px -224px}.ui-icon-grip-diagonal-se{background-position:-80px -224px}.ui-corner-tl{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px}.ui-corner-tr{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-bl{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-br{-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-top{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px}.ui-corner-bottom{-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-right{-moz-border-radius-topright:4px;-webkit-border-top-right-radius:4px;border-top-right-radius:4px;-moz-border-radius-bottomright:4px;-webkit-border-bottom-right-radius:4px;border-bottom-right-radius:4px}.ui-corner-left{-moz-border-radius-topleft:4px;-webkit-border-top-left-radius:4px;border-top-left-radius:4px;-moz-border-radius-bottomleft:4px;-webkit-border-bottom-left-radius:4px;border-bottom-left-radius:4px}.ui-corner-all{-moz-border-radius:4px;-webkit-border-radius:4px;border-radius:4px}.ui-widget-overlay{background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.30;filter:Alpha(Opacity=30)}.ui-widget-shadow{margin:-8px 0 0 -8px;padding:8px;background:#aaa url(images/ui-bg_flat_0_aaaaaa_40x100.png) 50% 50% repeat-x;opacity:.30;filter:Alpha(Opacity=30);-moz-border-radius:8px;-webkit-border-radius:8px;border-radius:8px}#colorbox,#cboxOverlay,#cboxWrapper{position:absolute;top:0;left:0;z-index:9999;overflow:hidden}#cboxOverlay{position:fixed;width:100%;height:100%}#cboxMiddleLeft,#cboxBottomLeft{clear:left}#cboxContent{position:relative}#cboxLoadedContent{overflow:auto}#cboxTitle{margin:0}#cboxLoadingOverlay,#cboxLoadingGraphic{position:absolute;top:0;left:0;width:100%;height:100%}#cboxPrevious,#cboxNext,#cboxClose,#cboxSlideshow{cursor:pointer}.cboxPhoto{float:left;margin:auto;border:0;display:block;max-width:none}.cboxIframe{width:100%;height:100%;display:block;border:0}#colorbox,#cboxContent,#cboxLoadedContent{box-sizing:content-box}#cboxOverlay{background:#000}#cboxTopLeft{width:14px;height:14px;background:url(colorbox/controls.png) no-repeat 0 0}#cboxTopCenter{height:14px;background:url(colorbox/border.png) repeat-x top left}#cboxTopRight{width:14px;height:14px;background:url(colorbox/controls.png) no-repeat -36px 0}#cboxBottomLeft{width:14px;height:43px;background:url(colorbox/controls.png) no-repeat 0 -32px}#cboxBottomCenter{height:43px;background:url(colorbox/border.png) repeat-x bottom left}#cboxBottomRight{width:14px;height:43px;background:url(colorbox/controls.png) no-repeat -36px -32px}#cboxMiddleLeft{width:14px;background:url(colorbox/controls.png) repeat-y -175px 0}#cboxMiddleRight{width:14px;background:url(colorbox/controls.png) repeat-y -211px 0}#cboxContent{background:#fff;overflow:visible}.cboxIframe{background:#fff}#cboxError{padding:50px;border:1px solid #ccc}#cboxLoadedContent{margin-bottom:5px}#cboxLoadingOverlay{background:url(colorbox/loading_background.png) no-repeat center center}#cboxLoadingGraphic{background:url(colorbox/loading.gif) no-repeat center center}#cboxTitle{position:absolute;bottom:-25px;left:0;text-align:center;width:100%;font-weight:bold;color:#7c7c7c}#cboxCurrent{position:absolute;bottom:-25px;left:58px;font-weight:bold;color:#7c7c7c}#cboxPrevious,#cboxNext,#cboxClose,#cboxSlideshow{position:absolute;bottom:-29px;background:url(colorbox/controls.png) no-repeat 0 0;width:23px;height:23px;text-indent:-9999px}#cboxPrevious{left:0;background-position:-51px -25px}#cboxPrevious:hover{background-position:-51px 0}#cboxNext{left:27px;background-position:-75px -25px}#cboxNext:hover{background-position:-75px 0}#cboxClose{right:0;background-position:-100px -25px}#cboxClose:hover{background-position:-100px 0}.cboxSlideshow_on #cboxSlideshow{background-position:-125px 0;right:27px}.cboxSlideshow_on #cboxSlideshow:hover{background-position:-150px 0}.cboxSlideshow_off #cboxSlideshow{background-position:-150px -25px;right:27px}.cboxSlideshow_off #cboxSlideshow:hover{background-position:-125px 0}#loading{position:fixed;left:40%;top:50%}a{color:#333;text-decoration:none}a:hover{color:#000;text-decoration:underline}body{font-family:"Lucida Grande",Helvetica,"Helvetica Neue",Arial,sans-serif;padding:12px;background-color:#333}h1,h2,h3,h4{color:#1c2324;margin:0;padding:0;margin-bottom:12px}table{width:100%}#content{clear:left;background-color:white;border:2px solid #ddd;border-top:8px solid #ddd;padding:18px;-webkit-border-bottom-left-radius:5px;-webkit-border-bottom-right-radius:5px;-webkit-border-top-right-radius:5px;-moz-border-radius-bottomleft:5px;-moz-border-radius-bottomright:5px;-moz-border-radius-topright:5px;border-bottom-left-radius:5px;border-bottom-right-radius:5px;border-top-right-radius:5px}.dataTables_filter,.dataTables_info{padding:2px 6px}abbr.timeago{text-decoration:none;border:0;font-weight:bold}.timestamp{float:right;color:#ddd}.group_tabs{list-style:none;float:left;margin:0;padding:0}.group_tabs li{display:inline;float:left}.group_tabs li a{font-family:Helvetica,Arial,sans-serif;display:block;float:left;text-decoration:none;padding:4px 8px;background-color:#aaa;background:-webkit-gradient(linear,0 0,0 bottom,from(#ddd),to(#aaa));background:-moz-linear-gradient(#ddd,#aaa);background:linear-gradient(#ddd,#aaa);text-shadow:#e5e5e5 1px 1px 0;border-bottom:0;color:#333;font-weight:bold;margin-right:8px;border-top:1px solid #efefef;-webkit-border-top-left-radius:2px;-webkit-border-top-right-radius:2px;-moz-border-radius-topleft:2px;-moz-border-radius-topright:2px;border-top-left-radius:2px;border-top-right-radius:2px}.group_tabs li a:hover{background-color:#ccc;background:-webkit-gradient(linear,0 0,0 bottom,from(#eee),to(#aaa));background:-moz-linear-gradient(#eee,#aaa);background:linear-gradient(#eee,#aaa)}.group_tabs li a:active{padding-top:5px;padding-bottom:3px}.group_tabs li.active a{color:black;text-shadow:#fff 1px 1px 0;background-color:#ddd;background:-webkit-gradient(linear,0 0,0 bottom,from(white),to(#ddd));background:-moz-linear-gradient(white,#ddd);background:linear-gradient(white,#ddd)}.file_list{margin-bottom:18px}.file_list--responsive{overflow-x:auto;overflow-y:hidden}a.src_link{background:url("./magnify.png") no-repeat left 50%;padding-left:18px}tr,td{margin:0;padding:0}th{white-space:nowrap}th.ui-state-default{cursor:pointer}th span.ui-icon{float:left}td{padding:4px 8px}td.strong{font-weight:bold}.cell--number{text-align:right}.source_table h3,.source_table h4{padding:0;margin:0;margin-bottom:4px}.source_table .header{padding:10px}.source_table pre{margin:0;padding:0;white-space:normal;color:#000;font-family:"Monaco","Inconsolata","Consolas",monospace}.source_table code{color:#000;font-family:"Monaco","Inconsolata","Consolas",monospace}.source_table pre{background-color:#333}.source_table pre ol{margin:0;padding:0;margin-left:45px;font-size:12px;color:white}.source_table pre li{margin:0;padding:2px 6px;border-left:5px solid white}.source_table pre li code{white-space:pre;white-space:pre-wrap}.source_table pre .hits{float:right;margin-left:10px;padding:2px 4px;background-color:#444;background:-webkit-gradient(linear,0 0,0 bottom,from(#222),to(#666));background:-moz-linear-gradient(#222,#666);background:linear-gradient(#222,#666);color:white;font-family:Helvetica,"Helvetica Neue",Arial,sans-serif;font-size:10px;font-weight:bold;text-align:center;border-radius:6px}#footer{color:#ddd;font-size:12px;font-weight:bold;margin-top:12px;text-align:right}#footer a{color:#eee;text-decoration:underline}#footer a:hover{color:#fff;text-decoration:none}.green{color:#090}.red{color:#900}.yellow{color:#da0}.blue{color:blue}thead th{background:white}.source_table .covered{border-color:#090}.source_table .missed{border-color:#900}.source_table .never{border-color:black}.source_table .skipped{border-color:#fc0}.source_table .missed-branch{border-color:#bf0000}.source_table .covered:nth-child(odd){background-color:#cdf2cd}.source_table .covered:nth-child(even){background-color:#dbf2db}.source_table .missed:nth-child(odd){background-color:#f7c0c0}.source_table .missed:nth-child(even){background-color:#f7cfcf}.source_table .never:nth-child(odd){background-color:#efefef}.source_table .never:nth-child(even){background-color:#f4f4f4}.source_table .skipped:nth-child(odd){background-color:#fbf0c0}.source_table .skipped:nth-child(even){background-color:#fbffcf}.source_table .missed-branch:nth-child(odd){background-color:#cc8e8e}.source_table .missed-branch:nth-child(even){background-color:#cc6e6e}
\ No newline at end of file
diff --git a/coverage/assets/0.12.2/application.js b/coverage/assets/0.12.2/application.js
new file mode 100644
index 0000000000..e1c2ab2346
--- /dev/null
+++ b/coverage/assets/0.12.2/application.js
@@ -0,0 +1,7 @@
+!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(T,e){"use strict";function g(e,t,n){var r,a,i=(n=n||le).createElement("script");if(i.text=e,t)for(r in Se)(a=t[r]||t.getAttribute&&t.getAttribute(r))&&i.setAttribute(r,a);n.head.appendChild(i).parentNode.removeChild(i)}function m(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?pe[ge.call(e)]||"object":typeof e}function s(e){var t=!!e&&"length"in e&&e.length,n=m(e);return!we(e)&&!xe(e)&&("array"===n||0===t||"number"==typeof t&&0
D.cacheLength&&delete n[r.shift()],n[e+" "]=t}var r=[];return n}function l(e){return e[q]=!0,e}function a(e){var t=E.createElement("fieldset");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function t(e,t){for(var n=e.split("|"),r=n.length;r--;)D.attrHandle[n[r]]=t}function u(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function r(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function i(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function o(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&_e(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function s(o){return l(function(i){return i=+i,l(function(e,t){for(var n,r=o([],e.length,i),a=r.length;a--;)e[n=r[a]]&&(e[n]=!(t[n]=e[n]))})})}function p(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function c(){}function g(e){for(var t=0,n=e.length,r="";t+~]|"+re+")"+re+"*"),fe=new RegExp(re+"|>"),de=new RegExp(oe),he=new RegExp("^"+ae+"$"),pe={ID:new RegExp("^#("+ae+")"),CLASS:new RegExp("^\\.("+ae+")"),TAG:new RegExp("^("+ae+"|[*])"),ATTR:new RegExp("^"+ie),PSEUDO:new RegExp("^"+oe),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+re+"*(even|odd|(([+-]|)(\\d*)n|)"+re+"*(?:([+-]|)"+re+"*(\\d+)|))"+re+"*\\)|)","i"),bool:new RegExp("^(?:"+ne+")$","i"),needsContext:new RegExp("^"+re+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+re+"*((?:-\\d)?\\d*)"+re+"*\\)|)(?=[^-]|$)","i")},ge=/HTML$/i,me=/^(?:input|select|textarea|button)$/i,ve=/^h\d$/i,ye=/^[^{]+\{\s*\[native \w/,be=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,we=/[+~]/,xe=new RegExp("\\\\([\\da-f]{1,6}"+re+"?|("+re+")|.)","ig"),Se=function(e,t,n){var r="0x"+t-65536;return r!=r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},De=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,Te=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},Ce=function(){L()},_e=f(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{Q.apply(Y=ee.call(W.childNodes),W.childNodes),Y[W.childNodes.length].nodeType}catch(Ae){Q={apply:Y.length?function(e,t){K.apply(e,ee.call(t))}:function(e,t){for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}for(v in S=w.support={},C=w.isXML=function(e){var t=e.namespaceURI,n=(e.ownerDocument||e).documentElement;return!ge.test(t||n&&n.nodeName||"HTML")},L=w.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:W;return r!==E&&9===r.nodeType&&r.documentElement&&(R=(E=r).documentElement,F=!C(E),W!==E&&(n=E.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",Ce,!1):n.attachEvent&&n.attachEvent("onunload",Ce)),S.attributes=a(function(e){return e.className="i",!e.getAttribute("className")}),S.getElementsByTagName=a(function(e){return e.appendChild(E.createComment("")),!e.getElementsByTagName("*").length}),S.getElementsByClassName=ye.test(E.getElementsByClassName),S.getById=a(function(e){return R.appendChild(e).id=q,!E.getElementsByName||!E.getElementsByName(q).length}),S.getById?(D.filter.ID=function(e){var t=e.replace(xe,Se);return function(e){return e.getAttribute("id")===t}},D.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&F){var n=t.getElementById(e);return n?[n]:[]}}):(D.filter.ID=function(e){var n=e.replace(xe,Se);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},D.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&F){var n,r,a,i=t.getElementById(e);if(i){if((n=i.getAttributeNode("id"))&&n.value===e)return[i];for(a=t.getElementsByName(e),r=0;i=a[r++];)if((n=i.getAttributeNode("id"))&&n.value===e)return[i]}return[]}}),D.find.TAG=S.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):S.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],a=0,i=t.getElementsByTagName(e);if("*"!==e)return i;for(;n=i[a++];)1===n.nodeType&&r.push(n);return r},D.find.CLASS=S.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&F)return t.getElementsByClassName(e)},H=[],P=[],(S.qsa=ye.test(E.querySelectorAll))&&(a(function(e){R.appendChild(e).innerHTML=" ",e.querySelectorAll("[msallowcapture^='']").length&&P.push("[*^$]="+re+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||P.push("\\["+re+"*(?:value|"+ne+")"),e.querySelectorAll("[id~="+q+"-]").length||P.push("~="),e.querySelectorAll(":checked").length||P.push(":checked"),e.querySelectorAll("a#"+q+"+*").length||P.push(".#.+[+~]")}),a(function(e){e.innerHTML=" ";var t=E.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&P.push("name"+re+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&P.push(":enabled",":disabled"),R.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&P.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),P.push(",.*:")})),(S.matchesSelector=ye.test(M=R.matches||R.webkitMatchesSelector||R.mozMatchesSelector||R.oMatchesSelector||R.msMatchesSelector))&&a(function(e){S.disconnectedMatch=M.call(e,"*"),M.call(e,"[s!='']:x"),H.push("!=",oe)}),P=P.length&&new RegExp(P.join("|")),H=H.length&&new RegExp(H.join("|")),t=ye.test(R.compareDocumentPosition),O=t||ye.test(R.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},G=t?function(e,t){if(e===t)return j=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!S.sortDetached&&t.compareDocumentPosition(e)===n?e===E||e.ownerDocument===W&&O(W,e)?-1:t===E||t.ownerDocument===W&&O(W,t)?1:I?te(I,e)-te(I,t):0:4&n?-1:1)}:function(e,t){if(e===t)return j=!0,0;var n,r=0,a=e.parentNode,i=t.parentNode,o=[e],s=[t];if(!a||!i)return e===E?-1:t===E?1:a?-1:i?1:I?te(I,e)-te(I,t):0;if(a===i)return u(e,t);for(n=e;n=n.parentNode;)o.unshift(n);for(n=t;n=n.parentNode;)s.unshift(n);for(;o[r]===s[r];)r++;return r?u(o[r],s[r]):o[r]===W?-1:s[r]===W?1:0}),E},w.matches=function(e,t){return w(e,null,null,t)},w.matchesSelector=function(e,t){if((e.ownerDocument||e)!==E&&L(e),S.matchesSelector&&F&&!V[t+" "]&&(!H||!H.test(t))&&(!P||!P.test(t)))try{var n=M.call(e,t);if(n||S.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(Ae){V(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(xe,Se),e[3]=(e[3]||e[4]||e[5]||"").replace(xe,Se),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||w.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&w.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return pe.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&de.test(n)&&(t=_(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(xe,Se).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=U[e+" "];return t||(t=new RegExp("(^|"+re+")"+e+"("+re+"|$)"))&&U(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,a){return function(e){var t=w.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===a:"!="===r?t!==a:"^="===r?a&&0===t.indexOf(a):"*="===r?a&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;Te.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?Te.find.matchesSelector(r,e)?[r]:[]:Te.find.matches(e,Te.grep(t,function(e){return 1===e.nodeType}))},Te.fn.extend({find:function(e){var t,n,r=this.length,a=this;if("string"!=typeof e)return this.pushStack(Te(e).filter(function(){for(t=0;t)[^>]*|#([\w-]+))$/;(Te.fn.init=function(e,t,n){var r,a;if(!e)return this;if(n=n||je,"string"!=typeof e)return e.nodeType?(this[0]=e,this.length=1,this):we(e)?n.ready!==undefined?n.ready(e):e(Te):Te.makeArray(e,this);if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:Le.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof Te?t[0]:t,Te.merge(this,Te.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:le,!0)),Ie.test(r[1])&&Te.isPlainObject(t))for(r in t)we(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(a=le.getElementById(r[2]))&&(this[0]=a,this.length=1),this}).prototype=Te.fn,je=Te(le);var Ee=/^(?:parents|prev(?:Until|All))/,Re={children:!0,contents:!0,next:!0,prev:!0};Te.fn.extend({has:function(e){var t=Te(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,rt=/^$|^module$|\/(?:java|ecma)script/i,at={option:[1,""," "],thead:[1,""],col:[2,""],tr:[2,""],td:[3,""],_default:[0,"",""]};at.optgroup=at.option,at.tbody=at.tfoot=at.colgroup=at.caption=at.thead,at.th=at.td;var it,ot,st=/<|?\w+;/;it=le.createDocumentFragment().appendChild(le.createElement("div")),(ot=le.createElement("input")).setAttribute("type","radio"),ot.setAttribute("checked","checked"),ot.setAttribute("name","t"),it.appendChild(ot),be.checkClone=it.cloneNode(!0).cloneNode(!0).lastChild.checked,it.innerHTML="",be.noCloneChecked=!!it.cloneNode(!0).lastChild.defaultValue;var lt=/^key/,ut=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,ct=/^([^.]*)(?:\.(.+)|)/;Te.event={global:{},add:function(t,e,n,r,a){var i,o,s,l,u,c,f,d,h,p,g,m=Be.get(t);if(m)for(n.handler&&(n=(i=n).handler,a=i.selector),a&&Te.find.matchesSelector(Je,a),n.guid||(n.guid=Te.guid++),(l=m.events)||(l=m.events={}),(o=m.handle)||(o=m.handle=function(e){return void 0!==Te&&Te.event.triggered!==e.type?Te.event.dispatch.apply(t,arguments):undefined}),u=(e=(e||"").match(Fe)||[""]).length;u--;)h=g=(s=ct.exec(e[u])||[])[1],p=(s[2]||"").split(".").sort(),h&&(f=Te.event.special[h]||{},h=(a?f.delegateType:f.bindType)||h,f=Te.event.special[h]||{},c=Te.extend({type:h,origType:g,data:r,handler:n,guid:n.guid,selector:a,needsContext:a&&Te.expr.match.needsContext.test(a),namespace:p.join(".")},i),(d=l[h])||((d=l[h]=[]).delegateCount=0,f.setup&&!1!==f.setup.call(t,r,p,o)||t.addEventListener&&t.addEventListener(h,o)),f.add&&(f.add.call(t,c),c.handler.guid||(c.handler.guid=n.guid)),a?d.splice(d.delegateCount++,0,c):d.push(c),Te.event.global[h]=!0)},remove:function(e,t,n,r,a){var i,o,s,l,u,c,f,d,h,p,g,m=Be.hasData(e)&&Be.get(e);if(m&&(l=m.events)){for(u=(t=(t||"").match(Fe)||[""]).length;u--;)if(h=g=(s=ct.exec(t[u])||[])[1],p=(s[2]||"").split(".").sort(),h){for(f=Te.event.special[h]||{},d=l[h=(r?f.delegateType:f.bindType)||h]||[],s=s[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),o=i=d.length;i--;)c=d[i],!a&&g!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(d.splice(i,1),c.selector&&d.delegateCount--,f.remove&&f.remove.call(e,c));o&&!d.length&&(f.teardown&&!1!==f.teardown.call(e,p,m.handle)||Te.removeEvent(e,h,m.handle),delete l[h])}else for(h in l)Te.event.remove(e,h+t[u],n,r,!0);Te.isEmptyObject(l)&&Be.remove(e,"handle events")}},dispatch:function(e){var t,n,r,a,i,o,s=Te.event.fix(e),l=new Array(arguments.length),u=(Be.get(this,"events")||{})[s.type]||[],c=Te.event.special[s.type]||{};for(l[0]=s,t=1;t\x20\t\r\n\f]*)[^>]*)\/>/gi,dt=/
+
+
+
+
+
+
+
+
+
+
+
Generated
2020-06-19T10:02:25-07:00
+
+
+
+
+
+ All Files
+ (
+
+ 90.05%
+
+
+
+ covered at
+
+
+ 3.44
+
+ hits/line
+ )
+
+
+
+
+
+ 38 files in total.
+
+
+
+ 432 relevant lines,
+ 389 lines covered and
+ 43 lines missed.
+ (
+ 90.05%
+
+)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class ApplicationController < ActionController::Base
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :set_users
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+ 1
+
+
+
+
+ def set_users
+
+
+
+
+
+ 94
+
+
+
+
+ @users = User.all
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :find_user
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def render_404
+
+
+
+
+
+ 2
+
+
+
+
+ render :file => "#{Rails.root}/public/404.html", layout: false, status: :not_found
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def find_user
+
+
+
+
+
+ 94
+
+
+
+
+ if session[:user_id]
+
+
+
+
+
+ 24
+
+
+
+
+ @login_user = User.find_by(id: session[:user_id])
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def current_user
+
+
+
+
+
+ 4
+
+
+
+
+ return User.find_by(id: session[:user_id]) if session[:user_id]
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def require_login
+
+
+
+
+
+ 4
+
+
+
+
+ if current_user.nil?
+
+
+
+
+
+ 2
+
+
+
+
+ flash[:error] = "You must be logged in to do that"
+
+
+
+
+
+ 2
+
+
+
+
+ redirect_back(fallback_location: frontpage_path)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class CategoriesController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ helper_method :render_404, :require_login
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ around_action :require_login, only: [:new, :update], if: -> { !@login_user }
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :find_category, only: [:show, :edit, :update, :destroy]
+
+
+
+
+
+ 4
+
+
+
+
+ around_action :render_404, only: [:show, :edit, :update, :destroy], if: -> { @category.nil? }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def index
+
+
+
+
+
+ 2
+
+
+
+
+ @categories = Category.all.order(:name)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def show
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def new
+
+
+
+
+
+ 1
+
+
+
+
+ @category = Category.new
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 3
+
+
+
+
+ @category = Category.new(category_params)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ if @category.save
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "#{@category.name} was successfully added! 😄"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to categories_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ flash.now[:error] = "A problem occurred: Could not update '#{@category.name}'"
+
+
+
+
+
+ 2
+
+
+
+
+ render :new
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def update
+
+
+
+
+
+ 3
+
+
+
+
+ if @category.update(category_params)
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "#{@category.name} was successfully edited! 😄"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to category_path(@category.id)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ flash.now[:error] = "The category was not successfully edited :("
+
+
+
+
+
+ 2
+
+
+
+
+ render :edit
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def category_params
+
+
+
+
+
+ 6
+
+
+
+
+ return params.require(:category).permit(:name)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def find_category
+
+
+
+
+
+ 3
+
+
+
+
+ category_id = params[:id]
+
+
+
+
+
+ 3
+
+
+
+
+ @category = Category.find_by(id: category_id)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class OrderitemsController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :find_order_item, only: [:create]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def index
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 5
+
+
+
+
+ if !session[:cart]
+
+
+
+
+
+ 5
+
+
+
+
+ session[:cart] = Array.new
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ product = Product.find_by(id: params["product_id"])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ quantity = params["quantity"].to_i
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ session[:cart].each do |item|
+
+
+
+
+
+
+
+
+
+
+ if item['product_id'] == product.id
+
+
+
+
+
+
+
+
+
+
+ if item['quantity'] < product.inventory
+
+
+
+
+
+
+
+
+
+
+ item["quantity"] += 1
+
+
+
+
+
+
+
+
+
+
+ flash[:success] = "Item added to shopping cart."
+
+
+
+
+
+
+
+
+
+
+ redirect_to cart_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ elsif item['quantity'] == product.inventory
+
+
+
+
+
+
+
+
+
+
+ flash[:error] = "There is no inventory left. Item cannot be added to cart."
+
+
+
+
+
+
+
+
+
+
+ redirect_to cart_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ @orderitem = OrderItem.new(
+
+
+
+
+
+
+
+
+
+
+ quantity: quantity,
+
+
+
+
+
+
+
+
+
+
+ product_id: product.id,
+
+
+
+
+
+
+
+
+
+
+ )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 5
+
+
+
+
+ if @orderitem.save
+
+
+
+
+
+ 4
+
+
+
+
+ session[:cart] << @orderitem
+
+
+
+
+
+ 4
+
+
+
+
+ flash[:success] = "#{product.name} was successfully added to shopping cart! 😄"
+
+
+
+
+
+ 4
+
+
+
+
+ redirect_to product_path(product)
+
+
+
+
+
+ 4
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 1
+
+
+
+
+ flash.now[:error] = "A problem occurred: Could not update #{@orderitem.name} - : #{@orderitem.errors.messages}"
+
+
+
+
+
+
+
+
+
+
+ render :new, status: :bad_request
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ flash[:error] = "There is no inventory left. Item cannot be added to cart."
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ redirect_to product_path(product)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def increase_quantity
+
+
+
+
+
+ 2
+
+
+
+
+ return "You have nothing in your cart. :( " if !session[:cart]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ product = Product.find_by(id: params["format"]).inventory
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ session[:cart].each do |item|
+
+
+
+
+
+ 2
+
+
+
+
+ if item["product_id"] == params['format'].to_i && item['quantity'] < product
+
+
+
+
+
+ 1
+
+
+
+
+ item["quantity"] += 1
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "Item added to shopping cart."
+
+
+
+
+
+ 1
+
+
+
+
+ elsif item["product_id"] == params['format'].to_i && item['quantity'] == product
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:error] = "WARNING: There is no inventory left. No additional items can be added to cart."
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ fallback_location = orderitems_path
+
+
+
+
+
+ 2
+
+
+
+
+ redirect_back(fallback_location: fallback_location)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def decrease_quantity
+
+
+
+
+
+ 1
+
+
+
+
+ return "You have nothing in your cart. :( " if !session[:cart]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ session[:cart].each do |item|
+
+
+
+
+
+ 1
+
+
+
+
+ if item["product_id"] == params['format'].to_i
+
+
+
+
+
+ 1
+
+
+
+
+ item["quantity"] > 1 ? item["quantity"] -= 1 : session[:cart].delete(item)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ if item["quantity"] == 0
+
+
+
+
+
+
+
+
+
+
+ flash[:error] = "Cart error. Quantity cannot fall below 1."
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "Item removed from shopping cart."
+
+
+
+
+
+ 1
+
+
+
+
+ fallback_location = orderitems_path
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_back(fallback_location: fallback_location)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def find_order_item
+
+
+
+
+
+ 5
+
+
+
+
+ @order_item = OrderItem.find_by(id: params[:id])
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class OrdersController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :require_login, only: [:ordered, :index]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def index
+
+
+
+
+
+
+
+
+
+
+ @merchant_orders = []
+
+
+
+
+
+
+
+
+
+
+ @merchant_products = []
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Order.all.each do |order|
+
+
+
+
+
+
+
+
+
+
+ order.order_items.each do |item|
+
+
+
+
+
+
+
+
+
+
+ merchant_product = Product.find_by(id: item.product_id)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if session[:user_id] == merchant_product.user_id
+
+
+
+
+
+
+
+
+
+
+ if !@merchant_orders.include?(order)
+
+
+
+
+
+
+
+
+
+
+ @merchant_orders << order
+
+
+
+
+
+
+
+
+
+
+ @merchant_products << merchant_product
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def new
+
+
+
+
+
+ 1
+
+
+
+
+ @order = Order.new
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 2
+
+
+
+
+ @order = Order.new(order_params)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ if @order.save
+
+
+
+
+
+
+
+
+
+
+ session[:cart].each do |item|
+
+
+
+
+
+
+
+
+
+
+ ordered_item = OrderItem.find_by(id: item['id'])
+
+
+
+
+
+
+
+
+
+
+ product = Product.find_by(id: item['product_id'])
+
+
+
+
+
+
+
+
+
+
+ product.remove_inventory(item['quantity'])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ordered_item.order_id = @order.id
+
+
+
+
+
+
+
+
+
+
+ @order.order_items << ordered_item
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ session[:cart] = []
+
+
+
+
+
+
+
+
+
+
+ flash[:success] = "Your order was successfully placed!"
+
+
+
+
+
+
+
+
+
+
+ redirect_to order_path(@order)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ flash.now[:error] = "A problem occurred: Could not update #{@order.name} - : #{@order.errors.messages}"
+
+
+
+
+
+ 2
+
+
+
+
+ render :new, status: :bad_request
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def show
+
+
+
+
+
+ 2
+
+
+
+
+ @order = Order.find_by(id: params['id'])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ if @order.nil?
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_back(fallback_location: root_path)
+
+
+
+
+
+ 1
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ if @order.status == "pending"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to cart_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def mark_shipped
+
+
+
+
+
+ 1
+
+
+
+
+ order = Order.find_by(id: params['format'])
+
+
+
+
+
+ 1
+
+
+
+
+ order.mark_shipped
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ if order.status == 'shipped'
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "Order was successfully shipped! 😄"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to orders_path
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def ordered
+
+
+
+
+
+
+
+
+
+
+ @order = Order.find_by(id: params['id'])
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def order_params
+
+
+
+
+
+ 2
+
+
+
+
+ return params.require(:order).permit(:status, :name, :email_address, :address, :city, :state, :zipcode, :cc_num, :cc_exp_month, :cc_exp_year, :cc_cvv, :order_date, :user_id)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class PagesController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ def home
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def frontpage
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class ProductsController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ helper_method :current_user, :render_404, :require_login
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :find_product, only: [:show, :edit, :update, :destroy, :toggle_status]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+
+ around_action :require_login, only: [:new, :create, :edit, :update, :destroy, :toggle_status], if: -> { !@login_user }
+
+
+
+
+
+ 10
+
+
+
+
+ around_action :render_404, only: [:show, :edit, :update, :destroy, :toggle_status], if: -> { @product.nil? }
+
+
+
+
+
+ 8
+
+
+
+
+ around_action :check_authorization, only: [:edit, :update, :destroy, :toggle_status], if: -> { @login_user && @login_user != @product.user }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def index
+
+
+
+
+
+ 2
+
+
+
+
+ @products = Product.where(active: true).order(:name).paginate(:page=>params[:page],:per_page=>15)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def show
+
+
+
+
+
+ 1
+
+
+
+
+ @product.inactivate_product
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def new
+
+
+
+
+
+ 1
+
+
+
+
+ @product = Product.new
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 2
+
+
+
+
+ @product = Product.new(product_params)
+
+
+
+
+
+ 2
+
+
+
+
+ @product.user_id = @login_user.id
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ if @product.save
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "#{@product.name} was successfully added! 😄"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to product_path(@product)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 1
+
+
+
+
+ flash.now[:error] = "A problem occurred: Could not update #{@product.name} - : #{@product.errors.messages}"
+
+
+
+
+
+ 1
+
+
+
+
+ render :new, status: :bad_request
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def edit
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def update
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ if @product.update(product_params)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ @product.inactivate_product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "#{@product.name} was successfully edited! 😄"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to dashboard_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ flash.now[:error] = "The product was not successfully edited :( - #{@product.errors.messages}"
+
+
+
+
+
+ 2
+
+
+
+
+ render :edit, status: :bad_request
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def toggle_status
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ if @product.change_status
+
+
+
+
+
+ 2
+
+
+
+
+ flash[:success] = "#{@product.name}'s status was successfully updated! 😄"
+
+
+
+
+
+ 2
+
+
+
+
+ redirect_to dashboard_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def search
+
+
+
+
+
+ 4
+
+
+
+
+ if params[:search].blank?
+
+
+
+
+
+ 2
+
+
+
+
+ flash[:error] = "Empty field!"
+
+
+
+
+
+ 2
+
+
+
+
+ redirect_to(frontpage_path)
+
+
+
+
+
+ 2
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ @parameter = params[:search].downcase
+
+
+
+
+
+ 2
+
+
+
+
+ @products = Product.where("lower(name) LIKE ? ", "%#{@parameter}%")
+
+
+
+
+
+ 2
+
+
+
+
+ @categories = Category.where("lower(name) LIKE ? ", "%#{@parameter}%")
+
+
+
+
+
+ 2
+
+
+
+
+ @users = User.where("lower(name) LIKE ? ", "%#{@parameter}%")
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def product_params
+
+
+
+
+
+ 5
+
+
+
+
+ return params.require(:product).permit(:name, :description, :inventory, :price, :photo_url, :active, category_ids: [])
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def find_product
+
+
+
+
+
+ 9
+
+
+
+
+ product_id = params[:id]
+
+
+
+
+
+ 9
+
+
+
+
+ @product = Product.find_by(id: product_id)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def check_authorization
+
+
+
+
+
+ 1
+
+
+
+
+ if @login_user && @login_user != @product.user
+
+
+
+
+
+ 1
+
+
+
+
+ flash.now[:error] = "A problem occurred: You are not authorized to perform this action. This is not your product."
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_to dashboard_path
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class ReviewsController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ FIELDS = [:rating, :description, :product_id, :reviewer]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # TODO
+
+
+
+
+
+ 1
+
+
+
+
+ def new
+
+
+
+
+
+ 2
+
+
+
+
+ @review = Review.new
+
+
+
+
+
+ 2
+
+
+
+
+ @product = find_product
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 4
+
+
+
+
+ @product = find_product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4
+
+
+
+
+ if @login_user && (@login_user.id == @product.user_id)
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:error] = "A problem occurred: Cannot add a review for your own product!"
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_back(fallback_location: root_path)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ if @login_user
+
+
+
+
+
+ 1
+
+
+
+
+ @review = Review.new(review_params)
+
+
+
+
+
+ 1
+
+
+
+
+ @review.reviewer = @login_user.name
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ @review = Review.new(review_params)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ if @review.save
+
+
+
+
+
+ 2
+
+
+
+
+ flash[:success] = "The review was successfully added! 😄"
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 1
+
+
+
+
+ messages = []
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ FIELDS.each do |field|
+
+
+
+
+
+ 4
+
+
+
+
+ if @review.errors.messages.include?(field)
+
+
+
+
+
+ 1
+
+
+
+
+ messages << "'#{field.capitalize}' - #{@review.errors.messages[field].join(", ")}"
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:error] = "A problem occurred: Could not update the review \n\n#{messages.join(", ")}"
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 3
+
+
+
+
+ redirect_back(fallback_location: root_path)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ private
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def review_params
+
+
+
+
+
+ 3
+
+
+
+
+ return params.permit(*FIELDS)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def find_product
+
+
+
+
+
+ 6
+
+
+
+
+ return Product.find_by(id: params[:product_id])
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class UsersController < ApplicationController
+
+
+
+
+
+ 1
+
+
+
+
+ helper_method :render_404
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ before_action :require_login, only: [:destroy, :dashboard]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def index
+
+
+
+
+
+ 2
+
+
+
+
+ @users = User.all.order(:name).paginate(:page=>params[:page],:per_page=>15)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def show
+
+
+
+
+
+ 2
+
+
+
+
+ @user = User.find_by(id: params['id'])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ if @user.nil?
+
+
+
+
+
+ 1
+
+
+
+
+ return render_404
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def create
+
+
+
+
+
+ 37
+
+
+
+
+ auth_hash = request.env["omniauth.auth"]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 37
+
+
+
+
+ user = User.find_by(uid: auth_hash[:uid], provider: 'github')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 37
+
+
+
+
+ if user
+
+
+
+
+
+ 25
+
+
+
+
+ flash[:success] = "Logged in as returning user #{user.name}"
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+
+ user = User.build_from_github(auth_hash)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+
+ if user.save
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "Logged in as new user #{user.name}"
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 11
+
+
+
+
+ flash[:error] = "Could not create new user account: #{user.errors.messages}"
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 37
+
+
+
+
+ session[:user_id] = user.id
+
+
+
+
+
+ 37
+
+
+
+
+ redirect_back(fallback_location: frontpage_path)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def destroy
+
+
+
+
+
+ 1
+
+
+
+
+ session[:user_id] = nil
+
+
+
+
+
+ 1
+
+
+
+
+ flash[:success] = "Successfully logged out!"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ redirect_back(fallback_location: frontpage_path)
+
+
+
+
+
+
+
+
+
+
+ return
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def dashboard
+
+
+
+
+
+ 1
+
+
+
+
+ @user = User.find_by(id: session[:user_id])
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ @revenue = 0
+
+
+
+
+
+ 1
+
+
+
+
+ @paid_revenue = 0
+
+
+
+
+
+ 1
+
+
+
+
+ @paid_count = 0
+
+
+
+
+
+ 1
+
+
+
+
+ @shipped_revenue = 0
+
+
+
+
+
+ 1
+
+
+
+
+ @shipped_count = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ Order.all.each do |order|
+
+
+
+
+
+ 2
+
+
+
+
+ order.order_items.each do |item|
+
+
+
+
+
+
+
+
+
+
+ merchant_product = Product.find_by(id: item.product_id)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if @user.id == merchant_product.user_id
+
+
+
+
+
+
+
+
+
+
+ @revenue += (item['quantity'] * merchant_product.price)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ if order.status == 'shipped'
+
+
+
+
+
+
+
+
+
+
+ @shipped_revenue += item['quantity'] * merchant_product.price
+
+
+
+
+
+
+
+
+
+
+ @shipped_count += 1
+
+
+
+
+
+
+
+
+
+
+ elsif order.status == 'paid'
+
+
+
+
+
+
+
+
+
+
+ @paid_revenue += item['quantity'] * merchant_product.price
+
+
+
+
+
+
+
+
+
+
+ @paid_count += 1
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module ApplicationHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module CategoriesHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module OrderitemsHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module OrdersHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module PagesHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module ProductsHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module ReviewsHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ module UsersHelper
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class ApplicationRecord < ActiveRecord::Base
+
+
+
+
+
+ 1
+
+
+
+
+ self.abstract_class = true
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class Category < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ validates :name, uniqueness: true, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ has_and_belongs_to_many :products, dependent: :nullify
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.search_result(parameter)
+
+
+
+
+
+ 6
+
+
+
+
+ categories = self.where("lower(name) LIKE ? ", "%#{parameter}%")
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 12
+
+
+
+
+ return categories.reduce(0) { |sum, category| sum + category.products.length }
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ require 'date'
+
+
+
+
+
+ 1
+
+
+
+
+ require_relative '../validators/order_validator'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class Order < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ include ActiveModel::Validations
+
+
+
+
+
+ 1
+
+
+
+
+ validates_with OrderValidator
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :order_items, dependent: :destroy
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :products, through: :order_items
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ validates :status, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :name, presence: {message: "Name can't be blank"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :email_address, format: {with: /\A.+@.+\..{2,3}\z/, message: "E-mail address must be valid"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :address, presence: {message: "Address can't be blank"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :city, presence: {message: "City can't be blank"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :state, format: {with: /\A[a-zA-Z]{2}\z/, message: "State must be two letters in length"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :zipcode, format: {with: /\A\d{5}\z/, message: "Zip code must be 5 digits"}, :on => :update
+
+
+
+
+
+ 1
+
+
+
+
+ validates :cc_cvv, format: {with: /\A\d{3,4}\z/, message: "Credit card CVV must be 3-4 numbers in length"}, :on => :update
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def total
+
+
+
+
+
+ 3
+
+
+
+
+ return 0 if self.order_items.length == 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ order_total = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ self.order_items.each do |item|
+
+
+
+
+
+ 3
+
+
+
+
+ p = Product.find_by(id: item["product_id"])
+
+
+
+
+
+ 3
+
+
+
+
+ order_total += (p.price * item.quantity)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 2
+
+
+
+
+ return order_total
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.get_items(merchant_products)
+
+
+
+
+
+ 1
+
+
+
+
+ merchant_products.each do |product|
+
+
+
+
+
+ 1
+
+
+
+
+ return product.name
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def mark_shipped
+
+
+
+
+
+ 2
+
+
+
+
+ self.update!(status: 'shipped')
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class OrderItem < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ validates :product_id, presence: true, numericality: {only_integer: true, greater_than: 0}
+
+
+
+
+
+ 1
+
+
+
+
+ validates :quantity, presence: true, numericality: { only_integer: true, greater_than: 0 }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.cart_count(session)
+
+
+
+
+
+ 1
+
+
+
+
+ count = 0
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ session[:cart].each do |product|
+
+
+
+
+
+ 1
+
+
+
+
+ count += product["quantity"]
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ return count
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class Product < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ validates :name, presence: true, uniqueness: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :price, presence: true, numericality: {only_integer: true, greater_than: 0}
+
+
+
+
+
+ 1
+
+
+
+
+ validates :inventory, numericality: {only_integer: true, greater_than: -1}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ belongs_to :user
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :reviews, dependent: :destroy
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :order_items, dependent: :destroy
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :orders, :through => :order_items
+
+
+
+
+
+ 1
+
+
+
+
+ has_and_belongs_to_many :categories, dependent: :destroy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def change_status
+
+
+
+
+
+ 4
+
+
+
+
+ if self.active
+
+
+
+
+
+ 2
+
+
+
+
+ self.update!(active: false)
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 2
+
+
+
+
+ self.update!(active: true)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def num_of_ratings
+
+
+
+
+
+ 36
+
+
+
+
+ return self.reviews.length
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def average_rating
+
+
+
+
+
+ 47
+
+
+
+
+ all_reviews = self.reviews
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 47
+
+
+
+
+ if all_reviews.empty?
+
+
+
+
+
+ 31
+
+
+
+
+ return 0
+
+
+
+
+
+
+
+
+
+
+ else
+
+
+
+
+
+ 53
+
+
+
+
+ ratings = all_reviews.sum { |review| review.rating }
+
+
+
+
+
+ 16
+
+
+
+
+ avg_rating = (ratings / self.num_of_ratings.to_f)
+
+
+
+
+
+ 16
+
+
+
+
+ return avg_rating.round(1)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def display_categories
+
+
+
+
+
+ 19
+
+
+
+
+ return self.categories.map { |category| category.name }.join(", ")
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.top_rated_products
+
+
+
+
+
+ 1
+
+
+
+
+ result = self.all.sort_by { |product|
+
+
+
+
+
+ 10
+
+
+
+
+ product.average_rating
+
+
+
+
+
+
+
+
+
+
+ }.reverse!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ return result.first(4)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.popular_products
+
+
+
+
+
+ 1
+
+
+
+
+ result = self.all.sort_by { |product|
+
+
+
+
+
+ 10
+
+
+
+
+ product.num_of_ratings
+
+
+
+
+
+
+
+
+
+
+ }.reverse!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ return result.first(4)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def inactivate_product
+
+
+
+
+
+ 3
+
+
+
+
+ if self.inventory == 0
+
+
+
+
+
+ 1
+
+
+
+
+ self.update(active: false)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def remove_inventory(quantity)
+
+
+
+
+
+ 1
+
+
+
+
+ self.update!(inventory: (self.inventory - quantity))
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class Review < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ belongs_to :product
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ validates :reviewer, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :product_id, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :description, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :rating, presence: true, numericality: {only_integer: true, greater_than: 0, less_than: 6}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.display_rating(rating)
+
+
+
+
+
+ 18
+
+
+
+
+ return "☆" * 5 if rating == 0
+
+
+
+
+
+ 7
+
+
+
+
+ empty_star_count = 5 - rating.to_i
+
+
+
+
+
+ 7
+
+
+
+
+ return ("★" * rating) + ("☆" * empty_star_count)
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class User < ApplicationRecord
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :products
+
+
+
+
+
+ 1
+
+
+
+
+ has_many :orders
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ validates :name, uniqueness: true, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :email_address, uniqueness: true, presence: true
+
+
+
+
+
+ 1
+
+
+
+
+ validates :uid, uniqueness: true, presence: true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ def self.build_from_github(auth_hash)
+
+
+
+
+
+ 13
+
+
+
+
+ user = User.new
+
+
+
+
+
+ 13
+
+
+
+
+ user.uid = auth_hash[:uid]
+
+
+
+
+
+ 13
+
+
+
+
+ user.provider = "github"
+
+
+
+
+
+ 13
+
+
+
+
+ user.username = auth_hash["info"]["name"]
+
+
+
+
+
+ 13
+
+
+
+
+ user.name = auth_hash["info"]["nickname"]
+
+
+
+
+
+ 13
+
+
+
+
+ user.email_address = auth_hash["info"]["email"]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 13
+
+
+
+
+ return user
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ class OrderValidator < ActiveModel::Validator
+
+
+
+
+
+ 1
+
+
+
+
+ def validate(record)
+
+
+
+
+
+ 42
+
+
+
+
+ return true
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Load the Rails application.
+
+
+
+
+
+ 1
+
+
+
+
+ require_relative 'application'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Initialize the Rails application.
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.initialize!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # The test environment is used exclusively to run your application's
+
+
+
+
+
+
+
+
+
+
+ # test suite. You never need to work with it otherwise. Remember that
+
+
+
+
+
+
+
+
+
+
+ # your test database is "scratch space" for the test suite and is wiped
+
+
+
+
+
+
+
+
+
+
+ # and recreated between test runs. Don't rely on the data there!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.configure do
+
+
+
+
+
+
+
+
+
+
+ # Settings specified here will take precedence over those in config/application.rb.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ config.cache_classes = false
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_view.cache_template_loading = true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Do not eager load code on boot. This avoids loading your whole application
+
+
+
+
+
+
+
+
+
+
+ # just for the purpose of running a single test. If you are using a tool that
+
+
+
+
+
+
+
+
+
+
+ # preloads Rails for running tests, you may have to set it to true.
+
+
+
+
+
+ 1
+
+
+
+
+ config.eager_load = false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Configure public file server for tests with Cache-Control for performance.
+
+
+
+
+
+ 1
+
+
+
+
+ config.public_file_server.enabled = true
+
+
+
+
+
+ 1
+
+
+
+
+ config.public_file_server.headers = {
+
+
+
+
+
+
+
+
+
+
+ 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Show full error reports and disable caching.
+
+
+
+
+
+ 1
+
+
+
+
+ config.consider_all_requests_local = true
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_controller.perform_caching = false
+
+
+
+
+
+ 1
+
+
+
+
+ config.cache_store = :null_store
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Raise exceptions instead of rendering exception templates.
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_dispatch.show_exceptions = false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Disable request forgery protection in test environment.
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_controller.allow_forgery_protection = false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Store uploaded files on the local file system in a temporary directory.
+
+
+
+
+
+ 1
+
+
+
+
+ config.active_storage.service = :test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_mailer.perform_caching = false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Tell Action Mailer not to deliver emails to the real world.
+
+
+
+
+
+
+
+
+
+
+ # The :test delivery method accumulates sent emails in the
+
+
+
+
+
+
+
+
+
+
+ # ActionMailer::Base.deliveries array.
+
+
+
+
+
+ 1
+
+
+
+
+ config.action_mailer.delivery_method = :test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Print deprecation notices to the stderr.
+
+
+
+
+
+ 1
+
+
+
+
+ config.active_support.deprecation = :stderr
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Raises error for missing translations.
+
+
+
+
+
+
+
+
+
+
+ # config.action_view.raise_on_missing_translations = true
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.action_view.form_with_generates_remote_forms = false
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # ActiveSupport::Reloader.to_prepare do
+
+
+
+
+
+
+
+
+
+
+ # ApplicationController.renderer.defaults.merge!(
+
+
+
+
+
+
+
+
+
+
+ # http_host: 'example.org',
+
+
+
+
+
+
+
+
+
+
+ # https: false
+
+
+
+
+
+
+
+
+
+
+ # )
+
+
+
+
+
+
+
+
+
+
+ # end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Version of your assets, change this if you want to expire all your assets.
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.assets.version = '1.0'
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Add additional assets to the asset load path.
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.assets.paths << Emoji.images_path
+
+
+
+
+
+
+
+
+
+
+ # Add Yarn node_modules folder to the asset load path.
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.assets.paths << Rails.root.join('node_modules')
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Precompile additional assets.
+
+
+
+
+
+
+
+
+
+
+ # application.js, application.css, and all non-JS/CSS in the app/assets
+
+
+
+
+
+
+
+
+
+
+ # folder are already added.
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.assets.precompile += %w( admin.js admin.css )
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
+
+
+
+
+
+
+
+
+
+
+ # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
+
+
+
+
+
+
+
+
+
+
+ # Rails.backtrace_cleaner.remove_silencers!
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Define an application-wide content security policy
+
+
+
+
+
+
+
+
+
+
+ # For further information see the following documentation
+
+
+
+
+
+
+
+
+
+
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.content_security_policy do |policy|
+
+
+
+
+
+
+
+
+
+
+ # policy.default_src :self, :https
+
+
+
+
+
+
+
+
+
+
+ # policy.font_src :self, :https, :data
+
+
+
+
+
+
+
+
+
+
+ # policy.img_src :self, :https, :data
+
+
+
+
+
+
+
+
+
+
+ # policy.object_src :none
+
+
+
+
+
+
+
+
+
+
+ # policy.script_src :self, :https
+
+
+
+
+
+
+
+
+
+
+ # policy.style_src :self, :https
+
+
+
+
+
+
+
+
+
+
+ # # If you are using webpack-dev-server then specify webpack-dev-server host
+
+
+
+
+
+
+
+
+
+
+ # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # # Specify URI for violation reports
+
+
+
+
+
+
+
+
+
+
+ # # policy.report_uri "/csp-violation-report-endpoint"
+
+
+
+
+
+
+
+
+
+
+ # end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # If you are using UJS then enable automatic nonce generation
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Set the nonce only to specific directives
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Report CSP violations to a specified URI
+
+
+
+
+
+
+
+
+
+
+ # For further information see the following documentation:
+
+
+
+
+
+
+
+
+
+
+ # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
+
+
+
+
+
+
+
+
+
+
+ # Rails.application.config.content_security_policy_report_only = true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Specify a serializer for the signed and encrypted cookie jars.
+
+
+
+
+
+
+
+
+
+
+ # Valid options are :json, :marshal, and :hybrid.
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.action_dispatch.cookies_serializer = :json
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Configure sensitive parameters which will be filtered from the log file.
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.filter_parameters += [:password]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Add new inflection rules using the following format. Inflections
+
+
+
+
+
+
+
+
+
+
+ # are locale specific, and you may define rules for as many different
+
+
+
+
+
+
+
+
+
+
+ # locales as you wish. All of these examples are active by default:
+
+
+
+
+
+
+
+
+
+
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
+
+
+
+
+
+
+
+
+
+
+ # inflect.plural /^(ox)$/i, '\1en'
+
+
+
+
+
+
+
+
+
+
+ # inflect.singular /^(ox)en/i, '\1'
+
+
+
+
+
+
+
+
+
+
+ # inflect.irregular 'person', 'people'
+
+
+
+
+
+
+
+
+
+
+ # inflect.uncountable %w( fish sheep )
+
+
+
+
+
+
+
+
+
+
+ # end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # These inflection rules are supported but not enabled by default:
+
+
+
+
+
+
+
+
+
+
+ # ActiveSupport::Inflector.inflections(:en) do |inflect|
+
+
+
+
+
+
+
+
+
+
+ # inflect.acronym 'RESTful'
+
+
+
+
+
+
+
+
+
+
+ # end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Add new mime types for use in respond_to blocks:
+
+
+
+
+
+
+
+
+
+
+ # Mime::Type.register "text/richtext", :rtf
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.config.middleware.use OmniAuth::Builder do
+
+
+
+
+
+ 1
+
+
+
+
+ provider :github, ENV["GITHUB_CLIENT_ID"], ENV["GITHUB_CLIENT_SECRET"], scope: "user:email"
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Be sure to restart your server when you modify this file.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # This file contains settings for ActionController::ParamsWrapper which
+
+
+
+
+
+
+
+
+
+
+ # is enabled by default.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
+
+
+
+
+
+ 1
+
+
+
+
+ ActiveSupport.on_load(:action_controller) do
+
+
+
+
+
+ 2
+
+
+
+
+ wrap_parameters format: [:json]
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # To enable root element in JSON for ActiveRecord objects.
+
+
+
+
+
+
+
+
+
+
+ # ActiveSupport.on_load(:active_record) do
+
+
+
+
+
+
+
+
+
+
+ # self.include_root_in_json = true
+
+
+
+
+
+
+
+
+
+
+ # end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ Rails.application.routes.draw do
+
+
+
+
+
+ 1
+
+
+
+
+ get 'pages/home'
+
+
+
+
+
+
+
+
+
+
+ # For details on the DSL available within this file, see https://guides.rubyonrails.org/routing.html
+
+
+
+
+
+ 1
+
+
+
+
+ resources :users, except: [:update, :new, :edit, :update]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ get "/auth/github", as: "github_login"
+
+
+
+
+
+ 1
+
+
+
+
+ get "/auth/github/callback", to: "users#create", as: "auth_callback"
+
+
+
+
+
+ 1
+
+
+
+
+ delete "/logout", to: "users#destroy", as: "logout"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ root to: "pages#home"
+
+
+
+
+
+ 1
+
+
+
+
+ get "/search", to: "products#search", as: "search"
+
+
+
+
+
+ 1
+
+
+
+
+ get '/frontpage', to: 'pages#frontpage', as: "frontpage"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ patch '/products/:id/toggle_status', to: 'products#toggle_status', as: 'toggle_status'
+
+
+
+
+
+ 1
+
+
+
+
+ get '/dashboard', to: "users#dashboard", as: "dashboard"
+
+
+
+
+
+ 1
+
+
+
+
+ get '/ordered', to: "orders#ordered", as: "ordered"
+
+
+
+
+
+ 1
+
+
+
+
+ get '/cart', to: "orderitems#index", as: 'cart'
+
+
+
+
+
+ 1
+
+
+
+
+ patch 'order/mark_shipped', to: 'orders#mark_shipped', as: "mark_shipped"
+
+
+
+
+
+ 1
+
+
+
+
+ patch '/orderitems/add', to:"orderitems#increase_quantity", as: "add"
+
+
+
+
+
+ 1
+
+
+
+
+ patch '/orderitems/subtract', to:"orderitems#decrease_quantity", as: "subtract"
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ resources :products do
+
+
+
+
+
+ 1
+
+
+
+
+ resources :reviews, only: [:new, :create]
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 1
+
+
+
+
+ resources :orders
+
+
+
+
+
+ 1
+
+
+
+
+ resources :orderitems, only: [:index, :create]
+
+
+
+
+
+ 1
+
+
+
+
+ resources :products
+
+
+
+
+
+ 1
+
+
+
+
+ resources :categories
+
+
+
+
+
+
+
+
+
+
+ end
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ # review route - reference: https://stackoverflow.com/questions/25107038/ruby-on-rails-settting-up-reviews-functionality
+
+
+
+
+
+
+
+
+
+
+
+