├── log └── .keep ├── app ├── mailers │ └── .keep ├── models │ ├── .keep │ ├── concerns │ │ ├── .keep │ │ └── reaction_gifs.rb │ ├── lastgif.rb │ ├── gifvotes.rb │ ├── tagline.rb │ ├── team.rb │ ├── gif.rb │ ├── teamgif.rb │ └── user.rb ├── assets │ ├── images │ │ ├── .keep │ │ └── mochaGrunge.png │ ├── javascripts │ │ ├── dashboard.coffee │ │ └── application.js.coffee │ └── stylesheets │ │ ├── dashboard.scss │ │ ├── search.scss │ │ ├── global.scss │ │ ├── home.scss │ │ ├── application.scss │ │ ├── nprogress.scss │ │ └── toastr.scss ├── controllers │ ├── concerns │ │ └── .keep │ ├── home_controller.rb │ ├── api │ │ ├── base_controller.rb │ │ └── gifs_controller.rb │ ├── dashboard_controller.rb │ ├── teams_controller.rb │ ├── users │ │ ├── unlocks_controller.rb │ │ ├── sessions_controller.rb │ │ ├── confirmations_controller.rb │ │ ├── passwords_controller.rb │ │ ├── omniauth_callbacks_controller.rb │ │ └── registrations_controller.rb │ ├── application_controller.rb │ └── search_controller.rb ├── views │ ├── search │ │ ├── index.html.erb │ │ ├── _nothing_found.html.erb │ │ ├── show.html.erb │ │ └── _images.html.erb │ ├── dashboard │ │ ├── get_token.js.erb │ │ ├── _team_row.html.erb │ │ ├── api.html.erb │ │ ├── settings.html.erb │ │ ├── index.html.erb │ │ ├── _api_data.html.erb │ │ └── _team_table.html.erb │ ├── devise │ │ ├── mailer │ │ │ ├── confirmation_instructions.html.erb │ │ │ ├── unlock_instructions.html.erb │ │ │ └── reset_password_instructions.html.erb │ │ ├── unlocks │ │ │ └── new.html.erb │ │ ├── passwords │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ ├── confirmations │ │ │ └── new.html.erb │ │ ├── sessions │ │ │ └── new.html.erb │ │ ├── registrations │ │ │ ├── new.html.erb │ │ │ └── edit.html.erb │ │ └── shared │ │ │ └── _links.html.erb │ ├── shared │ │ └── _search_form.html.erb │ ├── home │ │ └── index.html.erb │ └── layouts │ │ ├── application.html.erb │ │ ├── _header.html.erb │ │ └── _login_overlay.html.erb ├── helpers │ ├── home_helper.rb │ ├── search_helper.rb │ ├── dashboard_helper.rb │ └── application_helper.rb └── serializers │ └── api_gifs_serializer.rb ├── lib ├── assets │ └── .keep ├── tasks │ └── .keep └── slack_poster.rb ├── public ├── favicon.ico ├── robots.txt ├── 500.html ├── 422.html └── 404.html ├── vendor └── assets │ ├── javascripts │ ├── .keep │ ├── toastr.js │ └── nprogress.js │ └── stylesheets │ └── .keep ├── spec ├── controllers │ ├── teams_controller_spec.rb │ ├── regression │ │ ├── home_controller_spec.rb │ │ ├── devise_controller_spec.rb │ │ ├── devise │ │ │ ├── confirmations_controller_spec.rb │ │ │ ├── omniauth_callbacks_controller_spec.rb │ │ │ ├── unlocks_controller_spec.rb │ │ │ ├── registrations_controller_spec.rb │ │ │ ├── sessions_controller_spec.rb │ │ │ └── passwords_controller_spec.rb │ │ ├── users │ │ │ ├── confirmations_controller_spec.rb │ │ │ ├── omniauth_callbacks_controller_spec.rb │ │ │ ├── unlocks_controller_spec.rb │ │ │ ├── passwords_controller_spec.rb │ │ │ ├── sessions_controller_spec.rb │ │ │ └── registrations_controller_spec.rb │ │ ├── teams_controller_spec.rb │ │ ├── search_controller_spec.rb │ │ └── dashboard_controller_spec.rb │ └── search_spec.rb ├── support │ ├── devise.rb │ ├── url_helpers.rb │ ├── controller_helpers.rb │ └── cassettes │ │ └── SearchController │ │ └── POST_slack │ │ └── when_params_aren_t_correct │ │ └── should_return_No_gifs_found_if_the_query_doesn_t_return_gifs.yml ├── spec_helper.rb ├── routing │ ├── search_routing_spec.rb │ └── dashboard_routing_spec.rb ├── factories │ ├── teams.rb │ └── users.rb ├── rails_helper.rb └── mocks │ ├── tagline_mock.rb │ ├── gif_mock.rb │ ├── teamgif_mock.rb │ ├── team_mock.rb │ └── user_mock.rb ├── dump.rdb ├── .rspec ├── bin ├── bundle ├── rake ├── rails ├── rspec ├── spring └── setup ├── config ├── boot.rb ├── initializers │ ├── ohm.rb │ ├── cookies_serializer.rb │ ├── session_store.rb │ ├── mime_types.rb │ ├── filter_parameter_logging.rb │ ├── backtrace_silencers.rb │ ├── assets.rb │ ├── wrap_parameters.rb │ ├── inflections.rb │ ├── regressor.rb │ └── devise.rb ├── application.yml ├── environment.rb ├── secrets.yml ├── database.yml ├── locales │ ├── en.yml │ └── devise.en.yml ├── routes.rb ├── application.rb └── environments │ ├── test.rb │ ├── development.rb │ └── production.rb ├── db ├── seeds.rb ├── migrate │ ├── 20150515084750_add_index_to_teams.rb │ ├── 20150514182325_add_name_to_users.rb │ ├── 20150528144349_add_token_to_user.rb │ ├── 20150515083616_add_domain_to_teams.rb │ ├── 20150515204058_add_webhook_to_teams.rb │ ├── 20150515083425_add_user_ref_to_teams.rb │ ├── 20150515084532_add_default_votes_to_teamgifs.rb │ ├── 20150514164755_create_teams.rb │ ├── 20150514212601_create_taglines.rb │ ├── 20150514154151_create_gifs.rb │ ├── 20150514165409_create_teamgifs.rb │ └── 20150514181257_devise_create_users.rb ├── seeds │ └── tagline.rb └── schema.rb ├── config.ru ├── Rakefile ├── .gitignore ├── README.rdoc ├── LICENSE ├── Gemfile └── Gemfile.lock /log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/search/index.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /vendor/assets/stylesheets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/helpers/home_helper.rb: -------------------------------------------------------------------------------- 1 | module HomeHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/helpers/search_helper.rb: -------------------------------------------------------------------------------- 1 | module SearchHelper 2 | end 3 | -------------------------------------------------------------------------------- /spec/controllers/teams_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | -------------------------------------------------------------------------------- /dump.rdb: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashrocket/reactif/HEAD/dump.rdb -------------------------------------------------------------------------------- /app/views/dashboard/get_token.js.erb: -------------------------------------------------------------------------------- 1 | $("#user_token").val("<%= @user.token %>"); -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --format documentation 3 | --require spec_helper 4 | --require rails_helper 5 | --format Fuubar 6 | -------------------------------------------------------------------------------- /app/assets/images/mochaGrunge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/slashrocket/reactif/HEAD/app/assets/images/mochaGrunge.png -------------------------------------------------------------------------------- /spec/support/devise.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Devise::TestHelpers, type: :controller 3 | end 4 | -------------------------------------------------------------------------------- /spec/support/url_helpers.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | config.include Rails.application.routes.url_helpers 3 | end 4 | -------------------------------------------------------------------------------- /app/serializers/api_gifs_serializer.rb: -------------------------------------------------------------------------------- 1 | class ApiGifsSerializer < ActiveModel::Serializer 2 | attributes :word, :url 3 | end 4 | -------------------------------------------------------------------------------- /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/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | Dir[Rails.root.join('db', 'seeds', '*.rb')].each do |seed_file| 2 | puts "\n === Load #{File.basename(seed_file)}" 3 | load seed_file 4 | end 5 | -------------------------------------------------------------------------------- /config/initializers/ohm.rb: -------------------------------------------------------------------------------- 1 | require "ohm" 2 | redisurl = ENV['REDIS_URL'] 3 | redisurl = "redis://127.0.0.1:6379" unless redisurl 4 | Ohm.redis = Redic.new(redisurl) 5 | -------------------------------------------------------------------------------- /db/migrate/20150515084750_add_index_to_teams.rb: -------------------------------------------------------------------------------- 1 | class AddIndexToTeams < ActiveRecord::Migration 2 | def change 3 | add_index :teams, :domain 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150514182325_add_name_to_users.rb: -------------------------------------------------------------------------------- 1 | class AddNameToUsers < ActiveRecord::Migration 2 | def change 3 | add_column :users, :name, :text 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require ::File.expand_path('../config/environment', __FILE__) 4 | run Rails.application 5 | -------------------------------------------------------------------------------- /db/migrate/20150528144349_add_token_to_user.rb: -------------------------------------------------------------------------------- 1 | class AddTokenToUser < ActiveRecord::Migration 2 | def change 3 | add_column :users, :token, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/application.yml: -------------------------------------------------------------------------------- 1 | development: 2 | REDIS_URL: redis://127.0.0.1:6379 3 | production: 4 | REDIS_URL: redis://127.0.0.1:6379 5 | test: 6 | REDIS_URL: redis://127.0.0.1:6379 -------------------------------------------------------------------------------- /config/initializers/cookies_serializer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.action_dispatch.cookies_serializer = :json 4 | -------------------------------------------------------------------------------- /db/migrate/20150515083616_add_domain_to_teams.rb: -------------------------------------------------------------------------------- 1 | class AddDomainToTeams < ActiveRecord::Migration 2 | def change 3 | add_column :teams, :domain, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /db/migrate/20150515204058_add_webhook_to_teams.rb: -------------------------------------------------------------------------------- 1 | class AddWebhookToTeams < ActiveRecord::Migration 2 | def change 3 | add_column :teams, :webhook, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /config/initializers/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_reactif_session' 4 | -------------------------------------------------------------------------------- /app/models/lastgif.rb: -------------------------------------------------------------------------------- 1 | class Lastgif < Ohm::Model 2 | attribute :team_domain 3 | attribute :channel 4 | attribute :gif_id 5 | 6 | index :team_domain 7 | index :channel 8 | end 9 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require File.expand_path('../application', __FILE__) 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /app/controllers/home_controller.rb: -------------------------------------------------------------------------------- 1 | class HomeController < ApplicationController 2 | def index 3 | @tagline = Tagline.random 4 | redirect_to dashboard_path if current_user 5 | end 6 | end 7 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | require_relative '../config/boot' 7 | require 'rake' 8 | Rake.application.run 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /db/migrate/20150515083425_add_user_ref_to_teams.rb: -------------------------------------------------------------------------------- 1 | class AddUserRefToTeams < ActiveRecord::Migration 2 | def change 3 | add_reference :teams, :user, index: true, foreign_key: true 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/search/_nothing_found.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Nothing found :(

4 | <%= render partial: 'shared/search_form' %> 5 |
6 |
7 | -------------------------------------------------------------------------------- /db/migrate/20150515084532_add_default_votes_to_teamgifs.rb: -------------------------------------------------------------------------------- 1 | class AddDefaultVotesToTeamgifs < ActiveRecord::Migration 2 | def change 3 | change_column :teamgifs, :votes, :integer, default: 25 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/views/search/show.html.erb: -------------------------------------------------------------------------------- 1 | <% if @found.present? %> 2 | <%= render partial: 'search/images', collection: @found, as: :image %> 3 | <% else %> 4 | <%= render partial: 'search/nothing_found' %> 5 | <% end %> 6 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /db/migrate/20150514164755_create_teams.rb: -------------------------------------------------------------------------------- 1 | class CreateTeams < ActiveRecord::Migration 2 | def change 3 | create_table :teams do |t| 4 | t.string :name 5 | 6 | t.timestamps null: false 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/assets/javascripts/dashboard.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/views/search/_images.html.erb: -------------------------------------------------------------------------------- 1 |
2 | <%= link_to image.url do %> 3 | type="image/gif"> 4 | 5 | 6 | <% end %> 7 |
-------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path("../spring", __FILE__) 4 | rescue LoadError 5 | end 6 | APP_PATH = File.expand_path('../../config/application', __FILE__) 7 | require_relative '../config/boot' 8 | require 'rails/commands' 9 | -------------------------------------------------------------------------------- /app/views/devise/mailer/confirmation_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Welcome <%= @email %>!

2 | 3 |

You can confirm your account email through the link below:

4 | 5 |

<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>

6 | -------------------------------------------------------------------------------- /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 File.expand_path('../config/application', __FILE__) 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /db/migrate/20150514212601_create_taglines.rb: -------------------------------------------------------------------------------- 1 | class CreateTaglines < ActiveRecord::Migration 2 | def change 3 | create_table :taglines do |t| 4 | t.string :header 5 | t.string :query 6 | 7 | t.timestamps null: false 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /app/models/gifvotes.rb: -------------------------------------------------------------------------------- 1 | class Gifvotes < Ohm::Model 2 | attribute :team_domain 3 | attribute :channel 4 | attribute :username 5 | attribute :gif_id 6 | attribute :expiration 7 | 8 | index :team_domain 9 | index :channel 10 | index :username 11 | index :gif_id 12 | end 13 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | RSpec.configure do |config| 2 | 3 | config.expect_with :rspec do |expectations| 4 | 5 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 6 | end 7 | 8 | config.mock_with :rspec do |mocks| 9 | mocks.verify_partial_doubles = true 10 | end 11 | 12 | end 13 | -------------------------------------------------------------------------------- /db/migrate/20150514154151_create_gifs.rb: -------------------------------------------------------------------------------- 1 | class CreateGifs < ActiveRecord::Migration 2 | def change 3 | create_table :gifs do |t| 4 | t.string :word 5 | t.string :url 6 | 7 | t.timestamps null: false 8 | end 9 | add_index :gifs, :word 10 | add_index :gifs, :url 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/helpers/dashboard_helper.rb: -------------------------------------------------------------------------------- 1 | module DashboardHelper 2 | def token_button(user) 3 | if user && user.token.present? 4 | button_to "Get New Token", get_token_path, class: "btn btn-info", remote: true 5 | else 6 | button_to "Get Token", get_token_path, class: "btn btn-success", remote: true 7 | end 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Your account has been locked due to an excessive number of unsuccessful sign in attempts.

4 | 5 |

Click the link below to unlock your account:

6 | 7 |

<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>

8 | -------------------------------------------------------------------------------- /app/assets/stylesheets/dashboard.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the dashboard controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | .dashboard { 6 | background: white; 7 | } 8 | 9 | .dashboard .sidebar { 10 | margin-top: 0px; 11 | padding: 15px 0 0 0; 12 | } -------------------------------------------------------------------------------- /db/migrate/20150514165409_create_teamgifs.rb: -------------------------------------------------------------------------------- 1 | class CreateTeamgifs < ActiveRecord::Migration 2 | def change 3 | create_table :teamgifs do |t| 4 | t.references :gif, index: true, foreign_key: true 5 | t.references :team, index: true, foreign_key: true 6 | t.integer :votes 7 | 8 | t.timestamps null: false 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/dashboard/_team_row.html.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= team.name %> 3 | <%= team.domain %> 4 | <%= team.webhook %> 5 | 6 |
7 | <%= link_to "Edit", "#", class: "btn btn-default" %> 8 | <%= link_to "Delete", team_path(team), method: :delete, class: "btn btn-danger" %> 9 |
10 | 11 | -------------------------------------------------------------------------------- /config/secrets.yml: -------------------------------------------------------------------------------- 1 | development: 2 | secret_key_base: adf9c3864bee999598527eef0f70bb9f949060e4ba6591c0aa7defe61f12bd1586599f844ac9f1aee3f7fee40f5753a7f69a3ace56092999294401cabc21dbd3 3 | 4 | test: 5 | secret_key_base: b8ea7551fea5fb68d15ac38bc8475116887d38fe2de5e5bc13e9d684227641d22e03356c4f50d71c4dd833a8a1243e2e517a9fb01b0c83c810ad5c6c9f3b7dd2 6 | 7 | production: 8 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 9 | -------------------------------------------------------------------------------- /app/assets/stylesheets/search.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the search controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | .search_result { 6 | background: white; 7 | margin: 5px; 8 | box-shadow: 6px 6px 16px 6px rgba(50, 50, 50, 0.5); 9 | object { 10 | padding: 10px 10px; 11 | width: 100%; 12 | } 13 | } -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: sqlite3 3 | pool: 5 4 | timeout: 5000 5 | 6 | development: 7 | <<: *default 8 | database: db/development.sqlite3 9 | 10 | test: 11 | <<: *default 12 | database: db/test.sqlite3 13 | 14 | production: 15 | adapter: postgresql 16 | encoding: unicode 17 | database: reactif_production 18 | pool: 5 19 | username: changeme 20 | password: changeme 21 | -------------------------------------------------------------------------------- /app/views/shared/_search_form.html.erb: -------------------------------------------------------------------------------- 1 | <% placeholder = @tagline.try(:query) || "Search..." %> 2 | <%= form_for :search, url: show_search_path, method: :get do |f| %> 3 |
4 | <%= f.text_field :query, placeholder: placeholder, class: 'form-control' %> 5 | 6 | <%= f.submit 'Go!', class: 'btn btn-default' %> 7 | 8 |
9 | <% end %> 10 | -------------------------------------------------------------------------------- /bin/rspec: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # 3 | # This file was generated by Bundler. 4 | # 5 | # The application 'rspec' is installed as part of a gem, and 6 | # this file is here to facilitate running it. 7 | # 8 | 9 | require 'pathname' 10 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile", 11 | Pathname.new(__FILE__).realpath) 12 | 13 | require 'rubygems' 14 | require 'bundler/setup' 15 | 16 | load Gem.bin_path('rspec-core', 'rspec') 17 | -------------------------------------------------------------------------------- /db/seeds/tagline.rb: -------------------------------------------------------------------------------- 1 | [ 2 | { header: "you can't even", query: "I can't even" }, 3 | { header: "your shoes are filled with spiders", query: "nope" }, 4 | { header: "it's too hard", query: "that's what she said" }, 5 | { header: "drama incoming", query: "popcorn" }, 6 | { header: "shit's going down", query: "dis gon b good"} 7 | ].each do |tagline| 8 | Tagline.first_or_create(header: tagline[:header], query: tagline[:query]) 9 | end 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/controllers/api/base_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | class BaseController < ApplicationController 3 | protect_from_forgery with: :null_session 4 | before_action :destroy_session 5 | rescue_from ActiveRecord::RecordNotFound, with: :not_found 6 | 7 | def not_found 8 | return api_error(status: 404, errors: 'Not found') 9 | end 10 | 11 | def destroy_session 12 | request.session_options[:skip] = true 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /app/controllers/api/gifs_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | class GifsController < BaseController 3 | 4 | def show 5 | user = User.find_by_token(params[:token]) 6 | if user && ActiveSupport::SecurityUtils.secure_compare(user.token, params[:token]) 7 | render json: Gif.getgifs(params[:q]).sample.to_json 8 | else 9 | render status: 401, json: { success: false, info: "Invalid or Missing"} 10 | end 11 | end 12 | end 13 | end -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |

Hello <%= @resource.email %>!

2 | 3 |

Someone has requested a link to change your password. You can do this through the link below.

4 | 5 |

<%= link_to 'Change my password', edit_password_url(@resource, reset_password_token: @token) %>

6 | 7 |

If you didn't request this, please ignore this email.

8 |

Your password won't change until you access the link above and create a new one.

9 | -------------------------------------------------------------------------------- /app/views/home/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 |
3 |

4 | Reactif 5 |

6 |

...<%= @tagline.header %>

7 |
8 |
9 |
10 | <%= render partial: 'shared/search_form' %> 11 |
12 |
13 |
14 | -------------------------------------------------------------------------------- /app/models/tagline.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: taglines 4 | # 5 | # id :integer not null, primary key 6 | # header :string 7 | # query :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class Tagline < ActiveRecord::Base 13 | validates :header, :query, presence: true 14 | 15 | def self.random 16 | Tagline.order('RANDOM()').first 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/routing/search_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe SearchController, type: :routing do 4 | describe "routing" do 5 | it "routes to #index" do 6 | expect(get: '/search').to route_to('search#index') 7 | end 8 | 9 | it "routes to #show" do 10 | expect(get: '/search/found/').to route_to('search#show') 11 | end 12 | 13 | it "routes to #slack" do 14 | expect(get: '/search/slack').to route_to('search#slack') 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | # for nice notifications with toastr 3 | def custom_bootstrap_flash 4 | flash_messages = [] 5 | flash.each do |type, message| 6 | type = 'success' if type == 'notice' 7 | type = 'error' if type == 'alert' 8 | puts type 9 | 10 | text = "" 11 | flash_messages << text.html_safe if message 12 | end 13 | flash_messages.join("\n").html_safe 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/global.scss: -------------------------------------------------------------------------------- 1 | body, html { 2 | background: url('mochaGrunge.png'); 3 | } 4 | 5 | .mainpage { 6 | background: none; 7 | background-color: #fff; 8 | box-shadow: 6px 6px 16px 6px rgba(50, 50, 50, 0.5); 9 | min-height: 85vh; 10 | margin-top: -18px; 11 | padding: 10px 10px; 12 | } 13 | 14 | .navbar { 15 | box-shadow: 1px 1px 5px 1px rgba(50, 50, 50, 0.5); 16 | margin-bottom: 5px !important; 17 | } 18 | 19 | .nav .dropdown-menu > li > a { 20 | cursor: pointer; 21 | } -------------------------------------------------------------------------------- /app/views/devise/unlocks/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend unlock instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: unlock_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Resend unlock instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /app/views/devise/passwords/new.html.erb: -------------------------------------------------------------------------------- 1 |

Forgot your password?

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Send me reset password instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /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 | if match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m) 11 | Gem.paths = { "GEM_PATH" => [Bundler.bundle_path.to_s, *Gem.path].uniq } 12 | gem "spring", match[1] 13 | require "spring/binstub" 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Resend confirmation instructions

2 | 3 | <%= form_for(resource, as: resource_name, url: confirmation_path(resource_name), html: { method: :post }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.submit "Resend confirmation instructions" %> 13 |
14 | <% end %> 15 | 16 | <%= render "devise/shared/links" %> 17 | -------------------------------------------------------------------------------- /.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 the default SQLite database. 11 | /db/*.sqlite3 12 | /db/*.sqlite3-journal 13 | 14 | # Ignore all logfiles and tempfiles. 15 | /log/* 16 | !/log/.keep 17 | /tmp 18 | -------------------------------------------------------------------------------- /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 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in app/assets folder are already added. 11 | # Rails.application.config.assets.precompile += %w( search.js ) 12 | -------------------------------------------------------------------------------- /app/views/dashboard/api.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 |

Dashboard for <%= @user.name %>

11 |
12 | <%= render 'api_data' %> 13 |
14 |
-------------------------------------------------------------------------------- /app/controllers/dashboard_controller.rb: -------------------------------------------------------------------------------- 1 | class DashboardController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def index 5 | @user = current_user 6 | @teamcount = Team.where('user_id = ?', @user.id).count 7 | end 8 | 9 | def settings 10 | @user = current_user 11 | @teams = current_user.teams 12 | end 13 | 14 | def api 15 | @user = current_user 16 | end 17 | 18 | def get_token 19 | @user = current_user 20 | @user.set_token 21 | respond_to do |format| 22 | format.js 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/models/team.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: teams 4 | # 5 | # id :integer not null, primary key 6 | # name :string 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # user_id :integer 10 | # domain :string 11 | # webhook :string 12 | # 13 | 14 | class Team < ActiveRecord::Base 15 | has_many :teamgifs 16 | has_many :gifs, through: :teamgifs 17 | belongs_to :user 18 | 19 | validates :domain, presence: true 20 | validates :webhook, uniqueness: { message: 'webhook already in use' } 21 | end 22 | -------------------------------------------------------------------------------- /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] if respond_to?(:wrap_parameters) 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 | -------------------------------------------------------------------------------- /spec/factories/teams.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: teams 4 | # 5 | # id :integer not null, primary key 6 | # name :string 7 | # created_at :datetime not null 8 | # updated_at :datetime not null 9 | # user_id :integer 10 | # domain :string 11 | # webhook :string 12 | # 13 | 14 | FactoryGirl.define do 15 | factory :team do 16 | association :user 17 | name Faker::Company.name 18 | domain Faker::Internet.domain_word 19 | webhook Faker::Internet.url('hooks.slack.com','/services/T0383B6CH/B04RRMZ1E/') 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/assets/stylesheets/home.scss: -------------------------------------------------------------------------------- 1 | // Place all the styles related to the home controller here. 2 | // They will automatically be included in application.css. 3 | // You can use Sass (SCSS) here: http://sass-lang.com/ 4 | 5 | .brand_header { 6 | text-align: center; 7 | .brand_name { 8 | text-transform: uppercase; 9 | font-size: 500%; 10 | margin-bottom: 5px; 11 | letter-spacing: 0.1em; 12 | #react { 13 | font-weight: bolder; 14 | } 15 | #if { 16 | font-weight: normal; 17 | } 18 | } 19 | .tagline { 20 | margin-top: 5px; 21 | letter-spacing: 0.1em; 22 | } 23 | } -------------------------------------------------------------------------------- /app/views/dashboard/settings.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 |

Dashboard for <%= @user.name %>

11 |
12 |

Teams

13 | <%= render 'team_table' %> 14 |
15 |
-------------------------------------------------------------------------------- /app/views/dashboard/index.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 9 |
10 | <% if @teamcount == 0 %> 11 |

You are not a member of a team, <%= link_to dashboard_settings_path do %>join a team?<% end %>

12 | <% end %> 13 |
14 |
-------------------------------------------------------------------------------- /spec/support/controller_helpers.rb: -------------------------------------------------------------------------------- 1 | module ControllerHelpers 2 | def sign_in(user = double('user')) 3 | if user.nil? 4 | allow(request.env['warden']).to receive(:authenticate!).and_throw(:warden, scope: :user) 5 | allow(controller).to receive(:current_user).and_return(nil) 6 | else 7 | allow(request.env['warden']).to receive(:authenticate!).and_return(user) 8 | allow(controller).to receive(:current_user).and_return(user) 9 | end 10 | end 11 | end 12 | 13 | RSpec.configure do |config| 14 | config.include Devise::TestHelpers, type: :controller 15 | config.include ControllerHelpers, type: :controller 16 | end 17 | -------------------------------------------------------------------------------- /app/models/concerns/reaction_gifs.rb: -------------------------------------------------------------------------------- 1 | module ReactionGIFS 2 | extend ActiveSupport::Concern 3 | 4 | module ClassMethods 5 | def reaction_gifs_response(search_query) 6 | Nokogiri::HTML HTTParty.get(reaction_gifs_search_query(search_query), timeout: 4).body 7 | end 8 | 9 | def reaction_gifs_search_query(search_query) 10 | "http://www.reactiongifs.com/?s=#{search_query}&submit=Search" 11 | end 12 | 13 | def reaction_gif_links(search_query) 14 | document = reaction_gifs_response(search_query) 15 | document.css('div.entry p a').map { |link| link['href'] }.select { |link| link[/\.gif$/] } 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /spec/routing/dashboard_routing_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe DashboardController, type: :routing do 4 | describe "routing" do 5 | it "routes to #index" do 6 | expect(get: '/dashboard').to route_to('dashboard#index') 7 | end 8 | 9 | it "routes to #show" do 10 | expect(get: '/dashboard/settings/').to route_to('dashboard#settings') 11 | end 12 | 13 | it "routes to #api" do 14 | expect(get: '/dashboard/api').to route_to('dashboard#api') 15 | end 16 | 17 | it "routes to #get_token" do 18 | expect(post: '/dashboard/get_token').to route_to('dashboard#get_token') 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /app/controllers/teams_controller.rb: -------------------------------------------------------------------------------- 1 | class TeamsController < ApplicationController 2 | before_action :authenticate_user! 3 | 4 | def create 5 | @team = current_user.teams.build(team_params) 6 | if @team.save 7 | flash[:notice] = 'Team added!' 8 | redirect_to dashboard_path 9 | else 10 | flash[:alert] = 'Failed to add team' 11 | redirect_to dashboard_path 12 | end 13 | end 14 | 15 | def destroy 16 | current_user.teams.find(params[:id]).destroy 17 | flash[:notice] = 'Team deleted.' 18 | redirect_to dashboard_path 19 | end 20 | 21 | private 22 | 23 | def team_params 24 | params.require(:team).permit(:name, :domain, :webhook) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/controllers/users/unlocks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::UnlocksController < Devise::UnlocksController 2 | # GET /resource/unlock/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/unlock 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/unlock?unlock_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after sending unlock password instructions 20 | # def after_sending_unlock_instructions_path_for(resource) 21 | # super(resource) 22 | # end 23 | 24 | # The path used after unlocking the resource 25 | # def after_unlock_path_for(resource) 26 | # super(resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /spec/controllers/regression/home_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe HomeController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:get, '/').to('home#index', {}) } 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:verify_authenticity_token) } 8 | it { should use_before_filter(:set_xhr_redirected_to) } 9 | it { should use_before_filter(:set_request_method_cookie) } 10 | it { should use_before_filter(:configure_permitted_parameters) } 11 | # === Callbacks (After) === 12 | it { should use_after_filter(:abort_xdomain_redirect) } 13 | it { should use_after_filter(:verify_same_origin_request) } 14 | # === Callbacks (Around) === 15 | 16 | end -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /spec/controllers/regression/devise_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe DeviseController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:assert_is_devise_resource!) } 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | # === Callbacks (After) === 13 | it { should use_after_filter(:abort_xdomain_redirect) } 14 | it { should use_after_filter(:verify_same_origin_request) } 15 | # === Callbacks (Around) === 16 | 17 | end -------------------------------------------------------------------------------- /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 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /app/views/devise/sessions/new.html.erb: -------------------------------------------------------------------------------- 1 |

Log in

2 | 3 | <%= form_for(resource, as: resource_name, url: session_path(resource_name)) do |f| %> 4 |
5 | <%= f.label :email %>
6 | <%= f.email_field :email, autofocus: true %> 7 |
8 | 9 |
10 | <%= f.label :password %>
11 | <%= f.password_field :password, autocomplete: "off" %> 12 |
13 | 14 | <% if devise_mapping.rememberable? -%> 15 |
16 | <%= f.check_box :remember_me %> 17 | <%= f.label :remember_me %> 18 |
19 | <% end -%> 20 | 21 |
22 | <%= f.submit "Log in" %> 23 |
24 | <% end %> 25 | 26 | <%= render "devise/shared/links" %> 27 | -------------------------------------------------------------------------------- /spec/controllers/regression/devise/confirmations_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::ConfirmationsController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:assert_is_devise_resource!) } 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | # === Callbacks (After) === 13 | it { should use_after_filter(:abort_xdomain_redirect) } 14 | it { should use_after_filter(:verify_same_origin_request) } 15 | # === Callbacks (Around) === 16 | 17 | end -------------------------------------------------------------------------------- /spec/controllers/regression/users/confirmations_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::ConfirmationsController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:assert_is_devise_resource!) } 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | # === Callbacks (After) === 13 | it { should use_after_filter(:abort_xdomain_redirect) } 14 | it { should use_after_filter(:verify_same_origin_request) } 15 | # === Callbacks (Around) === 16 | 17 | end -------------------------------------------------------------------------------- /spec/controllers/regression/devise/omniauth_callbacks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::OmniauthCallbacksController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:assert_is_devise_resource!) } 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | # === Callbacks (After) === 13 | it { should use_after_filter(:abort_xdomain_redirect) } 14 | it { should use_after_filter(:verify_same_origin_request) } 15 | # === Callbacks (Around) === 16 | 17 | end 18 | -------------------------------------------------------------------------------- /spec/controllers/regression/users/omniauth_callbacks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::OmniauthCallbacksController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:assert_is_devise_resource!) } 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | # === Callbacks (After) === 13 | it { should use_after_filter(:abort_xdomain_redirect) } 14 | it { should use_after_filter(:verify_same_origin_request) } 15 | # === Callbacks (Around) === 16 | 17 | end 18 | -------------------------------------------------------------------------------- /app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SessionsController < Devise::SessionsController 2 | prepend_before_filter :require_no_authentication, only: [:new, :create] 3 | 4 | # GET /resource/sign_in 5 | def new 6 | self.resource = resource_class.new(sign_in_params) 7 | clean_up_passwords(resource) 8 | yield resource if block_given? 9 | respond_with(resource, serialize_options(resource)) 10 | end 11 | 12 | # POST /resource/sign_in 13 | def create 14 | self.resource = warden.authenticate!(auth_options) 15 | set_flash_message(:notice, :signed_in) if is_flashing_format? 16 | sign_in(resource_name, resource) 17 | yield resource if block_given? 18 | respond_with resource, location: after_sign_in_path_for(resource) 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | protect_from_forgery with: :exception 3 | before_filter :configure_permitted_parameters, if: :devise_controller? 4 | 5 | def configure_permitted_parameters 6 | devise_parameter_sanitizer.for(:account_update) do |u| 7 | u.permit(:name, :email, :password, :current_password) 8 | end 9 | devise_parameter_sanitizer.for(:sign_up) do |u| 10 | u.permit(:name, :email, :password) 11 | end 12 | devise_parameter_sanitizer.for(:sign_in) do |u| 13 | u.permit(:email, :password) 14 | end 15 | end 16 | 17 | def after_sign_in_path_for(_user) 18 | dashboard_path 19 | end 20 | 21 | def after_sign_up_path_for(_user) 22 | dashboard_path 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/users/confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::ConfirmationsController < Devise::ConfirmationsController 2 | # GET /resource/confirmation/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/confirmation 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/confirmation?confirmation_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after resending confirmation instructions. 20 | # def after_resending_confirmation_instructions_path_for(resource_name) 21 | # super(resource_name) 22 | # end 23 | 24 | # The path used after confirmation. 25 | # def after_confirmation_path_for(resource_name, resource) 26 | # super(resource_name, resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /README.rdoc: -------------------------------------------------------------------------------- 1 | == README 2 | 3 | Output better gif responses to slack 4 | 5 | === Usage commands (in slack) 6 | 7 | * upvote: upvote the most recently posted gif to that channel, giving it a higher chance of showing up in the future. Ex: '/reactif upvote' 8 | * downvote: downvote the most recently posted gif to that channel, giving it a lower chance of showing up in the future. Ex: '/reactif downvote' 9 | 10 | === environment variables 11 | 12 | REDIS_URL = (using this format: "redis://127.0.0.1:6379") 13 | 14 | === dev testing 15 | 16 | You can send get requests directly to your dev box to have it send to your slack channel via the following: 17 | 18 | /search/slack?text=&team_domain=&channel_name=&user_name= -------------------------------------------------------------------------------- /app/controllers/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::PasswordsController < Devise::PasswordsController 2 | # GET /resource/password/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/password 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/password/edit?reset_password_token=abcdef 13 | # def edit 14 | # super 15 | # end 16 | 17 | # PUT /resource/password 18 | # def update 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # def after_resetting_password_path_for(resource) 25 | # super(resource) 26 | # end 27 | 28 | # The path used after sending reset password instructions 29 | # def after_sending_reset_password_instructions_path_for(resource_name) 30 | # super(resource_name) 31 | # end 32 | end 33 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Change your password

2 | 3 | <%= form_for(resource, as: resource_name, url: password_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | <%= f.hidden_field :reset_password_token %> 6 | 7 |
8 | <%= f.label :password, "New password" %>
9 | <%= f.password_field :password, autofocus: true, autocomplete: "off" %> 10 |
11 | 12 |
13 | <%= f.label :password_confirmation, "Confirm new password" %>
14 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 15 |
16 | 17 |
18 | <%= f.submit "Change my password" %> 19 |
20 | <% end %> 21 | 22 | <%= render "devise/shared/links" %> 23 | -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | # You should configure your model like this: 3 | # devise :omniauthable, omniauth_providers: [:twitter] 4 | 5 | # You should also create an action method in this controller like this: 6 | # def twitter 7 | # end 8 | 9 | # More info at: 10 | # https://github.com/plataformatec/devise#omniauth 11 | 12 | # GET|POST /resource/auth/twitter 13 | # def passthru 14 | # super 15 | # end 16 | 17 | # GET|POST /users/auth/twitter/callback 18 | # def failure 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # The path used when omniauth fails 25 | # def after_omniauth_failure_path_for(scope) 26 | # super(scope) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /spec/controllers/regression/devise/unlocks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::UnlocksController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:require_no_authentication) } 8 | it { should use_before_filter(:assert_is_devise_resource!) } 9 | it { should use_before_filter(:verify_authenticity_token) } 10 | it { should use_before_filter(:set_xhr_redirected_to) } 11 | it { should use_before_filter(:set_request_method_cookie) } 12 | it { should use_before_filter(:configure_permitted_parameters) } 13 | # === Callbacks (After) === 14 | it { should use_after_filter(:abort_xdomain_redirect) } 15 | it { should use_after_filter(:verify_same_origin_request) } 16 | # === Callbacks (Around) === 17 | 18 | end -------------------------------------------------------------------------------- /spec/controllers/regression/users/unlocks_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::UnlocksController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:require_no_authentication) } 8 | it { should use_before_filter(:assert_is_devise_resource!) } 9 | it { should use_before_filter(:verify_authenticity_token) } 10 | it { should use_before_filter(:set_xhr_redirected_to) } 11 | it { should use_before_filter(:set_request_method_cookie) } 12 | it { should use_before_filter(:configure_permitted_parameters) } 13 | # === Callbacks (After) === 14 | it { should use_after_filter(:abort_xdomain_redirect) } 15 | it { should use_after_filter(:verify_same_origin_request) } 16 | # === Callbacks (Around) === 17 | 18 | end -------------------------------------------------------------------------------- /lib/slack_poster.rb: -------------------------------------------------------------------------------- 1 | class SlackPoster 2 | 3 | def initialize(query, gif, username, channel, team) 4 | @query = query 5 | @gif = gif 6 | @username =username 7 | @channel = channel 8 | @team = team 9 | end 10 | 11 | def post_gif 12 | @responselink = '<' + @gif.url + '?' + Random.rand(500).to_s + '|' + ' /reactif ' + @gif.word + '>' 13 | post 14 | end 15 | 16 | def post_vote 17 | @responselink = 'The previous gif was ' + @query + 'd, ' + 'total votes: ' + @gif.votes.to_s 18 | post 19 | end 20 | 21 | def post_body 22 | { 23 | payload: { 24 | username: @username, 25 | channel: "##{@channel}", 26 | text: @responselink 27 | }.to_json 28 | } 29 | end 30 | 31 | def post 32 | HTTParty.post(@team.webhook, body: post_body) 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Reactif 5 | 6 | 7 | 8 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> 9 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> 10 | <%= csrf_meta_tags %> 11 | 12 | 13 | <%= render partial: 'layouts/header' %> 14 | <% unless user_signed_in? %> 15 | <%= render partial: 'layouts/login_overlay' %> 16 | <% end %> 17 | <% if notice or alert %> 18 | <%= custom_bootstrap_flash %> 19 | <% end %> 20 |
21 | <%= yield %> 22 |
23 | 24 | 25 | -------------------------------------------------------------------------------- /spec/controllers/regression/teams_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe TeamsController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:post, '/teams').to('teams#create', {}) } 6 | it { should route(:delete, '/teams/1').to('teams#destroy', {:id=>"1"}) } 7 | # === Callbacks (Before) === 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | it { should use_before_filter(:authenticate_user!) } 13 | # === Callbacks (After) === 14 | it { should use_after_filter(:abort_xdomain_redirect) } 15 | it { should use_after_filter(:verify_same_origin_request) } 16 | # === Callbacks (Around) === 17 | 18 | end -------------------------------------------------------------------------------- /spec/controllers/regression/search_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe SearchController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:get, '/search').to('search#index', {}) } 6 | it { should route(:get, '/search/found').to('search#show', {}) } 7 | it { should route(:get, '/search/slack').to('search#slack', {}) } 8 | # === Callbacks (Before) === 9 | it { should use_before_filter(:verify_authenticity_token) } 10 | it { should use_before_filter(:set_xhr_redirected_to) } 11 | it { should use_before_filter(:set_request_method_cookie) } 12 | it { should use_before_filter(:configure_permitted_parameters) } 13 | # === Callbacks (After) === 14 | it { should use_after_filter(:abort_xdomain_redirect) } 15 | it { should use_after_filter(:verify_same_origin_request) } 16 | # === Callbacks (Around) === 17 | 18 | end -------------------------------------------------------------------------------- /spec/controllers/regression/dashboard_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe DashboardController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:get, '/dashboard/index').to('dashboard#index', {}) } 6 | it { should route(:get, '/dashboard/settings').to('dashboard#settings', {}) } 7 | # === Callbacks (Before) === 8 | it { should use_before_filter(:verify_authenticity_token) } 9 | it { should use_before_filter(:set_xhr_redirected_to) } 10 | it { should use_before_filter(:set_request_method_cookie) } 11 | it { should use_before_filter(:configure_permitted_parameters) } 12 | it { should use_before_filter(:authenticate_user!) } 13 | # === Callbacks (After) === 14 | it { should use_after_filter(:abort_xdomain_redirect) } 15 | it { should use_after_filter(:verify_same_origin_request) } 16 | # === Callbacks (Around) === 17 | 18 | end -------------------------------------------------------------------------------- /spec/controllers/regression/users/passwords_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::PasswordsController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:require_no_authentication) } 8 | it { should use_before_filter(:assert_is_devise_resource!) } 9 | it { should use_before_filter(:verify_authenticity_token) } 10 | it { should use_before_filter(:set_xhr_redirected_to) } 11 | it { should use_before_filter(:set_request_method_cookie) } 12 | it { should use_before_filter(:configure_permitted_parameters) } 13 | it { should use_before_filter(:assert_reset_token_passed) } 14 | # === Callbacks (After) === 15 | it { should use_after_filter(:abort_xdomain_redirect) } 16 | it { should use_after_filter(:verify_same_origin_request) } 17 | # === Callbacks (Around) === 18 | 19 | end -------------------------------------------------------------------------------- /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, vendor/assets/stylesheets, 6 | * or any plugin's 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 styles 10 | * defined in the other CSS/SCSS files in this directory. It is generally better to create a new 11 | * file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | 17 | @import "bootstrap-sprockets"; 18 | @import "bootstrap"; 19 | 20 | html, body { 21 | margin-top: 35px; 22 | } -------------------------------------------------------------------------------- /spec/controllers/regression/devise/registrations_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::RegistrationsController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:authenticate_scope!) } 8 | it { should use_before_filter(:require_no_authentication) } 9 | it { should use_before_filter(:assert_is_devise_resource!) } 10 | it { should use_before_filter(:verify_authenticity_token) } 11 | it { should use_before_filter(:set_xhr_redirected_to) } 12 | it { should use_before_filter(:set_request_method_cookie) } 13 | it { should use_before_filter(:configure_permitted_parameters) } 14 | # === Callbacks (After) === 15 | it { should use_after_filter(:abort_xdomain_redirect) } 16 | it { should use_after_filter(:verify_same_origin_request) } 17 | # === Callbacks (Around) === 18 | 19 | end -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |

Sign up

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name)) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 |
12 | <%= f.label :password %> 13 | <% if @validatable %> 14 | (<%= @minimum_password_length %> characters minimum) 15 | <% end %>
16 | <%= f.password_field :password, autocomplete: "off" %> 17 |
18 | 19 |
20 | <%= f.label :password_confirmation %>
21 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 22 |
23 | 24 |
25 | <%= f.submit "Sign up" %> 26 |
27 | <% end %> 28 | 29 | <%= render "devise/shared/links" %> 30 | -------------------------------------------------------------------------------- /app/assets/stylesheets/nprogress.scss: -------------------------------------------------------------------------------- 1 | /* Make clicks pass-through */ 2 | #nprogress { 3 | pointer-events: none; 4 | } 5 | 6 | #nprogress .bar { 7 | background: #339933; 8 | 9 | position: fixed; 10 | z-index: 9999; 11 | top: 50px; 12 | left: 0; 13 | 14 | width: 100%; 15 | height: 4px; 16 | } 17 | 18 | /* Fancy blur effect */ 19 | #nprogress .peg { 20 | display: block; 21 | position: absolute; 22 | right: 0px; 23 | width: 5px; 24 | height: 100%; 25 | box-shadow: 0 0 3px #29d, 0 0 2px #29d; 26 | opacity: 1.0; 27 | 28 | -webkit-transform: rotate(3deg) translate(0px, -1px); 29 | -ms-transform: rotate(3deg) translate(0px, -1px); 30 | transform: rotate(3deg) translate(0px, -1px); 31 | } 32 | 33 | .nprogress-custom-parent { 34 | overflow: hidden; 35 | position: relative; 36 | } 37 | 38 | .nprogress-custom-parent #nprogress .bar { 39 | position: absolute; 40 | } 41 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | 4 | # path to your application root. 5 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 6 | 7 | Dir.chdir APP_ROOT do 8 | # This script is a starting point to setup your application. 9 | # Add necessary setup steps to this file: 10 | 11 | puts "== Installing dependencies ==" 12 | system "gem install bundler --conservative" 13 | system "bundle check || bundle install" 14 | 15 | # puts "\n== Copying sample files ==" 16 | # unless File.exist?("config/database.yml") 17 | # system "cp config/database.yml.sample config/database.yml" 18 | # end 19 | 20 | puts "\n== Preparing database ==" 21 | system "bin/rake db:setup" 22 | 23 | puts "\n== Removing old logs and tempfiles ==" 24 | system "rm -f log/*" 25 | system "rm -rf tmp/cache" 26 | 27 | puts "\n== Restarting application server ==" 28 | system "touch tmp/restart.txt" 29 | end 30 | -------------------------------------------------------------------------------- /spec/factories/users.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string default(""), not null 7 | # encrypted_password :string default(""), not null 8 | # reset_password_token :string 9 | # reset_password_sent_at :datetime 10 | # remember_created_at :datetime 11 | # sign_in_count :integer default(0), not null 12 | # current_sign_in_at :datetime 13 | # last_sign_in_at :datetime 14 | # current_sign_in_ip :string 15 | # last_sign_in_ip :string 16 | # created_at :datetime 17 | # updated_at :datetime 18 | # name :text 19 | # 20 | 21 | FactoryGirl.define do 22 | factory :user do 23 | name Faker::Name.name 24 | email Faker::Internet.email 25 | password Faker::Internet.password(8) 26 | end 27 | end -------------------------------------------------------------------------------- /spec/controllers/regression/devise/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::SessionsController, regressor: true do 4 | # === Routes (REST) === 5 | 6 | # === Callbacks (Before) === 7 | it { should use_before_filter(:verify_signed_out_user) } 8 | it { should use_before_filter(:allow_params_authentication!) } 9 | it { should use_before_filter(:require_no_authentication) } 10 | it { should use_before_filter(:assert_is_devise_resource!) } 11 | it { should use_before_filter(:verify_authenticity_token) } 12 | it { should use_before_filter(:set_xhr_redirected_to) } 13 | it { should use_before_filter(:set_request_method_cookie) } 14 | it { should use_before_filter(:configure_permitted_parameters) } 15 | # === Callbacks (After) === 16 | it { should use_after_filter(:abort_xdomain_redirect) } 17 | it { should use_after_filter(:verify_same_origin_request) } 18 | # === Callbacks (Around) === 19 | 20 | end 21 | -------------------------------------------------------------------------------- /app/views/dashboard/_api_data.html.erb: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

API Token

5 | <%= token_button @user %> 6 |
7 |
8 |
9 | 10 |
11 | <%= text_field(:user, :token, class: "form-control", readonly: true) %> 12 |
13 |
14 |
15 |
16 |

GET Gifs

17 |

18 |
19 |
20 | http://reactif.me/api/gifs.json?q=search terms&token=your token 21 |
22 | it will return an object like this one: 23 |
24 | {
25 | "word": "word",
26 | "url": "http://www.reactiongifs.com/wp-content/uploads/2012/12/you-got-it-dude.gif"
27 | } 28 |
29 |
30 |
-------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | get 'dashboard/index' 3 | namespace :api, defaults: { format:'json' } do 4 | get 'gifs', to: 'gifs#show' 5 | end 6 | match '/', to: 'home#index', via: 'get', as: 'home' 7 | match 'search', to: 'search#index', via: [:get, :post], as: 'search' 8 | match 'search/found/', to: 'search#show', via: 'get', as: 'show_search' 9 | match 'search/slack/', to: 'search#slack', via: [:get, :post], as: 'slack_search' 10 | match 'dashboard', to: 'dashboard#index', via: 'get', as: 'dashboard' 11 | match 'dashboard/settings', to: 'dashboard#settings', via: 'get', as: 'dashboard_settings' 12 | match 'dashboard/api', to: 'dashboard#api', via: 'get', as: 'dashboard_api' 13 | match 'dashboard/get_token', to: 'dashboard#get_token', via: 'post', as: 'get_token' 14 | devise_for :users, controllers: { sessions: "users/sessions", registrations: "users/registrations" } 15 | resources :teams, only: [:create, :destroy, :update] 16 | root 'home#index' 17 | end 18 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js.coffee: -------------------------------------------------------------------------------- 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, vendor/assets/javascripts, 5 | # or any plugin's 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. 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 turbolinks 16 | #= require bootstrap-sprockets 17 | #= require nprogress 18 | #= require toastr 19 | #= require_tree . 20 | 21 | $ -> 22 | $(document).on 'page:fetch', -> NProgress.start(); 23 | $(document).on 'page:change', -> NProgress.done(); 24 | $(document).on 'page:restore', -> NProgress.remove(); 25 | -------------------------------------------------------------------------------- /config/initializers/regressor.rb: -------------------------------------------------------------------------------- 1 | # If the regressor gem is inside a group wrap your initializer in 2 | # if defined?(Regressor) do .. end 3 | Regressor.configure do |config| 4 | # Defines the path where the generated files for your models will be placed 5 | # config.regression_path = 'spec/models/regression' 6 | 7 | # Defines the path where the generated files for your controllers will be placed 8 | # config.regression_controller_path = 'spec/controllers/regression' 9 | 10 | # Exclude Models for regression spec generation. 11 | # Provide model names as String (e.g. 'User') 12 | # config.excluded_models = [] 13 | 14 | # Exclude Controllers for regression generation. 15 | # Provide controller names as String (e.g. 'UsersController'). 16 | # config.excluded_controllers = [] 17 | 18 | # If you are using enums in Rails 4 enable this option to generate regression specs for enums. 19 | # If your Rails version is =< Rails 3 set this option to false. 20 | # Default this option is set to true. 21 | # config.include_enums = true 22 | end -------------------------------------------------------------------------------- /app/views/dashboard/_team_table.html.erb: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | <% unless @teams.empty? %> 12 | <% @teams.each do |team| %> 13 | <%= render 'team_row', team: team %> 14 | <% end %> 15 | <% end %> 16 | 17 | <%= form_for @user.teams.build do |f| -%> 18 | 19 | 20 | 21 | 24 | <% end %> 25 | 26 | 27 |
NameDomainWeb Hook
<%= f.text_field :name, class: 'form-control' %><%= f.text_field :domain, class: 'form-control' %><%= f.text_field :webhook, class: 'form-control' %> 22 | <%= f.submit "Add", class: 'btn btn-success btn-sm' %> 23 |
28 |
-------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | ENV['RAILS_ENV'] ||= 'test' 2 | require 'spec_helper' 3 | require File.expand_path('../../config/environment', __FILE__) 4 | require 'rspec/rails' 5 | require 'vcr' 6 | require 'webmock/rspec' 7 | require 'shoulda/matchers' 8 | 9 | Dir[Rails.root.join('spec/support/**/*.rb')].each { |f| require f } 10 | ActiveRecord::Migration.maintain_test_schema! 11 | 12 | VCR.configure do |c| 13 | c.hook_into :webmock 14 | c.cassette_library_dir = 'spec/support/cassettes' 15 | c.configure_rspec_metadata! 16 | c.default_cassette_options = { record: :new_episodes } 17 | end 18 | 19 | RSpec.configure do |config| 20 | 21 | config.before(:suite) do 22 | DatabaseCleaner[:active_record].strategy = :transaction 23 | DatabaseCleaner[:ohm].strategy = :truncation 24 | DatabaseCleaner.clean_with(:truncation) 25 | end 26 | 27 | config.before(:each) do 28 | DatabaseCleaner.start 29 | end 30 | 31 | config.after(:each) do 32 | DatabaseCleaner.clean 33 | end 34 | 35 | config.include FactoryGirl::Syntax::Methods 36 | 37 | config.use_transactional_fixtures = false 38 | 39 | config.infer_spec_type_from_file_location! 40 | end 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Learn Web Development 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /app/models/gif.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: gifs 4 | # 5 | # id :integer not null, primary key 6 | # word :string 7 | # url :string 8 | # created_at :datetime not null 9 | # updated_at :datetime not null 10 | # 11 | 12 | class Gif < ActiveRecord::Base 13 | include ReactionGIFS 14 | 15 | has_many :teams 16 | has_many :teamgifs 17 | 18 | validates :url, presence: true 19 | validates :word, presence: true 20 | 21 | class << self 22 | def getgifs(search_query) 23 | return nil unless search_query.present? 24 | 25 | @encoded_search_query = encode_query search_query 26 | save_gifs search_query 27 | end 28 | 29 | def encode_query(search_query) 30 | URI.encode(search_query) 31 | end 32 | 33 | # Save found gifs if they are new 34 | def save_gifs(search_query) 35 | gif_links.map do |url| 36 | Gif.find_or_create_by(url: url, word: search_query) 37 | end 38 | end 39 | 40 | # You can add here another gif services 41 | def gif_links 42 | [reaction_gif_links(@encoded_search_query)].flatten 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /spec/controllers/regression/users/sessions_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::SessionsController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:get, '/users/sign_in').to('users/sessions#new', {}) } 6 | it { should route(:post, '/users/sign_in').to('users/sessions#create', {}) } 7 | it { should route(:delete, '/users/sign_out').to('users/sessions#destroy', {}) } 8 | # === Callbacks (Before) === 9 | it { should use_before_filter(:require_no_authentication) } 10 | it { should use_before_filter(:verify_signed_out_user) } 11 | it { should use_before_filter(:allow_params_authentication!) } 12 | it { should use_before_filter(:assert_is_devise_resource!) } 13 | it { should use_before_filter(:verify_authenticity_token) } 14 | it { should use_before_filter(:set_xhr_redirected_to) } 15 | it { should use_before_filter(:set_request_method_cookie) } 16 | it { should use_before_filter(:configure_permitted_parameters) } 17 | # === Callbacks (After) === 18 | it { should use_after_filter(:abort_xdomain_redirect) } 19 | it { should use_after_filter(:verify_same_origin_request) } 20 | # === Callbacks (Around) === 21 | 22 | end 23 | -------------------------------------------------------------------------------- /spec/controllers/regression/devise/passwords_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Devise::PasswordsController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:get, '/users/password/new').to('devise/passwords#new', {}) } 6 | it { should route(:post, '/users/password').to('devise/passwords#create', {}) } 7 | it { should route(:get, '/users/password/edit').to('devise/passwords#edit', {}) } 8 | it { should route(:patch, '/users/password').to('devise/passwords#update', {}) } 9 | # === Callbacks (Before) === 10 | it { should use_before_filter(:require_no_authentication) } 11 | it { should use_before_filter(:assert_is_devise_resource!) } 12 | it { should use_before_filter(:verify_authenticity_token) } 13 | it { should use_before_filter(:set_xhr_redirected_to) } 14 | it { should use_before_filter(:set_request_method_cookie) } 15 | it { should use_before_filter(:configure_permitted_parameters) } 16 | it { should use_before_filter(:assert_reset_token_passed) } 17 | # === Callbacks (After) === 18 | it { should use_after_filter(:abort_xdomain_redirect) } 19 | it { should use_after_filter(:verify_same_origin_request) } 20 | # === Callbacks (Around) === 21 | 22 | end -------------------------------------------------------------------------------- /app/views/devise/shared/_links.html.erb: -------------------------------------------------------------------------------- 1 | <%- if controller_name != 'sessions' %> 2 | <%= link_to "Log in", new_session_path(resource_name) %>
3 | <% end -%> 4 | 5 | <%- if devise_mapping.registerable? && controller_name != 'registrations' %> 6 | <%= link_to "Sign up", new_registration_path(resource_name) %>
7 | <% end -%> 8 | 9 | <%- if devise_mapping.recoverable? && controller_name != 'passwords' && controller_name != 'registrations' %> 10 | <%= link_to "Forgot your password?", new_password_path(resource_name) %>
11 | <% end -%> 12 | 13 | <%- if devise_mapping.confirmable? && controller_name != 'confirmations' %> 14 | <%= link_to "Didn't receive confirmation instructions?", new_confirmation_path(resource_name) %>
15 | <% end -%> 16 | 17 | <%- if devise_mapping.lockable? && resource_class.unlock_strategy_enabled?(:email) && controller_name != 'unlocks' %> 18 | <%= link_to "Didn't receive unlock instructions?", new_unlock_path(resource_name) %>
19 | <% end -%> 20 | 21 | <%- if devise_mapping.omniauthable? %> 22 | <%- resource_class.omniauth_providers.each do |provider| %> 23 | <%= link_to "Sign in with #{provider.to_s.titleize}", omniauth_authorize_path(resource_name, provider) %>
24 | <% end -%> 25 | <% end -%> 26 | -------------------------------------------------------------------------------- /app/models/teamgif.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: teamgifs 4 | # 5 | # id :integer not null, primary key 6 | # gif_id :integer 7 | # team_id :integer 8 | # votes :integer default(25) 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # 12 | 13 | class Teamgif < ActiveRecord::Base 14 | belongs_to :gif 15 | belongs_to :team 16 | 17 | def upvote 18 | return if votes == 100 19 | self.votes += 1 20 | self.save! 21 | end 22 | 23 | def downvote 24 | return if votes == 0 25 | self.votes -= 1 26 | self.save! 27 | end 28 | 29 | def self.random_gif_id 30 | gifs = with_probability 31 | .map { |tg| { gif_id: tg.gif_id, probability: tg.probability } } 32 | prob_sum = 0 33 | (0..gifs.length).each do |i| 34 | prob_sum += gifs[i][:probability] 35 | return gifs[i][:gif_id] if random_number <= prob_sum 36 | end 37 | end 38 | 39 | private 40 | 41 | def self.total_votes 42 | sum(:votes) 43 | end 44 | 45 | def self.random_number 46 | @random_number ||= rand 47 | end 48 | 49 | def self.with_probability 50 | select("*, votes/#{total_votes.to_f} as probability") 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/controllers/regression/users/registrations_controller_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | RSpec.describe Users::RegistrationsController, regressor: true do 4 | # === Routes (REST) === 5 | it { should route(:post, '/users').to('users/registrations#create', {}) } 6 | it { should route(:patch, '/users').to('users/registrations#update', {}) } 7 | it { should route(:get, '/users/sign_up').to('users/registrations#new', {}) } 8 | it { should route(:get, '/users/edit').to('users/registrations#edit', {}) } 9 | it { should route(:delete, '/users').to('users/registrations#destroy', {}) } 10 | it { should route(:get, '/users/cancel').to('users/registrations#cancel', {}) } 11 | # === Callbacks (Before) === 12 | it { should use_before_filter(:authenticate_scope!) } 13 | it { should use_before_filter(:require_no_authentication) } 14 | it { should use_before_filter(:assert_is_devise_resource!) } 15 | it { should use_before_filter(:verify_authenticity_token) } 16 | it { should use_before_filter(:set_xhr_redirected_to) } 17 | it { should use_before_filter(:set_request_method_cookie) } 18 | it { should use_before_filter(:configure_permitted_parameters) } 19 | # === Callbacks (After) === 20 | it { should use_after_filter(:abort_xdomain_redirect) } 21 | it { should use_after_filter(:verify_same_origin_request) } 22 | # === Callbacks (Around) === 23 | 24 | end -------------------------------------------------------------------------------- /app/views/devise/registrations/edit.html.erb: -------------------------------------------------------------------------------- 1 |

Edit <%= resource_name.to_s.humanize %>

2 | 3 | <%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %> 4 | <%= devise_error_messages! %> 5 | 6 |
7 | <%= f.label :email %>
8 | <%= f.email_field :email, autofocus: true %> 9 |
10 | 11 | <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %> 12 |
Currently waiting confirmation for: <%= resource.unconfirmed_email %>
13 | <% end %> 14 | 15 |
16 | <%= f.label :password %> (leave blank if you don't want to change it)
17 | <%= f.password_field :password, autocomplete: "off" %> 18 |
19 | 20 |
21 | <%= f.label :password_confirmation %>
22 | <%= f.password_field :password_confirmation, autocomplete: "off" %> 23 |
24 | 25 |
26 | <%= f.label :current_password %> (we need your current password to confirm your changes)
27 | <%= f.password_field :current_password, autocomplete: "off" %> 28 |
29 | 30 |
31 | <%= f.submit "Update" %> 32 |
33 | <% end %> 34 | 35 |

Cancel my account

36 | 37 |

Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>

38 | 39 | <%= link_to "Back", :back %> 40 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rails' 4 | gem 'sqlite3' 5 | gem 'sass-rails' 6 | gem 'uglifier' 7 | gem 'coffee-rails' 8 | gem 'jquery-rails' 9 | gem 'turbolinks' 10 | gem 'jbuilder' 11 | gem 'sdoc', group: :doc 12 | gem 'bootstrap-sass' 13 | gem 'font-awesome-rails' 14 | gem 'devise' 15 | gem 'nokogiri' 16 | gem 'httparty' 17 | gem 'figaro' 18 | gem 'redic' # gem to connect ohm to redis 19 | gem 'ohm' # gem to use redis to store objects 20 | gem 'active_model_serializers' 21 | gem 'redis-throttle', git: 'git://github.com/andreareginato/redis-throttle.git' 22 | group :production do 23 | gem 'pg' 24 | end 25 | 26 | group :development, :test do 27 | gem 'byebug' 28 | gem 'web-console', '~> 2.0' 29 | gem 'spring' 30 | gem 'quiet_assets' # Turns off the Rails asset pipeline log 31 | gem 'bullet' # help reduce sql query speeds 32 | gem 'lol_dba' # helps scan for better indexing 33 | gem 'pry' # debugging from console anywhere 34 | gem 'better_errors' # neat error pages 35 | gem 'binding_of_caller' # turns debugging at error page 36 | gem 'annotate' # annotates the fields on the models to remove the need to remember the schema 37 | gem 'rspec-rails' 38 | gem 'factory_girl_rails' 39 | gem 'faker' 40 | gem 'timecop' 41 | gem 'regressor', git: 'https://github.com/ndea/regressor.git', branch: 'master' 42 | gem 'shoulda-matchers' 43 | end 44 | 45 | group :test do 46 | gem 'active_mocker' 47 | gem 'database_cleaner' 48 | gem 'webmock' 49 | gem 'vcr' 50 | gem 'fuubar' 51 | gem 'capybara' 52 | end 53 | -------------------------------------------------------------------------------- /db/migrate/20150514181257_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration 2 | def change 3 | create_table(:users) do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.string :current_sign_in_ip 20 | t.string :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps 35 | end 36 | 37 | add_index :users, :email, unique: true 38 | add_index :users, :reset_password_token, unique: true 39 | # add_index :users, :confirmation_token, unique: true 40 | # add_index :users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string default(""), not null 7 | # encrypted_password :string default(""), not null 8 | # reset_password_token :string 9 | # reset_password_sent_at :datetime 10 | # remember_created_at :datetime 11 | # sign_in_count :integer default(0), not null 12 | # current_sign_in_at :datetime 13 | # last_sign_in_at :datetime 14 | # current_sign_in_ip :string 15 | # last_sign_in_ip :string 16 | # created_at :datetime 17 | # updated_at :datetime 18 | # name :text 19 | # 20 | 21 | class User < ActiveRecord::Base 22 | # Include default devise modules. Others available are: 23 | # :confirmable, :lockable, :timeoutable and :omniauthable 24 | devise :database_authenticatable, :registerable, 25 | :recoverable, :rememberable, :trackable, :validatable 26 | 27 | has_many :teams 28 | # Convert username to friendly url format 29 | def slug 30 | return false unless name.present? 31 | name.downcase.gsub(' ', '-').parameterize 32 | end 33 | 34 | # Change default param for user from id to id-name for friendly urls. 35 | # When finding in DB, Rails auto calls .to_i on param, which tosses 36 | # name and doesn't cause any problems in locating user. 37 | def to_param 38 | return false unless name.present? 39 | "#{id}-#{name}" 40 | end 41 | 42 | def set_token 43 | self.token = SecureRandom.hex 44 | self.save! 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/application.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('../boot', __FILE__) 2 | 3 | require 'rails/all' 4 | require 'open-uri' 5 | require 'nokogiri' 6 | require 'devise' 7 | require 'rack/redis_throttle' 8 | require 'regressor' 9 | require 'rubygems' 10 | require 'httparty' 11 | 12 | 13 | # Require the gems listed in Gemfile, including any gems 14 | # you've limited to :test, :development, or :production. 15 | Bundler.require(*Rails.groups) 16 | 17 | module Reactif 18 | class Application < Rails::Application 19 | # Settings in config/environments/* take precedence over those specified here. 20 | # Application configuration should go into files in config/initializers 21 | # -- all .rb files in that directory are automatically loaded. 22 | config.middleware.use Rack::RedisThrottle::Daily, max: 90000 23 | # Set Time.zone default to the specified zone and make Active Record auto-convert to this zone. 24 | # Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC. 25 | # config.time_zone = 'Central Time (US & Canada)' 26 | config.autoload_paths << Rails.root.join('lib') 27 | # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. 28 | # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] 29 | # config.i18n.default_locale = :de 30 | config.generators do |g| 31 | g.test_framework :rspec, 32 | fixtures: true, 33 | view_specs: false, 34 | help_specs: false, 35 | routing_specs: false, 36 | controller_specs: true, 37 | request_specs: false 38 | g.fixure_replacement :factory_girl, dir: "spec/factories" 39 | end 40 | # Do not swallow errors in after_commit/after_rollback callbacks. 41 | config.active_record.raise_in_transactional_callbacks = true 42 | config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif) 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /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 static file server for tests with Cache-Control for performance. 16 | config.serve_static_files = true 17 | config.static_cache_control = 'public, max-age=3600' 18 | 19 | # Show full error reports and disable caching. 20 | config.consider_all_requests_local = true 21 | config.action_controller.perform_caching = false 22 | 23 | # Raise exceptions instead of rendering exception templates. 24 | config.action_dispatch.show_exceptions = false 25 | 26 | # Disable request forgery protection in test environment. 27 | config.action_controller.allow_forgery_protection = false 28 | 29 | # Tell Action Mailer not to deliver emails to the real world. 30 | # The :test delivery method accumulates sent emails in the 31 | # ActionMailer::Base.deliveries array. 32 | config.action_mailer.delivery_method = :test 33 | 34 | # Randomize the order test cases are executed. 35 | config.active_support.test_order = :random 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 | -------------------------------------------------------------------------------- /app/views/layouts/_header.html.erb: -------------------------------------------------------------------------------- 1 | 47 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::RegistrationsController < Devise::RegistrationsController 2 | prepend_before_filter :require_no_authentication, only: [ :new, :create, :cancel ] 3 | prepend_before_filter :authenticate_scope!, only: [:edit, :update, :destroy] 4 | 5 | # POST /resource 6 | def create 7 | build_resource(sign_up_params) 8 | resource.name = params[:user][:name] 9 | resource.save 10 | yield resource if block_given? 11 | if resource.persisted? 12 | if resource.active_for_authentication? 13 | set_flash_message :notice, :signed_up if is_flashing_format? 14 | sign_up(resource_name, resource) 15 | respond_with resource, location: after_sign_up_path_for(resource) 16 | else 17 | set_flash_message :notice, :"signed_up_but_#{resource.inactive_message}" if is_flashing_format? 18 | expire_data_after_sign_in! 19 | respond_with resource, location: after_inactive_sign_up_path_for(resource) 20 | render 'new' 21 | end 22 | else 23 | clean_up_passwords resource 24 | # set_minimum_password_length 25 | render 'new' 26 | end 27 | end 28 | 29 | def update 30 | @user = User.find(current_user.id) 31 | 32 | successfully_updated = if needs_password?(@user, params) 33 | @user.update_with_password(account_update_params) 34 | # Rails 3: @user.update_with_password(params[:user]) 35 | else 36 | # remove the virtual current_password attribute update_without_password 37 | # doesn't know how to ignore it 38 | params[:user].delete(:current_password) 39 | @user.update_without_password(account_update_params) 40 | # Rails 3: @user.update_without_password(params[:user]) 41 | end 42 | 43 | if successfully_updated 44 | set_flash_message :notice, :updated 45 | # Sign in the user bypassing validation in case his password changed 46 | sign_in @user, :bypass => true 47 | redirect_to after_update_path_for(@user) 48 | else 49 | render 'edit' 50 | end 51 | end 52 | 53 | private 54 | 55 | def needs_password?(user, params) 56 | user.email != params[:user][:email] || 57 | params[:user][:password].present? 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /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 and disable caching. 13 | config.consider_all_requests_local = true 14 | config.action_controller.perform_caching = false 15 | 16 | # Don't care if the mailer can't send. 17 | config.action_mailer.raise_delivery_errors = false 18 | 19 | # Print deprecation notices to the Rails logger. 20 | config.active_support.deprecation = :log 21 | 22 | # Raise an error on page load if there are pending migrations. 23 | config.active_record.migration_error = :page_load 24 | 25 | # Debug mode disables concatenation and preprocessing of assets. 26 | # This option may cause significant delays in view rendering with a large 27 | # number of complex assets. 28 | config.assets.debug = true 29 | 30 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 31 | # yet still be able to expire them through the digest params. 32 | config.assets.digest = true 33 | 34 | # Adds additional error checking when serving assets at runtime. 35 | # Checks for improperly declared sprockets dependencies. 36 | # Raises helpful error messages. 37 | config.assets.raise_runtime_errors = true 38 | 39 | # Raises error for missing translations 40 | # config.action_view.raise_on_missing_translations = true 41 | config.quiet_assets = true #tell assets to stfu 42 | 43 | # Activate Bullet development gem 44 | config.after_initialize do 45 | Bullet.enable = true #enable bullet 46 | #Bullet.alert = true #alert in the browser via a javascript popup 47 | #Bullet.bullet_logger = true #don't log things to a special log file 48 | #Bullet.console = true #dont log things to the web log file 49 | #Bullet.growl = true #we dont use growl 50 | #Bullet.xmpp = { :account => 'bullets_account@jabber.org', #we dont use jabber/xmpp 51 | # :password => 'bullets_password_for_jabber', 52 | # :receiver => 'your_account@jabber.org', 53 | # :show_online_status => true } 54 | #Bullet.rails_logger = true #let alerts be sent to the rails log 55 | #Bullet.airbrake = true #we dont use airbrake 56 | Bullet.add_footer = true 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /spec/mocks/tagline_mock.rb: -------------------------------------------------------------------------------- 1 | require 'active_mocker/mock' 2 | 3 | class TaglineMock < ActiveMocker::Mock::Base 4 | created_with('1.8.3') 5 | 6 | class << self 7 | 8 | def attributes 9 | @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "header"=>nil, "query"=>nil, "created_at"=>nil, "updated_at"=>nil}).merge(super) 10 | end 11 | 12 | def types 13 | @types ||= ActiveMocker::Mock::HashProcess.new({ id: Fixnum, header: String, query: String, created_at: DateTime, updated_at: DateTime }, method(:build_type)).merge(super) 14 | end 15 | 16 | def associations 17 | @associations ||= {}.merge(super) 18 | end 19 | 20 | def associations_by_class 21 | @associations_by_class ||= {}.merge(super) 22 | end 23 | 24 | def mocked_class 25 | "Tagline" 26 | end 27 | 28 | private :mocked_class 29 | 30 | def attribute_names 31 | @attribute_names ||= ["id", "header", "query", "created_at", "updated_at"] | super 32 | end 33 | 34 | def primary_key 35 | "id" 36 | end 37 | 38 | def abstract_class? 39 | false 40 | end 41 | 42 | def table_name 43 | "taglines" || super 44 | end 45 | 46 | end 47 | 48 | ################################## 49 | # Attributes getter/setters # 50 | ################################## 51 | 52 | def id 53 | read_attribute(:id) 54 | end 55 | 56 | def id=(val) 57 | write_attribute(:id, val) 58 | end 59 | 60 | def header 61 | read_attribute(:header) 62 | end 63 | 64 | def header=(val) 65 | write_attribute(:header, val) 66 | end 67 | 68 | def query 69 | read_attribute(:query) 70 | end 71 | 72 | def query=(val) 73 | write_attribute(:query, val) 74 | end 75 | 76 | def created_at 77 | read_attribute(:created_at) 78 | end 79 | 80 | def created_at=(val) 81 | write_attribute(:created_at, val) 82 | end 83 | 84 | def updated_at 85 | read_attribute(:updated_at) 86 | end 87 | 88 | def updated_at=(val) 89 | write_attribute(:updated_at, val) 90 | end 91 | 92 | ################################## 93 | # Associations # 94 | ################################## 95 | 96 | 97 | 98 | module Scopes 99 | include ActiveMocker::Mock::Base::Scopes 100 | 101 | end 102 | 103 | extend Scopes 104 | 105 | class ScopeRelation < ActiveMocker::Mock::Association 106 | include TaglineMock::Scopes 107 | end 108 | 109 | private 110 | 111 | def self.new_relation(collection) 112 | TaglineMock::ScopeRelation.new(collection) 113 | end 114 | 115 | public 116 | 117 | ################################## 118 | # Model Methods # 119 | ################################## 120 | 121 | 122 | def self.random 123 | call_mock_method :random, Kernel.caller 124 | end 125 | 126 | end -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # encoding: UTF-8 2 | # This file is auto-generated from the current state of the database. Instead 3 | # of editing this file, please use the migrations feature of Active Record to 4 | # incrementally modify your database, and then regenerate this schema definition. 5 | # 6 | # Note that this schema.rb definition is the authoritative source for your 7 | # database schema. If you need to create the application database on another 8 | # system, you should be using db:schema:load, not running all the migrations 9 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 10 | # you'll amass, the slower it'll run and the greater likelihood for issues). 11 | # 12 | # It's strongly recommended that you check this file into your version control system. 13 | 14 | ActiveRecord::Schema.define(version: 20150528144349) do 15 | 16 | create_table "gifs", force: :cascade do |t| 17 | t.string "word" 18 | t.string "url" 19 | t.datetime "created_at", null: false 20 | t.datetime "updated_at", null: false 21 | end 22 | 23 | add_index "gifs", ["url"], name: "index_gifs_on_url" 24 | add_index "gifs", ["word"], name: "index_gifs_on_word" 25 | 26 | create_table "taglines", force: :cascade do |t| 27 | t.string "header" 28 | t.string "query" 29 | t.datetime "created_at", null: false 30 | t.datetime "updated_at", null: false 31 | end 32 | 33 | create_table "teamgifs", force: :cascade do |t| 34 | t.integer "gif_id" 35 | t.integer "team_id" 36 | t.integer "votes", default: 25 37 | t.datetime "created_at", null: false 38 | t.datetime "updated_at", null: false 39 | end 40 | 41 | add_index "teamgifs", ["gif_id"], name: "index_teamgifs_on_gif_id" 42 | add_index "teamgifs", ["team_id"], name: "index_teamgifs_on_team_id" 43 | 44 | create_table "teams", force: :cascade do |t| 45 | t.string "name" 46 | t.datetime "created_at", null: false 47 | t.datetime "updated_at", null: false 48 | t.integer "user_id" 49 | t.string "domain" 50 | t.string "webhook" 51 | end 52 | 53 | add_index "teams", ["domain"], name: "index_teams_on_domain" 54 | add_index "teams", ["user_id"], name: "index_teams_on_user_id" 55 | 56 | create_table "users", force: :cascade do |t| 57 | t.string "email", default: "", null: false 58 | t.string "encrypted_password", default: "", null: false 59 | t.string "reset_password_token" 60 | t.datetime "reset_password_sent_at" 61 | t.datetime "remember_created_at" 62 | t.integer "sign_in_count", default: 0, null: false 63 | t.datetime "current_sign_in_at" 64 | t.datetime "last_sign_in_at" 65 | t.string "current_sign_in_ip" 66 | t.string "last_sign_in_ip" 67 | t.datetime "created_at" 68 | t.datetime "updated_at" 69 | t.text "name" 70 | t.string "token" 71 | end 72 | 73 | add_index "users", ["email"], name: "index_users_on_email", unique: true 74 | add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true 75 | 76 | end 77 | -------------------------------------------------------------------------------- /app/views/layouts/_login_overlay.html.erb: -------------------------------------------------------------------------------- 1 | <% @user = User.new %> 2 | 3 | 4 | 41 | 42 | -------------------------------------------------------------------------------- /app/controllers/search_controller.rb: -------------------------------------------------------------------------------- 1 | class SearchController < ApplicationController 2 | skip_before_filter :verify_authenticity_token, only: [:slack] 3 | 4 | def index 5 | end 6 | 7 | def show 8 | @found = Gif.getgifs(params[:search][:query]) 9 | end 10 | 11 | def slack 12 | return render json: 'Team not found' unless team 13 | return no_gifs unless process_command 14 | render nothing: true 15 | end 16 | 17 | private 18 | 19 | def process_command 20 | if (text =~ /^(upvote|downvote)$/).present? 21 | vote 22 | else 23 | found_gif = find_gif 24 | return false unless found_gif 25 | post_gif_to_slack(found_gif, text, channel, username) 26 | end 27 | true 28 | end 29 | 30 | def find_gif 31 | return false if (gifs.empty?) || (!gifs.sample.instance_of? Gif) 32 | store_teamgifs 33 | select_random_gif 34 | end 35 | 36 | def store_teamgifs 37 | team.gifs << gifs.reject { |gif| team.gifs.include?(gif) } 38 | end 39 | 40 | def select_random_gif 41 | gifs.find { |g| g.id == teamgifs.random_gif_id } 42 | end 43 | 44 | def post_gif_to_slack(gif, text, channel, username) 45 | store_last_gif_data gif.id 46 | SlackPoster.new(text, gif, username, channel, team).post_gif 47 | end 48 | 49 | def no_gifs 50 | render json: 'No gifs found' 51 | end 52 | 53 | def store_last_gif_data(gif_id) 54 | last_gif.try(:delete) 55 | Lastgif.create team_domain: team.domain, channel: channel, gif_id: gif_id 56 | end 57 | 58 | def last_gif 59 | @last_gif ||= Lastgif.find(team_domain: team.domain, channel: channel).first 60 | end 61 | 62 | def vote 63 | return false if already_voted?(last_teamgif.id) 64 | last_teamgif.send(text) 65 | create_gifvote(last_teamgif.id) 66 | post_vote_to_slack 67 | end 68 | 69 | def post_vote_to_slack 70 | SlackPoster.new(text, last_teamgif, username, channel, team).post_vote 71 | end 72 | 73 | def last_teamgif 74 | @teamgif ||= team.teamgifs.find_by(gif_id: last_gif.gif_id) 75 | end 76 | 77 | def create_gifvote(gif_id) 78 | Gifvotes.create team_domain: team.domain, 79 | channel: channel, 80 | username: username, 81 | gif_id: gif_id, 82 | expiration: Time.now + 1.week 83 | end 84 | 85 | def already_voted?(gif_id) 86 | vote = Gifvotes.find(team_domain: domain, 87 | channel: channel, 88 | username: username, 89 | gif_id: gif_id).first 90 | if vote.present? && vote.expiration <= Time.now 91 | vote.delete 92 | vote = nil 93 | end 94 | !vote.nil? 95 | end 96 | 97 | def text 98 | params[:text].downcase 99 | end 100 | 101 | def channel 102 | params[:channel_name] 103 | end 104 | 105 | def username 106 | params[:user_name] 107 | end 108 | 109 | def domain 110 | params[:team_domain] 111 | end 112 | 113 | def team 114 | @team ||= Team.find_by_domain domain 115 | end 116 | 117 | def gifs 118 | @gifs ||= Gif.getgifs(text) 119 | end 120 | 121 | def teamgifs 122 | @teamgifs ||= team.teamgifs.where(gif_id: gifs.map(&:id)) 123 | end 124 | end 125 | -------------------------------------------------------------------------------- /spec/controllers/search_spec.rb: -------------------------------------------------------------------------------- 1 | require 'timecop' 2 | require 'rails_helper' 3 | 4 | describe SearchController do 5 | describe "POST #slack", vcr: true do 6 | 7 | let(:team) { FactoryGirl.create(:team) } 8 | let(:channel_name) { "testing_reactif" } 9 | let!(:post_params) { post_params = {text: "dude", channel_name: channel_name, user_name: Faker::Internet.user_name, team_domain: team.domain}} 10 | 11 | context "when params aren't correct" do 12 | it "should return 'No gifs found' if the query doesn't return gifs" do 13 | post_params[:text] = "asdadas" 14 | post :slack, post_params 15 | expect(response.body).to eq('No gifs found') 16 | end 17 | 18 | it "should return 'Team not found' of the team wasn't registered" do 19 | post_params[:team_domain] = "inventedteam" 20 | post :slack, post_params 21 | expect(response.body).to eq('Team not found') 22 | end 23 | end 24 | 25 | context "when params are alright" do 26 | # we need to test more than this... but... let's get the essentials out... 27 | it "should not return errors if all params are valid" do 28 | post :slack, post_params 29 | expect(response).to be_success 30 | end 31 | 32 | it "should add gifs if all params are valid" do 33 | gifs_count = Gif.count 34 | post :slack, post_params 35 | expect(Gif.count).not_to eq(gifs_count) 36 | end 37 | 38 | context "when voting on the last gif" do 39 | before { post :slack, post_params } 40 | let(:last_gif) { Lastgif.find(team_domain: team.domain, channel: channel_name).first } 41 | let(:teamgif) { team.teamgifs.find_by_gif_id(last_gif.gif_id) } 42 | 43 | it "upvoting should increment votes by 1" do 44 | post_params[:text] = "upvote" 45 | expect{post :slack, post_params}.to change{teamgif.reload.votes}.by(1) 46 | end 47 | 48 | it "downvoting should increment votes by -1" do 49 | post_params[:text] = "downvote" 50 | expect{post :slack, post_params}.to change{teamgif.reload.votes}.by(-1) 51 | end 52 | 53 | it "the same user should not be able to vote more than once a week on the same gif" do 54 | post_params[:text] = "upvote" 55 | expect{post :slack, post_params}.to change{teamgif.reload.votes}.by(1) 56 | expect{post :slack, post_params}.not_to change{teamgif.reload.votes} 57 | post_params[:text] = "downvote" 58 | expect{post :slack, post_params}.not_to change{teamgif.reload.votes} 59 | Timecop.freeze(Time.now + 8.days) 60 | expect{post :slack, post_params}.to change{teamgif.reload.votes}.by(-1) 61 | end 62 | end 63 | end 64 | 65 | context "should work more than one team belong to the same user" do 66 | let(:user) { FactoryGirl.create(:user, email: Faker::Internet.email) } 67 | let(:team1) { FactoryGirl.create(:team, user: user, webhook: "http://webook1.com/") } 68 | let(:team2) { FactoryGirl.create(:team, user: user, webhook: "http://webook2.com/") } 69 | 70 | it "it should query properly for the first team of the user" do 71 | post_params[:team_domain] = team1.domain 72 | post :slack, post_params 73 | expect(response).to be_success 74 | end 75 | 76 | it "it should query properly for any other team of the same user" do 77 | post_params[:team_domain] = team2.domain 78 | post :slack, post_params 79 | expect(response).to be_success 80 | end 81 | end 82 | end 83 | end -------------------------------------------------------------------------------- /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 | # Enable Rack::Cache to put a simple HTTP cache in front of your application 18 | # Add `rack-cache` to your Gemfile before enabling this. 19 | # For large-scale production use, consider using a caching reverse proxy like 20 | # NGINX, varnish or squid. 21 | # config.action_dispatch.rack_cache = true 22 | 23 | # Disable serving static files from the `/public` folder by default since 24 | # Apache or NGINX already handles this. 25 | config.serve_static_files = ENV['RAILS_SERVE_STATIC_FILES'].present? 26 | 27 | # Compress JavaScripts and CSS. 28 | config.assets.js_compressor = :uglifier 29 | # config.assets.css_compressor = :sass 30 | 31 | # Do not fallback to assets pipeline if a precompiled asset is missed. 32 | config.assets.compile = false 33 | 34 | # Asset digests allow you to set far-future HTTP expiration dates on all assets, 35 | # yet still be able to expire them through the digest params. 36 | config.assets.digest = true 37 | 38 | # `config.assets.precompile` and `config.assets.version` have moved to config/initializers/assets.rb 39 | 40 | # Specifies the header that your server uses for sending files. 41 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 42 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 43 | 44 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 45 | # config.force_ssl = true 46 | 47 | # Use the lowest log level to ensure availability of diagnostic information 48 | # when problems arise. 49 | config.log_level = :debug 50 | 51 | # Prepend all log lines with the following tags. 52 | # config.log_tags = [ :subdomain, :uuid ] 53 | 54 | # Use a different logger for distributed setups. 55 | # config.logger = ActiveSupport::TaggedLogging.new(SyslogLogger.new) 56 | 57 | # Use a different cache store in production. 58 | # config.cache_store = :mem_cache_store 59 | 60 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 61 | # config.action_controller.asset_host = 'http://assets.example.com' 62 | 63 | # Ignore bad email addresses and do not raise email delivery errors. 64 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 65 | # config.action_mailer.raise_delivery_errors = false 66 | 67 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 68 | # the I18n.default_locale when a translation cannot be found). 69 | config.i18n.fallbacks = true 70 | 71 | # Send deprecation notices to registered listeners. 72 | config.active_support.deprecation = :notify 73 | 74 | # Use default logging formatter so that PID and timestamp are not suppressed. 75 | config.log_formatter = ::Logger::Formatter.new 76 | 77 | # Do not dump schema after migrations. 78 | config.active_record.dump_schema_after_migration = false 79 | end 80 | -------------------------------------------------------------------------------- /spec/mocks/gif_mock.rb: -------------------------------------------------------------------------------- 1 | require 'active_mocker/mock' 2 | 3 | class GifMock < ActiveMocker::Mock::Base 4 | created_with('1.8.3') 5 | prepend ReactionGIFS 6 | 7 | class << self 8 | 9 | def attributes 10 | @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "word"=>nil, "url"=>nil, "created_at"=>nil, "updated_at"=>nil}).merge(super) 11 | end 12 | 13 | def types 14 | @types ||= ActiveMocker::Mock::HashProcess.new({ id: Fixnum, word: String, url: String, created_at: DateTime, updated_at: DateTime }, method(:build_type)).merge(super) 15 | end 16 | 17 | def associations 18 | @associations ||= {:teams=>nil, :teamgifs=>nil}.merge(super) 19 | end 20 | 21 | def associations_by_class 22 | @associations_by_class ||= {"Team"=>{:has_many=>[:teams]}, "Teamgif"=>{:has_many=>[:teamgifs]}}.merge(super) 23 | end 24 | 25 | def mocked_class 26 | "Gif" 27 | end 28 | 29 | private :mocked_class 30 | 31 | def attribute_names 32 | @attribute_names ||= ["id", "word", "url", "created_at", "updated_at"] | super 33 | end 34 | 35 | def primary_key 36 | "id" 37 | end 38 | 39 | def abstract_class? 40 | false 41 | end 42 | 43 | def table_name 44 | "gifs" || super 45 | end 46 | 47 | end 48 | 49 | ################################## 50 | # Attributes getter/setters # 51 | ################################## 52 | 53 | def id 54 | read_attribute(:id) 55 | end 56 | 57 | def id=(val) 58 | write_attribute(:id, val) 59 | end 60 | 61 | def word 62 | read_attribute(:word) 63 | end 64 | 65 | def word=(val) 66 | write_attribute(:word, val) 67 | end 68 | 69 | def url 70 | read_attribute(:url) 71 | end 72 | 73 | def url=(val) 74 | write_attribute(:url, val) 75 | end 76 | 77 | def created_at 78 | read_attribute(:created_at) 79 | end 80 | 81 | def created_at=(val) 82 | write_attribute(:created_at, val) 83 | end 84 | 85 | def updated_at 86 | read_attribute(:updated_at) 87 | end 88 | 89 | def updated_at=(val) 90 | write_attribute(:updated_at, val) 91 | end 92 | 93 | ################################## 94 | # Associations # 95 | ################################## 96 | 97 | 98 | # has_many 99 | def teams 100 | read_association(:teams, -> { ActiveMocker::Mock::HasMany.new([],foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Team'), source: '') }) 101 | end 102 | 103 | def teams=(val) 104 | write_association(:teams, ActiveMocker::Mock::HasMany.new(val, foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Team'), source: '')) 105 | end 106 | 107 | def teamgifs 108 | read_association(:teamgifs, -> { ActiveMocker::Mock::HasMany.new([],foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Teamgif'), source: '') }) 109 | end 110 | 111 | def teamgifs=(val) 112 | write_association(:teamgifs, ActiveMocker::Mock::HasMany.new(val, foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Teamgif'), source: '')) 113 | end 114 | 115 | module Scopes 116 | include ActiveMocker::Mock::Base::Scopes 117 | 118 | end 119 | 120 | extend Scopes 121 | 122 | class ScopeRelation < ActiveMocker::Mock::Association 123 | include GifMock::Scopes 124 | end 125 | 126 | private 127 | 128 | def self.new_relation(collection) 129 | GifMock::ScopeRelation.new(collection) 130 | end 131 | 132 | public 133 | 134 | ################################## 135 | # Model Methods # 136 | ################################## 137 | 138 | 139 | def self.getgifs(search_query) 140 | call_mock_method :getgifs, Kernel.caller, search_query 141 | end 142 | 143 | def self.encode_query(search_query) 144 | call_mock_method :encode_query, Kernel.caller, search_query 145 | end 146 | 147 | def self.save_gifs(search_query) 148 | call_mock_method :save_gifs, Kernel.caller, search_query 149 | end 150 | 151 | def self.gif_links 152 | call_mock_method :gif_links, Kernel.caller 153 | end 154 | 155 | end -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid %{authentication_keys} or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid %{authentication_keys} or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | omniauth_callbacks: 27 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 28 | success: "Successfully authenticated from %{kind} account." 29 | passwords: 30 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 31 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 32 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 33 | updated: "Your password has been changed successfully. You are now signed in." 34 | updated_not_active: "Your password has been changed successfully." 35 | registrations: 36 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 37 | signed_up: "Welcome! You have signed up successfully." 38 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 39 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 40 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 41 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 42 | updated: "Your account has been updated successfully." 43 | sessions: 44 | signed_in: "Signed in successfully." 45 | signed_out: "Signed out successfully." 46 | already_signed_out: "Signed out successfully." 47 | unlocks: 48 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 49 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 50 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 51 | errors: 52 | messages: 53 | already_confirmed: "was already confirmed, please try signing in" 54 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 55 | expired: "has expired, please request a new one" 56 | not_found: "not found" 57 | not_locked: "was not locked" 58 | not_saved: 59 | one: "1 error prohibited this %{resource} from being saved:" 60 | other: "%{count} errors prohibited this %{resource} from being saved:" 61 | -------------------------------------------------------------------------------- /spec/mocks/teamgif_mock.rb: -------------------------------------------------------------------------------- 1 | require 'active_mocker/mock' 2 | 3 | class TeamgifMock < ActiveMocker::Mock::Base 4 | created_with('1.8.3') 5 | 6 | class << self 7 | 8 | def attributes 9 | @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "gif_id"=>nil, "team_id"=>nil, "votes"=>25, "created_at"=>nil, "updated_at"=>nil}).merge(super) 10 | end 11 | 12 | def types 13 | @types ||= ActiveMocker::Mock::HashProcess.new({ id: Fixnum, gif_id: Fixnum, team_id: Fixnum, votes: Fixnum, created_at: DateTime, updated_at: DateTime }, method(:build_type)).merge(super) 14 | end 15 | 16 | def associations 17 | @associations ||= {:gif=>nil, :team=>nil}.merge(super) 18 | end 19 | 20 | def associations_by_class 21 | @associations_by_class ||= {"Gif"=>{:belongs_to=>[:gif]}, "Team"=>{:belongs_to=>[:team]}}.merge(super) 22 | end 23 | 24 | def mocked_class 25 | "Teamgif" 26 | end 27 | 28 | private :mocked_class 29 | 30 | def attribute_names 31 | @attribute_names ||= ["id", "gif_id", "team_id", "votes", "created_at", "updated_at"] | super 32 | end 33 | 34 | def primary_key 35 | "id" 36 | end 37 | 38 | def abstract_class? 39 | false 40 | end 41 | 42 | def table_name 43 | "teamgifs" || super 44 | end 45 | 46 | end 47 | 48 | ################################## 49 | # Attributes getter/setters # 50 | ################################## 51 | 52 | def id 53 | read_attribute(:id) 54 | end 55 | 56 | def id=(val) 57 | write_attribute(:id, val) 58 | end 59 | 60 | def gif_id 61 | read_attribute(:gif_id) 62 | end 63 | 64 | def gif_id=(val) 65 | write_attribute(:gif_id, val) 66 | end 67 | 68 | def team_id 69 | read_attribute(:team_id) 70 | end 71 | 72 | def team_id=(val) 73 | write_attribute(:team_id, val) 74 | end 75 | 76 | def votes 77 | read_attribute(:votes) 78 | end 79 | 80 | def votes=(val) 81 | write_attribute(:votes, val) 82 | end 83 | 84 | def created_at 85 | read_attribute(:created_at) 86 | end 87 | 88 | def created_at=(val) 89 | write_attribute(:created_at, val) 90 | end 91 | 92 | def updated_at 93 | read_attribute(:updated_at) 94 | end 95 | 96 | def updated_at=(val) 97 | write_attribute(:updated_at, val) 98 | end 99 | 100 | ################################## 101 | # Associations # 102 | ################################## 103 | 104 | # belongs_to 105 | def gif 106 | read_association(:gif) || write_association(:gif, classes('Gif').try{ |k| k.find_by(id: gif_id)}) 107 | end 108 | 109 | def gif=(val) 110 | write_association(:gif, val) 111 | ActiveMocker::Mock::BelongsTo.new(val, child_self: self, foreign_key: :gif_id).item 112 | end 113 | 114 | def build_gif(attributes={}, &block) 115 | association = classes('Gif').try(:new, attributes, &block) 116 | write_association(:gif, association) unless association.nil? 117 | end 118 | 119 | def create_gif(attributes={}, &block) 120 | association = classes('Gif').try(:create,attributes, &block) 121 | write_association(:gif, association) unless association.nil? 122 | end 123 | alias_method :create_gif!, :create_gif 124 | 125 | def team 126 | read_association(:team) || write_association(:team, classes('Team').try{ |k| k.find_by(id: team_id)}) 127 | end 128 | 129 | def team=(val) 130 | write_association(:team, val) 131 | ActiveMocker::Mock::BelongsTo.new(val, child_self: self, foreign_key: :team_id).item 132 | end 133 | 134 | def build_team(attributes={}, &block) 135 | association = classes('Team').try(:new, attributes, &block) 136 | write_association(:team, association) unless association.nil? 137 | end 138 | 139 | def create_team(attributes={}, &block) 140 | association = classes('Team').try(:create,attributes, &block) 141 | write_association(:team, association) unless association.nil? 142 | end 143 | alias_method :create_team!, :create_team 144 | 145 | 146 | module Scopes 147 | include ActiveMocker::Mock::Base::Scopes 148 | 149 | end 150 | 151 | extend Scopes 152 | 153 | class ScopeRelation < ActiveMocker::Mock::Association 154 | include TeamgifMock::Scopes 155 | end 156 | 157 | private 158 | 159 | def self.new_relation(collection) 160 | TeamgifMock::ScopeRelation.new(collection) 161 | end 162 | 163 | public 164 | 165 | ################################## 166 | # Model Methods # 167 | ################################## 168 | 169 | 170 | def upvote 171 | call_mock_method :upvote, Kernel.caller 172 | end 173 | 174 | def downvote 175 | call_mock_method :downvote, Kernel.caller 176 | end 177 | 178 | end -------------------------------------------------------------------------------- /spec/mocks/team_mock.rb: -------------------------------------------------------------------------------- 1 | require 'active_mocker/mock' 2 | 3 | class TeamMock < ActiveMocker::Mock::Base 4 | created_with('1.8.3') 5 | 6 | class << self 7 | 8 | def attributes 9 | @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "name"=>nil, "created_at"=>nil, "updated_at"=>nil, "user_id"=>nil, "domain"=>nil, "webhook"=>nil}).merge(super) 10 | end 11 | 12 | def types 13 | @types ||= ActiveMocker::Mock::HashProcess.new({ id: Fixnum, name: String, created_at: DateTime, updated_at: DateTime, user_id: Fixnum, domain: String, webhook: String }, method(:build_type)).merge(super) 14 | end 15 | 16 | def associations 17 | @associations ||= {:user=>nil, :teamgifs=>nil, :gifs=>nil}.merge(super) 18 | end 19 | 20 | def associations_by_class 21 | @associations_by_class ||= {"User"=>{:belongs_to=>[:user]}, "Teamgif"=>{:has_many=>[:teamgifs]}, "Gif"=>{:has_many=>[:gifs]}}.merge(super) 22 | end 23 | 24 | def mocked_class 25 | "Team" 26 | end 27 | 28 | private :mocked_class 29 | 30 | def attribute_names 31 | @attribute_names ||= ["id", "name", "created_at", "updated_at", "user_id", "domain", "webhook"] | super 32 | end 33 | 34 | def primary_key 35 | "id" 36 | end 37 | 38 | def abstract_class? 39 | false 40 | end 41 | 42 | def table_name 43 | "teams" || super 44 | end 45 | 46 | end 47 | 48 | ################################## 49 | # Attributes getter/setters # 50 | ################################## 51 | 52 | def id 53 | read_attribute(:id) 54 | end 55 | 56 | def id=(val) 57 | write_attribute(:id, val) 58 | end 59 | 60 | def name 61 | read_attribute(:name) 62 | end 63 | 64 | def name=(val) 65 | write_attribute(:name, val) 66 | end 67 | 68 | def created_at 69 | read_attribute(:created_at) 70 | end 71 | 72 | def created_at=(val) 73 | write_attribute(:created_at, val) 74 | end 75 | 76 | def updated_at 77 | read_attribute(:updated_at) 78 | end 79 | 80 | def updated_at=(val) 81 | write_attribute(:updated_at, val) 82 | end 83 | 84 | def user_id 85 | read_attribute(:user_id) 86 | end 87 | 88 | def user_id=(val) 89 | write_attribute(:user_id, val) 90 | end 91 | 92 | def domain 93 | read_attribute(:domain) 94 | end 95 | 96 | def domain=(val) 97 | write_attribute(:domain, val) 98 | end 99 | 100 | def webhook 101 | read_attribute(:webhook) 102 | end 103 | 104 | def webhook=(val) 105 | write_attribute(:webhook, val) 106 | end 107 | 108 | ################################## 109 | # Associations # 110 | ################################## 111 | 112 | # belongs_to 113 | def user 114 | read_association(:user) || write_association(:user, classes('User').try{ |k| k.find_by(id: user_id)}) 115 | end 116 | 117 | def user=(val) 118 | write_association(:user, val) 119 | ActiveMocker::Mock::BelongsTo.new(val, child_self: self, foreign_key: :user_id).item 120 | end 121 | 122 | def build_user(attributes={}, &block) 123 | association = classes('User').try(:new, attributes, &block) 124 | write_association(:user, association) unless association.nil? 125 | end 126 | 127 | def create_user(attributes={}, &block) 128 | association = classes('User').try(:create,attributes, &block) 129 | write_association(:user, association) unless association.nil? 130 | end 131 | alias_method :create_user!, :create_user 132 | 133 | # has_many 134 | def teamgifs 135 | read_association(:teamgifs, -> { ActiveMocker::Mock::HasMany.new([],foreign_key: 'team_id', foreign_id: self.id, relation_class: classes('Teamgif'), source: '') }) 136 | end 137 | 138 | def teamgifs=(val) 139 | write_association(:teamgifs, ActiveMocker::Mock::HasMany.new(val, foreign_key: 'team_id', foreign_id: self.id, relation_class: classes('Teamgif'), source: '')) 140 | end 141 | 142 | def gifs 143 | read_association(:gifs, -> { ActiveMocker::Mock::HasMany.new([],foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Gif'), source: '') }) 144 | end 145 | 146 | def gifs=(val) 147 | write_association(:gifs, ActiveMocker::Mock::HasMany.new(val, foreign_key: 'gif_id', foreign_id: self.id, relation_class: classes('Gif'), source: '')) 148 | end 149 | 150 | module Scopes 151 | include ActiveMocker::Mock::Base::Scopes 152 | 153 | end 154 | 155 | extend Scopes 156 | 157 | class ScopeRelation < ActiveMocker::Mock::Association 158 | include TeamMock::Scopes 159 | end 160 | 161 | private 162 | 163 | def self.new_relation(collection) 164 | TeamMock::ScopeRelation.new(collection) 165 | end 166 | 167 | public 168 | 169 | ################################## 170 | # Model Methods # 171 | ################################## 172 | 173 | 174 | end -------------------------------------------------------------------------------- /spec/mocks/user_mock.rb: -------------------------------------------------------------------------------- 1 | require 'active_mocker/mock' 2 | 3 | class UserMock < ActiveMocker::Mock::Base 4 | created_with('1.8.3') 5 | 6 | class << self 7 | 8 | def attributes 9 | @attributes ||= HashWithIndifferentAccess.new({"id"=>nil, "email"=>"", "encrypted_password"=>"", "reset_password_token"=>nil, "reset_password_sent_at"=>nil, "remember_created_at"=>nil, "sign_in_count"=>0, "current_sign_in_at"=>nil, "last_sign_in_at"=>nil, "current_sign_in_ip"=>nil, "last_sign_in_ip"=>nil, "created_at"=>nil, "updated_at"=>nil, "name"=>nil}).merge(super) 10 | end 11 | 12 | def types 13 | @types ||= ActiveMocker::Mock::HashProcess.new({ id: Fixnum, email: String, encrypted_password: String, reset_password_token: String, reset_password_sent_at: DateTime, remember_created_at: DateTime, sign_in_count: Fixnum, current_sign_in_at: DateTime, last_sign_in_at: DateTime, current_sign_in_ip: String, last_sign_in_ip: String, created_at: DateTime, updated_at: DateTime, name: String }, method(:build_type)).merge(super) 14 | end 15 | 16 | def associations 17 | @associations ||= {:teams=>nil}.merge(super) 18 | end 19 | 20 | def associations_by_class 21 | @associations_by_class ||= {"Team"=>{:has_many=>[:teams]}}.merge(super) 22 | end 23 | 24 | def mocked_class 25 | "User" 26 | end 27 | 28 | private :mocked_class 29 | 30 | def attribute_names 31 | @attribute_names ||= ["id", "email", "encrypted_password", "reset_password_token", "reset_password_sent_at", "remember_created_at", "sign_in_count", "current_sign_in_at", "last_sign_in_at", "current_sign_in_ip", "last_sign_in_ip", "created_at", "updated_at", "name"] | super 32 | end 33 | 34 | def primary_key 35 | "id" 36 | end 37 | 38 | def abstract_class? 39 | false 40 | end 41 | 42 | def table_name 43 | "users" || super 44 | end 45 | 46 | end 47 | 48 | ################################## 49 | # Attributes getter/setters # 50 | ################################## 51 | 52 | def id 53 | read_attribute(:id) 54 | end 55 | 56 | def id=(val) 57 | write_attribute(:id, val) 58 | end 59 | 60 | def email 61 | read_attribute(:email) 62 | end 63 | 64 | def email=(val) 65 | write_attribute(:email, val) 66 | end 67 | 68 | def encrypted_password 69 | read_attribute(:encrypted_password) 70 | end 71 | 72 | def encrypted_password=(val) 73 | write_attribute(:encrypted_password, val) 74 | end 75 | 76 | def reset_password_token 77 | read_attribute(:reset_password_token) 78 | end 79 | 80 | def reset_password_token=(val) 81 | write_attribute(:reset_password_token, val) 82 | end 83 | 84 | def reset_password_sent_at 85 | read_attribute(:reset_password_sent_at) 86 | end 87 | 88 | def reset_password_sent_at=(val) 89 | write_attribute(:reset_password_sent_at, val) 90 | end 91 | 92 | def remember_created_at 93 | read_attribute(:remember_created_at) 94 | end 95 | 96 | def remember_created_at=(val) 97 | write_attribute(:remember_created_at, val) 98 | end 99 | 100 | def sign_in_count 101 | read_attribute(:sign_in_count) 102 | end 103 | 104 | def sign_in_count=(val) 105 | write_attribute(:sign_in_count, val) 106 | end 107 | 108 | def current_sign_in_at 109 | read_attribute(:current_sign_in_at) 110 | end 111 | 112 | def current_sign_in_at=(val) 113 | write_attribute(:current_sign_in_at, val) 114 | end 115 | 116 | def last_sign_in_at 117 | read_attribute(:last_sign_in_at) 118 | end 119 | 120 | def last_sign_in_at=(val) 121 | write_attribute(:last_sign_in_at, val) 122 | end 123 | 124 | def current_sign_in_ip 125 | read_attribute(:current_sign_in_ip) 126 | end 127 | 128 | def current_sign_in_ip=(val) 129 | write_attribute(:current_sign_in_ip, val) 130 | end 131 | 132 | def last_sign_in_ip 133 | read_attribute(:last_sign_in_ip) 134 | end 135 | 136 | def last_sign_in_ip=(val) 137 | write_attribute(:last_sign_in_ip, val) 138 | end 139 | 140 | def created_at 141 | read_attribute(:created_at) 142 | end 143 | 144 | def created_at=(val) 145 | write_attribute(:created_at, val) 146 | end 147 | 148 | def updated_at 149 | read_attribute(:updated_at) 150 | end 151 | 152 | def updated_at=(val) 153 | write_attribute(:updated_at, val) 154 | end 155 | 156 | def name 157 | read_attribute(:name) 158 | end 159 | 160 | def name=(val) 161 | write_attribute(:name, val) 162 | end 163 | 164 | ################################## 165 | # Associations # 166 | ################################## 167 | 168 | 169 | # has_many 170 | def teams 171 | read_association(:teams, -> { ActiveMocker::Mock::HasMany.new([],foreign_key: 'user_id', foreign_id: self.id, relation_class: classes('Team'), source: '') }) 172 | end 173 | 174 | def teams=(val) 175 | write_association(:teams, ActiveMocker::Mock::HasMany.new(val, foreign_key: 'user_id', foreign_id: self.id, relation_class: classes('Team'), source: '')) 176 | end 177 | 178 | module Scopes 179 | include ActiveMocker::Mock::Base::Scopes 180 | 181 | end 182 | 183 | extend Scopes 184 | 185 | class ScopeRelation < ActiveMocker::Mock::Association 186 | include UserMock::Scopes 187 | end 188 | 189 | private 190 | 191 | def self.new_relation(collection) 192 | UserMock::ScopeRelation.new(collection) 193 | end 194 | 195 | public 196 | 197 | ################################## 198 | # Model Methods # 199 | ################################## 200 | 201 | 202 | def slug 203 | call_mock_method :slug, Kernel.caller 204 | end 205 | 206 | def to_param 207 | call_mock_method :to_param, Kernel.caller 208 | end 209 | 210 | end -------------------------------------------------------------------------------- /app/assets/stylesheets/toastr.scss: -------------------------------------------------------------------------------- 1 | .toast-title { 2 | font-weight: bold; 3 | } 4 | .toast-message { 5 | -ms-word-wrap: break-word; 6 | word-wrap: break-word; 7 | } 8 | .toast-message a, 9 | .toast-message label { 10 | color: #ffffff; 11 | } 12 | .toast-message a:hover { 13 | color: #cccccc; 14 | text-decoration: none; 15 | } 16 | .toast-close-button { 17 | position: relative; 18 | right: -0.3em; 19 | top: -0.3em; 20 | float: right; 21 | font-size: 20px; 22 | font-weight: bold; 23 | color: #ffffff; 24 | -webkit-text-shadow: 0 1px 0 #ffffff; 25 | text-shadow: 0 1px 0 #ffffff; 26 | opacity: 0.8; 27 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); 28 | filter: alpha(opacity=80); 29 | } 30 | .toast-close-button:hover, 31 | .toast-close-button:focus { 32 | color: #000000; 33 | text-decoration: none; 34 | cursor: pointer; 35 | opacity: 0.4; 36 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); 37 | filter: alpha(opacity=40); 38 | } 39 | /*Additional properties for button version 40 | iOS requires the button element instead of an anchor tag. 41 | If you want the anchor version, it requires `href="#"`.*/ 42 | button.toast-close-button { 43 | padding: 0; 44 | cursor: pointer; 45 | background: transparent; 46 | border: 0; 47 | -webkit-appearance: none; 48 | } 49 | .toast-top-center { 50 | top: 0; 51 | right: 0; 52 | width: 100%; 53 | } 54 | .toast-bottom-center { 55 | bottom: 0; 56 | right: 0; 57 | width: 100%; 58 | } 59 | .toast-top-full-width { 60 | top: 0; 61 | right: 0; 62 | width: 100%; 63 | } 64 | .toast-bottom-full-width { 65 | bottom: 0; 66 | right: 0; 67 | width: 100%; 68 | } 69 | .toast-top-left { 70 | top: 12px; 71 | left: 12px; 72 | } 73 | .toast-top-right { 74 | top: 12px; 75 | right: 12px; 76 | } 77 | .toast-bottom-right { 78 | right: 12px; 79 | bottom: 12px; 80 | } 81 | .toast-bottom-left { 82 | bottom: 12px; 83 | left: 12px; 84 | } 85 | #toast-container { 86 | position: fixed; 87 | z-index: 999999; 88 | /*overrides*/ 89 | } 90 | #toast-container * { 91 | -moz-box-sizing: border-box; 92 | -webkit-box-sizing: border-box; 93 | box-sizing: border-box; 94 | } 95 | #toast-container > div { 96 | position: relative; 97 | overflow: hidden; 98 | margin: 0 0 6px; 99 | padding: 15px 15px 15px 50px; 100 | width: 300px; 101 | -moz-border-radius: 3px 3px 3px 3px; 102 | -webkit-border-radius: 3px 3px 3px 3px; 103 | border-radius: 3px 3px 3px 3px; 104 | background-position: 15px center; 105 | background-repeat: no-repeat; 106 | -moz-box-shadow: 0 0 12px #999999; 107 | -webkit-box-shadow: 0 0 12px #999999; 108 | box-shadow: 0 0 12px #999999; 109 | color: #ffffff; 110 | opacity: 0.8; 111 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=80); 112 | filter: alpha(opacity=80); 113 | } 114 | #toast-container > :hover { 115 | -moz-box-shadow: 0 0 12px #000000; 116 | -webkit-box-shadow: 0 0 12px #000000; 117 | box-shadow: 0 0 12px #000000; 118 | opacity: 1; 119 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=100); 120 | filter: alpha(opacity=100); 121 | cursor: pointer; 122 | } 123 | #toast-container > .toast-info { 124 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGwSURBVEhLtZa9SgNBEMc9sUxxRcoUKSzSWIhXpFMhhYWFhaBg4yPYiWCXZxBLERsLRS3EQkEfwCKdjWJAwSKCgoKCcudv4O5YLrt7EzgXhiU3/4+b2ckmwVjJSpKkQ6wAi4gwhT+z3wRBcEz0yjSseUTrcRyfsHsXmD0AmbHOC9Ii8VImnuXBPglHpQ5wwSVM7sNnTG7Za4JwDdCjxyAiH3nyA2mtaTJufiDZ5dCaqlItILh1NHatfN5skvjx9Z38m69CgzuXmZgVrPIGE763Jx9qKsRozWYw6xOHdER+nn2KkO+Bb+UV5CBN6WC6QtBgbRVozrahAbmm6HtUsgtPC19tFdxXZYBOfkbmFJ1VaHA1VAHjd0pp70oTZzvR+EVrx2Ygfdsq6eu55BHYR8hlcki+n+kERUFG8BrA0BwjeAv2M8WLQBtcy+SD6fNsmnB3AlBLrgTtVW1c2QN4bVWLATaIS60J2Du5y1TiJgjSBvFVZgTmwCU+dAZFoPxGEEs8nyHC9Bwe2GvEJv2WXZb0vjdyFT4Cxk3e/kIqlOGoVLwwPevpYHT+00T+hWwXDf4AJAOUqWcDhbwAAAAASUVORK5CYII=") !important; 125 | } 126 | #toast-container > .toast-error { 127 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAHOSURBVEhLrZa/SgNBEMZzh0WKCClSCKaIYOED+AAKeQQLG8HWztLCImBrYadgIdY+gIKNYkBFSwu7CAoqCgkkoGBI/E28PdbLZmeDLgzZzcx83/zZ2SSXC1j9fr+I1Hq93g2yxH4iwM1vkoBWAdxCmpzTxfkN2RcyZNaHFIkSo10+8kgxkXIURV5HGxTmFuc75B2RfQkpxHG8aAgaAFa0tAHqYFfQ7Iwe2yhODk8+J4C7yAoRTWI3w/4klGRgR4lO7Rpn9+gvMyWp+uxFh8+H+ARlgN1nJuJuQAYvNkEnwGFck18Er4q3egEc/oO+mhLdKgRyhdNFiacC0rlOCbhNVz4H9FnAYgDBvU3QIioZlJFLJtsoHYRDfiZoUyIxqCtRpVlANq0EU4dApjrtgezPFad5S19Wgjkc0hNVnuF4HjVA6C7QrSIbylB+oZe3aHgBsqlNqKYH48jXyJKMuAbiyVJ8KzaB3eRc0pg9VwQ4niFryI68qiOi3AbjwdsfnAtk0bCjTLJKr6mrD9g8iq/S/B81hguOMlQTnVyG40wAcjnmgsCNESDrjme7wfftP4P7SP4N3CJZdvzoNyGq2c/HWOXJGsvVg+RA/k2MC/wN6I2YA2Pt8GkAAAAASUVORK5CYII=") !important; 128 | } 129 | #toast-container > .toast-success { 130 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAADsSURBVEhLY2AYBfQMgf///3P8+/evAIgvA/FsIF+BavYDDWMBGroaSMMBiE8VC7AZDrIFaMFnii3AZTjUgsUUWUDA8OdAH6iQbQEhw4HyGsPEcKBXBIC4ARhex4G4BsjmweU1soIFaGg/WtoFZRIZdEvIMhxkCCjXIVsATV6gFGACs4Rsw0EGgIIH3QJYJgHSARQZDrWAB+jawzgs+Q2UO49D7jnRSRGoEFRILcdmEMWGI0cm0JJ2QpYA1RDvcmzJEWhABhD/pqrL0S0CWuABKgnRki9lLseS7g2AlqwHWQSKH4oKLrILpRGhEQCw2LiRUIa4lwAAAABJRU5ErkJggg==") !important; 131 | } 132 | #toast-container > .toast-warning { 133 | background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAAJcEhZcwAADsMAAA7DAcdvqGQAAAGYSURBVEhL5ZSvTsNQFMbXZGICMYGYmJhAQIJAICYQPAACiSDB8AiICQQJT4CqQEwgJvYASAQCiZiYmJhAIBATCARJy+9rTsldd8sKu1M0+dLb057v6/lbq/2rK0mS/TRNj9cWNAKPYIJII7gIxCcQ51cvqID+GIEX8ASG4B1bK5gIZFeQfoJdEXOfgX4QAQg7kH2A65yQ87lyxb27sggkAzAuFhbbg1K2kgCkB1bVwyIR9m2L7PRPIhDUIXgGtyKw575yz3lTNs6X4JXnjV+LKM/m3MydnTbtOKIjtz6VhCBq4vSm3ncdrD2lk0VgUXSVKjVDJXJzijW1RQdsU7F77He8u68koNZTz8Oz5yGa6J3H3lZ0xYgXBK2QymlWWA+RWnYhskLBv2vmE+hBMCtbA7KX5drWyRT/2JsqZ2IvfB9Y4bWDNMFbJRFmC9E74SoS0CqulwjkC0+5bpcV1CZ8NMej4pjy0U+doDQsGyo1hzVJttIjhQ7GnBtRFN1UarUlH8F3xict+HY07rEzoUGPlWcjRFRr4/gChZgc3ZL2d8oAAAAASUVORK5CYII=") !important; 134 | } 135 | #toast-container.toast-top-center > div, 136 | #toast-container.toast-bottom-center > div { 137 | width: 300px; 138 | margin: auto; 139 | } 140 | #toast-container.toast-top-full-width > div, 141 | #toast-container.toast-bottom-full-width > div { 142 | width: 96%; 143 | margin: auto; 144 | } 145 | .toast { 146 | background-color: #030303; 147 | } 148 | .toast-success { 149 | background-color: rgb(79,193,233); 150 | } 151 | .toast-error { 152 | background-color: rgb(237,85,101); 153 | } 154 | .toast-info { 155 | background-color: rgb(172,146,236); 156 | } 157 | .toast-warning { 158 | background-color: rgb(255,206,84); 159 | } 160 | .toast-progress { 161 | position: absolute; 162 | left: 0; 163 | bottom: 0; 164 | height: 4px; 165 | background-color: #000000; 166 | opacity: 0.4; 167 | -ms-filter: progid:DXImageTransform.Microsoft.Alpha(Opacity=40); 168 | filter: alpha(opacity=40); 169 | } 170 | /*Responsive Design*/ 171 | @media all and (max-width: 240px) { 172 | #toast-container > div { 173 | padding: 8px 8px 8px 50px; 174 | width: 11em; 175 | } 176 | #toast-container .toast-close-button { 177 | right: -0.2em; 178 | top: -0.2em; 179 | } 180 | } 181 | @media all and (min-width: 241px) and (max-width: 480px) { 182 | #toast-container > div { 183 | padding: 8px 8px 8px 50px; 184 | width: 18em; 185 | } 186 | #toast-container .toast-close-button { 187 | right: -0.2em; 188 | top: -0.2em; 189 | } 190 | } 191 | @media all and (min-width: 481px) and (max-width: 768px) { 192 | #toast-container > div { 193 | padding: 15px 15px 15px 50px; 194 | width: 25em; 195 | } 196 | } -------------------------------------------------------------------------------- /vendor/assets/javascripts/toastr.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Toastr 3 | * Copyright 2012-2014 4 | * Authors: John Papa, Hans Fjällemark, and Tim Ferrell. 5 | * All Rights Reserved. 6 | * Use, reproduction, distribution, and modification of this code is subject to the terms and 7 | * conditions of the MIT license, available at http://www.opensource.org/licenses/mit-license.php 8 | * 9 | * ARIA Support: Greta Krafsig 10 | * 11 | * Project: https://github.com/CodeSeven/toastr 12 | */ 13 | ; (function (define) { 14 | define(['jquery'], function ($) { 15 | return (function () { 16 | var $container; 17 | var listener; 18 | var toastId = 0; 19 | var toastType = { 20 | error: 'error', 21 | info: 'info', 22 | success: 'success', 23 | warning: 'warning' 24 | }; 25 | var toastr = { 26 | clear: clear, 27 | remove: remove, 28 | error: error, 29 | getContainer: getContainer, 30 | info: info, 31 | options: {}, 32 | subscribe: subscribe, 33 | success: success, 34 | version: '2.1.0', 35 | warning: warning 36 | }; 37 | var previousToast; 38 | return toastr; 39 | //#region Accessible Methods 40 | function error(message, title, optionsOverride) { 41 | return notify({ 42 | type: toastType.error, 43 | iconClass: getOptions().iconClasses.error, 44 | message: message, 45 | optionsOverride: optionsOverride, 46 | title: title 47 | }); 48 | } 49 | function getContainer(options, create) { 50 | if (!options) { options = getOptions(); } 51 | $container = $('#' + options.containerId); 52 | if ($container.length) { 53 | return $container; 54 | } 55 | if (create) { 56 | $container = createContainer(options); 57 | } 58 | return $container; 59 | } 60 | function info(message, title, optionsOverride) { 61 | return notify({ 62 | type: toastType.info, 63 | iconClass: getOptions().iconClasses.info, 64 | message: message, 65 | optionsOverride: optionsOverride, 66 | title: title 67 | }); 68 | } 69 | function subscribe(callback) { 70 | listener = callback; 71 | } 72 | function success(message, title, optionsOverride) { 73 | return notify({ 74 | type: toastType.success, 75 | iconClass: getOptions().iconClasses.success, 76 | message: message, 77 | optionsOverride: optionsOverride, 78 | title: title 79 | }); 80 | } 81 | function warning(message, title, optionsOverride) { 82 | return notify({ 83 | type: toastType.warning, 84 | iconClass: getOptions().iconClasses.warning, 85 | message: message, 86 | optionsOverride: optionsOverride, 87 | title: title 88 | }); 89 | } 90 | function clear($toastElement) { 91 | var options = getOptions(); 92 | if (!$container) { getContainer(options); } 93 | if (!clearToast($toastElement, options)) { 94 | clearContainer(options); 95 | } 96 | } 97 | function remove($toastElement) { 98 | var options = getOptions(); 99 | if (!$container) { getContainer(options); } 100 | if ($toastElement && $(':focus', $toastElement).length === 0) { 101 | removeToast($toastElement); 102 | return; 103 | } 104 | if ($container.children().length) { 105 | $container.remove(); 106 | } 107 | } 108 | //#endregion 109 | //#region Internal Methods 110 | function clearContainer (options) { 111 | var toastsToClear = $container.children(); 112 | for (var i = toastsToClear.length - 1; i >= 0; i--) { 113 | clearToast($(toastsToClear[i]), options); 114 | } 115 | } 116 | function clearToast ($toastElement, options) { 117 | if ($toastElement && $(':focus', $toastElement).length === 0) { 118 | $toastElement[options.hideMethod]({ 119 | duration: options.hideDuration, 120 | easing: options.hideEasing, 121 | complete: function () { removeToast($toastElement); } 122 | }); 123 | return true; 124 | } 125 | return false; 126 | } 127 | function createContainer(options) { 128 | $container = $('
') 129 | .attr('id', options.containerId) 130 | .addClass(options.positionClass) 131 | .attr('aria-live', 'polite') 132 | .attr('role', 'alert'); 133 | $container.appendTo($(options.target)); 134 | return $container; 135 | } 136 | function getDefaults() { 137 | return { 138 | tapToDismiss: true, 139 | toastClass: 'toast', 140 | containerId: 'toast-container', 141 | debug: false, 142 | showMethod: 'fadeIn', //fadeIn, slideDown, and show are built into jQuery 143 | showDuration: 300, 144 | showEasing: 'swing', //swing and linear are built into jQuery 145 | onShown: undefined, 146 | hideMethod: 'fadeOut', 147 | hideDuration: 1000, 148 | hideEasing: 'swing', 149 | onHidden: undefined, 150 | extendedTimeOut: 10000, 151 | iconClasses: { 152 | error: 'toast-error', 153 | info: 'toast-info', 154 | success: 'toast-success', 155 | warning: 'toast-warning' 156 | }, 157 | iconClass: 'toast-info', 158 | positionClass: 'toast-top-right', 159 | timeOut: 10000, // Set timeOut and extendedTimeOut to 0 to make it sticky 160 | titleClass: 'toast-title', 161 | messageClass: 'toast-message', 162 | target: 'body', 163 | closeHtml: '', 164 | newestOnTop: true, 165 | preventDuplicates: false, 166 | closeButton: true, 167 | progressBar: true 168 | }; 169 | } 170 | function publish(args) { 171 | if (!listener) { return; } 172 | listener(args); 173 | } 174 | function notify(map) { 175 | var options = getOptions(), 176 | iconClass = map.iconClass || options.iconClass; 177 | if (options.preventDuplicates) { 178 | if (map.message === previousToast) { 179 | return; 180 | } else { 181 | previousToast = map.message; 182 | } 183 | } 184 | if (typeof (map.optionsOverride) !== 'undefined') { 185 | options = $.extend(options, map.optionsOverride); 186 | iconClass = map.optionsOverride.iconClass || iconClass; 187 | } 188 | toastId++; 189 | $container = getContainer(options, true); 190 | var intervalId = null, 191 | $toastElement = $('
'), 192 | $titleElement = $('
'), 193 | $messageElement = $('
'), 194 | $progressElement = $('
'), 195 | $closeElement = $(options.closeHtml), 196 | progressBar = { 197 | intervalId: null, 198 | hideEta: null, 199 | maxHideTime: null 200 | }, 201 | response = { 202 | toastId: toastId, 203 | state: 'visible', 204 | startTime: new Date(), 205 | options: options, 206 | map: map 207 | }; 208 | if (map.iconClass) { 209 | $toastElement.addClass(options.toastClass).addClass(iconClass); 210 | } 211 | if (map.title) { 212 | $titleElement.append(map.title).addClass(options.titleClass); 213 | $toastElement.append($titleElement); 214 | } 215 | if (map.message) { 216 | $messageElement.append(map.message).addClass(options.messageClass); 217 | $toastElement.append($messageElement); 218 | } 219 | if (options.closeButton) { 220 | $closeElement.addClass('toast-close-button').attr('role', 'button'); 221 | $toastElement.prepend($closeElement); 222 | } 223 | if (options.progressBar) { 224 | $progressElement.addClass('toast-progress'); 225 | $toastElement.prepend($progressElement); 226 | } 227 | $toastElement.hide(); 228 | if (options.newestOnTop) { 229 | $container.prepend($toastElement); 230 | } else { 231 | $container.append($toastElement); 232 | } 233 | $toastElement[options.showMethod]( 234 | {duration: options.showDuration, easing: options.showEasing, complete: options.onShown} 235 | ); 236 | if (options.timeOut > 0) { 237 | intervalId = setTimeout(hideToast, options.timeOut); 238 | progressBar.maxHideTime = parseFloat(options.timeOut); 239 | progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; 240 | if (options.progressBar) { 241 | progressBar.intervalId = setInterval(updateProgress, 10); 242 | } 243 | } 244 | $toastElement.hover(stickAround, delayedHideToast); 245 | if (!options.onclick && options.tapToDismiss) { 246 | $toastElement.click(hideToast); 247 | } 248 | if (options.closeButton && $closeElement) { 249 | $closeElement.click(function (event) { 250 | if (event.stopPropagation) { 251 | event.stopPropagation(); 252 | } else if (event.cancelBubble !== undefined && event.cancelBubble !== true) { 253 | event.cancelBubble = true; 254 | } 255 | hideToast(true); 256 | }); 257 | } 258 | if (options.onclick) { 259 | $toastElement.click(function () { 260 | options.onclick(); 261 | hideToast(); 262 | }); 263 | } 264 | publish(response); 265 | if (options.debug && console) { 266 | console.log(response); 267 | } 268 | return $toastElement; 269 | function hideToast(override) { 270 | if ($(':focus', $toastElement).length && !override) { 271 | return; 272 | } 273 | clearTimeout(progressBar.intervalId); 274 | return $toastElement[options.hideMethod]({ 275 | duration: options.hideDuration, 276 | easing: options.hideEasing, 277 | complete: function () { 278 | removeToast($toastElement); 279 | if (options.onHidden && response.state !== 'hidden') { 280 | options.onHidden(); 281 | } 282 | response.state = 'hidden'; 283 | response.endTime = new Date(); 284 | publish(response); 285 | } 286 | }); 287 | } 288 | function delayedHideToast() { 289 | if (options.timeOut > 0 || options.extendedTimeOut > 0) { 290 | intervalId = setTimeout(hideToast, options.extendedTimeOut); 291 | progressBar.maxHideTime = parseFloat(options.extendedTimeOut); 292 | progressBar.hideEta = new Date().getTime() + progressBar.maxHideTime; 293 | } 294 | } 295 | function stickAround() { 296 | clearTimeout(intervalId); 297 | progressBar.hideEta = 0; 298 | $toastElement.stop(true, true)[options.showMethod]( 299 | {duration: options.showDuration, easing: options.showEasing} 300 | ); 301 | } 302 | function updateProgress() { 303 | var percentage = ((progressBar.hideEta - (new Date().getTime())) / progressBar.maxHideTime) * 100; 304 | $progressElement.width(percentage + '%'); 305 | } 306 | } 307 | function getOptions() { 308 | return $.extend({}, getDefaults(), toastr.options); 309 | } 310 | function removeToast($toastElement) { 311 | if (!$container) { $container = getContainer(); } 312 | if ($toastElement.is(':visible')) { 313 | return; 314 | } 315 | $toastElement.remove(); 316 | $toastElement = null; 317 | if ($container.children().length === 0) { 318 | $container.remove(); 319 | } 320 | } 321 | //#endregion 322 | })(); 323 | }); 324 | }(typeof define === 'function' && define.amd ? define : function (deps, factory) { 325 | if (typeof module !== 'undefined' && module.exports) { //Node 326 | module.exports = factory(require('jquery')); 327 | } else { 328 | window['toastr'] = factory(window['jQuery']); 329 | } 330 | })); -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: git://github.com/andreareginato/redis-throttle.git 3 | revision: 98cf0bf5f2c3955408f2b5667c7b6ddcd459b6d4 4 | specs: 5 | redis-throttle (0.0.1) 6 | activesupport 7 | hiredis 8 | rack 9 | rack-throttle 10 | redis 11 | redis-namespace 12 | 13 | GIT 14 | remote: https://github.com/ndea/regressor.git 15 | revision: 6439486e01aed530d23cba20c182f522eb5b8545 16 | branch: master 17 | specs: 18 | regressor (0.4.2) 19 | faker (~> 1.3) 20 | rails (~> 4.0) 21 | rspec-rails (~> 3.0) 22 | shoulda-matchers (~> 2.7.0) 23 | 24 | GEM 25 | remote: https://rubygems.org/ 26 | specs: 27 | abstract_type (0.0.7) 28 | actionmailer (4.2.4) 29 | actionpack (= 4.2.4) 30 | actionview (= 4.2.4) 31 | activejob (= 4.2.4) 32 | mail (~> 2.5, >= 2.5.4) 33 | rails-dom-testing (~> 1.0, >= 1.0.5) 34 | actionpack (4.2.4) 35 | actionview (= 4.2.4) 36 | activesupport (= 4.2.4) 37 | rack (~> 1.6) 38 | rack-test (~> 0.6.2) 39 | rails-dom-testing (~> 1.0, >= 1.0.5) 40 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 41 | actionview (4.2.4) 42 | activesupport (= 4.2.4) 43 | builder (~> 3.1) 44 | erubis (~> 2.7.0) 45 | rails-dom-testing (~> 1.0, >= 1.0.5) 46 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 47 | active_mocker (1.8.3) 48 | activesupport (~> 4.0) 49 | attr_permit (~> 0.0) 50 | parser (~> 2.1) 51 | rake (~> 10.0) 52 | ruby-progressbar (~> 1.4) 53 | unparser (~> 0.1) 54 | virtus (~> 1.0) 55 | active_model_serializers (0.9.3) 56 | activemodel (>= 3.2) 57 | activejob (4.2.4) 58 | activesupport (= 4.2.4) 59 | globalid (>= 0.3.0) 60 | activemodel (4.2.4) 61 | activesupport (= 4.2.4) 62 | builder (~> 3.1) 63 | activerecord (4.2.4) 64 | activemodel (= 4.2.4) 65 | activesupport (= 4.2.4) 66 | arel (~> 6.0) 67 | activesupport (4.2.4) 68 | i18n (~> 0.7) 69 | json (~> 1.7, >= 1.7.7) 70 | minitest (~> 5.1) 71 | thread_safe (~> 0.3, >= 0.3.4) 72 | tzinfo (~> 1.1) 73 | adamantium (0.2.0) 74 | ice_nine (~> 0.11.0) 75 | memoizable (~> 0.4.0) 76 | addressable (2.3.8) 77 | annotate (2.6.10) 78 | activerecord (>= 3.2, <= 4.3) 79 | rake (~> 10.4) 80 | arel (6.0.3) 81 | ast (2.0.0) 82 | attr_permit (0.0.4) 83 | activesupport (>= 4.0) 84 | virtus (~> 1.0) 85 | autoprefixer-rails (5.2.0.1) 86 | execjs 87 | json 88 | axiom-types (0.1.1) 89 | descendants_tracker (~> 0.0.4) 90 | ice_nine (~> 0.11.0) 91 | thread_safe (~> 0.3, >= 0.3.1) 92 | bcrypt (3.1.10) 93 | better_errors (2.1.1) 94 | coderay (>= 1.0.0) 95 | erubis (>= 2.6.6) 96 | rack (>= 0.9.0) 97 | binding_of_caller (0.7.2) 98 | debug_inspector (>= 0.0.1) 99 | bootstrap-sass (3.3.5) 100 | autoprefixer-rails (>= 5.0.0.1) 101 | sass (>= 3.2.19) 102 | builder (3.2.2) 103 | bullet (4.14.7) 104 | activesupport (>= 3.0.0) 105 | uniform_notifier (~> 1.9.0) 106 | byebug (5.0.0) 107 | columnize (= 0.9.0) 108 | capybara (2.4.4) 109 | mime-types (>= 1.16) 110 | nokogiri (>= 1.3.3) 111 | rack (>= 1.0.0) 112 | rack-test (>= 0.5.4) 113 | xpath (~> 2.0) 114 | coderay (1.1.0) 115 | coercible (1.0.0) 116 | descendants_tracker (~> 0.0.1) 117 | coffee-rails (4.1.0) 118 | coffee-script (>= 2.2.0) 119 | railties (>= 4.0.0, < 5.0) 120 | coffee-script (2.4.1) 121 | coffee-script-source 122 | execjs 123 | coffee-script-source (1.9.1.1) 124 | columnize (0.9.0) 125 | concord (0.1.5) 126 | adamantium (~> 0.2.0) 127 | equalizer (~> 0.0.9) 128 | crack (0.4.2) 129 | safe_yaml (~> 1.0.0) 130 | database_cleaner (1.4.1) 131 | debug_inspector (0.0.2) 132 | descendants_tracker (0.0.4) 133 | thread_safe (~> 0.3, >= 0.3.1) 134 | devise (3.5.1) 135 | bcrypt (~> 3.0) 136 | orm_adapter (~> 0.1) 137 | railties (>= 3.2.6, < 5) 138 | responders 139 | thread_safe (~> 0.1) 140 | warden (~> 1.2.3) 141 | diff-lcs (1.2.5) 142 | equalizer (0.0.11) 143 | erubis (2.7.0) 144 | execjs (2.5.2) 145 | factory_girl (4.5.0) 146 | activesupport (>= 3.0.0) 147 | factory_girl_rails (4.5.0) 148 | factory_girl (~> 4.5.0) 149 | railties (>= 3.0.0) 150 | faker (1.4.3) 151 | i18n (~> 0.5) 152 | figaro (1.1.1) 153 | thor (~> 0.14) 154 | font-awesome-rails (4.3.0.0) 155 | railties (>= 3.2, < 5.0) 156 | fuubar (2.0.0) 157 | rspec (~> 3.0) 158 | ruby-progressbar (~> 1.4) 159 | globalid (0.3.6) 160 | activesupport (>= 4.1.0) 161 | hiredis (0.6.0) 162 | httparty (0.13.5) 163 | json (~> 1.8) 164 | multi_xml (>= 0.5.2) 165 | i18n (0.7.0) 166 | ice_nine (0.11.1) 167 | jbuilder (2.3.0) 168 | activesupport (>= 3.0.0, < 5) 169 | multi_json (~> 1.2) 170 | jquery-rails (4.0.4) 171 | rails-dom-testing (~> 1.0) 172 | railties (>= 4.2.0) 173 | thor (>= 0.14, < 2.0) 174 | json (1.8.3) 175 | lol_dba (2.0.0) 176 | actionpack (>= 3.0, < 5.0) 177 | activerecord (>= 3.0, < 5.0) 178 | railties (>= 3.0, < 5.0) 179 | loofah (2.0.3) 180 | nokogiri (>= 1.5.9) 181 | mail (2.6.3) 182 | mime-types (>= 1.16, < 3) 183 | memoizable (0.4.2) 184 | thread_safe (~> 0.3, >= 0.3.1) 185 | method_source (0.8.2) 186 | mime-types (2.6.2) 187 | mini_portile (0.6.2) 188 | minitest (5.8.0) 189 | msgpack (0.5.12) 190 | multi_json (1.11.1) 191 | multi_xml (0.5.5) 192 | nido (0.0.1) 193 | nokogiri (1.6.6.2) 194 | mini_portile (~> 0.6.0) 195 | ohm (2.2.1) 196 | msgpack (~> 0.5.0) 197 | nido 198 | redic 199 | stal 200 | orm_adapter (0.5.0) 201 | parser (2.2.2.5) 202 | ast (>= 1.1, < 3.0) 203 | pg (0.18.2) 204 | procto (0.0.2) 205 | pry (0.10.1) 206 | coderay (~> 1.1.0) 207 | method_source (~> 0.8.1) 208 | slop (~> 3.4) 209 | quiet_assets (1.1.0) 210 | railties (>= 3.1, < 5.0) 211 | rack (1.6.4) 212 | rack-test (0.6.3) 213 | rack (>= 1.0) 214 | rack-throttle (0.4.0) 215 | rack (>= 1.0.0) 216 | rails (4.2.4) 217 | actionmailer (= 4.2.4) 218 | actionpack (= 4.2.4) 219 | actionview (= 4.2.4) 220 | activejob (= 4.2.4) 221 | activemodel (= 4.2.4) 222 | activerecord (= 4.2.4) 223 | activesupport (= 4.2.4) 224 | bundler (>= 1.3.0, < 2.0) 225 | railties (= 4.2.4) 226 | sprockets-rails 227 | rails-deprecated_sanitizer (1.0.3) 228 | activesupport (>= 4.2.0.alpha) 229 | rails-dom-testing (1.0.7) 230 | activesupport (>= 4.2.0.beta, < 5.0) 231 | nokogiri (~> 1.6.0) 232 | rails-deprecated_sanitizer (>= 1.0.1) 233 | rails-html-sanitizer (1.0.2) 234 | loofah (~> 2.0) 235 | railties (4.2.4) 236 | actionpack (= 4.2.4) 237 | activesupport (= 4.2.4) 238 | rake (>= 0.8.7) 239 | thor (>= 0.18.1, < 2.0) 240 | rake (10.4.2) 241 | rdoc (4.2.0) 242 | redic (1.5.0) 243 | hiredis 244 | redis (3.2.1) 245 | redis-namespace (1.5.2) 246 | redis (~> 3.0, >= 3.0.4) 247 | responders (2.1.0) 248 | railties (>= 4.2.0, < 5) 249 | rspec (3.3.0) 250 | rspec-core (~> 3.3.0) 251 | rspec-expectations (~> 3.3.0) 252 | rspec-mocks (~> 3.3.0) 253 | rspec-core (3.3.1) 254 | rspec-support (~> 3.3.0) 255 | rspec-expectations (3.3.0) 256 | diff-lcs (>= 1.2.0, < 2.0) 257 | rspec-support (~> 3.3.0) 258 | rspec-mocks (3.3.1) 259 | diff-lcs (>= 1.2.0, < 2.0) 260 | rspec-support (~> 3.3.0) 261 | rspec-rails (3.3.2) 262 | actionpack (>= 3.0, < 4.3) 263 | activesupport (>= 3.0, < 4.3) 264 | railties (>= 3.0, < 4.3) 265 | rspec-core (~> 3.3.0) 266 | rspec-expectations (~> 3.3.0) 267 | rspec-mocks (~> 3.3.0) 268 | rspec-support (~> 3.3.0) 269 | rspec-support (3.3.0) 270 | ruby-progressbar (1.7.5) 271 | safe_yaml (1.0.4) 272 | sass (3.4.14) 273 | sass-rails (5.0.3) 274 | railties (>= 4.0.0, < 5.0) 275 | sass (~> 3.1) 276 | sprockets (>= 2.8, < 4.0) 277 | sprockets-rails (>= 2.0, < 4.0) 278 | tilt (~> 1.1) 279 | sdoc (0.4.1) 280 | json (~> 1.7, >= 1.7.7) 281 | rdoc (~> 4.0) 282 | shoulda-matchers (2.7.0) 283 | activesupport (>= 3.0.0) 284 | slop (3.6.0) 285 | spring (1.3.6) 286 | sprockets (3.3.4) 287 | rack (~> 1.0) 288 | sprockets-rails (2.3.3) 289 | actionpack (>= 3.0) 290 | activesupport (>= 3.0) 291 | sprockets (>= 2.8, < 4.0) 292 | sqlite3 (1.3.10) 293 | stal (0.1.0) 294 | redic 295 | thor (0.19.1) 296 | thread_safe (0.3.5) 297 | tilt (1.4.1) 298 | timecop (0.7.4) 299 | turbolinks (2.5.3) 300 | coffee-rails 301 | tzinfo (1.2.2) 302 | thread_safe (~> 0.1) 303 | uglifier (2.7.1) 304 | execjs (>= 0.3.0) 305 | json (>= 1.8.0) 306 | uniform_notifier (1.9.0) 307 | unparser (0.2.4) 308 | abstract_type (~> 0.0.7) 309 | adamantium (~> 0.2.0) 310 | concord (~> 0.1.5) 311 | diff-lcs (~> 1.2.5) 312 | equalizer (~> 0.0.9) 313 | parser (~> 2.2.2) 314 | procto (~> 0.0.2) 315 | vcr (2.9.3) 316 | virtus (1.0.5) 317 | axiom-types (~> 0.1) 318 | coercible (~> 1.0) 319 | descendants_tracker (~> 0.0, >= 0.0.3) 320 | equalizer (~> 0.0, >= 0.0.9) 321 | warden (1.2.3) 322 | rack (>= 1.0) 323 | web-console (2.1.3) 324 | activemodel (>= 4.0) 325 | binding_of_caller (>= 0.7.2) 326 | railties (>= 4.0) 327 | sprockets-rails (>= 2.0, < 4.0) 328 | webmock (1.21.0) 329 | addressable (>= 2.3.6) 330 | crack (>= 0.3.2) 331 | xpath (2.0.0) 332 | nokogiri (~> 1.3) 333 | 334 | PLATFORMS 335 | ruby 336 | 337 | DEPENDENCIES 338 | active_mocker 339 | active_model_serializers 340 | annotate 341 | better_errors 342 | binding_of_caller 343 | bootstrap-sass 344 | bullet 345 | byebug 346 | capybara 347 | coffee-rails 348 | database_cleaner 349 | devise 350 | factory_girl_rails 351 | faker 352 | figaro 353 | font-awesome-rails 354 | fuubar 355 | httparty 356 | jbuilder 357 | jquery-rails 358 | lol_dba 359 | nokogiri 360 | ohm 361 | pg 362 | pry 363 | quiet_assets 364 | rails 365 | redic 366 | redis-throttle! 367 | regressor! 368 | rspec-rails 369 | sass-rails 370 | sdoc 371 | shoulda-matchers 372 | spring 373 | sqlite3 374 | timecop 375 | turbolinks 376 | uglifier 377 | vcr 378 | web-console (~> 2.0) 379 | webmock 380 | 381 | BUNDLED WITH 382 | 1.10.6 383 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. 2 | # Many of these configuration options can be set straight in your model. 3 | Devise.setup do |config| 4 | # The secret key used by Devise. Devise uses this key to generate 5 | # random tokens. Changing this key will render invalid all existing 6 | # confirmation, reset password and unlock tokens in the database. 7 | # config.secret_key = '742ae11dc24f0e0a56c1a9183b58d70ec33a0c303059975fb660ac9a13eca9d456940ad460d1dc20e5b8de769cda63374097332b5db3c27f0b90505316d3a222' 8 | 9 | # ==> Mailer Configuration 10 | # Configure the e-mail address which will be shown in Devise::Mailer, 11 | # note that it will be overwritten if you use your own mailer class 12 | # with default "from" parameter. 13 | config.mailer_sender = 'please-change-me-at-config-initializers-devise@example.com' 14 | 15 | # Configure the class responsible to send e-mails. 16 | # config.mailer = 'Devise::Mailer' 17 | 18 | # ==> ORM configuration 19 | # Load and configure the ORM. Supports :active_record (default) and 20 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 21 | # available as additional gems. 22 | require 'devise/orm/active_record' 23 | 24 | # ==> Configuration for any authentication mechanism 25 | # Configure which keys are used when authenticating a user. The default is 26 | # just :email. You can configure it to use [:username, :subdomain], so for 27 | # authenticating a user, both parameters are required. Remember that those 28 | # parameters are used only when authenticating and not when retrieving from 29 | # session. If you need permissions, you should implement that in a before filter. 30 | # You can also supply a hash where the value is a boolean determining whether 31 | # or not authentication should be aborted when the value is not present. 32 | # config.authentication_keys = [ :email ] 33 | 34 | # Configure parameters from the request object used for authentication. Each entry 35 | # given should be a request method and it will automatically be passed to the 36 | # find_for_authentication method and considered in your model lookup. For instance, 37 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 38 | # The same considerations mentioned for authentication_keys also apply to request_keys. 39 | # config.request_keys = [] 40 | 41 | # Configure which authentication keys should be case-insensitive. 42 | # These keys will be downcased upon creating or modifying a user and when used 43 | # to authenticate or find a user. Default is :email. 44 | config.case_insensitive_keys = [ :email ] 45 | 46 | # Configure which authentication keys should have whitespace stripped. 47 | # These keys will have whitespace before and after removed upon creating or 48 | # modifying a user and when used to authenticate or find a user. Default is :email. 49 | config.strip_whitespace_keys = [ :email ] 50 | 51 | # Tell if authentication through request.params is enabled. True by default. 52 | # It can be set to an array that will enable params authentication only for the 53 | # given strategies, for example, `config.params_authenticatable = [:database]` will 54 | # enable it only for database (email + password) authentication. 55 | # config.params_authenticatable = true 56 | 57 | # Tell if authentication through HTTP Auth is enabled. False by default. 58 | # It can be set to an array that will enable http authentication only for the 59 | # given strategies, for example, `config.http_authenticatable = [:database]` will 60 | # enable it only for database authentication. The supported strategies are: 61 | # :database = Support basic authentication with authentication key + password 62 | # config.http_authenticatable = false 63 | 64 | # If 401 status code should be returned for AJAX requests. True by default. 65 | # config.http_authenticatable_on_xhr = true 66 | 67 | # The realm used in Http Basic Authentication. 'Application' by default. 68 | # config.http_authentication_realm = 'Application' 69 | 70 | # It will change confirmation, password recovery and other workflows 71 | # to behave the same regardless if the e-mail provided was right or wrong. 72 | # Does not affect registerable. 73 | # config.paranoid = true 74 | 75 | # By default Devise will store the user in session. You can skip storage for 76 | # particular strategies by setting this option. 77 | # Notice that if you are skipping storage for all authentication paths, you 78 | # may want to disable generating routes to Devise's sessions controller by 79 | # passing skip: :sessions to `devise_for` in your config/routes.rb 80 | config.skip_session_storage = [:http_auth] 81 | 82 | # By default, Devise cleans up the CSRF token on authentication to 83 | # avoid CSRF token fixation attacks. This means that, when using AJAX 84 | # requests for sign in and sign up, you need to get a new CSRF token 85 | # from the server. You can disable this option at your own risk. 86 | # config.clean_up_csrf_token_on_authentication = true 87 | 88 | # ==> Configuration for :database_authenticatable 89 | # For bcrypt, this is the cost for hashing the password and defaults to 10. If 90 | # using other encryptors, it sets how many times you want the password re-encrypted. 91 | # 92 | # Limiting the stretches to just one in testing will increase the performance of 93 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 94 | # a value less than 10 in other environments. Note that, for bcrypt (the default 95 | # encryptor), the cost increases exponentially with the number of stretches (e.g. 96 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). 97 | config.stretches = Rails.env.test? ? 1 : 10 98 | 99 | # Setup a pepper to generate the encrypted password. 100 | # config.pepper = '45305c41f3bcf0b42b9d5248a8f92fd019df49f15c2e650b46cda5a770b9746bc571894c52dce12ad98f2d61cee96e235eb8d8917631dd97c8cb42994b4665d5' 101 | 102 | # ==> Configuration for :confirmable 103 | # A period that the user is allowed to access the website even without 104 | # confirming their account. For instance, if set to 2.days, the user will be 105 | # able to access the website for two days without confirming their account, 106 | # access will be blocked just in the third day. Default is 0.days, meaning 107 | # the user cannot access the website without confirming their account. 108 | # config.allow_unconfirmed_access_for = 2.days 109 | 110 | # A period that the user is allowed to confirm their account before their 111 | # token becomes invalid. For example, if set to 3.days, the user can confirm 112 | # their account within 3 days after the mail was sent, but on the fourth day 113 | # their account can't be confirmed with the token any more. 114 | # Default is nil, meaning there is no restriction on how long a user can take 115 | # before confirming their account. 116 | # config.confirm_within = 3.days 117 | 118 | # If true, requires any email changes to be confirmed (exactly the same way as 119 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 120 | # db field (see migrations). Until confirmed, new email is stored in 121 | # unconfirmed_email column, and copied to email column on successful confirmation. 122 | config.reconfirmable = true 123 | 124 | # Defines which key will be used when confirming an account 125 | # config.confirmation_keys = [ :email ] 126 | 127 | # ==> Configuration for :rememberable 128 | # The time the user will be remembered without asking for credentials again. 129 | # config.remember_for = 2.weeks 130 | 131 | # Invalidates all the remember me tokens when the user signs out. 132 | config.expire_all_remember_me_on_sign_out = true 133 | 134 | # If true, extends the user's remember period when remembered via cookie. 135 | # config.extend_remember_period = false 136 | 137 | # Options to be passed to the created cookie. For instance, you can set 138 | # secure: true in order to force SSL only cookies. 139 | # config.rememberable_options = {} 140 | 141 | # ==> Configuration for :validatable 142 | # Range for password length. 143 | config.password_length = 8..128 144 | 145 | # Email regex used to validate email formats. It simply asserts that 146 | # one (and only one) @ exists in the given string. This is mainly 147 | # to give user feedback and not to assert the e-mail validity. 148 | # config.email_regexp = /\A[^@]+@[^@]+\z/ 149 | 150 | # ==> Configuration for :timeoutable 151 | # The time you want to timeout the user session without activity. After this 152 | # time the user will be asked for credentials again. Default is 30 minutes. 153 | # config.timeout_in = 30.minutes 154 | 155 | # If true, expires auth token on session timeout. 156 | # config.expire_auth_token_on_timeout = false 157 | 158 | # ==> Configuration for :lockable 159 | # Defines which strategy will be used to lock an account. 160 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 161 | # :none = No lock strategy. You should handle locking by yourself. 162 | # config.lock_strategy = :failed_attempts 163 | 164 | # Defines which key will be used when locking and unlocking an account 165 | # config.unlock_keys = [ :email ] 166 | 167 | # Defines which strategy will be used to unlock an account. 168 | # :email = Sends an unlock link to the user email 169 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 170 | # :both = Enables both strategies 171 | # :none = No unlock strategy. You should handle unlocking by yourself. 172 | # config.unlock_strategy = :both 173 | 174 | # Number of authentication tries before locking an account if lock_strategy 175 | # is failed attempts. 176 | # config.maximum_attempts = 20 177 | 178 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 179 | # config.unlock_in = 1.hour 180 | 181 | # Warn on the last attempt before the account is locked. 182 | # config.last_attempt_warning = true 183 | 184 | # ==> Configuration for :recoverable 185 | # 186 | # Defines which key will be used when recovering the password for an account 187 | # config.reset_password_keys = [ :email ] 188 | 189 | # Time interval you can reset your password with a reset password key. 190 | # Don't put a too small interval or your users won't have the time to 191 | # change their passwords. 192 | config.reset_password_within = 6.hours 193 | 194 | # ==> Configuration for :encryptable 195 | # Allow you to use another encryption algorithm besides bcrypt (default). You can use 196 | # :sha1, :sha512 or encryptors from others authentication tools as :clearance_sha1, 197 | # :authlogic_sha512 (then you should set stretches above to 20 for default behavior) 198 | # and :restful_authentication_sha1 (then you should set stretches to 10, and copy 199 | # REST_AUTH_SITE_KEY to pepper). 200 | # 201 | # Require the `devise-encryptable` gem when using anything other than bcrypt 202 | # config.encryptor = :sha512 203 | 204 | # ==> Scopes configuration 205 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 206 | # "users/sessions/new". It's turned off by default because it's slower if you 207 | # are using only default views. 208 | # config.scoped_views = false 209 | 210 | # Configure the default scope given to Warden. By default it's the first 211 | # devise role declared in your routes (usually :user). 212 | # config.default_scope = :user 213 | 214 | # Set this configuration to false if you want /users/sign_out to sign out 215 | # only the current scope. By default, Devise signs out all scopes. 216 | # config.sign_out_all_scopes = true 217 | 218 | # ==> Navigation configuration 219 | # Lists the formats that should be treated as navigational. Formats like 220 | # :html, should redirect to the sign in page when the user does not have 221 | # access, but formats like :xml or :json, should return 401. 222 | # 223 | # If you have any extra navigational formats, like :iphone or :mobile, you 224 | # should add them to the navigational formats lists. 225 | # 226 | # The "*/*" below is required to match Internet Explorer requests. 227 | # config.navigational_formats = ['*/*', :html] 228 | 229 | # The default HTTP method used to sign out a resource. Default is :delete. 230 | config.sign_out_via = :delete 231 | 232 | # ==> OmniAuth 233 | # Add a new OmniAuth provider. Check the wiki for more information on setting 234 | # up on your models and hooks. 235 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' 236 | 237 | # ==> Warden configuration 238 | # If you want to use other strategies, that are not supported by Devise, or 239 | # change the failure app, you can configure them inside the config.warden block. 240 | # 241 | # config.warden do |manager| 242 | # manager.intercept_401 = false 243 | # manager.default_strategies(scope: :user).unshift :some_external_strategy 244 | # end 245 | 246 | # ==> Mountable engine configurations 247 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 248 | # is mountable, there are some extra configurations to be taken into account. 249 | # The following options are available, assuming the engine is mounted as: 250 | # 251 | # mount MyEngine, at: '/my_engine' 252 | # 253 | # The router that invoked `devise_for`, in the example above, would be: 254 | # config.router_name = :my_engine 255 | # 256 | # When using omniauth, Devise cannot automatically set Omniauth path, 257 | # so you need to do it manually. For the users scope, it would be: 258 | # config.omniauth_path_prefix = '/my_engine/users/auth' 259 | end 260 | -------------------------------------------------------------------------------- /vendor/assets/javascripts/nprogress.js: -------------------------------------------------------------------------------- 1 | /* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress 2 | * @license MIT */ 3 | 4 | ;(function(root, factory) { 5 | 6 | if (typeof define === 'function' && define.amd) { 7 | define(factory); 8 | } else if (typeof exports === 'object') { 9 | module.exports = factory(); 10 | } else { 11 | root.NProgress = factory(); 12 | } 13 | 14 | })(this, function() { 15 | var NProgress = {}; 16 | 17 | NProgress.version = '0.1.6'; 18 | 19 | var Settings = NProgress.settings = { 20 | minimum: 0.25, 21 | easing: 'ease', 22 | positionUsing: '', 23 | speed: 200, 24 | trickle: true, 25 | trickleRate: 0.1, 26 | trickleSpeed: 800, 27 | showSpinner: true, 28 | barSelector: '[role="bar"]', 29 | spinnerSelector: '[role="spinner"]', 30 | parent: 'body', 31 | template: '
' 32 | }; 33 | 34 | /** 35 | * Updates configuration. 36 | * 37 | * NProgress.configure({ 38 | * minimum: 0.1 39 | * }); 40 | */ 41 | NProgress.configure = function(options) { 42 | var key, value; 43 | for (key in options) { 44 | value = options[key]; 45 | if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value; 46 | } 47 | 48 | return this; 49 | }; 50 | 51 | /** 52 | * Last number. 53 | */ 54 | 55 | NProgress.status = null; 56 | 57 | /** 58 | * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. 59 | * 60 | * NProgress.set(0.4); 61 | * NProgress.set(1.0); 62 | */ 63 | 64 | NProgress.set = function(n) { 65 | var started = NProgress.isStarted(); 66 | 67 | n = clamp(n, Settings.minimum, 1); 68 | NProgress.status = (n === 1 ? null : n); 69 | 70 | var progress = NProgress.render(!started), 71 | bar = progress.querySelector(Settings.barSelector), 72 | speed = Settings.speed, 73 | ease = Settings.easing; 74 | 75 | progress.offsetWidth; /* Repaint */ 76 | 77 | queue(function(next) { 78 | // Set positionUsing if it hasn't already been set 79 | if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); 80 | 81 | // Add transition 82 | css(bar, barPositionCSS(n, speed, ease)); 83 | 84 | if (n === 1) { 85 | // Fade out 86 | css(progress, { 87 | transition: 'none', 88 | opacity: 1 89 | }); 90 | progress.offsetWidth; /* Repaint */ 91 | 92 | setTimeout(function() { 93 | css(progress, { 94 | transition: 'all ' + speed + 'ms linear', 95 | opacity: 0 96 | }); 97 | setTimeout(function() { 98 | NProgress.remove(); 99 | next(); 100 | }, speed); 101 | }, speed); 102 | } else { 103 | setTimeout(next, speed); 104 | } 105 | }); 106 | 107 | return this; 108 | }; 109 | 110 | NProgress.isStarted = function() { 111 | return typeof NProgress.status === 'number'; 112 | }; 113 | 114 | /** 115 | * Shows the progress bar. 116 | * This is the same as setting the status to 0%, except that it doesn't go backwards. 117 | * 118 | * NProgress.start(); 119 | * 120 | */ 121 | NProgress.start = function() { 122 | if (!NProgress.status) NProgress.set(0); 123 | 124 | var work = function() { 125 | setTimeout(function() { 126 | if (!NProgress.status) return; 127 | NProgress.trickle(); 128 | work(); 129 | }, Settings.trickleSpeed); 130 | }; 131 | 132 | if (Settings.trickle) work(); 133 | 134 | return this; 135 | }; 136 | 137 | /** 138 | * Hides the progress bar. 139 | * This is the *sort of* the same as setting the status to 100%, with the 140 | * difference being `done()` makes some placebo effect of some realistic motion. 141 | * 142 | * NProgress.done(); 143 | * 144 | * If `true` is passed, it will show the progress bar even if its hidden. 145 | * 146 | * NProgress.done(true); 147 | */ 148 | 149 | NProgress.done = function(force) { 150 | if (!force && !NProgress.status) return this; 151 | 152 | return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); 153 | }; 154 | 155 | /** 156 | * Increments by a random amount. 157 | */ 158 | 159 | NProgress.inc = function(amount) { 160 | var n = NProgress.status; 161 | 162 | if (!n) { 163 | return NProgress.start(); 164 | } else { 165 | if (typeof amount !== 'number') { 166 | amount = (1 - n) * clamp(Math.random() * n, 0.2, 0.95); 167 | } 168 | 169 | n = clamp(n + amount, 0, 0.994); 170 | return NProgress.set(n); 171 | } 172 | }; 173 | 174 | NProgress.trickle = function() { 175 | return NProgress.inc(Math.random() * Settings.trickleRate); 176 | }; 177 | 178 | /** 179 | * Waits for all supplied jQuery promises and 180 | * increases the progress as the promises resolve. 181 | * 182 | * @param $promise jQUery Promise 183 | */ 184 | (function() { 185 | var initial = 0, current = 0; 186 | 187 | NProgress.promise = function($promise) { 188 | if (!$promise || $promise.state() == "resolved") { 189 | return this; 190 | } 191 | 192 | if (current == 0) { 193 | NProgress.start(); 194 | } 195 | 196 | initial++; 197 | current++; 198 | 199 | $promise.always(function() { 200 | current--; 201 | if (current == 0) { 202 | initial = 0; 203 | NProgress.done(); 204 | } else { 205 | NProgress.set((initial - current) / initial); 206 | } 207 | }); 208 | 209 | return this; 210 | }; 211 | 212 | })(); 213 | 214 | /** 215 | * (Internal) renders the progress bar markup based on the `template` 216 | * setting. 217 | */ 218 | 219 | NProgress.render = function(fromStart) { 220 | if (NProgress.isRendered()) return document.getElementById('nprogress'); 221 | 222 | addClass(document.documentElement, 'nprogress-busy'); 223 | 224 | var progress = document.createElement('div'); 225 | progress.id = 'nprogress'; 226 | progress.innerHTML = Settings.template; 227 | 228 | var bar = progress.querySelector(Settings.barSelector), 229 | perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0), 230 | parent = document.querySelector(Settings.parent), 231 | spinner; 232 | 233 | css(bar, { 234 | transition: 'all 0 linear', 235 | transform: 'translate3d(' + perc + '%,0,0)' 236 | }); 237 | 238 | if (!Settings.showSpinner) { 239 | spinner = progress.querySelector(Settings.spinnerSelector); 240 | spinner && removeElement(spinner); 241 | } 242 | 243 | if (parent != document.body) { 244 | addClass(parent, 'nprogress-custom-parent'); 245 | } 246 | 247 | parent.appendChild(progress); 248 | return progress; 249 | }; 250 | 251 | /** 252 | * Removes the element. Opposite of render(). 253 | */ 254 | 255 | NProgress.remove = function() { 256 | removeClass(document.documentElement, 'nprogress-busy'); 257 | removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent') 258 | var progress = document.getElementById('nprogress'); 259 | progress && removeElement(progress); 260 | }; 261 | 262 | /** 263 | * Checks if the progress bar is rendered. 264 | */ 265 | 266 | NProgress.isRendered = function() { 267 | return !!document.getElementById('nprogress'); 268 | }; 269 | 270 | /** 271 | * Determine which positioning CSS rule to use. 272 | */ 273 | 274 | NProgress.getPositioningCSS = function() { 275 | // Sniff on document.body.style 276 | var bodyStyle = document.body.style; 277 | 278 | // Sniff prefixes 279 | var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : 280 | ('MozTransform' in bodyStyle) ? 'Moz' : 281 | ('msTransform' in bodyStyle) ? 'ms' : 282 | ('OTransform' in bodyStyle) ? 'O' : ''; 283 | 284 | if (vendorPrefix + 'Perspective' in bodyStyle) { 285 | // Modern browsers with 3D support, e.g. Webkit, IE10 286 | return 'translate3d'; 287 | } else if (vendorPrefix + 'Transform' in bodyStyle) { 288 | // Browsers without 3D support, e.g. IE9 289 | return 'translate'; 290 | } else { 291 | // Browsers without translate() support, e.g. IE7-8 292 | return 'margin'; 293 | } 294 | }; 295 | 296 | /** 297 | * Helpers 298 | */ 299 | 300 | function clamp(n, min, max) { 301 | if (n < min) return min; 302 | if (n > max) return max; 303 | return n; 304 | } 305 | 306 | /** 307 | * (Internal) converts a percentage (`0..1`) to a bar translateX 308 | * percentage (`-100%..0%`). 309 | */ 310 | 311 | function toBarPerc(n) { 312 | return (-1 + n) * 100; 313 | } 314 | 315 | 316 | /** 317 | * (Internal) returns the correct CSS for changing the bar's 318 | * position given an n percentage, and speed and ease from Settings 319 | */ 320 | 321 | function barPositionCSS(n, speed, ease) { 322 | var barCSS; 323 | 324 | if (Settings.positionUsing === 'translate3d') { 325 | barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; 326 | } else if (Settings.positionUsing === 'translate') { 327 | barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; 328 | } else { 329 | barCSS = { 'margin-left': toBarPerc(n)+'%' }; 330 | } 331 | 332 | barCSS.transition = 'all '+speed+'ms '+ease; 333 | 334 | return barCSS; 335 | } 336 | 337 | /** 338 | * (Internal) Queues a function to be executed. 339 | */ 340 | 341 | var queue = (function() { 342 | var pending = []; 343 | 344 | function next() { 345 | var fn = pending.shift(); 346 | if (fn) { 347 | fn(next); 348 | } 349 | } 350 | 351 | return function(fn) { 352 | pending.push(fn); 353 | if (pending.length == 1) next(); 354 | }; 355 | })(); 356 | 357 | /** 358 | * (Internal) Applies css properties to an element, similar to the jQuery 359 | * css method. 360 | * 361 | * While this helper does assist with vendor prefixed property names, it 362 | * does not perform any manipulation of values prior to setting styles. 363 | */ 364 | 365 | var css = (function() { 366 | var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ], 367 | cssProps = {}; 368 | 369 | function camelCase(string) { 370 | return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { 371 | return letter.toUpperCase(); 372 | }); 373 | } 374 | 375 | function getVendorProp(name) { 376 | var style = document.body.style; 377 | if (name in style) return name; 378 | 379 | var i = cssPrefixes.length, 380 | capName = name.charAt(0).toUpperCase() + name.slice(1), 381 | vendorName; 382 | while (i--) { 383 | vendorName = cssPrefixes[i] + capName; 384 | if (vendorName in style) return vendorName; 385 | } 386 | 387 | return name; 388 | } 389 | 390 | function getStyleProp(name) { 391 | name = camelCase(name); 392 | return cssProps[name] || (cssProps[name] = getVendorProp(name)); 393 | } 394 | 395 | function applyCss(element, prop, value) { 396 | prop = getStyleProp(prop); 397 | element.style[prop] = value; 398 | } 399 | 400 | return function(element, properties) { 401 | var args = arguments, 402 | prop, 403 | value; 404 | 405 | if (args.length == 2) { 406 | for (prop in properties) { 407 | value = properties[prop]; 408 | if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value); 409 | } 410 | } else { 411 | applyCss(element, args[1], args[2]); 412 | } 413 | } 414 | })(); 415 | 416 | /** 417 | * (Internal) Determines if an element or space separated list of class names contains a class name. 418 | */ 419 | 420 | function hasClass(element, name) { 421 | var list = typeof element == 'string' ? element : classList(element); 422 | return list.indexOf(' ' + name + ' ') >= 0; 423 | } 424 | 425 | /** 426 | * (Internal) Adds a class to an element. 427 | */ 428 | 429 | function addClass(element, name) { 430 | var oldList = classList(element), 431 | newList = oldList + name; 432 | 433 | if (hasClass(oldList, name)) return; 434 | 435 | // Trim the opening space. 436 | element.className = newList.substring(1); 437 | } 438 | 439 | /** 440 | * (Internal) Removes a class from an element. 441 | */ 442 | 443 | function removeClass(element, name) { 444 | var oldList = classList(element), 445 | newList; 446 | 447 | if (!hasClass(element, name)) return; 448 | 449 | // Replace the class name. 450 | newList = oldList.replace(' ' + name + ' ', ' '); 451 | 452 | // Trim the opening and closing spaces. 453 | element.className = newList.substring(1, newList.length - 1); 454 | } 455 | 456 | /** 457 | * (Internal) Gets a space separated list of the class names on the element. 458 | * The list is wrapped with a single space on each end to facilitate finding 459 | * matches within the list. 460 | */ 461 | 462 | function classList(element) { 463 | return (' ' + (element.className || '') + ' ').replace(/\s+/gi, ' '); 464 | } 465 | 466 | /** 467 | * (Internal) Removes an element from the DOM. 468 | */ 469 | 470 | function removeElement(element) { 471 | element && element.parentNode && element.parentNode.removeChild(element); 472 | } 473 | 474 | return NProgress; 475 | }); 476 | 477 | -------------------------------------------------------------------------------- /spec/support/cassettes/SearchController/POST_slack/when_params_aren_t_correct/should_return_No_gifs_found_if_the_query_doesn_t_return_gifs.yml: -------------------------------------------------------------------------------- 1 | --- 2 | http_interactions: 3 | - request: 4 | method: get 5 | uri: http://www.reactiongifs.com/?s=asdadas&submit=Search 6 | body: 7 | encoding: US-ASCII 8 | string: '' 9 | headers: 10 | Accept-Encoding: 11 | - gzip;q=1.0,deflate;q=0.6,identity;q=0.3 12 | Accept: 13 | - "*/*" 14 | User-Agent: 15 | - Ruby 16 | response: 17 | status: 18 | code: 200 19 | message: OK 20 | headers: 21 | Date: 22 | - Thu, 21 May 2015 14:30:40 GMT 23 | Content-Type: 24 | - text/html; charset=UTF-8 25 | Transfer-Encoding: 26 | - chunked 27 | Connection: 28 | - keep-alive 29 | Set-Cookie: 30 | - __cfduid=da5bfd5dd309a2a6d88f775d6befa1d3f1432218639; expires=Fri, 20-May-16 31 | 14:30:39 GMT; path=/; domain=.reactiongifs.com; HttpOnly 32 | X-Powered-By: 33 | - PHP/5.4.40 34 | X-Pingback: 35 | - http://www.reactiongifs.com/xmlrpc.php 36 | Vary: 37 | - Accept-Encoding 38 | Cache-Control: 39 | - private, max-age=360, must-revalidate 40 | Server: 41 | - cloudflare-nginx 42 | Cf-Ray: 43 | - 1ea0fb02976e0c23-AMS 44 | body: 45 | encoding: ASCII-8BIT 46 | string: "\n\n\nYou 48 | searched for asdadas - Reaction GIFs\n\n\n\n\n\n \n\n\n\n\n\n\n\n\n\n\n \n\n\n \n\n \n\n\n\n\n\n\n\n \n\n \n\n\n\n\n\n \n\n 92 | \n\n \n\n \n\n \n\n 102 | \n\n \n\n \n\n\n\n
\n
\n
\n
\n\n\"Reaction\n\n

Reaction 113 | GIFs

\nSay it with a GIF!\n
114 | \n \n
\n
\n
\n 132 | \n\n
\n \n
\n
\n
\n

It looks like your search term isn't 137 | in our database. The good news is, we will probably add it in the future. 138 | Frequently searched for terms are added to the tag database regularly. Care 139 | to search again?

\n
\n\n
143 | \n
\n
\n
\n

I am feeling . . .

\n\n
\n
\n

My answer is . . .

\n\n
\n
\n
168 |

Search

169 |
\n
\n\n\n
\n
\n
\n
\n\n
\n
\n
176 | \n
\n
\n
\n
\n
\n
\n\n
\n
179 |
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n
\n

© 182 | 2015 Reaction GIFs. All Rights Reserved. | Make 183 | a Funny Face | Zen Tips | Rad 184 | Dudes | Hungry Trees | Cute 185 | Cat GIFs

\n
\n
\n

\n \n

\n
\n
\n
\n
\n
\n \n\n" 193 | http_version: 194 | recorded_at: Thu, 21 May 2015 14:30:40 GMT 195 | recorded_with: VCR 2.9.3 196 | --------------------------------------------------------------------------------