├── .editorconfig ├── .env ├── .erdconfig ├── .github ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md └── labels.json ├── .gitignore ├── .rubocop.yml ├── .rubocop_todo.yml ├── .ruby-gemset ├── .ruby-version ├── .travis.yml ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── LICENSE ├── MAINTAINERS.md ├── Makefile ├── README.md ├── Rakefile ├── apiary.apib ├── app ├── admin │ ├── admin_user.rb │ ├── code_school.rb │ ├── dashboard.rb │ ├── location.rb │ ├── scholarship.rb │ ├── scholarship_application.rb │ ├── service.rb │ ├── team_member.rb │ └── user.rb ├── assets │ ├── javascripts │ │ └── active_admin.js.coffee │ └── stylesheets │ │ └── active_admin.scss ├── channels │ └── application_cable │ │ ├── channel.rb │ │ └── connection.rb ├── controllers │ ├── api │ │ └── v1 │ │ │ ├── airtable │ │ │ └── mentorships_controller.rb │ │ │ ├── code_schools_controller.rb │ │ │ ├── email_list_recipients_controller.rb │ │ │ ├── events_controller.rb │ │ │ ├── git_hub_statistics_controller.rb │ │ │ ├── locations_controller.rb │ │ │ ├── mentors_controller.rb │ │ │ ├── requests_controller.rb │ │ │ ├── resources_controller.rb │ │ │ ├── scholarship_applications_controller.rb │ │ │ ├── scholarships_controller.rb │ │ │ ├── services_controller.rb │ │ │ ├── sessions_controller.rb │ │ │ ├── slack_users_controller.rb │ │ │ ├── social_users_controller.rb │ │ │ ├── status_controller.rb │ │ │ ├── tags_controller.rb │ │ │ ├── team_members_controller.rb │ │ │ ├── users │ │ │ └── passwords_controller.rb │ │ │ ├── users_controller.rb │ │ │ └── votes_controller.rb │ ├── api_controller.rb │ ├── application_controller.rb │ ├── concerns │ │ └── .keep │ └── users │ │ ├── confirmations_controller.rb │ │ ├── omniauth_callbacks_controller.rb │ │ ├── passwords_controller.rb │ │ ├── registrations_controller.rb │ │ ├── sessions_controller.rb │ │ └── unlocks_controller.rb ├── inputs │ └── inet_input.rb ├── jobs │ ├── add_guest_to_send_grid_job.rb │ ├── add_user_to_send_grid_job.rb │ ├── application_job.rb │ ├── github_collect_statistics_job.rb │ ├── meetup_member_sync_job.rb │ ├── send_email_to_leaders_job.rb │ ├── slack_jobs.rb │ └── slack_jobs │ │ └── inviter_job.rb ├── lib │ ├── airtable │ │ ├── client.rb │ │ ├── error.rb │ │ └── mentorship.rb │ ├── code_schools.rb │ ├── discourse │ │ └── single_sign_on.rb │ ├── format_data.rb │ ├── git_hub │ │ ├── authentication.rb │ │ ├── client.rb │ │ ├── committer.rb │ │ ├── issues.rb │ │ ├── page_compiler.rb │ │ ├── presenter.rb │ │ ├── pull_requests.rb │ │ ├── settings.rb │ │ └── structure.rb │ ├── id_me.rb │ ├── json_web_token.rb │ ├── meetup.rb │ ├── seed_team_members.rb │ ├── send_grid_client.rb │ ├── send_grid_client │ │ ├── guest.rb │ │ └── response.rb │ ├── slack_service │ │ ├── client.rb │ │ └── error.rb │ └── users_by_location.rb ├── mailers │ ├── application_mailer.rb │ └── user_mailer.rb ├── models │ ├── ability.rb │ ├── admin_user.rb │ ├── application_record.rb │ ├── code_school.rb │ ├── concerns │ │ └── .keep │ ├── event.rb │ ├── git_hub_statistic.rb │ ├── git_hub_user.rb │ ├── location.rb │ ├── request.rb │ ├── resource.rb │ ├── role.rb │ ├── scholarship.rb │ ├── scholarship_application.rb │ ├── service.rb │ ├── slack_user.rb │ ├── team_member.rb │ ├── user.rb │ └── vote.rb ├── policies │ └── application_policy.rb ├── serializers │ ├── code_school_serializer.rb │ ├── location_serializer.rb │ ├── mentor_serializer.rb │ ├── request_serializer.rb │ └── user_serializer.rb └── views │ ├── devise │ ├── confirmations │ │ └── new.html.erb │ ├── mailer │ │ ├── confirmation_instructions.html.erb │ │ ├── email_changed.html.erb │ │ ├── password_change.html.erb │ │ ├── reset_password_instructions.html.erb │ │ └── unlock_instructions.html.erb │ ├── passwords │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── registrations │ │ ├── edit.html.erb │ │ └── new.html.erb │ ├── sessions │ │ └── new.html.erb │ ├── shared │ │ └── _links.html.erb │ └── unlocks │ │ └── new.html.erb │ ├── layouts │ ├── mailer.html.erb │ └── mailer.text.erb │ └── user_mailer │ ├── new_user_in_leader_area.html.erb │ └── welcome.html.erb ├── bin ├── bundle ├── deploy ├── publish ├── rails ├── rake ├── rancher_update ├── setup └── update ├── config.ru ├── config ├── application.rb ├── board_members.yml ├── boot.rb ├── cable.yml ├── code_schools.yml ├── database.yml ├── environment.rb ├── environments │ ├── development.rb │ ├── production.rb │ ├── staging.rb │ └── test.rb ├── initializers │ ├── active_admin.rb │ ├── application_controller_renderer.rb │ ├── backtrace_silencers.rb │ ├── core_extensions │ │ └── devise │ │ │ └── strategies │ │ │ └── json_web_token.rb │ ├── cors.rb │ ├── devise.rb │ ├── filter_parameter_logging.rb │ ├── inflections.rb │ ├── mime_types.rb │ ├── new_framework_defaults.rb │ ├── sidekiq.rb │ └── wrap_parameters.rb ├── job_schedule.yml ├── locales │ ├── devise.en.yml │ └── en.yml ├── puma.rb ├── resources.yml ├── routes.rb ├── secrets.yml ├── session_store.rb ├── spring.rb └── team_members.yml ├── db ├── migrate │ ├── 20170401221959_create_users.rb │ ├── 20170420033746_add_devise_to_users.rb │ ├── 20170519144023_add_fields_to_users.rb │ ├── 20170519145444_create_requests.rb │ ├── 20170519184936_create_services.rb │ ├── 20170520144243_create_squads.rb │ ├── 20170520150128_create_squad_mentors.rb │ ├── 20170520180320_add_members_to_squads.rb │ ├── 20170520232041_add_status_to_requests.rb │ ├── 20170613012405_create_events.rb │ ├── 20170616161847_change_url_to_url.rb │ ├── 20170622013242_add_verified_to_user.rb │ ├── 20170629014745_create_team_members.rb │ ├── 20170701172700_create_resources.rb │ ├── 20170701172846_create_votes.rb │ ├── 20170701194816_acts_as_taggable_on_migration.acts_as_taggable_on_engine.rb │ ├── 20170701194817_add_missing_unique_indices.acts_as_taggable_on_engine.rb │ ├── 20170701194818_add_taggings_counter_cache_to_tags.acts_as_taggable_on_engine.rb │ ├── 20170701194819_add_missing_taggable_index.acts_as_taggable_on_engine.rb │ ├── 20170701194820_change_collation_for_tag_names.acts_as_taggable_on_engine.rb │ ├── 20170701194821_add_missing_indexes_on_taggings.acts_as_taggable_on_engine.rb │ ├── 20170720215043_add_state_to_user.rb │ ├── 20170723213338_add_more_attributes_to_user.rb │ ├── 20170802201510_create_scholarships.rb │ ├── 20170802202307_create_applications.rb │ ├── 20170803215307_rename_applications_to_scholarship_applications.rb │ ├── 20170803223854_rename_start_date_to_open_time.rb │ ├── 20170806214216_add_additional_fields_for_profiles_to_users.rb │ ├── 20170807033541_create_git_hub_users.rb │ ├── 20170807033815_create_git_hub_statistics.rb │ ├── 20170809164559_add_scholarship_info_field_to_users.rb │ ├── 20170813163239_add_columns_to_events.rb │ ├── 20170821193457_change_scholarship_application.rb │ ├── 20170903130609_add_index_to_source_events.rb │ ├── 20170920093119_create_code_schools.rb │ ├── 20170920093217_create_locations.rb │ ├── 20170921055331_add_notes_to_code_schools.rb │ ├── 20171013230050_devise_create_admin_users.rb │ ├── 20171013230053_create_active_admin_comments.rb │ ├── 20171022215418_drop_squads.rb │ ├── 20180129223616_add_group_to_team_members.rb │ ├── 20180204235116_add_mooc_to_code_schools.rb │ ├── 20180401034441_create_roles.rb │ ├── 20180408172532_add_role_id_to_admin_user.rb │ ├── 20180416024105_add_email_to_team_member.rb │ ├── 20180420192231_add_is_partner_to_code_schools.rb │ ├── 20180422030623_create_slack_user.rb │ ├── 20180422031009_drop_user_slack_id.rb │ ├── 20180430161218_add_rep_name_to_codeschool.rb │ └── 20180711212702_add_militarystatus_to_user.rb ├── schema.rb └── seeds.rb ├── docker-compose.yml ├── docs ├── data_models │ ├── request.png │ └── squad.png ├── models │ └── generating_model_graph.md ├── setup │ ├── aws_vm_setup.md │ ├── docker_setup.md │ └── native_setup.md └── testing │ └── testing.md ├── lib └── tasks │ ├── .keep │ ├── auto_generate_diagram.rake │ ├── meetup.rake │ ├── resource.rake │ ├── roles.rake │ ├── schools_from_yml.rake │ ├── seed_members.rake │ ├── test.rake │ ├── update_is_partner_for_code_schools.rake │ ├── update_sabio.rake │ ├── update_schools_logo.rake │ └── users.rake ├── log └── .keep ├── public └── robots.txt ├── test ├── cassettes │ ├── airtable │ │ ├── client │ │ │ ├── get_records_successful.yml │ │ │ └── post_record_successful.yml │ │ └── mentorship │ │ │ ├── exceeded_rate_limit.yml │ │ │ ├── post_multiple_successful.yml │ │ │ ├── post_successful.yml │ │ │ └── successful.yml │ ├── git_hub │ │ ├── commits │ │ │ ├── slashbot │ │ │ │ ├── pr_6.yml │ │ │ │ └── pr_7.yml │ │ │ └── successful.yml │ │ ├── pull_request │ │ │ ├── slashbot │ │ │ │ ├── pr_6.yml │ │ │ │ └── pr_7.yml │ │ │ └── successful.yml │ │ └── search │ │ │ ├── issues │ │ │ └── successful.yml │ │ │ └── pull_requests │ │ │ ├── compile_prs.yml │ │ │ ├── success_for_multi_page.yml │ │ │ └── success_for_single_page.yml │ ├── slack_service │ │ └── client │ │ │ ├── invite_failure.yml │ │ │ └── invite_success.yml │ └── user_test │ │ └── accepts_non_us_zipcodes_uk_.yml ├── controllers │ ├── .keep │ └── api │ │ └── v1 │ │ ├── airtable │ │ └── mentorships_controller_test.rb │ │ ├── code_schools_controller_test.rb │ │ ├── email_list_recipients_controller_test.rb │ │ ├── locations_controller_test.rb │ │ ├── mentors_controller_test.rb │ │ ├── requests_controller_test.rb │ │ ├── resources_controller_test.rb │ │ ├── services_controller_test.rb │ │ ├── sessions_controller_test.rb │ │ ├── slack_users_controller_test.rb │ │ ├── social_users_controller_test.rb │ │ ├── tags_controller_test.rb │ │ ├── team_members_controller_test.rb │ │ ├── users_controller_test.rb │ │ └── votes_controller_test.rb ├── factories │ ├── code_schools.rb │ ├── events.rb │ ├── git_hub_statistics.rb │ ├── git_hub_users.rb │ ├── locations.rb │ ├── requests.rb │ ├── resources.rb │ ├── roles.rb │ ├── scholarship_applications.rb │ ├── scholarships.rb │ ├── services.rb │ ├── team_members.rb │ ├── users.rb │ └── votes.rb ├── integration │ ├── .keep │ └── events_create_test.rb ├── jobs │ ├── add_guest_to_send_grid_job_test.rb │ ├── add_user_to_send_grid_job_test.rb │ └── meetup_member_sync_job_test.rb ├── lib │ ├── code_schools_test.rb │ ├── format_data_test.rb │ ├── git_hub │ │ ├── authentication_test.rb │ │ ├── committer_test.rb │ │ └── presenter_test.rb │ ├── meetup_test.rb │ ├── seed_team_members_test.rb │ ├── send_grid_client │ │ └── guest_test.rb │ ├── send_grid_client_test.rb │ ├── update_school_logos_test.rb │ └── users_by_location_test.rb ├── mailers │ └── user_mailer_test.rb ├── models │ ├── .keep │ ├── code_schools_test.rb │ ├── event_test.rb │ ├── git_hub_statistic_test.rb │ ├── request_test.rb │ ├── resource_test.rb │ ├── role_test.rb │ ├── scholarship_test.rb │ ├── team_member_test.rb │ └── user_test.rb ├── requests │ ├── airtable │ │ ├── client_test.rb │ │ └── mentorship_test.rb │ ├── git_hub │ │ ├── client_test.rb │ │ ├── issues_test.rb │ │ ├── page_compiler_test.rb │ │ └── pull_requests_test.rb │ └── slack_service │ │ └── client_test.rb ├── support │ ├── code_schools │ │ └── test_config.yml │ └── meetups │ │ └── sample_api_responses.rb └── test_helper.rb └── tmp └── .keep /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # Tab indentation (no size specified) 13 | [Makefile] 14 | indent_style = tab 15 | 16 | [**.rb] 17 | indent_style = space 18 | indent_size = 2 19 | 20 | # Matches the exact files either package.json or .travis.yml 21 | [.travis.yml] 22 | indent_style = space 23 | indent_size = 2 24 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | COMPOSE_PROJECT_NAME=operationcodebackend 2 | REDIS_URL=redis://redis:6379/0 3 | RAILS_ENV=development 4 | POSTGRES_USER=postgres 5 | POSTGRES_HOST=operationcode-psql 6 | SLACK_SERVICE_URL=http://pybot.operationcode.org:8080/pybot/api/v1/slack 7 | SLACK_SERVICE_AUTH_TOKEN=XXXXX -------------------------------------------------------------------------------- /.erdconfig: -------------------------------------------------------------------------------- 1 | filename: docs/models/model_graph 2 | filetype: png 3 | title: "OperationCode Backend Domain Models" 4 | # List of model attributes to include in graph 5 | attribute: 6 | - foreign_keys 7 | - primary_keys 8 | - timestamps 9 | - inheritance 10 | - content 11 | # uses a notation style with more information built in. See documentation in docs/models/generating_model_graph.md 12 | notation: bachman 13 | orientation: horizontal 14 | # hides warnings from output when generating diagram 15 | warn: false 16 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | # Feature 3 | ## Why is this feature being added? 4 | 5 | 6 | ## What should your feature do? 7 | 8 | --- 9 | 10 | # Bug Report 11 | ## What is the current behavior? 12 | 13 | ## What is the expected behavior? 14 | 15 | ## What steps did you take to get this behavior? 16 | 17 | ## Additional Info 18 | ### Operating System 19 | 20 | ### Browser 21 | 22 | ### Screenshots 23 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Description of changes 2 | 3 | 4 | # Issue Resolved 5 | 6 | Fixes # 7 | -------------------------------------------------------------------------------- /.github/labels.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "name": "beginner friendly", "color": "#c2e0c6" }, 3 | { "name": "Status: Blocked", "color": "#e11d21" }, 4 | { "name": "Status: Duplicate", "color": "#fef2c0" }, 5 | { "name": "Status: In Progress", "color": "#cccccc" }, 6 | { "name": "Status: Available", "color": "#006b75" }, 7 | { "name": "Status: On Hold", "color": "#e11d21" }, 8 | { "name": "Type: Bug", "color": "#e11d21" }, 9 | { "name": "Type: Feature", "color": "#84b6eb" }, 10 | { "name": "Type: Question", "color": "#cc317c" }, 11 | { "name": "Priority: Low", "color": "#009800" }, 12 | { "name": "Priority: Medium", "color": "#fbca04" }, 13 | { "name": "Priority: High", "color": "#eb6420" }, 14 | { "name": "Priority: Critical", "color": "#e11d21" }, 15 | { "name": "Needs: Copy/Content", "color": "#d4c5f9" }, 16 | { "name": "Needs: More Detail", "color": "#b60205" } 17 | ] 18 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | 3 | AllCops: 4 | Exclude: 5 | - 'db/schema.rb' 6 | 7 | # Cop supports --auto-correct. 8 | # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. 9 | # SupportedStyles: single_quotes, double_quotes 10 | Style/StringLiterals: 11 | Exclude: 12 | - 'db/**/*' 13 | 14 | #turn off end of line checks, git will fix this 15 | EndOfLine: 16 | Enabled: false 17 | 18 | Metrics/ClassLength: 19 | Exclude: 20 | - 'test/models/user_test.rb' 21 | -------------------------------------------------------------------------------- /.ruby-gemset: -------------------------------------------------------------------------------- 1 | -global -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.3.3 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Since we build and test in Docker we dont 2 | # need node at build time 3 | language: c 4 | 5 | services: 6 | - docker 7 | 8 | script: 9 | - make build 10 | - | 11 | while ! psql --host=localhost --username=postgres -c 'SELECT 1'> /dev/null 2>&1; do 12 | echo 'Waiting for postgres...' 13 | sleep 1; 14 | done; 15 | - make test 16 | - if [ "$TRAVIS_PULL_REQUEST" == "false" ] && [ "$TRAVIS_BRANCH" == "master" ]; then make upgrade; fi 17 | 18 | env: 19 | secure: "AmnH0r1tF9HIVsUfhoJHzCEqTZz3TU4FUd2D+T0XzSDbz6kUSubmP4agYRsJI1xPTwegqSonwL0QGAlaIKEssz6rqAqBSvzHC95XvghP1HVVp2/j8vvFaJbpWsnTQ25FbMgDD89wHRKRs0qk2Ui0zi+CtNFylj83D13yoVys6C0=" 20 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.6.3 2 | 3 | RUN printf "deb http://deb.debian.org/debian/ stretch main\ndeb-src http://deb.debian.org/debian/ stretch main\ndeb http://security.debian.org stretch/updates main\ndeb-src http://security.debian.org stretch/updates main" > /etc/apt/sources.list 4 | 5 | RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs graphviz 6 | 7 | ENV APP_HOME /app 8 | 9 | RUN mkdir $APP_HOME 10 | WORKDIR $APP_HOME 11 | 12 | COPY Gemfile $APP_HOME/ 13 | COPY Gemfile.lock $APP_HOME/ 14 | 15 | ENV BUNDLE_GEMFILE=$APP_HOME/Gemfile \ 16 | BUNDLE_JOBS=2 \ 17 | BUNDLE_PATH=/bundle 18 | 19 | RUN gem update --system && \ 20 | gem install bundler --no-document --version $(tail -n 1 Gemfile.lock) | sed -e 's/^[[:space:]]*//' && \ 21 | bundle install --system 22 | 23 | COPY . $APP_HOME/ 24 | 25 | CMD ["bundle", "exec", "puma", "-C", "config/puma.rb"] 26 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | #ruby=ruby-2.3.3 2 | #ruby-gemset=operationcode_backend 3 | 4 | source 'https://rubygems.org' 5 | 6 | git_source(:github) do |repo_name| 7 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?('/') 8 | "https://github.com/#{repo_name}.git" 9 | end 10 | 11 | gem 'activeadmin', '~> 1.1' 12 | gem 'active_model_serializers' 13 | gem 'acts-as-taggable-on', '~> 5.0' 14 | gem 'cancancan', '~> 2.0' 15 | gem 'devise' 16 | gem 'geocoder' 17 | gem 'gibbon' 18 | gem 'httparty' 19 | gem 'jwt' 20 | gem 'lograge' 21 | gem 'logstash-event' 22 | gem 'operationcode-slack' 23 | gem 'pg', '~> 0.18' 24 | gem 'puma', '~> 3.0' 25 | gem 'rack-cors' 26 | gem 'rails', '~> 5.0.2' 27 | gem 'redis', '~> 3.0' 28 | gem 'rubocop', '~> 0.55.0', require: false 29 | gem 'sendgrid-ruby' 30 | gem 'sentry-raven' 31 | gem 'sidekiq' 32 | gem 'sidekiq-cron' 33 | gem 'skylight' 34 | gem 'pry-rails' 35 | 36 | group :development, :test do 37 | gem 'awesome_print', '~> 1.8' 38 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 39 | gem 'byebug', platform: :mri 40 | gem 'factory_girl_rails' 41 | gem 'faker' 42 | gem 'mocha' 43 | gem 'vcr', '~> 4.0.0' 44 | gem 'webmock', '~> 3.5.1' 45 | end 46 | 47 | group :development do 48 | gem 'listen', '~> 3.0.5' 49 | gem 'rails-erd' 50 | gem 'spring' 51 | gem 'spring-watcher-listen', '~> 2.0.0' 52 | end 53 | 54 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 55 | 56 | 57 | # Use ActiveModel has_secure_password 58 | # gem 'bcrypt', '~> 3.1.7' 59 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Operation Code 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /MAINTAINERS.md: -------------------------------------------------------------------------------- 1 | # Maintainers 2 | 3 | This file lists how the Operation Code Back End project is maintained. When making changes to the system, this file tells you who needs to review your contribution - you need a simple majority of maintainers for the relevant subsystems to provide a 👍 on your pull request. Additionally, you need to not receive a veto from a Sergeant or the Project Lead. 4 | 5 | Check out [how Operation Code Open Source projects are maintained](https://github.com/OperationCode/START_HERE/blob/61cebc02875ef448679e1130d3a68ef2f855d6c4/open_source_maintenance_policy.md) for details on the process, how to become a maintainer, sergeant, or the project lead. 6 | 7 | # Project Lead 8 | 9 | * [Nell Shamrell-Harrington](http://www.github.com/nellshamrell) 10 | 11 | # Sergeant 12 | 13 | * [Colleen Schnettler](http://www.github.com/leenyburger) 14 | * [Robb Kidd](http://www.github.com/robbkidd) 15 | 16 | # Maintainers 17 | 18 | * [William Montgomery](http://www.github.com/wimo7083) 19 | 20 | # Founding Contributors 21 | 22 | * [Harry Levine](https://github.com/hpjaj) -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/admin/admin_user.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register AdminUser do 2 | permit_params :email, :password, :password_confirmation, :role_id 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | column :email 8 | 9 | column 'Role' do |admin_user| 10 | admin_user.role.title 11 | end 12 | 13 | column :current_sign_in_at 14 | column :sign_in_count 15 | column :created_at 16 | actions 17 | end 18 | 19 | filter :email 20 | filter :current_sign_in_at 21 | filter :sign_in_count 22 | filter :created_at 23 | 24 | form do |f| 25 | f.inputs do 26 | f.input :email 27 | 28 | if current_admin_user.role.super_admin? 29 | f.input :role_id, label: 'Role', as: :select, collection: Role.all.order(:title).map { |role| [role.title, role.id]}, include_blank: false 30 | end 31 | 32 | f.input :password 33 | f.input :password_confirmation 34 | end 35 | 36 | f.actions 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/admin/code_school.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register CodeSchool do 2 | permit_params :name, :url, :logo, :full_time, :hardware_included, :has_online, :online_only, :created_at, :updated_at, :notes, :mooc, :rep_name, :rep_email, :is_partner 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :url 10 | column :logo 11 | column :full_time 12 | column :hardware_included 13 | column :has_online 14 | column :online_only 15 | column :created_at 16 | column :updated_at 17 | column :notes 18 | column :mooc 19 | column :rep_name 20 | column :rep_email 21 | column :is_partner 22 | column :created_at 23 | column :updated_at 24 | 25 | actions 26 | end 27 | 28 | form do |f| 29 | f.inputs do 30 | f.input :name 31 | f.input :url 32 | f.input :logo 33 | f.input :full_time 34 | f.input :hardware_included 35 | f.input :has_online 36 | f.input :online_only 37 | f.input :notes 38 | f.input :mooc 39 | f.input :rep_name 40 | f.input :rep_email 41 | f.input :is_partner 42 | end 43 | 44 | f.actions 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /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 | columns do 6 | column do 7 | panel 'New User Counts' do 8 | para "Today: #{User.count_created_since(Date.today)}" 9 | para "In the last week: #{User.count_created_since(1.week.ago)}" 10 | para "In the last month: #{User.count_created_since(1.month.ago)}" 11 | para "In the last year: #{User.count_created_since(1.year.ago)}" 12 | end 13 | end 14 | 15 | column do 16 | panel 'Verified Users' do 17 | para User.verified.count 18 | end 19 | end 20 | end 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/admin/location.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Location do 2 | permit_params :va_accepted, :address1, :address2, :city, :state, :zip, :code_school_id 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column 'Code School' do |location| 9 | location.code_school.name 10 | end 11 | 12 | column :va_accepted 13 | column :address1 14 | column :address2 15 | column :city 16 | column :state 17 | column :zip 18 | column :created_at 19 | column :updated_at 20 | 21 | actions 22 | end 23 | 24 | form do |f| 25 | f.inputs do 26 | f.input :code_school_id, label: 'Code School', as: :select, collection: CodeSchool.all.order(:name).map { |school| [school.name, school.id]}, include_blank: false 27 | f.input :va_accepted 28 | f.input :address1 29 | f.input :address2 30 | f.input :city 31 | f.input :state 32 | f.input :zip 33 | end 34 | 35 | f.actions 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /app/admin/scholarship.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Scholarship do 2 | permit_params :name, :description, :location, :terms, :open_time, :close_time, :created_at, :updated_at 3 | index do 4 | selectable_column 5 | id_column 6 | 7 | column :name 8 | column :description 9 | column :location 10 | column :terms 11 | column :open_time 12 | column :close_time 13 | column :created_at 14 | column :updated_at 15 | 16 | actions 17 | end 18 | 19 | form do |f| 20 | f.inputs do 21 | f.input :name 22 | f.input :description 23 | f.input :location 24 | f.input :terms 25 | f.input :open_time 26 | f.input :close_time 27 | end 28 | 29 | f.actions 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /app/admin/scholarship_application.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register ScholarshipApplication do 2 | permit_params :reason, :terms_accepted, :scholarship_id, :user_id 3 | index do 4 | selectable_column 5 | id_column 6 | 7 | column 'Scholarship' do |scholarship_id| 8 | scholarship_id.scholarship.name 9 | end 10 | 11 | column 'User' do |user_id| 12 | user_id.user.name 13 | end 14 | 15 | column :reason 16 | column :terms_accepted 17 | column :scholarship_id 18 | column :user_id 19 | column :created_at 20 | column :updated_at 21 | 22 | actions 23 | end 24 | 25 | form do |f| 26 | f.inputs do 27 | f.input :scholarship_id, label: 'Scholarship', as: :select, collection: Scholarship.all.order(:name).map { |scholarship| [scholarship.name, scholarship.id] }, include_blank: false 28 | f.input :user_id, label: 'User', as: :select, collection: User.verified.order(:last_name).map { |user| [user.name, user.id] }, include_blank: false 29 | f.input :reason 30 | f.input :terms_accepted 31 | end 32 | 33 | f.actions 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/admin/service.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register Service do 2 | permit_params :name 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :created_at 10 | column :updated_at 11 | 12 | actions 13 | end 14 | 15 | form do |f| 16 | f.inputs do 17 | f.input :name 18 | end 19 | 20 | f.actions 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/admin/team_member.rb: -------------------------------------------------------------------------------- 1 | ActiveAdmin.register TeamMember do 2 | permit_params :name, :role, :description, :group, :image_src, :email 3 | 4 | index do 5 | selectable_column 6 | id_column 7 | 8 | column :name 9 | column :role 10 | column :email 11 | column :created_at 12 | column :updated_at 13 | column :description 14 | column :group 15 | column :image_src 16 | 17 | actions 18 | end 19 | 20 | form do |f| 21 | f.inputs do 22 | f.input :name 23 | f.input :role 24 | f.input :email 25 | f.input :description 26 | f.input :group 27 | f.input :image_src 28 | end 29 | 30 | f.actions 31 | end 32 | end 33 | -------------------------------------------------------------------------------- /app/assets/javascripts/active_admin.js.coffee: -------------------------------------------------------------------------------- 1 | #= require active_admin/base 2 | -------------------------------------------------------------------------------- /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/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/v1/airtable/mentorships_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | module Airtable 4 | class MentorshipsController < ApiController 5 | before_action :authenticate_user! 6 | 7 | def index 8 | mentorship_data = ::Airtable::Mentorship.new.mentor_request_data 9 | 10 | render json: mentorship_data, status: :ok 11 | rescue ::Airtable::Error => e 12 | render json: { error: e.message }, status: :unprocessable_entity 13 | end 14 | 15 | def create 16 | mentor_request = ::Airtable::Mentorship.new.create_mentor_request(mentorship_params) 17 | 18 | render json: mentor_request, status: :created 19 | rescue ::Airtable::Error => e 20 | render json: { error: e.message }, status: :unprocessable_entity 21 | end 22 | 23 | private 24 | 25 | def mentorship_params 26 | params.permit( 27 | :slack_user, 28 | :email, 29 | :services, 30 | :skillsets, 31 | :additional_details, 32 | :mentor_requested 33 | ) 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /app/controllers/api/v1/code_schools_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class CodeSchoolsController < ApiController 4 | before_action :authenticate_user!, only: [:create, :update, :destroy] 5 | 6 | def index 7 | render json: CodeSchool.order(:name).includes(:locations) 8 | end 9 | 10 | def create 11 | school = CodeSchool.new(code_school_params) 12 | 13 | if school.save 14 | render json: school 15 | else 16 | render json: { errors: school.errors.full_messages } 17 | end 18 | end 19 | 20 | def show 21 | school = CodeSchool.find_by(id: params[:id]) 22 | if school 23 | render json: school 24 | else 25 | render json: { error: 'No such record' }, status: :not_found 26 | end 27 | end 28 | 29 | def update 30 | school = CodeSchool.find(params[:id]) 31 | if school.update(code_school_params) 32 | render json: school 33 | else 34 | render json: { errors: school.errors.full_messages } 35 | end 36 | end 37 | 38 | def destroy 39 | school = CodeSchool.find(params[:id]) 40 | if school.destroy 41 | render json: { status: :ok } 42 | else 43 | render json: { errors: school.errors.full_messages } 44 | end 45 | end 46 | 47 | private 48 | 49 | def code_school_params 50 | params.require(:code_school).permit(:name, :url, :logo, :full_time, 51 | :hardware_included, :has_online, 52 | :online_only, :mooc, :is_partner, 53 | :rep_name, :rep_email) 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /app/controllers/api/v1/email_list_recipients_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class EmailListRecipientsController < ApiController 4 | def create 5 | raise "Invalid email address: #{permitted_params[:email]}" unless valid_email? 6 | 7 | AddGuestToSendGridJob.perform_async(permitted_params[:email]) 8 | 9 | render json: { email: permitted_params[:email], guest: true }, status: :created 10 | rescue StandardError => e 11 | render json: { errors: e.message }, status: :unprocessable_entity 12 | end 13 | 14 | private 15 | 16 | def permitted_params 17 | params.permit(:email) 18 | end 19 | 20 | def valid_email? 21 | params[:email] =~ User::VALID_EMAIL 22 | end 23 | end 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/api/v1/events_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class EventsController < ApiController 4 | def index 5 | render json: Event.all, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/api/v1/git_hub_statistics_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class GitHubStatisticsController < ApiController 4 | def oc_totals 5 | render json: GitHub::Presenter.new(params).oc_totals, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | 10 | def totals_by_repository 11 | render json: GitHub::Presenter.new(params).totals_by_repository, status: :ok 12 | rescue StandardError => e 13 | render json: { errors: e.message }, status: :unprocessable_entity 14 | end 15 | 16 | def totals_for_user 17 | render json: GitHub::Presenter.new(params).totals_for_user, status: :ok 18 | rescue StandardError => e 19 | render json: { errors: e.message }, status: :unprocessable_entity 20 | end 21 | 22 | def totals_for_user_in_repository 23 | render json: GitHub::Presenter.new(params).totals_for_user_in_repository, status: :ok 24 | rescue StandardError => e 25 | render json: { errors: e.message }, status: :unprocessable_entity 26 | end 27 | 28 | def oc_averages 29 | render json: GitHub::Presenter.new(params).oc_averages, status: :ok 30 | rescue StandardError => e 31 | render json: { errors: e.message }, status: :unprocessable_entity 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /app/controllers/api/v1/locations_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class LocationsController < ApiController 4 | before_action :set_location, except: :create 5 | before_action :authenticate_user! 6 | 7 | def create 8 | location = Location.create!(location_params) 9 | 10 | render json: { location: location.id }, status: :created 11 | rescue ActiveRecord::RecordInvalid => e 12 | render json: { errors: e.message }, status: :unprocessable_entity 13 | end 14 | 15 | def update 16 | @location.update!(location_params) 17 | 18 | render json: { status: :ok } 19 | rescue ActiveRecord::RecordInvalid => e 20 | render json: { errors: e.message }, status: :unprocessable_entity 21 | end 22 | 23 | def destroy 24 | @location.destroy! 25 | 26 | render json: { status: :ok } 27 | rescue ActiveRecord::RecordNotDestroyed, NoMethodError => e 28 | render json: { errors: e.message }, status: :unprocessable_entity 29 | end 30 | 31 | private 32 | 33 | def set_location 34 | @location = Location.find_by(id: params[:id]) 35 | end 36 | 37 | def location_params 38 | params.require(:location).permit(:code_school_id, :va_accepted, :address1, :address2, :city, :state, :zip) 39 | end 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/api/v1/mentors_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class MentorsController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | mentors = User.mentors 8 | render json: mentors 9 | end 10 | 11 | def show 12 | mentor = User.mentors.find(params[:id]) 13 | render json: MentorSerializer.new(mentor) 14 | end 15 | 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/v1/requests_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class RequestsController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | authorize! :read, Request 8 | render json: Request.unclaimed.order('created_at DESC') 9 | end 10 | 11 | def create 12 | @request = current_user.requests.create(request_params) 13 | render json: @request 14 | end 15 | 16 | def show 17 | @request = Request.find_by(id: params[:id]) 18 | authorize! :read, @request 19 | render json: @request 20 | end 21 | 22 | def update 23 | request = Request.find_by(id: params[:id]) 24 | if request.update(request_params) 25 | render json: request 26 | else 27 | render nothing: true, status: :bad_request 28 | end 29 | end 30 | 31 | private 32 | 33 | def request_params 34 | params.require(:request) 35 | .permit(:details, :language, 36 | :service_id, :requested_mentor_id, 37 | :status, :assigned_mentor_id) 38 | end 39 | 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /app/controllers/api/v1/scholarship_applications_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ScholarshipApplicationsController < ApiController 4 | def create 5 | current_user.scholarship_applications.create! scholarship_application_params 6 | 7 | render json: { redirect_to: '/success' }, status: :created 8 | rescue StandardError => e 9 | render json: { error: e.message }, status: :bad_request 10 | end 11 | 12 | private 13 | 14 | def scholarship_application_params 15 | params.require(:scholarship_application).permit(:reason, :terms_accepted, :scholarship_id) 16 | end 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/controllers/api/v1/scholarships_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ScholarshipsController < ApiController 4 | def index 5 | render json: Scholarship.unexpired 6 | end 7 | 8 | def show 9 | scholarship = Scholarship.find_by(id: params[:id]) 10 | if scholarship 11 | render json: scholarship 12 | else 13 | render json: { error: 'No such record' }, status: :not_found 14 | end 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/controllers/api/v1/services_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class ServicesController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def index 7 | services = Service.all 8 | render json: services 9 | end 10 | 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/controllers/api/v1/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SessionsController < Devise::SessionsController 4 | respond_to :json 5 | before_action :set_default_response_format 6 | before_action :authenticate_user!, only: %i[sso] 7 | 8 | def create 9 | self.resource = warden.authenticate!(auth_options) 10 | sign_in(resource_name, resource) 11 | 12 | set_sso_response if sso_request? 13 | @redirect_path ||= '/profile' 14 | 15 | render json: { 16 | token: resource.token, 17 | user: UserSerializer.new(current_user), 18 | redirect_to: @redirect_path 19 | } 20 | end 21 | 22 | def sso 23 | set_sso_response 24 | render json: { redirect_to: @redirect_path } 25 | end 26 | 27 | def set_default_response_format 28 | request.format = 'json' 29 | end 30 | 31 | private 32 | 33 | def sso_request? 34 | params[:sso].present? && params[:sig].present? 35 | end 36 | 37 | def sso_params 38 | { sso: params[:sso], sig: params[:sig] } 39 | end 40 | 41 | def set_sso_response 42 | @sso = Discourse::SingleSignOn.parse(sso_params, Discourse::SingleSignOn::SECRET) 43 | @sso.email = current_user.email 44 | @sso.name = current_user.name 45 | @sso.username = current_user.email 46 | @sso.external_id = current_user.id 47 | @sso.custom_fields['verified'] = current_user.verified 48 | @sso.add_groups = 'mentors' if current_user.mentor 49 | @sso.sso_secret = Discourse::SingleSignOn::SECRET 50 | 51 | @redirect_path = @sso.to_url('https://community.operationcode.org/session/sso_login') 52 | end 53 | end 54 | end 55 | end 56 | -------------------------------------------------------------------------------- /app/controllers/api/v1/slack_users_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SlackUsersController < ApiController 4 | before_action :authenticate_py_bot! 5 | 6 | def create 7 | # TODO: Placeholder for PyBot call to create new record 8 | end 9 | 10 | def access 11 | render json: { message: 'You have access!' }, status: :ok 12 | end 13 | 14 | private 15 | 16 | def authenticate_py_bot! 17 | auth = JsonWebToken.decode(auth_token) 18 | 19 | if auth['key'] == Rails.application.secrets.py_bot_auth_key 20 | true 21 | elsif auth.dig(:errors).present? 22 | render json: { errors: auth.dig(:errors) }, status: :unauthorized 23 | else 24 | render json: { errors: ['Auth token is invalid'] }, status: :unauthorized 25 | end 26 | end 27 | 28 | def auth_token 29 | @auth_token ||= request.headers.fetch('Authorization', '').split(' ').last 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /app/controllers/api/v1/social_users_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class SocialUsersController < ApiController 4 | 5 | # For social media logins, renders the appropriate `redirect_to` path depending on whether the user is registered. 6 | # 7 | # Requires that the ActionController::Parameters contain an :email key. 8 | # For example: { email: "john@example.com" } 9 | # 10 | # @return [String] A string of the user's redirect_to path 11 | # @see https://github.com/zquestz/omniauth-google-oauth2#devise 12 | # 13 | def show 14 | user = User.find_by(email: params.dig(:email)); 15 | redirect_path = '/login' 16 | 17 | if user.nil? 18 | redirect_path = '/social_login' 19 | end 20 | render json: { redirect_to: redirect_path } 21 | end 22 | 23 | # For social media logins, creates the user in the database if necessary, 24 | # then logs them in, and renders the appropriate `redirect_to` path depending on whether the user is logging in 25 | # for the first time. 26 | # 27 | # @return [String] A string of the user's redirect_to path 28 | # @return [Json] A serialied JSON object derived from current_user 29 | # @return [Token] A token that the frontend stores to know the user is logged in 30 | # @see https://github.com/OperationCode/operationcode_backend/blob/master/app/controllers/api/v1/sessions_controller.rb#L8-L20 31 | # 32 | def create 33 | @user, redirect_path = User.fetch_social_user_and_redirect_path(params.dig(:user)) 34 | 35 | if @user.save 36 | sign_in @user, event: :authenticate_user 37 | render json: { 38 | token: @user.token, 39 | user: UserSerializer.new(current_user), 40 | redirect_to: redirect_path 41 | } 42 | else 43 | redirect_to new_user_registration_url, alert: @user.errors.full_messages.join("\n") 44 | end 45 | end 46 | 47 | private 48 | 49 | def social_user_params 50 | params.require(:user).permit( 51 | :email, 52 | :first_name, 53 | :last_name, 54 | :zip, 55 | :password 56 | ) 57 | end 58 | end 59 | end 60 | end 61 | -------------------------------------------------------------------------------- /app/controllers/api/v1/status_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class StatusController < ApiController 4 | before_action :authenticate_user!, only: :protected 5 | 6 | def all 7 | render json: { status: :ok } 8 | end 9 | 10 | def protected 11 | render json: { status: :ok, protected: true } 12 | end 13 | 14 | end 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /app/controllers/api/v1/tags_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class TagsController < ApiController 4 | def index 5 | render json: ActsAsTaggableOn::Tag.all, status: :ok 6 | rescue StandardError => e 7 | render json: { errors: e.message }, status: :unprocessable_entity 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/controllers/api/v1/team_members_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class TeamMembersController < ApiController 4 | before_action :authenticate_user!, except: :index 5 | 6 | def index 7 | render json: TeamMember.all, status: :ok 8 | end 9 | 10 | def create 11 | team_member = TeamMember.create! team_member_params 12 | 13 | render json: { team_member: team_member.id }, status: :created 14 | rescue StandardError => e 15 | render json: { errors: e.message }, status: :unprocessable_entity 16 | end 17 | 18 | def update 19 | team_member = TeamMember.find params[:id] 20 | 21 | team_member.update! team_member_params 22 | render json: { status: :ok } 23 | rescue StandardError => e 24 | render json: { errors: e.message }, status: :unprocessable_entity 25 | end 26 | 27 | def destroy 28 | team_member = TeamMember.find params[:id] 29 | 30 | team_member.destroy! 31 | render json: { status: :ok } 32 | rescue StandardError => e 33 | render json: { errors: e.message }, status: :unprocessable_entity 34 | end 35 | 36 | private 37 | 38 | def team_member_params 39 | params.permit(:name, :role, :group, :description, :image_src, :email) 40 | end 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /app/controllers/api/v1/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | module Users 4 | class PasswordsController < ApiController 5 | 6 | # Resets password token and sends reset password instructions by email. 7 | # 8 | # @see http://www.rubydoc.info/github/plataformatec/devise/master/Devise/Models/Recoverable 9 | # 10 | def forgot 11 | user = User.find_by email: params[:email] 12 | raise 'Could not find a user with that email address' unless user.present? 13 | user.generate_password_token! 14 | user.send_reset_password_instructions 15 | render json: { status: :ok } 16 | rescue StandardError => e 17 | render json: { errors: e.message }, status: :unprocessable_entity 18 | end 19 | 20 | def reset 21 | token = params[:reset_password_token].to_s 22 | user = User.with_reset_password_token(token) 23 | 24 | if user.present? && user.password_token_valid? 25 | if user.reset_password!(params[:password]) 26 | render json: { status: 'ok' }, status: :ok 27 | else 28 | render json: { error: user.errors.full_messages }, status: :unprocessable_entity 29 | end 30 | else 31 | render json: { error: ['Link not valid or expired. Try generating a new link.'] }, status: :not_found 32 | end 33 | end 34 | 35 | def update 36 | render json: { error: 'Password not present' }, status: :unprocessable_entity unless params[:password].present 37 | 38 | if current_user.reset_password(params[:password]) 39 | render json: { status: 'ok' }, status: :ok 40 | else 41 | render json: { errors: current_user.errors.full_messages }, status: :unprocessable_entity 42 | end 43 | end 44 | end 45 | end 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /app/controllers/api/v1/votes_controller.rb: -------------------------------------------------------------------------------- 1 | module Api 2 | module V1 3 | class VotesController < ApiController 4 | before_action :authenticate_user! 5 | 6 | def create 7 | vote = current_user.votes.create! resource_id: params[:resource_id] 8 | 9 | render json: { vote: vote.id }, status: :ok 10 | rescue StandardError => e 11 | render json: { errors: e.message }, status: :unprocessable_entity 12 | end 13 | 14 | def destroy 15 | vote = Vote.find params[:id] 16 | 17 | vote.destroy! 18 | render json: { status: :ok } 19 | rescue StandardError => e 20 | render json: { errors: e.message }, status: :unprocessable_entity 21 | end 22 | end 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/controllers/api_controller.rb: -------------------------------------------------------------------------------- 1 | class ApiController < ActionController::API 2 | include CanCan::ControllerAdditions 3 | wrap_parameters format: [:json] 4 | 5 | rescue_from CanCan::AccessDenied do |exception| 6 | exception.default_message = 'You are not authorized to access this resource.' 7 | render json: { error: exception.message }, status: :forbidden 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /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: :null_session 5 | 6 | def admin_access_denied(exception) 7 | redirect_to admin_root_path, alert: exception.message 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /app/controllers/users/confirmations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::ConfirmationsController < Devise::ConfirmationsController 2 | # GET /resource/confirmation/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/confirmation 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/confirmation?confirmation_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after resending confirmation instructions. 20 | # def after_resending_confirmation_instructions_path_for(resource_name) 21 | # super(resource_name) 22 | # end 23 | 24 | # The path used after confirmation. 25 | # def after_confirmation_path_for(resource_name, resource) 26 | # super(resource_name, resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/users/omniauth_callbacks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController 2 | # You should configure your model like this: 3 | # devise :omniauthable, omniauth_providers: [:twitter] 4 | 5 | # You should also create an action method in this controller like this: 6 | # def twitter 7 | # end 8 | 9 | # More info at: 10 | # https://github.com/plataformatec/devise#omniauth 11 | 12 | # GET|POST /resource/auth/twitter 13 | # def passthru 14 | # super 15 | # end 16 | 17 | # GET|POST /users/auth/twitter/callback 18 | # def failure 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # The path used when OmniAuth fails 25 | # def after_omniauth_failure_path_for(scope) 26 | # super(scope) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/controllers/users/passwords_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::PasswordsController < Devise::PasswordsController 2 | # GET /resource/password/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/password 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/password/edit?reset_password_token=abcdef 13 | # def edit 14 | # super 15 | # end 16 | 17 | # PUT /resource/password 18 | # def update 19 | # super 20 | # end 21 | 22 | # protected 23 | 24 | # def after_resetting_password_path_for(resource) 25 | # super(resource) 26 | # end 27 | 28 | # The path used after sending reset password instructions 29 | # def after_sending_reset_password_instructions_path_for(resource_name) 30 | # super(resource_name) 31 | # end 32 | end 33 | -------------------------------------------------------------------------------- /app/controllers/users/registrations_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::RegistrationsController < Devise::RegistrationsController 2 | # before_action :configure_sign_up_params, only: [:create] 3 | # before_action :configure_account_update_params, only: [:update] 4 | 5 | # GET /resource/sign_up 6 | # def new 7 | # super 8 | # end 9 | 10 | # POST /resource 11 | # def create 12 | # super 13 | # end 14 | 15 | # GET /resource/edit 16 | # def edit 17 | # super 18 | # end 19 | 20 | # PUT /resource 21 | # def update 22 | # super 23 | # end 24 | 25 | # DELETE /resource 26 | # def destroy 27 | # super 28 | # end 29 | 30 | # GET /resource/cancel 31 | # Forces the session data which is usually expired after sign 32 | # in to be expired now. This is useful if the user wants to 33 | # cancel oauth signing in/up in the middle of the process, 34 | # removing all OAuth session data. 35 | # def cancel 36 | # super 37 | # end 38 | 39 | # protected 40 | 41 | # If you have extra params to permit, append them to the sanitizer. 42 | # def configure_sign_up_params 43 | # devise_parameter_sanitizer.permit(:sign_up, keys: [:attribute]) 44 | # end 45 | 46 | # If you have extra params to permit, append them to the sanitizer. 47 | # def configure_account_update_params 48 | # devise_parameter_sanitizer.permit(:account_update, keys: [:attribute]) 49 | # end 50 | 51 | # The path used after sign up. 52 | # def after_sign_up_path_for(resource) 53 | # super(resource) 54 | # end 55 | 56 | # The path used after sign up for inactive accounts. 57 | # def after_inactive_sign_up_path_for(resource) 58 | # super(resource) 59 | # end 60 | end 61 | -------------------------------------------------------------------------------- /app/controllers/users/sessions_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::SessionsController < Devise::SessionsController 2 | # before_action :configure_sign_in_params, only: [:create] 3 | 4 | # GET /resource/sign_in 5 | # def new 6 | # super 7 | # end 8 | 9 | # POST /resource/sign_in 10 | # def create 11 | # super 12 | # end 13 | 14 | # DELETE /resource/sign_out 15 | # def destroy 16 | # super 17 | # end 18 | 19 | # protected 20 | 21 | # If you have extra params to permit, append them to the sanitizer. 22 | # def configure_sign_in_params 23 | # devise_parameter_sanitizer.permit(:sign_in, keys: [:attribute]) 24 | # end 25 | end 26 | -------------------------------------------------------------------------------- /app/controllers/users/unlocks_controller.rb: -------------------------------------------------------------------------------- 1 | class Users::UnlocksController < Devise::UnlocksController 2 | # GET /resource/unlock/new 3 | # def new 4 | # super 5 | # end 6 | 7 | # POST /resource/unlock 8 | # def create 9 | # super 10 | # end 11 | 12 | # GET /resource/unlock?unlock_token=abcdef 13 | # def show 14 | # super 15 | # end 16 | 17 | # protected 18 | 19 | # The path used after sending unlock password instructions 20 | # def after_sending_unlock_instructions_path_for(resource) 21 | # super(resource) 22 | # end 23 | 24 | # The path used after unlocking the resource 25 | # def after_unlock_path_for(resource) 26 | # super(resource) 27 | # end 28 | end 29 | -------------------------------------------------------------------------------- /app/inputs/inet_input.rb: -------------------------------------------------------------------------------- 1 | class InetInput < Formtastic::Inputs::StringInput 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/add_guest_to_send_grid_job.rb: -------------------------------------------------------------------------------- 1 | class AddGuestToSendGridJob 2 | include Sidekiq::Worker 3 | 4 | # Adds the passed guest to our Contact List in SendGrid. 5 | # 6 | # @param guest_email [String] A string of the guest's email 7 | # 8 | def perform(guest_email) 9 | SendGridClient.new.add_user guest(guest_email) 10 | end 11 | 12 | private 13 | 14 | # Creates a user-like Struct, representing a guest. 15 | # 16 | # @param guest_email [String] A string of the guest's email 17 | # @return [Struct] Returns a user-like Struct 18 | # 19 | def guest(guest_email) 20 | SendGridClient::Guest.user(guest_email) 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /app/jobs/add_user_to_send_grid_job.rb: -------------------------------------------------------------------------------- 1 | class AddUserToSendGridJob < ApplicationJob 2 | include Sidekiq::Worker 3 | 4 | def perform(user_id) 5 | user = User.find(user_id) 6 | SendGridClient.new.add_user(user) 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob 2 | include Sidekiq::Worker 3 | end 4 | -------------------------------------------------------------------------------- /app/jobs/github_collect_statistics_job.rb: -------------------------------------------------------------------------------- 1 | class GithubCollectStatisticsJob 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | limit = GitHub::Client.new.rate_limit 6 | logger.info("Github Ratelimit: #{limit}") 7 | 8 | GitHub::PullRequests.new.fetch_and_save! 9 | GitHub::Issues.new.fetch_and_save! 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/jobs/meetup_member_sync_job.rb: -------------------------------------------------------------------------------- 1 | class MeetupMemberSyncJob 2 | include Sidekiq::Worker 3 | 4 | def perform 5 | meetup_client = Meetup.new 6 | members = meetup_client.members_by_email 7 | 8 | User.where(email: members.keys).each do |user| 9 | member = members[user.email] 10 | user.latitude ||= member['lat'] 11 | user.longitude ||= member['lon'] 12 | user.city ||= member['city'] 13 | user.state ||= member['state'] 14 | 15 | # Only update a user if values have changed 16 | user.save if user.changed? 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/jobs/send_email_to_leaders_job.rb: -------------------------------------------------------------------------------- 1 | class SendEmailToLeadersJob < ApplicationJob 2 | include Sidekiq::Worker 3 | 4 | def perform(_user_id) 5 | logger.debug 'Deprecated pathway, trying to determine what is placing this on queue.' 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /app/jobs/slack_jobs.rb: -------------------------------------------------------------------------------- 1 | class SlackJobs < ApplicationJob 2 | end 3 | -------------------------------------------------------------------------------- /app/jobs/slack_jobs/inviter_job.rb: -------------------------------------------------------------------------------- 1 | class SlackJobs 2 | class InviterJob < SlackJobs 3 | include Sidekiq::Worker 4 | sidekiq_options retry: 5 5 | 6 | def perform(email) 7 | Raven.user_context email: email 8 | Raven.tags_context slack_invite: 'yes' 9 | 10 | SlackService::Client.new.invite(email) 11 | end 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/lib/airtable/error.rb: -------------------------------------------------------------------------------- 1 | module Airtable 2 | class Error < StandardError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/lib/airtable/mentorship.rb: -------------------------------------------------------------------------------- 1 | module Airtable 2 | class Mentorship 3 | attr_reader :client 4 | 5 | def initialize 6 | @client = Airtable::Client.new 7 | end 8 | 9 | # Fetches all the records from the mentors, services, and skillsets Airtables, 10 | # and re-formats the data into a custom hash 11 | # 12 | # @return [Hash] Hash of mentor, services, and skillsets data 13 | # 14 | def mentor_request_data 15 | { 16 | mentors: data_for('Mentors', name_key: 'Full Name'), 17 | services: data_for('Services'), 18 | skillsets: data_for('Skillsets') 19 | } 20 | end 21 | 22 | # Creates a new mentor request record in the Mentor Request Airtable 23 | # 24 | # @param body [Hash] Hash of mentor request attributes 25 | # @return [Hash] Hash of the newly created mentor request Airtable record 26 | # 27 | def create_mentor_request(body) 28 | request_body = { 29 | 'fields' => { 30 | 'Slack User' => body[:slack_user], 31 | 'Email' => body[:email], 32 | 'Service' => format_for_posting(body[:services]), 33 | 'Skillsets' => format_for_posting(body[:skillsets]), 34 | 'Additional Details' => body[:additional_details], 35 | 'Mentor Requested' => format_for_posting(body[:mentor_requested]) 36 | } 37 | }.to_json 38 | validate_user(body[:email]) 39 | client.post_record('Mentor Request', request_body) 40 | end 41 | 42 | private 43 | 44 | def data_for(table, name_key: 'Name') 45 | records = client.get_records_for(table) 46 | 47 | records['records'].map do |record| 48 | { 49 | id: record['id'], 50 | name: record.dig('fields', name_key) 51 | } 52 | end 53 | end 54 | 55 | def validate_user(email) 56 | return if Rails.env.test? # I really don't like this, TODO: replace with a proper mock 57 | return unless Slack::Utils.new.email_is_registered(email) 58 | raise Airtable::Error, "#{email} is not registered on slack" 59 | end 60 | 61 | # Converts a comma separated string into an array of strings 62 | # 63 | # @param data_string [String] Comma separated strings of data 64 | # @return [Array] An array of stripped strings 65 | # 66 | def format_for_posting(data_string) 67 | data_string.split(',').map(&:strip) 68 | end 69 | end 70 | end 71 | -------------------------------------------------------------------------------- /app/lib/code_schools.rb: -------------------------------------------------------------------------------- 1 | class CodeSchools 2 | def self.all(config = Rails.root + 'config/code_schools.yml') 3 | YAML.load_file(config) 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /app/lib/format_data.rb: -------------------------------------------------------------------------------- 1 | class FormatData 2 | 3 | # Converts a comma-separated string into an array of its contents. 4 | # 5 | # @param comma_separated_string [String] String of comma-separated values, i.e '80123, 80202' 6 | # @return [Array] An array of strings, i.e. ['80123', '80202'] 7 | # 8 | def self.csv_to_array(comma_separated_string) 9 | comma_separated_string.upcase.split(',').map(&:strip) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/lib/git_hub/authentication.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Authentication 3 | BASIC = 'basic_authentication' 4 | O_AUTH_2_TOKEN = 'o_auth_2_token' 5 | O_AUTH_2_KEY_SECRET = 'o_auth_2_key_secret' 6 | 7 | attr_reader :options, :auth_level 8 | 9 | def initialize(options) 10 | @options = options 11 | @auth_level = GitHub::Settings.authentication_level 12 | end 13 | 14 | def set_options 15 | return options unless Rails.env.prod? 16 | 17 | authentication_options.deep_merge options 18 | end 19 | 20 | private 21 | 22 | def authentication_options 23 | case auth_level 24 | when BASIC 25 | # tbd 26 | when O_AUTH_2_TOKEN 27 | o_auth_2_token_options 28 | when O_AUTH_2_KEY_SECRET 29 | key_secret_options 30 | end 31 | end 32 | 33 | def o_auth_2_token_options 34 | { 35 | headers: { 36 | 'Authorization' => "Bearer #{GitHub::Settings.o_auth_2_token}" 37 | } 38 | } 39 | end 40 | 41 | def key_secret_options 42 | { 43 | query: { 44 | client_id: GitHub::Settings.client_id, 45 | client_secret: GitHub::Settings.client_secret, 46 | } 47 | } 48 | end 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /app/lib/git_hub/committer.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Committer 3 | def self.find_or_create_user!(user_attrs) 4 | GitHubUser.find_or_create_by!(git_hub_id: user_attrs[:id]) do |user| 5 | user.git_hub_login = user_attrs[:login] 6 | user.avatar_url = user_attrs[:avatar_url] 7 | user.api_url = user_attrs[:api_url] 8 | user.html_url = user_attrs[:html_url] 9 | end 10 | end 11 | 12 | def self.find_or_create_statistic!(stat_attrs, source_type, git_hub_user_id) 13 | GitHubStatistic.find_or_create_by!(source_type: source_type, source_id: stat_attrs[:source_id]) do |stat| 14 | stat.git_hub_user_id = git_hub_user_id 15 | stat.state = stat_attrs[:state] 16 | stat.additions = stat_attrs[:additions].to_i 17 | stat.deletions = stat_attrs[:deletions].to_i 18 | stat.repository = stat_attrs[:repository] 19 | stat.url = stat_attrs[:url] 20 | stat.title = stat_attrs[:title] 21 | stat.number = stat_attrs[:number] 22 | stat.completed_on = stat_attrs[:closed_on] 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/lib/git_hub/issues.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Issues 3 | attr_reader :client, :compiled_issues, :existing_issue_ids, :date 4 | 5 | def initialize 6 | @client = GitHub::Client.new 7 | @compiled_issues = [] 8 | @existing_issue_ids = GitHubStatistic.issues.pluck(:source_id) 9 | @date = GitHubStatistic.last_issue_completed_on 10 | end 11 | 12 | def fetch_and_save!(print_results: true) 13 | get_issues.each do |issue| 14 | git_hub_user = GitHub::Committer.find_or_create_user! issue[:git_hub_user] 15 | git_hub_issue_stat = GitHub::Committer.find_or_create_statistic! issue, issue[:source_type], git_hub_user.id 16 | 17 | next unless print_results 18 | p 'git_hub_user:' 19 | p git_hub_user 20 | 21 | p 'git_hub_issue_stat:' 22 | p git_hub_issue_stat 23 | end 24 | end 25 | 26 | private 27 | 28 | def get_issues 29 | client.repositories.each do |repo| 30 | build_details_for repo 31 | end 32 | 33 | compiled_issues 34 | end 35 | 36 | def build_details_for(repo) 37 | get_all_issues_for(repo).each do |issue| 38 | next if existing_issue_ids.include? issue['id'].to_s 39 | next unless assignee_present? issue 40 | 41 | compiled_issues << GitHub::Structure.new(issue, repo).issue 42 | end 43 | end 44 | 45 | def get_all_issues_for(repo) 46 | query = { 47 | repo: repo, 48 | type: 'issue', 49 | is: 'closed', 50 | page_number: 1, 51 | filters: "+closed:>=#{date}" 52 | } 53 | response = client.search_for(query) 54 | 55 | if response.headers['link'].present? 56 | GitHub::PageCompiler.new(query, response, client).compile_prs 57 | else 58 | response['items'] 59 | end 60 | end 61 | 62 | def assignee_present?(issue) 63 | issue['assignee'].present? && issue['assignee']['login'].present? 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /app/lib/git_hub/page_compiler.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class PageCompiler 3 | attr_reader :search_params, :response, :page_count, :client 4 | 5 | # @see https://developer.github.com/v3/guides/traversing-with-pagination/#consuming-the-information 6 | # 7 | def initialize(search_params, response, client) 8 | @search_params = search_params 9 | @response = response 10 | @page = 2 11 | @page_count = pages_in(response) - 1 12 | @client = client 13 | end 14 | 15 | def compile_prs 16 | compiled_prs = response['items'] 17 | 18 | page_count.times do 19 | pr_response = client.search_for(search_params.merge({ page_number: @page })) 20 | 21 | compiled_prs << pr_response['items'] 22 | 23 | @page += 1 24 | end 25 | 26 | compiled_prs.flatten 27 | end 28 | 29 | def compile_commits 30 | compiled_commits = response.parsed_response 31 | 32 | page_count.times do 33 | response << client.commits_for(search_params[:repo], search_params[:pr_number], @page) 34 | 35 | compiled_commits << response.parsed_response 36 | 37 | @page += 1 38 | end 39 | 40 | compiled_commits.flatten 41 | end 42 | 43 | private 44 | 45 | def pages_in(response) 46 | pages = response.headers['link'].split(',').last.match(/&page=(\d+).*$/)[1] 47 | 48 | pages.to_i 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /app/lib/git_hub/settings.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Settings 3 | BACKEND = 'operationcode_backend' 4 | FRONTEND = 'operationcode_frontend' 5 | OC = 'operationcode' 6 | BOT = 'operationcode_bot' 7 | SLASHBOT = 'operationcode_slashbot' 8 | 9 | def self.owner 10 | 'OperationCode' 11 | end 12 | 13 | def self.repositories 14 | [BACKEND, FRONTEND, OC, BOT, SLASHBOT] 15 | end 16 | 17 | # @see https://developer.github.com/v3/#user-agent-required 18 | # 19 | def self.user_agent 20 | owner 21 | end 22 | 23 | # Number of items that are returned per page, per request. 24 | # Max of 100. 25 | # 26 | # @see https://developer.github.com/v3/#pagination 27 | # 28 | def self.per_page 29 | 100 30 | end 31 | 32 | def self.client_id 33 | Rails.application.secrets.git_hub_client_id 34 | end 35 | 36 | def self.client_secret 37 | Rails.application.secrets.git_hub_client_secret 38 | end 39 | 40 | def self.o_auth_2_token 41 | Rails.application.secrets.git_hub_oauth_token 42 | end 43 | 44 | # There are three ways to authenticate through GitHub's API v3. 45 | # Choices are: BASIC, O_AUTH_2_TOKEN, or O_AUTH_2_KEY_SECRET. 46 | # 47 | # This method sets the authentication level from constants in 48 | # GitHub::Authentication 49 | # 50 | # @see https://developer.github.com/v3/#authentication 51 | # 52 | def self.authentication_level 53 | GitHub::Authentication::O_AUTH_2_KEY_SECRET 54 | end 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /app/lib/git_hub/structure.rb: -------------------------------------------------------------------------------- 1 | module GitHub 2 | class Structure 3 | attr_reader :repository, :response 4 | 5 | def initialize(response, repository) 6 | @response = response 7 | @repository = repository 8 | end 9 | 10 | def pull_request 11 | { 12 | source_type: GitHubStatistic::PR, 13 | source_id: response['id'], 14 | repository: repository, 15 | number: response['number'], 16 | state: response['state'], 17 | additions: response['additions'], 18 | deletions: response['deletions'], 19 | url: response['html_url'], 20 | title: response['title'], 21 | closed_on: response['merged_at'].to_date, 22 | git_hub_user: append_git_hub_user('user') 23 | } 24 | end 25 | 26 | def commit 27 | { 28 | source_type: GitHubStatistic::COMMIT, 29 | source_id: response['sha'], 30 | url: response['html_url'], 31 | title: response['commit']['message'], 32 | repository: repository, 33 | closed_on: response['commit']['committer']['date'].to_date, 34 | git_hub_user: append_git_hub_user('author') 35 | } 36 | end 37 | 38 | def issue 39 | { 40 | source_type: GitHubStatistic::ISSUE, 41 | source_id: response['id'], 42 | url: response['html_url'], 43 | title: response['title'], 44 | number: response['number'], 45 | state: response['state'], 46 | repository: repository, 47 | closed_on: response['closed_at'].to_date, 48 | git_hub_user: append_git_hub_user('assignee') 49 | } 50 | end 51 | 52 | private 53 | 54 | def append_git_hub_user(user_key) 55 | { 56 | login: response[user_key]['login'], 57 | avatar_url: response[user_key]['avatar_url'], 58 | api_url: response[user_key]['url'], 59 | html_url: response[user_key]['html_url'], 60 | id: response[user_key]['id'], 61 | } 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /app/lib/id_me.rb: -------------------------------------------------------------------------------- 1 | require 'httparty' 2 | 3 | class IdMe 4 | include HTTParty 5 | base_uri 'api.id.me' 6 | 7 | private_class_method :headers 8 | 9 | def self.verify!(access_token) 10 | options = { headers: headers } 11 | 12 | response = get("/api/public/v2/attributes.json?access_token=#{access_token}", options) 13 | 14 | verified = response.body['verified'] == 'verified' ? true : false 15 | 16 | fail response.body if !verified 17 | 18 | verified 19 | end 20 | 21 | def self.headers 22 | { 'Accepts' => 'application/json' } 23 | end 24 | end 25 | -------------------------------------------------------------------------------- /app/lib/json_web_token.rb: -------------------------------------------------------------------------------- 1 | class JsonWebToken 2 | def self.encode(payload, expiration = nil) 3 | expiration ||= Rails.application.secrets.jwt_expiration_hours 4 | 5 | payload = payload.dup 6 | payload['exp'] = expiration.to_i.hours.from_now.to_i 7 | 8 | JWT.encode(payload, Rails.application.secrets.jwt_secret) 9 | rescue => e 10 | raise "Failed to encode JsonWebToken due to: #{e}" 11 | end 12 | 13 | def self.decode(token) 14 | decoded_token = JWT.decode(token, Rails.application.secrets.jwt_secret) 15 | 16 | decoded_token.first 17 | rescue JWT::ExpiredSignature 18 | { errors: ['Auth token has expired'], status: 401 } 19 | rescue JWT::DecodeError 20 | { errors: ['Invalid auth token'], status: 401 } 21 | rescue => e 22 | Rails.logger.debug "Failed to decode token: '#{token}'" 23 | Rails.logger.debug e 24 | nil 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /app/lib/seed_team_members.rb: -------------------------------------------------------------------------------- 1 | class SeedTeamMembers 2 | BOARD_DATA_PATH = 'board_members.yml'.freeze 3 | TEAM_DATA_PATH = 'team_members.yml'.freeze 4 | class << self 5 | def from_yaml(file_name, group) 6 | members_seed_file = Rails.root.join('config', file_name) 7 | members = YAML.load_file(members_seed_file) 8 | members.each do |member| 9 | create_or_update(member.merge(group: group)) 10 | end 11 | end 12 | 13 | def seed_all 14 | seed_board 15 | seed_team 16 | end 17 | 18 | def seed_board 19 | from_yaml( 20 | BOARD_DATA_PATH, 21 | TeamMember::BOARD_GROUP_NAME 22 | ) 23 | end 24 | 25 | def seed_team 26 | from_yaml( 27 | TEAM_DATA_PATH, 28 | TeamMember::TEAM_GROUP_NAME 29 | ) 30 | end 31 | 32 | def clean_seed 33 | TeamMember.delete_all 34 | seed_all 35 | end 36 | 37 | private 38 | 39 | def create_or_update(member) 40 | TeamMember.find_or_create_by!(name: member['name']) do |c| 41 | c.update!(member) 42 | end 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /app/lib/send_grid_client/guest.rb: -------------------------------------------------------------------------------- 1 | # The OC has end users that would like to be on our emailing list; however, 2 | # have not committed to creating a User account with us, yet. 3 | # 4 | # This class allows us to create Structs for these 'guests', that respond 5 | # identically to a User object, within the SendGridClient class. 6 | # 7 | class SendGridClient::Guest 8 | Visitor = Struct.new(:id, :email, :first_name, :last_name) do 9 | def attributes 10 | to_h.as_json 11 | end 12 | end 13 | 14 | # Creates a user-like Struct, to represent an OC guest. 15 | # 16 | # Defaults the :id attribute to 'guest-id', as their is no database id; 17 | # however, the id needs to be logged for debugging in the SendGridClient. 18 | # 19 | # @param email_address [String] A string of the guest's email address 20 | # @return [Struct] A user-like struct 21 | # 22 | def self.user(email_address) 23 | Visitor.new('guest-id', email_address, '', '') 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /app/lib/send_grid_client/response.rb: -------------------------------------------------------------------------------- 1 | class SendGridClient::Response 2 | attr_reader :body, :status_code 3 | 4 | def initialize(response) 5 | @response = response 6 | @body = JSON.parse(response.body) unless response.body.blank? 7 | @status_code = response.status_code 8 | end 9 | 10 | def successful? 11 | successful_status_code && successful_error_count 12 | end 13 | 14 | private 15 | 16 | def successful_status_code 17 | status_code == '201' 18 | end 19 | 20 | # Some requests don't return a body so we default to just checking 21 | # the status code 22 | # 23 | def successful_error_count 24 | return true if body.blank? 25 | 26 | body['error_count'] == 0 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /app/lib/slack_service/client.rb: -------------------------------------------------------------------------------- 1 | module SlackService 2 | class Client 3 | include HTTParty 4 | 5 | base_uri ENV['SLACK_SERVICE_URL'] 6 | 7 | attr_reader :headers 8 | 9 | def initialize 10 | bearer_token = "Bearer #{ENV['SLACK_SERVICE_AUTH_TOKEN']}" 11 | @headers = { Authorization: bearer_token } 12 | end 13 | 14 | def verify(email) 15 | response = self.class.get( 16 | '/verify', 17 | headers: headers, 18 | query: { email: email } 19 | ) 20 | 21 | Rails.logger.info "Slack service #verify response: #{response.inspect}" 22 | 23 | return_value_for(response) 24 | end 25 | 26 | def invite(email) 27 | response = self.class.post( 28 | '/invite', 29 | headers: headers, 30 | body: { email: email }.to_json 31 | ) 32 | 33 | Rails.logger.info "Slack service #invite response: #{response.inspect}" 34 | 35 | return_value_for(response) 36 | end 37 | 38 | private 39 | 40 | def response_json(response) 41 | response.present? ? response.parsed_response : {} 42 | end 43 | 44 | def return_value_for(response) 45 | json = response_json(response) 46 | case response.code 47 | when 200 48 | json 49 | else 50 | raise SlackService::Error, response.inspect 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /app/lib/slack_service/error.rb: -------------------------------------------------------------------------------- 1 | module SlackService 2 | class Error < StandardError 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /app/lib/users_by_location.rb: -------------------------------------------------------------------------------- 1 | class UsersByLocation 2 | def initialize(params) 3 | @state = params[:state] 4 | @zip = params[:zip] 5 | @city = params[:city] 6 | @lat_long = params[:lat_long] 7 | @radius = params[:radius] || 20 8 | end 9 | 10 | def count 11 | if @state.present? 12 | User.count_by_state @state 13 | elsif @zip.present? 14 | User.count_by_zip @zip 15 | elsif @city.present? 16 | User.count_by_location @city, @radius 17 | elsif @lat_long.present? 18 | User.count_by_location @lat_long, @radius 19 | else 20 | raise 'A city, zip code, or latitude/longitude must be provided' 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: 'from@example.com' 3 | layout 'mailer' 4 | end 5 | -------------------------------------------------------------------------------- /app/mailers/user_mailer.rb: -------------------------------------------------------------------------------- 1 | class UserMailer < ActionMailer::Base 2 | default from: 'staff@operationcode.org' 3 | 4 | def welcome(user) 5 | @user = user 6 | mail to: @user.email, subject: 'Welcome to Operation Code!' 7 | end 8 | 9 | def new_user_in_leader_area(leader, new_user) 10 | @new_user = new_user 11 | mail to: leader.email, subject: 'A new user has joined within 50 miles of you' 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /app/models/ability.rb: -------------------------------------------------------------------------------- 1 | 2 | class Ability 3 | include CanCan::Ability 4 | 5 | # Follows CanCanCan best practices for granting permissions. 6 | # 7 | # @see https://github.com/CanCanCommunity/cancancan/wiki/Defining-Abilities%3A-Best-Practices#give-permissions-dont-take-them-away 8 | # 9 | def initialize(user) 10 | return unless user.persisted? 11 | 12 | if user.class == AdminUser 13 | return unless user.role&.board_accessible? 14 | can :read, ActiveAdmin::Page, name: 'Dashboard', namespace_name: 'admin' 15 | can :read, CodeSchool 16 | can :read, Location 17 | can :read, Scholarship 18 | can :read, ScholarshipApplication 19 | can :read, Service 20 | can :read, TeamMember 21 | can :read, User 22 | can [:read, :update], AdminUser, id: user.id 23 | 24 | return unless user.role&.admin_accessible? 25 | can :read, :all 26 | can :manage, CodeSchool 27 | can :manage, Location 28 | can :manage, Scholarship 29 | can :manage, ScholarshipApplication 30 | can :manage, Service 31 | can :manage, TeamMember 32 | 33 | return unless user.role&.super_admin? 34 | can :manage, :all 35 | else 36 | return unless user.mentor? 37 | can :read, Request 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /app/models/admin_user.rb: -------------------------------------------------------------------------------- 1 | class AdminUser < ApplicationRecord 2 | # Include default devise modules. Others available are: 3 | # :confirmable, :lockable, :timeoutable and :omniauthable 4 | devise :database_authenticatable, 5 | :recoverable, :rememberable, :trackable, :validatable 6 | 7 | belongs_to :role 8 | 9 | before_save :set_role 10 | 11 | private 12 | 13 | def set_role 14 | if role_id.blank? 15 | self.role_id = Role.find_by(title: Role::BOARD)&.id 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | self.abstract_class = true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/code_school.rb: -------------------------------------------------------------------------------- 1 | class CodeSchool < ApplicationRecord 2 | validates :name, :url, :logo, presence: true 3 | validates_inclusion_of :full_time, :hardware_included, :has_online, :online_only, :in => [true, false] 4 | has_many :locations, -> { order('state ASC, city ASC') }, dependent: :destroy 5 | end 6 | -------------------------------------------------------------------------------- /app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OperationCode/operationcode_backend/ae23877a168b7a1d0fb248001b6bc149f28cf29b/app/models/concerns/.keep -------------------------------------------------------------------------------- /app/models/event.rb: -------------------------------------------------------------------------------- 1 | class Event < ApplicationRecord 2 | validates :name, :start_date, :source_updated, :source_id, :source_type, :address1, :city, presence: true 3 | validates :source_id, uniqueness: { scope: :source_type } 4 | 5 | VALID_URL_REGEX = URI::regexp 6 | validates :url, format: {with:VALID_URL_REGEX} 7 | end 8 | -------------------------------------------------------------------------------- /app/models/git_hub_user.rb: -------------------------------------------------------------------------------- 1 | class GitHubUser < ApplicationRecord 2 | has_many :git_hub_statistics 3 | 4 | validates :git_hub_login, presence: true 5 | end 6 | -------------------------------------------------------------------------------- /app/models/location.rb: -------------------------------------------------------------------------------- 1 | class Location < ApplicationRecord 2 | belongs_to :code_school 3 | validates :address1, :city, :state, :zip, presence: true 4 | validates :va_accepted, inclusion: { in: [true, false] } 5 | end 6 | -------------------------------------------------------------------------------- /app/models/request.rb: -------------------------------------------------------------------------------- 1 | class Request < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :requested_mentor, class_name: 'User', optional: true 4 | belongs_to :assigned_mentor, class_name: 'User', optional: true 5 | belongs_to :service 6 | 7 | validates_presence_of :user 8 | 9 | scope :unclaimed, -> { where(assigned_mentor: nil) } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/resource.rb: -------------------------------------------------------------------------------- 1 | class Resource < ApplicationRecord 2 | acts_as_taggable 3 | 4 | has_many :votes 5 | 6 | validates :name, :url, :category, presence: true 7 | 8 | # Uses ActsAsTaggableOn gem to return relevant Resources. 9 | # 10 | # @see https://github.com/mbleigh/acts-as-taggable-on#usage 11 | # 12 | def self.with_tags(tags='') 13 | if tags.present? 14 | tagged_with(tags, any: true, parse: true) 15 | else 16 | all 17 | end 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/models/role.rb: -------------------------------------------------------------------------------- 1 | class Role < ApplicationRecord 2 | SUPER_ADMIN = 'super_admin' 3 | ADMIN = 'admin' 4 | BOARD = 'board_member' 5 | 6 | has_many :users 7 | has_many :admin_users 8 | 9 | validates :title, presence: true, uniqueness: true 10 | 11 | def board_accessible? 12 | [BOARD, ADMIN, SUPER_ADMIN].include? title 13 | end 14 | 15 | def admin_accessible? 16 | [ADMIN, SUPER_ADMIN].include? title 17 | end 18 | 19 | def super_admin? 20 | title == SUPER_ADMIN 21 | end 22 | 23 | def admin? 24 | title == ADMIN 25 | end 26 | 27 | def board? 28 | title == BOARD 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /app/models/scholarship.rb: -------------------------------------------------------------------------------- 1 | class Scholarship < ApplicationRecord 2 | has_many :scholarship_applications 3 | 4 | scope :unexpired, -> { where('close_time > ? OR close_time IS NULL', Time.current) } 5 | end 6 | -------------------------------------------------------------------------------- /app/models/scholarship_application.rb: -------------------------------------------------------------------------------- 1 | class ScholarshipApplication < ApplicationRecord 2 | validates :user_id, uniqueness: {scope: :scholarship_id} 3 | 4 | belongs_to :user 5 | belongs_to :scholarship 6 | 7 | delegate :name, to: :user, prefix: true 8 | delegate :email, to: :user, prefix: true 9 | end 10 | -------------------------------------------------------------------------------- /app/models/service.rb: -------------------------------------------------------------------------------- 1 | class Service < ApplicationRecord 2 | validates_presence_of :name 3 | end 4 | -------------------------------------------------------------------------------- /app/models/slack_user.rb: -------------------------------------------------------------------------------- 1 | class SlackUser < ApplicationRecord 2 | validates :slack_id, :slack_name, :slack_real_name, :slack_email, :user_id, presence: true 3 | end 4 | -------------------------------------------------------------------------------- /app/models/team_member.rb: -------------------------------------------------------------------------------- 1 | class TeamMember < ApplicationRecord 2 | BOARD_GROUP_NAME = 'board'.freeze 3 | EXECUTIVE_GROUP_NAME = 'executive'.freeze 4 | TEAM_GROUP_NAME = 'team'.freeze 5 | 6 | validates :name, :role, :group, presence: true 7 | validates :group, inclusion: { 8 | in: [BOARD_GROUP_NAME, EXECUTIVE_GROUP_NAME, TEAM_GROUP_NAME] 9 | } 10 | end 11 | -------------------------------------------------------------------------------- /app/models/vote.rb: -------------------------------------------------------------------------------- 1 | class Vote < ApplicationRecord 2 | belongs_to :user 3 | belongs_to :resource, counter_cache: true 4 | 5 | validates :user_id, :resource_id, presence: true 6 | validates :user_id, uniqueness: { scope: :resource_id, message: 'has already voted for this resource' } 7 | end 8 | -------------------------------------------------------------------------------- /app/policies/application_policy.rb: -------------------------------------------------------------------------------- 1 | class ApplicationPolicy 2 | 3 | end 4 | -------------------------------------------------------------------------------- /app/serializers/code_school_serializer.rb: -------------------------------------------------------------------------------- 1 | class CodeSchoolSerializer < ActiveModel::Serializer 2 | attributes :name, :url, :logo, :full_time, :hardware_included, :has_online, :online_only, :mooc, :is_partner, :rep_name, :rep_email 3 | has_many :locations 4 | end 5 | -------------------------------------------------------------------------------- /app/serializers/location_serializer.rb: -------------------------------------------------------------------------------- 1 | class LocationSerializer < ActiveModel::Serializer 2 | attributes :va_accepted, :address1, :address2, :city, :state, :zip 3 | end 4 | -------------------------------------------------------------------------------- /app/serializers/mentor_serializer.rb: -------------------------------------------------------------------------------- 1 | class MentorSerializer < UserSerializer 2 | attributes :bio 3 | 4 | end 5 | -------------------------------------------------------------------------------- /app/serializers/request_serializer.rb: -------------------------------------------------------------------------------- 1 | class RequestSerializer < ActiveModel::Serializer 2 | attributes :id, 3 | :details, 4 | :language, 5 | :status, 6 | :created_at 7 | 8 | belongs_to :user 9 | belongs_to :requested_mentor 10 | belongs_to :assigned_mentor 11 | belongs_to :service 12 | end 13 | -------------------------------------------------------------------------------- /app/serializers/user_serializer.rb: -------------------------------------------------------------------------------- 1 | class UserSerializer < ActiveModel::Serializer 2 | attributes :id, :email, :zip, :first_name, :last_name, 3 | :mentor, :verified 4 | attributes :role 5 | 6 | def role 7 | object.role&.title 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /app/views/devise/confirmations/new.html.erb: -------------------------------------------------------------------------------- 1 |
Welcome <%= @email %>!
2 | 3 |You can confirm your account email through the link below:
4 | 5 |<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
6 | -------------------------------------------------------------------------------- /app/views/devise/mailer/email_changed.html.erb: -------------------------------------------------------------------------------- 1 |Hello <%= @email %>!
2 | 3 | <% if @resource.try(:unconfirmed_email?) %> 4 |We're contacting you to notify you that your email is being changed to <%= @resource.unconfirmed_email %>.
5 | <% else %> 6 |We're contacting you to notify you that your email has been changed to <%= @resource.email %>.
7 | <% end %> 8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/password_change.html.erb: -------------------------------------------------------------------------------- 1 |Hello <%= @resource.email %>!
2 | 3 |We're contacting you to notify you that your password has been changed.
4 | -------------------------------------------------------------------------------- /app/views/devise/mailer/reset_password_instructions.html.erb: -------------------------------------------------------------------------------- 1 |Hello <%= @resource.email %>!
2 | 3 |Someone has requested a link to change your password. You can do this through the link below.
4 | 5 |<%= link_to 'Change my password', "https://operationcode.org/reset_password?reset_password_token=#{@token}" %>
6 |If you didn't request this, please ignore this email.
7 |Your password won't change until you access the link above and create a new one.
8 | -------------------------------------------------------------------------------- /app/views/devise/mailer/unlock_instructions.html.erb: -------------------------------------------------------------------------------- 1 |Hello <%= @resource.email %>!
2 | 3 |Your account has been locked due to an excessive number of unsuccessful sign in attempts.
4 | 5 |Click the link below to unlock your account:
6 | 7 |<%= link_to 'Unlock my account', unlock_url(@resource, unlock_token: @token) %>
8 | -------------------------------------------------------------------------------- /app/views/devise/passwords/edit.html.erb: -------------------------------------------------------------------------------- 1 |Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %>
42 | 43 | <%= link_to "Back", :back %> 44 | -------------------------------------------------------------------------------- /app/views/devise/registrations/new.html.erb: -------------------------------------------------------------------------------- 1 |
2 | Welcome to Operation Code!
3 |
6 | Be on the lookout for an email invite to the Operation Code Slack community. Slack is where we communicate online in real-time to ask questions, collaborate, and share information. It's a pivotal aspect of our community! If you've never used Slack before, consider watching this instructional video on using the application.
7 |
10 | You've just joined a community of 3000+ software developers, veterans, and military spouses, who are all looking to further their skills and careers in tech! We are a decentralized organization, and we work both online (via Slack) and in local chapters across the country. We highly recommend giving yourself an introduction in the #general channel on Slack, and to sign up to go to your next local meetup! An example could be:
11 | "Hi all. My name is David. I'm from Washington and I'm interested in web development!"
12 | Please read our Code of Conduct, as well.
13 |
16 | Are you interested in helping Operation Code's website through code or design, and building your experience and portfolio? We're open source and love onboarding new developers! Read more here, and join the #oc-projects channel on Slack - feel free to ask about anything. 17 |
18 | 19 |Again, welcome aboard, and don't forget to follow and share Operation Code on Twitter and Facebook.
20 | 21 |
22 | Cheers, and keep coding,
23 |
the Operation Code team :)
24 |
P.S. - If you do not receive an invite from Slack within 24 hours, please email our support team, or ping us on Twitter at @operation_code.
27 | -------------------------------------------------------------------------------- /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/deploy: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | 5 | KUBE_CONFIG=kubectl.config 6 | 7 | # This script updates our kubernetes deploymnet from travis 8 | 9 | # Install kubectl 10 | curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/amd64/kubectl 11 | chmod +x ./kubectl 12 | 13 | # Generate our kubectl config 14 | cat << EOF > $KUBE_CONFIG 15 | apiVersion: v1 16 | clusters: 17 | - cluster: 18 | certificate-authority-data: $KUBE_CA_DATA 19 | server: $KUBE_SERVER 20 | name: cluster-operationcode 21 | contexts: 22 | - context: 23 | cluster: cluster-operationcode 24 | namespace: operationcode 25 | user: $KUBE_USER 26 | name: operationcode 27 | - context: 28 | cluster: cluster-operationcode 29 | namespace: operationcode-staging 30 | user: $KUBE_USER 31 | name: staging 32 | current-context: operationcode 33 | kind: Config 34 | preferences: {} 35 | users: 36 | - name: $KUBE_USER 37 | user: 38 | client-certificate-data: $KUBE_CLIENT_CERT 39 | client-key-data: $KUBE_CLIENT_KEY 40 | EOF 41 | 42 | # Updates the deployment 43 | # 44 | # Container names come from: 45 | # https://github.com/OperationCode/operationcode_infra/blob/master/kubernetes/operationcode_backend/deployment.yml 46 | # 47 | # current-context 48 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend app=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} 49 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend sidekiq=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} 50 | # specifically do staging 51 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend app=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} -n operationcode-staging 52 | KUBECONFIG=${KUBE_CONFIG} ./kubectl set image deployment/operationcode-backend sidekiq=operationcode/operationcode_backend:${TRAVIS_BUILD_NUMBER} -n operationcode-staging 53 | -------------------------------------------------------------------------------- /bin/publish: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | # Publishes the most recent web container to docker hubs repo. 4 | # This script assumes docker push works. 5 | # You must set up docker push on your own. 6 | 7 | set -eu 8 | 9 | DOCKER_REPO="operationcode/operationcode_backend" 10 | 11 | RAILS_ROOT="$(dirname $0)/.." 12 | cd $RAILS_ROOT 13 | 14 | PROJECT_NAME=$(grep COMPOSE_PROJECT_NAME $RAILS_ROOT/.env | cut -d= -f2) 15 | 16 | IMAGE_NAME="${PROJECT_NAME}_web" 17 | IMAGE_ID=$(docker images $IMAGE_NAME:latest --format "{{.ID}}") 18 | 19 | if [ -n "$DOCKER_USERNAME" ]; then echo "Found username"; fi 20 | if [ -n "$DOCKER_PASSWORD" ]; then echo "Found password"; fi 21 | 22 | if [ -n "$DOCKER_USERNAME" ] && [ -n "$DOCKER_PASSWORD" ]; then 23 | echo "Logging in using ENV creds" 24 | docker login -u="$DOCKER_USERNAME" -p="$DOCKER_PASSWORD" 25 | fi 26 | 27 | docker tag $IMAGE_ID $DOCKER_REPO 28 | docker tag $IMAGE_ID ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 29 | docker push $DOCKER_REPO 30 | docker push ${DOCKER_REPO}:${TRAVIS_BUILD_NUMBER} 31 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path('../config/application', __dir__) 3 | require_relative '../config/boot' 4 | require 'rails/commands' 5 | -------------------------------------------------------------------------------- /bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative '../config/boot' 3 | require 'rake' 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /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/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 | module OperationcodeBackend 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | 25 | # Only loads a smaller set of middleware suitable for API only apps. 26 | # Middleware like session, flash, cookies can be added back manually. 27 | # Skip views, helpers and assets when generating a new resource. 28 | config.api_only = true 29 | 30 | config.middleware.use Rack::MethodOverride 31 | config.middleware.use ActionDispatch::Flash 32 | config.middleware.use ActionDispatch::Cookies 33 | config.middleware.use ActionDispatch::Session::CookieStore 34 | 35 | config.active_job.queue_adapter = :sidekiq 36 | config.log_level = :debug 37 | 38 | config.logger = Logger.new(STDOUT) 39 | config.lograge.enabled = true 40 | 41 | config.secret_path = Rails.root.join('run/secrets') 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /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 | staging: 8 | adapter: redis 9 | url: redis://localhost:6379/1 10 | 11 | production: 12 | adapter: redis 13 | url: redis://localhost:6379/1 14 | -------------------------------------------------------------------------------- /config/database.yml: -------------------------------------------------------------------------------- 1 | # PostgreSQL. Versions 9.1 and up are supported. 2 | # 3 | # Install the pg driver: 4 | # gem install pg 5 | # On OS X with Homebrew: 6 | # gem install pg -- --with-pg-config=/usr/local/bin/pg_config 7 | # On OS X with MacPorts: 8 | # gem install pg -- --with-pg-config=/opt/local/lib/postgresql84/bin/pg_config 9 | # On Windows: 10 | # gem install pg 11 | # Choose the win32 build. 12 | # Install PostgreSQL and put its /bin directory on your path. 13 | # 14 | # Configure Using Gemfile 15 | # gem 'pg' 16 | # 17 | default: &default 18 | adapter: postgresql 19 | encoding: unicode 20 | # For details on connection pooling, see rails configuration guide 21 | # http://guides.rubyonrails.org/configuring.html#database-pooling 22 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 23 | user: <%= ENV['POSTGRES_USER'] %> 24 | host: <%= ENV['POSTGRES_HOST'] %> 25 | password: <%= ENV['POSTGRES_PASSWORD'] %> 26 | 27 | development: 28 | <<: *default 29 | database: operationcode_development 30 | 31 | test: 32 | <<: *default 33 | database: operationcode_test 34 | 35 | staging: 36 | <<: *default 37 | database: operationcode_production 38 | 39 | production: 40 | <<: *default 41 | host: <%= ENV['POSTGRES_HOST'].present? ? ENV['POSTGRES_HOST'] : 'invalid_postgres_host' %> 42 | database: operationcode_production 43 | -------------------------------------------------------------------------------- /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 = false 31 | 32 | config.action_mailer.perform_caching = false 33 | 34 | # Print deprecation notices to the Rails logger. 35 | config.active_support.deprecation = :log 36 | 37 | # Raise an error on page load if there are pending migrations. 38 | config.active_record.migration_error = :page_load 39 | 40 | config.action_mailer.default_url_options = { host: 'localhost:3000' } 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 | config.lograge.enabled = true 48 | config.lograge.formatter = Lograge::Formatters::Logstash.new 49 | end 50 | -------------------------------------------------------------------------------- /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.log_level = :warn 44 | end 45 | -------------------------------------------------------------------------------- /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 but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /config/initializers/core_extensions/devise/strategies/json_web_token.rb: -------------------------------------------------------------------------------- 1 | module Devise 2 | module Strategies 3 | class JsonWebToken < Base 4 | def valid? 5 | request.headers['Authorization'].present? 6 | end 7 | 8 | def authenticate! 9 | return fail! unless claims 10 | return fail! unless claims.has_key?('user_id') 11 | 12 | success! User.find_by_id claims['user_id'] 13 | end 14 | 15 | protected 16 | 17 | def claims 18 | strategy, token = request.headers['Authorization'].split(' ') 19 | 20 | return nil unless valid_strategy?(strategy) 21 | 22 | claim = ::JsonWebToken.decode(token) 23 | Rails.logger.debug 'Claim decoded' 24 | claim 25 | rescue => e 26 | Rails.logger.debug "Failed to decode token #{token}" 27 | Rails.logger.debug e 28 | nil 29 | end 30 | 31 | def valid_strategy?(strategy) 32 | (strategy || '').downcase == 'bearer' 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | allow do 10 | origins '*' 11 | 12 | resource '*', 13 | headers: :any, 14 | methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /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 Guide for Upgrading Ruby on Rails 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 | # Do not halt callback chains when a callback returns false. Previous versions had true. 15 | ActiveSupport.halt_callback_chains_on_return_false = false 16 | 17 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 18 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 19 | -------------------------------------------------------------------------------- /config/initializers/sidekiq.rb: -------------------------------------------------------------------------------- 1 | require 'sidekiq' 2 | require 'sidekiq-cron' 3 | 4 | schedule_file = 'config/job_schedule.yml' 5 | 6 | Sidekiq.configure_server do |config| 7 | # https://stackoverflow.com/questions/28412913/disable-automatic-retry-with-activejob-used-with-sidekiq 8 | # prevent excessive retries from some default status. 9 | config.death_handlers << lambda { |job, e| 10 | # thanks rob for the nice message 11 | evt = Raven::Event.new(message: "#{job['class']} #{job['jid']} done run outta tries.") 12 | Raven.send_event(evt) 13 | } 14 | Rails.logger = Sidekiq::Logging.logger 15 | ActiveRecord::Base.logger = Sidekiq::Logging.logger 16 | 17 | Sidekiq::Cron::Job.load_from_hash YAML.load_file(schedule_file) if File.exist?(schedule_file) 18 | end 19 | -------------------------------------------------------------------------------- /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/job_schedule.yml: -------------------------------------------------------------------------------- 1 | ## Examples of job configurations 2 | 3 | #my_first_job: 4 | # cron: "*/5 * * * *" 5 | # class: "HardWorker" 6 | # queue: hard_worker 7 | # 8 | #second_job: 9 | # cron: "*/30 * * * *" # execute at every 30 minutes 10 | # class: "HardWorker" 11 | # queue: hard_worker_long 12 | # args: 13 | # hard: "stuff" 14 | 15 | 16 | # At 04:00 every day run the meetup member sync job 17 | meetup_member_sync_job: 18 | #cron: "0 4 * * *" 19 | cron: "0 0 5 31 2 ?" # only run Feb 31 20 | class: "MeetupMemberSyncJob" 21 | queue: "default" 22 | 23 | # At 03:00 every day run the github collect statistics job 24 | github_collect_statistics_job: 25 | #cron: "0 3 * * *" 26 | cron: "0 0 5 31 2 ?" # only run Feb 31 27 | class: "GithubCollectStatisticsJob" 28 | queue: "default" 29 | -------------------------------------------------------------------------------- /config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | activerecord: 25 | errors: 26 | models: 27 | scholarship_application: 28 | attributes: 29 | user_id: 30 | taken: "One application per scholarship." 31 | -------------------------------------------------------------------------------- /config/puma.rb: -------------------------------------------------------------------------------- 1 | workers Integer(ENV['WEB_CONCURRENCY'] || 2) 2 | threads_count = Integer(ENV['RAILS_MAX_THREADS'] || 5) 3 | threads threads_count, threads_count 4 | 5 | preload_app! 6 | 7 | rackup DefaultRackup 8 | port ENV['PORT'] || 3000 9 | environment ENV['RACK_ENV'] || 'development' 10 | 11 | on_worker_boot do 12 | # Worker specific setup for Rails 4.1+ 13 | # See: https://devcenter.heroku.com/articles/deploying-rails-applications-with-the-puma-web-server#on-worker-boot 14 | ActiveRecord::Base.establish_connection 15 | end 16 | -------------------------------------------------------------------------------- /config/session_store.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | Rails.application.config.session_store :cookie_store, key: '_app_session' 4 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /config/team_members.yml: -------------------------------------------------------------------------------- 1 | # Operation Code Team Member Listing 2 | # Each entry should contain the following: 3 | #- name: