├── .github ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── pull_request_template.md ├── .gitignore ├── .pryrc ├── .reek.yml ├── .rubocop.yml ├── .ruby-version ├── .slugignore ├── .travis.yml ├── CHANGELOG.md ├── Gemfile ├── Gemfile.lock ├── LICENSE.md ├── Procfile ├── README.md ├── Rakefile ├── apiary.apib ├── app ├── admin │ ├── admin_user.rb │ ├── dashboard.rb │ └── user.rb ├── assets │ ├── config │ │ └── manifest.js │ ├── javascripts │ │ ├── active_admin.js │ │ └── application.js │ └── stylesheets │ │ ├── active_admin.scss │ │ └── application.css ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── api │ │ ├── concerns │ │ │ └── act_as_api_request.rb │ │ └── v1 │ │ │ ├── api_controller.rb │ │ │ └── github_webhook_controller.rb │ ├── application_controller.rb │ └── concerns │ │ └── .keep ├── jobs │ └── application_job.rb ├── mailers │ └── application_mailer.rb ├── models │ ├── admin_user.rb │ ├── application_record.rb │ ├── concerns │ │ └── .keep │ └── user.rb ├── services │ ├── pull_request.rb │ ├── slack_bot.rb │ └── slack_notification_service.rb └── views │ └── layouts │ ├── mailer.html.haml │ └── mailer.text.haml ├── bin ├── bundle ├── delayed_job ├── rails ├── rake ├── setup ├── spring └── update ├── config.ru ├── config ├── application.rb ├── application.travis.yml ├── application.yml.example ├── boot.rb ├── cable.yml ├── database.travis.yml ├── database.yml.example ├── env_variables.rb ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── active_admin.rb │ ├── application_controller_renderer.rb │ ├── backtrace_silencers.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── rack_cors.rb │ ├── slack.rb │ └── wrap_parameters.rb ├── locales │ ├── devise.en.yml │ └── en.yml ├── puma.rb ├── rails_best_practices.yml ├── routes.rb └── spring.rb ├── db ├── migrate │ ├── 20161011151353_devise_create_users.rb │ ├── 20161011184702_devise_create_admin_users.rb │ ├── 20161017183759_add_devise_token_auth_fields_users.rb │ ├── 20161027190856_create_delayed_jobs.rb │ └── 20190418132233_modify_users.rb ├── schema.rb └── seeds.rb ├── lib └── tasks │ ├── .keep │ ├── add_blocklisted_user.rake │ ├── auto_annotate_models.rake │ ├── code_analysis.rake │ └── rails_best_practices.rake ├── public └── robots.txt └── spec ├── admin ├── active_admin_spec.rb └── controllers │ └── users_controller_spec.rb ├── factories ├── spec.rb └── user.rb ├── helpers.rb ├── models └── user_spec.rb ├── rails_helper.rb ├── requests └── api │ └── v1 │ └── github_webhook │ └── filter_spec.rb ├── services ├── slack_bot_spec.rb └── slack_notification_service_spec.rb └── spec_helper.rb /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at anthony@rootstrap.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## Contributing ## 2 | 3 | You can contribute to this repo if you have an issue, found a bug or think there's some functionality required that would add value to the gem. To do so, please check if there's not already an [issue](https://github.com/rootstrap/pull_requests_to_slack/issues) for that, if you find there's not, create a new one with as much detail as possible. 4 | 5 | If you want to contribute with code as well, please follow the next steps: 6 | 7 | 1. Read, understand and agree to our [code of conduct](https://github.com/rootstrap/pull_requests_to_slack/blob/master/CODE_OF_CONDUCT.md) 8 | 2. [Fork the repo](https://help.github.com/articles/about-forks/) 9 | 3. Clone the project into your machine: 10 | `$ git clone git@github.com:rootstrap/pull_requests_to_slack.git` 11 | 4. Access the repo: 12 | `$ cd pull_requests_to_slack` 13 | 5. Create your feature/bugfix branch: 14 | `$ git checkout -b your_new_feature` 15 | or 16 | `$ git checkout -b fix/your_fix` in case of a bug fix 17 | (if your PR is to address an existing issue, it would be good to name the branch after the issue, for example: if you are trying to solve issue 182, then a good idea for the branch name would be `182_your_new_feature`) 18 | 6. Write tests for your changes (feature/bug) 19 | 7. Code your (feature/bugfix) 20 | 8. Run the code analysis tool by doing: 21 | `$ rake code_analysis` 22 | 9. Run the tests: 23 | `$ bundle exec rspec` 24 | All tests must pass. If all tests (both code analysis and rspec) do pass, then you are ready to go to the next step: 25 | 10. Commit your changes: 26 | `$ git commit -m 'Your feature or bugfix title'` 27 | 11. Push to the branch `$ git push origin your_new_feature` 28 | 12. Create a new [pull request](https://help.github.com/articles/creating-a-pull-request/) 29 | 30 | Some helpful guides that will help you know how we work: 31 | 1. [Code review](https://github.com/rootstrap/tech-guides/tree/master/code-review) 32 | 2. [GIT workflow](https://github.com/rootstrap/tech-guides/tree/master/git) 33 | 3. [Ruby style guide](https://github.com/rootstrap/tech-guides/tree/master/ruby) 34 | 4. [Rails style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rails.md) 35 | 5. [RSpec style guide](https://github.com/rootstrap/tech-guides/blob/master/ruby/rspec/README.md) 36 | 37 | For more information or guides like the ones mentioned above, please check our [tech guides](https://github.com/rootstrap/tech-guides). Keep in mind that the more you know about these guides, the easier it will be for your code to get approved and merged. 38 | 39 | Note: You can push as many commits as you want when working on a pull request, we just ask that they are descriptive and tell a story. Try to open a pull request with just one commit but if you think you need to divide what you did into more commits to convey what you are trying to do go for it. 40 | 41 | Thank you very much for your time and for considering helping in this project. 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | When posting issues, please include the following information to speed up the troubleshooting process: 2 | 3 | * **Version**: which version of this repo do you have? 4 | * **Ruby Version**: which version of ruby do you have? 5 | * **Environment**: in which env is happening? 6 | * **Rails Stacktrace**: this can be found in the `log/development.log` or `log/test.log`, if this is applicable. 7 | 8 | Use labels to categorize it and thanks for posting the issue! 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: "[Bug] " 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | ## Bug report: 11 | * **Expected Behavior**: 12 | * **Actual Behavior**: 13 | * **Steps to Reproduce**: 14 | 1. 15 | 2. 16 | 3. 17 | 18 | * **Version of the repo**: 19 | * **Ruby and Rails Version**: 20 | * **Rails Stacktrace**: this can be found in the `log/development.log` or `log/test.log`, if this is applicable. 21 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '[FEATURE]' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 11 | 12 | **Is your feature request related to a problem? Please describe.** 13 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 14 | 15 | **Describe the solution you'd like** 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | A clear and concise description of any alternative solutions or features you've considered. 20 | 21 | **Additional context** 22 | Add any other context or screenshots about the feature request here. 23 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | ### Summary 2 | 3 | 7 | 8 | ### Other Information 9 | 10 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /config/database.yml 2 | *.rbc 3 | *.swp 4 | capybara-*.html 5 | .rspec 6 | /log 7 | /tmp 8 | /db/*.sqlite3 9 | /public/system 10 | /coverage/ 11 | /spec/tmp 12 | .idea/ 13 | .DS_Store 14 | **.orig 15 | rerun.txt 16 | pickle-email-*.html 17 | config/initializers/secret_token.rb 18 | config/secrets.yml 19 | config/application.yml 20 | 21 | ## Environment normalisation: 22 | /.bundle 23 | /vendor/bundle 24 | 25 | # these should all be checked in to normalise the environment: 26 | # Gemfile.lock, .ruby-version, .ruby-gemset 27 | 28 | # unless supporting rvm < 1.11.0 or doing something fancy, ignore this: 29 | .rvmrc 30 | 31 | # Ignore byebug history 32 | .byebug_history 33 | -------------------------------------------------------------------------------- /.pryrc: -------------------------------------------------------------------------------- 1 | if defined?(PryByebug) 2 | Pry.commands.alias_command 'c', 'continue' 3 | Pry.commands.alias_command 's', 'step' 4 | Pry.commands.alias_command 'n', 'next' 5 | Pry.commands.alias_command 'f', 'finish' 6 | end 7 | -------------------------------------------------------------------------------- /.reek.yml: -------------------------------------------------------------------------------- 1 | detectors: 2 | Attribute: 3 | enabled: false 4 | exclude: [] 5 | BooleanParameter: 6 | enabled: true 7 | exclude: [] 8 | ClassVariable: 9 | enabled: false 10 | exclude: [] 11 | ControlParameter: 12 | enabled: true 13 | exclude: [] 14 | DataClump: 15 | enabled: true 16 | exclude: [] 17 | max_copies: 2 18 | min_clump_size: 2 19 | DuplicateMethodCall: 20 | enabled: true 21 | exclude: [] 22 | max_calls: 1 23 | allow_calls: [] 24 | FeatureEnvy: 25 | enabled: true 26 | exclude: 27 | - "SlackBot#message" 28 | InstanceVariableAssumption: 29 | enabled: false 30 | IrresponsibleModule: 31 | enabled: false 32 | exclude: [] 33 | LongParameterList: 34 | enabled: true 35 | exclude: [] 36 | max_params: 4 37 | overrides: 38 | initialize: 39 | max_params: 5 40 | LongYieldList: 41 | enabled: true 42 | exclude: [] 43 | max_params: 3 44 | ManualDispatch: 45 | enabled: true 46 | exclude: [] 47 | MissingSafeMethod: 48 | enabled: false 49 | exclude: [] 50 | ModuleInitialize: 51 | enabled: true 52 | exclude: [] 53 | NestedIterators: 54 | enabled: true 55 | exclude: [] 56 | max_allowed_nesting: 2 57 | ignore_iterators: [] 58 | NilCheck: 59 | enabled: false 60 | exclude: [] 61 | RepeatedConditional: 62 | enabled: true 63 | exclude: [] 64 | max_ifs: 3 65 | SubclassedFromCoreClass: 66 | enabled: true 67 | exclude: [] 68 | TooManyConstants: 69 | enabled: true 70 | exclude: [] 71 | max_constants: 5 72 | TooManyInstanceVariables: 73 | enabled: true 74 | exclude: [] 75 | max_instance_variables: 9 76 | TooManyMethods: 77 | enabled: true 78 | exclude: [] 79 | max_methods: 25 80 | TooManyStatements: 81 | enabled: true 82 | exclude: 83 | - initialize 84 | max_statements: 12 85 | UncommunicativeMethodName: 86 | enabled: true 87 | exclude: [] 88 | reject: 89 | - "/^[a-z]$/" 90 | - "/[0-9]$/" 91 | - "/[A-Z]/" 92 | accept: [] 93 | UncommunicativeModuleName: 94 | enabled: true 95 | exclude: [] 96 | reject: 97 | - "/^.$/" 98 | - "/[0-9]$/" 99 | accept: 100 | - Inline::C 101 | - "/V[0-9]/" 102 | UncommunicativeParameterName: 103 | enabled: true 104 | exclude: [] 105 | reject: 106 | - "/^.$/" 107 | - "/[0-9]$/" 108 | - "/[A-Z]/" 109 | accept: [] 110 | UncommunicativeVariableName: 111 | enabled: false 112 | exclude: [] 113 | reject: 114 | - "/^.$/" 115 | - "/[0-9]$/" 116 | - "/[A-Z]/" 117 | accept: 118 | - _ 119 | UnusedParameters: 120 | enabled: true 121 | exclude: [] 122 | UnusedPrivateMethod: 123 | enabled: false 124 | UtilityFunction: 125 | enabled: false 126 | 127 | exclude_paths: 128 | - config 129 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | Documentation: 2 | Enabled: false 3 | 4 | Layout/ArgumentAlignment: 5 | Exclude: 6 | - config/initializers/rack_cors.rb 7 | 8 | Layout/HashAlignment: 9 | Exclude: 10 | - lib/tasks/auto_annotate_models.rake 11 | 12 | Layout/SpaceBeforeFirstArg: 13 | Exclude: 14 | - app/views/api/**/**/* 15 | 16 | Lint/AmbiguousBlockAssociation: 17 | Exclude: 18 | - spec/**/* 19 | 20 | Metrics/AbcSize: 21 | # The ABC size is a calculated magnitude, so this number can be an Integer or 22 | # a Float. 23 | Max: 15 24 | 25 | Metrics/BlockLength: 26 | CountComments: false # count full line comments? 27 | Max: 25 28 | Exclude: 29 | - config/**/* 30 | - spec/**/* 31 | - app/admin/**/* 32 | 33 | Metrics/BlockNesting: 34 | Max: 4 35 | 36 | Metrics/ClassLength: 37 | CountComments: false # count full line comments? 38 | Max: 200 39 | 40 | # Avoid complex methods. 41 | Metrics/CyclomaticComplexity: 42 | Max: 6 43 | 44 | Metrics/MethodLength: 45 | CountComments: false # count full line comments? 46 | Max: 24 47 | 48 | Metrics/ModuleLength: 49 | CountComments: false # count full line comments? 50 | Max: 200 51 | 52 | Metrics/LineLength: 53 | Max: 100 54 | # To make it possible to copy or click on URIs in the code, we allow lines 55 | # containing a URI to be longer than Max. 56 | AllowURI: true 57 | URISchemes: 58 | - http 59 | - https 60 | 61 | Metrics/ParameterLists: 62 | Max: 5 63 | CountKeywordArgs: true 64 | 65 | Metrics/PerceivedComplexity: 66 | Max: 12 67 | 68 | Style/FrozenStringLiteralComment: 69 | Enabled: false 70 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.7.2 2 | -------------------------------------------------------------------------------- /.slugignore: -------------------------------------------------------------------------------- 1 | /spec 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | 3 | cache: bundler 4 | 5 | before_install: gem install bundler -v 1.17 6 | 7 | rvm: 8 | - 2.6.3 9 | 10 | sudo: false 11 | 12 | services: 13 | - postgresql 14 | 15 | addons: 16 | postgresql: "9.4" 17 | 18 | env: 19 | global: 20 | - CC_TEST_REPORTER_ID=54db504b0c710f8ec07866e91808973e20566c7018b7b0d13ad2e40227c23c79 21 | 22 | before_script: 23 | - cp config/database.travis.yml config/database.yml 24 | - cp config/application.travis.yml config/application.yml 25 | - RAILS_ENV=test bundle exec rake db:create 26 | - RAILS_ENV=test bundle exec rake db:schema:load 27 | - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter 28 | - chmod +x ./cc-test-reporter 29 | - ./cc-test-reporter before-build 30 | 31 | script: 32 | - bundle exec rake code_analysis 33 | - bundle exec rspec 34 | 35 | after_script: 36 | - ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT 37 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | ## [v1.0.0](https://github.com/rootstrap/rails_api_base/tree/v1.0.0) (2017-09-22) 4 | **Implemented enhancements:** 5 | 6 | - Fix Code Climate Issues [\#88](https://github.com/rootstrap/rails_api_base/issues/88) 7 | - Increase version of rubocop [\#78](https://github.com/rootstrap/rails_api_base/issues/78) 8 | - Add carrierwave base 64 uploader [\#65](https://github.com/rootstrap/rails_api_base/issues/65) 9 | - Configure automatic deploy to heroku [\#62](https://github.com/rootstrap/rails_api_base/issues/62) 10 | - Add bullet to detect N+1 queries [\#60](https://github.com/rootstrap/rails_api_base/issues/60) 11 | - Migrate facebook mock to webmock [\#49](https://github.com/rootstrap/rails_api_base/issues/49) 12 | - Migrate to requests tests [\#45](https://github.com/rootstrap/rails_api_base/issues/45) 13 | - Use single resource \('user'\) instead of 'users/me' in routes [\#33](https://github.com/rootstrap/rails_api_base/issues/33) 14 | - Add shoulda [\#26](https://github.com/rootstrap/rails_api_base/issues/26) 15 | - Add api doc [\#25](https://github.com/rootstrap/rails_api_base/issues/25) 16 | - Removed email default value, now facebook users should use null, vali… [\#41](https://github.com/rootstrap/rails_api_base/pull/41) ([felipegarcia92](https://github.com/felipegarcia92)) 17 | - Fixed readme instructions [\#39](https://github.com/rootstrap/rails_api_base/pull/39) ([felipegarcia92](https://github.com/felipegarcia92)) 18 | 19 | **Fixed bugs:** 20 | 21 | - Increase version of rubocop [\#78](https://github.com/rootstrap/rails_api_base/issues/78) 22 | - Devise Token Auth Issue: IndexError: string not matched [\#73](https://github.com/rootstrap/rails_api_base/issues/73) 23 | - ENV variables aren't loaded yet in application.rb [\#66](https://github.com/rootstrap/rails_api_base/issues/66) 24 | - ActiveAdmin panel for users should require password [\#61](https://github.com/rootstrap/rails_api_base/issues/61) 25 | - ActionDispatch::Cookies::CookieOverflow [\#42](https://github.com/rootstrap/rails_api_base/issues/42) 26 | - Sign Up is wrongly requiring an "Accept: application/json" header to work [\#40](https://github.com/rootstrap/rails_api_base/issues/40) 27 | - In update password, the user isn't being set with the headers [\#30](https://github.com/rootstrap/rails_api_base/issues/30) 28 | - Removed email default value, now facebook users should use null, vali… [\#41](https://github.com/rootstrap/rails_api_base/pull/41) ([felipegarcia92](https://github.com/felipegarcia92)) 29 | 30 | **Closed issues:** 31 | 32 | - Add/Define a new code\_climate.yml file. [\#91](https://github.com/rootstrap/rails_api_base/issues/91) 33 | - Remove Facebook revert line from readme [\#75](https://github.com/rootstrap/rails_api_base/issues/75) 34 | - Add api doc [\#24](https://github.com/rootstrap/rails_api_base/issues/24) 35 | 36 | **Merged pull requests:** 37 | 38 | - Add ratings config to Code Climate file [\#93](https://github.com/rootstrap/rails_api_base/pull/93) ([MaicolBen](https://github.com/MaicolBen)) 39 | - Add Code Climate config file [\#92](https://github.com/rootstrap/rails_api_base/pull/92) ([MaicolBen](https://github.com/MaicolBen)) 40 | - Import devise token auth using https [\#90](https://github.com/rootstrap/rails_api_base/pull/90) ([matiasmansilla1989](https://github.com/matiasmansilla1989)) 41 | - Add badges [\#87](https://github.com/rootstrap/rails_api_base/pull/87) ([MaicolBen](https://github.com/MaicolBen)) 42 | - Update devise token auth reference [\#85](https://github.com/rootstrap/rails_api_base/pull/85) ([diebarral](https://github.com/diebarral)) 43 | - Fix api docs and add license [\#84](https://github.com/rootstrap/rails_api_base/pull/84) ([MaicolBen](https://github.com/MaicolBen)) 44 | - Add list of features to README [\#82](https://github.com/rootstrap/rails_api_base/pull/82) ([santiagovidal](https://github.com/santiagovidal)) 45 | - Remove old comment in password update spec [\#80](https://github.com/rootstrap/rails_api_base/pull/80) ([santiagovidal](https://github.com/santiagovidal)) 46 | - Update rubocop and reek version [\#79](https://github.com/rootstrap/rails_api_base/pull/79) ([gabrielbursztein](https://github.com/gabrielbursztein)) 47 | - Add gems to readme [\#76](https://github.com/rootstrap/rails_api_base/pull/76) ([MaicolBen](https://github.com/MaicolBen)) 48 | - remove refactor tag and fix rspec test [\#74](https://github.com/rootstrap/rails_api_base/pull/74) ([GuilleLeopold](https://github.com/GuilleLeopold)) 49 | - Migrate erb to haml & clean code [\#72](https://github.com/rootstrap/rails_api_base/pull/72) ([MaicolBen](https://github.com/MaicolBen)) 50 | - Add password for new users in admin [\#68](https://github.com/rootstrap/rails_api_base/pull/68) ([MaicolBen](https://github.com/MaicolBen)) 51 | - Add carrierwave-base64 [\#67](https://github.com/rootstrap/rails_api_base/pull/67) ([MaicolBen](https://github.com/MaicolBen)) 52 | - Fix tilt engine for heroku [\#64](https://github.com/rootstrap/rails_api_base/pull/64) ([MaicolBen](https://github.com/MaicolBen)) 53 | - Upgrade rails to 5.1.2 & Add bullet [\#63](https://github.com/rootstrap/rails_api_base/pull/63) ([MaicolBen](https://github.com/MaicolBen)) 54 | - Fix wrong parameter after add error logging [\#59](https://github.com/rootstrap/rails_api_base/pull/59) ([MaicolBen](https://github.com/MaicolBen)) 55 | - Added 500 handler & make facebook webmock more flexible [\#58](https://github.com/rootstrap/rails_api_base/pull/58) ([MaicolBen](https://github.com/MaicolBen)) 56 | - Allow user registration to receive first and last name [\#57](https://github.com/rootstrap/rails_api_base/pull/57) ([santiagovidal](https://github.com/santiagovidal)) 57 | - Add staging environment [\#56](https://github.com/rootstrap/rails_api_base/pull/56) ([MaicolBen](https://github.com/MaicolBen)) 58 | - Fix repeated email validation message [\#54](https://github.com/rootstrap/rails_api_base/pull/54) ([MaicolBen](https://github.com/MaicolBen)) 59 | - allow\_indentation\_for\_json\_responses [\#52](https://github.com/rootstrap/rails_api_base/pull/52) ([adicaff](https://github.com/adicaff)) 60 | - Enable mail errors in development [\#51](https://github.com/rootstrap/rails_api_base/pull/51) ([MaicolBen](https://github.com/MaicolBen)) 61 | - Migrate facebook mock to webmock [\#50](https://github.com/rootstrap/rails_api_base/pull/50) ([MaicolBen](https://github.com/MaicolBen)) 62 | - Migrate controller test to request/routing tests [\#48](https://github.com/rootstrap/rails_api_base/pull/48) ([MaicolBen](https://github.com/MaicolBen)) 63 | - Fix ActionDispatch::Cookies::CookieOverflow in facebook login [\#46](https://github.com/rootstrap/rails_api_base/pull/46) ([MaicolBen](https://github.com/MaicolBen)) 64 | - Use user single resources instead of /users/me [\#44](https://github.com/rootstrap/rails_api_base/pull/44) ([MaicolBen](https://github.com/MaicolBen)) 65 | - Change token configuration for devise token auth [\#43](https://github.com/rootstrap/rails_api_base/pull/43) ([MaicolBen](https://github.com/MaicolBen)) 66 | - Set uid as email if the provider is email [\#38](https://github.com/rootstrap/rails_api_base/pull/38) ([MaicolBen](https://github.com/MaicolBen)) 67 | - Add code coverage [\#37](https://github.com/rootstrap/rails_api_base/pull/37) ([MaicolBen](https://github.com/MaicolBen)) 68 | - Ignore test files for the heroku slug [\#36](https://github.com/rootstrap/rails_api_base/pull/36) ([MaicolBen](https://github.com/MaicolBen)) 69 | - Add api-blueprint standard documentation [\#35](https://github.com/rootstrap/rails_api_base/pull/35) ([matiasmansilla1989](https://github.com/matiasmansilla1989)) 70 | - Add webmock and shoulda [\#34](https://github.com/rootstrap/rails_api_base/pull/34) ([MaicolBen](https://github.com/MaicolBen)) 71 | - Use symbols instead of strings in parsed responses in tests [\#32](https://github.com/rootstrap/rails_api_base/pull/32) ([MaicolBen](https://github.com/MaicolBen)) 72 | - skip tests that are having inappropriate behavior [\#31](https://github.com/rootstrap/rails_api_base/pull/31) ([GAKINDUSTRIES](https://github.com/GAKINDUSTRIES)) 73 | - add exception to protect\_from\_forgery method and add skip verify\_auth… [\#29](https://github.com/rootstrap/rails_api_base/pull/29) ([GAKINDUSTRIES](https://github.com/GAKINDUSTRIES)) 74 | - add byebug history to gitgnore [\#28](https://github.com/rootstrap/rails_api_base/pull/28) ([GAKINDUSTRIES](https://github.com/GAKINDUSTRIES)) 75 | - Move authenticate user callback to api controller [\#27](https://github.com/rootstrap/rails_api_base/pull/27) ([MaicolBen](https://github.com/MaicolBen)) 76 | - Removed deprecation warning about mime types [\#22](https://github.com/rootstrap/rails_api_base/pull/22) ([MaicolBen](https://github.com/MaicolBen)) 77 | - Fix typo in sendgrid config [\#21](https://github.com/rootstrap/rails_api_base/pull/21) ([MaicolBen](https://github.com/MaicolBen)) 78 | - add break to carrierwave [\#20](https://github.com/rootstrap/rails_api_base/pull/20) ([GuilleLeopold](https://github.com/GuilleLeopold)) 79 | - Fix token auth persist [\#19](https://github.com/rootstrap/rails_api_base/pull/19) ([juanlacruz](https://github.com/juanlacruz)) 80 | - Remove devise cookie storage in api [\#18](https://github.com/rootstrap/rails_api_base/pull/18) ([MaicolBen](https://github.com/MaicolBen)) 81 | - Add login with facebook [\#16](https://github.com/rootstrap/rails_api_base/pull/16) ([MaicolBen](https://github.com/MaicolBen)) 82 | - Added missing configuration [\#15](https://github.com/rootstrap/rails_api_base/pull/15) ([MaicolBen](https://github.com/MaicolBen)) 83 | - Added carrierawave, delayed job and draper [\#14](https://github.com/rootstrap/rails_api_base/pull/14) ([MaicolBen](https://github.com/MaicolBen)) 84 | - Added sengrid [\#13](https://github.com/rootstrap/rails_api_base/pull/13) ([MaicolBen](https://github.com/MaicolBen)) 85 | - Add reset password tests [\#12](https://github.com/rootstrap/rails_api_base/pull/12) ([MaicolBen](https://github.com/MaicolBen)) 86 | - Add users show/update [\#11](https://github.com/rootstrap/rails_api_base/pull/11) ([MaicolBen](https://github.com/MaicolBen)) 87 | - Added sign in and logout [\#10](https://github.com/rootstrap/rails_api_base/pull/10) ([MaicolBen](https://github.com/MaicolBen)) 88 | - Add signup [\#9](https://github.com/rootstrap/rails_api_base/pull/9) ([MaicolBen](https://github.com/MaicolBen)) 89 | - Added rspec, factory girl, faker and database cleanner [\#8](https://github.com/rootstrap/rails_api_base/pull/8) ([MaicolBen](https://github.com/MaicolBen)) 90 | - Add useful gems [\#7](https://github.com/rootstrap/rails_api_base/pull/7) ([MaicolBen](https://github.com/MaicolBen)) 91 | - Add Active Admin [\#6](https://github.com/rootstrap/rails_api_base/pull/6) ([MaicolBen](https://github.com/MaicolBen)) 92 | - Added devise [\#5](https://github.com/rootstrap/rails_api_base/pull/5) ([MaicolBen](https://github.com/MaicolBen)) 93 | - Added CORS config [\#4](https://github.com/rootstrap/rails_api_base/pull/4) ([MaicolBen](https://github.com/MaicolBen)) 94 | - Setup database [\#3](https://github.com/rootstrap/rails_api_base/pull/3) ([MaicolBen](https://github.com/MaicolBen)) 95 | - Added code analysis [\#2](https://github.com/rootstrap/rails_api_base/pull/2) ([MaicolBen](https://github.com/MaicolBen)) 96 | - Setup project [\#1](https://github.com/rootstrap/rails_api_base/pull/1) ([MaicolBen](https://github.com/MaicolBen)) 97 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | ruby '~> 3.1.3' 3 | 4 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 5 | gem 'rails', '~> 6.1.7', '>= 6.1.7.1' 6 | 7 | gem 'activeadmin', '~> 2.9' 8 | gem 'devise', '~> 4.7', '>= 4.7.2' 9 | gem 'pg', '~> 1.1', '>= 1.1.4' 10 | gem 'puma', '~> 5.6.2' 11 | gem 'rack-cors', '~> 1.0', '>= 1.0.6' 12 | gem 'sass-rails', '~> 5.1' 13 | gem 'slack-ruby-client', '~> 2.0.0' 14 | gem 'sprockets', '~> 3.7.2' 15 | 16 | group :development, :test do 17 | gem 'bullet', '~> 6.1' 18 | gem 'factory_bot_rails', '~> 6.2.0' 19 | gem 'pry-byebug', '~> 3.10.1', platform: :mri 20 | gem 'pry-rails', '~> 0.3.9' 21 | gem 'rspec-core', '~> 3.12.2' 22 | gem 'rspec-rails', '~> 6.0.1' 23 | end 24 | 25 | group :development do 26 | gem 'annotate', '~> 3.0', '>= 3.0.3' 27 | gem 'better_errors', '~> 2.8.0' 28 | gem 'brakeman', '~> 4.7.1' 29 | gem 'letter_opener', '~> 1.7' 30 | gem 'listen', '~> 3.2' 31 | gem 'rails_best_practices', '~> 1.19.4' 32 | gem 'reek', '~> 5.5' 33 | gem 'rubocop-rails', '~> 2.3.2' 34 | gem 'spring', '~> 2.1.0' 35 | gem 'spring-watcher-listen', '~> 2.0.0' 36 | end 37 | 38 | group :test do 39 | gem 'database_cleaner', '~> 1.4.1' 40 | gem 'faker', '~> 2.13' 41 | gem 'simplecov', '~> 0.13.0', require: false 42 | gem 'webmock', '~> 2.3.2' 43 | end 44 | 45 | group :assets do 46 | gem 'uglifier', '~> 4.2' 47 | end 48 | 49 | gem "ruby-lsp", "~> 0.3.8", :group => :development 50 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (6.1.7.3) 5 | actionpack (= 6.1.7.3) 6 | activesupport (= 6.1.7.3) 7 | nio4r (~> 2.0) 8 | websocket-driver (>= 0.6.1) 9 | actionmailbox (6.1.7.3) 10 | actionpack (= 6.1.7.3) 11 | activejob (= 6.1.7.3) 12 | activerecord (= 6.1.7.3) 13 | activestorage (= 6.1.7.3) 14 | activesupport (= 6.1.7.3) 15 | mail (>= 2.7.1) 16 | actionmailer (6.1.7.3) 17 | actionpack (= 6.1.7.3) 18 | actionview (= 6.1.7.3) 19 | activejob (= 6.1.7.3) 20 | activesupport (= 6.1.7.3) 21 | mail (~> 2.5, >= 2.5.4) 22 | rails-dom-testing (~> 2.0) 23 | actionpack (6.1.7.3) 24 | actionview (= 6.1.7.3) 25 | activesupport (= 6.1.7.3) 26 | rack (~> 2.0, >= 2.0.9) 27 | rack-test (>= 0.6.3) 28 | rails-dom-testing (~> 2.0) 29 | rails-html-sanitizer (~> 1.0, >= 1.2.0) 30 | actiontext (6.1.7.3) 31 | actionpack (= 6.1.7.3) 32 | activerecord (= 6.1.7.3) 33 | activestorage (= 6.1.7.3) 34 | activesupport (= 6.1.7.3) 35 | nokogiri (>= 1.8.5) 36 | actionview (6.1.7.3) 37 | activesupport (= 6.1.7.3) 38 | builder (~> 3.1) 39 | erubi (~> 1.4) 40 | rails-dom-testing (~> 2.0) 41 | rails-html-sanitizer (~> 1.1, >= 1.2.0) 42 | activeadmin (2.13.1) 43 | arbre (~> 1.2, >= 1.2.1) 44 | formtastic (>= 3.1, < 5.0) 45 | formtastic_i18n (~> 0.4) 46 | inherited_resources (~> 1.7) 47 | jquery-rails (~> 4.2) 48 | kaminari (~> 1.0, >= 1.2.1) 49 | railties (>= 6.1, < 7.1) 50 | ransack (>= 2.1.1, < 4) 51 | activejob (6.1.7.3) 52 | activesupport (= 6.1.7.3) 53 | globalid (>= 0.3.6) 54 | activemodel (6.1.7.3) 55 | activesupport (= 6.1.7.3) 56 | activerecord (6.1.7.3) 57 | activemodel (= 6.1.7.3) 58 | activesupport (= 6.1.7.3) 59 | activestorage (6.1.7.3) 60 | actionpack (= 6.1.7.3) 61 | activejob (= 6.1.7.3) 62 | activerecord (= 6.1.7.3) 63 | activesupport (= 6.1.7.3) 64 | marcel (~> 1.0) 65 | mini_mime (>= 1.1.0) 66 | activesupport (6.1.7.3) 67 | concurrent-ruby (~> 1.0, >= 1.0.2) 68 | i18n (>= 1.6, < 2) 69 | minitest (>= 5.1) 70 | tzinfo (~> 2.0) 71 | zeitwerk (~> 2.3) 72 | addressable (2.8.4) 73 | public_suffix (>= 2.0.2, < 6.0) 74 | annotate (3.2.0) 75 | activerecord (>= 3.2, < 8.0) 76 | rake (>= 10.4, < 14.0) 77 | arbre (1.5.0) 78 | activesupport (>= 3.0.0, < 7.1) 79 | ruby2_keywords (>= 0.0.2, < 1.0) 80 | ast (2.4.2) 81 | bcrypt (3.1.18) 82 | better_errors (2.8.3) 83 | coderay (>= 1.0.0) 84 | erubi (>= 1.0.0) 85 | rack (>= 0.9.0) 86 | brakeman (4.7.2) 87 | builder (3.2.4) 88 | bullet (6.1.5) 89 | activesupport (>= 3.0.0) 90 | uniform_notifier (~> 1.11) 91 | byebug (11.1.3) 92 | code_analyzer (0.5.5) 93 | sexp_processor 94 | codeclimate-engine-rb (0.4.2) 95 | coderay (1.1.3) 96 | concurrent-ruby (1.2.2) 97 | crack (0.4.5) 98 | rexml 99 | crass (1.0.6) 100 | database_cleaner (1.4.1) 101 | date (3.3.3) 102 | devise (4.9.2) 103 | bcrypt (~> 3.0) 104 | orm_adapter (~> 0.1) 105 | railties (>= 4.1.0) 106 | responders 107 | warden (~> 1.2.3) 108 | diff-lcs (1.5.0) 109 | docile (1.1.5) 110 | erubi (1.12.0) 111 | erubis (2.7.0) 112 | execjs (2.8.1) 113 | factory_bot (6.2.1) 114 | activesupport (>= 5.0.0) 115 | factory_bot_rails (6.2.0) 116 | factory_bot (~> 6.2.0) 117 | railties (>= 5.0.0) 118 | faker (2.23.0) 119 | i18n (>= 1.8.11, < 2) 120 | faraday (2.7.4) 121 | faraday-net_http (>= 2.0, < 3.1) 122 | ruby2_keywords (>= 0.0.4) 123 | faraday-mashify (0.1.1) 124 | faraday (~> 2.0) 125 | hashie 126 | faraday-multipart (1.0.4) 127 | multipart-post (~> 2) 128 | faraday-net_http (3.0.2) 129 | ffi (1.15.5) 130 | formtastic (4.0.0) 131 | actionpack (>= 5.2.0) 132 | formtastic_i18n (0.7.0) 133 | gli (2.21.0) 134 | globalid (1.1.0) 135 | activesupport (>= 5.0) 136 | has_scope (0.8.1) 137 | actionpack (>= 5.2) 138 | activesupport (>= 5.2) 139 | hashdiff (1.0.1) 140 | hashie (5.0.0) 141 | i18n (1.12.0) 142 | concurrent-ruby (~> 1.0) 143 | inherited_resources (1.13.1) 144 | actionpack (>= 5.2, < 7.1) 145 | has_scope (~> 0.6) 146 | railties (>= 5.2, < 7.1) 147 | responders (>= 2, < 4) 148 | jquery-rails (4.5.1) 149 | rails-dom-testing (>= 1, < 3) 150 | railties (>= 4.2.0) 151 | thor (>= 0.14, < 2.0) 152 | json (2.6.3) 153 | kaminari (1.2.2) 154 | activesupport (>= 4.1.0) 155 | kaminari-actionview (= 1.2.2) 156 | kaminari-activerecord (= 1.2.2) 157 | kaminari-core (= 1.2.2) 158 | kaminari-actionview (1.2.2) 159 | actionview 160 | kaminari-core (= 1.2.2) 161 | kaminari-activerecord (1.2.2) 162 | activerecord 163 | kaminari-core (= 1.2.2) 164 | kaminari-core (1.2.2) 165 | kwalify (0.7.2) 166 | language_server-protocol (3.17.0.3) 167 | launchy (2.5.2) 168 | addressable (~> 2.8) 169 | letter_opener (1.8.1) 170 | launchy (>= 2.2, < 3) 171 | listen (3.8.0) 172 | rb-fsevent (~> 0.10, >= 0.10.3) 173 | rb-inotify (~> 0.9, >= 0.9.10) 174 | loofah (2.20.0) 175 | crass (~> 1.0.2) 176 | nokogiri (>= 1.5.9) 177 | mail (2.8.1) 178 | mini_mime (>= 0.1.1) 179 | net-imap 180 | net-pop 181 | net-smtp 182 | marcel (1.0.2) 183 | method_source (1.0.0) 184 | mini_mime (1.1.2) 185 | mini_portile2 (2.8.1) 186 | minitest (5.18.0) 187 | multipart-post (2.3.0) 188 | net-imap (0.3.4) 189 | date 190 | net-protocol 191 | net-pop (0.1.2) 192 | net-protocol 193 | net-protocol (0.2.1) 194 | timeout 195 | net-smtp (0.3.3) 196 | net-protocol 197 | nio4r (2.5.9) 198 | nokogiri (1.14.3) 199 | mini_portile2 (~> 2.8.0) 200 | racc (~> 1.4) 201 | orm_adapter (0.5.0) 202 | parallel (1.23.0) 203 | parser (2.7.2.0) 204 | ast (~> 2.4.1) 205 | pg (1.4.6) 206 | prettier_print (1.2.1) 207 | pry (0.14.2) 208 | coderay (~> 1.1) 209 | method_source (~> 1.0) 210 | pry-byebug (3.10.1) 211 | byebug (~> 11.0) 212 | pry (>= 0.13, < 0.15) 213 | pry-rails (0.3.9) 214 | pry (>= 0.10.4) 215 | psych (3.1.0) 216 | public_suffix (5.0.1) 217 | puma (5.6.5) 218 | nio4r (~> 2.0) 219 | racc (1.6.2) 220 | rack (2.2.6.4) 221 | rack-cors (1.1.1) 222 | rack (>= 2.0.0) 223 | rack-test (2.1.0) 224 | rack (>= 1.3) 225 | rails (6.1.7.3) 226 | actioncable (= 6.1.7.3) 227 | actionmailbox (= 6.1.7.3) 228 | actionmailer (= 6.1.7.3) 229 | actionpack (= 6.1.7.3) 230 | actiontext (= 6.1.7.3) 231 | actionview (= 6.1.7.3) 232 | activejob (= 6.1.7.3) 233 | activemodel (= 6.1.7.3) 234 | activerecord (= 6.1.7.3) 235 | activestorage (= 6.1.7.3) 236 | activesupport (= 6.1.7.3) 237 | bundler (>= 1.15.0) 238 | railties (= 6.1.7.3) 239 | sprockets-rails (>= 2.0.0) 240 | rails-dom-testing (2.0.3) 241 | activesupport (>= 4.2.0) 242 | nokogiri (>= 1.6) 243 | rails-html-sanitizer (1.5.0) 244 | loofah (~> 2.19, >= 2.19.1) 245 | rails_best_practices (1.19.5) 246 | activesupport 247 | code_analyzer (>= 0.5.1) 248 | erubis 249 | i18n 250 | json 251 | require_all (~> 3.0) 252 | ruby-progressbar 253 | railties (6.1.7.3) 254 | actionpack (= 6.1.7.3) 255 | activesupport (= 6.1.7.3) 256 | method_source 257 | rake (>= 12.2) 258 | thor (~> 1.0) 259 | rainbow (3.1.1) 260 | rake (13.0.6) 261 | ransack (3.2.1) 262 | activerecord (>= 6.1.5) 263 | activesupport (>= 6.1.5) 264 | i18n 265 | rb-fsevent (0.11.2) 266 | rb-inotify (0.10.1) 267 | ffi (~> 1.0) 268 | reek (5.6.0) 269 | codeclimate-engine-rb (~> 0.4.0) 270 | kwalify (~> 0.7.0) 271 | parser (>= 2.5.0.0, < 2.8, != 2.5.1.1) 272 | psych (~> 3.1.0) 273 | rainbow (>= 2.0, < 4.0) 274 | regexp_parser (2.8.0) 275 | require_all (3.0.0) 276 | responders (3.1.0) 277 | actionpack (>= 5.2) 278 | railties (>= 5.2) 279 | rexml (3.2.5) 280 | rspec-core (3.12.2) 281 | rspec-support (~> 3.12.0) 282 | rspec-expectations (3.12.3) 283 | diff-lcs (>= 1.2.0, < 2.0) 284 | rspec-support (~> 3.12.0) 285 | rspec-mocks (3.12.5) 286 | diff-lcs (>= 1.2.0, < 2.0) 287 | rspec-support (~> 3.12.0) 288 | rspec-rails (6.0.1) 289 | actionpack (>= 6.1) 290 | activesupport (>= 6.1) 291 | railties (>= 6.1) 292 | rspec-core (~> 3.11) 293 | rspec-expectations (~> 3.11) 294 | rspec-mocks (~> 3.11) 295 | rspec-support (~> 3.11) 296 | rspec-support (3.12.0) 297 | rubocop (1.7.0) 298 | parallel (~> 1.10) 299 | parser (>= 2.7.1.5) 300 | rainbow (>= 2.2.2, < 4.0) 301 | regexp_parser (>= 1.8, < 3.0) 302 | rexml 303 | rubocop-ast (>= 1.2.0, < 2.0) 304 | ruby-progressbar (~> 1.7) 305 | unicode-display_width (>= 1.4.0, < 2.0) 306 | rubocop-ast (1.4.1) 307 | parser (>= 2.7.1.5) 308 | rubocop-rails (2.3.2) 309 | rack (>= 1.1) 310 | rubocop (>= 0.72.0) 311 | ruby-lsp (0.3.8) 312 | language_server-protocol (~> 3.17.0) 313 | sorbet-runtime 314 | syntax_tree (>= 5.0.0, < 6) 315 | ruby-progressbar (1.13.0) 316 | ruby2_keywords (0.0.5) 317 | sass (3.7.4) 318 | sass-listen (~> 4.0.0) 319 | sass-listen (4.0.0) 320 | rb-fsevent (~> 0.9, >= 0.9.4) 321 | rb-inotify (~> 0.9, >= 0.9.7) 322 | sass-rails (5.1.0) 323 | railties (>= 5.2.0) 324 | sass (~> 3.1) 325 | sprockets (>= 2.8, < 4.0) 326 | sprockets-rails (>= 2.0, < 4.0) 327 | tilt (>= 1.1, < 3) 328 | sexp_processor (4.16.1) 329 | simplecov (0.13.0) 330 | docile (~> 1.1.0) 331 | json (>= 1.8, < 3) 332 | simplecov-html (~> 0.10.0) 333 | simplecov-html (0.10.2) 334 | slack-ruby-client (2.0.0) 335 | faraday (>= 2.0) 336 | faraday-mashify 337 | faraday-multipart 338 | gli 339 | hashie 340 | websocket-driver 341 | sorbet-runtime (0.5.10789) 342 | spring (2.1.1) 343 | spring-watcher-listen (2.0.1) 344 | listen (>= 2.7, < 4.0) 345 | spring (>= 1.2, < 3.0) 346 | sprockets (3.7.2) 347 | concurrent-ruby (~> 1.0) 348 | rack (> 1, < 3) 349 | sprockets-rails (3.4.2) 350 | actionpack (>= 5.2) 351 | activesupport (>= 5.2) 352 | sprockets (>= 3.0.0) 353 | syntax_tree (5.3.0) 354 | prettier_print (>= 1.2.0) 355 | thor (1.2.1) 356 | tilt (2.1.0) 357 | timeout (0.3.2) 358 | tzinfo (2.0.6) 359 | concurrent-ruby (~> 1.0) 360 | uglifier (4.2.0) 361 | execjs (>= 0.3.0, < 3) 362 | unicode-display_width (1.8.0) 363 | uniform_notifier (1.16.0) 364 | warden (1.2.9) 365 | rack (>= 2.0.9) 366 | webmock (2.3.2) 367 | addressable (>= 2.3.6) 368 | crack (>= 0.3.2) 369 | hashdiff 370 | websocket-driver (0.7.5) 371 | websocket-extensions (>= 0.1.0) 372 | websocket-extensions (0.1.5) 373 | zeitwerk (2.6.7) 374 | 375 | PLATFORMS 376 | ruby 377 | 378 | DEPENDENCIES 379 | activeadmin (~> 2.9) 380 | annotate (~> 3.0, >= 3.0.3) 381 | better_errors (~> 2.8.0) 382 | brakeman (~> 4.7.1) 383 | bullet (~> 6.1) 384 | database_cleaner (~> 1.4.1) 385 | devise (~> 4.7, >= 4.7.2) 386 | factory_bot_rails (~> 6.2.0) 387 | faker (~> 2.13) 388 | letter_opener (~> 1.7) 389 | listen (~> 3.2) 390 | pg (~> 1.1, >= 1.1.4) 391 | pry-byebug (~> 3.10.1) 392 | pry-rails (~> 0.3.9) 393 | puma (~> 5.6.2) 394 | rack-cors (~> 1.0, >= 1.0.6) 395 | rails (~> 6.1.7, >= 6.1.7.1) 396 | rails_best_practices (~> 1.19.4) 397 | reek (~> 5.5) 398 | rspec-core (~> 3.12.2) 399 | rspec-rails (~> 6.0.1) 400 | rubocop-rails (~> 2.3.2) 401 | ruby-lsp (~> 0.3.8) 402 | sass-rails (~> 5.1) 403 | simplecov (~> 0.13.0) 404 | slack-ruby-client (~> 2.0.0) 405 | spring (~> 2.1.0) 406 | spring-watcher-listen (~> 2.0.0) 407 | sprockets (~> 3.7.2) 408 | uglifier (~> 4.2) 409 | webmock (~> 2.3.2) 410 | 411 | RUBY VERSION 412 | ruby 3.1.3p185 413 | 414 | BUNDLED WITH 415 | 2.4.6 416 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Rootstrap 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 | -------------------------------------------------------------------------------- /Procfile: -------------------------------------------------------------------------------- 1 | release: rake db:migrate 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rootstrap Pull request to Slack 2 | 3 | [![Build Status](https://travis-ci.org/rootstrap/pull_requests_to_slack.svg?branch=master)](https://travis-ci.org/rootstrap/pull_requests_to_slack) 4 | [![Maintainability](https://api.codeclimate.com/v1/badges/9e5ed337285c4b6f7882/maintainability)](https://codeclimate.com/github/rootstrap/pull_requests_to_slack/maintainability) 5 | [![Test Coverage](https://api.codeclimate.com/v1/badges/9e5ed337285c4b6f7882/test_coverage)](https://codeclimate.com/github/rootstrap/pull_requests_to_slack/test_coverage) 6 | 7 | Send Github pull request notifications to Slack. 8 | 9 | - Each time a PR is created in your organization it will send a message to a specific Slack channel with a link to the PR and its technology emoji assigned. 10 | - When the PR is merged it will add a merged reaction emoji. 11 | Screen Shot 2019-08-02 at 11 06 30 AM 12 | 13 | 14 | - You can add ``` \slack `This is a small pr @slack_user` ``` at the end of the PR's description to add a message to the notification and to notify specific Slack users. 15 | Screen Shot 2019-08-02 at 11 11 30 AM 16 | 17 | Make sure to use ``` ` ` ``` in the message in case the Slack user name is the same as someone's GitHub name, so the github user is not notified. 18 | Screen Shot 2019-08-02 at 11 54 58 AM 19 | 20 | 21 | - It will not send a notification if the PR is a draft. 22 | - It will remove the notification if the PR has an `ON HOLD` label and resend the notification when the label is removed. 23 | 24 | 25 | ## Installation 26 | 27 | 1. Clone this repo 28 | 2. Install PostgreSQL in case you don't have it 29 | 3. Create your `database.yml` and `application.yml` files. There are sample files in `/config` 30 | 4. `bundle install` 31 | 5. Generate a secret key with `rake secret` and paste this value into the `application.yml`. 32 | 6. Fill the `SLACK_API_TOKEN` and `SLACK_BOT_TOKEN` in `application.yml`. 33 | To get the credentials: log in to https://api.slack.com/apps, select your application and then click OAuth Tokens & Redirect URLs. `SLACK_API_TOKEN` is the `OAuth Access Token` and `SLACK_BOT_TOKEN` is `Bot User OAuth Access Token` 34 | 6. `rails db:create` 35 | 7. `rails db:migrate` 36 | 8. `rails db:seed` # this will create an admin with admin@example.com:password 37 | 9. `npm install -g ngrok` Install Ngrok 38 | 10. `rspec` and make sure all tests pass 39 | 11. `rails s` 40 | 12. You are ready! 41 | 42 | ## How to test the webhook locally? 43 | - Create a dummy repository in github with a couple branches. 44 | - Run server: `rails s -p 3001` 45 | - In another terminal run ngrok: `ngrok http 3001` 46 | - Copy ngrok url to github configuration page (settings->webhooks) 47 | `http://xxxxxxx.ngrok.io/api/v1/notifications_filter` 48 | - Change CHANNEL in SlackNotificationService to your `@name` or `#some_test_channel` 49 | - Create/edit pull request adding or removing labels. This will execute the webhook. 50 | 51 | ## ActiveAdmin page 52 | You can access the admin page at `http://localhost:3001/admin/users` and add users that you want to ignore 53 | 54 | ## Deploy to Heroku 55 | Install heroku cli https://devcenter.heroku.com/articles/heroku-cli#download-and-install 56 | * Setup: 57 | ``` 58 | heroku login 59 | enter credentials 60 | heroku git:remote -a rootstrap-pull-request-to-slack 61 | ``` 62 | 63 | * Push: 64 | ``` 65 | git push heroku master 66 | ``` 67 | 68 | ## Docs 69 | 70 | #### Ngrok 71 | Public URLs for exposing your local web server 72 | https://ngrok.com/ 73 | 74 | #### Github Hooks 75 | Info about github hooks and Pull request payload 76 | 77 | https://developer.github.com/webhooks/configuring/ 78 | https://developer.github.com/v3/activity/events/types/#pullrequestevent 79 | 80 | #### Slack methods 81 | https://api.slack.com/methods 82 | 83 | ## Contributing 84 | Bug reports (please use Issues) and pull requests are welcome on GitHub at https://github.com/rootstrap/pull_requests_to_slack/issues. 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. 85 | 86 | ## License 87 | The library is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 88 | 89 | ## Credits 90 | Github for Slack is maintained by [Rootstrap](http://www.rootstrap.com) with the help of our [contributors](https://github.com/rootstrap/pull_requests_to_slack/contributors). 91 | 92 | [](http://www.rootstrap.com) 93 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /apiary.apib: -------------------------------------------------------------------------------- 1 | FORMAT: 1A 2 | HOST: http://rails5-api-base.herokuapp.com 3 | 4 | # API BASE 5 | 6 | Rails Api Base is a boilerplate project for JSON RESTful APIs. It follows the community best practices in terms of standards, security and maintainability, integrating a variety of testing and code quality tools. 7 | 8 | ## Users Collection [/api/v1/users] 9 | 10 | ### Sign Up [POST] 11 | 12 | + Request (application/json) 13 | + Attributes 14 | + user (Profile Edition, required) 15 | 16 | 17 | + Response 200 (application/json) 18 | + Headers 19 | 20 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 21 | client: QADgNCWRJj0LyRruqzYbBg 22 | expiry: 1489009792 23 | uid: test@test.com 24 | 25 | 26 | + Attributes 27 | + user (Profile Response, required) 28 | 29 | + Response 401 30 | 31 | 32 | ## Current user's profile [/api/v1/user/profile] 33 | 34 | ### Get current user profile [GET] 35 | 36 | + Request (application/json) 37 | + Headers 38 | 39 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 40 | client: QADgNCWRJj0LyRruqzYbBg 41 | uid: test@test.com 42 | 43 | 44 | + Response 200 (application/json) 45 | + Headers 46 | 47 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 48 | client: QADgNCWRJj0LyRruqzYbBg 49 | expiry: 1489009792 50 | uid: test@test.com 51 | 52 | 53 | + Attributes 54 | + user (Profile Response, required) 55 | 56 | 57 | + Response 401 58 | 59 | 60 | ### Update current user profile [PUT] 61 | 62 | + Request (application/json) 63 | + Headers 64 | 65 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 66 | client: QADgNCWRJj0LyRruqzYbBg 67 | uid: test@test.com 68 | 69 | + Attributes 70 | + user (Profile Edition, required) 71 | 72 | + Response 200 (application/json) 73 | + Headers 74 | 75 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 76 | client: QADgNCWRJj0LyRruqzYbBg 77 | expiry: 1489009792 78 | uid: test@test.com 79 | 80 | 81 | + Attributes 82 | + user (Profile Response, required) 83 | 84 | ## Get other user's profile [/api/v1/users/{id}] 85 | 86 | ### Get user [GET] 87 | 88 | + Request (application/json) 89 | + Parameters 90 | + id (integer, required) 91 | 92 | + Headers 93 | 94 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 95 | client: QADgNCWRJj0LyRruqzYbBg 96 | uid: test@test.com 97 | 98 | 99 | 100 | + Response 200 (application/json) 101 | + Headers 102 | 103 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 104 | client: QADgNCWRJj0LyRruqzYbBg 105 | expiry: 1489009792 106 | uid: test@test.com 107 | 108 | 109 | + Attributes 110 | + user (User, required) 111 | 112 | + Response 401 113 | 114 | 115 | ## Login [/api/v1/users/sign_in] 116 | 117 | ### Login [POST] 118 | 119 | + Request (application/json) 120 | 121 | + Body 122 | 123 | { 124 | "user": 125 | { 126 | "email": "test@gmail.com", 127 | "password": "password" 128 | } 129 | } 130 | 131 | 132 | + Response 200 (application/json) 133 | + Headers 134 | 135 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 136 | client: QADgNCWRJj0LyRruqzYbBg 137 | expiry: 1489009792 138 | uid: test@test.com 139 | 140 | 141 | + Attributes 142 | + user (Profile Response, required) 143 | 144 | + Response 401 145 | 146 | 147 | ## Login with Facebook [/api/v1/user/facebook] 148 | 149 | ### Login with Facebook [POST] 150 | 151 | 152 | + Response 200 (application/json) 153 | + Headers 154 | 155 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 156 | client: QADgNCWRJj0LyRruqzYbBg 157 | uid: test@test.com 158 | 159 | 160 | + Attributes 161 | + user (Profile Response, required) 162 | 163 | 164 | ## Logout [/api/v1/users/sign_out] 165 | 166 | ### Logout [DELETE] 167 | 168 | + Request (application/json) 169 | + Headers 170 | 171 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 172 | client: QADgNCWRJj0LyRruqzYbBg 173 | expiry: 1489009792 174 | uid: test@test.com 175 | 176 | + Response 200 (application/json) 177 | 178 | 179 | ## Reset password [/api/v1/users/password] 180 | 181 | More information of how reset password works: https://github.com/lynndylanhurley/devise_token_auth/wiki/Reset-Password-Flow 182 | 183 | ### Reset password [POST] 184 | 185 | Use this route to send a password reset confirmation email 186 | 187 | + Request (application/json) 188 | + Body 189 | 190 | { 191 | "email": "test@test.com", 192 | "redirect_url": "http://www.example.com" 193 | } 194 | 195 | + Response 200 (application/json) 196 | + Body 197 | 198 | { 199 | "success": true, 200 | "message": "An email has been sent to 'example@mail.com' containing instructions for resetting your password." 201 | } 202 | 203 | ### Reset password [PUT] 204 | 205 | Use this route to change user's passwords 206 | 207 | + Request (application/json) 208 | + Parameters 209 | + reset_password_token (string, required) 210 | 211 | + Body 212 | 213 | { 214 | "password": "12345678", 215 | "password_confirmation": "12345678" 216 | } 217 | 218 | + Response 200 (application/json) 219 | + Headers 220 | 221 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 222 | client: QADgNCWRJj0LyRruqzYbBg 223 | expiry: 1489009792 224 | uid: test@test.com 225 | 226 | + Attributes (Profile Response) 227 | 228 | ## Edit reset password [/api/v1/users/password/edit] 229 | 230 | ### Edit reset password [GET] 231 | 232 | This route is the destination URL for password reset confirmation 233 | 234 | + Request (application/json) 235 | + Parameters 236 | + reset_password_token (string, required) 237 | + redirect_url (string) 238 | 239 | + Response 200 (application/json) 240 | + Headers 241 | 242 | access-token: sO2bm_Bpdyoo8r78jZ-fqg 243 | client: QADgNCWRJj0LyRruqzYbBg 244 | expiry: 1489009792 245 | uid: test@test.com 246 | 247 | # Data Structures 248 | 249 | ## Profile Response (object) 250 | + id: 100 (number) 251 | + email: test@test.com (string) 252 | + provider: email, facebook, twitter (enum[string]) - it's way the user logins 253 | + uid: test@test.com (string) - the provider identifier 254 | + first_name: John (string) 255 | + last_name: Doe (string) 256 | + username: jdoe (string) 257 | + created_at: 2017-02-23T13:54:33.283Z (string) 258 | 259 | ## Profile Edition (object) 260 | + email: test@test.com (string) 261 | + password: 12345678 (string) 262 | + first_name: John (string) 263 | + last_name: Doe (string) 264 | + username: jdoe (string) 265 | 266 | ## User (object) 267 | + email: test@test.com (string) 268 | + first_name: John (string) 269 | + last_name: Doe (string) 270 | + username: jdoe (string) 271 | -------------------------------------------------------------------------------- /app/admin/admin_user.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register AdminUser do 2 | permit_params :email, :password, :password_confirmation 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | column :email 8 | column :current_sign_in_at 9 | column :sign_in_count 10 | column :created_at 11 | actions 12 | end 13 | 14 | filter :email 15 | filter :current_sign_in_at 16 | filter :sign_in_count 17 | filter :created_at 18 | 19 | form do |f| 20 | f.inputs 'Admin Details' do 21 | f.input :email 22 | f.input :password 23 | f.input :password_confirmation 24 | end 25 | f.actions 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /app/admin/dashboard.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register_page 'Dashboard' do 2 | menu priority: 1, label: proc { I18n.t('active_admin.dashboard') } 3 | 4 | content title: proc { I18n.t('active_admin.dashboard') } do 5 | div class: 'blank_slate_container', id: 'dashboard_default_message' do 6 | span class: 'blank_slate' do 7 | span I18n.t('active_admin.dashboard_welcome.welcome') 8 | small I18n.t('active_admin.dashboard_welcome.call_to_action') 9 | end 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /app/admin/user.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register User do 2 | permit_params :email, :first_name, :last_name, :github_name, :blacklisted 3 | 4 | form do |f| 5 | f.inputs 'Details' do 6 | f.input :email 7 | f.input :first_name 8 | f.input :last_name 9 | f.input :github_name 10 | f.input :blacklisted 11 | end 12 | 13 | actions 14 | end 15 | 16 | index do 17 | selectable_column 18 | id_column 19 | column :email 20 | column :first_name 21 | column :last_name 22 | column :github_name 23 | column :blacklisted 24 | column :created_at 25 | column :updated_at 26 | actions 27 | end 28 | 29 | filter :id 30 | filter :email 31 | filter :github_name 32 | filter :first_name 33 | filter :last_name 34 | filter :blacklisted 35 | filter :created_at 36 | filter :updated_at 37 | 38 | show do 39 | attributes_table do 40 | row :id 41 | row :email 42 | row :first_name 43 | row :last_name 44 | row :github_name 45 | row :blacklisted 46 | row :created_at 47 | row :updated_at 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | -------------------------------------------------------------------------------- /app/assets/javascripts/active_admin.js: -------------------------------------------------------------------------------- 1 | #= require active_admin/base 2 | -------------------------------------------------------------------------------- /app/assets/javascripts/application.js: -------------------------------------------------------------------------------- 1 | // This is a manifest file that'll be compiled into application.js, which will include all the files 2 | // listed below. 3 | // 4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts, 5 | // or vendor/assets/javascripts of plugins, if any, can be referenced here using a relative path. 6 | // 7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the 8 | // compiled file. 9 | // 10 | // Read Sprockets README (https://github.com/sstephenson/sprockets#sprockets-directives) for details 11 | // about supported directives. 12 | // 13 | //= require jquery 14 | //= require jquery_ujs 15 | //= require_tree . 16 | -------------------------------------------------------------------------------- /app/assets/stylesheets/active_admin.scss: -------------------------------------------------------------------------------- 1 | // SASS variable overrides must be declared before loading up Active Admin's styles. 2 | // 3 | // To view the variables that Active Admin provides, take a look at 4 | // `app/assets/stylesheets/active_admin/mixins/_variables.scss` in the 5 | // Active Admin source. 6 | // 7 | // For example, to change the sidebar width: 8 | // $sidebar-width: 242px; 9 | 10 | // Active Admin's got SASS! 11 | @import "active_admin/mixins"; 12 | @import "active_admin/base"; 13 | 14 | // Overriding any non-variable SASS must be done after the fact. 15 | // For example, to change the default status-tag color: 16 | // 17 | // .status_tag { background: #6090DB; } 18 | -------------------------------------------------------------------------------- /app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the top of the 9 | * compiled file, but it's generally better to create a new file per style scope. 10 | * 11 | *= require_self 12 | *= require_tree . 13 | */ 14 | -------------------------------------------------------------------------------- /app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/controllers/api/concerns/act_as_api_request.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module Concerns 3 | module ActAsApiRequest 4 | extend ActiveSupport::Concern 5 | 6 | included do 7 | skip_before_action :verify_authenticity_token 8 | before_action :skip_session_storage 9 | before_action :check_json_request 10 | end 11 | 12 | def check_json_request 13 | return if request.content_type =~ /json/ 14 | 15 | render json: { error: I18n.t('api.errors.invalid_content_type') }, status: :not_acceptable 16 | end 17 | 18 | def skip_session_storage 19 | # Devise stores the cookie by default, so in api requests, it is disabled 20 | # http://stackoverflow.com/a/12205114/2394842 21 | request.session_options[:skip] = true 22 | end 23 | 24 | def render_error(status, message, _data = nil) 25 | response = { 26 | error: message 27 | } 28 | render json: response, status: status 29 | end 30 | end 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/controllers/api/v1/api_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ApiController < ApplicationController 4 | include Concerns::ActAsApiRequest 5 | 6 | before_action :authenticate_user!, except: :status 7 | 8 | layout false 9 | respond_to :json 10 | 11 | rescue_from Exception, with: :render_error 12 | rescue_from ActiveRecord::RecordNotFound, with: :render_not_found 13 | rescue_from ActiveRecord::RecordInvalid, with: :render_record_invalid 14 | rescue_from ActionController::RoutingError, with: :render_not_found 15 | rescue_from AbstractController::ActionNotFound, with: :render_not_found 16 | rescue_from ActionController::ParameterMissing, with: :render_parameter_missing 17 | 18 | def status 19 | render json: { online: true } 20 | end 21 | 22 | private 23 | 24 | def render_error(exception) 25 | logger.error(exception) # Report to your error managment tool here 26 | render json: { error: I18n.t('api.errors.server') }, status: 500 unless performed? 27 | end 28 | 29 | def render_not_found(exception) 30 | logger.info(exception) # for logging 31 | render json: { error: I18n.t('api.errors.not_found') }, status: :not_found 32 | end 33 | 34 | def render_record_invalid(exception) 35 | logger.info(exception) # for logging 36 | render json: { errors: exception.record.errors.as_json }, status: :bad_request 37 | end 38 | 39 | def render_parameter_missing(exception) 40 | logger.info(exception) # for logging 41 | render json: { error: I18n.t('api.errors.missing_param') }, status: :unprocessable_entity 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/controllers/api/v1/github_webhook_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class GithubWebhookController < Api::V1::ApiController 4 | protect_from_forgery with: :null_session 5 | include Concerns::ActAsApiRequest 6 | skip_before_action :authenticate_user! 7 | 8 | def filter 9 | slack_notification_service = SlackNotificationService.new(params) 10 | slack_notification_service.send_notification 11 | head :no_content 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::Base 2 | # Prevent CSRF attacks by raising an exception. 3 | # For APIs, you may want to use :null_session instead. 4 | protect_from_forgery with: :exception 5 | end 6 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/pull_requests_to_slack/67ee96a3d3f4a45a0ec977c4de3855355ff0579b/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/models/admin_user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: admin_users 4 | # 5 | # id :integer not null, primary key 6 | # email :string default(""), not null 7 | # encrypted_password :string default(""), not null 8 | # reset_password_token :string 9 | # reset_password_sent_at :datetime 10 | # remember_created_at :datetime 11 | # sign_in_count :integer default(0), not null 12 | # current_sign_in_at :datetime 13 | # last_sign_in_at :datetime 14 | # current_sign_in_ip :inet 15 | # last_sign_in_ip :inet 16 | # created_at :datetime not null 17 | # updated_at :datetime not null 18 | # 19 | # Indexes 20 | # 21 | # index_admin_users_on_email (email) UNIQUE 22 | # index_admin_users_on_reset_password_token (reset_password_token) UNIQUE 23 | # 24 | 25 | class AdminUser < ApplicationRecord 26 | # Include default devise modules. Others available are: 27 | # :confirmable, :lockable, :timeoutable and :omniauthable 28 | devise :database_authenticatable, 29 | :recoverable, :rememberable, :trackable, :validatable 30 | end 31 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/pull_requests_to_slack/67ee96a3d3f4a45a0ec977c4de3855355ff0579b/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/user.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string 7 | # first_name :string default("") 8 | # last_name :string default("") 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # github_name :string 12 | # slack_name :string 13 | # blacklisted :boolean default(FALSE) 14 | # 15 | # Indexes 16 | # 17 | # index_users_on_github_name (github_name) 18 | # 19 | 20 | class User < ApplicationRecord 21 | validates :github_name, uniqueness: true 22 | 23 | def full_name 24 | return github_name unless first_name.present? 25 | 26 | "#{first_name} #{last_name}" 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/services/pull_request.rb: -------------------------------------------------------------------------------- 1 | class PullRequest 2 | ON_HOLD = 'ON HOLD'.freeze 3 | attr_reader :info, :username, :avatar_url, :webhook_label, 4 | :url, :body, :language, :repo_name 5 | 6 | def initialize(params) 7 | @info = params.fetch(:pull_request, {}) 8 | @username = @info.dig(:user, :login) 9 | @avatar_url = @info.dig(:user, :avatar_url) 10 | @webhook_label = params.dig(:github_webhook, :label, :name) 11 | @url = @info[:html_url] 12 | @body = @info[:body] 13 | @language = params.dig(:repository, :language) 14 | @repo_name = params.dig(:repository, :name)&.downcase 15 | end 16 | 17 | def on_hold_webhook? 18 | on_hold_label? @webhook_label 19 | end 20 | 21 | def on_hold? 22 | @info[:labels]&.any? { |label| on_hold_label? label[:name] } || false 23 | end 24 | 25 | def draft? 26 | @info[:draft] == true 27 | end 28 | 29 | def merged? 30 | @info[:merged] == true 31 | end 32 | 33 | def on_hold_label?(label) 34 | label.downcase == ON_HOLD.downcase 35 | end 36 | 37 | def blacklisted? 38 | User.where(github_name: username, blacklisted: true).exists? 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/services/slack_bot.rb: -------------------------------------------------------------------------------- 1 | class SlackBot 2 | ON_HOLD = 'ON HOLD'.freeze 3 | 4 | EMOJI_HASH = { 'JavaScript' => ':javascript:', 5 | 'TypeScript' => ':ts:', 6 | 'Ruby' => ':ruby:', 7 | 'Java' => ':java:', 8 | 'Kotlin' => ':kotlin:', 9 | 'Swift' => ':swift:', 10 | 'React' => ':react:', 11 | 'React-Native' => ':react_native:', 12 | 'Angular' => ':angular:', 13 | 'Vue' => ':vue:', 14 | 'Node' => ':nodejs:', 15 | 'Python' => ':python:', 16 | 'HTML' => ':html5:', 17 | 'Elixir' => ':elixir:', 18 | 'R' => ':r-lang:', 19 | 'PHP' => ':php:' }.freeze 20 | 21 | attr_reader :channel 22 | 23 | def initialize(channel:) 24 | raise ArgumentError.new('Invalid channel') unless channel.present? 25 | 26 | @client = Slack::Web::Client.new 27 | @channel = channel 28 | end 29 | 30 | def notify(message, username, avatar_url) 31 | @client.chat_postMessage(channel: @channel, text: message, 32 | username: username, icon_url: avatar_url) 33 | end 34 | 35 | def add_merge_emoji(matches) 36 | matches.each do |match| 37 | timestamp = match[:ts] 38 | begin 39 | add_emoji :merged, timestamp 40 | rescue StandardError => e 41 | puts "An error of type #{e.class} happened, message is #{e.message}." 42 | end 43 | end 44 | end 45 | 46 | def add_emoji(emoji, timestamp) 47 | @client.reactions_add(name: emoji, 48 | channel: @channel, 49 | timestamp: timestamp, 50 | as_user: false) 51 | end 52 | 53 | def find_message(text) 54 | client_find = Slack::Web::Client.new(token: ENV['SLACK_API_TOKEN']) 55 | response = client_find.search_messages(channel: @channel, query: "#{text} in:#{@channel}") 56 | response.dig(:messages, :matches) 57 | end 58 | 59 | def delete_message(matches) 60 | matches.each do |match| 61 | timestamp = match[:ts] 62 | begin 63 | @client.chat_delete(channel: @channel, ts: timestamp) if timestamp 64 | rescue StandardError => e 65 | puts "An error of type #{e.class} happened, message is #{e.message}." 66 | end 67 | end 68 | end 69 | 70 | def message(pull_req) 71 | slack_body = extract_slack_body(pull_req.body) 72 | "#{pull_req.url} #{slack_body} #{language_emoji(pull_req.language, pull_req.repo_name)}" 73 | end 74 | 75 | def extract_slack_body(body) 76 | body_gsub = body&.gsub("\r\n", ' ')&.split('\slack ') 77 | body = body_gsub[1].present? ? body_gsub[1] : '' 78 | format_body body 79 | end 80 | 81 | # To show the notification @test it should be formatted like <@test> 82 | # https://api.slack.com/docs/message-formatting 83 | def format_body(body) 84 | body.gsub(/([@#][A-Za-z0-9_]+)/, '<\\1>') 85 | end 86 | 87 | def language_emoji(language, repo_name) 88 | if repo_name =~ /reactnative|react-native|-rn$/ 89 | language = 'React-Native' 90 | elsif repo_name.include? 'react' 91 | language = 'React' 92 | elsif repo_name.include? 'angular' 93 | language = 'Angular' 94 | elsif repo_name.include? 'node' 95 | language = 'Node' 96 | end 97 | 98 | EMOJI_HASH.fetch language, ":#{language&.downcase}:" 99 | end 100 | end 101 | -------------------------------------------------------------------------------- /app/services/slack_notification_service.rb: -------------------------------------------------------------------------------- 1 | class SlackNotificationService 2 | ACTION_METHODS = { 'opened' => :filter_opened_action, 3 | 'ready_for_review' => :filter_ready_for_review_action, 4 | 'unlabeled' => :filter_unlabeled_action, 5 | 'labeled' => :filter_on_hold_labeled_action, 6 | 'closed' => :filter_closed_action }.freeze 7 | 8 | DEFAULT_CHANNEL = '#code-review'.freeze 9 | 10 | LANGUAGES = { 11 | 'Ruby': 'ruby', 12 | 'Node': 'node', 13 | 'Python': 'python', 14 | 'React': 'react', 15 | 'React-Native': 'react-native', 16 | 'Dart': 'flutter', 17 | 'Objective-C': 'ios', 18 | 'Kotlin': 'android', 19 | 'Swift': 'ios', 20 | 'TypeScript': 'typescript', 21 | 'JavaScript': 'javascript' 22 | }.freeze 23 | 24 | attr_reader :action, :extra_params, :slack_bot, :pr 25 | 26 | def initialize(params) 27 | @action = params.dig(:github_webhook, :action) 28 | if @action.blank? 29 | Rails.logger.warn("Warning! No action found on params: [#{params}]") 30 | end 31 | 32 | @extra_params = params || {} 33 | @slack_bot = SlackBot.new(channel: channel(params)) 34 | @pr = PullRequest.new(params) 35 | end 36 | 37 | def send_notification 38 | return if pr.blacklisted? 39 | 40 | send(ACTION_METHODS[action]) if ACTION_METHODS.key? action 41 | end 42 | 43 | private 44 | 45 | def notify_pull_request 46 | message = slack_bot.message(pr) 47 | slack_bot.notify(message, pr.username, pr.avatar_url) 48 | end 49 | 50 | def filter_unlabeled_action 51 | notify_pull_request if pr.on_hold_webhook? 52 | end 53 | 54 | def filter_on_hold_labeled_action 55 | return unless pr.on_hold_webhook? 56 | 57 | matches = slack_bot.find_message pr.url 58 | slack_bot.delete_message matches 59 | end 60 | 61 | def channel(params) 62 | lang = LANGUAGES[params.dig(:repository, :language)&.to_sym] 63 | repository_info = params[:repository] 64 | 65 | channel = build_channel(lang, repository_info) 66 | channel_name = "##{channel}" 67 | 68 | if channel.present? && search_channel(channel_name) 69 | channel_name 70 | else 71 | DEFAULT_CHANNEL 72 | end 73 | end 74 | 75 | def search_channel(channel) 76 | Slack::Web::Client.new.conversations_info(channel: channel) 77 | rescue StandardError => e 78 | Rails.logger.error("Error #{e.inspect} for channel #{channel}") 79 | nil 80 | end 81 | 82 | def build_channel(lang, repository_info) 83 | return 'devops-code-review' if devops_pr?(repository_info) 84 | 85 | return build_lang_channel(lang, repository_info) if lang 86 | end 87 | 88 | def build_lang_channel(lang, repository_info) 89 | return js_channels(repository_info, lang) if %w[javascript typescript].include?(lang) 90 | 91 | "#{lang}-code-review" 92 | end 93 | 94 | def devops_pr?(repository_info) 95 | return false unless repository_info 96 | 97 | repo_name = repository_info[:name] 98 | repo_topics = repository_info[:topics] 99 | 100 | (repo_name&.include? 'devops') || 101 | (repo_name&.include? 'infra') || 102 | (repo_topics&.include? 'terraform') 103 | end 104 | 105 | def filter_opened_action 106 | notify_pull_request unless pr.on_hold? || pr.draft? 107 | end 108 | 109 | def filter_ready_for_review_action 110 | notify_pull_request unless pr.on_hold? 111 | end 112 | 113 | def filter_closed_action 114 | filter_merged_action if pr.merged? 115 | end 116 | 117 | def filter_merged_action 118 | matches = slack_bot.find_message pr.url 119 | slack_bot.add_merge_emoji matches 120 | end 121 | 122 | def js_channels(pull_request, _lang) 123 | return unless pull_request[:name] 124 | 125 | repo_name = pull_request[:name].downcase 126 | topics = pull_request[:topics].present? ? pull_request[:topics] : [] 127 | 128 | js_repo_name(repo_name, topics) 129 | end 130 | 131 | # rubocop:disable Metrics/CyclomaticComplexity 132 | def js_repo_name(repo_name, topics) 133 | if ((repo_name.include? 'react') && 134 | (repo_name.include? 'native')) || 135 | (topics.include? 'react-native') 136 | "#{LANGUAGES[:'React-Native']}-code-review" 137 | elsif (repo_name.include? 'react') || (topics.include? 'react') 138 | "#{LANGUAGES[:React]}-code-review" 139 | elsif (repo_name.include? 'node') || (topics.include? 'node') 140 | "#{LANGUAGES[:Node]}-code-review" 141 | end 142 | end 143 | # rubocop:enable Metrics/CyclomaticComplexity 144 | end 145 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.html.haml: -------------------------------------------------------------------------------- 1 | !!! 2 | %html 3 | %head 4 | %meta{:content => "text/html; charset=utf-8", "http-equiv" => "Content-Type"}/ 5 | :css 6 | /* Email styles need to be inline */ 7 | %body 8 | = yield 9 | -------------------------------------------------------------------------------- /app/views/layouts/mailer.text.haml: -------------------------------------------------------------------------------- 1 | = yield 2 | -------------------------------------------------------------------------------- /bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /bin/delayed_job: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require File.expand_path(File.join(File.dirname(__FILE__), '..', 'config', 'environment')) 4 | require 'delayed/command' 5 | Delayed::Command.new(ARGV).daemonize 6 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | if spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 13 | gem 'spring', spring.version 14 | require 'spring/binstub' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails' 4 | # Pick the frameworks you want: 5 | require 'active_model/railtie' 6 | require 'active_job/railtie' 7 | require 'active_record/railtie' 8 | require 'action_controller/railtie' 9 | require 'action_mailer/railtie' 10 | require 'action_view/railtie' 11 | require 'action_cable/engine' 12 | require 'sprockets/railtie' 13 | require 'rails/test_unit/railtie' 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | require_relative 'env_variables' 20 | 21 | module App 22 | class Application < Rails::Application 23 | # Settings in config/environments/* take precedence over those specified here. 24 | # Application configuration should go into files in config/initializers 25 | # -- all .rb files in that directory are automatically loaded. 26 | 27 | config.secret_key_base = ENV['SECRET_KEY_BASE'] 28 | 29 | config.autoload_paths += %W[#{config.root}/lib] 30 | 31 | ActionMailer::Base.smtp_settings = { 32 | address: 'smtp.sendgrid.net', 33 | port: 25, 34 | domain: 'www.api.com', 35 | authentication: :plain, 36 | user_name: ENV['SENDGRID_USERNAME'], 37 | password: ENV['SENDGRID_PASSWORD'] 38 | } 39 | config.action_mailer.default_url_options = { host: ENV['SERVER_URL'] } 40 | config.action_mailer.default_options = { 41 | from: 'no-reply@api.com' 42 | } 43 | end 44 | end 45 | -------------------------------------------------------------------------------- /config/application.travis.yml: -------------------------------------------------------------------------------- 1 | SECRET_KEY_BASE: <%= ENV["SECRET_KEY_BASE"] %> 2 | -------------------------------------------------------------------------------- /config/application.yml.example: -------------------------------------------------------------------------------- 1 | SECRET_KEY_BASE: 2 | SERVER_URL: 'localhost:3000' 3 | PASSWORD_RESET_URL: 'http://127.0.0.1:3000' 4 | SENDGRID_USERNAME: 5 | SENDGRID_PASSWORD: 6 | AWS_ACCESS_KEY_ID: 7 | AWS_SECRET_ACCESS_KEY: 8 | S3_BUCKET_NAME: 9 | AWS_BUCKET_REGION: 10 | SLACK_API_TOKEN: 11 | SLACK_BOT_TOKEN: 12 | -------------------------------------------------------------------------------- /config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: async 6 | 7 | production: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | -------------------------------------------------------------------------------- /config/database.travis.yml: -------------------------------------------------------------------------------- 1 | test: 2 | adapter: postgresql 3 | database: pull_request_slack_test 4 | username: postgres 5 | -------------------------------------------------------------------------------- /config/database.yml.example: -------------------------------------------------------------------------------- 1 | default: &default 2 | adapter: postgresql 3 | encoding: unicode 4 | pool: 5 5 | username: 6 | password: 7 | host: localhost 8 | port: 5432 9 | 10 | development: 11 | <<: *default 12 | database: 13 | 14 | test: 15 | <<: *default 16 | database: 17 | 18 | production: 19 | <<: *default 20 | database: 21 | -------------------------------------------------------------------------------- /config/env_variables.rb: -------------------------------------------------------------------------------- 1 | begin 2 | ENV.update YAML.safe_load(File.read('config/application.yml')) 3 | rescue Errno::ENOENT 4 | puts 'You have to add a correct application.yml file' 5 | end 6 | -------------------------------------------------------------------------------- /config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Don't care if the mailer can't send. 30 | config.action_mailer.raise_delivery_errors = true 31 | config.action_mailer.delivery_method = :letter_opener 32 | 33 | config.action_mailer.perform_caching = false 34 | 35 | # Print deprecation notices to the Rails logger. 36 | config.active_support.deprecation = :log 37 | 38 | # Raise an error on page load if there are pending migrations. 39 | config.active_record.migration_error = :page_load 40 | 41 | # Raises error for missing translations 42 | # config.action_view.raise_on_missing_translations = true 43 | 44 | # Use an evented file watcher to asynchronously detect changes in source code, 45 | # routes, locales, etc. This feature depends on the listen gem. 46 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 47 | 48 | config.after_initialize do 49 | Bullet.enable = true 50 | Bullet.alert = false 51 | Bullet.bullet_logger = true 52 | Bullet.console = true 53 | Bullet.rails_logger = true 54 | Bullet.add_footer = true 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /config/environments/production.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # Code is not reloaded between requests. 5 | config.cache_classes = true 6 | 7 | # Eager load code on boot. This eager loads most of Rails and 8 | # your application in memory, allowing both threaded web servers 9 | # and those relying on copy on write to perform better. 10 | # Rake tasks automatically ignore this option for performance. 11 | config.eager_load = true 12 | 13 | # Full error reports are disabled and caching is turned on. 14 | config.consider_all_requests_local = false 15 | config.action_controller.perform_caching = true 16 | 17 | # Disable serving static files from the `/public` folder by default since 18 | # Apache or NGINX already handles this. 19 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present? 20 | 21 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 22 | # config.action_controller.asset_host = 'http://assets.example.com' 23 | config.assets.js_compressor = :uglifier 24 | config.assets.compile = true 25 | config.assets.digest = true 26 | 27 | # Version of your assets, change this if you want to expire all your assets. 28 | config.assets.version = '1.0' 29 | 30 | # Specifies the header that your server uses for sending files. 31 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache 32 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX 33 | 34 | # Mount Action Cable outside main process or domain 35 | # config.action_cable.mount_path = nil 36 | # config.action_cable.url = 'wss://example.com/cable' 37 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ] 38 | 39 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 40 | # config.force_ssl = true 41 | 42 | # Use the lowest log level to ensure availability of diagnostic information 43 | # when problems arise. 44 | config.log_level = :info 45 | 46 | # Prepend all log lines with the following tags. 47 | config.log_tags = [:request_id] 48 | 49 | # Use a different cache store in production. 50 | # config.cache_store = :mem_cache_store 51 | 52 | # Use a real queuing backend for Active Job (and separate queues per environment) 53 | # config.active_job.queue_adapter = :resque 54 | # config.active_job.queue_name_prefix = "rails5_api_base_#{Rails.env}" 55 | config.action_mailer.perform_caching = false 56 | 57 | # Ignore bad email addresses and do not raise email delivery errors. 58 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 59 | # config.action_mailer.raise_delivery_errors = false 60 | 61 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 62 | # the I18n.default_locale when a translation cannot be found). 63 | config.i18n.fallbacks = true 64 | 65 | # Send deprecation notices to registered listeners. 66 | config.active_support.deprecation = :notify 67 | 68 | # Use default logging formatter so that PID and timestamp are not suppressed. 69 | config.log_formatter = ::Logger::Formatter.new 70 | 71 | # Use a different logger for distributed setups. 72 | # require 'syslog/logger' 73 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name') 74 | 75 | if ENV['RAILS_LOG_TO_STDOUT'].present? 76 | logger = ActiveSupport::Logger.new(STDOUT) 77 | logger.formatter = config.log_formatter 78 | config.logger = ActiveSupport::TaggedLogging.new(logger) 79 | end 80 | 81 | # Do not dump schema after migrations. 82 | config.active_record.dump_schema_after_migration = false 83 | 84 | config.assets.precompile += %w[ 85 | active_admin.js 86 | active_admin.css 87 | ] 88 | end 89 | -------------------------------------------------------------------------------- /config/environments/staging.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path('production', __dir__) 2 | -------------------------------------------------------------------------------- /config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | config.action_mailer.perform_caching = false 31 | 32 | # Tell Action Mailer not to deliver emails to the real world. 33 | # The :test delivery method accumulates sent emails in the 34 | # ActionMailer::Base.deliveries array. 35 | config.action_mailer.delivery_method = :test 36 | 37 | # Print deprecation notices to the stderr. 38 | config.active_support.deprecation = :stderr 39 | 40 | # Raises error for missing translations 41 | # config.action_view.raise_on_missing_translations = true 42 | 43 | config.after_initialize do 44 | Bullet.enable = true 45 | Bullet.bullet_logger = true 46 | Bullet.raise = true # raise an error if n+1 query occurs 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /config/initializers/active_admin.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.setup do |config| 2 | # == Site Title 3 | # 4 | # Set the title that is displayed on the main layout 5 | # for each of the active admin pages. 6 | # 7 | config.site_title = 'App' 8 | 9 | # Set the link url for the title. For example, to take 10 | # users to your main site. Defaults to no link. 11 | # 12 | # config.site_title_link = "/" 13 | 14 | # Set an optional image to be displayed for the header 15 | # instead of a string (overrides :site_title) 16 | # 17 | # Note: Aim for an image that's 21px high so it fits in the header. 18 | # 19 | # config.site_title_image = "logo.png" 20 | 21 | # == Default Namespace 22 | # 23 | # Set the default namespace each administration resource 24 | # will be added to. 25 | # 26 | # eg: 27 | # config.default_namespace = :hello_world 28 | # 29 | # This will create resources in the HelloWorld module and 30 | # will namespace routes to /hello_world/* 31 | # 32 | # To set no namespace by default, use: 33 | # config.default_namespace = false 34 | # 35 | # Default: 36 | # config.default_namespace = :admin 37 | # 38 | # You can customize the settings for each namespace by using 39 | # a namespace block. For example, to change the site title 40 | # within a namespace: 41 | # 42 | # config.namespace :admin do |admin| 43 | # admin.site_title = "Custom Admin Title" 44 | # end 45 | # 46 | # This will ONLY change the title for the admin section. Other 47 | # namespaces will continue to use the main "site_title" configuration. 48 | 49 | # == User Authentication 50 | # 51 | # Active Admin will automatically call an authentication 52 | # method in a before filter of all controller actions to 53 | # ensure that there is a currently logged in admin user. 54 | # 55 | # This setting changes the method which Active Admin calls 56 | # within the application controller. 57 | config.authentication_method = :authenticate_admin_user! 58 | 59 | # == User Authorization 60 | # 61 | # Active Admin will automatically call an authorization 62 | # method in a before filter of all controller actions to 63 | # ensure that there is a user with proper rights. You can use 64 | # CanCanAdapter or make your own. Please refer to documentation. 65 | # config.authorization_adapter = ActiveAdmin::CanCanAdapter 66 | 67 | # In case you prefer Pundit over other solutions you can here pass 68 | # the name of default policy class. This policy will be used in every 69 | # case when Pundit is unable to find suitable policy. 70 | # config.pundit_default_policy = "MyDefaultPunditPolicy" 71 | 72 | # You can customize your CanCan Ability class name here. 73 | # config.cancan_ability_class = "Ability" 74 | 75 | # You can specify a method to be called on unauthorized access. 76 | # This is necessary in order to prevent a redirect loop which happens 77 | # because, by default, user gets redirected to Dashboard. If user 78 | # doesn't have access to Dashboard, he'll end up in a redirect loop. 79 | # Method provided here should be defined in application_controller.rb. 80 | # config.on_unauthorized_access = :access_denied 81 | 82 | # == Current User 83 | # 84 | # Active Admin will associate actions with the current 85 | # user performing them. 86 | # 87 | # This setting changes the method which Active Admin calls 88 | # (within the application controller) to return the currently logged in user. 89 | config.current_user_method = :current_admin_user 90 | 91 | # == Logging Out 92 | # 93 | # Active Admin displays a logout link on each screen. These 94 | # settings configure the location and method used for the link. 95 | # 96 | # This setting changes the path where the link points to. If it's 97 | # a string, the strings is used as the path. If it's a Symbol, we 98 | # will call the method to return the path. 99 | # 100 | # Default: 101 | config.logout_link_path = :destroy_admin_user_session_path 102 | 103 | # This setting changes the http method used when rendering the 104 | # link. For example :get, :delete, :put, etc.. 105 | # 106 | # Default: 107 | # config.logout_link_method = :get 108 | 109 | # == Root 110 | # 111 | # Set the action to call for the root path. You can set different 112 | # roots for each namespace. 113 | # 114 | # Default: 115 | # config.root_to = 'dashboard#index' 116 | 117 | # == Admin Comments 118 | # 119 | # This allows your users to comment on any resource registered with Active Admin. 120 | # 121 | # You can completely disable comments: 122 | config.comments = false 123 | # 124 | # You can change the name under which comments are registered: 125 | # config.comments_registration_name = 'AdminComment' 126 | # 127 | # You can change the order for the comments and you can change the column 128 | # to be used for ordering: 129 | # config.comments_order = 'created_at ASC' 130 | # 131 | # You can disable the menu item for the comments index page: 132 | # config.comments_menu = false 133 | # 134 | # You can customize the comment menu: 135 | # config.comments_menu = { parent: 'Admin', priority: 1 } 136 | 137 | # == Batch Actions 138 | # 139 | # Enable and disable Batch Actions 140 | # 141 | config.batch_actions = true 142 | 143 | # == Controller Filters 144 | # 145 | # You can add before, after and around filters to all of your 146 | # Active Admin resources and pages from here. 147 | # 148 | # config.before_action :do_something_awesome 149 | 150 | # == Localize Date/Time Format 151 | # 152 | # Set the localize format to display dates and times. 153 | # To understand how to localize your app with I18n, read more at 154 | # https://github.com/svenfuchs/i18n/blob/master/lib%2Fi18n%2Fbackend%2Fbase.rb#L52 155 | # 156 | config.localize_format = :long 157 | 158 | # == Setting a Favicon 159 | # 160 | # config.favicon = 'favicon.ico' 161 | 162 | # == Meta Tags 163 | # 164 | # Add additional meta tags to the head element of active admin pages. 165 | # 166 | # Add tags to all pages logged in users see: 167 | # config.meta_tags = { author: 'My Company' } 168 | 169 | # By default, sign up/sign in/recover password pages are excluded 170 | # from showing up in search engine results by adding a robots meta 171 | # tag. You can reset the hash of meta tags included in logged out 172 | # pages: 173 | # config.meta_tags_for_logged_out_pages = {} 174 | 175 | # == Removing Breadcrumbs 176 | # 177 | # Breadcrumbs are enabled by default. You can customize them for individual 178 | # resources or you can disable them globally from here. 179 | # 180 | # config.breadcrumb = false 181 | 182 | # == Register Stylesheets & Javascripts 183 | # 184 | # We recommend using the built in Active Admin layout and loading 185 | # up your own stylesheets / javascripts to customize the look 186 | # and feel. 187 | # 188 | # To load a stylesheet: 189 | # config.register_stylesheet 'my_stylesheet.css' 190 | # 191 | # You can provide an options hash for more control, 192 | # which is passed along to stylesheet_link_tag(): 193 | # config.register_stylesheet 'my_print_stylesheet.css', media: :print 194 | # 195 | # To load a javascript file: 196 | # config.register_javascript 'my_javascript.js' 197 | 198 | # == CSV options 199 | # 200 | # Set the CSV builder separator 201 | # config.csv_options = { col_sep: ';' } 202 | # 203 | # Force the use of quotes 204 | # config.csv_options = { force_quotes: true } 205 | 206 | # == Menu System 207 | # 208 | # You can add a navigation menu to be used in your application, or configure a provided menu 209 | # 210 | # To change the default utility navigation to show a link to your website & a logout btn 211 | # 212 | # config.namespace :admin do |admin| 213 | # admin.build_menu :utility_navigation do |menu| 214 | # menu.add label: "My Great Website", 215 | # url: "http://www.mygreatwebsite.com", html_options: { target: :blank } 216 | # admin.add_logout_button_to_menu menu 217 | # end 218 | # end 219 | # 220 | # If you wanted to add a static menu item to the default menu provided: 221 | # 222 | # config.namespace :admin do |admin| 223 | # admin.build_menu :default do |menu| 224 | # menu.add label: "My Great Website", 225 | # url: "http://www.mygreatwebsite.com", html_options: { target: :blank } 226 | # end 227 | # end 228 | 229 | # == Download Links 230 | # 231 | # You can disable download links on resource listing pages, 232 | # or customize the formats shown per namespace/globally 233 | # 234 | # To disable/customize for the :admin namespace: 235 | # 236 | # config.namespace :admin do |admin| 237 | # 238 | # # Disable the links entirely 239 | # admin.download_links = false 240 | # 241 | # # Only show XML & PDF options 242 | # admin.download_links = [:xml, :pdf] 243 | # 244 | # # Enable/disable the links based on block 245 | # # (for example, with cancan) 246 | # admin.download_links = proc { can?(:view_download_links) } 247 | # 248 | # end 249 | 250 | # == Pagination 251 | # 252 | # Pagination is enabled by default for all resources. 253 | # You can control the default per page count for all resources here. 254 | # 255 | # config.default_per_page = 30 256 | # 257 | # You can control the max per page count too. 258 | # 259 | # config.max_per_page = 10_000 260 | 261 | # == Filters 262 | # 263 | # By default the index screen includes a "Filters" sidebar on the right 264 | # hand side with a filter for each attribute of the registered model. 265 | # You can enable or disable them for all resources here. 266 | # 267 | # config.filters = true 268 | # 269 | # By default the filters include associations in a select, which means 270 | # that every record will be loaded for each association. 271 | # You can enabled or disable the inclusion 272 | # of those filters by default here. 273 | # 274 | # config.include_default_association_filters = true 275 | end 276 | -------------------------------------------------------------------------------- /config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using 4 | # but don't wish to see in your backtraces. 5 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 6 | 7 | # You can also remove all the silencers if you're trying to debug a problem 8 | # that might stem from framework code. 9 | # Rails.backtrace_cleaner.remove_silencers! 10 | -------------------------------------------------------------------------------- /config/initializers/devise.rb: -------------------------------------------------------------------------------- 1 | # Use this hook to configure devise mailer, warden hooks and so forth. 2 | # Many of these configuration options can be set straight in your model. 3 | Devise.setup do |config| 4 | # ==> Mailer Configuration 5 | # Configure the e-mail address which will be shown in Devise::Mailer, 6 | # note that it will be overwritten if you use your own mailer class 7 | # with default "from" parameter. 8 | config.mailer_sender = 'no-reply@yourapi.com' 9 | 10 | # Configure the class responsible to send e-mails. 11 | # config.mailer = 'Devise::Mailer' 12 | 13 | # Configure the parent class responsible to send e-mails. 14 | # config.parent_mailer = 'ActionMailer::Base' 15 | 16 | # ==> ORM configuration 17 | # Load and configure the ORM. Supports :active_record (default) and 18 | # :mongoid (bson_ext recommended) by default. Other ORMs may be 19 | # available as additional gems. 20 | require 'devise/orm/active_record' 21 | 22 | # ==> Configuration for any authentication mechanism 23 | # Configure which keys are used when authenticating a user. The default is 24 | # just :email. You can configure it to use [:username, :subdomain], so for 25 | # authenticating a user, both parameters are required. Remember that those 26 | # parameters are used only when authenticating and not when retrieving from 27 | # session. If you need permissions, you should implement that in a before filter. 28 | # You can also supply a hash where the value is a boolean determining whether 29 | # or not authentication should be aborted when the value is not present. 30 | # config.authentication_keys = [:email] 31 | 32 | # Configure parameters from the request object used for authentication. Each entry 33 | # given should be a request method and it will automatically be passed to the 34 | # find_for_authentication method and considered in your model lookup. For instance, 35 | # if you set :request_keys to [:subdomain], :subdomain will be used on authentication. 36 | # The same considerations mentioned for authentication_keys also apply to request_keys. 37 | # config.request_keys = [] 38 | 39 | # Configure which authentication keys should be case-insensitive. 40 | # These keys will be downcased upon creating or modifying a user and when used 41 | # to authenticate or find a user. Default is :email. 42 | config.case_insensitive_keys = [:email] 43 | 44 | # Configure which authentication keys should have whitespace stripped. 45 | # These keys will have whitespace before and after removed upon creating or 46 | # modifying a user and when used to authenticate or find a user. Default is :email. 47 | config.strip_whitespace_keys = [:email] 48 | 49 | # Tell if authentication through request.params is enabled. True by default. 50 | # It can be set to an array that will enable params authentication only for the 51 | # given strategies, for example, `config.params_authenticatable = [:database]` will 52 | # enable it only for database (email + password) authentication. 53 | # config.params_authenticatable = true 54 | 55 | # Tell if authentication through HTTP Auth is enabled. False by default. 56 | # It can be set to an array that will enable http authentication only for the 57 | # given strategies, for example, `config.http_authenticatable = [:database]` will 58 | # enable it only for database authentication. The supported strategies are: 59 | # :database = Support basic authentication with authentication key + password 60 | # config.http_authenticatable = false 61 | 62 | # If 401 status code should be returned for AJAX requests. True by default. 63 | # config.http_authenticatable_on_xhr = true 64 | 65 | # The realm used in Http Basic Authentication. 'Application' by default. 66 | # config.http_authentication_realm = 'Application' 67 | 68 | # It will change confirmation, password recovery and other workflows 69 | # to behave the same regardless if the e-mail provided was right or wrong. 70 | # Does not affect registerable. 71 | # config.paranoid = true 72 | 73 | # By default Devise will store the user in session. You can skip storage for 74 | # particular strategies by setting this option. 75 | # Notice that if you are skipping storage for all authentication paths, you 76 | # may want to disable generating routes to Devise's sessions controller by 77 | # passing skip: :sessions to `devise_for` in your config/routes.rb 78 | config.skip_session_storage = [:http_auth] 79 | 80 | # By default, Devise cleans up the CSRF token on authentication to 81 | # avoid CSRF token fixation attacks. This means that, when using AJAX 82 | # requests for sign in and sign up, you need to get a new CSRF token 83 | # from the server. You can disable this option at your own risk. 84 | # config.clean_up_csrf_token_on_authentication = true 85 | 86 | # When false, Devise will not attempt to reload routes on eager load. 87 | # This can reduce the time taken to boot the app but if your application 88 | # requires the Devise mappings to be loaded during boot time the application 89 | # won't boot properly. 90 | # config.reload_routes = true 91 | 92 | # ==> Configuration for :database_authenticatable 93 | # For bcrypt, this is the cost for hashing the password and defaults to 11. If 94 | # using other algorithms, it sets how many times you want the password to be hashed. 95 | # 96 | # Limiting the stretches to just one in testing will increase the performance of 97 | # your test suite dramatically. However, it is STRONGLY RECOMMENDED to not use 98 | # a value less than 10 in other environments. Note that, for bcrypt (the default 99 | # algorithm), the cost increases exponentially with the number of stretches (e.g. 100 | # a value of 20 is already extremely slow: approx. 60 seconds for 1 calculation). 101 | config.stretches = Rails.env.test? ? 1 : 11 102 | 103 | # Set up a pepper to generate the hashed password. 104 | # config.pepper = '74b9703c1236923e970d8586f6d7b9e6e12faae5d680c46d006b86d595ea811e5e2c37c' 105 | 106 | # Send a notification email when the user's password is changed 107 | # config.send_password_change_notification = false 108 | 109 | # ==> Configuration for :confirmable 110 | # A period that the user is allowed to access the website even without 111 | # confirming their account. For instance, if set to 2.days, the user will be 112 | # able to access the website for two days without confirming their account, 113 | # access will be blocked just in the third day. Default is 0.days, meaning 114 | # the user cannot access the website without confirming their account. 115 | # config.allow_unconfirmed_access_for = 2.days 116 | 117 | # A period that the user is allowed to confirm their account before their 118 | # token becomes invalid. For example, if set to 3.days, the user can confirm 119 | # their account within 3 days after the mail was sent, but on the fourth day 120 | # their account can't be confirmed with the token any more. 121 | # Default is nil, meaning there is no restriction on how long a user can take 122 | # before confirming their account. 123 | # config.confirm_within = 3.days 124 | 125 | # If true, requires any email changes to be confirmed (exactly the same way as 126 | # initial account confirmation) to be applied. Requires additional unconfirmed_email 127 | # db field (see migrations). Until confirmed, new email is stored in 128 | # unconfirmed_email column, and copied to email column on successful confirmation. 129 | config.reconfirmable = true 130 | 131 | # Defines which key will be used when confirming an account 132 | # config.confirmation_keys = [:email] 133 | 134 | # ==> Configuration for :rememberable 135 | # The time the user will be remembered without asking for credentials again. 136 | # config.remember_for = 2.weeks 137 | 138 | # Invalidates all the remember me tokens when the user signs out. 139 | config.expire_all_remember_me_on_sign_out = true 140 | 141 | # If true, extends the user's remember period when remembered via cookie. 142 | # config.extend_remember_period = false 143 | 144 | # Options to be passed to the created cookie. For instance, you can set 145 | # secure: true in order to force SSL only cookies. 146 | # config.rememberable_options = {} 147 | 148 | # ==> Configuration for :validatable 149 | # Range for password length. 150 | config.password_length = 8..128 151 | 152 | # Email regex used to validate email formats. It simply asserts that 153 | # one (and only one) @ exists in the given string. This is mainly 154 | # to give user feedback and not to assert the e-mail validity. 155 | config.email_regexp = /\A[^@\s]+@[^@\s]+\z/ 156 | 157 | # ==> Configuration for :timeoutable 158 | # The time you want to timeout the user session without activity. After this 159 | # time the user will be asked for credentials again. Default is 30 minutes. 160 | # config.timeout_in = 30.minutes 161 | 162 | # ==> Configuration for :lockable 163 | # Defines which strategy will be used to lock an account. 164 | # :failed_attempts = Locks an account after a number of failed attempts to sign in. 165 | # :none = No lock strategy. You should handle locking by yourself. 166 | # config.lock_strategy = :failed_attempts 167 | 168 | # Defines which key will be used when locking and unlocking an account 169 | # config.unlock_keys = [:email] 170 | 171 | # Defines which strategy will be used to unlock an account. 172 | # :email = Sends an unlock link to the user email 173 | # :time = Re-enables login after a certain amount of time (see :unlock_in below) 174 | # :both = Enables both strategies 175 | # :none = No unlock strategy. You should handle unlocking by yourself. 176 | # config.unlock_strategy = :both 177 | 178 | # Number of authentication tries before locking an account if lock_strategy 179 | # is failed attempts. 180 | # config.maximum_attempts = 20 181 | 182 | # Time interval to unlock the account if :time is enabled as unlock_strategy. 183 | # config.unlock_in = 1.hour 184 | 185 | # Warn on the last attempt before the account is locked. 186 | # config.last_attempt_warning = true 187 | 188 | # ==> Configuration for :recoverable 189 | # 190 | # Defines which key will be used when recovering the password for an account 191 | # config.reset_password_keys = [:email] 192 | 193 | # Time interval you can reset your password with a reset password key. 194 | # Don't put a too small interval or your users won't have the time to 195 | # change their passwords. 196 | config.reset_password_within = 6.hours 197 | 198 | # When set to false, does not sign a user in automatically after their password is 199 | # reset. Defaults to true, so a user is signed in automatically after a reset. 200 | # config.sign_in_after_reset_password = true 201 | 202 | # ==> Configuration for :encryptable 203 | # Allow you to use another hashing or encryption algorithm besides bcrypt (default). 204 | # You can use :sha1, :sha512 or algorithms from others authentication tools as 205 | # :clearance_sha1, :authlogic_sha512 (then you should set stretches above to 20 206 | # for default behavior) and :restful_authentication_sha1 (then you should set 207 | # stretches to 10, and copy REST_AUTH_SITE_KEY to pepper). 208 | # 209 | # Require the `devise-encryptable` gem when using anything other than bcrypt 210 | # config.encryptor = :sha512 211 | 212 | # ==> Scopes configuration 213 | # Turn scoped views on. Before rendering "sessions/new", it will first check for 214 | # "users/sessions/new". It's turned off by default because it's slower if you 215 | # are using only default views. 216 | # config.scoped_views = false 217 | 218 | # Configure the default scope given to Warden. By default it's the first 219 | # devise role declared in your routes (usually :user). 220 | # config.default_scope = :user 221 | 222 | # Set this configuration to false if you want /users/sign_out to sign out 223 | # only the current scope. By default, Devise signs out all scopes. 224 | # config.sign_out_all_scopes = true 225 | 226 | # ==> Navigation configuration 227 | # Lists the formats that should be treated as navigational. Formats like 228 | # :html, should redirect to the sign in page when the user does not have 229 | # access, but formats like :xml or :json, should return 401. 230 | # 231 | # If you have any extra navigational formats, like :iphone or :mobile, you 232 | # should add them to the navigational formats lists. 233 | # 234 | # The "*/*" below is required to match Internet Explorer requests. 235 | config.navigational_formats = ['*/*', :html, :json] 236 | 237 | # The default HTTP method used to sign out a resource. Default is :delete. 238 | config.sign_out_via = :delete 239 | 240 | # ==> OmniAuth 241 | # Add a new OmniAuth provider. Check the wiki for more information on setting 242 | # up on your models and hooks. 243 | # config.omniauth :github, 'APP_ID', 'APP_SECRET', scope: 'user,public_repo' 244 | 245 | # ==> Warden configuration 246 | # If you want to use other strategies, that are not supported by Devise, or 247 | # change the failure app, you can configure them inside the config.warden block. 248 | # 249 | # config.warden do |manager| 250 | # manager.intercept_401 = false 251 | # manager.default_strategies(scope: :user).unshift :some_external_strategy 252 | # end 253 | 254 | # ==> Mountable engine configurations 255 | # When using Devise inside an engine, let's call it `MyEngine`, and this engine 256 | # is mountable, there are some extra configurations to be taken into account. 257 | # The following options are available, assuming the engine is mounted as: 258 | # 259 | # mount MyEngine, at: '/my_engine' 260 | # 261 | # The router that invoked `devise_for`, in the example above, would be: 262 | # config.router_name = :my_engine 263 | # 264 | # When using OmniAuth, Devise cannot automatically set OmniAuth path, 265 | # so you need to do it manually. For the users scope, it would be: 266 | # config.omniauth_path_prefix = '/my_engine/users/auth' 267 | end 268 | -------------------------------------------------------------------------------- /config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Rails 5.0 release notes for more info on each option. 6 | 7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 8 | # Previous versions had false. 9 | ActiveSupport.to_time_preserves_timezone = true 10 | 11 | # Require `belongs_to` associations by default. Previous versions had false. 12 | Rails.application.config.active_record.belongs_to_required_by_default = true 13 | 14 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 15 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 16 | -------------------------------------------------------------------------------- /config/initializers/rack_cors.rb: -------------------------------------------------------------------------------- 1 | module App 2 | class Application < Rails::Application 3 | config.middleware.insert_before 0, Rack::Cors do 4 | allow do 5 | origins '*' 6 | resource '*', 7 | headers: :any, 8 | methods: %i[get post options put delete], 9 | expose: %w[access-token uid client] 10 | end 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /config/initializers/slack.rb: -------------------------------------------------------------------------------- 1 | Slack.configure do |config| 2 | config.token = ENV['SLACK_BOT_TOKEN'] 3 | end 4 | -------------------------------------------------------------------------------- /config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | 11 | # To enable root element in JSON for ActiveRecord objects. 12 | # ActiveSupport.on_load(:active_record) do 13 | # self.include_root_in_json = true 14 | # end 15 | -------------------------------------------------------------------------------- /config/locales/devise.en.yml: -------------------------------------------------------------------------------- 1 | # Additional translations at https://github.com/plataformatec/devise/wiki/I18n 2 | 3 | en: 4 | devise: 5 | confirmations: 6 | confirmed: "Your email address has been successfully confirmed." 7 | send_instructions: "You will receive an email with instructions for how to confirm your email address in a few minutes." 8 | send_paranoid_instructions: "If your email address exists in our database, you will receive an email with instructions for how to confirm your email address in a few minutes." 9 | failure: 10 | already_authenticated: "You are already signed in." 11 | inactive: "Your account is not activated yet." 12 | invalid: "Invalid %{authentication_keys} or password." 13 | locked: "Your account is locked." 14 | last_attempt: "You have one more attempt before your account is locked." 15 | not_found_in_database: "Invalid %{authentication_keys} or password." 16 | timeout: "Your session expired. Please sign in again to continue." 17 | unauthenticated: "You need to sign in or sign up before continuing." 18 | unconfirmed: "You have to confirm your email address before continuing." 19 | mailer: 20 | confirmation_instructions: 21 | subject: "Confirmation instructions" 22 | reset_password_instructions: 23 | subject: "Reset password instructions" 24 | unlock_instructions: 25 | subject: "Unlock instructions" 26 | password_change: 27 | subject: "Password Changed" 28 | omniauth_callbacks: 29 | failure: "Could not authenticate you from %{kind} because \"%{reason}\"." 30 | success: "Successfully authenticated from %{kind} account." 31 | passwords: 32 | no_token: "You can't access this page without coming from a password reset email. If you do come from a password reset email, please make sure you used the full URL provided." 33 | send_instructions: "You will receive an email with instructions on how to reset your password in a few minutes." 34 | send_paranoid_instructions: "If your email address exists in our database, you will receive a password recovery link at your email address in a few minutes." 35 | updated: "Your password has been changed successfully. You are now signed in." 36 | updated_not_active: "Your password has been changed successfully." 37 | registrations: 38 | destroyed: "Bye! Your account has been successfully cancelled. We hope to see you again soon." 39 | signed_up: "Welcome! You have signed up successfully." 40 | signed_up_but_inactive: "You have signed up successfully. However, we could not sign you in because your account is not yet activated." 41 | signed_up_but_locked: "You have signed up successfully. However, we could not sign you in because your account is locked." 42 | signed_up_but_unconfirmed: "A message with a confirmation link has been sent to your email address. Please follow the link to activate your account." 43 | update_needs_confirmation: "You updated your account successfully, but we need to verify your new email address. Please check your email and follow the confirm link to confirm your new email address." 44 | updated: "Your account has been updated successfully." 45 | sessions: 46 | signed_in: "Signed in successfully." 47 | signed_out: "Signed out successfully." 48 | already_signed_out: "Signed out successfully." 49 | unlocks: 50 | send_instructions: "You will receive an email with instructions for how to unlock your account in a few minutes." 51 | send_paranoid_instructions: "If your account exists, you will receive an email with instructions for how to unlock it in a few minutes." 52 | unlocked: "Your account has been unlocked successfully. Please sign in to continue." 53 | errors: 54 | messages: 55 | already_confirmed: "was already confirmed, please try signing in" 56 | confirmation_period_expired: "needs to be confirmed within %{period}, please request a new one" 57 | expired: "has expired, please request a new one" 58 | not_found: "not found" 59 | not_locked: "was not locked" 60 | not_saved: 61 | one: "1 error prohibited this %{resource} from being saved:" 62 | other: "%{count} errors prohibited this %{resource} from being saved:" 63 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | api: 24 | errors: 25 | server: 'An error ocurred' 26 | not_found: "Couldn't find the record" 27 | missing_param: 'A required param is missing' 28 | invalid_content_type: 'Invalid content type header' 29 | facebook: 30 | not_authorized: 'Not Authorized' 31 | already_registerd: 'User already registered with email/password' 32 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch('RAILS_MAX_THREADS') { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch('PORT') { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch('RAILS_ENV') { 'development' } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch('WEB_CONCURRENCY') { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /config/rails_best_practices.yml: -------------------------------------------------------------------------------- 1 | AddModelVirtualAttributeCheck: { } 2 | AlwaysAddDbIndexCheck: { } 3 | #CheckSaveReturnValueCheck: { } 4 | DefaultScopeIsEvilCheck: { } 5 | DryBundlerInCapistranoCheck: { } 6 | #HashSyntaxCheck: { } 7 | IsolateSeedDataCheck: { } 8 | KeepFindersOnTheirOwnModelCheck: { } 9 | LawOfDemeterCheck: { } 10 | #LongLineCheck: { max_line_length: 80 } 11 | MoveCodeIntoControllerCheck: { } 12 | MoveCodeIntoHelperCheck: { array_count: 3 } 13 | MoveCodeIntoModelCheck: { use_count: 2 } 14 | MoveFinderToNamedScopeCheck: { } 15 | MoveModelLogicIntoModelCheck: { use_count: 4 } 16 | NeedlessDeepNestingCheck: { nested_count: 2 } 17 | NotRescueExceptionCheck: { } 18 | NotUseDefaultRouteCheck: { } 19 | NotUseTimeAgoInWordsCheck: { } 20 | OveruseRouteCustomizationsCheck: { customize_count: 10 } 21 | ProtectMassAssignmentCheck: { } 22 | RemoveEmptyHelpersCheck: { } 23 | #RemoveTabCheck: { } 24 | RemoveTrailingWhitespaceCheck: { } 25 | # RemoveUnusedMethodsInControllersCheck: { except_methods: []} 26 | RemoveUnusedMethodsInHelpersCheck: { except_methods: [] } 27 | # RemoveUnusedMethodsInModelsCheck: { ignored_files: ['.*typeable\.rb'], except_methods: [] } 28 | ReplaceComplexCreationWithFactoryMethodCheck: { attribute_assignment_count: 2 } 29 | ReplaceInstanceVariableWithLocalVariableCheck: { } 30 | RestrictAutoGeneratedRoutesCheck: { } 31 | SimplifyRenderInControllersCheck: { } 32 | SimplifyRenderInViewsCheck: { } 33 | #UseBeforeFilterCheck: { customize_count: 2 } 34 | UseModelAssociationCheck: { } 35 | # UseMultipartAlternativeAsContentTypeOfEmailCheck: { } 36 | #UseParenthesesInMethodDefCheck: { } 37 | UseObserverCheck: { } 38 | UseQueryAttributeCheck: { } 39 | UseSayWithTimeInMigrationsCheck: { } 40 | UseScopeAccessCheck: { } 41 | UseTurboSprocketsRails3Check: { } 42 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | devise_for :admin_users, ActiveAdmin::Devise.config 3 | ActiveAdmin.routes(self) 4 | 5 | namespace :api do 6 | namespace :v1, defaults: { format: :json } do 7 | post '/notifications_filter', to: 'github_webhook#filter' 8 | end 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /config/spring.rb: -------------------------------------------------------------------------------- 1 | %w[ 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ].each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /db/migrate/20161011151353_devise_create_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :users do |t| 4 | ## Database authenticatable 5 | t.string :email 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | t.boolean :allow_password_change, :default => false 12 | 13 | ## Rememberable 14 | t.datetime :remember_created_at 15 | 16 | ## Trackable 17 | t.integer :sign_in_count, default: 0, null: false 18 | t.datetime :current_sign_in_at 19 | t.datetime :last_sign_in_at 20 | t.inet :current_sign_in_ip 21 | t.inet :last_sign_in_ip 22 | 23 | ## Confirmable 24 | # t.string :confirmation_token 25 | # t.datetime :confirmed_at 26 | # t.datetime :confirmation_sent_at 27 | # t.string :unconfirmed_email # Only if using reconfirmable 28 | 29 | ## Lockable 30 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 31 | # t.string :unlock_token # Only if unlock strategy is :email or :both 32 | # t.datetime :locked_at 33 | 34 | t.string :first_name, default: "" 35 | t.string :last_name, default: "" 36 | t.string :username, default: "" 37 | 38 | 39 | t.timestamps null: false 40 | end 41 | 42 | add_index :users, :email, unique: true 43 | add_index :users, :reset_password_token, unique: true 44 | # add_index :users, :confirmation_token, unique: true 45 | # add_index :users, :unlock_token, unique: true 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /db/migrate/20161011184702_devise_create_admin_users.rb: -------------------------------------------------------------------------------- 1 | class DeviseCreateAdminUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | create_table :admin_users do |t| 4 | ## Database authenticatable 5 | t.string :email, null: false, default: "" 6 | t.string :encrypted_password, null: false, default: "" 7 | 8 | ## Recoverable 9 | t.string :reset_password_token 10 | t.datetime :reset_password_sent_at 11 | 12 | ## Rememberable 13 | t.datetime :remember_created_at 14 | 15 | ## Trackable 16 | t.integer :sign_in_count, default: 0, null: false 17 | t.datetime :current_sign_in_at 18 | t.datetime :last_sign_in_at 19 | t.inet :current_sign_in_ip 20 | t.inet :last_sign_in_ip 21 | 22 | ## Confirmable 23 | # t.string :confirmation_token 24 | # t.datetime :confirmed_at 25 | # t.datetime :confirmation_sent_at 26 | # t.string :unconfirmed_email # Only if using reconfirmable 27 | 28 | ## Lockable 29 | # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts 30 | # t.string :unlock_token # Only if unlock strategy is :email or :both 31 | # t.datetime :locked_at 32 | 33 | 34 | t.timestamps null: false 35 | end 36 | 37 | add_index :admin_users, :email, unique: true 38 | add_index :admin_users, :reset_password_token, unique: true 39 | # add_index :admin_users, :confirmation_token, unique: true 40 | # add_index :admin_users, :unlock_token, unique: true 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /db/migrate/20161017183759_add_devise_token_auth_fields_users.rb: -------------------------------------------------------------------------------- 1 | class AddDeviseTokenAuthFieldsUsers < ActiveRecord::Migration[5.0] 2 | def change 3 | add_column :users, :provider, :string, null: false, default: 'email' 4 | add_column :users, :uid, :string, null: false, default: '' 5 | add_column :users, :tokens, :json 6 | 7 | add_index :users, [:uid, :provider], unique: true 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /db/migrate/20161027190856_create_delayed_jobs.rb: -------------------------------------------------------------------------------- 1 | class CreateDelayedJobs < ActiveRecord::Migration[5.0] 2 | def self.up 3 | create_table :delayed_jobs, force: true do |table| 4 | table.integer :priority, default: 0, null: false # Allows some jobs to jump to the front of the queue 5 | table.integer :attempts, default: 0, null: false # Provides for retries, but still fail eventually. 6 | table.text :handler, null: false # YAML-encoded string of the object that will do work 7 | table.text :last_error # reason for last failure (See Note below) 8 | table.datetime :run_at # When to run. Could be Time.zone.now for immediately, or sometime in the future. 9 | table.datetime :locked_at # Set when a client is working on this object 10 | table.datetime :failed_at # Set when all retries have failed (actually, by default, the record is deleted instead) 11 | table.string :locked_by # Who is working on this object (if locked) 12 | table.string :queue # The name of the queue this job is in 13 | table.timestamps null: true 14 | end 15 | 16 | add_index :delayed_jobs, [:priority, :run_at], name: "delayed_jobs_priority" 17 | end 18 | 19 | def self.down 20 | drop_table :delayed_jobs 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /db/migrate/20190418132233_modify_users.rb: -------------------------------------------------------------------------------- 1 | class ModifyUsers < ActiveRecord::Migration[5.1] 2 | def change 3 | remove_column :users, :provider, :string, null: false, default: 'email' 4 | remove_column :users, :uid, :string, null: false, default: '' 5 | remove_column :users, :tokens, :json 6 | remove_column :users, :encrypted_password, :string, null: false, default: '' 7 | remove_column :users, :reset_password_token, :string 8 | remove_column :users, :reset_password_sent_at, :datetime 9 | remove_column :users, :allow_password_change, :boolean, default: false 10 | remove_column :users, :remember_created_at, :datetime 11 | remove_column :users, :sign_in_count, :integer, null: false, default: 0 12 | remove_column :users, :current_sign_in_at, :datetime 13 | remove_column :users, :last_sign_in_at, :datetime 14 | remove_column :users, :current_sign_in_ip, :inet 15 | remove_column :users, :last_sign_in_ip, :inet 16 | remove_column :users, :username, :string, default: '' 17 | remove_index :users, :email 18 | 19 | add_column :users, :github_name, :string 20 | add_index :users, :github_name 21 | add_column :users, :slack_name, :string 22 | add_column :users, :blacklisted, :boolean, default: false 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # Note that this schema.rb definition is the authoritative source for your 6 | # database schema. If you need to create the application database on another 7 | # system, you should be using db:schema:load, not running all the migrations 8 | # from scratch. The latter is a flawed and unsustainable approach (the more migrations 9 | # you'll amass, the slower it'll run and the greater likelihood for issues). 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema.define(version: 20190418132233) do 14 | 15 | # These are extensions that must be enabled in order to support this database 16 | enable_extension "plpgsql" 17 | 18 | create_table "admin_users", id: :serial, force: :cascade do |t| 19 | t.string "email", default: "", null: false 20 | t.string "encrypted_password", default: "", null: false 21 | t.string "reset_password_token" 22 | t.datetime "reset_password_sent_at" 23 | t.datetime "remember_created_at" 24 | t.integer "sign_in_count", default: 0, null: false 25 | t.datetime "current_sign_in_at" 26 | t.datetime "last_sign_in_at" 27 | t.inet "current_sign_in_ip" 28 | t.inet "last_sign_in_ip" 29 | t.datetime "created_at", null: false 30 | t.datetime "updated_at", null: false 31 | t.index ["email"], name: "index_admin_users_on_email", unique: true 32 | t.index ["reset_password_token"], name: "index_admin_users_on_reset_password_token", unique: true 33 | end 34 | 35 | create_table "delayed_jobs", id: :serial, force: :cascade do |t| 36 | t.integer "priority", default: 0, null: false 37 | t.integer "attempts", default: 0, null: false 38 | t.text "handler", null: false 39 | t.text "last_error" 40 | t.datetime "run_at" 41 | t.datetime "locked_at" 42 | t.datetime "failed_at" 43 | t.string "locked_by" 44 | t.string "queue" 45 | t.datetime "created_at" 46 | t.datetime "updated_at" 47 | t.index ["priority", "run_at"], name: "delayed_jobs_priority" 48 | end 49 | 50 | create_table "users", id: :serial, force: :cascade do |t| 51 | t.string "email" 52 | t.string "first_name", default: "" 53 | t.string "last_name", default: "" 54 | t.datetime "created_at", null: false 55 | t.datetime "updated_at", null: false 56 | t.string "github_name" 57 | t.string "slack_name" 58 | t.boolean "blacklisted", default: false 59 | t.index ["github_name"], name: "index_users_on_github_name" 60 | end 61 | 62 | end 63 | -------------------------------------------------------------------------------- /db/seeds.rb: -------------------------------------------------------------------------------- 1 | AdminUser.create!(email: 'admin@example.com', password: 'password') 2 | 3 | User.create!(github_name: 'dependabot', blacklisted: true) 4 | -------------------------------------------------------------------------------- /lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rootstrap/pull_requests_to_slack/67ee96a3d3f4a45a0ec977c4de3855355ff0579b/lib/tasks/.keep -------------------------------------------------------------------------------- /lib/tasks/add_blocklisted_user.rake: -------------------------------------------------------------------------------- 1 | desc 'Adds a user to the blocklist. ' \ 2 | 'Use "rake add_blocklisted_user some_gh_name"' 3 | task add_blocklisted_user: [:environment] do 4 | ARGV.each { |a| task(a.to_sym {}) } 5 | name = ARGV[1] 6 | 7 | if name.present? 8 | User.create!(github_name: name, blacklisted: true) 9 | else 10 | warn 'Invalid or missing GitHub name param' 11 | warn 'Use "rake add_blocklisted_user some_gh_name"' 12 | exit(255) 13 | end 14 | end 15 | -------------------------------------------------------------------------------- /lib/tasks/auto_annotate_models.rake: -------------------------------------------------------------------------------- 1 | # NOTE: only doing this in development as some production environments (Heroku) 2 | # NOTE: are sensitive to local FS writes, and besides -- it's just not proper 3 | # NOTE: to have a dev-mode tool do its thing in production. 4 | if Rails.env.development? 5 | task :set_annotation_options do 6 | # You can override any of these by setting an environment variable of the 7 | # same name. 8 | Annotate.set_defaults( 9 | 'position_in_routes' => 'before', 10 | 'position_in_class' => 'before', 11 | 'position_in_test' => 'before', 12 | 'position_in_fixture' => 'before', 13 | 'position_in_factory' => 'before', 14 | 'show_indexes' => 'true', 15 | 'simple_indexes' => 'false', 16 | 'model_dir' => 'app/models', 17 | 'include_version' => 'false', 18 | 'require' => '', 19 | 'exclude_tests' => 'false', 20 | 'exclude_fixtures' => 'false', 21 | 'exclude_factories' => 'false', 22 | 'ignore_model_sub_dir' => 'false', 23 | 'skip_on_db_migrate' => 'false', 24 | 'format_bare' => 'true', 25 | 'format_rdoc' => 'false', 26 | 'format_markdown' => 'false', 27 | 'sort' => 'false', 28 | 'force' => 'false', 29 | 'trace' => 'false' 30 | ) 31 | end 32 | 33 | Annotate.load_tasks 34 | end 35 | -------------------------------------------------------------------------------- /lib/tasks/code_analysis.rake: -------------------------------------------------------------------------------- 1 | task :code_analysis do 2 | sh 'bundle exec brakeman . -z -q' 3 | sh 'bundle exec rubocop app config lib spec' 4 | sh 'bundle exec reek app config lib' 5 | sh 'bundle exec rails_best_practices .' 6 | end 7 | -------------------------------------------------------------------------------- /lib/tasks/rails_best_practices.rake: -------------------------------------------------------------------------------- 1 | task :rails_best_practices do 2 | sh 'bundle exec rails_best_practices .' 3 | end 4 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /spec/admin/active_admin_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe 'activeadmin resources' do 4 | let(:all_resources) { ActiveAdmin.application.namespaces[:admin].resources } 5 | 6 | it 'should have admin user resource' do 7 | expect(all_resources[:AdminUser]).to be 8 | end 9 | 10 | it 'should have user resource' do 11 | expect(all_resources[:User]).to be 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /spec/admin/controllers/users_controller_spec.rb: -------------------------------------------------------------------------------- 1 | describe Admin::UsersController, type: :controller do 2 | describe 'index' do 3 | it 'redirect to login page when not logged in' do 4 | get :index 5 | expect(response).to redirect_to new_admin_user_session_path 6 | end 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /spec/factories/spec.rb: -------------------------------------------------------------------------------- 1 | describe FactoryBot do 2 | FactoryBot.factories.map(&:name).each do |factory_name| 3 | describe "The #{factory_name} factory" do 4 | it { expect(FactoryBot.build(factory_name)).to be_valid } 5 | end 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/factories/user.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | email { Faker::Internet.unique.email } 4 | github_name { Faker::Internet.unique.user_name } 5 | blacklisted { false } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /spec/helpers.rb: -------------------------------------------------------------------------------- 1 | module Helpers 2 | # Helper method to parse a response 3 | # 4 | # @return [Hash] 5 | def json 6 | JSON.parse(response.body).with_indifferent_access 7 | end 8 | 9 | def auth_headers 10 | user.create_new_auth_token 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/models/user_spec.rb: -------------------------------------------------------------------------------- 1 | # == Schema Information 2 | # 3 | # Table name: users 4 | # 5 | # id :integer not null, primary key 6 | # email :string 7 | # first_name :string default("") 8 | # last_name :string default("") 9 | # created_at :datetime not null 10 | # updated_at :datetime not null 11 | # github_name :string 12 | # slack_name :string 13 | # blacklisted :boolean default(FALSE) 14 | # 15 | # Indexes 16 | # 17 | # index_users_on_github_name (github_name) 18 | # 19 | 20 | require 'rails_helper' 21 | 22 | describe User do 23 | context 'when was created with regular login' do 24 | let!(:user) { create(:user) } 25 | let(:full_name) { user.full_name } 26 | 27 | it 'returns the correct name' do 28 | expect(full_name).to eq(user.github_name) 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/rails_helper.rb: -------------------------------------------------------------------------------- 1 | # This file is copied to spec/ when you run 'rails generate rspec:install' 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../config/environment', __dir__) 4 | # Prevent database truncation if the environment is production 5 | abort('The Rails environment is running in production mode!') if Rails.env.production? 6 | require 'rspec/core' 7 | require 'spec_helper' 8 | require 'rspec/rails' 9 | require 'simplecov' 10 | 11 | # save to CircleCI's artifacts directory if we're on CircleCI 12 | if ENV['CIRCLE_ARTIFACTS'] 13 | dir = File.join(ENV['CIRCLE_ARTIFACTS'], 'coverage') 14 | SimpleCov.coverage_dir(dir) 15 | end 16 | SimpleCov.start 17 | 18 | ActiveRecord::Migration.maintain_test_schema! 19 | WebMock.disable_net_connect!(allow_localhost: true) 20 | 21 | RSpec.configure do |config| 22 | config.include Devise::Test::ControllerHelpers, type: :controller 23 | config.use_transactional_fixtures = true 24 | config.infer_spec_type_from_file_location! 25 | end 26 | -------------------------------------------------------------------------------- /spec/requests/api/v1/github_webhook/filter_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe 'GET api/v1/notifications_filter', type: :request do 4 | let(:channel) { '#code-review' } 5 | let(:pull_request_link) { 'https://github.com/rootstrap/example-project/pull/1' } 6 | let(:message) { "#{pull_request_link} <@user> Tiny PR :javascript:" } 7 | let(:pull_request) do 8 | { 9 | html_url: pull_request_link, 10 | title: 'Update the README with new information', 11 | ts: '1234', 12 | body: 'This is the body \slack @user Tiny PR', 13 | user: { 14 | login: 'user', 15 | avatar_url: 'image.png' 16 | } 17 | } 18 | end 19 | 20 | let(:recovered_channel) do 21 | Slack::Messages::Message.new(channel: channel) 22 | end 23 | 24 | before { mock_channel_response(nil) } 25 | 26 | context 'when there is an open pull request notification' do 27 | let(:params) do 28 | { 29 | action: 'opened', 30 | pull_request: pull_request, 31 | repository: { 32 | language: 'JavaScript', 33 | name: 'example' 34 | } 35 | } 36 | end 37 | 38 | it 'sends a slack notification to a given channel with the PR notification' do 39 | expect_notification 40 | end 41 | 42 | context 'when repository includes angular code' do 43 | it 'sends message to correct channel' do 44 | params[:repository][:name] = 'example-Angular-repository' 45 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :angular:") 46 | end 47 | end 48 | 49 | context 'when repository is a devops repository' do 50 | let(:channel) { '#devops-code-review' } 51 | before { mock_channel_response(channel) } 52 | 53 | it 'sends message to correct channel' do 54 | params[:repository][:name] = 'repository-devops-project ' 55 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :javascript:") 56 | end 57 | 58 | it 'sends message to correct channel' do 59 | params[:repository][:name] = 'repository-infrastructure ' 60 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :javascript:") 61 | end 62 | 63 | it 'sends message to correct channel' do 64 | params[:repository][:topics] = ['terraform'] 65 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :javascript:") 66 | end 67 | end 68 | 69 | context 'repository name includes a technology' do 70 | before { mock_channel_response(channel) } 71 | 72 | context 'technology is react native' do 73 | let(:channel) { '#react-native-code-review' } 74 | 75 | it 'sends a slack notification with the PR link and language emoji' do 76 | params[:repository][:name] = 'example-React-Native' 77 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :react_native:") 78 | end 79 | end 80 | 81 | context 'technology is node' do 82 | let(:channel) { '#node-code-review' } 83 | 84 | it 'sends a slack notification with the PR link and language emoji' do 85 | params[:repository][:name] = 'example-Node-repo' 86 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :nodejs:") 87 | end 88 | end 89 | end 90 | 91 | context 'with a technology topic' do 92 | context 'with node topic' do 93 | let(:channel) { '#node-code-review' } 94 | before { mock_channel_response(channel) } 95 | 96 | it 'sends a slack notification with the PR link and language emoji' do 97 | params[:repository][:topics] = ['github', 'node', 'bot', 'slakbot'] 98 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :javascript:") 99 | end 100 | end 101 | 102 | context 'with react-native topic' do 103 | let(:channel) { '#react-native-code-review' } 104 | before { mock_channel_response(channel) } 105 | 106 | it 'sends a slack notification with the PR link and language emoji' do 107 | params[:repository][:topics] = ['github', 'react-native', 'bot', 'slakbot'] 108 | params[:repository][:language] = 'TypeScript' 109 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :ts:") 110 | end 111 | end 112 | end 113 | 114 | context 'with a language not supported' do 115 | let(:channel) { '#code-review' } 116 | 117 | it 'sends a slack notification with the PR link to #code-reviewers channel' do 118 | params[:repository][:language] = 'gherkin' 119 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :gherkin:") 120 | end 121 | end 122 | 123 | context 'with a language with no channel created' do 124 | let(:channel) { '#code-review' } 125 | 126 | it 'sends a slack notification with the PR link to #code-reviewers channel' do 127 | params[:repository][:language] = 'Makefile' 128 | expect_notification(text: "#{pull_request_link} <@user> Tiny PR :makefile:") 129 | end 130 | end 131 | 132 | context 'pr body does not include a \slack message' do 133 | it 'sends a slack notification with the PR link and language emoji' do 134 | pull_request[:body] = 'This is a simple body' 135 | expect_notification(text: "#{pull_request_link} :javascript:") 136 | end 137 | end 138 | 139 | context 'when the user is blacklisted' do 140 | let!(:user) { create(:user, github_name: 'blacklisted_user', blacklisted: true) } 141 | it 'does not sends a slack notification with the PR link' do 142 | pull_request[:user][:login] = 'blacklisted_user' 143 | expect_not_notification 144 | end 145 | end 146 | end 147 | 148 | context 'when there is a closed pull request notification' do 149 | let(:params) do 150 | { 151 | action: 'closed', 152 | pull_request: pull_request 153 | } 154 | end 155 | 156 | it 'does NOT send a notification' do 157 | expect_not_notification 158 | end 159 | end 160 | 161 | context 'when there is a merged pull request notification' do 162 | let(:params) do 163 | { 164 | action: 'closed', 165 | pull_request: pull_request.merge('merged' => true) 166 | } 167 | end 168 | 169 | it ' adds the merged reaction' do 170 | expect_any_instance_of(Slack::Web::Client).to receive(:search_messages) 171 | .and_return(messages: { matches: [{ ts: '1234' }] }) 172 | expect_any_instance_of(Slack::Web::Client).to receive(:reactions_add) 173 | .with(name: :merged, channel: channel, timestamp: '1234', as_user: false) 174 | 175 | post api_v1_notifications_filter_path, params: params, as: :json 176 | end 177 | end 178 | 179 | context 'when there is an draft request notification' do 180 | let(:params) do 181 | { 182 | action: 'opened', 183 | pull_request: pull_request.merge('draft' => true), 184 | repository: { 185 | language: 'JavaScript', 186 | name: 'example' 187 | } 188 | } 189 | end 190 | 191 | it 'does not sends a slack notification to a given channel with the PR notification' do 192 | expect_not_notification 193 | end 194 | end 195 | 196 | context 'when there is an ready_for_review request notification' do 197 | let(:params) do 198 | { 199 | action: 'ready_for_review', 200 | pull_request: pull_request, 201 | repository: { 202 | language: 'JavaScript', 203 | name: 'example' 204 | } 205 | } 206 | end 207 | 208 | it 'sends a slack notification to a given channel with the PR notification' do 209 | expect_notification 210 | end 211 | end 212 | 213 | context 'when the "on hold" label is removed' do 214 | let(:params) do 215 | { 216 | action: 'unlabeled', 217 | pull_request: pull_request, 218 | label: { name: 'ON HOLD' }, 219 | repository: { 220 | language: 'JavaScript', 221 | name: 'example' 222 | } 223 | } 224 | end 225 | 226 | it 'sends a slack notification with the PR link and language emoji' do 227 | expect_notification 228 | end 229 | end 230 | 231 | context 'when there is an open pull request notification with the "ON HOLD" label' do 232 | let(:params) do 233 | { 234 | action: 'opened', 235 | pull_request: { 236 | html_url: pull_request_link, 237 | title: 'Update the README with new information', 238 | labels: [ 239 | { name: 'ON HOLD' }, 240 | { name: 'bug' } 241 | ] 242 | }, 243 | repository: { 244 | language: 'JavaScript' 245 | } 246 | } 247 | end 248 | 249 | it 'does NOT send a notification' do 250 | expect_not_notification 251 | end 252 | end 253 | 254 | context 'when the "ON HOLD" label is added' do 255 | let(:params) do 256 | { 257 | action: 'labeled', 258 | pull_request: pull_request, 259 | label: { name: 'ON HOLD' } 260 | } 261 | end 262 | 263 | it 'deletes the messages that contains the PR link' do 264 | expect_any_instance_of(Slack::Web::Client).to receive(:search_messages) 265 | .and_return(messages: { matches: [{ ts: '1234' }] }) 266 | expect_any_instance_of(Slack::Web::Client).to receive(:chat_delete) 267 | .with(channel: channel, ts: '1234') 268 | post api_v1_notifications_filter_path, params: params, as: :json 269 | end 270 | end 271 | end 272 | 273 | def expect_notification(text: message) 274 | expect_any_instance_of(Slack::Web::Client).to receive(:chat_postMessage) 275 | .with(channel: channel, text: text, username: 'user', icon_url: 'image.png') 276 | 277 | post api_v1_notifications_filter_path, params: params, as: :json 278 | end 279 | 280 | def expect_not_notification 281 | expect_any_instance_of(Slack::Web::Client).to_not receive(:chat_postMessage) 282 | post api_v1_notifications_filter_path, params: params, as: :json 283 | end 284 | 285 | def mock_channel_response(return_value) 286 | allow_any_instance_of(SlackNotificationService) 287 | .to receive(:search_channel) 288 | .and_return(return_value) 289 | end 290 | -------------------------------------------------------------------------------- /spec/services/slack_bot_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe 'SlackBot' do 4 | let(:subject) { SlackBot.new(channel: 'demo_channel') } 5 | 6 | describe 'initialization' do 7 | it 'raise an error if channel is not present' do 8 | expect { SlackBot.new(channel: nil) }.to raise_error(ArgumentError, 'Invalid channel') 9 | expect { SlackBot.new(channel: '') }.to raise_error(ArgumentError, 'Invalid channel') 10 | end 11 | end 12 | 13 | describe '#language_emoji' do 14 | context 'when the repo includes React' do 15 | it 'returns the correct emoji' do 16 | expect(subject.language_emoji('javascript', 'this-is-react-repo')).to eq ':react:' 17 | end 18 | end 19 | 20 | context 'when there is no emoji in the hash' do 21 | it 'returns the default emoji (downcase language)' do 22 | expect(subject.language_emoji('Exotic', 'repo_name')).to eq ':exotic:' 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/services/slack_notification_service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'rails_helper' 2 | 3 | describe SlackNotificationService do 4 | before do 5 | stub_request(:post, 'https://slack.com/api/conversations.list') 6 | .to_return(status: 200, body: '', headers: {}) 7 | end 8 | 9 | describe 'initialization' do 10 | subject { described_class.new({}) } 11 | 12 | it 'logs an error if no "action" is set in params' do 13 | expect(Rails.logger).to receive(:warn).with('Warning! No action found on params: [{}]') 14 | subject 15 | end 16 | end 17 | 18 | describe '#send_notification' do 19 | subject { described_class.new(params).send_notification } 20 | 21 | let(:username) { Faker::Internet.username } 22 | let!(:user) do 23 | User.create(github_name: username, blacklisted: false) 24 | end 25 | 26 | context 'with valid params' do 27 | let(:random_action) { described_class::ACTION_METHODS.keys.sample } 28 | let(:channel_name) { described_class::DEFAULT_CHANNEL } 29 | let(:some_language) { 'Node' } 30 | let(:params) do 31 | { 32 | github_webhook: { 33 | action: random_action, 34 | label: { name: Faker::Internet.slug(words: '_') } 35 | }, 36 | channel: channel_name, 37 | pull_request: { 38 | user: { login: username }, 39 | body: Faker::Lorem.sentence 40 | }, 41 | repository: { 42 | name: Faker::Internet.slug, 43 | language: some_language 44 | } 45 | } 46 | end 47 | 48 | before do 49 | stub_request(:post, 'https://slack.com/api/chat.postMessage') 50 | .with( 51 | body: { 52 | 'channel': channel_name, 53 | 'text': ' :nodejs:', 54 | 'username': username 55 | } 56 | ).to_return(status: 200, body: '', headers: {}) 57 | end 58 | 59 | it 'sends the message' do 60 | expect_any_instance_of(described_class) 61 | .to receive(:send) 62 | .with(described_class::ACTION_METHODS[random_action]) 63 | .and_call_original 64 | subject 65 | end 66 | 67 | context 'with a PR from a blacklisted user' do 68 | let!(:user) { User.create(github_name: username, blacklisted: true) } 69 | 70 | it 'does NOT send the message' do 71 | expect_any_instance_of(described_class).not_to receive(:send) 72 | subject 73 | end 74 | end 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 2 | ENV['RAILS_ENV'] ||= 'test' 3 | require File.expand_path('../config/environment', __dir__) 4 | 5 | require 'factory_bot_rails' 6 | require 'helpers' 7 | require 'webmock/rspec' 8 | 9 | FactoryBot.factories.clear 10 | FactoryBot.reload 11 | 12 | Dir[Rails.root.join('spec/support/**/*.rb')].sort.each { |file| require file } 13 | 14 | RSpec.configure do |config| 15 | config.include Helpers 16 | config.expect_with :rspec do |expectations| 17 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 18 | end 19 | 20 | config.mock_with :rspec do |mocks| 21 | mocks.verify_partial_doubles = true 22 | end 23 | 24 | config.filter_run_excluding on_refactor: true 25 | 26 | config.shared_context_metadata_behavior = :apply_to_host_groups 27 | config.order = 'random' 28 | config.expect_with :rspec do |c| 29 | c.syntax = :expect 30 | end 31 | config.include FactoryBot::Syntax::Methods 32 | 33 | config.before :each do 34 | DatabaseCleaner.strategy = :transaction 35 | DatabaseCleaner.start 36 | ActionMailer::Base.deliveries.clear 37 | end 38 | 39 | config.after do 40 | DatabaseCleaner.clean 41 | end 42 | 43 | config.color = true 44 | end 45 | --------------------------------------------------------------------------------