├── .ruby-gemset ├── Procfile ├── app ├── lib │ ├── learner.rb │ ├── responder.rb │ ├── botsolver.rb │ ├── ngram.rb │ ├── nscaler.rb │ ├── markov.rb │ ├── wordchain.rb │ ├── lexicon.rb │ ├── learn.rb │ ├── formulator.rb │ ├── entrychooser.rb │ └── jeffserver.rb ├── models │ └── entries.rb ├── tester.rb └── main.rb ├── scripts └── setupdb.sh ├── db ├── migrate │ ├── 20160427050108_add_nword_to_entries.rb │ └── 20160422190000_create_entries.rb ├── config.yml └── schema.rb ├── config └── environments.rb ├── Gemfile ├── CONTRIBUTING.md ├── .travis.yml ├── Rakefile ├── .gitignore ├── LICENSE ├── README.md └── Gemfile.lock /.ruby-gemset: -------------------------------------------------------------------------------- 1 | JeffBot 2 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | worker: bundle exec ruby ./app/main.rb 2 | -------------------------------------------------------------------------------- /app/lib/learner.rb: -------------------------------------------------------------------------------- 1 | class Learner 2 | def self.go 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/models/entries.rb: -------------------------------------------------------------------------------- 1 | class Entries < ActiveRecord::Base 2 | 3 | end -------------------------------------------------------------------------------- /app/lib/responder.rb: -------------------------------------------------------------------------------- 1 | class Responder 2 | def self.go 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/tester.rb: -------------------------------------------------------------------------------- 1 | require_relative 'lib/botsolver' 2 | 3 | puts Botsolver.go("power makes me happy") -------------------------------------------------------------------------------- /scripts/setupdb.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | echo 'Creating test database...' 3 | psql -c 'create database travis_ci_test;' -U postgres 4 | rake db:migrate 5 | echo 'Done!' -------------------------------------------------------------------------------- /db/migrate/20160427050108_add_nword_to_entries.rb: -------------------------------------------------------------------------------- 1 | class AddNwordToEntries < ActiveRecord::Migration 2 | def change 3 | add_column :entries, :nword, :string 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/lib/botsolver.rb: -------------------------------------------------------------------------------- 1 | require_relative 'markov' 2 | require_relative 'lexicon' 3 | 4 | class Botsolver 5 | def self.go(arg) 6 | query = Lexicon.clean(arg) 7 | return Markov.go(query) 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/lib/ngram.rb: -------------------------------------------------------------------------------- 1 | module Ngram 2 | def Ngram.ngrams(n, string) 3 | string.split(' ').each_cons(n).to_a 4 | end 5 | def Ngram.bigram(string) 6 | return ngrams(2, string) 7 | end 8 | def Ngram.trigram(string) 9 | return ngrams(3, string) 10 | end 11 | end -------------------------------------------------------------------------------- /db/migrate/20160422190000_create_entries.rb: -------------------------------------------------------------------------------- 1 | class CreateEntries < ActiveRecord::Migration 2 | def up 3 | create_table :entries do |t| 4 | t.string :word 5 | t.string :definition 6 | t.integer :count 7 | end 8 | end 9 | 10 | def down 11 | drop_table :entries 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /config/environments.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | if ENV['DATABASE_URL'] 3 | ActiveRecord::Base.establish_connection(ENV['DATABASE_URL']) 4 | else 5 | ActiveRecord::Base.establish_connection(YAML::load(IO.read('db/config.yml'))[ENV["RACK_ENV"] || 'development']) 6 | end 7 | ActiveRecord::Base.logger = Logger.new(STDOUT) 8 | -------------------------------------------------------------------------------- /db/config.yml: -------------------------------------------------------------------------------- 1 | common: &common 2 | host: localhost 3 | adapter: postgresql 4 | encoding: unicode 5 | pool: 5 6 | 7 | development: 8 | <<: *common 9 | username: jeff_bot 10 | password: jeff_bot 11 | database: jeff_bot_dev 12 | 13 | test: 14 | <<: *common 15 | username: jeff_bot 16 | database: jeff_bot_test 17 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby "2.3.0" 4 | 5 | gem 'bunny' 6 | gem 'standalone_migrations' 7 | gem "pg" 8 | gem "rake" 9 | gem 'engtagger' 10 | gem 'json' 11 | gem 'awesome_print' 12 | gem 'celluloid-io' 13 | 14 | group :development do 15 | gem 'foreman' 16 | end 17 | 18 | group :test do 19 | gem 'codacy-coverage' 20 | end 21 | -------------------------------------------------------------------------------- /app/lib/nscaler.rb: -------------------------------------------------------------------------------- 1 | require_relative 'entrychooser' 2 | 3 | module NScaler 4 | def NScaler.scale(start_level, query) 5 | if start_level <= 1 6 | return "" 7 | end 8 | result = EntryChooser.get(start_level, query) 9 | if result.eql?("") 10 | return EntryChooser.get(start_level - 1, query.last(start_level - 1)) 11 | else 12 | return result 13 | end 14 | end 15 | end -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Can I contribute? 2 | Yes! JeffBot is currently an ongoing project that is always in need of work. 3 | 4 | # How can I contribute? 5 | - Submit a code patch 6 | - Update the GitHub wiki 7 | - File an issues if you find a bug or would like to see a new feature 8 | - ~~donate on GratiPay~~ 9 | 10 | # Are there any rules on contributing? 11 | The only rules we have are keep your code documented and test your changes before filing a PR. 12 | -------------------------------------------------------------------------------- /app/lib/markov.rb: -------------------------------------------------------------------------------- 1 | require_relative 'ngram' 2 | require_relative 'lexicon' 3 | require_relative 'formulator' 4 | require_relative 'learn' 5 | 6 | module Markov 7 | def Markov.go(query) 8 | if Lexicon.get_number_of_tokens(query) >= 3 9 | grams = Ngram.trigram(query) 10 | Learn.train_array(grams) 11 | end 12 | subject = Lexicon.get_subject(query) 13 | return Formulator.create_sentence_random(subject) 14 | end 15 | end -------------------------------------------------------------------------------- /app/main.rb: -------------------------------------------------------------------------------- 1 | require "bunny" 2 | require_relative "../config/environments" 3 | require_relative "lib/botsolver" 4 | require_relative "lib/responder" 5 | require_relative "lib/learner" 6 | require_relative "lib/jeffserver" 7 | 8 | conn = Bunny.new(ENV['RABBITMQ_BIGWIG_URL']) 9 | conn.start 10 | ch = conn.create_channel 11 | server = JeffServer.new(ch) 12 | puts "A new backend worker is now online." 13 | begin 14 | server.respond 15 | server.learn 16 | server.start 17 | rescue StandardError => ex 18 | puts ex 19 | ch.close 20 | conn.close 21 | end 22 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | cache: bundler 3 | rvm: 4 | - 2.3.0 5 | git: 6 | submodules: false 7 | env: 8 | global: 9 | - RACK_ENV=test 10 | services: 11 | - rabbitmq 12 | notifications: 13 | email: 14 | recipients: 15 | - wordman05@gmail.com 16 | on_success: change 17 | on_failure: always 18 | branches: 19 | only: 20 | - master 21 | install: 22 | - bundle install 23 | script: 24 | - ./scripts/setupdb.sh 25 | # - ruby ./backend/main.rb & 26 | - rake test --trace 27 | deploy: 28 | provider: heroku 29 | api_key: "9951e24c-b489-48fe-8a60-b325e8d850dd" 30 | app: jeffchatbot-backend 31 | run: "rake db:migrate --trace" 32 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'tasks/standalone_migrations' 2 | 3 | StandaloneMigrations::Configurator.environments_config do |env| 4 | env.on "production" do 5 | if (ENV['DATABASE_URL']) 6 | db = URI.parse(ENV['DATABASE_URL']) 7 | return { 8 | :adapter => db.scheme == 'postgres' ? 'postgresql' : db.scheme, 9 | :host => db.host, 10 | :username => db.user, 11 | :password => db.password, 12 | :database => db.path[1..-1], 13 | :encoding => 'utf8' 14 | } 15 | end 16 | 17 | nil 18 | end 19 | end 20 | 21 | task :test do 22 | #system 'ruby ./backend/main.rb &' 23 | #ruby "test/test.rb" 24 | end 25 | -------------------------------------------------------------------------------- /app/lib/wordchain.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require_relative '../models/entries' 3 | 4 | class WordChain 5 | def self.next_word(previous_tokens) 6 | tokens = Entries.where(word: previous_tokens[0], nword: previous_tokens[1]).order(count: :desc) 7 | find_best_token(tokens) 8 | end 9 | 10 | def self.bigram_word(previous_token) 11 | tokens = Entries.where(word: previous_token).order(count: :desc) 12 | find_best_token(tokens) 13 | end 14 | 15 | private 16 | def self.find_best_token(tokens) 17 | return "" if(tokens.length == 0) 18 | 19 | tokens.map(&:definition).each do |token| 20 | return token if rand < 0.9 21 | end 22 | return tokens.last.definition 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/lib/lexicon.rb: -------------------------------------------------------------------------------- 1 | module Lexicon 2 | def Lexicon.clean(str) 3 | result = "" 4 | Lexicon.get_tokens(str).each do |token| 5 | result = result + " " + token.gsub(/[^a-zA-Z]/, "") 6 | end 7 | return result.strip.downcase 8 | end 9 | def Lexicon.get_subject(query) 10 | return query.split(" ")[rand(query.split(" ").length)] 11 | end 12 | def Lexicon.get_tokens(query) 13 | return query.split(" ") 14 | end 15 | def Lexicon.get_number_of_tokens(query) 16 | return Lexicon.get_tokens(query).length 17 | end 18 | def Lexicon.has_equal_tokens(a, b) 19 | if Lexicon.get_number_of_tokens(a) == Lexicon.get_number_of_tokens(b) 20 | return true 21 | else 22 | return false 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/lib/learn.rb: -------------------------------------------------------------------------------- 1 | require 'active_record' 2 | require_relative '../models/entries' 3 | 4 | module Learn 5 | def Learn.train_array(inputs) 6 | inputs.each do |input| 7 | Learn.train(input) 8 | end 9 | end 10 | def Learn.train(input) 11 | learn_data = Entries.find_by(word: input[0], nword: input[1], definition: input[2]) 12 | if learn_data.nil? 13 | if Entries.count < 9900 14 | learn_data = Entries.create(id: Entries.count, word: input[0], nword: input[1], definition: input[2], count: 1) 15 | learn_data.save 16 | end 17 | else 18 | learn_data.count = learn_data.count + 1 19 | learn_data.save 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.gem 2 | *.rbc 3 | /.config 4 | /coverage/ 5 | /InstalledFiles 6 | /pkg/ 7 | /spec/reports/ 8 | /test/tmp/ 9 | /test/version_tmp/ 10 | /tmp/ 11 | 12 | ## Specific to RubyMotion: 13 | .dat* 14 | .repl_history 15 | build/ 16 | 17 | ## Documentation cache and generated files: 18 | /.yardoc/ 19 | /_yardoc/ 20 | 21 | ## Environment normalisation: 22 | /.bundle/ 23 | /vendor/bundle 24 | /lib/bundler/man/ 25 | 26 | # for a library or gem, you might want to ignore these files since the code is 27 | # intended to run in multiple environments; otherwise, check them in: 28 | Gemfile.lock 29 | # .ruby-version 30 | # .ruby-gemset 31 | 32 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 33 | .rvmrc 34 | 35 | # NewRelic stuff 36 | logfile 37 | log/newrelic_agent.log 38 | 39 | *.swp 40 | *.swo 41 | -------------------------------------------------------------------------------- /app/lib/formulator.rb: -------------------------------------------------------------------------------- 1 | require_relative 'wordchain' 2 | 3 | module Formulator 4 | def Formulator.create_sentence(length, subject) 5 | iterator = 0 6 | result = [] 7 | result << subject.chomp 8 | previous_token = subject 9 | token = WordChain.bigram_word(previous_token) 10 | for _ in 0..length*5 11 | break if token.blank? 12 | result << token.chomp 13 | earlier_token = previous_token 14 | previous_token = token 15 | token = WordChain.next_word([earlier_token, previous_token]) 16 | if token.blank? || ([false]*5+[true]).sample 17 | break if [true, false, false].sample 18 | token = WordChain.bigram_word(previous_token) 19 | end 20 | end 21 | 22 | result[0] = result[0].titleize 23 | "#{result.join(' ')}." 24 | end 25 | 26 | def Formulator.create_sentence_random(subject) 27 | return Formulator.create_sentence(3 + rand(8), subject) 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /app/lib/entrychooser.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra/activerecord' 2 | require_relative '../models/entries' 3 | require_relative '../models/twoentries' 4 | 5 | module EntryChooser 6 | def EntryChooser.get(level, query) 7 | if level > 3 or level < 2 8 | return "" 9 | end 10 | case level 11 | when 2 12 | return EntryChooser.pick_from_list(Entries.where(word: query.last).order(count: :desc)) 13 | when 3 14 | return EntryChooser.pick_from_list(TwoEntries.where(word: query.last, nword: query.last(2).first).order(count: :desc)) 15 | else 16 | return "" 17 | end 18 | end 19 | def EntryChooser.pick_from_list(options) 20 | if(options.length == 0) 21 | return "" 22 | else 23 | tokens = Array.new 24 | options.each do |item| 25 | for i in 0..item.count 26 | tokens.push(item.definition) 27 | end 28 | end 29 | return tokens[rand(tokens.length)] 30 | end 31 | end 32 | end -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Joshua Zenn 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 | -------------------------------------------------------------------------------- /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: 20160427050108) do 15 | 16 | # These are extensions that must be enabled in order to support this database 17 | enable_extension "plpgsql" 18 | 19 | create_table "entries", force: :cascade do |t| 20 | t.string "word" 21 | t.string "definition" 22 | t.integer "count" 23 | t.string "nword" 24 | end 25 | 26 | end 27 | -------------------------------------------------------------------------------- /app/lib/jeffserver.rb: -------------------------------------------------------------------------------- 1 | require 'awesome_print' 2 | 3 | class JeffServer 4 | def initialize(ch) 5 | @ch = ch 6 | end 7 | 8 | def start(queue_name='jeff_bot') 9 | subscribe(queue_name, Botsolver, respond: true, blocking: true) 10 | end 11 | 12 | def learn(queue_name='jeff_bot_learn') 13 | subscribe(queue_name, Learner) 14 | end 15 | 16 | def respond(queue_name='jeff_bot_respond') 17 | subscribe(queue_name, Responder, respond: true) 18 | end 19 | 20 | private 21 | def subscribe(queue_name, message_parser, blocking: false, respond: false) 22 | ap "STARTING QUEUE #{queue_name}" 23 | queue = @ch.queue(queue_name) 24 | exchange = @ch.default_exchange 25 | 26 | queue.subscribe(:block => blocking) do |delivery_info, properties, payload| 27 | begin 28 | r = "" 29 | begin 30 | r = message_parser.go(payload) 31 | rescue StandardError => ex 32 | r = "ERROR: #{ex}" 33 | end 34 | 35 | if respond 36 | exchange.publish(r, :routing_key => properties.reply_to, :correlation_id => properties.correlation_id) 37 | end 38 | rescue StandardError => ex 39 | ap ex 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/ARMmaster17/JeffBot.svg?branch=master)](https://travis-ci.org/ARMmaster17/JeffBot) 2 | [![Gratipay Team](https://img.shields.io/gratipay/team/JeffBot-AI.svg?maxAge=2592000?style=flat-square)](https://gratipay.com/JeffBot-AI/) 3 | 4 | # JeffBot2 is out! Check it out at [here](https://github.com/ARMmaster17/jeffbot2) 5 | 6 | # JeffBot 7 | (Yet another) comical and extensible chat bot. 8 | 9 | ## What is it? 10 | JeffBot is just another chat bot...nothing to see here. 11 | 12 | ## Ok, seriously, what is it? 13 | JeffBot is a markov/neural-network based machine learning experiment in the form of a chat bot. 14 | 15 | ## What is the project status? 16 | We are still in the early stages of development. JeffBot was originally supposed to be a fun weekend project, but due to the request of multiple developers, it has been published here on GitHub for all to see. 17 | 18 | ## Can I help? 19 | Sure! Just be sure to document your code and your PRs. You are responsible for testing your code before submitting. Also, make sure that when you submit a PR that you do it against the `dev` branch. Other than that, remember to keep it simple! For more details, take a look at our [CONTRIBUTING](https://github.com/ARMmaster17/JeffBot/blob/master/CONTRIBUTING.md) file. 20 | 21 | ### Migrations 22 | We use the [standalone-migrations](https://github.com/thuss/standalone-migrations) gem. You can add migrations by running: 23 | ```bash 24 | $ rake db:new_migration name=add_other_field_to_entries 25 | ``` 26 | You can then edit the migration as normal: 27 | ```ruby 28 | class AddOtherFieldToEntries < ActiveRecord::Migration 29 | def change 30 | add_column :entries, :other_field, :string 31 | end 32 | end 33 | ``` 34 | 35 | ## What if I can't program? 36 | ~~We accept Gratipay donations~~. We also benefit from sharing the site with your friends. The more people that talk to JeffBot, the faster it learns! 37 | 38 | ## How does it work? 39 | This is the backend process. This is meerly a RabbitMQ subscriber & it will call the correct method based on the queue the message was received on. 40 | 41 | ## How can I talk to it? 42 | You can use any one of the fron end applications! 43 | 44 | - [JeffBot Slack](https://github.com/FreekingDean/JeffBot-Slack) 45 | - [JeffBot Web](https://github.com/FreekingDean/JeffBot-Web) 46 | 47 | ## Got more questions? 48 | Checkout our [FAQ page](https://github.com/ARMmaster17/JeffBot/wiki/FAQ) for answers! 49 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actionpack (4.2.5.2) 5 | actionview (= 4.2.5.2) 6 | activesupport (= 4.2.5.2) 7 | rack (~> 1.6) 8 | rack-test (~> 0.6.2) 9 | rails-dom-testing (~> 1.0, >= 1.0.5) 10 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 11 | actionview (4.2.5.2) 12 | activesupport (= 4.2.5.2) 13 | builder (~> 3.1) 14 | erubis (~> 2.7.0) 15 | rails-dom-testing (~> 1.0, >= 1.0.5) 16 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 17 | activemodel (4.2.5.2) 18 | activesupport (= 4.2.5.2) 19 | builder (~> 3.1) 20 | activerecord (4.2.5.2) 21 | activemodel (= 4.2.5.2) 22 | activesupport (= 4.2.5.2) 23 | arel (~> 6.0) 24 | activesupport (4.2.5.2) 25 | i18n (~> 0.7) 26 | json (~> 1.7, >= 1.7.7) 27 | minitest (~> 5.1) 28 | thread_safe (~> 0.3, >= 0.3.4) 29 | tzinfo (~> 1.1) 30 | amq-protocol (2.0.1) 31 | arel (6.0.3) 32 | awesome_print (1.6.1) 33 | builder (3.2.4) 34 | bunny (2.3.1) 35 | amq-protocol (>= 2.0.1) 36 | celluloid (0.17.3) 37 | celluloid-essentials 38 | celluloid-extras 39 | celluloid-fsm 40 | celluloid-pool 41 | celluloid-supervision 42 | timers (>= 4.1.1) 43 | celluloid-essentials (0.20.5) 44 | timers (>= 4.1.1) 45 | celluloid-extras (0.20.5) 46 | timers (>= 4.1.1) 47 | celluloid-fsm (0.20.5) 48 | timers (>= 4.1.1) 49 | celluloid-io (0.17.3) 50 | celluloid (>= 0.17.2) 51 | nio4r (>= 1.1) 52 | timers (>= 4.1.1) 53 | celluloid-pool (0.20.5) 54 | timers (>= 4.1.1) 55 | celluloid-supervision (0.20.5) 56 | timers (>= 4.1.1) 57 | codacy-coverage (0.3.1) 58 | rest-client (~> 1.8) 59 | simplecov (>= 0.10.0) 60 | concurrent-ruby (1.1.8) 61 | crass (1.0.6) 62 | docile (1.1.5) 63 | domain_name (0.5.20160310) 64 | unf (>= 0.0.5, < 1.0.0) 65 | engtagger (0.2.0) 66 | erubis (2.7.0) 67 | foreman (0.81.0) 68 | thor (~> 0.19.1) 69 | hitimes (1.2.3) 70 | http-cookie (1.0.2) 71 | domain_name (~> 0.5) 72 | i18n (0.9.5) 73 | concurrent-ruby (~> 1.0) 74 | json (1.8.3) 75 | loofah (2.9.1) 76 | crass (~> 1.0.2) 77 | nokogiri (>= 1.5.9) 78 | mime-types (2.99.1) 79 | mini_portile2 (2.4.0) 80 | minitest (5.14.4) 81 | netrc (0.11.0) 82 | nio4r (1.2.1) 83 | nokogiri (1.10.10) 84 | mini_portile2 (~> 2.4.0) 85 | pg (0.18.4) 86 | rack (1.6.13) 87 | rack-test (0.6.3) 88 | rack (>= 1.0) 89 | rails-deprecated_sanitizer (1.0.4) 90 | activesupport (>= 4.2.0.alpha) 91 | rails-dom-testing (1.0.9) 92 | activesupport (>= 4.2.0, < 5.0) 93 | nokogiri (~> 1.6) 94 | rails-deprecated_sanitizer (>= 1.0.1) 95 | rails-html-sanitizer (1.3.0) 96 | loofah (~> 2.3) 97 | railties (4.2.5.2) 98 | actionpack (= 4.2.5.2) 99 | activesupport (= 4.2.5.2) 100 | rake (>= 0.8.7) 101 | thor (>= 0.18.1, < 2.0) 102 | rake (10.5.0) 103 | rest-client (1.8.0) 104 | http-cookie (>= 1.0.2, < 2.0) 105 | mime-types (>= 1.16, < 3.0) 106 | netrc (~> 0.7) 107 | simplecov (0.11.2) 108 | docile (~> 1.1.0) 109 | json (~> 1.8) 110 | simplecov-html (~> 0.10.0) 111 | simplecov-html (0.10.0) 112 | standalone_migrations (4.0.3) 113 | activerecord (~> 4.2.5.1) 114 | railties (~> 4.2.5.1) 115 | rake (~> 10.0) 116 | thor (0.19.4) 117 | thread_safe (0.3.6) 118 | timers (4.1.1) 119 | hitimes 120 | tzinfo (1.2.9) 121 | thread_safe (~> 0.1) 122 | unf (0.1.4) 123 | unf_ext 124 | unf_ext (0.0.7.2) 125 | 126 | PLATFORMS 127 | ruby 128 | 129 | DEPENDENCIES 130 | awesome_print 131 | bunny 132 | celluloid-io 133 | codacy-coverage 134 | engtagger 135 | foreman 136 | json 137 | pg 138 | rake 139 | standalone_migrations 140 | 141 | BUNDLED WITH 142 | 1.11.2 143 | --------------------------------------------------------------------------------