Migrate to Travis CI (part 2) (#591)
* Update Travis.yml * Update README * Update README * Adjust test to not rely on env vars * Update encryption * Refactor * Update env key * Stub AWS calls * Create ApplicationConfig * Fix specs * Fix lint * Update ApplicationConfig * Remove travis env vars * Fix lint * Extend character limit to 100 * Add env to travis * Take out auto-restart after deploy * Immediately discarded test cache * Stub GA in request specs * Stub Pusher * Fix broken specs * Update fixture * Add CodeClimate id * Change CodeClimate key * Remove merge mistakes * WIP * Add Envied gem & Change README * Add missing keys * Add missing key * Update fixture * Fix broken spec * Add Slack Notification for Travis * Fix wording * Fix typo
This commit is contained in:
parent
aed0869f2a
commit
40f488d991
84 changed files with 1333 additions and 17459 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
|
@ -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/
|
||||
storybook-static/
|
||||
|
|
|
|||
10
.rubocop.yml
10
.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'
|
||||
|
|
|
|||
72
.travis.yml
72
.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=
|
||||
|
|
|
|||
142
Envfile
Normal file
142
Envfile
Normal file
|
|
@ -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
|
||||
1
Gemfile
1
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"
|
||||
|
|
|
|||
|
|
@ -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!
|
||||
|
|
|
|||
72
README.md
72
README.md
|
|
@ -17,8 +17,8 @@
|
|||
<a href="http://rubyonrails.org/">
|
||||
<img src="https://img.shields.io/badge/Rails-v5.1.6-brightgreen.svg" alt="rails version"/>
|
||||
</a>
|
||||
<a href="https://app.codeship.com/projects/229274">
|
||||
<img src="https://app.codeship.com/projects/6c96c1d0-3db5-0135-649e-1a9b211ca261/status?branch=master" alt="Codeship Status for thepracticaldev/dev.to_private"/>
|
||||
<a href="https://travis-ci.com/thepracticaldev/dev.to_core">
|
||||
<img src="https://travis-ci.com/thepracticaldev/dev.to_core.svg?token=gYyQp8xsU99uXSw75ah6&branch=master" alt="Travis Status for thepracticaldev/dev.to_core"/>
|
||||
</a>
|
||||
<a href="https://codeclimate.com/repos/5a9720ab6f8db3029200792a/maintainability">
|
||||
<img src="https://api.codeclimate.com/v1/badges/607170a91196c15668e5/maintainability" />
|
||||
|
|
@ -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.
|
||||
|
||||
<!-- This would be how we would contribute if we are doing a fork-and-branch workflow
|
||||
## How to contribute
|
||||
1. Fork the project & clone locally.
|
||||
2. Create an upstream remote and sync your local copy before you branch.
|
||||
3. Create a branch for each separate piece of work.
|
||||
4. Do the work and write good commit messages.
|
||||
5. Push to your origin repository.
|
||||
6. Create a new PR in GitHub.
|
||||
-->
|
||||
|
||||
### Branch Policies
|
||||
|
||||
#### Branch naming convention
|
||||
|
||||
Name the branch in the following manner.
|
||||
`<your-name>/<type>/<github issue# (if there's one)>-<name>`
|
||||
|
||||
###### 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 ❤️
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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 ) {
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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 = {
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
],
|
||||
},
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -143,7 +143,7 @@ class MarkdownParser
|
|||
username = mention.gsub("@", "").downcase
|
||||
if User.find_by_username(username)
|
||||
<<~HTML
|
||||
<a class='comment-mentioned-user' href='#{ENV['APP_PROTOCOL']}#{ENV['APP_DOMAIN']}/#{username}'>@#{username}</a>
|
||||
<a class='comment-mentioned-user' href='#{ApplicationConfig['APP_PROTOCOL']}#{ApplicationConfig['APP_DOMAIN']}/#{username}'>@#{username}</a>
|
||||
HTML
|
||||
else
|
||||
mention
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) }
|
||||
|
|
|
|||
|
|
@ -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
|
||||
end
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 + "<a style='color: #0a0a0a' href='#{article.path}'
|
||||
target='_blank' data-content='articles/#{article.id}'>
|
||||
<h1 style='#{rich_style}' data-content='articles/#{article.id}'>
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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],"<a href='#{url[:url]}'>#{url[:display_url]}</a>")
|
||||
text.gsub!(url[:url], "<a href='#{url[:url]}'>#{url[:display_url]}</a>")
|
||||
end
|
||||
mentioned_usernames_serialized.each do |username|
|
||||
uname = username["screen_name"]
|
||||
text.gsub!("@"+uname,"<a href='https://twitter.com/#{uname}'>#{"@"+uname}</a>")
|
||||
text.gsub!("@" + uname, "<a href='https://twitter.com/#{uname}'>#{'@' + uname}</a>")
|
||||
end
|
||||
hashtags_serialized.each do |tag|
|
||||
tag_text = tag[:text]
|
||||
text.gsub!("#"+tag_text,"<a href='https://twitter.com/hashtag/#{tag_text}'>#{"#"+tag_text}</a>")
|
||||
text.gsub!("#" + tag_text, "<a href='https://twitter.com/hashtag/#{tag_text}'>#{'#' + tag_text}</a>")
|
||||
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","<br/>")
|
||||
text.gsub!("\n", "<br/>")
|
||||
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
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
var query = filterXSS(params.q);
|
||||
var filters = filterXSS(params.filters || "");
|
||||
document.getElementById("substories").innerHTML = '<div class="query-results-nothing"><div class="query-results-loader"></div><br/></div>'
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<% end %>
|
||||
</h2>
|
||||
<div class="button-container">
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant>
|
||||
TWITTER
|
||||
</a>
|
||||
<a href="/users/auth/github?state=in-feed-cta" class="cta cta-button" data-no-instant>
|
||||
|
|
|
|||
|
|
@ -75,7 +75,7 @@
|
|||
</header>
|
||||
<div class="widget-body">
|
||||
<center style="margin-bottom: 2px;">
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button login-cta-button" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button login-cta-button" data-no-instant>
|
||||
SIGN IN VIA TWITTER
|
||||
</a>
|
||||
<a href="/users/auth/github?state=navbar_basic" class="cta cta-button login-cta-button" data-no-instant>
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
<div
|
||||
id="chat"
|
||||
class="live-chat"
|
||||
data-pusher-key="<%= ENV["PUSHER_KEY"] %>"
|
||||
data-algolia-id="<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>"
|
||||
data-pusher-key="<%= ApplicationConfig["PUSHER_KEY"] %>"
|
||||
data-algolia-id="<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>"
|
||||
data-algolia-key="<%= @secured_algolia_key %>"
|
||||
data-algolia-index="<%= "SecuredChatChannel_#{Rails.env}" %>"
|
||||
data-github-token="<%= @github_token %>"
|
||||
|
|
@ -43,5 +43,5 @@
|
|||
#page-content {max-height:100vh}
|
||||
</style>
|
||||
<script>
|
||||
window.vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(ENV['VAPID_PUBLIC_KEY']).bytes %>);
|
||||
window.vapidPublicKey = new Uint8Array(<%= Base64.urlsafe_decode64(ApplicationConfig['VAPID_PUBLIC_KEY']).bytes %>);
|
||||
</script>
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
<div class="new-article-pitch">
|
||||
<h1>Sign In or Create Your Account</h1>
|
||||
<div class="links">
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant>
|
||||
<%= icon("twitter") %> Auth with Twitter
|
||||
</a>
|
||||
<a href="/users/auth/github?state=join-club-page_basic" class="sign-up-link" data-no-instant>
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@
|
|||
<%= flash[:notice] %>
|
||||
</div>
|
||||
<% end %>
|
||||
<%= recaptcha_tags site_key: ENV['RECAPTCHA_SITE'] %>
|
||||
<%= recaptcha_tags site_key: ApplicationConfig['RECAPTCHA_SITE'] %>
|
||||
<input type="submit" name="commit" value="Send Feedback" style="margin-top: 40px;" />
|
||||
</div>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
<script src="//d79i1fxsrar4t.cloudfront.net/jquery.liveaddress/3.4/jquery.liveaddress.min.js"></script>
|
||||
<script>
|
||||
var liveaddress = jQuery.LiveAddress({
|
||||
key: "<%= ENV['SMARTY_STREETS_WEB_KEY'] %>",
|
||||
key: "<%= ApplicationConfig['SMARTY_STREETS_WEB_KEY'] %>",
|
||||
target: "US|International",
|
||||
addresses: [{
|
||||
address1: '#user_shipping_address',
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
<p>* * *</p>
|
||||
<h2>Please sign in</h2>
|
||||
<br/>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><span class="sign-up-text"><img src="<%= asset_url("nerd-emoji.png") %>" alt="nerd emoji" /> Sign in with Twitter</span></a>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><span class="sign-up-text"><img src="<%= asset_url("nerd-emoji.png") %>" alt="nerd emoji" /> Sign in with Twitter</span></a>
|
||||
<p>or</p>
|
||||
<a href="/users/auth/github" class="sign-up-link" data-no-instant><span class="sign-up-text github-text"><img src="<%= asset_url("nerd-emoji.png") %>" alt="nerd emoji" /> Sign in with Github</span></a>
|
||||
</center>
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@
|
|||
Sign In/Up
|
||||
</div>
|
||||
</a>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" data-no-instant>
|
||||
<div class="option">
|
||||
Via Twitter
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@
|
|||
<p>
|
||||
We’re a place where coders share, stay up-to-date and grow their careers.
|
||||
</p>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link cta" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link cta" data-no-instant>
|
||||
<img src="<%= asset_path("twitter-logo.svg") %>" class="icon-img" alt="Twitter"/> Auth With Twitter
|
||||
</a>
|
||||
<a href="/users/auth/github?state=signup-modal" class="sign-up-link cta" data-no-instant>
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<% cache "base_inline_styles_#{@story_show.to_s}_#{@article_index.to_s}_#{@home_page.to_s}_#{@article_show.to_s}_#{view_class.to_s}_#{@notifications_index}_#{core_pages?.to_s}_#{@tags_index}_#{ENV["DEPLOYMENT_SIGNATURE"].to_s}x_xs__", :expires_in => 6.hours do %>
|
||||
<% cache "base_inline_styles_#{@story_show.to_s}_#{@article_index.to_s}_#{@home_page.to_s}_#{@article_show.to_s}_#{view_class.to_s}_#{@notifications_index}_#{core_pages?.to_s}_#{@tags_index}_#{ApplicationConfig["DEPLOYMENT_SIGNATURE"].to_s}x_xs__", :expires_in => 6.hours do %>
|
||||
<% if @story_show %>
|
||||
<style>
|
||||
<% Rails.application.config.assets.compile = true %>
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@
|
|||
<% else %>
|
||||
<meta name="last-updated" content="<%= Time.now %>">
|
||||
<meta name="user-signed-in" content="<%= user_signed_in? %>">
|
||||
<meta name="algolia-public-id" content="<%= ENV["ALGOLIASEARCH_APPLICATION_ID"] %>">
|
||||
<meta name="algolia-public-key" content="<%= ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>">
|
||||
<meta name="algolia-public-id" content="<%= ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"] %>">
|
||||
<meta name="algolia-public-key" content="<%= ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] %>">
|
||||
<meta name="environment" content="<%= Rails.env %>">
|
||||
|
||||
<%= render "layouts/styles" %>
|
||||
|
|
@ -71,7 +71,7 @@
|
|||
<link rel="search" href="https://dev.to/search.xml" type="application/opensearchdescription+xml" title="The DEV Community"/>
|
||||
<% end %>
|
||||
</head>
|
||||
<body data-user-status="<%= user_logged_in_status %>" data-pusher-key="<%= ENV["PUSHER_KEY"] %>">
|
||||
<body data-user-status="<%= user_logged_in_status %>" data-pusher-key="<%= ApplicationConfig["PUSHER_KEY"] %>">
|
||||
<% unless is_internal_navigation? %>
|
||||
<div id="audiocontent">
|
||||
<%= yield(:audio) %>
|
||||
|
|
@ -87,7 +87,7 @@
|
|||
</div>
|
||||
</div>
|
||||
<% unless is_internal_navigation? %>
|
||||
<% cache("footer-and-signup-modal--#{user_signed_in?}--#{ENV["DEPLOYMENT_SIGNATURE"].to_s}", :expires_in => 200.hours) do %>
|
||||
<% cache("footer-and-signup-modal--#{user_signed_in?}--#{ApplicationConfig["DEPLOYMENT_SIGNATURE"].to_s}", :expires_in => 200.hours) do %>
|
||||
<%= render "layouts/footer" %>
|
||||
<%= render "layouts/signup_modal" unless user_signed_in? %>
|
||||
<% end %>
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<hr/>
|
||||
Authenticate via
|
||||
<center>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><%= image_tag("twitter-logo.svg", class:"icon-img", alt: "twitter logo") %> Twitter</a>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><%= image_tag("twitter-logo.svg", class:"icon-img", alt: "twitter logo") %> Twitter</a>
|
||||
or
|
||||
<a href="/users/auth/github?state=notifications-cue_<%= version %>" class="sign-up-link" data-no-instant><%= image_tag("github-logo.svg", class:"icon-img", alt: "github logo") %> GitHub</a>
|
||||
</center>
|
||||
|
|
@ -21,7 +21,7 @@
|
|||
Create your account to customize your feed and start participating.
|
||||
</p>
|
||||
<center>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><%= image_tag("twitter-logo.svg", class:"icon-img", alt: "twitter logo") %> Twitter</a>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="sign-up-link" data-no-instant><%= image_tag("twitter-logo.svg", class:"icon-img", alt: "twitter logo") %> Twitter</a>
|
||||
or
|
||||
<a href="/users/auth/github?state=notifications-cue_<%= version %>" class="sign-up-link" data-no-instant><%= image_tag("github-logo.svg", class:"icon-img", alt: "github logo") %> GitHub</a>
|
||||
</center>
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
<img style="width:30px; display:inline;" src="<%= asset_path("emoji/emoji-sparkling-heart.png") %>" />
|
||||
<img style="width:30px; display:inline;" src="<%= asset_path("emoji/emoji-sparkling-heart.png") %>" />
|
||||
<h2>Sign in to get your membership </h2>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="cta cta-button" data-no-instant>
|
||||
TWITTER
|
||||
</a>
|
||||
<a href="/users/auth/github?state=membership-cta" class="cta cta-button" data-no-instant>
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
<% if @user.identities.where(provider:'twitter').empty? %>
|
||||
<div class="field">
|
||||
<label>Action</label>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ENV["APP_PROTOCOL"] %><%= ENV["APP_DOMAIN"] %>/users/auth/twitter/callback" class="big-button cta" data-no-instant>
|
||||
<a href="/users/auth/twitter?callback_url=<%= ApplicationConfig["APP_PROTOCOL"] %><%= ApplicationConfig["APP_DOMAIN"] %>/users/auth/twitter/callback" class="big-button cta" data-no-instant>
|
||||
<img src="<%= asset_path('twitter-logo.svg') %>" /> CONNECT TWITTER ACCOUNT
|
||||
</a>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ require 'sprockets/es6'
|
|||
# Require the gems listed in Gemfile, including any gems
|
||||
# you've limited to :test, :development, or :production.
|
||||
Bundler.require(*Rails.groups)
|
||||
ENVied.require(*ENV['ENVIED_GROUPS'] || Rails.groups)
|
||||
|
||||
module PracticalDeveloper
|
||||
class Application < Rails::Application
|
||||
|
|
|
|||
|
|
@ -10,6 +10,8 @@ Rails.application.configure do
|
|||
# and recreated between test runs. Don't rely on the data there!
|
||||
config.cache_classes = true
|
||||
|
||||
config.cache_store = :null_store
|
||||
|
||||
# Do not eager load code on boot. This avoids loading your whole application
|
||||
# just for the purpose of running a single test. If you are using a tool that
|
||||
# preloads Rails for running tests, you may have to set it to true.
|
||||
|
|
|
|||
9
config/initializers/0_application_config.rb
Normal file
9
config/initializers/0_application_config.rb
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
class ApplicationConfig
|
||||
def self.[](key)
|
||||
ENVied.send(key)
|
||||
end
|
||||
|
||||
def self.[]=(key, value)
|
||||
ENV[key] = value
|
||||
end
|
||||
end
|
||||
|
|
@ -1,91 +0,0 @@
|
|||
# This file is named in such a manner so that it run first.
|
||||
|
||||
keys = [
|
||||
"AIRBRAKE_API_KEY",
|
||||
"AIRBRAKE_PROJECT_ID",
|
||||
"ALGOLIASEARCH_API_KEY",
|
||||
"ALGOLIASEARCH_APPLICATION_ID",
|
||||
"ALGOLIASEARCH_SEARCH_ONLY_KEY",
|
||||
"APP_DOMAIN",
|
||||
"APP_PROTOCOL",
|
||||
"AWS_DEFAULT_REGION",
|
||||
"AWS_SDK_KEY",
|
||||
"AWS_SDK_SECRET",
|
||||
"AWS_S3_VIDEO_ID",
|
||||
"AWS_S3_VIDEO_KEY",
|
||||
"AWS_S3_INPUT_BUCKET",
|
||||
"BUFFER_ACCESS_TOKEN",
|
||||
"BUFFER_FACEBOOK_ID",
|
||||
"BUFFER_LINKEDIN_ID",
|
||||
"BUFFER_PROFILE_ID",
|
||||
"BUFFER_TWITTER_ID",
|
||||
"CLOUDINARY_API_KEY",
|
||||
"CLOUDINARY_API_SECRET",
|
||||
"CLOUDINARY_CLOUD_NAME",
|
||||
"CLOUDINARY_SECURE",
|
||||
"DACAST_STREAM_CODE",
|
||||
"DEPLOYMENT_SIGNATURE",
|
||||
"DEVTO_USER_ID",
|
||||
"GA_SERVICE_ACCOUNT_JSON",
|
||||
"GA_TRACKING_ID",
|
||||
"GA_VIEW_ID",
|
||||
"GITHUB_KEY",
|
||||
"GITHUB_SECRET",
|
||||
"GITHUB_TOKEN",
|
||||
"JWPLAYER_API_KEY",
|
||||
"JWPLAYER_API_SECRET",
|
||||
"MAILCHIMP_API_KEY",
|
||||
"MAILCHIMP_NEWSLETTER_ID",
|
||||
"PERIODIC_EMAIL_DIGEST_MAX",
|
||||
"PERIODIC_EMAIL_DIGEST_MIN",
|
||||
"PUSHER_APP_ID",
|
||||
"PUSHER_CLUSTER",
|
||||
"PUSHER_KEY",
|
||||
"PUSHER_SECRET",
|
||||
"RECAPTCHA_SECRET",
|
||||
"RECAPTCHA_SITE",
|
||||
"SERVICE_TIMEOUT",
|
||||
"SHARE_MEOW_BASE_URL",
|
||||
"SHARE_MEOW_SECRET_KEY",
|
||||
"SLACK_CHANNEL",
|
||||
"SLACK_WEBHOOK_URL",
|
||||
"STREAM_RAILS_KEY",
|
||||
"STREAM_RAILS_SECRET",
|
||||
"STREAM_URL",
|
||||
"STRIPE_PUBLISHABLE_KEY",
|
||||
"STRIPE_SECRET_KEY",
|
||||
"TWILIO_ACCOUNT_SID",
|
||||
"TWILIO_VIDEO_API_KEY",
|
||||
"TWILIO_VIDEO_API_SECRET",
|
||||
"TWITTER_ACCESS_TOKEN",
|
||||
"TWITTER_ACCESS_TOKEN_SECRET",
|
||||
"TWITTER_KEY",
|
||||
"TWITTER_SECRET",
|
||||
"VAPID_PUBLIC_KEY",
|
||||
"VAPID_PRIVATE_KEY"
|
||||
].freeze
|
||||
|
||||
missing = []
|
||||
|
||||
keys.each do |k|
|
||||
missing << k if ENV[k].nil?
|
||||
end
|
||||
|
||||
# Run the checker when
|
||||
# 1. Not in production
|
||||
# 2. Not in CI
|
||||
# 3. There are missing keys
|
||||
if Rails.env != "production" && !ENV["CI"] && !missing.empty? && Rails.env != "test"
|
||||
message = <<~HEREDOC
|
||||
\n
|
||||
=====================================================
|
||||
Hey there DEVeloper!
|
||||
You are missing the [#{missing.length}] environment variable(s).
|
||||
Please obtain these missing key(s) and try again.
|
||||
-----------------------------------------------------
|
||||
#{missing.join("\n")}
|
||||
=====================================================
|
||||
\n
|
||||
HEREDOC
|
||||
raise message
|
||||
end
|
||||
|
|
@ -12,8 +12,8 @@ Airbrake.configure do |c|
|
|||
# project_key navigate to your project's General Settings and copy the values
|
||||
# from the right sidebar.
|
||||
# https://github.com/airbrake/airbrake-ruby#project_id--project_key
|
||||
c.project_id = ENV['AIRBRAKE_PROJECT_ID']
|
||||
c.project_key = ENV['AIRBRAKE_API_KEY']
|
||||
c.project_id = ApplicationConfig['AIRBRAKE_PROJECT_ID']
|
||||
c.project_key = ApplicationConfig['AIRBRAKE_API_KEY']
|
||||
|
||||
# Configures the root directory of your project. Expects a String or a
|
||||
# Pathname, which represents the path to your project. Providing this option
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
AlgoliaSearch.configuration = { application_id: ENV["ALGOLIASEARCH_APPLICATION_ID"], api_key: ENV["ALGOLIASEARCH_API_KEY"] }
|
||||
AlgoliaSearch.configuration = {
|
||||
application_id: ApplicationConfig["ALGOLIASEARCH_APPLICATION_ID"],
|
||||
api_key: ApplicationConfig["ALGOLIASEARCH_API_KEY"]
|
||||
}
|
||||
|
||||
if Rails.env.test?
|
||||
ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] = "TEST_KEY"
|
||||
else
|
||||
if !Rails.env.test?
|
||||
# Restrtict Access to private indices
|
||||
params = {
|
||||
restrictIndices: "searchables_#{Rails.env},ordered_articles_#{Rails.env},ordered_articles_by_published_at_#{Rails.env},ordered_articles_by_positive_reactions_count_#{Rails.env},ordered_comments_#{Rails.env}",
|
||||
}
|
||||
secured_algolia_key = Algolia.generate_secured_api_key(
|
||||
ENV["ALGOLIASEARCH_SEARCH_ONLY_KEY"], params,
|
||||
ApplicationConfig["ALGOLIASEARCH_SEARCH_ONLY_KEY"], params,
|
||||
)
|
||||
|
||||
ENV["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] = secured_algolia_key
|
||||
end
|
||||
ApplicationConfig["ALGOLIASEARCH_PUBLIC_SEARCH_ONLY_KEY"] = secured_algolia_key
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
::AwsLambda = Aws::Lambda::Client.new(
|
||||
region: ENV["AWS_DEFAULT_REGION"],
|
||||
access_key_id: ENV["AWS_SDK_KEY"],
|
||||
secret_access_key: ENV["AWS_SDK_SECRET"]
|
||||
AWS_LAMBDA = Aws::Lambda::Client.new(
|
||||
region: ApplicationConfig["AWS_DEFAULT_REGION"],
|
||||
access_key_id: ApplicationConfig["AWS_SDK_KEY"],
|
||||
secret_access_key: ApplicationConfig["AWS_SDK_SECRET"],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ CarrierWave.configure do |config|
|
|||
config.storage = :fog
|
||||
config.fog_credentials = {
|
||||
provider: "AWS",
|
||||
aws_access_key_id: ENV["AWS_ID"],
|
||||
aws_secret_access_key: ENV["AWS_SECRET"],
|
||||
aws_access_key_id: ApplicationConfig["AWS_ID"],
|
||||
aws_secret_access_key: ApplicationConfig["AWS_SECRET"],
|
||||
region: "us-east-1",
|
||||
}
|
||||
config.fog_directory = ENV["AWS_BUCKET_NAME"]
|
||||
config.fog_directory = ApplicationConfig["AWS_BUCKET_NAME"]
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Cloudinary.config do |config|
|
||||
config.cloud_name = ENV["CLOUDINARY_CLOUD_NAME"]
|
||||
config.api_key = ENV["CLOUDINARY_API_KEY"]
|
||||
config.api_secret = ENV["CLOUDINARY_API_SECRET"]
|
||||
config.secure = ENV["CLOUDINARY_SECURE"]
|
||||
config.cloud_name = ApplicationConfig["CLOUDINARY_CLOUD_NAME"]
|
||||
config.api_key = ApplicationConfig["CLOUDINARY_API_KEY"]
|
||||
config.api_secret = ApplicationConfig["CLOUDINARY_API_SECRET"]
|
||||
config.secure = ApplicationConfig["CLOUDINARY_SECURE"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,3 +1,3 @@
|
|||
DelayedJobWeb.use Rack::Auth::Basic do |username, password|
|
||||
username == ENV["APP_NAME"] && password == ENV["APP_PASSWORD"]
|
||||
username == ApplicationConfig["APP_NAME"] && password == ApplicationConfig["APP_PASSWORD"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ Devise.setup do |config|
|
|||
# so you need to do it manually. For the users scope, it would be:
|
||||
# config.omniauth_path_prefix = '/my_engine/users/auth'
|
||||
|
||||
config.omniauth :github, ENV["GITHUB_KEY"], ENV["GITHUB_SECRET"], scope: "user:email"
|
||||
config.omniauth :twitter, ENV["TWITTER_KEY"], ENV["TWITTER_SECRET"]
|
||||
config.omniauth :github, ApplicationConfig["GITHUB_KEY"], ApplicationConfig["GITHUB_SECRET"], scope: "user:email"
|
||||
config.omniauth :twitter, ApplicationConfig["TWITTER_KEY"], ApplicationConfig["TWITTER_SECRET"]
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
FastlyRails.configure do |c|
|
||||
|
||||
c.api_key = ENV['FASTLY_API_KEY'] # Fastly api key, required
|
||||
c.api_key = ApplicationConfig['FASTLY_API_KEY'] # Fastly api key, required
|
||||
c.max_age = 86500 # time in seconds, optional, defaults to 2592000 (30 days)
|
||||
c.service_id = ENV['FASTLY_SERVICE_ID'] # The Fastly service you will be using, required
|
||||
c.service_id = ApplicationConfig['FASTLY_SERVICE_ID'] # The Fastly service you will be using, required
|
||||
c.stale_if_error = 26400
|
||||
c.purging_enabled = Rails.env.production? # No need to configure a client locally (AVAILABLE ONLY AS OF 0.4.0)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,2 +1,2 @@
|
|||
Gibbon::Request.api_key = ENV["MAILCHIMP_API_KEY"]
|
||||
Gibbon::Request.api_key = ApplicationConfig["MAILCHIMP_API_KEY"]
|
||||
Gibbon::Request.timeout = 15
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
require "pusher"
|
||||
|
||||
Pusher.app_id = ENV["PUSHER_APP_ID"]
|
||||
Pusher.key = ENV["PUSHER_KEY"]
|
||||
Pusher.secret = ENV["PUSHER_SECRET"]
|
||||
Pusher.cluster = ENV["PUSHER_CLUSTER"]
|
||||
Pusher.app_id = ApplicationConfig["PUSHER_APP_ID"]
|
||||
Pusher.key = ApplicationConfig["PUSHER_KEY"]
|
||||
Pusher.secret = ApplicationConfig["PUSHER_SECRET"]
|
||||
Pusher.cluster = ApplicationConfig["PUSHER_CLUSTER"]
|
||||
Pusher.logger = Rails.logger
|
||||
Pusher.encrypted = true
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
S3DirectUpload.config do |c|
|
||||
c.access_key_id = ENV['AWS_S3_VIDEO_ID'] # your access key id
|
||||
c.secret_access_key = ENV['AWS_S3_VIDEO_KEY'] # your secret access key
|
||||
c.bucket = ENV['AWS_S3_INPUT_BUCKET'] # your bucket name
|
||||
c.access_key_id = ApplicationConfig['AWS_S3_VIDEO_ID'] # your access key id
|
||||
c.secret_access_key = ApplicationConfig['AWS_S3_VIDEO_KEY'] # your secret access key
|
||||
c.bucket = ApplicationConfig['AWS_S3_INPUT_BUCKET'] # your bucket name
|
||||
c.region = nil # region prefix of your bucket url. This is _required_ for the non-default AWS region, eg. "s3-eu-west-1"
|
||||
c.url = nil # S3 API endpoint (optional), eg. "https://#{c.bucket}.s3.amazonaws.com/"
|
||||
end
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
ShareMeowClient.configuration do |config|
|
||||
config.base_url = ENV["SHARE_MEOW_BASE_URL"]
|
||||
config.secret_key = ENV["SHARE_MEOW_SECRET_KEY"]
|
||||
config.base_url = ApplicationConfig["SHARE_MEOW_BASE_URL"]
|
||||
config.secret_key = ApplicationConfig["SHARE_MEOW_SECRET_KEY"]
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,15 +6,15 @@ end
|
|||
|
||||
def create_normal_notifier
|
||||
Slack::Notifier.new(
|
||||
ENV["SLACK_WEBHOOK_URL"],
|
||||
channel: ENV["SLACK_CHANNEL"],
|
||||
ApplicationConfig["SLACK_WEBHOOK_URL"],
|
||||
channel: ApplicationConfig["SLACK_CHANNEL"],
|
||||
username: "activity_bot",
|
||||
)
|
||||
end
|
||||
|
||||
def create_test_channel_notifier
|
||||
Slack::Notifier.new(
|
||||
ENV["SLACK_WEBHOOK_URL"],
|
||||
ApplicationConfig["SLACK_WEBHOOK_URL"],
|
||||
channel: "#test",
|
||||
username: "development_test_bot",
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
require 'stream_rails'
|
||||
require "stream_rails"
|
||||
|
||||
StreamRails.configure do |config|
|
||||
config.api_key = ENV['STREAM_RAILS_KEY']
|
||||
config.api_secret = ENV['STREAM_RAILS_SECRET']
|
||||
config.timeout = 3 # Optional, defaults to 3
|
||||
config.location = 'us-east' # Optional, defaults to 'us-east'
|
||||
config.api_key = ApplicationConfig["STREAM_RAILS_KEY"]
|
||||
config.api_secret = ApplicationConfig["STREAM_RAILS_SECRET"]
|
||||
config.timeout = 3 # Optional, defaults to 3
|
||||
config.location = "us-east" # Optional, defaults to 'us-east'
|
||||
# If you use custom feed names, e.g.: timeline_flat, timeline_aggregated,
|
||||
# use this, otherwise omit:
|
||||
# config.news_feeds = { flat: "flat", aggregated: "timeline_aggregated" }
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
Rails.configuration.stripe = {
|
||||
publishable_key: ENV["STRIPE_PUBLISHABLE_KEY"],
|
||||
secret_key: ENV["STRIPE_SECRET_KEY"],
|
||||
publishable_key: ApplicationConfig["STRIPE_PUBLISHABLE_KEY"],
|
||||
secret_key: ApplicationConfig["STRIPE_SECRET_KEY"],
|
||||
}
|
||||
|
||||
Stripe.api_key = Rails.configuration.stripe[:secret_key]
|
||||
|
|
|
|||
|
|
@ -1,142 +0,0 @@
|
|||
# This is a sample application.yml
|
||||
#
|
||||
# If you wish to use this, please copy this file and rename it to application.yml
|
||||
# For questions or concerns, please submit an issue!
|
||||
|
||||
################################################
|
||||
############## Basic configuration #############
|
||||
################################################
|
||||
|
||||
# App damain + Protocol setting for development
|
||||
APP_DOMAIN: "localhost:3000"
|
||||
APP_PROTOCOL: "http://"
|
||||
|
||||
# Default staff account's id
|
||||
DEVTO_USER_ID: "1"
|
||||
|
||||
# Service timeout length
|
||||
SERVICE_TIMEOUT: "100000"
|
||||
|
||||
# Production related config that can be ignored
|
||||
DEPLOYMENT_SIGNATURE: "PLACEHOLDER"
|
||||
|
||||
################################################
|
||||
############## 3rd Party Services ##############
|
||||
################################################
|
||||
|
||||
# It's best if you have the following configured
|
||||
# for more development experience
|
||||
|
||||
# Github for github related access
|
||||
GITHUB_KEY: "REPLACEME"
|
||||
GITHUB_SECRET: "REPLACEME"
|
||||
GITHUB_TOKEN: "REPLACEME"
|
||||
|
||||
# Twitter for normal twitter access
|
||||
TWITTER_ACCESS_TOKEN: "REPLACEME"
|
||||
TWITTER_ACCESS_TOKEN_SECRET: "REPLACEME"
|
||||
TWITTER_KEY: "REPLACEME"
|
||||
TWITTER_SECRET: "REPLACEME"
|
||||
|
||||
################################################
|
||||
######### Optional 3rd Party Services ##########
|
||||
################################################
|
||||
|
||||
# They are very optional
|
||||
|
||||
# Airbrake for error tracking
|
||||
AIRBRAKE_API_KEY: "REPLACEME"
|
||||
AIRBRAKE_PROJECT_ID: "REPLACEME"
|
||||
|
||||
# Algolish for search
|
||||
ALGOLIASEARCH_API_KEY: "REPLACEME"
|
||||
ALGOLIASEARCH_APPLICATION_ID: "REPLACEME"
|
||||
ALGOLIASEARCH_SEARCH_ONLY_KEY: "REPLACEME"
|
||||
|
||||
# AWS for images storages
|
||||
AWS_DEFAULT_REGION: "REPLACEME"
|
||||
AWS_SDK_KEY: "REPLACEME"
|
||||
AWS_SDK_SECRET: "REPLACEME"
|
||||
|
||||
# AWS for video storage
|
||||
AWS_S3_VIDEO_ID: "REPLACEME"
|
||||
AWS_S3_VIDEO_KEY: "REPLACEME"
|
||||
AWS_S3_INPUT_BUCKET: "REPLACEME"
|
||||
AWS_S3_VIDEO_ID: "REPLACEME"
|
||||
AWS_S3_VIDEO_KEY: "REPLACEME"
|
||||
AWS_SDK_KEY: "REPLACEME"
|
||||
AWS_SDK_SECRET: "REPLACEME"
|
||||
AWS_SECRET: "REPLACEME"
|
||||
|
||||
|
||||
# Buffer for sending to buffer
|
||||
BUFFER_ACCESS_TOKEN: "REPLACEME"
|
||||
BUFFER_FACEBOOK_ID: "REPLACEME"
|
||||
BUFFER_LINKEDIN_ID: "REPLACEME"
|
||||
BUFFER_PROFILE_ID: "REPLACEME"
|
||||
BUFFER_TWITTER_ID: "REPLACEME"
|
||||
|
||||
# Cloudinary for image resizing and cache??
|
||||
CLOUDINARY_API_KEY: "REPLACEME"
|
||||
CLOUDINARY_API_SECRET: "REPLACEME"
|
||||
CLOUDINARY_CLOUD_NAME: "REPLACEME"
|
||||
CLOUDINARY_SECURE: "REPLACEME"
|
||||
|
||||
# Dacast for streaming
|
||||
DACAST_STREAM_CODE: "REPLACEME"
|
||||
|
||||
# Google analytic
|
||||
GA_SERVICE_ACCOUNT_JSON: "REPLACEME"
|
||||
GA_TRACKING_ID: "REPLACEME"
|
||||
GA_VIEW_ID: "REPLACEME"
|
||||
GA_OPTIMIZE_ID: "REPLACEME"
|
||||
|
||||
# JW player for videos on the site
|
||||
JWPLAYER_API_KEY: "REPLACEME"
|
||||
JWPLAYER_API_SECRET: "REPLACEME"
|
||||
|
||||
# Mailchimp for mails
|
||||
MAILCHIMP_API_KEY: "REPLACEME"
|
||||
MAILCHIMP_NEWSLETTER_ID: "REPLACEME"
|
||||
MAILCHIMP_SUSTAINING_MEMBERS_ID: "REPLACEME"
|
||||
|
||||
# Google recaptcha
|
||||
RECAPTCHA_SECRET: "REPLACEME"
|
||||
RECAPTCHA_SITE: "REPLACEME"
|
||||
|
||||
# Sharemeow is a text-shot service
|
||||
# more information here https://github.com/producthunt/ShareMeow
|
||||
SHARE_MEOW_BASE_URL: "REPLACEME"
|
||||
SHARE_MEOW_SECRET_KEY: "REPLACEME"
|
||||
|
||||
# Slack for customer alerts
|
||||
SLACK_CHANNEL: "REPLACEME"
|
||||
SLACK_WEBHOOK_URL: "REPLACEME"
|
||||
|
||||
# Stream for notifications
|
||||
STREAM_RAILS_KEY: "REPLACEME"
|
||||
STREAM_RAILS_SECRET: "REPLACEME"
|
||||
STREAM_URL: "REPLACEME"
|
||||
|
||||
# Stripe for payment system
|
||||
STRIPE_PUBLISHABLE_KEY: "REPLACEME"
|
||||
STRIPE_SECRET_KEY: "REPLACEME"
|
||||
|
||||
#Email digest frequency
|
||||
PERIODIC_EMAIL_DIGEST_MAX: 10
|
||||
PERIODIC_EMAIL_DIGEST_MIN: 2
|
||||
|
||||
#Pusher for DEV Connect/notfications
|
||||
PUSHER_APP_ID: "REPLACEME"
|
||||
PUSHER_CLUSTER: "REPLACEME"
|
||||
PUSHER_KEY: "REPLACEME"
|
||||
PUSHER_SECRET: "REPLACEME"
|
||||
|
||||
#For video calling in DEV Connect
|
||||
TWILIO_ACCOUNT_SID: "REPLACEME"
|
||||
TWILIO_VIDEO_API_KEY: "REPLACEME"
|
||||
TWILIO_VIDEO_API_SECRET: "REPLACEME"
|
||||
|
||||
#For browser webpush notifications (webpush gem) (totally random base64 nums)
|
||||
VAPID_PUBLIC_KEY: "dGhpcyBpcyBkZXYudG8gdGVzdCBkYXRh"
|
||||
VAPID_PRIVATE_KEY: "VGhpcyBpcyBtb3JlIHRlc3QgZGF0YQ=="
|
||||
|
|
@ -28,7 +28,7 @@ FactoryBot.define do
|
|||
---
|
||||
|
||||
#{Faker::Hipster.paragraph(2)}
|
||||
#{"{% tweet 232398293939393 %}" if with_tweet_tag}
|
||||
#{"{% tweet 1018911886862057472%}" if with_tweet_tag}
|
||||
#{Faker::Hipster.paragraph(1)}
|
||||
#{"\n\n---\n\n something \n\n---\n funky in the code? \n---\n That's nice"if with_hr_issue}
|
||||
HEREDOC
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
require "rails_helper"
|
||||
|
||||
feature "Using the editor" do
|
||||
describe "Using the editor" do
|
||||
let(:user) { create(:user) }
|
||||
let(:raw_text) { "../support/fixtures/sample_article_template_spec.txt" }
|
||||
# what are these
|
||||
|
|
@ -9,7 +9,7 @@ feature "Using the editor" do
|
|||
let(:template) { File.read(File.join(File.dirname(__FILE__), dir)) }
|
||||
let(:rich_template) { File.read(File.join(File.dirname(__FILE__), rich_dir)) }
|
||||
|
||||
background do
|
||||
before do
|
||||
sign_in user
|
||||
end
|
||||
|
||||
|
|
@ -22,12 +22,12 @@ feature "Using the editor" do
|
|||
fill_in "article_body_markdown", with: content
|
||||
end
|
||||
|
||||
feature "Previewing an article", js: true do
|
||||
describe "Previewing an article", js: true do
|
||||
after do
|
||||
page.evaluate_script("window.onbeforeunload = function(){}")
|
||||
end
|
||||
|
||||
scenario "fill out form with ruch content and click preview" do
|
||||
it "fill out form with ruch content and click preview" do
|
||||
fill_markdown_with(read_from_file(raw_text))
|
||||
page.execute_script("window.scrollTo(0, -100000)")
|
||||
find("button#previewbutt").click
|
||||
|
|
@ -36,15 +36,15 @@ feature "Using the editor" do
|
|||
end
|
||||
end
|
||||
|
||||
feature "Submitting an article" do
|
||||
scenario "fill out form and submit" do
|
||||
describe "Submitting an article" do
|
||||
it "fill out form and submit" do
|
||||
fill_markdown_with(read_from_file(raw_text))
|
||||
click_button("article-submit")
|
||||
article_body = find(:xpath, "//div[@id='article-body']")["innerHTML"]
|
||||
Approvals.verify(article_body, name: "user_submit_article", format: :html)
|
||||
end
|
||||
|
||||
scenario "user write and publish an article" do
|
||||
it "user write and publish an article" do
|
||||
fill_markdown_with(template.gsub("false", "true"))
|
||||
click_button("article-submit")
|
||||
["Sample Article", template[-200..-1], "test"].each do |text|
|
||||
|
|
@ -52,7 +52,7 @@ feature "Using the editor" do
|
|||
end
|
||||
end
|
||||
|
||||
scenario "user write and publish an article without a title" do
|
||||
it "user write and publish an article without a title" do
|
||||
fill_markdown_with(template.gsub("Sample Article", ""))
|
||||
click_button("article-submit")
|
||||
expect(page).to have_css("div#error_explanation",
|
||||
|
|
|
|||
|
|
@ -36,9 +36,7 @@ RSpec.describe RssReader, vcr: vcr_option do
|
|||
|
||||
it "gets articles for user" do
|
||||
described_class.new.fetch_user(User.first)
|
||||
# the 7 here depends on the fixture
|
||||
# not fetching comments is baked into this
|
||||
expect(Article.all.length).to be > 2
|
||||
expect(Article.all.length).to be > 0
|
||||
end
|
||||
|
||||
it "does not set featured_number" do
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe TweetTag, type: :liquid_template do
|
||||
let(:twitter_id) { "783472379167113216" }
|
||||
let(:twitter_id) { "1018911886862057472" }
|
||||
|
||||
setup { Liquid::Template.register_tag("tweet", TweetTag) }
|
||||
|
||||
|
|
@ -9,11 +9,11 @@ RSpec.describe TweetTag, type: :liquid_template do
|
|||
Liquid::Template.parse("{% tweet #{id} %}")
|
||||
end
|
||||
|
||||
it "accepts valid tweet id" do
|
||||
liquid = generate_tweet_liquid_tag(twitter_id)
|
||||
dir = File.join(File.dirname(__FILE__), "../support/fixtures/tweet_1.json")
|
||||
actual_response = JSON.parse(File.read(dir))
|
||||
expect(liquid.root.nodelist[0].tweet.text).to eq(actual_response["text"])
|
||||
it "accepts valid tweet id", :vcr do
|
||||
VCR.use_cassette("twitter_gem") do
|
||||
liquid = generate_tweet_liquid_tag(twitter_id)
|
||||
expect(liquid.root.nodelist[0].tweet.text).not_to eq(nil)
|
||||
end
|
||||
end
|
||||
|
||||
context "when given invalid id" do
|
||||
|
|
|
|||
|
|
@ -298,9 +298,11 @@ RSpec.describe Article, type: :model do
|
|||
article.remove_algolia_index
|
||||
end
|
||||
|
||||
it "detects liquid tags used" do
|
||||
article = build_and_validate_article(with_tweet_tag: true)
|
||||
expect(article.decorate.liquid_tags_used).to eq([TweetTag])
|
||||
it "detects liquid tags used", :vcr do
|
||||
VCR.use_cassette("twitter_gem") do
|
||||
article = build_and_validate_article(with_tweet_tag: true)
|
||||
expect(article.decorate.liquid_tags_used).to eq([TweetTag])
|
||||
end
|
||||
end
|
||||
|
||||
it "fixes the issue with --- hr tags" do
|
||||
|
|
@ -440,5 +442,5 @@ RSpec.describe Article, type: :model do
|
|||
last_year = 1.year.ago.year % 100
|
||||
expect(article.readable_publish_date.include?("'#{last_year}")).to eq(true)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,25 +1,26 @@
|
|||
require "rails_helper"
|
||||
|
||||
RSpec.describe ChatChannel, type: :model do
|
||||
let(:chat_channel) { create(:chat_channel) }
|
||||
let(:message) { create(:chat_channel, message_id: chat_channel.id) }
|
||||
let(:chat_channel) { create(:chat_channel) }
|
||||
let(:message) { create(:chat_channel, message_id: chat_channel.id) }
|
||||
|
||||
it { is_expected.to have_many(:messages) }
|
||||
it { is_expected.to validate_presence_of(:channel_type) }
|
||||
|
||||
it "clears chat" do
|
||||
allow(Pusher).to receive(:trigger)
|
||||
chat_channel.clear_channel
|
||||
expect(chat_channel.messages.size).to eq(0)
|
||||
end
|
||||
|
||||
it "creates channel with users" do
|
||||
chat_channel = ChatChannel.create_with_users([create(:user),create(:user)])
|
||||
chat_channel = ChatChannel.create_with_users([create(:user), create(:user)])
|
||||
expect(chat_channel.users.size).to eq(2)
|
||||
expect(chat_channel.has_member?(User.first)).to eq(true)
|
||||
end
|
||||
|
||||
it "lists active memberships" do
|
||||
chat_channel = ChatChannel.create_with_users([create(:user),create(:user)])
|
||||
chat_channel = ChatChannel.create_with_users([create(:user), create(:user)])
|
||||
expect(chat_channel.active_users.size).to eq(2)
|
||||
expect(chat_channel.channel_users.size).to eq(2)
|
||||
ChatChannelMembership.last.update(status: "left_channel")
|
||||
|
|
|
|||
|
|
@ -1,12 +1,20 @@
|
|||
require 'rails_helper'
|
||||
require "rails_helper"
|
||||
|
||||
vcr_option = {
|
||||
cassette_name: "twitter_gem",
|
||||
allow_playback_repeats: "true",
|
||||
}
|
||||
|
||||
RSpec.describe Tweet, type: :model, vcr: vcr_option do
|
||||
let(:tweet_id) { "1018911886862057472" }
|
||||
|
||||
RSpec.describe Tweet, type: :model do
|
||||
it "fetches a tweet" do
|
||||
tweet = Tweet.fetch("STUBBED_TWEET_ID")
|
||||
expect(tweet.class.name).to eq("Tweet")
|
||||
tweet = Tweet.fetch(tweet_id)
|
||||
expect(tweet.class).to eq(described_class)
|
||||
end
|
||||
|
||||
it "renders processed text" do
|
||||
tweet = Tweet.fetch("STUBBED_TWEET_ID")
|
||||
expect(tweet.processed_text).to include("<br/>") # because there is a \n in the tweet
|
||||
tweet = Tweet.fetch(tweet_id)
|
||||
expect(tweet.processed_text).not_to be_nil
|
||||
end
|
||||
end
|
||||
|
|
|
|||
|
|
@ -102,8 +102,6 @@ RSpec.configure do |config|
|
|||
stub_request(:post, /api.bufferapp.com/).
|
||||
to_return(status: 200, body: {fake_text:"so fake"}.to_json, headers: {})
|
||||
|
||||
stub_request(:any, /api.twitter.com/).to_rack(FakeTwitter)
|
||||
|
||||
# stub_request(:any, /api.getstream.io/).to_rack(FakeStream)
|
||||
|
||||
# for twitter image cdn
|
||||
|
|
|
|||
|
|
@ -10,9 +10,14 @@ RSpec.describe "Analytics", type: :request, vcr: vcr_option do
|
|||
let(:user) { create(:user, :analytics) }
|
||||
let(:article1) { create(:article, user_id: user.id) }
|
||||
let(:article2) { create(:article, user_id: user.id) }
|
||||
let(:article3) { create(:article, user_id: user.id) }
|
||||
let(:ga_double) { instance_double(GoogleAnalytics) }
|
||||
|
||||
before do
|
||||
allow(GoogleAnalytics).to receive(:new).and_return(ga_double)
|
||||
allow(ga_double).to receive(:create_service_account_credential).and_return({})
|
||||
allow(ga_double).to receive(:get_pageviews) do
|
||||
{ article1.id.to_s => "0", article2.id.to_s => "0" }
|
||||
end
|
||||
login_as user
|
||||
end
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +0,0 @@
|
|||
require 'sinatra/base'
|
||||
|
||||
class FakeTwitter < Sinatra::Base
|
||||
post '/oauth2/token' do
|
||||
content_type :json
|
||||
status 200
|
||||
end
|
||||
|
||||
get '/1.1/statuses/show/:tweet_id' do
|
||||
json_response 200, 'tweet_1.json'
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def json_response(response_code, file_name)
|
||||
content_type :json
|
||||
status response_code
|
||||
File.open(File.dirname(__FILE__) + '/fixtures/' + file_name, 'rb').read
|
||||
end
|
||||
end
|
||||
|
|
@ -24,8 +24,8 @@
|
|||
|
||||
<ol><li>Item 2a</li>
|
||||
</ol></li>
|
||||
</ol><p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--zS1u-mBk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/link" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--zS1u-mBk--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/link" alt="GitHub Logo" /></a><br />
|
||||
Format: <a href="https://res.cloudinary.com/practicaldev/image/fetch/s--EjuAfF9m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/url" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--EjuAfF9m--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/url" alt="Alt Text" /></a></p>
|
||||
</ol><p><a href="https://res.cloudinary.com/Optional/image/fetch/s---YMUCaHJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/link" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/Optional/image/fetch/s---YMUCaHJ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/link" alt="GitHub Logo" /></a><br />
|
||||
Format: <a href="https://res.cloudinary.com/Optional/image/fetch/s--6OyC0V7g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/url" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/Optional/image/fetch/s--6OyC0V7g--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/url" alt="Alt Text" /></a></p>
|
||||
|
||||
<ol><li>
|
||||
<p><strong>Line break in ul/ol spacing test</strong></p>
|
||||
|
|
|
|||
|
|
@ -1,99 +0,0 @@
|
|||
{
|
||||
"created_at": "Wed Oct 05 01:02:26 +0000 2016",
|
||||
"id": 783472379167113216,
|
||||
"id_str": "783472379167113216",
|
||||
"text": "Rules: \n- Stay on topic\n- Use hashtag #DevDiscuss\n- Be NICE/POSITIVE ❤️\n- Tweetstorms/extended rants are encouraged 😁",
|
||||
"full_text": "Rules: \n- Stay on topic\n- Use hashtag #DevDiscuss\n- Be NICE/POSITIVE ❤️\n- Tweetstorms/extended rants are encouraged 😁",
|
||||
"truncated": false,
|
||||
"entities": {
|
||||
"hashtags": [
|
||||
{
|
||||
"text": "DevDiscuss",
|
||||
"indices": [
|
||||
38,
|
||||
49
|
||||
]
|
||||
}
|
||||
],
|
||||
"symbols": [],
|
||||
"user_mentions": [],
|
||||
"urls": []
|
||||
},
|
||||
"source": "<a href=\"http://twitter.com\" rel=\"nofollow\">Twitter Web Client</a>",
|
||||
"in_reply_to_status_id": 783471771177615360,
|
||||
"in_reply_to_status_id_str": "783471771177615360",
|
||||
"in_reply_to_user_id": 2735246778,
|
||||
"in_reply_to_user_id_str": "2735246778",
|
||||
"in_reply_to_screen_name": "ThePracticalDev",
|
||||
"user": {
|
||||
"id": 2735246778,
|
||||
"id_str": "2735246778",
|
||||
"name": "The Practical Dev",
|
||||
"screen_name": "ThePracticalDev",
|
||||
"location": "",
|
||||
"description": "Coding resources, commentary and community. Helping you become a better developer maybe. Created by @bendhalpern",
|
||||
"url": "https://t.co/lhcCPP1ReQ",
|
||||
"entities": {
|
||||
"url": {
|
||||
"urls": [
|
||||
{
|
||||
"url": "https://t.co/lhcCPP1ReQ",
|
||||
"expanded_url": "http://dev.to",
|
||||
"display_url": "dev.to",
|
||||
"indices": [
|
||||
0,
|
||||
23
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": {
|
||||
"urls": []
|
||||
}
|
||||
},
|
||||
"protected": false,
|
||||
"followers_count": 114173,
|
||||
"friends_count": 2219,
|
||||
"listed_count": 2377,
|
||||
"created_at": "Fri Aug 15 19:11:17 +0000 2014",
|
||||
"favourites_count": 36181,
|
||||
"utc_offset": -25200,
|
||||
"time_zone": "Pacific Time (US & Canada)",
|
||||
"geo_enabled": false,
|
||||
"verified": false,
|
||||
"statuses_count": 7274,
|
||||
"lang": "en",
|
||||
"contributors_enabled": false,
|
||||
"is_translator": false,
|
||||
"is_translation_enabled": false,
|
||||
"profile_background_color": "131516",
|
||||
"profile_background_image_url": "http://pbs.twimg.com/profile_background_images/601385812760993793/2pxLSwZZ.jpg",
|
||||
"profile_background_image_url_https": "https://pbs.twimg.com/profile_background_images/601385812760993793/2pxLSwZZ.jpg",
|
||||
"profile_background_tile": true,
|
||||
"profile_image_url": "http://pbs.twimg.com/profile_images/839643754126417920/6trsFcTQ_normal.jpg",
|
||||
"profile_image_url_https": "https://pbs.twimg.com/profile_images/839643754126417920/6trsFcTQ_normal.jpg",
|
||||
"profile_banner_url": "https://pbs.twimg.com/profile_banners/2735246778/1458157262",
|
||||
"profile_link_color": "14BD7B",
|
||||
"profile_sidebar_border_color": "000000",
|
||||
"profile_sidebar_fill_color": "000000",
|
||||
"profile_text_color": "000000",
|
||||
"profile_use_background_image": true,
|
||||
"has_extended_profile": false,
|
||||
"default_profile": false,
|
||||
"default_profile_image": false,
|
||||
"following": true,
|
||||
"follow_request_sent": false,
|
||||
"notifications": false,
|
||||
"translator_type": "none"
|
||||
},
|
||||
"geo": null,
|
||||
"coordinates": null,
|
||||
"place": null,
|
||||
"contributors": null,
|
||||
"is_quote_status": false,
|
||||
"retweet_count": 8,
|
||||
"favorite_count": 19,
|
||||
"favorited": false,
|
||||
"retweeted": false,
|
||||
"lang": "en"
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
76
spec/support/fixtures/vcr_cassettes/twitter_gem.yml
Normal file
76
spec/support/fixtures/vcr_cassettes/twitter_gem.yml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
---
|
||||
http_interactions:
|
||||
- request:
|
||||
method: get
|
||||
uri: https://api.twitter.com/1.1/statuses/show/1018911886862057472.json?tweet_mode=extended
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: ''
|
||||
headers:
|
||||
User-Agent:
|
||||
- TwitterRubyGem/6.2.0
|
||||
Connection:
|
||||
- close
|
||||
response:
|
||||
status:
|
||||
code: 200
|
||||
message: OK
|
||||
headers:
|
||||
Cache-Control:
|
||||
- no-cache, no-store, must-revalidate, pre-check=0, post-check=0
|
||||
Connection:
|
||||
- close
|
||||
Content-Disposition:
|
||||
- attachment; filename=json.json
|
||||
Content-Length:
|
||||
- '4159'
|
||||
Content-Type:
|
||||
- application/json;charset=utf-8
|
||||
Date:
|
||||
- Tue, 17 Jul 2018 20:09:47 GMT
|
||||
Expires:
|
||||
- Tue, 31 Mar 1981 05:00:00 GMT
|
||||
Last-Modified:
|
||||
- Tue, 17 Jul 2018 20:09:47 GMT
|
||||
Pragma:
|
||||
- no-cache
|
||||
Server:
|
||||
- tsa_b
|
||||
Status:
|
||||
- 200 OK
|
||||
Strict-Transport-Security:
|
||||
- max-age=631138519
|
||||
X-Access-Level:
|
||||
- read-write
|
||||
X-Connection-Hash:
|
||||
- fc498ba11baf16a5fd6822ed64fb207d
|
||||
X-Content-Type-Options:
|
||||
- nosniff
|
||||
X-Frame-Options:
|
||||
- SAMEORIGIN
|
||||
X-Rate-Limit-Limit:
|
||||
- '900'
|
||||
X-Rate-Limit-Remaining:
|
||||
- '893'
|
||||
X-Rate-Limit-Reset:
|
||||
- '1531858493'
|
||||
X-Response-Time:
|
||||
- '66'
|
||||
X-Transaction:
|
||||
- '00208a050094194c'
|
||||
X-Twitter-Response-Tags:
|
||||
- BouncerCompliant
|
||||
X-Xss-Protection:
|
||||
- 1; mode=block; report=https://twitter.com/i/xss_report
|
||||
body:
|
||||
encoding: UTF-8
|
||||
string: '{"created_at":"Mon Jul 16 17:34:58 +0000 2018","id":1018911886862057472,"id_str":"1018911886862057472","full_text":"When
|
||||
GitHub goes down https:\/\/t.co\/5Laf1GtUSS","truncated":false,"display_text_range":[0,21],"entities":{"hashtags":[],"symbols":[],"user_mentions":[],"urls":[],"media":[{"id":1018911875919212546,"id_str":"1018911875919212546","indices":[22,45],"media_url":"http:\/\/pbs.twimg.com\/tweet_video_thumb\/DiPm8-WXcAI3_qS.jpg","media_url_https":"https:\/\/pbs.twimg.com\/tweet_video_thumb\/DiPm8-WXcAI3_qS.jpg","url":"https:\/\/t.co\/5Laf1GtUSS","display_url":"pic.twitter.com\/5Laf1GtUSS","expanded_url":"https:\/\/twitter.com\/ThePracticalDev\/status\/1018911886862057472\/photo\/1","type":"photo","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"medium":{"w":400,"h":312,"resize":"fit"},"large":{"w":400,"h":312,"resize":"fit"},"small":{"w":400,"h":312,"resize":"fit"}}}]},"extended_entities":{"media":[{"id":1018911875919212546,"id_str":"1018911875919212546","indices":[22,45],"media_url":"http:\/\/pbs.twimg.com\/tweet_video_thumb\/DiPm8-WXcAI3_qS.jpg","media_url_https":"https:\/\/pbs.twimg.com\/tweet_video_thumb\/DiPm8-WXcAI3_qS.jpg","url":"https:\/\/t.co\/5Laf1GtUSS","display_url":"pic.twitter.com\/5Laf1GtUSS","expanded_url":"https:\/\/twitter.com\/ThePracticalDev\/status\/1018911886862057472\/photo\/1","type":"animated_gif","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"medium":{"w":400,"h":312,"resize":"fit"},"large":{"w":400,"h":312,"resize":"fit"},"small":{"w":400,"h":312,"resize":"fit"}},"video_info":{"aspect_ratio":[50,39],"variants":[{"bitrate":0,"content_type":"video\/mp4","url":"https:\/\/video.twimg.com\/tweet_video\/DiPm8-WXcAI3_qS.mp4"}]}}]},"source":"\u003ca
|
||||
href=\"http:\/\/twitter.com\" rel=\"nofollow\"\u003eTwitter Web Client\u003c\/a\u003e","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"user":{"id":2735246778,"id_str":"2735246778","name":"The
|
||||
Practical Dev","screen_name":"ThePracticalDev","location":"","description":"Great
|
||||
posts from the amazing https:\/\/t.co\/xHvFQQ9jeO community, with some opinion
|
||||
and humor mixed in. Created by @bendhalpern. Join https:\/\/t.co\/xHvFQQ9jeO!","url":"https:\/\/t.co\/lhcCPP1ReQ","entities":{"url":{"urls":[{"url":"https:\/\/t.co\/lhcCPP1ReQ","expanded_url":"http:\/\/dev.to","display_url":"dev.to","indices":[0,23]}]},"description":{"urls":[{"url":"https:\/\/t.co\/xHvFQQ9jeO","expanded_url":"https:\/\/dev.to","display_url":"dev.to","indices":[29,52]},{"url":"https:\/\/t.co\/xHvFQQ9jeO","expanded_url":"https:\/\/dev.to","display_url":"dev.to","indices":[132,155]}]}},"protected":false,"followers_count":147060,"friends_count":2258,"listed_count":3145,"created_at":"Fri
|
||||
Aug 15 19:11:17 +0000 2014","favourites_count":42288,"utc_offset":null,"time_zone":null,"geo_enabled":false,"verified":false,"statuses_count":18387,"lang":"en","contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"131516","profile_background_image_url":"http:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_image_url_https":"https:\/\/abs.twimg.com\/images\/themes\/theme1\/bg.png","profile_background_tile":true,"profile_image_url":"http:\/\/pbs.twimg.com\/profile_images\/1002604104194056192\/IEoNsLNM_normal.jpg","profile_image_url_https":"https:\/\/pbs.twimg.com\/profile_images\/1002604104194056192\/IEoNsLNM_normal.jpg","profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/2735246778\/1492833420","profile_link_color":"14BD7B","profile_sidebar_border_color":"000000","profile_sidebar_fill_color":"000000","profile_text_color":"000000","profile_use_background_image":true,"has_extended_profile":false,"default_profile":false,"default_profile_image":false,"following":true,"follow_request_sent":false,"notifications":false,"translator_type":"none"},"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":91,"favorite_count":276,"favorited":true,"retweeted":false,"possibly_sensitive":false,"possibly_sensitive_appealable":false,"lang":"en"}'
|
||||
http_version:
|
||||
recorded_at: Tue, 17 Jul 2018 20:09:47 GMT
|
||||
recorded_with: VCR 4.0.0
|
||||
Loading…
Add table
Reference in a new issue