├── log └── .keep ├── vendor └── .keep ├── lib ├── assets │ └── .keep └── tasks │ └── .keep ├── public ├── favicon.ico ├── apple-touch-icon.png ├── apple-touch-icon-precomposed.png ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── test ├── helpers │ └── .keep ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── like_test.rb │ ├── chirp_test.rb │ └── follow_test.rb ├── system │ └── .keep ├── controllers │ ├── .keep │ ├── chirps_controller_test.rb │ ├── likes_controller_test.rb │ ├── users_controller_test.rb │ ├── follows_controller_test.rb │ ├── api │ │ ├── chirps_controller_test.rb │ │ ├── likes_controller_test.rb │ │ ├── users_controller_test.rb │ │ └── sessions_controller_test.rb │ ├── root_controller_test.rb │ └── sessions_controller_test.rb ├── fixtures │ ├── .keep │ ├── files │ │ └── .keep │ ├── chirps.yml │ ├── likes.yml │ └── follows.yml ├── integration │ └── .keep ├── application_system_test_case.rb └── test_helper.rb ├── wiki ├── .keep ├── bluebird_user_search.png ├── bluebird_session_page.png ├── bluebird_chirp_form_page.png ├── bluebird_dashboard_page.png ├── bluebird_show_chirp_page.png ├── bluebird_show_chirp_page.xml ├── bluebird_chirp_form_page.xml ├── bluebird_session_page.xml ├── bluebird_dashboard_page.xml └── bluebird_user_search.xml ├── app ├── assets │ ├── images │ │ └── .keep │ ├── javascripts │ │ ├── channels │ │ │ └── .keep │ │ ├── likes.coffee │ │ ├── root.coffee │ │ ├── users.coffee │ │ ├── api │ │ │ ├── chirps.coffee │ │ │ ├── likes.coffee │ │ │ ├── users.coffee │ │ │ └── sessions.coffee │ │ ├── chirps.coffee │ │ ├── follows.coffee │ │ ├── sessions.coffee │ │ ├── cable.js │ │ └── application.js │ ├── config │ │ └── manifest.js │ └── stylesheets │ │ ├── chirps.scss │ │ ├── likes.scss │ │ ├── root.scss │ │ ├── users.scss │ │ ├── follows.scss │ │ ├── sessions.scss │ │ ├── api │ │ ├── chirps.scss │ │ ├── likes.scss │ │ ├── users.scss │ │ └── sessions.scss │ │ └── application.scss ├── models │ ├── concerns │ │ └── .keep │ ├── application_record.rb │ ├── follow.rb │ ├── chirp.rb │ ├── like.rb │ └── user.rb ├── controllers │ ├── concerns │ │ └── .keep │ ├── root_controller.rb │ ├── follows_controller.rb │ ├── api │ │ ├── sessions_controller.rb │ │ ├── likes_controller.rb │ │ ├── chirps_controller.rb │ │ └── users_controller.rb │ ├── likes_controller.rb │ ├── sessions_controller.rb │ ├── application_controller.rb │ ├── chirps_controller.rb │ └── users_controller.rb ├── views │ ├── layouts │ │ ├── mailer.text.erb │ │ ├── _errors.html.erb │ │ ├── mailer.html.erb │ │ └── application.html.erb │ ├── api │ │ ├── users │ │ │ └── show.json.jbuilder │ │ └── chirps │ │ │ ├── show.json.jbuilder │ │ │ ├── index.json.jbuilder │ │ │ └── _chirp.json.jbuilder │ └── root │ │ └── root.html.erb ├── helpers │ ├── likes_helper.rb │ ├── root_helper.rb │ ├── users_helper.rb │ ├── chirps_helper.rb │ ├── follows_helper.rb │ ├── api │ │ ├── likes_helper.rb │ │ ├── users_helper.rb │ │ ├── chirps_helper.rb │ │ └── sessions_helper.rb │ ├── sessions_helper.rb │ └── application_helper.rb ├── jobs │ └── application_job.rb ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb └── mailers │ └── application_mailer.rb ├── bin ├── bundle ├── rake ├── rails ├── yarn ├── spring ├── update └── setup ├── config ├── spring.rb ├── boot.rb ├── environment.rb ├── initializers │ ├── mime_types.rb │ ├── application_controller_renderer.rb │ ├── filter_parameter_logging.rb │ ├── cookies_serializer.rb │ ├── backtrace_silencers.rb │ ├── wrap_parameters.rb │ ├── assets.rb │ └── inflections.rb ├── cable.yml ├── routes.rb ├── application.rb ├── locales │ └── en.yml ├── secrets.yml ├── environments │ ├── test.rb │ ├── development.rb │ └── production.rb ├── puma.rb └── database.yml ├── config.ru ├── db ├── migrate │ ├── 20170816171327_add_index_to_chirp.rb │ ├── 20170816164052_add_email_to_users.rb │ ├── 20170816162825_create_users.rb │ ├── 20170823202541_add_stuff_to_users.rb │ ├── 20170816174156_create_likes.rb │ ├── 20170816170838_create_chirps.rb │ └── 20170828221731_create_follows.rb ├── seeds.rb └── schema.rb ├── frontend ├── reducers │ ├── entities.js │ ├── root.js │ ├── session.js │ └── chirps.js ├── components │ ├── nav_bar │ │ ├── welcome_bar.jsx │ │ ├── welcome_bar_container.jsx │ │ ├── nav_bar_container.jsx │ │ └── nav_bar.jsx │ ├── root.jsx │ ├── home │ │ └── home.jsx │ ├── session │ │ ├── signup_container.jsx │ │ ├── login_container.jsx │ │ ├── login.jsx │ │ └── signup.jsx │ ├── chirps │ │ ├── chirp_item.jsx │ │ ├── chirp_index_container.jsx │ │ └── chirp_index.jsx │ └── app.jsx ├── thunk │ └── thunk.js ├── store │ └── store.js ├── utils │ ├── chirps.js │ ├── session.js │ └── route_util.jsx ├── bluebird.jsx └── actions │ ├── chirps.js │ └── session.js ├── Rakefile ├── README.md ├── .gitignore ├── test.js ├── webpack.config.js ├── package.json ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/system/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /wiki/.keep: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/javascripts/channels/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /app/helpers/likes_helper.rb: -------------------------------------------------------------------------------- 1 | module LikesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/root_helper.rb: -------------------------------------------------------------------------------- 1 | module RootHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/chirps_helper.rb: -------------------------------------------------------------------------------- 1 | module ChirpsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/follows_helper.rb: -------------------------------------------------------------------------------- 1 | module FollowsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/api/likes_helper.rb: -------------------------------------------------------------------------------- 1 | module Api::LikesHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/api/users_helper.rb: -------------------------------------------------------------------------------- 1 | module Api::UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/api/chirps_helper.rb: -------------------------------------------------------------------------------- 1 | module Api::ChirpsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/api/sessions_helper.rb: -------------------------------------------------------------------------------- 1 | module Api::SessionsHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/views/api/users/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! @user, :username, :id, :email 2 | -------------------------------------------------------------------------------- /app/views/root/root.html.erb: -------------------------------------------------------------------------------- 1 |
2 |

No Reaction

3 |
4 | -------------------------------------------------------------------------------- /app/views/api/chirps/show.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.partial! 'api/chirps/chirp', chirp: @chirp 2 | -------------------------------------------------------------------------------- /wiki/bluebird_user_search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appacademy/bluebird/HEAD/wiki/bluebird_user_search.png -------------------------------------------------------------------------------- /app/controllers/root_controller.rb: -------------------------------------------------------------------------------- 1 | class RootController < ApplicationController 2 | def root 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /wiki/bluebird_session_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appacademy/bluebird/HEAD/wiki/bluebird_session_page.png -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | -------------------------------------------------------------------------------- /wiki/bluebird_chirp_form_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appacademy/bluebird/HEAD/wiki/bluebird_chirp_form_page.png -------------------------------------------------------------------------------- /wiki/bluebird_dashboard_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appacademy/bluebird/HEAD/wiki/bluebird_dashboard_page.png -------------------------------------------------------------------------------- /wiki/bluebird_show_chirp_page.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/appacademy/bluebird/HEAD/wiki/bluebird_show_chirp_page.png -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../javascripts .js 3 | //= link_directory ../stylesheets .css 4 | -------------------------------------------------------------------------------- /app/views/api/chirps/index.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.array! @chirps do |chirp| 2 | json.partial! 'api/chirps/chirp', chirp: chirp 3 | end 4 | 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /db/migrate/20170816171327_add_index_to_chirp.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToChirp < ActiveRecord::Migration[5.1] 2 | def change 3 | add_index :chirps, :author_id 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20170816164052_add_email_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddEmailToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :email, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/layouts/_errors.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /frontend/reducers/entities.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import chirpsReducer from './chirps'; 3 | 4 | export default combineReducers({ 5 | chirps: chirpsReducer 6 | }); 7 | -------------------------------------------------------------------------------- /app/views/api/chirps/_chirp.json.jbuilder: -------------------------------------------------------------------------------- 1 | json.extract! chirp, :id, :body, :author_id 2 | json.likes chirp.likes.count 3 | json.liked_by_current_user !!chirp.likes.find_by(user_id: current_user.id) 4 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /test/controllers/chirps_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class ChirpsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/likes_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class LikesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/application_system_test_case.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ApplicationSystemTestCase < ActionDispatch::SystemTestCase 4 | driven_by :selenium, using: :chrome, screen_size: [1400, 1400] 5 | end 6 | -------------------------------------------------------------------------------- /test/controllers/follows_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class FollowsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/api/chirps_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::ChirpsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/api/likes_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::LikesControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/controllers/api/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::UsersControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/chirps.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the chirps controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/likes.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the likes controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/root.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the root controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | channel_prefix: BlueBird_production 11 | -------------------------------------------------------------------------------- /test/controllers/api/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class Api::SessionsControllerTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/follows.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the Follows controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/api/chirps.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the API::Chirps controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/api/likes.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the API::Likes controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/api/users.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the API::Users controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /app/assets/stylesheets/api/sessions.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the API::Sessions controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /frontend/components/nav_bar/welcome_bar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({ user }) => ( 4 |
5 |

Bluebird

6 |

Welcome { user.username }!

7 |
8 | ); 9 | -------------------------------------------------------------------------------- /frontend/thunk/thunk.js: -------------------------------------------------------------------------------- 1 | const thunk = ({ dispatch, getState }) => next => action => { 2 | if (typeof action === 'function') { 3 | return action(dispatch, getState); 4 | } 5 | return next(action); 6 | }; 7 | 8 | export default thunk; -------------------------------------------------------------------------------- /app/assets/javascripts/likes.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/root.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/users.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /test/controllers/root_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class RootControllerTest < ActionDispatch::IntegrationTest 4 | test "should get root" do 5 | get root_root_url 6 | assert_response :success 7 | end 8 | 9 | end 10 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /app/assets/javascripts/api/chirps.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/api/likes.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/api/users.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/chirps.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/follows.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/sessions.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /app/assets/javascripts/api/sessions.coffee: -------------------------------------------------------------------------------- 1 | # Place all the behaviors and hooks related to the matching controller here. 2 | # All this logic will automatically be available in application.js. 3 | # You can use CoffeeScript in this file: http://coffeescript.org/ 4 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /frontend/reducers/root.js: -------------------------------------------------------------------------------- 1 | import { combineReducers } from 'redux'; 2 | import sessionReducer from './session'; 3 | import entitiesReducer from './entities'; 4 | 5 | export default combineReducers({ 6 | entities: entitiesReducer, 7 | session: sessionReducer 8 | }); -------------------------------------------------------------------------------- /db/migrate/20170816162825_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :users do |t| 4 | t.string :username, null: false 5 | end 6 | 7 | add_index :users, :username, unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20170823202541_add_stuff_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddStuffToUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | add_column :users, :password_digest, :string 4 | add_column :users, :session_token, :string 5 | add_index :users, :session_token 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /db/migrate/20170816174156_create_likes.rb: -------------------------------------------------------------------------------- 1 | class CreateLikes < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :likes do |t| 4 | t.integer :user_id, null: false 5 | t.integer :chirp_id, null: false 6 | t.timestamps 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Specify a serializer for the signed and encrypted cookie jars. 4 | # Valid options are :json, :marshal, and :hybrid. 5 | Rails.application.config.action_dispatch.cookies_serializer = :json 6 | -------------------------------------------------------------------------------- /db/migrate/20170816170838_create_chirps.rb: -------------------------------------------------------------------------------- 1 | class CreateChirps < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :chirps do |t| 4 | t.text :body, null: false 5 | t.integer :author_id, null: false 6 | 7 | t.timestamps 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /frontend/components/nav_bar/welcome_bar_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | import WelcomeBar from './welcome_bar'; 4 | 5 | const mapStateToProps = (state) => ({ 6 | user: state.session.currentUser 7 | }); 8 | 9 | export default connect(mapStateToProps)(WelcomeBar); -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /frontend/components/root.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | import { HashRouter } from 'react-router-dom'; 4 | import App from './app'; 5 | 6 | export default ({store}) => ( 7 | 8 | 9 | 10 | 11 | 12 | ); 13 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../../config/environment', __FILE__) 2 | require 'rails/test_help' 3 | 4 | class ActiveSupport::TestCase 5 | # Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order. 6 | fixtures :all 7 | 8 | # Add more helper methods to be used by all tests here... 9 | end 10 | -------------------------------------------------------------------------------- /bin/yarn: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | VENDOR_PATH = File.expand_path('..', __dir__) 3 | Dir.chdir(VENDOR_PATH) do 4 | begin 5 | exec "yarnpkg #{ARGV.join(" ")}" 6 | rescue Errno::ENOENT 7 | $stderr.puts "Yarn executable was not detected in the system." 8 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install" 9 | exit 1 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /frontend/components/home/home.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default () => ( 4 |
5 |
6 | 7 |
8 |

Where birds can chirp.

9 |
10 | ) 11 | -------------------------------------------------------------------------------- /db/migrate/20170828221731_create_follows.rb: -------------------------------------------------------------------------------- 1 | class CreateFollows < ActiveRecord::Migration[5.1] 2 | def change 3 | create_table :follows do |t| 4 | t.integer :follower_id, null: false 5 | t.integer :followee_id, null: false 6 | t.timestamps 7 | end 8 | 9 | add_index :follows, :follower_id 10 | add_index :follows, :followee_id 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /frontend/components/session/signup_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { createNewUser } from '../../actions/session'; 4 | import Signup from './signup'; 5 | 6 | const mapDispatchToProps = (dispatch) => ({ 7 | createNewUser: formUser => dispatch(createNewUser(formUser)), 8 | }); 9 | 10 | export default connect(undefined, mapDispatchToProps)(Signup); 11 | -------------------------------------------------------------------------------- /frontend/components/session/login_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { login } from '../../actions/session'; 4 | import Login from './login'; 5 | import { withRouter } from 'react-router-dom'; 6 | 7 | const mapDispatchToProps = (dispatch) => ({ 8 | login: formUser => dispatch(login(formUser)), 9 | }); 10 | 11 | export default connect(undefined, mapDispatchToProps)(Login); 12 | -------------------------------------------------------------------------------- /app/assets/javascripts/cable.js: -------------------------------------------------------------------------------- 1 | // Action Cable provides the framework to deal with WebSockets in Rails. 2 | // You can generate new channels where WebSocket features live using the `rails generate channel` command. 3 | // 4 | //= require action_cable 5 | //= require_self 6 | //= require_tree ./channels 7 | 8 | (function() { 9 | this.App || (this.App = {}); 10 | 11 | App.cable = ActionCable.createConsumer(); 12 | 13 | }).call(this); 14 | -------------------------------------------------------------------------------- /frontend/store/store.js: -------------------------------------------------------------------------------- 1 | import { createStore, applyMiddleware } from 'redux'; 2 | import { composeWithDevTools } from 'redux-devtools-extension'; 3 | import { createLogger } from 'redux-logger'; 4 | 5 | import rootReducer from '../reducers/root'; 6 | import thunk from '../thunk/thunk'; 7 | 8 | export default (preloadedState = {}) => createStore( 9 | rootReducer, preloadedState, 10 | composeWithDevTools(applyMiddleware(createLogger(), thunk)) 11 | ); 12 | -------------------------------------------------------------------------------- /frontend/utils/chirps.js: -------------------------------------------------------------------------------- 1 | export const getChirps = () => { 2 | return $.ajax({ 3 | url: '/api/chirps' 4 | }); 5 | } 6 | 7 | export const postLikeToChirp = id => { 8 | return $.ajax({ 9 | url: '/api/likes', 10 | method: 'POST', 11 | data: { id } 12 | }); 13 | } 14 | 15 | export const deleteLikeFromChirp = id => { 16 | return $.ajax({ 17 | url: '/api/likes', 18 | method: 'DELETE', 19 | data: { id } 20 | }); 21 | } 22 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /frontend/components/nav_bar/nav_bar_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import {connect} from 'react-redux'; 3 | import NavBar from './nav_bar'; 4 | import { logout } from '../../actions/session'; 5 | 6 | const mapStateToProps = (state) => ({ 7 | currentUser: state.session.currentUser 8 | }); 9 | 10 | const mapDispatchToProps = (dispatch) => ({ 11 | logout: () => dispatch(logout()) 12 | }) 13 | 14 | export default connect(mapStateToProps, mapDispatchToProps)(NavBar); -------------------------------------------------------------------------------- /test/models/like_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: likes 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # chirp_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class LikeTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /test/controllers/sessions_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | class SessionsControllerTest < ActionDispatch::IntegrationTest 4 | test "should get new" do 5 | get sessions_new_url 6 | assert_response :success 7 | end 8 | 9 | test "should get create" do 10 | get sessions_create_url 11 | assert_response :success 12 | end 13 | 14 | test "should get destroy" do 15 | get sessions_destroy_url 16 | assert_response :success 17 | end 18 | 19 | end 20 | -------------------------------------------------------------------------------- /test/models/chirp_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: chirps 4 | # 5 | # id :integer not null, primary key 6 | # body :text not null 7 | # author_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class ChirpTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /test/models/follow_test.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: follows 4 | # 5 | # id :integer not null, primary key 6 | # follower_id :integer not null 7 | # followee_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | require 'test_helper' 13 | 14 | class FollowTest < ActiveSupport::TestCase 15 | # test "the truth" do 16 | # assert true 17 | # end 18 | end 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Blue Bird 2 | 3 | ### July 31, 2017 App Academy Cohort 4 | 5 | ![blue-bird](https://media.giphy.com/media/3oKIPmJonGimU9bI2s/giphy.gif) 6 | 7 | ##### W3D3 8 | 9 | + Added bare bones migration files for Users, Chirps, and Likes 10 | + Added basic model validations, associations and custom validations for the above tables 11 | 12 | ##### W4D1 13 | 14 | + Added routes and controllers for Users, Chirps, and Likes 15 | 16 | ##### W4D2 17 | 18 | + Added views for Users and Chirps (delaying likes until auth) 19 | -------------------------------------------------------------------------------- /app/controllers/follows_controller.rb: -------------------------------------------------------------------------------- 1 | class FollowsController < ApplicationController 2 | def create 3 | @follow = Follow.new 4 | @follow.followee_id = params[:id] 5 | @follow.follower_id = current_user.id 6 | unless @follow.save 7 | flash[:errors] = @follow.errors.full_messages 8 | end 9 | redirect_to user_url(params[:id]) 10 | end 11 | 12 | def destroy 13 | @follow = Follow.find(params[:id]) 14 | @follow.destroy 15 | redirect_to user_url(@follow.followee_id) 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/api/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::SessionsController < ApplicationController 2 | def create 3 | # Find user by credentials 4 | @user = User.find_by_credentials(params[:user][:username], params[:user][:password]) 5 | if @user.nil? 6 | render json: ['Nope. Wrong credentials!'], status: 401 7 | else 8 | login!(@user) 9 | render 'api/users/show'; 10 | end 11 | end 12 | 13 | def destroy 14 | logout! 15 | render json: { message: 'Logout successful.' } 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /frontend/reducers/session.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_CURRENT_USER, LOGOUT_CURRENT_USER } from '../actions/session'; 2 | 3 | const _nullSession = { 4 | currentUser: null 5 | }; 6 | 7 | export default (state = _nullSession, action) => { 8 | Object.freeze(state); 9 | switch(action.type) { 10 | case RECEIVE_CURRENT_USER: 11 | const currentUser = action.user 12 | return Object.assign({}, { currentUser }); 13 | case LOGOUT_CURRENT_USER: 14 | return _nullSession; 15 | default: 16 | return state; 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | /node_modules 17 | /yarn-error.log 18 | 19 | .byebug_history 20 | -------------------------------------------------------------------------------- /test.js: -------------------------------------------------------------------------------- 1 | const stateShape = { 2 | entities: { 3 | images: { 4 | byId: { 5 | 1: { 6 | id: 1, 7 | image_url: 'something.com', 8 | tagIds: [1] 9 | }, 10 | 2: { 11 | id: 2, 12 | image_url: 'something.com', 13 | } 14 | }, 15 | ids: [2, 1] 16 | }, 17 | tags: { 18 | byId: { 19 | 1: { 20 | id: 1, 21 | name: 'themed tag' 22 | } 23 | }, 24 | ids: [1] 25 | } 26 | }, 27 | session: {} 28 | }; 29 | -------------------------------------------------------------------------------- /frontend/reducers/chirps.js: -------------------------------------------------------------------------------- 1 | import { RECEIVE_CHIRPS, RECEIVE_SINGLE_CHIRP } from '../actions/chirps'; 2 | 3 | export default (state = {}, action) => { 4 | Object.freeze(state); 5 | switch (action.type) { 6 | case RECEIVE_CHIRPS: 7 | const chirps = {}; 8 | action.chirps.forEach(chirp => { 9 | chirps[chirp.id] = chirp; 10 | }); 11 | return chirps; 12 | case RECEIVE_SINGLE_CHIRP: 13 | return Object.assign({}, state, { [action.chirp.id]: action.chirp }); 14 | default: 15 | return state; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | 3 | module.exports = { 4 | entry: './frontend/bluebird.jsx', 5 | output: { 6 | path: path.resolve(__dirname, 'app', 'assets', 'javascripts'), 7 | filename: 'bundle.js' 8 | }, 9 | module: { 10 | loaders: [ 11 | { 12 | test: [/\.jsx?$/], 13 | exclude: /node_modules/, 14 | loader: 'babel-loader', 15 | query: { 16 | presets: ['es2015', 'react'] 17 | } 18 | } 19 | ] 20 | }, 21 | resolve: { 22 | extensions: ['.js', '.jsx', '*'], 23 | } 24 | }; 25 | -------------------------------------------------------------------------------- /app/controllers/api/likes_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::LikesController < ApplicationController 2 | def create 3 | @like = Like.new 4 | @like.user_id = current_user.id 5 | @like.chirp_id = params[:id] 6 | if @like.save 7 | @chirp = @like.chirp 8 | render 'api/chirps/show' 9 | else 10 | render json: @like.errors.full_messages, status: 401 11 | end 12 | end 13 | 14 | def destroy 15 | @like = Like.find_by(user_id: current_user.id, chirp_id: params[:id]) 16 | @like.destroy 17 | @chirp = @like.chirp 18 | render 'api/chirps/show' 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /frontend/components/chirps/chirp_item.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default ({ chirp, likeChirp, unLikeChirp }) => { 4 | let likeButtonText = "You don't like this."; 5 | let likeButtonAction = () => likeChirp(chirp.id); 6 | if (chirp.liked_by_current_user) { 7 | likeButtonText = "You like this"; 8 | likeButtonAction = () => unLikeChirp(chirp.id); 9 | } 10 | return ( 11 |
  • 12 |

    {chirp.body}

    13 |

    Likes: {chirp.likes}

    14 | 15 |
  • 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "BlueBird", 3 | "private": true, 4 | "dependencies": { 5 | "babel-core": "^6.26.0", 6 | "babel-loader": "^7.1.2", 7 | "babel-preset-es2015": "^6.24.1", 8 | "babel-preset-react": "^6.24.1", 9 | "react": "^15.6.1", 10 | "react-dom": "^15.6.1", 11 | "react-redux": "^5.0.6", 12 | "react-router-dom": "^4.2.2", 13 | "redux": "^3.7.2", 14 | "redux-devtools-extension": "^2.13.2", 15 | "redux-logger": "^3.0.6", 16 | "webpack": "^3.5.6" 17 | }, 18 | "devDependencies": { 19 | "redux-devtools-extension": "^2.13.2" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/models/follow.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: follows 4 | # 5 | # id :integer not null, primary key 6 | # follower_id :integer not null 7 | # followee_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class Follow < ApplicationRecord 13 | 14 | belongs_to :follower, 15 | primary_key: :id, 16 | foreign_key: :follower_id, 17 | class_name: :User 18 | 19 | belongs_to :followee, 20 | primary_key: :id, 21 | foreign_key: :followee_id, 22 | class_name: :User 23 | end 24 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 4 | namespace :api, defaults: { format: :json } do 5 | resources :users do 6 | resources :chirps, only: [:index] 7 | end 8 | 9 | resource :session, only: [:new, :create, :destroy] 10 | 11 | post '/search', to: 'users#search' 12 | 13 | resources :chirps 14 | resources :likes, only: [:create] 15 | delete '/likes', to: 'likes#destroy' 16 | resources :follows, only: [:create, :destroy] 17 | end 18 | root to: 'root#root' 19 | end 20 | -------------------------------------------------------------------------------- /frontend/bluebird.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import createStore from './store/store'; 4 | import Root from './components/root'; 5 | 6 | document.addEventListener('DOMContentLoaded', () => { 7 | const root = document.getElementById('root'); 8 | let preloadedState = undefined; 9 | if (window.currentUser) { 10 | preloadedState = { 11 | session: { 12 | currentUser: window.currentUser 13 | } 14 | }; 15 | } 16 | const store = createStore(preloadedState); 17 | // const store = createStore(); 18 | 19 | ReactDOM.render(, root); 20 | }) -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | BlueBird 5 | <%= csrf_meta_tags %> 6 | 7 | <%= stylesheet_link_tag 'application', media: 'all' %> 8 | <%= javascript_include_tag 'application' %> 9 | <% if current_user %> 10 | 17 | <% end %> 18 | 19 | 20 | 21 | <%= yield %> 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/controllers/likes_controller.rb: -------------------------------------------------------------------------------- 1 | # This class doesn't make a whole lot of sense unless there's a logged 2 | # in user. We're going to go ahead and skip the views for this for now. 3 | 4 | class LikesController < ApplicationController 5 | def create 6 | @like = Like.new 7 | @like.user_id = current_user.id 8 | @like.chirp_id = params[:id] 9 | unless @like.save 10 | flash[:errors] = @like.errors.full_messages 11 | end 12 | redirect_to chirp_url(params[:id]) 13 | end 14 | 15 | def destroy 16 | @like = Like.find(params[:id]) 17 | @like.destroy 18 | redirect_to chirp_url(@like.chirp_id) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /frontend/components/chirps/chirp_index_container.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ChirpIndex from './chirp_index'; 3 | import { fetchChirps, likeChirp, unLikeChirp } from '../../actions/chirps'; 4 | import { connect } from 'react-redux'; 5 | 6 | const mapStateToProps = (state) => ({ 7 | chirps: Object.keys(state.entities.chirps).map(key => state.entities.chirps[key]) 8 | }); 9 | 10 | const mapDispatchToProps = (dispatch) => ({ 11 | fetchChirps: () => dispatch(fetchChirps()), 12 | likeChirp: id => dispatch(likeChirp(id)), 13 | unLikeChirp: id => dispatch(unLikeChirp(id)) 14 | }); 15 | 16 | export default connect(mapStateToProps, mapDispatchToProps)(ChirpIndex); 17 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | module BlueBird 10 | class Application < Rails::Application 11 | # Initialize configuration defaults for originally generated Rails version. 12 | config.load_defaults 5.1 13 | 14 | # Settings in config/environments/* take precedence over those specified here. 15 | # Application configuration should go into files in config/initializers 16 | # -- all .rb files in that directory are automatically loaded. 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /frontend/components/nav_bar/nav_bar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | export default ({ currentUser, logout }) => { 5 | const display = currentUser ? ( 6 |
    7 |

    Welcome {currentUser.username}!

    8 | 9 |
    10 | ) : ( 11 |
    12 | Sign Up 13 | Log In 14 |
    15 | ); 16 | return ( 17 |
    18 |

    BLUEBIRD

    19 |
    20 | {display} 21 |
    22 |
    23 | ) 24 | } 25 | -------------------------------------------------------------------------------- /config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = '1.0' 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | # Add Yarn node_modules folder to the asset load path. 9 | Rails.application.config.assets.paths << Rails.root.join('node_modules') 10 | 11 | # Precompile additional assets. 12 | # application.js, application.css, and all non-JS/CSS in the app/assets 13 | # folder are already added. 14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 15 | -------------------------------------------------------------------------------- /test/fixtures/chirps.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: chirps 4 | # 5 | # id :integer not null, primary key 6 | # body :text not null 7 | # author_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 13 | 14 | # This model initially had no columns defined. If you add columns to the 15 | # model remove the '{}' from the fixture names and add the columns immediately 16 | # below each fixture, per the syntax in the comments below 17 | # 18 | one: {} 19 | # column: value 20 | # 21 | two: {} 22 | # column: value 23 | -------------------------------------------------------------------------------- /test/fixtures/likes.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: likes 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # chirp_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 13 | 14 | # This model initially had no columns defined. If you add columns to the 15 | # model remove the '{}' from the fixture names and add the columns immediately 16 | # below each fixture, per the syntax in the comments below 17 | # 18 | one: {} 19 | # column: value 20 | # 21 | two: {} 22 | # column: value 23 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /test/fixtures/follows.yml: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: follows 4 | # 5 | # id :integer not null, primary key 6 | # follower_id :integer not null 7 | # followee_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html 13 | 14 | # This model initially had no columns defined. If you add columns to the 15 | # model remove the '{}' from the fixture names and add the columns immediately 16 | # below each fixture, per the syntax in the comments below 17 | # 18 | one: {} 19 | # column: value 20 | # 21 | two: {} 22 | # column: value 23 | -------------------------------------------------------------------------------- /frontend/utils/session.js: -------------------------------------------------------------------------------- 1 | export const postUser = (user) => { 2 | return $.ajax({ 3 | url: '/api/users', 4 | method: 'POST', 5 | data: { user } 6 | }); 7 | }; 8 | 9 | // const user = { 10 | // id: 1, 11 | // username: "", 12 | // email: "", 13 | // password: "password" 14 | // } 15 | 16 | // const data = { 17 | // user: { 18 | // id: 1, 19 | // username: "", 20 | // email: "", 21 | // password: "password" 22 | // } 23 | // } 24 | 25 | export const postSession = (user) => { 26 | return $.ajax({ 27 | url: '/api/session', 28 | method: 'POST', 29 | data: { user } 30 | }); 31 | }; 32 | 33 | export const deleteSession = () => { 34 | return $.ajax({ 35 | url: '/api/session', 36 | method: 'DELETE' 37 | }); 38 | }; 39 | -------------------------------------------------------------------------------- /app/controllers/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class SessionsController < ApplicationController 2 | def new 3 | render :new 4 | end 5 | 6 | def create 7 | # Find user by credentials 8 | @user = User.find_by_credentials(params[:user][:username], params[:user][:password]) 9 | # Flash errors, if any. 10 | # Render :new if invalid credentials (give the user another chance to login) 11 | if @user.nil? 12 | flash.now[:errors] = ['Invalid username or password.'] 13 | render :new 14 | else 15 | # Log them in and redirect them if we find them 16 | login!(@user) 17 | redirect_to user_url(@user) 18 | end 19 | 20 | end 21 | 22 | def destroy 23 | logout! 24 | # redirect to login page 25 | redirect_to new_session_url 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, or any plugin's 5 | // vendor/assets/javascripts directory can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. JavaScript code in this file should be added after the last require_* statement. 9 | // 10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require rails-ujs 16 | //= require_tree . 17 | -------------------------------------------------------------------------------- /frontend/components/chirps/chirp_index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ChirpItem from './chirp_item'; 3 | 4 | class ChirpIndex extends React.Component { 5 | constructor(props) { 6 | super(props); 7 | } 8 | 9 | componentDidMount() { 10 | this.props.fetchChirps(); 11 | } 12 | 13 | render() { 14 | const { chirps } = this.props; 15 | return ( 16 |
    17 | 29 |
    30 | ) 31 | } 32 | } 33 | 34 | export default ChirpIndex; 35 | -------------------------------------------------------------------------------- /frontend/utils/route_util.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { connect } from 'react-redux'; 3 | import { Redirect, Route, withRouter} from 'react-router-dom'; 4 | 5 | const mapStateToProps = state => ({ 6 | loggedIn: Boolean(state.session.currentUser) 7 | }); 8 | 9 | const Auth = ({ component: Component, path, loggedIn }) => ( 10 | ( 11 | loggedIn ? : 12 | )} /> 13 | ); 14 | 15 | const Protected = ({ component: Component, path, loggedIn }) => ( 16 | ( 17 | loggedIn ? : 18 | )} /> 19 | ); 20 | 21 | export const AuthRoute = withRouter(connect(mapStateToProps)(Auth)); 22 | export const ProtectedRoute = withRouter(connect(mapStateToProps, undefined)(Protected)); 23 | -------------------------------------------------------------------------------- /app/models/chirp.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: chirps 4 | # 5 | # id :integer not null, primary key 6 | # body :text not null 7 | # author_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class Chirp < ApplicationRecord 13 | validates :body, presence: true 14 | validate :chirp_too_long 15 | 16 | has_many :likes, 17 | primary_key: :id, 18 | foreign_key: :chirp_id, 19 | class_name: :Like 20 | 21 | has_many :likers, 22 | through: :likes, 23 | source: :user 24 | 25 | belongs_to :author, 26 | primary_key: :id, 27 | foreign_key: :author_id, 28 | class_name: :User 29 | 30 | def chirp_too_long 31 | if body.length > 140 32 | errors[:body] << "too long" 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/models/like.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: likes 4 | # 5 | # id :integer not null, primary key 6 | # user_id :integer not null 7 | # chirp_id :integer not null 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class Like < ApplicationRecord 13 | # This is actually redundant since Rails 5+ will automatically validate 14 | # belongs to relationships, and the foreign keys will be implicitly 15 | # checked as a result 16 | validates :user_id, :chirp_id, presence: true 17 | 18 | belongs_to :chirp, 19 | primary_key: :id, 20 | foreign_key: :chirp_id, 21 | class_name: :Chirp 22 | # optional: true 23 | 24 | belongs_to :user, 25 | primary_key: :id, 26 | foreign_key: :user_id, 27 | class_name: :User 28 | # optional: true 29 | end 30 | -------------------------------------------------------------------------------- /frontend/actions/chirps.js: -------------------------------------------------------------------------------- 1 | export const RECEIVE_CHIRPS = 'RECEIVE_CHIRPS'; 2 | export const RECEIVE_SINGLE_CHIRP = 'RECEIVE_SINGLE_CHIRP'; 3 | import { getChirps, postLikeToChirp, deleteLikeFromChirp } from '../utils/chirps'; 4 | 5 | 6 | const receiveChirps = chirps => ({ 7 | type: RECEIVE_CHIRPS, 8 | chirps 9 | }); 10 | 11 | const receiveSingleChirp = chirp => ({ 12 | type: RECEIVE_SINGLE_CHIRP, 13 | chirp 14 | }); 15 | 16 | export const fetchChirps = () => dispatch => { 17 | return getChirps() 18 | .then(chirps => dispatch(receiveChirps(chirps))); 19 | } 20 | 21 | export const likeChirp = id => dispatch => { 22 | return postLikeToChirp(id) 23 | .then(chirp => dispatch(receiveSingleChirp(chirp))); 24 | } 25 | 26 | export const unLikeChirp = id => dispatch => { 27 | return deleteLikeFromChirp(id) 28 | .then(chirp => dispatch(receiveSingleChirp(chirp))); 29 | } 30 | -------------------------------------------------------------------------------- /frontend/components/app.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WelcomeBar from './nav_bar/welcome_bar_container'; 3 | import NavBarContainer from './nav_bar/nav_bar_container'; 4 | import SignupContainer from './session/signup_container'; 5 | import LoginContainer from './session/login_container'; 6 | import ChirpIndexContainer from './chirps/chirp_index_container'; 7 | import Home from './home/home'; 8 | import { Route } from 'react-router-dom'; 9 | import { AuthRoute, ProtectedRoute } from '../utils/route_util'; 10 | 11 | export default () => ( 12 |
    13 | 14 | 15 | 16 | 17 | 18 |
    19 | ); 20 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /frontend/actions/session.js: -------------------------------------------------------------------------------- 1 | import { postUser, deleteSession, postSession } from '../utils/session'; 2 | 3 | export const RECEIVE_CURRENT_USER = "RECEIVE_CURRENT_USER"; 4 | 5 | export const LOGOUT_CURRENT_USER = "LOGOUT_CURRENT_USER"; 6 | 7 | const receiveCurrentUser = user => ({ 8 | type: RECEIVE_CURRENT_USER, 9 | user 10 | }); 11 | 12 | // const user = { 13 | // id: 1, 14 | // username: "", 15 | // email: "" 16 | // } 17 | 18 | const logoutCurrentUser = () => ({ 19 | type: LOGOUT_CURRENT_USER 20 | }); 21 | 22 | export const createNewUser = formUser => dispatch => { 23 | return postUser(formUser) 24 | .then(user => dispatch(receiveCurrentUser(user))); 25 | }; 26 | 27 | export const login = formUser => dispatch => { 28 | return postSession(formUser) 29 | .then(user => dispatch(receiveCurrentUser(user))); 30 | }; 31 | 32 | export const logout = () => dispatch => { 33 | return deleteSession() 34 | .then(() => dispatch(logoutCurrentUser())); 35 | }; 36 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # The following keys must be escaped otherwise they will not be retrieved by 20 | # the default I18n backend: 21 | # 22 | # true, false, on, off, yes, no 23 | # 24 | # Instead, surround them with single quotes. 25 | # 26 | # en: 27 | # 'true': 'foo' 28 | # 29 | # To learn more, please read the Rails Internationalization guide 30 | # available at http://guides.rubyonrails.org/i18n.html. 31 | 32 | en: 33 | hello: "Hello world" 34 | -------------------------------------------------------------------------------- /app/controllers/api/chirps_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::ChirpsController < ApplicationController 2 | def create 3 | @chirp = Chirp.new(chirp_params) 4 | if @chirp.save 5 | render :show 6 | else 7 | render @chirp.errors.full_messages, status: 401 8 | end 9 | end 10 | 11 | def update 12 | @chirp = selected_chirp 13 | if @chirp && @chirp.update_attributes(chirp_params) 14 | render :show 15 | elsif !@chirp 16 | render json: ['Could not locate chirp'], status: 400 17 | else 18 | render json: @chirp.errors.full_messages, status: 401 19 | end 20 | end 21 | 22 | def show 23 | @chirp = selected_chirp 24 | end 25 | 26 | def index 27 | @chirps = Chirp.all 28 | end 29 | 30 | def destroy 31 | @chirp = selected_chirp 32 | if @chirp 33 | @chirp.destroy 34 | render :show 35 | else 36 | render ['Could not find chirp'] 37 | end 38 | end 39 | 40 | private 41 | 42 | def selected_chirp 43 | Chirp.find_by(params[:id]) 44 | end 45 | 46 | def chirp_params 47 | params.require(:chirp).permit(:body, :author_id) 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # Install JavaScript dependencies if using Yarn 22 | # system('bin/yarn') 23 | 24 | 25 | # puts "\n== Copying sample files ==" 26 | # unless File.exist?('config/database.yml') 27 | # cp 'config/database.yml.sample', 'config/database.yml' 28 | # end 29 | 30 | puts "\n== Preparing database ==" 31 | system! 'bin/rails db:setup' 32 | 33 | puts "\n== Removing old logs and tempfiles ==" 34 | system! 'bin/rails log:clear tmp:clear' 35 | 36 | puts "\n== Restarting application server ==" 37 | system! 'bin/rails restart' 38 | end 39 | -------------------------------------------------------------------------------- /app/controllers/api/users_controller.rb: -------------------------------------------------------------------------------- 1 | class Api::UsersController < ApplicationController 2 | def create 3 | @user = User.new(user_params) 4 | if @user.save 5 | login!(@user) 6 | render :show 7 | else 8 | render json: @user.errors.full_messages, status: 401 9 | end 10 | end 11 | 12 | def update 13 | @user = selected_user 14 | if @user && @user.update_attributes(user_params) 15 | render :show 16 | elsif !@user 17 | render json: ['Could not locate user'], status: 400 18 | else 19 | render json: @user.errors.full_messages, status: 401 20 | end 21 | end 22 | 23 | def show 24 | @user = selected_user 25 | end 26 | 27 | def index 28 | @users = User.all 29 | end 30 | 31 | def destroy 32 | @user = selected_user 33 | if @user 34 | @user.destroy 35 | render :show 36 | else 37 | render ['Could not find user'] 38 | end 39 | end 40 | 41 | private 42 | 43 | def selected_user 44 | User.find(params[:id]) 45 | end 46 | 47 | def user_params 48 | params.require(:user).permit(:username, :email, :password) 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /frontend/components/session/login.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class Login extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | username: "", 8 | password: "", 9 | }; 10 | 11 | this.handleSubmit = this.handleSubmit.bind(this); 12 | } 13 | 14 | handleInput(type) { 15 | return (e) => { 16 | this.setState({ [type]: e.target.value }); 17 | } 18 | } 19 | 20 | handleSubmit(e) { 21 | e.preventDefault(); 22 | this.props.login(this.state) 23 | .then(() => this.props.history.push('/chirps')); 24 | } 25 | 26 | render() { 27 | // console.log(this.props); 28 | return ( 29 |
    30 |

    Log In!

    31 |
    32 | 36 | 37 | 43 |
    44 |
    45 | ) 46 | } 47 | } 48 | 49 | export default Login; 50 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # This will be on the assessment 3 | # protect_from_forgery with: :exception 4 | 5 | # make these methods available from within the views 6 | helper_method :current_user, :logged_in? 7 | 8 | def login!(user) 9 | # set the session_token for the connection to be the 10 | session[:session_token] = user.session_token 11 | end 12 | 13 | def logout! 14 | # Scramble the current_user's session_token 15 | current_user.reset_session_token! 16 | 17 | # Reset the session 18 | session[:session_token] = nil 19 | end 20 | 21 | def current_user 22 | # Check for session_token 23 | return nil unless session[:session_token] 24 | 25 | # Return the user associated with the session_token (if token is valid) 26 | @current_user ||= User.find_by_session_token(session[:session_token]) 27 | end 28 | 29 | def logged_in? 30 | !current_user.nil? 31 | end 32 | 33 | def require_logged_out 34 | # Prevent logged-in users from seeing certain pages 35 | redirect_to user_url(current_user) if logged_in? 36 | end 37 | 38 | def require_logged_in 39 | # Prevent logged-out users from seeing certain pages 40 | redirect_to new_session_url unless logged_in? 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | # Shared secrets are available across all environments. 14 | 15 | # shared: 16 | # api_key: a1B2c3D4e5F6 17 | 18 | # Environmental secrets are only available for that specific environment. 19 | 20 | development: 21 | secret_key_base: 86da4c75b517ed0480afe349ef6bb5fff2e7e7fd135fef1476385ebc642aa06fa5255c6b17bfc3f1f5e47ac37be432a42bb0d6f09d66aeb4ee839c4a33c57812 22 | 23 | test: 24 | secret_key_base: 3867ecfc458d4da3872d835a53bef89034178d32d1efe60f3fc710945fefdcf9c9fa6bf3925caf4d380d9b48b3fccf822a103b69b97b8dd05d104d09064c0a5b 25 | 26 | # Do not keep production secrets in the unencrypted secrets file. 27 | # Instead, either read values from the environment. 28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets 29 | # and move the `production:` environment over there. 30 | 31 | production: 32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 33 | -------------------------------------------------------------------------------- /app/controllers/chirps_controller.rb: -------------------------------------------------------------------------------- 1 | class ChirpsController < ApplicationController 2 | 3 | def new 4 | @chirp = Chirp.new 5 | render :new 6 | end 7 | 8 | def create 9 | @chirp = Chirp.new(chirp_params) 10 | @chirp.author_id = current_user.id 11 | if @chirp.save 12 | redirect_to chirp_url(@chirp) 13 | else 14 | render json: @chirp.errors.full_messages, status: 422 15 | end 16 | end 17 | 18 | def show 19 | @chirp = Chirp.find(params[:id]) 20 | if @chirp 21 | render :show 22 | else 23 | render json: @chirp.errors.full_messages, status: 404 24 | end 25 | end 26 | 27 | def index 28 | @chirps = if params[:user_id] 29 | Chirp.where(author_id: params[:user_id]) 30 | else 31 | Chirp.all 32 | end 33 | render :index 34 | end 35 | 36 | def edit 37 | @chirp = Chirp.find(params[:id]) 38 | render :edit 39 | end 40 | 41 | def update 42 | @chirp = Chirp.find(params[:id]) 43 | if @chirp.update(chirp_params) 44 | redirect_to chirp_url(@chirp) 45 | else 46 | render json: @chirp.errors.full_messages, status: 422 47 | end 48 | end 49 | 50 | def destroy 51 | @chirp = Chirp.find(params[:id]) 52 | if @chirp.destroy 53 | redirect_to chirps_url 54 | else 55 | render plain: "You can't destroy what's not there." 56 | end 57 | end 58 | 59 | private 60 | 61 | def chirp_params 62 | params.require(:chirp).permit(:body, :author_id) 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /frontend/components/session/signup.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | class Signup extends React.Component { 4 | constructor(props) { 5 | super(props); 6 | this.state = { 7 | username: "", 8 | email: "", 9 | password: "", 10 | }; 11 | 12 | this.handleSubmit = this.handleSubmit.bind(this); 13 | } 14 | 15 | handleInput(type) { 16 | return (e) => { 17 | this.setState({ [type]: e.target.value }); 18 | } 19 | } 20 | 21 | handleSubmit(e) { 22 | e.preventDefault(); 23 | this.props.createNewUser(this.state) 24 | .then(() => this.props.history.push('/chirps')); 25 | } 26 | 27 | render() { 28 | // console.log(this.props); 29 | return ( 30 |
    31 |

    Sign Up!

    32 | 33 |
    34 | 39 | 44 | 50 |
    51 |
    52 | ) 53 | } 54 | } 55 | 56 | export default Signup; 57 | -------------------------------------------------------------------------------- /app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | class UsersController < ApplicationController 2 | 3 | def new 4 | @user = User.new 5 | render :new 6 | end 7 | 8 | def create 9 | @user = User.new(user_params) 10 | if @user.save 11 | login!(@user) 12 | redirect_to user_url(@user) 13 | else 14 | # Tell the user that something went wrong. Let them try again. 15 | flash.now[:errors] = @user.errors.full_messages 16 | render :new 17 | end 18 | end 19 | 20 | def show 21 | @user = User.find(params[:id]) 22 | if @user 23 | render :show 24 | else 25 | render json: @user.errors.full_messages, status: 404 26 | end 27 | end 28 | 29 | def index 30 | @users = User.all 31 | render :index 32 | end 33 | 34 | def edit 35 | @user = User.find(params[:id]) 36 | render :edit 37 | end 38 | 39 | def update 40 | @user = User.find(params[:id]) 41 | if @user.update(user_params) 42 | redirect_to user_url(@user) 43 | else 44 | render json: @user.errors.full_messages, status: 422 45 | end 46 | end 47 | 48 | def destroy 49 | @user = User.find(params[:id]) 50 | if @user.destroy 51 | redirect_to users_url 52 | else 53 | render plain: "You can't destroy what's not there." 54 | end 55 | end 56 | 57 | def search 58 | @users = User.where("username LIKE '%#{params[:query]}%'") 59 | render json: @users 60 | end 61 | 62 | private 63 | 64 | def user_params 65 | # params.require(:user).permit(:username, :email) 66 | # Add password 67 | params.require(:user).permit(:username, :email, :password) 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /wiki/bluebird_show_chirp_page.xml: -------------------------------------------------------------------------------- 1 | 3Vpbk+ooEP41Ps5UEkzUx9G57FbN7EyVD3v26RQaTFgxuARv++sXEsgNHOOcaM3qg8IHAfJ100039sBktX9hcB2/0RCRnueE+x547HleMBiKbwkccgD0vRyIGA5zyC2BKf4XKdBR6AaHKK115JQSjtd1cE6TBM15DYOM0V2924KS+qxrGCEDmM4hMdE/ccjjHB36Ton/hnAU65ldR7XM4HwZMbpJ1Hw9DyyyT968gnos1T+NYUh3FQg89cCEUcrz0mo/QURSq2nLn3s+0lqsm6GEt3lAiWULyQbpFWfr4gfNRfY2SPZ3emC8izFH0zWcy9adEL7AYr4iouaKojm/WtIWMY72FUit5wXRFeLsILroVqC4ORTk5vVdKQlf8x1XpAA0qVBJPyrGLhkQBUWCnRBwQ4T4HfDRt/AREDHBeEHFG1WJCf7ZUN1wl2Z7+kF0cJ31vmwUpUj+TnGUiMb3DdfjiZXkQ+YdPqHdvQrtftCg3fMN2vu+yXoXWugbrI9FaaapStcw0TxhFlYYrLYYDIoX53WaUs7oEk0ooUwgCU2QlB8mpAFBIsUFHueCTiTwsaQRC3v5oBpWOAzlNFa5dCAMoJn+RBgDyxboQhiBQSQKhbdQVcp4TCOaQPJUouO6jagwjvaY/1CwLP8ly/e+qP2NOD8oVwg3nAqoHPuV0vUp1U7phs3VCgfKaUIWIW0ec0iu/VPGGSKQ423dE/4KfwNDmf+A2zFkt6Sfwei0sfAvpJ/D0y4LJeGDPBaVjIUwjQtjatPPe8dxKzp6J4GRAj4Qw2KZkulMkVupZD8wddJrqZMnWNRYa9VVM3xQnPkwLeJhXYh9168Pkb+Oeqp6qmoM5DU8NggaA+UcGANlki5eu5XwR50ap0TM+6NauZB50ieYqi5o/bi+gdKrqeygZ3ECRzdloQZOQ7md61ko1zWY/F9oqfd1i9W9kpqh2hvEyYeMZG9ITZuO1AvMYOdSBz3XDP5e31/eb4le128R1FyKXvMgaFB7VmxdJE8+PYO0J6dwBFr3HFP3gI0dd9RF5sG10JMHdiHeWiNtqYh3SqdkqE3Qgpuh9rkBu9e3Bew1xB6wV4PT09OM7LMIJgO4kpJOZqn8mUk6PGcmAt1UviNeZlWFUs6JLMzpYoGQ5fGjwfFVSRFwJsRvFpl3voOa1ntgmpfilN21fQGmhzw7VzW0SVnq2s8Y7pZtU1U3I06vnnURm/ZqzliHA78gTaswXzCPNzOroWgDveJkicLfky8P8HZIM6FdTpV6HnjOPt/iwAGaYUfbbH4XOtTixHFGauRI3OE4ngZaJ0a+FPheKQvSTF6MRvej6qc+YNucSKDPN8Www/pA3eVE+mZGrGLCv6OtliZAR8OWO7yzd11/ePqYfynD7Ztu+CnE3CD+rPucOltHjF0XxAUNj2e5awPWCKAD4jq9aOg0E+L7ptnS4dz1UyG+eT02iTFbT2OhIt9yf3egmUZiZGjZ0pe6jjT96CMiws9dbVMbPFnYPH6T20h92ja1Paw/mzpRLf+8kbuu8g8y4Ok/ -------------------------------------------------------------------------------- /wiki/bluebird_chirp_form_page.xml: -------------------------------------------------------------------------------- 1 | 3VrRbqM4FP2aSLMPU2EcSPLYZNruSO22UrWa2aeVE9zgLcGscZpkvn5ssMFgktCURGl4aM21sc25x8f3mvTgZLG+YygJH2iAo57rBOse/NZz3SEYir/SsMkNfeDmhjkjQW4CpeGZ/MLK6CjrkgQ4rTTklEacJFXjjMYxnvGKDTFGV9VmLzSqjpqgObYMzzMU2dYfJOChei3PKe1/YjIP9cjAUTVTNHudM7qM1Xg9F75kV169QLov1T4NUUBXhgne9OCEUcrz0mI9wZGEVsOWP3e7pbaYN8Mxb/OAcssbipZYzzibF99oLLK3wbK904PjVUg4fk7QTNauhPOFLeSLSNwBUbTHV1N6w4zjtWFS87nDdIE524gmuhYqbDYFuPn9qvSEp/EODS9ADSpS3p8XfZcIiIICoRkQeEGAeB3g0W/Aw4/EAOMXKt7IBMb/f0l1xdc0W9PXogFwknVZKUpz+f+ZzGNR+bjkuj8xk7zLvMEO2MFJYPf8GuyuZ8He92zUu2ChZ6E+FqWphipNUKxxIiwwEDRrLATFi/MqTCln9BVPaESZsMQ0xtJ/JIpqJhRJd8FvMwEnFvaxhJEIvbxWFQsSBHKYRr904Ayokd7hjEHDEujCGb4FJA7EbqFuKeMhndMYRTeldVzVCANxvCb8pzLL8j+yfOWJu/8w5xu1FaIlp8JU9n1PabKP2ildspma4UBtmojNsZbH3CTnvhNxhiPEyVt1J/wIfgOLzH+htzFil8RPf7RfLLwj8XO4f8vCcXAtw6ISsQClYSGmTfy8chxgcPSrNIyU4QkzIqYpkc6I3IqSfd/mpNuSk3tQ1LbW1FUjPFGS7WHaxcOqE/vAq3aRv456yoyqah25tR0b+rWOcgysjjJPF6/dyvmjTsUpFuP+NG+OJE86gjG5oPlxeoHSszFW0K2IwPFFKdTAqZHbOZ1CAWAh+SlY6h6uWN2T1E7VHhCJn2Qme0E0rW+krm8nO8cK9ICd/N0/3j1eErzAa5HUHAteOxC0oH1Xbl0cnuyMQdqDU2wEmnuOzT3YhA4YdZFpAwueqSj9G6LVqzBnyfSZELF74AdV4N3TLXoddnzghGPYdMBxR3i4nErHuT5aSNTiaZqv4jamexK/4uB7fHAHD5s0c1rL05UDqNRz4W12nYWwwXp40/bUsAsOtVC2d6RgW+Ibx3G1oXUCdlCAfaJsq54kjUZXI/Oqdtg29/IhqHU7rHbUXe7VtzNvQ7HPUaulBOiou+FbwbtXXX+4P5w4lnB7dkA8CQlLbilbnA36HWBsRcTDE2IMbR37BImbpoapfjrE6C5z26I/rnMFjat6bg1htRYOWomT2DnQxmiWyAbpjkkMapwZebtFc3d7UchncKhSek2fkbaG/i2+71RVbUtQ0sHic+uLD9iLrzFhKj7SfWj52d+Bnmgq/ZYp3eeAsF/7jlZkUHv0q4vAzBt0qV/v06ia0OwXrYbz8ZwAJzhd8uws6Msjy9MKldj8nQSI45J72v7H2Wy2DWmpRdMGN7Q+Mi0+QhrMBW43W6+4LX/1kSts+csaePMb -------------------------------------------------------------------------------- /wiki/bluebird_session_page.xml: -------------------------------------------------------------------------------- 1 | 5VpNc+I4EP0te+A4U7aEHDiGTJLZqmQnVdRUZo8KVmxNbIuVRYD99SvZkr9kgiF2lmG4YD8JSX6v3epuMYJX8eaW42V4z3wSjYDjb0bwywgA15148ksh2xxB00kOBJz6ulMJzOm/RIOORlfUJ2mto2AsEnRZBxcsSchC1DDMOVvXuz2zqD7rEgfEAuYLHNnoI/VFmKMT5JT4V0KD0MzsOrrlCS9eAs5WiZ5vBOBz9smbY2zG0v3TEPtsXYHg9QheccZEfhVvrkikuDW05b+72dFarJuTRHT5Ach/8IqjFTErztYltoaL7GmI6u+M4GwdUkHmS7xQrWupvsRCEUfyzpWX9vx6Sa+EC7KpQHo9t4TFRPCt7GJaoeZmW5Cb369LJZDhO6yoAA2pWKsfFGOXDMgLTUI7IfCMCEE98DFu4cOL5ASzZyafqEqM98+KmYZPafZOX8oOrrPclI3yKlDfcxoksvH70gwnF5KPmLe/wbr7Iawjt8E6QBbrY2ST3ocResOS/mdysqR7/x/pFxbpM3n1ZKhKlzgxPFHuVxistlgMygcXdZpSwdkLuWIR4xJJWEKUfDSKGhCOlFrwy0LSSSQ+UzRSuUdd6oaY+r6aplWXHsSAhuk3xLhocTt9iDG1iCS+3KH1LeMiZAFLcHRdorO6X64wTjZU/NCwuv5bXX9G8u4nEWKrww+8EkxC5dh3jC33mXbKVnyhV6hDHIF5QMyWlENq7W8yzkmEBX2tRx/v4W9iGfNf+HWG+TnZpzfd7yzQQPbpuhbBtsUm/qWKRUvKfJyGhTdtM9DPjuNWjPSTAqYaeCCcynUqqjNL7mSTZhOrGiXoaJR7aDRYZ9vVMzwwmu1hRuNJXcWxi+pD5I+jf1UNZRsDgUaYBL3GQDkH1kCZ1MVjd1N/3Kt7SuTEP6o3AzkoF9rGYAzk412Ua4faNzLvIWfloy6chnU7H+ijvF/TStHxLqt/I0WWkd5jmjyo+sEZmWlzKwWenWIOFeq5duB99+322znR66IOac1A9IIOkcpBFY2iZPVmENKdnHEj0QaObXugrbxR1EXeRU+vmUZXR7g7I+nkIk14358/bA+pvKY0oFtIJcNevK10W6oO6eHzlBrmIx4br7WkQ3OSppQlN4zHJ+Np+n6zvEZoDVoKh5OWF6uP98pM9Y4a1qSthHVLRbh6GinP6OFYsZY8pbmb7gLd0eSF+KoAduQA99s0E61jAe0IUxoBeJN9TmLngs34tWsxvg8bAvu3rgOS7B0BrOMAA3ROsY/KoD4on96l2KH5tAfd2kDQnOH1n0+jNmexM0bpUIquedJdL1cfscu0XiItzL6i9XSgwA61BXZ9nBJ8TwlPcDykjzvJ7XIM94vpDSVmm6vrQ8wHnKZrxv3fXkxkp1yDiXnQIfIpu7MW0gZzZ/ZJc+W48ldgDzV23iK2rm78Ax1eIrtOdbD/aI23H0m0YGovcGQ2C5zsSJRy/4/fzZ+4jZNp0FLILULB3tW1/xDwGBKuVFFiyMzaWagzaEfwVaQW/Rxtz1YJ0CwWjG0l3DYlxuhgJeRt+V+sPLAt//AGr/8D -------------------------------------------------------------------------------- /public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    We're sorry, but something went wrong.

    62 |
    63 |

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

    64 |
    65 | 66 | 67 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => "public, max-age=#{1.hour.seconds.to_i}" 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | end 43 | -------------------------------------------------------------------------------- /public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The change you wanted was rejected.

    62 |

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

    63 |
    64 |

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

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
    60 |
    61 |

    The page you were looking for doesn't exist.

    62 |

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

    63 |
    64 |

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

    65 |
    66 | 67 | 68 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => "public, max-age=#{2.days.seconds.to_i}" 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | # Debug mode disables concatenation and preprocessing of assets. 41 | # This option may cause significant delays in view rendering with a large 42 | # number of complex assets. 43 | config.assets.debug = true 44 | 45 | # Suppress logger output for asset requests. 46 | config.assets.quiet = true 47 | 48 | # Raises error for missing translations 49 | # config.action_view.raise_on_missing_translations = true 50 | 51 | # Use an evented file watcher to asynchronously detect changes in source code, 52 | # routes, locales, etc. This feature depends on the listen gem. 53 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 54 | end 55 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | git_source(:github) do |repo_name| 4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 5 | "https://github.com/#{repo_name}.git" 6 | end 7 | 8 | 9 | 10 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 11 | gem 'rails', '~> 5.1.3' 12 | # Use postgresql as the database for Active Record 13 | gem 'pg', '~> 0.18' 14 | # Use Puma as the app server 15 | gem 'puma', '~> 3.7' 16 | # Use SCSS for stylesheets 17 | gem 'sass-rails', '~> 5.0' 18 | # Use Uglifier as compressor for JavaScript assets 19 | gem 'uglifier', '>= 1.3.0' 20 | # See https://github.com/rails/execjs#readme for more supported runtimes 21 | # gem 'therubyracer', platforms: :ruby 22 | 23 | # Use CoffeeScript for .coffee assets and views 24 | gem 'coffee-rails', '~> 4.2' 25 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 26 | gem 'jbuilder', '~> 2.5' 27 | # Use Redis adapter to run Action Cable in production 28 | # gem 'redis', '~> 3.0' 29 | # Use ActiveModel has_secure_password 30 | gem 'bcrypt', '~> 3.1.7' 31 | 32 | gem 'jquery-rails' 33 | 34 | # Use Capistrano for deployment 35 | # gem 'capistrano-rails', group: :development 36 | 37 | group :development, :test do 38 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 39 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw] 40 | # Adds support for Capybara system testing and selenium driver 41 | gem 'capybara', '~> 2.13' 42 | gem 'selenium-webdriver' 43 | gem 'pry-rails' 44 | gem 'annotate' 45 | gem 'faker' 46 | end 47 | 48 | group :development do 49 | # Access an IRB console on exception pages or by using <%= console %> anywhere in the code. 50 | gem 'web-console', '>= 3.3.0' 51 | gem 'listen', '>= 3.0.5', '< 3.2' 52 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 53 | gem 'spring' 54 | gem 'spring-watcher-listen', '~> 2.0.0' 55 | end 56 | 57 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 58 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 59 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | 9 | User.destroy_all 10 | Chirp.destroy_all 11 | Like.destroy_all 12 | 13 | user1 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 14 | user2 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 15 | user3 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 16 | user4 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 17 | user5 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 18 | user6 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 19 | user7 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 20 | user8 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 21 | user9 = User.create(username: Faker::Name.name, email: Faker::Internet.email, password: 'password') 22 | 23 | chirp1 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user1.id) 24 | chirp2 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user1.id) 25 | chirp3 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user1.id) 26 | chirp4 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user2.id) 27 | chirp5 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user2.id) 28 | chirp6 = Chirp.create(body: Faker::RickAndMorty.quote, author_id: user2.id) 29 | 30 | Like.create(user_id: user3.id, chirp_id: chirp1.id) 31 | Like.create(user_id: user4.id, chirp_id: chirp2.id) 32 | Like.create(user_id: user5.id, chirp_id: chirp3.id) 33 | Like.create(user_id: user3.id, chirp_id: chirp3.id) 34 | Like.create(user_id: user4.id, chirp_id: chirp1.id) 35 | Like.create(user_id: user5.id, chirp_id: chirp2.id) 36 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20170828221731) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "chirps", force: :cascade do |t| 19 | t.text "body", null: false 20 | t.integer "author_id", null: false 21 | t.datetime "created_at", null: false 22 | t.datetime "updated_at", null: false 23 | t.index ["author_id"], name: "index_chirps_on_author_id" 24 | end 25 | 26 | create_table "follows", force: :cascade do |t| 27 | t.integer "follower_id", null: false 28 | t.integer "followee_id", null: false 29 | t.datetime "created_at", null: false 30 | t.datetime "updated_at", null: false 31 | t.index ["followee_id"], name: "index_follows_on_followee_id" 32 | t.index ["follower_id"], name: "index_follows_on_follower_id" 33 | end 34 | 35 | create_table "likes", force: :cascade do |t| 36 | t.integer "user_id", null: false 37 | t.integer "chirp_id", null: false 38 | t.datetime "created_at", null: false 39 | t.datetime "updated_at", null: false 40 | end 41 | 42 | create_table "users", force: :cascade do |t| 43 | t.string "username", null: false 44 | t.string "email" 45 | t.string "password_digest" 46 | t.string "session_token" 47 | t.index ["session_token"], name: "index_users_on_session_token" 48 | t.index ["username"], name: "index_users_on_username", unique: true 49 | end 50 | 51 | end 52 | -------------------------------------------------------------------------------- /wiki/bluebird_dashboard_page.xml: -------------------------------------------------------------------------------- 1 | 7VxRc6M2EP41nrk+5AYQEPwY55I0M0mTmUznrk8d2ShGDUaukGO7v74SIBuQbGMHsHGShztYYUl8+nZXK63ogevJ4o7CafBIfBT2LMNf9MCPnmV5psf/FYJlKrBNKxWMKfZTkbkWvOD/UCY0MukM+yguPMgICRmeFoUjEkVoxAoySCmZFx97JWGx1SkcI0XwMoKhKv2JfRZkr+UYa/nvCI8D2bJpZCVDOHobUzKLsvZ6FnhN/tLiCZR1Zc/HAfTJPCcCNz1wTQlh6dVkcY1CAa2ELf3d7YbSVb8piliVH4D0B+8wnCHZ46RfbCmxSN4GieeNHhjMA8zQyxSOROmcDz6XBWwS8juTX6rtZ116R5ShRU6U9ecOkQlidMkfkaUgw2a5Aje9n69HwpF4B7lRABJUmI3+eFX3GgF+kYGgB8Q+I0CcGvBwNXi4IW9g8Er4G+WBcf+dEVlwESc6fcUfMI3pYl3Ir8bi/xc8jnjh04zJ+nhP0irTB7bAbrYCu+OWYLccBXbbUVGvg4WXCuoDfjWUUMVTGEmcMPVzCOZLFAT5i7MiTDGj5A1dk5BQLolIhMT44TAsiWAohgv8GHE4EZcPBIyY28urrGCCfV80ox2XGgYDSKS3DMalRgXqGAxPARL53Ftkt4SygIxJBMObtXRQtBE5xNECs1+ZWFz/Ja6/O/zuH8TYMnOFcMYIF63rfiBkuovaMZnRUdbDfuY0IR0jVrBsou9bEacohAy/Fz3hR/DrK2T+A74PID0nfrr93cbCaYif0h1s81ko8q/EvGgNmQ/jYGVNdQT9bhhmjqQXQtDPBM+IYt5PAXXC5EqctD2VlKAiKXfAKGWVuZu18Exw4sTkGHvFUbRNp1hF+jrZr/LTqlJFVsllA7dUUYqBUlEy1KvXrjb6pjrYHzBPEW/4V/6mIQMlA4KChfKOZaJkb3IqdMvn4OisbNSlUWK30aKNAt1kqX24yaqfpGps8ghx9Cxi2TOiadmVWq4a7jQ11ZP2Pgfxw9Pd0znBazoVwpqm4NVFk0cO81ZcknhcqnRbvXztgFx20yxqZnJybI9gFz2FVX/GiL4gSEfBOamu6+2mqtcUU9UobovqVlgYW618bo0f9tBjs+Q2DBUcoDdsNcBjVYjBjgxPcQHF0iwiAqCBBzg1oGN2DR1bdYrNoaMGHyeOjtcmd+reomgaHWC2yZ269ysaR8dpkzvqdP7E0fEqcqe8MHUQOq4CRhcmn3JzpDD5zGYn7U8+NVs11wGm03uO0kLB91iTz7p5W56JWv32YvR692OqUnPzvk010mo2ZOrfkdEvRjulvTMArGIVGxajryiFy9xjU/FAvLkd1y6SArhevrp9n+cXaQ8OXRnXbDytVfOeocn5qmdpTQM4nqKe/YbUE2giIXB18dsWx1thoad2x2uVDJhu0qZDqAa/C9RoaMiv/p6KzCV2tqS03DLkKimb8hnS4mnSWHz8XsBbJqoI2C8yBEUaS4hemZrGUszI2J0M09flwojR52VDTP1YtITfkluR8YEoxSjemOCxEidvcWJpH3UTCDi7CWQ2tXQNdGHoqTIIhjEpEklgTxhk5ItMG8jkmC2SSY3aEw8QwPnb2SKu2H8N4o3Zf81CQAcmJbZq4BqblGxO9Dw9+/blISsaNa9ND6muhHzKaa0G88bMmrrz2QWzplvkbMqs6bYtv8xat1SsbNaA3BNqw6zZ6nrGZzRrOsybMmu2Zrv49M3a6vhRC2bN/lrP6L6KKWZNs0rbnFlT1zM+pVnTYN6YWdPs1XfArDntzdbsjoTpVhEhzeZnYwipYfrnUNvdkDemtmpc++Vru0Yg4JQI5PVb9LWaMH3PQ9SebuzvMAtmw57oogsnArZoGKe5xlVEDzh6Q/59dHAFj8s4GbWKB7gP4FLPArfJ34cJVQOHyufwbU2Gsf7DBHVwqEL+9R6HPDckaxmGJQWVj3gedIKvpfOcm0Zs3/OcrtyzX/kfr1hRfec55at3Z29IKL3MvtJ8gGRvPbPLxxxaPOvvaJYerA9+i6II1gbrtgEkDZRbkklLuLmqj9POS60acKv1fOd++aIlY7MzF09OBfKGy2krX1Q2niPYtwjNuWQkctPE3G3GGInUmOckdL0Omh7vEJOjRuA3PlbDppNUb6d0fFu3f65NFe/XANyJporvrfqORvXttlRfXd34hhL6fRrdd93SSSLNmvnq81Yf1H5+u/6OWjoRW3+rDtz8Dw== -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers: a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum; this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # If you are preloading your application and using Active Record, it's 36 | # recommended that you close any connections to the database before workers 37 | # are forked to prevent connection leakage. 38 | # 39 | # before_fork do 40 | # ActiveRecord::Base.connection_pool.disconnect! if defined?(ActiveRecord) 41 | # end 42 | 43 | # The code in the `on_worker_boot` will be called if you are using 44 | # clustered mode by specifying a number of `workers`. After each worker 45 | # process is booted, this block will be run. If you are using the `preload_app!` 46 | # option, you will want to use this block to reconnect to any threads 47 | # or connections that may have been created at application boot, as Ruby 48 | # cannot share connections between processes. 49 | # 50 | # on_worker_boot do 51 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 52 | # end 53 | # 54 | 55 | # Allow puma to be restarted by `rails restart` command. 56 | plugin :tmp_restart 57 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's 6 | * vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | 17 | html, body, div { 18 | padding: 0; 19 | margin: 0; 20 | font-family: sans-serif; 21 | } 22 | 23 | .hero-img-frame { 24 | position: absolute; 25 | width: 100%; 26 | max-height: 500px; 27 | overflow: hidden; 28 | display: flex; 29 | justify-content: center; 30 | align-items: center; 31 | z-index: -1; 32 | } 33 | 34 | .hero-img { 35 | width: 100%; 36 | } 37 | 38 | .home { 39 | min-height: 400px; 40 | display: flex; 41 | justify-content: center; 42 | align-items: flex-end; 43 | h1 { 44 | height: 100%; 45 | z-index: 1; 46 | color: white; 47 | text-shadow: 1px 1px 15px black; 48 | text-align: center; 49 | width: 100%; 50 | } 51 | } 52 | 53 | .nav-bar { 54 | display: flex; 55 | justify-content: space-between; 56 | padding: 20px; 57 | background: lightblue; 58 | align-items: center; 59 | .btn { 60 | background-color: white; 61 | padding: 10px; 62 | box-shadow: 3px 3px 3px darkblue; 63 | text-decoration: none; 64 | } 65 | .btn:hover { 66 | background-color: rgb(240, 240, 240); 67 | box-shadow: 1px 1px 1px darkblue; 68 | } 69 | } 70 | 71 | .session-form { 72 | display: flex; 73 | flex-direction: column; 74 | width: 100%; 75 | height: 90vh; 76 | justify-content: center; 77 | align-items: center; 78 | form { 79 | height: 300px; 80 | width: 300px; 81 | background: rgb(240, 240, 240); 82 | display: flex; 83 | flex-direction: column; 84 | justify-content: center; 85 | align-items: center; 86 | } 87 | label { 88 | padding: 10px; 89 | display: block; 90 | } 91 | button { 92 | display: block; 93 | padding: 10px; 94 | margin: 20px; 95 | } 96 | } -------------------------------------------------------------------------------- /wiki/bluebird_user_search.xml: -------------------------------------------------------------------------------- 1 | 7V1bc6JIFP41PsxDpoAGxMckk5lNVWZnqlJbO/u01ZGOskHaBYy6v367hUagDxFjNxHVhwQO2DTfuZ++OEC3s9W3GM+n36lPwoFl+KsB+jKwLM/02F9OWGcE27QywiQO/IxkbgmPwX8kJxo5dRH4JKncmFIapsG8ShzTKCLjtELDcUyX1dueaVh96hxPiER4HONQpv4Z+Ok0fy3H2NJ/I8FkKp5sGvmVJzx+mcR0EeXPG1joefPJLs+waCu/P5liny5LJHQ3QLcxpWl2NFvdkpBDK2DLvve14WrR75hEaZsv5Gx5xeGCiB5v+pWuBRabtyH8fmOAbpbTICWPczzmV5eM+Yw2TWchOzPZYd4ciVOyauySWbwokx9CZySN1+wW8QWUY7MuwM3Ol1tOOALvaYkLSICKc+5Pira3CLCDHAQYEHRCgDgK8LABPNyQPeDmmbLul4Fx/11QceEq2ej0NbvBNOar7UV2NOH/H4NJxC7+WKSiPdaTrMnshjdgNzuB3XFrsFuOBLvtyKirkEJHQv2GHT0JqJI5jgROQeyXECxfkRBkWKRVmJI0pi/kloY0ZpSIRoTzLwjDGgmHnF3oy5hhRxj9hiMbMHt5nV+YBb7PHwPyRQEzkED6DWYMARVQwQxXApL4zFvkpzROp3RCIxzebak3VRtRQpysgvRXTubHf/Hjzw47+4ek6Tp3hXiRUkbatv1A6bwi2rwLFSwTuojHOWmYu0wcT4gwjq0Rj0mI0+C12voh+A0lYf4dv97g+JTk0x3tNhaOJvn0drssEvnXPCzaIubjZFoYU0g+PxuGWZLRK04Y5YSfJA5YNznSG0FuIZK2K8uk1RrlHSgKWmvRzZ/wkwYbHya47lWZaJtOtYnshfJvlaOqWkNWzWMjt9ZQhoLU0IbTxWu3Yv5IqXGK2HN/lU+0mCcRv5RlQcjHRxgo0Z+SBn1lETg5KQs1NGrCbXRnoUxTQrIHUmodYrHUC6mcqn3HQfSTZ7InJKZ1R2q5crKjK9Az5eTv4ce3H6cEr+m0SGp0wasgl7SgXPKJt3ekWWQhrALwoSzPBbrKEXf6aHeBSFGIzocYXlcS2z8SEj8SHI+np2QbXG+3qHq6JFVOEt9Q3RZ1t6KwaijSY7PmlwwZHARbThXwtMjxPhiean3GAmqUCAHwIEcBOqO+oWPLXlcbOpac3Rw5Ol6HsiO62xt0kNml7MhJx5Gj43QpO6oHi7Sj47WUnXrh613oAD4LXV99egOiFiG5coisml+H1Aty6yoQkv0WT2X+nvMh7FTC6aNCS+WQu3XIPQlyXSk+gpxhljn6wSuYgnLYr3IEeQ4akudUzkGrQ3O7M9lRcyJrPAWxn/AnBS+bUz70R+I4IEnjSF9B3rzFkY3/qRYg5OwWIFNXFQNBAcNFgnouQY7ZoQTJQdVZmn0Ac21mH4jUehCL2LJd0xWLoObi7MWs9UXFJLPmdekY5UlLZ2nWAMy1mTW5NN0HswZlobrMGlRXvpi1fqlY3awhUXjpxKzJZYxzNGsQ5trMGlDPP36zVkw/78Cs2ZcyRv9VTDJrTofRmi2XMc7SrAGY6zJrtpz398GsOd1Fa3ZP0nSritCoQ8Mvp+nnoba7IdemtnJee/G1fRMg5NQEyBt16GuBNH3PiY8exPtvQTpdPA14F10847BFT0k2GawN6SGIXoh/H727ge/rZMO1llMv3yFLAwt93XwOFigFMlRfh2kDU8DghakqZKjFBLk9Vvk0TOU0DEsQWq7xeecajo4W9DRxbN8FPa4Y9Sv8j1dtSN2CHruh3jDFy5fjNM9c6cWUYpFVHKJndn0eaodrPW2tUwmbTJuKCKk+zxyIkMB55qaS9cpAanPasKmw6c5e88f2zXS0olZbjT2SNVQoreppZc7hq0gaA+kijdIW0BxncGzVlaBdrVdM2j+Inc3Z1UGxMbv06dz4aLneTrWEchxThS2Dcpw+egALKOPr8wB7LXs5Yg9gATGaNg8ALYa5mAwlJgPiozaTAcXaqjz5DI/x8ty4WXfkVstBWxWOXAys9N8BAEtItDkAd6/lR8fsAIBhIl0OQAwGXByABgcA8FGXA3Ch/FdhKodfCDn7ZK6oMnThA06legYtQdXnA5rnKB6uBs9BxHcpOHMd8DrUAaig0UuPDkzT1ebRDy/qXTx6k0cH+KjLow+PbqOv5v02s55KA5oS0NXdaYTZKI97ZqMfMkP2G728jmO8Lt0w54OJSanl+piktCOJU2bVvvezg6wH7x24HMoJ4HafnHvG4dXJqpxb31ULKLwWuzarHrIcIpU6d+BGTru0xwK0xz1Qe1oDJaeNNQG9T8nshIV0d6nPhBy8EiGFoqIDd2frLDCqGU5TpDm75ntaCnCTa913fiBP9DxK4ByjnorKK37B3UdGewPHTrc/mpA5r+0PU6C7/wE= -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # username :string not null 7 | # email :string 8 | # password_digest :string 9 | # session_token :string 10 | # 11 | 12 | class User < ApplicationRecord 13 | # This makes it so we can validate our password length, without storing it in the DB 14 | attr_reader :password 15 | 16 | validates :username, presence: true, uniqueness: true 17 | validates :password_digest, :session_token, presence: true 18 | validates :password, length: { minimum: 6 }, allow_nil: true 19 | 20 | # This allows us to run methods before running validations 21 | # In this case, we need to have a session_token when a user is first created 22 | after_initialize :ensure_session_token 23 | 24 | has_many :likes, 25 | primary_key: :id, 26 | foreign_key: :user_id, 27 | class_name: :Like 28 | 29 | has_many :liked_chirps, 30 | through: :likes, 31 | source: :chirp 32 | 33 | has_many :chirps, 34 | primary_key: :id, 35 | foreign_key: :author_id, 36 | class_name: :Chirp 37 | 38 | has_many :fanships, 39 | primary_key: :id, 40 | foreign_key: :followee_id, 41 | class_name: :Follow 42 | 43 | has_many :followships, 44 | primary_key: :id, 45 | foreign_key: :follower_id, 46 | class_name: :Follow 47 | 48 | has_many :followers, 49 | through: :fanships, 50 | source: :follower 51 | 52 | has_many :followees, 53 | through: :followships, 54 | source: :followee 55 | 56 | # Class method for finding a user ONLY if we have the correct username and password 57 | def self.find_by_credentials(username, password) 58 | user = User.find_by(username: username) 59 | return nil unless user 60 | user.is_password?(password) ? user : nil 61 | end 62 | 63 | def password=(password) 64 | # Set temporary instance variable so that we can validate length 65 | @password = password 66 | # Create a password_digest so that we do not have to store the plain-text password in our DB 67 | self.password_digest = BCrypt::Password.create(password) 68 | end 69 | 70 | def is_password?(password) 71 | # Use BCrypt's built-in method for checking if the password provided is the user's password 72 | BCrypt::Password.new(self.password_digest).is_password?(password) 73 | end 74 | 75 | def ensure_session_token 76 | # Generate the initial session_token so that we pass the validation 77 | # This method runs right after the model is initialized, before any validations are run 78 | self.session_token ||= SecureRandom.urlsafe_base64 79 | end 80 | 81 | def reset_session_token! 82 | # When a user logs out, we want to scramble their session_token so that bad people cannot use the old one 83 | self.session_token = SecureRandom.urlsafe_base64 84 | self.save 85 | self.session_token 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see Rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | 24 | development: 25 | <<: *default 26 | database: BlueBird_development 27 | 28 | # The specified database role being used to connect to postgres. 29 | # To create additional roles in postgres see `$ createuser --help`. 30 | # When left blank, postgres will use the default role. This is 31 | # the same name as the operating system user that initialized the database. 32 | #username: BlueBird 33 | 34 | # The password associated with the postgres role (username). 35 | #password: 36 | 37 | # Connect on a TCP socket. Omitted by default since the client uses a 38 | # domain socket that doesn't need configuration. Windows does not have 39 | # domain sockets, so uncomment these lines. 40 | #host: localhost 41 | 42 | # The TCP port the server listens on. Defaults to 5432. 43 | # If your server runs on a different port number, change accordingly. 44 | #port: 5432 45 | 46 | # Schema search path. The server defaults to $user,public 47 | #schema_search_path: myapp,sharedapp,public 48 | 49 | # Minimum log levels, in increasing order: 50 | # debug5, debug4, debug3, debug2, debug1, 51 | # log, notice, warning, error, fatal, and panic 52 | # Defaults to warning. 53 | #min_messages: notice 54 | 55 | # Warning: The database defined as "test" will be erased and 56 | # re-generated from your development database when you run "rake". 57 | # Do not set this db to the same as development or production. 58 | test: 59 | <<: *default 60 | database: BlueBird_test 61 | 62 | # As with config/secrets.yml, you never want to store sensitive information, 63 | # like your database password, in your source code. If your source code is 64 | # ever seen by anyone, they now have access to your database. 65 | # 66 | # Instead, provide the password as a unix environment variable when you boot 67 | # the app. Read http://guides.rubyonrails.org/configuring.html#configuring-a-database 68 | # for a full rundown on how to provide these environment variables in a 69 | # production deployment. 70 | # 71 | # On Heroku and other platform providers, you may have a full connection URL 72 | # available as an environment variable. For example: 73 | # 74 | # DATABASE_URL="postgres://myuser:mypass@localhost/somedatabase" 75 | # 76 | # You can use this database configuration with: 77 | # 78 | # production: 79 | # url: <%= ENV['DATABASE_URL'] %> 80 | # 81 | production: 82 | <<: *default 83 | database: BlueBird_production 84 | username: BlueBird 85 | password: <%= ENV['BLUEBIRD_DATABASE_PASSWORD'] %> 86 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Attempt to read encrypted secrets from `config/secrets.yml.enc`. 18 | # Requires an encryption key in `ENV["RAILS_MASTER_KEY"]` or 19 | # `config/secrets.yml.key`. 20 | config.read_encrypted_secrets = true 21 | 22 | # Disable serving static files from the `/public` folder by default since 23 | # Apache or NGINX already handles this. 24 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 25 | 26 | # Compress JavaScripts and CSS. 27 | config.assets.js_compressor = :uglifier 28 | # config.assets.css_compressor = :sass 29 | 30 | # Do not fallback to assets pipeline if a precompiled asset is missed. 31 | config.assets.compile = false 32 | 33 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 34 | 35 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 36 | # config.action_controller.asset_host = 'http://assets.example.com' 37 | 38 | # Specifies the header that your server uses for sending files. 39 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 40 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 41 | 42 | # Mount Action Cable outside main process or domain 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = 'wss://example.com/cable' 45 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 46 | 47 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 48 | # config.force_ssl = true 49 | 50 | # Use the lowest log level to ensure availability of diagnostic information 51 | # when problems arise. 52 | config.log_level = :debug 53 | 54 | # Prepend all log lines with the following tags. 55 | config.log_tags = [ :request_id ] 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Use a real queuing backend for Active Job (and separate queues per environment) 61 | # config.active_job.queue_adapter = :resque 62 | # config.active_job.queue_name_prefix = "BlueBird_#{Rails.env}" 63 | config.action_mailer.perform_caching = false 64 | 65 | # Ignore bad email addresses and do not raise email delivery errors. 66 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 67 | # config.action_mailer.raise_delivery_errors = false 68 | 69 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 70 | # the I18n.default_locale when a translation cannot be found). 71 | config.i18n.fallbacks = true 72 | 73 | # Send deprecation notices to registered listeners. 74 | config.active_support.deprecation = :notify 75 | 76 | # Use default logging formatter so that PID and timestamp are not suppressed. 77 | config.log_formatter = ::Logger::Formatter.new 78 | 79 | # Use a different logger for distributed setups. 80 | # require 'syslog/logger' 81 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 82 | 83 | if ENV["RAILS_LOG_TO_STDOUT"].present? 84 | logger = ActiveSupport::Logger.new(STDOUT) 85 | logger.formatter = config.log_formatter 86 | config.logger = ActiveSupport::TaggedLogging.new(logger) 87 | end 88 | 89 | # Do not dump schema after migrations. 90 | config.active_record.dump_schema_after_migration = false 91 | end 92 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.1.3) 5 | actionpack (= 5.1.3) 6 | nio4r (~> 2.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.1.3) 9 | actionpack (= 5.1.3) 10 | actionview (= 5.1.3) 11 | activejob (= 5.1.3) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.1.3) 15 | actionview (= 5.1.3) 16 | activesupport (= 5.1.3) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.1.3) 22 | activesupport (= 5.1.3) 23 | builder (~> 3.1) 24 | erubi (~> 1.4) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.1.3) 28 | activesupport (= 5.1.3) 29 | globalid (>= 0.3.6) 30 | activemodel (5.1.3) 31 | activesupport (= 5.1.3) 32 | activerecord (5.1.3) 33 | activemodel (= 5.1.3) 34 | activesupport (= 5.1.3) 35 | arel (~> 8.0) 36 | activesupport (5.1.3) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | addressable (2.5.1) 42 | public_suffix (~> 2.0, >= 2.0.2) 43 | annotate (2.7.2) 44 | activerecord (>= 3.2, < 6.0) 45 | rake (>= 10.4, < 13.0) 46 | arel (8.0.0) 47 | bcrypt (3.1.11) 48 | bindex (0.5.0) 49 | builder (3.2.3) 50 | byebug (9.0.6) 51 | capybara (2.15.1) 52 | addressable 53 | mini_mime (>= 0.1.3) 54 | nokogiri (>= 1.3.3) 55 | rack (>= 1.0.0) 56 | rack-test (>= 0.5.4) 57 | xpath (~> 2.0) 58 | childprocess (0.7.1) 59 | ffi (~> 1.0, >= 1.0.11) 60 | coderay (1.1.1) 61 | coffee-rails (4.2.2) 62 | coffee-script (>= 2.2.0) 63 | railties (>= 4.0.0) 64 | coffee-script (2.4.1) 65 | coffee-script-source 66 | execjs 67 | coffee-script-source (1.12.2) 68 | concurrent-ruby (1.0.5) 69 | erubi (1.6.1) 70 | execjs (2.7.0) 71 | faker (1.8.3) 72 | i18n (~> 0.5) 73 | ffi (1.9.18) 74 | globalid (0.4.0) 75 | activesupport (>= 4.2.0) 76 | i18n (0.8.6) 77 | jbuilder (2.7.0) 78 | activesupport (>= 4.2.0) 79 | multi_json (>= 1.2) 80 | jquery-rails (4.3.1) 81 | rails-dom-testing (>= 1, < 3) 82 | railties (>= 4.2.0) 83 | thor (>= 0.14, < 2.0) 84 | listen (3.1.5) 85 | rb-fsevent (~> 0.9, >= 0.9.4) 86 | rb-inotify (~> 0.9, >= 0.9.7) 87 | ruby_dep (~> 1.2) 88 | loofah (2.0.3) 89 | nokogiri (>= 1.5.9) 90 | mail (2.6.6) 91 | mime-types (>= 1.16, < 4) 92 | method_source (0.8.2) 93 | mime-types (3.1) 94 | mime-types-data (~> 3.2015) 95 | mime-types-data (3.2016.0521) 96 | mini_mime (0.1.4) 97 | mini_portile2 (2.2.0) 98 | minitest (5.10.3) 99 | multi_json (1.12.1) 100 | nio4r (2.1.0) 101 | nokogiri (1.8.0) 102 | mini_portile2 (~> 2.2.0) 103 | pg (0.21.0) 104 | pry (0.10.4) 105 | coderay (~> 1.1.0) 106 | method_source (~> 0.8.1) 107 | slop (~> 3.4) 108 | pry-rails (0.3.6) 109 | pry (>= 0.10.4) 110 | public_suffix (2.0.5) 111 | puma (3.9.1) 112 | rack (2.0.3) 113 | rack-test (0.6.3) 114 | rack (>= 1.0) 115 | rails (5.1.3) 116 | actioncable (= 5.1.3) 117 | actionmailer (= 5.1.3) 118 | actionpack (= 5.1.3) 119 | actionview (= 5.1.3) 120 | activejob (= 5.1.3) 121 | activemodel (= 5.1.3) 122 | activerecord (= 5.1.3) 123 | activesupport (= 5.1.3) 124 | bundler (>= 1.3.0) 125 | railties (= 5.1.3) 126 | sprockets-rails (>= 2.0.0) 127 | rails-dom-testing (2.0.3) 128 | activesupport (>= 4.2.0) 129 | nokogiri (>= 1.6) 130 | rails-html-sanitizer (1.0.3) 131 | loofah (~> 2.0) 132 | railties (5.1.3) 133 | actionpack (= 5.1.3) 134 | activesupport (= 5.1.3) 135 | method_source 136 | rake (>= 0.8.7) 137 | thor (>= 0.18.1, < 2.0) 138 | rake (12.0.0) 139 | rb-fsevent (0.10.2) 140 | rb-inotify (0.9.10) 141 | ffi (>= 0.5.0, < 2) 142 | ruby_dep (1.5.0) 143 | rubyzip (1.2.1) 144 | sass (3.5.1) 145 | sass-listen (~> 4.0.0) 146 | sass-listen (4.0.0) 147 | rb-fsevent (~> 0.9, >= 0.9.4) 148 | rb-inotify (~> 0.9, >= 0.9.7) 149 | sass-rails (5.0.6) 150 | railties (>= 4.0.0, < 6) 151 | sass (~> 3.1) 152 | sprockets (>= 2.8, < 4.0) 153 | sprockets-rails (>= 2.0, < 4.0) 154 | tilt (>= 1.1, < 3) 155 | selenium-webdriver (3.5.1) 156 | childprocess (~> 0.5) 157 | rubyzip (~> 1.0) 158 | slop (3.6.0) 159 | spring (2.0.2) 160 | activesupport (>= 4.2) 161 | spring-watcher-listen (2.0.1) 162 | listen (>= 2.7, < 4.0) 163 | spring (>= 1.2, < 3.0) 164 | sprockets (3.7.1) 165 | concurrent-ruby (~> 1.0) 166 | rack (> 1, < 3) 167 | sprockets-rails (3.2.0) 168 | actionpack (>= 4.0) 169 | activesupport (>= 4.0) 170 | sprockets (>= 3.0.0) 171 | thor (0.19.4) 172 | thread_safe (0.3.6) 173 | tilt (2.0.8) 174 | tzinfo (1.2.3) 175 | thread_safe (~> 0.1) 176 | uglifier (3.2.0) 177 | execjs (>= 0.3.0, < 3) 178 | web-console (3.5.1) 179 | actionview (>= 5.0) 180 | activemodel (>= 5.0) 181 | bindex (>= 0.4.0) 182 | railties (>= 5.0) 183 | websocket-driver (0.6.5) 184 | websocket-extensions (>= 0.1.0) 185 | websocket-extensions (0.1.2) 186 | xpath (2.1.0) 187 | nokogiri (~> 1.3) 188 | 189 | PLATFORMS 190 | ruby 191 | 192 | DEPENDENCIES 193 | annotate 194 | bcrypt (~> 3.1.7) 195 | byebug 196 | capybara (~> 2.13) 197 | coffee-rails (~> 4.2) 198 | faker 199 | jbuilder (~> 2.5) 200 | jquery-rails 201 | listen (>= 3.0.5, < 3.2) 202 | pg (~> 0.18) 203 | pry-rails 204 | puma (~> 3.7) 205 | rails (~> 5.1.3) 206 | sass-rails (~> 5.0) 207 | selenium-webdriver 208 | spring 209 | spring-watcher-listen (~> 2.0.0) 210 | tzinfo-data 211 | uglifier (>= 1.3.0) 212 | web-console (>= 3.3.0) 213 | 214 | BUNDLED WITH 215 | 1.14.6 216 | --------------------------------------------------------------------------------