From a382cbdecb5543a55165f8915effbb1b5e47a9ed Mon Sep 17 00:00:00 2001 From: Erik Trautman Date: Tue, 23 Sep 2014 13:59:22 -0700 Subject: [PATCH 01/12] Create README.md --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 000000000..456758c6c --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +danebook_goes_live +================== + +Week 3 Solo Assignment + +This is your README! From eef25409ee9e46b7942d68201c8f14de96809f80 Mon Sep 17 00:00:00 2001 From: Erik Trautman Date: Mon, 20 Jul 2015 09:43:34 -0700 Subject: [PATCH 02/12] Update README.md --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index 456758c6c..9a28abe89 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,4 @@ danebook_goes_live ================== -Week 3 Solo Assignment - This is your README! From 66ed54fa01d4e436f9c53cde1e009eb9e11409f1 Mon Sep 17 00:00:00 2001 From: David Watts Date: Mon, 1 Aug 2016 09:32:45 -0700 Subject: [PATCH 03/12] initial commit --- .gitignore | 21 ++ Gemfile | 50 +++++ Gemfile.lock | 184 ++++++++++++++++++ README.md | 11 ++ Rakefile | 6 + app/assets/config/manifest.js | 3 + app/assets/images/.keep | 0 app/assets/javascripts/application.js | 16 ++ app/assets/javascripts/cable.js | 13 ++ app/assets/javascripts/channels/.keep | 0 app/assets/stylesheets/application.css | 15 ++ app/channels/application_cable/channel.rb | 4 + app/channels/application_cable/connection.rb | 4 + app/controllers/application_controller.rb | 3 + app/controllers/concerns/.keep | 0 app/helpers/application_helper.rb | 2 + app/jobs/application_job.rb | 2 + app/mailers/application_mailer.rb | 4 + app/models/application_record.rb | 3 + app/models/concerns/.keep | 0 app/views/layouts/application.html.erb | 14 ++ app/views/layouts/mailer.html.erb | 13 ++ app/views/layouts/mailer.text.erb | 1 + bin/bundle | 3 + bin/rails | 4 + bin/rake | 4 + bin/setup | 34 ++++ bin/update | 29 +++ config.ru | 5 + config/application.rb | 15 ++ config/boot.rb | 3 + config/cable.yml | 9 + config/database.yml | 25 +++ config/environment.rb | 5 + config/environments/development.rb | 54 +++++ config/environments/production.rb | 86 ++++++++ config/environments/test.rb | 42 ++++ .../application_controller_renderer.rb | 6 + config/initializers/assets.rb | 11 ++ config/initializers/backtrace_silencers.rb | 7 + config/initializers/cookies_serializer.rb | 5 + .../initializers/filter_parameter_logging.rb | 4 + config/initializers/inflections.rb | 16 ++ config/initializers/mime_types.rb | 4 + config/initializers/new_framework_defaults.rb | 24 +++ config/initializers/session_store.rb | 3 + config/initializers/wrap_parameters.rb | 14 ++ config/locales/en.yml | 23 +++ config/puma.rb | 47 +++++ config/routes.rb | 3 + config/secrets.yml | 22 +++ config/spring.rb | 6 + db/seeds.rb | 7 + lib/assets/.keep | 0 lib/tasks/.keep | 0 log/.keep | 0 public/404.html | 67 +++++++ public/422.html | 67 +++++++ public/500.html | 66 +++++++ public/apple-touch-icon-precomposed.png | 0 public/apple-touch-icon.png | 0 public/favicon.ico | 0 public/robots.txt | 5 + test/controllers/.keep | 0 test/fixtures/.keep | 0 test/fixtures/files/.keep | 0 test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/test_helper.rb | 10 + tmp/.keep | 0 vendor/assets/javascripts/.keep | 0 vendor/assets/stylesheets/.keep | 0 74 files changed, 1104 insertions(+) create mode 100644 .gitignore create mode 100644 Gemfile create mode 100644 Gemfile.lock create mode 100644 Rakefile create mode 100644 app/assets/config/manifest.js create mode 100644 app/assets/images/.keep create mode 100644 app/assets/javascripts/application.js create mode 100644 app/assets/javascripts/cable.js create mode 100644 app/assets/javascripts/channels/.keep create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/channels/application_cable/channel.rb create mode 100644 app/channels/application_cable/connection.rb create mode 100644 app/controllers/application_controller.rb create mode 100644 app/controllers/concerns/.keep create mode 100644 app/helpers/application_helper.rb create mode 100644 app/jobs/application_job.rb create mode 100644 app/mailers/application_mailer.rb create mode 100644 app/models/application_record.rb create mode 100644 app/models/concerns/.keep create mode 100644 app/views/layouts/application.html.erb create mode 100644 app/views/layouts/mailer.html.erb create mode 100644 app/views/layouts/mailer.text.erb create mode 100755 bin/bundle create mode 100755 bin/rails create mode 100755 bin/rake create mode 100755 bin/setup create mode 100755 bin/update create mode 100644 config.ru create mode 100644 config/application.rb create mode 100644 config/boot.rb create mode 100644 config/cable.yml create mode 100644 config/database.yml create mode 100644 config/environment.rb create mode 100644 config/environments/development.rb create mode 100644 config/environments/production.rb create mode 100644 config/environments/test.rb create mode 100644 config/initializers/application_controller_renderer.rb create mode 100644 config/initializers/assets.rb create mode 100644 config/initializers/backtrace_silencers.rb create mode 100644 config/initializers/cookies_serializer.rb create mode 100644 config/initializers/filter_parameter_logging.rb create mode 100644 config/initializers/inflections.rb create mode 100644 config/initializers/mime_types.rb create mode 100644 config/initializers/new_framework_defaults.rb create mode 100644 config/initializers/session_store.rb create mode 100644 config/initializers/wrap_parameters.rb create mode 100644 config/locales/en.yml create mode 100644 config/puma.rb create mode 100644 config/routes.rb create mode 100644 config/secrets.yml create mode 100644 config/spring.rb create mode 100644 db/seeds.rb create mode 100644 lib/assets/.keep create mode 100644 lib/tasks/.keep create mode 100644 log/.keep create mode 100644 public/404.html create mode 100644 public/422.html create mode 100644 public/500.html create mode 100644 public/apple-touch-icon-precomposed.png create mode 100644 public/apple-touch-icon.png create mode 100644 public/favicon.ico create mode 100644 public/robots.txt create mode 100644 test/controllers/.keep create mode 100644 test/fixtures/.keep create mode 100644 test/fixtures/files/.keep create mode 100644 test/helpers/.keep create mode 100644 test/integration/.keep create mode 100644 test/mailers/.keep create mode 100644 test/models/.keep create mode 100644 test/test_helper.rb create mode 100644 tmp/.keep create mode 100644 vendor/assets/javascripts/.keep create mode 100644 vendor/assets/stylesheets/.keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 000000000..bab620de0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,21 @@ +# See https://help.github.com/articles/ignoring-files for more about ignoring files. +# +# If you find yourself ignoring temporary files generated by your text editor +# or operating system, you probably want to add a global ignore instead: +# git config --global core.excludesfile '~/.gitignore_global' + +# Ignore bundler config. +/.bundle + +# Ignore the default SQLite database. +/db/*.sqlite3 +/db/*.sqlite3-journal + +# Ignore all logfiles and tempfiles. +/log/* +/tmp/* +!/log/.keep +!/tmp/.keep + +# Ignore Byebug command history file. +.byebug_history diff --git a/Gemfile b/Gemfile new file mode 100644 index 000000000..cf13ada73 --- /dev/null +++ b/Gemfile @@ -0,0 +1,50 @@ +source 'http://rubygems.org' + + +# Bundle edge Rails instead: gem 'rails', github: 'rails/rails' +gem 'rails', '~> 5.0.0' +# Use sqlite3 as the database for Active Record +gem 'sqlite3' +# Use Puma as the app server +gem 'puma', '~> 3.0' +# Use SCSS for stylesheets +gem 'sass-rails', '~> 5.0' +# Use Uglifier as compressor for JavaScript assets +gem 'uglifier', '>= 1.3.0' +# Use CoffeeScript for .coffee assets and views +gem 'coffee-rails', '~> 4.2' +# See https://github.com/rails/execjs#readme for more supported runtimes +# gem 'therubyracer', platforms: :ruby + +# Use jquery as the JavaScript library +gem 'jquery-rails' +# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks +gem 'turbolinks', '~> 5' +# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder +gem 'jbuilder', '~> 2.5' +# Use Redis adapter to run Action Cable in production +# gem 'redis', '~> 3.0' +# Use ActiveModel has_secure_password +# gem 'bcrypt', '~> 3.1.7' + +# Use Capistrano for deployment +# gem 'capistrano-rails', group: :development + +group :development, :test do + # Call 'byebug' anywhere in the code to stop execution and get a debugger console + gem 'byebug', platform: :mri +end + +group :development do + gem 'better_errors' + gem 'binding_of_caller' + # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. + gem 'web-console' + gem 'listen', '~> 3.0.5' + # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring + gem 'spring' + gem 'spring-watcher-listen', '~> 2.0.0' +end + +# Windows does not include zoneinfo files, so bundle the tzinfo-data gem +gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 000000000..1878e13fd --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,184 @@ +GEM + remote: http://rubygems.org/ + specs: + actioncable (5.0.0) + actionpack (= 5.0.0) + nio4r (~> 1.2) + websocket-driver (~> 0.6.1) + actionmailer (5.0.0) + actionpack (= 5.0.0) + actionview (= 5.0.0) + activejob (= 5.0.0) + mail (~> 2.5, >= 2.5.4) + rails-dom-testing (~> 2.0) + actionpack (5.0.0) + actionview (= 5.0.0) + activesupport (= 5.0.0) + rack (~> 2.0) + rack-test (~> 0.6.3) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + actionview (5.0.0) + activesupport (= 5.0.0) + builder (~> 3.1) + erubis (~> 2.7.0) + rails-dom-testing (~> 2.0) + rails-html-sanitizer (~> 1.0, >= 1.0.2) + activejob (5.0.0) + activesupport (= 5.0.0) + globalid (>= 0.3.6) + activemodel (5.0.0) + activesupport (= 5.0.0) + activerecord (5.0.0) + activemodel (= 5.0.0) + activesupport (= 5.0.0) + arel (~> 7.0) + activesupport (5.0.0) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (~> 0.7) + minitest (~> 5.1) + tzinfo (~> 1.1) + arel (7.1.1) + better_errors (2.1.1) + coderay (>= 1.0.0) + erubis (>= 2.6.6) + rack (>= 0.9.0) + binding_of_caller (0.7.2) + debug_inspector (>= 0.0.1) + builder (3.2.2) + byebug (9.0.5) + coderay (1.1.1) + coffee-rails (4.2.1) + coffee-script (>= 2.2.0) + railties (>= 4.0.0, < 5.2.x) + coffee-script (2.4.1) + coffee-script-source + execjs + coffee-script-source (1.10.0) + concurrent-ruby (1.0.2) + debug_inspector (0.0.2) + erubis (2.7.0) + execjs (2.7.0) + ffi (1.9.14) + globalid (0.3.7) + activesupport (>= 4.1.0) + i18n (0.7.0) + jbuilder (2.6.0) + activesupport (>= 3.0.0, < 5.1) + multi_json (~> 1.2) + jquery-rails (4.1.1) + rails-dom-testing (>= 1, < 3) + railties (>= 4.2.0) + thor (>= 0.14, < 2.0) + listen (3.0.8) + rb-fsevent (~> 0.9, >= 0.9.4) + rb-inotify (~> 0.9, >= 0.9.7) + loofah (2.0.3) + nokogiri (>= 1.5.9) + mail (2.6.4) + mime-types (>= 1.16, < 4) + method_source (0.8.2) + mime-types (3.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2016.0521) + mini_portile2 (2.1.0) + minitest (5.9.0) + multi_json (1.12.1) + nio4r (1.2.1) + nokogiri (1.6.8) + mini_portile2 (~> 2.1.0) + pkg-config (~> 1.1.7) + pkg-config (1.1.7) + puma (3.6.0) + rack (2.0.1) + rack-test (0.6.3) + rack (>= 1.0) + rails (5.0.0) + actioncable (= 5.0.0) + actionmailer (= 5.0.0) + actionpack (= 5.0.0) + actionview (= 5.0.0) + activejob (= 5.0.0) + activemodel (= 5.0.0) + activerecord (= 5.0.0) + activesupport (= 5.0.0) + bundler (>= 1.3.0, < 2.0) + railties (= 5.0.0) + sprockets-rails (>= 2.0.0) + rails-dom-testing (2.0.1) + activesupport (>= 4.2.0, < 6.0) + nokogiri (~> 1.6.0) + rails-html-sanitizer (1.0.3) + loofah (~> 2.0) + railties (5.0.0) + actionpack (= 5.0.0) + activesupport (= 5.0.0) + method_source + rake (>= 0.8.7) + thor (>= 0.18.1, < 2.0) + rake (11.2.2) + rb-fsevent (0.9.7) + rb-inotify (0.9.7) + ffi (>= 0.5.0) + sass (3.4.22) + sass-rails (5.0.6) + railties (>= 4.0.0, < 6) + sass (~> 3.1) + sprockets (>= 2.8, < 4.0) + sprockets-rails (>= 2.0, < 4.0) + tilt (>= 1.1, < 3) + spring (1.7.2) + spring-watcher-listen (2.0.0) + listen (>= 2.7, < 4.0) + spring (~> 1.2) + sprockets (3.7.0) + concurrent-ruby (~> 1.0) + rack (> 1, < 3) + sprockets-rails (3.1.1) + actionpack (>= 4.0) + activesupport (>= 4.0) + sprockets (>= 3.0.0) + sqlite3 (1.3.11) + thor (0.19.1) + thread_safe (0.3.5) + tilt (2.0.5) + turbolinks (5.0.1) + turbolinks-source (~> 5) + turbolinks-source (5.0.0) + tzinfo (1.2.2) + thread_safe (~> 0.1) + uglifier (3.0.1) + execjs (>= 0.3.0, < 3) + web-console (3.3.1) + actionview (>= 5.0) + activemodel (>= 5.0) + debug_inspector + railties (>= 5.0) + websocket-driver (0.6.4) + websocket-extensions (>= 0.1.0) + websocket-extensions (0.1.2) + +PLATFORMS + ruby + +DEPENDENCIES + better_errors + binding_of_caller + byebug + coffee-rails (~> 4.2) + jbuilder (~> 2.5) + jquery-rails + listen (~> 3.0.5) + puma (~> 3.0) + rails (~> 5.0.0) + sass-rails (~> 5.0) + spring + spring-watcher-listen (~> 2.0.0) + sqlite3 + turbolinks (~> 5) + tzinfo-data + uglifier (>= 1.3.0) + web-console + +BUNDLED WITH + 1.12.5 diff --git a/README.md b/README.md index 9a28abe89..4a148d624 100644 --- a/README.md +++ b/README.md @@ -2,3 +2,14 @@ danebook_goes_live ================== This is your README! + +Danebook + +`David Watts>` + +This is an assignment for viking code school in which we use rails to serve +already created static assets. + +Here is a link to viking code school: + +http://vikingcodeschool.com diff --git a/Rakefile b/Rakefile new file mode 100644 index 000000000..e85f91391 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +# Add your own tasks in files placed in lib/tasks ending in .rake, +# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. + +require_relative 'config/application' + +Rails.application.load_tasks diff --git a/app/assets/config/manifest.js b/app/assets/config/manifest.js new file mode 100644 index 000000000..b16e53d6d --- /dev/null +++ b/app/assets/config/manifest.js @@ -0,0 +1,3 @@ +//= link_tree ../images +//= link_directory ../javascripts .js +//= link_directory ../stylesheets .css diff --git a/app/assets/images/.keep b/app/assets/images/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js new file mode 100644 index 000000000..b12018d09 --- /dev/null +++ b/app/assets/javascripts/application.js @@ -0,0 +1,16 @@ +// This is a manifest file that'll be compiled into application.js, which will include all the files +// listed below. +// +// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, +// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path. +// +// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the +// compiled file. JavaScript code in this file should be added after the last require_* statement. +// +// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details +// about supported directives. +// +//= require jquery +//= require jquery_ujs +//= require turbolinks +//= require_tree . diff --git a/app/assets/javascripts/cable.js b/app/assets/javascripts/cable.js new file mode 100644 index 000000000..71ee1e66d --- /dev/null +++ b/app/assets/javascripts/cable.js @@ -0,0 +1,13 @@ +// Action Cable provides the framework to deal with WebSockets in Rails. +// You can generate new channels where WebSocket features live using the rails generate channel command. +// +//= require action_cable +//= require_self +//= require_tree ./channels + +(function() { + this.App || (this.App = {}); + + App.cable = ActionCable.createConsumer(); + +}).call(this); diff --git a/app/assets/javascripts/channels/.keep b/app/assets/javascripts/channels/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 000000000..0ebd7fe82 --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,15 @@ +/* + * This is a manifest file that'll be compiled into application.css, which will include all the files + * listed below. + * + * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, + * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. + * + * You're free to add application-wide styles to this file and they'll appear at the bottom of the + * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS + * files in this directory. Styles in this file should be added after the last require_* statement. + * It is generally better to create a new file per style scope. + * + *= require_tree . + *= require_self + */ diff --git a/app/channels/application_cable/channel.rb b/app/channels/application_cable/channel.rb new file mode 100644 index 000000000..d67269728 --- /dev/null +++ b/app/channels/application_cable/channel.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Channel < ActionCable::Channel::Base + end +end diff --git a/app/channels/application_cable/connection.rb b/app/channels/application_cable/connection.rb new file mode 100644 index 000000000..0ff5442f4 --- /dev/null +++ b/app/channels/application_cable/connection.rb @@ -0,0 +1,4 @@ +module ApplicationCable + class Connection < ActionCable::Connection::Base + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb new file mode 100644 index 000000000..1c07694e9 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,3 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb new file mode 100644 index 000000000..de6be7945 --- /dev/null +++ b/app/helpers/application_helper.rb @@ -0,0 +1,2 @@ +module ApplicationHelper +end diff --git a/app/jobs/application_job.rb b/app/jobs/application_job.rb new file mode 100644 index 000000000..a009ace51 --- /dev/null +++ b/app/jobs/application_job.rb @@ -0,0 +1,2 @@ +class ApplicationJob < ActiveJob::Base +end diff --git a/app/mailers/application_mailer.rb b/app/mailers/application_mailer.rb new file mode 100644 index 000000000..286b2239d --- /dev/null +++ b/app/mailers/application_mailer.rb @@ -0,0 +1,4 @@ +class ApplicationMailer < ActionMailer::Base + default from: 'from@example.com' + layout 'mailer' +end diff --git a/app/models/application_record.rb b/app/models/application_record.rb new file mode 100644 index 000000000..10a4cba84 --- /dev/null +++ b/app/models/application_record.rb @@ -0,0 +1,3 @@ +class ApplicationRecord < ActiveRecord::Base + self.abstract_class = true +end diff --git a/app/models/concerns/.keep b/app/models/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb new file mode 100644 index 000000000..ba857f157 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,14 @@ + + + + Danebook + <%= csrf_meta_tags %> + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.html.erb b/app/views/layouts/mailer.html.erb new file mode 100644 index 000000000..cbd34d2e9 --- /dev/null +++ b/app/views/layouts/mailer.html.erb @@ -0,0 +1,13 @@ + + + + + + + + + <%= yield %> + + diff --git a/app/views/layouts/mailer.text.erb b/app/views/layouts/mailer.text.erb new file mode 100644 index 000000000..37f0bddbd --- /dev/null +++ b/app/views/layouts/mailer.text.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/bin/bundle b/bin/bundle new file mode 100755 index 000000000..66e9889e8 --- /dev/null +++ b/bin/bundle @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) +load Gem.bin_path('bundler', 'bundle') diff --git a/bin/rails b/bin/rails new file mode 100755 index 000000000..073966023 --- /dev/null +++ b/bin/rails @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +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 000000000..17240489f --- /dev/null +++ b/bin/rake @@ -0,0 +1,4 @@ +#!/usr/bin/env ruby +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 000000000..e620b4dad --- /dev/null +++ b/bin/setup @@ -0,0 +1,34 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a starting point to setup your application. + # Add necessary setup steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + # puts "\n== Copying sample files ==" + # unless File.exist?('config/database.yml') + # cp 'config/database.yml.sample', 'config/database.yml' + # end + + puts "\n== Preparing database ==" + system! 'bin/rails db:setup' + + 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/update b/bin/update new file mode 100755 index 000000000..a8e4462f2 --- /dev/null +++ b/bin/update @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'pathname' +require 'fileutils' +include FileUtils + +# path to your application root. +APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) + +def system!(*args) + system(*args) || abort("\n== Command #{args} failed ==") +end + +chdir APP_ROOT do + # This script is a way to update your development environment automatically. + # Add necessary update steps to this file. + + puts '== Installing dependencies ==' + system! 'gem install bundler --conservative' + system('bundle check') || system!('bundle install') + + puts "\n== Updating database ==" + system! 'bin/rails db:migrate' + + 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/config.ru b/config.ru new file mode 100644 index 000000000..f7ba0b527 --- /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 000000000..f13c54c98 --- /dev/null +++ b/config/application.rb @@ -0,0 +1,15 @@ +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 Danebook + class Application < Rails::Application + # Settings in config/environments/* take precedence over those specified here. + # Application configuration should go into files in config/initializers + # -- all .rb files in that directory are automatically loaded. + end +end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 000000000..30f5120df --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,3 @@ +ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) + +require 'bundler/setup' # Set up gems listed in the Gemfile. diff --git a/config/cable.yml b/config/cable.yml new file mode 100644 index 000000000..0bbde6f74 --- /dev/null +++ b/config/cable.yml @@ -0,0 +1,9 @@ +development: + adapter: async + +test: + adapter: async + +production: + adapter: redis + url: redis://localhost:6379/1 diff --git a/config/database.yml b/config/database.yml new file mode 100644 index 000000000..1c1a37ca8 --- /dev/null +++ b/config/database.yml @@ -0,0 +1,25 @@ +# SQLite version 3.x +# gem install sqlite3 +# +# Ensure the SQLite 3 gem is defined in your Gemfile +# gem 'sqlite3' +# +default: &default + adapter: sqlite3 + pool: 5 + timeout: 5000 + +development: + <<: *default + database: db/development.sqlite3 + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + <<: *default + database: db/test.sqlite3 + +production: + <<: *default + database: db/production.sqlite3 diff --git a/config/environment.rb b/config/environment.rb new file mode 100644 index 000000000..426333bb4 --- /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 000000000..6f7197045 --- /dev/null +++ b/config/environments/development.rb @@ -0,0 +1,54 @@ +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. + if Rails.root.join('tmp/caching-dev.txt').exist? + config.action_controller.perform_caching = true + + config.cache_store = :memory_store + config.public_file_server.headers = { + 'Cache-Control' => 'public, max-age=172800' + } + else + config.action_controller.perform_caching = false + + config.cache_store = :null_store + end + + # 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 + + # 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 000000000..e43500ee1 --- /dev/null +++ b/config/environments/production.rb @@ -0,0 +1,86 @@ +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 + + # 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 JavaScripts and CSS. + config.assets.js_compressor = :uglifier + # config.assets.css_compressor = :sass + + # Do not fallback to assets pipeline if a precompiled asset is missed. + config.assets.compile = false + + # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb + + # 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 + + # 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 = "danebook_#{Rails.env}" + 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 +end diff --git a/config/environments/test.rb b/config/environments/test.rb new file mode 100644 index 000000000..30587ef6d --- /dev/null +++ b/config/environments/test.rb @@ -0,0 +1,42 @@ +Rails.application.configure do + # Settings specified here will take precedence over those in config/application.rb. + + # 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! + config.cache_classes = 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=3600' + } + + # Show full error reports and disable caching. + config.consider_all_requests_local = true + config.action_controller.perform_caching = false + + # 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 + 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/application_controller_renderer.rb b/config/initializers/application_controller_renderer.rb new file mode 100644 index 000000000..51639b67a --- /dev/null +++ b/config/initializers/application_controller_renderer.rb @@ -0,0 +1,6 @@ +# Be sure to restart your server when you modify this file. + +# ApplicationController.renderer.defaults.merge!( +# http_host: 'example.org', +# https: false +# ) diff --git a/config/initializers/assets.rb b/config/initializers/assets.rb new file mode 100644 index 000000000..01ef3e663 --- /dev/null +++ b/config/initializers/assets.rb @@ -0,0 +1,11 @@ +# 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 + +# Precompile additional assets. +# application.js, application.css, and all non-JS/CSS in app/assets folder are already added. +# Rails.application.config.assets.precompile += %w( search.js ) diff --git a/config/initializers/backtrace_silencers.rb b/config/initializers/backtrace_silencers.rb new file mode 100644 index 000000000..59385cdf3 --- /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/cookies_serializer.rb b/config/initializers/cookies_serializer.rb new file mode 100644 index 000000000..5a6a32d37 --- /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 000000000..4a994e1e7 --- /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 000000000..ac033bf9d --- /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 000000000..dc1899682 --- /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/new_framework_defaults.rb b/config/initializers/new_framework_defaults.rb new file mode 100644 index 000000000..0706cafd4 --- /dev/null +++ b/config/initializers/new_framework_defaults.rb @@ -0,0 +1,24 @@ +# Be sure to restart your server when you modify this file. +# +# This file contains migration options to ease your Rails 5.0 upgrade. +# +# Read the Rails 5.0 release notes for more info on each option. + +# Enable per-form CSRF tokens. Previous versions had false. +Rails.application.config.action_controller.per_form_csrf_tokens = true + +# Enable origin-checking CSRF mitigation. Previous versions had false. +Rails.application.config.action_controller.forgery_protection_origin_check = true + +# Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. +# Previous versions had false. +ActiveSupport.to_time_preserves_timezone = true + +# Require `belongs_to` associations by default. Previous versions had false. +Rails.application.config.active_record.belongs_to_required_by_default = true + +# Do not halt callback chains when a callback returns false. Previous versions had true. +ActiveSupport.halt_callback_chains_on_return_false = false + +# Configure SSL options to enable HSTS with subdomains. Previous versions had false. +Rails.application.config.ssl_options = { hsts: { subdomains: true } } diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb new file mode 100644 index 000000000..f7b5ca3e4 --- /dev/null +++ b/config/initializers/session_store.rb @@ -0,0 +1,3 @@ +# Be sure to restart your server when you modify this file. + +Rails.application.config.session_store :cookie_store, key: '_danebook_session' diff --git a/config/initializers/wrap_parameters.rb b/config/initializers/wrap_parameters.rb new file mode 100644 index 000000000..bbfc3961b --- /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 000000000..065395716 --- /dev/null +++ b/config/locales/en.yml @@ -0,0 +1,23 @@ +# 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. +# +# To learn more, please read the Rails Internationalization guide +# available at http://guides.rubyonrails.org/i18n.html. + +en: + hello: "Hello world" diff --git a/config/puma.rb b/config/puma.rb new file mode 100644 index 000000000..c7f311f81 --- /dev/null +++ b/config/puma.rb @@ -0,0 +1,47 @@ +# 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. +# +threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i +threads threads_count, 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 number of `workers` to boot in clustered mode. +# Workers are forked webserver 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. If you use this option +# you need to make sure to reconnect any threads in the `on_worker_boot` +# block. +# +# preload_app! + +# The code in the `on_worker_boot` will be called if you are using +# clustered mode by specifying a number of `workers`. After each worker +# process is booted this block will be run, if you are using `preload_app!` +# option you will want to use this block to reconnect to any threads +# or connections that may have been created at application boot, Ruby +# cannot share connections between processes. +# +# on_worker_boot do +# ActiveRecord::Base.establish_connection if defined?(ActiveRecord) +# end + +# 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 000000000..787824f88 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,3 @@ +Rails.application.routes.draw do + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html +end diff --git a/config/secrets.yml b/config/secrets.yml new file mode 100644 index 000000000..9578564b3 --- /dev/null +++ b/config/secrets.yml @@ -0,0 +1,22 @@ +# Be sure to restart your server when you modify this file. + +# Your secret key is used for verifying the integrity of signed cookies. +# If you change this key, all old signed cookies will become invalid! + +# Make sure the secret is at least 30 characters and all random, +# no regular words or you'll be exposed to dictionary attacks. +# You can use `rails secret` to generate a secure secret key. + +# Make sure the secrets in this file are kept private +# if you're sharing your code publicly. + +development: + secret_key_base: eb5e04f4d29ef9ac432d5fa0e9b1f5427c38defe082b156a73a34c094adcf3b4f52566045aa88c757089e01e3d97f0ab472775ad36faef1e6451b4a81f7bf648 + +test: + secret_key_base: 5fb0b410f7a54978d11d5b92d47b30e2a2434663eda95636b454dcf548a729500d4a0252361967045114360f82598de0003e4d8eeb40f3100f4b7cf2782400f6 + +# Do not keep production secrets in the repository, +# instead read values from the environment. +production: + secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> diff --git a/config/spring.rb b/config/spring.rb new file mode 100644 index 000000000..c9119b40c --- /dev/null +++ b/config/spring.rb @@ -0,0 +1,6 @@ +%w( + .ruby-version + .rbenv-vars + tmp/restart.txt + tmp/caching-dev.txt +).each { |path| Spring.watch(path) } diff --git a/db/seeds.rb b/db/seeds.rb new file mode 100644 index 000000000..1beea2acc --- /dev/null +++ b/db/seeds.rb @@ -0,0 +1,7 @@ +# This file should contain all the record creation needed to seed the database with its default values. +# The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). +# +# Examples: +# +# movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) +# Character.create(name: 'Luke', movie: movies.first) diff --git a/lib/assets/.keep b/lib/assets/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/lib/tasks/.keep b/lib/tasks/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/log/.keep b/log/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/public/404.html b/public/404.html new file mode 100644 index 000000000..b612547fc --- /dev/null +++ b/public/404.html @@ -0,0 +1,67 @@ + + + + The page you were looking for doesn't exist (404) + + + + + + +
+
+

The page you were looking for doesn't exist.

+

You may have mistyped the address or the page may have moved.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/422.html b/public/422.html new file mode 100644 index 000000000..a21f82b3b --- /dev/null +++ b/public/422.html @@ -0,0 +1,67 @@ + + + + The change you wanted was rejected (422) + + + + + + +
+
+

The change you wanted was rejected.

+

Maybe you tried to change something you didn't have access to.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/500.html b/public/500.html new file mode 100644 index 000000000..061abc587 --- /dev/null +++ b/public/500.html @@ -0,0 +1,66 @@ + + + + We're sorry, but something went wrong (500) + + + + + + +
+
+

We're sorry, but something went wrong.

+
+

If you are the application owner check the logs for more information.

+
+ + diff --git a/public/apple-touch-icon-precomposed.png b/public/apple-touch-icon-precomposed.png new file mode 100644 index 000000000..e69de29bb diff --git a/public/apple-touch-icon.png b/public/apple-touch-icon.png new file mode 100644 index 000000000..e69de29bb diff --git a/public/favicon.ico b/public/favicon.ico new file mode 100644 index 000000000..e69de29bb diff --git a/public/robots.txt b/public/robots.txt new file mode 100644 index 000000000..3c9c7c01f --- /dev/null +++ b/public/robots.txt @@ -0,0 +1,5 @@ +# See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file +# +# To ban all spiders from the entire site uncomment the next two lines: +# User-agent: * +# Disallow: / diff --git a/test/controllers/.keep b/test/controllers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/.keep b/test/fixtures/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/helpers/.keep b/test/helpers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/integration/.keep b/test/integration/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/mailers/.keep b/test/mailers/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/models/.keep b/test/models/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 000000000..92e39b2d7 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,10 @@ +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +require 'rails/test_help' + +class ActiveSupport::TestCase + # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. + fixtures :all + + # Add more helper methods to be used by all tests here... +end diff --git a/tmp/.keep b/tmp/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/assets/javascripts/.keep b/vendor/assets/javascripts/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/vendor/assets/stylesheets/.keep b/vendor/assets/stylesheets/.keep new file mode 100644 index 000000000..e69de29bb From 73d25d81ec149c902428de356ef671331b563d4c Mon Sep 17 00:00:00 2001 From: David Watts Date: Mon, 1 Aug 2016 11:14:55 -0700 Subject: [PATCH 04/12] finish site and change gemfile --- Gemfile | 4 +- Gemfile.lock | 13 +- app/assets/images/cover.jpg | Bin 0 -> 73659 bytes app/assets/images/def-user.gif | Bin 0 -> 2750 bytes app/assets/images/logo.gif | Bin 0 -> 2268 bytes app/assets/images/photo.png | Bin 0 -> 7321 bytes app/assets/images/user.jpg | Bin 0 -> 12773 bytes app/assets/javascripts/static_pages.coffee | 3 + app/assets/javascripts/static_pages1.coffee | 3 + app/assets/stylesheets/static_pages.scss | 68 ++++ app/assets/stylesheets/static_pages1.scss | 309 ++++++++++++++++++ app/controllers/static_pages1_controller.rb | 12 + app/controllers/static_pages_controller.rb | 6 + app/helpers/static_pages1_helper.rb | 2 + app/helpers/static_pages_helper.rb | 2 + app/views/layouts/application.html.erb | 2 +- app/views/static_pages/_nav_home.html.erb | 46 +++ app/views/static_pages/home.html.erb | 91 ++++++ app/views/static_pages1/_nav.html.erb | 28 ++ app/views/static_pages1/about.html.erb | 115 +++++++ app/views/static_pages1/about_edit.html.erb | 117 +++++++ app/views/static_pages1/friends.html.erb | 82 +++++ app/views/static_pages1/photos.html.erb | 85 +++++ app/views/static_pages1/timeline.html.erb | 229 +++++++++++++ config/database.yml | 1 + config/routes.rb | 8 + .../static_pages1_controller_test.rb | 7 + .../static_pages_controller_test.rb | 7 + 28 files changed, 1236 insertions(+), 4 deletions(-) create mode 100644 app/assets/images/cover.jpg create mode 100644 app/assets/images/def-user.gif create mode 100644 app/assets/images/logo.gif create mode 100644 app/assets/images/photo.png create mode 100644 app/assets/images/user.jpg create mode 100644 app/assets/javascripts/static_pages.coffee create mode 100644 app/assets/javascripts/static_pages1.coffee create mode 100644 app/assets/stylesheets/static_pages.scss create mode 100644 app/assets/stylesheets/static_pages1.scss create mode 100644 app/controllers/static_pages1_controller.rb create mode 100644 app/controllers/static_pages_controller.rb create mode 100644 app/helpers/static_pages1_helper.rb create mode 100644 app/helpers/static_pages_helper.rb create mode 100644 app/views/static_pages/_nav_home.html.erb create mode 100644 app/views/static_pages/home.html.erb create mode 100644 app/views/static_pages1/_nav.html.erb create mode 100644 app/views/static_pages1/about.html.erb create mode 100644 app/views/static_pages1/about_edit.html.erb create mode 100644 app/views/static_pages1/friends.html.erb create mode 100644 app/views/static_pages1/photos.html.erb create mode 100644 app/views/static_pages1/timeline.html.erb create mode 100644 test/controllers/static_pages1_controller_test.rb create mode 100644 test/controllers/static_pages_controller_test.rb diff --git a/Gemfile b/Gemfile index cf13ada73..002361d8d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,10 +1,12 @@ source 'http://rubygems.org' +ruby '2.2.4' +gem 'rails_12factor' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.0' # Use sqlite3 as the database for Active Record -gem 'sqlite3' +gem 'pg' # Use Puma as the app server gem 'puma', '~> 3.0' # Use SCSS for stylesheets diff --git a/Gemfile.lock b/Gemfile.lock index 1878e13fd..753e0dae9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -88,6 +88,7 @@ GEM nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) + pg (0.18.4) pkg-config (1.1.7) puma (3.6.0) rack (2.0.1) @@ -110,6 +111,11 @@ GEM nokogiri (~> 1.6.0) rails-html-sanitizer (1.0.3) loofah (~> 2.0) + rails_12factor (0.0.3) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.5) + rails_stdout_logging (0.0.5) railties (5.0.0) actionpack (= 5.0.0) activesupport (= 5.0.0) @@ -138,7 +144,6 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) - sqlite3 (1.3.11) thor (0.19.1) thread_safe (0.3.5) tilt (2.0.5) @@ -169,16 +174,20 @@ DEPENDENCIES jbuilder (~> 2.5) jquery-rails listen (~> 3.0.5) + pg puma (~> 3.0) rails (~> 5.0.0) + rails_12factor sass-rails (~> 5.0) spring spring-watcher-listen (~> 2.0.0) - sqlite3 turbolinks (~> 5) tzinfo-data uglifier (>= 1.3.0) web-console +RUBY VERSION + ruby 2.2.4p230 + BUNDLED WITH 1.12.5 diff --git a/app/assets/images/cover.jpg b/app/assets/images/cover.jpg new file mode 100644 index 0000000000000000000000000000000000000000..94a8abeff8c8b2c336d837008f00ad9e1c580f13 GIT binary patch literal 73659 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 literal 0 HcmV?d00001 diff --git a/app/assets/images/def-user.gif b/app/assets/images/def-user.gif new file mode 100644 index 0000000000000000000000000000000000000000..e52535f55bc33068954e637c45b012dc23f5e039 GIT binary patch literal 2750 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 q*0|2Ku6NDrUi0RTI^bbqA) literal 0 HcmV?d00001 diff --git a/app/assets/images/photo.png b/app/assets/images/photo.png new file mode 100644 index 0000000000000000000000000000000000000000..0a900d16fb0ee5b0eeb92447a8b48871a29a5c08 GIT binary patch literal 7321 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 OQPqUisc7r<=l=yD>bA82 literal 0 HcmV?d00001 diff --git a/app/assets/javascripts/static_pages.coffee b/app/assets/javascripts/static_pages.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/static_pages.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/static_pages1.coffee b/app/assets/javascripts/static_pages1.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/static_pages1.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/static_pages.scss b/app/assets/stylesheets/static_pages.scss new file mode 100644 index 000000000..acdf2de40 --- /dev/null +++ b/app/assets/stylesheets/static_pages.scss @@ -0,0 +1,68 @@ +// Place all the styles related to the StaticPages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ +/* */ +body { + padding-top: 50px; } + body nav { + background-color: #418EFF; + height: 75px; + border-bottom: solid black 2px; } + body nav #brand { + color: white; + font-size: 38px; + font-weight: 550; + line-height: 38px; + padding-left: 30px; } + body nav label { + color: white; } + body #navbar { + padding-right: 15px; + border-bottom: solid black 2px; + padding-bottom: 7px; } + body #submit { + padding-top: 5px; } + body #submit button { + border: 2px black solid; + box-shadow: 3px 3px; } + body #password { + padding: 0px 10px; } + body #password input { + border: 2px black solid; } + body #email input { + border: 2px black solid; } + body h2 { + font-weight: 600; } + body h1 { + font-weight: 600; } + body h4 { + text-align: left; } + body #gender { + text-align: left; } + body main header { + text-align: left; } + body main header li { + text-align: left; + padding: 10px; + font-size: 14px; } + body main #content { + padding-top: 50px; + padding-bottom: 40px; + padding-left: 30px; } + body main #signup { + padding-top: 20px; } + +#sign-btn { + border: 1px black solid; + box-shadow: 2px 2px; } + +#last { + display: inline; + padding-left: 23px; } + +#main-wrapper { + padding-top: 15px; + padding-bottom: 15px; + text-align: center; } + +/*# sourceMappingURL=styles.css.map */ diff --git a/app/assets/stylesheets/static_pages1.scss b/app/assets/stylesheets/static_pages1.scss new file mode 100644 index 000000000..66f0d1d26 --- /dev/null +++ b/app/assets/stylesheets/static_pages1.scss @@ -0,0 +1,309 @@ +// Place all the styles related to the StaticPages1 controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ +/* */ + +body { +padding-top: 70px; + +/* NAV */ +} + +#navcolor { + background-color: #418EFF; + height: 60px; +} + +h1{ + display: inline; +} + +#logoimg { + float: left; + margin-top: 10px; + padding-left: 0px; +} + +#logotxt { + padding-left: 20px; + line-height: 20px; + padding-top: 8px; + padding-right: 0px; + color: white; +} + +#search { + width: 100%; + margin-top: 5px; +} + +#harry { + color: white; + float: right; +} + +/* ABOUT */ + +#cover-col { + background-image: url("cover.jpg"); + height: 300px; + background-size: 100%; + background-position: center; + +} + +#user-img { + margin-top: 170px; + margin-right: 30px; + float: left; +} + +#about-name { + color: white; + margin-top: 255px; +} + +h3 { + margin: 20px 0; +} +#about-content { +h5 { + margin: 20px 0; + margin-left: 15px; +} +} + + +#about-links { + border: solid black 1px; + height: 40px; +} + +#link-box1 { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; +} + +#link-box2 { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; +} + +#link-box3{ + height: 100%; + border-left: solid black 1px; + border-right: solid black 1px; + margin-right: 0px; + text-align: center; + box-sizing: border-box; + padding-left: 0px; + padding-right: 0px; + width: 14%; + a { + color: black; + } +} +#link-box4 { + height: 100%; + padding-left: 10px; + border-right: solid black 1px; + text-align: center; + width: 14%; + a { + color: black; + } +} + +#edit-link { + float: right; + text-align: right; +} + +#about-row { + background-color: #ddd; + margin-top: 20px; + height: 45px; + padding: 0px; + border: solid black 1px; +} + +#about-col { + text-align: center; + padding: 0px; + margin-top: 7px; +} + +#edit-btn { + float: right; + margin-top: 10px; +} + +#about-content { + border: solid black 1px; + border-top: 0; + margin-bottom: 40px; +} + +/* EDIT */ + +#save-btn { + margin-top: 50px; + margin-bottom: 100px; + line-height: 40px; +} + +#link-box2-current { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; + background-color: #ddd; +} + +/* TIMELINE */ + +#link-box1-current { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; + background-color: #ddd; +} + +div .box-header { + background-color: #ddd; + text-align: center; + margin-top: 15px; + border: solid black 1px; + border-bottom: 0; +} + +div .box-content { + border: solid black 1px; + padding-top: 15px; + padding-bottom: 8px; +} + +div .box-foot { + background-color: #ddd; + border: solid black 1px; + border-top: 0; + margin-bottom: 30px; +} + +div .center-link { + text-align: center; + text-decoration: underline; + margin-bottom: 10px; +} + +div .user-img { + width: 50px; + height: 50px; + overflow: hidden; + display: inline-block; + float: left; + margin-right: 10px; +} + +div .com-img { + width: 50px; + height: 50px; + overflow: hidden; + float: right; + +} +div .delete-link { + display: inline-block; + float: right; +} + +#post-btn { + float: right; + margin: 8px 0; +} + +#comment-area { + margin-top: 20px; +} + +/* FRIENDS */ + +#link-box4-current { + background-color: #ddd; + height: 100%; + padding-left: 10px; + border-right: solid black 1px; + text-align: center; + width: 14%; + a { + color: black; + } +} + +div .friends-content { + padding-top: 20px; + padding-bottom: 10px; +} + +div .friend-box { + margin-bottom: 15px; + padding-bottom: 0; +} + +#unfriend-btn { + background-color: #ddd; + display: inline-block; + float: right; + margin-top: 15px; + +} +#friend-name { + width: 50%; + display: inline-block; + +} + +/* PHOTOS */ +#link-box3-current{ + background-color: #ddd; + height: 100%; + border-left: solid black 1px; + border-right: solid black 1px; + margin-right: 0px; + text-align: center; + box-sizing: border-box; + padding-left: 0px; + padding-right: 0px; + width: 14%; + a { + color: black; + } +} + +div .photo-thumb { + justify-content: center; + img { + margin-left: 15%; + } +} diff --git a/app/controllers/static_pages1_controller.rb b/app/controllers/static_pages1_controller.rb new file mode 100644 index 000000000..47e9712f9 --- /dev/null +++ b/app/controllers/static_pages1_controller.rb @@ -0,0 +1,12 @@ +class StaticPages1Controller < ApplicationController + def timeline + end + def photos + end + def about + end + def about_edit + end + def friends + end +end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb new file mode 100644 index 000000000..35d2f8fa6 --- /dev/null +++ b/app/controllers/static_pages_controller.rb @@ -0,0 +1,6 @@ +class StaticPagesController < ApplicationController + def home + end + + +end diff --git a/app/helpers/static_pages1_helper.rb b/app/helpers/static_pages1_helper.rb new file mode 100644 index 000000000..a405f3f53 --- /dev/null +++ b/app/helpers/static_pages1_helper.rb @@ -0,0 +1,2 @@ +module StaticPages1Helper +end diff --git a/app/helpers/static_pages_helper.rb b/app/helpers/static_pages_helper.rb new file mode 100644 index 000000000..2d63e79e6 --- /dev/null +++ b/app/helpers/static_pages_helper.rb @@ -0,0 +1,2 @@ +module StaticPagesHelper +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index ba857f157..a1e91259c 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -3,7 +3,7 @@ Danebook <%= csrf_meta_tags %> - + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> diff --git a/app/views/static_pages/_nav_home.html.erb b/app/views/static_pages/_nav_home.html.erb new file mode 100644 index 000000000..6f1a6e3a4 --- /dev/null +++ b/app/views/static_pages/_nav_home.html.erb @@ -0,0 +1,46 @@ + diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb new file mode 100644 index 000000000..e6e2f90e2 --- /dev/null +++ b/app/views/static_pages/home.html.erb @@ -0,0 +1,91 @@ +<%= render "nav_home" %> + +
+
+
+
+
+

Connect with all your friends!

+ +

+

    +
  • See updates and photos in your newsfeed
  • +
  • Post your status for the world to see with your news feed
  • +
  • Get in touch with your friends by "friending" them
  • +
  • Like things because you're a positive person!
  • +
+

+
+
+
+ +
+

Sign Up

+
+ + +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+

Birthday

+ +
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ +
+ + +
+ +
+
+
+
diff --git a/app/views/static_pages1/_nav.html.erb b/app/views/static_pages1/_nav.html.erb new file mode 100644 index 000000000..da2f4f32d --- /dev/null +++ b/app/views/static_pages1/_nav.html.erb @@ -0,0 +1,28 @@ + diff --git a/app/views/static_pages1/about.html.erb b/app/views/static_pages1/about.html.erb new file mode 100644 index 000000000..ca48688ab --- /dev/null +++ b/app/views/static_pages1/about.html.erb @@ -0,0 +1,115 @@ +<%= render 'nav' %> + + +
+
+
+ +

Harry Potter

+
+
+ + +
+ + +
+
+
+

About

+
+
+ +
+
+ +
+
+
+

Basic Information

+
+
+
+ Birthday: +
+
+ July 31st, 1980 +
+
+
+
+ College: +
+
+ Hogwarts College +
+
+
+
+ Hometown: +
+
+ Godrick's Hollow, England +
+
+
+
+ Currently Lives: +
+
+ Godrick's Hollow, England +
+
+ +
+

Contact Information

+
+
+
+ Email: +
+
+ harry_potter@hotwarts.edu +
+
+
+
+ Telephone: +
+
+ 555-123-4567 +
+
+
+
+
+

Words to Live By

+
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut

+
+
+

About Me

+
+
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+
+
+
+
diff --git a/app/views/static_pages1/about_edit.html.erb b/app/views/static_pages1/about_edit.html.erb new file mode 100644 index 000000000..0becd3baa --- /dev/null +++ b/app/views/static_pages1/about_edit.html.erb @@ -0,0 +1,117 @@ +<%= render 'nav' %> + +
+
+
+ ' /> +

Harry Potter

+
+
+ + +
+ + +
+
+
+

Edit Profile

+
+ +
+ +
+
+
+

Basic Information

+
+
+
+ Birthday: +
+
+ +
+
+
+
+ College: +
+
+ +
+
+
+
+ Hometown: +
+
+ +
+
+
+
+ Currently Lives: +
+
+ +
+
+ +
+

Contact Information

+
+
+
+ Email: +
+
+ +
+
+
+
+ Telephone: +
+
+ +
+
+
+
+
+

Words to Live By

+
+
+ +
+
+

About Me

+
+
+ + +
+
+
+ +
+ +
+
diff --git a/app/views/static_pages1/friends.html.erb b/app/views/static_pages1/friends.html.erb new file mode 100644 index 000000000..6229a7ff2 --- /dev/null +++ b/app/views/static_pages1/friends.html.erb @@ -0,0 +1,82 @@ +<%= render 'nav' %> + + +
+
+
+ +

Harry Potter

+
+
+ + +
+ +
+
+
+

Friends

+
+ +
+ +
+
+
+ +
+ +
Hermione Granger
432 Friends
+ +
+
+
+ +
Hermione Granger
432 Friends
+ +
+
+
+ +
Hermione Granger
432 Friends
+ +
+
+
+
+
+ +
Hermione Granger
432 Friends
+ +
+
+
+ +
Hermione Granger
432 Friends
+ +
+
+
+ +
Hermione Granger
432 Friends
+ +
+
+
+
diff --git a/app/views/static_pages1/photos.html.erb b/app/views/static_pages1/photos.html.erb new file mode 100644 index 000000000..c9e9bb4c6 --- /dev/null +++ b/app/views/static_pages1/photos.html.erb @@ -0,0 +1,85 @@ +<%= render 'nav' %> + + +
+
+
+ +

Harry Potter

+
+
+ + +
+ +
+
+
+

Photos

+
+
+ +
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+ + +
+ +
+ diff --git a/app/views/static_pages1/timeline.html.erb b/app/views/static_pages1/timeline.html.erb new file mode 100644 index 000000000..92826e799 --- /dev/null +++ b/app/views/static_pages1/timeline.html.erb @@ -0,0 +1,229 @@ +<%= render 'nav' %> + +
+
+
+ +

Harry Potter

+
+
+ + +
+ + + +
+ + +
+
+
+
+
About (Text Area)
+
+
+
+
+

Born on: July 31st, 1980

+

Went to school at: Hogwarts

+

Hometown: Goodrick's Hollow, England

+

Currently Lives: Goodrick's Hollow

+
+
+
+
+
Photos (123) (Copied Text Area)
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
Friends (542) (Copied Text Area)
+
+
+
+
+
+ + + + + + +
+ +
+
+
+ +
+
+
+
Post (Copied)
+
+
+
+
+
+ +
+
+
+
+
+ +
+
+
+
+
+
Harry Potter
Posted on Thursday 6/1/2014
+
+


Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui

+
+
+
+
+
+ Like   Comment + +
+
+
+
+
+
Harry Potter
Posted on Thursday 5/21/2014
+
+


Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui

+
+
+
+
+
+ Like   Comment + +


Hermione Granger likes this.

+
+
+ +
+
+
+
Harry Potter
Posted on Thursday 5/21/2014
+
+


Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui

+
+
+
+
+
+ Unlike + +


You and Hermione Granger and 3 others like this.

+
+
+
+
+
+
+
Ron Weasley Said on Thursday 5/21/2014
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in

+ Like   3 other people like this +
+
+
+
+
+
+
+
Harry Potter Said on Friday 5/22/2014
+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in

+ Like   3 other people like this + +
+
+
+ +
+ Cancel + +
+
+ + +
+
+
diff --git a/config/database.yml b/config/database.yml index 1c1a37ca8..cd8e154fa 100644 --- a/config/database.yml +++ b/config/database.yml @@ -21,5 +21,6 @@ test: database: db/test.sqlite3 production: + adapter: postgresql <<: *default database: db/production.sqlite3 diff --git a/config/routes.rb b/config/routes.rb index 787824f88..3d401b3a4 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,11 @@ Rails.application.routes.draw do + root 'static_pages#home' + get '/timeline' => 'static_pages1#timeline' + get '/friends' => 'static_pages1#friends' + get '/photos' => 'static_pages1#photos' + get '/about' => 'static_pages1#about' + get '/about_edit' => 'static_pages1#about_edit' + + # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/test/controllers/static_pages1_controller_test.rb b/test/controllers/static_pages1_controller_test.rb new file mode 100644 index 000000000..0147f0954 --- /dev/null +++ b/test/controllers/static_pages1_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class StaticPages1ControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/static_pages_controller_test.rb b/test/controllers/static_pages_controller_test.rb new file mode 100644 index 000000000..76b4b38eb --- /dev/null +++ b/test/controllers/static_pages_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class StaticPagesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end From 823526c9b2fce29e55fa0b56b89e6beb674bac30 Mon Sep 17 00:00:00 2001 From: David Watts Date: Mon, 1 Aug 2016 11:38:41 -0700 Subject: [PATCH 05/12] change production settings for static files --- Gemfile | 1 + Gemfile.lock | 2 ++ app/assets/stylesheets/static_pages1.scss | 2 +- config/environments/production.rb | 4 +++- 4 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Gemfile b/Gemfile index 002361d8d..ab9247424 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,7 @@ group :development, :test do end group :development do + gem 'sqlite3' gem 'better_errors' gem 'binding_of_caller' # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. diff --git a/Gemfile.lock b/Gemfile.lock index 753e0dae9..8289e2ce4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -144,6 +144,7 @@ GEM actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) + sqlite3 (1.3.11) thor (0.19.1) thread_safe (0.3.5) tilt (2.0.5) @@ -181,6 +182,7 @@ DEPENDENCIES sass-rails (~> 5.0) spring spring-watcher-listen (~> 2.0.0) + sqlite3 turbolinks (~> 5) tzinfo-data uglifier (>= 1.3.0) diff --git a/app/assets/stylesheets/static_pages1.scss b/app/assets/stylesheets/static_pages1.scss index 66f0d1d26..2deb85692 100644 --- a/app/assets/stylesheets/static_pages1.scss +++ b/app/assets/stylesheets/static_pages1.scss @@ -45,7 +45,7 @@ h1{ /* ABOUT */ #cover-col { - background-image: url("cover.jpg"); + background-image: url('cover.jpg'); height: 300px; background-size: 100%; background-position: center; diff --git a/config/environments/production.rb b/config/environments/production.rb index e43500ee1..4d8d4f3cd 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -23,7 +23,9 @@ # config.assets.css_compressor = :sass # Do not fallback to assets pipeline if a precompiled asset is missed. - config.assets.compile = false + config.serve_static_assets = true + config.assets.compile = true + config.assets.digest = true # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb From bb4cead567e54435734eb514140a911a0f374db9 Mon Sep 17 00:00:00 2001 From: David J Watts Date: Wed, 24 Aug 2016 16:31:17 -0700 Subject: [PATCH 06/12] cookie auth and edit users --- Gemfile | 4 +- Gemfile.lock | 4 + .../{static_pages1.coffee => sessions.coffee} | 0 app/assets/javascripts/users.coffee | 3 + app/assets/stylesheets/sessions.scss | 3 + app/assets/stylesheets/static_pages.scss | 307 ++++++++++++++++++ .../{static_pages1.scss => users.scss} | 78 ++++- app/controllers/application_controller.rb | 47 +++ app/controllers/sessions_controller.rb | 28 ++ app/controllers/static_pages1_controller.rb | 12 - app/controllers/static_pages_controller.rb | 11 +- app/controllers/users_controller.rb | 55 ++++ app/helpers/sessions_helper.rb | 2 + app/helpers/static_pages1_helper.rb | 2 - app/helpers/users_helper.rb | 2 + app/models/user.rb | 22 ++ app/views/layouts/application.html.erb | 3 + app/views/sessions/new.html.erb | 14 + .../_nav.html.erb | 0 app/views/static_pages/_nav_home.html.erb | 46 --- .../about_edit.html.erb | 0 .../friends.html.erb | 0 app/views/static_pages/home.html.erb | 91 ------ .../photos.html.erb | 0 .../timeline.html.erb | 0 app/views/users/_nav.html.erb | 38 +++ app/views/users/_nav_home.html.erb | 54 +++ .../{static_pages1 => users}/about.html.erb | 24 +- app/views/users/about_edit.html.erb | 117 +++++++ app/views/users/new.html.erb | 101 ++++++ config/routes.rb | 16 +- db/migrate/20160824185215_create_users.rb | 10 + db/migrate/20160824211959_addtokens.rb | 6 + db/migrate/20160824213314_addsignupdetails.rb | 17 + db/migrate/20160824223109_moredate.rb | 11 + db/schema.rb | 36 ++ ...er_test.rb => sessions_controller_test.rb} | 2 +- test/controllers/users_controller_test.rb | 7 + test/fixtures/users.yml | 9 + test/models/user_test.rb | 7 + 40 files changed, 1015 insertions(+), 174 deletions(-) rename app/assets/javascripts/{static_pages1.coffee => sessions.coffee} (100%) create mode 100644 app/assets/javascripts/users.coffee create mode 100644 app/assets/stylesheets/sessions.scss rename app/assets/stylesheets/{static_pages1.scss => users.scss} (71%) create mode 100644 app/controllers/sessions_controller.rb delete mode 100644 app/controllers/static_pages1_controller.rb create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/sessions_helper.rb delete mode 100644 app/helpers/static_pages1_helper.rb create mode 100644 app/helpers/users_helper.rb create mode 100644 app/models/user.rb create mode 100644 app/views/sessions/new.html.erb rename app/views/{static_pages1 => static_pages}/_nav.html.erb (100%) delete mode 100644 app/views/static_pages/_nav_home.html.erb rename app/views/{static_pages1 => static_pages}/about_edit.html.erb (100%) rename app/views/{static_pages1 => static_pages}/friends.html.erb (100%) delete mode 100644 app/views/static_pages/home.html.erb rename app/views/{static_pages1 => static_pages}/photos.html.erb (100%) rename app/views/{static_pages1 => static_pages}/timeline.html.erb (100%) create mode 100644 app/views/users/_nav.html.erb create mode 100644 app/views/users/_nav_home.html.erb rename app/views/{static_pages1 => users}/about.html.erb (76%) create mode 100644 app/views/users/about_edit.html.erb create mode 100644 app/views/users/new.html.erb create mode 100644 db/migrate/20160824185215_create_users.rb create mode 100644 db/migrate/20160824211959_addtokens.rb create mode 100644 db/migrate/20160824213314_addsignupdetails.rb create mode 100644 db/migrate/20160824223109_moredate.rb create mode 100644 db/schema.rb rename test/controllers/{static_pages1_controller_test.rb => sessions_controller_test.rb} (53%) create mode 100644 test/controllers/users_controller_test.rb create mode 100644 test/fixtures/users.yml create mode 100644 test/models/user_test.rb diff --git a/Gemfile b/Gemfile index ab9247424..681a0625d 100644 --- a/Gemfile +++ b/Gemfile @@ -27,8 +27,8 @@ gem 'jbuilder', '~> 2.5' # Use Redis adapter to run Action Cable in production # gem 'redis', '~> 3.0' # Use ActiveModel has_secure_password -# gem 'bcrypt', '~> 3.1.7' - +gem 'bcrypt', '~> 3.1.7' +gem 'hirb' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development diff --git a/Gemfile.lock b/Gemfile.lock index 8289e2ce4..71a6e8366 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,6 +39,7 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) arel (7.1.1) + bcrypt (3.1.11) better_errors (2.1.1) coderay (>= 1.0.0) erubis (>= 2.6.6) @@ -62,6 +63,7 @@ GEM ffi (1.9.14) globalid (0.3.7) activesupport (>= 4.1.0) + hirb (0.7.3) i18n (0.7.0) jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) @@ -168,10 +170,12 @@ PLATFORMS ruby DEPENDENCIES + bcrypt (~> 3.1.7) better_errors binding_of_caller byebug coffee-rails (~> 4.2) + hirb jbuilder (~> 2.5) jquery-rails listen (~> 3.0.5) diff --git a/app/assets/javascripts/static_pages1.coffee b/app/assets/javascripts/sessions.coffee similarity index 100% rename from app/assets/javascripts/static_pages1.coffee rename to app/assets/javascripts/sessions.coffee diff --git a/app/assets/javascripts/users.coffee b/app/assets/javascripts/users.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/users.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 000000000..7bef9cf82 --- /dev/null +++ b/app/assets/stylesheets/sessions.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/static_pages.scss b/app/assets/stylesheets/static_pages.scss index acdf2de40..47fc04361 100644 --- a/app/assets/stylesheets/static_pages.scss +++ b/app/assets/stylesheets/static_pages.scss @@ -66,3 +66,310 @@ body { text-align: center; } /*# sourceMappingURL=styles.css.map */ + +// new stuff! + +body { +padding-top: 70px; + +/* NAV */ +} + +#navcolor { + background-color: #418EFF; + height: 60px; +} + +h1{ + display: inline; +} + +#logoimg { + float: left; + margin-top: 10px; + padding-left: 0px; +} + +#logotxt { + padding-left: 20px; + line-height: 20px; + padding-top: 8px; + padding-right: 0px; + color: white; +} + +#search { + width: 100%; + margin-top: 5px; +} + +#harry { + color: white; + float: right; +} + +/* ABOUT */ + +#cover-col { + background-image: url('cover.jpg'); + height: 300px; + background-size: 100%; + background-position: center; + +} + +#user-img { + margin-top: 170px; + margin-right: 30px; + float: left; +} + +#about-name { + color: white; + margin-top: 255px; +} + +h3 { + margin: 20px 0; +} +#about-content { +h5 { + margin: 20px 0; + margin-left: 15px; +} +} + + +#about-links { + border: solid black 1px; + height: 40px; +} + +#link-box1 { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; +} + +#link-box2 { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; +} + +#link-box3{ + height: 100%; + border-left: solid black 1px; + border-right: solid black 1px; + margin-right: 0px; + text-align: center; + box-sizing: border-box; + padding-left: 0px; + padding-right: 0px; + width: 14%; + a { + color: black; + } +} +#link-box4 { + height: 100%; + padding-left: 10px; + border-right: solid black 1px; + text-align: center; + width: 14%; + a { + color: black; + } +} + +#edit-link { + float: right; + text-align: right; +} + +#about-row { + background-color: #ddd; + margin-top: 20px; + height: 45px; + padding: 0px; + border: solid black 1px; +} + +#about-col { + text-align: center; + padding: 0px; + margin-top: 7px; +} + +#edit-btn { + float: right; + margin-top: 10px; +} + +#about-content { + border: solid black 1px; + border-top: 0; + margin-bottom: 40px; +} + +/* EDIT */ + +#save-btn { + margin-top: 50px; + margin-bottom: 100px; + line-height: 40px; +} + +#link-box2-current { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; + background-color: #ddd; +} + +/* TIMELINE */ + +#link-box1-current { + border-left: solid black 1px; + text-align: center; + width: 12%; + a { + color: black; + } + height: 100%; + line-height: 20px; + background-color: #ddd; +} + +div .box-header { + background-color: #ddd; + text-align: center; + margin-top: 15px; + border: solid black 1px; + border-bottom: 0; +} + +div .box-content { + border: solid black 1px; + padding-top: 15px; + padding-bottom: 8px; +} + +div .box-foot { + background-color: #ddd; + border: solid black 1px; + border-top: 0; + margin-bottom: 30px; +} + +div .center-link { + text-align: center; + text-decoration: underline; + margin-bottom: 10px; +} + +div .user-img { + width: 50px; + height: 50px; + overflow: hidden; + display: inline-block; + float: left; + margin-right: 10px; +} + +div .com-img { + width: 50px; + height: 50px; + overflow: hidden; + float: right; + +} +div .delete-link { + display: inline-block; + float: right; +} + +#post-btn { + float: right; + margin: 8px 0; +} + +#comment-area { + margin-top: 20px; +} + +/* FRIENDS */ + +#link-box4-current { + background-color: #ddd; + height: 100%; + padding-left: 10px; + border-right: solid black 1px; + text-align: center; + width: 14%; + a { + color: black; + } +} + +div .friends-content { + padding-top: 20px; + padding-bottom: 10px; +} + +div .friend-box { + margin-bottom: 15px; + padding-bottom: 0; +} + +#unfriend-btn { + background-color: #ddd; + display: inline-block; + float: right; + margin-top: 15px; + +} +#friend-name { + width: 50%; + display: inline-block; + +} + +/* PHOTOS */ +#link-box3-current{ + background-color: #ddd; + height: 100%; + border-left: solid black 1px; + border-right: solid black 1px; + margin-right: 0px; + text-align: center; + box-sizing: border-box; + padding-left: 0px; + padding-right: 0px; + width: 14%; + a { + color: black; + } +} + +div .photo-thumb { + justify-content: center; + img { + margin-left: 15%; + } +} diff --git a/app/assets/stylesheets/static_pages1.scss b/app/assets/stylesheets/users.scss similarity index 71% rename from app/assets/stylesheets/static_pages1.scss rename to app/assets/stylesheets/users.scss index 2deb85692..2e7cbd5fe 100644 --- a/app/assets/stylesheets/static_pages1.scss +++ b/app/assets/stylesheets/users.scss @@ -1,7 +1,81 @@ -// Place all the styles related to the StaticPages1 controller here. +// Place all the styles related to the users controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/ -/* */ + +// Place all the styles related to the StaticPages controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ +/* */ +body { + padding-top: 50px; } + body nav { + background-color: #418EFF; + height: 75px; + border-bottom: solid black 2px; } + body nav #brand { + color: white; + font-size: 38px; + font-weight: 550; + line-height: 38px; + padding-left: 30px; } + body nav label { + color: white; } + body #navbar { + padding-right: 15px; + border-bottom: solid black 2px; + padding-bottom: 7px; } + body #submit { + padding-top: 5px; } + body #submit button { + border: 2px black solid; + box-shadow: 3px 3px; } + body #password { + color: black; + padding: 0px 10px; } + body #password input { + border: 2px black solid; } + body #email { + color: black; + } + body #email input { + border: 2px black solid; } + body h2 { + font-weight: 600; } + body h1 { + font-weight: 600; } + body h4 { + text-align: left; } + body #gender { + text-align: left; } + body main header { + text-align: left; } + body main header li { + text-align: left; + padding: 10px; + font-size: 14px; } + body main #content { + padding-top: 50px; + padding-bottom: 40px; + padding-left: 30px; } + body main #signup { + padding-top: 20px; } + +#sign-btn { + border: 1px black solid; + box-shadow: 2px 2px; } + +#last { + display: inline; + padding-left: 23px; } + +#main-wrapper { + padding-top: 15px; + padding-bottom: 15px; + text-align: center; } + +/*# sourceMappingURL=styles.css.map */ + +// new stuff! body { padding-top: 70px; diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 1c07694e9..6f22a6117 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,3 +1,50 @@ class ApplicationController < ActionController::Base protect_from_forgery with: :exception + + before_action :require_login, :except => [:new, :create] + + + private + def require_current_user + unless params[:id] == current_user.id.to_s + flash[:error] = "You're not authorized to view this" + redirect_to root_url + end + end + + def require_login + unless signed_in_user? + flash[:error] = "Not authorized, please sign in!" + redirect_to login_path #< Remember this is a custom route + end + end + + def current_user + @current_user ||= User.find_by_auth_token(cookies[:auth_token]) if cookies[:auth_token] + end + helper_method :current_user + + def signed_in_user? + !!current_user + end + helper_method :signed_in_user? + + def sign_in(user) + user.regenerate_auth_token + cookies[:auth_token] = user.auth_token + @current_user = user + end + + def permanent_sign_in(user) + user.regenerate_auth_token + cookies.permanent[:auth_token] = user.auth_token + @current_user = user + end + + + def sign_out + @current_user = nil + cookies.delete(:auth_token) + end + end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 000000000..203901c46 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,28 @@ +class SessionsController < ApplicationController + skip_before_action :require_login, :only => [:new, :create] + + def new + end + + def create + @user = User.find_by_email(params[:email]) + if @user && @user.authenticate(params[:password]) + if params[:remember_me] + permanent_sign_in(@user) + else + sign_in(@user) + end + flash[:success] = "You've successfully signed in" + redirect_to root_url + else + flash.now[:error] = "We couldn't sign you in" + render :new + end + end + + def destroy + sign_out + flash[:success] = "You've successfully signed out" + redirect_to root_url + end +end diff --git a/app/controllers/static_pages1_controller.rb b/app/controllers/static_pages1_controller.rb deleted file mode 100644 index 47e9712f9..000000000 --- a/app/controllers/static_pages1_controller.rb +++ /dev/null @@ -1,12 +0,0 @@ -class StaticPages1Controller < ApplicationController - def timeline - end - def photos - end - def about - end - def about_edit - end - def friends - end -end diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index 35d2f8fa6..e02cccd49 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -1,6 +1,15 @@ class StaticPagesController < ApplicationController def home end - + def timeline + end + def photos + end + def about + end + def about_edit + end + def friends + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 000000000..1c25c8aec --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,55 @@ +class UsersController < ApplicationController + skip_before_action :require_login, :only => [:new, :create, :show] + before_action :require_current_user, :only => [:edit, :update, :destroy] + + def new + @user = User.new + end + + def create + @user = User.new(user_params) + if @user.save + sign_in(@user) + flash[:notice] = "User successfully created" + render :about + else + flash[:notice] = "User not created, fix your errors" + render :new + end + end + + def update + @user = User.find(params[:id]) + if current_user.update(user_params) + flash[:notice] = "User succesfully updated" + @user = User.find(params[:id]) + render "about" + else + flash[:notice] = "user not created, fix your errors" + render :edit + end + end + + def edit + @current_user + render "about_edit" + end + + def show + @user = User.find(params[:id]) + render "about" + end + + def destroy + current_user.destroy + sign_out + redirect_to root + end + + private + + def user_params + params.require(:user).permit(:first_name, :last_name, :b_day, :b_month, :b_year, :gender, :email, :password, :password_confirmation, :college, :home, :lives, :phone, :words, :bio) + end + +end diff --git a/app/helpers/sessions_helper.rb b/app/helpers/sessions_helper.rb new file mode 100644 index 000000000..309f8b2eb --- /dev/null +++ b/app/helpers/sessions_helper.rb @@ -0,0 +1,2 @@ +module SessionsHelper +end diff --git a/app/helpers/static_pages1_helper.rb b/app/helpers/static_pages1_helper.rb deleted file mode 100644 index a405f3f53..000000000 --- a/app/helpers/static_pages1_helper.rb +++ /dev/null @@ -1,2 +0,0 @@ -module StaticPages1Helper -end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb new file mode 100644 index 000000000..2310a240d --- /dev/null +++ b/app/helpers/users_helper.rb @@ -0,0 +1,2 @@ +module UsersHelper +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..9e86ae081 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,22 @@ +class User < ApplicationRecord + before_create :generate_token + + has_secure_password + + validates :password, + :length => { :in => 3..24 }, + :allow_nil => true + + def generate_token + begin + self[:auth_token] = SecureRandom.urlsafe_base64 + end while User.exists?(:auth_token => self[:auth_token]) + end + + def regenerate_auth_token + self.auth_token = nil + generate_token + save! + end + +end diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index a1e91259c..204359770 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -9,6 +9,9 @@ + <% flash.each do |key, value| %> +

<%= content_tag :div, value, class: "flash #{key}" %>

+ <% end %> <%= yield %> diff --git a/app/views/sessions/new.html.erb b/app/views/sessions/new.html.erb new file mode 100644 index 000000000..0f49f52bf --- /dev/null +++ b/app/views/sessions/new.html.erb @@ -0,0 +1,14 @@ +<%= form_tag session_path do %> + + <%= label_tag do %> + Email + <%= text_field_tag :email %> + <% end %> + + <%= label_tag do %> + Password + <%= password_field_tag :password %> + <% end %> + + <%= submit_tag "Log in" %> +<% end %> diff --git a/app/views/static_pages1/_nav.html.erb b/app/views/static_pages/_nav.html.erb similarity index 100% rename from app/views/static_pages1/_nav.html.erb rename to app/views/static_pages/_nav.html.erb diff --git a/app/views/static_pages/_nav_home.html.erb b/app/views/static_pages/_nav_home.html.erb deleted file mode 100644 index 6f1a6e3a4..000000000 --- a/app/views/static_pages/_nav_home.html.erb +++ /dev/null @@ -1,46 +0,0 @@ - diff --git a/app/views/static_pages1/about_edit.html.erb b/app/views/static_pages/about_edit.html.erb similarity index 100% rename from app/views/static_pages1/about_edit.html.erb rename to app/views/static_pages/about_edit.html.erb diff --git a/app/views/static_pages1/friends.html.erb b/app/views/static_pages/friends.html.erb similarity index 100% rename from app/views/static_pages1/friends.html.erb rename to app/views/static_pages/friends.html.erb diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb deleted file mode 100644 index e6e2f90e2..000000000 --- a/app/views/static_pages/home.html.erb +++ /dev/null @@ -1,91 +0,0 @@ -<%= render "nav_home" %> - -
-
-
-
-
-

Connect with all your friends!

- -

-

    -
  • See updates and photos in your newsfeed
  • -
  • Post your status for the world to see with your news feed
  • -
  • Get in touch with your friends by "friending" them
  • -
  • Like things because you're a positive person!
  • -
-

-
-
-
- -
-

Sign Up

-
- - -
- -
- -
-
- -
-
- -
-
- -
-
- -
-
-

Birthday

- -
- -
-
- -
-
- -
-
-
- - -
-
- -
- - -
- -
-
-
-
diff --git a/app/views/static_pages1/photos.html.erb b/app/views/static_pages/photos.html.erb similarity index 100% rename from app/views/static_pages1/photos.html.erb rename to app/views/static_pages/photos.html.erb diff --git a/app/views/static_pages1/timeline.html.erb b/app/views/static_pages/timeline.html.erb similarity index 100% rename from app/views/static_pages1/timeline.html.erb rename to app/views/static_pages/timeline.html.erb diff --git a/app/views/users/_nav.html.erb b/app/views/users/_nav.html.erb new file mode 100644 index 000000000..4978821f8 --- /dev/null +++ b/app/views/users/_nav.html.erb @@ -0,0 +1,38 @@ + diff --git a/app/views/users/_nav_home.html.erb b/app/views/users/_nav_home.html.erb new file mode 100644 index 000000000..c630d13cc --- /dev/null +++ b/app/views/users/_nav_home.html.erb @@ -0,0 +1,54 @@ + diff --git a/app/views/static_pages1/about.html.erb b/app/views/users/about.html.erb similarity index 76% rename from app/views/static_pages1/about.html.erb rename to app/views/users/about.html.erb index ca48688ab..7a7b3e037 100644 --- a/app/views/static_pages1/about.html.erb +++ b/app/views/users/about.html.erb @@ -23,7 +23,7 @@
Friends(542)
@@ -35,8 +35,10 @@

About

- -
+ <% if current_user && params[:id] == current_user.id.to_s %> + <%= link_to "Edit your Profile", edit_user_path(@user), type: "button", id: "edit_btn", class: "btn btn-primary" %> + <%end%> +
@@ -49,7 +51,7 @@ Birthday:
- July 31st, 1980 + <%="#{@user.b_month}/#{@user.b_day}/#{@user.b_year}"%>
@@ -57,7 +59,7 @@ College:
- Hogwarts College + <%= @user.college %>
@@ -65,7 +67,7 @@ Hometown:
- Godrick's Hollow, England + <%= @user.home %>
@@ -73,7 +75,7 @@ Currently Lives:
- Godrick's Hollow, England + <%= @user.lives %>
@@ -85,7 +87,7 @@ Email:
- harry_potter@hotwarts.edu + <%= @user.email %>
@@ -93,7 +95,7 @@ Telephone:
- 555-123-4567 + <%= @user.phone %>
@@ -102,13 +104,13 @@

Words to Live By

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut

+

<%=@user.words%>

About Me

-

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

+

<%= @user.bio %>

diff --git a/app/views/users/about_edit.html.erb b/app/views/users/about_edit.html.erb new file mode 100644 index 000000000..832dd7075 --- /dev/null +++ b/app/views/users/about_edit.html.erb @@ -0,0 +1,117 @@ +<%= render 'nav' %> + +
+
+
+ ' /> +

Harry Potter

+
+
+ + +
+ + +
+
+
+

Edit Profile

+
+ +
+ <%= form_for @current_user do |u| %> +
+
+
+

Basic Information

+
+
+
+ Birthday: +
+
+ <%= "#{@current_user.b_month}/#{@current_user.b_day}/#{@current_user.b_year}"%> +
+
+
+
+ <%=u.label :college, value: "College: " %> +
+
+ <%= u.text_field :college %> +
+
+
+
+ <%=u.label :home, value: "Hometown: " %> +
+
+ <%= u.text_field :home %> +
+
+
+
+ <%=u.label :lives, value: "Currently Lives: " %> +
+
+ <%= u.text_field :lives %> +
+
+ +
+

Contact Information

+
+
+
+ Email: +
+
+ <%= @current_user.email %> +
+
+
+
+ <%= u.label :phone, value: "Telephone: " %> +
+
+ <%= u.text_field :phone %> +
+
+
+
+
+

<%= u.label :words, value: "Words to Live By: " %>

+
+
+ <%= u.text_area :words, class: "form-control", width: "100%" %> +
+
+

<%= u.label :bio, value: "About Me: " %>

+
+
+ <%= u.text_area :bio, class: "form-control", width: "100%" %> + +
+
+
+ <%= u.submit "Save Changes", class: "btn-primary btn-block btn-large"%> +
+ <%end%> +
+
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 000000000..39d288906 --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,101 @@ +<%= render "nav_home" %> + +
+
+
+
+
+

Connect with all your friends!

+ +

+

    +
  • See updates and photos in your newsfeed
  • +
  • Post your status for the world to see with your news feed
  • +
  • Get in touch with your friends by "friending" them
  • +
  • Like things because you're a positive person!
  • +
+

+
+
+
+ +
+

Sign Up

+
+ + + <%=form_for @user do |f| %> + <% if @user.errors.any? %> +
+

<%= pluralize(@user.errors.count, "error") %> prohibited this user from being saved:

+
    + <% @user.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+ <%=f.text_field :first_name, placeholder: "First Name", class: "form-control" %> +
+
+ <%=f.text_field :last_name, placeholder: "Last Name", class: "form-control" %> +
+
+ <%=f.text_field :email, placeholder: "email", class: "form-control" %> +
+
+ <%=f.password_field :password, placeholder: "password", class: "form-control" %> +
+
+ <%=f.password_field :password_confirmation, placeholder: "password confirmation", class: "form-control" %> +
+
+

Birthday

+ +
+ +
+
+ +
+
+ +
+
+
+ + +
+
+ <%=f.submit "Sign Up!", :class => "btn-block btn-success", :id => "sign-btn" %> +
+<%end%> + + + +
+
+
+
diff --git a/config/routes.rb b/config/routes.rb index 3d401b3a4..d6abbcb7a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,15 @@ Rails.application.routes.draw do - root 'static_pages#home' - get '/timeline' => 'static_pages1#timeline' - get '/friends' => 'static_pages1#friends' - get '/photos' => 'static_pages1#photos' - get '/about' => 'static_pages1#about' - get '/about_edit' => 'static_pages1#about_edit' + root 'users#new' + get '/timeline' => 'static_pages#timeline' + get '/friends' => 'static_pages#friends' + get '/photos' => 'static_pages#photos' + get '/about' => 'users#about' + get '/about_edit' => 'static_pages#about_edit' + resource :session, :only => [:new, :create, :destroy] + get "login" => "sessions#new" + delete "logout" => "sessions#destroy" + resources :users, :only => [:new, :create, :show, :edit, :update] # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/db/migrate/20160824185215_create_users.rb b/db/migrate/20160824185215_create_users.rb new file mode 100644 index 000000000..1b80dcf73 --- /dev/null +++ b/db/migrate/20160824185215_create_users.rb @@ -0,0 +1,10 @@ +class CreateUsers < ActiveRecord::Migration[5.0] + def change + create_table :users do |t| + t.string :email, :null => false + t.string :password_digest, :null => false + + t.timestamps + end + end +end diff --git a/db/migrate/20160824211959_addtokens.rb b/db/migrate/20160824211959_addtokens.rb new file mode 100644 index 000000000..a5ed697ae --- /dev/null +++ b/db/migrate/20160824211959_addtokens.rb @@ -0,0 +1,6 @@ +class Addtokens < ActiveRecord::Migration[5.0] + def change + add_column :users, :auth_token, :string + add_index :users, :auth_token, :unique => true + end +end diff --git a/db/migrate/20160824213314_addsignupdetails.rb b/db/migrate/20160824213314_addsignupdetails.rb new file mode 100644 index 000000000..17c60108d --- /dev/null +++ b/db/migrate/20160824213314_addsignupdetails.rb @@ -0,0 +1,17 @@ +class Addsignupdetails < ActiveRecord::Migration[5.0] + def change + add_column :users, :first_name, :string + change_column :users, :first_name, :string, :null => false + add_column :users, :last_name, :string + add_column :users, :gender, :string + add_column :users, :b_month, :integer + add_column :users, :b_day, :integer + add_column :users, :b_year, :integer + change_column :users, :last_name, :string, :null => false + change_column :users, :gender, :string, :null => false + change_column :users, :b_month, :integer, :null => false + change_column :users, :b_day, :integer, :null => false + change_column :users, :b_year, :integer, :null => false + + end +end diff --git a/db/migrate/20160824223109_moredate.rb b/db/migrate/20160824223109_moredate.rb new file mode 100644 index 000000000..e493d464e --- /dev/null +++ b/db/migrate/20160824223109_moredate.rb @@ -0,0 +1,11 @@ +class Moredate < ActiveRecord::Migration[5.0] + def change + add_column :users, :home, :string + add_column :users, :lives, :string + add_column :users, :phone, :string + add_column :users, :college, :string + add_column :users, :words, :text + add_column :users, :bio, :text + + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..7843fbaae --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,36 @@ +# This file is auto-generated from the current state of the database. Instead +# of editing this file, please use the migrations feature of Active Record to +# incrementally modify your database, and then regenerate this schema definition. +# +# Note that this schema.rb definition is the authoritative source for your +# database schema. If you need to create the application database on another +# system, you should be using db:schema:load, not running all the migrations +# from scratch. The latter is a flawed and unsustainable approach (the more migrations +# you'll amass, the slower it'll run and the greater likelihood for issues). +# +# It's strongly recommended that you check this file into your version control system. + +ActiveRecord::Schema.define(version: 20160824223109) do + + create_table "users", force: :cascade do |t| + t.string "email", null: false + t.string "password_digest", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "auth_token" + t.string "first_name", null: false + t.string "last_name", null: false + t.string "gender", null: false + t.integer "b_month", null: false + t.integer "b_day", null: false + t.integer "b_year", null: false + t.string "home" + t.string "lives" + t.string "phone" + t.string "college" + t.text "words" + t.text "bio" + t.index ["auth_token"], name: "index_users_on_auth_token", unique: true + end + +end diff --git a/test/controllers/static_pages1_controller_test.rb b/test/controllers/sessions_controller_test.rb similarity index 53% rename from test/controllers/static_pages1_controller_test.rb rename to test/controllers/sessions_controller_test.rb index 0147f0954..6135ce6af 100644 --- a/test/controllers/static_pages1_controller_test.rb +++ b/test/controllers/sessions_controller_test.rb @@ -1,6 +1,6 @@ require 'test_helper' -class StaticPages1ControllerTest < ActionDispatch::IntegrationTest +class SessionsControllerTest < ActionDispatch::IntegrationTest # test "the truth" do # assert true # end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb new file mode 100644 index 000000000..6c3da770c --- /dev/null +++ b/test/controllers/users_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UsersControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 000000000..0821459be --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + email: MyString + password_digest: MyString + +two: + email: MyString + password_digest: MyString diff --git a/test/models/user_test.rb b/test/models/user_test.rb new file mode 100644 index 000000000..82f61e010 --- /dev/null +++ b/test/models/user_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class UserTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From d2ba165d92c4b59a013195ff7d3bd71e85d0cae7 Mon Sep 17 00:00:00 2001 From: David J Watts Date: Wed, 24 Aug 2016 16:51:26 -0700 Subject: [PATCH 07/12] redirect fix upon user create --- app/controllers/users_controller.rb | 4 ++-- app/views/users/about.html.erb | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 1c25c8aec..f4fee041a 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -11,7 +11,7 @@ def create if @user.save sign_in(@user) flash[:notice] = "User successfully created" - render :about + redirect_to user_path(@user) else flash[:notice] = "User not created, fix your errors" render :new @@ -23,7 +23,7 @@ def update if current_user.update(user_params) flash[:notice] = "User succesfully updated" @user = User.find(params[:id]) - render "about" + redirect_to user_path(@user) else flash[:notice] = "user not created, fix your errors" render :edit diff --git a/app/views/users/about.html.erb b/app/views/users/about.html.erb index 7a7b3e037..4d581ad7a 100644 --- a/app/views/users/about.html.erb +++ b/app/views/users/about.html.erb @@ -23,7 +23,8 @@
Friends(542)
@@ -35,7 +36,7 @@

About

- <% if current_user && params[:id] == current_user.id.to_s %> + <% if current_user && @user == current_user %> <%= link_to "Edit your Profile", edit_user_path(@user), type: "button", id: "edit_btn", class: "btn btn-primary" %> <%end%>
From 3481786248a23e045ad54e7e82208a6fe0580fa3 Mon Sep 17 00:00:00 2001 From: David J Watts Date: Mon, 29 Aug 2016 18:47:41 -0700 Subject: [PATCH 08/12] finish posts likes comments --- app/assets/javascripts/comments.coffee | 3 + app/assets/javascripts/likes.coffee | 3 + app/assets/javascripts/posts.coffee | 3 + app/assets/stylesheets/comments.scss | 3 + app/assets/stylesheets/likes.scss | 3 + app/assets/stylesheets/posts.scss | 3 + app/controllers/application_controller.rb | 12 + app/controllers/comments_controller.rb | 13 ++ app/controllers/likes_controller.rb | 28 +++ app/controllers/posts_controller.rb | 31 +++ app/controllers/sessions_controller.rb | 2 +- app/controllers/users_controller.rb | 13 +- app/helpers/comments_helper.rb | 2 + app/helpers/likes_helper.rb | 2 + app/helpers/posts_helper.rb | 2 + app/helpers/users_helper.rb | 1 + app/models/comment.rb | 7 + app/models/like.rb | 4 + app/models/post.rb | 11 + app/models/user.rb | 13 +- app/views/users/timeline.html.erb | 226 +++++++++++++++++++ config/routes.rb | 10 +- db/migrate/20160829182205_create_posts.rb | 13 ++ db/migrate/20160829225937_create_likes.rb | 12 + db/migrate/20160829230603_likpoly.rb | 6 + db/migrate/20160829231113_indexpoly.rb | 5 + db/migrate/20160830001934_create_comments.rb | 14 ++ db/migrate/20160830002216_addbody.rb | 6 + db/schema.rb | 28 ++- test/controllers/comments_controller_test.rb | 7 + test/controllers/likes_controller_test.rb | 7 + test/controllers/posts_controller_test.rb | 7 + test/fixtures/comments.yml | 11 + test/fixtures/likes.yml | 9 + test/fixtures/posts.yml | 9 + test/models/comment_test.rb | 7 + test/models/like_test.rb | 7 + test/models/post_test.rb | 7 + 38 files changed, 541 insertions(+), 9 deletions(-) create mode 100644 app/assets/javascripts/comments.coffee create mode 100644 app/assets/javascripts/likes.coffee create mode 100644 app/assets/javascripts/posts.coffee create mode 100644 app/assets/stylesheets/comments.scss create mode 100644 app/assets/stylesheets/likes.scss create mode 100644 app/assets/stylesheets/posts.scss create mode 100644 app/controllers/comments_controller.rb create mode 100644 app/controllers/likes_controller.rb create mode 100644 app/controllers/posts_controller.rb create mode 100644 app/helpers/comments_helper.rb create mode 100644 app/helpers/likes_helper.rb create mode 100644 app/helpers/posts_helper.rb create mode 100644 app/models/comment.rb create mode 100644 app/models/like.rb create mode 100644 app/models/post.rb create mode 100644 app/views/users/timeline.html.erb create mode 100644 db/migrate/20160829182205_create_posts.rb create mode 100644 db/migrate/20160829225937_create_likes.rb create mode 100644 db/migrate/20160829230603_likpoly.rb create mode 100644 db/migrate/20160829231113_indexpoly.rb create mode 100644 db/migrate/20160830001934_create_comments.rb create mode 100644 db/migrate/20160830002216_addbody.rb create mode 100644 test/controllers/comments_controller_test.rb create mode 100644 test/controllers/likes_controller_test.rb create mode 100644 test/controllers/posts_controller_test.rb create mode 100644 test/fixtures/comments.yml create mode 100644 test/fixtures/likes.yml create mode 100644 test/fixtures/posts.yml create mode 100644 test/models/comment_test.rb create mode 100644 test/models/like_test.rb create mode 100644 test/models/post_test.rb diff --git a/app/assets/javascripts/comments.coffee b/app/assets/javascripts/comments.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/comments.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/likes.coffee b/app/assets/javascripts/likes.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/likes.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/posts.coffee b/app/assets/javascripts/posts.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/posts.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/comments.scss b/app/assets/stylesheets/comments.scss new file mode 100644 index 000000000..3722c124e --- /dev/null +++ b/app/assets/stylesheets/comments.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the comments controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/likes.scss b/app/assets/stylesheets/likes.scss new file mode 100644 index 000000000..1f08d329f --- /dev/null +++ b/app/assets/stylesheets/likes.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the likes controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/posts.scss b/app/assets/stylesheets/posts.scss new file mode 100644 index 000000000..1a7e15390 --- /dev/null +++ b/app/assets/stylesheets/posts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the posts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 6f22a6117..16e3c2987 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -12,6 +12,11 @@ def require_current_user end end + def correct_user? + params[:id].nil? || params[:id] == current_user.id.to_s + end + helper_method :correct_user? + def require_login unless signed_in_user? flash[:error] = "Not authorized, please sign in!" @@ -47,4 +52,11 @@ def sign_out cookies.delete(:auth_token) end + def login_redirect + redirect_to timeline_path if signed_in_user? + end + helper_method :login_redirect + + + end diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb new file mode 100644 index 000000000..497752752 --- /dev/null +++ b/app/controllers/comments_controller.rb @@ -0,0 +1,13 @@ +class CommentsController < ApplicationController + + def destroy + + @comment = Comment.find(params[:id]) + if @comment.destroy + flash[:success] = "Your Comment has been deleted!" + else + flash[:error] = "Error! The Comment lives on!" + end + redirect_to :back + end +end diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb new file mode 100644 index 000000000..c7f74a0dd --- /dev/null +++ b/app/controllers/likes_controller.rb @@ -0,0 +1,28 @@ +class LikesController < ApplicationController + def create + @like = Like.new(like_params) + @like.liker_id = current_user.id + if @like.save + flash[:notice] = "Like successfully created" + redirect_to :back + else + flash[:notice] = "like has failed" + redirect_to :back + end + end + + def destroy + @like = Like.find(params[:id]) + if @like.destroy + flash[:success] = "Your like has been deleted!" + else + flash[:error] = "Error! The like lives on!" + end + redirect_to :back + + end + + def like_params + params.permit(:likable_id, :likable_type) + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb new file mode 100644 index 000000000..b157e8e58 --- /dev/null +++ b/app/controllers/posts_controller.rb @@ -0,0 +1,31 @@ +class PostsController < ApplicationController + + def update + @post = Post.find(params[:id]) + if @post.update(post_params) + flash[:notice] = "post succesfully updated" + redirect_to :back + else + flash[:notice] = "post not updated, fix your errors" + redirect_to :back + end + + end + + def destroy + + @post = Post.find(params[:id]) + @user = @post.author + if @post.destroy + flash[:success] = "Your Post has been deleted!" + else + flash[:error] = "Error! The Post lives on!" + end + redirect_to :back + end + + def post_params + params.require(:post).permit( :comments_attributes => [ :body, :commentable_id, :commentable_type, :commenter_id ]) + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 203901c46..e31187c3e 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,7 +13,7 @@ def create sign_in(@user) end flash[:success] = "You've successfully signed in" - redirect_to root_url + redirect_to :back else flash.now[:error] = "We couldn't sign you in" render :new diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index f4fee041a..deb48382d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -3,15 +3,21 @@ class UsersController < ApplicationController before_action :require_current_user, :only => [:edit, :update, :destroy] def new + login_redirect @user = User.new end + def timeline + params[:id].nil? ? @user = current_user : @user = User.find(params[:id]) + + end + def create @user = User.new(user_params) if @user.save sign_in(@user) flash[:notice] = "User successfully created" - redirect_to user_path(@user) + redirect_to :back else flash[:notice] = "User not created, fix your errors" render :new @@ -20,10 +26,11 @@ def create def update @user = User.find(params[:id]) + if current_user.update(user_params) flash[:notice] = "User succesfully updated" @user = User.find(params[:id]) - redirect_to user_path(@user) + redirect_to :back else flash[:notice] = "user not created, fix your errors" render :edit @@ -49,7 +56,7 @@ def destroy private def user_params - params.require(:user).permit(:first_name, :last_name, :b_day, :b_month, :b_year, :gender, :email, :password, :password_confirmation, :college, :home, :lives, :phone, :words, :bio) + params.require(:user).permit(:first_name, :last_name, :b_day, :b_month, :b_year, :gender, :email, :password, :password_confirmation, :college, :home, :lives, :phone, :words, :bio, :posts_attributes => [:id, :body, :author_id, :user_id ] ) end end diff --git a/app/helpers/comments_helper.rb b/app/helpers/comments_helper.rb new file mode 100644 index 000000000..0ec9ca5f2 --- /dev/null +++ b/app/helpers/comments_helper.rb @@ -0,0 +1,2 @@ +module CommentsHelper +end diff --git a/app/helpers/likes_helper.rb b/app/helpers/likes_helper.rb new file mode 100644 index 000000000..a78a75964 --- /dev/null +++ b/app/helpers/likes_helper.rb @@ -0,0 +1,2 @@ +module LikesHelper +end diff --git a/app/helpers/posts_helper.rb b/app/helpers/posts_helper.rb new file mode 100644 index 000000000..a7b8cec89 --- /dev/null +++ b/app/helpers/posts_helper.rb @@ -0,0 +1,2 @@ +module PostsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 2310a240d..44e7820a1 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,2 +1,3 @@ module UsersHelper + end diff --git a/app/models/comment.rb b/app/models/comment.rb new file mode 100644 index 000000000..48ee5b2d6 --- /dev/null +++ b/app/models/comment.rb @@ -0,0 +1,7 @@ +class Comment < ApplicationRecord + has_many :likes, :as => :likable + has_many :likers, :through => :likes + + belongs_to :commentable, :polymorphic => true + belongs_to :commenter, :class_name => "User", :inverse_of => :comments +end diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 000000000..5b648f9a9 --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,4 @@ +class Like < ApplicationRecord + belongs_to :likable, :polymorphic => true + belongs_to :liker, :class_name => "User" +end diff --git a/app/models/post.rb b/app/models/post.rb new file mode 100644 index 000000000..948b390ee --- /dev/null +++ b/app/models/post.rb @@ -0,0 +1,11 @@ +class Post < ApplicationRecord + belongs_to :author, :class_name => "User", :inverse_of => :posts + has_many :likes, :as => :likable + has_many :likers, :through => :likes + + has_many :comments, :as => :commentable, :inverse_of => :commentable + has_many :commenters, :through => :comments + accepts_nested_attributes_for :comments, + :reject_if => :all_blank, + :allow_destroy => true +end diff --git a/app/models/user.rb b/app/models/user.rb index 9e86ae081..b7d74fcb3 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -3,6 +3,10 @@ class User < ApplicationRecord has_secure_password + def full_name + "#{self.first_name} #{self.last_name}" + end + validates :password, :length => { :in => 3..24 }, :allow_nil => true @@ -18,5 +22,12 @@ def regenerate_auth_token generate_token save! end - + + has_many :posts, :foreign_key => :author_id, :inverse_of => :author + accepts_nested_attributes_for :posts, + :reject_if => :all_blank, + :allow_destroy => true + + has_many :comments, :foreign_key => :commenter_id, :inverse_of => :commenter + end diff --git a/app/views/users/timeline.html.erb b/app/views/users/timeline.html.erb new file mode 100644 index 000000000..5662a181e --- /dev/null +++ b/app/views/users/timeline.html.erb @@ -0,0 +1,226 @@ +<%= render 'nav' %> + +
+
+
+ +

Harry Potter

+
+
+ + +
+ + + +
+ + +
+
+
+
+
About (Text Area)
+
+
+
+
+

Born on: <%="#{@user.b_month}/#{@user.b_day}/#{@user.b_year}"%>

+

Went to school at: <%= @user.college %>

+

Hometown: <%= @user.home %>d

+

Currently Lives: <%= @user.lives %>

+
+
+
+
+
Photos (123) (Copied Text Area)
+
+ +
+
+
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+
Friends (542) (Copied Text Area)
+
+
+
+
+
+ + + + + + +
+ +
+
+
+ +
+ <% if correct_user? %> +
+
+
Post
+
+
+ + <%= form_for @user do |u| %> + + <%= u.fields_for :posts, Post.new do |p| %> +
+ +
+ <%= p.text_area :body, rows: 3, placeholder: "Tell the world something...", id: "comment-area", class: "form_control" %> + + +
+ +
+ <%end%> +
+
+ + <%= u.submit "Post", class: "btn-primary", id: "post-btn"%> + +
+
+ <%end%> + <%else%> +
+ <%end%> + + + + <% @user.posts.reverse.each do |post| %> +
+
+
+
<%= link_to "#{@user.first_name} #{@user.last_name}", user_path(@user) %>
Posted on <%= post.created_at.strftime("%d %b. %Y")%>
+
+


<%= post.body %>

+
+
+
+
+
+ <% unless post.likers.include?(current_user) %> + <%=link_to "Like", likes_path(:likable_id => post.id, :likable_type => post.class), :method => :post%> + <%else%> + <%=link_to "Unlike", like_path(post.likes.where("liker_id = ?", current_user.id).first), :method => :delete%> + <%end%> + + +


<%= post.likes.count %> people have liked this post.

+
+ <% post.comments.each do |c| %> +
+
+
+
+
+
<%=link_to c.commenter.full_name, user_path(c.commenter) %> Said on <%= c.created_at.strftime("%d %b. %Y")%>
+

<%=c.body%>

+ <% unless c.likers.include?(current_user) %> + <%=link_to "Like", likes_path(:likable_id => c.id, :likable_type => c.class), :method => :post%> + <%else%> + <%=link_to "Unlike", like_path(c.likes.where("liker_id = ?", current_user.id).first), :method => :delete%> + <%end%> + + <%= c.likes.count %> people like this +
+
+ + + <%= form_for post do |p| %> + <%= p.fields_for :comments, Comment.new do |c| %> + <%= c.text_area :body, rows: 3, placeholder: "Tell the world something...", id: "comment-area", class: "form_control" %> + <%= c.hidden_field :commentable_type, value: post.class %> + <%= c.hidden_field :commentable_id, value: post.id %> + <%= c.hidden_field :commenter_id, value: current_user.id %> + Cancel + <%end%> + <%= p.submit "Comment", class: "btn-primary", id: "post-btn"%> + <%end%> + + <%end%> +
+
+ <%end%> + + +
+
+
diff --git a/config/routes.rb b/config/routes.rb index d6abbcb7a..ca4b5fbbb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,15 +1,19 @@ Rails.application.routes.draw do root 'users#new' - get '/timeline' => 'static_pages#timeline' + get '/timeline' => 'users#timeline' + get '/timeline/:id' => 'users#timeline' get '/friends' => 'static_pages#friends' get '/photos' => 'static_pages#photos' - get '/about' => 'users#about' get '/about_edit' => 'static_pages#about_edit' + resource :session, :only => [:new, :create, :destroy] get "login" => "sessions#new" delete "logout" => "sessions#destroy" - resources :users, :only => [:new, :create, :show, :edit, :update] + resources :users, :only => [:new, :create, :show, :edit, :update, :timeline] + resources :posts, :only => [:destroy, :update] + resources :likes, :only => [:create, :destroy] + resources :comments, :only => [:create, :destroy] # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/db/migrate/20160829182205_create_posts.rb b/db/migrate/20160829182205_create_posts.rb new file mode 100644 index 000000000..6fb5fc559 --- /dev/null +++ b/db/migrate/20160829182205_create_posts.rb @@ -0,0 +1,13 @@ +class CreatePosts < ActiveRecord::Migration[5.0] + def change + create_table :posts do |t| + t.text :body + t.integer :author_id + + t.timestamps + end + + change_column :posts, :body, :text, :null => false + change_column :posts, :author_id, :integer, :null => false + end +end diff --git a/db/migrate/20160829225937_create_likes.rb b/db/migrate/20160829225937_create_likes.rb new file mode 100644 index 000000000..7ac6eaa3b --- /dev/null +++ b/db/migrate/20160829225937_create_likes.rb @@ -0,0 +1,12 @@ +class CreateLikes < ActiveRecord::Migration[5.0] + def change + create_table :likes do |t| + t.integer :likable_id + t.integer :liker_id + + t.timestamps + end + change_column :likes, :likable_id, :integer, :null => false + change_column :likes, :liker_id, :integer, :null => false + end +end diff --git a/db/migrate/20160829230603_likpoly.rb b/db/migrate/20160829230603_likpoly.rb new file mode 100644 index 000000000..d1280b06d --- /dev/null +++ b/db/migrate/20160829230603_likpoly.rb @@ -0,0 +1,6 @@ +class Likpoly < ActiveRecord::Migration[5.0] + def change + add_column :likes, :likable_type, :string + + end +end diff --git a/db/migrate/20160829231113_indexpoly.rb b/db/migrate/20160829231113_indexpoly.rb new file mode 100644 index 000000000..6e8c6fabb --- /dev/null +++ b/db/migrate/20160829231113_indexpoly.rb @@ -0,0 +1,5 @@ +class Indexpoly < ActiveRecord::Migration[5.0] + def change + add_index :likes, [:likable_type, :likable_id] + end +end diff --git a/db/migrate/20160830001934_create_comments.rb b/db/migrate/20160830001934_create_comments.rb new file mode 100644 index 000000000..34d1b942d --- /dev/null +++ b/db/migrate/20160830001934_create_comments.rb @@ -0,0 +1,14 @@ +class CreateComments < ActiveRecord::Migration[5.0] + def change + create_table :comments do |t| + t.integer :commentable_id + t.string :commentable_type + t.integer :commenter_id + + t.timestamps + end + add_index :comments, [:commentable_type, :commentable_id] + change_column :comments, :commentable_id, :integer, :null => false + change_column :comments, :commenter_id, :integer, :null => false + end +end diff --git a/db/migrate/20160830002216_addbody.rb b/db/migrate/20160830002216_addbody.rb new file mode 100644 index 000000000..9f66d3aca --- /dev/null +++ b/db/migrate/20160830002216_addbody.rb @@ -0,0 +1,6 @@ +class Addbody < ActiveRecord::Migration[5.0] + def change + add_column :comments, :body, :text + change_column :comments, :body, :text, :null => false + end +end diff --git a/db/schema.rb b/db/schema.rb index 7843fbaae..2101370e1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,33 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160824223109) do +ActiveRecord::Schema.define(version: 20160830002216) do + + create_table "comments", force: :cascade do |t| + t.integer "commentable_id", null: false + t.string "commentable_type" + t.integer "commenter_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.text "body", null: false + t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id" + end + + create_table "likes", force: :cascade do |t| + t.integer "likable_id", null: false + t.integer "liker_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "likable_type" + t.index ["likable_type", "likable_id"], name: "index_likes_on_likable_type_and_likable_id" + end + + create_table "posts", force: :cascade do |t| + t.text "body", null: false + t.integer "author_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end create_table "users", force: :cascade do |t| t.string "email", null: false diff --git a/test/controllers/comments_controller_test.rb b/test/controllers/comments_controller_test.rb new file mode 100644 index 000000000..a812ddae9 --- /dev/null +++ b/test/controllers/comments_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CommentsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/likes_controller_test.rb b/test/controllers/likes_controller_test.rb new file mode 100644 index 000000000..78e007545 --- /dev/null +++ b/test/controllers/likes_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LikesControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb new file mode 100644 index 000000000..12c4a7fd0 --- /dev/null +++ b/test/controllers/posts_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PostsControllerTest < ActionDispatch::IntegrationTest + # test "the truth" do + # assert true + # end +end diff --git a/test/fixtures/comments.yml b/test/fixtures/comments.yml new file mode 100644 index 000000000..171bf5cc0 --- /dev/null +++ b/test/fixtures/comments.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + commentable_id: 1 + commentable_type: MyString + commenter_id: 1 + +two: + commentable_id: 1 + commentable_type: MyString + commenter_id: 1 diff --git a/test/fixtures/likes.yml b/test/fixtures/likes.yml new file mode 100644 index 000000000..c969b5067 --- /dev/null +++ b/test/fixtures/likes.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + likable_id: 1 + liker_id: 1 + +two: + likable_id: 1 + liker_id: 1 diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml new file mode 100644 index 000000000..d3a0bec5b --- /dev/null +++ b/test/fixtures/posts.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + body: MyText + author_id: 1 + +two: + body: MyText + author_id: 1 diff --git a/test/models/comment_test.rb b/test/models/comment_test.rb new file mode 100644 index 000000000..b6d6131a9 --- /dev/null +++ b/test/models/comment_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class CommentTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/like_test.rb b/test/models/like_test.rb new file mode 100644 index 000000000..1eea9915b --- /dev/null +++ b/test/models/like_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class LikeTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end diff --git a/test/models/post_test.rb b/test/models/post_test.rb new file mode 100644 index 000000000..6d9d463a7 --- /dev/null +++ b/test/models/post_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PostTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From 6c97b5cd5cf612ebac14f2189f5ecb9eadee438a Mon Sep 17 00:00:00 2001 From: David J Watts Date: Thu, 1 Sep 2016 16:05:05 -0700 Subject: [PATCH 09/12] add rspec testing, model and feature tests --- .rspec | 3 + Gemfile | 7 ++ Gemfile.lock | 76 +++++++++++++++++ Guardfile | 70 +++++++++++++++ app/controllers/sessions_controller.rb | 2 +- app/views/users/timeline.html.erb | 4 +- spec/factories.rb | 41 +++++++++ spec/features/users_spec.rb | 77 +++++++++++++++++ spec/models/comment_spec.rb | 24 ++++++ spec/models/like_spec.rb | 18 ++++ spec/models/post_spec.rb | 31 +++++++ spec/models/user_spec.rb | 69 +++++++++++++++ spec/rails_helper.rb | 61 +++++++++++++ spec/spec_helper.rb | 85 +++++++++++++++++++ spec/support/login_macros.rb | 13 +++ test/controllers/.keep | 0 test/controllers/comments_controller_test.rb | 7 -- test/controllers/likes_controller_test.rb | 7 -- test/controllers/posts_controller_test.rb | 7 -- test/controllers/sessions_controller_test.rb | 7 -- .../static_pages_controller_test.rb | 7 -- test/controllers/users_controller_test.rb | 7 -- test/fixtures/.keep | 0 test/fixtures/comments.yml | 11 --- test/fixtures/files/.keep | 0 test/fixtures/likes.yml | 9 -- test/fixtures/posts.yml | 9 -- test/fixtures/users.yml | 9 -- test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/models/comment_test.rb | 7 -- test/models/like_test.rb | 7 -- test/models/post_test.rb | 7 -- test/models/user_test.rb | 7 -- test/test_helper.rb | 10 --- 37 files changed, 578 insertions(+), 121 deletions(-) create mode 100644 .rspec create mode 100644 Guardfile create mode 100644 spec/factories.rb create mode 100644 spec/features/users_spec.rb create mode 100644 spec/models/comment_spec.rb create mode 100644 spec/models/like_spec.rb create mode 100644 spec/models/post_spec.rb create mode 100644 spec/models/user_spec.rb create mode 100644 spec/rails_helper.rb create mode 100644 spec/spec_helper.rb create mode 100644 spec/support/login_macros.rb delete mode 100644 test/controllers/.keep delete mode 100644 test/controllers/comments_controller_test.rb delete mode 100644 test/controllers/likes_controller_test.rb delete mode 100644 test/controllers/posts_controller_test.rb delete mode 100644 test/controllers/sessions_controller_test.rb delete mode 100644 test/controllers/static_pages_controller_test.rb delete mode 100644 test/controllers/users_controller_test.rb delete mode 100644 test/fixtures/.keep delete mode 100644 test/fixtures/comments.yml delete mode 100644 test/fixtures/files/.keep delete mode 100644 test/fixtures/likes.yml delete mode 100644 test/fixtures/posts.yml delete mode 100644 test/fixtures/users.yml delete mode 100644 test/helpers/.keep delete mode 100644 test/integration/.keep delete mode 100644 test/mailers/.keep delete mode 100644 test/models/.keep delete mode 100644 test/models/comment_test.rb delete mode 100644 test/models/like_test.rb delete mode 100644 test/models/post_test.rb delete mode 100644 test/models/user_test.rb delete mode 100644 test/test_helper.rb diff --git a/.rspec b/.rspec new file mode 100644 index 000000000..7e3eb5c7c --- /dev/null +++ b/.rspec @@ -0,0 +1,3 @@ +--color +--require spec_helper +--format doc diff --git a/Gemfile b/Gemfile index 681a0625d..3a31664ba 100644 --- a/Gemfile +++ b/Gemfile @@ -31,13 +31,20 @@ gem 'bcrypt', '~> 3.1.7' gem 'hirb' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development +gem 'pry-byebug' group :development, :test do + gem 'shoulda-matchers', '~> 3.1' + gem 'capybara' + gem 'launchy' + gem 'factory_girl_rails', '~> 4.0' + gem 'rspec-rails' # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri end group :development do + gem 'guard-rspec', require: false gem 'sqlite3' gem 'better_errors' gem 'binding_of_caller' diff --git a/Gemfile.lock b/Gemfile.lock index 71a6e8366..97f0faa1a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -38,6 +38,7 @@ GEM i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) + addressable (2.4.0) arel (7.1.1) bcrypt (3.1.11) better_errors (2.1.1) @@ -48,6 +49,13 @@ GEM debug_inspector (>= 0.0.1) builder (3.2.2) byebug (9.0.5) + capybara (2.8.1) + addressable + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) coderay (1.1.1) coffee-rails (4.2.1) coffee-script (>= 2.2.0) @@ -58,11 +66,32 @@ GEM coffee-script-source (1.10.0) concurrent-ruby (1.0.2) debug_inspector (0.0.2) + diff-lcs (1.2.5) erubis (2.7.0) execjs (2.7.0) + factory_girl (4.5.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.5.0) + factory_girl (~> 4.5.0) + railties (>= 3.0.0) ffi (1.9.14) + formatador (0.2.5) globalid (0.3.7) activesupport (>= 4.1.0) + guard (2.12.7) + formatador (>= 0.2.4) + listen (>= 2.7, <= 4.0) + lumberjack (~> 1.0) + nenv (~> 0.1) + notiffany (~> 0.0) + pry (>= 0.9.12) + shellany (~> 0.0) + thor (>= 0.18.1) + guard-compat (1.2.1) + guard-rspec (4.6.0) + guard (~> 2.1) + guard-compat (~> 1.1) + rspec (>= 2.99.0, < 4.0) hirb (0.7.3) i18n (0.7.0) jbuilder (2.6.0) @@ -72,11 +101,14 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + launchy (2.4.3) + addressable (~> 2.3) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) loofah (2.0.3) nokogiri (>= 1.5.9) + lumberjack (1.0.9) mail (2.6.4) mime-types (>= 1.16, < 4) method_source (0.8.2) @@ -86,12 +118,23 @@ GEM mini_portile2 (2.1.0) minitest (5.9.0) multi_json (1.12.1) + nenv (0.2.0) nio4r (1.2.1) nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) + notiffany (0.0.6) + nenv (~> 0.1) + shellany (~> 0.0) pg (0.18.4) pkg-config (1.1.7) + pry (0.10.4) + coderay (~> 1.1.0) + method_source (~> 0.8.1) + slop (~> 3.4) + pry-byebug (3.4.0) + byebug (~> 9.0) + pry (~> 0.10) puma (3.6.0) rack (2.0.1) rack-test (0.6.3) @@ -128,6 +171,26 @@ GEM rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) + rspec (3.1.0) + rspec-core (~> 3.1.0) + rspec-expectations (~> 3.1.0) + rspec-mocks (~> 3.1.0) + rspec-core (3.1.7) + rspec-support (~> 3.1.0) + rspec-expectations (3.1.2) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.1.0) + rspec-mocks (3.1.3) + rspec-support (~> 3.1.0) + rspec-rails (3.1.0) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + rspec-core (~> 3.1.0) + rspec-expectations (~> 3.1.0) + rspec-mocks (~> 3.1.0) + rspec-support (~> 3.1.0) + rspec-support (3.1.2) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -135,6 +198,10 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + shellany (0.0.1) + shoulda-matchers (3.1.1) + activesupport (>= 4.0.0) + slop (3.6.0) spring (1.7.2) spring-watcher-listen (2.0.0) listen (>= 2.7, < 4.0) @@ -165,6 +232,8 @@ GEM websocket-driver (0.6.4) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -174,16 +243,23 @@ DEPENDENCIES better_errors binding_of_caller byebug + capybara coffee-rails (~> 4.2) + factory_girl_rails (~> 4.0) + guard-rspec hirb jbuilder (~> 2.5) jquery-rails + launchy listen (~> 3.0.5) pg + pry-byebug puma (~> 3.0) rails (~> 5.0.0) rails_12factor + rspec-rails sass-rails (~> 5.0) + shoulda-matchers (~> 3.1) spring spring-watcher-listen (~> 2.0.0) sqlite3 diff --git a/Guardfile b/Guardfile new file mode 100644 index 000000000..deea540d9 --- /dev/null +++ b/Guardfile @@ -0,0 +1,70 @@ +# A sample Guardfile +# More info at https://github.com/guard/guard#readme + +## Uncomment and set this to only include directories you want to watch +# directories %w(app lib config test spec features) \ +# .select{|d| Dir.exists?(d) ? d : UI.warning("Directory #{d} does not exist")} + +## Note: if you are using the `directories` clause above and you are not +## watching the project directory ('.'), then you will want to move +## the Guardfile to a watched dir and symlink it back, e.g. +# +# $ mkdir config +# $ mv Guardfile config/ +# $ ln -s config/Guardfile . +# +# and, you'll have to watch "config/Guardfile" instead of "Guardfile" + +# Note: The cmd option is now required due to the increasing number of ways +# rspec may be run, below are examples of the most common uses. +# * bundler: 'bundle exec rspec' +# * bundler binstubs: 'bin/rspec' +# * spring: 'bin/rspec' (This will use spring if running and you have +# installed the spring binstubs per the docs) +# * zeus: 'zeus rspec' (requires the server to be started separately) +# * 'just' rspec: 'rspec' + +guard :rspec, cmd: "bundle exec rspec" do + require "guard/rspec/dsl" + dsl = Guard::RSpec::Dsl.new(self) + + # Feel free to open issues for suggestions and improvements + + # RSpec files + rspec = dsl.rspec + watch(rspec.spec_helper) { rspec.spec_dir } + watch(rspec.spec_support) { rspec.spec_dir } + watch(rspec.spec_files) + + # Ruby files + ruby = dsl.ruby + dsl.watch_spec_files_for(ruby.lib_files) + + # Rails files + rails = dsl.rails(view_extensions: %w(erb haml slim)) + dsl.watch_spec_files_for(rails.app_files) + dsl.watch_spec_files_for(rails.views) + + watch(rails.controllers) do |m| + [ + rspec.spec.("routing/#{m[1]}_routing"), + rspec.spec.("controllers/#{m[1]}_controller"), + rspec.spec.("acceptance/#{m[1]}") + ] + end + + # Rails config changes + watch(rails.spec_helper) { rspec.spec_dir } + watch(rails.routes) { "#{rspec.spec_dir}/routing" } + watch(rails.app_controller) { "#{rspec.spec_dir}/controllers" } + + # Capybara features specs + watch(rails.view_dirs) { |m| rspec.spec.("features/#{m[1]}") } + watch(rails.layouts) { |m| rspec.spec.("features/#{m[1]}") } + + # Turnip features and steps + watch(%r{^spec/acceptance/(.+)\.feature$}) + watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) do |m| + Dir[File.join("**/#{m[1]}.feature")][0] || "spec/acceptance" + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index e31187c3e..203901c46 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -13,7 +13,7 @@ def create sign_in(@user) end flash[:success] = "You've successfully signed in" - redirect_to :back + redirect_to root_url else flash.now[:error] = "We couldn't sign you in" render :new diff --git a/app/views/users/timeline.html.erb b/app/views/users/timeline.html.erb index 5662a181e..879a2c39e 100644 --- a/app/views/users/timeline.html.erb +++ b/app/views/users/timeline.html.erb @@ -203,7 +203,7 @@ - + <%end%> <%= form_for post do |p| %> <%= p.fields_for :comments, Comment.new do |c| %> <%= c.text_area :body, rows: 3, placeholder: "Tell the world something...", id: "comment-area", class: "form_control" %> @@ -215,7 +215,7 @@ <%= p.submit "Comment", class: "btn-primary", id: "post-btn"%> <%end%> - <%end%> + <%end%> diff --git a/spec/factories.rb b/spec/factories.rb new file mode 100644 index 000000000..c3bd51726 --- /dev/null +++ b/spec/factories.rb @@ -0,0 +1,41 @@ +FactoryGirl.define do + + factory :user, :aliases => [:author, :commenter, :liker] do + sequence(:last_name) {|n| "Bar#{n}"} + sequence(:first_name) {|n| "Foo#{n}"} + gender "Male" + sequence(:b_month) {|n| 1+n} + sequence(:b_day) {|n| n+1} + sequence(:b_year) {|n| n+1} + email {"#{first_name}@bar.com"} + password "foobar" + password_confirmation "foobar" + + trait :frequent_author do + after_build do |user| + create_list( :post, 5, author: user ) + end + end + + end + + factory :post, :aliases => [:commentable] do + sequence(:body) {|n| "#{n} Ipsem Lorem"} + association :author, :factory => :user + end + + factory :comment do + sequence(:body) {|n| "#{n} Dubium Lorensioremal"} + association :commenter, :factory => :user + commentable_type "Post" + association :commentable, :factory => :post + end + + factory :like do + likable_type "Post" + association :likable, :factory => :post + association :liker, :factory => :user + + + end +end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb new file mode 100644 index 000000000..135c56504 --- /dev/null +++ b/spec/features/users_spec.rb @@ -0,0 +1,77 @@ +require 'rails_helper' + +feature 'Visitor Access' do + let(:userb){ build(:user) } + let(:userc){ create(:user) } + let(:post){build(:post, :author => userc)} + + before do + visit root_path + end + +feature 'visitor acccess' do + scenario "sign up" do + fill_in "First Name", with: userb.first_name + fill_in "Last Name", with: userb.last_name + fill_in "user[email]", with: userb.email + fill_in "user[password]", with: userb.password + fill_in "user[password_confirmation]", with: userb.password_confirmation + choose('Male') + select('Jan', :from => 'user[b_month]') + select('1', :from => 'user[b_day]') + select('1985', :from => 'user[b_year]') + click_button "Sign Up!" + expect(page).to have_content "User successfully created" + end +end + + + +end + +feature 'member access' do + + let(:user){ create(:user) } + let(:post){build(:post, :author => user)} + let(:comment){build(:comment)} + + before do + sign_in(user) + end + + scenario 'sign in as a registered user' do + sign_out + sign_in(user) + expect(page).to have_content "You've successfully signed in" + end + + scenario 'as a signed-in user, I want to be able to create a post' do + fill_in "Tell the world something...", with: post.body + click_button 'Post' + expect(page).to have_content "User succesfully updated" + end + + scenario 'as a signed-in user, I want to be able to like a post' do + post.save + visit root_path + click_link "Like" + expect(page).to have_content "Like successfully created" + end + + scenario 'as a signed-in user, I want to be able to unlike a post' do + post.save + visit root_path + click_link "Like" + click_link "Unlike" + expect(page).to have_content "Your like has been deleted!" + end + + scenario "as a signed in user, I want to be able to comment on a post" do + post.save + visit root_path + fill_in "post[comments_attributes][0][body]", with: comment.body + click_button "Comment" + expect(page).to have_content "post succesfully updated" + end + +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb new file mode 100644 index 000000000..59e9364e9 --- /dev/null +++ b/spec/models/comment_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe Comment do + let(:commentb) { build(:comment)} + let(:commentc) { create(:comment)} + + it "responds to association #commenter" do + expect(commentc).to respond_to(:commenter) + end + it "has polymorphic assocation #commentable" do + expect(commentc).to respond_to(:commentable) + end + + it "has assocation #likes" do + expect(commentc).to respond_to(:likes) + end + it "has assocation #likers" do + expect(commentc).to respond_to(:likers) + end + it "requires commentable_id, commenter_id, and body to be valid" do + expect(commentb).to be_valid + end + +end diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb new file mode 100644 index 000000000..1c4922e83 --- /dev/null +++ b/spec/models/like_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +describe Like do + let(:likeb){build(:like)} + let(:likec){create(:like)} + + it "requires likable_id, liker_id to be valid" do + expect(likeb).to be_valid + end + it "has polymorphic assocation #likable" do + expect(likec).to respond_to(:likable) + end + it "has assocation #liker" do + expect(likec).to respond_to(:likable) + end + + +end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb new file mode 100644 index 000000000..f80d1015c --- /dev/null +++ b/spec/models/post_spec.rb @@ -0,0 +1,31 @@ +require 'rails_helper' + +describe Post do + let(:postb){ build(:post)} + let(:postc){ create(:post)} + + it "requires body and author id to be valid" do + expect(postb).to be_valid + end + it "has association author" do + expect(postc).to respond_to(:author) + end + it "has many likes" do + expect(postc).to respond_to(:likes) + end + it "has many likers" do + expect(postc).to respond_to(:likers) + end + it "has many comments" do + expect(postc).to respond_to(:comments) + end + it "has many commenters" do + expect(postc).to respond_to(:commenters) + end + it "has poly ass commentable" do + expect(postc).to respond_to(:commentable) + end + + + +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 000000000..9f6b156ba --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,69 @@ +require 'rails_helper' + +describe User do + let(:userb){ build(:user) } + let(:userc){create(:user)} + let( :frequent_author ){ create(:user, :frequent_author) } + + it "requires email, password, first_name, last_name, gender, b_month, b_day, b_year to be valid" do + expect(userb).to be_valid + end + + it "has method #full_name which concatenates first and last name" do + expect(userc.full_name).to eq("#{userc.first_name} #{userc.last_name}") + end + + it "requires that the password be in(3..24)" do + userb.password = "fo" + userb.password_confirmation = "fo" + expect(userb).to_not be_valid + end + + it "has many posts" do + expect(userc).to respond_to(:posts) + end + + it "has many comments" do + expect(userc).to respond_to(:comments) + end + +end + + +# describe User do +# let(:user){ build(:user) } +# let(:secret){create(:secret)} +# +# it "with a name, email, and password is valid" do +# expect(user).to be_valid +# end +# +# it "without a name is invalid" do +# user.name = nil +# +# expect(!user.valid?).to be_truthy +# end +# it "without an email is invalid" do +# user.email = nil +# expect(user).to_not be_valid +# end +# +# it "without a password is invalid" do +# user.password_digest = nil +# expect(user).to_not be_valid +# end +# +# it "with too short of a name is invalid" do +# user.name = "fa" +# expect(user).to_not be_valid +# end +# +# it "with too short of a password is invalid" do +# user = build(:user, password: "foo", password_confirmation: "foo") +# expect(user).to_not be_valid +# end +# +# it "responds to the secrets association" do +# expect(user).to respond_to(:secrets) +# +# end diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 000000000..e60712fda --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,61 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV["RAILS_ENV"] ||= 'test' +require 'spec_helper' +require File.expand_path("../../config/environment", __FILE__) +require 'rspec/rails' +require 'factory_girl_rails' +require 'capybara/rails' + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + with.test_framework :rspec + with.library :rails + end +end +# Add additional requires below this line. Rails is not loaded until this point! + +# Requires supporting ruby files with custom matchers and macros, etc, in +# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are +# run as spec files by default. This means that files in spec/support that end +# in _spec.rb will both be required and run as specs, causing the specs to be +# run twice. It is recommended that you do not name files matching this glob to +# end with _spec.rb. You can configure this pattern with the --pattern +# option on the command line or in ~/.rspec, .rspec or `.rspec-local`. +# +# The following line is provided for convenience purposes. It has the downside +# of increasing the boot-up time by auto-requiring all files in the support +# directory. Alternatively, in the individual `*_spec.rb` files, manually +# require only the support files necessary. +# +Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } + +# Checks for pending migrations before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.maintain_test_schema! + +RSpec.configure do |config| + config.include LoginMacros + config.include FactoryGirl::Syntax::Methods + # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures + config.fixture_path = "#{::Rails.root}/spec/fixtures" + + # If you're not using ActiveRecord, or you'd prefer not to run each of your + # examples within a transaction, remove the following line or assign false + # instead of true. + config.use_transactional_fixtures = true + + # RSpec Rails can automatically mix in different behaviours to your tests + # based on their file location, for example enabling you to call `get` and + # `post` in specs under `spec/controllers`. + # + # You can disable this behaviour by removing the line below, and instead + # explicitly tag your specs with their type, e.g.: + # + # RSpec.describe UsersController, :type => :controller do + # # ... + # end + # + # The different available types are documented in the features, such as in + # https://relishapp.com/rspec/rspec-rails/docs + config.infer_spec_type_from_file_location! +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..275ba496f --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,85 @@ +# This file was generated by the `rails generate rspec:install` command. Conventionally, all +# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. +# The generated `.rspec` file contains `--require spec_helper` which will cause this +# file to always be loaded, without a need to explicitly require it in any files. +# +# Given that it is always loaded, you are encouraged to keep this file as +# light-weight as possible. Requiring heavyweight dependencies from this file +# will add to the boot time of your test suite on EVERY test run, even for an +# individual file that may not need all of that loaded. Instead, consider making +# a separate helper file that requires the additional dependencies and performs +# the additional setup, and require it from the spec files that actually need it. +# +# The `.rspec` file also contains a few flags that are not defaults but that +# users commonly want. +# +# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration +RSpec.configure do |config| + # rspec-expectations config goes here. You can use an alternate + # assertion/expectation library such as wrong or the stdlib/minitest + # assertions if you prefer. + config.expect_with :rspec do |expectations| + # This option will default to `true` in RSpec 4. It makes the `description` + # and `failure_message` of custom matchers include text for helper methods + # defined using `chain`, e.g.: + # be_bigger_than(2).and_smaller_than(4).description + # # => "be bigger than 2 and smaller than 4" + # ...rather than: + # # => "be bigger than 2" + expectations.include_chain_clauses_in_custom_matcher_descriptions = true + end + + # rspec-mocks config goes here. You can use an alternate test double + # library (such as bogus or mocha) by changing the `mock_with` option here. + config.mock_with :rspec do |mocks| + # Prevents you from mocking or stubbing a method that does not exist on + # a real object. This is generally recommended, and will default to + # `true` in RSpec 4. + mocks.verify_partial_doubles = true + end + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # These two settings work together to allow you to limit a spec run + # to individual examples or groups you care about by tagging them with + # `:focus` metadata. When nothing is tagged with `:focus`, all examples + # get run. + config.filter_run :focus + config.run_all_when_everything_filtered = true + + # Limits the available syntax to the non-monkey patched syntax that is recommended. + # For more details, see: + # - http://myronmars.to/n/dev-blog/2012/06/rspecs-new-expectation-syntax + # - http://teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://myronmars.to/n/dev-blog/2014/05/notable-changes-in-rspec-3#new__config_option_to_disable_rspeccore_monkey_patching + config.disable_monkey_patching! + + # Many RSpec users commonly either run the entire suite or an individual + # file, and it's useful to allow more verbose output when running an + # individual spec file. + if config.files_to_run.one? + # Use the documentation formatter for detailed output, + # unless a formatter has already been configured + # (e.g. via a command-line flag). + config.default_formatter = 'doc' + end + + # Print the 10 slowest examples and example groups at the + # end of the spec run, to help surface which specs are running + # particularly slow. + config.profile_examples = 10 + + # Run specs in random order to surface order dependencies. If you find an + # order dependency and want to debug it, you can fix the order by providing + # the seed, which is printed after each run. + # --seed 1234 + config.order = :random + + # Seed global randomization in this process using the `--seed` CLI option. + # Setting this allows you to use `--seed` to deterministically reproduce + # test failures related to randomization by passing the same `--seed` value + # as the one that triggered the failure. + Kernel.srand config.seed +=end +end diff --git a/spec/support/login_macros.rb b/spec/support/login_macros.rb new file mode 100644 index 000000000..4350ae267 --- /dev/null +++ b/spec/support/login_macros.rb @@ -0,0 +1,13 @@ +module LoginMacros + def sign_in(user) + visit root_path + fill_in 'Email', with: user.email + fill_in 'Password', with: user.password + click_button 'Log in' + end + + def sign_out + visit root_path + click_link "Logout" + end +end diff --git a/test/controllers/.keep b/test/controllers/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/controllers/comments_controller_test.rb b/test/controllers/comments_controller_test.rb deleted file mode 100644 index a812ddae9..000000000 --- a/test/controllers/comments_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class CommentsControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/likes_controller_test.rb b/test/controllers/likes_controller_test.rb deleted file mode 100644 index 78e007545..000000000 --- a/test/controllers/likes_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class LikesControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/posts_controller_test.rb b/test/controllers/posts_controller_test.rb deleted file mode 100644 index 12c4a7fd0..000000000 --- a/test/controllers/posts_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class PostsControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb deleted file mode 100644 index 6135ce6af..000000000 --- a/test/controllers/sessions_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class SessionsControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/static_pages_controller_test.rb b/test/controllers/static_pages_controller_test.rb deleted file mode 100644 index 76b4b38eb..000000000 --- a/test/controllers/static_pages_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class StaticPagesControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb deleted file mode 100644 index 6c3da770c..000000000 --- a/test/controllers/users_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class UsersControllerTest < ActionDispatch::IntegrationTest - # test "the truth" do - # assert true - # end -end diff --git a/test/fixtures/.keep b/test/fixtures/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/fixtures/comments.yml b/test/fixtures/comments.yml deleted file mode 100644 index 171bf5cc0..000000000 --- a/test/fixtures/comments.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - commentable_id: 1 - commentable_type: MyString - commenter_id: 1 - -two: - commentable_id: 1 - commentable_type: MyString - commenter_id: 1 diff --git a/test/fixtures/files/.keep b/test/fixtures/files/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/fixtures/likes.yml b/test/fixtures/likes.yml deleted file mode 100644 index c969b5067..000000000 --- a/test/fixtures/likes.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - likable_id: 1 - liker_id: 1 - -two: - likable_id: 1 - liker_id: 1 diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml deleted file mode 100644 index d3a0bec5b..000000000 --- a/test/fixtures/posts.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - body: MyText - author_id: 1 - -two: - body: MyText - author_id: 1 diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml deleted file mode 100644 index 0821459be..000000000 --- a/test/fixtures/users.yml +++ /dev/null @@ -1,9 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - email: MyString - password_digest: MyString - -two: - email: MyString - password_digest: MyString diff --git a/test/helpers/.keep b/test/helpers/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/integration/.keep b/test/integration/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/mailers/.keep b/test/mailers/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/models/.keep b/test/models/.keep deleted file mode 100644 index e69de29bb..000000000 diff --git a/test/models/comment_test.rb b/test/models/comment_test.rb deleted file mode 100644 index b6d6131a9..000000000 --- a/test/models/comment_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class CommentTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/like_test.rb b/test/models/like_test.rb deleted file mode 100644 index 1eea9915b..000000000 --- a/test/models/like_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class LikeTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/post_test.rb b/test/models/post_test.rb deleted file mode 100644 index 6d9d463a7..000000000 --- a/test/models/post_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class PostTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/models/user_test.rb b/test/models/user_test.rb deleted file mode 100644 index 82f61e010..000000000 --- a/test/models/user_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class UserTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end diff --git a/test/test_helper.rb b/test/test_helper.rb deleted file mode 100644 index 92e39b2d7..000000000 --- a/test/test_helper.rb +++ /dev/null @@ -1,10 +0,0 @@ -ENV['RAILS_ENV'] ||= 'test' -require File.expand_path('../../config/environment', __FILE__) -require 'rails/test_help' - -class ActiveSupport::TestCase - # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. - fixtures :all - - # Add more helper methods to be used by all tests here... -end From 0bf1dd8aa1d84e24c13ffa03c0ee889066d96767 Mon Sep 17 00:00:00 2001 From: David Watts Date: Fri, 2 Sep 2016 18:27:49 -0700 Subject: [PATCH 10/12] add view/controller tests --- spec/controllers/users_controller_spec.rb | 109 ++++++++++++++++++++++ spec/models/post_spec.rb | 4 +- spec/rails_helper.rb | 1 + spec/views/users/timeline_spec.rb | 39 ++++++++ 4 files changed, 150 insertions(+), 3 deletions(-) create mode 100644 spec/controllers/users_controller_spec.rb create mode 100644 spec/views/users/timeline_spec.rb diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 000000000..1f6f499c3 --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,109 @@ +require 'rails_helper' + +describe UsersController do + let(:user) { create(:user) } + let(:post) {create(:post, :author => user)} + + before :each do + cookies[:auth_token] = user.auth_token + end + + + + + # describe "GET #edit" do + # it "for this user works as normal" do + # get :edit, :id => user.id + # expect(response).to render_template :about_edit + # end + # + # it "for another user denies access" do + # another_user = create(:user) + # get :edit, :id => another_user.id + # expect(response).to redirect_to root_url + # end + # end + # + # describe "PATCH #update" do + # + # before { user } + # + # context "with valid attributes" do + # + # let(:updated_name){ "updated_foo" } + # + # # let's make sure the ID parameter works + # it "finds the specified user" do + # put :update, :id => user.id, + # :user => attributes_for(:user, + # :name => updated_name) + # expect(assigns(:user)).to eq(user) + # end + # + # it "redirects to the updated user" do + # put :update, :id => user.id, + # :user => attributes_for(:user, + # :name => updated_name) + # + # expect(response).to redirect_to user_path(assigns(:user)) + # end + # + # it "actually updates the user" do + # put :update, :id => user.id, + # :user => attributes_for(:user, + # :name => updated_name) + # + # user.reload + # expect(user.name).to eq(updated_name) + # end + # end + # + # context "with invalid attributes" do + # it "for another user denies access" do + # another_user = create(:user) + # get :update, :id => another_user.id, + # :user => attributes_for(:user) + # expect(response).to redirect_to root_url + # end + # end + # end + # + # describe "GET #show" do + # it "creates teh appropriate instance variable" do + # + # get :show, :id => user.id + # + # + # expect(assigns(:user)).to eq(user) + # end + # end + # + # describe "DELETE #destroy" do + # + # before { user } # force let to evaluate + # context "with valid attributes" do + # it "destroys the user" do + # expect{ + # delete :destroy, :id => user.id + # }.to change(User, :count).by(-1) + # end + # + # it "redirects to the root" do + # delete :destroy, :id => user.id + # expect(response).to redirect_to users_url + # end + # end + # + # context "with invalid attributes" do + # it "for another user denies access" do + # another_user = create(:user) + # delete :destroy, :id => another_user.id + # expect(response).to redirect_to root_url + # end + # + # end + # end + + + +end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index f80d1015c..3a095450b 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -22,9 +22,7 @@ it "has many commenters" do expect(postc).to respond_to(:commenters) end - it "has poly ass commentable" do - expect(postc).to respond_to(:commentable) - end + diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index e60712fda..ac40c0428 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -34,6 +34,7 @@ ActiveRecord::Migration.maintain_test_schema! RSpec.configure do |config| + config.include Capybara::DSL config.include LoginMacros config.include FactoryGirl::Syntax::Methods # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures diff --git a/spec/views/users/timeline_spec.rb b/spec/views/users/timeline_spec.rb new file mode 100644 index 000000000..84921e8c0 --- /dev/null +++ b/spec/views/users/timeline_spec.rb @@ -0,0 +1,39 @@ +require 'rails_helper' + +describe "users/timeline.html.erb" do + let(:user){create(:user)} + before do + def view.signed_in_user? + true + end + @user = user + def view.current_user + @user + end + def view.correct_user? + true + end + end + it "shows the h1" do + + # get ready to set our instance variable + + + + # Actually set the instance variable. + # This is identical to writing `@users = users` + # but is more "RSpec-like" + + + + # render the view + render + + # Check that it contains our user's first name + + # ... HTML style + expect(rendered).to have_text("Danebook") + end + + +end From a71da67a7557bd7d39ada2ee534370597584dd19 Mon Sep 17 00:00:00 2001 From: David J Watts Date: Wed, 7 Sep 2016 18:56:39 -0700 Subject: [PATCH 11/12] add friend/unfriend ability --- app/assets/javascripts/friendings.coffee | 3 + app/assets/stylesheets/friendings.scss | 3 + app/controllers/friendings_controller.rb | 31 ++++++++++ app/helpers/friendings_helper.rb | 2 + app/helpers/users_helper.rb | 13 ++++ app/models/friending.rb | 12 ++++ app/models/user.rb | 12 ++++ app/views/users/timeline.html.erb | 7 ++- config/routes.rb | 1 + .../20160907232647_create_friendings.rb | 12 ++++ db/schema.rb | 10 +++- db/seeds.rb | 59 +++++++++++++++++++ .../controllers/friendings_controller_spec.rb | 5 ++ spec/factories.rb | 5 +- spec/helpers/friendings_helper_spec.rb | 15 +++++ spec/models/friending_spec.rb | 5 ++ 16 files changed, 192 insertions(+), 3 deletions(-) create mode 100644 app/assets/javascripts/friendings.coffee create mode 100644 app/assets/stylesheets/friendings.scss create mode 100644 app/controllers/friendings_controller.rb create mode 100644 app/helpers/friendings_helper.rb create mode 100644 app/models/friending.rb create mode 100644 db/migrate/20160907232647_create_friendings.rb create mode 100644 spec/controllers/friendings_controller_spec.rb create mode 100644 spec/helpers/friendings_helper_spec.rb create mode 100644 spec/models/friending_spec.rb diff --git a/app/assets/javascripts/friendings.coffee b/app/assets/javascripts/friendings.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/friendings.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/friendings.scss b/app/assets/stylesheets/friendings.scss new file mode 100644 index 000000000..25e6461f0 --- /dev/null +++ b/app/assets/stylesheets/friendings.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the friendings controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/friendings_controller.rb b/app/controllers/friendings_controller.rb new file mode 100644 index 000000000..27aaec9a9 --- /dev/null +++ b/app/controllers/friendings_controller.rb @@ -0,0 +1,31 @@ +class FriendingsController < ApplicationController + def create + @friending = Friending.new(friend_params) + @user = User.find(@friending.friend_id) + if @friending.save + flash[:notice] = "Friending successfully created" + redirect_to :back + else + flash[:notice] = "Friending has failed" + + redirect_to :back + end + end + + def destroy + @friending = Friending.find(params[:id]) + @user = User.find(@friending.friend_id) + if @friending.destroy + flash[:success] = "Your Friending has been deleted!" + else + flash[:error] = "Error! The Friending lives on!" + end + + redirect_to :back + + end + + def friend_params + params.permit(:friender_id, :friend_id) + end +end diff --git a/app/helpers/friendings_helper.rb b/app/helpers/friendings_helper.rb new file mode 100644 index 000000000..fce356995 --- /dev/null +++ b/app/helpers/friendings_helper.rb @@ -0,0 +1,2 @@ +module FriendingsHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 44e7820a1..b6e277f23 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,3 +1,16 @@ module UsersHelper + def friend_button + + if @current_user == @user + nil + + elsif @current_user.friended_users.include?(@user) + link_to "Unfriend", friending_path(Friending.where("friend_id = ?", @user.id).where("friender_id = ?", @current_user.id).first), :method => :delete, class: "btn btn-primary" + else + link_to "Friend", friendings_path(friender_id: @current_user.id, friend_id: @user.id), :method => :post, class: "btn btn-primary" + end + + + end end diff --git a/app/models/friending.rb b/app/models/friending.rb new file mode 100644 index 000000000..ad93906c8 --- /dev/null +++ b/app/models/friending.rb @@ -0,0 +1,12 @@ +class Friending < ApplicationRecord + + belongs_to :friend_initiator, :foreign_key => :friender_id, + :class_name => "User" + + + belongs_to :friend_recipient, :foreign_key => :friend_id, + :class_name => "User" + + + validates :friend_id, :uniqueness => { :scope => :friender_id } +end diff --git a/app/models/user.rb b/app/models/user.rb index b7d74fcb3..effccb93b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -30,4 +30,16 @@ def regenerate_auth_token has_many :comments, :foreign_key => :commenter_id, :inverse_of => :commenter + has_many :initiated_friendings, :foreign_key => :friender_id, + :class_name => "Friending" + has_many :friended_users, :through => :initiated_friendings, + :source => :friend_recipient + + # When acting as the recipient of the friending + has_many :received_friendings, :foreign_key => :friend_id, + :class_name => "Friending" + has_many :users_friended_by, :through => :received_friendings, + :source => :friend_initiator + + end diff --git a/app/views/users/timeline.html.erb b/app/views/users/timeline.html.erb index 879a2c39e..c283fac9a 100644 --- a/app/views/users/timeline.html.erb +++ b/app/views/users/timeline.html.erb @@ -4,7 +4,10 @@
-

Harry Potter

+

<%= @user.first_name + " " + @user.last_name%>


+ + <%= friend_button%> +
@@ -21,6 +24,8 @@ + + diff --git a/config/routes.rb b/config/routes.rb index ca4b5fbbb..63199044a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,6 +14,7 @@ resources :posts, :only => [:destroy, :update] resources :likes, :only => [:create, :destroy] resources :comments, :only => [:create, :destroy] + resources :friendings, :only => [:create, :destroy] # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html end diff --git a/db/migrate/20160907232647_create_friendings.rb b/db/migrate/20160907232647_create_friendings.rb new file mode 100644 index 000000000..9eb495823 --- /dev/null +++ b/db/migrate/20160907232647_create_friendings.rb @@ -0,0 +1,12 @@ +class CreateFriendings < ActiveRecord::Migration[5.0] + def change + create_table :friendings do |t| + t.integer :friend_id, :null => false + t.integer :friender_id, :null => false + t.timestamps + end + add_index :friendings, [:friend_id, :friender_id], :unique => true + end + + +end diff --git a/db/schema.rb b/db/schema.rb index 2101370e1..541c1f49e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160830002216) do +ActiveRecord::Schema.define(version: 20160907232647) do create_table "comments", force: :cascade do |t| t.integer "commentable_id", null: false @@ -22,6 +22,14 @@ t.index ["commentable_type", "commentable_id"], name: "index_comments_on_commentable_type_and_commentable_id" end + create_table "friendings", force: :cascade do |t| + t.integer "friend_id", null: false + t.integer "friender_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["friend_id", "friender_id"], name: "index_friendings_on_friend_id_and_friender_id", unique: true + end + create_table "likes", force: :cascade do |t| t.integer "likable_id", null: false t.integer "liker_id", null: false diff --git a/db/seeds.rb b/db/seeds.rb index 1beea2acc..90103ab89 100644 --- a/db/seeds.rb +++ b/db/seeds.rb @@ -5,3 +5,62 @@ # # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) # Character.create(name: 'Luke', movie: movies.first) +puts "seeding.. destroying assets..." +User.destroy_all +Post.destroy_all +Like.destroy_all +Friending.destroy_all +Comment.destroy_all +puts "creating users..." +5.times do |i| + User.create(email: "foo#{i}@bar.com", + password: "foobar", password_confirmation: "foobar", + first_name: "foo#{i}", last_name: "bar#{i}", + gender: "male", b_month: 5, b_day: 3, b_year: 1985) +end + +puts "creating posts.." +User.all.each do |user| + Post.create(body: "I'm #{user.first_name}. Lorem Ipsem blah blee blue", + author_id: user.id) +end + +puts "creating comments" + +Post.all.each do |post| + user = User.all.sample + Comment.create(commentable_id: post.id, commentable_type: "Post", + commenter_id: user.id, body: "I'm #{user.first_name}. You're #{post.author.first_name}.") +end + +puts "creating likes" + Post.all.each do |post| + num = rand(3)+1 + num.times do + Like.create(likable_id: post.id, liker_id: User.all.sample.id, + likable_type: "Post") + end + end + + Comment.all.each do |comment| + num = rand(3)+1 + num.times do + Like.create(likable_id: comment.id, liker_id: User.all.sample.id, + likable_type: "Comment") + end + end + +puts "creating friends" +User.all.each do |user| + friends = [] + until friends.length == 2 + friend = User.all.sample + friend == user ? nil : friends << friend + friends.uniq! + end + friends.each do |friend| + Friending.create(friend_id: friend.id, friender_id: user.id) + end +end + +puts "done seeding" diff --git a/spec/controllers/friendings_controller_spec.rb b/spec/controllers/friendings_controller_spec.rb new file mode 100644 index 000000000..c06a36d2c --- /dev/null +++ b/spec/controllers/friendings_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe FriendingsController, :type => :controller do + +end diff --git a/spec/factories.rb b/spec/factories.rb index c3bd51726..6328501e6 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -1,4 +1,7 @@ -FactoryGirl.define do +FactoryGirl.define do factory :friending do + + end + factory :user, :aliases => [:author, :commenter, :liker] do sequence(:last_name) {|n| "Bar#{n}"} diff --git a/spec/helpers/friendings_helper_spec.rb b/spec/helpers/friendings_helper_spec.rb new file mode 100644 index 000000000..11de3fbab --- /dev/null +++ b/spec/helpers/friendings_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the FriendingsHelper. For example: +# +# describe FriendingsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe FriendingsHelper, :type => :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/friending_spec.rb b/spec/models/friending_spec.rb new file mode 100644 index 000000000..f5b657a41 --- /dev/null +++ b/spec/models/friending_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Friending, :type => :model do + pending "add some examples to (or delete) #{__FILE__}" +end From 4fa68cfe5b1f18567bdd3c837d4bb6d7f5e9da2f Mon Sep 17 00:00:00 2001 From: David J Watts Date: Fri, 9 Sep 2016 11:57:09 -0700 Subject: [PATCH 12/12] add photos and friends functionality --- .gitignore | 3 + Gemfile | 4 +- Gemfile.lock | 144 +++++++++++-------- app/assets/javascripts/covers.coffee | 3 + app/assets/javascripts/photos.coffee | 3 + app/assets/javascripts/profiles.coffee | 3 + app/assets/stylesheets/covers.scss | 3 + app/assets/stylesheets/photos.scss | 3 + app/assets/stylesheets/profiles.scss | 3 + app/controllers/application_controller.rb | 5 + app/controllers/covers_controller.rb | 33 +++++ app/controllers/photos_controller.rb | 62 ++++++++ app/controllers/profiles_controller.rb | 35 +++++ app/controllers/users_controller.rb | 14 ++ app/helpers/covers_helper.rb | 2 + app/helpers/photos_helper.rb | 3 + app/helpers/profiles_helper.rb | 2 + app/helpers/users_helper.rb | 14 +- app/models/cover.rb | 4 + app/models/photo.rb | 20 +++ app/models/profile.rb | 4 + app/models/user.rb | 28 ++++ app/views/photos/index.html.erb | 35 +++++ app/views/photos/new.html.erb | 12 ++ app/views/photos/show.html.erb | 98 +++++++++++++ app/views/users/_header.html.erb | 26 ++++ app/views/users/_nav.html.erb | 8 +- app/views/users/about.html.erb | 28 +--- app/views/users/about_edit.html.erb | 27 +--- app/views/users/friends.html.erb | 30 ++++ app/views/users/index.html.erb | 31 ++++ app/views/users/timeline.html.erb | 96 +++---------- config/environments/development.rb | 20 +++ config/environments/production.rb | 20 +++ config/routes.rb | 16 ++- config/secrets.yml | 11 +- db/migrate/20160908201444_create_photos.rb | 11 ++ db/migrate/20160909000243_create_covers.rb | 9 ++ db/migrate/20160909000250_create_profiles.rb | 9 ++ db/schema.rb | 26 +++- spec/controllers/covers_controller_spec.rb | 5 + spec/controllers/photos_controller_spec.rb | 5 + spec/controllers/profiles_controller_spec.rb | 5 + spec/helpers/covers_helper_spec.rb | 15 ++ spec/helpers/photos_helper_spec.rb | 15 ++ spec/helpers/profiles_helper_spec.rb | 15 ++ spec/models/cover_spec.rb | 5 + spec/models/photo_spec.rb | 5 + spec/models/profile_spec.rb | 5 + 49 files changed, 773 insertions(+), 210 deletions(-) create mode 100644 app/assets/javascripts/covers.coffee create mode 100644 app/assets/javascripts/photos.coffee create mode 100644 app/assets/javascripts/profiles.coffee create mode 100644 app/assets/stylesheets/covers.scss create mode 100644 app/assets/stylesheets/photos.scss create mode 100644 app/assets/stylesheets/profiles.scss create mode 100644 app/controllers/covers_controller.rb create mode 100644 app/controllers/photos_controller.rb create mode 100644 app/controllers/profiles_controller.rb create mode 100644 app/helpers/covers_helper.rb create mode 100644 app/helpers/photos_helper.rb create mode 100644 app/helpers/profiles_helper.rb create mode 100644 app/models/cover.rb create mode 100644 app/models/photo.rb create mode 100644 app/models/profile.rb create mode 100644 app/views/photos/index.html.erb create mode 100644 app/views/photos/new.html.erb create mode 100644 app/views/photos/show.html.erb create mode 100644 app/views/users/_header.html.erb create mode 100644 app/views/users/friends.html.erb create mode 100644 app/views/users/index.html.erb create mode 100644 db/migrate/20160908201444_create_photos.rb create mode 100644 db/migrate/20160909000243_create_covers.rb create mode 100644 db/migrate/20160909000250_create_profiles.rb create mode 100644 spec/controllers/covers_controller_spec.rb create mode 100644 spec/controllers/photos_controller_spec.rb create mode 100644 spec/controllers/profiles_controller_spec.rb create mode 100644 spec/helpers/covers_helper_spec.rb create mode 100644 spec/helpers/photos_helper_spec.rb create mode 100644 spec/helpers/profiles_helper_spec.rb create mode 100644 spec/models/cover_spec.rb create mode 100644 spec/models/photo_spec.rb create mode 100644 spec/models/profile_spec.rb diff --git a/.gitignore b/.gitignore index bab620de0..f0774c34e 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,6 @@ # Ignore Byebug command history file. .byebug_history + +# Ignore application configuration +/config/application.yml diff --git a/Gemfile b/Gemfile index 3a31664ba..004321fef 100644 --- a/Gemfile +++ b/Gemfile @@ -32,7 +32,9 @@ gem 'hirb' # Use Capistrano for deployment # gem 'capistrano-rails', group: :development gem 'pry-byebug' - +gem 'figaro' +gem 'paperclip' +gem 'aws-sdk' group :development, :test do gem 'shoulda-matchers', '~> 3.1' gem 'capybara' diff --git a/Gemfile.lock b/Gemfile.lock index 97f0faa1a..269428c52 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,45 +1,51 @@ GEM remote: http://rubygems.org/ specs: - actioncable (5.0.0) - actionpack (= 5.0.0) + actioncable (5.0.0.1) + actionpack (= 5.0.0.1) nio4r (~> 1.2) websocket-driver (~> 0.6.1) - actionmailer (5.0.0) - actionpack (= 5.0.0) - actionview (= 5.0.0) - activejob (= 5.0.0) + actionmailer (5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) mail (~> 2.5, >= 2.5.4) rails-dom-testing (~> 2.0) - actionpack (5.0.0) - actionview (= 5.0.0) - activesupport (= 5.0.0) + actionpack (5.0.0.1) + actionview (= 5.0.0.1) + activesupport (= 5.0.0.1) rack (~> 2.0) rack-test (~> 0.6.3) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - actionview (5.0.0) - activesupport (= 5.0.0) + actionview (5.0.0.1) + activesupport (= 5.0.0.1) builder (~> 3.1) erubis (~> 2.7.0) rails-dom-testing (~> 2.0) rails-html-sanitizer (~> 1.0, >= 1.0.2) - activejob (5.0.0) - activesupport (= 5.0.0) + activejob (5.0.0.1) + activesupport (= 5.0.0.1) globalid (>= 0.3.6) - activemodel (5.0.0) - activesupport (= 5.0.0) - activerecord (5.0.0) - activemodel (= 5.0.0) - activesupport (= 5.0.0) + activemodel (5.0.0.1) + activesupport (= 5.0.0.1) + activerecord (5.0.0.1) + activemodel (= 5.0.0.1) + activesupport (= 5.0.0.1) arel (~> 7.0) - activesupport (5.0.0) + activesupport (5.0.0.1) concurrent-ruby (~> 1.0, >= 1.0.2) i18n (~> 0.7) minitest (~> 5.1) tzinfo (~> 1.1) addressable (2.4.0) arel (7.1.1) + aws-sdk (2.5.10) + aws-sdk-resources (= 2.5.10) + aws-sdk-core (2.5.10) + jmespath (~> 1.0) + aws-sdk-resources (2.5.10) + aws-sdk-core (= 2.5.10) bcrypt (3.1.11) better_errors (2.1.1) coderay (>= 1.0.0) @@ -56,6 +62,10 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + climate_control (0.0.3) + activesupport (>= 3.0) + cocaine (0.5.8) + climate_control (>= 0.0.3, < 1.0) coderay (1.1.1) coffee-rails (4.2.1) coffee-script (>= 2.2.0) @@ -69,18 +79,20 @@ GEM diff-lcs (1.2.5) erubis (2.7.0) execjs (2.7.0) - factory_girl (4.5.0) + factory_girl (4.7.0) activesupport (>= 3.0.0) - factory_girl_rails (4.5.0) - factory_girl (~> 4.5.0) + factory_girl_rails (4.7.0) + factory_girl (~> 4.7.0) railties (>= 3.0.0) ffi (1.9.14) + figaro (1.1.1) + thor (~> 0.14) formatador (0.2.5) globalid (0.3.7) activesupport (>= 4.1.0) - guard (2.12.7) + guard (2.14.0) formatador (>= 0.2.4) - listen (>= 2.7, <= 4.0) + listen (>= 2.7, < 4.0) lumberjack (~> 1.0) nenv (~> 0.1) notiffany (~> 0.0) @@ -88,7 +100,7 @@ GEM shellany (~> 0.0) thor (>= 0.18.1) guard-compat (1.2.1) - guard-rspec (4.6.0) + guard-rspec (4.7.3) guard (~> 2.1) guard-compat (~> 1.1) rspec (>= 2.99.0, < 4.0) @@ -97,7 +109,8 @@ GEM jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) - jquery-rails (4.1.1) + jmespath (1.3.1) + jquery-rails (4.2.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) @@ -108,24 +121,31 @@ GEM rb-inotify (~> 0.9, >= 0.9.7) loofah (2.0.3) nokogiri (>= 1.5.9) - lumberjack (1.0.9) + lumberjack (1.0.10) mail (2.6.4) mime-types (>= 1.16, < 4) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) + mimemagic (0.3.2) mini_portile2 (2.1.0) minitest (5.9.0) multi_json (1.12.1) - nenv (0.2.0) + nenv (0.3.0) nio4r (1.2.1) nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) - notiffany (0.0.6) + notiffany (0.1.1) nenv (~> 0.1) shellany (~> 0.0) + paperclip (5.1.0) + activemodel (>= 4.2.0) + activesupport (>= 4.2.0) + cocaine (~> 0.5.5) + mime-types + mimemagic (~> 0.3.0) pg (0.18.4) pkg-config (1.1.7) pry (0.10.4) @@ -139,17 +159,17 @@ GEM rack (2.0.1) rack-test (0.6.3) rack (>= 1.0) - rails (5.0.0) - actioncable (= 5.0.0) - actionmailer (= 5.0.0) - actionpack (= 5.0.0) - actionview (= 5.0.0) - activejob (= 5.0.0) - activemodel (= 5.0.0) - activerecord (= 5.0.0) - activesupport (= 5.0.0) + rails (5.0.0.1) + actioncable (= 5.0.0.1) + actionmailer (= 5.0.0.1) + actionpack (= 5.0.0.1) + actionview (= 5.0.0.1) + activejob (= 5.0.0.1) + activemodel (= 5.0.0.1) + activerecord (= 5.0.0.1) + activesupport (= 5.0.0.1) bundler (>= 1.3.0, < 2.0) - railties (= 5.0.0) + railties (= 5.0.0.1) sprockets-rails (>= 2.0.0) rails-dom-testing (2.0.1) activesupport (>= 4.2.0, < 6.0) @@ -161,9 +181,9 @@ GEM rails_stdout_logging rails_serve_static_assets (0.0.5) rails_stdout_logging (0.0.5) - railties (5.0.0) - actionpack (= 5.0.0) - activesupport (= 5.0.0) + railties (5.0.0.1) + actionpack (= 5.0.0.1) + activesupport (= 5.0.0.1) method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) @@ -171,26 +191,27 @@ GEM rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) - rspec (3.1.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-core (3.1.7) - rspec-support (~> 3.1.0) - rspec-expectations (3.1.2) + rspec (3.5.0) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-core (3.5.3) + rspec-support (~> 3.5.0) + rspec-expectations (3.5.0) + diff-lcs (>= 1.2.0, < 2.0) + rspec-support (~> 3.5.0) + rspec-mocks (3.5.0) diff-lcs (>= 1.2.0, < 2.0) - rspec-support (~> 3.1.0) - rspec-mocks (3.1.3) - rspec-support (~> 3.1.0) - rspec-rails (3.1.0) + rspec-support (~> 3.5.0) + rspec-rails (3.5.2) actionpack (>= 3.0) activesupport (>= 3.0) railties (>= 3.0) - rspec-core (~> 3.1.0) - rspec-expectations (~> 3.1.0) - rspec-mocks (~> 3.1.0) - rspec-support (~> 3.1.0) - rspec-support (3.1.2) + rspec-core (~> 3.5.0) + rspec-expectations (~> 3.5.0) + rspec-mocks (~> 3.5.0) + rspec-support (~> 3.5.0) + rspec-support (3.5.0) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -209,7 +230,7 @@ GEM sprockets (3.7.0) concurrent-ruby (~> 1.0) rack (> 1, < 3) - sprockets-rails (3.1.1) + sprockets-rails (3.2.0) actionpack (>= 4.0) activesupport (>= 4.0) sprockets (>= 3.0.0) @@ -222,7 +243,7 @@ GEM turbolinks-source (5.0.0) tzinfo (1.2.2) thread_safe (~> 0.1) - uglifier (3.0.1) + uglifier (3.0.2) execjs (>= 0.3.0, < 3) web-console (3.3.1) actionview (>= 5.0) @@ -239,6 +260,7 @@ PLATFORMS ruby DEPENDENCIES + aws-sdk bcrypt (~> 3.1.7) better_errors binding_of_caller @@ -246,12 +268,14 @@ DEPENDENCIES capybara coffee-rails (~> 4.2) factory_girl_rails (~> 4.0) + figaro guard-rspec hirb jbuilder (~> 2.5) jquery-rails launchy listen (~> 3.0.5) + paperclip pg pry-byebug puma (~> 3.0) diff --git a/app/assets/javascripts/covers.coffee b/app/assets/javascripts/covers.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/covers.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/photos.coffee b/app/assets/javascripts/photos.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/photos.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/profiles.coffee b/app/assets/javascripts/profiles.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/profiles.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/covers.scss b/app/assets/stylesheets/covers.scss new file mode 100644 index 000000000..bac8df8c7 --- /dev/null +++ b/app/assets/stylesheets/covers.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the covers controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/photos.scss b/app/assets/stylesheets/photos.scss new file mode 100644 index 000000000..1a3e082cd --- /dev/null +++ b/app/assets/stylesheets/photos.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the photos controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/profiles.scss b/app/assets/stylesheets/profiles.scss new file mode 100644 index 000000000..287733f3e --- /dev/null +++ b/app/assets/stylesheets/profiles.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the profiles controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 16e3c2987..0cd011c3b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -29,6 +29,11 @@ def current_user end helper_method :current_user + def current_cover(user) + user.cover_photo ? user.cover_photo.data.url(:thumb) : ActionController::Base.helpers.asset_path("user.jpg") + end + helper_method :current_cover + def signed_in_user? !!current_user end diff --git a/app/controllers/covers_controller.rb b/app/controllers/covers_controller.rb new file mode 100644 index 000000000..3f78c4309 --- /dev/null +++ b/app/controllers/covers_controller.rb @@ -0,0 +1,33 @@ +class CoversController < ApplicationController + def create + @cover = Cover.new(cover_params) + @cover + if @cover.save + flash[:notice] = "Cover successfully created" + + else + flash[:notice] = "cover has not saved" + end + @photo = Photo.find(cover_params[:photo_id]) + @user = User.find(cover_params[:user_id]) + redirect_to :back + end + + def update + @cover = Cover.find(params[:id]) + @cover.photo_id = cover_params[:photo_id] + if @cover.save + flash[:success] = "Your cover has been updated!" + else + flash[:error] = "Error! The cover was not changed!" + end + @photo = Photo.find(cover_params[:photo_id]) + @user = User.find(cover_params[:user_id]) + redirect_to :back + + end + + def cover_params + params.permit(:user_id, :photo_id) + end +end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb new file mode 100644 index 000000000..df34805bb --- /dev/null +++ b/app/controllers/photos_controller.rb @@ -0,0 +1,62 @@ +class PhotosController < ApplicationController + + def index + @user = User.find(params[:user_id]) + @photos = @user.photos + end + + def new + @user = User.find(params[:user_id]) + @photo = Photo.new + end + + def create + @photo = Photo.new(photo_params) + @user = User.find(params[:user_id]) + + if @photo.save + + flash[:notice] = "photo successfully created" + redirect_to user_photo_path(@user,@photo) + else + flash[:notice] = "photo not created, fix your errors" + render :new + end + end + + def show + @photo = Photo.find(params[:id]) + @user = @photo.user + + end + + def update + @photo = Photo.find(params[:id]) + @user = @photo.user + if @photo.update(photo_params) + flash[:notice] = "photo succesfully updated" + redirect_to :back + else + flash[:notice] = "photo not updated, fix your errors" + redirect_to :back + end + end + + def destroy + @photo = Photo.find(params[:id]) + @user = @photo.user + + if @photo.destroy + flash[:success] = "Your photo has been deleted!" + else + flash[:error] = "Error! The photo lives on!" + end + redirect_to user_photos_path(@user) + + end + + def photo_params + params.require(:photo).permit(:user_id, :data, :comments_attributes => [ :body, :commentable_id, :commentable_type, :commenter_id ]) + end + +end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb new file mode 100644 index 000000000..80b8bc29f --- /dev/null +++ b/app/controllers/profiles_controller.rb @@ -0,0 +1,35 @@ +class ProfilesController < ApplicationController + + + def create + @profile = Profile.new(profile_params) + @profile + if @profile.save + flash[:notice] = "Profile successfully created" + + else + flash[:notice] = "profile has not saved" + end + @photo = Photo.find(profile_params[:photo_id]) + @user = User.find(profile_params[:user_id]) + redirect_to :back + end + + def update + @profile = Profile.find(params[:id]) + @profile.photo_id = profile_params[:photo_id] + if @profile.save + flash[:success] = "Your profile has been updated!" + else + flash[:error] = "Error! The profile was not changed!" + end + @photo = Photo.find(profile_params[:photo_id]) + @user = User.find(profile_params[:user_id]) + redirect_to :back + + end + + def profile_params + params.permit(:user_id, :photo_id) + end +end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index deb48382d..937588f70 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -9,6 +9,19 @@ def new def timeline params[:id].nil? ? @user = current_user : @user = User.find(params[:id]) + @friends = @user.friends.sample(6) + @photos = @user.photos.sample(6) + + end + + def index + @users = User.search(params[:query]) + + end + + def friends + params[:user_id].nil? ? @user = current_user : @user = User.find(params[:user_id]) + @friends = @user.friends end @@ -39,6 +52,7 @@ def update def edit @current_user + @user = @current_user render "about_edit" end diff --git a/app/helpers/covers_helper.rb b/app/helpers/covers_helper.rb new file mode 100644 index 000000000..9ae92e711 --- /dev/null +++ b/app/helpers/covers_helper.rb @@ -0,0 +1,2 @@ +module CoversHelper +end diff --git a/app/helpers/photos_helper.rb b/app/helpers/photos_helper.rb new file mode 100644 index 000000000..b2396d3a5 --- /dev/null +++ b/app/helpers/photos_helper.rb @@ -0,0 +1,3 @@ +module PhotosHelper + +end diff --git a/app/helpers/profiles_helper.rb b/app/helpers/profiles_helper.rb new file mode 100644 index 000000000..4e430508f --- /dev/null +++ b/app/helpers/profiles_helper.rb @@ -0,0 +1,2 @@ +module ProfilesHelper +end diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index b6e277f23..3e3111da8 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -1,13 +1,17 @@ module UsersHelper - def friend_button + def friend_button(user) - if @current_user == @user + if @current_user == user nil - elsif @current_user.friended_users.include?(@user) - link_to "Unfriend", friending_path(Friending.where("friend_id = ?", @user.id).where("friender_id = ?", @current_user.id).first), :method => :delete, class: "btn btn-primary" + elsif @current_user.friends.include?(user) + if @current_user.friended_users.include?(user) + link_to "Unfriend", friending_path(Friending.where("friend_id = ?", user.id).where("friender_id = ?", @current_user.id).first), :method => :delete, class: "btn btn-default" + else + link_to "Unfriend", friending_path(Friending.where("friender_id = ?", user.id).where("friend_id = ?", @current_user.id).first), :method => :delete, class: "btn btn-default" + end else - link_to "Friend", friendings_path(friender_id: @current_user.id, friend_id: @user.id), :method => :post, class: "btn btn-primary" + link_to "Friend", friendings_path(friender_id: @current_user.id, friend_id: user.id), :method => :post, class: "btn btn-primary" end diff --git a/app/models/cover.rb b/app/models/cover.rb new file mode 100644 index 000000000..69959f3a7 --- /dev/null +++ b/app/models/cover.rb @@ -0,0 +1,4 @@ +class Cover < ApplicationRecord + belongs_to :user + belongs_to :photo +end diff --git a/app/models/photo.rb b/app/models/photo.rb new file mode 100644 index 000000000..4c2449273 --- /dev/null +++ b/app/models/photo.rb @@ -0,0 +1,20 @@ +class Photo < ApplicationRecord + belongs_to :user + has_attached_file :data, :styles => { :medium => "300x300", :thumb => "100x100" } + + validates_attachment_content_type :data, :content_type => /\Aimage\/.*\Z/ + + has_one :cover + has_one :cover_user, :through => :cover, :source => :user + has_one :profile + has_one :profile_user, :through => :profile, :source => :user + + has_many :likes, :as => :likable + has_many :likers, :through => :likes + + has_many :comments, :as => :commentable, :inverse_of => :commentable + has_many :commenters, :through => :comments + accepts_nested_attributes_for :comments, + :reject_if => :all_blank, + :allow_destroy => true +end diff --git a/app/models/profile.rb b/app/models/profile.rb new file mode 100644 index 000000000..a0d376eff --- /dev/null +++ b/app/models/profile.rb @@ -0,0 +1,4 @@ +class Profile < ApplicationRecord + belongs_to :user + belongs_to :photo +end diff --git a/app/models/user.rb b/app/models/user.rb index effccb93b..b8c3fdcfe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,12 +1,34 @@ class User < ApplicationRecord + before_create :generate_token has_secure_password + has_many :photos + has_one :cover + has_one :cover_photo, :through => :cover, :source => :photo + has_one :profile + has_one :profile_photo, :through => :profile, :source => :photo + def self.search(query) + if query.nil? || query == "" + where("") + else + where("first_name LIKE ? OR last_name LIKE ?", "%#{query}%", "%#{query}%") + end + end + + def current_cover + self.cover_photo ? self.cover_photo.data.url(:thumb) : image_path('user.jpg') + end + def full_name "#{self.first_name} #{self.last_name}" end + def bday_string + "#{self.b_month}/#{self.b_day}/#{self.b_year}" + end + validates :password, :length => { :in => 3..24 }, :allow_nil => true @@ -41,5 +63,11 @@ def regenerate_auth_token has_many :users_friended_by, :through => :received_friendings, :source => :friend_initiator + def friends + (self.friended_users + self.users_friended_by).uniq + end + def photo_count + self.photos.count + end end diff --git a/app/views/photos/index.html.erb b/app/views/photos/index.html.erb new file mode 100644 index 000000000..de57e57ed --- /dev/null +++ b/app/views/photos/index.html.erb @@ -0,0 +1,35 @@ +<%= render '/users/nav' %> +<%= render '/users/header'%> + + + +
+
+
+

Photos

+
+
+ <%= link_to "Add Photo", new_user_photo_path(@user), :method => :get, class: "btn btn-primary", id: "edit_btn" %> + + +
+ +
+ +
+ <% @photos.each do |photo|%> +
+ <% if @user.friends.include?(@current_user) || @user == @current_user %> + + + + <%else %> + + <%end%> +
+ <%end%> + +
+ +
+ diff --git a/app/views/photos/new.html.erb b/app/views/photos/new.html.erb new file mode 100644 index 000000000..ca6ed042b --- /dev/null +++ b/app/views/photos/new.html.erb @@ -0,0 +1,12 @@ +<%= render '/users/nav' %> + +

create a photo!

+

+<%= form_for [@user, @photo] do |f| %> +<%= f.hidden_field :user_id, value: @user.id %> +<%= f.label :data %> +<%= f.file_field :data %> +<%= f.submit "Upload Photo", class: "btn btn-primary" %> + +<%end%> +

diff --git a/app/views/photos/show.html.erb b/app/views/photos/show.html.erb new file mode 100644 index 000000000..1e2c7dddd --- /dev/null +++ b/app/views/photos/show.html.erb @@ -0,0 +1,98 @@ +<%= render '/users/nav' %> + + +

+
+
+

Your Photo

+
+ +
+ +
+
+ +
+ + <%= image_tag @photo.data.url %>
+ <% if @user.cover_photo.nil? %> + <%=link_to "Set as Cover", covers_path(:user_id => @user.id, :photo_id => @photo.id), :method => :post%> + <%elsif @user.cover_photo != @photo %> + <%=link_to "Set as Cover", cover_path(@user.cover, :photo_id => @photo.id), :method => :patch%> + <% else %> + This is your cover photo + <%end%> +
+ <% if @user.profile_photo.nil? %> + <%=link_to "Set as Profile", profiles_path(:user_id => @user.id, :photo_id => @photo.id), :method => :post%> + <%elsif @user.profile_photo != @photo %> + <%=link_to "Set as Profile", profile_path(@user.profile, :photo_id => @photo.id), :method => :patch%> + <% else %> + This is your profile photo + <%end%> +
+ <% if @user == @current_user %> + <%=link_to "Delete Photo", user_photo_path(@user, @photo), :method => :delete, data: {:confirm => "Are you sure?"} %> + <%end%> +
+ + +
+
+
+
+
+
<%= link_to @user.full_name, user_path(@user) %>
Posted on <%= @photo.created_at.strftime("%d %b. %Y")%>
+ + <% unless @photo.likers.include?(current_user) %> + <%=link_to "Like", likes_path(:likable_id => @photo.id, :likable_type => @photo.class), :method => :post%> + <%else%> + <%=link_to "Unlike", like_path(@photo.likes.where("liker_id = ?", current_user.id).first), :method => :delete%> + <%end%> + <%= @photo.likes.count %> people have liked this photo. + + <%= form_for [@user, @photo] do |f| %> + <%= f.fields_for :comments, Comment.new do |c| %> + <%= c.text_area :body, rows: 3, placeholder: "Tell the world something...", id: "comment-area", class: "form_control" %> + <%= c.hidden_field :commentable_type, value: @photo.class %> + <%= c.hidden_field :commentable_id, value: @photo.id %> + <%= c.hidden_field :commenter_id, value: current_user.id %> + Cancel + <%end%> + <%= f.submit "Comment", class: "btn-primary", id: "post-btn"%> + <%end%> + +
+ +
+
+
+ <% @photo.comments.each do |c| %> +
+
+
+
+
+
<%=link_to c.commenter.full_name, user_path(c.commenter) %> Said on <%= c.created_at.strftime("%d %b. %Y")%>
+

<%=c.body%>

+ <% unless c.likers.include?(current_user) %> + <%=link_to "Like", likes_path(:likable_id => c.id, :likable_type => c.class), :method => :post%> + <%else%> + <%=link_to "Unlike", like_path(c.likes.where("liker_id = ?", current_user.id).first), :method => :delete%> + <%end%> + <% if c.commenter == current_user %> + + <%end%> + <%= c.likes.count %> people like this +
+
+ + + <%end%> + +
+
+
+ +
+
diff --git a/app/views/users/_header.html.erb b/app/views/users/_header.html.erb new file mode 100644 index 000000000..eba9cf878 --- /dev/null +++ b/app/views/users/_header.html.erb @@ -0,0 +1,26 @@ +
+
+
+ +

<%= @user.full_name%>

+
+
+ + +
diff --git a/app/views/users/_nav.html.erb b/app/views/users/_nav.html.erb index 4978821f8..d611cb56e 100644 --- a/app/views/users/_nav.html.erb +++ b/app/views/users/_nav.html.erb @@ -3,16 +3,16 @@
-