From 32bc848680c8d2a65696aeed0f20fd023e25d0d0 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Wed, 10 Aug 2016 20:07:56 -0400 Subject: [PATCH 01/31] Danebook day 1. Create session, models users and profile --- .gitignore | 21 ++ Gemfile | 62 ++++++ Gemfile.lock | 203 ++++++++++++++++++ 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/javascripts/profile.coffee | 3 + app/assets/javascripts/sessions.coffee | 3 + app/assets/javascripts/users.coffee | 3 + app/assets/stylesheets/application.css | 39 ++++ app/assets/stylesheets/profile.scss | 3 + app/assets/stylesheets/sessions.scss | 3 + app/assets/stylesheets/users.scss | 4 + app/channels/application_cable/channel.rb | 4 + app/channels/application_cable/connection.rb | 4 + app/controllers/application_controller.rb | 51 +++++ app/controllers/concerns/.keep | 0 app/controllers/profiles_controller.rb | 13 ++ app/controllers/sessions_controller.rb | 24 +++ app/controllers/users_controller.rb | 54 +++++ app/helpers/application_helper.rb | 2 + app/helpers/profile_helper.rb | 2 + app/helpers/sessions_helper.rb | 2 + app/helpers/users_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/models/profile.rb | 3 + app/models/user.rb | 24 +++ app/views/layouts/application.html.erb | 25 +++ app/views/layouts/mailer.html.erb | 13 ++ app/views/layouts/mailer.text.erb | 1 + app/views/profile/destroy.html.erb | 2 + app/views/profile/edit.html.erb | 2 + app/views/profile/show.html.erb | 2 + app/views/profile/update.html.erb | 2 + app/views/users/_new_navbar.html.erb | 24 +++ app/views/users/edit.html.erb | 2 + app/views/users/index.html.erb | 2 + app/views/users/new.html.erb | 12 ++ app/views/users/show.html.erb | 2 + bin/bundle | 3 + bin/rails | 9 + bin/rake | 9 + bin/setup | 34 +++ bin/spring | 15 ++ 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 | 22 ++ config/secrets.yml | 22 ++ config/spring.rb | 6 + db/migrate/20160810213213_create_users.rb | 8 + ...10214240_add_password_and_email_to_user.rb | 6 + db/migrate/20160810220240_add_auth_token.rb | 6 + db/migrate/20160810232400_create_profiles.rb | 15 ++ db/schema.rb | 38 ++++ 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/controllers/profile_controller_test.rb | 24 +++ test/controllers/sessions_controller_test.rb | 7 + test/controllers/users_controller_test.rb | 39 ++++ test/fixtures/.keep | 0 test/fixtures/files/.keep | 0 test/fixtures/profiles.yml | 7 + test/fixtures/users.yml | 11 + test/helpers/.keep | 0 test/integration/.keep | 0 test/mailers/.keep | 0 test/models/.keep | 0 test/models/profile_test.rb | 7 + test/models/user_test.rb | 7 + test/test_helper.rb | 10 + tmp/.keep | 0 vendor/assets/javascripts/.keep | 0 vendor/assets/stylesheets/.keep | 0 109 files changed, 1619 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/javascripts/profile.coffee create mode 100644 app/assets/javascripts/sessions.coffee create mode 100644 app/assets/javascripts/users.coffee create mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/profile.scss create mode 100644 app/assets/stylesheets/sessions.scss create mode 100644 app/assets/stylesheets/users.scss 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/controllers/profiles_controller.rb create mode 100644 app/controllers/sessions_controller.rb create mode 100644 app/controllers/users_controller.rb create mode 100644 app/helpers/application_helper.rb create mode 100644 app/helpers/profile_helper.rb create mode 100644 app/helpers/sessions_helper.rb create mode 100644 app/helpers/users_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/models/profile.rb create mode 100644 app/models/user.rb 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 100644 app/views/profile/destroy.html.erb create mode 100644 app/views/profile/edit.html.erb create mode 100644 app/views/profile/show.html.erb create mode 100644 app/views/profile/update.html.erb create mode 100644 app/views/users/_new_navbar.html.erb create mode 100644 app/views/users/edit.html.erb create mode 100644 app/views/users/index.html.erb create mode 100644 app/views/users/new.html.erb create mode 100644 app/views/users/show.html.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/spring 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/migrate/20160810213213_create_users.rb create mode 100644 db/migrate/20160810214240_add_password_and_email_to_user.rb create mode 100644 db/migrate/20160810220240_add_auth_token.rb create mode 100644 db/migrate/20160810232400_create_profiles.rb create mode 100644 db/schema.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/controllers/profile_controller_test.rb create mode 100644 test/controllers/sessions_controller_test.rb create mode 100644 test/controllers/users_controller_test.rb create mode 100644 test/fixtures/.keep create mode 100644 test/fixtures/files/.keep create mode 100644 test/fixtures/profiles.yml create mode 100644 test/fixtures/users.yml 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/models/profile_test.rb create mode 100644 test/models/user_test.rb 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..4b3488b02 --- /dev/null +++ b/Gemfile @@ -0,0 +1,62 @@ +source 'https://rubygems.org' + +gem 'better_errors' + +gem 'hirb' + +gem 'bootstrap' + +gem 'bootstrap-sass' + +gem 'binding_of_caller' + +gem 'faker' + +gem 'pg' + +gem 'bcrypt' +# 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 + # 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..0a44d2cc9 --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,203 @@ +GEM + remote: https://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) + autoprefixer-rails (6.4.0.1) + execjs + bcrypt (3.1.11) + 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) + bootstrap (4.0.0.alpha3.1) + autoprefixer-rails (>= 6.0.3) + sass (>= 3.4.19) + bootstrap-sass (3.3.7) + autoprefixer-rails (>= 5.2.1) + sass (>= 3.3.4) + 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) + faker (1.6.6) + i18n (~> 0.5) + 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) + 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) + pg (0.18.4) + 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 + bcrypt + better_errors + binding_of_caller + bootstrap + bootstrap-sass + byebug + coffee-rails (~> 4.2) + faker + hirb + jbuilder (~> 2.5) + jquery-rails + listen (~> 3.0.5) + pg + 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/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/javascripts/profile.coffee b/app/assets/javascripts/profile.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/profile.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/sessions.coffee b/app/assets/javascripts/sessions.coffee new file mode 100644 index 000000000..24f83d18b --- /dev/null +++ b/app/assets/javascripts/sessions.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/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/application.css b/app/assets/stylesheets/application.css new file mode 100644 index 000000000..9a16b7c5e --- /dev/null +++ b/app/assets/stylesheets/application.css @@ -0,0 +1,39 @@ +/* + * 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 + */ + + .new-user-navbar { + background-color: white; + } + + .log-in-navbar { + background-color: #3b5998; + + } + + .log-in-button { + background-color: #1E90FF; + } + +.log-in-information { + float:right; + display:inline-block; + margin-top:15px; + margin-right:20px; +} + +.danebook-words { + float:left; +} \ No newline at end of file diff --git a/app/assets/stylesheets/profile.scss b/app/assets/stylesheets/profile.scss new file mode 100644 index 000000000..22ee50876 --- /dev/null +++ b/app/assets/stylesheets/profile.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Profile controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/sessions.scss b/app/assets/stylesheets/sessions.scss new file mode 100644 index 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/users.scss b/app/assets/stylesheets/users.scss new file mode 100644 index 000000000..b9aee0d51 --- /dev/null +++ b/app/assets/stylesheets/users.scss @@ -0,0 +1,4 @@ +// 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/ + 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..8decdefc7 --- /dev/null +++ b/app/controllers/application_controller.rb @@ -0,0 +1,51 @@ +class ApplicationController < ActionController::Base + protect_from_forgery with: :exception + + + private + + 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 + + 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 require_login + unless signed_in_user? + flash[:error] = "You are not logged in. Please login." + redirect_to new_user_path + end + end + + def require_current_user + unless params[:id] == current_user.id.to_s + flash[:error] = "You're not allowed there. Nice try." + redirect_to root_url + end + end + + +end diff --git a/app/controllers/concerns/.keep b/app/controllers/concerns/.keep new file mode 100644 index 000000000..e69de29bb diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb new file mode 100644 index 000000000..eeaf2d50b --- /dev/null +++ b/app/controllers/profiles_controller.rb @@ -0,0 +1,13 @@ +class ProfileController < ApplicationController + def edit + end + + def update + end + + def show + end + + def destroy + end +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb new file mode 100644 index 000000000..087c11a64 --- /dev/null +++ b/app/controllers/sessions_controller.rb @@ -0,0 +1,24 @@ +class SessionsController < ApplicationController + + 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 into Danebook!" + redirect_to user_profile_path(@user) + else + flash.now[:error] = "We did not sign you into Danebook" + render :new + end + end + + def destroy + sign_out + flash[:success] = "You've successfully signed out" + redirect_to root_url + end +end \ No newline at end of file diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb new file mode 100644 index 000000000..16589de51 --- /dev/null +++ b/app/controllers/users_controller.rb @@ -0,0 +1,54 @@ +class UsersController < ApplicationController + before_action :set_user, only: [:edit, :update, :destroy, :show] + before_action :require_login, except: [:new, :create] + before_action :require_current_user, only: [:edit, :update, :destroy] + + def new + @user = User.new + end + + def index + end + + def edit + + end + + def update + if current_user.update(whitelisted_user_params) + flash[:success] = "Your Danebook profile has been updated" + redirect_to current_user + else + flash.now[:failure] = "Failed to update your Danebook profile" + render :edit + end + end + + def create + @user = User.new(user_params) + if @user.save + sign_in(@user) + flash[:success] = "Welcome to Danebook!" + redirect_to user_profile_path([@user.id, @user.profile.id]) + else + flash.now[:error] = "Something went wrong and your account was not saved." + render :new + end + end + + def destroy + end + + def show + end + + private + + def user_params + params.require(:user).permit(:username, :email, :password, :password_confirmation) + end + + def set_user + @user = User.find(params[:id]) + end +end 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/helpers/profile_helper.rb b/app/helpers/profile_helper.rb new file mode 100644 index 000000000..5a0d6b31f --- /dev/null +++ b/app/helpers/profile_helper.rb @@ -0,0 +1,2 @@ +module ProfileHelper +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/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/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/models/profile.rb b/app/models/profile.rb new file mode 100644 index 000000000..ad3560b45 --- /dev/null +++ b/app/models/profile.rb @@ -0,0 +1,3 @@ +class Profile < ApplicationRecord + belongs_to :user, optional: :required +end diff --git a/app/models/user.rb b/app/models/user.rb new file mode 100644 index 000000000..2a2a9f6f8 --- /dev/null +++ b/app/models/user.rb @@ -0,0 +1,24 @@ +class User < ApplicationRecord + has_one :profile + + before_create :generate_token + after_create :create_profile + has_secure_password + + validates :password, + :length => { :in => 5..20 }, + :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 new file mode 100644 index 000000000..8f600f813 --- /dev/null +++ b/app/views/layouts/application.html.erb @@ -0,0 +1,25 @@ + + + + ProjectDanebook + <%= csrf_meta_tags %> + + + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> + <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %> + + + + +
+ <% flash.each do |key, value| %> +
<%= value %>
+ <% end %> +
+ <%= 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/app/views/profile/destroy.html.erb b/app/views/profile/destroy.html.erb new file mode 100644 index 000000000..3f502cfd2 --- /dev/null +++ b/app/views/profile/destroy.html.erb @@ -0,0 +1,2 @@ +

Profile#destroy

+

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

diff --git a/app/views/profile/edit.html.erb b/app/views/profile/edit.html.erb new file mode 100644 index 000000000..250b02908 --- /dev/null +++ b/app/views/profile/edit.html.erb @@ -0,0 +1,2 @@ +

Profile#edit

+

Find me in app/views/profile/edit.html.erb

diff --git a/app/views/profile/show.html.erb b/app/views/profile/show.html.erb new file mode 100644 index 000000000..6d27929b8 --- /dev/null +++ b/app/views/profile/show.html.erb @@ -0,0 +1,2 @@ +

Profile#show

+

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

diff --git a/app/views/profile/update.html.erb b/app/views/profile/update.html.erb new file mode 100644 index 000000000..e3e90c203 --- /dev/null +++ b/app/views/profile/update.html.erb @@ -0,0 +1,2 @@ +

Profile#update

+

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

diff --git a/app/views/users/_new_navbar.html.erb b/app/views/users/_new_navbar.html.erb new file mode 100644 index 000000000..2937bc9da --- /dev/null +++ b/app/views/users/_new_navbar.html.erb @@ -0,0 +1,24 @@ + + diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb new file mode 100644 index 000000000..1881fbdba --- /dev/null +++ b/app/views/users/edit.html.erb @@ -0,0 +1,2 @@ +

Users#edit

+

Find me in app/views/users/edit.html.erb

diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb new file mode 100644 index 000000000..51968c88d --- /dev/null +++ b/app/views/users/index.html.erb @@ -0,0 +1,2 @@ +

Users#index

+

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

diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb new file mode 100644 index 000000000..7e4bfd399 --- /dev/null +++ b/app/views/users/new.html.erb @@ -0,0 +1,12 @@ +<%= render "new_navbar" %> + +<%= form_for @user, html: {class: "new-user-navbar"} do |f| %> + <%= f.label :email%> + <%= f.text_field :email, placeholder: "Email", html: {class: "form-control"} %> + <%= f.label :password%> + <%= f.password_field :password, placeholder: "Password", html: {class: "form-control"} %> + <%= f.label :password%> + <%= f.password_field :password_confirmation, placeholder: "Password Confirmation", html: {class: "form-control"} %> + <%= f.submit :Create %> +<% end %> + diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb new file mode 100644 index 000000000..e5fa3adf1 --- /dev/null +++ b/app/views/users/show.html.erb @@ -0,0 +1,2 @@ +

Users#show

+

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

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..5badb2fde --- /dev/null +++ b/bin/rails @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +APP_PATH = File.expand_path('../config/application', __dir__) +require_relative '../config/boot' +require 'rails/commands' diff --git a/bin/rake b/bin/rake new file mode 100755 index 000000000..d87d5f578 --- /dev/null +++ b/bin/rake @@ -0,0 +1,9 @@ +#!/usr/bin/env ruby +begin + load File.expand_path('../spring', __FILE__) +rescue LoadError => e + raise unless e.message.include?('spring') +end +require_relative '../config/boot' +require 'rake' +Rake.application.run diff --git a/bin/setup b/bin/setup new file mode 100755 index 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/spring b/bin/spring new file mode 100755 index 000000000..7fe232c3a --- /dev/null +++ b/bin/spring @@ -0,0 +1,15 @@ +#!/usr/bin/env ruby + +# This file loads spring without using Bundler, in order to be fast. +# It gets overwritten when you run the `spring binstub` command. + +unless defined?(Spring) + require 'rubygems' + require 'bundler' + + if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m)) + Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) } + gem 'spring', match[1] + require 'spring/binstub' + end +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..af54ad3ac --- /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 ProjectDanebook + 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..5562853de --- /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: postgresql + pool: 5 + timeout: 5000 + +development: + <<: *default + database: db/danebook + +# 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/danebook + +production: + <<: *default + database: db/danebook 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..9f3378f91 --- /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 = "project_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..f2d118131 --- /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: '_project_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..ba198e447 --- /dev/null +++ b/config/routes.rb @@ -0,0 +1,22 @@ +Rails.application.routes.draw do + + get 'profile/edit' + + get 'profile/update' + + get 'profile/show' + + get 'profile/destroy' + + resources :users do + resources :profiles + end + + root "users#new" + + resource :session, :only => [:new, :create, :destroy] + get "login" => "sessions#new" + delete "logout" => "sessions#destroy" + + # 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..9dae25df2 --- /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: 4a8ab98976c8adefce7471fd7a3d1c240af246cc1dc57ead037ac053faab4f42b5d9f0ee2c3017d9825c535efe6a3e6e87ca18b841207161b336a9e14f5cfee4 + +test: + secret_key_base: 8ed5c1fef5f8594431a217909d96653bfd594285b6180e7786c16347f7f4671ae5a32a2104643aeaf846a87f832b0f59cbc2595fe2c204f720ae792e1a23b17c + +# 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/migrate/20160810213213_create_users.rb b/db/migrate/20160810213213_create_users.rb new file mode 100644 index 000000000..851ed6b47 --- /dev/null +++ b/db/migrate/20160810213213_create_users.rb @@ -0,0 +1,8 @@ +class CreateUsers < ActiveRecord::Migration[5.0] + def change + create_table :users do |t| + + t.timestamps + end + end +end diff --git a/db/migrate/20160810214240_add_password_and_email_to_user.rb b/db/migrate/20160810214240_add_password_and_email_to_user.rb new file mode 100644 index 000000000..2024bb0fd --- /dev/null +++ b/db/migrate/20160810214240_add_password_and_email_to_user.rb @@ -0,0 +1,6 @@ +class AddPasswordAndEmailToUser < ActiveRecord::Migration[5.0] + def change + add_column :users, :email, :string + add_column :users, :password_digest, :string + end +end diff --git a/db/migrate/20160810220240_add_auth_token.rb b/db/migrate/20160810220240_add_auth_token.rb new file mode 100644 index 000000000..c0f80d652 --- /dev/null +++ b/db/migrate/20160810220240_add_auth_token.rb @@ -0,0 +1,6 @@ +class AddAuthToken < 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/20160810232400_create_profiles.rb b/db/migrate/20160810232400_create_profiles.rb new file mode 100644 index 000000000..b700877f2 --- /dev/null +++ b/db/migrate/20160810232400_create_profiles.rb @@ -0,0 +1,15 @@ +class CreateProfiles < ActiveRecord::Migration[5.0] + def change + create_table :profiles do |t| + t.references :user, foreign_key: true + t.string :month + t.integer :year + t.integer :day + t.string :college + t.string :hometown + t.string :current_location + t.string :telephone + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb new file mode 100644 index 000000000..13e7b9ea8 --- /dev/null +++ b/db/schema.rb @@ -0,0 +1,38 @@ +# 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: 20160810232400) do + + create_table "profiles", force: :cascade do |t| + t.integer "user_id" + t.string "month" + t.integer "year" + t.integer "day" + t.string "college" + t.string "hometown" + t.string "current_location" + t.string "telephone" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_profiles_on_user_id" + end + + create_table "users", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "email" + t.string "password_digest" + t.string "auth_token" + t.index ["auth_token"], name: "index_users_on_auth_token", unique: true + end + +end 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/controllers/profile_controller_test.rb b/test/controllers/profile_controller_test.rb new file mode 100644 index 000000000..19f57e97b --- /dev/null +++ b/test/controllers/profile_controller_test.rb @@ -0,0 +1,24 @@ +require 'test_helper' + +class ProfileControllerTest < ActionDispatch::IntegrationTest + test "should get edit" do + get profile_edit_url + assert_response :success + end + + test "should get update" do + get profile_update_url + assert_response :success + end + + test "should get show" do + get profile_show_url + assert_response :success + end + + test "should get destroy" do + get profile_destroy_url + assert_response :success + end + +end diff --git a/test/controllers/sessions_controller_test.rb b/test/controllers/sessions_controller_test.rb new file mode 100644 index 000000000..6135ce6af --- /dev/null +++ b/test/controllers/sessions_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class SessionsControllerTest < 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 new file mode 100644 index 000000000..83fab60fb --- /dev/null +++ b/test/controllers/users_controller_test.rb @@ -0,0 +1,39 @@ +require 'test_helper' + +class UsersControllerTest < ActionDispatch::IntegrationTest + test "should get new" do + get users_new_url + assert_response :success + end + + test "should get index" do + get users_index_url + assert_response :success + end + + test "should get edit" do + get users_edit_url + assert_response :success + end + + test "should get update" do + get users_update_url + assert_response :success + end + + test "should get create" do + get users_create_url + assert_response :success + end + + test "should get destroy" do + get users_destroy_url + assert_response :success + end + + test "should get show" do + get users_show_url + assert_response :success + end + +end 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/fixtures/profiles.yml b/test/fixtures/profiles.yml new file mode 100644 index 000000000..526860bce --- /dev/null +++ b/test/fixtures/profiles.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: one + +two: + user: two diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml new file mode 100644 index 000000000..80aed36e3 --- /dev/null +++ b/test/fixtures/users.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/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/models/profile_test.rb b/test/models/profile_test.rb new file mode 100644 index 000000000..3dfa94302 --- /dev/null +++ b/test/models/profile_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class ProfileTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end 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 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 08cfd03964c899680d7776f1d2b9ba70cdc8c0bb Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Wed, 10 Aug 2016 20:37:06 -0400 Subject: [PATCH 02/31] Add postgresq; --- Gemfile | 2 +- Gemfile.lock | 2 -- app/controllers/profiles_controller.rb | 2 +- app/controllers/sessions_controller.rb | 3 ++- app/controllers/users_controller.rb | 2 +- app/views/{profile => profiles}/destroy.html.erb | 0 app/views/{profile => profiles}/edit.html.erb | 0 app/views/{profile => profiles}/show.html.erb | 0 app/views/{profile => profiles}/update.html.erb | 0 config/database.yml | 6 +++--- config/routes.rb | 9 +-------- db/schema.rb | 8 ++++++-- 12 files changed, 15 insertions(+), 19 deletions(-) rename app/views/{profile => profiles}/destroy.html.erb (100%) rename app/views/{profile => profiles}/edit.html.erb (100%) rename app/views/{profile => profiles}/show.html.erb (100%) rename app/views/{profile => profiles}/update.html.erb (100%) diff --git a/Gemfile b/Gemfile index 4b3488b02..f6e51230c 100644 --- a/Gemfile +++ b/Gemfile @@ -18,7 +18,7 @@ gem 'bcrypt' # 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 'sqlite3' # Use Puma as the app server gem 'puma', '~> 3.0' # Use SCSS for stylesheets diff --git a/Gemfile.lock b/Gemfile.lock index 0a44d2cc9..364648895 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -151,7 +151,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) @@ -193,7 +192,6 @@ 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/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index eeaf2d50b..66071f3d0 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -1,4 +1,4 @@ -class ProfileController < ApplicationController +class ProfilesController < ApplicationController def edit end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 087c11a64..5c80d98dd 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -9,9 +9,10 @@ def create sign_in(@user) end flash[:success] = "You've successfully signed into Danebook!" - redirect_to user_profile_path(@user) + redirect_to root_url else flash.now[:error] = "We did not sign you into Danebook" + #fix this render :new end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 16589de51..66d3008c9 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -29,7 +29,7 @@ def create if @user.save sign_in(@user) flash[:success] = "Welcome to Danebook!" - redirect_to user_profile_path([@user.id, @user.profile.id]) + redirect_to user_profiles_path(@user) else flash.now[:error] = "Something went wrong and your account was not saved." render :new diff --git a/app/views/profile/destroy.html.erb b/app/views/profiles/destroy.html.erb similarity index 100% rename from app/views/profile/destroy.html.erb rename to app/views/profiles/destroy.html.erb diff --git a/app/views/profile/edit.html.erb b/app/views/profiles/edit.html.erb similarity index 100% rename from app/views/profile/edit.html.erb rename to app/views/profiles/edit.html.erb diff --git a/app/views/profile/show.html.erb b/app/views/profiles/show.html.erb similarity index 100% rename from app/views/profile/show.html.erb rename to app/views/profiles/show.html.erb diff --git a/app/views/profile/update.html.erb b/app/views/profiles/update.html.erb similarity index 100% rename from app/views/profile/update.html.erb rename to app/views/profiles/update.html.erb diff --git a/config/database.yml b/config/database.yml index 5562853de..57fcbb54e 100644 --- a/config/database.yml +++ b/config/database.yml @@ -11,15 +11,15 @@ default: &default development: <<: *default - database: db/danebook + database: db/danebook_development # 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/danebook + database: db/danebook_test production: <<: *default - database: db/danebook + database: db/danebook_production diff --git a/config/routes.rb b/config/routes.rb index ba198e447..179a852f5 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,15 +1,8 @@ Rails.application.routes.draw do - get 'profile/edit' - - get 'profile/update' - - get 'profile/show' - - get 'profile/destroy' resources :users do - resources :profiles + resource :profiles end root "users#new" diff --git a/db/schema.rb b/db/schema.rb index 13e7b9ea8..677f9b092 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -12,6 +12,9 @@ ActiveRecord::Schema.define(version: 20160810232400) do + # These are extensions that must be enabled in order to support this database + enable_extension "plpgsql" + create_table "profiles", force: :cascade do |t| t.integer "user_id" t.string "month" @@ -23,7 +26,7 @@ t.string "telephone" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.index ["user_id"], name: "index_profiles_on_user_id" + t.index ["user_id"], name: "index_profiles_on_user_id", using: :btree end create_table "users", force: :cascade do |t| @@ -32,7 +35,8 @@ t.string "email" t.string "password_digest" t.string "auth_token" - t.index ["auth_token"], name: "index_users_on_auth_token", unique: true + t.index ["auth_token"], name: "index_users_on_auth_token", unique: true, using: :btree end + add_foreign_key "profiles", "users" end From 94f13731c0838bc4cf176630935aa20d0fa04793 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Thu, 11 Aug 2016 20:20:59 -0400 Subject: [PATCH 03/31] Finish homepage and start about page --- Gemfile | 1 + Gemfile.lock | 73 ++++--- app/assets/images/default_cover.jpg | Bin 0 -> 73659 bytes app/assets/images/default_image.jpg | Bin 0 -> 12773 bytes app/assets/stylesheets/application.css | 39 ---- app/assets/stylesheets/application.scss | 191 ++++++++++++++++++ app/controllers/profiles_controller.rb | 6 + app/controllers/sessions_controller.rb | 4 +- app/controllers/users_controller.rb | 2 +- app/models/photo.rb | 3 + app/models/profile.rb | 2 +- app/models/user.rb | 5 +- app/views/profiles/edit.html.erb | 5 + app/views/profiles/show.html.erb | 26 ++- app/views/shared/_profile_header.html.erb | 25 +++ app/views/shared/_search_navbar.html.erb | 47 +++++ app/views/users/_new_navbar.html.erb | 62 ++++-- app/views/users/new.html.erb | 47 ++++- config/routes.rb | 3 +- ...195710_add_first_name_last_name_to_user.rb | 6 + db/migrate/20160811214951_create_photos.rb | 9 + db/schema.rb | 13 +- test/fixtures/photos.yml | 7 + test/models/photo_test.rb | 7 + 24 files changed, 475 insertions(+), 108 deletions(-) create mode 100644 app/assets/images/default_cover.jpg create mode 100644 app/assets/images/default_image.jpg delete mode 100644 app/assets/stylesheets/application.css create mode 100644 app/assets/stylesheets/application.scss create mode 100644 app/models/photo.rb create mode 100644 app/views/shared/_profile_header.html.erb create mode 100644 app/views/shared/_search_navbar.html.erb create mode 100644 db/migrate/20160811195710_add_first_name_last_name_to_user.rb create mode 100644 db/migrate/20160811214951_create_photos.rb create mode 100644 test/fixtures/photos.yml create mode 100644 test/models/photo_test.rb diff --git a/Gemfile b/Gemfile index f6e51230c..dcedb8a5a 100644 --- a/Gemfile +++ b/Gemfile @@ -1,4 +1,5 @@ source 'https://rubygems.org' +gem 'carrierwave' gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index 364648895..c905e2bb3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,39 +1,39 @@ GEM remote: https://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) @@ -56,6 +56,12 @@ GEM sass (>= 3.3.4) builder (3.2.2) byebug (9.0.5) + carrierwave (0.11.2) + activemodel (>= 3.2.0) + activesupport (>= 3.2.0) + json (>= 1.7) + mime-types (>= 1.16) + mimemagic (>= 0.3.0) coderay (1.1.1) coffee-rails (4.2.1) coffee-script (>= 2.2.0) @@ -82,6 +88,7 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + json (2.0.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -93,6 +100,7 @@ GEM 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) @@ -106,26 +114,26 @@ 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) 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) + 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) @@ -180,6 +188,7 @@ DEPENDENCIES bootstrap bootstrap-sass byebug + carrierwave coffee-rails (~> 4.2) faker hirb diff --git a/app/assets/images/default_cover.jpg b/app/assets/images/default_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/default_image.jpg b/app/assets/images/default_image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..3d660d77d5774558ed2514cb52ef4972e34aab36 GIT binary patch literal 12773 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 OQPqUisc7r<=l=yD>bA82 literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/application.css b/app/assets/stylesheets/application.css deleted file mode 100644 index 9a16b7c5e..000000000 --- a/app/assets/stylesheets/application.css +++ /dev/null @@ -1,39 +0,0 @@ -/* - * This is a manifest file that'll be compiled into application.css, which will include all the files - * listed below. - * - * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, - * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. - * - * You're free to add application-wide styles to this file and they'll appear at the bottom of the - * compiled file so the styles you add here take precedence over styles defined in any 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 - */ - - .new-user-navbar { - background-color: white; - } - - .log-in-navbar { - background-color: #3b5998; - - } - - .log-in-button { - background-color: #1E90FF; - } - -.log-in-information { - float:right; - display:inline-block; - margin-top:15px; - margin-right:20px; -} - -.danebook-words { - float:left; -} \ No newline at end of file diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss new file mode 100644 index 000000000..ae9f59fe4 --- /dev/null +++ b/app/assets/stylesheets/application.scss @@ -0,0 +1,191 @@ +/* + * 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 + */ + +body { + background-color: #dfe3ee; + font-family: "Helvetica"; +} + +/* USERS/NEW */ + + .log-in-navbar { + background-color: #3b5998; + + } + + .log-in-button { + background-color: #1E90FF; + margin-top:25px; + margin-left:-40px; + + } + +.log-in-information { + float:right; + display:inline-block; + margin-top:10px; + +} + +.danebook-text { + color:white; +} + +.remember-me { + margin-top:25px; +} + +.danebook { + color:white; + text-decoration: none; +} + +.logged-in-text { + display:inline-block; + color:white; + font-weight: bold; +} + +.connect-information-container { + +} + +.connect-information { + margin-top:50px; +} + +.connect-information-point { + margin-top:30px; + color:grey; + font-size:20px; +} + +.sign-up-field { + margin-top: 40px; +} + +.danebook-logo { + float:left; + margin-left:-70px; + color:white; + border: 4px solid white; + padding:0px 10px 0px 0px; +} + + +.search-input { + width:65%; + vertical-align: center; +} + + +/* USER/:id/PROFILES */ + +.cover-photo { + width:90%; + height:300px; + display:block; + margin:auto; + margin-top:-20px; +} + +.profile-photo { + position:absolute; + margin-top:61px; + height:35%; + width:15%; +} + +.profile-navigation { + border: 2px solid black; + +} + +.timeline-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#d3d3d3; + } +} + +.about-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#d3d3d3; + } +} + +.photos-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#d3d3d3; + } +} + +.friends-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + transition: all 0.5s ease; + &:hover { + background-color:#d3d3d3; + } +} + +.edit-link { + border: 1px solid black; + height:40px; + text-align: center; + font-size:20px; + color:#3b5998; + transition: all 0.5s ease; + &:hover { + background-color:#d3d3d3; + } +} + +.about-user-section { + background-color: #d3d3d3; + text-align: center; + border:2px solid black; + font-size:30px; + margin-top:2em; +} + +.basic-and-words-to-live { + +} + +.basic { + background-color: red; +} + +.words-to-live { + background-color: black; +} \ No newline at end of file diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 66071f3d0..1ee881e6c 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -6,8 +6,14 @@ def update end def show + @user = User.find(params[:user_id]) + @profile = @user.profile end def destroy end + + def index + render :show + end end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 5c80d98dd..1f8d500a7 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -11,9 +11,9 @@ def create flash[:success] = "You've successfully signed into Danebook!" redirect_to root_url else - flash.now[:error] = "We did not sign you into Danebook" + flash[:error] = "We did not sign you into Danebook. Please provide a valid email and password" #fix this - render :new + redirect_to root_url end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 66d3008c9..64bd3d39f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -45,7 +45,7 @@ def show private def user_params - params.require(:user).permit(:username, :email, :password, :password_confirmation) + params.require(:user).permit(:username, :email, :first_name, :last_name, :password, :password_confirmation) end def set_user diff --git a/app/models/photo.rb b/app/models/photo.rb new file mode 100644 index 000000000..78f6bc668 --- /dev/null +++ b/app/models/photo.rb @@ -0,0 +1,3 @@ +class Photo < ApplicationRecord + belongs_to :profile +end diff --git a/app/models/profile.rb b/app/models/profile.rb index ad3560b45..926c9f226 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -1,3 +1,3 @@ class Profile < ApplicationRecord - belongs_to :user, optional: :required + belongs_to :user end diff --git a/app/models/user.rb b/app/models/user.rb index 2a2a9f6f8..3b774489b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,5 +1,5 @@ class User < ApplicationRecord - has_one :profile + has_one :profile, dependent: :nullify before_create :generate_token after_create :create_profile @@ -8,6 +8,9 @@ class User < ApplicationRecord validates :password, :length => { :in => 5..20 }, :allow_nil => true + validates :first_name, :last_name, :email, presence: true + + accepts_nested_attributes_for :profile def generate_token begin diff --git a/app/views/profiles/edit.html.erb b/app/views/profiles/edit.html.erb index 250b02908..053ecf720 100644 --- a/app/views/profiles/edit.html.erb +++ b/app/views/profiles/edit.html.erb @@ -1,2 +1,7 @@

Profile#edit

Find me in app/views/profile/edit.html.erb

+ <%= form_for @profile do |p| %> + <%= p.text_field :college %> + <%= p.text_field :hometown %> + <%= p.text_field :current_location%> + <% end %> \ No newline at end of file diff --git a/app/views/profiles/show.html.erb b/app/views/profiles/show.html.erb index 6d27929b8..a21dddf92 100644 --- a/app/views/profiles/show.html.erb +++ b/app/views/profiles/show.html.erb @@ -1,2 +1,24 @@ -

Profile#show

-

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

+<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> + +
+
+
+ ABOUT +
+
+
+
+ <%= @profile.id%> + <%= @profile.current_location %> + <%= @profile.telephone %> + <%= @profile.user.first_name %> + <%= @profile.user.last_name %> + Basic +
+ +
+ Words to Live by +
+
+
\ No newline at end of file diff --git a/app/views/shared/_profile_header.html.erb b/app/views/shared/_profile_header.html.erb new file mode 100644 index 000000000..8f3cfe510 --- /dev/null +++ b/app/views/shared/_profile_header.html.erb @@ -0,0 +1,25 @@ +
+
+
+ <%= image_tag("default_image.jpg", class: "profile-photo") %> + <%= image_tag("default_cover.jpg", class: "cover-photo") %> +
+
+
+ + + + + +
+
\ No newline at end of file diff --git a/app/views/shared/_search_navbar.html.erb b/app/views/shared/_search_navbar.html.erb new file mode 100644 index 000000000..291e040a9 --- /dev/null +++ b/app/views/shared/_search_navbar.html.erb @@ -0,0 +1,47 @@ + + diff --git a/app/views/users/_new_navbar.html.erb b/app/views/users/_new_navbar.html.erb index 2937bc9da..fbe81c744 100644 --- a/app/views/users/_new_navbar.html.erb +++ b/app/views/users/_new_navbar.html.erb @@ -1,24 +1,48 @@ diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 7e4bfd399..1784d8d0f 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,12 +1,41 @@ + <%= render "new_navbar" %> -<%= form_for @user, html: {class: "new-user-navbar"} do |f| %> - <%= f.label :email%> - <%= f.text_field :email, placeholder: "Email", html: {class: "form-control"} %> - <%= f.label :password%> - <%= f.password_field :password, placeholder: "Password", html: {class: "form-control"} %> - <%= f.label :password%> - <%= f.password_field :password_confirmation, placeholder: "Password Confirmation", html: {class: "form-control"} %> - <%= f.submit :Create %> -<% end %> + +
+
+

Connect with all of your friends!

+
    +
  • See photos and updates in your news feed
  • +
  • Post your status for the world to see on your proile
  • +
  • Get in your with your friends by "friending" them
  • +
  • Like things beacause you are a postive person!
  • +
+
+
+ <%= form_for @user do |f| %> + + + + + <%= f.submit :Create, class: "btn btn-success sign-up-field" %> + <% end %> +
+
+ diff --git a/config/routes.rb b/config/routes.rb index 179a852f5..f93b8da80 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -2,7 +2,8 @@ resources :users do - resource :profiles + resource :profiles + resources :photos end root "users#new" diff --git a/db/migrate/20160811195710_add_first_name_last_name_to_user.rb b/db/migrate/20160811195710_add_first_name_last_name_to_user.rb new file mode 100644 index 000000000..f71848b74 --- /dev/null +++ b/db/migrate/20160811195710_add_first_name_last_name_to_user.rb @@ -0,0 +1,6 @@ +class AddFirstNameLastNameToUser < ActiveRecord::Migration[5.0] + def change + add_column :users, :first_name, :string + add_column :users, :last_name, :string + end +end diff --git a/db/migrate/20160811214951_create_photos.rb b/db/migrate/20160811214951_create_photos.rb new file mode 100644 index 000000000..5a536ef74 --- /dev/null +++ b/db/migrate/20160811214951_create_photos.rb @@ -0,0 +1,9 @@ +class CreatePhotos < ActiveRecord::Migration[5.0] + def change + create_table :photos do |t| + t.references :profile, foreign_key: true + t.string :url + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 677f9b092..99fc8db11 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,19 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160810232400) do +ActiveRecord::Schema.define(version: 20160811214951) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "photos", force: :cascade do |t| + t.integer "profile_id" + t.string "url" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["profile_id"], name: "index_photos_on_profile_id", using: :btree + end + create_table "profiles", force: :cascade do |t| t.integer "user_id" t.string "month" @@ -35,8 +43,11 @@ t.string "email" t.string "password_digest" t.string "auth_token" + t.string "first_name" + t.string "last_name" t.index ["auth_token"], name: "index_users_on_auth_token", unique: true, using: :btree end + add_foreign_key "photos", "profiles" add_foreign_key "profiles", "users" end diff --git a/test/fixtures/photos.yml b/test/fixtures/photos.yml new file mode 100644 index 000000000..b513c6b37 --- /dev/null +++ b/test/fixtures/photos.yml @@ -0,0 +1,7 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + profile: one + +two: + profile: two diff --git a/test/models/photo_test.rb b/test/models/photo_test.rb new file mode 100644 index 000000000..e2ec03a1b --- /dev/null +++ b/test/models/photo_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class PhotoTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end From aecbd0f0b2d2e233c64fb26f483793022da05c18 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Fri, 12 Aug 2016 13:51:53 -0400 Subject: [PATCH 04/31] Finish edit and about page --- app/assets/stylesheets/application.scss | 15 ++++- app/controllers/application_controller.rb | 4 +- app/controllers/profiles_controller.rb | 7 ++- app/controllers/users_controller.rb | 13 +++-- app/models/user.rb | 5 +- app/views/profiles/edit.html.erb | 57 ++++++++++++++++--- app/views/profiles/show.html.erb | 23 ++++---- app/views/shared/_profile_header.html.erb | 4 +- app/views/shared/_search_navbar.html.erb | 2 +- app/views/users/_new_navbar.html.erb | 3 +- app/views/users/new.html.erb | 34 ++++++----- ...60812160556_add_profile_text_attributes.rb | 6 ++ db/schema.rb | 4 +- 13 files changed, 121 insertions(+), 56 deletions(-) create mode 100644 db/migrate/20160812160556_add_profile_text_attributes.rb diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index ae9f59fe4..018777f8c 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -179,13 +179,22 @@ body { } .basic-and-words-to-live { - + margin-bottom: 10px; } .basic { - background-color: red; + border: 2px solid black } .words-to-live { - background-color: black; + border: 2px solid black +} + +.about-edit-link { + float:right; + text-decoration:none; +} + +.submit-edit-button { + margin-bottom:50px } \ No newline at end of file diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 8decdefc7..eddb3cb8d 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -41,9 +41,9 @@ def require_login end def require_current_user - unless params[:id] == current_user.id.to_s + unless params[:id] && params[:id] == current_user.id.to_s flash[:error] = "You're not allowed there. Nice try." - redirect_to root_url + redirect_to "http://nouveller.com/404/" end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 1ee881e6c..b96aa2380 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -1,12 +1,15 @@ class ProfilesController < ApplicationController + before_action :require_current_user, only: [:edit, :update, :destroy] def edit + @user = current_user + @profile = @user.profile end def update end def show - @user = User.find(params[:user_id]) + @user = current_user @profile = @user.profile end @@ -14,6 +17,6 @@ def destroy end def index - render :show + end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 64bd3d39f..5fc138d8e 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -15,12 +15,12 @@ def edit end def update - if current_user.update(whitelisted_user_params) + if current_user.update(user_params) flash[:success] = "Your Danebook profile has been updated" - redirect_to current_user + redirect_to user_profiles_path(current_user) else flash.now[:failure] = "Failed to update your Danebook profile" - render :edit + redirect_to user_profiles_path(current_user) end end @@ -29,7 +29,7 @@ def create if @user.save sign_in(@user) flash[:success] = "Welcome to Danebook!" - redirect_to user_profiles_path(@user) + redirect_to user_profiles_path(current_user) else flash.now[:error] = "Something went wrong and your account was not saved." render :new @@ -45,10 +45,11 @@ def show private def user_params - params.require(:user).permit(:username, :email, :first_name, :last_name, :password, :password_confirmation) + params.require(:user).permit(:email, :first_name, :last_name, :password, :password_confirmation, + profile_attributes: [:about_me, :words_to_live_by, :telephone, :current_location, :hometown, :college, :id]) end def set_user - @user = User.find(params[:id]) + @user = current_user end end diff --git a/app/models/user.rb b/app/models/user.rb index 3b774489b..863e3b0c8 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,13 +1,14 @@ class User < ApplicationRecord has_one :profile, dependent: :nullify + has_many :photos before_create :generate_token after_create :create_profile has_secure_password validates :password, - :length => { :in => 5..20 }, - :allow_nil => true + :length => { :in => 5..20 }, + :allow_nil => true validates :first_name, :last_name, :email, presence: true accepts_nested_attributes_for :profile diff --git a/app/views/profiles/edit.html.erb b/app/views/profiles/edit.html.erb index 053ecf720..e9a179900 100644 --- a/app/views/profiles/edit.html.erb +++ b/app/views/profiles/edit.html.erb @@ -1,7 +1,50 @@ -

Profile#edit

-

Find me in app/views/profile/edit.html.erb

- <%= form_for @profile do |p| %> - <%= p.text_field :college %> - <%= p.text_field :hometown %> - <%= p.text_field :current_location%> - <% end %> \ No newline at end of file +<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> + +
+
+
+ ABOUT + <%= link_to "Edit", edit_user_profiles_path(@user), class: "about-edit-link" %> +
+
+
+
+

Basic Information

+ <%= form_for @user do |p| %> +
+ Email: <%= p.text_field :email, placeholder: @user.email, class: "form-control" %> +
+
+ First Name: <%= p.text_field :first_name, placeholder: @user.first_name, class: "form-control" %> +
+
+ Last Name: <%= p.text_field :last_name, placeholder: @user.last_name, class: "form-control" %> +
+ <%= p.fields_for :profile do |user_profile| %> +
+ College: <%= user_profile.text_field :college, placeholder: @profile.college, class: "form-control" %> +
+
+ Hometown: <%= user_profile.text_field :hometown, placeholder: @profile.college, class: "form-control" %> +
+
+ Current Location: <%= user_profile.text_field :current_location, placeholder: @profile.college, class: "form-control" %> +
+
+ Telephone: <%= user_profile.text_field :telephone, placeholder: @profile.college, class: "form-control" %>
+
+

+
+

Words to Live by

+

<%= user_profile.text_area :about_me, placeholder: @profile.about_me, class: "form-control", cols: 8, rows: 8 %>

+

About me

+

<%= user_profile.text_area :words_to_live_by, placeholder: @profile.words_to_live_by, class: "form-control", cols: 9, rows: 9 %>

+
+ <% end %> +
+
+ <%= p.submit :Submit, class: "form-control btn btn-primary submit-edit-button" %> +
+ <% end %> +
\ No newline at end of file diff --git a/app/views/profiles/show.html.erb b/app/views/profiles/show.html.erb index a21dddf92..0f4283fad 100644 --- a/app/views/profiles/show.html.erb +++ b/app/views/profiles/show.html.erb @@ -5,20 +5,23 @@
ABOUT + <%= link_to "Edit", edit_user_profiles_path(@user), class: "about-edit-link" %>
-
- <%= @profile.id%> - <%= @profile.current_location %> - <%= @profile.telephone %> - <%= @profile.user.first_name %> - <%= @profile.user.last_name %> - Basic +
+

Basic Information

+ Name: <%= @profile.user.first_name.capitalize %> <%= @profile.user.last_name.capitalize %>
+ College: <%= @profile.college %>
+ Current Location: <%= @profile.current_location %>
+ Hometown: <%= @profile.hometown %>
+ Telephone: <%= @profile.telephone %>

-
- Words to Live by -
+
+

Words to Live by

+

<%= @profile.about_me%>

+

About me

+

<%= @profile.words_to_live_by%>

\ No newline at end of file diff --git a/app/views/shared/_profile_header.html.erb b/app/views/shared/_profile_header.html.erb index 8f3cfe510..16ee64ac7 100644 --- a/app/views/shared/_profile_header.html.erb +++ b/app/views/shared/_profile_header.html.erb @@ -10,7 +10,7 @@ Timeline
\ No newline at end of file diff --git a/app/views/shared/_search_navbar.html.erb b/app/views/shared/_search_navbar.html.erb index 291e040a9..214f2f483 100644 --- a/app/views/shared/_search_navbar.html.erb +++ b/app/views/shared/_search_navbar.html.erb @@ -14,7 +14,7 @@ <% if signed_in_user? %> diff --git a/app/views/users/_new_navbar.html.erb b/app/views/users/_new_navbar.html.erb index fbe81c744..3647ebd25 100644 --- a/app/views/users/_new_navbar.html.erb +++ b/app/views/users/_new_navbar.html.erb @@ -19,7 +19,7 @@ <%= link_to "Logout", logout_path, :method => :delete, html: {class: "danebook-text"} %> - <% else %> + <% else %> <%= form_tag session_path do %> <% end %> - <% end %> diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index 1784d8d0f..d4c77aeef 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,9 +1,9 @@ <%= render "new_navbar" %> -
-
+
- <%= submit_tag "Log in", class: "log-in-button" %> + <%= submit_tag "Log in", class: "btn btn-primary log-in-button" %>
<% end %> diff --git a/app/views/users/_new_post.html.erb b/app/views/users/_new_post.html.erb new file mode 100644 index 000000000..d754fbe5e --- /dev/null +++ b/app/views/users/_new_post.html.erb @@ -0,0 +1,14 @@ +<% if current_user == @user %> +
+
+
Post
+
+ <%= form_for [@user, @post] do |post| %> + <%= post.text_area :content, placeholder: "What's on your mind?", class: "new-post-text-area", cols: 62, rows: 7 %>
+ <%= post.submit "Post to the world!", class: "btn btn-primary center-block" %> + <% end %> +
+
+
+
+<% end %> \ No newline at end of file diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb index d4c77aeef..851ebd1ec 100644 --- a/app/views/users/new.html.erb +++ b/app/views/users/new.html.erb @@ -1,4 +1,3 @@ - <%= render "new_navbar" %>
@@ -17,23 +16,22 @@ -
+ -
+ -
+ -
+ - <%= f.submit :Create, class: "btn btn-success sign-up-field" %> +
+ <%= f.submit "Sign Up!", class: "btn btn-success sign-up-field" %> +
<% end %>
-
- - - +
\ No newline at end of file diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index e5fa3adf1..4f0252b80 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -1,2 +1,45 @@ -

Users#show

-

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

+<%= render "shared/search_navbar" %> +<%= render "shared/profile_header" %> + +
+
+
+
+

About

+

Name: <%= @profile.user.first_name.capitalize %> <%= @profile.user.last_name.capitalize %>

+

College: <%= @profile.college %>

+

Current Location: <%= @profile.current_location %>

+

Hometown: <%= @profile.hometown %>

+

+
+
+

Photos

+

+
+
+
+ <%= render "new_post" %> + + <% @posts.each do |post|%> +
+
+
+
+ <%= image_tag("default_image.jpg", class: "post-photo") %> +
+
+

Posted at:<%= post.created_at%> +
+

<%= post.content %><%= post.id%>

+
+ <%= link_to "Like", root_url, class: "btn btn-primary"%> + <%= link_to "Comment on Post", root_url, class: "btn btn-success"%> + <%= link_to "Delete Post", user_post_path(current_user, post), method: :delete, class: "btn btn-danger" if current_user == @user%> +

+
+
+
+ <% end %> +
+
+
\ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index f93b8da80..2ee6da975 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,8 +1,11 @@ Rails.application.routes.draw do + get 'comments/new' + resources :users do resource :profiles + resources :posts resources :photos end diff --git a/db/migrate/20160812180656_add_birthday_to_profile.rb b/db/migrate/20160812180656_add_birthday_to_profile.rb new file mode 100644 index 000000000..cf9391c24 --- /dev/null +++ b/db/migrate/20160812180656_add_birthday_to_profile.rb @@ -0,0 +1,5 @@ +class AddBirthdayToProfile < ActiveRecord::Migration[5.0] + def change + add_column :profiles, :birthday, :date + end +end diff --git a/db/migrate/20160812182041_create_posts.rb b/db/migrate/20160812182041_create_posts.rb new file mode 100644 index 000000000..c2daa0c3e --- /dev/null +++ b/db/migrate/20160812182041_create_posts.rb @@ -0,0 +1,10 @@ +class CreatePosts < ActiveRecord::Migration[5.0] + def change + create_table :posts do |t| + t.references :user, foreign_key: true + t.text :content + + t.timestamps + end + end +end diff --git a/db/migrate/20160812221758_create_comments.rb b/db/migrate/20160812221758_create_comments.rb new file mode 100644 index 000000000..b4c493fb1 --- /dev/null +++ b/db/migrate/20160812221758_create_comments.rb @@ -0,0 +1,11 @@ +class CreateComments < ActiveRecord::Migration[5.0] + def change + create_table :comments do |t| + t.references :user + t.string :type + t.text :content + + t.timestamps + end + end +end diff --git a/db/schema.rb b/db/schema.rb index b22d76dd3..bf8f8b75b 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,11 +10,20 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160812160556) do +ActiveRecord::Schema.define(version: 20160812221758) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" + create_table "comments", force: :cascade do |t| + t.integer "user_id" + t.string "type" + t.text "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_comments_on_user_id", using: :btree + end + create_table "photos", force: :cascade do |t| t.integer "profile_id" t.string "url" @@ -23,6 +32,14 @@ t.index ["profile_id"], name: "index_photos_on_profile_id", using: :btree end + create_table "posts", force: :cascade do |t| + t.integer "user_id" + t.text "content" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["user_id"], name: "index_posts_on_user_id", using: :btree + end + create_table "profiles", force: :cascade do |t| t.integer "user_id" t.string "month" @@ -36,6 +53,7 @@ t.datetime "updated_at", null: false t.text "about_me" t.text "words_to_live_by" + t.date "birthday" t.index ["user_id"], name: "index_profiles_on_user_id", using: :btree end @@ -51,5 +69,6 @@ end add_foreign_key "photos", "profiles" + add_foreign_key "posts", "users" add_foreign_key "profiles", "users" end diff --git a/test/controllers/comments_controller_test.rb b/test/controllers/comments_controller_test.rb new file mode 100644 index 000000000..98b9e53b5 --- /dev/null +++ b/test/controllers/comments_controller_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +class CommentsControllerTest < ActionDispatch::IntegrationTest + test "should get new" do + get comments_new_url + assert_response :success + 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/controllers/timelines_controller_test.rb b/test/controllers/timelines_controller_test.rb new file mode 100644 index 000000000..63c8065c6 --- /dev/null +++ b/test/controllers/timelines_controller_test.rb @@ -0,0 +1,7 @@ +require 'test_helper' + +class TimelinesControllerTest < 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..647d50416 --- /dev/null +++ b/test/fixtures/comments.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: + type: + content: MyText + +two: + user: + type: + content: MyText diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml new file mode 100644 index 000000000..bbfeec5d5 --- /dev/null +++ b/test/fixtures/posts.yml @@ -0,0 +1,9 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +one: + user: one + content: MyText + +two: + user: two + content: MyText 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/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 5f8845409caede961dcaa41d19188628d2304dc0 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Fri, 12 Aug 2016 18:53:42 -0400 Subject: [PATCH 06/31] Add uniqueness to email --- app/models/user.rb | 1 + db/migrate/20160812225137_add_unique_to_email.rb | 5 +++++ db/schema.rb | 3 ++- 3 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 db/migrate/20160812225137_add_unique_to_email.rb diff --git a/app/models/user.rb b/app/models/user.rb index 23f043360..02ae2af44 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -11,6 +11,7 @@ class User < ApplicationRecord :length => { :in => 5..20 }, :allow_nil => true validates :first_name, :last_name, :email, presence: true + validates :email, uniqueness: true accepts_nested_attributes_for :profile diff --git a/db/migrate/20160812225137_add_unique_to_email.rb b/db/migrate/20160812225137_add_unique_to_email.rb new file mode 100644 index 000000000..e1bbce28f --- /dev/null +++ b/db/migrate/20160812225137_add_unique_to_email.rb @@ -0,0 +1,5 @@ +class AddUniqueToEmail < ActiveRecord::Migration[5.0] + def change + add_index :users, :email, :unique => true + end +end diff --git a/db/schema.rb b/db/schema.rb index bf8f8b75b..09d28f84c 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: 20160812221758) do +ActiveRecord::Schema.define(version: 20160812225137) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -66,6 +66,7 @@ t.string "first_name" t.string "last_name" t.index ["auth_token"], name: "index_users_on_auth_token", unique: true, using: :btree + t.index ["email"], name: "index_users_on_email", unique: true, using: :btree end add_foreign_key "photos", "profiles" From 7b12cb6065111ec6569280c3742e7031591eb31e Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Fri, 12 Aug 2016 20:43:55 -0400 Subject: [PATCH 07/31] Fix looking at other people's profiles --- app/controllers/users_controller.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5ccc1d445..324156ef6 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -40,7 +40,7 @@ def destroy end def show - @profile = current_user.profile + @profile = @user.profile @post = current_user.posts.build if current_user @posts = @user.all_posts end From 4a88636de6237f60b0a0b4f919a64ae29673472f Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Sat, 13 Aug 2016 12:31:37 -0400 Subject: [PATCH 08/31] Fix search query for first name and last name --- app/controllers/profiles_controller.rb | 1 - app/controllers/users_controller.rb | 1 + app/models/user.rb | 7 +++++++ app/views/shared/_search_navbar.html.erb | 6 +++--- app/views/users/index.html.erb | 6 ++++++ 5 files changed, 17 insertions(+), 4 deletions(-) diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 68701655c..00a5e781d 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -19,6 +19,5 @@ def destroy end def index - end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 324156ef6..d9fb70ba7 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -8,6 +8,7 @@ def new end def index + @users = User.search(params[:search]) end def edit diff --git a/app/models/user.rb b/app/models/user.rb index 02ae2af44..2bd34c312 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -31,4 +31,11 @@ def all_posts self.posts.all.order('created_at DESC') end + def self.search(name) + @users = [User.where('first_name ILIKE ?', "#{name}%")] + @users << User.where('last_name ILIKE ?', "#{name}%") + @users.flatten.uniq + + end + end diff --git a/app/views/shared/_search_navbar.html.erb b/app/views/shared/_search_navbar.html.erb index 214f2f483..93f21716d 100644 --- a/app/views/shared/_search_navbar.html.erb +++ b/app/views/shared/_search_navbar.html.erb @@ -7,9 +7,9 @@

D

<% end %>
- <%= form_tag("/search", method: "get") do %> - <%= text_field_tag(:q, nil, placeholder: "Search for User", class: "search-input") %> - <%= submit_tag("Search", class: "btn btn-primary") %> + <%= form_tag("/users", method: "get") do %> + <%= text_field_tag(:search, params[:search], placeholder: "Search for User", class: "search-input") %> + <%= submit_tag("Search", name: nil, class: "btn btn-primary") %> <% end %> <% if signed_in_user? %> -<% end %> \ No newline at end of file diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 4f0252b80..616bc798a 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -18,7 +18,7 @@
- <%= render "new_post" %> + <%= render "posts/new_post" %> <% @posts.each do |post|%>
@@ -28,7 +28,8 @@ <%= image_tag("default_image.jpg", class: "post-photo") %>
-

Posted at:<%= post.created_at%> +

<%=link_to from_user(post), user_path(post.user)%>
+
Posted:<%= post.created_at.strftime(' %b %d at %r')%>

<%= post.content %><%= post.id%>

@@ -42,4 +43,5 @@ <% end %>
-
\ No newline at end of file + + diff --git a/db/migrate/20160814021709_add_to_id_to_posts.rb b/db/migrate/20160814021709_add_to_id_to_posts.rb new file mode 100644 index 000000000..94466231b --- /dev/null +++ b/db/migrate/20160814021709_add_to_id_to_posts.rb @@ -0,0 +1,5 @@ +class AddToIdToPosts < ActiveRecord::Migration[5.0] + def change + add_column :posts, :from, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index 09d28f84c..33a222995 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: 20160812225137) do +ActiveRecord::Schema.define(version: 20160814021709) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -37,6 +37,7 @@ t.text "content" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.integer "from" t.index ["user_id"], name: "index_posts_on_user_id", using: :btree end From a836fbe1359c6a27acb8a3455ca69e43b0091a15 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Sun, 14 Aug 2016 23:53:14 -0400 Subject: [PATCH 11/31] Implented a very weak like and comment --- app/assets/javascripts/likes.coffee | 3 ++ app/assets/stylesheets/application.scss | 13 +++++++ app/assets/stylesheets/likes.scss | 3 ++ app/controllers/comments_controller.rb | 39 +++++++++++++++++++ app/controllers/likes_controller.rb | 22 +++++++++++ app/controllers/posts_controller.rb | 2 +- app/controllers/users_controller.rb | 2 + app/helpers/likes_helper.rb | 2 + app/models/comment.rb | 6 +++ app/models/like.rb | 8 ++++ app/models/post.rb | 9 ++++- app/models/user.rb | 1 + app/views/comments/_new_comment.html.erb | 10 +++++ app/views/posts/_new_post.html.erb | 19 +++++---- app/views/users/show.html.erb | 33 ++++++++++++++-- config/routes.rb | 18 ++++++++- .../20160814151629_add_commentable_type_id.rb | 7 ++++ ...60815020752_remove_user_id_from_comment.rb | 5 +++ db/migrate/20160815030937_create_likes.rb | 11 ++++++ db/schema.rb | 21 +++++++--- test/controllers/likes_controller_test.rb | 7 ++++ test/fixtures/likes.yml | 11 ++++++ test/models/like_test.rb | 7 ++++ 23 files changed, 237 insertions(+), 22 deletions(-) create mode 100644 app/assets/javascripts/likes.coffee create mode 100644 app/assets/stylesheets/likes.scss create mode 100644 app/controllers/likes_controller.rb create mode 100644 app/helpers/likes_helper.rb create mode 100644 app/models/like.rb create mode 100644 app/views/comments/_new_comment.html.erb create mode 100644 db/migrate/20160814151629_add_commentable_type_id.rb create mode 100644 db/migrate/20160815020752_remove_user_id_from_comment.rb create mode 100644 db/migrate/20160815030937_create_likes.rb create mode 100644 test/controllers/likes_controller_test.rb create mode 100644 test/fixtures/likes.yml create mode 100644 test/models/like_test.rb 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/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 33e1f9db7..472ff88c9 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -233,4 +233,17 @@ body { border: 1px solid black; margin-bottom:10px; margin-top:20px; +} + +.comment-picture { + height:50px; + width:50px; +} + +.comment-text { + display:inline-block; +} + +.input-group-addon { + display:inline-block; } \ No newline at end of file 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/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index 2569ab0d5..cb1a007dd 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -1,4 +1,43 @@ class CommentsController < ApplicationController def new + @comment = Comment.new end + + def create + # parent = params[:commentable] + # parent_id = (parent.downcase << "_id").to_sym + # @commentable = parent.classify.constantize.find(params[parent_id]) + # @comment = @commentable.comments.new(comment_params) + @post = Post.find(params[:post_id]) + @comment = @post.comments.build(comment_params) + if @comment.save! + flash[:success] = "Your comment has been saved!" + redirect_to current_user + else + flash[:danger] = "Your comment was not posted" + redirect_to current_user + end + end + + + def index + end + + def show + end + + + private + + def comment_params + params.require(:comment).permit(:content) + end + + # def underscore + # self.gsub(/::/, '/'). + # gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2'). + # gsub(/([a-z\d])([A-Z])/,'\1_\2'). + # tr("-", "_"). + # downcase + # end end diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb new file mode 100644 index 000000000..aa87a2356 --- /dev/null +++ b/app/controllers/likes_controller.rb @@ -0,0 +1,22 @@ +class LikesController < ApplicationController + def create + @post = Post.find(params[:post_id]) + @like = @post.likes.build(likes_params) + + if !Like.already_liked?(@post, current_user) && @like.save! + flash[:success] = "You have liked that comment" + redirect_to current_user + else + flash[:danger] = "You already liked the comment" + redirect_to current_user + end + end + + def destroy + end + + + def likes_params + params.require(:like).permit(:user_id) + end +end diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index f01314ec6..f82ae587c 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -25,7 +25,7 @@ def destroy private def post_params - params.require(:post).permit(:content, :from) + params.require(:post).permit(:content, :from, comments_attributes: [:content], :commentable, :post_id) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index e789f5590..9b22a9b30 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -43,6 +43,8 @@ def destroy def show @profile = @user.profile @post = current_user.posts.build if current_user + @comment = @post.comments.build if current_user + @like = @post.likes.build if current_user @posts = @user.all_posts 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/models/comment.rb b/app/models/comment.rb index 1d79d25c7..f45629e84 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,2 +1,8 @@ class Comment < ApplicationRecord + #belongs_to :user + belongs_to :commentable, polymorphic: true + has_many :likes, as: :likeable + has_many :comments, as: :commentable, dependent: :nullify + + accepts_nested_attributes_for :comments end diff --git a/app/models/like.rb b/app/models/like.rb new file mode 100644 index 000000000..419820394 --- /dev/null +++ b/app/models/like.rb @@ -0,0 +1,8 @@ +class Like < ApplicationRecord + belongs_to :likeable, polymorphic: true + + def self.already_liked?(post, user) + post.likes.pluck(:user_id).include?(user.id) + end + +end diff --git a/app/models/post.rb b/app/models/post.rb index f8db688fa..5de5a2dc8 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -1,6 +1,13 @@ class Post < ApplicationRecord belongs_to :user - + has_many :comments, as: :commentable + has_many :likes, as: :likeable validates :user_id, presence: true validates :content, presence: true, length: { minimum: 1 } + + accepts_nested_attributes_for :comments + + def all_comments + self.comments.order("created_at ASC") + end end diff --git a/app/models/user.rb b/app/models/user.rb index 410f53933..bd793e8be 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -2,6 +2,7 @@ class User < ApplicationRecord has_many :posts has_one :profile, dependent: :nullify has_many :photos + has_many :likes before_create :generate_token after_create :create_profile diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb new file mode 100644 index 000000000..de2bd1fed --- /dev/null +++ b/app/views/comments/_new_comment.html.erb @@ -0,0 +1,10 @@ +<%= form_for [comment.commentable, Comment.new] do |comment| %> +
+ <%= image_tag("default_image.jpg", class: "comment-picture") %> +
+
+

Media heading

+ <%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= comment.submit "Comment on Post", class: "btn btn-success" %> +
+<% end %> \ No newline at end of file diff --git a/app/views/posts/_new_post.html.erb b/app/views/posts/_new_post.html.erb index 5984d70bf..74da09260 100644 --- a/app/views/posts/_new_post.html.erb +++ b/app/views/posts/_new_post.html.erb @@ -1,13 +1,12 @@ - -
-
-
Post
-
- <%= form_for [@user, @post] do |post| %> - <%= post.text_area :content, placeholder: "What's on your mind?", class: "new-post-text-area", cols: 62, rows: 7 %>
- <%= post.submit "Post to the world!", class: "btn btn-primary center-block" %> - <% end %> -
+
+
+
Post
+
+ <%= form_for [@user, @post] do |post| %> + <%= post.text_area :content, placeholder: "What's on your mind?", class: "new-post-text-area", cols: 62, rows: 7 %>
+ <%= post.submit "Post to the world!", class: "btn btn-primary center-block" %> + <% end %>
+
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 616bc798a..786c1d61b 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -31,10 +31,37 @@
<%=link_to from_user(post), user_path(post.user)%>
Posted:<%= post.created_at.strftime(' %b %d at %r')%>
-

<%= post.content %><%= post.id%>

+

<%= post.content %>

+

<%= post.likes.count %> likes

+ <% if !post.comments.empty? %> + <% post.all_comments.each do |comment| %> +
    +
  • +
    + <%= image_tag("default_image.jpg", class: "comment-picture") %> +
    +
    +

    <%= link_to comment.commentable.user.first_name, user_profiles_path(comment.commentable.user) %>

    +

    <%= comment.content %>

    + + <%= render "comments/new_comment", comment: comment %> +
    +
  • +
+ <% end %> + <% else %> + <% end %>
- <%= link_to "Like", root_url, class: "btn btn-primary"%> - <%= link_to "Comment on Post", root_url, class: "btn btn-success"%> + <%= form_for [post, @post.comments.build] do |comment| %> + <%= image_tag("default_image.jpg", class: "comment-picture") %> + <%= comment.text_field :content, placeholder: "Write your coffmment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= comment.submit "Comment on Post", class: "btn btn-success" %> + <% end %> + <%= form_for [post, @post.likes.build] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary"%> + <% end %> + <%= link_to "Delete Post", user_post_path(current_user, post), method: :delete, class: "btn btn-danger" if current_user == @user%>
diff --git a/config/routes.rb b/config/routes.rb index 2ee6da975..9b636b670 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -4,11 +4,25 @@ get 'comments/new' resources :users do - resource :profiles - resources :posts + resource :profiles, only: [:edit, :show, :update] + resources :posts, only: [:create, :destroy] resources :photos + end + + resources :posts do + resources :comments, defaults: {commentable: "Post"} + resources :likes, defaults: {likeable: "Post"} + end + + resource :comments do + resources :comments, defaults: {commentable: "Comment"} + resources :likes, defaults: {likeable: "Comment"} end + + + + root "users#new" resource :session, :only => [:new, :create, :destroy] diff --git a/db/migrate/20160814151629_add_commentable_type_id.rb b/db/migrate/20160814151629_add_commentable_type_id.rb new file mode 100644 index 000000000..ff1b2b2b8 --- /dev/null +++ b/db/migrate/20160814151629_add_commentable_type_id.rb @@ -0,0 +1,7 @@ +class AddCommentableTypeId < ActiveRecord::Migration[5.0] + def change + add_column :comments, :commentable_type, :string + add_column :comments, :commentable_id, :integer + add_index :comments, [:commentable_id, :commentable_type] + end +end diff --git a/db/migrate/20160815020752_remove_user_id_from_comment.rb b/db/migrate/20160815020752_remove_user_id_from_comment.rb new file mode 100644 index 000000000..0db3513ff --- /dev/null +++ b/db/migrate/20160815020752_remove_user_id_from_comment.rb @@ -0,0 +1,5 @@ +class RemoveUserIdFromComment < ActiveRecord::Migration[5.0] + def change + remove_column :comments, :user_id + end +end diff --git a/db/migrate/20160815030937_create_likes.rb b/db/migrate/20160815030937_create_likes.rb new file mode 100644 index 000000000..fb550bdb0 --- /dev/null +++ b/db/migrate/20160815030937_create_likes.rb @@ -0,0 +1,11 @@ +class CreateLikes < ActiveRecord::Migration[5.0] + def change + create_table :likes do |t| + t.string :likeable_type + t.integer :likeable_id + t.references :user + t.timestamps + end + add_index :likes, [:likeable_id, :likeable_type] + end +end diff --git a/db/schema.rb b/db/schema.rb index 33a222995..cc6753a1f 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,18 +10,29 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20160814021709) do +ActiveRecord::Schema.define(version: 20160815030937) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" create_table "comments", force: :cascade do |t| - t.integer "user_id" t.string "type" t.text "content" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false - t.index ["user_id"], name: "index_comments_on_user_id", using: :btree + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "commentable_type" + t.integer "commentable_id" + t.index ["commentable_id", "commentable_type"], name: "index_comments_on_commentable_id_and_commentable_type", using: :btree + end + + create_table "likes", force: :cascade do |t| + t.string "likeable_type" + t.integer "likeable_id" + t.integer "user_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.index ["likeable_id", "likeable_type"], name: "index_likes_on_likeable_id_and_likeable_type", using: :btree + t.index ["user_id"], name: "index_likes_on_user_id", using: :btree end create_table "photos", force: :cascade do |t| 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/fixtures/likes.yml b/test/fixtures/likes.yml new file mode 100644 index 000000000..80aed36e3 --- /dev/null +++ b/test/fixtures/likes.yml @@ -0,0 +1,11 @@ +# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html + +# This model initially had no columns defined. If you add columns to the +# model remove the '{}' from the fixture names and add the columns immediately +# below each fixture, per the syntax in the comments below +# +one: {} +# column: value +# +two: {} +# column: value diff --git a/test/models/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 From 7711c4ffff94f151acbe114e563e0fd75104ef0d Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Mon, 15 Aug 2016 19:38:17 -0400 Subject: [PATCH 12/31] Add tests for User model and Post model --- .rspec | 2 + Gemfile | 5 + Gemfile.lock | 28 ++++++ app/views/comments/_new_comment.html.erb | 4 + config/routes.rb | 2 +- spec/factories/posts.rb | 7 ++ "spec/factories/users.rb\nusers.rb\nusers.rb" | 9 ++ spec/models/post_spec.rb | 38 +++++++ spec/models/user_spec.rb | 85 ++++++++++++++++ spec/rails_helper.rb | 69 +++++++++++++ spec/spec_helper.rb | 99 +++++++++++++++++++ test/controllers/.keep | 0 test/controllers/comments_controller_test.rb | 9 -- test/controllers/likes_controller_test.rb | 7 -- test/controllers/posts_controller_test.rb | 7 -- test/controllers/profile_controller_test.rb | 24 ----- test/controllers/sessions_controller_test.rb | 7 -- test/controllers/timelines_controller_test.rb | 7 -- test/controllers/users_controller_test.rb | 39 -------- test/fixtures/.keep | 0 test/fixtures/comments.yml | 11 --- test/fixtures/files/.keep | 0 test/fixtures/likes.yml | 11 --- test/fixtures/photos.yml | 7 -- test/fixtures/posts.yml | 9 -- test/fixtures/profiles.yml | 7 -- test/fixtures/users.yml | 11 --- 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/photo_test.rb | 7 -- test/models/post_test.rb | 7 -- test/models/profile_test.rb | 7 -- test/models/user_test.rb | 7 -- test/test_helper.rb | 10 -- 38 files changed, 347 insertions(+), 209 deletions(-) create mode 100644 .rspec create mode 100644 spec/factories/posts.rb create mode 100644 "spec/factories/users.rb\nusers.rb\nusers.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 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/profile_controller_test.rb delete mode 100644 test/controllers/sessions_controller_test.rb delete mode 100644 test/controllers/timelines_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/photos.yml delete mode 100644 test/fixtures/posts.yml delete mode 100644 test/fixtures/profiles.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/photo_test.rb delete mode 100644 test/models/post_test.rb delete mode 100644 test/models/profile_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..83e16f804 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/Gemfile b/Gemfile index 279e2bbc4..201fa835d 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,9 @@ source 'https://rubygems.org' +gem 'factory_girl_rails' + +gem 'rspec-rails' + gem 'will_paginate' gem 'carrierwave' @@ -50,6 +54,7 @@ gem 'jbuilder', '~> 2.5' group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri + gem 'shoulda-matchers' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 95ff32233..7b508f5d5 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -72,8 +72,14 @@ 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.7.0) + activesupport (>= 3.0.0) + factory_girl_rails (4.7.0) + factory_girl (~> 4.7.0) + railties (>= 3.0.0) faker (1.6.6) i18n (~> 0.5) ffi (1.9.14) @@ -141,6 +147,23 @@ GEM rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) + rspec-core (3.5.2) + 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.5.0) + rspec-rails (3.5.1) + actionpack (>= 3.0) + activesupport (>= 3.0) + railties (>= 3.0) + 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) @@ -148,6 +171,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + shoulda-matchers (3.1.1) + activesupport (>= 4.0.0) spring (1.7.2) spring-watcher-listen (2.0.0) listen (>= 2.7, < 4.0) @@ -188,6 +213,7 @@ DEPENDENCIES byebug carrierwave coffee-rails (~> 4.2) + factory_girl_rails faker hirb jbuilder (~> 2.5) @@ -196,7 +222,9 @@ DEPENDENCIES pg puma (~> 3.0) rails (~> 5.0.0) + rspec-rails sass-rails (~> 5.0) + shoulda-matchers spring spring-watcher-listen (~> 2.0.0) tzinfo-data diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb index de2bd1fed..d52de2861 100644 --- a/app/views/comments/_new_comment.html.erb +++ b/app/views/comments/_new_comment.html.erb @@ -6,5 +6,9 @@

Media heading

<%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> <%= comment.submit "Comment on Post", class: "btn btn-success" %> + <%= form_for [comment, comment.likes.build] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary"%> + <% end %> <% end %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 9b636b670..744e1802c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -14,7 +14,7 @@ resources :likes, defaults: {likeable: "Post"} end - resource :comments do + resources :comments do resources :comments, defaults: {commentable: "Comment"} resources :likes, defaults: {likeable: "Comment"} end diff --git a/spec/factories/posts.rb b/spec/factories/posts.rb new file mode 100644 index 000000000..48c7beb45 --- /dev/null +++ b/spec/factories/posts.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :post do + content "BLAH!!" + user_id 2 + from 1 + end +end \ No newline at end of file diff --git "a/spec/factories/users.rb\nusers.rb\nusers.rb" "b/spec/factories/users.rb\nusers.rb\nusers.rb" new file mode 100644 index 000000000..166207f51 --- /dev/null +++ "b/spec/factories/users.rb\nusers.rb\nusers.rb" @@ -0,0 +1,9 @@ +FactoryGirl.define do + factory :user do + first_name "Foo" + last_name "Bar" + email "foo@bar.com" + password "password" + password_confirmation "password" + end +end \ No newline at end of file diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb new file mode 100644 index 000000000..e88f429e1 --- /dev/null +++ b/spec/models/post_spec.rb @@ -0,0 +1,38 @@ +require 'rails_helper' + +describe Post do + + describe "Validations" do + + let(:post) { build(:post) } + + it "validates the presence of content" do + is_expected.to validate_presence_of(:content) + end + + it "validates the length of the post" do + is_expected.to validate_length_of(:content).is_at_least(1) + end + + end + + describe "Associations" do + it "accepts nested attributes for a comment" do + is_expected.to accept_nested_attributes_for(:comments) + end + + it "only has one user" do + is_expected.to belong_to(:user) + end + + it "has many comments" do + is_expected.to have_many(:comments) + end + + it "has many likes" do + is_expected.to have_many(:likes) + end + + end + +end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb new file mode 100644 index 000000000..642bf09cb --- /dev/null +++ b/spec/models/user_spec.rb @@ -0,0 +1,85 @@ +require 'rails_helper' + +describe User do + + describe "Validations" do + + let(:user) { build(:user) } + + it "validates the presence of first name" do + is_expected.to validate_presence_of(:first_name) + end + + it "validates the presence of a last name" do + is_expected.to validate_presence_of(:last_name) + end + + it "validates the presence of an email" do + is_expected.to validate_presence_of(:email) + end + + it "validates that the entered email is not taken already" do + is_expected.to validate_uniqueness_of(:email) + end + + it "should have a secure password" do + is_expected.to have_secure_password + end + + it "should validate a minimum and maximum length for password" do + should validate_length_of(:password).is_at_least(5).is_at_most(20) + end + + end + + describe "Associations" do + + it "should accept nested attribute for a profile object" do + is_expected.to accept_nested_attributes_for(:profile) + end + + it "should have many posts" do + is_expected.to have_many(:posts) + end + + it "should have one profile" do + is_expected.to have_one(:profile) + end + + end + + describe "#all_posts" do + let(:user1) {build(:user)} + it "should list all posts in ascending order of a user" do + 5.times { |n| user1.posts << build(:post, created_at: Time.now, id: n) } + expect(user1.posts.count).to eq(5) + end + end + + describe "#search" do + it "should return an array of all matching user objects with a given search input" do + bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} + search_results = User.search("Bob") + bobs.each do |bob| + expect(search_results).to include(bob) + end + end + + it "should return an empty array of if nothing matches" do + bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} + search_results = User.search("Joe") + bobs.each do |bob| + expect(search_results).to_not include(bob) + end + end + + it "should return unique users" do + bobs = Array.new(1) {create(:user, first_name:"Bob") } + bobs = bobs * 2 + search_results = User.search("Bob") + expect(search_results.count).to eq(1) + end + + end + +end \ No newline at end of file diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb new file mode 100644 index 000000000..98e21ae08 --- /dev/null +++ b/spec/rails_helper.rb @@ -0,0 +1,69 @@ +# This file is copied to spec/ when you run 'rails generate rspec:install' +ENV['RAILS_ENV'] ||= 'test' +require File.expand_path('../../config/environment', __FILE__) +# Prevent database truncation if the environment is production +abort("The Rails environment is running in production mode!") if Rails.env.production? +require 'spec_helper' +require 'rspec/rails' +require 'factory_girl_rails' +# 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 migration and applies them before tests are run. +# If you are not using ActiveRecord, you can remove this line. +ActiveRecord::Migration.maintain_test_schema! + + +RSpec.configure do |config| + # 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 + + config.include FactoryGirl::Syntax::Methods + + # 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! + + # Filter lines from Rails gems in backtraces. + config.filter_rails_from_backtrace! + # arbitrary gems may also be filtered via: + # config.filter_gems_from_backtrace("gem name") +end + +Shoulda::Matchers.configure do |config| + config.integrate do |with| + # Choose a test framework: + with.test_framework :rspec + with.library :rails + end +end \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 000000000..8f698be46 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,99 @@ +# 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 + + # This option will default to `:apply_to_host_groups` in RSpec 4 (and will + # have no way to turn it off -- the option exists only for backwards + # compatibility in RSpec 3). It causes shared context metadata to be + # inherited by the metadata hash of host groups and examples, rather than + # triggering implicit auto-inclusion in groups with matching metadata. + config.shared_context_metadata_behavior = :apply_to_host_groups + +# The settings below are suggested to provide a good initial experience +# with RSpec, but feel free to customize to your heart's content. +=begin + # This allows 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. RSpec also provides + # aliases for `it`, `describe`, and `context` that include `:focus` + # metadata: `fit`, `fdescribe` and `fcontext`, respectively. + config.filter_run_when_matching :focus + + # Allows RSpec to persist some state between runs in order to support + # the `--only-failures` and `--next-failure` CLI options. We recommend + # you configure your source control system to ignore this file. + config.example_status_persistence_file_path = "spec/examples.txt" + + # Limits the available syntax to the non-monkey patched syntax that is + # recommended. For more details, see: + # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ + # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ + # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode + 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/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 98b9e53b5..000000000 --- a/test/controllers/comments_controller_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -class CommentsControllerTest < ActionDispatch::IntegrationTest - test "should get new" do - get comments_new_url - assert_response :success - 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/profile_controller_test.rb b/test/controllers/profile_controller_test.rb deleted file mode 100644 index 19f57e97b..000000000 --- a/test/controllers/profile_controller_test.rb +++ /dev/null @@ -1,24 +0,0 @@ -require 'test_helper' - -class ProfileControllerTest < ActionDispatch::IntegrationTest - test "should get edit" do - get profile_edit_url - assert_response :success - end - - test "should get update" do - get profile_update_url - assert_response :success - end - - test "should get show" do - get profile_show_url - assert_response :success - end - - test "should get destroy" do - get profile_destroy_url - assert_response :success - 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/timelines_controller_test.rb b/test/controllers/timelines_controller_test.rb deleted file mode 100644 index 63c8065c6..000000000 --- a/test/controllers/timelines_controller_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class TimelinesControllerTest < 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 83fab60fb..000000000 --- a/test/controllers/users_controller_test.rb +++ /dev/null @@ -1,39 +0,0 @@ -require 'test_helper' - -class UsersControllerTest < ActionDispatch::IntegrationTest - test "should get new" do - get users_new_url - assert_response :success - end - - test "should get index" do - get users_index_url - assert_response :success - end - - test "should get edit" do - get users_edit_url - assert_response :success - end - - test "should get update" do - get users_update_url - assert_response :success - end - - test "should get create" do - get users_create_url - assert_response :success - end - - test "should get destroy" do - get users_destroy_url - assert_response :success - end - - test "should get show" do - get users_show_url - assert_response :success - 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 647d50416..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: - user: - type: - content: MyText - -two: - user: - type: - content: MyText 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 80aed36e3..000000000 --- a/test/fixtures/likes.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/fixtures/photos.yml b/test/fixtures/photos.yml deleted file mode 100644 index b513c6b37..000000000 --- a/test/fixtures/photos.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - profile: one - -two: - profile: two diff --git a/test/fixtures/posts.yml b/test/fixtures/posts.yml deleted file mode 100644 index bbfeec5d5..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: - user: one - content: MyText - -two: - user: two - content: MyText diff --git a/test/fixtures/profiles.yml b/test/fixtures/profiles.yml deleted file mode 100644 index 526860bce..000000000 --- a/test/fixtures/profiles.yml +++ /dev/null @@ -1,7 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -one: - user: one - -two: - user: two diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml deleted file mode 100644 index 80aed36e3..000000000 --- a/test/fixtures/users.yml +++ /dev/null @@ -1,11 +0,0 @@ -# Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html - -# This model initially had no columns defined. If you add columns to the -# model remove the '{}' from the fixture names and add the columns immediately -# below each fixture, per the syntax in the comments below -# -one: {} -# column: value -# -two: {} -# column: value diff --git a/test/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/photo_test.rb b/test/models/photo_test.rb deleted file mode 100644 index e2ec03a1b..000000000 --- a/test/models/photo_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class PhotoTest < 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/profile_test.rb b/test/models/profile_test.rb deleted file mode 100644 index 3dfa94302..000000000 --- a/test/models/profile_test.rb +++ /dev/null @@ -1,7 +0,0 @@ -require 'test_helper' - -class ProfileTest < 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 401f3f6b78bc124a798da1474f01ab0a45269637 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Mon, 15 Aug 2016 20:01:14 -0400 Subject: [PATCH 13/31] Add all_comments and all_posts for User and Post model --- app/models/post.rb | 2 +- spec/models/post_spec.rb | 24 ++++++++++++++++++++++++ spec/models/user_spec.rb | 11 ++++++++--- 3 files changed, 33 insertions(+), 4 deletions(-) diff --git a/app/models/post.rb b/app/models/post.rb index 5de5a2dc8..b5e252b30 100644 --- a/app/models/post.rb +++ b/app/models/post.rb @@ -8,6 +8,6 @@ class Post < ApplicationRecord accepts_nested_attributes_for :comments def all_comments - self.comments.order("created_at ASC") + self.comments.order("created_at DESC") end end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index e88f429e1..238c79ca9 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -14,6 +14,10 @@ is_expected.to validate_length_of(:content).is_at_least(1) end + it "has a database column for user ids" do + is_expected.to have_db_column(:user_id) + end + end describe "Associations" do @@ -32,7 +36,27 @@ it "has many likes" do is_expected.to have_many(:likes) end + end + + describe "#all_comments" do + let(:post) {build(:post)} + + it "should list all comments in ascending order of a comment" do + 5.times do |n| + post.comments.build(created_at: Time.now, id: n) + end + post.comments.each_with_index do |comment, index| + expect(comment.created_at).to be < post.comments[index + 1].created_at if post.comments[index + 1] + end + end + it "returns a list of all comments by the person" do + 10.times do |n| + post.comments.build(content: n) + end + all_comments = post.comments.length + expect(all_comments).to eq(10) + end end end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 642bf09cb..186245fee 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -49,10 +49,15 @@ end describe "#all_posts" do - let(:user1) {build(:user)} + let(:user) {build(:user)} + it "should list all posts in ascending order of a user" do - 5.times { |n| user1.posts << build(:post, created_at: Time.now, id: n) } - expect(user1.posts.count).to eq(5) + 5.times do |n| + user.posts.build(created_at: Time.now, id: n) + end + user.posts.each_with_index do |post, index| + expect(post.created_at).to be < user.posts[index + 1].created_at if user.posts[index + 1] + end end end From e09b1ad04f73697be8a7fd23d62fc07a1eb294d7 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Tue, 16 Aug 2016 19:58:27 -0400 Subject: [PATCH 14/31] Add like, delete, create_post, sign_in features. Stuck on edit_profile feature --- Gemfile | 4 ++++ Gemfile.lock | 24 +++++++++++++++++++++++ app/controllers/posts_controller.rb | 2 +- app/controllers/users_controller.rb | 3 ++- app/views/comments/_new_comment.html.erb | 7 ++++--- app/views/users/show.html.erb | 4 ++-- spec/factories/posts.rb | 2 +- spec/features/creating_a_new_post_spec.rb | 20 +++++++++++++++++++ spec/features/delete_a_post_spec.rb | 24 +++++++++++++++++++++++ spec/features/edit_profile_spec.rb | 24 +++++++++++++++++++++++ spec/features/like_a_post_spec.rb | 11 +++++++++++ spec/features/sign_in_spec.rb | 11 +++++++++++ spec/features/visit_homepage_spec.rb | 22 +++++++++++++++++++++ spec/features/visiting_timelines_spec.rb | 20 +++++++++++++++++++ spec/models/user_spec.rb | 12 +++++++----- spec/rails_helper.rb | 5 ++++- spec/support/login_macros.rb | 8 ++++++++ spec/support/signin_macros.rb | 0 18 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 spec/features/creating_a_new_post_spec.rb create mode 100644 spec/features/delete_a_post_spec.rb create mode 100644 spec/features/edit_profile_spec.rb create mode 100644 spec/features/like_a_post_spec.rb create mode 100644 spec/features/sign_in_spec.rb create mode 100644 spec/features/visit_homepage_spec.rb create mode 100644 spec/features/visiting_timelines_spec.rb create mode 100644 spec/support/login_macros.rb create mode 100644 spec/support/signin_macros.rb diff --git a/Gemfile b/Gemfile index 201fa835d..51dbce835 100644 --- a/Gemfile +++ b/Gemfile @@ -22,6 +22,8 @@ gem 'faker' gem 'pg' +gem 'selenium-webdriver' + gem 'bcrypt' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '~> 5.0.0' @@ -55,6 +57,8 @@ group :development, :test do # Call 'byebug' anywhere in the code to stop execution and get a debugger console gem 'byebug', platform: :mri gem 'shoulda-matchers' + gem 'capybara' + gem 'launchy' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 7b508f5d5..db1ac1f07 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) autoprefixer-rails (6.4.0.1) execjs @@ -56,12 +57,21 @@ GEM sass (>= 3.3.4) builder (3.2.2) byebug (9.0.5) + capybara (2.7.1) + addressable + mime-types (>= 1.16) + nokogiri (>= 1.3.3) + rack (>= 1.0.0) + rack-test (>= 0.5.4) + xpath (~> 2.0) carrierwave (0.11.2) activemodel (>= 3.2.0) activesupport (>= 3.2.0) json (>= 1.7) mime-types (>= 1.16) mimemagic (>= 0.3.0) + childprocess (0.5.9) + ffi (~> 1.0, >= 1.0.11) coderay (1.1.1) coffee-rails (4.2.1) coffee-script (>= 2.2.0) @@ -95,6 +105,8 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (2.0.2) + 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) @@ -164,6 +176,7 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) + rubyzip (1.1.7) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -171,6 +184,11 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) + selenium-webdriver (2.51.0) + childprocess (~> 0.5) + multi_json (~> 1.0) + rubyzip (~> 1.0) + websocket (~> 1.0) shoulda-matchers (3.1.1) activesupport (>= 4.0.0) spring (1.7.2) @@ -196,10 +214,13 @@ GEM activemodel (>= 5.0) debug_inspector railties (>= 5.0) + websocket (1.2.2) websocket-driver (0.6.4) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) will_paginate (3.1.0) + xpath (2.0.0) + nokogiri (~> 1.3) PLATFORMS ruby @@ -211,6 +232,7 @@ DEPENDENCIES bootstrap bootstrap-sass byebug + capybara carrierwave coffee-rails (~> 4.2) factory_girl_rails @@ -218,12 +240,14 @@ DEPENDENCIES hirb jbuilder (~> 2.5) jquery-rails + launchy listen (~> 3.0.5) pg puma (~> 3.0) rails (~> 5.0.0) rspec-rails sass-rails (~> 5.0) + selenium-webdriver shoulda-matchers spring spring-watcher-listen (~> 2.0.0) diff --git a/app/controllers/posts_controller.rb b/app/controllers/posts_controller.rb index f82ae587c..41540bc2d 100644 --- a/app/controllers/posts_controller.rb +++ b/app/controllers/posts_controller.rb @@ -25,7 +25,7 @@ def destroy private def post_params - params.require(:post).permit(:content, :from, comments_attributes: [:content], :commentable, :post_id) + params.require(:post).permit(:content, :from, comments_attributes: [:content]) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 9b22a9b30..ded6428b8 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -20,7 +20,8 @@ def update flash[:success] = "Your Danebook profile has been updated" redirect_to user_profiles_path(current_user) else - flash.now[:danger] = "Failed to update your Danebook profile" + flash[:danger] = "Failed to update your Danebook profile" + + current_user.errors.full_messages.join(", ") redirect_to user_profiles_path(current_user) end end diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb index d52de2861..ace354410 100644 --- a/app/views/comments/_new_comment.html.erb +++ b/app/views/comments/_new_comment.html.erb @@ -6,9 +6,10 @@

Media heading

<%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> <%= comment.submit "Comment on Post", class: "btn btn-success" %> - <%= form_for [comment, comment.likes.build] do |like| %> - <%= like.hidden_field :user_id, value: current_user.id %> + <%= comment.fields_for :like do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %>> <%= like.submit "Like", class: "btn btn-primary"%> <% end %> + -<% end %> \ No newline at end of file +<% end %> diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb index 786c1d61b..c6189cf0c 100644 --- a/app/views/users/show.html.erb +++ b/app/views/users/show.html.erb @@ -44,7 +44,7 @@

<%= link_to comment.commentable.user.first_name, user_profiles_path(comment.commentable.user) %>

<%= comment.content %>

- <%= render "comments/new_comment", comment: comment %> + <%= render "comments/new_comment", comment: @comment, like: @like %> @@ -54,7 +54,7 @@ <%= form_for [post, @post.comments.build] do |comment| %> <%= image_tag("default_image.jpg", class: "comment-picture") %> - <%= comment.text_field :content, placeholder: "Write your coffmment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> <%= comment.submit "Comment on Post", class: "btn btn-success" %> <% end %> <%= form_for [post, @post.likes.build] do |like| %> diff --git a/spec/factories/posts.rb b/spec/factories/posts.rb index 48c7beb45..17497335f 100644 --- a/spec/factories/posts.rb +++ b/spec/factories/posts.rb @@ -1,7 +1,7 @@ FactoryGirl.define do factory :post do content "BLAH!!" - user_id 2 from 1 + user end end \ No newline at end of file diff --git a/spec/features/creating_a_new_post_spec.rb b/spec/features/creating_a_new_post_spec.rb new file mode 100644 index 000000000..20e70dcf1 --- /dev/null +++ b/spec/features/creating_a_new_post_spec.rb @@ -0,0 +1,20 @@ +require 'rails_helper.rb' + +feature "A user can create a new post" do + let(:user) {create(:user)} + + scenario "on their wall" do + login(user) + click_on("Timeline") + filled_in = "HELLLOO!!" + fill_in('post[content]', with: filled_in) + click_on("Post to the world!") + expect(page).to have_content(filled_in) + + end + + scenario "A user can create a post on someone else's wall" do + login(user) + end + +end \ No newline at end of file diff --git a/spec/features/delete_a_post_spec.rb b/spec/features/delete_a_post_spec.rb new file mode 100644 index 000000000..e500b7012 --- /dev/null +++ b/spec/features/delete_a_post_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper.rb' + +feature "Deleting a post" do + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blah@gmail.com")} + let!(:post) {create(:post, user_id: user.id, content: "I AM A ROGUE POST", from: user2.id )} + + scenario "from a user's profile should delete it from their timeline and database" do + login(user) + click_on("Timeline") + expect(Post.count).to eq(1) + click_on("Delete Post") + expect(Post.count).to eq(0) + expect(page).to_not have_content("Delete Post") + end + + scenario "from a user not that current user should not change the post count" do + login(user2) + visit "/users/#{user.id}/profiles" + expect(Post.count).to eq(1) + click_on("Delete Post") + end + +end \ No newline at end of file diff --git a/spec/features/edit_profile_spec.rb b/spec/features/edit_profile_spec.rb new file mode 100644 index 000000000..fe3e279c0 --- /dev/null +++ b/spec/features/edit_profile_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper.rb' + +feature "Editing your profile", js: true do + let(:user) {create(:user, email: "myemail@gmail.com")} + let(:user2) {create(:user, email: "blah@gmail.com")} + + scenario "a newly logged in user will be able to edit their profile page" do + login(user) + click_link("Edit") + fill_in('user_email', with:"mynewemail@gmail.com") + save_and_open_page + click_button("Submit") + + expect(page).to have_text("Your Danebook profile has been updated") + end + + scenario "a newly logged in user should not have access to another user's edit page" do + login(user) + visit "/users/#{user2.id}/profiles/edit" + #save_and_open_page + expect(page).to have_content("404") + end + +end \ No newline at end of file diff --git a/spec/features/like_a_post_spec.rb b/spec/features/like_a_post_spec.rb new file mode 100644 index 000000000..f4ef0fef6 --- /dev/null +++ b/spec/features/like_a_post_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper.rb' + +feature "Liking a post" do + + scenario "" do + + end + + + +end \ No newline at end of file diff --git a/spec/features/sign_in_spec.rb b/spec/features/sign_in_spec.rb new file mode 100644 index 000000000..c642144bc --- /dev/null +++ b/spec/features/sign_in_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper.rb' + + +feature "Signing in" do + let(:user) {create(:user)} + + scenario "a user who has an account will log in" do + login(user) + expect(page).to have_content("You've successfully signed into Danebook!") + end +end \ No newline at end of file diff --git a/spec/features/visit_homepage_spec.rb b/spec/features/visit_homepage_spec.rb new file mode 100644 index 000000000..a14dce165 --- /dev/null +++ b/spec/features/visit_homepage_spec.rb @@ -0,0 +1,22 @@ +require 'rails_helper.rb' + +feature "visiting homepage as a new user" do + + scenario "a new visitor will see a log in button" do + visit root_path + expect(page).to have_button("Log in") + end + + scenario "a new visitor will create a new Danebook account" do + visit root_path + fill_in("First Name", with: "foo") + fill_in("Last Name", with: "bar") + emails = page.all(".form-control") + page.all(:fillable_field, 'Email')[1].set("foo@bar.com") + page.all(:fillable_field, 'Password')[1].set("password") + fill_in("Password Confirmation", with: "password") + click_button("Sign Up!") + expect(page).to have_content("Welcome to Danebook!") + end + +end diff --git a/spec/features/visiting_timelines_spec.rb b/spec/features/visiting_timelines_spec.rb new file mode 100644 index 000000000..7e6a10285 --- /dev/null +++ b/spec/features/visiting_timelines_spec.rb @@ -0,0 +1,20 @@ +require 'rails_helper.rb' + +feature "visting timelines" do + let(:user) {create(:user)} + let(:user2) {create(:user, first_name: "Blah", email: "blah@gmail.com")} + + scenario "a signed in user should be able to visit any person's timeline" do + login(user) + visit "/users/#{user2.id}" + expect(page).to have_content(user2.first_name) + expect(page).to have_selector(".btn.btn-primary.center-block") + end + + scenario "a signed in user should be able to view their own timeline" do + login(user) + visit "/users/#{user.id}" + expect(page).to have_content(user.first_name) + expect(page).to have_selector(".btn.btn-primary.center-block") + end +end \ No newline at end of file diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 186245fee..be74d5ef6 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -42,6 +42,10 @@ is_expected.to have_many(:posts) end + it "should have many likes" do + is_expected.to have_many(:likes) + end + it "should have one profile" do is_expected.to have_one(:profile) end @@ -50,7 +54,7 @@ describe "#all_posts" do let(:user) {build(:user)} - + it "should list all posts in ascending order of a user" do 5.times do |n| user.posts.build(created_at: Time.now, id: n) @@ -62,7 +66,7 @@ end describe "#search" do - it "should return an array of all matching user objects with a given search input" do + it "returns an array of all matching user objects with a given search input" do bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} search_results = User.search("Bob") bobs.each do |bob| @@ -73,9 +77,7 @@ it "should return an empty array of if nothing matches" do bobs = Array.new(5) { |n| create(:user, first_name: ("Bob#{n}"), email: n )} search_results = User.search("Joe") - bobs.each do |bob| - expect(search_results).to_not include(bob) - end + expect(search_results).to be_empty end it "should return unique users" do diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 98e21ae08..61c3aa8d7 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -6,6 +6,7 @@ require 'spec_helper' require 'rspec/rails' require 'factory_girl_rails' +require 'capybara/rails' # Add additional requires below this line. Rails is not loaded until this point! # Requires supporting ruby files with custom matchers and macros, etc, in @@ -21,7 +22,7 @@ # 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 } +Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } # Checks for pending migration and applies them before tests are run. # If you are not using ActiveRecord, you can remove this line. @@ -32,6 +33,8 @@ # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures config.fixture_path = "#{::Rails.root}/spec/fixtures" + + config.include(LoginMacros) # 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. diff --git a/spec/support/login_macros.rb b/spec/support/login_macros.rb new file mode 100644 index 000000000..1c51b9185 --- /dev/null +++ b/spec/support/login_macros.rb @@ -0,0 +1,8 @@ +module LoginMacros + def login(user) + visit root_path + page.all(:fillable_field, 'Email')[0].set(user.email) + page.all(:fillable_field, 'Password')[0].set(user.password) + click_button("Log in") + end +end diff --git a/spec/support/signin_macros.rb b/spec/support/signin_macros.rb new file mode 100644 index 000000000..e69de29bb From b19352df7baba1dd9a56e8c3d3fa079ba2aedd5a Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Fri, 19 Aug 2016 00:18:50 -0400 Subject: [PATCH 15/31] Add photos, clean up CSS on user timeline page --- .gitignore | 3 ++ Gemfile | 8 +++ Gemfile.lock | 38 +++++++++++--- app/assets/javascripts/photos.coffee | 3 ++ app/assets/stylesheets/application.scss | 8 +++ app/assets/stylesheets/photos.scss | 3 ++ app/controllers/photos_controller.rb | 39 ++++++++++++++ app/controllers/profiles_controller.rb | 9 ++-- app/controllers/users_controller.rb | 1 + app/helpers/photos_helper.rb | 2 + app/models/photo.rb | 13 +++++ app/models/profile.rb | 8 +++ app/views/comments/_new_comment.html.erb | 5 +- app/views/photos/index.html.erb | 10 ++++ app/views/photos/show.html.erb | 0 app/views/profiles/edit.html.erb | 13 +++-- app/views/shared/_profile_header.html.erb | 2 +- app/views/users/show.html.erb | 40 +++++++++------ config/environments/development.rb | 22 +++++++- config/routes.rb | 5 +- config/secrets.yml | 8 +++ .../20160818214302_add_avatar_to_photos.rb | 7 +++ db/schema.rb | 12 +++-- spec/controllers/photos_controller_spec.rb | 5 ++ spec/controllers/posts_controller_spec.rb | 39 ++++++++++++++ spec/controllers/users_controller_spec.rb | 51 +++++++++++++++++++ spec/factories/comments.rb | 10 ++++ spec/factories/photos.rb | 8 +++ spec/factories/posts.rb | 2 +- "spec/factories/users.rb\nusers.rb\nusers.rb" | 4 +- spec/features/creating_a_new_post_spec.rb | 8 ++- spec/features/delete_a_post_spec.rb | 4 +- spec/features/edit_profile_spec.rb | 10 +--- spec/features/like_a_post_spec.rb | 31 +++++++++-- spec/helpers/photos_helper_spec.rb | 15 ++++++ spec/models/comment_spec.rb | 18 +++++++ spec/models/like_spec.rb | 0 spec/models/photo_spec.rb | 14 +++++ spec/models/post_spec.rb | 12 +++-- spec/models/profile_spec.rb | 11 ++++ spec/views/users/index_spec.rb | 21 ++++++++ spec/views/users/new_spec.rb | 24 +++++++++ 42 files changed, 489 insertions(+), 57 deletions(-) create mode 100644 app/assets/javascripts/photos.coffee create mode 100644 app/assets/stylesheets/photos.scss create mode 100644 app/controllers/photos_controller.rb create mode 100644 app/helpers/photos_helper.rb create mode 100644 app/views/photos/index.html.erb create mode 100644 app/views/photos/show.html.erb create mode 100644 db/migrate/20160818214302_add_avatar_to_photos.rb create mode 100644 spec/controllers/photos_controller_spec.rb create mode 100644 spec/controllers/posts_controller_spec.rb create mode 100644 spec/controllers/users_controller_spec.rb create mode 100644 spec/factories/comments.rb create mode 100644 spec/factories/photos.rb create mode 100644 spec/helpers/photos_helper_spec.rb create mode 100644 spec/models/comment_spec.rb create mode 100644 spec/models/like_spec.rb create mode 100644 spec/models/photo_spec.rb create mode 100644 spec/models/profile_spec.rb create mode 100644 spec/views/users/index_spec.rb create mode 100644 spec/views/users/new_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 51dbce835..c0595a367 100644 --- a/Gemfile +++ b/Gemfile @@ -1,5 +1,13 @@ source 'https://rubygems.org' +gem 'figaro' + +gem 'aws-sdk' + +gem 'paperclip' + +gem 'rails-controller-testing' + gem 'factory_girl_rails' gem 'rspec-rails' diff --git a/Gemfile.lock b/Gemfile.lock index db1ac1f07..6d8b052f4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -40,8 +40,14 @@ GEM tzinfo (~> 1.1) addressable (2.4.0) arel (7.1.1) - autoprefixer-rails (6.4.0.1) + autoprefixer-rails (6.4.0.2) execjs + aws-sdk (2.5.4) + aws-sdk-resources (= 2.5.4) + aws-sdk-core (2.5.4) + jmespath (~> 1.0) + aws-sdk-resources (2.5.4) + aws-sdk-core (= 2.5.4) bcrypt (3.1.11) better_errors (2.1.1) coderay (>= 1.0.0) @@ -57,7 +63,7 @@ GEM sass (>= 3.3.4) builder (3.2.2) byebug (9.0.5) - capybara (2.7.1) + capybara (2.8.0) addressable mime-types (>= 1.16) nokogiri (>= 1.3.3) @@ -72,6 +78,10 @@ GEM mimemagic (>= 0.3.0) childprocess (0.5.9) ffi (~> 1.0, >= 1.0.11) + 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) @@ -93,6 +103,8 @@ GEM faker (1.6.6) i18n (~> 0.5) ffi (1.9.14) + figaro (1.1.1) + thor (~> 0.14) globalid (0.3.7) activesupport (>= 4.1.0) hirb (0.7.3) @@ -100,6 +112,7 @@ GEM jbuilder (2.6.0) activesupport (>= 3.0.0, < 5.1) multi_json (~> 1.2) + jmespath (1.3.1) jquery-rails (4.1.1) rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) @@ -126,6 +139,12 @@ GEM nokogiri (1.6.8) mini_portile2 (~> 2.1.0) pkg-config (~> 1.1.7) + paperclip (5.0.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) puma (3.6.0) @@ -144,6 +163,10 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 5.0.0.1) sprockets-rails (>= 2.0.0) + rails-controller-testing (1.0.1) + actionpack (~> 5.x) + actionview (~> 5.x) + activesupport (~> 5.x) rails-dom-testing (2.0.1) activesupport (>= 4.2.0, < 6.0) nokogiri (~> 1.6.0) @@ -176,7 +199,7 @@ GEM rspec-mocks (~> 3.5.0) rspec-support (~> 3.5.0) rspec-support (3.5.0) - rubyzip (1.1.7) + rubyzip (1.2.0) sass (3.4.22) sass-rails (5.0.6) railties (>= 4.0.0, < 6) @@ -184,9 +207,8 @@ GEM sprockets (>= 2.8, < 4.0) sprockets-rails (>= 2.0, < 4.0) tilt (>= 1.1, < 3) - selenium-webdriver (2.51.0) + selenium-webdriver (2.53.4) childprocess (~> 0.5) - multi_json (~> 1.0) rubyzip (~> 1.0) websocket (~> 1.0) shoulda-matchers (3.1.1) @@ -214,7 +236,7 @@ GEM activemodel (>= 5.0) debug_inspector railties (>= 5.0) - websocket (1.2.2) + websocket (1.2.3) websocket-driver (0.6.4) websocket-extensions (>= 0.1.0) websocket-extensions (0.1.2) @@ -226,6 +248,7 @@ PLATFORMS ruby DEPENDENCIES + aws-sdk bcrypt better_errors binding_of_caller @@ -237,14 +260,17 @@ DEPENDENCIES coffee-rails (~> 4.2) factory_girl_rails faker + figaro hirb jbuilder (~> 2.5) jquery-rails launchy listen (~> 3.0.5) + paperclip pg puma (~> 3.0) rails (~> 5.0.0) + rails-controller-testing rspec-rails sass-rails (~> 5.0) selenium-webdriver 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/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 472ff88c9..5502bafc6 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -246,4 +246,12 @@ body { .input-group-addon { display:inline-block; +} + +.index-photo { + margin-top:20px +} + +.comment-show { + margin-top:30px } \ No newline at end of file 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/controllers/photos_controller.rb b/app/controllers/photos_controller.rb new file mode 100644 index 000000000..7b362b985 --- /dev/null +++ b/app/controllers/photos_controller.rb @@ -0,0 +1,39 @@ +class PhotosController < ApplicationController + before_action :require_current_user, only: [:destroy, :create] + + def index + @profile = Profile.find(params[:profile_id]) + @photos = @profile.photos + @user = @profile.user + end + + def create + @profile = Profile.find(params[:profile_id]) + @photo = @profile.photos.build(photo_params) + if @photo.save + flash[:success] = "Your photo has been added!" + redirect_to user_profiles_path(current_user) + else + flash.now[:danger] = "Something went wrong. Your photo was not uploaded" + redirect_to user_profiles_path(current_user) + end + end + + def destroy + end + + def new + + end + + def show + @photo = Photo.find(params[:id]) + end + + private + + def photo_params + params.require(:photo).permit(:picture, :user_id) + end + +end \ No newline at end of file diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 00a5e781d..e619181b9 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -3,9 +3,12 @@ class ProfilesController < ApplicationController before_action :set_user, only: [:show, :edit] def edit - #HAD TO HARD CODE THIS IN, OTHER WISE PEOPLE CAN EDIT ANYONE - redirect_to "http://nouveller.com/404/" if @user != current_user - @profile = current_user.profile + if @user != current_user + redirect_to "http://nouveller.com/404/" + else + @profile = current_user.profile + @photo = current_user.profile.photos.build + end end def update diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index ded6428b8..5905d87cf 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -47,6 +47,7 @@ def show @comment = @post.comments.build if current_user @like = @post.likes.build if current_user @posts = @user.all_posts + @photos = Photo.newest_six(@profile) end private diff --git a/app/helpers/photos_helper.rb b/app/helpers/photos_helper.rb new file mode 100644 index 000000000..0a10d472b --- /dev/null +++ b/app/helpers/photos_helper.rb @@ -0,0 +1,2 @@ +module PhotosHelper +end diff --git a/app/models/photo.rb b/app/models/photo.rb index 78f6bc668..45dac58f7 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -1,3 +1,16 @@ class Photo < ApplicationRecord + + has_attached_file :picture, :styles => { :medium => "300x300", :thumb => "100x100" } + validates_attachment_content_type :picture, :content_type => /\Aimage\/.*\Z/ + + has_one :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id + has_one :profile_photo, class_name: "Photo", foreign_key: :profile_photo_id + belongs_to :profile + + + def self.newest_six(profile) + profile.photos.order("created_at DESC").take(6) + end + end diff --git a/app/models/profile.rb b/app/models/profile.rb index 926c9f226..5c409e4e1 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -1,3 +1,11 @@ class Profile < ApplicationRecord + has_many :photos belongs_to :user + + belongs_to :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id, optional: true + belongs_to :profile_photo, class_name: "Photo", foreign_key: :profile_photo_id, optional: true + + + + end diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb index ace354410..3d22161ee 100644 --- a/app/views/comments/_new_comment.html.erb +++ b/app/views/comments/_new_comment.html.erb @@ -1,13 +1,12 @@ -<%= form_for [comment.commentable, Comment.new] do |comment| %> +<%= form_for [comment.commentable, comment] do |comment| %>
<%= image_tag("default_image.jpg", class: "comment-picture") %>
-

Media heading

<%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> <%= comment.submit "Comment on Post", class: "btn btn-success" %> <%= comment.fields_for :like do |like| %> - <%= like.hidden_field :user_id, value: current_user.id %>> + <%= like.hidden_field :user_id, value: current_user.id %> <%= like.submit "Like", class: "btn btn-primary"%> <% end %> diff --git a/app/views/photos/index.html.erb b/app/views/photos/index.html.erb new file mode 100644 index 000000000..055259ae0 --- /dev/null +++ b/app/views/photos/index.html.erb @@ -0,0 +1,10 @@ +<%= render "shared/profile_header" %> + +
+ <% @photos.each do |photo| %> +
+ <%= link_to image_tag(photo.picture.url), profile_photo_path(photo.profile, photo) %> +
+ <% end %> +
+ diff --git a/app/views/photos/show.html.erb b/app/views/photos/show.html.erb new file mode 100644 index 000000000..e69de29bb diff --git a/app/views/profiles/edit.html.erb b/app/views/profiles/edit.html.erb index 4a16634a9..28ed02bb4 100644 --- a/app/views/profiles/edit.html.erb +++ b/app/views/profiles/edit.html.erb @@ -62,11 +62,16 @@

<%= user_profile.text_area :about_me, placeholder: @profile.about_me, class: "form-control text-area-about", cols: 11, rows: 11 %>

About me

<%= user_profile.text_area :words_to_live_by, placeholder: @profile.words_to_live_by, class: "form-control text-area-about", cols: 11, rows: 11%>

+
+ <%= p.submit :Submit, class: "form-control btn btn-primary submit-edit-button" %>
- <% end %> -
-
- <%= p.submit :Submit, class: "form-control btn btn-primary submit-edit-button" %> + <% end %> +
+ <% end %> + + <%= form_for [@profile, @photo] do |f| %> + <%= f.file_field :picture %> + <%= f.submit :submit %> <% end %> \ No newline at end of file diff --git a/app/views/shared/_profile_header.html.erb b/app/views/shared/_profile_header.html.erb index 043b3dc5e..2f959c2b0 100644 --- a/app/views/shared/_profile_header.html.erb +++ b/app/views/shared/_profile_header.html.erb @@ -13,7 +13,7 @@ <%= link_to "About", user_profiles_path(@user) %>
-

Photos

+

Photos

+ <% @photos.each do |photo| %> +
+ <%= link_to image_tag(photo.picture.url(:thumbnail)), profile_photo_path(photo.profile, photo) %> +
+ <% end %>
- <%= render "posts/new_post" %> + <%= render "posts/new_post" if @post && @user%> <% @posts.each do |post|%>
@@ -35,34 +40,39 @@

<%= post.likes.count %> likes

<% if !post.comments.empty? %> <% post.all_comments.each do |comment| %> +
  • <%= image_tag("default_image.jpg", class: "comment-picture") %>
    -

    <%= link_to comment.commentable.user.first_name, user_profiles_path(comment.commentable.user) %>

    +

    <%= link_to comment.commentable.user.first_name + " " + comment.commentable.user.last_name, user_profiles_path(comment.commentable.user) %>

    <%= comment.content %>

    - - <%= render "comments/new_comment", comment: @comment, like: @like %> +
    + <%= render "comments/new_comment", comment: @comment, like: @like if @comment%> +
+
<% end %> <% else %> <% end %>
- <%= form_for [post, @post.comments.build] do |comment| %> - <%= image_tag("default_image.jpg", class: "comment-picture") %> - <%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> - <%= comment.submit "Comment on Post", class: "btn btn-success" %> - <% end %> - <%= form_for [post, @post.likes.build] do |like| %> - <%= like.hidden_field :user_id, value: current_user.id %> - <%= like.submit "Like", class: "btn btn-primary"%> - <% end %> + <% if @post %> + <%= form_for [post, @comment], class: "comment-show" do |comment| %> + <%= image_tag("default_image.jpg", class: "comment-picture") %> + <%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= comment.submit "Comment on Post", class: "btn btn-success" %> + <% end %> + <%= form_for [post, @post.likes.build] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary"%> + <% end %> - <%= link_to "Delete Post", user_post_path(current_user, post), method: :delete, class: "btn btn-danger" if current_user == @user%> + <%= link_to "Delete Post", user_post_path(current_user, post), method: :delete, class: "btn btn-danger" if current_user == @user%> + <% end %>
diff --git a/config/environments/development.rb b/config/environments/development.rb index 6f7197045..f25a81296 100644 --- a/config/environments/development.rb +++ b/config/environments/development.rb @@ -1,11 +1,31 @@ Rails.application.configure do # Settings specified here will take precedence over those in config/application.rb. - + Paperclip.options[:command_path] = "/usr/local/bin" # 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 + config.paperclip_defaults = { + + :storage => :s3, + + :s3_credentials => { + + # put your host name here if needed + # see the reading below for more details + # NOTE: This must be the correct region for YOU + :host_name => "dylanlynch.s3-website-us-east-1.amazonaws.com", + + # NOTE: these lines are changed to use secrets.yml + # from the examples (which use ENV vars instead) + :bucket => Rails.application.secrets.s3_bucket_name, + :access_key_id => Rails.application.secrets.s3_api_id, + :secret_access_key => Rails.application.secrets.s3_api_key, + :s3_region=> Rails.application.secrets.s3_region + } +} + # Do not eager load code on boot. config.eager_load = false diff --git a/config/routes.rb b/config/routes.rb index 744e1802c..4006d7b07 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,9 +6,12 @@ resources :users do resource :profiles, only: [:edit, :show, :update] resources :posts, only: [:create, :destroy] - resources :photos end + resources :profile do + resources :photos + end + resources :posts do resources :comments, defaults: {commentable: "Post"} resources :likes, defaults: {likeable: "Post"} diff --git a/config/secrets.yml b/config/secrets.yml index 9dae25df2..507796a0a 100644 --- a/config/secrets.yml +++ b/config/secrets.yml @@ -12,6 +12,10 @@ development: secret_key_base: 4a8ab98976c8adefce7471fd7a3d1c240af246cc1dc57ead037ac053faab4f42b5d9f0ee2c3017d9825c535efe6a3e6e87ca18b841207161b336a9e14f5cfee4 + s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] %> + s3_api_id: <%= ENV["S3_API_ID"] %> + s3_api_key: <%= ENV["S3_API_KEY"] %> + s3_region: <%= ENV["S3_REGION"] %> test: secret_key_base: 8ed5c1fef5f8594431a217909d96653bfd594285b6180e7786c16347f7f4671ae5a32a2104643aeaf846a87f832b0f59cbc2595fe2c204f720ae792e1a23b17c @@ -20,3 +24,7 @@ test: # instead read values from the environment. production: secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> + s3_bucket_name: <%= ENV["S3_BUCKET_NAME"] %> + s3_api_id: <%= ENV["S3_API_ID"] %> + s3_api_key: <%= ENV["S3_API_KEY"] %> + s3_region: <%= ENV["S3_REGION"] %> diff --git a/db/migrate/20160818214302_add_avatar_to_photos.rb b/db/migrate/20160818214302_add_avatar_to_photos.rb new file mode 100644 index 000000000..6bfc1a25a --- /dev/null +++ b/db/migrate/20160818214302_add_avatar_to_photos.rb @@ -0,0 +1,7 @@ +class AddAvatarToPhotos < ActiveRecord::Migration[5.0] + def change + add_attachment :photos, :picture + add_column :profiles, :cover_photo_id, :integer + add_column :profiles, :profile_photo_id, :integer + end +end diff --git a/db/schema.rb b/db/schema.rb index cc6753a1f..dc2a4e7d8 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: 20160815030937) do +ActiveRecord::Schema.define(version: 20160818214302) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -38,8 +38,12 @@ create_table "photos", force: :cascade do |t| t.integer "profile_id" t.string "url" - t.datetime "created_at", null: false - t.datetime "updated_at", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "picture_file_name" + t.string "picture_content_type" + t.integer "picture_file_size" + t.datetime "picture_updated_at" t.index ["profile_id"], name: "index_photos_on_profile_id", using: :btree end @@ -66,6 +70,8 @@ t.text "about_me" t.text "words_to_live_by" t.date "birthday" + t.integer "cover_photo_id" + t.integer "profile_photo_id" t.index ["user_id"], name: "index_profiles_on_user_id", using: :btree end diff --git a/spec/controllers/photos_controller_spec.rb b/spec/controllers/photos_controller_spec.rb new file mode 100644 index 000000000..89e503b82 --- /dev/null +++ b/spec/controllers/photos_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe PhotosController, type: :controller do + +end diff --git a/spec/controllers/posts_controller_spec.rb b/spec/controllers/posts_controller_spec.rb new file mode 100644 index 000000000..6ac638514 --- /dev/null +++ b/spec/controllers/posts_controller_spec.rb @@ -0,0 +1,39 @@ +require 'rails_helper' + +describe PostsController do + let(:user) {create(:user)} + let(:micro_post) {create(:post, user_id: user.id)} + + context "for signed in user" do + + before do + request.cookies["auth_token"] = user.auth_token + end + + describe "POST #create" do + it "creates a new post for a user" do + post :create, params: {post: attributes_for(:post),user_id: user.id} + expect(response).to redirect_to user + end + it "changes the post count by 1" do + expect{post :create, params: {post: attributes_for(:post), user_id: user.id}}.to change(Post, :count).by(1) + end + end + + + describe "DELETE #destroy" do + it "destroys the post" do + micro_post + expect{ delete :destroy, params: {user_id: user.id, id: micro_post.id}}.to change(Post, :count).by(-1) + end + + it "redirects to the user" do + delete :destroy, params: {user_id: user.id, :id => micro_post.id} + expect(response).to redirect_to user + end + end + + end +end + + diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb new file mode 100644 index 000000000..ec4bfd4c6 --- /dev/null +++ b/spec/controllers/users_controller_spec.rb @@ -0,0 +1,51 @@ +require 'rails_helper' + +describe UsersController do + + let(:user){ create(:user) } + let(:micro_post){ create(:post) } + let(:user2){ create(:user) } + + + + + describe "GET #index" do + before do + request.cookies["auth_token"] = user.auth_token + end + it "returns a number of users from a search query" do + get :index, params: {search: ""} + expect(response.status).to eq(200) + end + end + + describe "POST #create" do + it "user#create will create a new User" do + expect{post :create, params: { user: attributes_for(:user)}}.to change(User, :count).by(1) + expect(response).to redirect_to user_profiles_path(User.last) + end + + it "user#create with no attributes will not create a new User" do + expect{post :create, params: {user: {email: "fail@gmail.com"}} }.to_not change(User, :count) + end + end + + describe "GET #edit" do + + context "for signed in user" do + before do + request.cookies["auth_token"] = user.auth_token + end + + it "authorized user has access to user#edit for own edit" do + get :edit, params: {id: user.id} + expect(response).to render_template :edit + end + + it "authorized user sets right instance variable for edit" do + get :edit, params: {id: user.id} + expect(assigns(:user)).to eq(user) + end + end + end + end \ No newline at end of file diff --git a/spec/factories/comments.rb b/spec/factories/comments.rb new file mode 100644 index 000000000..a8dd1f1ce --- /dev/null +++ b/spec/factories/comments.rb @@ -0,0 +1,10 @@ +FactoryGirl.define do + factory :comment do + content "BLAH!!" + association :commentable, factory: :post + + factory :comment_comment do + association :commentable, factory: :comment + end + end +end \ No newline at end of file diff --git a/spec/factories/photos.rb b/spec/factories/photos.rb new file mode 100644 index 000000000..6ce47ce80 --- /dev/null +++ b/spec/factories/photos.rb @@ -0,0 +1,8 @@ +FactoryGirl.define do + factory :photo do + picture_file_name "test_photo.jpg" + picture_content_type "image/jpeg" + picture_file_size 83261 + association :profile + end +end \ No newline at end of file diff --git a/spec/factories/posts.rb b/spec/factories/posts.rb index 17497335f..32f0bafb5 100644 --- a/spec/factories/posts.rb +++ b/spec/factories/posts.rb @@ -2,6 +2,6 @@ factory :post do content "BLAH!!" from 1 - user + association :user end end \ No newline at end of file diff --git "a/spec/factories/users.rb\nusers.rb\nusers.rb" "b/spec/factories/users.rb\nusers.rb\nusers.rb" index 166207f51..a415f67a4 100644 --- "a/spec/factories/users.rb\nusers.rb\nusers.rb" +++ "b/spec/factories/users.rb\nusers.rb\nusers.rb" @@ -1,8 +1,10 @@ FactoryGirl.define do factory :user do + sequence :email do |n| + "person#{n}@example.com" + end first_name "Foo" last_name "Bar" - email "foo@bar.com" password "password" password_confirmation "password" end diff --git a/spec/features/creating_a_new_post_spec.rb b/spec/features/creating_a_new_post_spec.rb index 20e70dcf1..4fb112b9d 100644 --- a/spec/features/creating_a_new_post_spec.rb +++ b/spec/features/creating_a_new_post_spec.rb @@ -1,7 +1,8 @@ require 'rails_helper.rb' feature "A user can create a new post" do - let(:user) {create(:user)} + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blahblah2@gmail.com")} scenario "on their wall" do login(user) @@ -15,6 +16,11 @@ scenario "A user can create a post on someone else's wall" do login(user) + visit "/users/#{user2.id}" + filled_in = "HELLLOO!!" + fill_in('post[content]', with: filled_in) + click_on("Post to the world!") + expect(page).to have_content(filled_in) end end \ No newline at end of file diff --git a/spec/features/delete_a_post_spec.rb b/spec/features/delete_a_post_spec.rb index e500b7012..f00be6d0f 100644 --- a/spec/features/delete_a_post_spec.rb +++ b/spec/features/delete_a_post_spec.rb @@ -16,9 +16,9 @@ scenario "from a user not that current user should not change the post count" do login(user2) - visit "/users/#{user.id}/profiles" + visit "/users/#{user.id}" expect(Post.count).to eq(1) - click_on("Delete Post") + expect(page).to_not have_content("Delete Post") end end \ No newline at end of file diff --git a/spec/features/edit_profile_spec.rb b/spec/features/edit_profile_spec.rb index fe3e279c0..6e44b246a 100644 --- a/spec/features/edit_profile_spec.rb +++ b/spec/features/edit_profile_spec.rb @@ -1,6 +1,6 @@ require 'rails_helper.rb' -feature "Editing your profile", js: true do +feature "Editing your profile" do let(:user) {create(:user, email: "myemail@gmail.com")} let(:user2) {create(:user, email: "blah@gmail.com")} @@ -8,17 +8,9 @@ login(user) click_link("Edit") fill_in('user_email', with:"mynewemail@gmail.com") - save_and_open_page click_button("Submit") expect(page).to have_text("Your Danebook profile has been updated") end - scenario "a newly logged in user should not have access to another user's edit page" do - login(user) - visit "/users/#{user2.id}/profiles/edit" - #save_and_open_page - expect(page).to have_content("404") - end - end \ No newline at end of file diff --git a/spec/features/like_a_post_spec.rb b/spec/features/like_a_post_spec.rb index f4ef0fef6..b73afc2bc 100644 --- a/spec/features/like_a_post_spec.rb +++ b/spec/features/like_a_post_spec.rb @@ -1,11 +1,36 @@ require 'rails_helper.rb' feature "Liking a post" do + let(:user) {create(:user)} + let(:user2) {create(:user, email: "blah@gmail.com")} + let!(:post) {create(:post, user_id: user.id, content: "I AM A ROGUE POST", from: user2.id )} - scenario "" do - + scenario "a user can like their own post" do + login(user) + click_on("Timeline") + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) end + scenario "a user can like another person's post" do + login(user2) + visit "/users/#{user.id}" + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) + end - + scenario "a user cannot like a post they have already liked" do + login(user) + click_on("Timeline") + expect(Like.count).to eq(0) + click_on("Like") + expect(Like.count).to eq(1) + click_on("Like") + expect(Like.count).to eq(1) + expect(page).to have_content("You already liked the comment +") + expect(page).to have_content("1") + end end \ No newline at end of file diff --git a/spec/helpers/photos_helper_spec.rb b/spec/helpers/photos_helper_spec.rb new file mode 100644 index 000000000..6b5aa7d30 --- /dev/null +++ b/spec/helpers/photos_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the PhotosHelper. For example: +# +# describe PhotosHelper 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 PhotosHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/comment_spec.rb b/spec/models/comment_spec.rb new file mode 100644 index 000000000..bc1505f49 --- /dev/null +++ b/spec/models/comment_spec.rb @@ -0,0 +1,18 @@ +require 'rails_helper' + +describe Comment do + let(:comment_comment){create(:comment_comment)} + + describe "associations" do + let(:comment) {create(:comment)} + + it "belongs to itself" do + is_expected.to belong_to(:commentable) + end + + it "should return the commentable" do + expect(comment_comment).to respond_to(:commentable) + end + end + +end \ No newline at end of file diff --git a/spec/models/like_spec.rb b/spec/models/like_spec.rb new file mode 100644 index 000000000..e69de29bb diff --git a/spec/models/photo_spec.rb b/spec/models/photo_spec.rb new file mode 100644 index 000000000..7c7872d1b --- /dev/null +++ b/spec/models/photo_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +describe Photo do + + describe "Associations" do + + let(:photo) { create(:photo) } + + it "validates the presence of content" do + is_expected.to respond_to(:profile) + end + + end +end diff --git a/spec/models/post_spec.rb b/spec/models/post_spec.rb index 238c79ca9..c6f2d0ba6 100644 --- a/spec/models/post_spec.rb +++ b/spec/models/post_spec.rb @@ -4,7 +4,7 @@ describe "Validations" do - let(:post) { build(:post) } + let(:post) { create(:post) } it "validates the presence of content" do is_expected.to validate_presence_of(:content) @@ -21,6 +21,9 @@ end describe "Associations" do + + let(:post) {create(:post)} + it "accepts nested attributes for a comment" do is_expected.to accept_nested_attributes_for(:comments) end @@ -36,14 +39,17 @@ it "has many likes" do is_expected.to have_many(:likes) end + it "should return the comments" do + expect(post).to respond_to(:comments) + end end describe "#all_comments" do - let(:post) {build(:post)} + let(:post) {create(:post)} it "should list all comments in ascending order of a comment" do 5.times do |n| - post.comments.build(created_at: Time.now, id: n) + post.comments.build(created_at: Time.now) end post.comments.each_with_index do |comment, index| expect(comment.created_at).to be < post.comments[index + 1].created_at if post.comments[index + 1] diff --git a/spec/models/profile_spec.rb b/spec/models/profile_spec.rb new file mode 100644 index 000000000..ce0c905cf --- /dev/null +++ b/spec/models/profile_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +describe Profile do + + describe "associations" do + it "has one user" do + is_expected.to belong_to(:user) + end + end + +end \ No newline at end of file diff --git a/spec/views/users/index_spec.rb b/spec/views/users/index_spec.rb new file mode 100644 index 000000000..c4ce8519c --- /dev/null +++ b/spec/views/users/index_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe "users/index.html.erb" do + let(:user) {create(:user)} + let(:user2) {create(:user)} + + it "shows the search query" do + users = [user, user2] + assign(:users, users) + render + expect(rendered).to match(/#{user.first_name}/) + end + + it "does not show any user for an empty search query" do + users = [] + assign(:users, users) + render + expect(rendered).to_not match(/#{user.first_name}/) + end + +end diff --git a/spec/views/users/new_spec.rb b/spec/views/users/new_spec.rb new file mode 100644 index 000000000..a7eb0bde6 --- /dev/null +++ b/spec/views/users/new_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe "users/new.html.erb" do + let(:user){build(:user)} + let(:user2){create(:user)} + + context "signed in" do + before do + def view.signed_in_user? + true + end + @user = user2 + def view.current_user + @user + end + end + + it "will show welcome text" do + render :template => 'users/new', :layout => 'layouts/application' + expect(rendered).to have_content(/Welcome/) + end + end + +end \ No newline at end of file From 29f02e0fe146e445394d3b1a2eb2a57655c8e2f5 Mon Sep 17 00:00:00 2001 From: Dylan Lynch Date: Fri, 19 Aug 2016 19:43:46 -0400 Subject: [PATCH 16/31] Add emails for commenting, likes and posts and photo show/index pages --- Gemfile | 1 + Gemfile.lock | 3 ++ app/assets/images/favicon.ico | Bin 0 -> 1150 bytes app/assets/stylesheets/application.scss | 27 ++++++++++--- app/controllers/comments_controller.rb | 18 ++++----- app/controllers/likes_controller.rb | 20 +++++----- app/controllers/photos_controller.rb | 12 +++--- app/controllers/profiles_controller.rb | 2 +- app/controllers/users_controller.rb | 2 +- app/mailers/user_mailer.rb | 28 ++++++++++++++ app/models/comment.rb | 12 +++++- app/models/like.rb | 1 + app/models/photo.rb | 10 +++-- app/models/profile.rb | 1 - app/models/user.rb | 5 +++ app/views/comments/_new_comment.html.erb | 25 ++++++------ app/views/layouts/application.html.erb | 8 ++-- app/views/photos/_no_photos.html.erb | 5 +++ app/views/photos/index.html.erb | 5 ++- app/views/photos/show.html.erb | 36 ++++++++++++++++++ app/views/profiles/edit.html.erb | 3 +- app/views/profiles/show.html.erb | 1 + app/views/shared/_flash.html.erb | 5 +++ app/views/shared/_profile_header.html.erb | 2 +- app/views/user_mailer/commented_by.html.erb | 5 +++ app/views/user_mailer/commented_by.text.erb | 5 +++ app/views/user_mailer/welcome.html.erb | 9 +++++ app/views/user_mailer/welcome.text.erb | 3 ++ app/views/users/index.html.erb | 1 + app/views/users/new.html.erb | 1 + app/views/users/show.html.erb | 28 ++++++++------ config/environments/development.rb | 11 +++--- config/routes.rb | 9 +++-- ...0160819192645_add_from_on_comment_table.rb | 11 ++++++ db/schema.rb | 11 ++++-- spec/mailers/previews/user_mailer_preview.rb | 4 ++ spec/mailers/user_mailer_spec.rb | 5 +++ 37 files changed, 251 insertions(+), 84 deletions(-) create mode 100644 app/assets/images/favicon.ico create mode 100644 app/mailers/user_mailer.rb create mode 100644 app/views/photos/_no_photos.html.erb create mode 100644 app/views/shared/_flash.html.erb create mode 100644 app/views/user_mailer/commented_by.html.erb create mode 100644 app/views/user_mailer/commented_by.text.erb create mode 100644 app/views/user_mailer/welcome.html.erb create mode 100644 app/views/user_mailer/welcome.text.erb create mode 100644 db/migrate/20160819192645_add_from_on_comment_table.rb create mode 100644 spec/mailers/previews/user_mailer_preview.rb create mode 100644 spec/mailers/user_mailer_spec.rb diff --git a/Gemfile b/Gemfile index c0595a367..f52b418da 100644 --- a/Gemfile +++ b/Gemfile @@ -67,6 +67,7 @@ group :development, :test do gem 'shoulda-matchers' gem 'capybara' gem 'launchy' + gem 'letter_opener' end group :development do diff --git a/Gemfile.lock b/Gemfile.lock index 6d8b052f4..02f3bd6d4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -120,6 +120,8 @@ GEM json (2.0.2) launchy (2.4.3) addressable (~> 2.3) + letter_opener (1.4.1) + launchy (~> 2.2) listen (3.0.8) rb-fsevent (~> 0.9, >= 0.9.4) rb-inotify (~> 0.9, >= 0.9.7) @@ -265,6 +267,7 @@ DEPENDENCIES jbuilder (~> 2.5) jquery-rails launchy + letter_opener listen (~> 3.0.5) paperclip pg diff --git a/app/assets/images/favicon.ico b/app/assets/images/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..9ee69eefcdce01db9bb1adab9ac62bb3031dc871 GIT binary patch literal 1150 zcmd6n!3lsc5Cm7SfV_PBv@~lGtkn|Y1sOQPMa)M%iJQ#sVqQvvJt@iFv({0XBrUcK z8M(MgIQb`Y>|5?x^|k4?I6m6S6<%jJdwtR`dB%4=ul;h4vJs=}JAO~6#op-i4eo-% kQP2Ip_?*4cL-EMh93RC;BMys{9vWrt7`?AG{3&$66Drt^fB*mh literal 0 HcmV?d00001 diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index 5502bafc6..1ffe859cf 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -244,14 +244,31 @@ body { display:inline-block; } -.input-group-addon { +.index-photo { + margin-top:20px; +} + +.no-photos { + color: #3b5998; +} + +.resize-index-photo { + height:100px; + width:100px; +} +.comment-section { display:inline-block; } -.index-photo { - margin-top:20px +.like-section { + display:inline-block; } -.comment-show { - margin-top:30px +.delete-section { + display:inline-block; +} + +.show-photo { + display:block; + margin: 0 auto; } \ No newline at end of file diff --git a/app/controllers/comments_controller.rb b/app/controllers/comments_controller.rb index cb1a007dd..8f46a8f3b 100644 --- a/app/controllers/comments_controller.rb +++ b/app/controllers/comments_controller.rb @@ -4,18 +4,18 @@ def new end def create - # parent = params[:commentable] - # parent_id = (parent.downcase << "_id").to_sym - # @commentable = parent.classify.constantize.find(params[parent_id]) - # @comment = @commentable.comments.new(comment_params) - @post = Post.find(params[:post_id]) - @comment = @post.comments.build(comment_params) + parent = params[:commentable] + parent_id = (parent.downcase << "_id").to_sym + @commentable = parent.classify.constantize.find(params[parent_id]) + @comment = @commentable.comments.build(comment_params) + @comment.user = User.find_by_id(@comment.commentable.user_id) + @comment.from = current_user.id if @comment.save! flash[:success] = "Your comment has been saved!" - redirect_to current_user + redirect_back(fallback_location: root_path) else flash[:danger] = "Your comment was not posted" - redirect_to current_user + redirect_to current_user(@comment.commentable.user_id) end end @@ -30,7 +30,7 @@ def show private def comment_params - params.require(:comment).permit(:content) + params.require(:comment).permit(:content, :from) end # def underscore diff --git a/app/controllers/likes_controller.rb b/app/controllers/likes_controller.rb index aa87a2356..70a70ca6a 100644 --- a/app/controllers/likes_controller.rb +++ b/app/controllers/likes_controller.rb @@ -1,14 +1,16 @@ class LikesController < ApplicationController def create - @post = Post.find(params[:post_id]) - @like = @post.likes.build(likes_params) - - if !Like.already_liked?(@post, current_user) && @like.save! - flash[:success] = "You have liked that comment" - redirect_to current_user + parent = params[:likeable] + parent_id = (parent.downcase << "_id").to_sym + @likeable = parent.classify.constantize.find(params[parent_id]) + @like = @likeable.likes.build(likes_params) + @like.from = current_user.id + if !Like.already_liked?(@likeable, current_user) && @like.save! + flash[:success] = "Liked!" + redirect_to user_path(@likeable.user) else - flash[:danger] = "You already liked the comment" - redirect_to current_user + flash[:danger] = "You already liked that" + redirect_to user_path(@likeable.user) end end @@ -17,6 +19,6 @@ def destroy def likes_params - params.require(:like).permit(:user_id) + params.require(:like).permit(:user_id, :from) end end diff --git a/app/controllers/photos_controller.rb b/app/controllers/photos_controller.rb index 7b362b985..34e14fa62 100644 --- a/app/controllers/photos_controller.rb +++ b/app/controllers/photos_controller.rb @@ -2,19 +2,19 @@ class PhotosController < ApplicationController before_action :require_current_user, only: [:destroy, :create] def index - @profile = Profile.find(params[:profile_id]) - @photos = @profile.photos - @user = @profile.user + @user = User.find_by_id(params[:user_id]) + @photos = @user.photos end def create - @profile = Profile.find(params[:profile_id]) - @photo = @profile.photos.build(photo_params) + @user = User.find_by_id(params[:user_id]) + @photo = @user.photos.build(photo_params) if @photo.save flash[:success] = "Your photo has been added!" redirect_to user_profiles_path(current_user) else - flash.now[:danger] = "Something went wrong. Your photo was not uploaded" + dsads + flash[:danger] = "Something went wrong. Your photo was not uploaded" redirect_to user_profiles_path(current_user) end end diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index e619181b9..0909bfe7c 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -7,7 +7,7 @@ def edit redirect_to "http://nouveller.com/404/" else @profile = current_user.profile - @photo = current_user.profile.photos.build + @photo = current_user.photos.build end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 5905d87cf..00367732d 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -47,7 +47,7 @@ def show @comment = @post.comments.build if current_user @like = @post.likes.build if current_user @posts = @user.all_posts - @photos = Photo.newest_six(@profile) + @photos = Photo.newest_six(@user) end private diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb new file mode 100644 index 000000000..e8616435b --- /dev/null +++ b/app/mailers/user_mailer.rb @@ -0,0 +1,28 @@ +class UserMailer < ApplicationMailer + default from: "welcome@danebook.com" + + def welcome(user) + @user = user + mail(to: @user.email, subject: 'Welcome to Danebook!') + end + + def commented_by(commenter, to_user, object) + @user = to_user + @commenter = commenter + @object = object + mail(to: @user.email, subject: "#{@commenter.first_name} just commented on a #{@object.class.to_s.downcase} of yours!") + end + + def liked_by(liker, to_user, object) + @user = to_user + @liker = liker + @object = object + mail(to: @user.email, subject: "#{@liker.first_name} just liked a #{@object.class.to_s.downcase} of yours!") + end + + def posted_by(poster, to_user, object) + @user = to_user + @poster = poster + mail(to: @user.email, subject: "#{@poster.first_name} just posted on your wall!") + end +end diff --git a/app/models/comment.rb b/app/models/comment.rb index f45629e84..9796c7556 100644 --- a/app/models/comment.rb +++ b/app/models/comment.rb @@ -1,8 +1,18 @@ class Comment < ApplicationRecord - #belongs_to :user + belongs_to :user belongs_to :commentable, polymorphic: true has_many :likes, as: :likeable has_many :comments, as: :commentable, dependent: :nullify + after_create :commented_by_email + accepts_nested_attributes_for :comments + + def commented_by_email + commenter = User.find_by_id(self.from) + the_user_id = self.commentable.respond_to?(:from) ? self.commentable.from : self.commentable.user_id + to_user = User.find_by_id(the_user_id) + commentable = self.commentable + UserMailer.commented_by(commenter, to_user, commentable).deliver! unless commenter == to_user + end end diff --git a/app/models/like.rb b/app/models/like.rb index 419820394..a30cbb224 100644 --- a/app/models/like.rb +++ b/app/models/like.rb @@ -1,5 +1,6 @@ class Like < ApplicationRecord belongs_to :likeable, polymorphic: true + belongs_to :user def self.already_liked?(post, user) post.likes.pluck(:user_id).include?(user.id) diff --git a/app/models/photo.rb b/app/models/photo.rb index 45dac58f7..1038876ee 100644 --- a/app/models/photo.rb +++ b/app/models/photo.rb @@ -1,16 +1,18 @@ class Photo < ApplicationRecord - has_attached_file :picture, :styles => { :medium => "300x300", :thumb => "100x100" } + has_attached_file :picture, :styles => { :medium => "300x300", :thumbnail => "100x100" } validates_attachment_content_type :picture, :content_type => /\Aimage\/.*\Z/ has_one :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id has_one :profile_photo, class_name: "Photo", foreign_key: :profile_photo_id + has_many :comments, as: :commentable + has_many :likes, as: :likeable - belongs_to :profile + belongs_to :user - def self.newest_six(profile) - profile.photos.order("created_at DESC").take(6) + def self.newest_six(user) + user.photos.order("created_at DESC").take(6) end end diff --git a/app/models/profile.rb b/app/models/profile.rb index 5c409e4e1..d865697e2 100644 --- a/app/models/profile.rb +++ b/app/models/profile.rb @@ -1,5 +1,4 @@ class Profile < ApplicationRecord - has_many :photos belongs_to :user belongs_to :cover_photo, class_name: "Photo", foreign_key: :cover_photo_id, optional: true diff --git a/app/models/user.rb b/app/models/user.rb index bd793e8be..f9da9c10c 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -6,6 +6,7 @@ class User < ApplicationRecord before_create :generate_token after_create :create_profile + after_create :welcome_email has_secure_password validates :password, @@ -39,4 +40,8 @@ def self.search(name) end.flatten.uniq end + def welcome_email + UserMailer.welcome(self).deliver! + end + end diff --git a/app/views/comments/_new_comment.html.erb b/app/views/comments/_new_comment.html.erb index 3d22161ee..d9938d760 100644 --- a/app/views/comments/_new_comment.html.erb +++ b/app/views/comments/_new_comment.html.erb @@ -1,14 +1,13 @@ -<%= form_for [comment.commentable, comment] do |comment| %> -
+
<%= image_tag("default_image.jpg", class: "comment-picture") %> -
-
- <%= comment.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> - <%= comment.submit "Comment on Post", class: "btn btn-success" %> - <%= comment.fields_for :like do |like| %> - <%= like.hidden_field :user_id, value: current_user.id %> - <%= like.submit "Like", class: "btn btn-primary"%> - <% end %> - -
-<% end %> +
+
+ <%= form_for [comment, new_comment] do |c| %> + <%= c.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= c.submit "Comment on Post", class: "btn btn-success" %> + <% end %> + <%= form_for [comment, comment.likes.build] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary like-section"%> + <% end %> +
\ No newline at end of file diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index da5843b67..f20b57d92 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -2,6 +2,7 @@ ProjectDanebook + <%= favicon_link_tag "favicon.ico" %> <%= csrf_meta_tags %> @@ -10,14 +11,11 @@ <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %> <%= javascript_include_tag 'application' %> + -
- <% flash.each do |key, value| %> -
<%= value %>
- <% end %> -
+ <%= yield %> diff --git a/app/views/photos/_no_photos.html.erb b/app/views/photos/_no_photos.html.erb new file mode 100644 index 000000000..f5808e95f --- /dev/null +++ b/app/views/photos/_no_photos.html.erb @@ -0,0 +1,5 @@ +
+

+

You currently have no photos!

+
+
\ No newline at end of file diff --git a/app/views/photos/index.html.erb b/app/views/photos/index.html.erb index 055259ae0..2b5ae8622 100644 --- a/app/views/photos/index.html.erb +++ b/app/views/photos/index.html.erb @@ -1,9 +1,10 @@ <%= render "shared/profile_header" %> - +<%= render "no_photos" if @photos.empty? %> +<%= render "shared/flash" %>
<% @photos.each do |photo| %>
- <%= link_to image_tag(photo.picture.url), profile_photo_path(photo.profile, photo) %> + <%= link_to image_tag(photo.picture.url), user_photo_path(photo.user, photo) %>
<% end %>
diff --git a/app/views/photos/show.html.erb b/app/views/photos/show.html.erb index e69de29bb..4b4da383a 100644 --- a/app/views/photos/show.html.erb +++ b/app/views/photos/show.html.erb @@ -0,0 +1,36 @@ +<%= render "shared/flash" %> +
+
+ <%= link_to image_tag(@photo.picture.url), user_photo_path(@photo.user, @photo) %> + <%= form_for [@photo, Comment.new] do |c| %> + <%= c.text_field :content, placeholder: "Write your comment here", class: "comment-text form-control", 'aria-label'=> "basic-addon1" %> + <%= c.submit "Comment on Post", class: "btn btn-success comment-section" %> + <% end %> + <%= form_for [@photo, Like.new] do |like| %> + <%= like.hidden_field :user_id, value: current_user.id %> + <%= like.submit "Like", class: "btn btn-primary like-section"%> + <% end %> +
+
+ <% if @photo.comments.count != 0 %> + <% @photo.comments.each do |comment| %> +
+
    +
  • +
    + <%= image_tag("default_image.jpg", class: "comment-picture") %> +
    +
    +

    Commented by: <%= link_to comment.commentable.user.first_name + " " + comment.commentable.user.last_name, user_profiles_path(comment.commentable.user) %>

    +

    <%=comment.likes.count %> likes

    +

    <%= comment.content %>

    +
    + +
    +
    +
  • +
+
+ <% end %> + <% end %> +
\ No newline at end of file diff --git a/app/views/profiles/edit.html.erb b/app/views/profiles/edit.html.erb index 28ed02bb4..8b0d02d53 100644 --- a/app/views/profiles/edit.html.erb +++ b/app/views/profiles/edit.html.erb @@ -1,5 +1,6 @@ <%= render "shared/search_navbar" %> <%= render "shared/profile_header" %> +<%= render "shared/flash" %>
@@ -70,7 +71,7 @@ <% end %>
- <%= form_for [@profile, @photo] do |f| %> + <%= form_for [@user, @photo] do |f| %> <%= f.file_field :picture %> <%= f.submit :submit %> <% end %> diff --git a/app/views/profiles/show.html.erb b/app/views/profiles/show.html.erb index 539fe1cf1..773132958 100644 --- a/app/views/profiles/show.html.erb +++ b/app/views/profiles/show.html.erb @@ -1,5 +1,6 @@ <%= render "shared/search_navbar" %> <%= render "shared/profile_header" %> +<%= render "shared/flash" %>
diff --git a/app/views/shared/_flash.html.erb b/app/views/shared/_flash.html.erb new file mode 100644 index 000000000..f2c7dbfb6 --- /dev/null +++ b/app/views/shared/_flash.html.erb @@ -0,0 +1,5 @@ +
+ <% flash.each do |key, value| %> +
<%= value %>
+ <% end %> +
\ No newline at end of file diff --git a/app/views/shared/_profile_header.html.erb b/app/views/shared/_profile_header.html.erb index 2f959c2b0..4ba455678 100644 --- a/app/views/shared/_profile_header.html.erb +++ b/app/views/shared/_profile_header.html.erb @@ -13,7 +13,7 @@ <%= link_to "About", user_profiles_path(@user) %>