bullet_train 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/docs/action-models.md +0 -2
- data/docs/field-partials/address-field.md +28 -0
- data/docs/field-partials/buttons.md +9 -0
- data/docs/field-partials/dynamic-forms-dependent-fields.md +123 -0
- data/docs/field-partials/file-field.md +0 -1
- data/docs/field-partials/super-select.md +9 -0
- data/docs/field-partials.md +38 -22
- data/docs/getting-started.md +2 -3
- data/docs/modeling.md +1 -1
- data/docs/super-scaffolding/delegated-types.md +1 -1
- data/docs/super-scaffolding.md +40 -52
- data/lib/bullet_train/version.rb +1 -1
- metadata +4 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1acc9aef5bfdec082aae9de0b523afc27395b9b41022ec4cf243e83a1d36e75
|
4
|
+
data.tar.gz: fef8231d3e320ce6b23978924b9a132585fae765842743112482f7bdbd2bb7e7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ebbab67c9b4ed7880f6ee020bddc7e6ed1cd337604835a76a5b0402fc962a60f24aa851e07806cba4bc8c7adc562c0b9242a17904321b3516bb41fa977934ce9
|
7
|
+
data.tar.gz: 8f6bf47611928395122564d43b85bb2c121843a651ce49201a41383b40c8e4c2b1802d6b1e300420180e62327cfaff4901654cd5127bb86a83f589c03e037126
|
data/docs/action-models.md
CHANGED
@@ -73,7 +73,6 @@ bin/super-scaffold action-model:targets-one-parent
|
|
73
73
|
### 1. Generate and scaffold an example `Project` model.
|
74
74
|
|
75
75
|
```
|
76
|
-
rails g model Project team:references name:string
|
77
76
|
bin/super-scaffold crud Project Team name:text_field
|
78
77
|
```
|
79
78
|
|
@@ -104,7 +103,6 @@ Because Action Models are just regular models, you can add new fields to them wi
|
|
104
103
|
For example:
|
105
104
|
|
106
105
|
```
|
107
|
-
rails g migration add_notify_users_to_projects_archive_actions notify_users:boolean
|
108
106
|
# side quest: update the generated migration with `default: false` on the new boolean field.
|
109
107
|
bin/super-scaffold crud-field Projects::ArchiveAction notify_users:boolean
|
110
108
|
```
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Examples for the `address_field` Field Partial
|
2
|
+
|
3
|
+
The address field partial adds a block of fields to your form. It creates and stores an instance of the `Address` model and associates it to your record.
|
4
|
+
|
5
|
+
## Sub-Fields Included in the Partial
|
6
|
+
|
7
|
+
| Field Label | Name | Data Type | Notes |
|
8
|
+
|---------------------------|---------------|-------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
|
9
|
+
| Country | `country_id` | `Addresses::Country` | For country values, see `config/address/countries.json` in bullet_train-core/bullet_train. |
|
10
|
+
| Address | `address_one` | `string` | |
|
11
|
+
| Address (cont'd) | `address_two` | `string` | |
|
12
|
+
| City | `city` | `string` | |
|
13
|
+
| State / Province / Region | `region_id` | `Addresses::Region` | Depending on the country selected, the label will change (e.g. Prefecture for Japan, Province or Territory for Canada). For all region values, see `config/addresses/states.json` in bullet_train-core/bullet_train. |
|
14
|
+
| Postal code | `postal_code` | `string` | Depending on the country selected, the label will change (e.g. Zip code for the United States). |
|
15
|
+
|
16
|
+
If you'd like to add or remove fields, you'll need to update your own version of the `Address` model and eject and modify the `shared/fields/address_field` partial.
|
17
|
+
|
18
|
+
## Dynamically Updating `region_id` and `postal_code` Fields and Labels
|
19
|
+
|
20
|
+
The `address_field` partial implements Bullet Train's [Dependent Fields Pattern](/docs/field-partials/dynamic-forms-dependent-fields.md) to automatically update the `region_id` and `postal_code` fields and their labels based on the value of the selected `country_id`.
|
21
|
+
|
22
|
+
## Customizing the Address Output
|
23
|
+
|
24
|
+
By default, `show` screens get a multi-line output and `index` table columns get a one-line format (use the `one_line: true` param).
|
25
|
+
|
26
|
+
To customize this output, eject the `shared/attributes/address` partial.
|
27
|
+
|
28
|
+
See the [Showcase preview](https://github.com/bullet-train-co/showcase)<i class="ti ti-new-window ml-2"></i> for the example output.
|
@@ -40,3 +40,12 @@ You can allow multiple buttons to be selected using the `multiple` option, like
|
|
40
40
|
<%= render 'shared/fields/buttons', form: form, method: :category_ids,
|
41
41
|
options: Category.all.map { |category| [category.id, category.label_string] }, multiple: true %>
|
42
42
|
```
|
43
|
+
|
44
|
+
## Dynamically Updating Form Fields
|
45
|
+
|
46
|
+
If you'd like to:
|
47
|
+
|
48
|
+
* modify other fields based on the value of your `buttons` field, or
|
49
|
+
* modify your `buttons` field based on the value of other fields
|
50
|
+
|
51
|
+
See [Dynamic Forms and Dependent Fields](/docs/field-partials/dynamic-forms-dependent-fields.md).
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Dynamic Forms and Dependent Fields
|
2
|
+
|
3
|
+
Bullet Train introduces two new concepts to make your Hotwire-powered forms update dynamically on field changes.
|
4
|
+
|
5
|
+
1. Dependent Fields Pattern
|
6
|
+
2. Dependent Fields Frame
|
7
|
+
|
8
|
+
## Dependent Fields Pattern
|
9
|
+
|
10
|
+
Let's say we have a `super_select` for a "Where have you heard from us?" field. And we'll have a `text_field` for "Other", `disabled` by default.
|
11
|
+
|
12
|
+
```erb
|
13
|
+
<%= render 'shared/fields/super_select',
|
14
|
+
method: :heard_from,
|
15
|
+
options: {include_blank: true},
|
16
|
+
other_options: {search: true}
|
17
|
+
%>
|
18
|
+
<%= render 'shared/fields/text_field',
|
19
|
+
method: :heard_from_other,
|
20
|
+
options: {disabled: true}
|
21
|
+
%>
|
22
|
+
```
|
23
|
+
|
24
|
+
Our goal: if `other` is selected, enable the "Other" field.
|
25
|
+
|
26
|
+
We'll wire the `super_select` field with the `dependable` Stimulus controller. We'll also tie both fields using the `dependable-dependents-selector-value`. In this case, the `id` of the the `heard_from_other` field.
|
27
|
+
|
28
|
+
```erb
|
29
|
+
<%= render 'shared/fields/super_select',
|
30
|
+
method: :heard_from,
|
31
|
+
options: {include_blank: true},
|
32
|
+
other_options: {search: true},
|
33
|
+
wrapper_options: {
|
34
|
+
data: {
|
35
|
+
'controller': "dependable",
|
36
|
+
'action': '$change->dependable#updateDependents',
|
37
|
+
'dependable-dependents-selector-value': "##{form.field_id(:heard_from_other)}"
|
38
|
+
}
|
39
|
+
}
|
40
|
+
%>
|
41
|
+
<%= render 'shared/fields/text_field',
|
42
|
+
method: :heard_from_other,
|
43
|
+
id: form.field_id(:heard_from_other),
|
44
|
+
options: {disabled: true}
|
45
|
+
%>
|
46
|
+
```
|
47
|
+
|
48
|
+
On `$change` ([See `super_select` dispatched events](/docs/field-partials/super-select#events)), a custom `dependable:updated` event will be dispatched to all elements matching the `dependable-dependents-selector-value`. This gives us flexibility: disparate form fields don't need to be wrapped with a common Stimulus controlled-wrapper. This approach is favored over Stimulus `outlets` because here we're not coupling the functionality of the `dependable` and `dependent` fields. We're just dispatching Custom Events and using CSS selectors, preferably good old `form.field_id`'s.
|
49
|
+
|
50
|
+
To let our `:heard_from_other` field handle the `dependable:updated` event, we'll assume we have created a custom `field-availability` Stimulus controller, with a `#toggle` method, looking for the `expected` value on the incoming event `target` element, in this case the `dependable` field.
|
51
|
+
|
52
|
+
```erb
|
53
|
+
<%= render 'shared/fields/text_field',
|
54
|
+
method: :heard_from_other,
|
55
|
+
id: form.field_id(:heard_from_other),
|
56
|
+
options: {disabled: true},
|
57
|
+
data: {
|
58
|
+
controller: "field-availability",
|
59
|
+
action: "dependable:updated->field-availability#toggle",
|
60
|
+
field_availability_expected_value: "other"
|
61
|
+
}
|
62
|
+
%>
|
63
|
+
```
|
64
|
+
|
65
|
+
Note: `field-availability` here is not implemented in Bullet Train. It serves as an example.
|
66
|
+
|
67
|
+
Next, we'll find a way to only serve the `:heard_from_other` field to the user if "other" is selected, this time by using server-side conditionals in a `turbo_frame`.
|
68
|
+
|
69
|
+
## Dependent Fields Frame
|
70
|
+
|
71
|
+
What if you'd instead want to:
|
72
|
+
|
73
|
+
* Not rely on a custom Stimulus controller to control the `disabled` state of the "Other" field
|
74
|
+
* Show/hide multiple dependent fields based on the value of the `dependable` field.
|
75
|
+
* Update more than the field itself, but also the value of its `label`. As an example, the [`address_field`](/docs/field-partials/address-field.md) partial shows an empty "State / Province / Region" sub-field by default, and on changing the `:country_id` field to the United States, changes the whole `:region_id` to "State" as its label and with all US States as its choices.
|
76
|
+
|
77
|
+
For these situations, Bullet Train has a `dependent_fields_turbo_frame` partial that's made to listen to `dependable:updated` events by default.
|
78
|
+
|
79
|
+
```erb
|
80
|
+
# update the super-select `dependable-dependents-selector-value` to "##{form.field_id(:heard_from, :dependent_fields)}" to match
|
81
|
+
|
82
|
+
<%= render "shared/fields/dependent_fields_frame",
|
83
|
+
id: form.field_id(:heard_from, :dependent_fields),
|
84
|
+
form: form,
|
85
|
+
dependable_fields: [:heard_from] do %>
|
86
|
+
|
87
|
+
<% if form.object&.heard_from == "other" %>
|
88
|
+
<%# no need for a custom `id` or the `disabled` attribute %>
|
89
|
+
<%= render 'shared/fields/text_field', method: :heard_from_other %>
|
90
|
+
<% end %>
|
91
|
+
|
92
|
+
<%# include additional fields if "other" is selected %>
|
93
|
+
<% end %>
|
94
|
+
```
|
95
|
+
|
96
|
+
This `dependent_fields_frame` serves two purposes:
|
97
|
+
|
98
|
+
1. Handle the `dependable:updated` event, so that the frame can...
|
99
|
+
2. Re-fetch the current form URL (it could be for a `#new` or a `#edit`, it works in both situations) with a GET request (not a submit) that contains the `heard_from` value as a `query_string` param. It then ensures that our `form.object.heard_from` value gets populated with the value found in the `query_string` param automatically, with **no changes needed to the resource controller**. That's all handled by the `dependent_fields_frame` partial by reading its `dependable_fields` param.
|
100
|
+
|
101
|
+
With this functionality, the contents of the underlying `turbo_frame` will be populated with the updated fields.
|
102
|
+
|
103
|
+
---
|
104
|
+
|
105
|
+
Now let's say we want to come back to the `disabled` use case above, while using the `dependent_fields_frame` approach.
|
106
|
+
|
107
|
+
We'll move the conditional on the `disabled` property. And we'll also let the `dependent_fields_frame` underlying controller handle disabling the field automatically when the `turbo_frame` awaits updates.
|
108
|
+
|
109
|
+
```erb
|
110
|
+
<%= render "shared/fields/dependent_fields_frame",
|
111
|
+
id: form.field_id(:heard_from, :dependent_fields),
|
112
|
+
form: form,
|
113
|
+
dependable_fields: [:heard_from] do |dependent_fields_controller_name| %>
|
114
|
+
|
115
|
+
<%= render 'shared/fields/text_field',
|
116
|
+
method: :heard_from_other,
|
117
|
+
options: {disabled: form.object&.heard_from != "other"},
|
118
|
+
data: {"#{dependent_fields_controller_name}-target": "field"}
|
119
|
+
%>
|
120
|
+
<% end %>
|
121
|
+
```
|
122
|
+
|
123
|
+
To learn more about its inner functionality, search the `bullet-train-core` repo for `dependable_controller.js`, `dependent_fields_frame_controller.js` and `_dependent_fields_frame.html.erb`. You can also see an implementation by looking at the `_address_field.html.erb` partial.
|
@@ -47,6 +47,5 @@ Run the following command to generate the scaffolding for the `documents` field
|
|
47
47
|
If you're starting fresh, and don't have an existing model you can do something like this:
|
48
48
|
|
49
49
|
```
|
50
|
-
rails g model Project team:references name:string specification:attachment documents:attachments
|
51
50
|
bin/super-scaffold crud Project Team name:text_field specification:file_field documents:file_field{multiple}
|
52
51
|
```
|
@@ -144,3 +144,12 @@ You can pass these options to the super select partial like so:
|
|
144
144
|
```
|
145
145
|
|
146
146
|
*Passing options like this doesn't allow JS callbacks or functions to be used, so you must extend the Stimulus controller and add options to the `optionsOverride` getter if you want to do so.
|
147
|
+
|
148
|
+
## Dynamically Updating Form Fields
|
149
|
+
|
150
|
+
If you'd like to:
|
151
|
+
|
152
|
+
* modify other fields based on the value of your `super_select`, or
|
153
|
+
* modify your `super_select` based on the value of other fields
|
154
|
+
|
155
|
+
See [Dynamic Forms and Dependent Fields](/docs/field-partials/dynamic-forms-dependent-fields.md).
|
data/docs/field-partials.md
CHANGED
@@ -122,36 +122,40 @@ Certain form field partials like `buttons` and `super_select` can also have thei
|
|
122
122
|
|
123
123
|
## Available Field Partials
|
124
124
|
|
125
|
-
| Field Partial
|
126
|
-
|
127
|
-
| `
|
128
|
-
|
|
129
|
-
| `
|
130
|
-
| `
|
131
|
-
| `
|
132
|
-
| `
|
133
|
-
| `
|
134
|
-
| `
|
135
|
-
|
|
136
|
-
| `
|
137
|
-
| `
|
138
|
-
| `
|
139
|
-
|
|
140
|
-
| `
|
141
|
-
| `
|
142
|
-
| `
|
143
|
-
| `
|
125
|
+
| Field Partial | Data Type | Multiple Values? | Assignment Helpers | JavaScript Library | Description |
|
126
|
+
|----------------------------------------------------------|---------------------------|------------------|-------------------------|-----------------------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------|
|
127
|
+
| [`address_field`](/docs/field-partials/address-field.md) | `Address` | | | | Adds a block of address fields. On change, its country super-select auto-updates the state/province/region super-select and postal/zip code field label. |
|
128
|
+
| `boolean` | `boolean` | | `assign_boolean` | | |
|
129
|
+
| [`buttons`](/docs/field-partials/buttons.md) | `string` | Optionally | `assign_checkboxes` | | |
|
130
|
+
| `image` | `string` or `attachment`* | | | | |
|
131
|
+
| `color_picker` | `string` | | | [pickr](https://simonwep.github.io/pickr/) | |
|
132
|
+
| `date_and_time_field` | `datetime` | | | [Date Range Picker](https://www.daterangepicker.com) | |
|
133
|
+
| `date_field` | `date` | | | [Date Range Picker](https://www.daterangepicker.com) | |
|
134
|
+
| `email_field` | `string` | | | | |
|
135
|
+
| `emoji_field` | `string` | | | [Emoji Mart](https://missiveapp.com/open/emoji-mart) | A front-end library which allows users to browse and select emojis with ease. | |
|
136
|
+
| [`file_field`](/docs/field-partials/file-field.md) | `attachment` | | | [Active Storage](https://edgeguides.rubyonrails.org/active_storage_overview.html) | |
|
137
|
+
| `options` | `string` | Optionally | `assign_checkboxes` | | |
|
138
|
+
| `password_field` | `string` | | | | |
|
139
|
+
| `phone_field` | `string` | | | [International Telephone Input](https://intl-tel-input.com) | Ensures telephone numbers are in a format that can be used by providers like Twilio. |
|
140
|
+
| [`super_select`](/docs/field-partials/super-select.md) | `string` | Optionally | `assign_select_options` | [Select2](https://select2.org) | Provides powerful option search, AJAX search, and multi-select functionality. |
|
141
|
+
| `text_area` | `text` | | | | |
|
142
|
+
| `text_field` | `string` | | | | |
|
143
|
+
| `number_field` | `integer` | | | | |
|
144
|
+
| `trix_editor` | `text` | | | [Trix](https://github.com/basecamp/trix) | Basic HTML-powered formatting features and support for at-mentions amongst team members. |
|
144
145
|
|
145
146
|
* The data type for `image` fields will vary based on whether you're using Cloudinary or ActiveStorage.
|
146
147
|
For Cloudinary you should use `string`, and for ActiveStorage you should use `attachment`.
|
147
148
|
|
148
149
|
## A Note On Data Types
|
149
|
-
|
150
|
+
When creating a `multiple` option attribute, Bullet Train generates these values as a `jsonb`.
|
150
151
|
```
|
151
|
-
|
152
|
-
> bin/super-scaffold crud Project Team multiple_buttons:buttons{multiple}
|
152
|
+
bin/super-scaffold crud Project Team multiple_buttons:buttons{multiple}
|
153
153
|
```
|
154
154
|
|
155
|
+
This will run the following rails command.
|
156
|
+
```
|
157
|
+
rails generate model Project team:references multiple_buttons:jsonb
|
158
|
+
```
|
155
159
|
## Formating `date` and `date_and_time`
|
156
160
|
After Super Scaffolding a `date` or `date_and_time` field, you can pass a format for the object like so:
|
157
161
|
|
@@ -161,7 +165,19 @@ After Super Scaffolding a `date` or `date_and_time` field, you can pass a format
|
|
161
165
|
|
162
166
|
Please refer to the [Ruby on Rails documentation](https://guides.rubyonrails.org/i18n.html#adding-date-time-formats) for more information.
|
163
167
|
|
168
|
+
## Dynamic Forms and Dependent Fields
|
169
|
+
|
170
|
+
To dynamically update your forms on field changes, Bullet Train introduces two new concepts:
|
171
|
+
|
172
|
+
1. Dependent Fields Pattern
|
173
|
+
2. Dependent Fields Frame
|
174
|
+
|
175
|
+
These concepts are currently used by the `address_field` to dynamically update the _State / Province / Region_ field on _Country_ change, as well as the label for the _Postal Code_ field.
|
176
|
+
|
177
|
+
[Read more about Dynamic Forms and Dependent Fields](/docs/field-partials/dynamic-forms-dependent-fields.md)
|
178
|
+
|
164
179
|
## Additional Field Partials Documentation
|
180
|
+
- [`address_field`](/docs/field-partials/address-field.md)
|
165
181
|
- [`buttons`](/docs/field-partials/buttons.md)
|
166
182
|
- [`super_select`](/docs/field-partials/super-select.md)
|
167
183
|
- [`file_field`](/docs/field-partials/file-field.md)
|
data/docs/getting-started.md
CHANGED
@@ -8,16 +8,15 @@ Whether you want to build a new application with Bullet Train or contribute to B
|
|
8
8
|
|
9
9
|
If you're using Bullet Train for the first time, begin by learning these five important techniques:
|
10
10
|
|
11
|
-
1. Use `
|
11
|
+
1. Use `bin/super-scaffold crud` to scaffold a new model:
|
12
12
|
|
13
13
|
```
|
14
|
-
rails g model Project team:references name:string
|
15
14
|
bin/super-scaffold crud Project Team name:text_field
|
16
15
|
```
|
17
16
|
|
18
17
|
In this example, `Team` refers to the immediate parent of the `Project` resource. For more details, just run `bin/super-scaffold` or [read the documentation](/docs/super-scaffolding.md).
|
19
18
|
|
20
|
-
2. Use `
|
19
|
+
2. Use `bin/super-scaffold crud-field` to add a new field to a model you've already scaffolded:
|
21
20
|
|
22
21
|
```
|
23
22
|
rails g migration add_description_to_projects description:text
|
data/docs/modeling.md
CHANGED
@@ -68,7 +68,7 @@ Even if you know there's an attribute or model that you're going to want to poli
|
|
68
68
|
|
69
69
|
## A Systematic Approach
|
70
70
|
|
71
|
-
### 1. Write `
|
71
|
+
### 1. Write `bin/super-scaffold` commands in a scratch file.
|
72
72
|
|
73
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
74
|
|
@@ -39,7 +39,7 @@ Note that in this specific approach we don't need a `team:references` on `Messag
|
|
39
39
|
### 2. Super Scaffolding for `Entry`
|
40
40
|
|
41
41
|
```
|
42
|
-
bin/super-scaffold crud Entry Team entryable_type:buttons
|
42
|
+
bin/super-scaffold crud Entry Team entryable_type:buttons --skip-migration-generation
|
43
43
|
```
|
44
44
|
|
45
45
|
We use `entryable_type:buttons` because we're going to allow people to choose which type of `Entry` they're creating with a list of buttons. This isn't the only option available to us, but it's the easiest to implement for now.
|
data/docs/super-scaffolding.md
CHANGED
@@ -41,25 +41,28 @@ Let's implement the following feature:
|
|
41
41
|
|
42
42
|
> An organization has many projects.
|
43
43
|
|
44
|
-
First,
|
44
|
+
First, run the `crud` scaffolder:
|
45
|
+
```
|
46
|
+
bin/super-scaffold crud Project Team name:text_field
|
47
|
+
rake db:migrate
|
48
|
+
```
|
49
|
+
|
50
|
+
In the above example, `team` represents the model that a `Project` primarily belongs to. Also, `text_field` was selected from [the list of available field partials](/docs/field-partials.md). We'll show examples with `trix_editor` and `super_select` later.
|
51
|
+
|
52
|
+
Super Scaffolding automatically generates models for you. However, if you want to split this process, you can pass the `--skip-migration-generation` to the command.
|
45
53
|
|
54
|
+
For example, generate the model with the standard Rails generator:
|
46
55
|
```
|
47
56
|
rails g model Project team:references name:string
|
48
57
|
```
|
49
58
|
|
50
|
-
In the above example, `team` represents the model that a `Project` primarily belongs to.
|
51
|
-
|
52
59
|
⚠️ Don't run migrations right away. It would be fine in this case, but sometimes the subsequent Super Scaffolding step actually updates the migration as part of its magic.
|
53
60
|
|
54
|
-
|
55
|
-
|
61
|
+
Then you can run the scaffolder with the flag:
|
56
62
|
```
|
57
|
-
bin/super-scaffold crud Project Team name:text_field
|
58
|
-
rake db:migrate
|
63
|
+
bin/super-scaffold crud Project Team name:text_field --skip-migration-generation
|
59
64
|
```
|
60
65
|
|
61
|
-
In the above example, `text_field` was selected from [the list of available field partials](/docs/field-partials.md). We'll show examples with `trix_editor` and `super_select` later.
|
62
|
-
|
63
66
|
### 2. Nested CRUD Scaffolding with `crud`
|
64
67
|
|
65
68
|
Building on that example, let's implement the following feature:
|
@@ -68,22 +71,14 @@ Building on that example, let's implement the following feature:
|
|
68
71
|
A project has many goals.
|
69
72
|
```
|
70
73
|
|
71
|
-
|
72
|
-
|
73
|
-
```
|
74
|
-
rails g model Goal project:references description:string
|
75
|
-
```
|
76
|
-
|
77
|
-
You can see in the example above that there is no direct reference to team, but to `project` instead, since that's where a `Goal` belongs.
|
78
|
-
|
79
|
-
Now, run the `crud` scaffolder:
|
74
|
+
First, run the `crud` scaffolder:
|
80
75
|
|
81
76
|
```
|
82
77
|
bin/super-scaffold crud Goal Project,Team description:text_field
|
83
78
|
rake db:migrate
|
84
79
|
```
|
85
80
|
|
86
|
-
You can see in the example above how we've specified `Project,Team`, because we want to specify the entire chain of ownership back to the `Team`. This allows Super Scaffolding to automatically generate the required permissions.
|
81
|
+
You can see in the example above how we've specified `Project,Team`, because we want to specify the entire chain of ownership back to the `Team`. This allows Super Scaffolding to automatically generate the required permissions. Take note that this generates a foreign key for `Project` and not for `Team`.
|
87
82
|
|
88
83
|
### 3. Adding New Fields with `crud-field`
|
89
84
|
|
@@ -93,13 +88,7 @@ Building on the earlier example, consider the following new requirement:
|
|
93
88
|
|
94
89
|
> In addition to a name, a project can have a description.
|
95
90
|
|
96
|
-
|
97
|
-
|
98
|
-
```
|
99
|
-
rails g migration add_description_to_projects description:text
|
100
|
-
```
|
101
|
-
|
102
|
-
Then, use the `crud-field` scaffolder to add it throughout the application:
|
91
|
+
Use the `crud-field` scaffolder to add it throughout the application:
|
103
92
|
|
104
93
|
```
|
105
94
|
bin/super-scaffold crud-field Project description:trix_editor
|
@@ -113,6 +102,11 @@ If you want to scaffold a new field to use for read-only purposes, add the follo
|
|
113
102
|
bin/super-scaffold crud-field Project description:trix_editor{readonly}
|
114
103
|
```
|
115
104
|
|
105
|
+
Again, if you would like to automatically generate the migration on your own, pass the `--skip-migration-generation` flag:
|
106
|
+
```
|
107
|
+
bin/super-scaffold crud-field Project description:trix_editor --skip-migration-generation
|
108
|
+
```
|
109
|
+
|
116
110
|
### 4. Adding Option Fields with Fixed, Translatable Options
|
117
111
|
|
118
112
|
Continuing with the earlier example, let's address the following new requirement:
|
@@ -124,7 +118,6 @@ We have multiple [field partials](/docs/field-partials.md) that we could use for
|
|
124
118
|
In this example, let's add a status attribute and present it as buttons:
|
125
119
|
|
126
120
|
```
|
127
|
-
rails g migration add_status_to_projects status:string
|
128
121
|
bin/super-scaffold crud-field Project status:buttons
|
129
122
|
```
|
130
123
|
|
@@ -152,21 +145,16 @@ Although you might think this calls for a reference to `User`, we've learned the
|
|
152
145
|
|
153
146
|
We can accomplish this like so:
|
154
147
|
|
155
|
-
```
|
156
|
-
rails g migration add_lead_to_projects lead:references
|
157
|
-
```
|
158
|
-
|
159
|
-
Then, to add the field, we specify the following:
|
160
|
-
|
161
148
|
```
|
162
149
|
bin/super-scaffold crud-field Project lead_id:super_select{class_name=Membership}
|
163
150
|
rake db:migrate
|
164
151
|
```
|
165
152
|
|
166
|
-
There are
|
153
|
+
There are three important things to point out here:
|
167
154
|
|
168
|
-
1.
|
169
|
-
2.
|
155
|
+
1. The scaffolder automatically adds a foreign key for `lead` to `Project`.
|
156
|
+
2. When adding this foreign key the `references` column is generated under the name `lead`, but when we're specifying the _field_ we want to scaffold, we specify it as `lead_id`, because that's the name of the attribute on the form, in strong parameters, etc.
|
157
|
+
3. We have to specify the model name with the `class_name` option so that Super Scaffolding can fully work it's magic. We can't reflect on the association, because at this point the association isn't properly defined yet. With this information, Super Scaffolding can handle that step for you.
|
170
158
|
|
171
159
|
Finally, Super Scaffolding will prompt you to edit `app/models/project.rb` and implement the required logic in the `valid_leads` method. This is a template method that will be used to both populate the select field on the `Project` form, but also enforce some important security concerns in this multi-tenant system. In this case, you can define it as:
|
172
160
|
|
@@ -189,17 +177,12 @@ We can accomplish this with a new model, a new join model, and a `super_select`
|
|
189
177
|
First, let's create the tag model:
|
190
178
|
|
191
179
|
```
|
192
|
-
rails g model Projects::Tag team:references name:string
|
193
180
|
bin/super-scaffold crud Projects::Tag Team name:text_field
|
194
181
|
```
|
195
182
|
|
196
183
|
Note that project tags are specifically defined at the `Team` level. The same tag can be applied to multiple `Project` models.
|
197
184
|
|
198
|
-
Now, let's create a join model for the has-many-through association
|
199
|
-
|
200
|
-
```
|
201
|
-
rails g model Projects::AppliedTag project:references tag:references
|
202
|
-
```
|
185
|
+
Now, let's create a join model for the has-many-through association.
|
203
186
|
|
204
187
|
We're not going to scaffold this model with the typical `crud` scaffolder, but some preparation is needed before we can use it with the `crud-field` scaffolder, so we need to do the following:
|
205
188
|
|
@@ -237,34 +220,39 @@ Bullet Train comes with two different ways to handle image uploads.
|
|
237
220
|
|
238
221
|
#### Scaffolding images with Cloudinary
|
239
222
|
|
240
|
-
When you scaffold your model
|
223
|
+
When you scaffold your model a `string` is generated where Cloudinary can store a reference to the image.
|
224
|
+
|
225
|
+
Make sure you have the `CLOUDINARY_URL` environment variable to use Cloudinary for your images.
|
241
226
|
|
242
227
|
For instance to scaffold a `Project` model with a `logo` image upload.
|
228
|
+
Use `image` as a field type for super scaffolding:
|
243
229
|
|
244
230
|
```
|
245
|
-
|
231
|
+
bin/super-scaffold crud Project Team name:text_field logo:image
|
232
|
+
rake db:migrate
|
246
233
|
```
|
247
234
|
|
248
|
-
|
249
|
-
|
235
|
+
Under the hood, Bullet Train will generate your model with the following command:
|
250
236
|
```
|
251
|
-
bin/super-scaffold crud Project Team name:text_field
|
237
|
+
bin/super-scaffold crud Project Team name:text_field
|
252
238
|
rake db:migrate
|
253
239
|
```
|
240
|
+
|
254
241
|
#### Scaffolding images with ActiveStorage
|
255
242
|
|
256
|
-
When you scaffold your model
|
243
|
+
When you scaffold your model we generate an `attachment` type attribute.
|
257
244
|
|
258
245
|
For instance to scaffold a `Project` model with a `logo` image upload.
|
246
|
+
Use `image` as a field type for super scaffolding:
|
259
247
|
|
260
248
|
```
|
261
|
-
|
249
|
+
bin/super-scaffold crud Project Team name:text_field logo:image
|
250
|
+
rake db:migrate
|
262
251
|
```
|
263
252
|
|
264
|
-
|
265
|
-
|
253
|
+
Under the hood, Bullet Train will generate your model with the following command:
|
266
254
|
```
|
267
|
-
bin/super-scaffold crud Project Team name:text_field
|
255
|
+
bin/super-scaffold crud Project Team name:text_field
|
268
256
|
rake db:migrate
|
269
257
|
```
|
270
258
|
|
data/lib/bullet_train/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet_train
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andrew Culver
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-10-
|
11
|
+
date: 2023-10-04 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|
@@ -164,20 +164,6 @@ dependencies:
|
|
164
164
|
- - ">="
|
165
165
|
- !ruby/object:Gem::Version
|
166
166
|
version: '0'
|
167
|
-
- !ruby/object:Gem::Dependency
|
168
|
-
name: masamune-ast
|
169
|
-
requirement: !ruby/object:Gem::Requirement
|
170
|
-
requirements:
|
171
|
-
- - ">="
|
172
|
-
- !ruby/object:Gem::Version
|
173
|
-
version: 1.2.0
|
174
|
-
type: :runtime
|
175
|
-
prerelease: false
|
176
|
-
version_requirements: !ruby/object:Gem::Requirement
|
177
|
-
requirements:
|
178
|
-
- - ">="
|
179
|
-
- !ruby/object:Gem::Version
|
180
|
-
version: 1.2.0
|
181
167
|
- !ruby/object:Gem::Dependency
|
182
168
|
name: image_processing
|
183
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -725,8 +711,10 @@ files:
|
|
725
711
|
- docs/billing/usage.md
|
726
712
|
- docs/desktop.md
|
727
713
|
- docs/field-partials.md
|
714
|
+
- docs/field-partials/address-field.md
|
728
715
|
- docs/field-partials/buttons.md
|
729
716
|
- docs/field-partials/date-related-fields.md
|
717
|
+
- docs/field-partials/dynamic-forms-dependent-fields.md
|
730
718
|
- docs/field-partials/file-field.md
|
731
719
|
- docs/field-partials/super-select.md
|
732
720
|
- docs/font-awesome-pro.md
|