diff --git a/.gitignore b/.gitignore index 8e0a125df..19e9fe2f3 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ # Ignore bundler config. /.bundle +vendor/bundle # Ignore all logfiles and tempfiles. /log/* @@ -39,4 +40,4 @@ latest.dump /node_modules # Ignore storybook static site generation -storybook-static/ \ No newline at end of file +storybook-static/ diff --git a/.rubocop.yml b/.rubocop.yml index 181c4b32c..9f38c43c5 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -10,6 +10,11 @@ RSpec/DescribeClass: Exclude: - spec/features/* - spec/requests/* + +Metrics/LineLength: + Description: 'Limit lines to 100 characters.' + Max: 100 + ############################# AllCops: @@ -219,11 +224,6 @@ Style/LineEndConcatenation: line end. Enabled: false -Metrics/LineLength: - Description: 'Limit lines to 80 characters.' - StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' - Max: 80 - Metrics/MethodLength: Description: 'Avoid methods longer than 10 lines of code.' StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' diff --git a/.travis.yml b/.travis.yml index a6b08a165..803315eb4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,58 +1,54 @@ sudo: required - language: ruby - cache: directories: - - vendor/bundle - - node_modules - + - vendor/bundle + - node_modules rvm: - - 2.5.1 - +- 2.5.1 addons: - postgresql: "9.6" + postgresql: '9.6' chrome: stable - env: global: - - RAILS_ENV=test - + - RAILS_ENV=test + - CC_TEST_REPORTER_ID=4d20fbc40b511f2ecc8322241848be64ef7f951fe34c8be956fb8675f51ee46d branches: only: - - master - + - master install: - - bundle install --path vendor/bundle - - bundle exec chromedriver-update - - yarn install - - curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter - - chmod +x ./cc-test-reporter - - +- bundle install --path vendor/bundle +- bundle exec chromedriver-update +- yarn install +- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 + > ./cc-test-reporter +- chmod +x ./cc-test-reporter before_script: - - cp config/database.yml.travis config/database.yml - - psql -c 'create database travis_ci_test;' -U postgres - +- cp config/database.yml.travis config/database.yml +- psql -c 'create database travis_ci_test;' -U postgres script: - - bundle exec rails webpacker:compile - - bundle exec rake db:schema:load - - ./cc-test-reporter before-build - - bundle exec rspec spec - - ./cc-test-reporter format-coverage -t simplecov -o coverage/codeclimate.simplecov.json - - yarn test - - ./cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.lcov.json - - ./cc-test-reporter sum-coverage coverage/codeclimate.*.json - - ./cc-test-reporter upload-coverage - +- bundle exec rails webpacker:compile +- bundle exec rake db:schema:load +- "./cc-test-reporter before-build" +- bundle exec rspec spec +- "./cc-test-reporter format-coverage -t simplecov -o coverage/codeclimate.simplecov.json" +- yarn test +- "./cc-test-reporter format-coverage -t lcov -o coverage/codeclimate.lcov.json" +- "./cc-test-reporter sum-coverage coverage/codeclimate.*.json" +- "./cc-test-reporter upload-coverage" +before_deploy: +- chmod -x ./after_deploy.sh deploy: provider: heroku - api_key: $HEROKU_AUTH_TOKEN + api_key: "$HEROKU_AUTH_TOKEN" app: master: practicaldev run: - - "rake db:migrate" - - restart - + - rake db:migrate after_deploy: - - ./after_deploy.sh +- "./after_deploy.sh" +notifications: + slack: + on_success: always + on_failure: always + secure: fMRLZjxN+14dpkRygscyRujF/DjJKiIRqi1wn2oRxAn5uNgv/wvV2iVgIvHcK31wtGcyVTntvTiDi7E4a6ufO+dYN3VuDG5oppK9zbCQl1dpdlKxFajAgS6KMD65XfzL51KDoznJ/3CKx/Y5ZFHqM+XmbMdrZCTvXg4wu4HLFJmkc6T4j7UgMCLlGN+9zI23OCmg0Pc9bQa8t5hyuqQISAyCrcWYqLc6XnbI0S9avudoW+Wi1gYu2uGYNYMPxjKCulvCSxxQl3zWS49nicQpanDrJwFETRhuqWeZ2fVlhArbdUn8B6q0gaB552YUx1L0Uyc02UoxxYTTsrBfSvW2v1zRstGHOruVgB9Uywbm1ajDaB5jNTrBbxI9To3dNANSm4WiBWQQPCXXacSkbhFk+4q7+DH7MdjvHhD1cgoa14ZL+Qkmzycw0RYMlYVbzhutUS0b19isNj7jIgDgV4oCPH22qFyIXDtuLxg75Rkf7jvsX3VUFS5Lp1uokA1pkvb9rlf/0o3maWYpwX/qyUTwceZHYk0TbLxEeFkv0BrJd7asQ4PMhA95BF6BxsO8mXOlmr3gO+nUFp0DlFQ4qslX45WuWt+rZxY3uXsD2ZLs9XYmDBG5Gae994E50klvgKt0uJtkE78sXXKkKCdB9ohZZi9OPacP2yzLdxDd/654h34= diff --git a/Envfile b/Envfile new file mode 100644 index 000000000..981c859c0 --- /dev/null +++ b/Envfile @@ -0,0 +1,142 @@ +# Most of the things are optional. If you need to use a +# custom key, please create an application.yml for it + +enable_defaults! { ENV['RACK_ENV'] != 'production' } + +################################################ +############## Basic configuration ############# +################################################ + +# App damain + Protocol setting for development +variable :APP_DOMAIN, :String, default: "localhost:3000" +variable :APP_PROTOCOL, :String, default: "http://" + +# Default staff account's id +variable :DEVTO_USER_ID, :Integer, default: 1 + +# Service timeout length +variable :SERVICE_TIMEOUT, :Integer, default: 100000 + +# Production related config that can be ignored +variable :DEPLOYMENT_SIGNATURE, :String, default: "Optional" + +################################################ +############## 3rd Party Services ############## +################################################ + +# It's best if you have the following configured +# for more development experience + +# Github for github related access +variable :GITHUB_KEY, :String, default: "Optional" +variable :GITHUB_SECRET, :String, default: "Optional" +variable :GITHUB_TOKEN, :String, default: "Optional" + +# Twitter for normal twitter access +variable :TWITTER_ACCESS_TOKEN, :String, default: "Optional" +variable :TWITTER_ACCESS_TOKEN_SECRET, :String, default: "Optional" +variable :TWITTER_KEY, :String, default: "Optional" +variable :TWITTER_SECRET, :String, default: "Optional" + +################################################ +######### Optional 3rd Party Services ########## +################################################ + +# Airbrake for error tracking +variable :AIRBRAKE_API_KEY, :String, default: "Optional" +variable :AIRBRAKE_PROJECT_ID, :integer, default: 00000 + +# Algolish for search +variable :ALGOLIASEARCH_API_KEY, :String, default: "Optional" +variable :ALGOLIASEARCH_APPLICATION_ID, :String, default: "Optional" +variable :ALGOLIASEARCH_SEARCH_ONLY_KEY, :String, default: "Optional" +variable :ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY, :String, default: "Optional" + +# AWS for images storages +variable :AWS_DEFAULT_REGION, :String, default: "us-east-1" +variable :AWS_SDK_KEY, :String, default: "Optional" +variable :AWS_SDK_SECRET, :String, default: "Optional" + +# AWS for video storage +variable :AWS_S3_INPUT_BUCKET, :String, default: "Optional" +variable :AWS_S3_VIDEO_ID, :String, default: "Optional" +variable :AWS_S3_VIDEO_KEY, :String, default: "Optional" + +# Buffer for sending to buffer +variable :BUFFER_ACCESS_TOKEN, :String, default: "Optional" +variable :BUFFER_FACEBOOK_ID, :String, default: "Optional" +variable :BUFFER_LINKEDIN_ID, :String, default: "Optional" +variable :BUFFER_PROFILE_ID, :Integer, default: 0 +variable :BUFFER_TWITTER_ID, :String, default: "Optional" + +# Cloudinary for image resizing and cache?? +variable :CLOUDINARY_API_KEY, :String, default: "Optional" +variable :CLOUDINARY_API_SECRET, :String, default: "Optional" +variable :CLOUDINARY_CLOUD_NAME, :String, default: "Optional" +variable :CLOUDINARY_SECURE, :String, default: "Optional" + +# Dacast for streaming +variable :DACAST_STREAM_CODE, :String, default: "Optional" + +# Fastly for edge caching +variable :FASTLY_API_KEY, :String, default: "Optional" +variable :FASTLY_SERVICE_ID, :String, default: "Optional" + +# Google analytic +variable :GA_SERVICE_ACCOUNT_JSON, :String, default: "Optional" +variable :GA_TRACKING_ID, :String, default: "Optional" +variable :GA_OPTIMIZE_ID, :String, default: "Optional" +variable :GA_VIEW_ID, :String, default: "Optional" + +# Mailchimp for mails +variable :MAILCHIMP_API_KEY, :String, default: "Optional-valid" +variable :MAILCHIMP_NEWSLETTER_ID, :String, default: "Optional" +variable :MAILCHIMP_SUSTAINING_MEMBERS_ID, :String, default: "Optional" + +# Email digest frequency +variable :PERIODIC_EMAIL_DIGEST_MAX, :Integer, default: 0 +variable :PERIODIC_EMAIL_DIGEST_MIN, :Integer, default: 2 + +# Pusher for DEV Connect/notfications +variable :PUSHER_APP_ID, :String, default: "Optional" +variable :PUSHER_CLUSTER, :String, default: "Optional" +variable :PUSHER_KEY, :String, default: "Optional" +variable :PUSHER_SECRET, :String, default: "Optional" + +# Google recaptcha +variable :RECAPTCHA_SECRET, :String, default: "Optional" +variable :RECAPTCHA_SITE, :String, default: "Optional" + +# Sharemeow is a text-shot service +# more information here https://github.com/producthunt/ShareMeow +variable :SHARE_MEOW_BASE_URL, :String, default: "Optional" +variable :SHARE_MEOW_SECRET_KEY, :String, default: "Optional" + +# Slack for customer alerts +variable :SLACK_CHANNEL, :String, default: "Optional" +variable :SLACK_WEBHOOK_URL, :String, default: "Optional" + +# Stream for notifications +variable :STREAM_RAILS_KEY, :String, default: "Optional" +variable :STREAM_RAILS_SECRET, :String, default: "Optional" + +# Stripe for payment system +variable :STRIPE_PUBLISHABLE_KEY, :String, default: "Optional" +variable :STRIPE_SECRET_KEY, :String, default: "Optional" + +# For browser webpush notifications (webpush gem) (totally random base64 nums) +variable :VAPID_PUBLIC_KEY, :String, default: "dGhpcyBpcyBkZXYudG8gdGVzdCBkYXRh" +variable :VAPID_PRIVATE_KEY, :String, default: "VGhpcyBpcyBtb3JlIHRlc3QgZGF0YQ==" + +#For video calling in DEV Connect +variable :TWILIO_ACCOUNT_SID, :String, default: "Optional" +variable :TWILIO_VIDEO_API_KEY, :String, default: "Optional" +variable :TWILIO_VIDEO_API_SECRET, :String, default: "Optional" + +##### Legacy test related env vars ###### +variable :FACEBOOK_PIXEL_ID, :String, default: "Optional" +variable :SMARTY_STREETS_WEB_KEY, :String, default: "Optional" + +group :production do + variable :SECRET_KEY_BASE, :String +end diff --git a/Gemfile b/Gemfile index c6378aa4f..12564953b 100644 --- a/Gemfile +++ b/Gemfile @@ -38,6 +38,7 @@ gem "delayed_job_web", "~> 1.4" gem "devise", "~> 4.4" gem "draper", "~> 3.0" gem "email_validator", "~> 1.6" +gem "envied", "~> 0.9" gem "fastly", "~> 1.13" gem "fastly-rails", "~> 0.8" gem "feedjira", "~> 2.1" diff --git a/Gemfile.lock b/Gemfile.lock index 99013941b..c2506ad86 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -257,6 +257,9 @@ GEM http_parser.rb (~> 0.6.0) email_validator (1.6.0) activemodel + envied (0.9.1) + coercible (~> 1.0) + thor (~> 0.15) environs (1.1.0) equalizer (0.0.11) errbase (0.0.3) @@ -949,6 +952,7 @@ DEPENDENCIES devise (~> 4.4) draper (~> 3.0) email_validator (~> 1.6) + envied (~> 0.9) factory_bot_rails (~> 4.10) fake_stripe (~> 0.1) faker! diff --git a/README.md b/README.md index f5e41428d..c44fa1cd3 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@ rails version - - Codeship Status for thepracticaldev/dev.to_private + + Travis Status for thepracticaldev/dev.to_core @@ -71,10 +71,15 @@ We are all humans trying to work together to improve things for the community. A 2. `bundle install` 3. `bin/yarn` 4. Set up your environment variables/secrets - * Create a `config/application.yml` file to store development secrets. This is a personal file that is ignored in git. - * Copy [`config/sample_application.yml`](config/sample_application.yml) in order to create a valid `application.yml` - * You'll need to get your own free API keys for a few services in order to get your development environment running. [**Follow this wiki to get them.**](https://github.com/thepracticaldev/dev.to_core/wiki/Getting-API-Keys-for-Basic-Development) - * If you are missing `ENV` variables on bootup, `_env_checker.rb` will let you know. If you add or remove `ENV` vars to the project, you must also modify this file before they can be merged. The wiki above should handle all the necessary keys for basic development. + * Take a look at `Envfile`. This file list all the `ENV` variables we use and provides a fake default a particular key is missing. You'll need to get your own free API keys for a few services in order to get your development environment running. [**Follow this wiki to get them.**](https://github.com/thepracticaldev/dev.to_core/wiki/Getting-API-Keys-for-Basic-Development) + * For any key that you wish to enter/replace: + 1. Create a `config/application.yml` file. This is a personal file that is ignored in git. + 2. Input the keys you want to replace. ie: + ``` + THAT_THIRD_PARTY_SERVICE_OAUTH: A_SOME_SECURE_AND_LONG_KEY + TWITTER_ACCESS_TOKEN: A_SOME_SECURE_AND_LONG_KEY + ``` + * If you are missing `ENV` variables on bootup, `envied` gem will alert you with messages similar to `'error_on_missing_variables!': The following environment variables should be set: A_MISSING_KEY.`. * You do not need "real" keys for basic development. Some features require certain keys, so you may be able to add them as you go. 5. Run `bin/setup` @@ -167,66 +172,35 @@ The following technologies are used for testing: You can modify the test in `/test/mailers/previews` You can view the previews at (for example) `http://localhost:3000/rails/mailers/notify_mailer/new_reply_email` -## How to contribute (Internal) - -1. Clone the project locally. -2. Create a branch for each separate piece of work. -3. Do the work and write [good commit messages](https://chris.beams.io/posts/git-commit/). - -* If your work includes adding a new environment variable, make sure you update `_env_checker.rb`. - -4. Push your branch up to this repository. -5. Create a new pull-reqest. -6. After the pull-request is approved and merged, delete your branch on github. - -**Avoid pushing spike(test) branches up to the main repository**. If you must, push the spike branches up to a forked repository. - - - -### Branch Policies - -#### Branch naming convention - -Name the branch in the following manner. -`//-` - -###### Examples - -```text -ben/feature/renderer-cookies -jess/hotfix/dockerfile-base-image -andy-mac/issue/#132-broken-link -``` +2. Create a feature branch: `git checkout -b my-name/feature/that-new-feature` +3. Code and Commit your changes. Bonus point if you write a [good commit message](https://chris.beams.io/posts/git-commit/): `git commit -am 'Add some feature'` +4. Push to the branch: `git push origin my-name/feature/that-new-feature` +5. Create a new pull request for your feature branch 🎉 #### Pull request guideline -* Keep the pull request small; a pull request should try it's very best to address only a single concern. +* Try to keep the pull request small; a pull request should try it's very best to address only a single concern. * Make sure all the tests pass and add additional tests for the code you submit. * Document your reasoning behind the changes. Explain why you wrote the code in the way you did, not what it does. * If there's an existing issue related to the pull request, reference to it. [More info here](https://github.com/blog/1506-closing-issues-via-pull-requests) -Please note that we squash all pull request. **After a pull request is approved, we will remove the branch the PR is on unless you state it otherwise** +Please note that we squash all pull request. ## Continuous Integration & Continuous Deployment -We are using Codeship for CI and CD. Codeship will run a build (in isolated environment for testing) for every push to this repository. Keep in mind that a passing-build does not necessarily mean the project won't run into any issues. Strive to write good tests for any chunk of code you wish to contribute. Only pushes to the `deployment` branch will evoke the CD portion of Codeship after CI passes. Our test suite is not perfect and sometimes a re-rerun is needed. +We are using Travis for CI and CD. Travis will run a build (in isolated environment for testing) for every push to this repository. Keep in mind that a passing-build does not necessarily mean the project won't run into any issues. Strive to write good tests for any chunk of code you wish to contribute. Travis will deploy a pull request to production after CI passes. Our test suite is not perfect and sometimes a re-rerun is needed. #### Skipping CI build (Not recommended) -If your changes are minor (i.e. updating README), you can skip CI by adding `--skip-ci` to your commit message. More info [here](https://documentation.codeship.com/general/projects/skipping-builds/). +If your changes are minor (i.e. updating README), you can skip CI by adding `[skip ci]` to your commit message. ## CodeClimate and Simplecov We are using CodeClimate to track code quality and code coverage. Codeclimate will grade the quality of the code of every PR but not the entirety of the project. If you feel that the current linting rule is unreasonable, feel free to submit a _separate_ PR to change it. Fix any errors that Codeclimate provides and strive to leave code better than you found it. -Simplecov is a gem that is tracking the coverage of our test suite. Codeship will upload Simplecov data to CodeClimate. We are still in the early stage of using it so it may not provide an accurate measurement our of codebase. +Simplecov is a gem that is tracking the coverage of our test suite. Travis will upload Simplecov data to CodeClimate. We are still in the early stage of using it so it may not provide an accurate measurement our of codebase. #### Using simplecov locally @@ -234,3 +208,7 @@ Simplecov is a gem that is tracking the coverage of our test suite. Codeship wil 2. After rspec is complete, open `index.html` within the coverage folder to view code coverages. Run `bin/rspecov` to do all of this in one step + +*** + +## Happy Coding ❤️ diff --git a/app/assets/javascripts/base.js.erb b/app/assets/javascripts/base.js.erb index 581644667..9c28c8644 100644 --- a/app/assets/javascripts/base.js.erb +++ b/app/assets/javascripts/base.js.erb @@ -656,7 +656,7 @@ var instantClick function initializeAirbrake() { <% if Rails.env.production? %> - var airbrake = new airbrakeJs.Client({projectId: 1, projectKey: '<%=ENV["AIRBRAKE_API_KEY"]%>'}); + var airbrake = new airbrakeJs.Client({projectId: 1, projectKey: '<%=ApplicationConfig["AIRBRAKE_API_KEY"]%>'}); airbrake.addFilter(function(notice) { notice.context.environment = "<%= Rails.env %>"; return notice; diff --git a/app/assets/javascripts/initializers/initScrolling.js.erb b/app/assets/javascripts/initializers/initScrolling.js.erb index 763588d85..1771b58d6 100644 --- a/app/assets/javascripts/initializers/initScrolling.js.erb +++ b/app/assets/javascripts/initializers/initScrolling.js.erb @@ -6,8 +6,8 @@ function checkIfNearBottomOfPage() { var elCheck = document.getElementById("index-container"); if (elCheck) { - var publicSearchKey = '<%= ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' - client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); + var publicSearchKey = '<%= ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' + client = algoliasearch('<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); initScrolling.called = true; if (document.getElementsByClassName("single-article").length < 2 || location.search.indexOf("q=") > -1 ) { diff --git a/app/assets/javascripts/initializers/initializeBaseTracking.js.erb b/app/assets/javascripts/initializers/initializeBaseTracking.js.erb index 75dc4f940..019692e7d 100644 --- a/app/assets/javascripts/initializers/initializeBaseTracking.js.erb +++ b/app/assets/javascripts/initializers/initializeBaseTracking.js.erb @@ -11,9 +11,9 @@ function initializeBaseTracking() { addedGA = true; wait++; if (window.ga && ga.create) { - ga('create', '<%= ENV["GA_TRACKING_ID"] %>', 'auto'); + ga('create', '<%= ApplicationConfig["GA_TRACKING_ID"] %>', 'auto'); if (!checkUserLoggedIn() && Math.random() < 0.35) { - ga('require', '<%= ENV["GA_OPTIMIZE_ID"] %>'); + ga('require', '<%= ApplicationConfig["GA_OPTIMIZE_ID"] %>'); } ga('send', 'pageview', location.pathname + location.search); if (document.location.search.indexOf('newly-registered-user=true') > -1) { @@ -81,7 +81,7 @@ function facebookTrackingScript() { t.src=v;s=b.getElementsByTagName(e)[0]; s.parentNode.insertBefore(t,s)}(window, document,'script', 'https://connect.facebook.net/en_US/fbevents.js'); - fbq('init', '<%= ENV["FACEBOOK_PIXEL_ID"] %>'); + fbq('init', '<%= ApplicationConfig["FACEBOOK_PIXEL_ID"] %>'); fbTrack(); } else { fbTrack(); diff --git a/app/assets/javascripts/initializers/initializeFetchFollowedArticles.js.erb b/app/assets/javascripts/initializers/initializeFetchFollowedArticles.js.erb index 202aa0d46..1ff193f03 100644 --- a/app/assets/javascripts/initializers/initializeFetchFollowedArticles.js.erb +++ b/app/assets/javascripts/initializers/initializeFetchFollowedArticles.js.erb @@ -20,8 +20,8 @@ function algoliaFollowedArticles(){ if (user.followed_user_ids){ followedUsersArray = user.followed_user_ids.map(function(id){return 'user_'+id}); } - var publicSearchKey = '<%= ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' - var client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); + var publicSearchKey = '<%= ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' + var client = algoliasearch('<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); var index = client.initIndex('ordered_articles_<%= Rails.env %>'); var searchObj = { diff --git a/app/assets/javascripts/initializers/initializeReadingListPage.js.erb b/app/assets/javascripts/initializers/initializeReadingListPage.js.erb index 83ec129c7..a5ee35ac9 100644 --- a/app/assets/javascripts/initializers/initializeReadingListPage.js.erb +++ b/app/assets/javascripts/initializers/initializeReadingListPage.js.erb @@ -1,7 +1,7 @@ function initializeReadingListPage(){ if ( getCurrentPage("reading_list_items-index") && checkUserLoggedIn() ) { - var publicSearchKey = '<%= ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' - client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); + var publicSearchKey = '<%= ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>' + client = algoliasearch('<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>', publicSearchKey); readinglistIndex = client.initIndex('ordered_articles_<%= Rails.env %>'); commentsIndex = client.initIndex('ordered_comments_<%= Rails.env %>'); var params = getQueryParams(document.location.search); diff --git a/app/assets/javascripts/utilities/getCsrfToken.js.erb b/app/assets/javascripts/utilities/getCsrfToken.js.erb index 9df772f12..2b564661f 100644 --- a/app/assets/javascripts/utilities/getCsrfToken.js.erb +++ b/app/assets/javascripts/utilities/getCsrfToken.js.erb @@ -10,7 +10,7 @@ function getCsrfToken() { return resolve(authToken); } else if (i === 1000) { clearInterval(waitingOnCSRF); - var airbrake = new airbrakeJs.Client({projectId: 1, projectKey: '<%=ENV["AIRBRAKE_API_KEY"]%>'}); + var airbrake = new airbrakeJs.Client({projectId: 1, projectKey: '<%=ApplicationConfig["AIRBRAKE_API_KEY"]%>'}); airbrake.addFilter(function(notice) { notice.context.environment = "<%= Rails.env %>"; return notice; diff --git a/app/black_box/google_analytics.rb b/app/black_box/google_analytics.rb index 93883d67f..9078fe443 100644 --- a/app/black_box/google_analytics.rb +++ b/app/black_box/google_analytics.rb @@ -32,7 +32,7 @@ class GoogleAnalytics def make_report_request(url) ReportRequest.new( - view_id: ENV["GA_VIEW_ID"], + view_id: ApplicationConfig["GA_VIEW_ID"], filters_expression: "ga:pagePath==#{url}", metrics: [Metric.new(expression: "ga:pageviews")], date_ranges: [ @@ -51,7 +51,7 @@ class GoogleAnalytics def create_service_account_credential ServiceAccountCredentials.make_creds( - json_key_io: OpenStruct.new(read: ENV["GA_SERVICE_ACCOUNT_JSON"]), + json_key_io: OpenStruct.new(read: ApplicationConfig["GA_SERVICE_ACCOUNT_JSON"]), scope: [AUTH_ANALYTICS_READONLY], ) end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3fcf1823b..bee4edfcd 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -5,7 +5,7 @@ class ApplicationController < ActionController::Base def require_http_auth authenticate_or_request_with_http_basic do |username, password| - username == ENV["APP_NAME"] && password == ENV["APP_PASSWORD"] + username == ApplicationConfig["APP_NAME"] && password == ApplicationConfig["APP_PASSWORD"] end end @@ -37,7 +37,7 @@ class ApplicationController < ActionController::Base end def raise_banned - raise "BANNED" if current_user && current_user.banned + raise "BANNED" if current_user&.banned end def is_internal_navigation? @@ -53,7 +53,7 @@ class ApplicationController < ActionController::Base # We are at least secure for now. return if Rails.env.test? if request.referer.present? - request.referer.start_with?(ENV["APP_PROTOCOL"].to_s + ENV["APP_DOMAIN"].to_s) + request.referer.start_with?(ApplicationConfig["APP_PROTOCOL"].to_s + ApplicationConfig["APP_DOMAIN"].to_s) else logger.info "**REQUEST ORIGIN CHECK** #{request.origin}" raise InvalidAuthenticityToken, NULL_ORIGIN_MESSAGE if request.origin == "null" diff --git a/app/controllers/chat_channels_controller.rb b/app/controllers/chat_channels_controller.rb index cdf1e4a6f..de97dff9e 100644 --- a/app/controllers/chat_channels_controller.rb +++ b/app/controllers/chat_channels_controller.rb @@ -131,7 +131,7 @@ class ChatChannelsController < ApplicationController current_user_id = current_user.id params = {filters: "viewable_by:#{current_user_id} AND status: active"} @secured_algolia_key = Algolia.generate_secured_api_key( - ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"], params, + ApplicationConfig["ALGOLIASEARCH_SEARCH_ONLY_KEY"], params, ) end diff --git a/app/controllers/feedback_messages_controller.rb b/app/controllers/feedback_messages_controller.rb index 5fe8a4e40..77cda7f14 100644 --- a/app/controllers/feedback_messages_controller.rb +++ b/app/controllers/feedback_messages_controller.rb @@ -27,7 +27,7 @@ class FeedbackMessagesController < ApplicationController private def recaptcha_verified? - params["g-recaptcha-response"] && verify_recaptcha(secret_key: ENV["RECAPTCHA_SECRET"]) + params["g-recaptcha-response"] && verify_recaptcha(secret_key: ApplicationConfig["RECAPTCHA_SECRET"]) end def send_slack_message diff --git a/app/controllers/ga_events_controller.rb b/app/controllers/ga_events_controller.rb index cf1d7b42f..5726aabc4 100644 --- a/app/controllers/ga_events_controller.rb +++ b/app/controllers/ga_events_controller.rb @@ -8,7 +8,7 @@ class GaEventsController < ApplicationController json = JSON.parse(request.raw_post) user_id = user_signed_in? ? current_user.id : nil client_id = "#{scrambled_ip[0..12]}_#{json['user_agent']}_#{user_id}" - tracker = Staccato.tracker(ENV["GA_TRACKING_ID"], client_id) + tracker = Staccato.tracker(ApplicationConfig["GA_TRACKING_ID"], client_id) tracker.pageview( path: json["path"], user_id: user_id, diff --git a/app/controllers/internal/welcome_controller.rb b/app/controllers/internal/welcome_controller.rb index dd37972e5..f95558e62 100644 --- a/app/controllers/internal/welcome_controller.rb +++ b/app/controllers/internal/welcome_controller.rb @@ -8,7 +8,7 @@ class Internal::WelcomeController < Internal::ApplicationController def create welcome_thread = Article.create( body_markdown: welcome_thread_content, - user_id: ENV["DEVTO_USER_ID"], + user_id: ApplicationConfig["DEVTO_USER_ID"], ) redirect_to welcome_thread.path + "/edit" end diff --git a/app/controllers/omniauth_callbacks_controller.rb b/app/controllers/omniauth_callbacks_controller.rb index 4b995077d..9a47bf1ed 100644 --- a/app/controllers/omniauth_callbacks_controller.rb +++ b/app/controllers/omniauth_callbacks_controller.rb @@ -2,8 +2,8 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController # Don't need a policy for this since this is our sign up/in route include Devise::Controllers::Rememberable def self.provides_callback_for(provider) - # raise env["omniauth.auth"].to_yaml - class_eval %Q{ + # raise ApplicationConfig["omniauth.auth"].to_yaml + class_eval %{ def #{provider} cta_variant = request.env["omniauth.params"]['state'].to_s @user = AuthorizationService.new(request.env["omniauth.auth"], current_user, cta_variant).get_user @@ -19,8 +19,7 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController } end - [:twitter, :github].each do |provider| + %i[twitter github].each do |provider| provides_callback_for provider end - end diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 5434ca4ab..9012e5b31 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -55,7 +55,7 @@ class PagesController < ApplicationController private # helpers def latest_published_welcome_thread - Article.where(user_id: ENV["DEVTO_USER_ID"], published: true). + Article.where(user_id: ApplicationConfig["DEVTO_USER_ID"], published: true). tagged_with("welcome").last end diff --git a/app/labor/bufferizer.rb b/app/labor/bufferizer.rb index 46770740e..b0a2483fb 100644 --- a/app/labor/bufferizer.rb +++ b/app/labor/bufferizer.rb @@ -5,13 +5,13 @@ class Bufferizer end def twitter_post! - client = Buffer::Client.new(ENV["BUFFER_ACCESS_TOKEN"]) + client = Buffer::Client.new(ApplicationConfig["BUFFER_ACCESS_TOKEN"]) client.create_update( body: { text: twitter_buffer_text, profile_ids: [ - ENV["BUFFER_TWITTER_ID"], + ApplicationConfig["BUFFER_TWITTER_ID"], ], }, ) @@ -19,14 +19,14 @@ class Bufferizer end def facebook_post! - client = Buffer::Client.new(ENV["BUFFER_ACCESS_TOKEN"]) + client = Buffer::Client.new(ApplicationConfig["BUFFER_ACCESS_TOKEN"]) client.create_update( body: { text: fb_buffer_text, profile_ids: [ - ENV["BUFFER_FACEBOOK_ID"], # We're sending to LinkedIn and FB with this. - ENV["BUFFER_LINKEDIN_ID"], # That's why there are two profile IDs + ApplicationConfig["BUFFER_FACEBOOK_ID"], # We're sending to LinkedIn and FB with this. + ApplicationConfig["BUFFER_LINKEDIN_ID"], # That's why there are two profile IDs ], }, ) diff --git a/app/labor/color_from_image.rb b/app/labor/color_from_image.rb index 3bac7e91e..c39281177 100644 --- a/app/labor/color_from_image.rb +++ b/app/labor/color_from_image.rb @@ -17,7 +17,7 @@ class ColorFromImage input = { url: @url } - client = Algorithmia.client(ENV["ALGORITHMIA_KEY"]) + client = Algorithmia.client(ApplicationConfig["ALGORITHMIA_KEY"]) algo = client.algo('vagrant/ColorSchemeExtraction/0.2.0') algo.pipe(input).result end diff --git a/app/labor/email_logic.rb b/app/labor/email_logic.rb index 5c31172fb..a9dcee1f1 100644 --- a/app/labor/email_logic.rb +++ b/app/labor/email_logic.rb @@ -55,8 +55,8 @@ class EmailLogic def get_days_until_next_email # Relies on hyperbolic tangent function to model the frequency of the digest email - max_day = ENV["PERIODIC_EMAIL_DIGEST_MAX"].to_i - min_day = ENV["PERIODIC_EMAIL_DIGEST_MIN"].to_i + max_day = ApplicationConfig["PERIODIC_EMAIL_DIGEST_MAX"].to_i + min_day = ApplicationConfig["PERIODIC_EMAIL_DIGEST_MIN"].to_i result = max_day * (1 - Math.tanh(2 * @open_percentage)) result = result.round result < min_day ? min_day : result diff --git a/app/labor/function_caller.rb b/app/labor/function_caller.rb index e12be3f9d..8ae2a08ed 100644 --- a/app/labor/function_caller.rb +++ b/app/labor/function_caller.rb @@ -6,7 +6,7 @@ class FunctionCaller end def call - response = AwsLambda.invoke(function_name: function_name, payload: payload) + response = AWS_LAMBDA.invoke(function_name: function_name, payload: payload) JSON.parse(JSON.parse(response.payload.as_json[0])["body"])["message"] end end diff --git a/app/labor/language_detector.rb b/app/labor/language_detector.rb index 8dbd5faa6..4ce8e3b4e 100644 --- a/app/labor/language_detector.rb +++ b/app/labor/language_detector.rb @@ -15,7 +15,7 @@ class LanguageDetector def get_language return { "result" => "en", "confidence" => 0.9 } unless Rails.env.production? - client = Algorithmia.client(ENV["ALGORITHMIA_KEY"]) + client = Algorithmia.client(ApplicationConfig["ALGORITHMIA_KEY"]) algo = client.algo('miguelher/LanguageDetector/0.1.0') algo.pipe(text).result end diff --git a/app/labor/mailchimp_bot.rb b/app/labor/mailchimp_bot.rb index 91f257427..5ddb5cace 100644 --- a/app/labor/mailchimp_bot.rb +++ b/app/labor/mailchimp_bot.rb @@ -17,7 +17,7 @@ class MailchimpBot # attempt to update email if user changed email addresses success = false begin - gibbon.lists(ENV["MAILCHIMP_NEWSLETTER_ID"]).members(target_md5_email).upsert( + gibbon.lists(ApplicationConfig["MAILCHIMP_NEWSLETTER_ID"]).members(target_md5_email).upsert( body: { email_address: user.email, status: user.email_newsletter ? "subscribed" : "unsubscribed", @@ -57,7 +57,7 @@ class MailchimpBot status = user.email_membership_newsletter ? "subscribed" : "unsubscribed" begin - gibbon.lists(ENV["MAILCHIMP_SUSTAINING_MEMBERS_ID"]).members(target_md5_email).upsert( + gibbon.lists(ApplicationConfig["MAILCHIMP_SUSTAINING_MEMBERS_ID"]).members(target_md5_email).upsert( body: { email_address: user.email, status: status, diff --git a/app/labor/markdown_parser.rb b/app/labor/markdown_parser.rb index ec5ebf3a2..856d38b3a 100644 --- a/app/labor/markdown_parser.rb +++ b/app/labor/markdown_parser.rb @@ -143,7 +143,7 @@ class MarkdownParser username = mention.gsub("@", "").downcase if User.find_by_username(username) <<~HTML - @#{username} + @#{username} HTML else mention diff --git a/app/labor/profile_image.rb b/app/labor/profile_image.rb index e1e78626a..8315f0b92 100644 --- a/app/labor/profile_image.rb +++ b/app/labor/profile_image.rb @@ -25,6 +25,6 @@ class ProfileImage end def get_external_link - image_link ? "#{ENV['APP_PROTOCOL']}#{ENV['APP_DOMAIN']}#{image_link}" : backup_link + image_link ? "#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}#{image_link}" : backup_link end end diff --git a/app/labor/rss_reader.rb b/app/labor/rss_reader.rb index 951c6b8fe..187fb423f 100644 --- a/app/labor/rss_reader.rb +++ b/app/labor/rss_reader.rb @@ -22,8 +22,8 @@ class RssReader def valid_feed_url?(link) true if fetch_rss(link) - rescue - return false + rescue StandardError + false end private @@ -31,17 +31,15 @@ class RssReader def create_articles_for_user(user) feed = fetch_rss(user.feed_url) feed.entries.reverse_each do |item| - begin - make_from_rss_item(item, user, feed) - rescue => e - log_error("RssReaderError: occurred while creating article for " \ - "USER: #{user.username} " \ - "FEED-URL: #{user.feed_url} " \ - "ITEM-TITLE: #{item.title ? item.title : 'no title'} " \ - "ERROR: #{e}") - end + make_from_rss_item(item, user, feed) + rescue StandardError => e + log_error("RssReaderError: occurred while creating article for " \ + "USER: #{user.username} " \ + "FEED-URL: #{user.feed_url} " \ + "ITEM-TITLE: #{item.title || 'no title'} " \ + "ERROR: #{e}") end - rescue => e + rescue StandardError => e log_error("RssReaderError: occurred while fetch feed for " \ "USER: #{user.username} " \ "FEED-URL: #{user.feed_url} " \ @@ -130,7 +128,7 @@ class RssReader a_tag = iframe.css("a") next if a_tag.empty? possible_link = a_tag[0].inner_html - if possible_link =~ /medium\.com\/media\/.+\/href/ + if /medium\.com\/media\/.+\/href/.match?(possible_link) real_link = "" open(possible_link) do |h| real_link = h.base_uri.to_s @@ -161,7 +159,7 @@ class RssReader def parse_and_translate_youtube_iframe!(html_doc) html_doc.css("iframe").each do |iframe| - if iframe.attributes["src"].value =~ /youtube\.com/ + if /youtube\.com/.match?(iframe.attributes["src"].value) iframe.name = "p" youtube_id = iframe.attributes["src"].value.scan(/embed%2F(.{4,12})%3F/).flatten.first iframe.keys.each { |attr| iframe.remove_attribute(attr) } diff --git a/app/labor/twilio_token.rb b/app/labor/twilio_token.rb index 45649a117..172124660 100644 --- a/app/labor/twilio_token.rb +++ b/app/labor/twilio_token.rb @@ -7,10 +7,9 @@ class TwilioToken end def get - # Replace with ENV vars - account_sid = ENV["TWILIO_ACCOUNT_SID"] - api_key = ENV["TWILIO_VIDEO_API_KEY"] - api_secret = ENV["TWILIO_VIDEO_API_SECRET"] + account_sid = ApplicationConfig["TWILIO_ACCOUNT_SID"] + api_key = ApplicationConfig["TWILIO_VIDEO_API_KEY"] + api_secret = ApplicationConfig["TWILIO_VIDEO_API_SECRET"] token = Twilio::JWT::AccessToken.new( account_sid, @@ -27,4 +26,4 @@ class TwilioToken token.to_jwt end -end \ No newline at end of file +end diff --git a/app/labor/twitter_bot.rb b/app/labor/twitter_bot.rb index 882797919..70f89a25b 100644 --- a/app/labor/twitter_bot.rb +++ b/app/labor/twitter_bot.rb @@ -1,23 +1,17 @@ class TwitterBot + attr_reader :token, :secret - def initialize(identity=nil) - if identity - token = identity.token - secret = identity.secret - else - token = ENV["TWITTER_KEY"] - secret = ENV["TWITTER_SECRET"] - end - @twitter = Twitter::REST::Client.new do |config| - config.consumer_key = ENV["TWITTER_KEY"] - config.consumer_secret = ENV["TWITTER_SECRET"] - config.access_token = token - config.access_token_secret = secret - end + def initialize(token:, secret:) + @token = token + @secret = secret end def client - @twitter + Twitter::REST::Client.new do |config| + config.consumer_key = ApplicationConfig["TWITTER_KEY"] + config.consumer_secret = ApplicationConfig["TWITTER_SECRET"] + config.access_token = @token + config.access_token_secret = @secret + end end - end diff --git a/app/models/message.rb b/app/models/message.rb index 0bf3e792c..b7e980424 100644 --- a/app/models/message.rb +++ b/app/models/message.rb @@ -33,8 +33,8 @@ class Message < ApplicationRecord ttl: 24 * 60 * 60, vapid: { subject: "https://dev.to", - public_key: ENV["VAPID_PUBLIC_KEY"], - private_key: ENV["VAPID_PRIVATE_KEY"], + public_key: ApplicationConfig["VAPID_PUBLIC_KEY"], + private_key: ApplicationConfig["VAPID_PRIVATE_KEY"], }, ) end @@ -67,7 +67,7 @@ class Message < ApplicationRecord doc = Nokogiri::HTML(html) rich_style = "border: 1px solid #0a0a0a; border-radius: 3px; padding: 8px;" doc.css("a").each do |a| - if a["href"].include?("//#{ENV["APP_DOMAIN"]}/") && article = Article.find_by_slug(a["href"].split("/")[4].split("?")[0]) + if a["href"].include?("//#{ApplicationConfig["APP_DOMAIN"]}/") && article = Article.find_by_slug(a["href"].split("/")[4].split("?")[0]) html = html + "

diff --git a/app/models/notification.rb b/app/models/notification.rb index b427f7d6a..8a48d39b9 100644 --- a/app/models/notification.rb +++ b/app/models/notification.rb @@ -49,7 +49,7 @@ class Notification < ApplicationRecord def activity_actor if notifiable.class.name == "Broadcast" || action == "Moderation" - User.find(ENV["DEVTO_USER_ID"]) + User.find(ApplicationConfig["DEVTO_USER_ID"]) else notifiable.user end diff --git a/app/models/tweet.rb b/app/models/tweet.rb index 9d57e6acd..bfa6655b9 100644 --- a/app/models/tweet.rb +++ b/app/models/tweet.rb @@ -22,10 +22,10 @@ class Tweet < ApplicationRecord def self.fetch(twitter_id_code) tries = 0 tweet = nil - until (tries > 4 || tweet) do + until tries > 4 || tweet begin return tweet = try_to_get_tweet(twitter_id_code) - rescue => e + rescue StandardError => e puts e tries += 1 end @@ -34,24 +34,24 @@ class Tweet < ApplicationRecord def processed_text urls_serialized.each do |url| - text.gsub!(url[:url],"#{url[:display_url]}") + text.gsub!(url[:url], "#{url[:display_url]}") end mentioned_usernames_serialized.each do |username| uname = username["screen_name"] - text.gsub!("@"+uname,"#{"@"+uname}") + text.gsub!("@" + uname, "#{'@' + uname}") end hashtags_serialized.each do |tag| tag_text = tag[:text] - text.gsub!("#"+tag_text,"#{"#"+tag_text}") + text.gsub!("#" + tag_text, "#{'#' + tag_text}") end if extended_entities_serialized && extended_entities_serialized[:media] extended_entities_serialized[:media].each do |media| - text.gsub!(media[:url],"") + text.gsub!(media[:url], "") end end - text.gsub!("\n","
") + text.gsub!("\n", "
") text end @@ -98,10 +98,10 @@ class Tweet < ApplicationRecord def self.random_identity if Rails.env.production? - Identity.where(provider:"twitter").last(250).sample + Identity.where(provider: "twitter").last(250).sample else - Identity.where(provider:"twitter").last + Identity.where(provider: "twitter").last || + { token: ApplicationConfig["TWITTER_KEY"], secret: ApplicationConfig["TWITTER_SECRET"] } end end - end diff --git a/app/views/articles/_algolia_search.html.erb b/app/views/articles/_algolia_search.html.erb index 2a7b9e5a7..948a617de 100644 --- a/app/views/articles/_algolia_search.html.erb +++ b/app/views/articles/_algolia_search.html.erb @@ -20,7 +20,7 @@ var query = filterXSS(params.q); var filters = filterXSS(params.filters || ""); document.getElementById("substories").innerHTML = '

' - var client = algoliasearch('<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>'); + var client = algoliasearch('<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>', '<%= ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>'); var index = client.initIndex('searchables_<%= Rails.env %>'); if (document.getElementById("query-wrapper")) { search(query, index, filters); diff --git a/app/views/articles/_main_stories_feed.html.erb b/app/views/articles/_main_stories_feed.html.erb index 34b509e34..3af6e8346 100644 --- a/app/views/articles/_main_stories_feed.html.erb +++ b/app/views/articles/_main_stories_feed.html.erb @@ -14,7 +14,7 @@ <% end %>

- <%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant> + <%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant> TWITTER diff --git a/app/views/articles/_sidebar_additional.html.erb b/app/views/articles/_sidebar_additional.html.erb index 3ca64911e..254e5e866 100644 --- a/app/views/articles/_sidebar_additional.html.erb +++ b/app/views/articles/_sidebar_additional.html.erb @@ -75,7 +75,7 @@
- <%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button login-cta-button" data-no-instant> + <%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button login-cta-button" data-no-instant> SIGN IN VIA TWITTER