bullet_train 1.0.34 → 1.0.37
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/views/layouts/docs.html.erb +46 -18
- data/app/views/public/home/docs.html.erb +18 -1
- data/config/routes.rb +1 -1
- data/docs/api.md +3 -0
- data/docs/authentication.md +13 -0
- data/docs/billing/stripe.md +90 -0
- data/docs/desktop.md +13 -0
- data/docs/field-partials/buttons.md +42 -0
- data/docs/field-partials/super-select.md +58 -0
- data/docs/field-partials.md +132 -0
- data/docs/font-awesome-pro.md +50 -0
- data/docs/getting-started.md +55 -0
- data/docs/heroku.md +91 -0
- data/docs/i18n.md +3 -0
- data/docs/index.md +52 -0
- data/docs/indirection.md +53 -0
- data/docs/modeling.md +93 -0
- data/docs/namespacing.md +11 -0
- data/docs/oauth.md +27 -0
- data/docs/onboarding.md +41 -0
- data/docs/permissions.md +18 -0
- data/docs/seeds.md +48 -0
- data/docs/super-scaffolding/delegated-types.md +328 -0
- data/docs/super-scaffolding.md +246 -0
- data/docs/teams.md +8 -0
- data/docs/testing.md +34 -0
- data/docs/themes.md +101 -0
- data/docs/tunneling.md +29 -0
- data/docs/upgrades.md +69 -0
- data/docs/webhooks/incoming.md +3 -0
- data/docs/webhooks/outgoing.md +3 -0
- data/lib/bullet_train/version.rb +1 -1
- metadata +30 -2
data/docs/heroku.md
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
# Deploying to Heroku
|
2
|
+
|
3
|
+
When you're ready to deploy to Heroku, it's highly recommended you use this button:
|
4
|
+
|
5
|
+
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy?template=http://github.com/bullet-train-co/bullet_train)
|
6
|
+
|
7
|
+
This button leverages the configuration found in `app.json`, including sensible defaults for dyno formation, third-party services, buildpack configuration, etc.
|
8
|
+
|
9
|
+
## What's Included?
|
10
|
+
|
11
|
+
### Required Add-Ons
|
12
|
+
We've included the "entry-level but production-grade" service tier across the board for:
|
13
|
+
|
14
|
+
- [Heroku Postgres](https://elements.heroku.com/addons/heroku-postgresql)
|
15
|
+
- [Heroku Redis](https://elements.heroku.com/addons/heroku-redis) to support Sidekiq and Action Cable.
|
16
|
+
- [Memcachier](https://elements.heroku.com/addons/memcachier) to support Rails Cache.
|
17
|
+
- [HDrive](https://elements.heroku.com/addons/hdrive) to support off-server file uploads backed by AWS S3.
|
18
|
+
- [Cloudinary](https://cloudinary.com) to support off-server image uploads and ImageMagick processing.
|
19
|
+
- [Heroku Scheduler](https://elements.heroku.com/addons/scheduler) for cron jobs.
|
20
|
+
- [Rails Autoscale](https://railsautoscale.com) for best-of-breed reactive performance monitoring.
|
21
|
+
- [Honeybadger](https://www.honeybadger.io) and [Sentry](https://elements.heroku.com/addons/sentry), both free, for redundant error tracking.
|
22
|
+
- [Expedited Security](https://expeditedsecurity.com)'s [Real Email](https://elements.heroku.com/addons/realemail) to reduce accounts created with fake and unreachable emails, which will subsequently hurt your email deliverability.
|
23
|
+
|
24
|
+
## Additional Required Steps
|
25
|
+
Even after using the above button, there are a few steps that need to be performed manually using the [Heroku CLI](https://devcenter.heroku.com/articles/heroku-cli):
|
26
|
+
|
27
|
+
### 1. Add Heroku as a Remote in Your Local Repository
|
28
|
+
|
29
|
+
```
|
30
|
+
heroku git:remote -a YOUR_HEROKU_APP_NAME
|
31
|
+
```
|
32
|
+
|
33
|
+
After this, you'll be able to deploy updates to your app like so:
|
34
|
+
|
35
|
+
```
|
36
|
+
git push heroku main
|
37
|
+
````
|
38
|
+
|
39
|
+
### 2. Running Database Migrations and Seeds
|
40
|
+
We've decided not to configure the application to automatically run database migrations after a deploy for the time being. For that reason, you'll need to run the migrations and seeds manually, like so:
|
41
|
+
|
42
|
+
```
|
43
|
+
heroku run rake db:migrate
|
44
|
+
heroku run rake db:seed
|
45
|
+
```
|
46
|
+
|
47
|
+
### 3. Enabling Runtime Dyno Metadata
|
48
|
+
We include [Honeybadger](http://honeybadger.io) and Sentry (both at a free tier) for redundant error tracking by default. Sentry requires the following Heroku labs feature to be enabled:
|
49
|
+
|
50
|
+
```
|
51
|
+
heroku labs:enable runtime-dyno-metadata
|
52
|
+
```
|
53
|
+
|
54
|
+
### 4. Improve Boot Time
|
55
|
+
You can cut your application boot time in half by enabling the following Heroku Labs feature. See [this blog post](https://dev.to/dbackeus/cut-your-rails-boot-times-on-heroku-in-half-with-a-single-command-514d) for more details.
|
56
|
+
|
57
|
+
```
|
58
|
+
heroku labs:enable build-in-app-dir
|
59
|
+
```
|
60
|
+
|
61
|
+
### 5. Adding Your Actual Domain
|
62
|
+
|
63
|
+
The most common use case for Bullet Train applications is to be hosted at some appropriate subdomain (e.g. `app.YOURDOMAIN.COM`) while a marketing site is hosted with a completely different service at the apex domain (e.g. just `YOURDOMAIN.COM`) or `www.YOURDOMAIN.COM`. To accomplish this, do the following in your shell:
|
64
|
+
|
65
|
+
```
|
66
|
+
heroku domains:add app.YOURDOMAIN.COM
|
67
|
+
```
|
68
|
+
|
69
|
+
The output for this command will say something like:
|
70
|
+
|
71
|
+
```
|
72
|
+
Configure your app's DNS provider to point to the DNS Target SOMETHING-SOMETHING-XXX.herokudns.com.
|
73
|
+
```
|
74
|
+
|
75
|
+
On most DNS providers this means going into the DNS records for `YOURDOMAIN.COM` and adding a *CNAME* record for the `app` subdomain with a value of `SOMETHING-SOMETHING-XXX.herokudns.com` (except using the actual value provided by the Heroku CLI) and whatever TTL refresh rate you desire. I always set this as low as possible at first to make it easier to fix any mistakes I've made.
|
76
|
+
|
77
|
+
After you've added that record, you need to update the following environment settings on the Heroku app:
|
78
|
+
|
79
|
+
```
|
80
|
+
heroku config:set BASE_URL=https://app.YOURDOMAIN.COM
|
81
|
+
heroku config:set MARKETING_SITE_URL=https://YOURDOMAIN.COM
|
82
|
+
```
|
83
|
+
|
84
|
+
You'll also need to enable Heroku's Automated Certificate Management to have them handle provisioning and renewing your Let's Encrypt SSL certificates:
|
85
|
+
|
86
|
+
```
|
87
|
+
heroku certs:auto:enable
|
88
|
+
heroku certs:auto
|
89
|
+
```
|
90
|
+
|
91
|
+
You should be done now and your app should be available at `https://app.YOURDOMAIN.COM/account` and any hits to `https://app.YOURDOMAIN.COM` (e.g. when users sign out, etc.) will be redirected to your marketing site.
|
data/docs/i18n.md
ADDED
@@ -0,0 +1,3 @@
|
|
1
|
+
# Translations and Internationalization
|
2
|
+
|
3
|
+
Bullet Train and views generated by Super Scaffolding are localized by default, meaning all of the human-readable text in your application has been extracted into a YAML configuration file in `config/locales/en` and can be translated into any language you would like to target.
|
data/docs/index.md
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# Bullet Train Developer Documentation
|
2
|
+
|
3
|
+
> This release of Bullet Train is still a pre-release, so please pardon the dust while we work to complete the documentation for certain topics.
|
4
|
+
|
5
|
+
## Introduction
|
6
|
+
- [What is Bullet Train?](https://bullettrain.co) <i class="ti ti-new-window ml-2"></i>
|
7
|
+
- [Getting Started](/docs/getting-started.md)
|
8
|
+
- [Upgrading](/docs/upgrades.md)
|
9
|
+
|
10
|
+
## General Topics
|
11
|
+
- [Domain Modeling](/docs/modeling.md)
|
12
|
+
- [Dealing with Indirection](/docs/indirection.md)
|
13
|
+
- [Overriding the Framework](/docs/overriding.md)
|
14
|
+
- [Setting up a Tunnel](/docs/tunneling.md)
|
15
|
+
|
16
|
+
## Developer Tools
|
17
|
+
- [Super Scaffolding](/docs/super-scaffolding.md)
|
18
|
+
- [Database Seeds](/docs/seeds.md)
|
19
|
+
- [Test Suite](/docs/testing.md)
|
20
|
+
- [Point-and-Click Test Writing](https://github.com/bullet-train-co/magic_test) <i class="ti ti-new-window ml-2"></i>
|
21
|
+
|
22
|
+
## Accounts & Teams
|
23
|
+
- [Authentication](/docs/authentication.md)
|
24
|
+
- [Teams](/docs/teams.md)
|
25
|
+
- [Roles and Permissions](/docs/permissions.md)
|
26
|
+
- [Onboarding](/docs/onboarding.md)
|
27
|
+
- [Namespacing](/docs/namespacing.md)
|
28
|
+
|
29
|
+
## UI
|
30
|
+
- [Field Partials](/docs/field-partials.md)
|
31
|
+
- [Theme Engine](/docs/themes.md)
|
32
|
+
- [Partial Improvements](https://github.com/bullet-train-co/nice_partials) <i class="ti ti-new-window ml-2"></i>
|
33
|
+
|
34
|
+
## Billing
|
35
|
+
- [Stripe](/docs/billing/stripe.md)
|
36
|
+
|
37
|
+
## Integration
|
38
|
+
- [OAuth Providers](/docs/oauth.md)
|
39
|
+
- [REST API](/docs/api.md)
|
40
|
+
- [Outgoing Webhooks](/docs/webhooks/outgoing.md)
|
41
|
+
- [Incoming Webhooks](/docs/webhooks/incoming.md)
|
42
|
+
|
43
|
+
## Add-Ons
|
44
|
+
- [Font Awesome Pro](/docs/font-awesome-pro.md)
|
45
|
+
|
46
|
+
## Production
|
47
|
+
- [Heroku](/docs/heroku.md)
|
48
|
+
- [Desktop Applications](/docs/desktop.md)
|
49
|
+
|
50
|
+
<hr>
|
51
|
+
|
52
|
+
Overwhelmed? [YOLO](https://github.com/bullet-train-co/bullet_train#readme).
|
data/docs/indirection.md
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
# Dealing with Indirection
|
2
|
+
|
3
|
+
## The Problem with Indirection
|
4
|
+
|
5
|
+
In software development, indirection is everywhere and takes many forms.
|
6
|
+
|
7
|
+
For example, in vanilla Rails development, you introduce a type of indirection when you extract a button label out of a view file and use the `t` helper to render the string from a translation YAML file. In the future, when another developer goes to update the button label, they will first open the view, they'll see `t(".submit")` and then have to reason a little bit about which translation file they need to open up in order to update that label.
|
8
|
+
|
9
|
+
Our goal in Bullet Train is to improve developer experience, not reduce it, so it was important that along with any instances of indirection we were introducing, we also included new tooling to ensure it was never a burden to developers. Thankfully, in practice we found that some of this new tooling improves even layers of indirection that have always been with us in Rails development.
|
10
|
+
|
11
|
+
## Solving Indirection in Views
|
12
|
+
|
13
|
+
### Resolving Partial Paths with `bin/resolve`
|
14
|
+
|
15
|
+
Even in vanilla Rails development, when you're looking at a view file, the path you see passed to a `render` call isn't the actual file name of the partial that will be rendered. This is even more true in Bullet Train where certain partial paths are [magically served from theme gems](/docs/themes.md).
|
16
|
+
|
17
|
+
`bin/resolve` makes it easy to figure out where where a partial is being served from:
|
18
|
+
|
19
|
+
```
|
20
|
+
$ bin/resolve shared/box
|
21
|
+
```
|
22
|
+
|
23
|
+
### Exposing Rendered Views with Xray
|
24
|
+
|
25
|
+
> TODO Is this still true in Rails 7? Does it not do something like this by default now?
|
26
|
+
|
27
|
+
If you're looking at a rendered view in the browser, it can be hard to know which file to open in order to make a change. To help, Bullet Train includes [Xray](https://github.com/brentd/xray-rails) by default, so you can right click on any element you see, select "Inspect Element", and you'll see comments in the HTML source telling you which file is powering a particular portion of the view, like this:
|
28
|
+
|
29
|
+
```
|
30
|
+
<!--XRAY START 90 /Users/andrewculver/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/bullet_train-themes-light-1.0.10/app/views/themes/light/workflow/_box.html.erb-->
|
31
|
+
```
|
32
|
+
|
33
|
+
Note that in the example above, the view in question isn't actually coming from the application repository. Instead, it's being included from the `bullet_train-themes-light` package. For instructions on how to customize it, see [Overriding the Framework](/docs/override).
|
34
|
+
|
35
|
+
### Drilling Down on Translation Keys
|
36
|
+
|
37
|
+
Even in vanilla Rails applications, extracting strings from view files into I18N translation YAML files introduces a layer of indirection. Bullet Train tries to improve the resulting DX with a couple tools that make it easier to figure out where a translation you see in your browser is coming from.
|
38
|
+
|
39
|
+
#### Show Translation Keys in the Browser with `?show_locales=true`
|
40
|
+
|
41
|
+
You can see the full translation key of any string on the page by adding `?show_locales=true` to the URL.
|
42
|
+
|
43
|
+
#### Log Translation Keys to the Console with `?log_locales=true`
|
44
|
+
|
45
|
+
You can also log all the translation key for anything being rendered to the console by adding `?log_locales=true` to the request URL. This can make it easier to copy and paste translation keys for strings that are rendered in non-selectable UI elements.
|
46
|
+
|
47
|
+
#### Resolving Translation Keys with `bin/resolve`
|
48
|
+
|
49
|
+
Once you have the full I18N translation key, you can use `bin/resolve` to figure out which package and file it's coming from. At that point, if you need to customize it, you can also use the `--eject` option to copy the the framework for customization in your local application:
|
50
|
+
|
51
|
+
```
|
52
|
+
$ bin/resolve en.account.onboarding.user_details.edit.header --eject --open
|
53
|
+
```
|
data/docs/modeling.md
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
# Domain Modeling in Bullet Train
|
2
|
+
|
3
|
+
Domain modeling is one of the most important activities in software development. With [Super Scaffolding](/docs/super-scaffolding.md), it's also one of the highest leverage activities in Bullet Train development.
|
4
|
+
|
5
|
+
## What is a "Domain Model"?
|
6
|
+
|
7
|
+
In software application development, your domain model is the object-oriented representation (or "abstraction") of the problem space you're solving problems for. In Rails, the classes that represent your domain model live in `app/models`.
|
8
|
+
|
9
|
+
## What is "Domain Modeling"?
|
10
|
+
|
11
|
+
"Domain modeling" refers to the process by which you decide which entities to introduce into your application's domain model, how those models relate to each other, and which attributes belong on which models.
|
12
|
+
|
13
|
+
Because in Rails most of your models will typically be backed by tables in a database, the process of domain modeling substantially overlaps with "database design". This is especially true in systems like Rails that implement their domain model with the Active Record pattern, because your table structure and object-oriented models are pretty much mapped one-to-one[<sup>*</sup>](https://en.wikipedia.org/wiki/Object–relational_impedance_mismatch).
|
14
|
+
|
15
|
+
## The Importance of Domain Modeling
|
16
|
+
|
17
|
+
### Usability
|
18
|
+
|
19
|
+
Generally speaking, the better your domain model is mapped to the real-world problem space, the more likely it is to map to your own users conceptual model of the problem space in their own head. Since the structure of your domain model ends up having such a large influence on the default structure and navigation of your application UI, getting the domain model "right" is the first step in creating an application that is easy for users to understand and use.
|
20
|
+
|
21
|
+
### Extendability
|
22
|
+
|
23
|
+
Feature requests come from the real world, and specifically from the parts of reality your application doesn't already solve for. The better your existing domain model captures the existing real-world problem space you've tried to solve problems for, the easier it will be for your application to add new models for features that solve the new problems your users are actually experiencing in real life. Inversely, if you take the wrong shortcuts when representing the problem space, it will be difficult to find the right place to add new features in a way that makes sense to your users.
|
24
|
+
|
25
|
+
## Important Bullet Train Concepts
|
26
|
+
|
27
|
+
### The "Parent Model"
|
28
|
+
|
29
|
+
The idea of a "parent model" is different than [a "parent class" in an object-oriented sense](https://en.wikipedia.org/wiki/Inheritance_\(object-oriented_programming\)). When we say "parent model", we're referring to the model that another model primarily belongs to. For example, a `Task` might primarily belong to a `Project`. Although this type of hierarchy isn't an entirely natural concept in object-oriented programming itself, (where our UML diagrams have many types of relationships flying in different directions,) it's actually a very natural concept in the navigation structure of software, which is why breadcrumbs are such a popular tool for navigation. It's also a concept that is very natural in traditional Rails development, expressed in the definition of nested RESTful routes for resources.
|
30
|
+
|
31
|
+
## Philosophies
|
32
|
+
|
33
|
+
### Take your time and get it right.
|
34
|
+
|
35
|
+
Because Super Scaffolding makes it so easy to bring your domain model to life, you don't have to rush into that implementation phase of writing code. Instead, you can take your sweet time thinking through your proposed domain model and mentally running it through the different scenarios and use cases it needs to solve for. If you get aspects of your domain model wrong, it can be really hard to fix later.
|
36
|
+
|
37
|
+
### More minds are better.
|
38
|
+
|
39
|
+
Subject your proposed domain model to review from other developers or potential users and invite their thoughts.
|
40
|
+
|
41
|
+
### Tear it down to get it right.
|
42
|
+
|
43
|
+
In traditional Rails development, it can be so much work to bring your domain model to life in views and controllers that if you afterward realize you missed something or got something wrong structurally, it can be tempting not to fix it or refactor it. Because Super Scaffolding eliminates so much of the busy work of bringing your domain model to life in the initial implementation phase, you don't have to worry so much about tearing down your scaffolds, reworking the domain model, and running through the scaffolding process again.
|
44
|
+
|
45
|
+
### Focus on the structure and namespacing. Don't worry about every attribute.
|
46
|
+
|
47
|
+
One of the unique features of Super Scaffolding is that it allows you to scaffold additional attributes with `bin/super-scaffold crud-field` after the initial scaffolding of a model with `bin/super-scaffold crud`. That means that you don't have to worry about figuring out every single attribute that might exist on a model before running Super Scaffolding. Instead, the really important piece is:
|
48
|
+
|
49
|
+
1. Naming the model.
|
50
|
+
2. Determining which parent model it primarily belongs to.
|
51
|
+
3. Determining whether the model should be [in a topic namespace](https://blog.bullettrain.co/rails-model-namespacing/).
|
52
|
+
|
53
|
+
### Start with CRUD, then polish.
|
54
|
+
|
55
|
+
Even if you know there's an attribute or model that you're going to want to polish up the user experience for, still start with the scaffolding. This ensures that any model or attribute is also represented in your REST API and you have feature parity between your web-based experience and what developers can integrate with and automate.
|
56
|
+
|
57
|
+
### Pluralize preemptively.
|
58
|
+
|
59
|
+
> Before you write any code — ask if you could ever possibly want multiple kinds of the thing you are coding. If yes, just do it. Now, not later.
|
60
|
+
|
61
|
+
— [Shawn Wang](https://twitter.com/swyx)
|
62
|
+
|
63
|
+
> I've done this refactoring a million times. I'll be like, I thought there would only ever be one subscription team, user plan, name, address, and it always ends up being like, "Oh, actually there's more." I almost never go the other way. What if you just paid the upfront cost of thinking "This is just always a collection"?
|
64
|
+
|
65
|
+
— [Ben Orenstein](https://twitter.com/r00k)
|
66
|
+
|
67
|
+
[I believe this is one of the most important articles in software development in the last ten years.](https://www.swyx.io/preemptive-pluralization/) However, with great domain modeling power comes great UX responsibility, which we'll touch on later.
|
68
|
+
|
69
|
+
## A Systematic Approach
|
70
|
+
|
71
|
+
### 1. Write `rails g` and `bin/super-scaffold` commands in a scratch file.
|
72
|
+
|
73
|
+
See the [Super Scaffolding documentation](/docs/super-scaffolding.md) for more specific guidance. Leave plenty of comments in your scratch file describing anything that isn't obvious and providing examples of values that might populate attributes.
|
74
|
+
|
75
|
+
### 2. Review with other developers.
|
76
|
+
|
77
|
+
Push up a pull request with your scratch file and invite review from other developers. They might bring up scenarios and use cases you didn't think of, better ways of representing something, or generate questions that don't have an obvious answer and require feedback from a subject matter expert.
|
78
|
+
|
79
|
+
### 3. Review with non-developer stakeholders.
|
80
|
+
|
81
|
+
Engage with product owners or potential users and talk through each part of the domain model in your scratch file without showing it to them or getting too technical. Just talk through which entities you chose to represent, which attributes and options are available. Talk through the features those models and attributes allow you to provide and which use cases you think you've covered.
|
82
|
+
|
83
|
+
In these discussions, you're looking for inspiration of additional nouns, verbs, and use cases you may not have considered in your initial modeling. Even if you choose not to incorporate certain ideas or feature requests immediately, you're at least taking into consideration whether they would fit nicely into the mental model you have represented in your domain model.
|
84
|
+
|
85
|
+
### 4. Run the commands.
|
86
|
+
|
87
|
+
Be sure to commit your results at this point. This helps isolate the computer generated code from the work you'll do in the next step (which you may want to have someone review.)
|
88
|
+
|
89
|
+
### 5. Polish the UI.
|
90
|
+
|
91
|
+
By default, Bullet Train produces a very CRUD-y user experience. The intention has always been that the productivity gains provided by Super Scaffolding should be reinvested into the steps that come before and after. Spend more time domain modeling, and then spend more time polishing up the resulting UX.
|
92
|
+
|
93
|
+
This is especially true in situations where you've chosen to pluralize preemptively. Ask yourself: Does every user need this option in the plural? If not, should we try to simplify the conceptual model by representing it as a "has one" until they opt-in to complexity? Doing this allows you to have the best of both worlds: Simplicity for those who can fit within it, and advanced functionality that users can opt into.
|
data/docs/namespacing.md
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
# Namespacing in Bullet Train
|
2
|
+
|
3
|
+
## The `Account` Namespace for Controllers and Views
|
4
|
+
Bullet Train comes preconfigured with an `Account` and controller and view namespace. This is the place where Super Scaffolding will, by default, put new resource views and controllers. The intention here is to ensure that in systems that have both authenticated resource workflows and public-facing resources, those two different facets of the application are served by separate resource views and controllers. (By default, public-facing resources would be in the `Public` namespace.)
|
5
|
+
|
6
|
+
## Alternative Authenticated Namespaces
|
7
|
+
In Bullet Train applications with [multiple team types](/docs/teams.md), you may find it helpful to introduce additional controller and view namespaces to represent and organize user interfaces and experiences for certain team types that vary substantially from the `Account` namespace default. In Super Scaffolding, you can specify a namespace other than `Account` with the `--namespace` option, for example:
|
8
|
+
|
9
|
+
```
|
10
|
+
$ bin/super-scaffold crud Event Team name:text_field --namespace=customers
|
11
|
+
```
|
data/docs/oauth.md
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
# OAuth Providers
|
2
|
+
Bullet Train includes [Omniauth](https://github.com/omniauth/omniauth) by default which enables [Super Scaffolding](/docs/super-scaffolding) to easily add [any of the third-party OAuth providers in its community-maintained list of strategies](https://github.com/omniauth/omniauth/wiki/List-of-Strategies) for user-level authentication and team-level integrations via API and incoming webhooks.
|
3
|
+
|
4
|
+
For specific instructions on adding new OAuth providers, run the following on your shell:
|
5
|
+
|
6
|
+
> TODO This scaffolder still needs to be updated to support the new way we distribute the Stripe Connect example via Ruby gem.
|
7
|
+
|
8
|
+
```
|
9
|
+
bin/super-scaffold oauth-provider
|
10
|
+
```
|
11
|
+
|
12
|
+
## Stripe Connect Example
|
13
|
+
Similar to the "Tangible Things" template for [Super Scaffolding CRUD workflows](/docs/super-scaffolding.md), Bullet Train includes a Stripe Connect integration by default and this example also serves as a template for Super Scaffolding to implement other providers you might want to add.
|
14
|
+
|
15
|
+
## Dealing with Last Mile Issues
|
16
|
+
|
17
|
+
You should be able to add many third-party OAuth providers with Super Scaffolding without any manual effort. However, there are sometimes quirks from provider to provider, so if you need to dig in to get things working on a specific provider, here are the files you'll probably be looking for:
|
18
|
+
|
19
|
+
### Core Functionality
|
20
|
+
- `config.omniauth` in `config/initializers/devise.rb`
|
21
|
+
- Third-party OAuth providers are registered at the top of this file.
|
22
|
+
- `app/controllers/account/oauth/omniauth_callbacks_controller.rb`
|
23
|
+
- This controller contains all the logic that executes when a user returns back to your application after working their way through the third-party OAuth provider's workflow.
|
24
|
+
- `omniauth_callbacks` in `config/routes.rb`
|
25
|
+
- This file just registers the above controller with Devise.
|
26
|
+
- `app/views/devise/shared/_oauth.html.erb`
|
27
|
+
- This partial includes all the buttons for presentation on the sign in and sign up pages.
|
data/docs/onboarding.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# Onboarding
|
2
|
+
Bullet Train provides an easy to understand and modifiable structure for defining required onboarding steps and forms. This system makes it easy for users to complete required steps before seeing your application's full account interface. The code for this feature is well documented in the code with comments, so rather than duplicating that document here, we'll simply direct you to the relevant files.
|
3
|
+
|
4
|
+
## Included Onboarding Steps
|
5
|
+
|
6
|
+
### Collect User Details
|
7
|
+
|
8
|
+
The included "user details" onboarding step is intended to collect any fields that are required for a user account to be "complete" while not requiring those fields to be collected on the initial sign up form. This is a deliberate UX decision to try and increase conversions on the initial form.
|
9
|
+
|
10
|
+
### Collect User Email
|
11
|
+
|
12
|
+
The "user email" onboarding step is specifically used in situations where a user signs up with an OAuth provider that either doesn't supply their email address to the application or doesn't have a verified email address for the user. In this situation, we want to have an email address on their account, so we prompt them for it.
|
13
|
+
|
14
|
+
## Relevant Files
|
15
|
+
|
16
|
+
> TODO This section needs to be updated now that a bunch of these files are coming in from gems.
|
17
|
+
|
18
|
+
### Controllers
|
19
|
+
- `ensure_onboarding_is_complete` in `app/controllers/account/application_controller.rb`
|
20
|
+
- `app/controllers/account/onboarding/user_details_controller.rb`
|
21
|
+
- `app/controllers/account/onboarding/user_email_controller.rb`
|
22
|
+
|
23
|
+
### Views
|
24
|
+
- `app/views/account/onboarding/user_details/edit.html.erb`
|
25
|
+
- `app/views/account/onboarding/user_email/edit.html.erb`
|
26
|
+
|
27
|
+
### Models
|
28
|
+
- `user#details_provided?` in `app/models/user.rb`
|
29
|
+
|
30
|
+
### Routes
|
31
|
+
- `namespace :onboarding` in `config/routes.rb`
|
32
|
+
|
33
|
+
## Adding Additional Steps
|
34
|
+
Although you can implement onboarding steps from scratch, we always just copy and paste one of the existing steps as a starting point, like so:
|
35
|
+
|
36
|
+
1. Copy, rename, and modify of the existing onboarding controllers.
|
37
|
+
2. Copy, rename, and modify the corresponding `edit.html.erb` view.
|
38
|
+
3. Copy and rename the route entry in `config/routes.rb`.
|
39
|
+
4. Add the appropriate gating logic in `ensure_onboarding_is_complete` in `app/controllers/account/application_controller.rb`
|
40
|
+
|
41
|
+
Onboarding steps aren't limited to targeting `User` models. It's possible to add onboarding steps to help flesh out team `Membership` records or `Team` records as well. You can use this pattern for setting up any sort of required data for either the user or the team.
|
data/docs/permissions.md
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# Roles, Permissions, Abilities, and Authorization
|
2
|
+
|
3
|
+
## CanCanCan
|
4
|
+
Bullet Train leans heavily on [CanCanCan](https://github.com/CanCanCommunity/cancancan) for implementing authorization and permissions. (We’re also proud sponsors of its ongoing maintenance.) The original CanCan library by Ryan Bates was, in our opinion, a masterpiece and a software engineering marvel that has stood the test of time. It's truly a diamond among Ruby Gems. If you're not already familiar with CanCanCan, you should [read its documentation](https://github.com/CanCanCommunity/cancancan) to get familiar with its features and DSL.
|
5
|
+
|
6
|
+
## Bullet Train Roles
|
7
|
+
Over many years of successfully implementing applications with CanCanCan, it became apparent to us that a supplemental level of abstraction could help streamline and simplify the definition of many common permissions, especially in large applications. We've since extracted this functionality into [a standalone Ruby Gem](https://github.com/bullet-train-co/bullet_train-roles) and moved the documentation that used to be here into [the README for that project](https://github.com/bullet-train-co/bullet_train-roles/blob/main/README.md). Should you encounter situations where this abstraction doesn't meet your specific needs, you can always implement the permissions you need using standard CanCanCan directives in `app/models/ability.rb`.
|
8
|
+
|
9
|
+
## Additional Notes
|
10
|
+
|
11
|
+
### Caching
|
12
|
+
Because abilities are being evaluated on basically every request, it made sense to introduce a thin layer of caching to help speed things up. When evaluating permissions, we store a cache of the result in the `ability_cache` attribute of the `User`. By default, making changes to a model that includes the `Roles::Support` concern will invalidate that user's cache.
|
13
|
+
|
14
|
+
### Naming and Labeling
|
15
|
+
What we call a `Role` in the domain model is referred to as “Special Privileges” in the user-facing application. You can rename this to whatever you like in `config/locales/en/roles.en.yml`.
|
16
|
+
|
17
|
+
## Note About Pundit
|
18
|
+
There’s nothing stopping you from utilizing Pundit in a Bullet Train project for specific hard-to-implement cases in your permissions model, but you wouldn’t want to try and replace CanCanCan with it. We do too much automatically with CanCanCan for that to be recommended. That said, in those situations where there is a permission that needs to be implemented that isn’t easily implemented with CanCanCan, consider just writing vanilla Ruby code for that purpose.
|
data/docs/seeds.md
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Database Seeds
|
2
|
+
|
3
|
+
Bullet Train introduces a new, slightly different expectation for Rails seed data: **It should be possible to run `rake db:seed` multiple times without creating duplicate data.**
|
4
|
+
|
5
|
+
## The Rails Default
|
6
|
+
|
7
|
+
This is different than the Rails default, [as evidenced by the Rails example](https://guides.rubyonrails.org/v6.1.1/active_record_migrations.html#migrations-and-seed-data) which uses `Product.create`:
|
8
|
+
|
9
|
+
```
|
10
|
+
5.times do |i|
|
11
|
+
Product.create(name: "Product ##{i}", description: "A product.")
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
## Bullet Train Example
|
16
|
+
|
17
|
+
In Bullet Train applications, you would implement that same `db/seeds.rb` logic like so:
|
18
|
+
|
19
|
+
```
|
20
|
+
5.times do |i|
|
21
|
+
Product.find_or_create_by(name: "Product ##{i}") do |product|
|
22
|
+
# this only happens if on a `create`.
|
23
|
+
production.description = "A product."
|
24
|
+
end
|
25
|
+
end
|
26
|
+
```
|
27
|
+
|
28
|
+
## Why?
|
29
|
+
We do this so Bullet Train applications can re-use the logic in `db/seeds.rb` for three purposes:
|
30
|
+
|
31
|
+
1. Set up new local development environments.
|
32
|
+
2. Ensure the test suite has the same configuration for features whose configuration is backed by Active Record (e.g. [subscriptions](/docs/subscriptions.md) and [outgoing webhooks](/docs/webhooks/outgoing.md)).
|
33
|
+
3. Ensure any updates to the baseline configuration that have been tested both locally and in CI are the exact same updates being executed in production upon deploy.
|
34
|
+
|
35
|
+
This makes `db/seeds.rb` a single source of truth for this sort of baseline data, instead of having this concern spread and sometimes duplicated across `db/seeds.rb`, `db/migrations/*`, and `test/fixtures`.
|
36
|
+
|
37
|
+
## Seeds for Different Environments
|
38
|
+
In some cases, you may have core seed data like roles that needs to exist in every environment, but you also have development data to populate in your non-production environments. Bullet Train makes this easy by supporting per-environment seed files in the `db/seeds` folder like `db/seeds/test.rb` and `db/seeds/development.rb`.
|
39
|
+
|
40
|
+
Then in `db/seeds.rb`, you can load all of the shared core seed data at the beginning of `db/seeds.rb` and then load the environment-specific seeds only when you've specified one of those environments.
|
41
|
+
|
42
|
+
```
|
43
|
+
load "#{Rails.root}/db/seeds/development.rb" if Rails.env.development?
|
44
|
+
load "#{Rails.root}/db/seeds/test.rb" if Rails.env.test?
|
45
|
+
```
|
46
|
+
|
47
|
+
## Feedback
|
48
|
+
We're always very hesitant to stray from Rails defaults, so it must be said that our commitment to this approach isn't set in stone. It's worked very well for us in a number of applications, so we've standardized on it, but the approach is certainly open to discussion.
|