├── .ruby-version ├── .ruby-gemset ├── .gitignore ├── .env ├── config.ru ├── lib ├── squad.rb ├── help_menu │ ├── ruby.rb │ ├── slack.rb │ ├── python.rb │ ├── javascript.rb │ ├── mentorship.rb │ └── opcode_challenge.rb ├── button_press │ ├── greeted.rb │ └── help_menu.rb ├── airtables │ └── mentorship_squads.rb ├── help_menu.rb ├── event.rb ├── button_press.rb └── event │ ├── team_join.rb │ └── message.rb ├── .github ├── PULL_REQUEST_TEMPLATE.md ├── ISSUE_TEMPLATE.md └── labels.json ├── Dockerfile ├── test ├── lib │ ├── squad_test.rb │ ├── event_test.rb │ ├── button_press │ │ └── help_menu_test.rb │ └── event │ │ ├── team_join_test.rb │ │ └── message_test.rb ├── test_helper.rb └── operationcode_bot_test.rb ├── views ├── help_menu │ ├── opcode_challenge_message.txt.erb │ ├── mentorship_message.txt.erb │ ├── python_message.txt.erb │ ├── ruby_message.txt.erb │ ├── slack_message.txt.erb │ └── javascript_message.txt.erb └── event │ └── team_join │ └── welcome_message.txt.erb ├── .travis.yml ├── .codeclimate.yml ├── Gemfile ├── docker-compose.yml ├── rakefile ├── bin ├── publish └── deploy ├── LICENSE ├── Gemfile.lock ├── operationcode_bot.rb ├── README.md ├── CODE_OF_CONDUCT.md └── .rubocop.yml /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.5 2 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | mentor_bot 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage 2 | 3 | .DS_Store 4 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=operationcodebot 2 | PORT=4567 3 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | require './operationcode_bot' 2 | 3 | $stdout.sync = true 4 | run Sinatra::Application 5 | -------------------------------------------------------------------------------- /lib/squad.rb: -------------------------------------------------------------------------------- 1 | module Squad 2 | def self.channel_id_for(squad) 3 | ENV.fetch("SLACK_CHANNEL_#{squad.upcase}_SQUAD") 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/help_menu/ruby.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::Ruby < HelpMenu 2 | def self.help_action 3 | { name: :ruby, text: ':ruby: Ruby', value: :ruby_help } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/help_menu/slack.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::Slack < HelpMenu 2 | def self.help_action 3 | { name: :slack, text: ':slack: Slack', value: :slack_help } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/help_menu/python.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::Python < HelpMenu 2 | def self.help_action 3 | { name: :python, text: ':python: Python', value: :python_help } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/help_menu/javascript.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::Javascript < HelpMenu 2 | def self.help_action 3 | { name: :javascript, text: 'Javascript', value: :javascript_help } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /lib/help_menu/mentorship.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::Mentorship < HelpMenu 2 | def self.help_action 3 | { name: :mentorship, text: 'I need a mentor!', value: :mentorship } 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description of changes 2 | 3 | 4 | # Issue Resolved 5 | 6 | Fixes # 7 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.3 2 | 3 | RUN apt-get update -qq && apt-get install -y build-essential 4 | 5 | RUN mkdir /app 6 | WORKDIR /app 7 | 8 | ADD Gemfile* $APP_HOME/ 9 | RUN bundle install --system 10 | 11 | ADD . /app 12 | 13 | CMD ["rackup", "-p", "4567"] 14 | -------------------------------------------------------------------------------- /lib/button_press/greeted.rb: -------------------------------------------------------------------------------- 1 | class ButtonPress::Greeted < ButtonPress 2 | def process 3 | updated_message = @data['original_message'] 4 | updated_message['attachments'].first['text'] = "<@#{@user['id']}> has greeted the new user" 5 | updated_message['attachments'].first['actions'] = [] 6 | 7 | @response = updated_message.to_json 8 | end 9 | end 10 | 11 | -------------------------------------------------------------------------------- /test/lib/squad_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../test_helper' 2 | 3 | require 'squad' 4 | 5 | class SquadTest < Minitest::Test 6 | def test_it_returns_the_channel_id_of_a_squad 7 | ENV.expects(:fetch).with('SLACK_CHANNEL_1ST_SQUAD').twice.returns('1234') 8 | assert_equal '1234', Squad.channel_id_for(:'1st') 9 | assert_equal '1234', Squad.channel_id_for('1st') 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /lib/help_menu/opcode_challenge.rb: -------------------------------------------------------------------------------- 1 | class HelpMenu::OpcodeChallenge < HelpMenu 2 | def self.help_action 3 | { name: :opcode_challenge, text: ':opcode: OpCode Challenge', value: :opcode_challenge } 4 | end 5 | 6 | def self.text 7 | names_file = File.join(File.dirname(__FILE__), '../../opcode_challenge/names.txt') 8 | @names = File.read(names_file) 9 | 10 | super 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /views/help_menu/opcode_challenge_message.txt.erb: -------------------------------------------------------------------------------- 1 | Welcome to the Operation Code challenge! The goal of this challenge is to get 2 | you to easily commit your first change to a program, see the results of the 3 | change, and leave your mark on Operation Code itself! 4 | 5 | To get started all you need to do is visit https://operationcode.org/challenge 6 | and follow the instructions. When you're all done your name will be on the 7 | Operation Code website for all to see! 8 | -------------------------------------------------------------------------------- /views/help_menu/mentorship_message.txt.erb: -------------------------------------------------------------------------------- 1 | The Operation Code mentorship program aims to pair you with an experienced developer in order to further your programming or career goals. When you sign up for our mentorship program you'll fill out a form with your interests. You'll then be paired up with an available mentor that best meets those interests. 2 | 3 | If you're interested in getting paired with a mentor, please fill out our sign up form here: http://op.co.de/mentor-request. 4 | -------------------------------------------------------------------------------- /views/help_menu/python_message.txt.erb: -------------------------------------------------------------------------------- 1 | Python is a widely used high-level programming language used for general-purpose programming. 2 | It's very friendly for beginners and is great for everything from web development to 3 | data science. 4 | 5 | Here are some python resources: 6 | Operation Code Python Room: <#C04D6M3JT|python> 7 | Python's official site: https://www.python.org/ 8 | Learn Python The Hard Way: https://learnpythonthehardway.org/book/ 9 | Automate The Boring Stuff: https://automatetheboringstuff.com/ 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | # Feature 3 | ## Why is this feature being added? 4 | 5 | 6 | ## What should your feature do? 7 | 8 | --- 9 | 10 | # Bug Report 11 | ## What is the current behavior? 12 | 13 | ## What is the expected behavior? 14 | 15 | ## What steps did you take to get this behavior? 16 | 17 | ## Additional Info 18 | ### Operating System 19 | 20 | ### Browser 21 | 22 | ### Screenshots 23 | -------------------------------------------------------------------------------- /views/help_menu/ruby_message.txt.erb: -------------------------------------------------------------------------------- 1 | Ruby is one of the most popular languages to learn as a beginner. 2 | While it can be used in any situation it's most popular for it's 3 | web framework 'Rails' which allows people to build websites quickly 4 | and easily. 5 | 6 | Here are some ruby resources: 7 | Operation Code Ruby Room: <#C04D6GTGT|ruby> 8 | Try Ruby Online: http://tryruby.org/ 9 | Learn Ruby The Hard Way: http://ruby.learncodethehardway.org/book 10 | Learn To Program: http://pine.fm/LearnToProgram/ 11 | Ruby Koans: http://rubykoans.com/ 12 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | sudo: required 4 | 5 | install: true 6 | 7 | services: 8 | - docker 9 | 10 | script: 11 | - docker-compose build 12 | - docker run operationcodebot_app /bin/sh -c 'cd /app && bundle exec rake' 13 | - if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then bash ./bin/publish && bash ./bin/deploy; fi 14 | 15 | addons: 16 | code_climate: 17 | repo_token: c4fda4d7944229b1f03f2a7e996730e7ed025196631e3ef6d07ae19ad5e2328e 18 | 19 | after_success: 20 | - bundle exec codeclimate-test-reporter 21 | 22 | -------------------------------------------------------------------------------- /.codeclimate.yml: -------------------------------------------------------------------------------- 1 | --- 2 | engines: 3 | bundler-audit: 4 | enabled: true 5 | duplication: 6 | enabled: true 7 | config: 8 | languages: 9 | - ruby 10 | - javascript 11 | - python 12 | - php 13 | fixme: 14 | enabled: true 15 | exclude_fingerprints: 16 | - 4afaf78ab58a30359c6970fee43dc8c7 17 | rubocop: 18 | enabled: true 19 | ratings: 20 | paths: 21 | - Gemfile.lock 22 | - "**.inc" 23 | - "**.js" 24 | - "**.jsx" 25 | - "**.module" 26 | - "**.php" 27 | - "**.py" 28 | - "**.rb" 29 | exclude_paths: 30 | - test/ 31 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ruby '2.3.5' 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'activesupport' 6 | gem 'airtable' 7 | gem 'httparty' 8 | gem 'operationcode-airtable', git: 'https://github.com/OperationCode/operationcode-airtable' 9 | gem 'operationcode-slack', git: 'https://github.com/OperationCode/operationcode-slack' 10 | gem 'pry' 11 | gem 'rake' 12 | gem 'rb-readline' 13 | gem 'sinatra' 14 | 15 | group :test do 16 | gem "codeclimate-test-reporter", "~> 1.0.0" 17 | gem 'minitest' 18 | gem 'minitest-reporters' 19 | gem 'rack-test' 20 | gem 'mocha' 21 | gem "simplecov" 22 | end 23 | -------------------------------------------------------------------------------- /views/help_menu/slack_message.txt.erb: -------------------------------------------------------------------------------- 1 | Slack is an online chatroom service that the Operation Code community uses. 2 | It can be accessed online, via https://operation-code.slack.com/ or via 3 | desktop or mobile apps, located at https://slack.com/downloads/. In addition to 4 | chatting, Slack also allows us to share files, audio conference and even program 5 | our own bots! Here are some tips to get you started: 6 | - You can customize your notifications per channel by clicking the gear to the 7 | left of the search box 8 | - Join as many channels as you want via the + next to Channels in the side bar. 9 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | app: 4 | build: . 5 | environment: 6 | AIRTABLE_API_KEY: CHANGE_ME 7 | AIRTABLE_MENTORSHIP_SQUADS_KEY: CHANGE_ME 8 | INVITE_USER: 'false' 9 | RACK_ENV: production 10 | SLACK_CHANNEL_1ST_SQUAD: CHANGE_ME 11 | SLACK_CHANNEL_2ND_SQUAD: CHANGE_ME 12 | SLACK_CHANNEL_3RD_SQUAD: CHANGE_ME 13 | SLACK_CLIENT_ID: CHANGE_ME 14 | SLACK_CLIENT_SECRET: CHANGE_ME 15 | SLACK_TOKEN: CHANGE_ME 16 | ports: 17 | - 4567:4567/tcp 18 | command: 19 | - rackup 20 | - -p 21 | - '4567' 22 | 23 | -------------------------------------------------------------------------------- /test/lib/event_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../test_helper' 2 | require 'squad' 3 | 4 | class EventTest < Minitest::Test 5 | def test_it_checks_for_dev_mode 6 | ENV['PRODUCTION_MODE'] = 'true' 7 | assert Event.new('fake data').production_mode? 8 | 9 | ENV['PRODUCTION_MODE'] = 'false' 10 | refute Event.new('fake data').production_mode? 11 | end 12 | 13 | def test_it_sets_the_template_path 14 | expected_path = Pathname.new(__dir__) + '../..' + 'views' + 'event' + 'team_join' 15 | assert_equal expected_path.to_s, Event::TeamJoin.new('fakedata').template_path.to_s 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /views/help_menu/javascript_message.txt.erb: -------------------------------------------------------------------------------- 1 | Javascript is a high-level programming language used for general-purpose programming. 2 | In recent years it has exploded in popularity and with the popular node.js runtime 3 | environment it can run anywhere from the browser to a server. 4 | 5 | Here are some javascript resources: 6 | Operation Code Javascript Room: <#C04CJ8H2S|javascript> 7 | Javascript Koans: https://github.com/mrdavidlaing/javascript-koans 8 | Eloquent Javascript: http://eloquentjavascript.net/ 9 | Node School: http://nodeschool.io/ 10 | Node University: http://node.university/courses 11 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "simplecov" 2 | SimpleCov.start 3 | 4 | ENV['RACK_ENV'] = 'test' 5 | 6 | require 'operationcode_bot' 7 | require 'minitest/autorun' 8 | require "mocha/mini_test" 9 | require 'rack/test' 10 | 11 | require "minitest/reporters" 12 | Minitest::Reporters.use! Minitest::Reporters::ProgressReporter.new 13 | 14 | ENV['SLACK_TOKEN'] = 'test_token' 15 | ENV['SLACK_CLIENT_ID'] = 'test_client_id' 16 | ENV['SLACK_CLIENT_SECRET'] = 'test_client_id' 17 | ENV['AIRTABLE_API_KEY'] = 'FAKEAIRTABLEKEY' 18 | ENV['AIRTABLE_MENTORSHIP_SQUADS_KEY'] = 'FAKEMENTORSHIPSQUADSKEY' 19 | ENV['INVITE_USER'] = 'true' 20 | ENV['SLACK_CHANNEL_1ST_SQUAD'] = '1STSQUADID' 21 | -------------------------------------------------------------------------------- /lib/airtables/mentorship_squads.rb: -------------------------------------------------------------------------------- 1 | module Airtables 2 | module MentorshipSquads 3 | TABLE_NAME = 'squads'.freeze 4 | BASE_ID = ENV.fetch('AIRTABLE_MENTORSHIP_SQUADS_KEY').freeze 5 | 6 | def self.create(record) 7 | client.create(record) 8 | end 9 | 10 | def self.least_populated 11 | client 12 | .all 13 | .group_by { |r| r[:squad] }.map { |k, v| [v.length, k] } 14 | .sort 15 | .first[1] 16 | .to_sym 17 | end 18 | 19 | def self.user_exists?(username) 20 | client.find_by(slack_username: username) 21 | end 22 | 23 | def self.client 24 | Operationcode::Airtable.new(base_id: BASE_ID, table: TABLE_NAME) 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new do |t| 4 | require "simplecov" 5 | SimpleCov.start 6 | 7 | ENV['SLACK_TOKEN'] = 'test_token' 8 | ENV['SLACK_CLIENT_ID'] = 'test_client_id' 9 | ENV['SLACK_CLIENT_SECRET'] = 'test_client_id' 10 | ENV['AIRTABLE_API_KEY'] = 'FAKEAIRTABLEKEY' 11 | ENV['AIRTABLE_MENTORSHIP_SQUADS_KEY'] = 'FAKEMENTORSHIPSQUADSKEY' 12 | ENV['INVITE_USER'] = 'true' 13 | ENV['SLACK_CHANNEL_1ST_SQUAD'] = '1STSQUADID' 14 | 15 | 16 | base_dir = File.expand_path(File.dirname(__FILE__)) 17 | lib_dir = File.join(base_dir, "lib") 18 | test_dir = File.join(base_dir, "test") 19 | 20 | t.libs << base_dir 21 | t.libs << lib_dir 22 | 23 | t.test_files = FileList['test/**/*_test.rb'] 24 | t.verbose = false 25 | end 26 | 27 | task default: :test 28 | -------------------------------------------------------------------------------- /lib/help_menu.rb: -------------------------------------------------------------------------------- 1 | require 'operationcode/slack' 2 | 3 | class HelpMenu 4 | def self.items 5 | ObjectSpace.each_object(Class).select { |klass| klass < self } 6 | end 7 | 8 | def self.generate_interactive_message 9 | Operationcode::Slack::Im::Interactive.new(text: 'Please click on a topic', id: 'help_menu', actions: ::HelpMenu.items.collect(&:help_action)) 10 | end 11 | 12 | def self.text 13 | template = File.read(template_path + class_to_filename) 14 | ERB.new(template).result(binding) 15 | end 16 | 17 | private 18 | 19 | def self.template_path 20 | Pathname.new(__dir__) + '..' + 'views' + self.name.to_s.underscore + '..' 21 | end 22 | 23 | def self.class_to_filename 24 | "#{self.name.to_s.demodulize.underscore.downcase}_message.txt.erb" 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /.github/labels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "name": "beginner friendly", "color": "#c2e0c6" }, 3 | { "name": "Status: Blocked", "color": "#e11d21" }, 4 | { "name": "Status: Duplicate", "color": "#fef2c0" }, 5 | { "name": "Status: In Progress", "color": "#cccccc" }, 6 | { "name": "Status: Available", "color": "#006b75" }, 7 | { "name": "Status: On Hold", "color": "#e11d21" }, 8 | { "name": "Type: Bug", "color": "#e11d21" }, 9 | { "name": "Type: Feature", "color": "#84b6eb" }, 10 | { "name": "Type: Question", "color": "#cc317c" }, 11 | { "name": "Priority: Low", "color": "#009800" }, 12 | { "name": "Priority: Medium", "color": "#fbca04" }, 13 | { "name": "Priority: High", "color": "#eb6420" }, 14 | { "name": "Priority: Critical", "color": "#e11d21" }, 15 | { "name": "Needs: Copy/Content", "color": "#d4c5f9" }, 16 | { "name": "Needs: More Detail", "color": "#b60205" } 17 | ] 18 | -------------------------------------------------------------------------------- /lib/event.rb: -------------------------------------------------------------------------------- 1 | # Base class for events 2 | class Event 3 | COMMUNITY_CHANNEL = 'G3MD48QTD' 4 | 5 | def initialize(data, token: nil, logger: nil) 6 | @data = data 7 | @token = token 8 | @logger = logger 9 | end 10 | 11 | # Make sure we are in dev mode unless explicitly disabled 12 | def production_mode? 13 | ENV['PRODUCTION_MODE'] == 'true' ? true : false 14 | end 15 | 16 | def template_path 17 | Pathname.new(__dir__) + '..' + 'views' + self.class.name.to_s.underscore 18 | end 19 | 20 | private 21 | 22 | # Interface for logging via Sinatra or STDOUT 23 | # 24 | # @param message [String] the message to log 25 | # @param level [Symbol] the level to log at (if logging via Sinatra) 26 | def log(message, level: :info) 27 | if @logger 28 | @logger.send(level, message) 29 | else 30 | puts message 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /lib/button_press/help_menu.rb: -------------------------------------------------------------------------------- 1 | class ButtonPress::HelpMenu < ButtonPress 2 | def process 3 | help_klass = "HelpMenu::#{@action['name'].classify}".constantize 4 | Operationcode::Slack::Im.new(user: @user['id'], text: help_klass.text).deliver 5 | Operationcode::Slack::Im.new( 6 | channel: Event::COMMUNITY_CHANNEL, 7 | text: "User #{@user['name']} has just clicked the #{@action['name']} button" 8 | ).deliver 9 | nil 10 | end 11 | 12 | private 13 | 14 | def template_path 15 | Pathname.new(__dir__) + '..' + 'views' + self.class.name.to_s.underscore + '..' 16 | end 17 | 18 | def class_to_filename 19 | "#{self.class.name.to_s.demodulize.underscore.downcase}_message.txt.erb" 20 | end 21 | 22 | def render_button_template 23 | template = File.read(template_path + class_to_filename) 24 | ERB.new(template).result(binding) 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /lib/button_press.rb: -------------------------------------------------------------------------------- 1 | class ButtonPress 2 | attr_reader :response 3 | 4 | def initialize(data) 5 | @data = data 6 | @user = data['user'] 7 | @action = data['actions'].first 8 | @response = nil 9 | end 10 | 11 | def self.help_action 12 | nil 13 | end 14 | 15 | def self.show_in_help_menu? 16 | help_action 17 | end 18 | 19 | def template_path 20 | Pathname.new(__dir__) + '..' + 'views' + self.class.name.to_s.underscore + '..' 21 | end 22 | 23 | def class_to_filename 24 | "#{self.class.name.to_s.demodulize.underscore.downcase}_message.txt.erb" 25 | end 26 | 27 | def render_button_template 28 | template = File.read(template_path + class_to_filename) 29 | ERB.new(template).result(binding) 30 | end 31 | 32 | def process 33 | Operationcode::Slack::Im.new(user: @user['id'], text: render_button_template) 34 | nil 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /test/lib/button_press/help_menu_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | 3 | class ButtonPress::HelpMenuTest < Minitest::Test 4 | def test_it_delivers_messages 5 | mock_data = { 6 | 'user' => { 'name' => 'test_user', 'id' => 'test_id' }, 7 | 'actions' => [ 'name' => 'test_action' ] 8 | } 9 | 10 | mock_help_menu_item = mock() 11 | mock_help_menu_item.stubs(:text).returns('Mock test item text') 12 | String.any_instance.stubs(:constantize).returns(mock_help_menu_item) 13 | 14 | mock_im = mock() 15 | mock_im.stubs(:deliver).returns(true) 16 | 17 | Operationcode::Slack::Im.expects(:new).with(user: 'test_id', text: 'Mock test item text').returns(mock_im) 18 | Operationcode::Slack::Im.expects(:new).with(channel: 'G3MD48QTD', text: 'User test_user has just clicked the test_action button').returns(mock_im) 19 | 20 | ButtonPress::HelpMenu.new(mock_data).process 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Publishes the most recent web container to docker hubs repo. 4 | # This script assumes docker push works. 5 | # You must set up docker push on your own. 6 | 7 | set -eu 8 | 9 | DOCKER_REPO="operationcode/operationcode_bot" 10 | 11 | ROOT_DIR="$(dirname $0)/.." 12 | cd $ROOT_DIR 13 | 14 | PROJECT_NAME=$(grep COMPOSE_PROJECT_NAME $ROOT_DIR/.env | cut -d= -f2) 15 | 16 | IMAGE_NAME="${PROJECT_NAME}_app" 17 | IMAGE_ID=$(docker images $IMAGE_NAME:latest --format "{{.ID}}") 18 | 19 | if [ -n "$DOCKER_USERNAME" ]; then echo "Found username"; fi 20 | if [ -n "$DOCKER_PASSWORD" ]; then echo "Found password"; fi 21 | 22 | if [ -n "$DOCKER_USERNAME" ] && [ -n "$DOCKER_PASSWORD" ]; then 23 | echo "Logging in using ENV creds" 24 | docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 25 | fi 26 | 27 | docker tag $IMAGE_ID $DOCKER_REPO 28 | docker tag $IMAGE_ID ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 29 | docker push $DOCKER_REPO 30 | docker push ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Operation Code 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 | -------------------------------------------------------------------------------- /views/event/team_join/welcome_message.txt.erb: -------------------------------------------------------------------------------- 1 | Hi <%= @user.try(:name) %>, 2 | 3 | Welcome to Operation Code! I'm a bot designed to help answer questions and get you on your 4 | way in our community. 5 | 6 | Please take a moment to review our Code of Conduct: 7 | https://op.co.de/code-of-conduct 8 | 9 | Our goal here at Operation Code is to get veterans and their families started on the path to 10 | a career in programming. We do that through providing you with scholarships, mentoring, career 11 | development opportunities, conference tickets, and more! 12 | 13 | You're currently in Slack, a chat application that serves as the hub of Operation Code. 14 | If you're currently visiting us via your browser, Slack provides a stand alone program 15 | to make staying in touch even more convenient. You can download it here: 16 | https://slack.com/downloads 17 | 18 | Below you'll see a list of topics. You can click on each topic to get more info. If you 19 | want to see the topics again just reply to me with any message. 20 | 21 | Want to make your first change to a program right now? Click on the 'OpCode Challenge' 22 | button to get instructions on how to make a change to this very bot! 23 | -------------------------------------------------------------------------------- /bin/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | KUBE_CONFIG=kubectl.config 6 | 7 | # This script updates our kubernetes deploymnet from travis 8 | 9 | # Install kubectl 10 | curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 11 | chmod +x ./kubectl 12 | 13 | # Generate our kubectl config 14 | cat << EOF > $KUBE_CONFIG 15 | apiVersion: v1 16 | clusters: 17 | - cluster: 18 | certificate-authority-data: $KUBE_CA_DATA 19 | server: $KUBE_SERVER 20 | name: cluster-operationcode 21 | contexts: 22 | - context: 23 | cluster: cluster-operationcode 24 | namespace: operationcode 25 | user: $KUBE_USER 26 | name: operationcode 27 | - context: 28 | cluster: cluster-operationcode 29 | namespace: operationcode-staging 30 | user: $KUBE_USER 31 | name: staging 32 | current-context: operationcode 33 | kind: Config 34 | preferences: {} 35 | users: 36 | - name: $KUBE_USER 37 | user: 38 | client-certificate-data: $KUBE_CLIENT_CERT 39 | client-key-data: $KUBE_CLIENT_KEY 40 | EOF 41 | 42 | # Update the deployment 43 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-bot app=operationcode/operationcode_bot:${TRAVIS_BUILD_NUMBER} 44 | -------------------------------------------------------------------------------- /lib/event/team_join.rb: -------------------------------------------------------------------------------- 1 | require 'operationcode/slack' 2 | 3 | # Handler for a Team Join event. When a new user joins Slack we want them to 4 | # * Explain our mentorship program and ask them if they want to join a squad 5 | # * If they respond affirmatively our message handler will: 6 | # * Be assigned to the least populated mentorship squad 7 | # * Have their squad and user info added to the Mentorship air table 8 | # * Be invited to the squad's slack private channel 9 | class Event::TeamJoin < Event 10 | attr_reader :user 11 | 12 | def initialize(data, token: nil, logger: nil) 13 | super 14 | @template = File.read(template_path + 'welcome_message.txt.erb') 15 | end 16 | 17 | def process 18 | @user = Operationcode::Slack::User.new(@data['event']['user']) 19 | log "Production mode: #{production_mode?}" 20 | log "Welcoming user #{resolve_user_name}" 21 | 22 | welcome_user! 23 | notify_community! 24 | end 25 | 26 | private 27 | 28 | def welcome_user! 29 | im = Operationcode::Slack::Im.new(user: resolve_user_name, text: ERB.new(@template).result(binding)) 30 | im.make_interactive_with!(HelpMenu.generate_interactive_message) 31 | im.deliver 32 | end 33 | 34 | def notify_community! 35 | im = Operationcode::Slack::Im.new( 36 | channel: Event::COMMUNITY_CHANNEL, 37 | text: ":tada: <@#{user.id}> has joined the Slack team :tada:" 38 | ) 39 | im.make_interactive_with!( 40 | Operationcode::Slack::Im::Interactive.new( 41 | text: 'Have they been greeted via direct message?', 42 | id: 'greeted', 43 | actions: [ 44 | {name: 'yes', text: 'Yes', value: 'yes'} 45 | ] 46 | ) 47 | ) 48 | im.deliver 49 | end 50 | 51 | def resolve_user_name 52 | production_mode? ? user.id : 'U08U56D5K' 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /test/lib/event/team_join_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | require 'event/team_join' 3 | 4 | class Event::TeamJoinTest < Minitest::Test 5 | def setup 6 | Event::TeamJoin.any_instance.stubs(:user_exists?).returns(nil) 7 | @user = mock 8 | 9 | @user.stubs(:name).returns('FAKE.USERNAME') 10 | @mock_im = mock 11 | @mock_im.stubs(:deliver) 12 | @mock_im.stubs(:make_interactive_with!) 13 | end 14 | 15 | def test_it_is_an_object 16 | assert_instance_of Event::TeamJoin, Event::TeamJoin.new(mock_team_join_event) 17 | end 18 | 19 | def test_it_sends_a_message_to_the_user_if_an_env_var_is_set 20 | ENV['PRODUCTION_MODE'] = 'true' 21 | assert_equal 'true', ENV['PRODUCTION_MODE'] 22 | Operationcode::Slack::Im.expects(:new).returns(@mock_im) 23 | Operationcode::Slack::Im.expects(:new).with(channel: 'G3MD48QTD', text: ':tada: <@FAKEUSERID> has joined the Slack team :tada:').returns(@mock_im) 24 | 25 | Event::TeamJoin.new(mock_team_join_event).process 26 | 27 | ENV['PRODUCTION_MODE'] = 'false' 28 | assert_equal 'false', ENV['PRODUCTION_MODE'] 29 | Operationcode::Slack::Im.expects(:new).returns(@mock_im) 30 | Operationcode::Slack::Im.expects(:new).with(channel: 'G3MD48QTD', text: ':tada: <@FAKEUSERID> has joined the Slack team :tada:').returns(@mock_im) 31 | 32 | Event::TeamJoin.new(mock_team_join_event).process 33 | end 34 | 35 | def test_it_notifies_staff 36 | Operationcode::Slack::Im.stubs(:new).returns(@mock_im) 37 | @mock_im.expects(:deliver) 38 | 39 | Event::TeamJoin.new(mock_team_join_event).process 40 | end 41 | 42 | def mock_team_join_event 43 | { 44 | 'token' => 'FAKETOKEN', 45 | 'type' => 'team_join', 46 | 'event' => { 47 | 'user' => { 48 | 'id' =>'FAKEUSERID', 49 | 'name' =>'FAKE.USERNAME', 50 | 'real_name' => 'FAKE NAME', 51 | 'profile' => { 52 | 'first_name' => 'FAKEFIRSTNAME', 53 | 'last_name' => 'FAKELASTNAME' 54 | } 55 | } 56 | } 57 | } 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /lib/event/message.rb: -------------------------------------------------------------------------------- 1 | require 'operationcode/slack' 2 | 3 | # Handler for a Team Join event. When a new user joins Slack we want them to 4 | # * Be assigned to the least populated mentorship squad 5 | # * Have their squad and user info added to the Mentorship air table 6 | # * Be invited to the squad's slack private channel 7 | class Event::Message < Event 8 | attr_reader :user 9 | 10 | ACTIONABLE_KEYWORD = 'yes' 11 | 12 | def initialize(data, token: nil, logger: nil) 13 | @message = data['event']['text'] 14 | @user = Operationcode::Slack::User.new(data['event']['user']) 15 | @channel = data['event']['channel'] 16 | 17 | super 18 | end 19 | 20 | def process 21 | im = Operationcode::Slack::Im.new(user: user.id, channel: @channel, text: "I'm sorry. I don't know how to talk to humans yet. Here's what I do know.") 22 | im.make_interactive_with!(HelpMenu.generate_interactive_message) 23 | im.deliver 24 | end 25 | 26 | private 27 | 28 | # The rest of this has to do with actionable items 29 | # and will be moved over soon 30 | def user_wants_to_join? 31 | @data['event']['text'].downcase != 'no' 32 | end 33 | 34 | def add_user 35 | if user_exists? 36 | log "user #{user.name} already exists" 37 | else 38 | invite_user_to least_populated_squad 39 | save_user_to_airtables! 40 | end 41 | end 42 | 43 | def user_exists? 44 | Airtables::MentorshipSquads.user_exists?(user.name) 45 | end 46 | 47 | def invite_user_to(squad) 48 | channel_id = Squad.channel_id_for squad 49 | 50 | log "Inviting #{user.id} (#{user.name}) to channel #{channel_id} (#{squad})" 51 | if ENV['INVITE_USER'] == 'true' 52 | Operationcode::Slack::Api::ChannelsInvite.post( 53 | with_data: { 54 | token: @token, 55 | channel: channel_id, 56 | user: user.id 57 | } 58 | ) 59 | end 60 | end 61 | 62 | def save_user_to_airtables! 63 | log "Adding slack username #{user.name} to squad #{@squad} to airtables" 64 | Airtables::MentorshipSquads.create({slack_username: user.name, squad: @squad}) 65 | end 66 | 67 | def least_populated_squad 68 | @squad ||= Airtables::MentorshipSquads.least_populated 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GIT 2 | remote: https://github.com/OperationCode/operationcode-airtable 3 | revision: d9a13577b90ac3af599b08d058979750b4025a46 4 | specs: 5 | operationcode-airtable (0.2.1) 6 | activesupport 7 | airtable 8 | 9 | GIT 10 | remote: https://github.com/OperationCode/operationcode-slack 11 | revision: aad8cffb05d929b8dd3f9493061266ddd0e1a259 12 | specs: 13 | operationcode-slack (0.6.0) 14 | activesupport 15 | httparty 16 | 17 | GEM 18 | remote: https://rubygems.org/ 19 | specs: 20 | activesupport (5.1.4) 21 | concurrent-ruby (~> 1.0, >= 1.0.2) 22 | i18n (~> 0.7) 23 | minitest (~> 5.1) 24 | tzinfo (~> 1.1) 25 | airtable (0.0.9) 26 | httparty 27 | ansi (1.5.0) 28 | builder (3.2.3) 29 | codeclimate-test-reporter (1.0.8) 30 | simplecov (<= 0.13) 31 | coderay (1.1.2) 32 | concurrent-ruby (1.0.5) 33 | docile (1.1.5) 34 | httparty (0.15.6) 35 | multi_xml (>= 0.5.2) 36 | i18n (0.8.6) 37 | json (2.1.0) 38 | metaclass (0.0.4) 39 | method_source (0.9.0) 40 | minitest (5.10.3) 41 | minitest-reporters (1.1.18) 42 | ansi 43 | builder 44 | minitest (>= 5.0) 45 | ruby-progressbar 46 | mocha (1.3.0) 47 | metaclass (~> 0.0.1) 48 | multi_xml (0.6.0) 49 | mustermann (1.0.1) 50 | pry (0.11.1) 51 | coderay (~> 1.1.0) 52 | method_source (~> 0.9.0) 53 | rack (2.0.3) 54 | rack-protection (2.0.0) 55 | rack 56 | rack-test (0.7.0) 57 | rack (>= 1.0, < 3) 58 | rake (12.1.0) 59 | rb-readline (0.5.5) 60 | ruby-progressbar (1.9.0) 61 | simplecov (0.13.0) 62 | docile (~> 1.1.0) 63 | json (>= 1.8, < 3) 64 | simplecov-html (~> 0.10.0) 65 | simplecov-html (0.10.2) 66 | sinatra (2.0.0) 67 | mustermann (~> 1.0) 68 | rack (~> 2.0) 69 | rack-protection (= 2.0.0) 70 | tilt (~> 2.0) 71 | thread_safe (0.3.6) 72 | tilt (2.0.8) 73 | tzinfo (1.2.3) 74 | thread_safe (~> 0.1) 75 | 76 | PLATFORMS 77 | ruby 78 | 79 | DEPENDENCIES 80 | activesupport 81 | airtable 82 | codeclimate-test-reporter (~> 1.0.0) 83 | httparty 84 | minitest 85 | minitest-reporters 86 | mocha 87 | operationcode-airtable! 88 | operationcode-slack! 89 | pry 90 | rack-test 91 | rake 92 | rb-readline 93 | simplecov 94 | sinatra 95 | 96 | RUBY VERSION 97 | ruby 2.3.5p376 98 | 99 | BUNDLED WITH 100 | 1.15.4 101 | -------------------------------------------------------------------------------- /test/lib/event/message_test.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../test_helper' 2 | 3 | require 'event/message' 4 | 5 | class Event::MessageTest < Minitest::Test 6 | def setup 7 | Event::MessageTest.any_instance.stubs(:user_exists?).returns(nil) 8 | Operationcode::Slack::Api::UsersInfo.stubs(:post).returns({ 'ok' => 'true', 'user' => { 'real_name' => 'RETURNED_NAME' } }) 9 | end 10 | 11 | def test_it_is_an_object 12 | assert_instance_of Event::Message, Event::Message.new(mock_message_event) 13 | end 14 | 15 | def test_presents_the_help_menu_if_given_an_unknown_message 16 | Operationcode::Slack::Im.any_instance.expects(:deliver) 17 | Event::Message.new(mock_message_event).process 18 | 19 | Operationcode::Slack::Im.any_instance.expects(:deliver) 20 | Event::Message.new(mock_message_event.merge({ 'event' => { 'text' => 'r', 'user' => 'FAKEUSERID' } })).process 21 | end 22 | 23 | def test_it_invites_a_user_to_a_channel_if_an_env_var_is_set 24 | skip 25 | 26 | Airtables::MentorshipSquads.stubs(:least_populated).returns('1st') 27 | assert_equal ENV['INVITE_USER'], 'true' 28 | 29 | HTTParty.expects(:post) 30 | .with('https://slack.com/api/channels.invite', body: { token: nil, user: 'FAKEUSERID', channel: '1STSQUADID' }) 31 | .returns({ok: true}.to_json) 32 | 33 | event = Event::Message.new(mock_message_event(with_text: 'yes')) 34 | event.process 35 | 36 | ENV['INVITE_USER'] = 'false' 37 | refute_equal ENV['INVITE_USER'], 'true' 38 | 39 | event = Event::Message.new(mock_message_event(with_text: 'yes')) 40 | event.process 41 | end 42 | 43 | def test_it_creates_an_airtable_record 44 | skip 45 | 46 | Airtables::MentorshipSquads.expects(:least_populated).returns('1st') 47 | Airtables::MentorshipSquads.expects(:create).with(slack_username: 'FAKE.USERNAME', squad: '1st') 48 | Operationcode::Slack::Api::UsersInfo.expects(:post) 49 | .with(with_data: { user: 'FAKEUSERID' }).once 50 | .returns({ 'ok' => 'true', 'user' => { 'real_name' => 'FAKE.USERNAME' } }) 51 | 52 | event = Event::Message.new(mock_message_event(with_text: 'yes')) 53 | event.process 54 | end 55 | 56 | def test_it_doesnt_create_a_record_if_the_slack_username_exists 57 | Event::Message.any_instance.stubs(:user_exists?).returns(::Airtable::Record.new(slack_username: 'FAKE.USERNAME')) 58 | Airtables::MentorshipSquads.expects(:create).never 59 | 60 | event = Event::Message.new(mock_message_event) 61 | event.process 62 | end 63 | 64 | def mock_message_event(with_text: 'MESSAGE TEXT') 65 | { 66 | 'token'=>'FAKETOKEN', 67 | 'team_id'=>'TEAMID', 68 | 'event' => { 69 | 'type' => 'message', 70 | 'user' => 'FAKEUSERID', 71 | 'text' => with_text, 72 | 'channel' => 'D3HDX3R2T' 73 | } 74 | } 75 | end 76 | 77 | def negative_mock_message_event 78 | mock_message_event.merge({ 'event' => { 'text' => 'No', 'user' => 'FAKEUSERID' } }) 79 | end 80 | end 81 | -------------------------------------------------------------------------------- /operationcode_bot.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | require 'httparty' 3 | require 'json' 4 | require 'operationcode/airtable' 5 | require 'operationcode/slack' 6 | 7 | Dir[File.join(File.dirname(__FILE__), 'lib', '**/*.rb')].sort.each { |file| require file } 8 | 9 | set :slack_token, ENV.fetch('SLACK_TOKEN') 10 | set :lock, true 11 | 12 | before do 13 | content_type 'application/json' 14 | end 15 | 16 | get '/ping' do 17 | { success: :ok, data: :pong }.to_json 18 | end 19 | 20 | post '/event' do 21 | event_data = read_post_body 22 | logger.info "Received Event with data #{event_data}" 23 | 24 | event_type = event_data.has_key?('event') ? event_data['event']['type'] : event_data['type'] 25 | 26 | dispatch_event(type: event_type, with_data: event_data, token: event_data['token']) 27 | end 28 | 29 | post '/slack/button_press' do 30 | button_data = JSON.parse params['payload'] 31 | logger.info "Received Button Press with data #{button_data}" 32 | 33 | callback = button_data['callback_id'] 34 | button_press_klass = "ButtonPress::#{callback.classify}".constantize 35 | 36 | button_press = button_press_klass.new(button_data) 37 | button_press.process 38 | button_press.response ? button_press.response : halt(200) 39 | end 40 | 41 | get '/oauth/redirect' do 42 | logger.info "OAUTH with code #{params['code']}" 43 | response = Operationcode::Slack::Api::OauthAccess.post(with_data: { code: params['code'] }) 44 | logger.info "RX #{response}" 45 | empty_response 46 | end 47 | 48 | private 49 | 50 | def event_callback(data, token: nil) 51 | event_data = data['event'] 52 | event_type = event_data['type'].to_sym 53 | logger.info "Received Event #{event_type}" 54 | dispatch_event(type: event_type, with_data: event_data, token: data['token']) 55 | end 56 | 57 | def url_verification(data, token: nil) 58 | { challenge: data['challenge'] }.to_json 59 | end 60 | 61 | def team_join(data, token: nil) 62 | logger.info "New member joined: #{data}" 63 | 64 | Event::TeamJoin.new(data, token: token, logger: logger).process 65 | 66 | empty_response 67 | end 68 | 69 | def message(data, token: nil) 70 | logger.info "New message recieved: #{data}" 71 | 72 | if bot_message?(data) 73 | logger.info 'Skipping bot message' 74 | else 75 | Event::Message.new(data, token: token, logger: logger).process 76 | end 77 | 78 | empty_response 79 | end 80 | 81 | def bot_message?(data) 82 | data['event']['subtype'] == 'bot_message' || (data['event']['message'] && data['event']['message']['subtype'] == 'bot_message') 83 | end 84 | 85 | def empty_response 86 | {}.to_json 87 | end 88 | 89 | def read_post_body 90 | request.body.rewind 91 | JSON.parse request.body.read 92 | end 93 | 94 | # Dynamically calls a method of the same name as the +event_type+ 95 | # If anything fails we return an empty response 96 | def dispatch_event(type: event_type, with_data: data, token: nil) 97 | # This wierd Object include check has to do with Ruby's odd main/top level behaviours 98 | # We are basically just checking that the method was defined in this file 99 | if(type && Object.private_instance_methods.include?(type.to_sym)) 100 | send(type.to_sym, with_data, token: token) 101 | else 102 | logger.info "Did not find a handler for event type '#{type}'" 103 | empty_response 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [OperationCodeBot](https://github.com/OperationCode/operationcode_bot) 2 | 3 | [![Build Status](https://travis-ci.org/OperationCode/operationcode_bot.svg?branch=master)](https://travis-ci.org/OperationCode/operationcode_bot) 4 | [![Code Climate](https://codeclimate.com/github/OperationCode/operationcode_bot/badges/gpa.svg)](https://codeclimate.com/github/OperationCode/operationcode_bot) 5 | [![Test Coverage](https://codeclimate.com/github/OperationCode/operationcode_bot/badges/coverage.svg)](https://codeclimate.com/github/OperationCode/operationcode_bot/coverage) 6 | 7 | OperationCode Bot is a bot built to deal with the [Slack Events API](https://api.slack.com/events). 8 | When a subscribed event occurs: 9 | * Slack sends a POST to this bot 10 | * The bot processes the event and calls a method with the same name as the event passed in 11 | * The event named method does whatever tasks are needed 12 | 13 | It also handles interactive message button presses. When a button click clicked: 14 | * Slack send us a POST with the buttom params 15 | * The bot calls the `process` method on an instance of the callback_id of the button 16 | * If `@response` is set we reply with the response and slack updates the message. 17 | This can be just text or a an entire message payload. 18 | 19 | ## Installation 20 | 21 | * Clone this repo 22 | * Run 23 | 24 | $ bundle 25 | * Run the tests with 26 | 27 | $ rake 28 | 29 | ## Usage 30 | 31 | ### Adding New Button Responses 32 | 33 | Button Presses are called by as a class by way of the `callback_id` 34 | passed in from the slack API. To handle a new action define a new class 35 | in `lib/button_press`. The `#process` method must be defined. See the existing 36 | classes for more help. 37 | 38 | ### Adding new help menu items 39 | 40 | To add a new item to the help menu simply create a new class in `lib/help_menu`. 41 | See existing help menu classes for more help. 42 | Then you'll just need to add the messages template with $CLASS_NAME_message.txt.erb in `views/help_menu/`. 43 | 44 | ### Adding A New Event 45 | 46 | To create a new event, within the [operationcode_bot.rb](https://github.com/OperationCode/operationcode_bot/blob/master/operationcode_bot.rb) file, simply define a new `private` method with the same name as the method you want to handle. 47 | 48 | For example, if you wanted to write some code any time someone [pins an item](https://api.slack.com/events/pin_added) (the ```pin_added``` event) you would define 49 | a new method like so: 50 | 51 | ```ruby 52 | private 53 | 54 | def pin_added(data, token: nil) 55 | logger.add "Got pin_added event with data: #{data}" 56 | empty_response 57 | end 58 | ``` 59 | 60 | You can now write your business logic inside of this method. 61 | If your logic is complex (more than a 5-10 lines) I'd recommend using a ruby object. 62 | There is an Event class that can be inherited from to make your life a bit easier. 63 | See ```Event::TeamJoin``` as a reference. 64 | 65 | 66 | ## Contributing 67 | 68 | Bug reports and pull requests are welcome on GitHub at https://github.com/OperationCode/operationcode_bot. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 69 | 70 | ## License 71 | 72 | The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). 73 | 74 | 75 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Operation Code: Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at . All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /test/operationcode_bot_test.rb: -------------------------------------------------------------------------------- 1 | require_relative './test_helper' 2 | 3 | class OperationcodeBotTest < Minitest::Test 4 | include Rack::Test::Methods 5 | 6 | SLACK_OAUTH_ACCESS_PATH = 'https://slack.com/api/oauth.access' 7 | SLACK_USERS_INFO_PATH = 'https://slack.com/api/users.info' 8 | 9 | def app 10 | Sinatra::Application 11 | end 12 | 13 | def test_it_can_verify_its_url 14 | post '/event', { type: 'url_verification', challenge: 'yeEDKbeqUN9bAGUeP7sRDqTvF1CAlLdJjyNl20QAStFGXPDCNak6' }.to_json 15 | assert last_response.ok? 16 | assert_equal '{"challenge":"yeEDKbeqUN9bAGUeP7sRDqTvF1CAlLdJjyNl20QAStFGXPDCNak6"}', last_response.body 17 | end 18 | 19 | def test_it_responds_to_ping 20 | get '/ping' 21 | assert last_response.ok? 22 | assert_equal ({ success: :ok, data: :pong }.to_json), last_response.body 23 | end 24 | 25 | def test_it_can_oauth 26 | client_id = ENV['SLACK_CLIENT_ID'] 27 | client_secret = ENV['SLACK_CLIENT_SECRET'] 28 | oauth_code = '3570763187.114951093348.e285c6000c' 29 | 30 | assert client_id 31 | assert client_secret 32 | 33 | HTTParty.expects(:post).with(SLACK_OAUTH_ACCESS_PATH, body: { client_id: client_id, client_secret: client_secret, code: oauth_code }) 34 | 35 | get '/oauth/redirect', { code: oauth_code, state: nil } 36 | assert last_response.ok? 37 | end 38 | 39 | def test_it_handles_unknown_payloads 40 | post '/event', { type: 'this_is_a_type_that_will_never_be_defined' }.to_json 41 | assert last_response.ok? 42 | assert_equal '{}', last_response.body 43 | 44 | post '/event', {}.to_json 45 | assert last_response.ok? 46 | assert_equal '{}', last_response.body 47 | end 48 | 49 | def test_it_only_runs_methods_defined 50 | Sinatra::Application.any_instance.expects(:self).never 51 | post '/event', { type: 'self' }.to_json 52 | assert_equal '{}', last_response.body 53 | 54 | Sinatra::Application.any_instance.expects(:inspect).never 55 | post '/event', { type: 'inspect' }.to_json 56 | assert_equal '{}', last_response.body 57 | end 58 | 59 | def test_it_welcomes_the_user_on_new_user_join 60 | Operationcode::Airtable.any_instance.stubs(:find_by).returns(nil) 61 | @user = mock 62 | @user.stubs(:name).returns('FAKEUSERNAME') 63 | ENV['PRODUCTION_MODE'] = 'true' 64 | 65 | mock_im = mock 66 | mock_im.expects(:deliver) 67 | mock_im.expects(:make_interactive_with!) 68 | 69 | mock_notification_im = mock 70 | mock_notification_im.expects(:deliver) 71 | mock_notification_im.expects(:make_interactive_with!) 72 | 73 | Operationcode::Slack::User.any_instance.stubs(:name).returns('FAKEUSERNAME') 74 | Operationcode::Slack::Im.expects(:new).returns(mock_im) 75 | Operationcode::Slack::Im.expects(:new).with(channel: 'G3MD48QTD', text: ':tada: <@FAKEUSERID> has joined the Slack team :tada:').returns(mock_notification_im) 76 | 77 | team_join_data = { 78 | token: 'FAKE_TOKEN', 79 | team_id: 'TXXXXXXXX', 80 | api_app_id: 'AXXXXXXXXX', 81 | event: { 82 | type: 'team_join', 83 | event_ts: '1234567890.123456', 84 | user: { 85 | id: "FAKEUSERID", 86 | name: "FAKEUSERNAME" 87 | }, 88 | }, 89 | type: 'event_callback', 90 | authed_users: [ 91 | 'UXXXXXXX1', 92 | 'UXXXXXXX2' 93 | ] 94 | } 95 | 96 | post '/event', team_join_data.to_json 97 | assert last_response.ok? 98 | end 99 | 100 | def test_handles_button_presses 101 | button_press_data = { 102 | 'actions' => [{ 'name' => 'yes', 'value' => 'yes' }], 103 | 'callback_id' => 'greeted', 104 | 'channel' => { 'id'=>'CHANNEL_ID', 'name'=>'privategroup' }, 105 | 'user' => { 'id'=>'TEST_USER_ID', 'name'=>'TEST_USER_NAME' }, 106 | 'action_ts' => '1000000000.000000', 107 | 'message_ts' => '1999999999.999999', 108 | 'attachment_id' => '1', 109 | 'token' => 'FAKE_TOKEN', 110 | 'response_url' => 'https://hooks.slack.com/actions/T03GSNF5H/133143284945/4VEOGJbL8tKPLRvgAa9CFG9i', 111 | 'original_message' => { 112 | 'text' => ':tada: TEST_USER has joined the slack team :tada:', 113 | 'username' => 'operationcodebot', 114 | 'bot_id' => 'TEST_BOT_ID', 115 | 'attachments' => [ 116 | { 117 | 'callback_id' => 'greeted', 118 | 'fallback' => 'Have they been greeted?', 119 | 'text' => 'Have they been greeted?', 120 | 'id' => 1, 121 | 'color' => '3AA3E3', 122 | 'actions' => [{ 'id' => '1', 'name' => 'yes', 'text' => 'Yes', 'type' => 'button', 'value' => 'yes', 'style' => 'default' }] 123 | } 124 | ], 125 | 'type' => 'message', 126 | 'subtype' => 'bot_message', 127 | 'ts' => '1485611482.000040' 128 | } 129 | } 130 | 131 | button_press_response = "{\"text\":\":tada: TEST_USER has joined the slack team :tada:\",\"username\":\"operationcodebot\",\"bot_id\":\"TEST_BOT_ID\",\"attachments\":[{\"callback_id\":\"greeted\",\"fallback\":\"Have they been greeted?\",\"text\":\"\\u003c@TEST_USER_ID\\u003e has greeted the new user\",\"id\":1,\"color\":\"3AA3E3\",\"actions\":[]}],\"type\":\"message\",\"subtype\":\"bot_message\",\"ts\":\"1485611482.000040\"}" 132 | 133 | post '/slack/button_press', { 'payload' => button_press_data.to_json } 134 | assert_equal button_press_response, last_response.body 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | AllCops: 2 | DisabledByDefault: true 3 | 4 | #################### Lint ################################ 5 | 6 | Lint/AmbiguousOperator: 7 | Description: >- 8 | Checks for ambiguous operators in the first argument of a 9 | method invocation without parentheses. 10 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' 11 | Enabled: true 12 | 13 | Lint/AmbiguousRegexpLiteral: 14 | Description: >- 15 | Checks for ambiguous regexp literals in the first argument of 16 | a method invocation without parenthesis. 17 | Enabled: true 18 | 19 | Lint/AssignmentInCondition: 20 | Description: "Don't use assignment in conditions." 21 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' 22 | Enabled: true 23 | 24 | Lint/BlockAlignment: 25 | Description: 'Align block ends correctly.' 26 | Enabled: true 27 | 28 | Lint/CircularArgumentReference: 29 | Description: "Don't refer to the keyword argument in the default value." 30 | Enabled: true 31 | 32 | Lint/ConditionPosition: 33 | Description: >- 34 | Checks for condition placed in a confusing position relative to 35 | the keyword. 36 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' 37 | Enabled: true 38 | 39 | Lint/Debugger: 40 | Description: 'Check for debugger calls.' 41 | Enabled: true 42 | 43 | Lint/DefEndAlignment: 44 | Description: 'Align ends corresponding to defs correctly.' 45 | Enabled: true 46 | 47 | Lint/DeprecatedClassMethods: 48 | Description: 'Check for deprecated class method calls.' 49 | Enabled: true 50 | 51 | Lint/DuplicateMethods: 52 | Description: 'Check for duplicate methods calls.' 53 | Enabled: true 54 | 55 | Lint/EachWithObjectArgument: 56 | Description: 'Check for immutable argument given to each_with_object.' 57 | Enabled: true 58 | 59 | Lint/ElseLayout: 60 | Description: 'Check for odd code arrangement in an else block.' 61 | Enabled: true 62 | 63 | Lint/EmptyEnsure: 64 | Description: 'Checks for empty ensure block.' 65 | Enabled: true 66 | 67 | Lint/EmptyInterpolation: 68 | Description: 'Checks for empty string interpolation.' 69 | Enabled: true 70 | 71 | Lint/EndAlignment: 72 | Description: 'Align ends correctly.' 73 | Enabled: true 74 | 75 | Lint/EndInMethod: 76 | Description: 'END blocks should not be placed inside method definitions.' 77 | Enabled: true 78 | 79 | Lint/EnsureReturn: 80 | Description: 'Do not use return in an ensure block.' 81 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' 82 | Enabled: true 83 | 84 | Lint/Eval: 85 | Description: 'The use of eval represents a serious security risk.' 86 | Enabled: true 87 | 88 | Lint/FormatParameterMismatch: 89 | Description: 'The number of parameters to format/sprint must match the fields.' 90 | Enabled: true 91 | 92 | Lint/HandleExceptions: 93 | Description: "Don't suppress exception." 94 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' 95 | Enabled: true 96 | 97 | Lint/InvalidCharacterLiteral: 98 | Description: >- 99 | Checks for invalid character literals with a non-escaped 100 | whitespace character. 101 | Enabled: true 102 | 103 | Lint/LiteralInCondition: 104 | Description: 'Checks of literals used in conditions.' 105 | Enabled: true 106 | 107 | Lint/LiteralInInterpolation: 108 | Description: 'Checks for literals used in interpolation.' 109 | Enabled: true 110 | 111 | Lint/Loop: 112 | Description: >- 113 | Use Kernel#loop with break rather than begin/end/until or 114 | begin/end/while for post-loop tests. 115 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' 116 | Enabled: true 117 | 118 | Lint/NestedMethodDefinition: 119 | Description: 'Do not use nested method definitions.' 120 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' 121 | Enabled: true 122 | 123 | Lint/NonLocalExitFromIterator: 124 | Description: 'Do not use return in iterator to cause non-local exit.' 125 | Enabled: true 126 | 127 | Lint/ParenthesesAsGroupedExpression: 128 | Description: >- 129 | Checks for method calls with a space before the opening 130 | parenthesis. 131 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 132 | Enabled: true 133 | 134 | Lint/RequireParentheses: 135 | Description: >- 136 | Use parentheses in the method call to avoid confusion 137 | about precedence. 138 | Enabled: true 139 | 140 | Lint/RescueException: 141 | Description: 'Avoid rescuing the Exception class.' 142 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' 143 | Enabled: true 144 | 145 | Lint/ShadowingOuterLocalVariable: 146 | Description: >- 147 | Do not use the same name as outer local variable 148 | for block arguments or block local variables. 149 | Enabled: true 150 | 151 | Lint/StringConversionInInterpolation: 152 | Description: 'Checks for Object#to_s usage in string interpolation.' 153 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' 154 | Enabled: true 155 | 156 | Lint/UnderscorePrefixedVariableName: 157 | Description: 'Do not use prefix `_` for a variable that is used.' 158 | Enabled: true 159 | 160 | Lint/UnneededDisable: 161 | Description: >- 162 | Checks for rubocop:disable comments that can be removed. 163 | Note: this cop is not disabled when disabling all cops. 164 | It must be explicitly disabled. 165 | Enabled: true 166 | 167 | Lint/UnusedBlockArgument: 168 | Description: 'Checks for unused block arguments.' 169 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 170 | Enabled: true 171 | 172 | Lint/UnusedMethodArgument: 173 | Description: 'Checks for unused method arguments.' 174 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 175 | Enabled: true 176 | 177 | Lint/UnreachableCode: 178 | Description: 'Unreachable code.' 179 | Enabled: true 180 | 181 | Lint/UselessAccessModifier: 182 | Description: 'Checks for useless access modifiers.' 183 | Enabled: true 184 | 185 | Lint/UselessAssignment: 186 | Description: 'Checks for useless assignment to a local variable.' 187 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' 188 | Enabled: true 189 | 190 | Lint/UselessComparison: 191 | Description: 'Checks for comparison of something with itself.' 192 | Enabled: true 193 | 194 | Lint/UselessElseWithoutRescue: 195 | Description: 'Checks for useless `else` in `begin..end` without `rescue`.' 196 | Enabled: true 197 | 198 | Lint/UselessSetterCall: 199 | Description: 'Checks for useless setter call to a local variable.' 200 | Enabled: true 201 | 202 | Lint/Void: 203 | Description: 'Possible use of operator/literal/variable in void context.' 204 | Enabled: true 205 | 206 | ###################### Metrics #################################### 207 | 208 | Metrics/AbcSize: 209 | Description: >- 210 | A calculated magnitude based on number of assignments, 211 | branches, and conditions. 212 | Reference: 'http://c2.com/cgi/wiki?AbcMetric' 213 | Enabled: false 214 | Max: 20 215 | 216 | Metrics/BlockNesting: 217 | Description: 'Avoid excessive block nesting' 218 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' 219 | Enabled: true 220 | Max: 4 221 | 222 | Metrics/ClassLength: 223 | Description: 'Avoid classes longer than 250 lines of code.' 224 | Enabled: true 225 | Max: 250 226 | 227 | Metrics/CyclomaticComplexity: 228 | Description: >- 229 | A complexity metric that is strongly correlated to the number 230 | of test cases needed to validate a method. 231 | Enabled: true 232 | 233 | Metrics/LineLength: 234 | Description: 'Limit lines to 80 characters.' 235 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' 236 | Enabled: false 237 | 238 | Metrics/MethodLength: 239 | Description: 'Avoid methods longer than 30 lines of code.' 240 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' 241 | Enabled: true 242 | Max: 30 243 | 244 | Metrics/ModuleLength: 245 | Description: 'Avoid modules longer than 250 lines of code.' 246 | Enabled: true 247 | Max: 250 248 | 249 | Metrics/ParameterLists: 250 | Description: 'Avoid parameter lists longer than three or four parameters.' 251 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' 252 | Enabled: true 253 | 254 | Metrics/PerceivedComplexity: 255 | Description: >- 256 | A complexity metric geared towards measuring complexity for a 257 | human reader. 258 | Enabled: false 259 | 260 | ##################### Performance ############################# 261 | 262 | Performance/Count: 263 | Description: >- 264 | Use `count` instead of `select...size`, `reject...size`, 265 | `select...count`, `reject...count`, `select...length`, 266 | and `reject...length`. 267 | Enabled: true 268 | 269 | Performance/Detect: 270 | Description: >- 271 | Use `detect` instead of `select.first`, `find_all.first`, 272 | `select.last`, and `find_all.last`. 273 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' 274 | Enabled: true 275 | 276 | Performance/FlatMap: 277 | Description: >- 278 | Use `Enumerable#flat_map` 279 | instead of `Enumerable#map...Array#flatten(1)` 280 | or `Enumberable#collect..Array#flatten(1)` 281 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' 282 | Enabled: true 283 | EnabledForFlattenWithoutParams: false 284 | # If enabled, this cop will warn about usages of 285 | # `flatten` being called without any parameters. 286 | # This can be dangerous since `flat_map` will only flatten 1 level, and 287 | # `flatten` without any parameters can flatten multiple levels. 288 | 289 | Performance/ReverseEach: 290 | Description: 'Use `reverse_each` instead of `reverse.each`.' 291 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' 292 | Enabled: true 293 | 294 | Performance/Sample: 295 | Description: >- 296 | Use `sample` instead of `shuffle.first`, 297 | `shuffle.last`, and `shuffle[Fixnum]`. 298 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' 299 | Enabled: true 300 | 301 | Performance/Size: 302 | Description: >- 303 | Use `size` instead of `count` for counting 304 | the number of elements in `Array` and `Hash`. 305 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' 306 | Enabled: true 307 | 308 | Performance/StringReplacement: 309 | Description: >- 310 | Use `tr` instead of `gsub` when you are replacing the same 311 | number of characters. Use `delete` instead of `gsub` when 312 | you are deleting characters. 313 | Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' 314 | Enabled: true 315 | 316 | ##################### Rails ################################## 317 | 318 | Rails/ActionFilter: 319 | Description: 'Enforces consistent use of action filter methods.' 320 | Enabled: false 321 | 322 | Rails/Date: 323 | Description: >- 324 | Checks the correct usage of date aware methods, 325 | such as Date.today, Date.current etc. 326 | Enabled: false 327 | 328 | Rails/Delegate: 329 | Description: 'Prefer delegate method for delegations.' 330 | Enabled: false 331 | 332 | Rails/FindBy: 333 | Description: 'Prefer find_by over where.first.' 334 | Enabled: false 335 | 336 | Rails/FindEach: 337 | Description: 'Prefer all.find_each over all.find.' 338 | Enabled: false 339 | 340 | Rails/HasAndBelongsToMany: 341 | Description: 'Prefer has_many :through to has_and_belongs_to_many.' 342 | Enabled: false 343 | 344 | Rails/Output: 345 | Description: 'Checks for calls to puts, print, etc.' 346 | Enabled: false 347 | 348 | Rails/ReadWriteAttribute: 349 | Description: >- 350 | Checks for read_attribute(:attr) and 351 | write_attribute(:attr, val). 352 | Enabled: false 353 | 354 | Rails/ScopeArgs: 355 | Description: 'Checks the arguments of ActiveRecord scopes.' 356 | Enabled: false 357 | 358 | Rails/TimeZone: 359 | Description: 'Checks the correct usage of time zone aware methods.' 360 | StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' 361 | Reference: 'http://danilenko.org/2012/7/6/rails_timezones' 362 | Enabled: false 363 | 364 | Rails/Validation: 365 | Description: 'Use validates :attribute, hash of validations.' 366 | Enabled: false 367 | 368 | ################## Style ################################# 369 | 370 | Style/AccessModifierIndentation: 371 | Description: Check indentation of private/protected visibility modifiers. 372 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' 373 | Enabled: false 374 | 375 | Style/AccessorMethodName: 376 | Description: Check the naming of accessor methods for get_/set_. 377 | Enabled: false 378 | 379 | Style/Alias: 380 | Description: 'Use alias_method instead of alias.' 381 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' 382 | Enabled: false 383 | 384 | Style/AlignArray: 385 | Description: >- 386 | Align the elements of an array literal if they span more than 387 | one line. 388 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' 389 | Enabled: false 390 | 391 | Style/AlignHash: 392 | Description: >- 393 | Align the elements of a hash literal if they span more than 394 | one line. 395 | Enabled: false 396 | 397 | Style/AlignParameters: 398 | Description: >- 399 | Align the parameters of a method call if they span more 400 | than one line. 401 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' 402 | Enabled: false 403 | 404 | Style/AndOr: 405 | Description: 'Use &&/|| instead of and/or.' 406 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' 407 | Enabled: false 408 | 409 | Style/ArrayJoin: 410 | Description: 'Use Array#join instead of Array#*.' 411 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' 412 | Enabled: false 413 | 414 | Style/AsciiComments: 415 | Description: 'Use only ascii symbols in comments.' 416 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' 417 | Enabled: false 418 | 419 | Style/AsciiIdentifiers: 420 | Description: 'Use only ascii symbols in identifiers.' 421 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' 422 | Enabled: false 423 | 424 | Style/Attr: 425 | Description: 'Checks for uses of Module#attr.' 426 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' 427 | Enabled: false 428 | 429 | Style/BeginBlock: 430 | Description: 'Avoid the use of BEGIN blocks.' 431 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' 432 | Enabled: false 433 | 434 | Style/BarePercentLiterals: 435 | Description: 'Checks if usage of %() or %Q() matches configuration.' 436 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' 437 | Enabled: false 438 | 439 | Style/BlockComments: 440 | Description: 'Do not use block comments.' 441 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' 442 | Enabled: false 443 | 444 | Style/BlockEndNewline: 445 | Description: 'Put end statement of multiline block on its own line.' 446 | Enabled: false 447 | 448 | Style/BlockDelimiters: 449 | Description: >- 450 | Avoid using {...} for multi-line blocks (multiline chaining is 451 | always ugly). 452 | Prefer {...} over do...end for single-line blocks. 453 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 454 | Enabled: false 455 | 456 | Style/BracesAroundHashParameters: 457 | Description: 'Enforce braces style around hash parameters.' 458 | Enabled: false 459 | 460 | Style/CaseEquality: 461 | Description: 'Avoid explicit use of the case equality operator(===).' 462 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' 463 | Enabled: false 464 | 465 | Style/CaseIndentation: 466 | Description: 'Indentation of when in a case/when/[else/]end.' 467 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' 468 | Enabled: false 469 | 470 | Style/CharacterLiteral: 471 | Description: 'Checks for uses of character literals.' 472 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' 473 | Enabled: false 474 | 475 | Style/ClassAndModuleCamelCase: 476 | Description: 'Use CamelCase for classes and modules.' 477 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' 478 | Enabled: false 479 | 480 | Style/ClassAndModuleChildren: 481 | Description: 'Checks style of children classes and modules.' 482 | Enabled: false 483 | 484 | Style/ClassCheck: 485 | Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' 486 | Enabled: false 487 | 488 | Style/ClassMethods: 489 | Description: 'Use self when defining module/class methods.' 490 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods' 491 | Enabled: false 492 | 493 | Style/ClassVars: 494 | Description: 'Avoid the use of class variables.' 495 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' 496 | Enabled: false 497 | 498 | Style/ClosingParenthesisIndentation: 499 | Description: 'Checks the indentation of hanging closing parentheses.' 500 | Enabled: false 501 | 502 | Style/ColonMethodCall: 503 | Description: 'Do not use :: for method call.' 504 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' 505 | Enabled: false 506 | 507 | Style/CommandLiteral: 508 | Description: 'Use `` or %x around command literals.' 509 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' 510 | Enabled: false 511 | 512 | Style/CommentAnnotation: 513 | Description: 'Checks formatting of annotation comments.' 514 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' 515 | Enabled: false 516 | 517 | Style/CommentIndentation: 518 | Description: 'Indentation of comments.' 519 | Enabled: false 520 | 521 | Style/ConstantName: 522 | Description: 'Constants should use SCREAMING_SNAKE_CASE.' 523 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' 524 | Enabled: false 525 | 526 | Style/DefWithParentheses: 527 | Description: 'Use def with parentheses when there are arguments.' 528 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 529 | Enabled: false 530 | 531 | Style/PreferredHashMethods: 532 | Description: 'Checks for use of deprecated Hash methods.' 533 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' 534 | Enabled: false 535 | 536 | Style/Documentation: 537 | Description: 'Document classes and non-namespace modules.' 538 | Enabled: false 539 | 540 | Style/DotPosition: 541 | Description: 'Checks the position of the dot in multi-line method calls.' 542 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' 543 | Enabled: false 544 | 545 | Style/DoubleNegation: 546 | Description: 'Checks for uses of double negation (!!).' 547 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' 548 | Enabled: false 549 | 550 | Style/EachWithObject: 551 | Description: 'Prefer `each_with_object` over `inject` or `reduce`.' 552 | Enabled: false 553 | 554 | Style/ElseAlignment: 555 | Description: 'Align elses and elsifs correctly.' 556 | Enabled: false 557 | 558 | Style/EmptyElse: 559 | Description: 'Avoid empty else-clauses.' 560 | Enabled: false 561 | 562 | Style/EmptyLineBetweenDefs: 563 | Description: 'Use empty lines between defs.' 564 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' 565 | Enabled: false 566 | 567 | Style/EmptyLines: 568 | Description: "Don't use several empty lines in a row." 569 | Enabled: false 570 | 571 | Style/EmptyLinesAroundAccessModifier: 572 | Description: "Keep blank lines around access modifiers." 573 | Enabled: false 574 | 575 | Style/EmptyLinesAroundBlockBody: 576 | Description: "Keeps track of empty lines around block bodies." 577 | Enabled: false 578 | 579 | Style/EmptyLinesAroundClassBody: 580 | Description: "Keeps track of empty lines around class bodies." 581 | Enabled: false 582 | 583 | Style/EmptyLinesAroundModuleBody: 584 | Description: "Keeps track of empty lines around module bodies." 585 | Enabled: false 586 | 587 | Style/EmptyLinesAroundMethodBody: 588 | Description: "Keeps track of empty lines around method bodies." 589 | Enabled: false 590 | 591 | Style/EmptyLiteral: 592 | Description: 'Prefer literals to Array.new/Hash.new/String.new.' 593 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' 594 | Enabled: false 595 | 596 | Style/EndBlock: 597 | Description: 'Avoid the use of END blocks.' 598 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' 599 | Enabled: false 600 | 601 | Style/EndOfLine: 602 | Description: 'Use Unix-style line endings.' 603 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' 604 | Enabled: false 605 | 606 | Style/EvenOdd: 607 | Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' 608 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 609 | Enabled: false 610 | 611 | Style/ExtraSpacing: 612 | Description: 'Do not use unnecessary spacing.' 613 | Enabled: false 614 | 615 | Style/FileName: 616 | Description: 'Use snake_case for source file names.' 617 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' 618 | Enabled: false 619 | 620 | Style/InitialIndentation: 621 | Description: >- 622 | Checks the indentation of the first non-blank non-comment line in a file. 623 | Enabled: false 624 | 625 | Style/FirstParameterIndentation: 626 | Description: 'Checks the indentation of the first parameter in a method call.' 627 | Enabled: false 628 | 629 | Style/FlipFlop: 630 | Description: 'Checks for flip flops' 631 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' 632 | Enabled: false 633 | 634 | Style/For: 635 | Description: 'Checks use of for or each in multiline loops.' 636 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' 637 | Enabled: false 638 | 639 | Style/FormatString: 640 | Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' 641 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' 642 | Enabled: false 643 | 644 | Style/GlobalVars: 645 | Description: 'Do not introduce global variables.' 646 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' 647 | Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' 648 | Enabled: false 649 | 650 | Style/GuardClause: 651 | Description: 'Check for conditionals that can be replaced with guard clauses' 652 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 653 | Enabled: false 654 | 655 | Style/HashSyntax: 656 | Description: >- 657 | Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax 658 | { :a => 1, :b => 2 }. 659 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' 660 | Enabled: false 661 | 662 | Style/IfUnlessModifier: 663 | Description: >- 664 | Favor modifier if/unless usage when you have a 665 | single-line body. 666 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' 667 | Enabled: false 668 | 669 | Style/IfWithSemicolon: 670 | Description: 'Do not use if x; .... Use the ternary operator instead.' 671 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' 672 | Enabled: false 673 | 674 | Style/IndentationConsistency: 675 | Description: 'Keep indentation straight.' 676 | Enabled: false 677 | 678 | Style/IndentationWidth: 679 | Description: 'Use 2 spaces for indentation.' 680 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 681 | Enabled: false 682 | 683 | Style/IndentArray: 684 | Description: >- 685 | Checks the indentation of the first element in an array 686 | literal. 687 | Enabled: false 688 | 689 | Style/IndentHash: 690 | Description: 'Checks the indentation of the first key in a hash literal.' 691 | Enabled: false 692 | 693 | Style/InfiniteLoop: 694 | Description: 'Use Kernel#loop for infinite loops.' 695 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' 696 | Enabled: false 697 | 698 | Style/Lambda: 699 | Description: 'Use the new lambda literal syntax for single-line blocks.' 700 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' 701 | Enabled: false 702 | 703 | Style/LambdaCall: 704 | Description: 'Use lambda.call(...) instead of lambda.(...).' 705 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' 706 | Enabled: false 707 | 708 | Style/LeadingCommentSpace: 709 | Description: 'Comments should start with a space.' 710 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' 711 | Enabled: false 712 | 713 | Style/LineEndConcatenation: 714 | Description: >- 715 | Use \ instead of + or << to concatenate two string literals at 716 | line end. 717 | Enabled: false 718 | 719 | Style/MethodCallParentheses: 720 | Description: 'Do not use parentheses for method calls with no arguments.' 721 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' 722 | Enabled: false 723 | 724 | Style/MethodDefParentheses: 725 | Description: >- 726 | Checks if the method definitions have or don't have 727 | parentheses. 728 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' 729 | Enabled: false 730 | 731 | Style/MethodName: 732 | Description: 'Use the configured style when naming methods.' 733 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 734 | Enabled: false 735 | 736 | Style/ModuleFunction: 737 | Description: 'Checks for usage of `extend self` in modules.' 738 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' 739 | Enabled: false 740 | 741 | Style/MultilineBlockChain: 742 | Description: 'Avoid multi-line chains of blocks.' 743 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' 744 | Enabled: false 745 | 746 | Style/MultilineBlockLayout: 747 | Description: 'Ensures newlines after multiline block do statements.' 748 | Enabled: false 749 | 750 | Style/MultilineIfThen: 751 | Description: 'Do not use then for multi-line if/unless.' 752 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' 753 | Enabled: false 754 | 755 | Style/MultilineOperationIndentation: 756 | Description: >- 757 | Checks indentation of binary operations that span more than 758 | one line. 759 | Enabled: false 760 | 761 | Style/MultilineTernaryOperator: 762 | Description: >- 763 | Avoid multi-line ?: (the ternary operator); 764 | use if/unless instead. 765 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' 766 | Enabled: false 767 | 768 | Style/NegatedIf: 769 | Description: >- 770 | Favor unless over if for negative conditions 771 | (or control flow or). 772 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' 773 | Enabled: false 774 | 775 | Style/NegatedWhile: 776 | Description: 'Favor until over while for negative conditions.' 777 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' 778 | Enabled: false 779 | 780 | Style/NestedTernaryOperator: 781 | Description: 'Use one expression per branch in a ternary operator.' 782 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' 783 | Enabled: false 784 | 785 | Style/Next: 786 | Description: 'Use `next` to skip iteration instead of a condition at the end.' 787 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' 788 | Enabled: false 789 | 790 | Style/NilComparison: 791 | Description: 'Prefer x.nil? to x == nil.' 792 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' 793 | Enabled: false 794 | 795 | Style/NonNilCheck: 796 | Description: 'Checks for redundant nil checks.' 797 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' 798 | Enabled: false 799 | 800 | Style/Not: 801 | Description: 'Use ! instead of not.' 802 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' 803 | Enabled: false 804 | 805 | Style/NumericLiterals: 806 | Description: >- 807 | Add underscores to large numeric literals to improve their 808 | readability. 809 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' 810 | Enabled: false 811 | 812 | Style/OneLineConditional: 813 | Description: >- 814 | Favor the ternary operator(?:) over 815 | if/then/else/end constructs. 816 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' 817 | Enabled: false 818 | 819 | Style/OpMethod: 820 | Description: 'When defining binary operators, name the argument other.' 821 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' 822 | Enabled: false 823 | 824 | Style/OptionalArguments: 825 | Description: >- 826 | Checks for optional arguments that do not appear at the end 827 | of the argument list 828 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments' 829 | Enabled: false 830 | 831 | Style/ParallelAssignment: 832 | Description: >- 833 | Check for simple usages of parallel assignment. 834 | It will only warn when the number of variables 835 | matches on both sides of the assignment. 836 | This also provides performance benefits 837 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' 838 | Enabled: false 839 | 840 | Style/ParenthesesAroundCondition: 841 | Description: >- 842 | Don't use parentheses around the condition of an 843 | if/unless/while. 844 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' 845 | Enabled: false 846 | 847 | Style/PercentLiteralDelimiters: 848 | Description: 'Use `%`-literal delimiters consistently' 849 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' 850 | Enabled: false 851 | 852 | Style/PercentQLiterals: 853 | Description: 'Checks if uses of %Q/%q match the configured preference.' 854 | Enabled: false 855 | 856 | Style/PerlBackrefs: 857 | Description: 'Avoid Perl-style regex back references.' 858 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' 859 | Enabled: false 860 | 861 | Style/PredicateName: 862 | Description: 'Check the names of predicate methods.' 863 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' 864 | Enabled: false 865 | 866 | Style/Proc: 867 | Description: 'Use proc instead of Proc.new.' 868 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' 869 | Enabled: false 870 | 871 | Style/RaiseArgs: 872 | Description: 'Checks the arguments passed to raise/fail.' 873 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' 874 | Enabled: false 875 | 876 | Style/RedundantBegin: 877 | Description: "Don't use begin blocks when they are not needed." 878 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' 879 | Enabled: false 880 | 881 | Style/RedundantException: 882 | Description: "Checks for an obsolete RuntimeException argument in raise/fail." 883 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' 884 | Enabled: false 885 | 886 | Style/RedundantReturn: 887 | Description: "Don't use return where it's not required." 888 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' 889 | Enabled: false 890 | 891 | Style/RedundantSelf: 892 | Description: "Don't use self where it's not needed." 893 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' 894 | Enabled: false 895 | 896 | Style/RegexpLiteral: 897 | Description: 'Use / or %r around regular expressions.' 898 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' 899 | Enabled: false 900 | 901 | Style/RescueEnsureAlignment: 902 | Description: 'Align rescues and ensures correctly.' 903 | Enabled: false 904 | 905 | Style/RescueModifier: 906 | Description: 'Avoid using rescue in its modifier form.' 907 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' 908 | Enabled: false 909 | 910 | Style/SelfAssignment: 911 | Description: >- 912 | Checks for places where self-assignment shorthand should have 913 | been used. 914 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' 915 | Enabled: false 916 | 917 | Style/Semicolon: 918 | Description: "Don't use semicolons to terminate expressions." 919 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' 920 | Enabled: false 921 | 922 | Style/SignalException: 923 | Description: 'Checks for proper usage of fail and raise.' 924 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' 925 | Enabled: false 926 | 927 | Style/SingleLineBlockParams: 928 | Description: 'Enforces the names of some block params.' 929 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' 930 | Enabled: false 931 | 932 | Style/SingleLineMethods: 933 | Description: 'Avoid single-line methods.' 934 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' 935 | Enabled: false 936 | 937 | Style/SpaceBeforeFirstArg: 938 | Description: >- 939 | Checks that exactly one space is used between a method name 940 | and the first argument for method calls without parentheses. 941 | Enabled: true 942 | 943 | Style/SpaceAfterColon: 944 | Description: 'Use spaces after colons.' 945 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 946 | Enabled: false 947 | 948 | Style/SpaceAfterComma: 949 | Description: 'Use spaces after commas.' 950 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 951 | Enabled: false 952 | 953 | Style/SpaceAroundKeyword: 954 | Description: 'Use spaces around keywords.' 955 | Enabled: false 956 | 957 | Style/SpaceAfterMethodName: 958 | Description: >- 959 | Do not put a space between a method name and the opening 960 | parenthesis in a method definition. 961 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' 962 | Enabled: false 963 | 964 | Style/SpaceAfterNot: 965 | Description: Tracks redundant space after the ! operator. 966 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' 967 | Enabled: false 968 | 969 | Style/SpaceAfterSemicolon: 970 | Description: 'Use spaces after semicolons.' 971 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 972 | Enabled: false 973 | 974 | Style/SpaceBeforeBlockBraces: 975 | Description: >- 976 | Checks that the left block brace has or doesn't have space 977 | before it. 978 | Enabled: false 979 | 980 | Style/SpaceBeforeComma: 981 | Description: 'No spaces before commas.' 982 | Enabled: false 983 | 984 | Style/SpaceBeforeComment: 985 | Description: >- 986 | Checks for missing space between code and a comment on the 987 | same line. 988 | Enabled: false 989 | 990 | Style/SpaceBeforeSemicolon: 991 | Description: 'No spaces before semicolons.' 992 | Enabled: false 993 | 994 | Style/SpaceInsideBlockBraces: 995 | Description: >- 996 | Checks that block braces have or don't have surrounding space. 997 | For blocks taking parameters, checks that the left brace has 998 | or doesn't have trailing space. 999 | Enabled: false 1000 | 1001 | Style/SpaceAroundBlockParameters: 1002 | Description: 'Checks the spacing inside and after block parameters pipes.' 1003 | Enabled: false 1004 | 1005 | Style/SpaceAroundEqualsInParameterDefault: 1006 | Description: >- 1007 | Checks that the equals signs in parameter default assignments 1008 | have or don't have surrounding space depending on 1009 | configuration. 1010 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' 1011 | Enabled: false 1012 | 1013 | Style/SpaceAroundOperators: 1014 | Description: 'Use a single space around operators.' 1015 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 1016 | Enabled: false 1017 | 1018 | Style/SpaceInsideBrackets: 1019 | Description: 'No spaces after [ or before ].' 1020 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 1021 | Enabled: false 1022 | 1023 | Style/SpaceInsideHashLiteralBraces: 1024 | Description: "Use spaces inside hash literal braces - or don't." 1025 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' 1026 | Enabled: false 1027 | 1028 | Style/SpaceInsideParens: 1029 | Description: 'No spaces after ( or before ).' 1030 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' 1031 | Enabled: false 1032 | 1033 | Style/SpaceInsideRangeLiteral: 1034 | Description: 'No spaces inside range literals.' 1035 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' 1036 | Enabled: false 1037 | 1038 | Style/SpaceInsideStringInterpolation: 1039 | Description: 'Checks for padding/surrounding spaces inside string interpolation.' 1040 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation' 1041 | Enabled: false 1042 | 1043 | Style/SpecialGlobalVars: 1044 | Description: 'Avoid Perl-style global variables.' 1045 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' 1046 | Enabled: false 1047 | 1048 | Style/StringLiterals: 1049 | Description: 'Checks if uses of quotes match the configured preference.' 1050 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' 1051 | Enabled: false 1052 | 1053 | Style/StringLiteralsInInterpolation: 1054 | Description: >- 1055 | Checks if uses of quotes inside expressions in interpolated 1056 | strings match the configured preference. 1057 | Enabled: false 1058 | 1059 | Style/StructInheritance: 1060 | Description: 'Checks for inheritance from Struct.new.' 1061 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new' 1062 | Enabled: false 1063 | 1064 | Style/SymbolLiteral: 1065 | Description: 'Use plain symbols instead of string symbols when possible.' 1066 | Enabled: false 1067 | 1068 | Style/SymbolProc: 1069 | Description: 'Use symbols as procs instead of blocks when possible.' 1070 | Enabled: false 1071 | 1072 | Style/Tab: 1073 | Description: 'No hard tabs.' 1074 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' 1075 | Enabled: false 1076 | 1077 | Style/TrailingBlankLines: 1078 | Description: 'Checks trailing blank lines and final newline.' 1079 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' 1080 | Enabled: false 1081 | 1082 | Style/TrailingCommaInArguments: 1083 | Description: 'Checks for trailing comma in parameter lists.' 1084 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma' 1085 | Enabled: false 1086 | 1087 | Style/TrailingCommaInLiteral: 1088 | Description: 'Checks for trailing comma in literals.' 1089 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' 1090 | Enabled: false 1091 | 1092 | Style/TrailingWhitespace: 1093 | Description: 'Avoid trailing whitespace.' 1094 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' 1095 | Enabled: false 1096 | 1097 | Style/TrivialAccessors: 1098 | Description: 'Prefer attr_* methods to trivial readers/writers.' 1099 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' 1100 | Enabled: false 1101 | 1102 | Style/UnlessElse: 1103 | Description: >- 1104 | Do not use unless with else. Rewrite these with the positive 1105 | case first. 1106 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' 1107 | Enabled: false 1108 | 1109 | Style/UnneededCapitalW: 1110 | Description: 'Checks for %W when interpolation is not needed.' 1111 | Enabled: false 1112 | 1113 | Style/UnneededPercentQ: 1114 | Description: 'Checks for %q/%Q when single quotes or double quotes would do.' 1115 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' 1116 | Enabled: false 1117 | 1118 | Style/TrailingUnderscoreVariable: 1119 | Description: >- 1120 | Checks for the usage of unneeded trailing underscores at the 1121 | end of parallel variable assignment. 1122 | Enabled: false 1123 | 1124 | Style/VariableInterpolation: 1125 | Description: >- 1126 | Don't interpolate global, instance and class variables 1127 | directly in strings. 1128 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' 1129 | Enabled: false 1130 | 1131 | Style/VariableName: 1132 | Description: 'Use the configured style when naming variables.' 1133 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' 1134 | Enabled: false 1135 | 1136 | Style/WhenThen: 1137 | Description: 'Use when x then ... for one-line cases.' 1138 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' 1139 | Enabled: false 1140 | 1141 | Style/WhileUntilDo: 1142 | Description: 'Checks for redundant do after while or until.' 1143 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' 1144 | Enabled: false 1145 | 1146 | Style/WhileUntilModifier: 1147 | Description: >- 1148 | Favor modifier while/until usage when you have a 1149 | single-line body. 1150 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' 1151 | Enabled: false 1152 | 1153 | Style/WordArray: 1154 | Description: 'Use %w or %W for arrays of words.' 1155 | StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' 1156 | Enabled: false 1157 | --------------------------------------------------------------------------------