Remove docs from repo ✂✂✂ (#14579)
* Remove docs ✂✂✂✂ * Remove all references to docs and gitdocs * Update docs.forem.com to developers.forem.com * Remove .gitdocs_build/ * Update yarn.lock
|
|
@ -38,7 +38,6 @@ node_modules/
|
|||
/app/assets/javascripts/generated/*
|
||||
latest.dump
|
||||
.byebug_history
|
||||
.gitdocs_build/
|
||||
|
||||
# Ignore application configuration
|
||||
/public/packs
|
||||
|
|
|
|||
28
.gitdocs.js
|
|
@ -1,28 +0,0 @@
|
|||
/*
|
||||
* This file is created so that it's possible to
|
||||
* Run `gitdocs serve` from root
|
||||
*/
|
||||
|
||||
var config = require('./docs/.gitdocs.json');
|
||||
|
||||
module.exports = new Promise((resolve, reject) => {
|
||||
config.root = 'docs/';
|
||||
setupHost(config);
|
||||
resolve(config);
|
||||
});
|
||||
|
||||
function fetchAppDomain() {
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
const appDomainGetCmd =
|
||||
'rails runner "puts ApplicationConfig.app_domain_no_port"';
|
||||
return execSync(appDomainGetCmd).toString().replace(/\s/g, '');
|
||||
}
|
||||
|
||||
function setupHost(config) {
|
||||
const appDomain = fetchAppDomain();
|
||||
|
||||
if (config.host === '0.0.0.0' && appDomain) {
|
||||
config.host = appDomain;
|
||||
}
|
||||
}
|
||||
4
.github/PULL_REQUEST_TEMPLATE.md
vendored
|
|
@ -41,7 +41,7 @@ images for UI changes._
|
|||
|
||||
_If your PR includes UI changes, please replace this line with details on how
|
||||
accessibility is impacted and tested. For more info, check out the
|
||||
[Forem Accessibility Docs](https://docs.forem.com/frontend/accessibility)._
|
||||
[Forem Accessibility Docs](https://developers.forem.com/frontend/accessibility)._
|
||||
|
||||
## Added/updated tests?
|
||||
|
||||
|
|
@ -56,7 +56,7 @@ _Will this PR introduce a change that impacts Forem members or creators, the
|
|||
development process, or any of our internal teams? If so, please note how you
|
||||
will share this change with the people who need to know about it._
|
||||
|
||||
- [ ] I've updated the [Developer Docs](https://docs.forem.com) and/or
|
||||
- [ ] I've updated the [Developer Docs](https://developers.forem.com) and/or
|
||||
[Admin Guide](https://admin.forem.com/), or
|
||||
[Storybook](https://storybook.forem.com/) (for Crayons components)
|
||||
- [ ] I've updated the README or added inline documentation
|
||||
|
|
|
|||
6
.gitignore
vendored
|
|
@ -32,7 +32,6 @@ node_modules/
|
|||
/app/assets/javascripts/generated/*
|
||||
latest.dump
|
||||
.byebug_history
|
||||
.gitdocs_build/
|
||||
|
||||
# Ignore application configuration
|
||||
/config/application.yml
|
||||
|
|
@ -64,11 +63,6 @@ package-lock.json
|
|||
# Development Docker storage location
|
||||
_docker-storage/
|
||||
|
||||
# YARD generated doc (in the Gitdocs folder)
|
||||
docs/.static/ruby-doc/
|
||||
# ReDoc generated API doc (in the Gitdocs folder)
|
||||
docs/.static/api/
|
||||
|
||||
# AWS Cloud9 specific folder for user settings
|
||||
.c9/
|
||||
|
||||
|
|
|
|||
|
|
@ -100,7 +100,6 @@ Layout/LineLength:
|
|||
Description: 'Checks that line length does not exceed the configured limit.'
|
||||
AutoCorrect: true # this is false by default
|
||||
Exclude:
|
||||
- docs/Gemfile
|
||||
- Gemfile
|
||||
- app/lib/constants/site_config.rb
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@
|
|||
.eslintignore
|
||||
.eslintrc.js
|
||||
.gitattributes
|
||||
.gitdocs.js
|
||||
.github/
|
||||
.gitpod.dockerfile
|
||||
.gitpod.yml
|
||||
|
|
@ -40,7 +39,6 @@ cypress.dev.json
|
|||
cypress.json
|
||||
cypress/
|
||||
docker-compose.yml
|
||||
docs/
|
||||
empty-module.js
|
||||
jest.config.js
|
||||
postcss_error.log
|
||||
|
|
|
|||
|
|
@ -19,4 +19,4 @@ We are all humans trying to work together to improve the community. Always be
|
|||
kind and appreciate the need for trade-offs. ❤️
|
||||
|
||||
Before making your first pull request or issue, give our full
|
||||
[Contributor's Guide](https://docs.forem.com/contributing/forem/) a read.
|
||||
[Contributor's Guide](https://developers.forem.com/contributing/forem/) a read.
|
||||
|
|
|
|||
12
README.md
|
|
@ -94,14 +94,14 @@ proceed.
|
|||
## Getting Started
|
||||
|
||||
This section provides a high-level quick start guide. If you're looking for the
|
||||
[installation guide](https://docs.forem.com/installation/), you'll want to refer
|
||||
to our complete [Developer Documentation](https://docs.forem.com/).
|
||||
[installation guide](https://developers.forem.com/installation/), you'll want to
|
||||
refer to our complete [Developer Documentation](https://developers.forem.com/).
|
||||
|
||||
We run on a [Rails](https://rubyonrails.org/) backend, and we are currently
|
||||
transitioning to a [Preact](https://preactjs.com/)-first frontend.
|
||||
|
||||
A more complete overview of our stack is available in
|
||||
[our docs](https://docs.forem.com/technical-overview/).
|
||||
[our docs](https://developers.forem.com/technical-overview/).
|
||||
|
||||
### Prerequisites
|
||||
|
||||
|
|
@ -130,11 +130,11 @@ A more complete overview of our stack is available in
|
|||
|
||||
### Installation Documentation
|
||||
|
||||
[View Full Installation Documentation](https://docs.forem.com/installation/).
|
||||
[View Full Installation Documentation](https://developers.forem.com/installation/).
|
||||
|
||||
## Developer Documentation
|
||||
|
||||
[Check out our dedicated docs page for more technical documentation](https://docs.forem.com).
|
||||
[Check out our dedicated docs page for more technical documentation](https://developers.forem.com).
|
||||
|
||||
## Core team
|
||||
|
||||
|
|
@ -192,7 +192,7 @@ Agreement, which they generously made available to the public domain under
|
|||
Creative Commons CC0 1.0 Universal.
|
||||
|
||||
Any questions, please refer to our
|
||||
[license FAQ](https://docs.forem.com/licensing/) doc or email yo@dev.to.
|
||||
[license FAQ](https://developers.forem.com/licensing/) doc or email yo@dev.to.
|
||||
|
||||
<br>
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ module EdgeCacheSafetyCheck
|
|||
def current_user
|
||||
# In production, current_user will cause a cache leak if it's placed within an edge-cached code path.
|
||||
# More information here:
|
||||
# https://docs.forem.com/technical-overview/architecture/#we-cache-many-content-pages-on-the-edge
|
||||
# https://developers.forem.com/technical-overview/architecture/#we-cache-many-content-pages-on-the-edge
|
||||
return super unless RequestStore.store[:edge_caching_in_place]
|
||||
|
||||
return if session_current_user_id.blank?
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import { Meta } from '@storybook/addon-docs/blocks';
|
|||
|
||||
Forem is a Rails application. Most of what we build for views uses ERB templates
|
||||
(\*.html.erb files). We also build out parts of or sometimes complete views
|
||||
using [Preact](https://docs.forem.com/frontend/preact), typically for the logged
|
||||
using [Preact](https://developers.forem.com/frontend/preact), typically for the logged
|
||||
on user experience. For example, the main page feed.
|
||||
|
||||
Because of that, we components that are written in pure HTML & CSS as well as
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
<div class="crayons-card crayons-card--content-rows">
|
||||
<header>
|
||||
<h2 class="mb-2"><%= community_name %> API Keys</h2>
|
||||
<p>You can generate personal API keys to use for authentication with the <%= community_name %> API. The API is still in its beta stage. The <a href="https://docs.forem.com/api">documentation</a> contains further information.</a></p>
|
||||
<p>You can generate personal API keys to use for authentication with the <%= community_name %> API. The API is still in its beta stage. The <a href="https://developers.forem.com/api">documentation</a> contains further information.</a></p>
|
||||
</header>
|
||||
|
||||
<div>
|
||||
|
|
|
|||
|
|
@ -296,7 +296,7 @@ Rails.application.routes.draw do
|
|||
|
||||
# You can have the root of your site routed with "root
|
||||
get "/robots.:format", to: "pages#robots"
|
||||
get "/api", to: redirect("https://docs.forem.com/api")
|
||||
get "/api", to: redirect("https://developers.forem.com/api")
|
||||
get "/privacy", to: "pages#privacy"
|
||||
get "/terms", to: "pages#terms"
|
||||
get "/contact", to: "pages#contact"
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
{
|
||||
"name": "Forem Docs",
|
||||
"root": "./",
|
||||
"logo": "seedling.png",
|
||||
"host": "0.0.0.0",
|
||||
"header_links": [
|
||||
{
|
||||
"title": "Repo",
|
||||
"href": "https://github.com/forem/forem",
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer nofollow"
|
||||
},
|
||||
{
|
||||
"title": "DEV",
|
||||
"href": "https://dev.to",
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer nofollow"
|
||||
},
|
||||
{
|
||||
"title": "Ruby doc",
|
||||
"href": "https://docs.forem.com/ruby-doc",
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer nofollow"
|
||||
},
|
||||
{
|
||||
"title": "API doc",
|
||||
"href": "https://docs.forem.com/api",
|
||||
"target": "_blank",
|
||||
"rel": "noopener noreferrer nofollow"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -1 +0,0 @@
|
|||
lts/erbium
|
||||
|
|
@ -1 +0,0 @@
|
|||
2.7.2
|
||||
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 370 KiB |
|
|
@ -1 +0,0 @@
|
|||
<svg version="1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 293 180"><path d="M81 1782 c-19 -9 -44 -30 -55 -45 -21 -28 -21 -35 -21 -837 0 -802 0 -809 21 -837 11 -15 36 -36 55 -45 32 -17 118 -18 1384 -18 1266 0 1352 1 1384 18 19 9 44 30 55 45 21 28 21 35 21 837 0 802 0 809 -21 837 -11 15 -36 36 -55 45 -32 17 -118 18 -1384 18 -1266 0 -1352 -1 -1384 -18z m814 -409 c50 -26 118 -98 142 -150 16 -34 18 -71 18 -348 0 -294 -1 -312 -21 -356 -31 -66 -78 -114 -142 -146 -56 -27 -61 -28 -259 -31 l-203 -4 0 532 0 531 213 -3 c195 -3 215 -5 252 -25z m795 -83 l0 -110 -165 0 -165 0 0 -100 0 -100 100 0 100 0 0 -110 0 -110 -100 0 -100 0 0 -100 0 -100 165 0 165 0 0 -110 0 -110 -225 0 c-248 0 -260 2 -309 60 l-26 32 0 434 c0 480 -1 476 63 514 29 18 52 20 265 20 l232 0 0 -110z m381 -194 c43 -164 80 -303 81 -308 2 -5 39 129 83 297 43 169 84 309 90 311 6 3 63 3 126 2 l116 -3 -123 -462 c-67 -253 -128 -472 -134 -485 -47 -90 -145 -132 -219 -94 -35 19 -83 73 -99 112 -6 16 -64 227 -128 468 -64 242 -118 446 -121 453 -4 11 18 13 123 11 l128 -3 77 -299z" transform="matrix(.1 0 0 -.1 0 180)"/><path d="M660 870 l0 -310 45 0 c52 0 99 21 115 49 6 12 10 116 10 266 l0 247 -29 29 c-26 25 -37 29 -85 29 l-56 0 0 -310z" transform="matrix(.1 0 0 -.1 0 180)"/></svg>
|
||||
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 30 KiB |
|
Before Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 68 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
11
docs/Gemfile
|
|
@ -1,11 +0,0 @@
|
|||
ruby_version = File.read(
|
||||
File.join(File.dirname(File.dirname(__FILE__)), ".ruby-version"),
|
||||
).strip
|
||||
|
||||
source "https://rubygems.org"
|
||||
ruby ruby_version
|
||||
|
||||
gem "activerecord", "~> 5.2.3" # Databases on Rails
|
||||
gem "yard", "~> 0.9.19" # YARD is a documentation generation tool for the Ruby programming language
|
||||
gem "yard-activerecord", "~> 0.0.16" # YARD extension that handles and interprets methods used when developing applications with ActiveRecord
|
||||
gem "yard-activesupport-concern", "~> 0.0.1" # YARD extension that brings support for modules making use of ActiveSupport::Concern
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
.PHONY: build
|
||||
|
||||
build: api-doc ruby-doc gitdocs
|
||||
|
||||
api-doc:
|
||||
# docs/.static/api will let gitdocs serve the API doc as static content
|
||||
cd .. && \
|
||||
npm install -g redoc-cli@latest && \
|
||||
redoc-cli bundle docs/api_v0.yml --options.suppressWarnings --options.pathInMiddlePanel --options.jsonSampleExpandLevel=all --options.menuToggle -t docs/api_template.hbs && \
|
||||
mkdir -p docs/.static/api/ && \
|
||||
mv redoc-static.html docs/.static/api/index.html
|
||||
|
||||
ruby-doc:
|
||||
# docs/.static/ruby-doc will let gitdocs serve the Ruby doc as static content
|
||||
cd .. && yard doc -o docs/.static/ruby-doc
|
||||
|
||||
gitdocs:
|
||||
npm install -g gitdocs@latest && gitdocs build
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
---
|
||||
title: Admin Search
|
||||
---
|
||||
|
||||
# Admin Search
|
||||
|
||||
For admin views that need to take advantage of searching and filtering, we've
|
||||
chosen to use [Ransack][ransack].
|
||||
|
||||
Ransack is a Ruby gem that makes searching relatively painless. It has excellent
|
||||
documentation, but if you're looking for an example of how it's being used on
|
||||
Forem, we've implemented it to help searching and sorting user reports.
|
||||
|
||||
The view responsible for managing user reports can be found at
|
||||
`/admin/moderation/reports` and Ransack can be seen in use on the
|
||||
index action of the [`admin/feedback_messages_controller`][feedback_messages].
|
||||
|
||||
For Forem, Ransack is being used exclusively in admin, for search problems in
|
||||
other parts of the app we use [PostgreSQL Full Text Search][postgres_fts].
|
||||
|
||||
[feedback_messages]:
|
||||
https://github.com/forem/forem/blob/4e41e4a2ac893fa2a6c36990cfe475858ffb086a/app/controllers/admin/feedback_messages_controller.rb#L4
|
||||
[ransack]: https://github.com/activerecord-hackery/ransack
|
||||
[postgres_fts]: https://www.postgresql.org/docs/11/textsearch.html
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
---
|
||||
title: Admin User Interface
|
||||
---
|
||||
|
||||
# User Interface
|
||||
|
||||
Our admin dashboard is primarily ERB views that render on the server. Largely,
|
||||
we try to adhere to
|
||||
[ActionView](https://guides.rubyonrails.org/action_view_overview.html)'s
|
||||
conventions in these views.
|
||||
|
||||
For layout, basic styles, and some interactions, we use
|
||||
[Bootstrap 4](https://getbootstrap.com/). Forem isn't dedicated to using
|
||||
Bootstrap for everything, but because our design team hasn't spent much time on
|
||||
these views, we find it's easier to stick with something many developers already
|
||||
know.
|
||||
|
||||
When a view requires some custom interactivity, we've historically leaned on
|
||||
vanilla JavaScript or jQuery, but going forward we've elected to use
|
||||
[StimulusJS](https://stimulusjs.org/) for DOM manipulation and interactivity
|
||||
inside of admin.
|
||||
|
||||
# Forms
|
||||
|
||||
Inside of the admin views, we're
|
||||
[actively moving from the old ERB syntax for forms](https://m.patrikonrails.com/rails-5-1s-form-with-vs-old-form-helpers-3a5f72a8c78a).
|
||||
We tend to prefer the `form_with` helper over the previous `form_for` and
|
||||
`form_tag` helpers.
|
||||
|
||||
# StimulusJS
|
||||
|
||||
Stimulus is a modest frontend framework; its primary purpose is manipulating
|
||||
HTML. It does not provide templating features.
|
||||
|
||||
In the Forem application, [Webpacker](/frontend/webpacker/) is used to load
|
||||
Stimulus controllers via the `app/javascript/packs/admin.js`
|
||||
[pack file](https://github.com/rails/webpacker/blob/main/docs/folder-structure.md#packs-aka-webpack-entries).
|
||||
Ideally, controllers serve as an abstraction for shared functionality between
|
||||
views.
|
||||
|
||||
New controllers can be added in `/app/javascript/admin/controllers`. Unit tests
|
||||
should exist for each controller in the adjacent
|
||||
`/app/javascript/admin/__tests__/contollers` directory.
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
---
|
||||
title: Admin Guide
|
||||
items:
|
||||
- admin-search.md
|
||||
- internal-user-interface.md
|
||||
---
|
||||
|
||||
# Admin Guide
|
||||
|
||||
The Forem application contains a rudimentary administration dashboard that lives
|
||||
behind the admin route.
|
||||
|
||||
The admin dashboard is made up of a series of views that range from
|
||||
administration tools to simplified reports. These tools are used by users with
|
||||
the `admin` or `super_admin` roles to administrate the Forem application.
|
||||
|
||||
Authorization for these tools is handled by the [Rolify][rolify] gem.
|
||||
|
||||
Currently, a workaround exists to give access to certain `admin` views via
|
||||
Rolify by assigning the role `single_resource_admin` to a user.
|
||||
|
||||
`single_resource_admin` users are given access to a Ruby class. In the codebase,
|
||||
there are admin models, not backed by database tables, that exist for this
|
||||
purpose. For example, if you needed to give a user access to only
|
||||
`/admin/apps/welcome`, you'd run the following command in the Rails console:
|
||||
|
||||
```ruby
|
||||
user = User.find(some_user_id)
|
||||
user.add_role(:single_resource_admin, Welcome)
|
||||
```
|
||||
|
||||
This gives the user administration privileges on the controller associated with
|
||||
an almost empty Rails model that lives in `app/models/admin/welcome.rb`:
|
||||
|
||||
```ruby
|
||||
class Welcome < ApplicationRecord
|
||||
resourcify
|
||||
# This class exists to take advantage of Rolify for limiting authorization
|
||||
# on admin reports.
|
||||
# NOTE: It is not backed by a database table and should not be expected to
|
||||
# function like a traditional Rails model
|
||||
end
|
||||
```
|
||||
|
||||
Now that user will be able to access the view at `admin/welcome`. The same
|
||||
workaround has been implemented for most of the admin views.
|
||||
|
||||
[rolify]: https://github.com/RolifyCommunity/rolify
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
{{! This template is used by ReDoc to render the final API doc }}
|
||||
{{! It is written in HandlerbarsJS syntax }}
|
||||
{{! Please refer to https://github.com/Redocly/redoc/blob/master/cli/README.md }}
|
||||
{{! and https://github.com/Redocly/redoc/blob/master/cli/template.hbs }}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
|
||||
<head>
|
||||
<meta charset="utf8" />
|
||||
<title>DEV API (beta)</title>
|
||||
<meta name="description" content="DEV REST API documentation">
|
||||
|
||||
<meta property="og:type" content="article" />
|
||||
<meta property="og:url" content="https://docs.forem.com/api" />
|
||||
<meta property="og:title" content="DEV API (beta)" />
|
||||
|
||||
{{! needed for adaptive design }}
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<style>
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
</style>
|
||||
{{{redocHead}}}
|
||||
<link href="https://fonts.googleapis.com/css?family=Montserrat:300,400,700|Roboto:300,400,700" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
{{{redocHTML}}}
|
||||
</body>
|
||||
|
||||
</html>
|
||||
3782
docs/api_v0.yml
|
|
@ -1,65 +0,0 @@
|
|||
---
|
||||
title: Audit Log
|
||||
---
|
||||
|
||||
# Audit Log
|
||||
|
||||
To help maintain accountability for users with elevated permissions the Forem
|
||||
application has a special model that records certain actions.
|
||||
|
||||
For example, when a user with the `trusted` role creates a negative reaction on
|
||||
an article a record is created with certain information about that action.
|
||||
|
||||
That record (which we call an `AuditLog`) looks something like this:
|
||||
|
||||
```ruby
|
||||
#<AuditLog:0x00005629f019a490
|
||||
id: 1,
|
||||
category: "moderator.audit.log",
|
||||
created_at: Thu, 07 May 2020 20:25:31 UTC +00:00,
|
||||
data:
|
||||
{"action"=>"create", "category"=>"vomit", "controller"=>"reactions", "reactable_id"=>"16", "reactable_type"=>"Article"},
|
||||
roles: ["trusted"],
|
||||
slug: "create",
|
||||
updated_at: Thu, 07 May 2020 20:25:31 UTC +00:00,
|
||||
user_id: 21>
|
||||
```
|
||||
|
||||
You can see from this record that the user with an id of `21` created a vomit
|
||||
reaction on the article with an id of `16`. If that's not obvious to you from
|
||||
this object, don't worry, just take our word on it.
|
||||
|
||||
You can find an example of `Audit::Logger` in action in
|
||||
`app/controllers/admin/reactions_controller.rb`:
|
||||
|
||||
```ruby
|
||||
after_action only: [:update] do
|
||||
Audit::Logger.log(:moderator, current_user, params.dup)
|
||||
end
|
||||
```
|
||||
|
||||
This code creates a record to indicate that a someone modified a reaction from
|
||||
the admin controller.
|
||||
|
||||
It's a good idea to add a similar `after_action` to any controller action that
|
||||
might benefit from increased transparency.
|
||||
|
||||
Additionally, the `AuditLog` is used to track important actions performed on a
|
||||
user's account, e.g. adding or removing a credit card:
|
||||
|
||||
```ruby
|
||||
#<AuditLog:0x00000001193ce348> {
|
||||
category: "user.credit_card.edit",
|
||||
created_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00,
|
||||
data: {
|
||||
"action" => "create",
|
||||
"controller" => "stripe_active_cards",
|
||||
"user_action" => "add"
|
||||
},
|
||||
id: 4,
|
||||
roles: [],
|
||||
slug: "credit_card_add",
|
||||
updated_at: Tue, 21 Jul 2020 06:35:13 +03 +03:00,
|
||||
user_id: 53
|
||||
}
|
||||
```
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
---
|
||||
title: Apple Authentication
|
||||
---
|
||||
|
||||
# Sign in with Apple Authentication
|
||||
|
||||
Forem allows you to authenticate using
|
||||
[Sign in with Apple](https://developer.apple.com/sign-in-with-apple/). In order
|
||||
to use this authentication method you'll need to be enrolled to the
|
||||
[Apple Developer Program](https://developer.apple.com/programs/) in order to
|
||||
retrieve the necessary credentials and an HTTPS supported URL for the callback
|
||||
configuration (HTTP won't work). Then you'll need to provide the keys to the
|
||||
Rails application.
|
||||
|
||||
# Apple Developer Portal Configuration
|
||||
|
||||
[Register/Sign in](https://developer.apple.com/account) to your Apple Developer
|
||||
Account.
|
||||
|
||||
## Service ID Configuration
|
||||
|
||||
1. [Create a Service ID](https://developer.apple.com/account/resources/identifiers/list/serviceId)
|
||||
|
||||

|
||||
|
||||
2. Name the Service and finalize the registration
|
||||
|
||||

|
||||
|
||||
3. Configure Domains and Subdomains & the callback URL. This example uses
|
||||
[ngrok](https://ngrok.io) for HTTPS support.
|
||||
|
||||

|
||||
|
||||
## Key Configuration
|
||||
|
||||
1. [Register a new Key](https://developer.apple.com/account/resources/authkeys/add).
|
||||
Enable the "Sign in with Apple" option and configure it so it's associated
|
||||
with the corresponding App ID
|
||||
|
||||

|
||||
|
||||
2. Download the Key
|
||||
|
||||

|
||||
|
||||
# Configuring the Rails Application
|
||||
|
||||
Now with both the Service ID and Key you'll need to enable Apple Authentication
|
||||
and pass in the credentials in the admin dashboard
|
||||
`/admin/customization/config`.
|
||||
|
||||

|
||||
|
||||
Add the corresponding configuration data. Make sure the PEM key you downloaded
|
||||
has explicit linebreaks (`\n`), don't forget the one at the very end of it.
|
||||
|
||||

|
||||
|
||||
Save the changes and restart your server for these values to take effect.
|
||||
|
||||
## Email configuration
|
||||
|
||||
Apple uses what they call Private Email Relay Service to hide user's emails. For
|
||||
this to work first
|
||||
[create a new email source](https://developer.apple.com/account/resources/services/list).
|
||||
|
||||

|
||||
|
||||
Emails sent need to be authenticated and the configuration depends on the
|
||||
different providers available:
|
||||
|
||||
- [Mailchimp](https://mailchimp.com/help/set-up-custom-domain-authentication-dkim-and-spf/)
|
||||
- [SendGrid](https://sendgrid.com/docs/ui/account-and-settings/how-to-set-up-domain-authentication/)
|
||||
- [SES](https://docs.aws.amazon.com/es_es/ses/latest/DeveloperGuide/send-email-authentication-dkim.html)
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
---
|
||||
title: Facebook Authentication
|
||||
---
|
||||
|
||||
# Facebook App and Authentication
|
||||
|
||||
Forem allows you to authenticate using Facebook. In order to use this
|
||||
authentication method in local development, you will need to setup a Facebook
|
||||
App and retrieve its keys. Then you'll need to provide these keys to the Rails
|
||||
application.
|
||||
|
||||
## Sign up
|
||||
|
||||
1. [Sign in](https://facebook.com) to your Facebook account.
|
||||
|
||||
2. In order to get the API keys, you will have to
|
||||
[convert your account to a developer account](https://developers.facebook.com/).
|
||||
|
||||
## Get API keys
|
||||
|
||||
1. [Sign up](#facebook-sign-up) or [sign in](https://developers.facebook.com) to
|
||||
your Facebook developer account.
|
||||
|
||||
2. From **My Apps** dashboard, click on **Add a New App**.
|
||||
|
||||

|
||||
|
||||
3. Select **For Everything Else**
|
||||
|
||||

|
||||
|
||||
4. Fill in the app display name and contact email, then click on **Create App
|
||||
ID**
|
||||
|
||||

|
||||
|
||||
5. On the **Add a Product** screen, click **Set Up** under the **Facebook
|
||||
Login** section
|
||||
|
||||

|
||||
|
||||
6. Ignore the quickstart options, and click **Settings -> Basic** in the sidebar
|
||||
|
||||

|
||||
|
||||
7. From the basic settings screen dashboard copy the **App ID** and **App
|
||||
Secret** values to your `.env` file accordingly (name of Facebook key -> name
|
||||
of our `Settings::General` variable).
|
||||
|
||||
```text
|
||||
APP ID -> FACEBOOK_APP_ID
|
||||
API secret -> FACEBOOK_APP_SECRET
|
||||
```
|
||||
|
||||

|
||||
|
||||
## Configure the Facebook App
|
||||
|
||||
1. From the basic settings screen dashboard set your application's domain name
|
||||
in **App Domains** field, and be sure to click **Save Changes**
|
||||
|
||||

|
||||
|
||||
2. Naviate to **Facebook Login --> Settings**, and enter the following callback
|
||||
URL in the field **Valid OAuth Redirect URIs**:
|
||||
|
||||
`https://<your domain>>/users/auth/facebook/callback`
|
||||
|
||||

|
||||
|
|
@ -1,31 +0,0 @@
|
|||
---
|
||||
title: GitHub Authentication
|
||||
---
|
||||
|
||||
# GitHub App and Authentication
|
||||
|
||||
Forem allows you to authenticate using GitHub. To use this authentication method
|
||||
in local development, you will need to set up a GitHub App and retrieve its
|
||||
keys. Then you'll need to provide these keys to the Rails application.
|
||||
|
||||
1. [Click this link to create a new OAuth application in your Github account](https://github.com/settings/applications/new) -
|
||||
you will be redirected to sign in to Github account if you have not already.
|
||||
|
||||
2. Fill in the form with an application name, description, and the URL
|
||||
`http://localhost:3000/users/auth/github/callback`. Replace the port `3000` if you run Forem on another
|
||||
port.
|
||||
|
||||

|
||||
|
||||
3. You will be redirected to the app's **Developer settings**. Here you will
|
||||
find the keys. Add them to your `.env` file accordingly (name of GitHub key
|
||||
-> name of our `ENV` variable):
|
||||
|
||||
```text
|
||||
Client ID -> GITHUB_KEY
|
||||
Client Secret -> GITHUB_SECRET
|
||||
```
|
||||
|
||||

|
||||
|
||||
4. Done.
|
||||
|
|
@ -1,97 +0,0 @@
|
|||
---
|
||||
title: Twitter Authentication
|
||||
---
|
||||
|
||||
# Twitter App and Authentication
|
||||
|
||||
Forem allows you to authenticate using Twitter. In order to use this
|
||||
authentication method in local development, you will need to setup a Twitter App
|
||||
and retrieve its keys. Then you'll need to provide these keys to the Rails
|
||||
application.
|
||||
|
||||
## Sign up
|
||||
|
||||
1. [Sign in](https://developer.twitter.com/apps) to your Twitter account.
|
||||
|
||||
2. In order to get the API keys, you will have to
|
||||
[apply for a developer account](https://developer.twitter.com/en/apply-for-access).
|
||||
Click the **Apply** button.
|
||||
|
||||

|
||||
|
||||
3. Setup your Twitter account. Be sure you have your phone number and email
|
||||
address filled in.
|
||||
|
||||

|
||||
|
||||
4. Fill in your account information and give a name to your **developer
|
||||
account**.
|
||||
|
||||

|
||||
|
||||
5. Write down the reasons that you want to use Twitter API. Mention Forem's
|
||||
community and describe the issues and tests and things that you want to work
|
||||
on. Copy it, you might use it later ;)
|
||||
|
||||

|
||||
|
||||
6. Read :) and accept the Terms and Conditions.
|
||||
|
||||

|
||||
|
||||
7. Verify your email address once more, and you will be done.
|
||||
|
||||
8. You are done.
|
||||
|
||||
## Get API keys
|
||||
|
||||
1. [Sign up](#twitter-sign-up) or [sign in](https://developer.twitter.com/apps)
|
||||
to your Twitter developer account.
|
||||
|
||||
2. From **Apps** dashboard, click on **Create and app**.
|
||||
|
||||

|
||||
|
||||
3. Fill in the app name, description, and URL `https://dev.to`.
|
||||
|
||||

|
||||
|
||||
4. Check the **Enable Sign in with Twitter** option and fill in the Callback URL
|
||||
`http://localhost:3000/users/auth/twitter/callback` (or whatever port you run
|
||||
Forem on).
|
||||
|
||||

|
||||
|
||||
5. Fill in the information, **Terms of Service** `http://dev.to/terms` and
|
||||
**Privacy policy** `http://dev.to/privacy`.
|
||||
|
||||

|
||||
|
||||
6. Write down (or paste) the things that you will work on. Press **Create**.
|
||||
|
||||

|
||||
|
||||
7. Review the
|
||||
[Twitter Developer Terms](https://developer.twitter.com/en/developer-terms/agreement-and-policy.html)
|
||||
and agree to do nothing sketchy.
|
||||
|
||||

|
||||
|
||||
8. The app is all set!
|
||||
|
||||
9. One more change: From the app dashboard, go to **Permissions** and check
|
||||
**Request email addresses from users** option.
|
||||
|
||||

|
||||
|
||||
10. From the same dashboard access the **Keys and tokens** and add them to your
|
||||
`.env` file accordingly (name of Twitter key -> name of our `ENV` variable).
|
||||
Be sure to copy the _access token_ and _access token secret_ right away
|
||||
because it will be hidden from you in the future.
|
||||
|
||||
```text
|
||||
API key -> TWITTER_KEY
|
||||
API secret key -> TWITTER_SECRET
|
||||
```
|
||||
|
||||

|
||||
|
|
@ -1,20 +0,0 @@
|
|||
---
|
||||
title: Authentication
|
||||
---
|
||||
|
||||
# Authentication
|
||||
|
||||
Authentication is handled by [Devise](https://github.com/plataformatec/devise)
|
||||
and [OmniAuth](https://github.com/omniauth/omniauth).
|
||||
|
||||
On Forem you can authenticate through Facebook, GitHub, or Twitter. Please check out
|
||||
the respective guides on how to authenticate:
|
||||
|
||||
- [Apple authentication](/backend/auth-apple)
|
||||
- [Facebook authentication](/backend/auth-facebook)
|
||||
- [Github authentication](/backend/auth-github)
|
||||
- [Twitter authentication](/backend/auth-twitter)
|
||||
|
||||
We may add other authentication providers in the future. Please check back
|
||||
again, or search
|
||||
[our GitHub repository's issues.](https://github.com/forem/forem/issues)
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
---
|
||||
title: Authorization
|
||||
---
|
||||
|
||||
# Authorization
|
||||
|
||||
Authorization is handled by the third-party gem
|
||||
[Pundit](https://github.com/varvet/pundit) through the `authorize` method which
|
||||
you can find in various controllers, look for statements like:
|
||||
|
||||
```ruby
|
||||
authorize @user
|
||||
```
|
||||
|
||||
All authorization policies can be found in `/app/policies`.
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
---
|
||||
title: Configuration
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
We currently use the following gems for configuring the application:
|
||||
|
||||
- [dotenv](https://github.com/bkeepers/dotenv)
|
||||
- [rails-settings-cached](https://github.com/huacnlee/rails-settings-cached)
|
||||
- [vault](https://github.com/hashicorp/vault-ruby)
|
||||
|
||||
## dotenv
|
||||
|
||||
This gem is used for configuring environment variables for test and development
|
||||
environments. Examples:
|
||||
|
||||
- `REDIS_URL`
|
||||
- `FASTLY_API_KEY`
|
||||
- `STRIPE_SECRET_KEY`
|
||||
|
||||
Settings managed via your ENV can be found in
|
||||
[Configuring Environment Variables](/getting-started/config-env)) and viewed at
|
||||
`/admin/customization/config` (see [the Admin guide](/admin)):
|
||||
|
||||

|
||||
|
||||
## rails-settings-cached
|
||||
|
||||
We use this gem for managing settings used within the app's business logic.
|
||||
Examples:
|
||||
|
||||
- `Settings::General.main_social_image`
|
||||
- `Settings::RateLimit.follow_count_daily`
|
||||
- `Settings::Authentication.twitter_secret`
|
||||
|
||||
These settings can be accessed via the
|
||||
[`Settings::General`](https://github.com/forem/forem/blob/master/app/models/settins/general.rb)
|
||||
object and various models in the `Settings::` namespace and viewed / modified
|
||||
via `/admin/customization/config` (see [the Admin guide](/admin)).
|
||||
|
||||

|
||||
|
||||
## Vault
|
||||
|
||||
The vault Ruby gem allows us to interact with
|
||||
[Vault](https://www.vaultproject.io/docs/what-is-vault). In a nutshell, Vault is
|
||||
a tool for securely storing and accessing secrets. It is completely optional for
|
||||
running a Forem. To access it we use the wrapper `AppSecrets`.
|
||||
|
||||
```ruby
|
||||
class AppSecrets
|
||||
def self.[](key)
|
||||
result = Vault.kv(namespace).read(key)&.data&.fetch(:value) if ENV["VAULT_TOKEN"].present?
|
||||
result ||= ApplicationConfig[key]
|
||||
|
||||
result
|
||||
rescue Vault::VaultError
|
||||
ApplicationConfig[key]
|
||||
end
|
||||
|
||||
def self.[]=(key, value)
|
||||
Vault.kv(namespace).write(key, value: value)
|
||||
end
|
||||
|
||||
def self.namespace
|
||||
ENV["VAULT_SECRET_NAMESPACE"]
|
||||
end
|
||||
private_class_method :namespace
|
||||
end
|
||||
```
|
||||
|
||||
We attempt to access a secret from Vault if it is enabled, i.e. if the
|
||||
`VAULT_TOKEN` is present. If Vault is not enabled or if we cannot find the
|
||||
secret in it, then we fallback to fetching the secret from the
|
||||
`ApplicationConfig`.
|
||||
|
||||
One advantage of using Vault with Forem is that it allows you to update your
|
||||
secrets easily through the application rather than having to mess with ENV
|
||||
files. If you would like to try out Vault, follow our
|
||||
[installation guide for setting it up locally](/installation/vault).
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
---
|
||||
title: Data Update Scripts
|
||||
---
|
||||
|
||||
## What are Data Update Scripts?
|
||||
|
||||
Data Update Scripts were introduced in
|
||||
[this PR](https://github.com/forem/forem/pull/6025) and allow us to run any data
|
||||
updates we might need. For example, if we added a column to the database and
|
||||
then wanted to backfill that column with data, rather than going and manually
|
||||
doing it in a console, we would use a DataUpdateScript.
|
||||
|
||||
## How it works
|
||||
|
||||
First off, we added a
|
||||
[DataUpdateScript model](https://github.com/forem/forem/blob/master/app/models/data_update_script.rb)
|
||||
to Rails and a corresponding database table. This table is what we use to keep
|
||||
track of what scripts have been run and which ones have not/still need to be.
|
||||
|
||||
To create a script you can use our custom Rails generator:
|
||||
|
||||
```
|
||||
rails generate data_update BackfillColumnForArticles
|
||||
```
|
||||
|
||||
This will create a simple Ruby class like below and all you have to do is fill
|
||||
in the code it will run.
|
||||
|
||||
```ruby
|
||||
module DataUpdateScripts
|
||||
class BackfillColumnForArticles
|
||||
def run
|
||||
# Place your data update logic here
|
||||
# Make sure your code is idempotent and can be run safely
|
||||
# multiple times at any time
|
||||
end
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The generator will also automatically create the corresponding spec file.
|
||||
|
||||
```ruby
|
||||
require "rails_helper"
|
||||
require Rails.root.join(
|
||||
"lib/data_updates/20201103042915_backfill_column_for_articles.rb",
|
||||
)
|
||||
|
||||
describe DataUpdateScripts::BackfillColumnForArticles do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
```
|
||||
|
||||
While we encourage adding tests for data update scripts, you can skip spec
|
||||
creation by adding the `--no-spec` option to the `rails generate` command:
|
||||
|
||||
```
|
||||
rails generate data_update BackfillColumnForArticles --no-spec
|
||||
```
|
||||
|
||||
Once your script is in place then you can either run `rails data_updates:run`
|
||||
manually or you can let our setup script handle it. In our local
|
||||
[bin/setup](https://github.com/forem/forem/blob/main/bin/setup) script you will
|
||||
see we have added an additional task to update data. This kicks off the rake
|
||||
task `data_updates:run` for you.
|
||||
|
||||
The rake task itself will check the `lib/data_update_scripts` folder to see if
|
||||
there are any new scripts that need to be run. It does this by reading all of
|
||||
the files and then checking to see if they have a corresponding database entry.
|
||||
If they do not, then we create a new one and run the script. If a database entry
|
||||
already exists and it indicates the script has been run, then we skip that
|
||||
script.
|
||||
|
||||
## In production
|
||||
|
||||
DataUpdateScripts are also run automatically when a production deploy goes out.
|
||||
However, to ensure the new code they need to use has been deployed we use a
|
||||
[`DataUpdateWorker`](https://github.com/forem/forem/blob/main/app/workers/data_update_worker.rb)
|
||||
via Sidekiq and set it to run 10 minutes after the deploy script has completed.
|
||||
|
||||
## Best practices
|
||||
|
||||
### Working with large collections of rows
|
||||
|
||||
From time to time, scripts need to operate on a large amount of rows; in those
|
||||
cases we encourage:
|
||||
|
||||
- adding explicit logging to the script
|
||||
- reversing the order, to start processing the most recent records first
|
||||
|
||||
For example:
|
||||
|
||||
```ruby
|
||||
def run
|
||||
Article.find_each(order: :desc).with_index do |article, index|
|
||||
Rails.logging.info("...") if index % 1000 == 0 # this will log every 1000 articles
|
||||
|
||||
article.save
|
||||
end
|
||||
end
|
||||
```
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
title: Emails
|
||||
---
|
||||
|
||||
# Setting up email
|
||||
|
||||
If you would like to enable transactional email using services like SendGrid or
|
||||
Mailgun, you can configure it by using the following environment variables:
|
||||
|
||||
```shell
|
||||
SMTP_ADDRESS= # ie. "smtp.sendgrid.net"
|
||||
SMTP_PORT= # ie. 587
|
||||
SMTP_DOMAIN=
|
||||
SMTP_USER_NAME=
|
||||
SMTP_PASSWORD=
|
||||
SMTP_AUTHENTICATION= # defaults to :plain
|
||||
```
|
||||
|
||||
We follow the standard `ActionMailer` configuration. For more info, please check
|
||||
out Rails'
|
||||
[official documentation](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration).
|
||||
|
||||
# Previewing emails in development
|
||||
|
||||
You can view email previews at <http://localhost:3000/rails/mailers>.
|
||||
|
||||
Previews are setup in the directory `spec/mailers/previews`.
|
||||
|
||||
# Overriding mailer templates
|
||||
|
||||
To update the contents of emails that the app sends, edit the views under
|
||||
`app/views/mailers`. Note that this app uses the
|
||||
[`devise_invitable` gem](https://github.com/scambra/devise_invitable) for
|
||||
invitations. The views for this gem are stored under `app/views/devise/mailer`.
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
---
|
||||
title: Fastly
|
||||
---
|
||||
|
||||
## What is Fastly?
|
||||
|
||||
[Fastly](https://www.fastly.com/) is a third party service we use for caching on
|
||||
the edge. It allows us to scale up and serve our millions of visitors quickly
|
||||
and efficiently.
|
||||
|
||||
If you want to learn more about we use Fastly, check out this
|
||||
[talk](https://www.youtube.com/watch?v=Afy7H04X9Us) that one of our founders,
|
||||
[@benhalpern](https://dev.to/ben), gave at RailsConf 2018 talking about how we
|
||||
made our app so fast it went viral.
|
||||
|
||||
Fastly offers many different configurations (conditions,
|
||||
[snippets](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/), etc.).
|
||||
|
||||
## Snippets
|
||||
|
||||
Snippets, in general, are _"short blocks of VCL logic that can be included
|
||||
directly in your service configurations. They're ideal for adding small sections
|
||||
of code when you don't need more complex, specialized configurations that
|
||||
sometimes require custom VCL."_
|
||||
|
||||
You can learn more about VCL snippets on
|
||||
[Fastly's website](https://docs.fastly.com/vcl/vcl-snippets/about-vcl-snippets/)
|
||||
|
||||
### Adding query string parameters to a safe list
|
||||
|
||||
In the context of contributing, here's what you need to know about Fastly. In
|
||||
order for our servers to receive any sort of query string parameters in a
|
||||
request, they must first be marked as safe in Fastly. For example, if you're
|
||||
creating a new API endpoint or updating an existing one to accept new
|
||||
parameters, you'll need to update Fastly.
|
||||
|
||||
The reason we have a safe list of parameters in Fastly this way is so we don't
|
||||
have to consider junk parameters when busting the caches. Check out our
|
||||
[`EdgeCache` services](https://github.com/forem/forem/tree/main/app/services/edge_cache)
|
||||
to see examples of this.
|
||||
|
||||
Previously this was a manual process done by an internal team member. Now we do
|
||||
it programmatically using the Fastly
|
||||
[gem](https://github.com/fastly/fastly-ruby).
|
||||
|
||||
### How it works
|
||||
|
||||
We created a new file, `config/fastly/safe_params_list.vcl`, to house all of the
|
||||
safe params in Fastly.
|
||||
|
||||
If you need to update this list, simply update this file. It's as easy as that!
|
||||
This is a VCL file and you'll see a little Regex checking for a list of safe
|
||||
params.
|
||||
|
||||
_Fastly is not setup for development._
|
||||
|
||||
### In production
|
||||
|
||||
If you have Fastly configured in Production, all Fastly configs are updated
|
||||
automatically when a production deploy goes out.
|
||||
|
||||
We do this by executing `bin/rails fastly:update_configs` in our
|
||||
`release-tasks.sh` script.
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
---
|
||||
title: Internationalization (i18n)
|
||||
---
|
||||
|
||||
# Internationalization (i18n)
|
||||
|
||||
## What is internationlization (i18n)?
|
||||
|
||||
To over simplify the concept a bit, internalization (i18n for short) is the
|
||||
process of making the platform more user-friendly in various languages for
|
||||
people around the globe. This includes, but is certainly not limited to, things
|
||||
like making the site available in different languages, changing currency values
|
||||
to match your region, changing date formats, etc.
|
||||
|
||||
## What do we currently support?
|
||||
|
||||
We introduced some routing to lay the groundwork for a more comprehensive i18n
|
||||
implementation.
|
||||
|
||||
## What is the goal?
|
||||
|
||||
We want everyone to feel included, regardless of where they're located or what
|
||||
language(s) they speak. The goal is to make the platform available in various
|
||||
languages.
|
||||
|
||||
## How do you get involved?
|
||||
|
||||
The following is a high level outline of an approach to internationalization.
|
||||
This is by no means set in stone.
|
||||
|
||||
We encourage you to open a pull request (PR) to this documentation or to
|
||||
contribute to internationalization with your ideas - we're
|
||||
[open-source](https://github.com/forem/forem/pulls)!
|
||||
|
||||
## Routing
|
||||
|
||||
We have logic for routes setup. You can visit a page and add `/locale/:locale`
|
||||
to the beginning of the path. For example, if you visit the homepage, you can
|
||||
add `/locale/fr-ca` for French, Canadian where `fr` is the language code and
|
||||
`ca` is the region code.
|
||||
|
||||
Setting up languages under this "sub-folder" approach helps with Search Engine
|
||||
Optimization (SEO), routing, and more.
|
||||
|
||||
_Currently, the various language routes will not do anything - it will stil show
|
||||
the site in English (US)._
|
||||
|
||||
Once i18n is up and running, users will be able to select their preferred
|
||||
language to view the platform in. These routes will be the location of various
|
||||
languages.
|
||||
|
||||
## Translating content
|
||||
|
||||
There are many ways to translate static content on the platform. To start, we
|
||||
can explore tools like [i18n-tasks](https://glebm.github.io/i18n-tasks/) which
|
||||
also has an option to leverage Google Translate programmatically. We'll need to
|
||||
create locale files (likely `.yml`) to house the translations.
|
||||
|
||||
## Search Engine Optimization (SEO)
|
||||
|
||||
It seems search engines, especially Google, don't particularly like content on a
|
||||
page to be in multiple languages. To account for this on pages like articles, we
|
||||
can try an approach using the canonical URL for the language the article was
|
||||
written in.
|
||||
|
||||
For example, if we detect an article is written in Spanish, we can set the
|
||||
canonical URL for that article to be `/locale/es/username/article-slug`. We can
|
||||
then hide comments that are not in the same language as the article/rest of the
|
||||
page (Spanish in this example) only for the views the crawlers would see. That
|
||||
way, when the search engine crawler hits an article written in Spanish, the
|
||||
crawler will see the entire page in Spanish. We will not hide comments for the
|
||||
views that real users see.
|
||||
|
||||
## Caching
|
||||
|
||||
The platform relies on edge caching, especially with regards to articles. To
|
||||
account for this, we'll need to add logic at the edge that understands what
|
||||
languages the platform currently supports and where to look up the language
|
||||
variant in the cache.
|
||||
|
||||
If the edge doesn't pick up on a user selected preference (possibly sent as an
|
||||
additional header or cookie), the edge will look at the `Accept-Language` header
|
||||
and normalize it. The header can include more specific preferences and look
|
||||
something like: `Accept-Language: fr-ca, fr;q=0.9, en;q=0.8, de;q=0.7, *;q=0.5`.
|
||||
There are 2 things going on here. 1) A user can specify country/region variants
|
||||
for a language - `fr-ca` (French - Canada) and `fr-fr` (French - France). For
|
||||
simplicity's sake, we want to normalize that sort of preference to `fr` to
|
||||
start. 2) A user can specify priority using the q argument. We'll want to
|
||||
interpret the user's priority preferences to match their highest priority
|
||||
language with one we currently support.
|
||||
|
||||
Once the edge is aware of what language it should be looking for, it will set
|
||||
the cache key accordingly.
|
||||
|
||||
We also make use of fragment caching in several places. We need to update the
|
||||
keys for those caches to account for `locale` so we're not mistakenly serving a
|
||||
cached fragment in a different language than intended.
|
||||
|
||||
## Additional considerations
|
||||
|
||||
- _Translating URLs_. For the best SEO result, we should also translate URLs
|
||||
themselves into various languages. Something like the `/about` page could be
|
||||
translated, for example. For now, we aren't going to account for this.
|
||||
- _Translating dynamic/user generated content_. For now, we plan to _not_
|
||||
automatically translate any dynamic/user generated content (articles,
|
||||
comments, listings, etc.). In the future we could explore what that looks
|
||||
like, how a user can opt-in/out of that, etc.
|
||||
|
||||
## Styles, design, and UI
|
||||
|
||||
We'll want to expand some design aspects to support other languages that may be
|
||||
right-to-left, have different spacings, have special characters, etc.
|
||||
|
||||
## Next steps
|
||||
|
||||
A few next steps we can take on the road to internationalization.
|
||||
|
||||
- Update our logic to allow special characters/encodings in URLs. Currently, we
|
||||
generate slugs on dynamic content like articles and tags that may include
|
||||
characters that make the URL
|
||||
invalid.[Here](https://github.com/forem/forem/issues/10116) is a good example.
|
||||
We want to update this logic so these characters work in URLs as expected.
|
||||
- Allow Forem Admins to set a "default language". Currently, if a user doesn't
|
||||
select a language preference, it defaults to English ("en").
|
||||
- Clean up some code. There are some places we're hard-coding strings on the
|
||||
frontend. We'll want to explore moving that sort data to the backend to unify
|
||||
where and how we're translating.
|
||||
- Translate areas of the site into English (US) first to ensure things are still
|
||||
working. In other words, have the platform adhere to the default locale
|
||||
instead of hard-coded strings.
|
||||
- Start translating!
|
||||
|
||||
## Resources
|
||||
|
||||
- [Rails Guides - Rails Internationalization (I18n)](https://guides.rubyonrails.org/i18n.html)
|
||||
- [i18n-tasks](https://glebm.github.io/i18n-tasks/)
|
||||
- [Google: Managing multi-regional and multilingual sites](https://support.google.com/webmasters/answer/182192)
|
||||
- [forem.dev post: What internationalization features should we support?](https://forem.dev/vaidehijoshi/what-internationalization-features-should-we-support-4kl)
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
title: Metrics
|
||||
---
|
||||
|
||||
## Time series data
|
||||
|
||||
We track point-in-time data by sending data points to Datadog using
|
||||
[Statdsd](https://github.com/DataDog/dogstatsd-ruby).
|
||||
|
||||
Currently, we run a daily metric fetching job in `fetch.rake` which is called
|
||||
via the Heroku scheduler. If we wanted to do other frequencies like hourly, etc.
|
||||
we could add similar tasks.
|
||||
|
||||
The rake task calls `Metrics::RecordDailyUsageWorker.perform_async`, which
|
||||
performs algorithms to come up with data points to send, for example:
|
||||
|
||||
```ruby
|
||||
DataDogStatsClient.count("users.active_days_past_week", one_day_users, tags: { resource: "users", group: "new_users, day_count: 1 })
|
||||
```
|
||||
|
||||
If you want to create a new periodic data send, follow this pattern to do so.
|
||||
|
||||
## Vendor-Agnostic
|
||||
|
||||
While we currently are not vendor-agnostic in how we do this (Heroku/Datadog),
|
||||
it is set up in a way that could become so in the future. The main pattern is
|
||||
`Every x minutes/hours/etc. send aggregate data to warehouse where it can be examined on a timeseries basis`.
|
||||
This could, in the future, be bundled right into the platform using an open
|
||||
source timeseries database and data visualization.
|
||||
|
||||
Once in Datadog, dashboards can created using
|
||||
|
||||

|
||||
|
|
@ -1,30 +0,0 @@
|
|||
---
|
||||
title: Notifications
|
||||
---
|
||||
|
||||
# Notifications
|
||||
|
||||
Since notifications are run asynchronously, we'll want to make sure jobs are
|
||||
running: `bundle exec sidekiq`. If that's not running, you won't receive any
|
||||
notifications. You might need to create another account to pass notifications
|
||||
back and forth if you're doing this all through the UI.
|
||||
|
||||
Otherwise, you can generate notifications from the Rails console and run the
|
||||
class methods from `app/models/notification.rb`. For example:
|
||||
|
||||
```ruby
|
||||
# follow notification
|
||||
me = User.last
|
||||
follow = User.first.follow(me)
|
||||
Notification.send_new_follower_notification_without_delay(follow)
|
||||
# reaction notification
|
||||
rxn = Reaction.create(
|
||||
user_id: 1,
|
||||
category: "like",
|
||||
reactable: me.articles.last, # this assumes you have an article written
|
||||
)
|
||||
Notification.send_reaction_notification_without_delay(rxn, me)
|
||||
```
|
||||
|
||||
Notice you have to run these methods `without_delay` since this is assuming jobs
|
||||
are not running.
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
title: Push Notifications
|
||||
---
|
||||
|
||||
# Push Notifications Delivery
|
||||
|
||||
Forem instances rely on [Rpush](https://github.com/rpush/rpush) to deliver push
|
||||
notifications. This decision was heavily influenced by the desire to provide as
|
||||
much flexibility as possible to Creators. In order to do this, Forem instances
|
||||
can register and configure a `ConsumerApp`.
|
||||
|
||||
These consumer apps represent mobile applications that users use to browse and
|
||||
consume content on a Forem. Authenticated users of a specific Forem instance can
|
||||
register a `Device` associated to a `ConsumerApp`. With these pieces we are able
|
||||
to deliver push notifications to users devices.
|
||||
|
||||
The `ConsumerApp` configuration page can be found at
|
||||
`/admin/apps/consumer_apps`. The official Forem apps are supported by default
|
||||
and require their secret credential to be provided via ENV variable.
|
||||
|
||||
## Rpush Implementation
|
||||
|
||||
We use Rpush's `rpush-redis` implementation (read
|
||||
[this thread](https://github.com/forem/forem/pull/12419/files#r564660917) for
|
||||
the reasons why), hence all `Rpush` models are persisted in Redis. More details
|
||||
about how this works [here](https://github.com/rpush/rpush/wiki/Using-Redis).
|
||||
|
|
@ -1,42 +0,0 @@
|
|||
---
|
||||
title: Pusher
|
||||
---
|
||||
|
||||
# Pusher for Realtime Notifications
|
||||
|
||||
Pusher is a third party service being used to power the
|
||||
[chat system](https://dev.to/connect) and Push Notifications on
|
||||
[iOS](https://apps.apple.com/us/app/dev-community/id1439094790) &
|
||||
[Android](https://play.google.com/store/apps/details?id=to.dev.dev_android)
|
||||
native apps.
|
||||
|
||||
## Chat System
|
||||
|
||||
In order to use the chat functionality within your development environment, you
|
||||
will need to sign up for a free-tier Pusher account and retrieve its keys. Then
|
||||
you'll need to provide those keys to the Rails application.
|
||||
|
||||
1. [Sign up](https://dashboard.pusher.com/accounts/sign_up) or
|
||||
[sign in](https://dashboard.pusher.com/) to your Pusher account.
|
||||
|
||||
2. Once signed in, fill in the prompt to create a new Pusher Channels app.
|
||||
|
||||

|
||||
|
||||
3. In your new Pusher Channels app, click the "App Keys" tab.
|
||||
|
||||

|
||||
|
||||
4. Change your keys accordingly (name of Pusher key -> name of our application
|
||||
key):
|
||||
|
||||
```text
|
||||
app_id -> PUSHER_APP_ID
|
||||
key -> PUSHER_KEY
|
||||
secret -> PUSHER_SECRET
|
||||
cluster -> PUSHER_CLUSTER
|
||||
```
|
||||
|
||||

|
||||
|
||||
5. Done.
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
title: Backend Guide
|
||||
items:
|
||||
- audit-log.md
|
||||
- authentication.md
|
||||
- auth-apple.md
|
||||
- auth-facebook.md
|
||||
- auth-twitter.md
|
||||
- auth-github.md
|
||||
- authorization.md
|
||||
- configuration.md
|
||||
- data-update-scripts.md
|
||||
- emails.md
|
||||
- fastly.md
|
||||
- internationalization.md
|
||||
- roles.md
|
||||
- pusher.md
|
||||
- resource-admin.md
|
||||
- notification.md
|
||||
- scheduled-jobs.md
|
||||
- metrics.md
|
||||
- push-notifications.md
|
||||
- tracking.md
|
||||
- service-objects.md
|
||||
---
|
||||
|
||||
# Backend Guide
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
---
|
||||
title: Roles
|
||||
---
|
||||
|
||||
# Roles
|
||||
|
||||
## What is a role?
|
||||
|
||||
If authorization is about who has permission to be allowed to do what you want
|
||||
to do, then Roles are common patterns of authorization across users - reducing
|
||||
the administrative overhead.
|
||||
|
||||
## Why do I need to know about roles?
|
||||
|
||||
Some bugs can only be seen for users with specific roles. You will need to
|
||||
change the role to reproduce a problem.
|
||||
|
||||
## How do we implement roles in Forem?
|
||||
|
||||
Roles are implemented in this application using [Rolify][1]. The list of roles
|
||||
can be found in [app/models/role.rb][2] and you can search for [has_role in the
|
||||
codebase][3] to find which pages need which roles.
|
||||
|
||||
A new user starts without any roles, and there is no administrative way of
|
||||
adding roles to users yet. To assign a user a role you will have to run commands
|
||||
at the console.
|
||||
|
||||
## Example of adding permissions to a user
|
||||
|
||||
- open the Rails console
|
||||
|
||||
```shell
|
||||
rails console
|
||||
```
|
||||
|
||||
- after verifying the user `test_user_name` is missing the `trusted` role we
|
||||
proceed to add it and then verify the role has been added:
|
||||
|
||||
```ruby
|
||||
> user = User.find_by(username: "test_user_name")
|
||||
> user.has_role?(:trusted)
|
||||
=> false
|
||||
|
||||
> user.add_role(:trusted)
|
||||
=> #<Role:
|
||||
...
|
||||
name: "trusted"
|
||||
.. >
|
||||
|
||||
> user.has_role?(:trusted)
|
||||
=> true
|
||||
```
|
||||
|
||||
Another common requirement is changing to the administrative role, and an
|
||||
example of this is found [on the admin page][5].
|
||||
|
||||
## Verification
|
||||
|
||||
A more complex query to list all the users and their roles:
|
||||
|
||||
```ruby
|
||||
User.joins(:roles).order(:id).group(:id).pluck(:id, :username, Arel.sql("array_agg(roles.name)"))
|
||||
```
|
||||
|
||||
## Further Reading
|
||||
|
||||
1. [Rolify README.md][1]
|
||||
2. [What is the purpose of Rolify?][4]
|
||||
3. [Admin][5]
|
||||
|
||||
[1]: https://github.com/RolifyCommunity/rolify
|
||||
[2]: https://github.com/forem/forem/blob/main/app/models/role.rb
|
||||
[3]: https://github.com/forem/forem/search?q=has_role&unscoped_q=has_role
|
||||
[4]: https://stackoverflow.com/a/16096790/1511504
|
||||
[5]: /backend/resource-admin
|
||||
|
|
@ -1,27 +0,0 @@
|
|||
---
|
||||
title: Scheduled Jobs
|
||||
---
|
||||
|
||||
# Scheduled Jobs
|
||||
|
||||
As in the [Technical Overview](/technical-overview), We use
|
||||
[Heroku Scheduler](https://devcenter.heroku.com/articles/scheduler) for
|
||||
scheduled jobs. As the name suggests, this is for regularly recurring tasks that
|
||||
need to be run every day, week, month, year, decade, and century.
|
||||
|
||||
Tasks are implemented in `forem/lib/tasks/fetch.rake`, typically in the form of:
|
||||
|
||||
```
|
||||
task some_unique_task_name:, [optional_arg] => :environment do |optional_arg|
|
||||
... do your thing here ...
|
||||
end
|
||||
```
|
||||
|
||||
You can explore the
|
||||
[official Heroku documentation](https://devcenter.heroku.com/articles/scheduler#defining-tasks)
|
||||
for defining tasks and read through tasks we have implemented for busting cache,
|
||||
awarding badges, and more.
|
||||
|
||||
In your Pull Request, communicate with a Forem Core Team Member to discuss at
|
||||
what frequency and ensure your task is scheduled on Heroku once your code is
|
||||
reviewed, approved, and merged.
|
||||
|
|
@ -1,114 +0,0 @@
|
|||
---
|
||||
title: Service Objects
|
||||
---
|
||||
|
||||
## What are Service Objects
|
||||
|
||||
Service objects are Plain Old Ruby Objects (POROs) which encapsulate a whole
|
||||
business process/user interaction.
|
||||
|
||||
Our services are located in `app/services` with the corresponding specs in
|
||||
`spec/services`. Their main interface is a class level method named `call`, as
|
||||
in the following example:
|
||||
|
||||
```ruby
|
||||
class ImportUsers
|
||||
def self.call(arg1)
|
||||
new(arg1).call
|
||||
end
|
||||
|
||||
def initialize(arg1)
|
||||
@arg1 = arg1
|
||||
end
|
||||
|
||||
def call
|
||||
# import code goes here
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
To distinguish services from models we often give them verb names vs noun names,
|
||||
e.g. `ImportUsers` instead of `UserImporter`.
|
||||
|
||||
## Generating Service Objects
|
||||
|
||||
To make our services more consistent we use a custom Rails generator. Some usage
|
||||
examples:
|
||||
|
||||
**Generate a non-namespaced service without arguments**
|
||||
|
||||
`$ rails generate service DoTheThing`
|
||||
|
||||
```ruby
|
||||
# app/services/do_the_thing.rb
|
||||
class DoTheThing
|
||||
def self.call
|
||||
new.call
|
||||
end
|
||||
|
||||
def call
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# spec/services/do_the_thing_spec.rb
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe DoTheThing, type: :service do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
```
|
||||
|
||||
**Generate a non-namespaced service with arguments:**
|
||||
|
||||
`$ rails generate service DoTheThing arg1 arg2`
|
||||
|
||||
```ruby
|
||||
# app/services/do_the_thing.rb
|
||||
class DoTheThing
|
||||
def self.call(arg1, arg2)
|
||||
new(arg1, arg2).call
|
||||
end
|
||||
|
||||
def initialize(arg1, arg2)
|
||||
@arg1 = arg1
|
||||
@arg2 = arg2
|
||||
end
|
||||
|
||||
def call
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
The generated spec is the same as above.
|
||||
|
||||
**Generate a namespaced service with arguments**
|
||||
|
||||
`$ rails generate service things/dothem arg1 arg2`
|
||||
|
||||
```ruby
|
||||
# app/services/things/do_them.rb
|
||||
class Things::DoThem
|
||||
def self.call(arg1, arg2)
|
||||
new(arg1, arg2).call
|
||||
end
|
||||
|
||||
def initialize(arg1, arg2)
|
||||
@arg1 = arg1
|
||||
@arg2 = arg2
|
||||
end
|
||||
|
||||
def call
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
```ruby
|
||||
# spec/services/things/do_them_spec.rb
|
||||
require "rails_helper"
|
||||
|
||||
RSpec.describe Things::DoThem, type: :service do
|
||||
pending "add some examples to (or delete) #{__FILE__}"
|
||||
end
|
||||
```
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
---
|
||||
title: Tracking
|
||||
---
|
||||
|
||||
## Visits & Events
|
||||
|
||||
For first-party analytics, we use the
|
||||
[`ahoy_matey` gem](https://github.com/ankane/ahoy), which tracks visits and
|
||||
events.
|
||||
|
||||
We intentionally choose to limit what user data we track and persist, and have
|
||||
opted to follow the GDPR compliance standards
|
||||
[set by Ahoy](https://github.com/ankane/ahoy#gdpr-compliance-1). By default, we
|
||||
have configured the Ahoy library to mask IP addresses, disable geocode tracking,
|
||||
and not track user cookies.
|
||||
|
||||
### Visits
|
||||
|
||||
Ahoy creates an `Ahoy::Visit` record for each visit that it tracks.
|
||||
|
||||
By default, we have turned off visit tracking in the `ApplicationController`:
|
||||
|
||||
```ruby
|
||||
skip_before_action :track_ahoy_visit
|
||||
```
|
||||
|
||||
We currently only create visits on the server-side when they are required to be
|
||||
created by events. Visits can be re-enabled for specific controller actions if
|
||||
necessary, but this should be done so _with explict care_.
|
||||
|
||||
We do not collect any personal user data when tracking visits. Our collected
|
||||
data is limited to the user's `id`. Each user has a unique `visitor_token`,
|
||||
while each visit to the site is marked with a unique `visit_token`.
|
||||
|
||||
### Events
|
||||
|
||||
Ahoy creates an `Ahoy::Event` record for each event that it tracks. If no visit
|
||||
is recorded for a user when an event is tracked, Ahoy will simultaneously create
|
||||
an `Ahoy::Visit` for the event being tracked.
|
||||
|
||||
Events can be tracked in a controller action on the backend, or with JavaScript
|
||||
on the frontend. Learn more about tracking events with JavaScript in our
|
||||
[frontend tracking guide](/frontend/tracking).
|
||||
|
||||
When an event is tracked, it should include a `name` and a `properties` hash.
|
||||
When adding new events, be sure that the name is unique per-event. The
|
||||
properties will help you differentiate between events.
|
||||
|
||||
In order to track a specific event in a controller, use the `ahoy.track` call:
|
||||
|
||||
```ruby
|
||||
class YourController < ApplicationController
|
||||
after_action :track_my_action
|
||||
|
||||
protected
|
||||
|
||||
def track_my_action
|
||||
ahoy.track "A specific description of your event", request.path_parameters
|
||||
end
|
||||
end
|
||||
```
|
||||
|
||||
Event tracking can be enabled for specific controller actions, but should be
|
||||
done so _with explict care_.
|
||||
|
||||
## Messages
|
||||
|
||||
For email analytics, we use the
|
||||
[`ahoy_messages` gem](https://github.com/ankane/ahoy_email), which tracks a
|
||||
history of email messages sent to users.
|
||||
|
||||
Ahoy creates an `Ahoy::Message` record for each email sent by default, but can
|
||||
be disabled on a per-mailer basis.
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
---
|
||||
title: Contributing to the API Specification Docs
|
||||
---
|
||||
|
||||
# Contributing to API specification docs
|
||||
|
||||
The API v0 is described with the
|
||||
[OpenAPI 3 specification](https://spec.openapis.org/oas/v3.0.3).
|
||||
|
||||
Swagger.io has
|
||||
[great docs](https://swagger.io/docs/specification/basic-structure/) that are
|
||||
helpful to understand the specification better.
|
||||
|
||||
## Where you can find the spec file
|
||||
|
||||
We auto-generate the documentation from `api_v0.yml` within the `/docs`
|
||||
directory. We use [ReDoc](https://github.com/Redocly/redoc) to turn the OpenAPI
|
||||
3 format into a readable and searchable HTML documentation.
|
||||
|
||||
## Updating API docs
|
||||
|
||||
Whenever you make changes to the API docs, make sure to bump the version at the
|
||||
top of `api_v0.yml`, in `info.version`.
|
||||
|
||||
## Running and editing the docs locally
|
||||
|
||||
If you want to browse the documentation locally you can use:
|
||||
|
||||
```shell
|
||||
yarn api-docs:serve
|
||||
```
|
||||
|
||||
This will let you browse the auto-generated version of the doc locally, and it
|
||||
will reload the documentation after every change of the specification file.
|
||||
|
||||
## Linting and validation
|
||||
|
||||
We use [spectral](https://github.com/stoplightio/spectral) and
|
||||
[ibm-openapi-validator](https://github.com/IBM/openapi-validator) to validate
|
||||
the spec file. The validation is performed as a `pre-commit` hook.
|
||||
|
||||
You can also manually validate the document, running:
|
||||
|
||||
```shell
|
||||
yarn api-docs:lint
|
||||
```
|
||||
|
||||
If you have Visual Studio Code, we suggest you install the following extensions
|
||||
that enable validation and navigation within the spec file:
|
||||
|
||||
- [OpenAPI (Swagger) editor](https://marketplace.visualstudio.com/items?itemName=42Crunch.vscode-openapi)
|
||||
- [openapi-designer live preview](https://marketplace.visualstudio.com/items?itemName=philosowaffle.openapi-designer)
|
||||
|
|
@ -1,77 +0,0 @@
|
|||
---
|
||||
title: Contributing to the Docs
|
||||
---
|
||||
|
||||
# Contributing to Forem's developer documentation
|
||||
|
||||
Contributions to the documentation are always appreciated! Thank you for making
|
||||
an effort to improve the developer experience of contributing to the DEV
|
||||
project.
|
||||
|
||||
# Running the documentation locally
|
||||
|
||||
Like Forem, this site is open source and the code is [hosted on GitHub][docs].
|
||||
If you find any incorrect information, or a even a typo, we'd love to see a pull
|
||||
request. Follow these steps to get the documentation site running locally.
|
||||
|
||||
Forem's documentation is built with [GitDocs NodeJS library][gitdocs].
|
||||
|
||||
Once installed, you should run `yarn gitdocs serve` from the root of the `forem`
|
||||
project or from the `/docs` directory.
|
||||
|
||||
```shell
|
||||
yarn gitdocs serve
|
||||
```
|
||||
|
||||
This will start a server where you can browse the documentation:
|
||||
<http://localhost:8000/>
|
||||
|
||||
If you add new pages or rename existing pages, you'll need to restart the server
|
||||
for those changes to take effect.
|
||||
|
||||
Since our documentation is built on GitDocs, which is built on Netlify, you can
|
||||
use the generated deploy preview link to check out your documentation changes.
|
||||
Once you make a PR, click on "Show all checks" and find the "deploy/netlify"
|
||||
row. If your deploy preview is ready, you can click on "Details" to see the
|
||||
preview. Please note that the deploy preview only reflects any documentation
|
||||
changes you make (and not any changes elsewhere in the app).
|
||||
|
||||
# Useful links
|
||||
|
||||
The docs are a collection of [Markdown files][markdown] that also utilize
|
||||
[FrontMatter][frontmatter].
|
||||
|
||||
For more information on how to use GitDocs read the [GitDocs
|
||||
guide][gitdocs_guide].
|
||||
|
||||
# Regarding language & style
|
||||
|
||||
We ask that you avoid trivializing terms when contributing to documentation.
|
||||
This includes words like "just", "simply", "easy", "obvious", and
|
||||
"straightforward". You can learn more about why we want to avoid this kind of
|
||||
language in [this blog post](https://jessitron.com/2020/06/26/just-dont).
|
||||
|
||||
Generally speaking, the documentation hosted on this site is informal. There is
|
||||
no need to make things more complicated by writing these articles like a
|
||||
textbooks.
|
||||
|
||||
However, it's expected that contributions to these documents are reasonably
|
||||
structured and mostly free of spelling and grammar errors. For this reason, if
|
||||
you submit a PR you might be asked to make changes before your PR is merged.
|
||||
|
||||
Prettier is used to autowrap lines in these files to 80 characters. Using 80
|
||||
characters per line allows us to retain a more specific git history over time.
|
||||
If lines are not wrapped, changing a comma in a paragraph would attribute the
|
||||
entire paragraph to one commit. By line wrapping we are helping git to correctly
|
||||
attribute smaller changes to their commits. This keeps information from getting
|
||||
lost over time.
|
||||
|
||||
For more information on effective technical writing, check out
|
||||
[writethedocs.org][writethedocs].
|
||||
|
||||
[docs]: https://github.com/forem/forem/tree/main/docs/
|
||||
[gitdocs]: https://www.npmjs.com/package/gitdocs/
|
||||
[markdown]: https://en.wikipedia.org/wiki/Markdown
|
||||
[frontmatter]: https://jekyllrb.com/docs/front-matter/
|
||||
[gitdocs_guide]: https://gitdocs.netlify.com/
|
||||
[writethedocs]: https://www.writethedocs.org/guide/
|
||||
|
|
@ -1,202 +0,0 @@
|
|||
---
|
||||
title: Contributing to Forem
|
||||
---
|
||||
|
||||
# Contributing to Forem
|
||||
|
||||
We expect contributors to abide by our underlying
|
||||
[Code of Conduct](https://dev.to/code-of-conduct). All discussions about this
|
||||
project must be respectful and harassment-free.
|
||||
|
||||
Remember that communication is the lifeblood of any Open Source project. We are
|
||||
all working on this together, and we are all benefiting from this software.
|
||||
|
||||
It's very easy to misunderstand one another in asynchronous, text-based
|
||||
conversations. When in doubt, assume everyone has the best intentions.
|
||||
|
||||
If you feel anyone has violated our Code of Conduct, you should anonymously
|
||||
contact the team with our [abuse report form](https://dev.to/report-abuse).
|
||||
|
||||
### Where to contribute
|
||||
|
||||
All [issues](https://github.com/forem/forem/issues) labeled
|
||||
[ready for dev](https://github.com/forem/forem/issues?q=is%3Aissue+is%3Aopen+label%3A%22ready+for+dev%22)
|
||||
and
|
||||
[bug](https://github.com/forem/forem/issues?q=is%3Aissue+is%3Aopen+label%3A%22type%3A+bug%22+label%3Abug)
|
||||
are up for grabs. Please note that issues with the
|
||||
[Forem team](https://github.com/forem/forem/labels/Forem%20team) label are
|
||||
internal tasks that will be completed by a Forem
|
||||
[core team member](https://github.com/forem/forem/#core-team).
|
||||
|
||||
- [good first issue](https://github.com/forem/forem/issues?utf8=%E2%9C%93&q=is%3Aissue+is%3Aopen+label%3A%22good+first+issue%22+)
|
||||
issues are meant for newer developers.
|
||||
- [difficulty: easy](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+easy%22)
|
||||
issues are usually confined to isolated areas of existing code.
|
||||
- [difficulty: medium](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+medium%22)
|
||||
issues sometimes entail new features and might affect a significant area of
|
||||
the codebase, but aren't overly complex.
|
||||
- [difficulty: hard](https://github.com/forem/forem/issues?q=is%3Aopen+is%3Aissue+label%3A%22difficulty%3A+hard%22)
|
||||
issues are typically far-reaching, and might need architecture decisions
|
||||
during implementation. This label might also denote highly complex issues.
|
||||
|
||||
PRs without an associated issue may still be merged, but the core team will
|
||||
focus on changes that solve existing issues. We strongly encourage creating an
|
||||
issue before working on a PR!
|
||||
|
||||
When in doubt, ask a
|
||||
[core team member](https://github.com/forem/forem/#core-team) by mentioning us
|
||||
on the issue.
|
||||
|
||||
**Documentation** is almost always a great place to start contributing to a new
|
||||
project. Forem is an Open Source, community-driven project. Therefore, providing
|
||||
and maintaining quality documentation is one of our most important jobs. You can
|
||||
find more information in our
|
||||
[docs guide](https://docs.forem.com/contributing/docs)!
|
||||
|
||||
**Refactoring**, which involves improving the code without modifying behavior,
|
||||
is a great place to help out! Generally speaking, you can rely on existing tests
|
||||
to ensure that your refactor doesn't introduce any unexpected behavior. If an
|
||||
area isn't well tested, you might be asked to include a regression test with
|
||||
your refactoring PR. Refactors can touch many files, so we encourage breaking
|
||||
big changes into small PRs.
|
||||
|
||||
**Fixing bugs** is a super fast way to improve the experience for our users!
|
||||
When you're fixing bugs, we appreciate communication in a GitHub issue. If an
|
||||
issue exists, please claim that issue and link it in your PR, otherwise creating
|
||||
an issue is the best first step! Be sure to surround bug fixes with ample tests;
|
||||
bugs are magnets for other bugs. Write tests around bugs!
|
||||
|
||||
**Features** tend to be subjective and might spur some debate. The Forem core
|
||||
team uses an internal RFC ("request for comments") process to assess and
|
||||
prioritize new features. This process is intended to provide a consistent and
|
||||
standardized path for new changes to enter the Forem ecosystem. If you'd like to
|
||||
propose a new feature, please visit [forem.dev](https://forem.dev) to start a
|
||||
discussion around a new feature (or chime in on a pre-existing discussion!).
|
||||
|
||||
There may be some open issues in our repository that we think evolve into
|
||||
impactful features. For such issues, we use the
|
||||
[potential RFC](https://github.com/forem/forem/labels/potential%20RFC) label in
|
||||
order to highlight the potential feature to the Forem core team members so that
|
||||
someone from the team can champion that feature.
|
||||
|
||||
You can learn more about our internal RFC process and how we use forem.dev
|
||||
[here](https://forem.dev/foremteam/internal-rfc-process-and-forem-dev-discussions-3gl4)
|
||||
|
||||
### How to contribute
|
||||
|
||||
1. [Fork the project](https://docs.forem.com/getting-started/forking/) and clone
|
||||
it to your local machine. Follow the
|
||||
[installation guide](https://docs.forem.com/installation/)!
|
||||
2. Create a branch with your GitHub username and the ID of the
|
||||
[issue](https://github.com/forem/forem/issues), for example:
|
||||
`git checkout -b USERNAME/some-new-feature-1234`
|
||||
3. Code and commit your changes. Bonus points if you write a
|
||||
[good commit message](https://chris.beams.io/posts/git-commit/):
|
||||
`git commit -m 'Add some feature'`
|
||||
4. Push to the branch: `git push -u origin USERNAME/some-new-feature-1234`
|
||||
5. [Create a pull request](https://docs.forem.com/getting-started/pull-request/)
|
||||
for your branch. 🎉
|
||||
|
||||
## Contribution guidelines
|
||||
|
||||
### Create an issue
|
||||
|
||||
Nobody's perfect. Something doesn't work? Something could be better? Check to
|
||||
see if the issue already exists, and if it does, leave a comment to get our
|
||||
attention! If the issue doesn't already exist, feel free to create a new one. A
|
||||
core team member will triage incoming issues.
|
||||
|
||||
_Please note: core team members may update the title of an issue to reflect the
|
||||
discussion._
|
||||
|
||||
### Please include tests
|
||||
|
||||
Some areas of the project could use updated tests, and new features should
|
||||
always include test coverage. Please give our
|
||||
[testing guide](https://docs.forem.com/tests/) a read!
|
||||
|
||||
### Code quality
|
||||
|
||||
We use [Code Climate](https://codeclimate.com/) to find code smells. If a pull
|
||||
request contains code smells, we might recommend a refactor before merging. We
|
||||
like readable code, and encourage DRY when it's reasonable!
|
||||
|
||||
More importantly, we avoid
|
||||
[wrong abstractions](https://www.sandimetz.com/blog/2016/1/20/the-wrong-abstraction).
|
||||
Code quality tools are not perfect, so don't obsess over your Code Climate
|
||||
score.
|
||||
|
||||
### Consider accessibility in UI changes
|
||||
|
||||
If the change you're proposing touches a user interface, include accessibility
|
||||
in your approach. This includes things like color contrast, keyboard
|
||||
accessibility, screen reader labels, and other common requirements. For more
|
||||
information, check out the
|
||||
[Forem Accessibility docs page](https://docs.forem.com/frontend/accessibility).
|
||||
|
||||
### Please use inclusive language
|
||||
|
||||
Inclusion and respect are core tenets of our
|
||||
[Code of Conduct](https://dev.to/code-of-conduct). We expect thoughtful language
|
||||
all the way down to the code. Some technical metaphors are alienating or
|
||||
triggering. We ask that contributors go the extra mile to submit code which is
|
||||
inclusive in nature.
|
||||
|
||||
If you unintentionally use language deemed harmful, there is no shame. We will
|
||||
work together to find a better alternative. Being thoughtful about language also
|
||||
encourages more thoughtful code!
|
||||
|
||||
### Create a pull request
|
||||
|
||||
- Try to keep the pull requests small. A pull request should try its very best
|
||||
to address only a single concern.
|
||||
- For work in progress pull requests, please use the
|
||||
[Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
|
||||
feature.
|
||||
- Make sure all tests pass and add additional tests for the code you submit.
|
||||
[More info here](https://docs.forem.com/tests/).
|
||||
- Document your reasoning behind the changes. Explain why you wrote the code in
|
||||
the way you did. The code should explain what it does.
|
||||
- If there's an existing issue, reference to it by adding something like
|
||||
`References/Closes/Fixes/Resolves #123`, where 123 is the issue number.
|
||||
[More info here](https://github.com/blog/1506-closing-issues-via-pull-requests).
|
||||
- Please fill out the PR Template when making a PR.
|
||||
- All commits in a pull request will be squashed when merged.
|
||||
|
||||
_Please note: a core team member may close your PR if it has gone stale or if we
|
||||
don't plan to merge the code._
|
||||
|
||||
### Pull request reviews
|
||||
|
||||
All community pull requests are reviewed by our core team.
|
||||
|
||||
- All contributors must sign the CLA.
|
||||
- All required checks are expected to pass on each PR.
|
||||
- In the case of flaky or unrelated test failures, a core team member will
|
||||
restart CI.
|
||||
- We require 2 approvals from core team members for each PR.
|
||||
- Requested Changes must be resolved (with code or discussion) before merging.
|
||||
- If you make changes to a PR, be sure to re-request a review.
|
||||
- Style discussions are generally discouraged in PR reviews; make a PR to the
|
||||
linter configurations instead.
|
||||
- Your code will be deployed shortly after it is merged.
|
||||
|
||||
### A note on "force pushing"
|
||||
|
||||
After you submit your pull request, one of the members of the core team will
|
||||
review your code.
|
||||
|
||||
Please avoid force pushing unless you need to rebase with the main branch.
|
||||
|
||||
If feedback is provided, any changes should be contained in new commits. Please
|
||||
don't force push or worry about squashing your commits.
|
||||
|
||||
Force pushing (despite being useful) has some drawbacks. GitHub doesn't always
|
||||
keep the review history, which results in lost context for the reviewers.
|
||||
|
||||
We squash every PR before merging, so there is no need to force push!
|
||||
|
||||
## The bottom line
|
||||
|
||||
We are all humans trying to work together to improve the community. Always be
|
||||
kind and appreciate the need for tradeoffs. ❤️
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
title: Contributing Guide
|
||||
items:
|
||||
- docs.md
|
||||
- forem.md
|
||||
- api.md
|
||||
---
|
||||
|
||||
# Contributing Guide
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
---
|
||||
title: Configuring Forem
|
||||
---
|
||||
|
||||
# Basic Configuration Guide for Forem
|
||||
|
||||
## Overview
|
||||
|
||||
As a Forem admin, one of the first steps of managing your site will be to tailor
|
||||
your content, branding and other important details based on your community
|
||||
goals. This is made simple by the configuration page which can be found at
|
||||
https://<span></span>your.forem.url/admin/customization/config.
|
||||
|
||||
_We advise that you first complete the minimum set up before sending out your
|
||||
Forem link to your community._
|
||||
|
||||
## Complete your Set Up, Configure your Forem
|
||||
|
||||
Once your Forem instance is set up for the first time, you will most likely see
|
||||
the following banner:
|
||||
|
||||

|
||||
|
||||
This banner indicates that the Forem configuration process hasn't been completed
|
||||
yet.
|
||||
|
||||
When you click on the link on banner, it will take you to the configuration page
|
||||
(i.e. https://<span></span>your.forem.url/admin/customization/config).
|
||||
|
||||
On this page you will see that the Get Started section is expanded. It contains
|
||||
all the mandatory fields that need to be filled out in order for the site to be
|
||||
in a usable state. Once it is filled out and submitted, the banner will then
|
||||
disappear.
|
||||
|
||||
## Access and Permissions
|
||||
|
||||
The following permissions are required to be able to view and/or edit the config
|
||||
page:
|
||||
|
||||
#### `Role: super_admin`
|
||||
|
||||
When providing this role to a user they will be able to access the config page.
|
||||
However, this page will be a read only view for them. They will see the
|
||||
following:
|
||||
|
||||

|
||||
|
||||
#### `Role: single_resource_admin`
|
||||
|
||||
When providing this role to a user they will be able to access the config page,
|
||||
and they will be able to edit the config variables.
|
||||
|
||||

|
||||
|
||||
The first admin of your Forem will be given the highest permissions. Thereafter,
|
||||
they can provide the necessary permissions to subsequent users via the
|
||||
https://<span></span>your.forem.url/admin/permissions.
|
||||
|
||||
## The Configuration Sections
|
||||
|
||||
Currently, the configuration page is split into 3 sections. They are as follows:
|
||||
|
||||
- A **Get Started section** that contains all required fields. These are the
|
||||
fields that are required to be filled out, in order to get your Forem in a
|
||||
usable state.
|
||||
- An **All Settings section** that contains all the possible variables that you
|
||||
can configure on the site. This section is broken down into sub sections,
|
||||
whereby each subsection will contain a description of what it does, and then
|
||||
list the fields that are available for configuration. Each field will also
|
||||
contain a concise description of what it is used for.
|
||||
- An **Environment Variables section** that provides a read-only view of the
|
||||
environment variables that are available to be set on the server. If your
|
||||
instance is hosted by Forem, please get in touch with customer support to
|
||||
change any of these variables.
|
||||
|
||||

|
||||
|
||||
All required fields are marked as such. In addition, you will notice that we
|
||||
have set some defaults for certain fields, you may amend them as you see
|
||||
relevant for your Forem.
|
||||
|
||||
## Updating your configurations
|
||||
|
||||
In order to update any of the variables within the Get Started and All Settings
|
||||
sections, you will set the new value and then navigate to the end of the section
|
||||
where you will verify that you would like to make the change by typing the
|
||||
following sentence:
|
||||
|
||||
```
|
||||
My username is <specify your username here> and this action is 100% safe and appropriate.
|
||||
```
|
||||
|
||||
You will now see your updated values and the changes on your site will be in
|
||||
effect.
|
||||
|
||||

|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
title: Forem Creators Guide
|
||||
items:
|
||||
- configure-forem.md
|
||||
---
|
||||
|
||||
# Forem Creators Guide
|
||||
|
||||
This document contains basic instructions for new Forem creators. It's a work in-progress, but will provide Forem Creators with a guide on how to best make use of the tools and features available to them.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
title: Branding Guidelines
|
||||
---
|
||||
|
||||
# Branding Guidelines
|
||||
|
||||
## Forem Theme
|
||||
|
||||
Forem's design inspiration is meant to be a throwback. In personality and
|
||||
design, it is ATARI inspired. Magazines like PAPER are also an inspiration in
|
||||
terms of being a
|
||||
[bold contrast with some more indy](https://www.google.com/search?biw=1440&bih=780&tbm=isch&sa=1&ei=KSN8W5WVLoy55gLI77TgBA&q=paper+magazine+cover&oq=paper+magazine+cover).
|
||||
|
||||
We also think in terms of how people use the Forem brand for their own purposes.
|
||||
Profiles are very much public-facing records of what people have put out into
|
||||
the world. `dev.to/{username}` is your developer identity.
|
||||
|
||||
Imagine a developer's face with the DEV heading above them. If we existed in the
|
||||
80's, we would have been "DEV MAGAZINE," with the exact same branding.
|
||||
|
||||
## Branding Rules
|
||||
|
||||
- [Joystick](http://www.pixelsagas.com/?download=joystick) should be used for
|
||||
logos, not for headlines/phrases.
|
||||
- Black and white is our default logo, but the logo can be drawn in any color.
|
||||
- Forem/DEV is "old school cool."
|
||||
- We use bold shadows (no gradient) for boxes.
|
||||
- We have a minimal approach to importing dependencies, so few if any custom
|
||||
fonts on-site for that reason.
|
||||
- Refer to the `variables.scss`
|
||||
[file in our main repo](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss)
|
||||
to reference commonly-used colors and fonts.
|
||||
|
||||
## Design License Info
|
||||
|
||||
- We use the [Joystick](http://www.pixelsagas.com/?download=joystick) font by
|
||||
Neale Davidson and [Pixel Sagas](http://www.pixelsagas.com/) for the DEV Logo.
|
||||
We have a commercial license.
|
||||
- We use [EmojiOne](https://www.emojione.com/) icons from JoyPixels. We have a
|
||||
commercial license.
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
# Human Language
|
||||
|
||||
Forem is built on concepts, and those concepts have names. Here are some language concepts to be aware of and some known "gotchas" where there may be multiple terms currently being used.
|
||||
|
||||
This is an incomplete first take. It is also written down _so that it can be changed when needed._ No language concept is set in stone. And no concept is exclusively _english_. As we internationalize we need to evolve our notion of this, and it will remain important that we settle on unified terms so that we limit conceptual sprawl.
|
||||
|
||||
# Post
|
||||
|
||||
A main published unit on sites are officially called posts. The gotcha is that they are currently called `Article` in the codebase. This is because the original version of the site had much more the concept of the pure "article" but as the site has evolved and the purposes of Forem have evolved, we have diversified the use of a post idea. Just as a "tweet" could be used for different things: An announcement, a question, the start of a thread, etc. we also have a broad idea of what a post is "for", and all concepts are built off of this.
|
||||
|
||||
# Comment
|
||||
|
||||
A single comment is the unit of follow up dialog that lives underneath a post. Comments have some associated terminology.
|
||||
- A Discussion is the collection of all comments under a post. Discussion is a word that implies a certain civility.
|
||||
- A thread is a single line of comments and their reply comments within a full discussion.
|
||||
- A comment within a thread may be thought of as a "reply" contextually, but it is still a comment.
|
||||
|
||||
# Reaction
|
||||
|
||||
The remotional responses to an article or comment, a click of a button to let the author know how one feels. The phrase "reaction" is used in the app, but should mostly be _implied_. Reactions are purposefully lightweight in nature.
|
||||
|
||||
# Tag
|
||||
|
||||
A tag is the main unit of organized content on a Forem.
|
||||
|
||||
# User
|
||||
|
||||
A signed up account is generically referred to as a "user", but contextually we've called them "members" or "people", there may be some need for unification here.
|
||||
|
||||
# Listing
|
||||
|
||||
The marketplace/classified-listing concept within a forem is called `/listings` and a single unit is a listing. Listings have categories for segmentation as well as tags, like posts do.
|
||||
|
||||
# Connect
|
||||
|
||||
Connect is the area of the site for synchronous conversation, closed group chat and/or video or audio conversation. It's generally not as site-wide or public, though they _can_ be public in some contexts.
|
||||
|
||||
# Connect Channel
|
||||
|
||||
A single unit of chat is called a "channel".
|
||||
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
---
|
||||
title: Design Guide
|
||||
items:
|
||||
- branding.md
|
||||
- language.md
|
||||
- theming.md
|
||||
---
|
||||
|
||||
# Design Guide
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
---
|
||||
title: Theming Guidelines
|
||||
---
|
||||
|
||||
# Theming Guidelines
|
||||
|
||||
Forem supports different themes, such as Default, Night, Pink.
|
||||
|
||||
You can switch the theme at <http://localhost:3000/settings/ux> in the "Style
|
||||
Customization" section.
|
||||
|
||||
These themes are powered by
|
||||
[CSS custom properties](https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties),
|
||||
loaded at runtime by JavaScript via `layouts/_user_config.html.erb`.
|
||||
|
||||
An example of how it works:
|
||||
|
||||
```javascript
|
||||
try {
|
||||
const bodyClass = localStorage.getItem('config_body_class');
|
||||
|
||||
if (bodyClass) {
|
||||
document.body.className = bodyClass;
|
||||
|
||||
if (bodyClass.includes('night-theme')) {
|
||||
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/night.css"].to_s.squish.html_safe %></style>';
|
||||
} else if (bodyClass.includes('ten-x-hacker-theme')) {
|
||||
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/hacker.css"].to_s.squish.html_safe %></style>'
|
||||
} else if (bodyClass.includes('pink-theme')) {
|
||||
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/pink.css"].to_s.squish.html_safe %></style>'
|
||||
} else if(bodyClass.includes('minimal-light-theme')) {
|
||||
document.getElementById('body-styles').innerHTML = '<style><%= Rails.application.assets["themes/minimal.css"].to_s.squish.html_safe %></style>'
|
||||
}
|
||||
...
|
||||
```
|
||||
|
||||
Within SCSS files located at `app/assets/stylesheets` you can use `var()`
|
||||
statements, which will call these CSS custom properties. If a CSS custom
|
||||
property is not defined `var()` will fall back on the second parameter provided,
|
||||
which can be a SCSS variable defined in `app/assets/stylesheets/variables.scss`.
|
||||
|
||||
An example of how to use `var()`:
|
||||
|
||||
```scss
|
||||
div {
|
||||
color: var(--theme-color, $black);
|
||||
}
|
||||
```
|
||||
|
||||
Note that fallback values aren't used to fix the browser compatibility. If the
|
||||
browser doesn't support CSS custom Properties, the fallback value won't help. To
|
||||
prevent this issue on older browsers, we need to write our styles like this:
|
||||
|
||||
```scss
|
||||
div {
|
||||
color: $black;
|
||||
color: var(--theme-color, $black);
|
||||
}
|
||||
```
|
||||
|
||||
This can be too much work and browser fallback can be forgotten. For a better
|
||||
developer experience, you should use the two mixins defined in
|
||||
`app/assets/stylesheets/_mixins.scss` called `themeable` and
|
||||
`themeable-important`. They take three arguments. The first argument is the CSS
|
||||
property like `color` or `background`, the second argument is the CSS custom
|
||||
property name (without `--`) like `theme-color`, and the third argument is the
|
||||
CSS value, i.e., `$black` or `white`.
|
||||
|
||||
Make sure to import the mixin in your SCSS file and use it like this:
|
||||
|
||||
```scss
|
||||
div {
|
||||
@include themeable(color, theme-color, $black);
|
||||
}
|
||||
```
|
||||
|
||||
`themeable-important` is used when a CSS variable requires `!important` as a
|
||||
postfix of the CSS property's value where the CSS variable is being used. You
|
||||
can use it the same way you would use `themeable` mixin, but you should avoid
|
||||
`!important` if possible.
|
||||
|
||||
# Other user config
|
||||
|
||||
In addition to themes, users can also directly configure their preferred fonts
|
||||
and their nav bar preferences. The implementation of these is similar to themes.
|
||||
97
docs/faqs.md
|
|
@ -1,97 +0,0 @@
|
|||
---
|
||||
title: FAQs
|
||||
---
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
## How do I log in after starting up Forem for the first time?
|
||||
|
||||
Seeding the database create an admin user (see
|
||||
[Database](/getting-started/db/#default-admin-user)) with the following
|
||||
credentials:
|
||||
|
||||
```
|
||||
email: admin@forem.local
|
||||
password: password
|
||||
```
|
||||
|
||||
Once logged in as this admin user, you can turn on any authentication methods
|
||||
you'd like (see [Authentication](/backend/authentication/))
|
||||
|
||||
## How do I build my local copy of the Ruby source code documentation?
|
||||
|
||||
```shell
|
||||
cd docs
|
||||
make ruby-doc
|
||||
```
|
||||
|
||||
Then open `.static/ruby-doc/index.html` in the `docs` directory and browse the
|
||||
Ruby documentation
|
||||
|
||||
## How do I enable logging to standard output in development?
|
||||
|
||||
By default Rails logs to `log.development.log`.
|
||||
|
||||
If, instead, you wish to log to `STDOUT` you can add the variable:
|
||||
|
||||
```shell
|
||||
export RAILS_LOG_TO_STDOUT=true
|
||||
```
|
||||
|
||||
to your own `.env` file.
|
||||
|
||||
## How do I see comments in the Feed?
|
||||
|
||||
On the home Feed, we only show comments above certain "score". It's likely the
|
||||
comments in the local environment will never meet this score. If you want to see
|
||||
comments locally, you will need to update the score of your local comments
|
||||
manually. Here's how:
|
||||
|
||||
1. Open the terminal.
|
||||
2. Run `rails dbconsole` to open the PostgreSQL terminal. Alternatively, run
|
||||
`psql PracticalDeveloper_development` to open `psql`, the PostgreSQL
|
||||
terminal.
|
||||
3. Enter `update comments set score = 30;`.
|
||||
4. Type `exit` to leave the PostgreSQL terminal.
|
||||
|
||||
> Note: dbconsole reads database information from config/database.yml which is
|
||||
> always better since database configs might change in the future.
|
||||
|
||||
Once you refresh the app, you should be able to see some comments in the Feed.
|
||||
|
||||
## How do I make someone follow me on my local environment?
|
||||
|
||||
In certain cases, for example when testing various functionalities, you may need
|
||||
to be able to make some user follow you. Here's how:
|
||||
|
||||
1. Open the rails console by running `rails c` in your terminal.
|
||||
2. Get any user you want to follow you, for example `user = User.first`.
|
||||
3. Then make this user follow you: `user.follow(your_username)`.
|
||||
|
||||
Boom, you have a new follower!
|
||||
|
||||
## How do I remove / leave organization I created?
|
||||
|
||||
1. Open the rails console by running `rails c` in your terminal.
|
||||
2. Enter the following commands:
|
||||
```ruby
|
||||
user = User.find_by(username: "your_username")
|
||||
organization_id = Organization.find_by(slug: "organization_slug").id
|
||||
user.organization_memberships.where(organization_id: organization_id).destroy_all
|
||||
```
|
||||
|
||||
## How do I add credits to my account?
|
||||
|
||||
If you ever want to add Listings locally, you must have credits on your account
|
||||
to "pay" for listing. Here's how:
|
||||
|
||||
1. Open the rails console `rails console`.
|
||||
2. Enter the following commands:
|
||||
|
||||
```ruby
|
||||
user = User.find_by(username: "your_username")
|
||||
Credit.add_to(user, 1000)
|
||||
```
|
||||
|
||||
^ This will add 1000 credits to your account. But you know, you can't really buy
|
||||
anything with it :D
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
---
|
||||
title: Accessibility
|
||||
---
|
||||
|
||||
# Accessibility
|
||||
|
||||
To make Forem the most inclusive community platform around, accessibility should
|
||||
be considered to enable people with disabilities to create and consume content.
|
||||
|
||||
## The Basics
|
||||
|
||||
Forem UI changes should consider accessibility wherever possible. Common issues
|
||||
to watch out for in frontend code:
|
||||
|
||||
- [Adequate color contrast](https://webaim.org/articles/contrast/evaluating)
|
||||
- [Semantic structure and headings](https://webaim.org/techniques/semanticstructure/)
|
||||
- [Alternative text for images](https://webaim.org/techniques/alttext/)
|
||||
- [Unique button and link text](https://webaim.org/techniques/hypertext/link_text)
|
||||
- [Accessible forms with labels](https://webaim.org/techniques/forms/)
|
||||
- [Visible keyboard focus styles](https://www.washington.edu/accessibility/checklist/focus/)
|
||||
|
||||
## More Advanced Things
|
||||
|
||||
If you're working on something JavaScript-heavy or animated, there are a few
|
||||
additional considerations for accessibility:
|
||||
|
||||
- [Forem Accessibility Tests](https://docs.forem.com/tests/accessibility-tests/)
|
||||
- [Intro to ARIA](https://webaim.org/techniques/aria/)
|
||||
- [Handle focus for client-side interactions](https://dev.to/robdodson/managing-focus-64l)
|
||||
- [Reducing motion with CSS media queries](https://css-tricks.com/introduction-reduced-motion-media-query/)
|
||||
- [Linting with eslint-plugin-jsx-a11y](https://github.com/jsx-eslint/eslint-plugin-jsx-a11y)
|
||||
- [Testing with Jest-axe](https://dev.to/bdougieyo/accessibility-testing-in-react-with-jest-axe-l7k)
|
||||
|
||||
## Accessibility Testing
|
||||
|
||||
See a list of testing steps to follow during development or for a Pull Request
|
||||
review on the
|
||||
[Forem Accessibility Testing Docs](https://docs.forem.com/tests/accessibility-tests/).
|
||||
|
||||
## Resources
|
||||
|
||||
There's a wealth of information out there to learn about digital accessibility!
|
||||
Here are some resources:
|
||||
|
||||
- [W3C's Web Accessibility Initiative](https://www.w3.org/WAI/)
|
||||
- [Web Content Accessibility Guidelines](https://www.w3.org/TR/WCAG21/)
|
||||
- [ARIA Authoring Practices](https://www.w3.org/TR/wai-aria-practices-1.1/)
|
||||
- [WebAIM](http://webaim.org/)
|
||||
- [A11y Project](https://a11yproject.com)
|
||||
- [Deque University](https://dequeuniversity.com/)
|
||||
- [React Accessibility Docs](https://reactjs.org/docs/accessibility.html) (most
|
||||
will apply to Preact)
|
||||
- [The Importance of Manual Accessibility Testing](https://www.smashingmagazine.com/2018/09/importance-manual-accessibility-testing/)
|
||||
- [Accessibility Insights extension](https://accessibilityinsights.com)
|
||||
|
|
@ -1,52 +0,0 @@
|
|||
---
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
# Debugging
|
||||
|
||||
## In Browser
|
||||
|
||||
Browsers ship with their own developer tools. These are amazing tools to help
|
||||
you debug your web application. Consider learning how to use them.
|
||||
|
||||
- [Chrome Developer Tools](https://developers.google.com/web/tools/chrome-devtools)
|
||||
- [Firefox Developer Tools](https://developer.mozilla.org/en-US/docs/Tools)
|
||||
- [Safari Developer Tools](https://support.apple.com/en-ca/guide/safari/sfri20948/mac)
|
||||
|
||||
## Visual Studio Code
|
||||
|
||||
[Visual Studio Code](https://code.visualstudio.com) (VS Code) is a popular
|
||||
editor that allows you to debug many languages including JavaScript. Thanks to
|
||||
remote debugging technologies, we can debug our frontend code in VS Code. When
|
||||
you clone the Forem repository and open the project in VS Code, you will be
|
||||
prompted to install recommended extensions which include the
|
||||
[Chrome Debugger](https://code.visualstudio.com/blogs/2016/02/23/introducing-chrome-debugger-for-vs-code)
|
||||
and the
|
||||
[Edge Debugger](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-edge).
|
||||
|
||||
Setup:
|
||||
|
||||
- Refer to the respective debugger extension documentation above to ensure that
|
||||
your browser is running with remote debugging enabled.
|
||||
- Once you have your local installation of Forem running, you can attach to
|
||||
either the Chrome or Edge Debugger.
|
||||
|
||||

|
||||
|
||||
- From there you can do all the usual stuff that you would do while debugging
|
||||
JavaScript in the browser: setting breakpoints, setting
|
||||
[logpoints](https://code.visualstudio.com/docs/editor/debugging#_logpoints),
|
||||
watches etc.
|
||||
|
||||
## Where is My Editor Debug Configuration?
|
||||
|
||||
If you do not see your editor here, consider contributing to the documentation.
|
||||
😉
|
||||
|
||||
## Preact Developer Tools
|
||||
|
||||
Preact has their
|
||||
[own developer tools](https://preactjs.github.io/preact-devtools/) in the form
|
||||
of a browser extension. The Forem codebase is configured out of the box to
|
||||
support the Preact Devtools.
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
---
|
||||
title: Dynamic Imports
|
||||
---
|
||||
|
||||
# Dynamic Imports
|
||||
|
||||
[Dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import/#Dynamic_Imports)
|
||||
are supported in all major browsers except for Edge
|
||||
([EdgeHTML](https://en.wikipedia.org/wiki/EdgeHTML) version) and Internet
|
||||
Explorer which are both unsupported browsers for Forem. They allow you to import
|
||||
JavaScript dynamically instead of statically. Why is this important?
|
||||
Performance.
|
||||
|
||||
Should you use them everywhere? No. They are a great tool when you need to load
|
||||
a JavaScript module on the fly for functionality that is not needed immediately
|
||||
for the page to be usable.
|
||||
|
||||
Here are a couple of examples of dynamic import usage on Forem:
|
||||
|
||||
- The
|
||||
[Onboarding flow](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/Onboarding.jsx#L21).
|
||||
- In
|
||||
[homepage](https://github.com/forem/forem/blob/0633d85b6b0e083bb7b21b11642b2b17d3fe9de6/app/javascript/packs/homePage.jsx#L59)
|
||||
(followed tags).
|
||||
|
||||
Forem uses [webpacker](frontend/webpacker) (webpack), so what webpack will do is
|
||||
create separate bundles for code that is dynamically imported. So not only do we
|
||||
end up loading code only when we need it, we also end up with smaller bundle
|
||||
sizes in the frontend.
|
||||
|
||||
For a great deep dive into dynamic imports, there is a great article from
|
||||
community member [@goenning](https://dev.to/goenning) about dynamic import
|
||||
usage,
|
||||
[How we reduced our initial JS/CSS size by 67%](https://dev.to/goenning/how-we-reduced-our-initial-jscss-size-by-67-3ac0).
|
||||
|
|
@ -1,43 +0,0 @@
|
|||
# InstantClick
|
||||
|
||||
Like the [InstantClick](http://instantclick.io/) tag line says, “InstantClick is
|
||||
a JavaScript library that dramatically speeds up your website, making navigation
|
||||
effectively instant in most cases.”.
|
||||
|
||||
The way it works is if a user hovers over a hyperlink, chances are their
|
||||
intentions are to click on it. InstantClick will start prefetching the page
|
||||
while a user is hovering over a hyperlink, so that by the time they do click on
|
||||
it, it's instantaneous. On mobile devices, preloading starts on
|
||||
[touchstart](https://developer.mozilla.org/en-US/docs/Web/API/Element/touchstart_event).
|
||||
|
||||
Aside from prefetching pages, InstantClick also allows you to customize what
|
||||
happens when an InstantClick page changes.
|
||||
|
||||
```javascript
|
||||
// Found in https://github.com/forem/forem/blob/main/app/javascript/packs/githubRepos.jsx#L11)
|
||||
window.InstantClick.on('change', () => {
|
||||
loadElement();
|
||||
});
|
||||
```
|
||||
|
||||
You can also decide whether or not to reevaluate a script in an InstantClick
|
||||
loaded page via the `data-no-instant` attribute.
|
||||
|
||||
```javascript
|
||||
// Found in https://github.com/forem/forem/blob/main/app/assets/javascripts/utilities/buildCommentHTML.js.erb#L80
|
||||
function actions(comment) {
|
||||
if (comment.newly_created) {
|
||||
return '<div class="actions" data-comment-id="'+comment.id+'" data-path="'+comment.url+'">\
|
||||
<span class="current-user-actions" style="display: '+ (comment.newly_created ? 'inline-block' : 'none') +';">\
|
||||
<a data-no-instant="" href="'+comment.url+'/delete_confirm" class="edit-butt" rel="nofollow">DELETE</a>\
|
||||
<a href="'+comment.url+'/edit" class="edit-butt" rel="nofollow">EDIT</a>\
|
||||
</span>\
|
||||
<a href="#" class="toggle-reply-form" rel="nofollow">REPLY</a>\
|
||||
</div>';
|
||||
} else {
|
||||
...
|
||||
```
|
||||
|
||||
For more information on this, see the
|
||||
[Events and script re-evaluation in InstantClick](http://instantclick.io/scripts)
|
||||
documentation.
|
||||
|
|
@ -1,33 +0,0 @@
|
|||
---
|
||||
title: Linting and Formatting
|
||||
---
|
||||
|
||||
# Linting and Formatting
|
||||
|
||||
The project uses [eslint](https://eslint.org/) with the
|
||||
[Prettier plugin](https://github.com/prettier/eslint-plugin-prettier). eslint
|
||||
handles linting, but eslint rules related to code formatting, they get handled
|
||||
by prettier. For the most part, out of the box rules provided by the
|
||||
configurations that are extended are used but there are some tweaks.
|
||||
|
||||
Forem also has some objects that live in the global scope, e.g. Pusher. The
|
||||
eslint globals section of the eslint configuration is what enables these to be
|
||||
reported as existing when eslint runs.
|
||||
|
||||
```javascript
|
||||
globals: {
|
||||
InstantClick: false,
|
||||
filterXSS: false,
|
||||
Pusher: false,
|
||||
}
|
||||
```
|
||||
|
||||
## Husky and lint-staged
|
||||
|
||||
The code base uses a pre-commit hook that is enabled by the
|
||||
[husky](https://github.com/typicode/husky) and
|
||||
[lint-staged](https://github.com/okonet/lint-staged) tools. The pre-commit hook
|
||||
runs eslint before frontend code is committed. If there are any issues that can
|
||||
automatically be fixed, eslint will fix them. If there are linting issues that
|
||||
cannot be resolved, the commit fails and the changes need to be handled
|
||||
manually. Prettier also runs during the pre-commit hook.
|
||||
|
|
@ -1,160 +0,0 @@
|
|||
---
|
||||
title: Liquid Tags
|
||||
---
|
||||
|
||||
# Liquid Tags
|
||||
|
||||
Liquid tags are special elements of the
|
||||
[Forem Markdown editor](https://dev.to/new).
|
||||
|
||||
They are custom embeds that are added via a `{% %}` syntax.
|
||||
[Liquid](https://shopify.github.io/liquid/) is a templating language developed
|
||||
by Shopify.
|
||||
|
||||
Liquid embeds are for tweets, like `{% tweet 765282762081329153 %}` or a Forem
|
||||
user profile preview, like `{% user jess %}` etc.
|
||||
|
||||
They make for good community contributions because they can be extended and
|
||||
improved consistently. It is truly how we extend the functionality of the
|
||||
editor. At the moment, there could be a lot of work refactoring and improving
|
||||
existing liquid tags, in addition to adding new ones.
|
||||
|
||||
Liquid tags are sort of like functions, which have a name and take arguments.
|
||||
Develop them with that mindset in terms of naming things. They should be
|
||||
documented but also intuitive. They should also be fairly flexible in the
|
||||
arguments they take. Currently, this could use improvements.
|
||||
|
||||
_Note: Liquid tags are "compiled" when an article is saved. So you will need to
|
||||
re-save articles to see HTML changes._
|
||||
|
||||
Here is a bunch of liquid tags supported on Forem:
|
||||
|
||||
```liquid
|
||||
{% link https://dev.to/kazz/boost-your-productivity-using-markdown-1be %}
|
||||
{% user jess %}
|
||||
{% tag git %}
|
||||
{% devcomment 2d1a %}
|
||||
{% podcast https://dev.to/basecspodcast/s2e2--queues-irl %}
|
||||
{% twitter 834439977220112384 %}
|
||||
{% glitch earthy-course %}
|
||||
{% github forem/forem %}
|
||||
{% youtube dQw4w9WgXcQ %}
|
||||
{% vimeo 193110695 %}
|
||||
{% twitch ClumsyPrettiestOilLitFam %}
|
||||
{% slideshare rdOzN9kr1yK5eE %}
|
||||
{% codepen https://codepen.io/twhite96/pen/XKqrJX %}
|
||||
{% stackblitz ball-demo %}
|
||||
{% codesandbox ppxnl191zx %}
|
||||
{% jsfiddle https://jsfiddle.net/link2twenty/v2kx9jcd %}
|
||||
{% dotnetfiddle https://dotnetfiddle.net/PmoDip %}
|
||||
{% replit @WigWog/PositiveFineOpensource %}
|
||||
{% stackery deeheber lambda-layer-example layer-resource %}
|
||||
{% nexttech https://nt.dev/s/6ba1fffbd09e %}
|
||||
{% instagram BXgGcAUjM39 %}
|
||||
{% speakerdeck 7e9f8c0fa0c949bd8025457181913fd0 %}
|
||||
{% soundcloud https://soundcloud.com/user-261265215/dev-to-review-episode-1 %}
|
||||
{% spotify spotify:episode:5V4XZWqZQJvbddd31n56mf %}
|
||||
{% blogcast 1234 %}
|
||||
{% kotlin https://pl.kotl.in/owreUFFUG %}
|
||||
{% wikipedia https://en.wikipedia.org/wiki/Wikipedia %}
|
||||
{% reddit https://www.reddit.com/r/aww/comments/ag3s4b/ive_waited_28_years_to_finally_havr_my_first_pet %}
|
||||
```
|
||||
|
||||
## How liquid tags are developed
|
||||
|
||||
Liquid tags are a matter of parsing the "arguments" and serving relevant
|
||||
JavaScript.
|
||||
|
||||
Liquid tags go in the `app/liquid_tags` folder. All liquid tags inherit from the
|
||||
base, like so...
|
||||
|
||||
```ruby
|
||||
class KotlinTag < LiquidTagBase
|
||||
```
|
||||
|
||||
Each liquid tag contains an `initialize` method which takes arguments and calls
|
||||
`super`. It also has a `render` method which calls the appropriate view.
|
||||
|
||||
```ruby
|
||||
def initialize(_tag_name, link, _parse_context)
|
||||
super
|
||||
stripped_link = ActionController::Base.helpers.strip_tags(link)
|
||||
the_link = stripped_link.split.first
|
||||
@embedded_url = KotlinTag.embedded_url(the_link)
|
||||
end
|
||||
|
||||
def render(_context)
|
||||
ApplicationController.render(
|
||||
partial: PARTIAL,
|
||||
locals: {
|
||||
url: @embedded_url
|
||||
}
|
||||
)
|
||||
end
|
||||
```
|
||||
|
||||
View files can be found in `app/views/liquids`.
|
||||
|
||||
Each new liquid tag should be accompanied by instructions in
|
||||
`app/views/pages/_editor_guide_text.html.erb`.
|
||||
|
||||
Liquid Tags should also be accompanied by tests in `spec/liquid_tags` which
|
||||
confirm expected behavior.
|
||||
|
||||
Some Liquid Tags are constructed using HTML and CSS within the app, and some are
|
||||
constructed by displaying an iframe of an external site.
|
||||
|
||||
CSS for Liquid Tags are found in `app/assets/stylesheets/ltags`. Liquid tag
|
||||
classes should generally be prepended by `ltag__`. e.g. `ltag__tag__content`
|
||||
etc.
|
||||
|
||||
Here is an example of a good Liquid Tag pull request...
|
||||
https://github.com/forem/forem/pull/3801
|
||||
|
||||
### Restricting liquid tags by roles
|
||||
|
||||
To only allow users with specific roles to use a liquid tag, you need to define
|
||||
a `VALID_ROLES` constant on the liquid tag itself. It needs to be an `Array` of
|
||||
valid roles. For [single resource roles](/admin), it needs to be an `Array` with
|
||||
the role and the resource. Here's an example:
|
||||
|
||||
```ruby
|
||||
class NewLiquidTag < LiquidTagBase
|
||||
VALID_ROLES = [
|
||||
:admin,
|
||||
[:restricted_liquid_tag, LiquidTags::UserSubscriptionTag]
|
||||
].freeze
|
||||
end
|
||||
```
|
||||
|
||||
Here we are saying that the `UserSubscriptionTag` is only usable by users with
|
||||
the `admin` role or with a role of `:restricted_liquid_tag` and a specified
|
||||
resource of `LiquidTags::UserSubscriptionTag`.
|
||||
|
||||
`LiquidTags::UserSubscriptionTag` is a resource model so we that can play nicely
|
||||
with the [Rolify][rolify] gem. See [/admin](/admin) for more information.
|
||||
|
||||
**REMINDER: if you do not define a `VALID_ROLES` constant, the liquid tag will
|
||||
be usable by all users by default.**
|
||||
|
||||
### Restricting liquid tags by context
|
||||
|
||||
Context, in terms of a liquid tag, is _where_ a liquid tag is being used (i.e.
|
||||
`Article`, `Comment`, etc.). In other words, if you want to make a liquid tag
|
||||
that can only be used in articles, you need to restrict the liquid tag by
|
||||
context.
|
||||
|
||||
To do this you need to add a `VALID_CONTEXTS` constant on the liquid tag itself.
|
||||
It needs to be an `Array` of class names that are valid. For example, to
|
||||
restrict a liquid tag to only be usable in articles you would do:
|
||||
|
||||
```ruby
|
||||
class NewLiquidTag < LiquidTagBase
|
||||
VALID_CONTEXTS = %w[Article].freeze
|
||||
end
|
||||
```
|
||||
|
||||
**REMINDER: if you do not define a `VALID_CONTEXTS` constant the liquid tag will
|
||||
be usable in all contexts by default.**
|
||||
|
||||
[rolify]: https://github.com/RolifyCommunity/rolify
|
||||
|
|
@ -1,44 +0,0 @@
|
|||
---
|
||||
title: JavaScript and Initializers
|
||||
---
|
||||
|
||||
# JavaScript and Initializers
|
||||
|
||||
Forem has two JavaScript codebases.
|
||||
|
||||
One is located in the directory `app/assets/javascripts` and contains plain
|
||||
JavaScript (mostly ES5+) being served using
|
||||
[Sprockets](https://github.com/rails/sprockets-rails) which packages static
|
||||
assets.
|
||||
|
||||
Webpacker manages the other one,
|
||||
[which you can read more about in this guide](/frontend/webpacker).
|
||||
|
||||
This source code is not transpiled, only packaged and minified, and will be
|
||||
limited to whatever flavor of JavaScript can run on the user's web browser.
|
||||
|
||||
`app/assets/javascripts/application.js` contains the manifest JavaScript file
|
||||
which is included globally in the primary template,
|
||||
`app/views/layouts/application.html.erb`.
|
||||
|
||||
`application.js` automatically includes all JS files via the statement:
|
||||
|
||||
```erb
|
||||
//= require_tree .
|
||||
```
|
||||
|
||||
One JS file in particular, `app/assets/javascripts/initializePage.js`,
|
||||
bootstraps the majority of the functionality. You will notice, within this file,
|
||||
that major sections of the websites are bootstrapped, for example:
|
||||
|
||||
```javascript
|
||||
initializeBaseTracking();
|
||||
initializeCommentsPage();
|
||||
initEditorResize();
|
||||
initLeaveEditorWarning();
|
||||
initializeArticleReactions();
|
||||
initNotifications();
|
||||
initializeSplitTestTracking();
|
||||
```
|
||||
|
||||
All the "initializers" are in `/app/assets/javascripts/initializers`.
|
||||
|
|
@ -1,85 +0,0 @@
|
|||
---
|
||||
title: Preact
|
||||
---
|
||||
|
||||
# Preact
|
||||
|
||||
[Preact](https://preactjs.com/) is an alternative to React with the same modern
|
||||
API.
|
||||
|
||||
Preact components are packaged using [Webpacker](/frontend/webpacker) and the
|
||||
Preact code is located in `app/javascript`.
|
||||
|
||||
Preact components get loaded via webpacker's helper function
|
||||
`javascript_packs_with_chunks_tag`.
|
||||
|
||||
## PropTypes
|
||||
|
||||
Preact supports
|
||||
[PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html). When
|
||||
creating Preact components, please ensure that you have defined your PropTypes.
|
||||
|
||||
### Common PropTypes
|
||||
|
||||
Using PropTypes can be repetitive. Some duplication is normal, like when a
|
||||
PropType is a string or a number. But for commonly-used PropTypes, like the user
|
||||
entity, you can use the provided common PropTypes, located in the
|
||||
`/app/javascript/common-prop-types`, as shown below.
|
||||
|
||||
```javascript
|
||||
import PropTypes from 'prop-types';
|
||||
|
||||
export const userPropTypes = PropTypes.shape({
|
||||
id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
profile_image_url: PropTypes.string.isRequired,
|
||||
summary: PropTypes.string.isRequired,
|
||||
});
|
||||
```
|
||||
|
||||
#### Using Common PropTypes
|
||||
|
||||
Common PropTypes are imported just like any other
|
||||
[JavaScript Module](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).
|
||||
For example, here are two scenarios where a component needs to use the
|
||||
`tagPropTypes`.
|
||||
|
||||
In the example below, our component `SomeComponentUsingTags` has a `tags` prop,
|
||||
which is an array of the tag entity. PropTypes have a built-in method called
|
||||
`arrayOf` that allows you to define a prop as an array of something. In our
|
||||
case, this is the tag entity, so we can use the `tagPropTypes` PropType.
|
||||
|
||||
```jsx
|
||||
import { h } from 'preact';
|
||||
import PropTypes from 'prop-types';
|
||||
import { tagPropTypes } from '../../../components/common-prop-types';
|
||||
|
||||
const SomeComponentUsingTags = ({ tags = [] }) => (
|
||||
<ul>
|
||||
{tags.map((tag) => (
|
||||
<li key={tag.id}>{tag.name}</li>
|
||||
))}
|
||||
</ul>
|
||||
);
|
||||
|
||||
SomeComponentUsingTags.displayName = 'SomeComponentUsingTags';
|
||||
SomeComponentUsingTags.propTypes = {
|
||||
tags: PropTypes.arrayOf(tagPropTypes).isRequired,
|
||||
};
|
||||
```
|
||||
|
||||
In the following example, the `SomeComponentUsingOneTag` component has a `tag`
|
||||
prop representing a single tag. In this case, we can just the `tagPropTypes` on
|
||||
their own to represent the shape of the `tag` prop.
|
||||
|
||||
```jsx
|
||||
import { h } from 'preact';
|
||||
import { tagPropTypes } from '../../../components/common-prop-types';
|
||||
|
||||
const SomeComponentUsingOneTag = ({ tag }) => <li key={tag.id}>{tag.name}</li>;
|
||||
|
||||
SomeComponentUsingOneTag.displayName = 'SomeComponentUsingTags';
|
||||
SomeComponentUsingOneTag.propTypes = {
|
||||
tag: tagPropTypes.isRequired,
|
||||
};
|
||||
```
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
---
|
||||
items:
|
||||
- plain-js.md
|
||||
- webpacker.md
|
||||
- preact.md
|
||||
- instant-click.md
|
||||
- dynamic-imports.md
|
||||
- styles.md
|
||||
- linting-formatting.md
|
||||
- liquid-tags.md
|
||||
- debugging.md
|
||||
- tracking.md
|
||||
- accessibility.md
|
||||
- tips.md
|
||||
---
|
||||
|
||||
# Frontend Guide
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
---
|
||||
title: Styles
|
||||
---
|
||||
|
||||
# Styles
|
||||
|
||||
The majority of the CSS in the application is written in
|
||||
[SASS](https://sass-lang.com/). There are a few places in the code base that
|
||||
have style blocks in ERB templates, for inlining critical CSS (good). There are
|
||||
also some styles that live in ERB templates that are not critical CSS (bad).
|
||||
That is a bit of refactoring that needs to be done. PRs welcome!
|
||||
|
||||
Important files when working with SASS in the project:
|
||||
|
||||
- variables:
|
||||
[/app/assets/stylesheets/variables.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/variables.scss)
|
||||
- mixins:
|
||||
[/app/assets/stylesheets/\_mixins.scss](https://github.com/forem/forem/blob/main/app/assets/stylesheets/_mixins.scss)
|
||||
|
||||
SASS is compiled and served using
|
||||
[Sprockets](https://github.com/rails/sprockets-rails) which packages static
|
||||
assets in Rails.
|
||||
|
||||
For more about branding, theming or design in general in regards to Forem, refer
|
||||
to the [Design Guide](/design) documentation.
|
||||
|
||||
## Crayons
|
||||
|
||||
Crayons is the design system used by Forem. A
|
||||
[storybook](https://storybook.js.org/) listing the various elements is available
|
||||
at https://storybook.forem.com/
|
||||
|
||||
You can also run it locally with the following command:
|
||||
|
||||
```
|
||||
$ yarn storybook
|
||||
```
|
||||
|
|
@ -1,46 +0,0 @@
|
|||
---
|
||||
title: Tips
|
||||
---
|
||||
|
||||
# Tips
|
||||
|
||||
## About query selectors
|
||||
|
||||
JavaScript has many different query selectors, some seemingly interchangeable,
|
||||
for example:
|
||||
|
||||
- `document.head` and `document.body`
|
||||
- `document.getElementById`, `document.getElementsByClassName` and
|
||||
`document.getElementsByTagName`
|
||||
- `document.querySelector` and `document.querySelectorAll`
|
||||
|
||||
Knowing which to use for optimal performance depends on the situation, but a
|
||||
good rule of thumb is:
|
||||
|
||||
- to access the **head** of the document, use `document.head` over
|
||||
`document.getElementsByTagName('head')`
|
||||
- to access the **body** of the document, use `document.body` over
|
||||
`document.getElementsByTagName('body')`
|
||||
- to access an element by id use `document.getElementById('id')` over
|
||||
`document.querySelector('#id')`
|
||||
- to access one element by class name use
|
||||
`document.getElementsByClassName('className')[0]` over
|
||||
`document.querySelector('.className')`
|
||||
- to access multiple elements by class name use
|
||||
`document.getElementsByClassName('className')` over
|
||||
`document.querySelectorAll('.className')`
|
||||
- to access one element by tag name use
|
||||
`document.getElementsByTagName('tagName')[0]` over
|
||||
`document.querySelector('tagName')`
|
||||
- to access multiple elements by tag name use
|
||||
`document.getElementsByTagName('tagName')` over
|
||||
`document.querySelectorAll('tagName')`
|
||||
|
||||
In most cases `querySelector` and `querySelectorAll` should be used only on
|
||||
selectors more sophisticated than a simple id, class or tag name.
|
||||
|
||||
### Resources
|
||||
|
||||
- [Forem PR 6380](https://github.com/forem/forem/issues/6380#issuecomment-592989438)
|
||||
- [Why is getElementsByTagName() faster than querySelectorAll()?](https://humanwhocodes.com/blog/2010/09/28/why-is-getelementsbytagname-faster-that-queryselectorall/)
|
||||
- [What is the difference between querySelectorAll and getElementsByTagName?](https://stackoverflow.com/a/30921553/4186181)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
title: Tracking
|
||||
---
|
||||
|
||||
## Ahoy.js
|
||||
|
||||
For first-party analytics, we use the
|
||||
[`ahoy.js` library](https://github.com/ankane/ahoy.js), which tracks visits and
|
||||
events. This library works in conjunction with the `ahoy_matey` gem, which is
|
||||
documented in our [backend tracking guide](/backend/tracking).
|
||||
|
||||
### Configuration
|
||||
|
||||
The configuration for `ahoy.js` lives in `app/assets/javascripts/base.js.erb`.
|
||||
Since we do not track user cookies on the backend, we have configured
|
||||
`ahoy.js`'s defaults to match that on the frontend.
|
||||
|
||||
### Events
|
||||
|
||||
In order to track an event, use the `ahoy.track` function:
|
||||
|
||||
```javascript
|
||||
ahoy.track(name, properties);
|
||||
```
|
||||
|
||||
This function will send a `POST` request to the `/ahoy/events` endpoint on our
|
||||
backend with the `name` and `properties` of the event. The backend endpoint will
|
||||
also create a corresponding `Ahoy::Visit` for the event if one does not exist
|
||||
already.
|
||||
|
|
@ -1,56 +0,0 @@
|
|||
---
|
||||
title: Webpacker
|
||||
---
|
||||
|
||||
# Webpacker
|
||||
|
||||
Forem has two JavaScript codebases.
|
||||
|
||||
One contains plain JavaScript,
|
||||
[which you can read more about in this guide](/frontend/plain-js).
|
||||
|
||||
The other one is managed by [Webpacker](https://github.com/rails/webpacker), and
|
||||
it's located inside `/app/javascript`, written using ES6+.
|
||||
|
||||
Currently, it's mainly used for Preact components, served via `webpack` which is
|
||||
integrated into the Rails app using `Webpacker`.
|
||||
|
||||
There is a packs directory `/app/javascript/packs` where you can create new
|
||||
"pack" files. Pack files are initializers for Webpacker.
|
||||
|
||||
Since Forem is not a Single Page Application (SPA), Preact components are
|
||||
mounted as needed by including the pack file in the view files.
|
||||
|
||||
For example:
|
||||
|
||||
```erb
|
||||
<%= javascript_packs_with_chunks_tag "webShare", defer: true %>
|
||||
```
|
||||
|
||||
The include statement corresponds to the pack `app/javascript/packs/webShare.js`
|
||||
|
||||
If you have more than one webpacker pack on the page, you need to include it in
|
||||
the same `javascript_packs_with_chunks_tag` call. The reason being is it avoids
|
||||
loading split chunks multiple times.
|
||||
|
||||
```erb
|
||||
<%= javascript_packs_with_chunks_tag "webShare", "someOtherPack", defer: true %>
|
||||
```
|
||||
|
||||
## Webpack aliases
|
||||
|
||||
The project uses
|
||||
[webpack aliases](https://webpack.js.org/configuration/resolve/#resolvealias).
|
||||
The aliases used in the project can be found under `alias` in
|
||||
https://github.com/forem/forem/blob/master/config/webpack/environment.js
|
||||
|
||||
## Additional Resources
|
||||
|
||||
For more information in regards to `javascript_packs_with_chunks_tag`, see
|
||||
https://github.com/rails/webpacker/blob/main/lib/webpacker/helper.rb
|
||||
|
||||
Aside from the Webpacker repository, see also Ross Kaffenberger's
|
||||
[visual guide to Webpacker](https://rossta.net/blog/visual-guide-to-webpacker.html).
|
||||
|
||||
If you're interested in bundles sizes and what's contained within them for a
|
||||
production build, run `bin/bundleAnalyzer` from the command line.
|
||||
|
|
@ -1,28 +0,0 @@
|
|||
---
|
||||
title: Creating a Branch
|
||||
---
|
||||
|
||||
# Creating a feature/bug branch
|
||||
|
||||
When you are working on a bug, feature, or improvement, you will need to create
|
||||
a branch.
|
||||
|
||||
Branches names should be prefixed with your own GitHub username. If they have an
|
||||
associated issue, the issue ID should be added as a suffix. For example:
|
||||
|
||||
```shell
|
||||
git checkout -b USERNAME/that-new-feature-1234
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```shell
|
||||
git checkout -b USERNAME/fixing-that-bug-1234
|
||||
```
|
||||
|
||||
where `USERNAME` should be replaced by your username on GitHub and `1234` is the
|
||||
ID of the issue tied to your pull request. If there is no related issue, you can
|
||||
leave the number out.
|
||||
|
||||
NOTE: It is ok if your branch name shows up as
|
||||
`USERNAME:USERNAME/fixing-that-bug-1234` when you create the pull request
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
---
|
||||
title: Committing
|
||||
---
|
||||
|
||||
# Committing and pre-commit hooks
|
||||
|
||||
## Commit messages
|
||||
|
||||
We encourage people to write
|
||||
[meaningful commit messages](https://chris.beams.io/posts/git-commit/).
|
||||
|
||||
## Style guide
|
||||
|
||||
This project follows
|
||||
[thoughtbot's Ruby Style Guide](https://github.com/thoughtbot/guides/blob/main/ruby/.rubocop.yml),
|
||||
using [Rubocop](https://github.com/bbatsov/rubocop) along with
|
||||
[Rubocop-Rspec](https://github.com/backus/rubocop-rspec) as the code analyzer.
|
||||
If you have Rubocop installed with your text editor of choice, you should be up
|
||||
and running.
|
||||
|
||||
For the frontend, [ESLint](https://eslint.org) and
|
||||
[prettier](https://github.com/prettier/prettier) are used. ESLint's recommended
|
||||
rules along with Preact's recommended rules are used for code-quality.
|
||||
Formatting is handled by prettier. If you have ESLint installed with your text
|
||||
editor of choice, you should be up and running.
|
||||
|
||||
## Husky hooks
|
||||
|
||||
When commits are made, a git precommit hook runs via
|
||||
[husky](https://github.com/typicode/husky) and
|
||||
[lint-staged](https://github.com/okonet/lint-staged). ESLint, prettier, and
|
||||
Rubocop will run on your code before it's committed. If there are linting errors
|
||||
that can't be automatically fixed, the commit will not happen. You will need to
|
||||
fix the issue manually then attempt to commit again.
|
||||
|
||||
Note: if you've already installed the [husky](https://github.com/typicode/husky)
|
||||
package at least once (used for pre-commit npm script), you will need to run
|
||||
`yarn --force` or `npm install --no-cache`. For some reason, the post-install
|
||||
script of husky does not run when the package is pulled from yarn or npm's
|
||||
cache. This is not husky specific, but rather a cached package issue.
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
---
|
||||
title: Configuring Environment Variables
|
||||
---
|
||||
|
||||
# Configuring environment variables and secret keys
|
||||
|
||||
Take a look at `.env_sample`. This file lists all the `ENV` variables we use and
|
||||
provides a fake default for any missing keys.
|
||||
|
||||
The [backend guide][backend_guide] will show you how to get free API keys for
|
||||
additional services that may be required to run certain parts of the app.
|
||||
|
||||
To set up keys for your local instance of Forem, you'll need to create an `.env`
|
||||
file. You can do this by copying the file called `.env_sample` in the app's main
|
||||
directory:
|
||||
|
||||
```shell
|
||||
cp .env_sample .env
|
||||
```
|
||||
|
||||
Then, add each key you need to the `.env` file. For example, if you're setting
|
||||
up Cloudinary:
|
||||
|
||||
```shell
|
||||
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
|
||||
```
|
||||
|
||||
(Don't worry, your `.env` file is ignored by git)
|
||||
|
||||
If you are missing `ENV` variables when you boot your application you will see a
|
||||
warning message in your logs when you try to access that variable
|
||||
`Unset ENV variable: xyz`.
|
||||
|
||||
Only certain features require "real" keys, so you may be able to add them as you
|
||||
work on different areas of the application.
|
||||
|
||||
[backend_guide]: /backend
|
||||
|
|
@ -1,68 +0,0 @@
|
|||
---
|
||||
title: Preparing the Database
|
||||
---
|
||||
|
||||
# Preparing the database
|
||||
|
||||
The next step is to create and prepare the database. Because Forem is a Rails
|
||||
application, we have built-in tools to help us.
|
||||
|
||||
We can use Rails to create our database, load the schema, and add some seed
|
||||
data:
|
||||
|
||||
```shell
|
||||
rails db:setup
|
||||
```
|
||||
|
||||
Note: If you've already run `bin/setup`, this will have already been done for
|
||||
you.
|
||||
|
||||
`db:setup` actually runs the following rake commands in order so alternatively,
|
||||
you could run each of these to produce the same result:
|
||||
|
||||
```shell
|
||||
rails db:create
|
||||
rails db:schema:load
|
||||
rails db:seed
|
||||
```
|
||||
|
||||
## Seed Data
|
||||
|
||||
By default, the amount of articles and users generated is quite tiny so that
|
||||
contributors experience a quick installation. If you require more data for your
|
||||
local installation, you can tune amount of data generated with the environment
|
||||
variable `SEEDS_MULTIPLIER`.
|
||||
|
||||
This variable, which defaults to `1`, allows the developer to increase the size
|
||||
of their local DB. For example:
|
||||
|
||||
```shell
|
||||
SEEDS_MULTIPLIER=2 rails db:setup
|
||||
```
|
||||
|
||||
will result in creating double the default amount of items in the database.
|
||||
|
||||
It's currently used only for `articles` and `users`.
|
||||
|
||||
It can also be used for `rails db:seed` and `rails db:reset`.
|
||||
|
||||
## Default Admin User
|
||||
|
||||
Seed data creates a handful of regular users, and a single admin user that can
|
||||
be used to log into the application with the Email login option:
|
||||
|
||||
```
|
||||
email: admin@forem.local
|
||||
password: password
|
||||
```
|
||||
|
||||
### Other seed modes
|
||||
|
||||
To put your local forem into "starter mode", as it would be for a new creator,
|
||||
use `MODE=STARTER` i.e...
|
||||
|
||||
```shell
|
||||
MODE=STARTER rails db:setup
|
||||
```
|
||||
|
||||
This mode skips creation of all sample data.
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
---
|
||||
title: Forking the Repo
|
||||
---
|
||||
|
||||
# Forking the repository
|
||||
|
||||
The first step in contributing to any Open Source project on GitHub is
|
||||
[forking][forking] so that you can create [Pull Requests][pull_requests]. If
|
||||
you're not familiar with the idea of forking a repository, [GitHub has a super
|
||||
simple tutorial][forking_tutorial]!
|
||||
|
||||
If you're ready to get started, [fork Forem's repository][fork_dev]!
|
||||
|
||||
Once you've got a fork on GitHub, you can clone your fork to run the project
|
||||
locally:
|
||||
|
||||
```shell
|
||||
git clone https://github.com/<your-username>/forem.git
|
||||
```
|
||||
|
||||
As time goes on, you may need to refresh your fork. If you have trouble with
|
||||
that, you can read about [keeping a fork caught up on Forem][refresh_your_fork]!
|
||||
|
||||
[forking]: https://help.github.com/en/articles/fork-a-repo
|
||||
[fork_dev]: https://github.com/forem/forem/fork
|
||||
[forking_tutorial]: https://guides.github.com/activities/forking/
|
||||
[pull_requests]: https://help.github.com/en/articles/about-pull-requests
|
||||
[refresh_your_fork]:
|
||||
https://dev.to/jacobherrington/a-fool-proof-way-to-keep-your-fork-caught-up-in-git-2e2e
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
---
|
||||
title: Preparing a Pull Request
|
||||
---
|
||||
|
||||
# Preparing a pull request
|
||||
|
||||
- Try to keep the pull requests small. A pull request should try its very best
|
||||
to address only a single concern.
|
||||
- If you plan to do further work after the PR has been submitted, please use the
|
||||
[Draft PR](https://github.blog/2019-02-14-introducing-draft-pull-requests/)
|
||||
feature.
|
||||
- Make sure all tests pass and add additional tests for the code you submit.
|
||||
[More info here](https://docs.forem.com/tests/).
|
||||
- Document your reasoning behind the changes. Explain why you wrote the code in
|
||||
the way you did. The code should explain what it does.
|
||||
- If there's an existing issue related to the pull request, reference to it by
|
||||
adding something like `References/Closes/Fixes/Resolves #305`, where 305 is
|
||||
the issue number.
|
||||
[More info here](https://github.com/blog/1506-closing-issues-via-pull-requests).
|
||||
- Please fill out the PR Template when making a PR.
|
||||
- All commits in a pull request will be squashed when merged, but when your PR
|
||||
is approved and passes our CI, it will eventually be live on production!
|
||||
|
||||
If the pull request affects the public API in any way, a post on Forem from the
|
||||
Forem Team account should accompany it. This is the duty of the core team to
|
||||
carry out.
|
||||
|
|
@ -1,16 +0,0 @@
|
|||
---
|
||||
title: Getting Started
|
||||
items:
|
||||
- forking.md
|
||||
- config-env.md
|
||||
- db.md
|
||||
- start-app.md
|
||||
- workflow.md
|
||||
- branching.md
|
||||
- committing.md
|
||||
- pull-request.md
|
||||
- syncing.md
|
||||
- architecture.md
|
||||
---
|
||||
|
||||
# Getting Started!
|
||||
|
|
@ -1,131 +0,0 @@
|
|||
---
|
||||
title: Starting the Application
|
||||
---
|
||||
|
||||
We're a Rails app, and we use [Webpacker][webpacker] to manage some of our
|
||||
JavaScript.
|
||||
|
||||
# Starting the application
|
||||
|
||||
Start the application, Webpack, and our job runner [Sidekiq][sidekiq] by
|
||||
running:
|
||||
|
||||
```shell
|
||||
bin/startup
|
||||
```
|
||||
|
||||
(This just runs `foreman start -f Procfile.dev`, for notes on how to install
|
||||
Foreman, please see [Other Tools](/installation/others/))
|
||||
|
||||
Then point your browser to http://localhost:3000/ to view the site. To log in
|
||||
use the admin account created by default (see
|
||||
[Database](/getting-started/db/#default-admin-user))
|
||||
|
||||
If you run into issues while trying to run `bin/setup` and the error message
|
||||
isn't helpful, try running `bin/rails s -p 3000`. For example, you may need to
|
||||
`yarn install` before starting the app.
|
||||
|
||||
If Sidekiq is producing errors similar to
|
||||
`No such file or directory - [SOME FILE]`, you may need to start Sidekiq by
|
||||
itself once to help it initialize itself fully. You can use the command
|
||||
`bundle exec sidekiq` to do this.
|
||||
|
||||
If you're working on Forem regularly, you can use `alias start="bin/startup"` to
|
||||
make this even easier. 😊
|
||||
|
||||
If you're using **`pry`** for debugging in Rails, note that using `foreman` and
|
||||
`pry` together works, but it's not as clean as `bin/rails server`.
|
||||
|
||||
Here are some singleton commands you may need, usually in a separate
|
||||
instance/tab of your shell.
|
||||
|
||||
- Running the job Sidekiq server (if using `bin/rails server`) -- this is mostly
|
||||
for notifications and emails: **`bundle exec sidekiq`**
|
||||
|
||||
Current gotchas: potential environment issues with external services need to be
|
||||
worked out.
|
||||
|
||||
# Starting the application (advanced)
|
||||
|
||||
To have additional control in your local environment, you might prefer using an
|
||||
advanced setup to start the application by using [Overmind][overmind].
|
||||
|
||||
The prerequisite is to install [Overmind][overmind], a process manager which
|
||||
takes advantage of [tmux][tmux].
|
||||
|
||||
This will allow you to launch all your app's processes in the same terminal,
|
||||
navigate the logs of each service separately, restart each service separately
|
||||
and have a better debugging experience.
|
||||
|
||||
After installing [Overmind][overmind], launch the application:
|
||||
|
||||
```shell
|
||||
overmind s -f Procfile.dev
|
||||
```
|
||||
|
||||
## Debugging the Rails application
|
||||
|
||||
[Overmind][overmind] lets you easily step through the Rails application in a
|
||||
debugging session.
|
||||
|
||||
Using the `pry` gem, you can add a `binding.pry` to set a breakpoint in the
|
||||
method you're trying to debug; the application will halt its execution there.
|
||||
You can then connect to the web server by opening a separate terminal window and
|
||||
typing:
|
||||
|
||||
```shell
|
||||
overmind c web
|
||||
```
|
||||
|
||||
This will open up a [tmux][tmux] window pane at the debugging statement
|
||||
position, which will look something like this:
|
||||
|
||||
```ruby
|
||||
pry(#<Admin::OverviewController>)> whereami
|
||||
|
||||
From: /forem/app/controllers/admin/overview_controller.rb:8 Admin::OverviewController#index:
|
||||
|
||||
5: def index
|
||||
6: a = "Hello debugger"
|
||||
7: binding.pry
|
||||
=> 8: end
|
||||
```
|
||||
|
||||
## Inspecting the logs of each service
|
||||
|
||||
Overmind launches the various services required for our local setup: `web` (the
|
||||
Rails web server), `webpacker` (the server managing JavaScript) and `sidekiq`
|
||||
(the server managing the asynchronous queue).
|
||||
|
||||
If, for example, you want to inspect just the Sidekiq logs, you can open a
|
||||
separate terminal window to look at those logs specifically:
|
||||
|
||||
```shell
|
||||
overmind c sidekiq
|
||||
```
|
||||
|
||||
This will open a `tmux` console, which will allow you to browse _only_ the
|
||||
Sidekiq logs.
|
||||
|
||||
There are also some handy `tmux` shortcuts that you may find useful.
|
||||
|
||||
- The shortcut `C-b [` (_Control-b-open square bracket_) activates "scroll
|
||||
mode", which allows you to use the arrows up and down and inspect the logs.
|
||||
- The shortcut `q` deactivates "scroll mode".
|
||||
|
||||
Please refer to [tmux][tmux] documentation for more information around `tmux`
|
||||
configuration and for additional shortcuts.
|
||||
|
||||
## Resources
|
||||
|
||||
Other than the official [Overmind][overmind] and [tmux][tmux] documentation, you
|
||||
may find the following resources useful:
|
||||
|
||||
- [Rails quick tips #6: tmux, tmuxinator and Overmind](https://dev.to/citizen428/rails-quick-tips-6-tmux-tmuxinator-and-overmind-4850)
|
||||
- [Give Your Terminal Super Powers: tmux Cheatsheet!](https://dev.to/jacobherrington/give-your-terminal-super-powers-tmux-cheatsheet-1p6p)
|
||||
- [Introducing Overmind and Hivemind](https://evilmartians.com/chronicles/introducing-overmind-and-hivemind)
|
||||
|
||||
[sidekiq]: https://github.com/mperham/sidekiq
|
||||
[webpacker]: https://github.com/rails/webpacker
|
||||
[overmind]: https://github.com/DarthSim/overmind
|
||||
[tmux]: https://github.com/tmux/tmux/wiki
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
---
|
||||
title: Keeping Your Fork In Sync
|
||||
---
|
||||
|
||||
# Keeping your fork in sync
|
||||
|
||||
Now that you have a fork of Forem's source code, there is work you will need to
|
||||
do to keep it updated.
|
||||
|
||||
## Setup your upstream
|
||||
|
||||
Inside your Forem directory, add a remote to the official Forem repo:
|
||||
|
||||
```shell
|
||||
git remote add upstream https://github.com/forem/forem.git
|
||||
```
|
||||
|
||||
## Rebasing from upstream
|
||||
|
||||
Do this prior to creating each branch for a PR:
|
||||
|
||||
Make sure you are on the main branch:
|
||||
|
||||
```shell
|
||||
$ git status
|
||||
On branch main
|
||||
Your branch is up-to-date with 'origin/main'.
|
||||
```
|
||||
|
||||
If you aren't on `main`, finish your work and checkout the `main` branch:
|
||||
|
||||
```shell
|
||||
git checkout main
|
||||
```
|
||||
|
||||
Do a pull with rebase against `upstream`:
|
||||
|
||||
```shell
|
||||
git pull --rebase upstream main
|
||||
```
|
||||
|
||||
This will pull down all of the changes to the official `main` branch, without
|
||||
making an additional commit in your local repo.
|
||||
|
||||
(Optional) Force push your updated `main` branch to your GitHub fork
|
||||
|
||||
```shell
|
||||
git push origin main --force
|
||||
```
|
||||
|
||||
This will overwrite the `main` branch of your fork.
|
||||
|
||||
## Keeping your branch up to date
|
||||
|
||||
Sometimes, your forked branch may get out of date. To get it up to date it,
|
||||
carry out the following steps:
|
||||
|
||||
Rebase from upstream once again:
|
||||
|
||||
```shell
|
||||
git checkout main
|
||||
git pull --rebase upstream main
|
||||
```
|
||||
|
||||
Checkout your feature branch locally and merge main back into your branch:
|
||||
|
||||
```shell
|
||||
git checkout <feature-branch-name>
|
||||
git merge main
|
||||
```
|
||||
|
||||
Merge any conflicts in editor if necessary:
|
||||
|
||||
```shell
|
||||
git commit -m "Fix merge conflicts"
|
||||
```
|
||||
|
||||
Push the changes back to your origin feature branch:
|
||||
|
||||
```shell
|
||||
git push origin <feature-branch-name>
|
||||
```
|
||||
|
||||
After you've fetched new commits from upstream, run `./bin/setup`, and it will
|
||||
install new gems, npm packages, update database, and restart Rails server.
|
||||
|
||||
## Additional resources
|
||||
|
||||
- [Syncing a fork](https://help.github.com/articles/syncing-a-fork/)
|
||||
|
|
@ -1,55 +0,0 @@
|
|||
---
|
||||
title: Suggested Workflow
|
||||
---
|
||||
|
||||
# Suggested workflow
|
||||
|
||||
We use [Spring](https://github.com/rails/spring), and it is already included in
|
||||
the project.
|
||||
|
||||
1. Use the provided bin stubs to automatically start Spring, i.e.
|
||||
`bin/rails server`, `bin/rspec spec/models/`, `bin/rails db:migrate`.
|
||||
1. If Spring isn't picking up on new changes, use `spring stop`. For example,
|
||||
Spring should always be restarted if there's a change in the environment
|
||||
keys.
|
||||
1. Check Spring's status whenever with `spring status`.
|
||||
|
||||
Caveat: `bin/rspec` is not equipped with Spring because it affects Simplecov's
|
||||
result. Instead, use `bin/spring rspec`.
|
||||
|
||||
## Synchronizing a fork with upstream / integrate latest changes
|
||||
|
||||
When changes in the upstream repository happen, the fork does not get those
|
||||
automatically and this is by design. To integrate the changes to the upstream
|
||||
repo that were committed since you cloned your fork or synced the last time, use
|
||||
the following script: `./scripts/sync_fork.sh` This will fetch the changes and
|
||||
merge them into your current workspace.
|
||||
|
||||
Use this:
|
||||
|
||||
- to get commits from upstream main into your branch
|
||||
- to sync with latest changes from upstream main before continuing with a new
|
||||
feature on your current branch
|
||||
|
||||
After you've fetched new commits from upstream, run `./bin/setup`, and it will
|
||||
install new gems, npm packages, update database, and restart Rails server.
|
||||
|
||||
Start the app by running `./bin/startup`, if it's not already running.
|
||||
|
||||
## Start over / discard all your changes
|
||||
|
||||
Sometimes it is neccesarry to start over from the beginning or reset the current
|
||||
workspace to the state of the upstream branch. Use the helper
|
||||
`./scripts/clean_fork.sh` to set your fork to the exact same state as the
|
||||
upstream main branch.
|
||||
|
||||
Use this:
|
||||
|
||||
- before working on a new feature
|
||||
- before creating a new branch to make sure to have all the latest changes in
|
||||
your fork also.
|
||||
|
||||
After you've done that, run `./bin/setup`, and it will update gems, npm
|
||||
packages, update database, and restart Rails server.
|
||||
|
||||
Start the app by running `./bin/startup`, if it's not already running.
|
||||
|
|
@ -1,121 +0,0 @@
|
|||
---
|
||||
title: Containers
|
||||
---
|
||||
|
||||
# Installing Forem using Containers
|
||||
|
||||
If you encounter any errors with our Container setup, please kindly
|
||||
[report any issues](https://github.com/forem/forem/issues/new/choose)!
|
||||
|
||||
## Installing prerequisites
|
||||
|
||||
_These prerequisites assume you're working on an operating system supported by
|
||||
Docker or Podman._
|
||||
|
||||
### Choosing a Container Engine
|
||||
|
||||
A container engine is software that runs and manages containers on a computer.
|
||||
One of the most widely known Container Engines is Docker, but there are many
|
||||
other Container Engines available, such as [Podman](https://podman.io/),
|
||||
[CRI-O](https://cri-o.io/), and
|
||||
[LXD](https://linuxcontainers.org/lxd/introduction/).
|
||||
|
||||
Forem supports two Container Engines: Docker and Podman.
|
||||
|
||||
### Docker
|
||||
|
||||
Forem can be setup with Docker and Docker Compose on macOS or Linux systems.
|
||||
|
||||
Docker is available for many different operating systems. You may use Docker as
|
||||
your Container Engine on both macOS and Linux workstations. As of right now
|
||||
Docker is the only Container Engine for macOS and we recommend you follow the
|
||||
[Docker Desktop on Mac](https://docs.docker.com/docker-for-mac/install/),
|
||||
install instructions to get Docker and Docker Compose installed.
|
||||
|
||||
Docker also works well on Linux distributions that have not moved to cgroup v2.
|
||||
You can install it by following their
|
||||
[Installation per distro](https://docs.docker.com/engine/install/) to get Docker
|
||||
and you can install Docker Compose by following these
|
||||
[instructions](https://docs.docker.com/compose/install/).
|
||||
|
||||
### Podman
|
||||
|
||||
Forem can be setup with Podman and Podman Compose on Linux systems.
|
||||
|
||||
[Podman](https://podman.io/) is an FOSS project that provides a Container Engine
|
||||
that is daemonless which only runs on Linux systems. It can be run as the root
|
||||
user or as a non-privileged user. It also provides a Docker-compatible command
|
||||
line interface. Podman is available on many different Linux distributions and it
|
||||
can be installed by following these
|
||||
[instructions](https://podman.io/getting-started/installation).
|
||||
|
||||
[Podman Compose](https://github.com/containers/podman-compose) is a an early
|
||||
project under development that is implementing docker-compose like experience
|
||||
with Podman. You can install it by following these
|
||||
[instructions](https://github.com/containers/podman-compose#installation).
|
||||
|
||||
## Setting up Forem
|
||||
|
||||
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
|
||||
1. Clone your forked repository, eg.
|
||||
`git clone https://github.com/<your-username>/forem.git`
|
||||
1. Set up your environment variables/secrets
|
||||
|
||||
1. Create `.env` by copying from the provided template`.env_sample` (i.e.
|
||||
with bash: `cp .env_sample .env`).
|
||||
|
||||
- `.env` is a personal file that is **ignored in git**.
|
||||
- `.env` lists all the `ENV` variables we use and provides a fake
|
||||
default for any missing keys.
|
||||
|
||||
2. For any key that you wish to enter/replace:
|
||||
|
||||
- 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.
|
||||
- The [backend guide](/backend) will show you how to get free API keys
|
||||
for additional services that may be required to run certain parts of
|
||||
the app.
|
||||
- Obtain the development variable and apply the key you wish to
|
||||
enter/replace. i.e.:
|
||||
|
||||
```shell
|
||||
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
|
||||
```
|
||||
|
||||
## Running Forem with Docker via docker-compose
|
||||
|
||||
1. Run `bin/container-setup`
|
||||
2. That's it! Navigate to <http://localhost:3000>
|
||||
|
||||
The script executes the following steps:
|
||||
|
||||
1. `docker-compose build`
|
||||
2. `docker-compose up`
|
||||
|
||||
## Running Forem with Podman via podman-compose
|
||||
|
||||
1. Run `bin/container-setup`
|
||||
2. That's it! Navigate to <http://localhost:3000>
|
||||
|
||||
The script executes the following steps:
|
||||
|
||||
1. `podman-compose build`
|
||||
2. `podman-compose up`
|
||||
|
||||
## Known Problems & Solutions
|
||||
|
||||
### Docker on Mac
|
||||
|
||||
- In case `rails server` starts with the following message:
|
||||
|
||||
```shell
|
||||
Data update scripts need to be run before you can start the application. Please run rails data_updates:run (RuntimeError)
|
||||
```
|
||||
|
||||
run the following command:
|
||||
|
||||
```shell
|
||||
docker-compose run rails rails data_updates:run
|
||||
```
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
---
|
||||
title: GitPod
|
||||
---
|
||||
|
||||
# GitPod
|
||||
|
||||
If you prefer working on a cloud IDE, you can spin up your own instance of Forem
|
||||
in the cloud, using [GitPod](https://gitpod.io/#https://github.com/forem/forem).
|
||||
|
||||
[](https://gitpod.io/#https://github.com/forem/forem)
|
||||
|
||||
Give it a couple of minutes to prepare the workspace. Wait for 'Awaiting port 3000... ok' message in the terminal.
|
||||
|
||||
You can now code, review, or just try out the project. Once you are done, simply close the tab.
|
||||
|
||||
You can find more information about this,
|
||||
[in a Forem's article](https://dev.to/ben/spin-up-a-local-instance-of-dev-in-the-cloud-with-gitpod-it-s-incredibly-simple-pij).
|
||||
|
|
@ -1,75 +0,0 @@
|
|||
---
|
||||
title: Imgproxy
|
||||
---
|
||||
|
||||
# Imgproxy
|
||||
|
||||
Imgproxy is a standalone server for resizing images. It is optional and you do
|
||||
not need it to start Forem locally. We are currently only using it in Forem
|
||||
Cloud.
|
||||
|
||||
## Installation
|
||||
|
||||
- MacOS: install via homebrew with `brew install imgproxy`.
|
||||
- Windows: please install
|
||||
[via docker](https://docs.imgproxy.net/#/installation?id=docker).
|
||||
- Linux: you can either use
|
||||
[docker installation](https://docs.imgproxy.net/#/installation?id=docker) or
|
||||
[build from source](https://docs.imgproxy.net/#/installation?id=from-the-source).
|
||||
|
||||
For more options not covered here, please take a look at the
|
||||
[official installation documentation](https://docs.imgproxy.net/#/installation).
|
||||
|
||||
## Usage
|
||||
|
||||
1. Generate a key/salt pair by running the following in your terminal twice.
|
||||
Copy those values to your `.env` in the next step
|
||||
|
||||
```
|
||||
echo $(xxd -g 2 -l 64 -p /dev/random | tr -d '\n')
|
||||
```
|
||||
|
||||
1. In your `.env`, add the following.
|
||||
|
||||
```
|
||||
export IMGPROXY_ENDPOINT='http://localhost:8080'
|
||||
export IMGPROXY_KEY='1b1c9aae804e070b0864f2547fba7ce8ff31bf7..........'
|
||||
export IMGPROXY_SALT='8c6d449d4fc2cada5bab538826cae709d2ade9f.........'
|
||||
```
|
||||
|
||||
1. Start the Forem app server normally.
|
||||
|
||||
1. Start Imgproxy in a terminal with the key and salt.
|
||||
|
||||
```
|
||||
# If you installed via homebrew or using the binary.
|
||||
> IMGPROXY_KEY='your key' IMGPROXY_SALT='your salt' imgproxy
|
||||
|
||||
# If you are using Docker or Podman. The commands are identical for both
|
||||
> docker run -p 8080:8080 \
|
||||
-e IMGPROXY_KEY="your key" \
|
||||
-e IMGPROXY_SALT="your salt" \
|
||||
-it darthsim/imgproxy
|
||||
```
|
||||
|
||||
1. That's it :)
|
||||
|
||||
You should verify it's working by starting the Forem app locally and see that
|
||||
each image is loaded properly, or run the following command while the Forem app
|
||||
is running:
|
||||
|
||||
```
|
||||
> curl -I http://localhost:8080/unsafe/aHR0cDovL2xvY2Fs/aG9zdDozMDAwL2Fz/c2V0cy8xLnBuZw
|
||||
|
||||
HTTP/1.1 200 OK
|
||||
Server: imgproxy
|
||||
X-Request-Id: GYvCGXb98JUwL3ujwpjzh
|
||||
Date: Tue, 27 Oct 2020 16:11:37 GMT
|
||||
```
|
||||
|
||||
## Sidenote
|
||||
|
||||
- Because Imgproxy is a standalone server of its own, all image URLs given to it
|
||||
need to be absolute URLs.
|
||||
- When working with Docker or Podman on Linux, provide the host network option
|
||||
(`--network="host"`) so Imgproxy can properly access the localhost.
|
||||
|
|
@ -1,115 +0,0 @@
|
|||
---
|
||||
title: Linux
|
||||
---
|
||||
|
||||
# Installing Forem on Linux
|
||||
|
||||
## Installing prerequisites
|
||||
|
||||
_These prerequisites assume you're working on a Linux-based operating system,
|
||||
but they have only been tested on Ubuntu 18.04._
|
||||
|
||||
### Ruby
|
||||
|
||||
1. If you don't already have a Ruby version manager, we highly recommend
|
||||
[rbenv](https://github.com/rbenv/rbenv). Please follow their
|
||||
[installation guide](https://github.com/rbenv/rbenv#installation).
|
||||
1. With the Ruby version manager, install the Ruby version listed on our badge.
|
||||
(ie with rbenv: `rbenv install $(cat .ruby-version)`)
|
||||
|
||||
For very detailed rbenv installation directions on several distros, please visit
|
||||
[DigitalOcean's guide](https://www.digitalocean.com/community/tutorials/how-to-install-ruby-on-rails-with-rbenv-on-ubuntu-18-04).
|
||||
|
||||
### Yarn
|
||||
|
||||
There are two ways to install Yarn.
|
||||
|
||||
- Yarn's official
|
||||
[installation guide](https://yarnpkg.com/en/docs/install#debian-stable)
|
||||
(recommended).
|
||||
- [DigitalOcean's detailed tutorial](https://www.digitalocean.com/community/tutorials/how-to-install-node-js-on-ubuntu-18-04)
|
||||
describes how to install
|
||||
[Node version Manager](https://github.com/creationix/nvm). By installing NVM
|
||||
you can select a Node version (we recommend either LTS or current); the guide
|
||||
will also explain how to install NPM. This way you'll have Node, NPM, and then
|
||||
you can run `npm install -g yarn` to install Yarn.
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
Forem requires PostgreSQL version 11 or higher.
|
||||
|
||||
1. Run
|
||||
`sudo apt update && sudo apt install postgresql postgresql-contrib libpq-dev`.
|
||||
1. To test the installation you can run `sudo -u postgres psql` which should
|
||||
open a PostgreSQL prompt. Exit the prompt by running `\q` then run
|
||||
`sudo -u postgres createuser -s $YOUR_USERNAME` where `$YOUR_USERNAME` is the
|
||||
username you are currently logged in as.
|
||||
|
||||
There are more than one ways to setup PostgreSQL. For additional configuration,
|
||||
check out our [PostgreSQL setup guide](/installation/postgresql) or the official
|
||||
[PostgreSQL](https://www.postgresql.org/) site for further information.
|
||||
|
||||
### ImageMagick
|
||||
|
||||
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
|
||||
upload.
|
||||
|
||||
Please refer to ImageMagick's
|
||||
[instructions](https://imagemagick.org/script/download.php) on how to install
|
||||
it.
|
||||
|
||||
### Redis
|
||||
|
||||
Forem requires Redis version 6.0 or higher.
|
||||
|
||||
We recommend following Digital Ocean's extensive guides
|
||||
[How to Install and Secure Redis](https://www.digitalocean.com/community/tutorial_collections/how-to-install-and-secure-redis)
|
||||
to setup Redis.
|
||||
|
||||
## Installing Forem
|
||||
|
||||
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
|
||||
1. Clone your forked repository, e.g.
|
||||
`git clone https://github.com/<your-username>/forem.git`
|
||||
1. Install bundler with `gem install bundler`
|
||||
1. Set up your environment variables/secrets
|
||||
|
||||
- Take a look at `.env_sample`. This file lists all the `ENV` variables we
|
||||
use and provides a fake default for any missing keys.
|
||||
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
|
||||
variable to the remote computer's domain name.
|
||||
- The [backend guide](/backend) will show you how to get free API keys for
|
||||
additional services that may be required to run certain parts of the app.
|
||||
- For any key that you wish to enter/replace:
|
||||
|
||||
1. Create `.env` by copying from the provided template (i.e. with bash:
|
||||
`cp .env_sample .env`). This is a personal file that is ignored in git.
|
||||
1. Obtain the development variable and apply the key you wish to
|
||||
enter/replace. i.e.:
|
||||
|
||||
```shell
|
||||
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
|
||||
```
|
||||
|
||||
- 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.
|
||||
|
||||
1. Run `bin/setup`
|
||||
|
||||
### Possible error messages
|
||||
|
||||
While installing, you might run into an error due to the `pg` gem requiring
|
||||
PostgreSQL libraries. If so, please run `sudo apt-get install libpq-dev` before
|
||||
retrying.
|
||||
|
||||
While installing, you might run into an error due to the `sass-rails` gem
|
||||
requiring `sassc`, which requires the `g++` compiler. If so, please run
|
||||
`sudo apt-get install g++` before retrying.
|
||||
|
||||
While installing, if you didn't install `node` or `nvm` manually, you might run
|
||||
into an error due to an older system node version being present, which can cause
|
||||
issues while `yarn` is installing packages. If so, you'll need to
|
||||
[install `nvm`](https://github.com/nvm-sh/nvm#installation-and-update) and then
|
||||
run `nvm install node` to get the most recent node version before retrying.
|
||||
|
|
@ -1,209 +0,0 @@
|
|||
---
|
||||
title: macOS
|
||||
---
|
||||
|
||||
# Installing Forem on macOS
|
||||
|
||||
## Installing prerequisites
|
||||
|
||||
### Ruby
|
||||
|
||||
1. **Note:** MacOS ships with a version of Ruby, needed for various operating
|
||||
systems. To avoid causing an issue with your operating system you should use
|
||||
a version manager for Ruby.
|
||||
|
||||
If you don't already have a Ruby version manager, we highly recommend
|
||||
[rbenv](https://github.com/rbenv/rbenv). This will allow you to have
|
||||
different versions running on a per project basis. The MacOS system version
|
||||
of Ruby will stay intact while giving you the ability to use the version
|
||||
needed for this Forem project. Please follow their
|
||||
[installation guide](https://github.com/rbenv/rbenv#installation).
|
||||
|
||||
2. With the Ruby version manager, install the Ruby version listed on our badge.
|
||||
(i.e. with rbenv: `rbenv install $(cat .ruby-version)`)
|
||||
|
||||
**Note:** The repository must be forked and cloned before running the
|
||||
`rbenv install $(cat .ruby-version)` command.
|
||||
|
||||
### Yarn
|
||||
|
||||
Please refer to their [installation guide](https://yarnpkg.com/en/docs/install).
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
Forem requires PostgreSQL version 11 or higher to run.
|
||||
|
||||
The easiest way to get started is to use
|
||||
[Postgres.app](https://postgresapp.com/). Alternatively, check out the official
|
||||
[PostgreSQL](https://www.postgresql.org/) site for more installation options.
|
||||
|
||||
For additional configuration options, check our
|
||||
[PostgreSQL setup guide](/installation/postgresql).
|
||||
|
||||
### ImageMagick
|
||||
|
||||
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
|
||||
upload.
|
||||
|
||||
You can install ImageMagick with `brew install imagemagick`.
|
||||
|
||||
### Redis
|
||||
|
||||
Forem requires Redis version 6.0 or higher to run.
|
||||
|
||||
We recommend using [Homebrew](https://brew.sh):
|
||||
|
||||
```shell
|
||||
brew install redis
|
||||
```
|
||||
|
||||
you can follow the post installation instructions, we recommend using
|
||||
`brew services` to start Redis in the background:
|
||||
|
||||
```shell
|
||||
brew services start redis
|
||||
```
|
||||
|
||||
You can test if it's up and running by issuing the following command:
|
||||
|
||||
```shell
|
||||
redis-cli ping
|
||||
```
|
||||
|
||||
## Installing Forem
|
||||
|
||||
1. Fork Forem's repository, e.g. <https://github.com/forem/forem/fork>
|
||||
2. Clone your forked repository in one of two ways:
|
||||
|
||||
- e.g. with HTTPS: `git clone https://github.com/<your-username>/forem.git`
|
||||
- e.g. with SSH: `git clone git@github.com:<your-username>/forem.git`
|
||||
|
||||
3. Install bundler with `gem install bundler`
|
||||
4. Set up your environment variables/secrets
|
||||
|
||||
- Take a look at `.env_sample` to see all the `ENV` variables we use and the
|
||||
fake default provided for any missing keys.
|
||||
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
|
||||
variable to the remote computer's domain name.
|
||||
- The [backend guide](/backend) will show you how to get free API keys for
|
||||
additional services that may be required to run certain parts of the app.
|
||||
- For any key that you wish to enter/replace, follow the steps below.
|
||||
|
||||
1. Create `.env` by copying from the provided template (i.e. with bash:
|
||||
`cp .env_sample .env`). This is a personal file that is ignored in git.
|
||||
2. Obtain the development variable and apply the key you wish to
|
||||
enter/replace. i.e.:
|
||||
|
||||
```shell
|
||||
export CLOUDINARY_API_KEY="SOME_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_API_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
|
||||
export CLOUDINARY_CLOUD_NAME="A_CLOUDINARY_NAME"
|
||||
```
|
||||
|
||||
- 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`
|
||||
|
||||
### Possible error messages
|
||||
|
||||
**Error:** `rbenv install hangs at ruby-build: using readline from homebrew`
|
||||
|
||||
**_Solution:_**
|
||||
[Stackoverflow answer](https://stackoverflow.com/questions/63599818/rbenv-install-hangs-at-ruby-build-using-readline-from-homebrew)
|
||||
`RUBY_CONFIGURE_OPTS=--with-readline-dir="$(brew --prefix readline)" rbenv install 2.0.0`
|
||||
|
||||
**Error:**
|
||||
`__NSPlaceholderDate initialize] may have been in progress in another thread when fork() was called`
|
||||
|
||||
**_Solution:_** Run the command `export OBJC_DISABLE_INITIALIZE_FORK_SAFETY=YES`
|
||||
(or `set -x OBJC_DISABLE_INITIALIZE_FORK_SAFETY YES` in fish shell)
|
||||
|
||||
---
|
||||
|
||||
**Error:** `User does not have CONNECT privilege.`
|
||||
|
||||
**_Solution:_** Complete the steps outlined in the
|
||||
[PostgreSQL setup guide](/installation/postgresql).
|
||||
|
||||
---
|
||||
|
||||
**Error:**
|
||||
`rbenv: version '<version number>' is not installed (set by /Path/To/Local/Repository/.ruby-version)`
|
||||
|
||||
**_Solution:_** Run the command `rbenv install <version number>`
|
||||
|
||||
---
|
||||
|
||||
**Error:** `ruby-build: definition not found: <version number>` when `rbenv` was
|
||||
installed via `brew`.
|
||||
|
||||
```shell
|
||||
ruby-build: definition not found: <version number>
|
||||
|
||||
See all available versions with `rbenv install --list`.
|
||||
If the version you need is missing, try upgrading ruby-build:
|
||||
```
|
||||
|
||||
**_Solution:_** Run the following to update `ruby-build`,
|
||||
`brew update && brew upgrade ruby-build`. After that, rerun
|
||||
`rbenv install <version number>` and that version will get installed.
|
||||
|
||||
---
|
||||
|
||||
**Error:**
|
||||
|
||||
```shell
|
||||
== Preparing database ==
|
||||
Sorry, you can't use byebug without Readline. To solve this, you need to
|
||||
rebuild Ruby with Readline support. If using Ubuntu, try `sudo apt-get
|
||||
install libreadline-dev` and then reinstall your Ruby.
|
||||
rails aborted!
|
||||
LoadError: dlopen(/Users/<username>/.rbenv/versions/2.6.5/lib/ruby/2.6.0/x86_64-darwin18/readline.bundle, 9): Library not loaded: /usr/local/opt/readline/lib/libreadline.<some version number>.dylib
|
||||
```
|
||||
|
||||
**_Solution:_** Run
|
||||
`ln -s /usr/local/opt/readline/lib/libreadline.dylib /usr/local/opt/readline/lib/libreadline.<some version number>.dylib`
|
||||
from the command line then run `bin/setup` again. You may have a different
|
||||
version of libreadline, so replace `<some version number>` with the version that
|
||||
errored.
|
||||
|
||||
---
|
||||
|
||||
**Error:**
|
||||
|
||||
```shell
|
||||
PG::Error: ERROR: invalid value for parameter "TimeZone": "UTC"
|
||||
: SET time zone 'UTC'
|
||||
```
|
||||
|
||||
**_Solution:_** Restart your Postgres.app, or, if you installed PostgreSQL with
|
||||
Homebrew, restart with:
|
||||
|
||||
```shell
|
||||
brew services restart postgresql
|
||||
```
|
||||
|
||||
If that doesn't work, reboot your Mac.
|
||||
|
||||
---
|
||||
|
||||
**Error:**
|
||||
|
||||
```shell
|
||||
ERROR: Error installing pg:
|
||||
ERROR: Failed to build gem native extension.
|
||||
[...]
|
||||
Can't find the 'libpq-fe.h header
|
||||
*** extconf.rb failed ***
|
||||
```
|
||||
|
||||
**_Solution:_** You may encounter this when installing PostgreSQL with the
|
||||
Postgres.app. Try restarting the app and reinitializing the database. If that
|
||||
doesn't work, install PostgreSQL with Homebrew instead:
|
||||
`brew install postgresql`
|
||||
|
||||
---
|
||||
|
||||
> If you encountered any errors that you subsequently resolved, **please
|
||||
> consider updating this section** with your errors and their solutions.
|
||||
|
|
@ -1,23 +0,0 @@
|
|||
---
|
||||
title: Other Tools
|
||||
---
|
||||
|
||||
# Miscellaneous
|
||||
|
||||
## Foreman
|
||||
|
||||
We use [Foreman](https://github.com/ddollar/foreman) to manage our application
|
||||
through `Procfile` and `Procfile.dev`. As the
|
||||
[documentation](https://github.com/ddollar/foreman/blob/master/README.md) points
|
||||
out,
|
||||
|
||||
> Ruby users should take care _not_ to install foreman in their project's
|
||||
> `Gemfile`. See this
|
||||
> [wiki article](https://github.com/ddollar/foreman/wiki/Don't-Bundle-Foreman)
|
||||
> for more details.
|
||||
|
||||
Instead install Foreman globally with the following command:
|
||||
|
||||
```sh
|
||||
$ gem install foreman
|
||||
```
|
||||
|
|
@ -1,71 +0,0 @@
|
|||
---
|
||||
title: PostgreSQL
|
||||
---
|
||||
|
||||
# Setup your application with PostgreSQL
|
||||
|
||||
Follow the instructions in the installation guide below that corresponds to your
|
||||
operating system:
|
||||
|
||||
1. macOS
|
||||
- [Postgres.app](https://postgresapp.com/): PostgreSQL installation as a Mac
|
||||
app
|
||||
- [Homebrew](https://brew.sh/): if you use Homebrew you can easily install
|
||||
PostgreSQL with `brew install postgresql`
|
||||
1. Linux (Ubuntu)
|
||||
- [Ubuntu `14.04`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-14-04)
|
||||
- [Ubuntu `16.04 and higher`](https://www.digitalocean.com/community/tutorials/how-to-install-and-use-postgresql-on-ubuntu-16-04)
|
||||
1. [Windows](https://www.postgresql.org/download/windows/)
|
||||
|
||||
_You can find all installation options for a variety of operating systems
|
||||
[on the official PostgreSQL download page](https://www.postgresql.org/download/)_.
|
||||
|
||||
## Configuration
|
||||
|
||||
By default, the application is configured to connect to a local database named
|
||||
`PracticalDeveloper_development`. If you need to specify a username and a
|
||||
password, you can go about it by using the environment variable `DATABASE_URL`
|
||||
with a connection string.
|
||||
|
||||
The
|
||||
[official Rails guides](https://guides.rubyonrails.org/configuring.html#connection-preference)
|
||||
go into depth on how Rails merges the existing `database.yml` with the
|
||||
connection string.
|
||||
|
||||
### Setup `DATABASE_URL` in .env
|
||||
|
||||
1. Open your `.env`
|
||||
|
||||
1. Add the following:
|
||||
|
||||
```shell
|
||||
export DATABASE_URL=postgresql://USERNAME:PASSWORD@localhost
|
||||
|
||||
# Optional: If your test database is in a different url, be sure to set this.
|
||||
export DATABASE_URL_TEST=postgresql://USERNAME:PASSWORD@localhost
|
||||
```
|
||||
|
||||
1. Replace `USERNAME` with your database username, `PASSWORD` with your database
|
||||
password.
|
||||
|
||||
You can find more details on connection strings in
|
||||
[PostgreSQL's own documentation](https://www.postgresql.org/docs/10/static/libpq-connect.html#LIBPQ-CONNSTRING).
|
||||
|
||||
NOTE: due to how Rails merges `database.yml` and `DATABASE_URL` it's recommended
|
||||
not to add the database name in the connection string. This will default to your
|
||||
development database name also during tests, which will effectively empty the
|
||||
development DB each time tests are run.
|
||||
|
||||
## Connection Pooling
|
||||
|
||||
We use [PgBouncer](http://www.pgbouncer.org) to manage connection pooling.
|
||||
|
||||
Database pooling creates a shared pool of connections to our database, rather
|
||||
than creating new connection each time. PgBouncer is a wrapper around our
|
||||
database connection and ensures that we only have a finite set of "cached"
|
||||
connections to the database. This means that our app doesn't need to actually
|
||||
connect to the database, it only needs to connect to the pool of connections.
|
||||
The number of connections to the database (the connection limit) is dependent on
|
||||
our
|
||||
[Heroku Postgres plan](https://devcenter.heroku.com/articles/heroku-postgres-plans).
|
||||
PgBouncer ensures that we do not exceed our plan's connection limit.
|
||||
|
|
@ -1,45 +0,0 @@
|
|||
---
|
||||
items:
|
||||
- mac.md
|
||||
- windows.md
|
||||
- linux.md
|
||||
- containers.md
|
||||
- gitpod.md
|
||||
- postgresql.md
|
||||
- imgproxy.md
|
||||
- vault.md
|
||||
- others.md
|
||||
---
|
||||
|
||||
# Installation Guide
|
||||
|
||||
There are many different ways you can install the **development environment** so
|
||||
you can help contribute code to Forem.
|
||||
|
||||
## Installing locally
|
||||
|
||||
You can install Forem to your local machine and we have instructions for
|
||||
[Mac](/installation/mac), [Windows](/installation/windows) and
|
||||
[Linux](/installation/linux).
|
||||
|
||||
## Running with containers
|
||||
|
||||
Installing to your local machine can be troublesome for many reasons such as a
|
||||
conflicting database and runtime versions.
|
||||
|
||||
Another way you can get a development environment up and running is with
|
||||
containers. They will set up everything you need in an isolated environment, and
|
||||
you need not worry about the details of setting everything up locally. We have
|
||||
instructions for getting set up with [containers](/installation/containers)
|
||||
quickly.
|
||||
|
||||
## GitPod _- beginner friendly!_
|
||||
|
||||
If you are having trouble with local installation and Docker confuses you, then
|
||||
you can launch a **development environment** with the press of a button with
|
||||
GitPod.
|
||||
|
||||
GitPod is an IDE in the cloud which is also attached to a web server. It will
|
||||
install everything you need. If you are comfortable doing everything from a web
|
||||
browser, then we have instructions for installing with
|
||||
[GitPod](/installation/gitpod) as well.
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
---
|
||||
title: Vault
|
||||
---
|
||||
|
||||
## Installing
|
||||
|
||||
To install Vault follow the
|
||||
[Vault installation](https://www.vaultproject.io/docs/install) instructions. If
|
||||
you have a Mac, you can install
|
||||
[Vault using Homebrew](https://formulae.brew.sh/formula/vault). You can validate
|
||||
that you have it installed by running the following command.
|
||||
|
||||
```bash
|
||||
vault -h
|
||||
```
|
||||
|
||||
After Vault has been installed, the next step is to start up a Vault server.
|
||||
|
||||
```
|
||||
vault server -dev
|
||||
```
|
||||
|
||||
This will give you an output that looks like this:
|
||||
|
||||
```
|
||||
==> Vault server configuration:
|
||||
|
||||
Api Address: http://127.0.0.1:8200
|
||||
Cgo: disabled
|
||||
Cluster Address: https://127.0.0.1:8201
|
||||
Listener 1: tcp (addr: "127.0.0.1:8200", cluster address: "127.0.0.1:8201", max_request_duration: "1m30s", max_request_size: "33554432", tls: "disabled")
|
||||
Log Level: info
|
||||
Mlock: supported: false, enabled: false
|
||||
Recovery Mode: false
|
||||
Storage: inmem
|
||||
Version: Vault v1.4.2
|
||||
Version Sha: 18f1c494be8b06788c2fdda1a4296eb3c4b174ce+CHANGES
|
||||
|
||||
WARNING! dev mode is enabled! In this mode, Vault runs entirely in-memory
|
||||
and starts unsealed with a single unseal key. The root token is already
|
||||
authenticated to the CLI, so you can immediately begin using Vault.
|
||||
|
||||
You may need to set the following environment variable:
|
||||
|
||||
$ export VAULT_ADDR='http://127.0.0.1:8200'
|
||||
|
||||
The unseal key and root token are displayed below in case you want to
|
||||
seal/unseal the Vault or re-authenticate.
|
||||
|
||||
Unseal Key: <a long key>
|
||||
Root Token: <a token>
|
||||
|
||||
Development mode should NOT be used in production installations!
|
||||
```
|
||||
|
||||
## Configuring
|
||||
|
||||
Once Vault is installed and running, the next step is to configure it to work
|
||||
with your application. Since we want multiple applications (in production) to be
|
||||
able to use the same Vault infrastructure, we control access with
|
||||
[Vault policies](https://www.vaultproject.io/docs/concepts/policies) and
|
||||
different
|
||||
[key/value secret paths](https://www.vaultproject.io/docs/secrets/kv/kv-v2#setup).
|
||||
In the following examples, `VAULT_SECRET_NAMESPACE` will be the secret path
|
||||
where your secrets are stored. This can be any string (except "secrets" since
|
||||
that exists by default) of your choosing, for example `local-secrets`.
|
||||
`VAULT_POLICY_NAME` will be the name of the policy that we use to control access
|
||||
to `VAULT_SECRET_NAMESPACE`. Once again, this should be a string. For example,
|
||||
`local-policy`.
|
||||
|
||||
To set up a policy and secret path run the following commands:
|
||||
|
||||
```
|
||||
vault secrets enable -path=<VAULT_SECRET_NAMESPACE>/ kv-v2
|
||||
|
||||
vault policy write VAULT_POLICY_NAME -<<EOF
|
||||
# grant permission to new VAULT_SECRET_NAMESPACE path
|
||||
path "VAULT_SECRET_NAMESPACE/data/*" {
|
||||
capabilities = ["create", "update", "read"]
|
||||
}
|
||||
EOF
|
||||
|
||||
vault token create -policy=VAULT_POLICY_NAME
|
||||
```
|
||||
|
||||
The final command will give you the following output:
|
||||
|
||||
```
|
||||
# Key Value
|
||||
# --- -----
|
||||
# token important-policy-token
|
||||
# token_accessor another-less-important-token
|
||||
# token_duration 768h
|
||||
# token_renewable true
|
||||
# token_policies ["default" VAULT_POLICY_NAME]
|
||||
# identity_policies []
|
||||
# policies ["default" VAULT_POLICY_NAME]
|
||||
```
|
||||
|
||||
The `token` output from above is what you will use in your application to access
|
||||
Vault. All that is left to do is set the appropriate ENV variables in your
|
||||
`.env` file.
|
||||
|
||||
```shell
|
||||
export VAULT_TOKEN=important-policy-token export VAULT_SECRET_NAMESPACE=<your
|
||||
namespace from above>
|
||||
```
|
||||
|
||||
Restart your application to start using Vault. One easy way to see it in action
|
||||
is via the Rails console.
|
||||
|
||||
```ruby
|
||||
# Enabled Example
|
||||
[3] pry(main)> AppSecrets["TEST_SET"]="success"
|
||||
=> "success"
|
||||
[4] pry(main)> AppSecrets["TEST_SET"]
|
||||
=> "success"
|
||||
|
||||
# Disabled Example
|
||||
[2] pry(main)> AppSecrets["TEST_SET"]="success"
|
||||
Vault::MissingTokenError: Missing Vault token! I cannot make requests to Vault without a token. Please
|
||||
set a Vault token in the client:
|
||||
|
||||
Vault.token = "1234"
|
||||
|
||||
or authenticate with Vault using the Vault CLI:
|
||||
|
||||
$ vault auth ...
|
||||
|
||||
or set the environment variable $VAULT_TOKEN to the token value:
|
||||
|
||||
$ export VAULT_TOKEN="..."
|
||||
|
||||
Please refer to the documentation for more examples.
|
||||
```
|
||||
|
|
@ -1,297 +0,0 @@
|
|||
---
|
||||
title: Windows
|
||||
---
|
||||
|
||||
# Installing Forem on Windows 10
|
||||
|
||||
## Installing prerequisites
|
||||
|
||||
These prerequisites assume you're working on a `64-bit Windows 10` operating
|
||||
system machine
|
||||
|
||||
- updated to _version 1903, Build 18362_ or _higher_ for **x64 system**
|
||||
- updated to _version 2004, Build 19041_ or _higher_ for **ARM64 system**
|
||||
|
||||
For further information regarding system requirements, head over to the
|
||||
[Microsoft Docs](https://docs.microsoft.com/en-us/windows/wsl/install-win10#requirements)
|
||||
or
|
||||
[Microsoft's blog on extending WSL 2 Support](https://devblogs.microsoft.com/commandline/wsl-2-support-is-coming-to-windows-10-versions-1903-and-1909/).
|
||||
|
||||
To check your Windows version, press `Win Logo key` + `R`, type `winver`, then
|
||||
click OK.
|
||||
|
||||
There are other ways to get Forem running on lower versions, but we recommend a
|
||||
complete WSL 2 installation.
|
||||
|
||||
### Installing WSL
|
||||
|
||||
Since Forem's codebase is using the Ruby on Rails framework, we will need to
|
||||
install the Windows Subsystem for Linux (WSL). Some dependencies used by the
|
||||
source code triggered errors when installing on Windows, so using WSL allows you
|
||||
to work on the software and not having to fix gem incompatibilities.
|
||||
|
||||
Follow the instructions for
|
||||
[Installing the Windows Subsystem for Linux](https://docs.microsoft.com/en-us/windows/wsl/install-win10).
|
||||
Once you've installed WSL, complete all the instructions under the following
|
||||
sections in the link above:
|
||||
|
||||
1. [Update to WSL 2](https://docs.microsoft.com/en-us/windows/wsl/install-win10#update-to-wsl-2).
|
||||
2. [Enable the "Virtual Machine Platform" optional component](https://docs.microsoft.com/en-us/windows/wsl/install-win10#enable-the-virtual-machine-platform-optional-component).
|
||||
Be sure to reboot your machine after this step.
|
||||
3. [Set WSL 2 as your default version](https://docs.microsoft.com/en-us/windows/wsl/install-win10#set-wsl-2-as-your-default-version).
|
||||
|
||||
Once all the steps mentioned above are completed,
|
||||
[install Ubuntu 18.04 on Windows](https://www.microsoft.com/store/productId/9N9TNGVNDL3Q).
|
||||
|
||||
On your first run, the system will ask for username and password. Take note of
|
||||
both since it will be used for `sudo` commands. More information about the
|
||||
process can be found at
|
||||
[create a user account and password for your new Linux distribution](https://docs.microsoft.com/en-us/windows/wsl/user-support)
|
||||
|
||||
### Installing rbenv
|
||||
|
||||
`rbenv` is a version manager for Ruby applications which allows one to guarantee
|
||||
that the Ruby version in development environment matches production. First,
|
||||
install Ruby language dependencies before installing `rbenv`:
|
||||
|
||||
```shell
|
||||
sudo apt-get update
|
||||
sudo apt-get install git-core curl zlib1g-dev build-essential libssl-dev libreadline-dev libyaml-dev libsqlite3-dev sqlite3 libxml2-dev libxslt1-dev libcurl4-openssl-dev software-properties-common libffi-dev
|
||||
```
|
||||
|
||||
Now, we install [rbenv](https://github.com/rbenv/rbenv) using the following
|
||||
commands:
|
||||
|
||||
```shell
|
||||
cd
|
||||
git clone https://github.com/rbenv/rbenv.git ~/.rbenv
|
||||
echo 'export PATH="$HOME/.rbenv/bin:$PATH"' >> ~/.bashrc
|
||||
echo 'eval "$(rbenv init -)"' >> ~/.bashrc
|
||||
exec $SHELL
|
||||
|
||||
git clone https://github.com/rbenv/ruby-build.git ~/.rbenv/plugins/ruby-build
|
||||
echo 'export PATH="$HOME/.rbenv/plugins/ruby-build/bin:$PATH"' >> ~/.bashrc
|
||||
exec $SHELL
|
||||
```
|
||||
|
||||
One can verify `rbenv` installation using the `rbenv-doctor` script with the
|
||||
following commands:
|
||||
|
||||
```shell
|
||||
curl -fsSL https://raw.githubusercontent.com/rbenv/rbenv-installer/main/bin/rbenv-doctor | bash
|
||||
```
|
||||
|
||||
### Installing nvm
|
||||
|
||||
As a pre-requisite to install Rails, Node.js needs to be installed.
|
||||
[nvm](https://github.com/nvm-sh/nvm) is a Node.js version manager that helps a
|
||||
developer select a specific Node.js version for development.
|
||||
|
||||
To install `nvm`, follow the instructions outlined in the
|
||||
[official nvm documentation](https://github.com/nvm-sh/nvm#install--update-script).
|
||||
|
||||
Be sure to reload the shell with the command `exec $SHELL` after the
|
||||
installation is complete.
|
||||
|
||||
Run the following command to verify that `nvm` is installed:
|
||||
|
||||
```shell
|
||||
command -v nvm
|
||||
```
|
||||
|
||||
If the shell outputs `nvm`, the installation is successful. Installation of the
|
||||
correct Node.js version will be done in a later part of the installation
|
||||
process.
|
||||
|
||||
### Yarn
|
||||
|
||||
The fastest way to install Yarn for WSL would be from Debian package repository.
|
||||
Configure the repository with the following commands:
|
||||
|
||||
```shell
|
||||
curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | sudo apt-key add -
|
||||
echo "deb https://dl.yarnpkg.com/debian/ stable main" | sudo tee /etc/apt/sources.list.d/yarn.list
|
||||
```
|
||||
|
||||
Since we do not have Node.js installed yet, we will be installing Yarn without
|
||||
the default Node.js with the following command:
|
||||
|
||||
```shell
|
||||
sudo apt update && sudo apt install --no-install-recommends yarn
|
||||
```
|
||||
|
||||
To verify Yarn's installation, run the command `yarn -v`. It should print
|
||||
`Yarn requires Node.js 4.0 or higher to be installed.`. This indicates that the
|
||||
Yarn installation succeeded but Node.js still needs to be installed for it to
|
||||
work fully. We install Node.js later on in the installation process.
|
||||
|
||||
### PostgreSQL
|
||||
|
||||
Forem requires PostgreSQL version 11 or higher. To Install PostgreSQL on WSL,
|
||||
follow steps under the
|
||||
[PostgreSQL APT Repository](https://www.postgresql.org/download/linux/ubuntu/)
|
||||
section.
|
||||
|
||||
Once Installed, perform the following steps in order to set up a username and
|
||||
password for PostgreSQL:
|
||||
|
||||
1. Use `sudo -i service postgresql start` to start the server.
|
||||
2. Next, replace `$YOUR_USERNAME` in the following commands with your Linux
|
||||
Username and execute them:
|
||||
|
||||
```shell
|
||||
sudo -u postgres createuser -s $YOUR_USERNAME
|
||||
createdb
|
||||
sudo -u $YOUR_USERNAME psql
|
||||
```
|
||||
|
||||
3. You should now be in PostgreSQL's shell interface. Execute `\password` to set
|
||||
a password for your PostgreSQL user.
|
||||
4. Be sure to make a note of your username and password for future use. Exit
|
||||
PostgreSQL by executing the command `\quit`.
|
||||
|
||||
For additional configuration options, check our
|
||||
[PostgreSQL setup guide](/installation/postgresql).
|
||||
|
||||
### ImageMagick
|
||||
|
||||
Forem uses [ImageMagick](https://imagemagick.org/) to manipulate images on
|
||||
upload.
|
||||
|
||||
ImageMagick can be installed to WSL via installing its
|
||||
[imagemagick](https://packages.ubuntu.com/bionic/imagemagick) package with the
|
||||
following command:
|
||||
|
||||
```shell
|
||||
sudo apt update && sudo apt install imagemagick
|
||||
```
|
||||
|
||||
To verify its installation, run `identify -version` command.
|
||||
|
||||
### Redis
|
||||
|
||||
Forem requires Redis version 6.0 or higher.
|
||||
|
||||
We recommend to follow
|
||||
[this guide](https://redislabs.com/blog/redis-on-windows-10/) to run Redis under
|
||||
WSL.
|
||||
|
||||
## Installing Forem
|
||||
|
||||
1. Fork Forem's repository, eg. <https://github.com/forem/forem/fork>
|
||||
1. Clone your forked repository, eg.
|
||||
`git clone https://github.com/<your-username>/forem.git`
|
||||
1. Open the cloned forem folder in terminal with `cd forem`. Next, install Ruby
|
||||
with the following commands:
|
||||
|
||||
```shell
|
||||
rbenv install $(cat .ruby-version)
|
||||
rbenv global $(cat .ruby-version)
|
||||
ruby -v
|
||||
```
|
||||
|
||||
1. Install Node.js with the following set of commands:
|
||||
|
||||
```shell
|
||||
nvm install $(cat .nvmrc)
|
||||
nvm use $(cat .nvmrc)
|
||||
node -v
|
||||
yarn -v
|
||||
```
|
||||
|
||||
1. Install bundler with `gem install bundler`
|
||||
1. Set up your environment variables/secrets
|
||||
|
||||
- Take a look at `.env_sample`. This file lists all the `ENV` variables we
|
||||
use and provides a fake default for any missing keys.
|
||||
- If you use a remote computer as dev env, you need to set `APP_DOMAIN`
|
||||
variable to the remote computer's domain name.
|
||||
- The [backend guide](/backend) will show you how to get free API keys for
|
||||
additional services that may be required to run certain parts of the app.
|
||||
- For any key that you wish to enter/replace:
|
||||
|
||||
1. Create `.env` by copying from the provided template (ie. with bash:
|
||||
`cp .env_sample .env`). This is a personal file that is ignored in git.
|
||||
1. Obtain the development variable and apply the key you wish to
|
||||
enter/replace. ie:
|
||||
|
||||
```shell
|
||||
export GITHUB_KEY="SOME_REAL_SECURE_KEY_HERE"
|
||||
export GITHUB_SECRET="ANOTHER_REAL_SECURE_KEY_HERE"
|
||||
```
|
||||
|
||||
- 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.
|
||||
|
||||
1. After ensuring that the PostgreSQL server and the Redis server are running,
|
||||
run `bin/setup`.
|
||||
|
||||
> The `bin/setup` script is responsible for installing a varienty of
|
||||
> dependencies. One can find it inside the `bin` folder by the name of
|
||||
> `setup`.
|
||||
>
|
||||
> - Its first task is to install the `bundler` gem. Next, it will make
|
||||
> `bundler` install all the gems, including `Rails`, located in `Gemfile`
|
||||
> in the root of the repository. It also installs `foreman`.
|
||||
> - It then installs JavaScript dependencies using the script in `bin/yarn`
|
||||
> file. These dependencies are located in `package.json` in the root of the
|
||||
> repository.
|
||||
> - Next, it uses various Rake files located inside the `lib` folder for the
|
||||
> PostgreSQL database creation and updation.
|
||||
> - Finally it cleans up all the log files and restarts the Puma server.
|
||||
|
||||
### Possible error messages
|
||||
|
||||
1. There is a possibility that you might encounter a _statement timeout_ when
|
||||
seeding the database for the first time. Please increase the value of
|
||||
`statement_timeout` to `9999999` in `config/database.yml`.
|
||||
|
||||
2. If the installation process failed with the following error
|
||||
`ERROR: Error installing pg`. Please consider installing the following
|
||||
package `libpq-dev` :
|
||||
|
||||
```bash
|
||||
sudo apt-get install libpq-dev
|
||||
```
|
||||
|
||||
3. If the command `bin/setup` fails at installing `cld-0.8.0` with the warnings
|
||||
`'aclocal-1.10' is missing on your system` and
|
||||
`'automake-1.10' is missing on your system`. Please install `automake-1.10`
|
||||
using the commands below.
|
||||
|
||||
```shell
|
||||
cd
|
||||
sudo apt-get update
|
||||
sudo apt-get install autoconf
|
||||
wget https://ftp.gnu.org/gnu/automake/automake-1.10.tar.gz
|
||||
tar xf automake-1.10.tar.gz
|
||||
cd automake-1.10/
|
||||
./configure --prefix=/usr/local
|
||||
make
|
||||
```
|
||||
|
||||
### WSL2 and System test
|
||||
|
||||
In WSL2, hostname/IP address are no longer shared between Windows and Linux.
|
||||
There are currently two work-arounds.
|
||||
|
||||
1. Use dockerized selenium, ie docker-selenium. You will need docker for the
|
||||
following steps
|
||||
|
||||
1. `docker run -d --name selenium-hub -p 4444:4444 selenium/hub:3.141.59-20200409`
|
||||
2. `CH=$(docker run --rm --name=ch --link selenium-hub:hub -v /dev/shm:/dev/shm selenium/node-chrome:3.141.59-20200409)`
|
||||
3. Add `export SELENIUM_URL="http://localhost:4444/wd/hub"` to your `.env`
|
||||
4. Run your System test!
|
||||
|
||||
2. Port forward with `socats` (more info needed).
|
||||
|
||||
> If you encountered any errors that you subsequently resolved, **please
|
||||
> consider updating this section** with your errors and their solutions.
|
||||
|
||||
### WSL2 and End to End Tests
|
||||
|
||||
In order to run Cypress Test Runner, a few extra setup steps are required to run
|
||||
GUIs natively inside of WSL2.
|
||||
|
||||
[This guide](https://nickymeuleman.netlify.app/blog/gui-on-wsl2-cypress)
|
||||
explains how to use graphical user interfaces like Cypress inside WSL2.
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
---
|
||||
title: Licensing
|
||||
---
|
||||
|
||||
# Licensing
|
||||
|
||||
This program is free software: you can redistribute it and/or modify it under
|
||||
the terms of the GNU Affero General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your option) any
|
||||
later version. Please see the
|
||||
[LICENSE](https://github.com/forem/forem/blob/main/LICENSE.md) file in our
|
||||
repository for the full text.
|
||||
|
||||
Like many open source projects, we require that contributors provide us with a
|
||||
Contributor License Agreement (CLA). By submitting code to the Forem project,
|
||||
you are granting us a right to use that code under the terms of the CLA.
|
||||
|
||||
Our version of the CLA was adapted from the Microsoft Contributor License
|
||||
Agreement, which they generously made available to the public domain under
|
||||
Creative Commons CC0 1.0 Universal.
|
||||
|
||||
## Where can I learn more about the AGPL-3 license?
|
||||
|
||||
Please refer to the official
|
||||
“[Why the Affero GPL](https://www.gnu.org/licenses/why-affero-gpl.html)” and the
|
||||
[FAQ page](https://www.gnu.org/licenses/gpl-faq.html) on
|
||||
[GNU.org](https://www.gnu.org) for full details regarding this license.
|
||||
|
||||
## Why does Forem have a CLA?
|
||||
|
||||
The Contributor License Agreement (CLA) is a legal document in which you state
|
||||
that you are entitled to contribute your code to the Forem codebase, and are
|
||||
willing to have it used in distributions and derivative work.
|
||||
|
||||
The CLA helps provide confidence to future users that they will be allowed to
|
||||
use the Forem codebase without fear that a contributor will try to withdraw
|
||||
permissions at a later date.
|
||||
|
||||
## Am I giving away the copyright of my contributions?
|
||||
|
||||
No. You are granting Forem/DEV a license to use and distribute your contribution
|
||||
without further restriction, not assigning the copyright.
|
||||
|
||||
## What if I have other questions?
|
||||
|
||||
Please check out our main repository or send an email to
|
||||
[yo@dev.to](mailto:yo@dev.to).
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
---
|
||||
title: Deployment Guide
|
||||
---
|
||||
|
||||
# Deploying Forem
|
||||
|
||||
Anyone with the ability to merge PRs on GitHub can deploy the application.
|
||||
**Whenever a PR is merged the code is deployed. When deploying complex code, be
|
||||
sure that other team members are around to help if something goes wrong.**
|
||||
|
||||
Generally, it's a good idea to keep the SRE team in the loop on high risk
|
||||
deploys. However, deployments are our collective responsibility, so it's
|
||||
important to monitor your deploys. You can see deployment status on
|
||||
Travis-ci.com and in the #deployment-pipeline channel on Slack. Be prepared to
|
||||
rollback or push a fix for any deployment!
|
||||
|
||||
# Deployment and CI/CD Process
|
||||
|
||||
## Overview
|
||||
|
||||
Forem relies on GitHub and Travis to deploy continuously to Heroku. If a Pull
|
||||
Request is merged it will automatically be deployed to production once the build
|
||||
steps complete successfully. The process currently takes about 20 minutes to
|
||||
complete and will need a few additional minutes before the change goes live.
|
||||
|
||||
## Travis Stages
|
||||
|
||||
The following stages can be explored in our
|
||||
[.travis.yml](https://github.com/forem/forem/blob/main/.travis.yml) and
|
||||
[Procfile](https://github.com/forem/forem/blob/main/Procfile). Our Travis CI
|
||||
process consists of 2 stages.
|
||||
|
||||
1. Running our test suite in 3 parallel jobs.
|
||||
2. Deploying the application.
|
||||
|
||||
### Stage 1: Running Tests
|
||||
|
||||
In stage 1, we use [KnapsackPro](https://knapsackpro.com/) to divide our Rspec
|
||||
tests evenly between 3 different jobs (virtual machines). This ensures that each
|
||||
job takes relatively the same amount of time to run. After running our Rspec
|
||||
tests, we then run a series of other checks. These additional checks are split
|
||||
up between the different jobs. Here is a list of those additional checks that
|
||||
are run.
|
||||
|
||||
- Job 0 is where we run JavaScript tests, Preact tests, and coverage checks.
|
||||
- Job 1 is where Travis builds Storybook to ensure its integrity, and where we
|
||||
check for any known vulnerabilities using `bundle-audit`.
|
||||
- Job 2 is where Travis fires up a Rails console to ensure the application loads
|
||||
properly.
|
||||
|
||||
If all of the jobs pass then we move on to Stage 2 of the Travis CI process.
|
||||
|
||||
### Stage 2: Deploying
|
||||
|
||||
If the build was kicked off from a pull request being created or updated this
|
||||
stage will do nothing. If the branch has been merged into main, then this stage
|
||||
will kick off a deploy. The deploy will run in its own job deploying our
|
||||
application to Heroku.
|
||||
|
||||
Prior to deploying the code, Heroku will run database migrations and do some
|
||||
final checks (more information on that below) to make sure everything is working
|
||||
as expected. If these all succeed, then the deploy completes and our team is
|
||||
notified.
|
||||
|
||||
## Deploying to Heroku
|
||||
|
||||
We use Heroku's
|
||||
[Release Phase](https://devcenter.heroku.com/articles/release-phase) feature.
|
||||
Upon deploy, the app installs dependencies, bundles assets, and gets the app
|
||||
ready for launch. However, before it launches and releases the app Heroku runs a
|
||||
release script on a one-off dyno. If that release script/step succeeds the new
|
||||
app is released on all of the dynos. If that release script/step fails then the
|
||||
deploy is halted and we are notified.
|
||||
|
||||
The name of the script we use is `release-tasks.sh` and its in our root
|
||||
directory. During this release step we do a few checks.
|
||||
|
||||
1. We first check the DEPLOY_STATUS environment variable. In the event that we
|
||||
want to prevent deploys, for example after a rollback, we will set
|
||||
DEPLOY_STATUS to "blocked". This will cause the release script to exit with a
|
||||
code of 1 which will halt the deploy. This ensures that we don't accidentally
|
||||
push out code while we are waiting for a fix or running other tasks.
|
||||
2. We run any outstanding migrations. This ensures that a migration finishes
|
||||
successfully before the code that uses it goes live.
|
||||
3. We run any data update scripts that need to be run. A data update script is
|
||||
one that allows us to update data in the background separate from a
|
||||
migration.
|
||||
4. Following updating all of our datastores we use the Rails runner to output a
|
||||
simple string. Executing a Rails runner command ensures that we can boot up
|
||||
the entire app successfully before it is deployed. We deploy asynchronously,
|
||||
so the website is running the new code a few minutes after deploy. A new
|
||||
instance of Heroku Rails console will immediately run a new code.
|
||||
|
||||

|
||||
|
|
@ -1,68 +0,0 @@
|
|||
---
|
||||
title: Pull Requests
|
||||
---
|
||||
|
||||
# Pull Requests
|
||||
|
||||
## General
|
||||
|
||||
- We generally follow a branch naming convention of
|
||||
`<user>/<feature>-<issue number>`, e.g. `sloan/make-things-awesome-5555`.
|
||||
Issue numbers can be omitted when unavailable, e.g. for things like quick
|
||||
fixes, spikes, experiments, etc.
|
||||
- Push early and often, you don’t need to wait until you’re finished to get
|
||||
feedback. Please use GitHub draft PRs when you have a work-in-progress (WIP)
|
||||
PR, or when you have a PR that is contingent on something else to be merged.
|
||||
We prefer draft PRs rather than adding "DO NOT MERGE" to a PR's title. Any PR
|
||||
that is not a draft should be ready to be merged by anyone once it has been
|
||||
reviewed.
|
||||
- Please follow our pull request template. Provide context for reviewers, when
|
||||
in doubt err on the side of too much information.
|
||||
- Core team members are not required to review draft PRs. To explicitly request
|
||||
feedback, please assign or mention people.
|
||||
|
||||
## PR Reviews
|
||||
|
||||
PR review is the Core Team's opportunity to weigh-in on changes before they go
|
||||
into production.
|
||||
|
||||
Keep in mind that our team is distributed across the world. It's a good idea to
|
||||
leave far-reaching PRs open for a day, to give everyone a change to share their
|
||||
thoughts.
|
||||
|
||||
- We require 2 approvals from core team members for each PR:
|
||||
- One from the same team because they have context and are working towards the
|
||||
same goal as you.
|
||||
- One from outside your team for the following reasons:
|
||||
- A different perspective from someone who has less context
|
||||
- To leverage our different strengths and garner additional insights
|
||||
spreading knowledge and code exposure throughout the team
|
||||
- To avoid overloading individuals
|
||||
- Tag people that you’d like to review your PR using GitHub’s ‘Reviewers’
|
||||
function.
|
||||
- Be kind! The goal here is to work together on shipping good code, not to judge
|
||||
people.
|
||||
- For serious issues, use Github’s “Request changes” feature. This should be
|
||||
reserved for serious problems, e.g.
|
||||
- Doesn’t do what the issue said
|
||||
- Foreseeable performance problems
|
||||
- Provable security issues
|
||||
- Breaks existing functionality
|
||||
- Everything else can be posted as a comment, but it’s up to the original PR
|
||||
author whether or not they want to incorporate the changes.
|
||||
- Please keep style discussions to an absolute minimum, that time is better
|
||||
spent making a PR for the configuration of our various lint tools.
|
||||
- If you make changes to your PR, please re-request feedback from previous
|
||||
reviewers.
|
||||
|
||||
# Merging Pull Requests
|
||||
|
||||
At the time of writing, we tend to prefer squashing PRs into a single commit and
|
||||
merging them. This is easily (and safely) achieved using the GitHub UI.
|
||||
|
||||
All required checks such as CI and Code Climate should be green.
|
||||
|
||||
Once a PR is merged, it might need to be deployed. Deployment is a team
|
||||
responsibility, and everyone on the core team should be comfortable deploying
|
||||
code. For more information, read the
|
||||
[deployment guide](https://docs.forem.com/maintainers/deployment).
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
---
|
||||
title: Maintainers Guide
|
||||
items:
|
||||
- pull_requests.md
|
||||
- deploying.md
|
||||
---
|
||||
|
||||
# Maintainer Processes
|
||||
|
||||
This document contains basic instructions for the maintainers of the Forem
|
||||
application. It's a work in-progress, but should allow the Forem Core Team to
|
||||
document processes and strategies in a transparent way!
|
||||
|
|
@ -1,17 +0,0 @@
|
|||
[build]
|
||||
command = "make"
|
||||
[environment]
|
||||
NETLIFY_USE_YARN = 1
|
||||
|
||||
# See <https://docs.netlify.com/routing/redirects/redirect-options/> for more options
|
||||
[[redirects]]
|
||||
from = "https://docs.dev.to"
|
||||
to = "https://docs.forem.com"
|
||||
status = 301
|
||||
force = true
|
||||
|
||||
[[redirects]]
|
||||
from = "https://docs.dev.to/*"
|
||||
to = "https://docs.forem.com/:splat"
|
||||
status = 301
|
||||
force = true
|
||||