bullet_train 1.4.3 → 1.4.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/account/onboarding/invitation_lists_controller.rb +3 -0
- data/app/controllers/concerns/account/onboarding/invitation_lists/controller_base.rb +64 -0
- data/app/controllers/concerns/account/onboarding/user_details/controller_base.rb +7 -1
- data/app/helpers/account/onboarding/invitation_lists_helper.rb +9 -0
- data/app/helpers/account/users_helper.rb +7 -2
- data/app/models/account/onboarding/invitation_list.rb +7 -0
- data/app/models/concerns/account/onboarding/invitation_lists/base.rb +11 -0
- data/app/models/concerns/invitations/base.rb +1 -0
- data/app/views/account/onboarding/invitation_lists/_invitation_form.html.erb +11 -0
- data/app/views/account/onboarding/invitation_lists/new.html.erb +31 -0
- data/app/views/account/users/_form.html.erb +7 -5
- data/config/locales/en/onboarding/invitation_lists.en.yml +16 -0
- data/config/routes.rb +1 -0
- data/docs/field-partials/super-select.md +1 -1
- data/docs/field-partials.md +7 -4
- data/docs/super-scaffolding.md +40 -0
- data/docs/upgrades/yolo-130.md +4 -4
- data/docs/upgrades/yolo-140.md +4 -4
- data/docs/upgrades.md +4 -2
- data/lib/bullet_train/configuration.rb +6 -1
- data/lib/bullet_train/resolver.rb +6 -2
- data/lib/bullet_train/version.rb +1 -1
- data/lib/bullet_train.rb +4 -0
- data/lib/tasks/bullet_train_tasks.rake +19 -9
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1996b94dbb7fda2db6472e01a23499b91947f4b3faf3a97ac1b5b20630505e3a
|
4
|
+
data.tar.gz: 608ea60207cb19ff2c4b73d8b9129ed5d1e3fe2a0190645754b55372132f1c72
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: be8f7010714d24a73dd42ef91530e2d61f20b520bc39ca1444d5cf6881df10869d1836823e2dab378b0b8d12e0bb6a00be201db9c896ddd6242bc067e2522713
|
7
|
+
data.tar.gz: 3907fad30d953b8f7e693ec1f3e2bf56917de2fcf0bcbb5e8d8306bdeb7664663408bf013158670f8e55da0428a2313a680446e888cc097b622552ccedfc4865
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Account::Onboarding::InvitationLists::ControllerBase
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
layout "devise"
|
6
|
+
|
7
|
+
before_action do
|
8
|
+
# TODO: Is this okay?
|
9
|
+
@user = current_user
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def new
|
14
|
+
@account_onboarding_invitation_list = Account::Onboarding::InvitationList.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def create
|
18
|
+
@account_onboarding_invitation_list = Account::Onboarding::InvitationList.new(account_onboarding_invitation_list_params)
|
19
|
+
|
20
|
+
# Set default values for invitations and memberships.
|
21
|
+
# `save` below checks if the values are valid or not.
|
22
|
+
@account_onboarding_invitation_list.team = current_team
|
23
|
+
@account_onboarding_invitation_list.invitations.each_with_index do |invitation, idx|
|
24
|
+
invitation.team = current_team
|
25
|
+
invitation.from_membership = current_membership
|
26
|
+
invitation.membership.team = current_team
|
27
|
+
invitation.membership.user_email = invitation.email
|
28
|
+
end
|
29
|
+
|
30
|
+
respond_to do |format|
|
31
|
+
if @account_onboarding_invitation_list.save
|
32
|
+
format.html { redirect_to account_team_path(@user.teams.first), notice: "" }
|
33
|
+
format.json { render :show, status: :ok, location: [:account, @user] }
|
34
|
+
else
|
35
|
+
format.html { render :new, status: :unprocessable_entity }
|
36
|
+
format.json { render json: @account_onboarding_invitation_list.errors, status: :unprocessable_entity }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
# Never trust parameters from the scary internet, only allow the white list through.
|
44
|
+
def account_onboarding_invitation_list_params
|
45
|
+
params.require(:account_onboarding_invitation_list).permit(
|
46
|
+
invitations_attributes: [
|
47
|
+
:email,
|
48
|
+
membership_attributes: [
|
49
|
+
role_ids: []
|
50
|
+
]
|
51
|
+
]
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def current_membership
|
56
|
+
current_user.memberships.find_by(team: current_team)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Since there is only one membership (an admin) on the team when sending bulk invitations,
|
60
|
+
# we don't have to worry about filtering these roles according to if they're manageable or not.
|
61
|
+
def available_roles
|
62
|
+
current_membership.roles.map { |role| [role.attributes[:key]] + role.attributes[:manageable_roles] }.flatten.uniq.reject { |role| role.match?("default") }
|
63
|
+
end
|
64
|
+
end
|
@@ -24,8 +24,14 @@ module Account::Onboarding::UserDetails::ControllerBase
|
|
24
24
|
# if you update your own user account, devise will normally kick you out, so we do this instead.
|
25
25
|
bypass_sign_in current_user.reload
|
26
26
|
|
27
|
+
# Only redirect users to the bulk invitation page if they're the
|
28
|
+
# one who created the team (meaning there's only one membership on the team).
|
27
29
|
if @user.details_provided?
|
28
|
-
|
30
|
+
if bulk_invitations_enabled? && @user.teams.first.memberships.size == 1
|
31
|
+
format.html { redirect_to new_account_onboarding_invitation_list_path(@user) }
|
32
|
+
else
|
33
|
+
format.html { redirect_to account_team_path(@user.teams.first), notice: "" }
|
34
|
+
end
|
29
35
|
else
|
30
36
|
format.html {
|
31
37
|
flash[:error] = I18n.t("global.notifications.all_fields_required")
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Account::Onboarding::InvitationListsHelper
|
2
|
+
# When sending bulk invitations, the current user is the only one with a membership on the team.
|
3
|
+
# This means that we can access all of the available roles with the following code.
|
4
|
+
def available_roles
|
5
|
+
current_user.memberships.first.roles.map do |role|
|
6
|
+
[role.attributes[:key]] + role.attributes[:manageable_roles]
|
7
|
+
end.flatten.uniq.reject { |role| role.match?("default") }
|
8
|
+
end
|
9
|
+
end
|
@@ -1,10 +1,15 @@
|
|
1
1
|
module Account::UsersHelper
|
2
|
+
def photo_url_for_active_storage_attachment attachment, options
|
3
|
+
size_details = {resize_to_limit: [options[:width], options[:height]]}
|
4
|
+
attachment.representation(size_details)
|
5
|
+
end
|
6
|
+
|
2
7
|
def profile_photo_for(url: nil, email: nil, first_name: nil, last_name: nil, profile_header: false)
|
3
8
|
size_details = profile_header ? {width: 700, height: 200} : {width: 100, height: 100}
|
4
9
|
size_details[:crop] = :fill
|
5
10
|
|
6
11
|
if cloudinary_enabled? && !url.blank?
|
7
|
-
cl_image_path(url, size_details
|
12
|
+
cl_image_path(url, size_details)
|
8
13
|
elsif !url.blank?
|
9
14
|
url + "?" + size_details.to_param
|
10
15
|
else
|
@@ -38,7 +43,7 @@ module Account::UsersHelper
|
|
38
43
|
# leaving them in case we have other developers depending on these methods.
|
39
44
|
def profile_header_photo_for(url: nil, email: nil, first_name: nil, last_name: nil)
|
40
45
|
if cloudinary_enabled? && !url.blank?
|
41
|
-
cl_image_path(url, width: 700, height: 200, crop: :fill)
|
46
|
+
cl_image_path(url, {width: 700, height: 200, crop: :fill})
|
42
47
|
elsif !url.blank?
|
43
48
|
url + "?" + {size: 200}.to_param
|
44
49
|
else
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Account::Onboarding::InvitationLists::Base
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
belongs_to :team
|
6
|
+
has_many :invitations
|
7
|
+
has_many :memberships, through: :invitations
|
8
|
+
|
9
|
+
accepts_nested_attributes_for :invitations, :memberships
|
10
|
+
end
|
11
|
+
end
|
@@ -4,6 +4,7 @@ module Invitations::Base
|
|
4
4
|
included do
|
5
5
|
belongs_to :team
|
6
6
|
belongs_to :from_membership, class_name: "Membership"
|
7
|
+
belongs_to :invitation_list, class_name: "Account::Onboarding::InvitationList", optional: true
|
7
8
|
has_one :membership, dependent: :nullify
|
8
9
|
|
9
10
|
accepts_nested_attributes_for :membership
|
@@ -0,0 +1,11 @@
|
|
1
|
+
<%= form.fields_for :invitations, Invitation.new do |invitation_form| %>
|
2
|
+
<div class="col-span-1">
|
3
|
+
<%= render 'shared/fields/email_field', form: invitation_form, method: :email, options: {autofocus: true} %>
|
4
|
+
</div>
|
5
|
+
|
6
|
+
<%= invitation_form.fields_for :membership, Membership.new do |membership_form| %>
|
7
|
+
<div class="col-span-1">
|
8
|
+
<%= render 'shared/fields/super_select', form: membership_form, method: :role_ids, choices: available_roles, html_options: {multiple: true}, select2_options: {placeholder: t('.membership.roles.default')}, other_options: {label: t('.membership.roles.label')} %>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
<% end %>
|
@@ -0,0 +1,31 @@
|
|
1
|
+
<% @title = t('.header') %>
|
2
|
+
|
3
|
+
<%= render 'account/shared/workflow/box' do |box| %>
|
4
|
+
<% box.title @title %>
|
5
|
+
<% box.body do %>
|
6
|
+
<% within_fields_namespace(:self) do %>
|
7
|
+
<%= form_with(model: @account_onboarding_invitation_list, class: 'form', local: true) do |f| %>
|
8
|
+
<% f.object.errors.each do |error| %>
|
9
|
+
<%= render 'account/shared/forms/errors', form: f, attributes: [error.attribute], resource: @account_onboarding_invitation_list %>
|
10
|
+
<% end %>
|
11
|
+
<%= render 'account/shared/notices', form: f %>
|
12
|
+
|
13
|
+
<div class="grid gap-y gap-x grid-cols-2">
|
14
|
+
<% 1.times do %>
|
15
|
+
<%= render 'invitation_form', form: f %>
|
16
|
+
<% end %>
|
17
|
+
</div>
|
18
|
+
|
19
|
+
<div class="buttons">
|
20
|
+
<%= f.submit t('.buttons.next'), class: first_button_primary %>
|
21
|
+
<%= link_to t('.buttons.skip'), main_app.account_teams_path, class: first_button_primary %>
|
22
|
+
<% if other_teams.any? %>
|
23
|
+
<%= link_to t('global.buttons.back'), main_app.account_teams_path, class: first_button_primary %>
|
24
|
+
<% else %>
|
25
|
+
<%= link_to t('menus.main.labels.logout'), main_app.destroy_user_session_path(@user, onboard_logout: true), class: first_button_primary, method: 'delete' %>
|
26
|
+
<% end %>
|
27
|
+
</div>
|
28
|
+
<% end %>
|
29
|
+
<% end %>
|
30
|
+
<% end %>
|
31
|
+
<% end %>
|
@@ -10,11 +10,13 @@
|
|
10
10
|
</div>
|
11
11
|
|
12
12
|
<div class="sm:col-span-2">
|
13
|
-
<%
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
13
|
+
<%
|
14
|
+
image_method = :profile_photo
|
15
|
+
if cloudinary_enabled?
|
16
|
+
image_method = :profile_photo_id
|
17
|
+
end
|
18
|
+
%>
|
19
|
+
<%= render 'shared/fields/image', method: image_method %>
|
18
20
|
</div>
|
19
21
|
|
20
22
|
<div class="sm:col-span-2">
|
@@ -0,0 +1,16 @@
|
|
1
|
+
en:
|
2
|
+
onboarding: &onboarding
|
3
|
+
invitation_lists:
|
4
|
+
buttons: &buttons
|
5
|
+
next: Next
|
6
|
+
skip: Skip
|
7
|
+
new:
|
8
|
+
header: Invite your team members
|
9
|
+
buttons: *buttons
|
10
|
+
invitation_form:
|
11
|
+
membership:
|
12
|
+
roles:
|
13
|
+
label: Roles
|
14
|
+
default: Default
|
15
|
+
account:
|
16
|
+
onboarding: *onboarding
|
data/config/routes.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Note: before you attempt to manually wire up a `super_select` field, note that Super Scaffolding will automatically do that for your models. See [Super
|
1
|
+
Note: before you attempt to manually wire up a `super_select` field, note that Super Scaffolding will automatically do that for your models. See [Super Scaffolding](/docs/super-scaffolding.md) docs, section 4, for an example. And make sure Super Scaffolding doesn't automatically do what you're trying to do.
|
2
2
|
|
3
3
|
# Examples for the `super_select` Field Partial
|
4
4
|
|
data/docs/field-partials.md
CHANGED
@@ -81,12 +81,12 @@ When you're including multiple fields, you can DRY up redundant settings (e.g. `
|
|
81
81
|
<% with_field_settings form: form do %>
|
82
82
|
<%= render 'shared/fields/text_field', method: :text_field_value, options: {autofocus: true} %>
|
83
83
|
<%= render 'shared/fields/buttons', method: :button_value %>
|
84
|
-
<%= render 'shared/fields/
|
84
|
+
<%= render 'shared/fields/image', method: :cloudinary_image_value %>
|
85
85
|
<% end %>
|
86
86
|
```
|
87
87
|
|
88
88
|
## Field partials that integrate with third-party service providers
|
89
|
-
- `
|
89
|
+
- `image` makes it trivial to upload photos and videos to [Cloudinary](https://cloudinary.com) and store their resulting Cloudinary ID as an attribute of the model backing the form. To enable this field partial, sign up for Cloudinary and copy the "Cloudinary URL" they provide you with into your `config/application.yml` as `CLOUDINARY_URL`. If you use our [Heroku app.json](https://github.com/bullet-train-co/bullet_train/blob/main/app.json) to provision your production environment, this will happen in that environment automatically.
|
90
90
|
|
91
91
|
## Yaml Configuration
|
92
92
|
The localization Yaml file (where you configure label and option values for a field) is automatically generated when you run Super Scaffolding for a model. If you haven't done this yet, the localization Yaml file for `Scaffolding::CompletelyConcrete::TangibleThing` serves as a good example. Under `en.scaffolding/completely_concrete/tangible_things.fields` you'll see definitions like this:
|
@@ -126,7 +126,7 @@ Certain form field partials like `buttons` and `super_select` can also have thei
|
|
126
126
|
|--------------------------------------------------------|--------------|------------------|-------------------------|-----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------|
|
127
127
|
| `boolean` | `boolean` | | `assign_boolean` | | |
|
128
128
|
| [`buttons`](/docs/field-partials/buttons.md) | `string` | Optionally | `assign_checkboxes` | | |
|
129
|
-
| `
|
129
|
+
| `image` | `string` or `attachment`* | | | | |
|
130
130
|
| `color_picker` | `string` | | | [pickr](https://simonwep.github.io/pickr/) | |
|
131
131
|
| `date_and_time_field` | `datetime` | | | [Date Range Picker](https://www.daterangepicker.com) | |
|
132
132
|
| `date_field` | `date` | | | [Date Range Picker](https://www.daterangepicker.com) | |
|
@@ -139,9 +139,12 @@ Certain form field partials like `buttons` and `super_select` can also have thei
|
|
139
139
|
| [`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. |
|
140
140
|
| `text_area` | `text` | | | | |
|
141
141
|
| `text_field` | `string` | | | | |
|
142
|
-
| `number_field`
|
142
|
+
| `number_field` | `integer` | | | | |
|
143
143
|
| `trix_editor` | `text` | | | [Trix](https://github.com/basecamp/trix) | Basic HTML-powered formatting features and support for at-mentions amongst team members. |
|
144
144
|
|
145
|
+
* The data type for `image` fields will vary based on whether you're using Cloudinary or ActiveStorage.
|
146
|
+
For Cloudinary you should use `string`, and for ActiveStorage you should use `attachment`.
|
147
|
+
|
145
148
|
## A Note On Data Types
|
146
149
|
Set the data type to `jsonb` whenever passing the `multiple` option to a new attribute.
|
147
150
|
```
|
data/docs/super-scaffolding.md
CHANGED
@@ -228,6 +228,46 @@ end
|
|
228
228
|
|
229
229
|
Honestly, it's crazy that we got to the point where we can handle this particular use case automatically. It seems simple, but there is so much going on to make this feature work.
|
230
230
|
|
231
|
+
### 7. Scaffolding image upload attributes
|
232
|
+
|
233
|
+
Bullet Train comes with two different ways to handle image uploads.
|
234
|
+
|
235
|
+
* Cloudinary - This option allows your app deployment to be simpler because you don't need to ship any image manipulation libraries. But it does introduce a dependence on a 3rd party service.
|
236
|
+
* ActiveStorage - This option doesn't include reliance on a 3rd party service, but you do have to include image manipulation libararies in your deployment process.
|
237
|
+
|
238
|
+
#### Scaffolding images with Cloudinary
|
239
|
+
|
240
|
+
When you scaffold your model you need to include a `string` where Cloudinary can store a reference to the image.
|
241
|
+
|
242
|
+
For instance to scaffold a `Project` model with a `logo` image upload.
|
243
|
+
|
244
|
+
```
|
245
|
+
rails g model Project team:references name:string logo:string
|
246
|
+
```
|
247
|
+
|
248
|
+
Then you can use `image` as a field type for super scaffolding:
|
249
|
+
|
250
|
+
```
|
251
|
+
bin/super-scaffold crud Project Team name:text_field logo:image
|
252
|
+
rake db:migrate
|
253
|
+
```
|
254
|
+
#### Scaffolding images with ActiveStorage
|
255
|
+
|
256
|
+
When you scaffold your model you need to generate an `attachment` type attribute.
|
257
|
+
|
258
|
+
For instance to scaffold a `Project` model with a `logo` image upload.
|
259
|
+
|
260
|
+
```
|
261
|
+
rails g model Project team:references name:string logo:attachment
|
262
|
+
```
|
263
|
+
|
264
|
+
Then you can use `image` as a field type for super scaffolding:
|
265
|
+
|
266
|
+
```
|
267
|
+
bin/super-scaffold crud Project Team name:text_field logo:image
|
268
|
+
rake db:migrate
|
269
|
+
```
|
270
|
+
|
231
271
|
## Additional Notes
|
232
272
|
|
233
273
|
### `TangibleThing` and `CreativeConcept`
|
data/docs/upgrades/yolo-130.md
CHANGED
@@ -136,7 +136,7 @@ gem "bullet_train-themes-tailwind_css", BULLET_TRAIN_VERSION
|
|
136
136
|
(We have to do this since we didn't start explicitly tracking versions until `1.4.0` and
|
137
137
|
want to make sure that our gem versions match what the starter repo expects.)
|
138
138
|
|
139
|
-
Then run `bundle
|
139
|
+
Then run `bundle install`
|
140
140
|
|
141
141
|
Then go ahead and commit the changes.
|
142
142
|
|
@@ -247,7 +247,7 @@ BULLET_TRAIN_VERSION = "1.3.1"
|
|
247
247
|
(We have to do this since we didn't start explicitly tracking versions until `1.4.0` and
|
248
248
|
want to make sure that our gem versions match what the starter repo expects.)
|
249
249
|
|
250
|
-
Then run `bundle
|
250
|
+
Then run `bundle install`
|
251
251
|
|
252
252
|
Then go ahead and commit the changes.
|
253
253
|
|
@@ -272,7 +272,7 @@ If anything fails, investigate the failures and get things working again, and co
|
|
272
272
|
git checkout main
|
273
273
|
git merge updating-bullet-train-v1.3.1
|
274
274
|
git push origin main
|
275
|
-
git branch -d updating-bullet-train-v1.3.1
|
275
|
+
git branch -d updating-bullet-train-v1.3.1
|
276
276
|
```
|
277
277
|
|
278
|
-
Alternatively, if you're using GitHub, you can push the `updating-bullet-train-v1.3.1` branch up and create a PR from it and let your CI integration do
|
278
|
+
Alternatively, if you're using GitHub, you can push the `updating-bullet-train-v1.3.1` branch up and create a PR from it and let your CI integration do its thing and then merge in the PR and delete the branch there. (That's what we typically do.)
|
data/docs/upgrades/yolo-140.md
CHANGED
@@ -57,7 +57,7 @@ generate a new one that matches what you need:
|
|
57
57
|
|
58
58
|
```
|
59
59
|
git checkout HEAD -- Gemfile.lock
|
60
|
-
bundle
|
60
|
+
bundle install
|
61
61
|
```
|
62
62
|
|
63
63
|
If you choose to sort out `Gemfile.lock` by hand it's a good idea to run `bundle install` just to make
|
@@ -84,11 +84,11 @@ If anything fails, investigate the failures and get things working again, and co
|
|
84
84
|
|
85
85
|
```
|
86
86
|
git checkout main
|
87
|
-
git merge updating-bullet-train-v1.
|
87
|
+
git merge updating-bullet-train-v1.4.0
|
88
88
|
git push origin main
|
89
|
-
git branch -d updating-bullet-train-v1.
|
89
|
+
git branch -d updating-bullet-train-v1.4.0
|
90
90
|
```
|
91
91
|
|
92
|
-
Alternatively, if you're using GitHub, you can push the `updating-bullet-train-v1.
|
92
|
+
Alternatively, if you're using GitHub, you can push the `updating-bullet-train-v1.4.0` branch up and create a PR from it and let your CI integration do it's thing and then merge in the PR and delete the branch there. (That's what we typically do.)
|
93
93
|
|
94
94
|
|
data/docs/upgrades.md
CHANGED
@@ -30,6 +30,8 @@ recent version of the starter repository should cause a merge conflict in Git. T
|
|
30
30
|
opportunity to compare our upstream changes with your local customizations and allow you to resolve them in a way that makes sense for
|
31
31
|
your application.
|
32
32
|
|
33
|
+
⚠️ If you have ejected files or a new custom theme, there is a possibility that those ejected files need to be updated although no merge conflicts arose from `git merge`. You will need to compare your ejected views with the original views in [bullet_train-core](https://github.com/bullet-train-co/bullet_train-core) to ensure everything is working properly. Please refer to the documentation on [indirection](indirection) to find out more about ejected views.
|
34
|
+
|
33
35
|
### 1. Decide which version you want to upgrade to
|
34
36
|
|
35
37
|
For the purposes of these instructions we'll assume that you're on version `1.4.0` and are going to upgrade to version `1.4.1`.
|
@@ -66,7 +68,7 @@ git checkout -b updating-bullet-train-1.4.1
|
|
66
68
|
|
67
69
|
### 5. Merge in the newest stuff from Bullet Train and resolve any merge conflicts.
|
68
70
|
|
69
|
-
Each version of the starter repo is tagged, so you can merge in the tag from the
|
71
|
+
Each version of the starter repo is tagged, so you can merge in the tag from the upstream repo.
|
70
72
|
|
71
73
|
```
|
72
74
|
git merge v1.4.1
|
@@ -114,5 +116,5 @@ git branch -d updating-bullet-train-1.4.1
|
|
114
116
|
```
|
115
117
|
|
116
118
|
Alternatively, if you're using GitHub, you can push the `updating-bullet-train-1.4.1` branch up and create a
|
117
|
-
PR from it and let your CI integration do
|
119
|
+
PR from it and let your CI integration do its thing and then merge in the PR and delete the branch there.
|
118
120
|
(That's what we typically do.)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module BulletTrain
|
2
2
|
class Configuration
|
3
3
|
include Singleton
|
4
|
-
attr_accessor :strong_passwords, :incoming_webhooks_parent_class_name
|
4
|
+
attr_accessor :strong_passwords, :enable_bulk_invitations, :incoming_webhooks_parent_class_name
|
5
5
|
|
6
6
|
@@config = nil
|
7
7
|
|
@@ -10,6 +10,7 @@ module BulletTrain
|
|
10
10
|
|
11
11
|
# Default values
|
12
12
|
@strong_passwords = true
|
13
|
+
@enable_bulk_invitations = false
|
13
14
|
@incoming_webhooks_parent_class_name = "ApplicationRecord"
|
14
15
|
end
|
15
16
|
|
@@ -18,6 +19,10 @@ module BulletTrain
|
|
18
19
|
@@config&.strong_passwords
|
19
20
|
end
|
20
21
|
|
22
|
+
def enable_bulk_invitations
|
23
|
+
@@config&.enable_bulk_invitations
|
24
|
+
end
|
25
|
+
|
21
26
|
def incoming_webhooks_parent_class_name
|
22
27
|
@@config&.incoming_webhooks_parent_class_name
|
23
28
|
end
|
@@ -11,12 +11,16 @@ module BulletTrain
|
|
11
11
|
def run(eject: false, open: false, force: false, interactive: false)
|
12
12
|
# Try to figure out what kind of thing they're trying to look up.
|
13
13
|
source_file = calculate_source_file_details
|
14
|
+
source_file[:relative_path] = nil
|
14
15
|
|
15
16
|
if source_file[:absolute_path]
|
16
17
|
puts ""
|
17
18
|
puts "Absolute path:".green
|
18
19
|
puts " #{source_file[:absolute_path]}".green
|
19
20
|
puts ""
|
21
|
+
|
22
|
+
source_file[:relative_path] = source_file[:absolute_path].split(/(?=#{source_file[:package_name]})/).pop
|
23
|
+
|
20
24
|
if source_file[:package_name].present?
|
21
25
|
puts "Package name:".green
|
22
26
|
puts " #{source_file[:package_name]}".green
|
@@ -44,9 +48,9 @@ module BulletTrain
|
|
44
48
|
File.open((source_file[:project_path]).to_s, "w+") do |file|
|
45
49
|
case source_file[:project_path].split(".").last
|
46
50
|
when "rb", "yml"
|
47
|
-
file.puts "# Ejected from `#{source_file[:package_name]}`.\n\n"
|
51
|
+
file.puts "# Ejected from `#{source_file[:relative_path] || source_file[:package_name]}`.\n\n"
|
48
52
|
when "erb"
|
49
|
-
file.puts "<% # Ejected from `#{source_file[:package_name]}`. %>\n\n"
|
53
|
+
file.puts "<% # Ejected from `#{source_file[:relative_path] || source_file[:package_name]}`. %>\n\n"
|
50
54
|
end
|
51
55
|
end
|
52
56
|
`cat #{source_file[:absolute_path]} >> #{source_file[:project_path]}`.strip
|
data/lib/bullet_train/version.rb
CHANGED
data/lib/bullet_train.rb
CHANGED
@@ -167,6 +167,10 @@ def openai_organization_exists?
|
|
167
167
|
ENV["OPENAI_ORGANIZATION_ID"]
|
168
168
|
end
|
169
169
|
|
170
|
+
def bulk_invitations_enabled?
|
171
|
+
BulletTrain::Configuration.enable_bulk_invitations
|
172
|
+
end
|
173
|
+
|
170
174
|
def disable_developer_menu?
|
171
175
|
ENV["DISABLE_DEVELOPER_MENU"].present?
|
172
176
|
end
|
@@ -82,20 +82,30 @@ namespace :bullet_train do
|
|
82
82
|
gem_names.each do |gem|
|
83
83
|
puts "Searching for locales in #{gem}...".blue
|
84
84
|
gem_path = `bundle show #{gem}`.chomp
|
85
|
+
gem_with_version = gem_path.split("/").last
|
85
86
|
locales = Dir.glob("#{gem_path}/**/config/locales/**/*.yml").reject { |path| path.match?("dummy") }
|
86
87
|
next if locales.empty?
|
87
88
|
|
88
89
|
puts "Found locales. Ejecting to your application...".green
|
89
90
|
locales.each do |locale|
|
90
|
-
relative_path = locale.split(
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
relative_path = locale.split(/(?=#{gem_with_version}\/config\/locales)/).last
|
92
|
+
path_in_locale = locale.split("/config/locales").pop
|
93
|
+
|
94
|
+
base_path = relative_path.split("/")
|
95
|
+
base_path.pop
|
96
|
+
base_path = base_path.join("/")
|
97
|
+
starter_repo_locale_path = base_path.gsub(gem_with_version, ".")
|
98
|
+
|
99
|
+
FileUtils.mkdir_p(starter_repo_locale_path) unless Dir.exist?(starter_repo_locale_path)
|
100
|
+
|
101
|
+
unless File.exist?("config/locales#{path_in_locale}")
|
102
|
+
puts "Ejecting #{path_in_locale}..."
|
103
|
+
File.new("config/locales#{path_in_locale}", "w")
|
104
|
+
`cp #{locale} config/locales#{path_in_locale}`
|
105
|
+
file = Pathname.new("config/locales#{path_in_locale}")
|
106
|
+
lines = file.readlines
|
107
|
+
lines.unshift("# Ejected from #{relative_path}\n\n")
|
108
|
+
file.write(lines.join)
|
99
109
|
end
|
100
110
|
end
|
101
111
|
end
|
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.
|
4
|
+
version: 1.4.5
|
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-09-
|
11
|
+
date: 2023-09-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: standard
|
@@ -513,6 +513,7 @@ files:
|
|
513
513
|
- app/assets/config/bullet_train_manifest.js
|
514
514
|
- app/controllers/account/invitations_controller.rb
|
515
515
|
- app/controllers/account/memberships_controller.rb
|
516
|
+
- app/controllers/account/onboarding/invitation_lists_controller.rb
|
516
517
|
- app/controllers/account/onboarding/user_details_controller.rb
|
517
518
|
- app/controllers/account/onboarding/user_email_controller.rb
|
518
519
|
- app/controllers/account/teams_controller.rb
|
@@ -521,6 +522,7 @@ files:
|
|
521
522
|
- app/controllers/concerns/account/controllers/base.rb
|
522
523
|
- app/controllers/concerns/account/invitations/controller_base.rb
|
523
524
|
- app/controllers/concerns/account/memberships/controller_base.rb
|
525
|
+
- app/controllers/concerns/account/onboarding/invitation_lists/controller_base.rb
|
524
526
|
- app/controllers/concerns/account/onboarding/user_details/controller_base.rb
|
525
527
|
- app/controllers/concerns/account/onboarding/user_email/controller_base.rb
|
526
528
|
- app/controllers/concerns/account/teams/controller_base.rb
|
@@ -541,6 +543,7 @@ files:
|
|
541
543
|
- app/helpers/account/locale_helper.rb
|
542
544
|
- app/helpers/account/markdown_helper.rb
|
543
545
|
- app/helpers/account/memberships_helper.rb
|
546
|
+
- app/helpers/account/onboarding/invitation_lists_helper.rb
|
544
547
|
- app/helpers/account/role_helper.rb
|
545
548
|
- app/helpers/account/teams_helper.rb
|
546
549
|
- app/helpers/account/users_helper.rb
|
@@ -567,12 +570,14 @@ files:
|
|
567
570
|
- app/mailers/concerns/mailers/base.rb
|
568
571
|
- app/mailers/devise_mailer.rb
|
569
572
|
- app/mailers/user_mailer.rb
|
573
|
+
- app/models/account/onboarding/invitation_list.rb
|
570
574
|
- app/models/address.rb
|
571
575
|
- app/models/addresses/continent.rb
|
572
576
|
- app/models/addresses/country.rb
|
573
577
|
- app/models/addresses/region.rb
|
574
578
|
- app/models/addresses/subcontinent.rb
|
575
579
|
- app/models/billing/mock_limiter.rb
|
580
|
+
- app/models/concerns/account/onboarding/invitation_lists/base.rb
|
576
581
|
- app/models/concerns/addresses/base.rb
|
577
582
|
- app/models/concerns/addresses/continents/base.rb
|
578
583
|
- app/models/concerns/addresses/countries/base.rb
|
@@ -606,6 +611,8 @@ files:
|
|
606
611
|
- app/views/account/memberships/edit.html.erb
|
607
612
|
- app/views/account/memberships/index.html.erb
|
608
613
|
- app/views/account/memberships/show.html.erb
|
614
|
+
- app/views/account/onboarding/invitation_lists/_invitation_form.html.erb
|
615
|
+
- app/views/account/onboarding/invitation_lists/new.html.erb
|
609
616
|
- app/views/account/onboarding/user_details/edit.html.erb
|
610
617
|
- app/views/account/onboarding/user_email/edit.html.erb
|
611
618
|
- app/views/account/teams/_breadcrumbs.html.erb
|
@@ -664,6 +671,7 @@ files:
|
|
664
671
|
- config/locales/en/invitations.en.yml
|
665
672
|
- config/locales/en/memberships.en.yml
|
666
673
|
- config/locales/en/oauth.en.yml
|
674
|
+
- config/locales/en/onboarding/invitation_lists.en.yml
|
667
675
|
- config/locales/en/onboarding/user_details.en.yml
|
668
676
|
- config/locales/en/onboarding/user_email.en.yml
|
669
677
|
- config/locales/en/roles.en.yml
|