diff --git a/.env b/.env deleted file mode 100644 index e69de29..0000000 diff --git a/.gitignore b/.gitignore index d219437..58f0e2a 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ coverage .DS_Store +.env diff --git a/Gemfile b/Gemfile index 96a7ed5..4422102 100644 --- a/Gemfile +++ b/Gemfile @@ -30,6 +30,10 @@ gem 'httparty' # OmniAuth Gems gem 'omniauth' +gem 'omniauth-instagram' + +# OAuth +gem 'twitter' # Use ActiveModel has_secure_password # gem 'bcrypt', '~> 3.1.7' @@ -73,4 +77,3 @@ end group :production do gem 'pg' end - diff --git a/Gemfile.lock b/Gemfile.lock index f9a54cc..f49a0fa 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -36,6 +36,7 @@ GEM minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) + addressable (2.3.8) arel (6.0.2) autoprefixer-rails (5.2.1.1) execjs @@ -49,6 +50,7 @@ GEM bootstrap-sass (3.3.5.1) autoprefixer-rails (>= 5.0.0.1) sass (>= 3.3.0) + buftok (0.2.0) builder (3.2.2) byebug (5.0.0) columnize (= 0.9.0) @@ -68,6 +70,7 @@ GEM dotenv-rails (2.0.2) dotenv (= 2.0.2) railties (~> 4.0) + equalizer (0.0.11) erubis (2.7.0) execjs (2.5.2) factory_girl (4.5.0) @@ -75,9 +78,14 @@ GEM factory_girl_rails (4.5.0) factory_girl (~> 4.5.0) railties (>= 3.0.0) + faraday (0.9.1) + multipart-post (>= 1.2, < 3) globalid (0.3.5) activesupport (>= 4.1.0) hashie (3.4.2) + http (0.6.4) + http_parser.rb (~> 0.6.0) + http_parser.rb (0.6.0) httparty (0.13.5) json (~> 1.8) multi_xml (>= 0.5.2) @@ -90,21 +98,38 @@ GEM railties (>= 4.2.0) thor (>= 0.14, < 2.0) json (1.8.3) + jwt (1.5.1) loofah (2.0.2) nokogiri (>= 1.5.9) mail (2.6.3) mime-types (>= 1.16, < 3) + memoizable (0.4.2) + thread_safe (~> 0.3, >= 0.3.1) method_source (0.8.2) mime-types (2.6.1) mini_portile (0.6.2) minitest (5.7.0) multi_json (1.11.2) multi_xml (0.5.5) + multipart-post (2.0.0) + naught (1.0.0) nokogiri (1.6.6.2) mini_portile (~> 0.6.0) + oauth2 (1.0.0) + faraday (>= 0.8, < 0.10) + jwt (~> 1.0) + multi_json (~> 1.3) + multi_xml (~> 0.5) + rack (~> 1.2) omniauth (1.2.2) hashie (>= 1.2, < 4) rack (~> 1.0) + omniauth-instagram (1.0.1) + omniauth (~> 1.0) + omniauth-oauth2 (~> 1.0) + omniauth-oauth2 (1.3.1) + oauth2 (~> 1.0) + omniauth (~> 1.2) pg (0.18.2) pry (0.10.1) coderay (~> 1.1.0) @@ -168,6 +193,7 @@ GEM sdoc (0.4.1) json (~> 1.7, >= 1.7.7) rdoc (~> 4.0) + simple_oauth (0.3.1) simplecov (0.10.0) docile (~> 1.1.0) json (~> 1.8) @@ -187,6 +213,17 @@ GEM tilt (1.4.1) turbolinks (2.5.3) coffee-rails + twitter (5.14.0) + addressable (~> 2.3) + buftok (~> 0.2.0) + equalizer (~> 0.0.9) + faraday (~> 0.9.0) + http (~> 0.6.0) + http_parser.rb (~> 0.6.0) + json (~> 1.8) + memoizable (~> 0.4.0) + naught (~> 1.0) + simple_oauth (~> 0.3.0) tzinfo (1.2.2) thread_safe (~> 0.1) uglifier (2.7.1) @@ -213,6 +250,7 @@ DEPENDENCIES jbuilder (~> 2.0) jquery-rails omniauth + omniauth-instagram pg pry-rails rails (= 4.2.3) @@ -223,6 +261,7 @@ DEPENDENCIES spring sqlite3 turbolinks + twitter uglifier (>= 1.3.0) web-console (~> 2.0) diff --git a/app/assets/images/notso-2.png b/app/assets/images/notso-2.png new file mode 100644 index 0000000..867f4dc Binary files /dev/null and b/app/assets/images/notso-2.png differ diff --git a/app/assets/javascripts/instagram_posts.coffee b/app/assets/javascripts/instagram_posts.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/instagram_posts.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/instagrams.coffee b/app/assets/javascripts/instagrams.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/instagrams.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/javascripts/tweets.coffee b/app/assets/javascripts/tweets.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/app/assets/javascripts/tweets.coffee @@ -0,0 +1,3 @@ +# Place all the behaviors and hooks related to the matching controller here. +# All this logic will automatically be available in application.js. +# You can use CoffeeScript in this file: http://coffeescript.org/ diff --git a/app/assets/stylesheets/custom.css.scss b/app/assets/stylesheets/custom.css.scss index baccee2..471f7d0 100644 --- a/app/assets/stylesheets/custom.css.scss +++ b/app/assets/stylesheets/custom.css.scss @@ -1,2 +1,21 @@ @import "bootstrap-sprockets"; @import "bootstrap"; + +.content { + padding-top: 35px; + // background: image-url("notso-2.png") no-repeat; +} + +.center { + text-align: center; + margin: auto; + float: none; +} + +.header { + font-size: 60px; +} + +a:hover { + text-decoration: none; +} diff --git a/app/assets/stylesheets/instagram_posts.scss b/app/assets/stylesheets/instagram_posts.scss new file mode 100644 index 0000000..a553731 --- /dev/null +++ b/app/assets/stylesheets/instagram_posts.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the InstagramPosts controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/instagrams.scss b/app/assets/stylesheets/instagrams.scss new file mode 100644 index 0000000..deda276 --- /dev/null +++ b/app/assets/stylesheets/instagrams.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Instagrams controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/assets/stylesheets/tweets.scss b/app/assets/stylesheets/tweets.scss new file mode 100644 index 0000000..5f231b3 --- /dev/null +++ b/app/assets/stylesheets/tweets.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the twitters controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index d83690e..22a6f52 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,5 +1,27 @@ +require 'twitter' +require 'twit' + class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + before_filter :twit + + INSTAGRAM_URI = "https://api.instagram.com/v1/" + MESSAGES = { + no_username: "There's no user by that name. Search again.", + success: "Success!", + follow_error: "Oops. Something went wrong.", + login_required: "You have to be logged in to do that!" + } + + def twit + @twitter ||= Twit.new + end + + def require_login + unless session[:user_id] + redirect_to root_path, flash: { error: MESSAGES[:login_required] } + end + end end diff --git a/app/controllers/feeds_controller.rb b/app/controllers/feeds_controller.rb index b1c6e94..8569a18 100644 --- a/app/controllers/feeds_controller.rb +++ b/app/controllers/feeds_controller.rb @@ -1,7 +1,42 @@ +require 'httparty' + class FeedsController < ApplicationController def index @user = User.find_by(id: session[:user_id]) - + @posts = [] + if @user && @user.instagrams + @user.instagram_posts.each do |post| + @posts << HTTParty.get(INSTAGRAM_URI + "media/" + post.post_id + "?access_token=#{session[:access_token]}") + end + @posts.sort_by! { |post| post["data"]["created_time"]} + @posts.reverse! + end + + if @user && @user.tweets + @people = [] + # @people << Instagram.find(@user.instagram_ids) + @people << Tweet.find(@user.tweet_ids) + @people.flatten! + @people.sort_by! { |person| person.username.downcase } + + @feed = [] + @people.each do |person| + username = person.username + @feed << @twitter.client.user_timeline(username, count: 10) + @feed.flatten! + @feed.sort_by { |tweet| tweet.created_at.strftime("%m/%d/%Y") } + end + end + end + + def search; end + + def people + @user = User.find_by(id: session[:user_id]) + if @user + @people = Instagram.find(@user.instagram_ids) + Tweet.find(@user.tweet_ids) + end end + end diff --git a/app/controllers/instagram_posts_controller.rb b/app/controllers/instagram_posts_controller.rb new file mode 100644 index 0000000..290a04b --- /dev/null +++ b/app/controllers/instagram_posts_controller.rb @@ -0,0 +1,42 @@ +require 'httparty' + +class InstagramPostsController < ApplicationController + before_action :require_login, only: [:create] + + def create + user = User.find_by(id: session[:user_id]) + if user && user.instagrams + get_posts(user) + + # Check if db table includes each post; + # if not, create new InstagramPost + @all_posts.each do |post| + params[:instagram_post] = { + post_id: post[:post_id], + instagram_id: post[:ig_id] + } + InstagramPost.find_or_create_by(instagram_post_params) + end + end + redirect_to root_path + end + + private + + def get_posts(user) + # Get an array of all recent post ids associated with + # all followed instagram users + @all_posts = [] + user.instagrams.each do |gram| + ig_user_posts = HTTParty.get(INSTAGRAM_URI + "users/#{gram.provider_id}/media/recent?count=10&access_token=#{session[:access_token]}") + all_post_ids = ig_user_posts["data"].each do |post| + @all_posts << { ig_id: gram.id, post_id: post["id"]} + end + end + return @all_posts + end + + def instagram_post_params + params.require(:instagram_post).permit(:post_id, :instagram_id) + end +end diff --git a/app/controllers/instagrams_controller.rb b/app/controllers/instagrams_controller.rb new file mode 100644 index 0000000..6de14ce --- /dev/null +++ b/app/controllers/instagrams_controller.rb @@ -0,0 +1,52 @@ +require 'httparty' + +class InstagramsController < ApplicationController + before_action :require_login, only: [:create] + + CALLBACK_URL = "http://localhost:3000/auth/instagram/callback" + + def search + if params[:instagram].present? + instagram_search = params[:instagram][:username] + response = HTTParty.get(INSTAGRAM_URI + "users/search?q=#{instagram_search}&client_id=#{ENV["INSTAGRAM_ID"]}") + + @users = response["data"] + + return render "feeds/search" + else + redirect_to root_path, flash: { error: MESSAGES[:no_username] } + end + end + + def create + @instagram_person = Instagram.find_or_create_by(instagram_params) + @person = @instagram_person.username + @instagram_person.users << User.find(session[:user_id]) + + if @instagram_person.save + return redirect_to root_path, flash: { alert: MESSAGES[:success] } + else + return render "feeds/search", flash: { error: MESSAGES[:follow_error] } + end + redirect_to search_path + # add flash: no search results were found for 'username' + end + + def destroy + user = User.find_by(id: session[:user_id]) + instagramer = Instagram.find(params[:id]) + + if instagramer + user.instagrams.destroy(instagramer) + end + + redirect_to people_path, flash: { alert: MESSAGES[:success] } + end + + private + + def instagram_params + params.require(:instagram).permit(:username, :provider_id, :image_url) + end + +end diff --git a/app/controllers/sessions_controller.rb b/app/controllers/sessions_controller.rb index 4fa18c1..3d89388 100644 --- a/app/controllers/sessions_controller.rb +++ b/app/controllers/sessions_controller.rb @@ -3,9 +3,12 @@ class SessionsController < ApplicationController def create auth_hash = request.env['omniauth.auth'] || params - @user = User.find_or_create_from_omniauth_developer(auth_hash) + @user = User.find_or_create_from_omniauth(auth_hash) session[:user_id] = @user.id + # NOTE: SM added. Gets the user's access_token from Instagram + session[:access_token] = auth_hash[:credentials][:token] + redirect_to root_path end diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb new file mode 100644 index 0000000..51dade0 --- /dev/null +++ b/app/controllers/tweets_controller.rb @@ -0,0 +1,47 @@ +class TweetsController < ApplicationController + before_action :require_login, only: [:create] + + def search + if params[:tweet].present? + username = params[:tweet][:username] + @users = @twitter.client.user_search(username) + # @feed = @twitter.client.user_timeline(username, count: 1) + return render "feeds/search" + else + redirect_to root_path, flash: { error: MESSAGES[:no_username] } + end + end + + def create + @twitter_person = Tweet.find_or_create_by(tweet_params) + @person = @twitter_person.username + @twitter_person.users << User.find(session[:user_id]) + + if @twitter_person.save + return redirect_to root_path, flash: { alert: MESSAGES[:success] } + else + return render "feeds/search", flash: { error: MESSAGES[:follow_error] } + end + end + + def destroy + user = User.find_by(id: session[:user_id]) + tweeter = Tweet.find(params[:id]) + + if tweeter + user.tweets.destroy(tweeter) + end + + redirect_to people_path, flash: { alert: MESSAGES[:success] } + end + + private + + def tweet_params + params.require(:tweet).permit( + :username, + :provider_id, + :image_url + ) + end +end diff --git a/app/helpers/instagram_posts_helper.rb b/app/helpers/instagram_posts_helper.rb new file mode 100644 index 0000000..270b7fc --- /dev/null +++ b/app/helpers/instagram_posts_helper.rb @@ -0,0 +1,2 @@ +module InstagramPostsHelper +end diff --git a/app/helpers/instagrams_helper.rb b/app/helpers/instagrams_helper.rb new file mode 100644 index 0000000..0eb63c9 --- /dev/null +++ b/app/helpers/instagrams_helper.rb @@ -0,0 +1,2 @@ +module InstagramsHelper +end diff --git a/app/helpers/tweets_helper.rb b/app/helpers/tweets_helper.rb new file mode 100644 index 0000000..6b7d657 --- /dev/null +++ b/app/helpers/tweets_helper.rb @@ -0,0 +1,2 @@ +module TweetsHelper +end diff --git a/app/models/instagram.rb b/app/models/instagram.rb new file mode 100644 index 0000000..7a9b8c0 --- /dev/null +++ b/app/models/instagram.rb @@ -0,0 +1,9 @@ +class Instagram < ActiveRecord::Base + # Associations ---------------------------------------------- + has_and_belongs_to_many :users + has_many :instagram_posts + + # Validations ---------------------------------------------- + validates :username, :provider_id, presence: true, uniqueness: true + +end diff --git a/app/models/instagram_post.rb b/app/models/instagram_post.rb new file mode 100644 index 0000000..396771e --- /dev/null +++ b/app/models/instagram_post.rb @@ -0,0 +1,9 @@ +class InstagramPost < ActiveRecord::Base + belongs_to :instagram + has_many :users, through: :instagram + + # Validations ----------- + validates :post_id, presence: true, uniqueness: true + validates :instagram_id, presence: true + +end diff --git a/app/models/tweet.rb b/app/models/tweet.rb new file mode 100644 index 0000000..9543931 --- /dev/null +++ b/app/models/tweet.rb @@ -0,0 +1,7 @@ +class Tweet < ActiveRecord::Base + # Associations ---------------------------------------------- + has_and_belongs_to_many :users + + # Validations ---------------------------------------------- + validates :username, :provider_id, presence: true, uniqueness: true +end diff --git a/app/models/user.rb b/app/models/user.rb index acc1e86..54cf1ff 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,13 +1,15 @@ class User < ActiveRecord::Base + # Associations ---------------------------------------------- + has_and_belongs_to_many :instagrams + has_and_belongs_to_many :tweets + has_many :instagram_posts, through: :instagrams - # Validations ---------------------------------------------- - # validates :email, presence: true - # validates :username, presence: true - validates :uid, presence: true - validates :provider, presence: true + # Validations ---------------------------------------------- + validates :username, :uid, :provider, presence: true + validates :username, :uid, uniqueness: true - def self.find_or_create_from_omniauth_developer(auth_hash) + def self.find_or_create_from_omniauth(auth_hash) uid = auth_hash[:uid] provider = auth_hash[:provider] diff --git a/app/views/feeds/index.html.erb b/app/views/feeds/index.html.erb index 1d00cf6..a732502 100644 --- a/app/views/feeds/index.html.erb +++ b/app/views/feeds/index.html.erb @@ -1,7 +1,49 @@ -

I can haz feedz?

+
+

NinjaPartay

+ <% if session[:user_id].nil? %> +

Choose Your Weapon

+ <%= link_to login_path(provider: :instagram) do %> + + <% end %> + <%= link_to login_path(provider: :twitter) do %> + + <% end %> + <% else %> + <% if @user %> +

<%= @user.username %>

+ <% end %> + <%= link_to "Follow People", search_path, class: "btn btn-primary" %> + + <%= link_to "Assasinate", people_path, class: "btn btn-primary" %> -<%= link_to "Login", login_path, class: "btn btn-primary" %> -<%= link_to "Logout", logout_path, method: :delete, class: "btn btn-danger" %> -<% unless @user.nil? %> - <%= @user.username %> -<% end %> + <%= render partial: "instagrams/refresh_feed" %> + + <% if @posts %> + <% @posts.each do |post| %> +
+
+
+ <%= link_to image_tag(post["data"]["images"]["standard_resolution"]["url"]), post["data"]["link"] %> +
+
Posted by: <%= post["data"]["user"]["username"] %>
+ <% unless post["data"]["caption"].nil? %> +

<%= post["data"]["caption"]["text"] %>

+ <% end %> + <%= Time.at(post["data"]["created_time"].to_i).strftime("%m/%d/%Y at %I:%M%p") %> +
+
+
+
+ <% end %> + <% end %> + <% end %> + + + <% if @user %> + <% @feed.each do |tweet| %> +

<%= tweet.text %> + <%= tweet.created_at %>

+ <% end %> + <% end %> + +
diff --git a/app/views/feeds/people.html.erb b/app/views/feeds/people.html.erb new file mode 100644 index 0000000..67834ee --- /dev/null +++ b/app/views/feeds/people.html.erb @@ -0,0 +1,13 @@ + +

Assassinate

+ +<% unless session[:user_id].nil? %> +

Targets

+ <% @people.each do |person| %> +

+ <%= person.username %> + <%= image_tag(person.image_url) %> + <%= link_to "X", polymorphic_path(person.class.name.downcase, id: person.id), method: :delete, class: "btn btn-danger" %> +

+ <% end %> +<% end %> diff --git a/app/views/feeds/search.html.erb b/app/views/feeds/search.html.erb new file mode 100644 index 0000000..81771e4 --- /dev/null +++ b/app/views/feeds/search.html.erb @@ -0,0 +1,23 @@ + +
+ <%= render partial: "shared/search_form", + locals: { + provider: :instagram, + url: search_instagram_url + } %> + + <%= render partial: "shared/search_form", + locals: { + provider: :tweet, + url: search_twitter_url + } %> + + + + <% if params[:instagram] %> + <%= render template: "instagrams/search", layout: false %> + <% elsif params[:tweet] %> + <%= render template: "tweets/search", layout: false %> + <% end %> + +
diff --git a/app/views/instagrams/_refresh_feed.html.erb b/app/views/instagrams/_refresh_feed.html.erb new file mode 100644 index 0000000..421b7f8 --- /dev/null +++ b/app/views/instagrams/_refresh_feed.html.erb @@ -0,0 +1 @@ +<%= link_to "Refresh Instagram Feed", { controller: :instagram_posts, action: :create }, method: :post, class: "btn btn-primary" %> diff --git a/app/views/instagrams/search.html.erb b/app/views/instagrams/search.html.erb new file mode 100644 index 0000000..f752e0a --- /dev/null +++ b/app/views/instagrams/search.html.erb @@ -0,0 +1,8 @@ +<% @users.each do |user| %> +

+ Name: <%= user["full_name"] %>
+ Username: <%= user["username"] %>
+ <%= image_tag(user["profile_picture"]) %> + <%= link_to "+", { controller: :instagrams, action: :create, instagram: { username: user["username"], provider_id: user["id"], image_url: user["profile_picture"] } }, method: :post, class: "btn btn-primary" %> +

+<% end %> diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index b323c2a..698fe84 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,14 +1,23 @@ - C3ProjectsSeemore + Ninja Feed <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> + + <%= render "shared/nav" %> + <% if flash[:error] %> +
<%= flash[:error] %>
+ <% end %> -<%= yield %> - + <% if flash[:alert] %> +
<%= flash[:alert] %>
+ <% end %> +
+ <%= yield %> +
diff --git a/app/views/shared/_nav.html.erb b/app/views/shared/_nav.html.erb new file mode 100644 index 0000000..1227da4 --- /dev/null +++ b/app/views/shared/_nav.html.erb @@ -0,0 +1,13 @@ + + diff --git a/app/views/shared/_search_form.html.erb b/app/views/shared/_search_form.html.erb new file mode 100644 index 0000000..ff7c9c2 --- /dev/null +++ b/app/views/shared/_search_form.html.erb @@ -0,0 +1,7 @@ +<%= form_for provider, url: url, html: { class: "form-inline" } do |f| %> + <%= f.label :username, "Search #{provider.to_s.titlecase} Users" %> + <%= f.text_field :username, placeholder: "Search Users", class: "form-control form-group" %> + <%= button_tag(type: 'submit', class: "btn btn-primary search") do %> + + <% end %> +<% end %> diff --git a/app/views/tweets/search.html.erb b/app/views/tweets/search.html.erb new file mode 100644 index 0000000..9727d65 --- /dev/null +++ b/app/views/tweets/search.html.erb @@ -0,0 +1,8 @@ +<% @users.each do |user| %> +

+ Name: <%= user.name %>
+ Username: <%= user.screen_name %>
+ <%= image_tag(user.profile_image_url(size = :bigger)) %> + <%= link_to "+", { controller: :tweets, action: :create, tweet: { username: user.screen_name, provider_id: user.id, image_url: user.profile_image_url(size = :bigger) } }, method: :post, class: "btn btn-primary" %> +

+<% end %> diff --git a/config/initializers/omniauth.rb b/config/initializers/omniauth.rb index 482aac0..caa7c91 100644 --- a/config/initializers/omniauth.rb +++ b/config/initializers/omniauth.rb @@ -1,3 +1,4 @@ Rails.application.config.middleware.use OmniAuth::Builder do provider :developer + provider :instagram, ENV['INSTAGRAM_ID'], ENV['INSTAGRAM_SECRET'] end diff --git a/config/routes.rb b/config/routes.rb index f34dcf1..b2d55ae 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,14 +1,26 @@ Rails.application.routes.draw do + root "feeds#index" - get "auth/:provider/callback" => 'sessions#create' + get "search/twitter" => 'tweets#search' + post "search/twitter", to: "tweets#search" - # Below allows you to login with developer. - # Will need routes for each third party you can login through? - get "auth/developer" => 'sessions#create', as: 'login' + get "search/instagram" => 'instagrams#search' + post "search/instagram", to: "instagrams#search" - delete "logout" => 'sessions#destroy' + get "/search", to: "feeds#search", as: "search" + get "/people", to: "feeds#people", as: "people" + + get "auth/:provider/callback" => 'sessions#create' # post is here for OmniAuth developer strategy post "auth/:provider/callback" => 'sessions#create' + get "auth/:provider" => 'sessions#create', as: 'login' + delete "logout" => 'sessions#destroy' + + resources :instagrams, only: [:create, :destroy] + resources :tweets, only: [:create, :destroy] + resources :instagram_posts, only: [:create] + + end diff --git a/db/migrate/20150805222548_create_instagrams.rb b/db/migrate/20150805222548_create_instagrams.rb new file mode 100644 index 0000000..5a9abc5 --- /dev/null +++ b/db/migrate/20150805222548_create_instagrams.rb @@ -0,0 +1,8 @@ +class CreateInstagrams < ActiveRecord::Migration + def change + create_table :instagrams do |t| + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20150805222847_add_columns_to_instagrams.rb b/db/migrate/20150805222847_add_columns_to_instagrams.rb new file mode 100644 index 0000000..cc98015 --- /dev/null +++ b/db/migrate/20150805222847_add_columns_to_instagrams.rb @@ -0,0 +1,9 @@ +class AddColumnsToInstagrams < ActiveRecord::Migration + def change + add_column :instagrams, :username, :string + add_column :instagrams, :provider_id, :string + + change_column :instagrams, :username, :string, :null => false + change_column :instagrams, :provider_id, :string, :null => false + end +end diff --git a/db/migrate/20150806212209_create_tweets.rb b/db/migrate/20150806212209_create_tweets.rb new file mode 100644 index 0000000..5b5da45 --- /dev/null +++ b/db/migrate/20150806212209_create_tweets.rb @@ -0,0 +1,10 @@ +class CreateTweets < ActiveRecord::Migration + def change + create_table :tweets do |t| + t.string :username, null: false + t.string :provider_id, null: false + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20150806213716_create_join_table.rb b/db/migrate/20150806213716_create_join_table.rb new file mode 100644 index 0000000..bbf4f05 --- /dev/null +++ b/db/migrate/20150806213716_create_join_table.rb @@ -0,0 +1,8 @@ +class CreateJoinTable < ActiveRecord::Migration + def change + create_join_table :instagrams, :users do |t| + t.index :instagram_id + t.index :user_id + end + end +end diff --git a/db/migrate/20150806214114_create_join_table_tweets_users.rb b/db/migrate/20150806214114_create_join_table_tweets_users.rb new file mode 100644 index 0000000..8825f87 --- /dev/null +++ b/db/migrate/20150806214114_create_join_table_tweets_users.rb @@ -0,0 +1,8 @@ +class CreateJoinTableTweetsUsers < ActiveRecord::Migration + def change + create_join_table :tweets, :users do |t| + t.index :tweet_id + t.index :user_id + end + end +end diff --git a/db/migrate/20150807203523_add_image_url_to_tweets.rb b/db/migrate/20150807203523_add_image_url_to_tweets.rb new file mode 100644 index 0000000..97aa8bc --- /dev/null +++ b/db/migrate/20150807203523_add_image_url_to_tweets.rb @@ -0,0 +1,5 @@ +class AddImageUrlToTweets < ActiveRecord::Migration + def change + add_column :tweets, :image_url, :string + end +end diff --git a/db/migrate/20150807203555_add_image_url_to_instagrams.rb b/db/migrate/20150807203555_add_image_url_to_instagrams.rb new file mode 100644 index 0000000..bb1e0fe --- /dev/null +++ b/db/migrate/20150807203555_add_image_url_to_instagrams.rb @@ -0,0 +1,5 @@ +class AddImageUrlToInstagrams < ActiveRecord::Migration + def change + add_column :instagrams, :image_url, :string + end +end diff --git a/db/migrate/20150809210224_create_instagram_posts.rb b/db/migrate/20150809210224_create_instagram_posts.rb new file mode 100644 index 0000000..e506b0a --- /dev/null +++ b/db/migrate/20150809210224_create_instagram_posts.rb @@ -0,0 +1,11 @@ +class CreateInstagramPosts < ActiveRecord::Migration + def change + create_table :instagram_posts do |t| + t.string :post_id, null: false + t.integer :instagram_id, null: false + t.index :instagram_id + + t.timestamps null: false + end + end +end diff --git a/db/schema.rb b/db/schema.rb index 4e0dbb4..916f203 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,48 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150804213804) do +ActiveRecord::Schema.define(version: 20150809210224) do + + create_table "instagram_posts", force: :cascade do |t| + t.string "post_id", null: false + t.integer "instagram_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + + add_index "instagram_posts", ["instagram_id"], name: "index_instagram_posts_on_instagram_id" + + create_table "instagrams", force: :cascade do |t| + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "username", null: false + t.string "provider_id", null: false + t.string "image_url" + end + + create_table "instagrams_users", id: false, force: :cascade do |t| + t.integer "instagram_id", null: false + t.integer "user_id", null: false + end + + add_index "instagrams_users", ["instagram_id"], name: "index_instagrams_users_on_instagram_id" + add_index "instagrams_users", ["user_id"], name: "index_instagrams_users_on_user_id" + + create_table "tweets", force: :cascade do |t| + t.string "username", null: false + t.string "provider_id", null: false + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + t.string "image_url" + end + + create_table "tweets_users", id: false, force: :cascade do |t| + t.integer "tweet_id", null: false + t.integer "user_id", null: false + end + + add_index "tweets_users", ["tweet_id"], name: "index_tweets_users_on_tweet_id" + add_index "tweets_users", ["user_id"], name: "index_tweets_users_on_user_id" create_table "users", force: :cascade do |t| t.string "username" diff --git a/lib/twit.rb b/lib/twit.rb new file mode 100644 index 0000000..ff8858f --- /dev/null +++ b/lib/twit.rb @@ -0,0 +1,13 @@ +class Twit + attr_reader :client + + def initialize + @client = Twitter::REST::Client.new do |config| + config.consumer_key = ENV['TWITTER_CONSUMER_KEY'] + config.consumer_secret = ENV['TWITTER_CONSUMER_SECRET'] + config.access_token = ENV['TWITTER_ACCESS_TOKEN'] + config.access_token_secret = ENV['TWITTER_ACCESS_TOKEN_SECRET'] + end + end + +end diff --git a/spec/controllers/instagram_posts_controller_spec.rb b/spec/controllers/instagram_posts_controller_spec.rb new file mode 100644 index 0000000..4821df1 --- /dev/null +++ b/spec/controllers/instagram_posts_controller_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe InstagramPostsController, type: :controller do + +end diff --git a/spec/controllers/instagrams_controller_spec.rb b/spec/controllers/instagrams_controller_spec.rb new file mode 100644 index 0000000..a3b2e17 --- /dev/null +++ b/spec/controllers/instagrams_controller_spec.rb @@ -0,0 +1,56 @@ +require 'rails_helper' +require 'pry' + +RSpec.describe InstagramsController, type: :controller do + + describe "POST #create" do + context "valid params" do + before :each do + @user = create :user + session[:user_id] = @user.id + post :create, instagram: attributes_for(:instagram) + end + + it "creates an instagram record" do + expect(Instagram.count).to eq(1) + expect(Instagram.first.username).to eq("Ada") + end + + it "associates with a user" do + expect(Instagram.first.user_ids).to include(@user.id) + end + + it "redirects to root_path" do + expect(response).to redirect_to(root_path) + expect(response).to have_http_status(302) + end + + + end + + context "invalid params" do + before :each do + user = create :user + session[:user_id] = user.id + post :create, instagram: attributes_for(:instagram, username: nil) + end + + it "invalid attributes fail validations" do + bad_instagram_record = Instagram.create(attributes_for(:instagram, username: nil, user_ids: [1])) + expect(bad_instagram_record).to_not be_valid + expect(bad_instagram_record.errors).to include(:username) + end + + it "does not create a instagram record" do + expect(Instagram.count).to eq 0 + end + + it "renders the feeds/search view" do + expect(response).to render_template("feeds/search") + end + end + + + end# post create + +end# controller diff --git a/spec/controllers/tweets_controller_spec.rb b/spec/controllers/tweets_controller_spec.rb new file mode 100644 index 0000000..fc54c22 --- /dev/null +++ b/spec/controllers/tweets_controller_spec.rb @@ -0,0 +1,49 @@ +require 'rails_helper' + +RSpec.describe TweetsController, type: :controller do + describe "POST #create" do + context "valid params" do + before :each do + @user = create :user + session[:user_id] = @user.id + post :create, tweet: attributes_for(:tweet) + end + + it "creates a twitter record" do + expect(Tweet.count).to eq(1) + expect(Tweet.first.username).to eq("Ada") + end + + it "associates with a user" do + expect(Tweet.first.user_ids).to include(@user.id) + end + + it "redirects to root_path" do + expect(response).to redirect_to(root_path) + expect(response).to have_http_status(302) + end + end + + context "invalid params" do + before :each do + user = create :user + session[:user_id] = user.id + post :create, tweet: attributes_for(:tweet, username: nil) + end + + it "invalid attributes fail validations" do + bad_tweet_record = Tweet.create(attributes_for(:tweet, username: nil, user_ids: [1])) + expect(bad_tweet_record).to_not be_valid + expect(bad_tweet_record.errors).to include(:username) + end + + it "does not create a tweet record" do + expect(Tweet.count).to eq 0 + end + + it "renders the feeds/search view" do + expect(response).to render_template("feeds/search") + end + end + end +end diff --git a/spec/factories.rb b/spec/factories.rb index e69de29..46e2bd9 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -0,0 +1,23 @@ +FactoryGirl.define do factory :instagram_post do + + end + + factory :user do + email "a@b.com" + username "Ada" + uid "12345" + provider "developer" + end + + factory :instagram do + username "Ada" + provider_id "1357" + user_ids [1, 2] + end + + factory :tweet do + username "Ada" + provider_id "1357" + user_ids [1, 2] + end +end diff --git a/spec/helpers/feeds_helper_spec.rb b/spec/helpers/feeds_helper_spec.rb deleted file mode 100644 index 89a42c0..0000000 --- a/spec/helpers/feeds_helper_spec.rb +++ /dev/null @@ -1,15 +0,0 @@ -require 'rails_helper' - -# Specs in this file have access to a helper object that includes -# the FeedsHelper. For example: -# -# describe FeedsHelper do -# describe "string concat" do -# it "concats two strings with spaces" do -# expect(helper.concat_strings("this","that")).to eq("this that") -# end -# end -# end -RSpec.describe FeedsHelper, type: :helper do - pending "add some examples to (or delete) #{__FILE__}" -end diff --git a/spec/models/instagram_post_spec.rb b/spec/models/instagram_post_spec.rb new file mode 100644 index 0000000..7cc6155 --- /dev/null +++ b/spec/models/instagram_post_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe InstagramPost, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/instagram_spec.rb b/spec/models/instagram_spec.rb new file mode 100644 index 0000000..5529e62 --- /dev/null +++ b/spec/models/instagram_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Instagram, type: :model do + + it_behaves_like "a provider" + +end diff --git a/spec/models/tweet_spec.rb b/spec/models/tweet_spec.rb new file mode 100644 index 0000000..86bed32 --- /dev/null +++ b/spec/models/tweet_spec.rb @@ -0,0 +1,7 @@ +require 'rails_helper' + +RSpec.describe Tweet, type: :model do + + it_behaves_like "a provider" + +end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 76a5223..c578de2 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,58 +1,94 @@ require 'rails_helper' RSpec.describe User, type: :model do - let(:user) { - User.new( - email: "a@b.com", - username: "Ada", - uid: "1234", - provider: "developer" - ) - } - - describe "validations" do - it "is valid" do - expect(user).to be_valid - end - # it "requires an email" do - # user.email = nil - # expect(user).to be_invalid - # end + context "user logging in with developer 'strategy'" do + describe "validations" do + let(:dev_user) { build :user } + + it "is valid" do + expect(dev_user).to be_valid + end + + it "requires a username" do + dev_user.username = nil + expect(dev_user).to be_invalid + end - # it "requires a username" do - # user.username = nil - # expect(user).to be_invalid - # end + it "requires a uid" do + dev_user.uid = nil + expect(dev_user).to be_invalid + end - it "requires a uid" do - user.uid = nil - expect(user).to be_invalid + it "requires a provider" do + dev_user.provider = nil + expect(dev_user).to be_invalid + end end - it "requires a provider" do - user.provider = nil - expect(user).to be_invalid + + describe ".initialize_from_omniauth" do + let(:user) { + User.find_or_create_from_omniauth(OmniAuth.config.mock_auth[:developer]) + } + + it "creates a valid user" do + expect(user).to be_valid + end + + context "when it's invalid" do + it "returns nil" do + user = User.find_or_create_from_omniauth({ + uid: "123", info: { } + }) + expect(user).to be_nil + end + end end end + context "user logging in with instagram" do + describe "validations" do + let(:ig_user) { build :user, provider: :instagram } - describe ".initialize_from_omniauth" do - let(:user) { - User.find_or_create_from_omniauth(OmniAuth.config.mock_auth[:developer]) - } + it "is valid" do + expect(ig_user).to be_valid + end - it "creates a valid user" do - expect(user).to be_valid + it "requires a username" do + ig_user.username = nil + expect(ig_user).to be_invalid + end + + it "requires a uid" do + ig_user.uid = nil + expect(ig_user).to be_invalid + end + + it "requires a provider" do + ig_user.provider = nil + expect(ig_user).to be_invalid + end end - # context "when it's invalid" do - # it "returns nil" do - # user = User.find_or_create_from_omniauth({ - # "uid" => "123", "info" => {} - # }) - # expect(user).to be_nil - # end - # end + describe ".initialize_from_omniauth" do + let(:user) { + User.find_or_create_from_omniauth(OmniAuth.config.mock_auth[:instagram]) + } + + it "creates a valid user" do + expect(user).to be_valid + end + + context "when it's invalid" do + it "returns nil" do + user = User.find_or_create_from_omniauth({ + uid: "123", info: { } + }) + expect(user).to be_nil + end + end + end end + end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index d5992da..b24ca8c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,5 +1,6 @@ require 'factory_girl' require 'simplecov' +require 'rails_helper' SimpleCov.start 'rails' # This file was generated by the `rails generate rspec:install` command. Conventionally, all @@ -37,11 +38,18 @@ OmniAuth.config.mock_auth[:developer] = OmniAuth::AuthHash.new({ :provider => 'developer', - :uid => '123545', - info: {email: "a@b.com", nickname: "Ada"} + :uid => '12345', + info: {email: "a@b.com", name: "Ada"} + }) + OmniAuth.config.mock_auth[:instagram] = OmniAuth::AuthHash.new({ + :provider => 'instagram', + :uid => '12345', + info: {email: "a@b.com", name: "Ada"} }) end + Dir["./spec/support/**/*.rb"].sort.each { |f| require f} + config.include FactoryGirl::Syntax::Methods # rspec-expectations config goes here. You can use an alternate # assertion/expectation library such as wrong or the stdlib/minitest diff --git a/spec/support/provider_spec.rb b/spec/support/provider_spec.rb new file mode 100644 index 0000000..c17e0d1 --- /dev/null +++ b/spec/support/provider_spec.rb @@ -0,0 +1,49 @@ +require 'spec_helper' + +RSpec.shared_examples "a provider" do + + describe "model validations" do + before :each do + user1 = create :user + user2 = create :user, username: "Ada2", uid: "34524" + end + + it "requires a username" do + provider = build described_class, username: nil + + expect(provider).not_to be_valid + expect(provider.errors.keys).to include(:username) + end + + it "username must be unique" do + create described_class + provider2 = build described_class + + expect(provider2).not_to be_valid + expect(described_class.count).to be 1 + expect(provider2.errors.keys).to include(:username) + end + + it "requires a provider_id" do + provider = build described_class, provider_id: nil + + expect(provider).not_to be_valid + expect(provider.errors.keys).to include(:provider_id) + end + + it "provider_id must be unique" do + create described_class + provider2 = build described_class + + expect(provider2).not_to be_valid + expect(described_class.count).to be 1 + expect(provider2.errors.keys).to include(:provider_id) + end + + it "validates a valid record" do + provider = create described_class + expect(described_class.count).to be 1 + expect(described_class.all).to include(provider) + end + end +end